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