CIRCT  18.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 //===----------------------------------------------------------------------===//
60 // Expression Conversion
61 //===----------------------------------------------------------------------===//
62 
63 struct ConstantOpConv : public OpConversionPattern<ConstantOp> {
64  using OpConversionPattern::OpConversionPattern;
65 
66  LogicalResult
67  matchAndRewrite(ConstantOp op, OpAdaptor adaptor,
68  ConversionPatternRewriter &rewriter) const override {
69 
70  rewriter.replaceOpWithNewOp<hw::ConstantOp>(op, op.getValueAttr());
71  return success();
72  }
73 };
74 
75 struct ConcatOpConversion : public OpConversionPattern<ConcatOp> {
76  using OpConversionPattern::OpConversionPattern;
77  LogicalResult
78  matchAndRewrite(ConcatOp op, OpAdaptor adaptor,
79  ConversionPatternRewriter &rewriter) const override {
80  rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, adaptor.getValues());
81  return success();
82  }
83 };
84 
85 //===----------------------------------------------------------------------===//
86 // Statement Conversion
87 //===----------------------------------------------------------------------===//
88 
89 struct VariableDeclOpConv : public OpConversionPattern<VariableDeclOp> {
90  using OpConversionPattern::OpConversionPattern;
91 
92  LogicalResult
93  matchAndRewrite(VariableDeclOp op, OpAdaptor adaptor,
94  ConversionPatternRewriter &rewriter) const override {
95  Type resultType = typeConverter->convertType(op.getResult().getType());
96  Value initVal =
97  rewriter.create<hw::ConstantOp>(op->getLoc(), op.getInitAttr());
98  rewriter.replaceOpWithNewOp<llhd::SigOp>(op, resultType, op.getName(),
99  initVal);
100  return success();
101  }
102 };
103 
104 struct AssignOpConv : public OpConversionPattern<AssignOp> {
105  using OpConversionPattern::OpConversionPattern;
106 
107  LogicalResult
108  matchAndRewrite(AssignOp op, OpAdaptor adaptor,
109  ConversionPatternRewriter &rewriter) const override {
110  Value timeVal =
111  rewriter.create<llhd::ConstantTimeOp>(op->getLoc(), 0, "s", 0, 1);
112  rewriter.replaceOpWithNewOp<llhd::DrvOp>(
113  op, adaptor.getDest(), adaptor.getSrc(), timeVal, Value());
114  return success();
115  }
116 };
117 
118 struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
119  using OpConversionPattern::OpConversionPattern;
120 
121  LogicalResult
122  matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
123  ConversionPatternRewriter &rewriter) const override {
124  rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
125  return success();
126  }
127 };
128 
129 struct CondBranchOpConversion : public OpConversionPattern<cf::CondBranchOp> {
130  using OpConversionPattern::OpConversionPattern;
131 
132  LogicalResult
133  matchAndRewrite(cf::CondBranchOp op, OpAdaptor adaptor,
134  ConversionPatternRewriter &rewriter) const override {
135  rewriter.replaceOpWithNewOp<cf::CondBranchOp>(
136  op, adaptor.getCondition(), adaptor.getTrueDestOperands(),
137  adaptor.getFalseDestOperands(), op.getTrueDest(), op.getFalseDest());
138  return success();
139  }
140 };
141 
142 struct BranchOpConversion : public OpConversionPattern<cf::BranchOp> {
143  using OpConversionPattern::OpConversionPattern;
144 
145  LogicalResult
146  matchAndRewrite(cf::BranchOp op, OpAdaptor adaptor,
147  ConversionPatternRewriter &rewriter) const override {
148  rewriter.replaceOpWithNewOp<cf::BranchOp>(op, op.getDest(),
149  adaptor.getDestOperands());
150  return success();
151  }
152 };
153 
154 struct CallOpConversion : public OpConversionPattern<func::CallOp> {
155  using OpConversionPattern::OpConversionPattern;
156 
157  LogicalResult
158  matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
159  ConversionPatternRewriter &rewriter) const override {
160  SmallVector<Type> convResTypes;
161  if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
162  return failure();
163  rewriter.replaceOpWithNewOp<func::CallOp>(
164  op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
165  return success();
166  }
167 };
168 
169 struct UnrealizedConversionCastConversion
170  : public OpConversionPattern<UnrealizedConversionCastOp> {
171  using OpConversionPattern::OpConversionPattern;
172 
173  LogicalResult
174  matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
175  ConversionPatternRewriter &rewriter) const override {
176  SmallVector<Type> convResTypes;
177  if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
178  return failure();
179 
180  // Drop the cast if the operand and result types agree after type
181  // conversion.
182  if (convResTypes == adaptor.getOperands().getTypes()) {
183  rewriter.replaceOp(op, adaptor.getOperands());
184  return success();
185  }
186 
187  rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
188  op, convResTypes, adaptor.getOperands());
189  return success();
190  }
191 };
192 
193 struct ShlOpConversion : public OpConversionPattern<ShlOp> {
194  using OpConversionPattern::OpConversionPattern;
195 
196  LogicalResult
197  matchAndRewrite(ShlOp op, OpAdaptor adaptor,
198  ConversionPatternRewriter &rewriter) const override {
199  Type resultType = typeConverter->convertType(op.getResult().getType());
200 
201  // Comb shift operations require the same bit-width for value and amount
202  Value amount =
203  adjustIntegerWidth(rewriter, adaptor.getAmount(),
204  resultType.getIntOrFloatBitWidth(), op->getLoc());
205  rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
206  amount, false);
207  return success();
208  }
209 };
210 
211 struct ShrOpConversion : public OpConversionPattern<ShrOp> {
212  using OpConversionPattern::OpConversionPattern;
213 
214  LogicalResult
215  matchAndRewrite(ShrOp op, OpAdaptor adaptor,
216  ConversionPatternRewriter &rewriter) const override {
217  Type resultType = typeConverter->convertType(op.getResult().getType());
218  bool hasSignedResultType = op.getResult()
219  .getType()
220  .cast<UnpackedType>()
221  .castToSimpleBitVector()
222  .isSigned();
223 
224  // Comb shift operations require the same bit-width for value and amount
225  Value amount =
226  adjustIntegerWidth(rewriter, adaptor.getAmount(),
227  resultType.getIntOrFloatBitWidth(), op->getLoc());
228 
229  if (adaptor.getArithmetic() && hasSignedResultType) {
230  rewriter.replaceOpWithNewOp<comb::ShrSOp>(
231  op, resultType, adaptor.getValue(), amount, false);
232  return success();
233  }
234 
235  rewriter.replaceOpWithNewOp<comb::ShrUOp>(
236  op, resultType, adaptor.getValue(), amount, false);
237  return success();
238  }
239 };
240 
241 } // namespace
242 
243 //===----------------------------------------------------------------------===//
244 // Conversion Infrastructure
245 //===----------------------------------------------------------------------===//
246 
247 static bool isMooreType(Type type) {
248  return type.isa<UnpackedType>() || type.isa<IntType>() ||
249  type.isa<LValueType>();
250 }
251 
252 static bool hasMooreType(TypeRange types) {
253  return llvm::any_of(types, isMooreType);
254 }
255 
256 static bool hasMooreType(ValueRange values) {
257  return hasMooreType(values.getTypes());
258 }
259 
260 template <typename Op>
261 static void addGenericLegality(ConversionTarget &target) {
262  target.addDynamicallyLegalOp<Op>([](Op op) {
263  return !hasMooreType(op->getOperands()) && !hasMooreType(op->getResults());
264  });
265 }
266 
267 static void populateLegality(ConversionTarget &target) {
268  target.addIllegalDialect<MooreDialect>();
269  target.addLegalDialect<mlir::BuiltinDialect>();
270  target.addLegalDialect<hw::HWDialect>();
271  target.addLegalDialect<llhd::LLHDDialect>();
272  target.addLegalDialect<comb::CombDialect>();
273 
274  addGenericLegality<cf::CondBranchOp>(target);
275  addGenericLegality<cf::BranchOp>(target);
276  addGenericLegality<func::CallOp>(target);
277  addGenericLegality<func::ReturnOp>(target);
278  addGenericLegality<UnrealizedConversionCastOp>(target);
279 
280  target.addDynamicallyLegalOp<func::FuncOp>([](func::FuncOp op) {
281  auto argsConverted = llvm::none_of(op.getBlocks(), [](auto &block) {
282  return hasMooreType(block.getArguments());
283  });
284  auto resultsConverted = !hasMooreType(op.getResultTypes());
285  return argsConverted && resultsConverted;
286  });
287 }
288 
289 static void populateTypeConversion(TypeConverter &typeConverter) {
290  typeConverter.addConversion([&](IntType type) {
291  return mlir::IntegerType::get(type.getContext(), type.getBitSize());
292  });
293  typeConverter.addConversion([&](LValueType type) {
294  auto inner = typeConverter.convertType(type.getNestedType());
295  return llhd::SigType::get(inner);
296  });
297 
298  // Directly map simple bit vector types to a compact integer type. This needs
299  // to be added after all of the other conversions above, such that SBVs
300  // conversion gets tried first before any of the others.
301  typeConverter.addConversion([&](UnpackedType type) -> std::optional<Type> {
302  if (auto sbv = type.getSimpleBitVectorOrNull())
303  return mlir::IntegerType::get(type.getContext(), sbv.size);
304  return std::nullopt;
305  });
306 
307  // Valid target types.
308  typeConverter.addConversion([](mlir::IntegerType type) { return type; });
309 }
310 
311 static void populateOpConversion(RewritePatternSet &patterns,
312  TypeConverter &typeConverter) {
313  auto *context = patterns.getContext();
314  // clang-format off
315  patterns.add<
316  ConstantOpConv,
317  ConcatOpConversion,
318  VariableDeclOpConv,
319  AssignOpConv,
320  ReturnOpConversion,
321  CondBranchOpConversion,
322  BranchOpConversion,
323  CallOpConversion,
324  ShlOpConversion,
325  ShrOpConversion,
326  UnrealizedConversionCastConversion
327  >(typeConverter, context);
328  // clang-format on
329  mlir::populateFunctionOpInterfaceTypeConversionPattern<func::FuncOp>(
330  patterns, typeConverter);
331 }
332 
333 //===----------------------------------------------------------------------===//
334 // Moore to Core Conversion Pass
335 //===----------------------------------------------------------------------===//
336 
337 namespace {
338 struct MooreToCorePass : public ConvertMooreToCoreBase<MooreToCorePass> {
339  void runOnOperation() override;
340 };
341 } // namespace
342 
343 /// Create a Moore to core dialects conversion pass.
344 std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
345  return std::make_unique<MooreToCorePass>();
346 }
347 
348 /// This is the main entrypoint for the Moore to Core conversion pass.
349 void MooreToCorePass::runOnOperation() {
350  MLIRContext &context = getContext();
351  ModuleOp module = getOperation();
352 
353  ConversionTarget target(context);
354  TypeConverter typeConverter;
355  RewritePatternSet patterns(&context);
356  populateLegality(target);
357  populateTypeConversion(typeConverter);
358  populateOpConversion(patterns, typeConverter);
359 
360  if (failed(applyFullConversion(module, target, std::move(patterns))))
361  signalPassFailure();
362 }
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:503
static unsigned getBitSize(Kind kind)
Get the size of one of the integer types.
Definition: MooreTypes.cpp:425
An unpacked SystemVerilog type.
Definition: MooreTypes.h:282
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
std::unique_ptr< OperationPass< ModuleOp > > createConvertMooreToCorePass()
Create an Moore to Comb/HW/LLHD conversion pass.