CIRCT  19.0.0git
MooreToCore.cpp
Go to the documentation of this file.
1 //===- MooreToCore.cpp - Moore To Core Conversion Pass --------------------===//
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 //
9 // This is the main Moore to Core Conversion Pass Implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "../PassDetail.h"
16 #include "circt/Dialect/HW/HWOps.h"
19 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
20 #include "mlir/Dialect/Func/IR/FuncOps.h"
21 #include "mlir/IR/BuiltinDialect.h"
22 #include "mlir/Transforms/DialectConversion.h"
23 #include "llvm/ADT/TypeSwitch.h"
24 
25 using namespace mlir;
26 using namespace circt;
27 using namespace moore;
28 
29 namespace {
30 
31 /// Returns the passed value if the integer width is already correct.
32 /// Zero-extends if it is too narrow.
33 /// Truncates if the integer is too wide and the truncated part is zero, if it
34 /// is not zero it returns the max value integer of target-width.
35 static Value adjustIntegerWidth(OpBuilder &builder, Value value,
36  uint32_t targetWidth, Location loc) {
37  uint32_t intWidth = value.getType().getIntOrFloatBitWidth();
38  if (intWidth == targetWidth)
39  return value;
40 
41  if (intWidth < targetWidth) {
42  Value zeroExt = builder.create<hw::ConstantOp>(
43  loc, builder.getIntegerType(targetWidth - intWidth), 0);
44  return builder.create<comb::ConcatOp>(loc, ValueRange{zeroExt, value});
45  }
46 
47  Value hi = builder.create<comb::ExtractOp>(loc, value, targetWidth,
48  intWidth - targetWidth);
49  Value zero = builder.create<hw::ConstantOp>(
50  loc, builder.getIntegerType(intWidth - targetWidth), 0);
51  Value isZero = builder.create<comb::ICmpOp>(loc, comb::ICmpPredicate::eq, hi,
52  zero, false);
53  Value lo = builder.create<comb::ExtractOp>(loc, value, 0, targetWidth);
54  Value max = builder.create<hw::ConstantOp>(
55  loc, builder.getIntegerType(targetWidth), -1);
56  return builder.create<comb::MuxOp>(loc, isZero, lo, max, false);
57 }
58 
59 /// Due to the result type of the `lt`, or `le`, or `gt`, or `ge` ops are
60 /// always unsigned, estimating their operands type.
61 static bool isSignedType(Operation *op) {
62  return TypeSwitch<Operation *, bool>(op)
63  .template Case<LtOp, LeOp, GtOp, GeOp>([&](auto op) -> bool {
64  return cast<UnpackedType>(op->getOperand(0).getType())
65  .castToSimpleBitVector()
66  .isSigned() &&
67  cast<UnpackedType>(op->getOperand(1).getType())
68  .castToSimpleBitVector()
69  .isSigned();
70  })
71  .Default([&](auto op) -> bool {
72  return cast<UnpackedType>(op->getResult(0).getType())
73  .castToSimpleBitVector()
74  .isSigned();
75  });
76 }
77 
78 /// Not define the predicate for `relation` and `equality` operations in the
79 /// MooreDialect, but comb needs it. Return a correct `comb::ICmpPredicate`
80 /// corresponding to different moore `relation` and `equality` operations.
81 static comb::ICmpPredicate getCombPredicate(Operation *op) {
82  using comb::ICmpPredicate;
83  return TypeSwitch<Operation *, ICmpPredicate>(op)
84  .Case<LtOp>([&](auto op) {
85  return isSignedType(op) ? ICmpPredicate::slt : ICmpPredicate::ult;
86  })
87  .Case<LeOp>([&](auto op) {
88  return isSignedType(op) ? ICmpPredicate::sle : ICmpPredicate::ule;
89  })
90  .Case<GtOp>([&](auto op) {
91  return isSignedType(op) ? ICmpPredicate::sgt : ICmpPredicate::ugt;
92  })
93  .Case<GeOp>([&](auto op) {
94  return isSignedType(op) ? ICmpPredicate::sge : ICmpPredicate::uge;
95  })
96  .Case<EqOp>([&](auto op) { return ICmpPredicate::eq; })
97  .Case<NeOp>([&](auto op) { return ICmpPredicate::ne; })
98  .Case<CaseEqOp>([&](auto op) { return ICmpPredicate::ceq; })
99  .Case<CaseNeOp>([&](auto op) { return ICmpPredicate::cne; })
100  .Case<WildcardEqOp>([&](auto op) { return ICmpPredicate::weq; })
101  .Case<WildcardNeOp>([&](auto op) { return ICmpPredicate::wne; });
102 }
103 
104 //===----------------------------------------------------------------------===//
105 // Expression Conversion
106 //===----------------------------------------------------------------------===//
107 
108 struct ConstantOpConv : public OpConversionPattern<ConstantOp> {
109  using OpConversionPattern::OpConversionPattern;
110 
111  LogicalResult
112  matchAndRewrite(ConstantOp op, OpAdaptor adaptor,
113  ConversionPatternRewriter &rewriter) const override {
114 
115  rewriter.replaceOpWithNewOp<hw::ConstantOp>(op, op.getValueAttr());
116  return success();
117  }
118 };
119 
120 struct ConcatOpConversion : public OpConversionPattern<ConcatOp> {
121  using OpConversionPattern::OpConversionPattern;
122  LogicalResult
123  matchAndRewrite(ConcatOp op, OpAdaptor adaptor,
124  ConversionPatternRewriter &rewriter) const override {
125  rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, adaptor.getValues());
126  return success();
127  }
128 };
129 
130 struct ReplicateOpConversion : public OpConversionPattern<ReplicateOp> {
131  using OpConversionPattern::OpConversionPattern;
132  LogicalResult
133  matchAndRewrite(ReplicateOp op, OpAdaptor adaptor,
134  ConversionPatternRewriter &rewriter) const override {
135  Type resultType = typeConverter->convertType(op.getResult().getType());
136 
137  rewriter.replaceOpWithNewOp<comb::ReplicateOp>(op, resultType,
138  adaptor.getValue());
139  return success();
140  }
141 };
142 
143 struct ExtractOpConversion : public OpConversionPattern<ExtractOp> {
144  using OpConversionPattern::OpConversionPattern;
145 
146  LogicalResult
147  matchAndRewrite(ExtractOp op, OpAdaptor adaptor,
148  ConversionPatternRewriter &rewriter) const override {
149  Type resultType = typeConverter->convertType(op.getResult().getType());
150  auto width = typeConverter->convertType(op.getInput().getType())
151  .getIntOrFloatBitWidth();
152  Value amount =
153  adjustIntegerWidth(rewriter, adaptor.getLowBit(), width, op->getLoc());
154  Value value =
155  rewriter.create<comb::ShrUOp>(op->getLoc(), adaptor.getInput(), amount);
156 
157  rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, value, 0);
158  return success();
159  }
160 };
161 
162 struct ReduceAndOpConversion : public OpConversionPattern<ReduceAndOp> {
163  using OpConversionPattern::OpConversionPattern;
164  LogicalResult
165  matchAndRewrite(ReduceAndOp op, OpAdaptor adaptor,
166  ConversionPatternRewriter &rewriter) const override {
167  Type resultType = typeConverter->convertType(op.getInput().getType());
168  Value max = rewriter.create<hw::ConstantOp>(op->getLoc(), resultType, -1);
169 
170  rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::eq,
171  adaptor.getInput(), max);
172  return success();
173  }
174 };
175 
176 struct ReduceOrOpConversion : public OpConversionPattern<ReduceOrOp> {
177  using OpConversionPattern::OpConversionPattern;
178  LogicalResult
179  matchAndRewrite(ReduceOrOp op, OpAdaptor adaptor,
180  ConversionPatternRewriter &rewriter) const override {
181  Type resultType = typeConverter->convertType(op.getInput().getType());
182  Value zero = rewriter.create<hw::ConstantOp>(op->getLoc(), resultType, 0);
183 
184  rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
185  adaptor.getInput(), zero);
186  return success();
187  }
188 };
189 
190 struct ReduceXorOpConversion : public OpConversionPattern<ReduceXorOp> {
191  using OpConversionPattern::OpConversionPattern;
192  LogicalResult
193  matchAndRewrite(ReduceXorOp op, OpAdaptor adaptor,
194  ConversionPatternRewriter &rewriter) const override {
195 
196  rewriter.replaceOpWithNewOp<comb::ParityOp>(op, adaptor.getInput());
197  return success();
198  }
199 };
200 
201 struct BoolCastOpConversion : public OpConversionPattern<BoolCastOp> {
202  using OpConversionPattern::OpConversionPattern;
203  LogicalResult
204  matchAndRewrite(BoolCastOp op, OpAdaptor adaptor,
205  ConversionPatternRewriter &rewriter) const override {
206  if (cast<UnpackedType>(op.getInput().getType())
207  .castToSimpleBitVectorOrNull()) {
208  Type resultType = typeConverter->convertType(op.getInput().getType());
209  Value zero = rewriter.create<hw::ConstantOp>(op->getLoc(), resultType, 0);
210 
211  rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
212  adaptor.getInput(), zero);
213  return success();
214  }
215 
216  return failure();
217  }
218 };
219 
220 struct NotOpConversion : public OpConversionPattern<NotOp> {
221  using OpConversionPattern::OpConversionPattern;
222  LogicalResult
223  matchAndRewrite(NotOp op, OpAdaptor adaptor,
224  ConversionPatternRewriter &rewriter) const override {
225  Type resultType =
226  ConversionPattern::typeConverter->convertType(op.getResult().getType());
227  Value max = rewriter.create<hw::ConstantOp>(op.getLoc(), resultType, -1);
228 
229  rewriter.replaceOpWithNewOp<comb::XorOp>(op, adaptor.getInput(), max);
230  return success();
231  }
232 };
233 
234 template <typename SourceOp, typename UnsignedOp,
235  typename SignedOp = UnsignedOp>
236 struct BinaryOpConversion : public OpConversionPattern<SourceOp> {
238  using OpAdaptor = typename SourceOp::Adaptor;
239 
240  LogicalResult
241  matchAndRewrite(SourceOp op, OpAdaptor adaptor,
242  ConversionPatternRewriter &rewriter) const override {
243 
244  isSignedType(op)
245  ? rewriter.replaceOpWithNewOp<SignedOp>(op, adaptor.getLhs(),
246  adaptor.getRhs(), false)
247  : rewriter.replaceOpWithNewOp<UnsignedOp>(op, adaptor.getLhs(),
248  adaptor.getRhs(), false);
249  return success();
250  }
251 };
252 
253 template <typename SourceOp>
254 struct ICmpOpConversion : public OpConversionPattern<SourceOp> {
256  using OpAdaptor = typename SourceOp::Adaptor;
257 
258  LogicalResult
259  matchAndRewrite(SourceOp op, OpAdaptor adapter,
260  ConversionPatternRewriter &rewriter) const override {
261  Type resultType =
262  ConversionPattern::typeConverter->convertType(op.getResult().getType());
263  comb::ICmpPredicate pred = getCombPredicate(op);
264 
265  rewriter.replaceOpWithNewOp<comb::ICmpOp>(
266  op, resultType, pred, adapter.getLhs(), adapter.getRhs());
267  return success();
268  }
269 };
270 
271 struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
272  using OpConversionPattern::OpConversionPattern;
273 
274  LogicalResult
275  matchAndRewrite(ConversionOp op, OpAdaptor adaptor,
276  ConversionPatternRewriter &rewriter) const override {
277  Type resultType = typeConverter->convertType(op.getResult().getType());
278  Value amount =
279  adjustIntegerWidth(rewriter, adaptor.getInput(),
280  resultType.getIntOrFloatBitWidth(), op->getLoc());
281 
282  rewriter.replaceOpWithNewOp<hw::BitcastOp>(op, resultType, amount);
283  return success();
284  }
285 };
286 
287 //===----------------------------------------------------------------------===//
288 // Statement Conversion
289 //===----------------------------------------------------------------------===//
290 
291 struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
292  using OpConversionPattern::OpConversionPattern;
293 
294  LogicalResult
295  matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
296  ConversionPatternRewriter &rewriter) const override {
297  rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
298  return success();
299  }
300 };
301 
302 struct CondBranchOpConversion : public OpConversionPattern<cf::CondBranchOp> {
303  using OpConversionPattern::OpConversionPattern;
304 
305  LogicalResult
306  matchAndRewrite(cf::CondBranchOp op, OpAdaptor adaptor,
307  ConversionPatternRewriter &rewriter) const override {
308  rewriter.replaceOpWithNewOp<cf::CondBranchOp>(
309  op, adaptor.getCondition(), adaptor.getTrueDestOperands(),
310  adaptor.getFalseDestOperands(), op.getTrueDest(), op.getFalseDest());
311  return success();
312  }
313 };
314 
315 struct BranchOpConversion : public OpConversionPattern<cf::BranchOp> {
316  using OpConversionPattern::OpConversionPattern;
317 
318  LogicalResult
319  matchAndRewrite(cf::BranchOp op, OpAdaptor adaptor,
320  ConversionPatternRewriter &rewriter) const override {
321  rewriter.replaceOpWithNewOp<cf::BranchOp>(op, op.getDest(),
322  adaptor.getDestOperands());
323  return success();
324  }
325 };
326 
327 struct CallOpConversion : public OpConversionPattern<func::CallOp> {
328  using OpConversionPattern::OpConversionPattern;
329 
330  LogicalResult
331  matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
332  ConversionPatternRewriter &rewriter) const override {
333  SmallVector<Type> convResTypes;
334  if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
335  return failure();
336  rewriter.replaceOpWithNewOp<func::CallOp>(
337  op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
338  return success();
339  }
340 };
341 
342 struct UnrealizedConversionCastConversion
343  : public OpConversionPattern<UnrealizedConversionCastOp> {
344  using OpConversionPattern::OpConversionPattern;
345 
346  LogicalResult
347  matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
348  ConversionPatternRewriter &rewriter) const override {
349  SmallVector<Type> convResTypes;
350  if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
351  return failure();
352 
353  // Drop the cast if the operand and result types agree after type
354  // conversion.
355  if (convResTypes == adaptor.getOperands().getTypes()) {
356  rewriter.replaceOp(op, adaptor.getOperands());
357  return success();
358  }
359 
360  rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
361  op, convResTypes, adaptor.getOperands());
362  return success();
363  }
364 };
365 
366 struct ShlOpConversion : public OpConversionPattern<ShlOp> {
367  using OpConversionPattern::OpConversionPattern;
368 
369  LogicalResult
370  matchAndRewrite(ShlOp op, OpAdaptor adaptor,
371  ConversionPatternRewriter &rewriter) const override {
372  Type resultType = typeConverter->convertType(op.getResult().getType());
373 
374  // Comb shift operations require the same bit-width for value and amount
375  Value amount =
376  adjustIntegerWidth(rewriter, adaptor.getAmount(),
377  resultType.getIntOrFloatBitWidth(), op->getLoc());
378  rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
379  amount, false);
380  return success();
381  }
382 };
383 
384 struct ShrOpConversion : public OpConversionPattern<ShrOp> {
385  using OpConversionPattern::OpConversionPattern;
386 
387  LogicalResult
388  matchAndRewrite(ShrOp op, OpAdaptor adaptor,
389  ConversionPatternRewriter &rewriter) const override {
390  Type resultType = typeConverter->convertType(op.getResult().getType());
391 
392  // Comb shift operations require the same bit-width for value and amount
393  Value amount =
394  adjustIntegerWidth(rewriter, adaptor.getAmount(),
395  resultType.getIntOrFloatBitWidth(), op->getLoc());
396  rewriter.replaceOpWithNewOp<comb::ShrUOp>(
397  op, resultType, adaptor.getValue(), amount, false);
398  return success();
399  }
400 };
401 
402 struct AShrOpConversion : public OpConversionPattern<AShrOp> {
403  using OpConversionPattern::OpConversionPattern;
404 
405  LogicalResult
406  matchAndRewrite(AShrOp op, OpAdaptor adaptor,
407  ConversionPatternRewriter &rewriter) const override {
408  Type resultType = typeConverter->convertType(op.getResult().getType());
409 
410  // Comb shift operations require the same bit-width for value and amount
411  Value amount =
412  adjustIntegerWidth(rewriter, adaptor.getAmount(),
413  resultType.getIntOrFloatBitWidth(), op->getLoc());
414  rewriter.replaceOpWithNewOp<comb::ShrSOp>(
415  op, resultType, adaptor.getValue(), amount, false);
416  return success();
417  }
418 };
419 
420 } // namespace
421 
422 //===----------------------------------------------------------------------===//
423 // Conversion Infrastructure
424 //===----------------------------------------------------------------------===//
425 
426 static bool isMooreType(Type type) { return type.isa<UnpackedType>(); }
427 
428 static bool hasMooreType(TypeRange types) {
429  return llvm::any_of(types, isMooreType);
430 }
431 
432 static bool hasMooreType(ValueRange values) {
433  return hasMooreType(values.getTypes());
434 }
435 
436 template <typename Op>
437 static void addGenericLegality(ConversionTarget &target) {
438  target.addDynamicallyLegalOp<Op>([](Op op) {
439  return !hasMooreType(op->getOperands()) && !hasMooreType(op->getResults());
440  });
441 }
442 
443 static void populateLegality(ConversionTarget &target) {
444  target.addIllegalDialect<MooreDialect>();
445  target.addLegalDialect<mlir::BuiltinDialect>();
446  target.addLegalDialect<hw::HWDialect>();
447  target.addLegalDialect<llhd::LLHDDialect>();
448  target.addLegalDialect<comb::CombDialect>();
449 
450  addGenericLegality<cf::CondBranchOp>(target);
451  addGenericLegality<cf::BranchOp>(target);
452  addGenericLegality<func::CallOp>(target);
453  addGenericLegality<func::ReturnOp>(target);
454  addGenericLegality<UnrealizedConversionCastOp>(target);
455 
456  target.addDynamicallyLegalOp<func::FuncOp>([](func::FuncOp op) {
457  auto argsConverted = llvm::none_of(op.getBlocks(), [](auto &block) {
458  return hasMooreType(block.getArguments());
459  });
460  auto resultsConverted = !hasMooreType(op.getResultTypes());
461  return argsConverted && resultsConverted;
462  });
463 }
464 
465 static void populateTypeConversion(TypeConverter &typeConverter) {
466  typeConverter.addConversion([&](IntType type) {
467  return mlir::IntegerType::get(type.getContext(), type.getBitSize());
468  });
469 
470  // Directly map simple bit vector types to a compact integer type. This needs
471  // to be added after all of the other conversions above, such that SBVs
472  // conversion gets tried first before any of the others.
473  typeConverter.addConversion([&](UnpackedType type) -> std::optional<Type> {
474  if (auto sbv = type.getSimpleBitVectorOrNull())
475  return mlir::IntegerType::get(type.getContext(), sbv.size);
476  if (isa<UnpackedRangeDim, PackedRangeDim>(type))
477  return mlir::IntegerType::get(type.getContext(),
478  type.getBitSize().value());
479  return std::nullopt;
480  });
481 
482  // Valid target types.
483  typeConverter.addConversion([](mlir::IntegerType type) { return type; });
484 }
485 
486 static void populateOpConversion(RewritePatternSet &patterns,
487  TypeConverter &typeConverter) {
488  auto *context = patterns.getContext();
489  // clang-format off
490  patterns.add<
491  // Patterns of miscellaneous operations.
492  ConstantOpConv, ConcatOpConversion, ReplicateOpConversion,
493  ExtractOpConversion, ConversionOpConversion,
494 
495  // Patterns of unary operations.
496  ReduceAndOpConversion, ReduceOrOpConversion, ReduceXorOpConversion,
497  BoolCastOpConversion, NotOpConversion,
498 
499  // Patterns of binary operations.
500  BinaryOpConversion<AddOp, comb::AddOp>,
501  BinaryOpConversion<SubOp, comb::SubOp>,
502  BinaryOpConversion<MulOp, comb::MulOp>,
503  BinaryOpConversion<DivOp, comb::DivUOp, comb::DivSOp>,
504  BinaryOpConversion<ModOp, comb::ModUOp, comb::ModSOp>,
505  BinaryOpConversion<AndOp, comb::AndOp>,
506  BinaryOpConversion<OrOp, comb::OrOp>,
507  BinaryOpConversion<XorOp, comb::XorOp>,
508 
509  // Patterns of relational operations.
510  ICmpOpConversion<LtOp>, ICmpOpConversion<LeOp>, ICmpOpConversion<GtOp>,
511  ICmpOpConversion<GeOp>, ICmpOpConversion<EqOp>, ICmpOpConversion<NeOp>,
512  ICmpOpConversion<CaseEqOp>, ICmpOpConversion<CaseNeOp>,
513  ICmpOpConversion<WildcardEqOp>, ICmpOpConversion<WildcardNeOp>,
514 
515  // Patterns of shifting operations.
516  ShrOpConversion, ShlOpConversion, AShrOpConversion,
517 
518  // Patterns of branch operations.
519  CondBranchOpConversion, BranchOpConversion,
520 
521  // Patterns of other operations outside Moore dialect.
522  ReturnOpConversion, CallOpConversion, UnrealizedConversionCastConversion
523  >(typeConverter, context);
524  // clang-format on
525  mlir::populateFunctionOpInterfaceTypeConversionPattern<func::FuncOp>(
526  patterns, typeConverter);
527 }
528 
529 //===----------------------------------------------------------------------===//
530 // Moore to Core Conversion Pass
531 //===----------------------------------------------------------------------===//
532 
533 namespace {
534 struct MooreToCorePass : public ConvertMooreToCoreBase<MooreToCorePass> {
535  void runOnOperation() override;
536 };
537 } // namespace
538 
539 /// Create a Moore to core dialects conversion pass.
540 std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
541  return std::make_unique<MooreToCorePass>();
542 }
543 
544 /// This is the main entrypoint for the Moore to Core conversion pass.
545 void MooreToCorePass::runOnOperation() {
546  MLIRContext &context = getContext();
547  ModuleOp module = getOperation();
548 
549  ConversionTarget target(context);
550  TypeConverter typeConverter;
551  RewritePatternSet patterns(&context);
552  populateLegality(target);
553  populateTypeConversion(typeConverter);
554  populateOpConversion(patterns, typeConverter);
555 
556  if (failed(applyFullConversion(module, target, std::move(patterns))))
557  signalPassFailure();
558 }
int32_t width
Definition: FIRRTL.cpp:36
static void populateLegality(ConversionTarget &target)
static bool hasMooreType(TypeRange types)
static void populateOpConversion(RewritePatternSet &patterns, TypeConverter &typeConverter)
static bool isMooreType(Type type)
static void addGenericLegality(ConversionTarget &target)
static void populateTypeConversion(TypeConverter &typeConverter)
Builder builder
An integer vector or atom type.
Definition: MooreTypes.h:520
static unsigned getBitSize(Kind kind)
Get the size of one of the integer types.
Definition: MooreTypes.cpp:427
An unpacked SystemVerilog type.
Definition: MooreTypes.h:291
def create(low_bit, result_type, input=None)
Definition: comb.py:187
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
std::unique_ptr< OperationPass< ModuleOp > > createConvertMooreToCorePass()
Create an Moore to Comb/HW/LLHD conversion pass.