From 46014cd277bb88d0ba023b845cec5a03c926c9ce Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Fri, 13 Sep 2024 16:43:33 -0400 Subject: [PATCH] [FIRRTL] Convert IMCP to walk Change IMCP's module update to use a walk instead of only visiting top-level ops. This is intended to be an entirely mechanical change and is broken out in a separate commit because of its mechanical nature. Signed-off-by: Schuyler Eldridge --- lib/Dialect/FIRRTL/Transforms/IMConstProp.cpp | 141 +++++++++--------- 1 file changed, 73 insertions(+), 68 deletions(-) diff --git a/lib/Dialect/FIRRTL/Transforms/IMConstProp.cpp b/lib/Dialect/FIRRTL/Transforms/IMConstProp.cpp index f151d3587dfd..073f8a5ea35e 100644 --- a/lib/Dialect/FIRRTL/Transforms/IMConstProp.cpp +++ b/lib/Dialect/FIRRTL/Transforms/IMConstProp.cpp @@ -19,6 +19,7 @@ #include "circt/Dialect/FIRRTL/FIRRTLUtils.h" #include "circt/Dialect/FIRRTL/Passes.h" #include "circt/Support/APInt.h" +#include "mlir/IR/Iterators.h" #include "mlir/IR/Threading.h" #include "mlir/Pass/Pass.h" #include "llvm/ADT/APSInt.h" @@ -1006,88 +1007,92 @@ void IMConstPropPass::rewriteModuleBody(FModuleOp module) { for (auto &port : body->getArguments()) replaceValueIfPossible(port); - // TODO: Walk 'when's preorder with `walk`. - // Walk the IR bottom-up when folding. We often fold entire chains of // operations into constants, which make the intermediate nodes dead. Going // bottom up eliminates the users of the intermediate ops, allowing us to // aggressively delete them. + // + // TODO: Handle WhenOps correctly. bool aboveCursor = false; - for (auto &op : llvm::make_early_inc_range(llvm::reverse(*body))) { - auto dropIfDead = [&](Operation &op, const Twine &debugPrefix) { - if (op.use_empty() && - (wouldOpBeTriviallyDead(&op) || isDeletableWireOrRegOrNode(&op))) { - LLVM_DEBUG( - { logger.getOStream() << debugPrefix << " : " << op << "\n"; }); - ++numErasedOp; - op.erase(); - return true; - } - return false; - }; + module.walk( + [&](Operation *op) { + auto dropIfDead = [&](Operation *op, const Twine &debugPrefix) { + if (op->use_empty() && + (wouldOpBeTriviallyDead(op) || isDeletableWireOrRegOrNode(op))) { + LLVM_DEBUG( + { logger.getOStream() << debugPrefix << " : " << op << "\n"; }); + ++numErasedOp; + op->erase(); + return true; + } + return false; + }; - if (aboveCursor) { - // Drop dead constants we materialized. - dropIfDead(op, "Trivially dead materialized constant"); - continue; - } - // Stop once hit the generated constants. - if (&op == cursor) { - cursor.erase(); - aboveCursor = true; - continue; - } + if (aboveCursor) { + // Drop dead constants we materialized. + dropIfDead(op, "Trivially dead materialized constant"); + return WalkResult::advance(); + } + // Stop once hit the generated constants. + if (op == cursor) { + cursor.erase(); + aboveCursor = true; + return WalkResult::advance(); + } - // Connects to values that we found to be constant can be dropped. - if (auto connect = dyn_cast(op)) { - if (auto *destOp = connect.getDest().getDefiningOp()) { - auto fieldRef = getOrCacheFieldRefFromValue(connect.getDest()); - // Don't remove a field-level connection even if the src value is - // constant. If other elements of the aggregate value are not constant, - // the aggregate value cannot be replaced. We can forward the constant - // to its users, so IMDCE (or SV/HW canonicalizer) should remove the - // aggregate if entire aggregate is dead. - auto type = type_dyn_cast(connect.getDest().getType()); - if (!type) - continue; - auto baseType = type_dyn_cast(type); - if (baseType && !baseType.isGround()) - continue; - if (isDeletableWireOrRegOrNode(destOp) && !isOverdefined(fieldRef)) { - connect.erase(); - ++numErasedOp; + // Connects to values that we found to be constant can be dropped. + if (auto connect = dyn_cast(op)) { + if (auto *destOp = connect.getDest().getDefiningOp()) { + auto fieldRef = getOrCacheFieldRefFromValue(connect.getDest()); + // Don't remove a field-level connection even if the src value is + // constant. If other elements of the aggregate value are not + // constant, the aggregate value cannot be replaced. We can forward + // the constant to its users, so IMDCE (or SV/HW canonicalizer) + // should remove the aggregate if entire aggregate is dead. + auto type = type_dyn_cast(connect.getDest().getType()); + if (!type) + return WalkResult::advance(); + auto baseType = type_dyn_cast(type); + if (baseType && !baseType.isGround()) + return WalkResult::advance(); + if (isDeletableWireOrRegOrNode(destOp) && + !isOverdefined(fieldRef)) { + connect.erase(); + ++numErasedOp; + } + } + return WalkResult::advance(); } - } - continue; - } - // We only fold single-result ops and instances in practice, because they - // are the expressions. - if (op.getNumResults() != 1 && !isa(op)) - continue; + // We only fold single-result ops and instances in practice, because + // they are the expressions. + if (op->getNumResults() != 1 && !isa(op)) + return WalkResult::advance(); - // If this operation is already dead, then go ahead and remove it. - if (dropIfDead(op, "Trivially dead")) - continue; + // If this operation is already dead, then go ahead and remove it. + if (dropIfDead(op, "Trivially dead")) + return WalkResult::advance(); - // Don't "fold" constants (into equivalent), also because they - // may have name hints we'd like to preserve. - if (op.hasTrait()) - continue; + // Don't "fold" constants (into equivalent), also because they + // may have name hints we'd like to preserve. + if (op->hasTrait()) + return WalkResult::advance(); - // If the op had any constants folded, replace them. - builder.setInsertionPoint(&op); - bool foldedAny = false; - for (auto result : op.getResults()) - foldedAny |= replaceValueIfPossible(result); + // If the op had any constants folded, replace them. + builder.setInsertionPoint(op); + bool foldedAny = false; + for (auto result : op->getResults()) + foldedAny |= replaceValueIfPossible(result); - if (foldedAny) - ++numFoldedOp; + if (foldedAny) + ++numFoldedOp; - // If the operation folded to a constant then we can probably nuke it. - if (foldedAny && dropIfDead(op, "Made dead")) - continue; - } + // If the operation folded to a constant then we can probably nuke it. + if (foldedAny && dropIfDead(op, "Made dead")) + return WalkResult::advance(); + + return WalkResult::advance(); + }); } std::unique_ptr circt::firrtl::createIMConstPropPass() {