CIRCT  18.0.0git
ArcCanonicalizer.cpp
Go to the documentation of this file.
1 //===- ArcCanonicalizer.cpp -------------------------------------*- 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 // Simulation centric canonicalizations for non-arc operations and
9 // canonicalizations that require efficient symbol lookups.
10 //
11 //===----------------------------------------------------------------------===//
12 
16 #include "circt/Dialect/HW/HWOps.h"
18 #include "circt/Support/SymCache.h"
19 #include "mlir/IR/Matchers.h"
20 #include "mlir/IR/PatternMatch.h"
21 #include "mlir/Pass/Pass.h"
22 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
23 #include "llvm/Support/Debug.h"
24 
25 #define DEBUG_TYPE "arc-canonicalizer"
26 
27 namespace circt {
28 namespace arc {
29 #define GEN_PASS_DEF_ARCCANONICALIZER
30 #include "circt/Dialect/Arc/ArcPasses.h.inc"
31 } // namespace arc
32 } // namespace circt
33 
34 using namespace circt;
35 using namespace arc;
36 
37 //===----------------------------------------------------------------------===//
38 // Datastructures
39 //===----------------------------------------------------------------------===//
40 
41 /// A combination of SymbolCache and SymbolUserMap that also allows to add users
42 /// and remove symbols on-demand.
43 class SymbolHandler : public SymbolCache {
44 public:
45  /// Return the users of the provided symbol operation.
46  ArrayRef<Operation *> getUsers(Operation *symbol) const {
47  auto it = userMap.find(symbol);
48  return it != userMap.end() ? it->second.getArrayRef() : std::nullopt;
49  }
50 
51  /// Return true if the given symbol has no uses.
52  bool useEmpty(Operation *symbol) {
53  return !userMap.count(symbol) || userMap[symbol].empty();
54  }
55 
56  void addUser(Operation *def, Operation *user) {
57  assert(isa<mlir::SymbolOpInterface>(def));
58  if (!symbolCache.contains(cast<mlir::SymbolOpInterface>(def).getNameAttr()))
59  symbolCache.insert(
60  {cast<mlir::SymbolOpInterface>(def).getNameAttr(), def});
61  userMap[def].insert(user);
62  }
63 
64  void removeUser(Operation *def, Operation *user) {
65  assert(isa<mlir::SymbolOpInterface>(def));
66  if (symbolCache.contains(cast<mlir::SymbolOpInterface>(def).getNameAttr()))
67  userMap[def].remove(user);
68  if (userMap[def].empty())
69  userMap.erase(def);
70  }
71 
72  void removeDefinitionAndAllUsers(Operation *def) {
73  assert(isa<mlir::SymbolOpInterface>(def));
74  symbolCache.erase(cast<mlir::SymbolOpInterface>(def).getNameAttr());
75  userMap.erase(def);
76  }
77 
78  void collectAllSymbolUses(Operation *symbolTableOp,
79  SymbolTableCollection &symbolTable) {
80  // NOTE: the following is almost 1-1 taken from the SymbolUserMap
81  // constructor. They made it difficult to extend the implementation by
82  // having a lot of members private and non-virtual methods.
83  SmallVector<Operation *> symbols;
84  auto walkFn = [&](Operation *symbolTableOp, bool allUsesVisible) {
85  for (Operation &nestedOp : symbolTableOp->getRegion(0).getOps()) {
86  auto symbolUses = SymbolTable::getSymbolUses(&nestedOp);
87  assert(symbolUses && "expected uses to be valid");
88 
89  for (const SymbolTable::SymbolUse &use : *symbolUses) {
90  symbols.clear();
91  (void)symbolTable.lookupSymbolIn(symbolTableOp, use.getSymbolRef(),
92  symbols);
93  for (Operation *symbolOp : symbols)
94  userMap[symbolOp].insert(use.getUser());
95  }
96  }
97  };
98  // We just set `allSymUsesVisible` to false here because it isn't necessary
99  // for building the user map.
100  SymbolTable::walkSymbolTables(symbolTableOp, /*allSymUsesVisible=*/false,
101  walkFn);
102  }
103 
104 private:
105  DenseMap<Operation *, SetVector<Operation *>> userMap;
106 };
107 
109  unsigned removeUnusedArcArgumentsPatternNumArgsRemoved = 0;
110 };
111 
112 //===----------------------------------------------------------------------===//
113 // Canonicalization patterns
114 //===----------------------------------------------------------------------===//
115 
116 namespace {
117 /// A rewrite pattern that has access to a symbol cache to access and modify the
118 /// symbol-defining op and symbol users as well as a namespace to query new
119 /// names. Each pattern has to make sure that the symbol handler is kept
120 /// up-to-date no matter whether the pattern succeeds of fails.
121 template <typename SourceOp>
122 class SymOpRewritePattern : public OpRewritePattern<SourceOp> {
123 public:
124  SymOpRewritePattern(MLIRContext *ctxt, SymbolHandler &symbolCache,
125  Namespace &names, PatternStatistics &stats,
126  mlir::PatternBenefit benefit = 1,
127  ArrayRef<StringRef> generatedNames = {})
128  : OpRewritePattern<SourceOp>(ctxt, benefit, generatedNames), names(names),
129  symbolCache(symbolCache), statistics(stats) {}
130 
131 protected:
132  Namespace &names;
133  SymbolHandler &symbolCache;
134  PatternStatistics &statistics;
135 };
136 
137 class MemWritePortEnableAndMaskCanonicalizer
138  : public SymOpRewritePattern<MemoryWritePortOp> {
139 public:
140  MemWritePortEnableAndMaskCanonicalizer(
141  MLIRContext *ctxt, SymbolHandler &symbolCache, Namespace &names,
142  PatternStatistics &stats, DenseMap<StringAttr, StringAttr> &arcMapping)
143  : SymOpRewritePattern<MemoryWritePortOp>(ctxt, symbolCache, names, stats),
144  arcMapping(arcMapping) {}
145  LogicalResult matchAndRewrite(MemoryWritePortOp op,
146  PatternRewriter &rewriter) const final;
147 
148 private:
149  DenseMap<StringAttr, StringAttr> &arcMapping;
150 };
151 
152 struct CallPassthroughArc : public SymOpRewritePattern<CallOp> {
153  using SymOpRewritePattern::SymOpRewritePattern;
154  LogicalResult matchAndRewrite(CallOp op,
155  PatternRewriter &rewriter) const final;
156 };
157 
158 struct StatePassthroughArc : public SymOpRewritePattern<StateOp> {
159  using SymOpRewritePattern::SymOpRewritePattern;
160  LogicalResult matchAndRewrite(StateOp op,
161  PatternRewriter &rewriter) const final;
162 };
163 
164 struct RemoveUnusedArcs : public SymOpRewritePattern<DefineOp> {
165  using SymOpRewritePattern::SymOpRewritePattern;
166  LogicalResult matchAndRewrite(DefineOp op,
167  PatternRewriter &rewriter) const final;
168 };
169 
170 struct ICMPCanonicalizer : public OpRewritePattern<comb::ICmpOp> {
171  using OpRewritePattern::OpRewritePattern;
172  LogicalResult matchAndRewrite(comb::ICmpOp op,
173  PatternRewriter &rewriter) const final;
174 };
175 
176 struct RemoveUnusedArcArgumentsPattern : public SymOpRewritePattern<DefineOp> {
177  using SymOpRewritePattern::SymOpRewritePattern;
178  LogicalResult matchAndRewrite(DefineOp op,
179  PatternRewriter &rewriter) const final;
180 };
181 
182 struct SinkArcInputsPattern : public SymOpRewritePattern<DefineOp> {
183  using SymOpRewritePattern::SymOpRewritePattern;
184  LogicalResult matchAndRewrite(DefineOp op,
185  PatternRewriter &rewriter) const final;
186 };
187 
188 } // namespace
189 
190 //===----------------------------------------------------------------------===//
191 // Helpers
192 //===----------------------------------------------------------------------===//
193 
194 LogicalResult canonicalizePassthoughCall(mlir::CallOpInterface callOp,
195  SymbolHandler &symbolCache,
196  PatternRewriter &rewriter) {
197  auto defOp = cast<DefineOp>(symbolCache.getDefinition(
198  callOp.getCallableForCallee().get<SymbolRefAttr>().getLeafReference()));
199  if (defOp.isPassthrough()) {
200  symbolCache.removeUser(defOp, callOp);
201  rewriter.replaceOp(callOp, callOp.getArgOperands());
202  return success();
203  }
204  return failure();
205 }
206 
207 //===----------------------------------------------------------------------===//
208 // Canonicalization pattern implementations
209 //===----------------------------------------------------------------------===//
210 
211 LogicalResult MemWritePortEnableAndMaskCanonicalizer::matchAndRewrite(
212  MemoryWritePortOp op, PatternRewriter &rewriter) const {
213  auto defOp = cast<DefineOp>(symbolCache.getDefinition(op.getArcAttr()));
214  APInt enable;
215 
216  if (op.getEnable() &&
217  mlir::matchPattern(
218  defOp.getBodyBlock().getTerminator()->getOperand(op.getEnableIdx()),
219  mlir::m_ConstantInt(&enable))) {
220  if (enable.isZero()) {
221  symbolCache.removeUser(defOp, op);
222  rewriter.eraseOp(op);
223  if (symbolCache.useEmpty(defOp)) {
224  symbolCache.removeDefinitionAndAllUsers(defOp);
225  rewriter.eraseOp(defOp);
226  }
227  return success();
228  }
229  if (enable.isAllOnes()) {
230  if (arcMapping.count(defOp.getNameAttr())) {
231  auto arcWithoutEnable = arcMapping[defOp.getNameAttr()];
232  // Remove the enable attribute
233  rewriter.updateRootInPlace(op, [&]() {
234  op.setEnable(false);
235  op.setArc(arcWithoutEnable.getValue());
236  });
237  symbolCache.removeUser(defOp, op);
238  symbolCache.addUser(symbolCache.getDefinition(arcWithoutEnable), op);
239  return success();
240  }
241 
242  auto newName = names.newName(defOp.getName());
243  auto users = SmallVector<Operation *>(symbolCache.getUsers(defOp));
244  symbolCache.removeDefinitionAndAllUsers(defOp);
245 
246  // Remove the enable attribute
247  rewriter.updateRootInPlace(op, [&]() {
248  op.setEnable(false);
249  op.setArc(newName);
250  });
251 
252  auto newResultTypes = op.getArcResultTypes();
253 
254  // Create a new arc that acts as replacement for other users
255  rewriter.setInsertionPoint(defOp);
256  auto newDefOp = rewriter.cloneWithoutRegions(defOp);
257  auto *block = rewriter.createBlock(
258  &newDefOp.getBody(), newDefOp.getBody().end(),
259  newDefOp.getArgumentTypes(),
260  SmallVector<Location>(newDefOp.getNumArguments(), defOp.getLoc()));
261  auto callOp = rewriter.create<CallOp>(newDefOp.getLoc(), newResultTypes,
262  newName, block->getArguments());
263  SmallVector<Value> results(callOp->getResults());
264  Value constTrue = rewriter.create<hw::ConstantOp>(
265  newDefOp.getLoc(), rewriter.getI1Type(), 1);
266  results.insert(results.begin() + op.getEnableIdx(), constTrue);
267  rewriter.create<OutputOp>(newDefOp.getLoc(), results);
268 
269  // Remove the enable output from the current arc
270  auto *terminator = defOp.getBodyBlock().getTerminator();
271  rewriter.updateRootInPlace(
272  terminator, [&]() { terminator->eraseOperand(op.getEnableIdx()); });
273  rewriter.updateRootInPlace(defOp, [&]() {
274  defOp.setName(newName);
275  defOp.setFunctionType(
276  rewriter.getFunctionType(defOp.getArgumentTypes(), newResultTypes));
277  });
278 
279  // Update symbol cache
280  symbolCache.addDefinition(defOp.getNameAttr(), defOp);
281  symbolCache.addDefinition(newDefOp.getNameAttr(), newDefOp);
282  symbolCache.addUser(defOp, callOp);
283  for (auto *user : users)
284  symbolCache.addUser(user == op ? defOp : newDefOp, user);
285 
286  arcMapping[newDefOp.getNameAttr()] = defOp.getNameAttr();
287  return success();
288  }
289  }
290  return failure();
291 }
292 
293 LogicalResult
294 CallPassthroughArc::matchAndRewrite(CallOp op,
295  PatternRewriter &rewriter) const {
296  return canonicalizePassthoughCall(op, symbolCache, rewriter);
297 }
298 
299 LogicalResult
300 StatePassthroughArc::matchAndRewrite(StateOp op,
301  PatternRewriter &rewriter) const {
302  if (op.getLatency() == 0)
303  return canonicalizePassthoughCall(op, symbolCache, rewriter);
304  return failure();
305 }
306 
307 LogicalResult
308 RemoveUnusedArcs::matchAndRewrite(DefineOp op,
309  PatternRewriter &rewriter) const {
310  if (symbolCache.useEmpty(op)) {
311  op.getBody().walk([&](mlir::CallOpInterface user) {
312  if (auto symbol = user.getCallableForCallee().dyn_cast<SymbolRefAttr>())
313  if (auto *defOp = symbolCache.getDefinition(symbol.getLeafReference()))
314  symbolCache.removeUser(defOp, user);
315  });
316  symbolCache.removeDefinitionAndAllUsers(op);
317  rewriter.eraseOp(op);
318  return success();
319  }
320  return failure();
321 }
322 
323 LogicalResult
324 ICMPCanonicalizer::matchAndRewrite(comb::ICmpOp op,
325  PatternRewriter &rewriter) const {
326  auto getConstant = [&](const APInt &constant) -> Value {
327  return rewriter.create<hw::ConstantOp>(op.getLoc(), constant);
328  };
329  auto sameWidthIntegers = [](TypeRange types) -> std::optional<unsigned> {
330  if (llvm::all_equal(types) && !types.empty())
331  if (auto intType = dyn_cast<IntegerType>(*types.begin()))
332  return intType.getWidth();
333  return std::nullopt;
334  };
335  auto negate = [&](Value input) -> Value {
336  auto constTrue = rewriter.create<hw::ConstantOp>(op.getLoc(), APInt(1, 1));
337  return rewriter.create<comb::XorOp>(op.getLoc(), input, constTrue,
338  op.getTwoState());
339  };
340 
341  APInt rhs;
342  if (matchPattern(op.getRhs(), mlir::m_ConstantInt(&rhs))) {
343  if (auto concatOp = op.getLhs().getDefiningOp<comb::ConcatOp>()) {
344  if (auto optionalWidth =
345  sameWidthIntegers(concatOp->getOperands().getTypes())) {
346  if ((op.getPredicate() == comb::ICmpPredicate::eq ||
347  op.getPredicate() == comb::ICmpPredicate::ne) &&
348  rhs.isAllOnes()) {
349  Value andOp = rewriter.create<comb::AndOp>(
350  op.getLoc(), concatOp.getInputs(), op.getTwoState());
351  if (*optionalWidth == 1) {
352  if (op.getPredicate() == comb::ICmpPredicate::ne)
353  andOp = negate(andOp);
354  rewriter.replaceOp(op, andOp);
355  return success();
356  }
357  rewriter.replaceOpWithNewOp<comb::ICmpOp>(
358  op, op.getPredicate(), andOp,
359  getConstant(APInt(*optionalWidth, rhs.getZExtValue())),
360  op.getTwoState());
361  return success();
362  }
363 
364  if ((op.getPredicate() == comb::ICmpPredicate::ne ||
365  op.getPredicate() == comb::ICmpPredicate::eq) &&
366  rhs.isZero()) {
367  Value orOp = rewriter.create<comb::OrOp>(
368  op.getLoc(), concatOp.getInputs(), op.getTwoState());
369  if (*optionalWidth == 1) {
370  if (op.getPredicate() == comb::ICmpPredicate::eq)
371  orOp = negate(orOp);
372  rewriter.replaceOp(op, orOp);
373  return success();
374  }
375  rewriter.replaceOpWithNewOp<comb::ICmpOp>(
376  op, op.getPredicate(), orOp,
377  getConstant(APInt(*optionalWidth, rhs.getZExtValue())),
378  op.getTwoState());
379  return success();
380  }
381  }
382  }
383  }
384  return failure();
385 }
386 
387 LogicalResult RemoveUnusedArcArgumentsPattern::matchAndRewrite(
388  DefineOp op, PatternRewriter &rewriter) const {
389  BitVector toDelete(op.getNumArguments());
390  for (auto [i, arg] : llvm::enumerate(op.getArguments()))
391  if (arg.use_empty())
392  toDelete.set(i);
393 
394  if (toDelete.none())
395  return failure();
396 
397  // Collect the mutable callers in a first iteration. If there is a user that
398  // does not implement the interface, we have to abort the rewrite and have to
399  // make sure that we didn't change anything so far.
400  SmallVector<mlir::CallOpInterface> mutableUsers;
401  for (auto *user : symbolCache.getUsers(op)) {
402  auto callOpMutable = dyn_cast<mlir::CallOpInterface>(user);
403  if (!callOpMutable)
404  return failure();
405  mutableUsers.push_back(callOpMutable);
406  }
407 
408  // Do the actual rewrites.
409  for (auto user : mutableUsers)
410  for (int i = toDelete.size() - 1; i >= 0; --i)
411  if (toDelete[i])
412  user.getArgOperandsMutable().erase(i);
413 
414  op.eraseArguments(toDelete);
415  op.setFunctionType(
416  rewriter.getFunctionType(op.getArgumentTypes(), op.getResultTypes()));
417 
418  statistics.removeUnusedArcArgumentsPatternNumArgsRemoved += toDelete.count();
419  return success();
420 }
421 
422 LogicalResult
423 SinkArcInputsPattern::matchAndRewrite(DefineOp op,
424  PatternRewriter &rewriter) const {
425  // First check that all users implement the interface we need to be able to
426  // modify the users.
427  auto users = symbolCache.getUsers(op);
428  if (llvm::any_of(
429  users, [](auto *user) { return !isa<mlir::CallOpInterface>(user); }))
430  return failure();
431 
432  // Find all arguments that use constant operands only.
433  SmallVector<Operation *> stateConsts(op.getNumArguments());
434  bool first = true;
435  for (auto *user : users) {
436  auto callOp = cast<mlir::CallOpInterface>(user);
437  for (auto [constArg, input] :
438  llvm::zip(stateConsts, callOp.getArgOperands())) {
439  if (auto *constOp = input.getDefiningOp();
440  constOp && constOp->template hasTrait<OpTrait::ConstantLike>()) {
441  if (first) {
442  constArg = constOp;
443  continue;
444  }
445  if (constArg &&
446  constArg->getName() == input.getDefiningOp()->getName() &&
447  constArg->getAttrDictionary() ==
448  input.getDefiningOp()->getAttrDictionary())
449  continue;
450  }
451  constArg = nullptr;
452  }
453  first = false;
454  }
455 
456  // Move the constants into the arc and erase the block arguments.
457  rewriter.setInsertionPointToStart(&op.getBodyBlock());
458  llvm::BitVector toDelete(op.getBodyBlock().getNumArguments());
459  for (auto [constArg, arg] : llvm::zip(stateConsts, op.getArguments())) {
460  if (!constArg)
461  continue;
462  auto *inlinedConst = rewriter.clone(*constArg);
463  rewriter.replaceAllUsesWith(arg, inlinedConst->getResult(0));
464  toDelete.set(arg.getArgNumber());
465  }
466  op.getBodyBlock().eraseArguments(toDelete);
467  op.setType(rewriter.getFunctionType(op.getBodyBlock().getArgumentTypes(),
468  op.getResultTypes()));
469 
470  // Rewrite all arc uses to not pass in the constant anymore.
471  for (auto *user : users) {
472  auto callOp = cast<mlir::CallOpInterface>(user);
473  SmallPtrSet<Value, 4> maybeUnusedValues;
474  SmallVector<Value> newInputs;
475  for (auto [index, value] : llvm::enumerate(callOp.getArgOperands())) {
476  if (toDelete[index])
477  maybeUnusedValues.insert(value);
478  else
479  newInputs.push_back(value);
480  }
481  rewriter.updateRootInPlace(
482  callOp, [&]() { callOp.getArgOperandsMutable().assign(newInputs); });
483  for (auto value : maybeUnusedValues)
484  if (value.use_empty())
485  rewriter.eraseOp(value.getDefiningOp());
486  }
487 
488  return success(toDelete.any());
489 }
490 
491 //===----------------------------------------------------------------------===//
492 // ArcCanonicalizerPass implementation
493 //===----------------------------------------------------------------------===//
494 
495 namespace {
496 struct ArcCanonicalizerPass
497  : public arc::impl::ArcCanonicalizerBase<ArcCanonicalizerPass> {
498  void runOnOperation() override;
499 };
500 } // namespace
501 
502 void ArcCanonicalizerPass::runOnOperation() {
503  MLIRContext &ctxt = getContext();
504  SymbolTableCollection symbolTable;
505  SymbolHandler cache;
506  cache.addDefinitions(getOperation());
507  cache.collectAllSymbolUses(getOperation(), symbolTable);
508  Namespace names;
509  names.add(cache);
510  DenseMap<StringAttr, StringAttr> arcMapping;
511 
512  mlir::GreedyRewriteConfig config;
513  config.enableRegionSimplification = false;
514  config.maxIterations = 10;
515  config.useTopDownTraversal = true;
516 
517  PatternStatistics statistics;
518  RewritePatternSet symbolPatterns(&getContext());
519  symbolPatterns.add<CallPassthroughArc, StatePassthroughArc, RemoveUnusedArcs,
520  RemoveUnusedArcArgumentsPattern, SinkArcInputsPattern>(
521  &getContext(), cache, names, statistics);
522  symbolPatterns.add<MemWritePortEnableAndMaskCanonicalizer>(
523  &getContext(), cache, names, statistics, arcMapping);
524 
525  if (failed(mlir::applyPatternsAndFoldGreedily(
526  getOperation(), std::move(symbolPatterns), config)))
527  return signalPassFailure();
528 
529  numArcArgsRemoved = statistics.removeUnusedArcArgumentsPatternNumArgsRemoved;
530 
531  RewritePatternSet patterns(&ctxt);
532  for (auto *dialect : ctxt.getLoadedDialects())
533  dialect->getCanonicalizationPatterns(patterns);
534  for (mlir::RegisteredOperationName op : ctxt.getRegisteredOperations())
535  op.getCanonicalizationPatterns(patterns, &ctxt);
536  patterns.add<ICMPCanonicalizer>(&getContext());
537 
538  // Don't test for convergence since it is often not reached.
539  (void)mlir::applyPatternsAndFoldGreedily(getOperation(), std::move(patterns),
540  config);
541 }
542 
543 std::unique_ptr<mlir::Pass> arc::createArcCanonicalizerPass() {
544  return std::make_unique<ArcCanonicalizerPass>();
545 }
LogicalResult canonicalizePassthoughCall(mlir::CallOpInterface callOp, SymbolHandler &symbolCache, PatternRewriter &rewriter)
lowerAnnotationsNoRefTypePorts FirtoolPreserveValuesMode value
Definition: Firtool.cpp:95
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 combination of SymbolCache and SymbolUserMap that also allows to add users and remove symbols on-de...
ArrayRef< Operation * > getUsers(Operation *symbol) const
Return the users of the provided symbol operation.
void addUser(Operation *def, Operation *user)
bool useEmpty(Operation *symbol)
Return true if the given symbol has no uses.
void removeUser(Operation *def, Operation *user)
DenseMap< Operation *, SetVector< Operation * > > userMap
void removeDefinitionAndAllUsers(Operation *def)
void collectAllSymbolUses(Operation *symbolTableOp, SymbolTableCollection &symbolTable)
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition: Namespace.h:29
void add(SymbolCache &symCache)
SymbolCache initializer; initialize from every key that is convertible to a StringAttr in the SymbolC...
Definition: Namespace.h:47
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
Definition: SymCache.cpp:23
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
Definition: SymCache.h:85
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
Definition: SymCache.h:94
def create(data_type, value)
Definition: hw.py:397
std::unique_ptr< mlir::Pass > createArcCanonicalizerPass()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21