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