20#include "mlir/IR/IRMapping.h"
21#include "mlir/IR/Matchers.h"
22#include "mlir/IR/PatternMatch.h"
23#include "mlir/Pass/Pass.h"
24#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
25#include "llvm/Support/Debug.h"
27#define DEBUG_TYPE "arc-canonicalizer"
31#define GEN_PASS_DEF_ARCCANONICALIZER
32#include "circt/Dialect/Arc/ArcPasses.h.inc"
50 ArrayRef<Operation *> getUsers(Operation *symbol)
const {
51 auto it = userMap.find(symbol);
52 return it != userMap.
end() ? it->second.getArrayRef() : std::nullopt;
56 bool useEmpty(Operation *symbol) {
57 return !userMap.count(symbol) || userMap[symbol].empty();
60 void addUser(Operation *def, Operation *user) {
61 assert(isa<mlir::SymbolOpInterface>(def));
62 if (!symbolCache.contains(cast<mlir::SymbolOpInterface>(def).getNameAttr()))
64 {cast<mlir::SymbolOpInterface>(def).getNameAttr(), def});
65 userMap[def].insert(user);
68 void removeUser(Operation *def, Operation *user) {
69 assert(isa<mlir::SymbolOpInterface>(def));
70 if (symbolCache.contains(cast<mlir::SymbolOpInterface>(def).getNameAttr()))
71 userMap[def].remove(user);
72 if (userMap[def].
empty())
76 void removeDefinitionAndAllUsers(Operation *def) {
77 assert(isa<mlir::SymbolOpInterface>(def));
78 symbolCache.erase(cast<mlir::SymbolOpInterface>(def).getNameAttr());
82 void collectAllSymbolUses(Operation *symbolTableOp,
83 SymbolTableCollection &symbolTable) {
87 SmallVector<Operation *> symbols;
88 auto walkFn = [&](Operation *symbolTableOp,
bool allUsesVisible) {
89 for (Operation &nestedOp : symbolTableOp->getRegion(0).getOps()) {
90 auto symbolUses = SymbolTable::getSymbolUses(&nestedOp);
91 assert(symbolUses &&
"expected uses to be valid");
93 for (
const SymbolTable::SymbolUse &use : *symbolUses) {
95 (void)symbolTable.lookupSymbolIn(symbolTableOp, use.getSymbolRef(),
97 for (Operation *symbolOp : symbols)
98 userMap[symbolOp].insert(use.getUser());
104 SymbolTable::walkSymbolTables(symbolTableOp,
false,
109 DenseMap<Operation *, SetVector<Operation *>> userMap;
115class ArcListener :
public mlir::RewriterBase::Listener {
117 explicit ArcListener(SymbolHandler *handler) : Listener(), handler(handler) {}
119 void notifyOperationReplaced(Operation *op, Operation *replacement)
override {
122 auto symOp = dyn_cast<mlir::SymbolOpInterface>(op);
123 auto symReplacement = dyn_cast<mlir::SymbolOpInterface>(replacement);
124 if (symOp && symReplacement &&
125 symOp.getNameAttr() == symReplacement.getNameAttr())
134 void notifyOperationReplaced(Operation *op, ValueRange replacement)
override {
138 void notifyOperationErased(Operation *op)
override { remove(op); }
140 void notifyOperationInserted(Operation *op,
141 mlir::IRRewriter::InsertPoint)
override {
148 FailureOr<Operation *> maybeGetDefinition(Operation *op) {
149 if (
auto callOp = dyn_cast<mlir::CallOpInterface>(op)) {
151 dyn_cast<mlir::SymbolRefAttr>(callOp.getCallableForCallee());
154 if (
auto *def = handler->getDefinition(symAttr.getLeafReference()))
160 void remove(Operation *op) {
161 auto maybeDef = maybeGetDefinition(op);
162 if (!failed(maybeDef))
163 handler->removeUser(*maybeDef, op);
165 if (isa<mlir::SymbolOpInterface>(op))
166 handler->removeDefinitionAndAllUsers(op);
169 void add(Operation *op) {
170 auto maybeDef = maybeGetDefinition(op);
171 if (!failed(maybeDef))
172 handler->addUser(*maybeDef, op);
174 if (
auto defOp = dyn_cast<mlir::SymbolOpInterface>(op))
175 handler->addDefinition(defOp.getNameAttr(), op);
178 SymbolHandler *handler;
181struct PatternStatistics {
182 unsigned removeUnusedArcArgumentsPatternNumArgsRemoved = 0;
196template <
typename SourceOp>
199 SymOpRewritePattern(MLIRContext *ctxt, SymbolHandler &symbolCache,
200 Namespace &names, PatternStatistics &stats,
201 mlir::PatternBenefit benefit = 1,
202 ArrayRef<StringRef> generatedNames = {})
204 symbolCache(symbolCache), statistics(stats) {}
208 SymbolHandler &symbolCache;
209 PatternStatistics &statistics;
212class MemWritePortEnableAndMaskCanonicalizer
213 :
public SymOpRewritePattern<MemoryWritePortOp> {
215 MemWritePortEnableAndMaskCanonicalizer(
216 MLIRContext *ctxt, SymbolHandler &symbolCache,
Namespace &names,
217 PatternStatistics &stats, DenseMap<StringAttr, StringAttr> &arcMapping)
218 : SymOpRewritePattern<MemoryWritePortOp>(
ctxt, symbolCache, names, stats),
219 arcMapping(arcMapping) {}
220 LogicalResult matchAndRewrite(MemoryWritePortOp op,
221 PatternRewriter &rewriter)
const final;
224 DenseMap<StringAttr, StringAttr> &arcMapping;
227struct CallPassthroughArc :
public SymOpRewritePattern<CallOp> {
228 using SymOpRewritePattern::SymOpRewritePattern;
229 LogicalResult matchAndRewrite(CallOp op,
230 PatternRewriter &rewriter)
const final;
233struct RemoveUnusedArcs :
public SymOpRewritePattern<DefineOp> {
234 using SymOpRewritePattern::SymOpRewritePattern;
235 LogicalResult matchAndRewrite(DefineOp op,
236 PatternRewriter &rewriter)
const final;
240 using OpRewritePattern::OpRewritePattern;
241 LogicalResult matchAndRewrite(comb::ICmpOp op,
242 PatternRewriter &rewriter)
const final;
246 using OpRewritePattern::OpRewritePattern;
248 PatternRewriter &rewriter)
const final;
251struct RemoveUnusedArcArgumentsPattern :
public SymOpRewritePattern<DefineOp> {
252 using SymOpRewritePattern::SymOpRewritePattern;
253 LogicalResult matchAndRewrite(DefineOp op,
254 PatternRewriter &rewriter)
const final;
257struct SinkArcInputsPattern :
public SymOpRewritePattern<DefineOp> {
258 using SymOpRewritePattern::SymOpRewritePattern;
259 LogicalResult matchAndRewrite(DefineOp op,
260 PatternRewriter &rewriter)
const final;
264 using OpRewritePattern::OpRewritePattern;
265 LogicalResult matchAndRewrite(VectorizeOp op,
266 PatternRewriter &rewriter)
const final;
270 using OpRewritePattern::OpRewritePattern;
271 LogicalResult matchAndRewrite(VectorizeOp op,
272 PatternRewriter &rewriter)
const final;
282 SymbolHandler &symbolCache,
283 PatternRewriter &rewriter) {
284 auto defOp = cast<DefineOp>(symbolCache.getDefinition(
285 llvm::cast<SymbolRefAttr>(callOp.getCallableForCallee())
286 .getLeafReference()));
287 if (defOp.isPassthrough()) {
288 symbolCache.removeUser(defOp, callOp);
289 rewriter.replaceOp(callOp, callOp.getArgOperands());
296 const SmallVector<Value> &newOperands) {
298 unsigned groupSize = vecOp.getResults().size();
299 unsigned numOfGroups = newOperands.size() / groupSize;
300 SmallVector<int32_t> newAttr(numOfGroups, groupSize);
301 vecOp.setInputOperandSegments(newAttr);
302 vecOp.getOperation()->setOperands(ValueRange(newOperands));
310LogicalResult MemWritePortEnableAndMaskCanonicalizer::matchAndRewrite(
311 MemoryWritePortOp op, PatternRewriter &rewriter)
const {
312 auto defOp = cast<DefineOp>(symbolCache.getDefinition(op.getArcAttr()));
315 if (op.getEnable() &&
317 defOp.getBodyBlock().getTerminator()->getOperand(op.getEnableIdx()),
318 mlir::m_ConstantInt(&enable))) {
319 if (enable.isZero()) {
320 symbolCache.removeUser(defOp, op);
321 rewriter.eraseOp(op);
322 if (symbolCache.useEmpty(defOp)) {
323 symbolCache.removeDefinitionAndAllUsers(defOp);
324 rewriter.eraseOp(defOp);
328 if (enable.isAllOnes()) {
329 if (arcMapping.count(defOp.getNameAttr())) {
330 auto arcWithoutEnable = arcMapping[defOp.getNameAttr()];
332 rewriter.modifyOpInPlace(op, [&]() {
334 op.setArc(arcWithoutEnable.getValue());
336 symbolCache.removeUser(defOp, op);
337 symbolCache.addUser(symbolCache.getDefinition(arcWithoutEnable), op);
341 auto newName = names.newName(defOp.getName());
342 auto users = SmallVector<Operation *>(symbolCache.getUsers(defOp));
343 symbolCache.removeDefinitionAndAllUsers(defOp);
346 rewriter.modifyOpInPlace(op, [&]() {
351 auto newResultTypes = op.getArcResultTypes();
354 rewriter.setInsertionPoint(defOp);
355 auto newDefOp = rewriter.cloneWithoutRegions(defOp);
356 auto *block = rewriter.createBlock(
357 &newDefOp.getBody(), newDefOp.getBody().end(),
358 newDefOp.getArgumentTypes(),
359 SmallVector<Location>(newDefOp.getNumArguments(), defOp.getLoc()));
360 auto callOp = rewriter.create<CallOp>(newDefOp.getLoc(), newResultTypes,
361 newName, block->getArguments());
362 SmallVector<Value> results(callOp->getResults());
364 newDefOp.getLoc(), rewriter.getI1Type(), 1);
365 results.insert(results.begin() + op.getEnableIdx(), constTrue);
366 rewriter.
create<OutputOp>(newDefOp.getLoc(), results);
369 auto *terminator = defOp.getBodyBlock().getTerminator();
370 rewriter.modifyOpInPlace(
371 terminator, [&]() { terminator->eraseOperand(op.getEnableIdx()); });
372 rewriter.modifyOpInPlace(defOp, [&]() {
373 defOp.setName(newName);
374 defOp.setFunctionType(
375 rewriter.getFunctionType(defOp.getArgumentTypes(), newResultTypes));
379 symbolCache.addDefinition(defOp.getNameAttr(), defOp);
380 symbolCache.addDefinition(newDefOp.getNameAttr(), newDefOp);
381 symbolCache.addUser(defOp, callOp);
382 for (
auto *user : users)
383 symbolCache.addUser(user == op ? defOp : newDefOp, user);
385 arcMapping[newDefOp.getNameAttr()] = defOp.getNameAttr();
393CallPassthroughArc::matchAndRewrite(CallOp op,
394 PatternRewriter &rewriter)
const {
399RemoveUnusedArcs::matchAndRewrite(DefineOp op,
400 PatternRewriter &rewriter)
const {
401 if (symbolCache.useEmpty(op)) {
402 op.getBody().walk([&](mlir::CallOpInterface user) {
403 if (
auto symbol = dyn_cast<SymbolRefAttr>(user.getCallableForCallee()))
404 if (
auto *defOp = symbolCache.getDefinition(symbol.getLeafReference()))
405 symbolCache.removeUser(defOp, user);
407 symbolCache.removeDefinitionAndAllUsers(op);
408 rewriter.eraseOp(op);
415ICMPCanonicalizer::matchAndRewrite(comb::ICmpOp op,
416 PatternRewriter &rewriter)
const {
417 auto getConstant = [&](
const APInt &constant) -> Value {
420 auto sameWidthIntegers = [](TypeRange types) -> std::optional<unsigned> {
421 if (llvm::all_equal(types) && !types.empty())
422 if (
auto intType = dyn_cast<IntegerType>(*types.begin()))
423 return intType.getWidth();
426 auto negate = [&](Value input) -> Value {
433 if (matchPattern(op.getRhs(), mlir::m_ConstantInt(&rhs))) {
434 if (
auto concatOp = op.getLhs().getDefiningOp<
comb::ConcatOp>()) {
435 if (
auto optionalWidth =
436 sameWidthIntegers(concatOp->getOperands().getTypes())) {
437 if ((op.getPredicate() == comb::ICmpPredicate::eq ||
438 op.getPredicate() == comb::ICmpPredicate::ne) &&
441 op.getLoc(), concatOp.getInputs(), op.getTwoState());
442 if (*optionalWidth == 1) {
443 if (op.getPredicate() == comb::ICmpPredicate::ne)
444 andOp = negate(andOp);
445 rewriter.replaceOp(op, andOp);
448 rewriter.replaceOpWithNewOp<comb::ICmpOp>(
449 op, op.getPredicate(), andOp,
450 getConstant(APInt(*optionalWidth, rhs.getZExtValue(),
456 if ((op.getPredicate() == comb::ICmpPredicate::ne ||
457 op.getPredicate() == comb::ICmpPredicate::eq) &&
460 op.getLoc(), concatOp.getInputs(), op.getTwoState());
461 if (*optionalWidth == 1) {
462 if (op.getPredicate() == comb::ICmpPredicate::eq)
464 rewriter.replaceOp(op, orOp);
467 rewriter.replaceOpWithNewOp<comb::ICmpOp>(
468 op, op.getPredicate(), orOp,
469 getConstant(APInt(*optionalWidth, rhs.getZExtValue(),
480LogicalResult RemoveUnusedArcArgumentsPattern::matchAndRewrite(
481 DefineOp op, PatternRewriter &rewriter)
const {
482 BitVector toDelete(op.getNumArguments());
483 for (
auto [i, arg] :
llvm::enumerate(op.getArguments()))
493 SmallVector<mlir::CallOpInterface> mutableUsers;
494 for (
auto *user : symbolCache.getUsers(op)) {
495 auto callOpMutable = dyn_cast<mlir::CallOpInterface>(user);
498 mutableUsers.push_back(callOpMutable);
502 for (
auto user : mutableUsers)
503 for (int i = toDelete.size() - 1; i >= 0; --i)
505 user.getArgOperandsMutable().erase(i);
507 op.eraseArguments(toDelete);
509 rewriter.getFunctionType(op.getArgumentTypes(), op.getResultTypes()));
511 statistics.removeUnusedArcArgumentsPatternNumArgsRemoved += toDelete.count();
516SinkArcInputsPattern::matchAndRewrite(DefineOp op,
517 PatternRewriter &rewriter)
const {
520 auto users = symbolCache.getUsers(op);
522 users, [](
auto *user) {
return !isa<mlir::CallOpInterface>(user); }))
526 SmallVector<Operation *> stateConsts(op.getNumArguments());
528 for (
auto *user : users) {
529 auto callOp = cast<mlir::CallOpInterface>(user);
530 for (
auto [constArg, input] :
531 llvm::zip(stateConsts, callOp.getArgOperands())) {
532 if (
auto *constOp = input.getDefiningOp();
533 constOp && constOp->template hasTrait<OpTrait::ConstantLike>()) {
539 constArg->getName() == input.getDefiningOp()->getName() &&
540 constArg->getAttrDictionary() ==
541 input.getDefiningOp()->getAttrDictionary())
550 rewriter.setInsertionPointToStart(&op.getBodyBlock());
551 llvm::BitVector toDelete(op.getBodyBlock().getNumArguments());
552 for (
auto [constArg, arg] :
llvm::zip(stateConsts, op.getArguments())) {
555 auto *inlinedConst = rewriter.clone(*constArg);
556 rewriter.replaceAllUsesWith(arg, inlinedConst->getResult(0));
557 toDelete.set(arg.getArgNumber());
559 op.getBodyBlock().eraseArguments(toDelete);
560 op.setType(rewriter.getFunctionType(op.getBodyBlock().getArgumentTypes(),
561 op.getResultTypes()));
564 for (
auto *user : users) {
565 auto callOp = cast<mlir::CallOpInterface>(user);
566 SmallPtrSet<Value, 4> maybeUnusedValues;
567 SmallVector<Value> newInputs;
568 for (
auto [index, value] :
llvm::enumerate(callOp.getArgOperands())) {
570 maybeUnusedValues.insert(value);
572 newInputs.push_back(value);
574 rewriter.modifyOpInPlace(
575 callOp, [&]() { callOp.getArgOperandsMutable().assign(newInputs); });
576 for (
auto value : maybeUnusedValues)
577 if (value.use_empty())
578 rewriter.eraseOp(value.getDefiningOp());
581 return success(toDelete.any());
586 PatternRewriter &rewriter)
const {
592 if (mlir::matchPattern(op.getResetValue(), mlir::m_ConstantInt(&constant)))
593 if (constant.isZero())
597 op->getLoc(), op.getReset(), op.getResetValue(), op.getInput());
598 rewriter.modifyOpInPlace(op, [&]() {
599 op.getInputMutable().set(newInput);
600 op.getResetMutable().clear();
601 op.getResetValueMutable().clear();
608MergeVectorizeOps::matchAndRewrite(VectorizeOp vecOp,
609 PatternRewriter &rewriter)
const {
610 auto ¤tBlock = vecOp.getBody().front();
611 IRMapping argMapping;
612 SmallVector<Value> newOperands;
613 SmallVector<VectorizeOp> vecOpsToRemove;
614 bool canBeMerged =
false;
616 unsigned paddedBy = 0;
618 for (
unsigned argIdx = 0, numArgs = vecOp.getInputs().size();
619 argIdx < numArgs; ++argIdx) {
620 auto inputVec = vecOp.getInputs()[argIdx];
624 auto otherVecOp = inputVec[0].getDefiningOp<VectorizeOp>();
625 if (!otherVecOp || otherVecOp == vecOp ||
626 !llvm::all_of(otherVecOp.getResults(),
627 [](
auto result) {
return result.hasOneUse(); }) ||
628 !llvm::all_of(inputVec, [&](
auto result) {
629 return result.template getDefiningOp<VectorizeOp>() == otherVecOp;
631 newOperands.insert(newOperands.end(), inputVec.begin(), inputVec.end());
638 DenseMap<Value, size_t> resultIdxMap;
639 for (
auto [resultIdx, result] :
llvm::enumerate(otherVecOp.getResults()))
640 resultIdxMap[result] = resultIdx;
642 SmallVector<Value> tempVec(inputVec.begin(), inputVec.end());
643 llvm::sort(tempVec, [&](Value a, Value b) {
644 return resultIdxMap[a] < resultIdxMap[b];
648 if (tempVec != SmallVector<Value>(otherVecOp.getResults().begin(),
649 otherVecOp.getResults().end())) {
650 newOperands.insert(newOperands.end(), inputVec.begin(), inputVec.end());
654 DenseMap<size_t, size_t> fromRealIdxToSortedIdx;
655 for (
auto [inIdx, in] :
llvm::enumerate(inputVec))
656 fromRealIdxToSortedIdx[inIdx] = resultIdxMap[in];
663 if (inputVec != otherVecOp.getResults()) {
664 for (
auto otherVecOpInputVec : otherVecOp.getInputs()) {
666 tempVec = SmallVector<Value>(inputVec.size());
667 for (
auto [realIdx, opernad] :
llvm::enumerate(otherVecOpInputVec))
669 otherVecOpInputVec[fromRealIdxToSortedIdx[realIdx]];
671 newOperands.insert(newOperands.end(), tempVec.begin(), tempVec.end());
675 newOperands.insert(newOperands.end(), otherVecOp.getOperands().begin(),
676 otherVecOp.getOperands().end());
678 auto &otherBlock = otherVecOp.getBody().front();
679 for (
auto &otherArg : otherBlock.getArguments()) {
680 auto newArg = currentBlock.insertArgument(
681 argIdx + paddedBy, otherArg.getType(), otherArg.getLoc());
682 argMapping.map(otherArg, newArg);
686 rewriter.setInsertionPointToStart(¤tBlock);
687 for (
auto &op : otherBlock.without_terminator())
688 rewriter.clone(op, argMapping);
690 unsigned argNewPos = paddedBy + argIdx;
693 auto retOp = cast<VectorizeReturnOp>(otherBlock.getTerminator());
694 rewriter.replaceAllUsesWith(currentBlock.getArgument(argNewPos),
695 argMapping.lookupOrDefault(retOp.getValue()));
696 currentBlock.eraseArgument(argNewPos);
697 vecOpsToRemove.push_back(otherVecOp);
709 for (
auto deadOp : vecOpsToRemove)
710 rewriter.eraseOp(deadOp);
716static unsigned hashValue(
const SmallVector<Value> &inputs) {
718 for (
auto input : inputs)
719 hash = hash_combine(hash, input);
726 return SmallVector<Value>();
730 return SmallVector<Value>();
737 static bool isEqual(
const SmallVector<Value> &lhs,
738 const SmallVector<Value> &rhs) {
744LogicalResult KeepOneVecOp::matchAndRewrite(VectorizeOp vecOp,
745 PatternRewriter &rewriter)
const {
746 DenseMap<SmallVector<Value>,
unsigned> inExists;
747 auto ¤tBlock = vecOp.getBody().front();
748 SmallVector<Value> newOperands;
749 BitVector argsToRemove(vecOp.getInputs().size(),
false);
750 for (
size_t argIdx = 0; argIdx < vecOp.getInputs().size(); ++argIdx) {
751 auto input = SmallVector<Value>(vecOp.getInputs()[argIdx].begin(),
752 vecOp.getInputs()[argIdx].end());
753 if (
auto in = inExists.find(input); in != inExists.end()) {
754 rewriter.replaceAllUsesWith(currentBlock.getArgument(argIdx),
755 currentBlock.getArgument(in->second));
756 argsToRemove.set(argIdx);
759 inExists[input] = argIdx;
760 newOperands.insert(newOperands.end(), input.begin(), input.end());
763 if (argsToRemove.none())
766 currentBlock.eraseArguments(argsToRemove);
775struct ArcCanonicalizerPass
776 :
public arc::impl::ArcCanonicalizerBase<ArcCanonicalizerPass> {
777 void runOnOperation()
override;
781void ArcCanonicalizerPass::runOnOperation() {
782 MLIRContext &
ctxt = getContext();
783 SymbolTableCollection symbolTable;
785 cache.addDefinitions(getOperation());
786 cache.collectAllSymbolUses(getOperation(), symbolTable);
789 DenseMap<StringAttr, StringAttr> arcMapping;
791 mlir::GreedyRewriteConfig config;
792 config.enableRegionSimplification = mlir::GreedySimplifyRegionLevel::Disabled;
793 config.maxIterations = 10;
794 config.useTopDownTraversal =
true;
795 ArcListener listener(&cache);
796 config.listener = &listener;
798 PatternStatistics statistics;
799 RewritePatternSet symbolPatterns(&getContext());
800 symbolPatterns.add<CallPassthroughArc, RemoveUnusedArcs,
801 RemoveUnusedArcArgumentsPattern, SinkArcInputsPattern>(
802 &getContext(), cache, names, statistics);
803 symbolPatterns.add<MemWritePortEnableAndMaskCanonicalizer>(
804 &getContext(), cache, names, statistics, arcMapping);
806 if (failed(mlir::applyPatternsGreedily(getOperation(),
807 std::move(symbolPatterns), config)))
808 return signalPassFailure();
810 numArcArgsRemoved = statistics.removeUnusedArcArgumentsPatternNumArgsRemoved;
813 for (
auto *dialect :
ctxt.getLoadedDialects())
814 dialect->getCanonicalizationPatterns(
patterns);
815 for (mlir::RegisteredOperationName op :
ctxt.getRegisteredOperations())
817 patterns.add<ICMPCanonicalizer, CompRegCanonicalizer, MergeVectorizeOps,
818 KeepOneVecOp>(&getContext());
821 (void)mlir::applyPatternsGreedily(getOperation(), std::move(
patterns),
826 return std::make_unique<ArcCanonicalizerPass>();
LogicalResult canonicalizePassthoughCall(mlir::CallOpInterface callOp, SymbolHandler &symbolCache, PatternRewriter &rewriter)
LogicalResult updateInputOperands(VectorizeOp &vecOp, const SmallVector< Value > &newOperands)
assert(baseType &&"element must be base type")
static std::optional< APSInt > getConstant(Attribute operand)
Determine the value of a constant operand for the sake of constant folding.
static InstancePath empty
A namespace that is used to store existing names and generate new names in some scope within the IR.
void add(mlir::ModuleOp module)
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
SymbolCacheBase::Iterator end() override
std::unique_ptr< mlir::Pass > createArcCanonicalizerPass()
static llvm::hash_code hash_value(const ModulePort &port)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
static unsigned hashValue(const SmallVector< Value > &inputs)
static SmallVector< Value > getTombstoneKey()
static SmallVector< Value > getEmptyKey()
static bool isEqual(const SmallVector< Value > &lhs, const SmallVector< Value > &rhs)
static unsigned getHashValue(const SmallVector< Value > &inputs)