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