CIRCT 20.0.0git
Loading...
Searching...
No Matches
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
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
32namespace circt {
33#define GEN_PASS_DEF_CONVERTMOORETOCORE
34#include "circt/Conversion/Passes.h.inc"
35} // namespace circt
36
37using namespace mlir;
38using namespace circt;
39using namespace moore;
40
41using comb::ICmpPredicate;
42using llvm::SmallDenseSet;
43
44namespace {
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.
50static 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.
75static 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
115struct 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
144struct 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
155struct 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
179static 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
219struct 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
300struct 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
484struct 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
516struct 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
558struct 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
573struct 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
601struct 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
611struct 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
624struct 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
659struct 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
705struct 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
743struct 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
788struct 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
801struct 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
813struct 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
826struct 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
840struct 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
854struct 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
865struct 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
881struct 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
895struct 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
909template <typename SourceOp, typename TargetOp>
910struct 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
923template <typename SourceOp, ICmpPredicate pred>
924struct 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
940template <typename SourceOp, bool withoutX>
941struct 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
988struct 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
1012struct 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
1024struct 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
1042struct 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
1060struct 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
1080struct 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
1091struct 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
1104struct 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
1116struct 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
1131struct 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
1155struct 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
1173struct 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
1191struct 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
1226struct 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
1269struct 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
1287struct 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
1298struct 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
1311template <typename OpTy, unsigned DeltaTime, unsigned EpsilonTime>
1312struct 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
1330struct 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
1386struct 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
1397template <typename SourceOp>
1398struct 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
1411template <typename MooreOpTy, typename VerifOpTy>
1412struct 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
1433struct 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
1444struct 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
1456struct 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
1480struct 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
1498static 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
1527static 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
1642static 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);
1739 hw::populateHWModuleLikeTypeConversionPattern(
1740 hw::HWModuleOp::getOperationName(), patterns, typeConverter);
1741}
1742
1743//===----------------------------------------------------------------------===//
1744// Moore to Core Conversion Pass
1745//===----------------------------------------------------------------------===//
1746
1747namespace {
1748struct MooreToCorePass
1749 : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
1750 void runOnOperation() override;
1751};
1752} // namespace
1753
1754/// Create a Moore to core dialects conversion pass.
1755std::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.
1760void 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
static void populateOpConversion(RewritePatternSet &patterns, TypeConverter &typeConverter)
static void populateLegality(ConversionTarget &target, const TypeConverter &converter)
static void populateTypeConversion(TypeConverter &typeConverter)
create(low_bit, result_type, input=None)
Definition comb.py:187
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
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< OperationPass< ModuleOp > > createConvertMooreToCorePass()
Create an Moore to Comb/HW/LLHD conversion pass.
This holds a decoded list of input/inout and output ports for a module or instance.
This holds the name, type, direction of a module's ports.