CIRCT  20.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 
16 #include "circt/Dialect/HW/HWOps.h"
21 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
22 #include "mlir/Dialect/Func/IR/FuncOps.h"
23 #include "mlir/Dialect/SCF/IR/SCF.h"
24 #include "mlir/IR/BuiltinDialect.h"
25 #include "mlir/IR/Iterators.h"
26 #include "mlir/Interfaces/SideEffectInterfaces.h"
27 #include "mlir/Pass/Pass.h"
28 #include "mlir/Transforms/DialectConversion.h"
29 #include "mlir/Transforms/RegionUtils.h"
30 #include "llvm/ADT/TypeSwitch.h"
31 
32 namespace circt {
33 #define GEN_PASS_DEF_CONVERTMOORETOCORE
34 #include "circt/Conversion/Passes.h.inc"
35 } // namespace circt
36 
37 using namespace mlir;
38 using namespace circt;
39 using namespace moore;
40 
41 using comb::ICmpPredicate;
42 using llvm::SmallDenseSet;
43 
44 namespace {
45 
46 /// Returns the passed value if the integer width is already correct.
47 /// Zero-extends if it is too narrow.
48 /// Truncates if the integer is too wide and the truncated part is zero, if it
49 /// is not zero it returns the max value integer of target-width.
50 static Value adjustIntegerWidth(OpBuilder &builder, Value value,
51  uint32_t targetWidth, Location loc) {
52  uint32_t intWidth = value.getType().getIntOrFloatBitWidth();
53  if (intWidth == targetWidth)
54  return value;
55 
56  if (intWidth < targetWidth) {
57  Value zeroExt = builder.create<hw::ConstantOp>(
58  loc, builder.getIntegerType(targetWidth - intWidth), 0);
59  return builder.create<comb::ConcatOp>(loc, ValueRange{zeroExt, value});
60  }
61 
62  Value hi = builder.create<comb::ExtractOp>(loc, value, targetWidth,
63  intWidth - targetWidth);
64  Value zero = builder.create<hw::ConstantOp>(
65  loc, builder.getIntegerType(intWidth - targetWidth), 0);
66  Value isZero = builder.create<comb::ICmpOp>(loc, comb::ICmpPredicate::eq, hi,
67  zero, false);
68  Value lo = builder.create<comb::ExtractOp>(loc, value, 0, targetWidth);
69  Value max = builder.create<hw::ConstantOp>(
70  loc, builder.getIntegerType(targetWidth), -1);
71  return builder.create<comb::MuxOp>(loc, isZero, lo, max, false);
72 }
73 
74 /// Get the ModulePortInfo from a SVModuleOp.
75 static hw::ModulePortInfo getModulePortInfo(const TypeConverter &typeConverter,
76  SVModuleOp op) {
77  size_t inputNum = 0;
78  size_t resultNum = 0;
79  auto moduleTy = op.getModuleType();
80  SmallVector<hw::PortInfo> inputs, outputs;
81  inputs.reserve(moduleTy.getNumInputs());
82  outputs.reserve(moduleTy.getNumOutputs());
83 
84  for (auto port : moduleTy.getPorts()) {
85  Type portTy = typeConverter.convertType(port.type);
86  if (auto ioTy = dyn_cast_or_null<hw::InOutType>(portTy)) {
87  inputs.push_back(hw::PortInfo(
88  {{port.name, ioTy.getElementType(), hw::ModulePort::InOut},
89  inputNum++,
90  {}}));
91  continue;
92  }
93 
94  if (port.dir == hw::ModulePort::Direction::Output) {
95  outputs.push_back(
96  hw::PortInfo({{port.name, portTy, port.dir}, resultNum++, {}}));
97  } else {
98  // FIXME: Once we support net<...>, ref<...> type to represent type of
99  // special port like inout or ref port which is not a input or output
100  // port. It can change to generate corresponding types for direction of
101  // port or do specified operation to it. Now inout and ref port is treated
102  // as input port.
103  inputs.push_back(
104  hw::PortInfo({{port.name, portTy, port.dir}, inputNum++, {}}));
105  }
106  }
107 
108  return hw::ModulePortInfo(inputs, outputs);
109 }
110 
111 //===----------------------------------------------------------------------===//
112 // Structural Conversion
113 //===----------------------------------------------------------------------===//
114 
115 struct SVModuleOpConversion : public OpConversionPattern<SVModuleOp> {
116  using OpConversionPattern::OpConversionPattern;
117 
118  LogicalResult
119  matchAndRewrite(SVModuleOp op, OpAdaptor adaptor,
120  ConversionPatternRewriter &rewriter) const override {
121  rewriter.setInsertionPoint(op);
122 
123  // Create the hw.module to replace moore.module
124  auto hwModuleOp =
125  rewriter.create<hw::HWModuleOp>(op.getLoc(), op.getSymNameAttr(),
126  getModulePortInfo(*typeConverter, op));
127  // Make hw.module have the same visibility as the moore.module.
128  // The entry/top level module is public, otherwise is private.
129  SymbolTable::setSymbolVisibility(hwModuleOp,
130  SymbolTable::getSymbolVisibility(op));
131  rewriter.eraseBlock(hwModuleOp.getBodyBlock());
132  if (failed(
133  rewriter.convertRegionTypes(&op.getBodyRegion(), *typeConverter)))
134  return failure();
135  rewriter.inlineRegionBefore(op.getBodyRegion(), hwModuleOp.getBodyRegion(),
136  hwModuleOp.getBodyRegion().end());
137 
138  // Erase the original op
139  rewriter.eraseOp(op);
140  return success();
141  }
142 };
143 
144 struct OutputOpConversion : public OpConversionPattern<OutputOp> {
145  using OpConversionPattern::OpConversionPattern;
146 
147  LogicalResult
148  matchAndRewrite(OutputOp op, OpAdaptor adaptor,
149  ConversionPatternRewriter &rewriter) const override {
150  rewriter.replaceOpWithNewOp<hw::OutputOp>(op, adaptor.getOperands());
151  return success();
152  }
153 };
154 
155 struct InstanceOpConversion : public OpConversionPattern<InstanceOp> {
156  using OpConversionPattern::OpConversionPattern;
157 
158  LogicalResult
159  matchAndRewrite(InstanceOp op, OpAdaptor adaptor,
160  ConversionPatternRewriter &rewriter) const override {
161  auto instName = op.getInstanceNameAttr();
162  auto moduleName = op.getModuleNameAttr();
163 
164  // Create the new hw instanceOp to replace the original one.
165  rewriter.setInsertionPoint(op);
166  auto instOp = rewriter.create<hw::InstanceOp>(
167  op.getLoc(), op.getResultTypes(), instName, moduleName, op.getInputs(),
168  op.getInputNamesAttr(), op.getOutputNamesAttr(),
169  /*Parameter*/ rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr);
170 
171  // Replace uses chain and erase the original op.
172  op.replaceAllUsesWith(instOp.getResults());
173  rewriter.eraseOp(op);
174  return success();
175  }
176 };
177 
178 static void getValuesToObserve(Region *region,
179  function_ref<void(Value)> setInsertionPoint,
180  const TypeConverter *typeConverter,
181  ConversionPatternRewriter &rewriter,
182  SmallVector<Value> &observeValues) {
183  SmallDenseSet<Value> alreadyObserved;
184  Location loc = region->getLoc();
185 
186  auto probeIfSignal = [&](Value value) -> Value {
187  if (!isa<hw::InOutType>(value.getType()))
188  return value;
189  return rewriter.create<llhd::PrbOp>(loc, value);
190  };
191 
192  region->getParentOp()->walk<WalkOrder::PreOrder, ForwardDominanceIterator<>>(
193  [&](Operation *operation) {
194  for (auto value : operation->getOperands()) {
195  if (region->isAncestor(value.getParentRegion()))
196  continue;
197  if (auto *defOp = value.getDefiningOp();
198  defOp && defOp->hasTrait<OpTrait::ConstantLike>())
199  continue;
200  if (!alreadyObserved.insert(value).second)
201  continue;
202 
203  OpBuilder::InsertionGuard g(rewriter);
204  if (auto remapped = rewriter.getRemappedValue(value)) {
205  setInsertionPoint(remapped);
206  observeValues.push_back(probeIfSignal(remapped));
207  } else {
208  setInsertionPoint(value);
209  auto type = typeConverter->convertType(value.getType());
210  auto converted = typeConverter->materializeTargetConversion(
211  rewriter, loc, type, value);
212  observeValues.push_back(probeIfSignal(converted));
213  }
214  }
215  });
216 }
217 
218 struct ProcedureOpConversion : public OpConversionPattern<ProcedureOp> {
219  using OpConversionPattern::OpConversionPattern;
220 
221  LogicalResult
222  matchAndRewrite(ProcedureOp op, OpAdaptor adaptor,
223  ConversionPatternRewriter &rewriter) const override {
224  // Collect values to observe before we do any modifications to the region.
225  SmallVector<Value> observedValues;
226  if (op.getKind() == ProcedureKind::AlwaysComb ||
227  op.getKind() == ProcedureKind::AlwaysLatch) {
228  auto setInsertionPoint = [&](Value value) {
229  rewriter.setInsertionPoint(op);
230  };
231  getValuesToObserve(&op.getBody(), setInsertionPoint, typeConverter,
232  rewriter, observedValues);
233  }
234 
235  auto loc = op.getLoc();
236  if (failed(rewriter.convertRegionTypes(&op.getBody(), *typeConverter)))
237  return failure();
238 
239  // Handle initial and final procedures. These lower to a corresponding
240  // `llhd.process` or `llhd.final` op that executes the body and then halts.
241  if (op.getKind() == ProcedureKind::Initial ||
242  op.getKind() == ProcedureKind::Final) {
243  Operation *newOp;
244  if (op.getKind() == ProcedureKind::Initial)
245  newOp = rewriter.create<llhd::ProcessOp>(loc);
246  else
247  newOp = rewriter.create<llhd::FinalOp>(loc);
248  auto &body = newOp->getRegion(0);
249  rewriter.inlineRegionBefore(op.getBody(), body, body.end());
250  for (auto returnOp :
251  llvm::make_early_inc_range(body.getOps<ReturnOp>())) {
252  rewriter.setInsertionPoint(returnOp);
253  rewriter.replaceOpWithNewOp<llhd::HaltOp>(returnOp);
254  }
255  rewriter.eraseOp(op);
256  return success();
257  }
258 
259  // All other procedures lower to a an `llhd.process`.
260  auto newOp = rewriter.create<llhd::ProcessOp>(loc);
261 
262  // We need to add an empty entry block because it is not allowed in MLIR to
263  // branch back to the entry block. Instead we put the logic in the second
264  // block and branch to that.
265  rewriter.createBlock(&newOp.getBody());
266  auto *block = &op.getBody().front();
267  rewriter.create<cf::BranchOp>(loc, block);
268  rewriter.inlineRegionBefore(op.getBody(), newOp.getBody(),
269  newOp.getBody().end());
270 
271  // Add special handling for `always_comb` and `always_latch` procedures.
272  // These run once at simulation startup and then implicitly wait for any of
273  // the values they access to change before running again. To implement this,
274  // we create another basic block that contains the implicit wait, and make
275  // all `moore.return` ops branch to that wait block instead of immediately
276  // jumping back up to the body.
277  if (op.getKind() == ProcedureKind::AlwaysComb ||
278  op.getKind() == ProcedureKind::AlwaysLatch) {
279  Block *waitBlock = rewriter.createBlock(&newOp.getBody());
280  rewriter.create<llhd::WaitOp>(loc, observedValues, Value(), ValueRange{},
281  block);
282  block = waitBlock;
283  }
284 
285  // Make all `moore.return` ops branch back up to the beginning of the
286  // process, or the wait block created above for `always_comb` and
287  // `always_latch` procedures.
288  for (auto returnOp : llvm::make_early_inc_range(newOp.getOps<ReturnOp>())) {
289  rewriter.setInsertionPoint(returnOp);
290  rewriter.create<cf::BranchOp>(loc, block);
291  rewriter.eraseOp(returnOp);
292  }
293 
294  rewriter.eraseOp(op);
295  return success();
296  }
297 };
298 
299 struct WaitEventOpConversion : public OpConversionPattern<WaitEventOp> {
300  using OpConversionPattern::OpConversionPattern;
301 
302  LogicalResult
303  matchAndRewrite(WaitEventOp op, OpAdaptor adaptor,
304  ConversionPatternRewriter &rewriter) const override {
305  // In order to convert the `wait_event` op we need to create three separate
306  // blocks at the location of the op:
307  //
308  // - A "wait" block that reads the current state of any values used to
309  // detect events and then waits until any of those values change. When a
310  // change occurs, control transfers to the "check" block.
311  // - A "check" block which is executed after any interesting signal has
312  // changed. This is where any `detect_event` ops read the current state of
313  // interesting values and compare them against their state before the wait
314  // in order to detect an event. If any events were detected, control
315  // transfers to the "resume" block; otherwise control goes back to the
316  // "wait" block.
317  // - A "resume" block which holds any ops after the `wait_event` op. This is
318  // where control is expected to resume after an event has happened.
319  //
320  // Block structure before:
321  // opA
322  // moore.wait_event { ... }
323  // opB
324  //
325  // Block structure after:
326  // opA
327  // cf.br ^wait
328  // ^wait:
329  // <read "before" values>
330  // llhd.wait ^check, ...
331  // ^check:
332  // <read "after" values>
333  // <detect edges>
334  // cf.cond_br %event, ^resume, ^wait
335  // ^resume:
336  // opB
337  auto *resumeBlock =
338  rewriter.splitBlock(op->getBlock(), ++Block::iterator(op));
339  auto *waitBlock = rewriter.createBlock(resumeBlock);
340  auto *checkBlock = rewriter.createBlock(resumeBlock);
341 
342  auto loc = op.getLoc();
343  rewriter.setInsertionPoint(op);
344  rewriter.create<cf::BranchOp>(loc, waitBlock);
345 
346  // We need to inline two copies of the `wait_event`'s body region: one is
347  // used to determine the values going into `detect_event` ops before the
348  // `llhd.wait`, and one will do the actual event detection after the
349  // `llhd.wait`.
350  //
351  // Create a copy of the entire `wait_event` op in the wait block, which also
352  // creates a copy of its region. Take note of all inputs to `detect_event`
353  // ops and delete the `detect_event` ops in this copy.
354  SmallVector<Value> valuesBefore;
355  rewriter.setInsertionPointToEnd(waitBlock);
356  auto clonedOp = cast<WaitEventOp>(rewriter.clone(*op));
357  for (auto detectOp :
358  llvm::make_early_inc_range(clonedOp.getOps<DetectEventOp>())) {
359  valuesBefore.push_back(detectOp.getInput());
360  rewriter.eraseOp(detectOp);
361  }
362 
363  // Determine the values used during event detection that are defined outside
364  // the `wait_event`'s body region. We want to wait for a change on these
365  // signals before we check if any interesting event happened.
366  SmallVector<Value> observeValues;
367  auto setInsertionPointAfterDef = [&](Value value) {
368  if (auto *op = value.getDefiningOp())
369  rewriter.setInsertionPointAfter(op);
370  if (auto arg = dyn_cast<BlockArgument>(value))
371  rewriter.setInsertionPointToStart(value.getParentBlock());
372  };
373 
374  getValuesToObserve(&clonedOp.getBody(), setInsertionPointAfterDef,
375  typeConverter, rewriter, observeValues);
376 
377  // Create the `llhd.wait` op that suspends the current process and waits for
378  // a change in the interesting values listed in `observeValues`. When a
379  // change is detected, execution resumes in the "check" block.
380  auto waitOp = rewriter.create<llhd::WaitOp>(loc, observeValues, Value(),
381  ValueRange{}, checkBlock);
382  rewriter.inlineBlockBefore(&clonedOp.getBody().front(), waitOp);
383  rewriter.eraseOp(clonedOp);
384 
385  // Collect a list of all detect ops and inline the `wait_event` body into
386  // the check block.
387  SmallVector<DetectEventOp> detectOps(op.getBody().getOps<DetectEventOp>());
388  rewriter.inlineBlockBefore(&op.getBody().front(), checkBlock,
389  checkBlock->end());
390  rewriter.eraseOp(op);
391 
392  // Helper function to detect if a certain change occurred between a value
393  // before the `llhd.wait` and after.
394  auto computeTrigger = [&](Value before, Value after, Edge edge) -> Value {
395  before = typeConverter->materializeTargetConversion(
396  rewriter, loc, rewriter.getI1Type(), before);
397  after = typeConverter->materializeTargetConversion(
398  rewriter, loc, rewriter.getI1Type(), after);
399 
400  if (edge == Edge::AnyChange)
401  return rewriter.create<comb::ICmpOp>(loc, ICmpPredicate::ne, before,
402  after, true);
403 
404  SmallVector<Value> disjuncts;
405  Value trueVal = rewriter.create<hw::ConstantOp>(loc, APInt(1, 1));
406 
407  if (edge == Edge::PosEdge || edge == Edge::BothEdges) {
408  Value notOldVal =
409  rewriter.create<comb::XorOp>(loc, before, trueVal, true);
410  Value posedge =
411  rewriter.create<comb::AndOp>(loc, notOldVal, after, true);
412  disjuncts.push_back(posedge);
413  }
414 
415  if (edge == Edge::NegEdge || edge == Edge::BothEdges) {
416  Value notCurrVal =
417  rewriter.create<comb::XorOp>(loc, after, trueVal, true);
418  Value posedge =
419  rewriter.create<comb::AndOp>(loc, before, notCurrVal, true);
420  disjuncts.push_back(posedge);
421  }
422 
423  return rewriter.createOrFold<comb::OrOp>(loc, disjuncts, true);
424  };
425 
426  // Convert all `detect_event` ops into a check for the corresponding event
427  // between the value before and after the `llhd.wait`. The "before" value
428  // has been collected into `valuesBefore` in the "wait" block; the "after"
429  // value corresponds to the detect op's input.
430  SmallVector<Value> triggers;
431  for (auto [detectOp, before] : llvm::zip(detectOps, valuesBefore)) {
432  // TODO: Support multi-bit values. Edge detection occurs per-bit.
433  if (auto intType = dyn_cast<IntType>(before.getType());
434  !intType || intType.getWidth() != 1)
435  return detectOp->emitError() << "requires single bit operand";
436 
437  rewriter.setInsertionPoint(detectOp);
438  auto trigger =
439  computeTrigger(before, detectOp.getInput(), detectOp.getEdge());
440  if (detectOp.getCondition()) {
441  auto condition = typeConverter->materializeTargetConversion(
442  rewriter, loc, rewriter.getI1Type(), detectOp.getCondition());
443  trigger = rewriter.create<comb::AndOp>(loc, trigger, condition, true);
444  }
445  triggers.push_back(trigger);
446  rewriter.eraseOp(detectOp);
447  }
448 
449  // If any `detect_event` op detected an event, branch to the "resume" block
450  // which contains any code after the `wait_event` op. If no events were
451  // detected, branch back to the "wait" block to wait for the next change on
452  // the interesting signals.
453  rewriter.setInsertionPointToEnd(checkBlock);
454  if (!triggers.empty()) {
455  auto triggered = rewriter.createOrFold<comb::OrOp>(loc, triggers, true);
456  rewriter.create<cf::CondBranchOp>(loc, triggered, resumeBlock, waitBlock);
457  } else {
458  rewriter.create<cf::BranchOp>(loc, waitBlock);
459  }
460 
461  return success();
462  }
463 };
464 
465 //===----------------------------------------------------------------------===//
466 // Declaration Conversion
467 //===----------------------------------------------------------------------===//
468 
469 struct VariableOpConversion : public OpConversionPattern<VariableOp> {
470  using OpConversionPattern::OpConversionPattern;
471 
472  LogicalResult
473  matchAndRewrite(VariableOp op, OpAdaptor adaptor,
474  ConversionPatternRewriter &rewriter) const override {
475  Location loc = op.getLoc();
476  Type resultType = typeConverter->convertType(op.getResult().getType());
477  if (!resultType)
478  return rewriter.notifyMatchFailure(op.getLoc(), "invalid variable type");
479 
480  // Determine the initial value of the signal.
481  Value init = adaptor.getInitial();
482  if (!init) {
483  Type elementType = cast<hw::InOutType>(resultType).getElementType();
484  int64_t width = hw::getBitWidth(elementType);
485  if (width == -1)
486  return failure();
487 
488  // TODO: Once the core dialects support four-valued integers, this code
489  // will additionally need to generate an all-X value for four-valued
490  // variables.
491  Value constZero = rewriter.create<hw::ConstantOp>(loc, APInt(width, 0));
492  init = rewriter.createOrFold<hw::BitcastOp>(loc, elementType, constZero);
493  }
494 
495  rewriter.replaceOpWithNewOp<llhd::SignalOp>(op, resultType,
496  op.getNameAttr(), init);
497  return success();
498  }
499 };
500 
501 struct NetOpConversion : public OpConversionPattern<NetOp> {
502  using OpConversionPattern::OpConversionPattern;
503 
504  LogicalResult
505  matchAndRewrite(NetOp op, OpAdaptor adaptor,
506  ConversionPatternRewriter &rewriter) const override {
507  auto loc = op.getLoc();
508  if (op.getKind() != NetKind::Wire)
509  return rewriter.notifyMatchFailure(loc, "only wire nets supported");
510 
511  auto resultType = typeConverter->convertType(op.getResult().getType());
512  if (!resultType)
513  return rewriter.notifyMatchFailure(loc, "invalid net type");
514 
515  // TODO: Once the core dialects support four-valued integers, this code
516  // will additionally need to generate an all-X value for four-valued nets.
517  auto elementType = cast<hw::InOutType>(resultType).getElementType();
518  int64_t width = hw::getBitWidth(elementType);
519  if (width == -1)
520  return failure();
521  auto constZero = rewriter.create<hw::ConstantOp>(loc, APInt(width, 0));
522  auto init =
523  rewriter.createOrFold<hw::BitcastOp>(loc, elementType, constZero);
524 
525  auto signal = rewriter.replaceOpWithNewOp<llhd::SignalOp>(
526  op, resultType, op.getNameAttr(), init);
527 
528  if (auto assignedValue = adaptor.getAssignment()) {
529  auto timeAttr = llhd::TimeAttr::get(resultType.getContext(), 0U,
530  llvm::StringRef("ns"), 0, 1);
531  auto time = rewriter.create<llhd::ConstantTimeOp>(loc, timeAttr);
532  rewriter.create<llhd::DrvOp>(loc, signal, assignedValue, time, Value{});
533  }
534 
535  return success();
536  }
537 };
538 
539 //===----------------------------------------------------------------------===//
540 // Expression Conversion
541 //===----------------------------------------------------------------------===//
542 
543 struct ConstantOpConv : public OpConversionPattern<ConstantOp> {
544  using OpConversionPattern::OpConversionPattern;
545 
546  LogicalResult
547  matchAndRewrite(ConstantOp op, OpAdaptor adaptor,
548  ConversionPatternRewriter &rewriter) const override {
549  // FIXME: Discard unknown bits and map them to 0 for now.
550  auto value = op.getValue().toAPInt(false);
551  auto type = rewriter.getIntegerType(value.getBitWidth());
552  rewriter.replaceOpWithNewOp<hw::ConstantOp>(
553  op, type, rewriter.getIntegerAttr(type, value));
554  return success();
555  }
556 };
557 
558 struct ConcatOpConversion : public OpConversionPattern<ConcatOp> {
559  using OpConversionPattern::OpConversionPattern;
560  LogicalResult
561  matchAndRewrite(ConcatOp op, OpAdaptor adaptor,
562  ConversionPatternRewriter &rewriter) const override {
563  rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, adaptor.getValues());
564  return success();
565  }
566 };
567 
568 struct ReplicateOpConversion : public OpConversionPattern<ReplicateOp> {
569  using OpConversionPattern::OpConversionPattern;
570  LogicalResult
571  matchAndRewrite(ReplicateOp op, OpAdaptor adaptor,
572  ConversionPatternRewriter &rewriter) const override {
573  Type resultType = typeConverter->convertType(op.getResult().getType());
574 
575  rewriter.replaceOpWithNewOp<comb::ReplicateOp>(op, resultType,
576  adaptor.getValue());
577  return success();
578  }
579 };
580 
581 struct ExtractOpConversion : public OpConversionPattern<ExtractOp> {
582  using OpConversionPattern::OpConversionPattern;
583 
584  LogicalResult
585  matchAndRewrite(ExtractOp op, OpAdaptor adaptor,
586  ConversionPatternRewriter &rewriter) const override {
587  // TODO: properly handle out-of-bounds accesses
588  Type resultType = typeConverter->convertType(op.getResult().getType());
589  Type inputType = adaptor.getInput().getType();
590 
591  if (isa<IntegerType>(inputType)) {
592  rewriter.replaceOpWithNewOp<comb::ExtractOp>(
593  op, resultType, adaptor.getInput(), adaptor.getLowBit());
594  return success();
595  }
596 
597  if (auto arrTy = dyn_cast<hw::ArrayType>(inputType)) {
598  int64_t width = llvm::Log2_64_Ceil(arrTy.getNumElements());
599  Value idx = rewriter.create<hw::ConstantOp>(
600  op.getLoc(), rewriter.getIntegerType(width), adaptor.getLowBit());
601  if (isa<hw::ArrayType>(resultType)) {
602  rewriter.replaceOpWithNewOp<hw::ArraySliceOp>(op, resultType,
603  adaptor.getInput(), idx);
604  return success();
605  }
606 
607  // Otherwise, it has to be the array's element type
608  rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(), idx);
609  return success();
610  }
611 
612  return failure();
613  }
614 };
615 
616 struct ExtractRefOpConversion : public OpConversionPattern<ExtractRefOp> {
617  using OpConversionPattern::OpConversionPattern;
618 
619  LogicalResult
620  matchAndRewrite(ExtractRefOp op, OpAdaptor adaptor,
621  ConversionPatternRewriter &rewriter) const override {
622  // TODO: properly handle out-of-bounds accesses
623  Type resultType = typeConverter->convertType(op.getResult().getType());
624  Type inputType =
625  cast<hw::InOutType>(adaptor.getInput().getType()).getElementType();
626 
627  if (auto intType = dyn_cast<IntegerType>(inputType)) {
628  int64_t width = hw::getBitWidth(inputType);
629  if (width == -1)
630  return failure();
631 
632  Value lowBit = rewriter.create<hw::ConstantOp>(
633  op.getLoc(), rewriter.getIntegerType(llvm::Log2_64_Ceil(width)),
634  adaptor.getLowBit());
635  rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
636  op, resultType, adaptor.getInput(), lowBit);
637  return success();
638  }
639 
640  if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
641  Value lowBit = rewriter.create<hw::ConstantOp>(
642  op.getLoc(),
643  rewriter.getIntegerType(llvm::Log2_64_Ceil(arrType.getNumElements())),
644  adaptor.getLowBit());
645 
646  if (isa<hw::ArrayType>(
647  cast<hw::InOutType>(resultType).getElementType())) {
648  rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
649  op, resultType, adaptor.getInput(), lowBit);
650  return success();
651  }
652 
653  rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
654  lowBit);
655  return success();
656  }
657 
658  return failure();
659  }
660 };
661 
662 struct DynExtractOpConversion : public OpConversionPattern<DynExtractOp> {
663  using OpConversionPattern::OpConversionPattern;
664 
665  LogicalResult
666  matchAndRewrite(DynExtractOp op, OpAdaptor adaptor,
667  ConversionPatternRewriter &rewriter) const override {
668  Type resultType = typeConverter->convertType(op.getResult().getType());
669  Type inputType = adaptor.getInput().getType();
670 
671  if (auto intType = dyn_cast<IntegerType>(inputType)) {
672  Value amount = adjustIntegerWidth(rewriter, adaptor.getLowBit(),
673  intType.getWidth(), op->getLoc());
674  Value value = rewriter.create<comb::ShrUOp>(op->getLoc(),
675  adaptor.getInput(), amount);
676 
677  rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, value, 0);
678  return success();
679  }
680 
681  if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
682  unsigned idxWidth = llvm::Log2_64_Ceil(arrType.getNumElements());
683  Value idx = adjustIntegerWidth(rewriter, adaptor.getLowBit(), idxWidth,
684  op->getLoc());
685 
686  if (isa<hw::ArrayType>(resultType)) {
687  rewriter.replaceOpWithNewOp<hw::ArraySliceOp>(op, resultType,
688  adaptor.getInput(), idx);
689  return success();
690  }
691 
692  rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(), idx);
693  return success();
694  }
695 
696  return failure();
697  }
698 };
699 
700 struct DynExtractRefOpConversion : public OpConversionPattern<DynExtractRefOp> {
701  using OpConversionPattern::OpConversionPattern;
702 
703  LogicalResult
704  matchAndRewrite(DynExtractRefOp op, OpAdaptor adaptor,
705  ConversionPatternRewriter &rewriter) const override {
706  // TODO: properly handle out-of-bounds accesses
707  Type resultType = typeConverter->convertType(op.getResult().getType());
708  Type inputType =
709  cast<hw::InOutType>(adaptor.getInput().getType()).getElementType();
710 
711  if (auto intType = dyn_cast<IntegerType>(inputType)) {
712  int64_t width = hw::getBitWidth(inputType);
713  if (width == -1)
714  return failure();
715 
716  Value amount =
717  adjustIntegerWidth(rewriter, adaptor.getLowBit(),
718  llvm::Log2_64_Ceil(width), op->getLoc());
719  rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
720  op, resultType, adaptor.getInput(), amount);
721  return success();
722  }
723 
724  if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
725  Value idx = adjustIntegerWidth(
726  rewriter, adaptor.getLowBit(),
727  llvm::Log2_64_Ceil(arrType.getNumElements()), op->getLoc());
728 
729  if (isa<hw::ArrayType>(
730  cast<hw::InOutType>(resultType).getElementType())) {
731  rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
732  op, resultType, adaptor.getInput(), idx);
733  return success();
734  }
735 
736  rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
737  idx);
738  return success();
739  }
740 
741  return failure();
742  }
743 };
744 
745 struct StructCreateOpConversion : public OpConversionPattern<StructCreateOp> {
746  using OpConversionPattern::OpConversionPattern;
747 
748  LogicalResult
749  matchAndRewrite(StructCreateOp op, OpAdaptor adaptor,
750  ConversionPatternRewriter &rewriter) const override {
751  Type resultType = typeConverter->convertType(op.getResult().getType());
752  rewriter.replaceOpWithNewOp<hw::StructCreateOp>(op, resultType,
753  adaptor.getFields());
754  return success();
755  }
756 };
757 
758 struct StructExtractOpConversion : public OpConversionPattern<StructExtractOp> {
759  using OpConversionPattern::OpConversionPattern;
760 
761  LogicalResult
762  matchAndRewrite(StructExtractOp op, OpAdaptor adaptor,
763  ConversionPatternRewriter &rewriter) const override {
764  rewriter.replaceOpWithNewOp<hw::StructExtractOp>(
765  op, adaptor.getInput(), adaptor.getFieldNameAttr());
766  return success();
767  }
768 };
769 
770 struct StructExtractRefOpConversion
771  : public OpConversionPattern<StructExtractRefOp> {
772  using OpConversionPattern::OpConversionPattern;
773 
774  LogicalResult
775  matchAndRewrite(StructExtractRefOp op, OpAdaptor adaptor,
776  ConversionPatternRewriter &rewriter) const override {
777  rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
778  op, adaptor.getInput(), adaptor.getFieldNameAttr());
779  return success();
780  }
781 };
782 
783 struct ReduceAndOpConversion : public OpConversionPattern<ReduceAndOp> {
784  using OpConversionPattern::OpConversionPattern;
785  LogicalResult
786  matchAndRewrite(ReduceAndOp op, OpAdaptor adaptor,
787  ConversionPatternRewriter &rewriter) const override {
788  Type resultType = typeConverter->convertType(op.getInput().getType());
789  Value max = rewriter.create<hw::ConstantOp>(op->getLoc(), resultType, -1);
790 
791  rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::eq,
792  adaptor.getInput(), max);
793  return success();
794  }
795 };
796 
797 struct ReduceOrOpConversion : public OpConversionPattern<ReduceOrOp> {
798  using OpConversionPattern::OpConversionPattern;
799  LogicalResult
800  matchAndRewrite(ReduceOrOp op, OpAdaptor adaptor,
801  ConversionPatternRewriter &rewriter) const override {
802  Type resultType = typeConverter->convertType(op.getInput().getType());
803  Value zero = rewriter.create<hw::ConstantOp>(op->getLoc(), resultType, 0);
804 
805  rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
806  adaptor.getInput(), zero);
807  return success();
808  }
809 };
810 
811 struct ReduceXorOpConversion : public OpConversionPattern<ReduceXorOp> {
812  using OpConversionPattern::OpConversionPattern;
813  LogicalResult
814  matchAndRewrite(ReduceXorOp op, OpAdaptor adaptor,
815  ConversionPatternRewriter &rewriter) const override {
816 
817  rewriter.replaceOpWithNewOp<comb::ParityOp>(op, adaptor.getInput());
818  return success();
819  }
820 };
821 
822 struct BoolCastOpConversion : public OpConversionPattern<BoolCastOp> {
823  using OpConversionPattern::OpConversionPattern;
824  LogicalResult
825  matchAndRewrite(BoolCastOp op, OpAdaptor adaptor,
826  ConversionPatternRewriter &rewriter) const override {
827  Type resultType = typeConverter->convertType(op.getInput().getType());
828  if (isa_and_nonnull<IntegerType>(resultType)) {
829  Value zero = rewriter.create<hw::ConstantOp>(op->getLoc(), resultType, 0);
830  rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
831  adaptor.getInput(), zero);
832  return success();
833  }
834  return failure();
835  }
836 };
837 
838 struct NotOpConversion : public OpConversionPattern<NotOp> {
839  using OpConversionPattern::OpConversionPattern;
840  LogicalResult
841  matchAndRewrite(NotOp op, OpAdaptor adaptor,
842  ConversionPatternRewriter &rewriter) const override {
843  Type resultType =
844  ConversionPattern::typeConverter->convertType(op.getResult().getType());
845  Value max = rewriter.create<hw::ConstantOp>(op.getLoc(), resultType, -1);
846 
847  rewriter.replaceOpWithNewOp<comb::XorOp>(op, adaptor.getInput(), max);
848  return success();
849  }
850 };
851 
852 struct NegOpConversion : public OpConversionPattern<NegOp> {
853  using OpConversionPattern::OpConversionPattern;
854  LogicalResult
855  matchAndRewrite(NegOp op, OpAdaptor adaptor,
856  ConversionPatternRewriter &rewriter) const override {
857  Type resultType =
858  ConversionPattern::typeConverter->convertType(op.getResult().getType());
859  Value zero = rewriter.create<hw::ConstantOp>(op.getLoc(), resultType, 0);
860 
861  rewriter.replaceOpWithNewOp<comb::SubOp>(op, zero, adaptor.getInput());
862  return success();
863  }
864 };
865 
866 template <typename SourceOp, typename TargetOp>
867 struct BinaryOpConversion : public OpConversionPattern<SourceOp> {
869  using OpAdaptor = typename SourceOp::Adaptor;
870 
871  LogicalResult
872  matchAndRewrite(SourceOp op, OpAdaptor adaptor,
873  ConversionPatternRewriter &rewriter) const override {
874  rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
875  adaptor.getRhs(), false);
876  return success();
877  }
878 };
879 
880 template <typename SourceOp, ICmpPredicate pred>
881 struct ICmpOpConversion : public OpConversionPattern<SourceOp> {
883  using OpAdaptor = typename SourceOp::Adaptor;
884 
885  LogicalResult
886  matchAndRewrite(SourceOp op, OpAdaptor adaptor,
887  ConversionPatternRewriter &rewriter) const override {
888  Type resultType =
889  ConversionPattern::typeConverter->convertType(op.getResult().getType());
890 
891  rewriter.replaceOpWithNewOp<comb::ICmpOp>(
892  op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
893  return success();
894  }
895 };
896 
897 template <typename SourceOp, bool withoutX>
898 struct CaseXZEqOpConversion : public OpConversionPattern<SourceOp> {
900  using OpAdaptor = typename SourceOp::Adaptor;
901 
902  LogicalResult
903  matchAndRewrite(SourceOp op, OpAdaptor adaptor,
904  ConversionPatternRewriter &rewriter) const override {
905  // Check each operand if it is a known constant and extract the X and/or Z
906  // bits to be ignored.
907  // TODO: Once the core dialects support four-valued integers, we will have
908  // to create ops that extract X and Z bits from the operands, since we also
909  // have to do the right casez/casex comparison on non-constant inputs.
910  unsigned bitWidth = op.getLhs().getType().getWidth();
911  auto ignoredBits = APInt::getZero(bitWidth);
912  auto detectIgnoredBits = [&](Value value) {
913  auto constOp = value.getDefiningOp<ConstantOp>();
914  if (!constOp)
915  return;
916  auto constValue = constOp.getValue();
917  if (withoutX)
918  ignoredBits |= constValue.getZBits();
919  else
920  ignoredBits |= constValue.getUnknownBits();
921  };
922  detectIgnoredBits(op.getLhs());
923  detectIgnoredBits(op.getRhs());
924 
925  // If we have detected any bits to be ignored, mask them in the operands for
926  // the comparison.
927  Value lhs = adaptor.getLhs();
928  Value rhs = adaptor.getRhs();
929  if (!ignoredBits.isZero()) {
930  ignoredBits.flipAllBits();
931  auto maskOp = rewriter.create<hw::ConstantOp>(op.getLoc(), ignoredBits);
932  lhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), lhs, maskOp);
933  rhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), rhs, maskOp);
934  }
935 
936  rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, ICmpPredicate::ceq, lhs, rhs);
937  return success();
938  }
939 };
940 
941 struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
942  using OpConversionPattern::OpConversionPattern;
943 
944  LogicalResult
945  matchAndRewrite(ConversionOp op, OpAdaptor adaptor,
946  ConversionPatternRewriter &rewriter) const override {
947  Location loc = op.getLoc();
948  Type resultType = typeConverter->convertType(op.getResult().getType());
949  int64_t inputBw = hw::getBitWidth(adaptor.getInput().getType());
950  int64_t resultBw = hw::getBitWidth(resultType);
951  if (inputBw == -1 || resultBw == -1)
952  return failure();
953 
954  Value input = rewriter.createOrFold<hw::BitcastOp>(
955  loc, rewriter.getIntegerType(inputBw), adaptor.getInput());
956  Value amount = adjustIntegerWidth(rewriter, input, resultBw, loc);
957 
958  Value result =
959  rewriter.createOrFold<hw::BitcastOp>(loc, resultType, amount);
960  rewriter.replaceOp(op, result);
961  return success();
962  }
963 };
964 
965 //===----------------------------------------------------------------------===//
966 // Statement Conversion
967 //===----------------------------------------------------------------------===//
968 
969 struct HWInstanceOpConversion : public OpConversionPattern<hw::InstanceOp> {
970  using OpConversionPattern::OpConversionPattern;
971 
972  LogicalResult
973  matchAndRewrite(hw::InstanceOp op, OpAdaptor adaptor,
974  ConversionPatternRewriter &rewriter) const override {
975  SmallVector<Type> convResTypes;
976  if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
977  return failure();
978 
979  rewriter.replaceOpWithNewOp<hw::InstanceOp>(
980  op, convResTypes, op.getInstanceName(), op.getModuleName(),
981  adaptor.getOperands(), op.getArgNames(),
982  op.getResultNames(), /*Parameter*/
983  rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr);
984 
985  return success();
986  }
987 };
988 
989 struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
990  using OpConversionPattern::OpConversionPattern;
991 
992  LogicalResult
993  matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
994  ConversionPatternRewriter &rewriter) const override {
995  rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
996  return success();
997  }
998 };
999 
1000 struct CondBranchOpConversion : public OpConversionPattern<cf::CondBranchOp> {
1001  using OpConversionPattern::OpConversionPattern;
1002 
1003  LogicalResult
1004  matchAndRewrite(cf::CondBranchOp op, OpAdaptor adaptor,
1005  ConversionPatternRewriter &rewriter) const override {
1006  rewriter.replaceOpWithNewOp<cf::CondBranchOp>(
1007  op, adaptor.getCondition(), adaptor.getTrueDestOperands(),
1008  adaptor.getFalseDestOperands(), op.getTrueDest(), op.getFalseDest());
1009  return success();
1010  }
1011 };
1012 
1013 struct BranchOpConversion : public OpConversionPattern<cf::BranchOp> {
1014  using OpConversionPattern::OpConversionPattern;
1015 
1016  LogicalResult
1017  matchAndRewrite(cf::BranchOp op, OpAdaptor adaptor,
1018  ConversionPatternRewriter &rewriter) const override {
1019  rewriter.replaceOpWithNewOp<cf::BranchOp>(op, op.getDest(),
1020  adaptor.getDestOperands());
1021  return success();
1022  }
1023 };
1024 
1025 struct CallOpConversion : public OpConversionPattern<func::CallOp> {
1026  using OpConversionPattern::OpConversionPattern;
1027 
1028  LogicalResult
1029  matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
1030  ConversionPatternRewriter &rewriter) const override {
1031  SmallVector<Type> convResTypes;
1032  if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1033  return failure();
1034  rewriter.replaceOpWithNewOp<func::CallOp>(
1035  op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
1036  return success();
1037  }
1038 };
1039 
1040 struct UnrealizedConversionCastConversion
1041  : public OpConversionPattern<UnrealizedConversionCastOp> {
1042  using OpConversionPattern::OpConversionPattern;
1043 
1044  LogicalResult
1045  matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
1046  ConversionPatternRewriter &rewriter) const override {
1047  SmallVector<Type> convResTypes;
1048  if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1049  return failure();
1050 
1051  // Drop the cast if the operand and result types agree after type
1052  // conversion.
1053  if (convResTypes == adaptor.getOperands().getTypes()) {
1054  rewriter.replaceOp(op, adaptor.getOperands());
1055  return success();
1056  }
1057 
1058  rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
1059  op, convResTypes, adaptor.getOperands());
1060  return success();
1061  }
1062 };
1063 
1064 struct ShlOpConversion : public OpConversionPattern<ShlOp> {
1065  using OpConversionPattern::OpConversionPattern;
1066 
1067  LogicalResult
1068  matchAndRewrite(ShlOp op, OpAdaptor adaptor,
1069  ConversionPatternRewriter &rewriter) const override {
1070  Type resultType = typeConverter->convertType(op.getResult().getType());
1071 
1072  // Comb shift operations require the same bit-width for value and amount
1073  Value amount =
1074  adjustIntegerWidth(rewriter, adaptor.getAmount(),
1075  resultType.getIntOrFloatBitWidth(), op->getLoc());
1076  rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
1077  amount, false);
1078  return success();
1079  }
1080 };
1081 
1082 struct ShrOpConversion : public OpConversionPattern<ShrOp> {
1083  using OpConversionPattern::OpConversionPattern;
1084 
1085  LogicalResult
1086  matchAndRewrite(ShrOp op, OpAdaptor adaptor,
1087  ConversionPatternRewriter &rewriter) const override {
1088  Type resultType = typeConverter->convertType(op.getResult().getType());
1089 
1090  // Comb shift operations require the same bit-width for value and amount
1091  Value amount =
1092  adjustIntegerWidth(rewriter, adaptor.getAmount(),
1093  resultType.getIntOrFloatBitWidth(), op->getLoc());
1094  rewriter.replaceOpWithNewOp<comb::ShrUOp>(
1095  op, resultType, adaptor.getValue(), amount, false);
1096  return success();
1097  }
1098 };
1099 
1100 struct AShrOpConversion : public OpConversionPattern<AShrOp> {
1101  using OpConversionPattern::OpConversionPattern;
1102 
1103  LogicalResult
1104  matchAndRewrite(AShrOp op, OpAdaptor adaptor,
1105  ConversionPatternRewriter &rewriter) const override {
1106  Type resultType = typeConverter->convertType(op.getResult().getType());
1107 
1108  // Comb shift operations require the same bit-width for value and amount
1109  Value amount =
1110  adjustIntegerWidth(rewriter, adaptor.getAmount(),
1111  resultType.getIntOrFloatBitWidth(), op->getLoc());
1112  rewriter.replaceOpWithNewOp<comb::ShrSOp>(
1113  op, resultType, adaptor.getValue(), amount, false);
1114  return success();
1115  }
1116 };
1117 
1118 struct ReadOpConversion : public OpConversionPattern<ReadOp> {
1119  using OpConversionPattern::OpConversionPattern;
1120 
1121  LogicalResult
1122  matchAndRewrite(ReadOp op, OpAdaptor adaptor,
1123  ConversionPatternRewriter &rewriter) const override {
1124  rewriter.replaceOpWithNewOp<llhd::PrbOp>(op, adaptor.getInput());
1125  return success();
1126  }
1127 };
1128 
1129 struct AssignedVariableOpConversion
1130  : public OpConversionPattern<AssignedVariableOp> {
1131  using OpConversionPattern::OpConversionPattern;
1132 
1133  LogicalResult
1134  matchAndRewrite(AssignedVariableOp op, OpAdaptor adaptor,
1135  ConversionPatternRewriter &rewriter) const override {
1136  rewriter.replaceOpWithNewOp<hw::WireOp>(op, adaptor.getInput(),
1137  adaptor.getNameAttr());
1138  return success();
1139  }
1140 };
1141 
1142 template <typename OpTy, unsigned DeltaTime, unsigned EpsilonTime>
1143 struct AssignOpConversion : public OpConversionPattern<OpTy> {
1145  using OpAdaptor = typename OpTy::Adaptor;
1146 
1147  LogicalResult
1148  matchAndRewrite(OpTy op, OpAdaptor adaptor,
1149  ConversionPatternRewriter &rewriter) const override {
1150  // TODO: When we support delay control in Moore dialect, we need to update
1151  // this conversion.
1152  auto timeAttr = llhd::TimeAttr::get(
1153  op->getContext(), 0U, llvm::StringRef("ns"), DeltaTime, EpsilonTime);
1154  auto time = rewriter.create<llhd::ConstantTimeOp>(op->getLoc(), timeAttr);
1155  rewriter.replaceOpWithNewOp<llhd::DrvOp>(op, adaptor.getDst(),
1156  adaptor.getSrc(), time, Value{});
1157  return success();
1158  }
1159 };
1160 
1161 struct ConditionalOpConversion : public OpConversionPattern<ConditionalOp> {
1162  using OpConversionPattern::OpConversionPattern;
1163 
1164  LogicalResult
1165  matchAndRewrite(ConditionalOp op, OpAdaptor adaptor,
1166  ConversionPatternRewriter &rewriter) const override {
1167  // TODO: This lowering is only correct if the condition is two-valued. If
1168  // the condition is X or Z, both branches of the conditional must be
1169  // evaluated and merged with the appropriate lookup table. See documentation
1170  // for `ConditionalOp`.
1171  auto type = typeConverter->convertType(op.getType());
1172 
1173  auto hasNoWriteEffect = [](Region &region) {
1174  auto result = region.walk([](Operation *operation) {
1175  if (auto memOp = dyn_cast<MemoryEffectOpInterface>(operation))
1176  if (!memOp.hasEffect<MemoryEffects::Write>() &&
1177  !memOp.hasEffect<MemoryEffects::Free>())
1178  return WalkResult::advance();
1179 
1180  return WalkResult::interrupt();
1181  });
1182  return !result.wasInterrupted();
1183  };
1184 
1185  if (hasNoWriteEffect(op.getTrueRegion()) &&
1186  hasNoWriteEffect(op.getFalseRegion())) {
1187  Operation *trueTerm = op.getTrueRegion().front().getTerminator();
1188  Operation *falseTerm = op.getFalseRegion().front().getTerminator();
1189 
1190  rewriter.inlineBlockBefore(&op.getTrueRegion().front(), op);
1191  rewriter.inlineBlockBefore(&op.getFalseRegion().front(), op);
1192 
1193  Value convTrueVal = typeConverter->materializeTargetConversion(
1194  rewriter, op.getLoc(), type, trueTerm->getOperand(0));
1195  Value convFalseVal = typeConverter->materializeTargetConversion(
1196  rewriter, op.getLoc(), type, falseTerm->getOperand(0));
1197 
1198  rewriter.eraseOp(trueTerm);
1199  rewriter.eraseOp(falseTerm);
1200 
1201  rewriter.replaceOpWithNewOp<comb::MuxOp>(op, adaptor.getCondition(),
1202  convTrueVal, convFalseVal);
1203  return success();
1204  }
1205 
1206  auto ifOp =
1207  rewriter.create<scf::IfOp>(op.getLoc(), type, adaptor.getCondition());
1208  rewriter.inlineRegionBefore(op.getTrueRegion(), ifOp.getThenRegion(),
1209  ifOp.getThenRegion().end());
1210  rewriter.inlineRegionBefore(op.getFalseRegion(), ifOp.getElseRegion(),
1211  ifOp.getElseRegion().end());
1212  rewriter.replaceOp(op, ifOp);
1213  return success();
1214  }
1215 };
1216 
1217 struct YieldOpConversion : public OpConversionPattern<YieldOp> {
1218  using OpConversionPattern::OpConversionPattern;
1219 
1220  LogicalResult
1221  matchAndRewrite(YieldOp op, OpAdaptor adaptor,
1222  ConversionPatternRewriter &rewriter) const override {
1223  rewriter.replaceOpWithNewOp<scf::YieldOp>(op, adaptor.getResult());
1224  return success();
1225  }
1226 };
1227 
1228 template <typename SourceOp>
1229 struct InPlaceOpConversion : public OpConversionPattern<SourceOp> {
1231  using OpAdaptor = typename SourceOp::Adaptor;
1232 
1233  LogicalResult
1234  matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1235  ConversionPatternRewriter &rewriter) const override {
1236  rewriter.modifyOpInPlace(op,
1237  [&]() { op->setOperands(adaptor.getOperands()); });
1238  return success();
1239  }
1240 };
1241 
1242 template <typename MooreOpTy, typename VerifOpTy>
1243 struct AssertLikeOpConversion : public OpConversionPattern<MooreOpTy> {
1245  using OpAdaptor = typename MooreOpTy::Adaptor;
1246 
1247  LogicalResult
1248  matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
1249  ConversionPatternRewriter &rewriter) const override {
1250  StringAttr label =
1251  op.getLabel().has_value()
1252  ? StringAttr::get(op->getContext(), op.getLabel().value())
1253  : StringAttr::get(op->getContext());
1254  rewriter.replaceOpWithNewOp<VerifOpTy>(op, adaptor.getCond(), mlir::Value(),
1255  label);
1256  return success();
1257  }
1258 };
1259 
1260 //===----------------------------------------------------------------------===//
1261 // Format String Conversion
1262 //===----------------------------------------------------------------------===//
1263 
1264 struct FormatLiteralOpConversion : public OpConversionPattern<FormatLiteralOp> {
1265  using OpConversionPattern::OpConversionPattern;
1266 
1267  LogicalResult
1268  matchAndRewrite(FormatLiteralOp op, OpAdaptor adaptor,
1269  ConversionPatternRewriter &rewriter) const override {
1270  rewriter.replaceOpWithNewOp<sim::FormatLitOp>(op, adaptor.getLiteral());
1271  return success();
1272  }
1273 };
1274 
1275 struct FormatConcatOpConversion : public OpConversionPattern<FormatConcatOp> {
1276  using OpConversionPattern::OpConversionPattern;
1277 
1278  LogicalResult
1279  matchAndRewrite(FormatConcatOp op, OpAdaptor adaptor,
1280  ConversionPatternRewriter &rewriter) const override {
1281  rewriter.replaceOpWithNewOp<sim::FormatStringConcatOp>(op,
1282  adaptor.getInputs());
1283  return success();
1284  }
1285 };
1286 
1287 struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
1288  using OpConversionPattern::OpConversionPattern;
1289 
1290  LogicalResult
1291  matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
1292  ConversionPatternRewriter &rewriter) const override {
1293  // TODO: These should honor the width, alignment, and padding.
1294  switch (op.getFormat()) {
1295  case IntFormat::Decimal:
1296  rewriter.replaceOpWithNewOp<sim::FormatDecOp>(op, adaptor.getValue());
1297  return success();
1298  case IntFormat::Binary:
1299  rewriter.replaceOpWithNewOp<sim::FormatBinOp>(op, adaptor.getValue());
1300  return success();
1301  case IntFormat::HexLower:
1302  case IntFormat::HexUpper:
1303  rewriter.replaceOpWithNewOp<sim::FormatHexOp>(op, adaptor.getValue());
1304  return success();
1305  default:
1306  return rewriter.notifyMatchFailure(op, "unsupported int format");
1307  }
1308  }
1309 };
1310 
1311 struct DisplayBIOpConversion : public OpConversionPattern<DisplayBIOp> {
1312  using OpConversionPattern::OpConversionPattern;
1313 
1314  LogicalResult
1315  matchAndRewrite(DisplayBIOp op, OpAdaptor adaptor,
1316  ConversionPatternRewriter &rewriter) const override {
1317  rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
1318  op, adaptor.getMessage());
1319  return success();
1320  }
1321 };
1322 
1323 } // namespace
1324 
1325 //===----------------------------------------------------------------------===//
1326 // Conversion Infrastructure
1327 //===----------------------------------------------------------------------===//
1328 
1329 static void populateLegality(ConversionTarget &target,
1330  const TypeConverter &converter) {
1331  target.addIllegalDialect<MooreDialect>();
1332  target.addLegalDialect<comb::CombDialect>();
1333  target.addLegalDialect<hw::HWDialect>();
1334  target.addLegalDialect<llhd::LLHDDialect>();
1335  target.addLegalDialect<mlir::BuiltinDialect>();
1336  target.addLegalDialect<sim::SimDialect>();
1337  target.addLegalDialect<verif::VerifDialect>();
1338 
1339  target.addLegalOp<debug::ScopeOp>();
1340 
1341  target.addDynamicallyLegalOp<
1342  cf::CondBranchOp, cf::BranchOp, scf::IfOp, scf::YieldOp, func::CallOp,
1343  func::ReturnOp, UnrealizedConversionCastOp, hw::OutputOp, hw::InstanceOp,
1344  debug::ArrayOp, debug::StructOp, debug::VariableOp>(
1345  [&](Operation *op) { return converter.isLegal(op); });
1346 
1347  target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
1348  return converter.isSignatureLegal(op.getFunctionType()) &&
1349  converter.isLegal(&op.getFunctionBody());
1350  });
1351 
1352  target.addDynamicallyLegalOp<hw::HWModuleOp>([&](hw::HWModuleOp op) {
1353  return converter.isSignatureLegal(op.getModuleType().getFuncType()) &&
1354  converter.isLegal(&op.getBody());
1355  });
1356 }
1357 
1358 static void populateTypeConversion(TypeConverter &typeConverter) {
1359  typeConverter.addConversion([&](IntType type) {
1360  return IntegerType::get(type.getContext(), type.getWidth());
1361  });
1362 
1363  typeConverter.addConversion([&](FormatStringType type) {
1364  return sim::FormatStringType::get(type.getContext());
1365  });
1366 
1367  typeConverter.addConversion([&](ArrayType type) -> std::optional<Type> {
1368  if (auto elementType = typeConverter.convertType(type.getElementType()))
1369  return hw::ArrayType::get(elementType, type.getSize());
1370  return {};
1371  });
1372 
1373  typeConverter.addConversion([&](StructType type) -> std::optional<Type> {
1374  SmallVector<hw::StructType::FieldInfo> fields;
1375  for (auto field : type.getMembers()) {
1376  hw::StructType::FieldInfo info;
1377  info.type = typeConverter.convertType(field.type);
1378  if (!info.type)
1379  return {};
1380  info.name = field.name;
1381  fields.push_back(info);
1382  }
1383  return hw::StructType::get(type.getContext(), fields);
1384  });
1385 
1386  // FIXME: Mapping unpacked struct type to struct type in hw dialect may be a
1387  // plain solution. The packed and unpacked data structures have some
1388  // differences though they look similarily. The packed data structure is
1389  // contiguous in memory but another is opposite. The differences will affect
1390  // data layout and granularity of event tracking in simulation.
1391  typeConverter.addConversion(
1392  [&](UnpackedStructType type) -> std::optional<Type> {
1393  SmallVector<hw::StructType::FieldInfo> fields;
1394  for (auto field : type.getMembers()) {
1395  hw::StructType::FieldInfo info;
1396  info.type = typeConverter.convertType(field.type);
1397  if (!info.type)
1398  return {};
1399  info.name = field.name;
1400  fields.push_back(info);
1401  }
1402  return hw::StructType::get(type.getContext(), fields);
1403  });
1404 
1405  typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
1406  if (auto innerType = typeConverter.convertType(type.getNestedType()))
1407  return hw::InOutType::get(innerType);
1408  return {};
1409  });
1410 
1411  // Valid target types.
1412  typeConverter.addConversion([](IntegerType type) { return type; });
1413  typeConverter.addConversion([](debug::ArrayType type) { return type; });
1414  typeConverter.addConversion([](debug::ScopeType type) { return type; });
1415  typeConverter.addConversion([](debug::StructType type) { return type; });
1416 
1417  typeConverter.addConversion([&](hw::InOutType type) -> std::optional<Type> {
1418  if (auto innerType = typeConverter.convertType(type.getElementType()))
1419  return hw::InOutType::get(innerType);
1420  return {};
1421  });
1422 
1423  typeConverter.addConversion([&](hw::ArrayType type) -> std::optional<Type> {
1424  if (auto elementType = typeConverter.convertType(type.getElementType()))
1425  return hw::ArrayType::get(elementType, type.getNumElements());
1426  return {};
1427  });
1428 
1429  typeConverter.addConversion([&](hw::StructType type) -> std::optional<Type> {
1430  SmallVector<hw::StructType::FieldInfo> fields;
1431  for (auto field : type.getElements()) {
1432  hw::StructType::FieldInfo info;
1433  info.type = typeConverter.convertType(field.type);
1434  if (!info.type)
1435  return {};
1436  info.name = field.name;
1437  fields.push_back(info);
1438  }
1439  return hw::StructType::get(type.getContext(), fields);
1440  });
1441 
1442  typeConverter.addTargetMaterialization(
1443  [&](mlir::OpBuilder &builder, mlir::Type resultType,
1444  mlir::ValueRange inputs,
1445  mlir::Location loc) -> std::optional<mlir::Value> {
1446  if (inputs.size() != 1 || !inputs[0])
1447  return std::nullopt;
1448  return builder
1449  .create<UnrealizedConversionCastOp>(loc, resultType, inputs[0])
1450  .getResult(0);
1451  });
1452 
1453  typeConverter.addSourceMaterialization(
1454  [&](mlir::OpBuilder &builder, mlir::Type resultType,
1455  mlir::ValueRange inputs,
1456  mlir::Location loc) -> std::optional<mlir::Value> {
1457  if (inputs.size() != 1)
1458  return std::nullopt;
1459  return builder
1460  .create<UnrealizedConversionCastOp>(loc, resultType, inputs[0])
1461  ->getResult(0);
1462  });
1463 }
1464 
1465 static void populateOpConversion(RewritePatternSet &patterns,
1466  TypeConverter &typeConverter) {
1467  auto *context = patterns.getContext();
1468  // clang-format off
1469  patterns.add<
1470  // Patterns of declaration operations.
1471  VariableOpConversion,
1472  NetOpConversion,
1473 
1474  // Patterns of miscellaneous operations.
1475  ConstantOpConv, ConcatOpConversion, ReplicateOpConversion,
1476  ExtractOpConversion, DynExtractOpConversion, DynExtractRefOpConversion,
1477  ConversionOpConversion, ReadOpConversion,
1478  StructExtractOpConversion, StructExtractRefOpConversion,
1479  ExtractRefOpConversion, StructCreateOpConversion, ConditionalOpConversion,
1480  YieldOpConversion, OutputOpConversion,
1481 
1482  // Patterns of unary operations.
1483  ReduceAndOpConversion, ReduceOrOpConversion, ReduceXorOpConversion,
1484  BoolCastOpConversion, NotOpConversion, NegOpConversion,
1485 
1486  // Patterns of binary operations.
1487  BinaryOpConversion<AddOp, comb::AddOp>,
1488  BinaryOpConversion<SubOp, comb::SubOp>,
1489  BinaryOpConversion<MulOp, comb::MulOp>,
1490  BinaryOpConversion<DivUOp, comb::DivUOp>,
1491  BinaryOpConversion<DivSOp, comb::DivSOp>,
1492  BinaryOpConversion<ModUOp, comb::ModUOp>,
1493  BinaryOpConversion<ModSOp, comb::ModSOp>,
1494  BinaryOpConversion<AndOp, comb::AndOp>,
1495  BinaryOpConversion<OrOp, comb::OrOp>,
1496  BinaryOpConversion<XorOp, comb::XorOp>,
1497 
1498  // Patterns of relational operations.
1499  ICmpOpConversion<UltOp, ICmpPredicate::ult>,
1500  ICmpOpConversion<SltOp, ICmpPredicate::slt>,
1501  ICmpOpConversion<UleOp, ICmpPredicate::ule>,
1502  ICmpOpConversion<SleOp, ICmpPredicate::sle>,
1503  ICmpOpConversion<UgtOp, ICmpPredicate::ugt>,
1504  ICmpOpConversion<SgtOp, ICmpPredicate::sgt>,
1505  ICmpOpConversion<UgeOp, ICmpPredicate::uge>,
1506  ICmpOpConversion<SgeOp, ICmpPredicate::sge>,
1507  ICmpOpConversion<EqOp, ICmpPredicate::eq>,
1508  ICmpOpConversion<NeOp, ICmpPredicate::ne>,
1509  ICmpOpConversion<CaseEqOp, ICmpPredicate::ceq>,
1510  ICmpOpConversion<CaseNeOp, ICmpPredicate::cne>,
1511  ICmpOpConversion<WildcardEqOp, ICmpPredicate::weq>,
1512  ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
1513  CaseXZEqOpConversion<CaseZEqOp, true>,
1514  CaseXZEqOpConversion<CaseXZEqOp, false>,
1515 
1516  // Patterns of structural operations.
1517  SVModuleOpConversion, InstanceOpConversion, ProcedureOpConversion, WaitEventOpConversion,
1518 
1519  // Patterns of shifting operations.
1520  ShrOpConversion, ShlOpConversion, AShrOpConversion,
1521 
1522  // Patterns of assignment operations.
1523  AssignOpConversion<ContinuousAssignOp, 0, 1>,
1524  AssignOpConversion<BlockingAssignOp, 0, 1>,
1525  AssignOpConversion<NonBlockingAssignOp, 1, 0>,
1526  AssignedVariableOpConversion,
1527 
1528  // Patterns of branch operations.
1529  CondBranchOpConversion, BranchOpConversion,
1530 
1531  // Patterns of other operations outside Moore dialect.
1532  HWInstanceOpConversion, ReturnOpConversion,
1533  CallOpConversion, UnrealizedConversionCastConversion,
1534  InPlaceOpConversion<debug::ArrayOp>,
1535  InPlaceOpConversion<debug::StructOp>,
1536  InPlaceOpConversion<debug::VariableOp>,
1537 
1538  // Patterns of assert-like operations
1539  AssertLikeOpConversion<AssertOp, verif::AssertOp>,
1540  AssertLikeOpConversion<AssumeOp, verif::AssumeOp>,
1541  AssertLikeOpConversion<CoverOp, verif::CoverOp>,
1542 
1543  // Format strings.
1544  FormatLiteralOpConversion,
1545  FormatConcatOpConversion,
1546  FormatIntOpConversion,
1547  DisplayBIOpConversion
1548  >(typeConverter, context);
1549  // clang-format on
1550 
1551  mlir::populateAnyFunctionOpInterfaceTypeConversionPattern(patterns,
1552  typeConverter);
1554  hw::HWModuleOp::getOperationName(), patterns, typeConverter);
1555 }
1556 
1557 //===----------------------------------------------------------------------===//
1558 // Moore to Core Conversion Pass
1559 //===----------------------------------------------------------------------===//
1560 
1561 namespace {
1562 struct MooreToCorePass
1563  : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
1564  void runOnOperation() override;
1565 };
1566 } // namespace
1567 
1568 /// Create a Moore to core dialects conversion pass.
1569 std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
1570  return std::make_unique<MooreToCorePass>();
1571 }
1572 
1573 /// This is the main entrypoint for the Moore to Core conversion pass.
1574 void MooreToCorePass::runOnOperation() {
1575  MLIRContext &context = getContext();
1576  ModuleOp module = getOperation();
1577 
1578  IRRewriter rewriter(module);
1579  (void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
1580 
1581  ConversionTarget target(context);
1582  TypeConverter typeConverter;
1583  RewritePatternSet patterns(&context);
1584  populateTypeConversion(typeConverter);
1585  populateLegality(target, typeConverter);
1586  populateOpConversion(patterns, typeConverter);
1587 
1588  if (failed(applyFullConversion(module, target, std::move(patterns))))
1589  signalPassFailure();
1590 }
MlirType elementType
Definition: CHIRRTL.cpp:29
int32_t width
Definition: FIRRTL.cpp:36
@ Output
Definition: HW.h:35
@ InOut
Definition: HW.h:35
static void populateOpConversion(RewritePatternSet &patterns, TypeConverter &typeConverter)
static void populateLegality(ConversionTarget &target, const TypeConverter &converter)
static void populateTypeConversion(TypeConverter &typeConverter)
def create(low_bit, result_type, input=None)
Definition: comb.py:187
def create(data_type, value)
Definition: hw.py:393
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
mlir::Type innerType(mlir::Type type)
Definition: ESITypes.cpp:184
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
void populateHWModuleLikeTypeConversionPattern(StringRef moduleLikeOpName, RewritePatternSet &patterns, TypeConverter &converter)
circt::hw::InOutType InOutType
Definition: SVTypes.h:25
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.