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