CIRCT 23.0.0git
Loading...
Searching...
No Matches
HWIMDeadCodeElim.cpp
Go to the documentation of this file.
1//===- IMDeadCodeElim.cpp - Intermodule Dead Code Elimination ---*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
13#include "circt/Support/Utils.h"
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"
20#include <variant>
21
22#define DEBUG_TYPE "hw-imdeadcodeelim"
23
24namespace circt {
25namespace hw {
26#define GEN_PASS_DEF_IMDEADCODEELIM
27#include "circt/Dialect/HW/Passes.h.inc"
28} // namespace hw
29} // namespace circt
30
31using namespace circt;
32using namespace hw;
33
34namespace {
35struct HWIMDeadCodeElim
36 : circt::hw::impl::IMDeadCodeElimBase<HWIMDeadCodeElim> {
37 using Base::Base;
38
39 void runOnOperation() override;
40
41private:
42 using ElementType = std::variant<Value, HWModuleLike, HWInstanceLike>;
43
44 void markAlive(ElementType element) {
45 if (!liveElements.insert(element).second)
46 return;
47 worklist.push_back(element);
48 }
49
50 bool isKnownAlive(HWInstanceLike instanceLike) const {
51 return liveElements.count(instanceLike);
52 }
53 bool isKnownAlive(Value value) const {
54 assert(value && "null should not be used");
55 return liveElements.count(value);
56 }
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); });
61 }
62 bool isBlockExecutable(Block *block) { return executableBlocks.count(block); }
63
64 SmallVector<ElementType, 64> worklist;
65 llvm::DenseSet<ElementType> liveElements;
66
67 InstanceGraph *instanceGraph;
68 DenseSet<Block *> executableBlocks;
69
70 void markBlockExecutable(Block *block);
71
72 void visitInstanceLike(HWInstanceLike instanceLike);
73 void visitValue(Value value);
74
75 void rewriteModuleSignature(HWModuleOp module);
76 void eraseEmptyModule(HWModuleOp module);
77 void rewriteModuleBody(HWModuleOp module);
78
79 void markUnknownSideEffectOp(Operation *op);
80 void markInstanceLike(HWInstanceLike instanceLike);
81
82 void markBlockUndeletable(Operation *op) {
83
84 markAlive(op->getParentOfType<HWModuleLike>());
85 }
86};
87} // namespace
88
89static bool hasUnknownSideEffect(Operation *op) {
90 if (!(mlir::isMemoryEffectFree(op) ||
91 mlir::hasSingleEffect<mlir::MemoryEffects::Allocate>(op) ||
92 mlir::hasSingleEffect<mlir::MemoryEffects::Read>(op))) {
93 return true;
94 }
95 if (auto innerSymOp = dyn_cast<InnerSymbolOpInterface>(op)) {
96 if (innerSymOp.getInnerName().has_value())
97 return true;
98 }
99
100 return false;
101}
102
103/// Clone \p instance, but with ports deleted according to
104/// the \p inErasures and \p outErasures BitVectors.
105static InstanceOp cloneWithErasedPorts(InstanceOp &instance,
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()");
112
113 // Restrict outputs
114 SmallVector<Type> newResultTypes = removeElementsAtIndices<Type>(
115 SmallVector<Type>(instance->result_type_begin(),
116 instance->result_type_end()),
117 outErasures);
118 auto newResultNames = removeElementsAtIndices<mlir::Attribute>(
119 SmallVector<mlir::Attribute>(instance.getResultNames().begin(),
120 instance.getResultNames().end()),
121 outErasures);
122
123 // Restrict inputs
124 auto newOperands = removeElementsAtIndices<mlir::Value>(
125 SmallVector<mlir::Value>(instance->getOperands().begin(),
126 instance->getOperands().end()),
127 inErasures);
128 auto newOperandNames = removeElementsAtIndices<mlir::Attribute>(
129 SmallVector<mlir::Attribute>(instance.getArgNames().begin(),
130 instance.getArgNames().end()),
131 inErasures);
132
133 ImplicitLocOpBuilder builder(instance->getLoc(), instance);
134
135 auto newOpNamesArrayAttr = builder.getArrayAttr(newOperandNames);
136 auto newResultNamesArrayAttr = builder.getArrayAttr(newResultNames);
137
138 auto newInstance = InstanceOp::create(
139 builder, instance->getLoc(), newResultTypes,
140 instance.getInstanceNameAttr(), instance.getModuleName(), newOperands,
141 newOpNamesArrayAttr, newResultNamesArrayAttr, instance.getParameters(),
142 instance.getInnerSymAttr());
143
144 return newInstance;
145}
146
147/// Static method for cloning \p instance of type InstanceOp
148/// with in- and output ports erased based on \p inErasures
149/// and \p outErasures respectively. The users of the results
150/// of the instance are also updated.
151static InstanceOp
153 const llvm::BitVector &inErasures,
154 const llvm::BitVector &outErasures) {
155
156 auto newInstance = cloneWithErasedPorts(instance, inErasures, outErasures);
157
158 // Replace all input operands
159 size_t erased = 0;
160 for (size_t index = 0, e = instance->getNumResults(); index < e; ++index) {
161 auto r1 = instance->getResult(index);
162 if (outErasures[index]) {
163 ++erased;
164 continue;
165 }
166 auto r2 = newInstance->getResult(index - erased);
167 r1.replaceAllUsesWith(r2);
168 }
169
170 return newInstance;
171}
172
173void HWIMDeadCodeElim::markUnknownSideEffectOp(Operation *op) {
174 // For operations with side effects, pessimistically mark results and
175 // operands as alive.
176 for (auto result : op->getResults())
177 markAlive(result);
178 for (auto operand : op->getOperands())
179 markAlive(operand);
180 markBlockUndeletable(op);
181}
182
183void HWIMDeadCodeElim::markInstanceLike(HWInstanceLike instanceLike) {
184
185 for (StringAttr moduleName :
186 instanceLike.getReferencedModuleNamesAttr().getAsRange<StringAttr>()) {
187 auto *node = instanceGraph->lookup(moduleName);
188
189 if (!node)
190 continue;
191
192 auto op = node->getModule();
193
194 // If this is an extmodule, just remember that any inputs and inouts are
195 // alive.
196 // Inputs are exactly what are passed into the module.
197 if (!isa<HWModuleOp>(op.getOperation())) {
198 for (auto operand : instanceLike->getOperands())
199 markAlive(operand);
200
201 markAlive(instanceLike);
202 }
203
204 if (auto moduleLike = dyn_cast<HWModuleLike>(op.getOperation()))
205 markBlockExecutable(moduleLike.getBodyBlock());
206 }
207}
208
209void HWIMDeadCodeElim::markBlockExecutable(Block *block) {
210 if (!block)
211 return;
212
213 if (!executableBlocks.insert(block).second)
214 return; // Already executable.
215
216 auto moduleLike = dyn_cast<HWModuleLike>(block->getParentOp());
217 // The only case when we do not mark the module alive automatically,
218 // is if it is a private HW module
219 auto module = dyn_cast<HWModuleOp>(moduleLike.getOperation());
220 if (!module)
221 markAlive(moduleLike);
222 else if (module.isPublic())
223 markAlive(module);
224
225 for (auto &op : *block) {
226 if (auto instance = dyn_cast<HWInstanceLike>(op))
227 markInstanceLike(instance);
228 else if (hasUnknownSideEffect(&op)) {
229 markUnknownSideEffectOp(&op);
230 // Recursively mark any blocks contained within these operations as
231 // executable.
232 for (auto &region : op.getRegions())
233 for (auto &block : region.getBlocks())
234 markBlockExecutable(&block);
235 }
236 }
237}
238/// Propagate liveness backwards through a HWInstanceLike op.
239/// For all possible ModuleLike's that is being instantiated,
240/// - If it is a HWModuleOp, mark outputs selectively based on
241/// their liveness in the module,
242/// - If it is a ModuleLike, but not a HWModuleOp, mark all inputs
243/// as alive.
244/// TODO: this might not be the best idea, since we can still trace
245/// out if something is actually used in a non HWModuleOp.
246void HWIMDeadCodeElim::visitInstanceLike(HWInstanceLike instanceLike) {
247
248 for (mlir::StringAttr moduleName :
249 instanceLike.getReferencedModuleNamesAttr().getAsRange<StringAttr>()) {
250 auto *moduleNode = instanceGraph->lookup(moduleName);
251
252 if (!moduleNode)
253 continue;
254
255 // We apply exact argument liveness iff the module is HW native.
256 if (auto module =
257 dyn_cast<HWModuleOp>(moduleNode->getModule().getOperation())) {
258
259 // All block args are inputs
260 for (auto blockArg : module.getBodyBlock()->getArguments()) {
261 auto portIndex = blockArg.getArgNumber();
262
263 if (isKnownAlive(blockArg))
264 markAlive(instanceLike->getOperand(portIndex));
265 }
266
267 continue;
268 }
269
270 // Otherwise we mark all outputs as alive
271 if (auto extModule =
272 dyn_cast<HWModuleLike>(moduleNode->getModule().getOperation()))
273 markAlive(extModule);
274 }
275}
276
277/// Propagate liveness through \p value.
278/// - If value is a block arg, it means that it had been found
279/// live through propagating within a module, so we mark corresponding
280/// instance inputs as alive.
281/// - If it is the result of an instance op, we mark the corresponding
282/// value in the body of the module as alive.
283/// - Otherwise, we mark all operands of the operation defining `value`
284/// as alive.
285void HWIMDeadCodeElim::visitValue(Value value) {
286 assert(isKnownAlive(value) && "only alive values reach here");
287
288 // Requiring an input port propagates the liveness to each instance.
289 // All arg block elements are inputs
290 if (auto blockArg = dyn_cast<BlockArgument>(value)) {
291 if (auto module =
292 dyn_cast<HWModuleLike>(blockArg.getParentBlock()->getParentOp())) {
293
294 for (auto *instRec : instanceGraph->lookup(module)->uses()) {
295 if (!instRec->getInstance())
296 continue;
297
298 if (auto instance = dyn_cast<HWInstanceLike>(
299 instRec->getInstance().getOperation())) {
300 if (liveElements.contains(instance))
301 markAlive(instance->getOperand(blockArg.getArgNumber()));
302 }
303 }
304 }
305 }
306
307 // Marking an instance port as alive propagates to the corresponding port of
308 // the module.
309 if (auto instanceLike = value.getDefiningOp<HWInstanceLike>()) {
310 auto instanceResult = cast<mlir::OpResult>(value);
311 // Update the src, when it's an instance op.
312
313 // For each module that the instance could refer to,
314 // mark
315 for (mlir::StringAttr moduleName :
316 instanceLike.getReferencedModuleNamesAttr().getAsRange<StringAttr>()) {
317 auto *node = instanceGraph->lookupOrNull(moduleName);
318
319 if (!node)
320 continue;
321
322 Operation *moduleOp = node->getModule().getOperation();
323 auto moduleLike = dyn_cast<HWModuleLike>(moduleOp);
324
325 if (!moduleLike)
326 continue;
327
328 if (!moduleLike.getBodyBlock())
329 continue;
330 auto *moduleOutputOp = moduleLike.getBodyBlock()->getTerminator();
331 auto modulePortVal =
332 moduleOutputOp->getOperand(instanceResult.getResultNumber());
333 markAlive(modulePortVal);
334 markAlive(instanceLike);
335 }
336
337 return;
338 }
339
340 // If the value is defined by an operation, mark its operands alive and any
341 // nested blocks executable.
342 if (auto *op = value.getDefiningOp()) {
343 for (auto operand : op->getOperands())
344 markAlive(operand);
345 for (auto &region : op->getRegions())
346 for (auto &block : region)
347 markBlockExecutable(&block);
348 }
349}
350
351void HWIMDeadCodeElim::runOnOperation() {
352
353 instanceGraph = &getAnalysis<hw::InstanceGraph>();
354
355 for (auto moduleLike : getOperation().getOps<hw::HWModuleLike>()) {
356 // Any ModuleLike that is not a private module will be marked
357 // executable, and all its output ports alive.
358 if (auto module = dyn_cast<HWModuleOp>(moduleLike.getOperation())) {
359 if (!module.isPublic())
360 continue;
361 }
362
363 if (!moduleLike.getBodyBlock())
364 continue;
365
366 markBlockExecutable(moduleLike.getBodyBlock());
367
368 // Do not mark inputs as alive, since they are only
369 // internally relevant.
370
371 // Mark all output values (i.e. SSA vals passed to hw.output) as alive
372 if (moduleLike.getBodyBlock()->empty())
373 continue;
374 auto *moduleOutputOp = moduleLike.getBodyBlock()->getTerminator();
375 if (!dyn_cast<OutputOp>(moduleOutputOp))
376 continue;
377 for (auto port : moduleOutputOp->getOperands())
378 markAlive(port);
379 }
380
381 // If an element changed liveness then propagate liveness through it.
382 while (!worklist.empty()) {
383 auto v = worklist.pop_back_val();
384 if (auto *value = std::get_if<Value>(&v)) {
385 visitValue(*value);
386 } else if (auto *instance = std::get_if<HWInstanceLike>(&v)) {
387 visitInstanceLike(*instance);
388 } else if (std::get_if<HWModuleLike>(&v)) {
389 continue;
390 }
391 }
392
393 if (printLiveness) {
394 auto liveAttr = StringAttr::get(&getContext(), "LIVE");
395 auto deadAttr = StringAttr::get(&getContext(), "DEAD");
396
397 auto getLiveness = [&](bool alive) { return alive ? liveAttr : deadAttr; };
398
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);
404 };
405
406 getOperation()->walk([&](Operation *op) {
407 if (auto module = dyn_cast<HWModuleLike>(op)) {
408 if (!module.getBodyBlock())
409 return;
410
411 op->setAttr("op-liveness",
412 getLiveness(isBlockExecutable(module.getBodyBlock())));
413 op->setAttr("val-liveness",
414 getValLiveness(module.getBodyBlock()->getArguments()));
415 return;
416 }
417
418 if (auto outputOp = dyn_cast<OutputOp>(op)) {
419 op->setAttr("val-liveness", getValLiveness(outputOp->getOperands()));
420 return;
421 }
422
423 if (op->getNumResults()) {
424 if (auto instance = dyn_cast<HWInstanceLike>(op))
425 op->setAttr("op-liveness", getLiveness(isKnownAlive(instance)));
426 else
427 op->setAttr("op-liveness", getLiveness(!isAssumedDead(op)));
428 op->setAttr("val-liveness", getValLiveness(op->getResults()));
429 }
430 });
431
432 return;
433 }
434
435 // Rewrite module signatures. Non-executable modules are still rewritten
436 // (dead ports stripped, dead instances removed) but are never erased, to
437 // avoid invalidating symbol references (e.g. sv.verbatim "{{0}}" {symbols =
438 // [@mod]}).
439 for (auto module :
440 llvm::make_early_inc_range(getOperation().getOps<HWModuleOp>()))
441 rewriteModuleSignature(module);
442
443 for (auto module :
444 llvm::make_early_inc_range(getOperation().getOps<HWModuleOp>()))
445 rewriteModuleBody(module);
446
447 for (auto module :
448 llvm::make_early_inc_range(getOperation().getOps<HWModuleOp>())) {
449 eraseEmptyModule(module);
450 }
451
452 // Clean up data structures.
453 executableBlocks.clear();
454 liveElements.clear();
455}
456
457void HWIMDeadCodeElim::rewriteModuleSignature(HWModuleOp module) {
458
459 igraph::InstanceGraphNode *instanceGraphNode = instanceGraph->lookup(module);
460 LLVM_DEBUG(llvm::dbgs() << "Prune ports of module: " << module.getName()
461 << "\n");
462
463 auto replaceInstanceResultWithConst =
464 [&](ImplicitLocOpBuilder &builder, unsigned index, InstanceOp instance) {
465 auto result = instance.getResult(index);
466 assert(isAssumedDead(result));
467
468 auto dummy =
469 mlir::UnrealizedConversionCastOp::create(
470 builder, ArrayRef<Type>{result.getType()}, ArrayRef<Value>{})
471 ->getResult(0);
472 result.replaceAllUsesWith(dummy);
473 return;
474 };
475
476 // First, delete dead instances.
477 for (auto *use : llvm::make_early_inc_range(instanceGraphNode->uses())) {
478 auto maybeInst = use->getInstance();
479 if (!maybeInst)
480 continue;
481
482 auto instance = cast<InstanceOp>(maybeInst);
483
484 if (!isKnownAlive(instance)) {
485 // Replace old instance results with dummy wires.
486 ImplicitLocOpBuilder builder(instance.getLoc(), instance);
487 for (auto index : llvm::seq(0u, instance.getNumResults()))
488 replaceInstanceResultWithConst(builder, index, instance);
489 // Make sure that we update the instance graph.
490 use->erase();
491 instance.erase();
492 }
493 }
494
495 // Ports of public modules cannot be modified.
496 if (module.isPublic())
497 return;
498
499 // Otherwise prepare data structures for tracking dead ports.
500 auto *outputOp = module.getBodyBlock()->getTerminator();
501
502 auto numInPorts = module.getBody().getNumArguments();
503 auto numOutPorts = outputOp->getNumOperands();
504
505 llvm::BitVector deadInPortBitVec(numInPorts);
506 llvm::BitVector deadOutPortBitVec(numOutPorts);
507
508 ImplicitLocOpBuilder builder(module.getLoc(), module.getContext());
509 builder.setInsertionPointToStart(module.getBodyBlock());
510
511 for (auto index : llvm::seq(0u, numInPorts)) {
512 auto inPort = module.getBodyBlock()->getArgument(index);
513 if (isKnownAlive(inPort))
514 continue;
515
516 auto placeholder =
517 mlir::UnrealizedConversionCastOp::create(
518 builder, ArrayRef<Type>{inPort.getType()}, ArrayRef<Value>{})
519 ->getResult(0);
520 inPort.replaceAllUsesWith(placeholder);
521 deadInPortBitVec.set(index);
522 }
523
524 // Find all unused results
525 unsigned erasures = 0;
526 for (auto index : llvm::seq(0u, numOutPorts)) {
527 auto argument = outputOp->getOperand(index - erasures);
528
529 if (isKnownAlive(argument))
530 continue;
531
532 outputOp->eraseOperand(index - erasures);
533 ++erasures;
534
535 deadOutPortBitVec.set(index);
536 }
537
538 // If there is nothing to remove, abort.
539 if (deadInPortBitVec.none() && deadOutPortBitVec.none())
540 return;
541
542 // Erase arguments of the old module from liveSet to prevent from creating
543 // dangling pointers.
544 for (auto arg : module.getBodyBlock()->getArguments())
545 liveElements.erase(arg);
546
547 for (auto op : outputOp->getOperands())
548 liveElements.erase(op);
549
550 // Delete ports from the module.
551 module.erasePorts(SmallVector<unsigned>(deadInPortBitVec.set_bits()),
552 SmallVector<unsigned>(deadOutPortBitVec.set_bits()));
553 module.getBodyBlock()->eraseArguments(deadInPortBitVec);
554
555 // Add arguments of the new module to liveSet.
556 for (auto arg : module.getBodyBlock()->getArguments())
557 liveElements.insert(arg);
558
559 for (auto op : outputOp->getOperands())
560 liveElements.insert(op);
561
562 // Rewrite all instantiation of the module.
563 for (auto *use : llvm::make_early_inc_range(instanceGraphNode->uses())) {
564 auto instance = cast<InstanceOp>(*use->getInstance());
565
566 ImplicitLocOpBuilder builder(instance.getLoc(), instance);
567 // Replace old instance results with dummy constants.
568 for (auto index : deadOutPortBitVec.set_bits())
569 replaceInstanceResultWithConst(builder, index, instance);
570
571 // Since we will rewrite instance op, it is necessary to remove old
572 // instance results from liveSet.
573 for (auto oldResult : instance.getResults())
574 liveElements.erase(oldResult);
575
576 auto newInstance = cloneWithErasePortsAndReplaceUses(
577 instance, deadInPortBitVec, deadOutPortBitVec);
578
579 for (auto newResult : newInstance.getResults())
580 liveElements.insert(newResult);
581
582 instanceGraph->replaceInstance(instance, newInstance);
583 instance->erase();
584 }
585
586 numRemovedPorts += deadInPortBitVec.count() + deadOutPortBitVec.count();
587}
588
589void HWIMDeadCodeElim::rewriteModuleBody(HWModuleOp module) {
590
591 // Walk the IR bottom-up when deleting operations.
592 module.walk<mlir::WalkOrder::PostOrder, mlir::ReverseIterator>(
593 [&](Operation *op) {
594 // Connects to values that we found to be dead can be dropped.
595 LLVM_DEBUG(llvm::dbgs() << "Visit: " << *op << "\n");
596
597 // Remove non-sideeffect op using `isOpTriviallyDead`.
598 // Skip instances - they're handled by
599 // rewriteModuleSignature/eraseEmptyModule and also need erasure from
600 // instanceGraph
601 if (!isa<InstanceOp>(op) && mlir::isOpTriviallyDead(op) &&
602 isAssumedDead(op)) {
603 op->erase();
604 ++numErasedOps;
605 }
606 });
607}
608
609void HWIMDeadCodeElim::eraseEmptyModule(HWModuleOp module) {
610 // Non-executable modules are preserved as empty shells to avoid invalidating
611 // symbol references that may point to them by name.
612 if (!isBlockExecutable(module.getBodyBlock()))
613 return;
614
615 // If the module is not empty, just skip.
616 if (!module.getBodyBlock()->without_terminator().empty())
617 return;
618
619 // It can also be the case that the only `hw.output` is nontrivial, also skip.
620 if (module.getBodyBlock()->getTerminator()->getNumOperands() != 0)
621 return;
622
623 // We cannot delete public modules so generate a warning.
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";
628 return;
629 }
630
631 // Ok, the module is empty. Delete instances unless they have symbols.
632 LLVM_DEBUG(llvm::dbgs() << "Erase " << module.getName() << "\n");
633 igraph::InstanceGraphNode *instanceGraphNode =
634 instanceGraph->lookup(module.getModuleNameAttr());
635
636 SmallVector<Location> instancesWithSymbols;
637 for (auto *use : llvm::make_early_inc_range(instanceGraphNode->uses())) {
638 auto maybeInst = use->getInstance();
639 if (!maybeInst)
640 continue;
641
642 auto instance = cast<InstanceOp>(maybeInst);
643 if (instance.getInnerSym()) {
644 instancesWithSymbols.push_back(instance.getLoc());
645 continue;
646 }
647 use->erase();
648 instance.erase();
649 }
650
651 // If there is an instance with a symbol, we don't delete the module itself.
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";
659 return;
660 }
661
662 // We cannot delete alive modules.
663 if (liveElements.contains(module))
664 return;
665
666 instanceGraph->erase(instanceGraphNode);
667 ++numErasedModules;
668}
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.
Definition hw.py:1
Definition seq.py:1