14#include "mlir/IR/BuiltinAttributes.h"
15#include "mlir/IR/Iterators.h"
16#include "mlir/Interfaces/SideEffectInterfaces.h"
17#include "llvm/ADT/DenseMapInfoVariant.h"
18#include "llvm/ADT/PostOrderIterator.h"
19#include "llvm/ADT/SmallVector.h"
22#define DEBUG_TYPE "hw-imdeadcodeelim"
26#define GEN_PASS_DEF_IMDEADCODEELIM
27#include "circt/Dialect/HW/Passes.h.inc"
35struct HWIMDeadCodeElim
36 : circt::hw::impl::IMDeadCodeElimBase<HWIMDeadCodeElim> {
39 void runOnOperation()
override;
42 using ElementType = std::variant<Value, HWModuleLike, HWInstanceLike>;
44 void markAlive(ElementType element) {
45 if (!liveElements.insert(element).second)
47 worklist.push_back(element);
50 bool isKnownAlive(HWInstanceLike instanceLike)
const {
51 return liveElements.count(instanceLike);
53 bool isKnownAlive(Value value)
const {
54 assert(value &&
"null should not be used");
55 return liveElements.count(value);
57 bool isAssumedDead(Value value)
const {
return !isKnownAlive(value); }
58 bool isAssumedDead(Operation *op)
const {
59 return llvm::none_of(op->getResults(),
60 [&](Value value) { return isKnownAlive(value); });
62 bool isBlockExecutable(Block *block) {
return executableBlocks.count(block); }
64 SmallVector<ElementType, 64> worklist;
65 llvm::DenseSet<ElementType> liveElements;
68 DenseSet<Block *> executableBlocks;
70 void markBlockExecutable(Block *block);
72 void visitInstanceLike(HWInstanceLike instanceLike);
73 void visitValue(Value value);
75 void rewriteModuleSignature(
HWModuleOp module);
79 void markUnknownSideEffectOp(Operation *op);
80 void markInstanceLike(HWInstanceLike instanceLike);
82 void markBlockUndeletable(Operation *op) {
84 markAlive(op->getParentOfType<HWModuleLike>());
90 if (!(mlir::isMemoryEffectFree(op) ||
91 mlir::hasSingleEffect<mlir::MemoryEffects::Allocate>(op) ||
92 mlir::hasSingleEffect<mlir::MemoryEffects::Read>(op))) {
95 if (
auto innerSymOp = dyn_cast<InnerSymbolOpInterface>(op)) {
96 if (innerSymOp.getInnerName().has_value())
106 const llvm::BitVector &inErasures,
107 const llvm::BitVector &outErasures) {
108 assert(outErasures.size() >= instance->getNumResults() &&
109 "out_erasures is not at least as large as getNumResults()");
110 assert(inErasures.size() >= instance->getNumOperands() &&
111 "in_erasures is not at least as large as getNumOperands()");
114 SmallVector<Type> newResultTypes = removeElementsAtIndices<Type>(
115 SmallVector<Type>(instance->result_type_begin(),
116 instance->result_type_end()),
118 auto newResultNames = removeElementsAtIndices<mlir::Attribute>(
119 SmallVector<mlir::Attribute>(instance.getResultNames().begin(),
120 instance.getResultNames().end()),
124 auto newOperands = removeElementsAtIndices<mlir::Value>(
125 SmallVector<mlir::Value>(instance->getOperands().begin(),
126 instance->getOperands().end()),
128 auto newOperandNames = removeElementsAtIndices<mlir::Attribute>(
129 SmallVector<mlir::Attribute>(instance.getArgNames().begin(),
130 instance.getArgNames().end()),
133 ImplicitLocOpBuilder builder(instance->getLoc(), instance);
135 auto newOpNamesArrayAttr = builder.getArrayAttr(newOperandNames);
136 auto newResultNamesArrayAttr = builder.getArrayAttr(newResultNames);
138 auto newInstance = InstanceOp::create(
139 builder, instance->getLoc(), newResultTypes,
140 instance.getInstanceNameAttr(), instance.getModuleName(), newOperands,
141 newOpNamesArrayAttr, newResultNamesArrayAttr, instance.getParameters(),
142 instance.getInnerSymAttr());
153 const llvm::BitVector &inErasures,
154 const llvm::BitVector &outErasures) {
160 for (
size_t index = 0, e = instance->getNumResults(); index < e; ++index) {
161 auto r1 = instance->getResult(index);
162 if (outErasures[index]) {
166 auto r2 = newInstance->getResult(index - erased);
167 r1.replaceAllUsesWith(r2);
173void HWIMDeadCodeElim::markUnknownSideEffectOp(Operation *op) {
176 for (
auto result : op->getResults())
178 for (
auto operand : op->getOperands())
180 markBlockUndeletable(op);
183void HWIMDeadCodeElim::markInstanceLike(HWInstanceLike instanceLike) {
185 for (StringAttr moduleName :
186 instanceLike.getReferencedModuleNamesAttr().getAsRange<StringAttr>()) {
187 auto *node = instanceGraph->lookup(moduleName);
192 auto op = node->getModule();
197 if (!isa<HWModuleOp>(op.getOperation())) {
198 for (
auto operand : instanceLike->getOperands())
201 markAlive(instanceLike);
204 if (
auto moduleLike = dyn_cast<HWModuleLike>(op.getOperation()))
205 markBlockExecutable(moduleLike.getBodyBlock());
209void HWIMDeadCodeElim::markBlockExecutable(Block *block) {
213 if (!executableBlocks.insert(block).second)
216 auto moduleLike = dyn_cast<HWModuleLike>(block->getParentOp());
219 auto module = dyn_cast<HWModuleOp>(moduleLike.getOperation());
221 markAlive(moduleLike);
222 else if (module.isPublic())
225 for (
auto &op : *block) {
226 if (
auto instance = dyn_cast<HWInstanceLike>(op))
227 markInstanceLike(instance);
229 markUnknownSideEffectOp(&op);
232 for (
auto ®ion : op.getRegions())
233 for (auto &block : region.getBlocks())
234 markBlockExecutable(&block);
246void HWIMDeadCodeElim::visitInstanceLike(HWInstanceLike instanceLike) {
248 for (mlir::StringAttr moduleName :
249 instanceLike.getReferencedModuleNamesAttr().getAsRange<StringAttr>()) {
250 auto *moduleNode = instanceGraph->lookup(moduleName);
257 dyn_cast<HWModuleOp>(moduleNode->getModule().getOperation())) {
260 for (
auto blockArg : module.
getBodyBlock()->getArguments()) {
261 auto portIndex = blockArg.getArgNumber();
263 if (isKnownAlive(blockArg))
264 markAlive(instanceLike->getOperand(portIndex));
272 dyn_cast<HWModuleLike>(moduleNode->getModule().getOperation()))
273 markAlive(extModule);
285void HWIMDeadCodeElim::visitValue(Value value) {
286 assert(isKnownAlive(value) &&
"only alive values reach here");
290 if (
auto blockArg = dyn_cast<BlockArgument>(value)) {
292 dyn_cast<HWModuleLike>(blockArg.getParentBlock()->getParentOp())) {
294 for (
auto *instRec : instanceGraph->lookup(module)->uses()) {
295 if (!instRec->getInstance())
298 if (
auto instance = dyn_cast<HWInstanceLike>(
299 instRec->getInstance().getOperation())) {
300 if (liveElements.contains(instance))
301 markAlive(instance->getOperand(blockArg.getArgNumber()));
309 if (
auto instanceLike = value.getDefiningOp<HWInstanceLike>()) {
310 auto instanceResult = cast<mlir::OpResult>(value);
315 for (mlir::StringAttr moduleName :
316 instanceLike.getReferencedModuleNamesAttr().getAsRange<StringAttr>()) {
317 auto *node = instanceGraph->lookupOrNull(moduleName);
322 Operation *moduleOp = node->getModule().getOperation();
323 auto moduleLike = dyn_cast<HWModuleLike>(moduleOp);
328 if (!moduleLike.getBodyBlock())
330 auto *moduleOutputOp = moduleLike.getBodyBlock()->getTerminator();
332 moduleOutputOp->getOperand(instanceResult.getResultNumber());
333 markAlive(modulePortVal);
334 markAlive(instanceLike);
342 if (
auto *op = value.getDefiningOp()) {
343 for (
auto operand : op->getOperands())
345 for (
auto ®ion : op->getRegions())
346 for (auto &block : region)
347 markBlockExecutable(&block);
351void HWIMDeadCodeElim::runOnOperation() {
353 instanceGraph = &getAnalysis<hw::InstanceGraph>();
355 for (
auto moduleLike : getOperation().getOps<
hw::HWModuleLike>()) {
358 if (
auto module = dyn_cast<HWModuleOp>(moduleLike.getOperation())) {
359 if (!module.isPublic())
363 if (!moduleLike.getBodyBlock())
366 markBlockExecutable(moduleLike.getBodyBlock());
372 if (moduleLike.getBodyBlock()->empty())
374 auto *moduleOutputOp = moduleLike.getBodyBlock()->getTerminator();
375 if (!dyn_cast<OutputOp>(moduleOutputOp))
377 for (
auto port : moduleOutputOp->getOperands())
382 while (!worklist.empty()) {
383 auto v = worklist.pop_back_val();
384 if (
auto *value = std::get_if<Value>(&v)) {
386 }
else if (
auto *instance = std::get_if<HWInstanceLike>(&v)) {
387 visitInstanceLike(*instance);
388 }
else if (std::get_if<HWModuleLike>(&v)) {
394 auto liveAttr = StringAttr::get(&getContext(),
"LIVE");
395 auto deadAttr = StringAttr::get(&getContext(),
"DEAD");
397 auto getLiveness = [&](
bool alive) {
return alive ? liveAttr : deadAttr; };
399 auto getValLiveness = [&](ValueRange values) {
400 SmallVector<Attribute> liveness;
401 for (
auto value : values)
402 liveness.push_back(getLiveness(isKnownAlive(value)));
403 return ArrayAttr::get(&getContext(), liveness);
406 getOperation()->walk([&](Operation *op) {
407 if (
auto module = dyn_cast<HWModuleLike>(op)) {
408 if (!module.getBodyBlock())
411 op->setAttr(
"op-liveness",
412 getLiveness(isBlockExecutable(module.getBodyBlock())));
413 op->setAttr(
"val-liveness",
414 getValLiveness(module.getBodyBlock()->getArguments()));
418 if (
auto outputOp = dyn_cast<OutputOp>(op)) {
419 op->setAttr(
"val-liveness", getValLiveness(outputOp->getOperands()));
423 if (op->getNumResults()) {
424 if (
auto instance = dyn_cast<HWInstanceLike>(op))
425 op->setAttr(
"op-liveness", getLiveness(isKnownAlive(instance)));
427 op->setAttr(
"op-liveness", getLiveness(!isAssumedDead(op)));
428 op->setAttr(
"val-liveness", getValLiveness(op->getResults()));
441 rewriteModuleSignature(module);
445 rewriteModuleBody(module);
448 llvm::make_early_inc_range(getOperation().getOps<
HWModuleOp>())) {
449 eraseEmptyModule(module);
453 executableBlocks.clear();
454 liveElements.clear();
457void HWIMDeadCodeElim::rewriteModuleSignature(
HWModuleOp module) {
460 LLVM_DEBUG(llvm::dbgs() <<
"Prune ports of module: " << module.getName()
463 auto replaceInstanceResultWithConst =
464 [&](ImplicitLocOpBuilder &builder,
unsigned index, InstanceOp instance) {
465 auto result = instance.getResult(index);
466 assert(isAssumedDead(result));
469 mlir::UnrealizedConversionCastOp::create(
470 builder, ArrayRef<Type>{result.getType()}, ArrayRef<Value>{})
472 result.replaceAllUsesWith(dummy);
477 for (
auto *use :
llvm::make_early_inc_range(instanceGraphNode->uses())) {
478 auto maybeInst = use->getInstance();
482 auto instance = cast<InstanceOp>(maybeInst);
484 if (!isKnownAlive(instance)) {
486 ImplicitLocOpBuilder builder(instance.getLoc(), instance);
487 for (
auto index :
llvm::
seq(0u, instance.getNumResults()))
488 replaceInstanceResultWithConst(builder, index, instance);
496 if (module.isPublic())
500 auto *outputOp =
module.getBodyBlock()->getTerminator();
502 auto numInPorts =
module.getBody().getNumArguments();
503 auto numOutPorts = outputOp->getNumOperands();
505 llvm::BitVector deadInPortBitVec(numInPorts);
506 llvm::BitVector deadOutPortBitVec(numOutPorts);
508 ImplicitLocOpBuilder builder(module.getLoc(), module.getContext());
509 builder.setInsertionPointToStart(module.getBodyBlock());
511 for (
auto index :
llvm::
seq(0u, numInPorts)) {
512 auto inPort =
module.getBodyBlock()->getArgument(index);
513 if (isKnownAlive(inPort))
517 mlir::UnrealizedConversionCastOp::create(
518 builder, ArrayRef<Type>{inPort.getType()}, ArrayRef<Value>{})
520 inPort.replaceAllUsesWith(placeholder);
521 deadInPortBitVec.set(index);
525 unsigned erasures = 0;
526 for (
auto index :
llvm::
seq(0u, numOutPorts)) {
527 auto argument = outputOp->getOperand(index - erasures);
529 if (isKnownAlive(argument))
532 outputOp->eraseOperand(index - erasures);
535 deadOutPortBitVec.set(index);
539 if (deadInPortBitVec.none() && deadOutPortBitVec.none())
545 liveElements.erase(arg);
547 for (
auto op : outputOp->getOperands())
548 liveElements.erase(op);
551 module.erasePorts(SmallVector<unsigned>(deadInPortBitVec.set_bits()),
552 SmallVector<unsigned>(deadOutPortBitVec.set_bits()));
553 module.getBodyBlock()->eraseArguments(deadInPortBitVec);
557 liveElements.insert(arg);
559 for (
auto op : outputOp->getOperands())
560 liveElements.insert(op);
563 for (
auto *use :
llvm::make_early_inc_range(instanceGraphNode->uses())) {
564 auto instance = cast<InstanceOp>(*use->getInstance());
566 ImplicitLocOpBuilder builder(instance.getLoc(), instance);
568 for (
auto index : deadOutPortBitVec.set_bits())
569 replaceInstanceResultWithConst(builder, index, instance);
573 for (
auto oldResult : instance.getResults())
574 liveElements.erase(oldResult);
577 instance, deadInPortBitVec, deadOutPortBitVec);
579 for (
auto newResult : newInstance.getResults())
580 liveElements.insert(newResult);
582 instanceGraph->replaceInstance(instance, newInstance);
586 numRemovedPorts += deadInPortBitVec.count() + deadOutPortBitVec.count();
589void HWIMDeadCodeElim::rewriteModuleBody(
HWModuleOp module) {
592 module.walk<mlir::WalkOrder::PostOrder, mlir::ReverseIterator>(
595 LLVM_DEBUG(llvm::dbgs() << "Visit: " << *op << "\n");
601 if (!isa<InstanceOp>(op) && mlir::isOpTriviallyDead(op) &&
609void HWIMDeadCodeElim::eraseEmptyModule(
HWModuleOp module) {
612 if (!isBlockExecutable(module.getBodyBlock()))
616 if (!module.getBodyBlock()->without_terminator().empty())
620 if (module.getBodyBlock()->getTerminator()->getNumOperands() != 0)
624 if (module.isPublic()) {
625 mlir::emitWarning(module.getLoc())
626 <<
"module `" <<
module.getName()
627 << "` is empty but cannot be removed because the module is public";
632 LLVM_DEBUG(llvm::dbgs() <<
"Erase " << module.getName() <<
"\n");
634 instanceGraph->lookup(module.getModuleNameAttr());
636 SmallVector<Location> instancesWithSymbols;
637 for (
auto *use :
llvm::make_early_inc_range(instanceGraphNode->uses())) {
638 auto maybeInst = use->getInstance();
642 auto instance = cast<InstanceOp>(maybeInst);
643 if (instance.getInnerSym()) {
644 instancesWithSymbols.push_back(instance.getLoc());
652 if (!instancesWithSymbols.empty()) {
653 auto diag =
module.emitWarning()
654 << "module `" << module.getName()
655 << "` is empty but cannot be removed because an instance is "
656 "referenced by name";
657 diag.attachNote(FusedLoc::get(&getContext(), instancesWithSymbols))
658 <<
"these are instances with symbols";
663 if (liveElements.contains(module))
666 instanceGraph->erase(instanceGraphNode);
assert(baseType &&"element must be base type")
static InstanceOp cloneWithErasePortsAndReplaceUses(InstanceOp &instance, const llvm::BitVector &inErasures, const llvm::BitVector &outErasures)
Static method for cloning instance of type InstanceOp with in- and output ports erased based on inEra...
static bool hasUnknownSideEffect(Operation *op)
static InstanceOp cloneWithErasedPorts(InstanceOp &instance, const llvm::BitVector &inErasures, const llvm::BitVector &outErasures)
Clone instance, but with ports deleted according to the inErasures and outErasures BitVectors.
static Block * getBodyBlock(FModuleLike mod)
HW-specific instance graph with a virtual entry node linking to all publicly visible modules.
This is a Node in the InstanceGraph.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.