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 (isa<hw::ArrayType>(
839 cast<hw::InOutType>(resultType).getElementType())) {
840 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
841 op, resultType, adaptor.getInput(), lowBit);
842 return success();
843 }
844
845 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
846 lowBit);
847 return success();
848 }
849
850 return failure();
851 }
852};
853
854struct DynExtractOpConversion : public OpConversionPattern<DynExtractOp> {
855 using OpConversionPattern::OpConversionPattern;
856
857 LogicalResult
858 matchAndRewrite(DynExtractOp op, OpAdaptor adaptor,
859 ConversionPatternRewriter &rewriter) const override {
860 Type resultType = typeConverter->convertType(op.getResult().getType());
861 Type inputType = adaptor.getInput().getType();
862
863 if (auto intType = dyn_cast<IntegerType>(inputType)) {
864 Value amount = adjustIntegerWidth(rewriter, adaptor.getLowBit(),
865 intType.getWidth(), op->getLoc());
866 Value value = comb::ShrUOp::create(rewriter, op->getLoc(),
867 adaptor.getInput(), amount);
868
869 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, value, 0);
870 return success();
871 }
872
873 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
874 unsigned idxWidth = llvm::Log2_64_Ceil(arrType.getNumElements());
875 Value idx = adjustIntegerWidth(rewriter, adaptor.getLowBit(), idxWidth,
876 op->getLoc());
877
878 if (isa<hw::ArrayType>(resultType)) {
879 rewriter.replaceOpWithNewOp<hw::ArraySliceOp>(op, resultType,
880 adaptor.getInput(), idx);
881 return success();
882 }
883
884 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(), idx);
885 return success();
886 }
887
888 return failure();
889 }
890};
891
892struct DynExtractRefOpConversion : public OpConversionPattern<DynExtractRefOp> {
893 using OpConversionPattern::OpConversionPattern;
894
895 LogicalResult
896 matchAndRewrite(DynExtractRefOp op, OpAdaptor adaptor,
897 ConversionPatternRewriter &rewriter) const override {
898 // TODO: properly handle out-of-bounds accesses
899 Type resultType = typeConverter->convertType(op.getResult().getType());
900 Type inputType =
901 cast<hw::InOutType>(adaptor.getInput().getType()).getElementType();
902
903 if (auto intType = dyn_cast<IntegerType>(inputType)) {
904 int64_t width = hw::getBitWidth(inputType);
905 if (width == -1)
906 return failure();
907
908 Value amount =
909 adjustIntegerWidth(rewriter, adaptor.getLowBit(),
910 llvm::Log2_64_Ceil(width), op->getLoc());
911 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
912 op, resultType, adaptor.getInput(), amount);
913 return success();
914 }
915
916 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
917 Value idx = adjustIntegerWidth(
918 rewriter, adaptor.getLowBit(),
919 llvm::Log2_64_Ceil(arrType.getNumElements()), op->getLoc());
920
921 if (isa<hw::ArrayType>(
922 cast<hw::InOutType>(resultType).getElementType())) {
923 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
924 op, resultType, adaptor.getInput(), idx);
925 return success();
926 }
927
928 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
929 idx);
930 return success();
931 }
932
933 return failure();
934 }
935};
936
937struct ArrayCreateOpConversion : public OpConversionPattern<ArrayCreateOp> {
938 using OpConversionPattern::OpConversionPattern;
939
940 LogicalResult
941 matchAndRewrite(ArrayCreateOp op, OpAdaptor adaptor,
942 ConversionPatternRewriter &rewriter) const override {
943 Type resultType = typeConverter->convertType(op.getResult().getType());
944 rewriter.replaceOpWithNewOp<hw::ArrayCreateOp>(op, resultType,
945 adaptor.getElements());
946 return success();
947 }
948};
949
950struct StructCreateOpConversion : public OpConversionPattern<StructCreateOp> {
951 using OpConversionPattern::OpConversionPattern;
952
953 LogicalResult
954 matchAndRewrite(StructCreateOp op, OpAdaptor adaptor,
955 ConversionPatternRewriter &rewriter) const override {
956 Type resultType = typeConverter->convertType(op.getResult().getType());
957 rewriter.replaceOpWithNewOp<hw::StructCreateOp>(op, resultType,
958 adaptor.getFields());
959 return success();
960 }
961};
962
963struct StructExtractOpConversion : public OpConversionPattern<StructExtractOp> {
964 using OpConversionPattern::OpConversionPattern;
965
966 LogicalResult
967 matchAndRewrite(StructExtractOp op, OpAdaptor adaptor,
968 ConversionPatternRewriter &rewriter) const override {
969 rewriter.replaceOpWithNewOp<hw::StructExtractOp>(
970 op, adaptor.getInput(), adaptor.getFieldNameAttr());
971 return success();
972 }
973};
974
975struct StructExtractRefOpConversion
976 : public OpConversionPattern<StructExtractRefOp> {
977 using OpConversionPattern::OpConversionPattern;
978
979 LogicalResult
980 matchAndRewrite(StructExtractRefOp op, OpAdaptor adaptor,
981 ConversionPatternRewriter &rewriter) const override {
982 rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
983 op, adaptor.getInput(), adaptor.getFieldNameAttr());
984 return success();
985 }
986};
987
988struct ReduceAndOpConversion : public OpConversionPattern<ReduceAndOp> {
989 using OpConversionPattern::OpConversionPattern;
990 LogicalResult
991 matchAndRewrite(ReduceAndOp op, OpAdaptor adaptor,
992 ConversionPatternRewriter &rewriter) const override {
993 Type resultType = typeConverter->convertType(op.getInput().getType());
994 Value max = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, -1);
995
996 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::eq,
997 adaptor.getInput(), max);
998 return success();
999 }
1000};
1001
1002struct ReduceOrOpConversion : public OpConversionPattern<ReduceOrOp> {
1003 using OpConversionPattern::OpConversionPattern;
1004 LogicalResult
1005 matchAndRewrite(ReduceOrOp op, OpAdaptor adaptor,
1006 ConversionPatternRewriter &rewriter) const override {
1007 Type resultType = typeConverter->convertType(op.getInput().getType());
1008 Value zero = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1009
1010 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1011 adaptor.getInput(), zero);
1012 return success();
1013 }
1014};
1015
1016struct ReduceXorOpConversion : public OpConversionPattern<ReduceXorOp> {
1017 using OpConversionPattern::OpConversionPattern;
1018 LogicalResult
1019 matchAndRewrite(ReduceXorOp op, OpAdaptor adaptor,
1020 ConversionPatternRewriter &rewriter) const override {
1021
1022 rewriter.replaceOpWithNewOp<comb::ParityOp>(op, adaptor.getInput());
1023 return success();
1024 }
1025};
1026
1027struct BoolCastOpConversion : public OpConversionPattern<BoolCastOp> {
1028 using OpConversionPattern::OpConversionPattern;
1029 LogicalResult
1030 matchAndRewrite(BoolCastOp op, OpAdaptor adaptor,
1031 ConversionPatternRewriter &rewriter) const override {
1032 Type resultType = typeConverter->convertType(op.getInput().getType());
1033 if (isa_and_nonnull<IntegerType>(resultType)) {
1034 Value zero =
1035 hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1036 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1037 adaptor.getInput(), zero);
1038 return success();
1039 }
1040 return failure();
1041 }
1042};
1043
1044struct NotOpConversion : public OpConversionPattern<NotOp> {
1045 using OpConversionPattern::OpConversionPattern;
1046 LogicalResult
1047 matchAndRewrite(NotOp op, OpAdaptor adaptor,
1048 ConversionPatternRewriter &rewriter) const override {
1049 Type resultType =
1050 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1051 Value max = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, -1);
1052
1053 rewriter.replaceOpWithNewOp<comb::XorOp>(op, adaptor.getInput(), max);
1054 return success();
1055 }
1056};
1057
1058struct NegOpConversion : public OpConversionPattern<NegOp> {
1059 using OpConversionPattern::OpConversionPattern;
1060 LogicalResult
1061 matchAndRewrite(NegOp op, OpAdaptor adaptor,
1062 ConversionPatternRewriter &rewriter) const override {
1063 Type resultType =
1064 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1065 Value zero = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, 0);
1066
1067 rewriter.replaceOpWithNewOp<comb::SubOp>(op, zero, adaptor.getInput());
1068 return success();
1069 }
1070};
1071
1072template <typename SourceOp, typename TargetOp>
1073struct BinaryOpConversion : public OpConversionPattern<SourceOp> {
1075 using OpAdaptor = typename SourceOp::Adaptor;
1076
1077 LogicalResult
1078 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1079 ConversionPatternRewriter &rewriter) const override {
1080 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1081 adaptor.getRhs(), false);
1082 return success();
1083 }
1084};
1085
1086template <typename SourceOp, ICmpPredicate pred>
1087struct ICmpOpConversion : public OpConversionPattern<SourceOp> {
1089 using OpAdaptor = typename SourceOp::Adaptor;
1090
1091 LogicalResult
1092 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1093 ConversionPatternRewriter &rewriter) const override {
1094 Type resultType =
1095 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1096
1097 rewriter.replaceOpWithNewOp<comb::ICmpOp>(
1098 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1099 return success();
1100 }
1101};
1102
1103template <typename SourceOp, bool withoutX>
1104struct CaseXZEqOpConversion : public OpConversionPattern<SourceOp> {
1106 using OpAdaptor = typename SourceOp::Adaptor;
1107
1108 LogicalResult
1109 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1110 ConversionPatternRewriter &rewriter) const override {
1111 // Check each operand if it is a known constant and extract the X and/or Z
1112 // bits to be ignored.
1113 // TODO: Once the core dialects support four-valued integers, we will have
1114 // to create ops that extract X and Z bits from the operands, since we also
1115 // have to do the right casez/casex comparison on non-constant inputs.
1116 unsigned bitWidth = op.getLhs().getType().getWidth();
1117 auto ignoredBits = APInt::getZero(bitWidth);
1118 auto detectIgnoredBits = [&](Value value) {
1119 auto constOp = value.getDefiningOp<ConstantOp>();
1120 if (!constOp)
1121 return;
1122 auto constValue = constOp.getValue();
1123 if (withoutX)
1124 ignoredBits |= constValue.getZBits();
1125 else
1126 ignoredBits |= constValue.getUnknownBits();
1127 };
1128 detectIgnoredBits(op.getLhs());
1129 detectIgnoredBits(op.getRhs());
1130
1131 // If we have detected any bits to be ignored, mask them in the operands for
1132 // the comparison.
1133 Value lhs = adaptor.getLhs();
1134 Value rhs = adaptor.getRhs();
1135 if (!ignoredBits.isZero()) {
1136 ignoredBits.flipAllBits();
1137 auto maskOp = hw::ConstantOp::create(rewriter, op.getLoc(), ignoredBits);
1138 lhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), lhs, maskOp);
1139 rhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), rhs, maskOp);
1140 }
1141
1142 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, ICmpPredicate::ceq, lhs, rhs);
1143 return success();
1144 }
1145};
1146
1147//===----------------------------------------------------------------------===//
1148// Conversions
1149//===----------------------------------------------------------------------===//
1150
1151struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
1152 using OpConversionPattern::OpConversionPattern;
1153
1154 LogicalResult
1155 matchAndRewrite(ConversionOp op, OpAdaptor adaptor,
1156 ConversionPatternRewriter &rewriter) const override {
1157 Location loc = op.getLoc();
1158 Type resultType = typeConverter->convertType(op.getResult().getType());
1159 int64_t inputBw = hw::getBitWidth(adaptor.getInput().getType());
1160 int64_t resultBw = hw::getBitWidth(resultType);
1161 if (inputBw == -1 || resultBw == -1)
1162 return failure();
1163
1164 Value input = rewriter.createOrFold<hw::BitcastOp>(
1165 loc, rewriter.getIntegerType(inputBw), adaptor.getInput());
1166 Value amount = adjustIntegerWidth(rewriter, input, resultBw, loc);
1167
1168 Value result =
1169 rewriter.createOrFold<hw::BitcastOp>(loc, resultType, amount);
1170 rewriter.replaceOp(op, result);
1171 return success();
1172 }
1173};
1174
1175template <typename SourceOp>
1176struct BitcastConversion : public OpConversionPattern<SourceOp> {
1178 using OpAdaptor = typename SourceOp::Adaptor;
1179 using ConversionPattern::typeConverter;
1180
1181 LogicalResult
1182 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1183 ConversionPatternRewriter &rewriter) const override {
1184 auto type = typeConverter->convertType(op.getResult().getType());
1185 if (type == adaptor.getInput().getType())
1186 rewriter.replaceOp(op, adaptor.getInput());
1187 else
1188 rewriter.replaceOpWithNewOp<hw::BitcastOp>(op, type, adaptor.getInput());
1189 return success();
1190 }
1191};
1192
1193struct TruncOpConversion : public OpConversionPattern<TruncOp> {
1194 using OpConversionPattern::OpConversionPattern;
1195
1196 LogicalResult
1197 matchAndRewrite(TruncOp op, OpAdaptor adaptor,
1198 ConversionPatternRewriter &rewriter) const override {
1199 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, adaptor.getInput(), 0,
1200 op.getType().getWidth());
1201 return success();
1202 }
1203};
1204
1205struct ZExtOpConversion : public OpConversionPattern<ZExtOp> {
1206 using OpConversionPattern::OpConversionPattern;
1207
1208 LogicalResult
1209 matchAndRewrite(ZExtOp op, OpAdaptor adaptor,
1210 ConversionPatternRewriter &rewriter) const override {
1211 auto targetWidth = op.getType().getWidth();
1212 auto inputWidth = op.getInput().getType().getWidth();
1213
1214 auto zeroExt = hw::ConstantOp::create(
1215 rewriter, op.getLoc(),
1216 rewriter.getIntegerType(targetWidth - inputWidth), 0);
1217
1218 rewriter.replaceOpWithNewOp<comb::ConcatOp>(
1219 op, ValueRange{zeroExt, adaptor.getInput()});
1220 return success();
1221 }
1222};
1223
1224struct SExtOpConversion : public OpConversionPattern<SExtOp> {
1225 using OpConversionPattern::OpConversionPattern;
1226
1227 LogicalResult
1228 matchAndRewrite(SExtOp op, OpAdaptor adaptor,
1229 ConversionPatternRewriter &rewriter) const override {
1230 auto type = typeConverter->convertType(op.getType());
1231 auto value =
1232 comb::createOrFoldSExt(op.getLoc(), adaptor.getInput(), type, rewriter);
1233 rewriter.replaceOp(op, value);
1234 return success();
1235 }
1236};
1237
1238//===----------------------------------------------------------------------===//
1239// Statement Conversion
1240//===----------------------------------------------------------------------===//
1241
1242struct HWInstanceOpConversion : public OpConversionPattern<hw::InstanceOp> {
1243 using OpConversionPattern::OpConversionPattern;
1244
1245 LogicalResult
1246 matchAndRewrite(hw::InstanceOp op, OpAdaptor adaptor,
1247 ConversionPatternRewriter &rewriter) const override {
1248 SmallVector<Type> convResTypes;
1249 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1250 return failure();
1251
1252 rewriter.replaceOpWithNewOp<hw::InstanceOp>(
1253 op, convResTypes, op.getInstanceName(), op.getModuleName(),
1254 adaptor.getOperands(), op.getArgNames(),
1255 op.getResultNames(), /*Parameter*/
1256 rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr);
1257
1258 return success();
1259 }
1260};
1261
1262struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
1263 using OpConversionPattern::OpConversionPattern;
1264
1265 LogicalResult
1266 matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
1267 ConversionPatternRewriter &rewriter) const override {
1268 rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
1269 return success();
1270 }
1271};
1272
1273struct CondBranchOpConversion : public OpConversionPattern<cf::CondBranchOp> {
1274 using OpConversionPattern::OpConversionPattern;
1275
1276 LogicalResult
1277 matchAndRewrite(cf::CondBranchOp op, OpAdaptor adaptor,
1278 ConversionPatternRewriter &rewriter) const override {
1279 rewriter.replaceOpWithNewOp<cf::CondBranchOp>(
1280 op, adaptor.getCondition(), adaptor.getTrueDestOperands(),
1281 adaptor.getFalseDestOperands(), /*branch_weights=*/nullptr,
1282 op.getTrueDest(), op.getFalseDest());
1283 return success();
1284 }
1285};
1286
1287struct BranchOpConversion : public OpConversionPattern<cf::BranchOp> {
1288 using OpConversionPattern::OpConversionPattern;
1289
1290 LogicalResult
1291 matchAndRewrite(cf::BranchOp op, OpAdaptor adaptor,
1292 ConversionPatternRewriter &rewriter) const override {
1293 rewriter.replaceOpWithNewOp<cf::BranchOp>(op, op.getDest(),
1294 adaptor.getDestOperands());
1295 return success();
1296 }
1297};
1298
1299struct CallOpConversion : public OpConversionPattern<func::CallOp> {
1300 using OpConversionPattern::OpConversionPattern;
1301
1302 LogicalResult
1303 matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
1304 ConversionPatternRewriter &rewriter) const override {
1305 SmallVector<Type> convResTypes;
1306 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1307 return failure();
1308 rewriter.replaceOpWithNewOp<func::CallOp>(
1309 op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
1310 return success();
1311 }
1312};
1313
1314struct UnrealizedConversionCastConversion
1315 : public OpConversionPattern<UnrealizedConversionCastOp> {
1316 using OpConversionPattern::OpConversionPattern;
1317
1318 LogicalResult
1319 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
1320 ConversionPatternRewriter &rewriter) const override {
1321 SmallVector<Type> convResTypes;
1322 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1323 return failure();
1324
1325 // Drop the cast if the operand and result types agree after type
1326 // conversion.
1327 if (convResTypes == adaptor.getOperands().getTypes()) {
1328 rewriter.replaceOp(op, adaptor.getOperands());
1329 return success();
1330 }
1331
1332 rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
1333 op, convResTypes, adaptor.getOperands());
1334 return success();
1335 }
1336};
1337
1338struct ShlOpConversion : public OpConversionPattern<ShlOp> {
1339 using OpConversionPattern::OpConversionPattern;
1340
1341 LogicalResult
1342 matchAndRewrite(ShlOp op, OpAdaptor adaptor,
1343 ConversionPatternRewriter &rewriter) const override {
1344 Type resultType = typeConverter->convertType(op.getResult().getType());
1345
1346 // Comb shift operations require the same bit-width for value and amount
1347 Value amount =
1348 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1349 resultType.getIntOrFloatBitWidth(), op->getLoc());
1350 rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
1351 amount, false);
1352 return success();
1353 }
1354};
1355
1356struct ShrOpConversion : public OpConversionPattern<ShrOp> {
1357 using OpConversionPattern::OpConversionPattern;
1358
1359 LogicalResult
1360 matchAndRewrite(ShrOp op, OpAdaptor adaptor,
1361 ConversionPatternRewriter &rewriter) const override {
1362 Type resultType = typeConverter->convertType(op.getResult().getType());
1363
1364 // Comb shift operations require the same bit-width for value and amount
1365 Value amount =
1366 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1367 resultType.getIntOrFloatBitWidth(), op->getLoc());
1368 rewriter.replaceOpWithNewOp<comb::ShrUOp>(
1369 op, resultType, adaptor.getValue(), amount, false);
1370 return success();
1371 }
1372};
1373
1374struct PowUOpConversion : public OpConversionPattern<PowUOp> {
1375 using OpConversionPattern::OpConversionPattern;
1376
1377 LogicalResult
1378 matchAndRewrite(PowUOp op, OpAdaptor adaptor,
1379 ConversionPatternRewriter &rewriter) const override {
1380 Type resultType = typeConverter->convertType(op.getResult().getType());
1381
1382 Location loc = op->getLoc();
1383
1384 Value zeroVal = hw::ConstantOp::create(rewriter, loc, APInt(1, 0));
1385 // zero extend both LHS & RHS to ensure the unsigned integers are
1386 // interpreted correctly when calculating power
1387 auto lhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getLhs());
1388 auto rhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getRhs());
1389
1390 // lower the exponentiation via MLIR's math dialect
1391 auto pow = mlir::math::IPowIOp::create(rewriter, loc, lhs, rhs);
1392
1393 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, pow, 0);
1394 return success();
1395 }
1396};
1397
1398struct PowSOpConversion : public OpConversionPattern<PowSOp> {
1399 using OpConversionPattern::OpConversionPattern;
1400
1401 LogicalResult
1402 matchAndRewrite(PowSOp op, OpAdaptor adaptor,
1403 ConversionPatternRewriter &rewriter) const override {
1404 Type resultType = typeConverter->convertType(op.getResult().getType());
1405
1406 // utilize MLIR math dialect's math.ipowi to handle the exponentiation of
1407 // expression
1408 rewriter.replaceOpWithNewOp<mlir::math::IPowIOp>(
1409 op, resultType, adaptor.getLhs(), adaptor.getRhs());
1410 return success();
1411 }
1412};
1413
1414struct AShrOpConversion : public OpConversionPattern<AShrOp> {
1415 using OpConversionPattern::OpConversionPattern;
1416
1417 LogicalResult
1418 matchAndRewrite(AShrOp op, OpAdaptor adaptor,
1419 ConversionPatternRewriter &rewriter) const override {
1420 Type resultType = typeConverter->convertType(op.getResult().getType());
1421
1422 // Comb shift operations require the same bit-width for value and amount
1423 Value amount =
1424 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1425 resultType.getIntOrFloatBitWidth(), op->getLoc());
1426 rewriter.replaceOpWithNewOp<comb::ShrSOp>(
1427 op, resultType, adaptor.getValue(), amount, false);
1428 return success();
1429 }
1430};
1431
1432struct ReadOpConversion : public OpConversionPattern<ReadOp> {
1433 using OpConversionPattern::OpConversionPattern;
1434
1435 LogicalResult
1436 matchAndRewrite(ReadOp op, OpAdaptor adaptor,
1437 ConversionPatternRewriter &rewriter) const override {
1438 rewriter.replaceOpWithNewOp<llhd::PrbOp>(op, adaptor.getInput());
1439 return success();
1440 }
1441};
1442
1443struct AssignedVariableOpConversion
1444 : public OpConversionPattern<AssignedVariableOp> {
1445 using OpConversionPattern::OpConversionPattern;
1446
1447 LogicalResult
1448 matchAndRewrite(AssignedVariableOp op, OpAdaptor adaptor,
1449 ConversionPatternRewriter &rewriter) const override {
1450 rewriter.replaceOpWithNewOp<hw::WireOp>(op, adaptor.getInput(),
1451 adaptor.getNameAttr());
1452 return success();
1453 }
1454};
1455
1456template <typename OpTy, unsigned DeltaTime, unsigned EpsilonTime>
1457struct AssignOpConversion : public OpConversionPattern<OpTy> {
1459 using OpAdaptor = typename OpTy::Adaptor;
1460
1461 LogicalResult
1462 matchAndRewrite(OpTy op, OpAdaptor adaptor,
1463 ConversionPatternRewriter &rewriter) const override {
1464 // TODO: When we support delay control in Moore dialect, we need to update
1465 // this conversion.
1466 auto timeAttr = llhd::TimeAttr::get(
1467 op->getContext(), 0U, llvm::StringRef("ns"), DeltaTime, EpsilonTime);
1468 auto time = llhd::ConstantTimeOp::create(rewriter, op->getLoc(), timeAttr);
1469 rewriter.replaceOpWithNewOp<llhd::DrvOp>(op, adaptor.getDst(),
1470 adaptor.getSrc(), time, Value{});
1471 return success();
1472 }
1473};
1474
1475struct ConditionalOpConversion : public OpConversionPattern<ConditionalOp> {
1476 using OpConversionPattern::OpConversionPattern;
1477
1478 LogicalResult
1479 matchAndRewrite(ConditionalOp op, OpAdaptor adaptor,
1480 ConversionPatternRewriter &rewriter) const override {
1481 // TODO: This lowering is only correct if the condition is two-valued. If
1482 // the condition is X or Z, both branches of the conditional must be
1483 // evaluated and merged with the appropriate lookup table. See documentation
1484 // for `ConditionalOp`.
1485 auto type = typeConverter->convertType(op.getType());
1486
1487 auto hasNoWriteEffect = [](Region &region) {
1488 auto result = region.walk([](Operation *operation) {
1489 if (auto memOp = dyn_cast<MemoryEffectOpInterface>(operation))
1490 if (!memOp.hasEffect<MemoryEffects::Write>() &&
1491 !memOp.hasEffect<MemoryEffects::Free>())
1492 return WalkResult::advance();
1493
1494 if (operation->hasTrait<OpTrait::HasRecursiveMemoryEffects>())
1495 return WalkResult::advance();
1496
1497 return WalkResult::interrupt();
1498 });
1499 return !result.wasInterrupted();
1500 };
1501
1502 if (hasNoWriteEffect(op.getTrueRegion()) &&
1503 hasNoWriteEffect(op.getFalseRegion())) {
1504 Operation *trueTerm = op.getTrueRegion().front().getTerminator();
1505 Operation *falseTerm = op.getFalseRegion().front().getTerminator();
1506
1507 rewriter.inlineBlockBefore(&op.getTrueRegion().front(), op);
1508 rewriter.inlineBlockBefore(&op.getFalseRegion().front(), op);
1509
1510 Value convTrueVal = typeConverter->materializeTargetConversion(
1511 rewriter, op.getLoc(), type, trueTerm->getOperand(0));
1512 Value convFalseVal = typeConverter->materializeTargetConversion(
1513 rewriter, op.getLoc(), type, falseTerm->getOperand(0));
1514
1515 rewriter.eraseOp(trueTerm);
1516 rewriter.eraseOp(falseTerm);
1517
1518 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, adaptor.getCondition(),
1519 convTrueVal, convFalseVal);
1520 return success();
1521 }
1522
1523 auto ifOp =
1524 scf::IfOp::create(rewriter, op.getLoc(), type, adaptor.getCondition());
1525 rewriter.inlineRegionBefore(op.getTrueRegion(), ifOp.getThenRegion(),
1526 ifOp.getThenRegion().end());
1527 rewriter.inlineRegionBefore(op.getFalseRegion(), ifOp.getElseRegion(),
1528 ifOp.getElseRegion().end());
1529 rewriter.replaceOp(op, ifOp);
1530 return success();
1531 }
1532};
1533
1534struct YieldOpConversion : public OpConversionPattern<YieldOp> {
1535 using OpConversionPattern::OpConversionPattern;
1536
1537 LogicalResult
1538 matchAndRewrite(YieldOp op, OpAdaptor adaptor,
1539 ConversionPatternRewriter &rewriter) const override {
1540 rewriter.replaceOpWithNewOp<scf::YieldOp>(op, adaptor.getResult());
1541 return success();
1542 }
1543};
1544
1545template <typename SourceOp>
1546struct InPlaceOpConversion : public OpConversionPattern<SourceOp> {
1548 using OpAdaptor = typename SourceOp::Adaptor;
1549
1550 LogicalResult
1551 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1552 ConversionPatternRewriter &rewriter) const override {
1553 rewriter.modifyOpInPlace(op,
1554 [&]() { op->setOperands(adaptor.getOperands()); });
1555 return success();
1556 }
1557};
1558
1559template <typename MooreOpTy, typename VerifOpTy>
1560struct AssertLikeOpConversion : public OpConversionPattern<MooreOpTy> {
1562 using OpAdaptor = typename MooreOpTy::Adaptor;
1563
1564 LogicalResult
1565 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
1566 ConversionPatternRewriter &rewriter) const override {
1567 StringAttr label =
1568 op.getLabel().has_value()
1569 ? StringAttr::get(op->getContext(), op.getLabel().value())
1570 : StringAttr::get(op->getContext());
1571 rewriter.replaceOpWithNewOp<VerifOpTy>(op, adaptor.getCond(), mlir::Value(),
1572 label);
1573 return success();
1574 }
1575};
1576
1577//===----------------------------------------------------------------------===//
1578// Format String Conversion
1579//===----------------------------------------------------------------------===//
1580
1581struct FormatLiteralOpConversion : public OpConversionPattern<FormatLiteralOp> {
1582 using OpConversionPattern::OpConversionPattern;
1583
1584 LogicalResult
1585 matchAndRewrite(FormatLiteralOp op, OpAdaptor adaptor,
1586 ConversionPatternRewriter &rewriter) const override {
1587 rewriter.replaceOpWithNewOp<sim::FormatLitOp>(op, adaptor.getLiteral());
1588 return success();
1589 }
1590};
1591
1592struct FormatConcatOpConversion : public OpConversionPattern<FormatConcatOp> {
1593 using OpConversionPattern::OpConversionPattern;
1594
1595 LogicalResult
1596 matchAndRewrite(FormatConcatOp op, OpAdaptor adaptor,
1597 ConversionPatternRewriter &rewriter) const override {
1598 rewriter.replaceOpWithNewOp<sim::FormatStringConcatOp>(op,
1599 adaptor.getInputs());
1600 return success();
1601 }
1602};
1603
1604struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
1605 using OpConversionPattern::OpConversionPattern;
1606
1607 LogicalResult
1608 matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
1609 ConversionPatternRewriter &rewriter) const override {
1610 // TODO: These should honor the width, alignment, and padding.
1611 switch (op.getFormat()) {
1612 case IntFormat::Decimal:
1613 rewriter.replaceOpWithNewOp<sim::FormatDecOp>(op, adaptor.getValue());
1614 return success();
1615 case IntFormat::Binary:
1616 rewriter.replaceOpWithNewOp<sim::FormatBinOp>(op, adaptor.getValue());
1617 return success();
1618 case IntFormat::HexLower:
1619 case IntFormat::HexUpper:
1620 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(op, adaptor.getValue());
1621 return success();
1622 default:
1623 return rewriter.notifyMatchFailure(op, "unsupported int format");
1624 }
1625 }
1626};
1627
1628struct DisplayBIOpConversion : public OpConversionPattern<DisplayBIOp> {
1629 using OpConversionPattern::OpConversionPattern;
1630
1631 LogicalResult
1632 matchAndRewrite(DisplayBIOp op, OpAdaptor adaptor,
1633 ConversionPatternRewriter &rewriter) const override {
1634 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
1635 op, adaptor.getMessage());
1636 return success();
1637 }
1638};
1639
1640} // namespace
1641
1642//===----------------------------------------------------------------------===//
1643// Simulation Control Conversion
1644//===----------------------------------------------------------------------===//
1645
1646// moore.builtin.stop -> sim.pause
1647static LogicalResult convert(StopBIOp op, StopBIOp::Adaptor adaptor,
1648 ConversionPatternRewriter &rewriter) {
1649 rewriter.replaceOpWithNewOp<sim::PauseOp>(op, /*verbose=*/false);
1650 return success();
1651}
1652
1653// moore.builtin.finish -> sim.terminate
1654static LogicalResult convert(FinishBIOp op, FinishBIOp::Adaptor adaptor,
1655 ConversionPatternRewriter &rewriter) {
1656 rewriter.replaceOpWithNewOp<sim::TerminateOp>(op, op.getExitCode() == 0,
1657 /*verbose=*/false);
1658 return success();
1659}
1660
1661// moore.builtin.finish_message
1662static LogicalResult convert(FinishMessageBIOp op,
1663 FinishMessageBIOp::Adaptor adaptor,
1664 ConversionPatternRewriter &rewriter) {
1665 // We don't support printing termination/pause messages yet.
1666 rewriter.eraseOp(op);
1667 return success();
1668}
1669
1670//===----------------------------------------------------------------------===//
1671// Conversion Infrastructure
1672//===----------------------------------------------------------------------===//
1673
1674static void populateLegality(ConversionTarget &target,
1675 const TypeConverter &converter) {
1676 target.addIllegalDialect<MooreDialect>();
1677 target.addLegalDialect<comb::CombDialect>();
1678 target.addLegalDialect<hw::HWDialect>();
1679 target.addLegalDialect<seq::SeqDialect>();
1680 target.addLegalDialect<llhd::LLHDDialect>();
1681 target.addLegalDialect<ltl::LTLDialect>();
1682 target.addLegalDialect<mlir::BuiltinDialect>();
1683 target.addLegalDialect<mlir::math::MathDialect>();
1684 target.addLegalDialect<sim::SimDialect>();
1685 target.addLegalDialect<verif::VerifDialect>();
1686
1687 target.addLegalOp<debug::ScopeOp>();
1688
1689 target.addDynamicallyLegalOp<
1690 cf::CondBranchOp, cf::BranchOp, scf::YieldOp, func::CallOp,
1691 func::ReturnOp, UnrealizedConversionCastOp, hw::OutputOp, hw::InstanceOp,
1692 debug::ArrayOp, debug::StructOp, debug::VariableOp>(
1693 [&](Operation *op) { return converter.isLegal(op); });
1694
1695 target.addDynamicallyLegalOp<scf::IfOp, scf::ForOp, scf::ExecuteRegionOp,
1696 scf::WhileOp, scf::ForallOp>([&](Operation *op) {
1697 return converter.isLegal(op) && !op->getParentOfType<llhd::ProcessOp>();
1698 });
1699
1700 target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
1701 return converter.isSignatureLegal(op.getFunctionType()) &&
1702 converter.isLegal(&op.getFunctionBody());
1703 });
1704
1705 target.addDynamicallyLegalOp<hw::HWModuleOp>([&](hw::HWModuleOp op) {
1706 return converter.isSignatureLegal(op.getModuleType().getFuncType()) &&
1707 converter.isLegal(&op.getBody());
1708 });
1709}
1710
1711static void populateTypeConversion(TypeConverter &typeConverter) {
1712 typeConverter.addConversion([&](IntType type) {
1713 return IntegerType::get(type.getContext(), type.getWidth());
1714 });
1715
1716 typeConverter.addConversion(
1717 [&](TimeType type) { return llhd::TimeType::get(type.getContext()); });
1718
1719 typeConverter.addConversion([&](FormatStringType type) {
1720 return sim::FormatStringType::get(type.getContext());
1721 });
1722
1723 typeConverter.addConversion([&](ArrayType type) -> std::optional<Type> {
1724 if (auto elementType = typeConverter.convertType(type.getElementType()))
1725 return hw::ArrayType::get(elementType, type.getSize());
1726 return {};
1727 });
1728
1729 // FIXME: Unpacked arrays support more element types than their packed
1730 // variants, and as such, mapping them to hw::Array is somewhat naive. See
1731 // also the analogous note below concerning unpacked struct type conversion.
1732 typeConverter.addConversion(
1733 [&](UnpackedArrayType type) -> std::optional<Type> {
1734 if (auto elementType = typeConverter.convertType(type.getElementType()))
1735 return hw::ArrayType::get(elementType, type.getSize());
1736 return {};
1737 });
1738
1739 typeConverter.addConversion([&](StructType type) -> std::optional<Type> {
1740 SmallVector<hw::StructType::FieldInfo> fields;
1741 for (auto field : type.getMembers()) {
1742 hw::StructType::FieldInfo info;
1743 info.type = typeConverter.convertType(field.type);
1744 if (!info.type)
1745 return {};
1746 info.name = field.name;
1747 fields.push_back(info);
1748 }
1749 return hw::StructType::get(type.getContext(), fields);
1750 });
1751
1752 // FIXME: Mapping unpacked struct type to struct type in hw dialect may be a
1753 // plain solution. The packed and unpacked data structures have some
1754 // differences though they look similarily. The packed data structure is
1755 // contiguous in memory but another is opposite. The differences will affect
1756 // data layout and granularity of event tracking in simulation.
1757 typeConverter.addConversion(
1758 [&](UnpackedStructType type) -> std::optional<Type> {
1759 SmallVector<hw::StructType::FieldInfo> fields;
1760 for (auto field : type.getMembers()) {
1761 hw::StructType::FieldInfo info;
1762 info.type = typeConverter.convertType(field.type);
1763 if (!info.type)
1764 return {};
1765 info.name = field.name;
1766 fields.push_back(info);
1767 }
1768 return hw::StructType::get(type.getContext(), fields);
1769 });
1770
1771 typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
1772 if (auto innerType = typeConverter.convertType(type.getNestedType()))
1773 if (hw::isHWValueType(innerType))
1774 return hw::InOutType::get(innerType);
1775 return {};
1776 });
1777
1778 // Valid target types.
1779 typeConverter.addConversion([](IntegerType type) { return type; });
1780 typeConverter.addConversion([](llhd::TimeType type) { return type; });
1781 typeConverter.addConversion([](debug::ArrayType type) { return type; });
1782 typeConverter.addConversion([](debug::ScopeType type) { return type; });
1783 typeConverter.addConversion([](debug::StructType type) { return type; });
1784
1785 typeConverter.addConversion([&](hw::InOutType type) -> std::optional<Type> {
1786 if (auto innerType = typeConverter.convertType(type.getElementType()))
1787 return hw::InOutType::get(innerType);
1788 return {};
1789 });
1790
1791 typeConverter.addConversion([&](hw::ArrayType type) -> std::optional<Type> {
1792 if (auto elementType = typeConverter.convertType(type.getElementType()))
1793 return hw::ArrayType::get(elementType, type.getNumElements());
1794 return {};
1795 });
1796
1797 typeConverter.addConversion([&](hw::StructType type) -> std::optional<Type> {
1798 SmallVector<hw::StructType::FieldInfo> fields;
1799 for (auto field : type.getElements()) {
1800 hw::StructType::FieldInfo info;
1801 info.type = typeConverter.convertType(field.type);
1802 if (!info.type)
1803 return {};
1804 info.name = field.name;
1805 fields.push_back(info);
1806 }
1807 return hw::StructType::get(type.getContext(), fields);
1808 });
1809
1810 typeConverter.addTargetMaterialization(
1811 [&](mlir::OpBuilder &builder, mlir::Type resultType,
1812 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
1813 if (inputs.size() != 1 || !inputs[0])
1814 return Value();
1815 return UnrealizedConversionCastOp::create(builder, loc, resultType,
1816 inputs[0])
1817 .getResult(0);
1818 });
1819
1820 typeConverter.addSourceMaterialization(
1821 [&](mlir::OpBuilder &builder, mlir::Type resultType,
1822 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
1823 if (inputs.size() != 1)
1824 return Value();
1825 return UnrealizedConversionCastOp::create(builder, loc, resultType,
1826 inputs[0])
1827 ->getResult(0);
1828 });
1829}
1830
1832 TypeConverter &typeConverter) {
1833 // clang-format off
1834 patterns.add<
1835 // Patterns of declaration operations.
1836 VariableOpConversion,
1837 NetOpConversion,
1838
1839 // Patterns for conversion operations.
1840 ConversionOpConversion,
1841 BitcastConversion<PackedToSBVOp>,
1842 BitcastConversion<SBVToPackedOp>,
1843 BitcastConversion<LogicToIntOp>,
1844 BitcastConversion<IntToLogicOp>,
1845 BitcastConversion<ToBuiltinBoolOp>,
1846 TruncOpConversion,
1847 ZExtOpConversion,
1848 SExtOpConversion,
1849
1850 // Patterns of miscellaneous operations.
1851 ConstantOpConv,
1852 ConcatOpConversion,
1853 ReplicateOpConversion,
1854 ConstantTimeOpConv,
1855 ExtractOpConversion,
1856 DynExtractOpConversion,
1857 DynExtractRefOpConversion,
1858 ReadOpConversion,
1859 StructExtractOpConversion,
1860 StructExtractRefOpConversion,
1861 ExtractRefOpConversion,
1862 StructCreateOpConversion,
1863 ConditionalOpConversion,
1864 ArrayCreateOpConversion,
1865 YieldOpConversion,
1866 OutputOpConversion,
1867 StringConstantOpConv,
1868
1869 // Patterns of unary operations.
1870 ReduceAndOpConversion,
1871 ReduceOrOpConversion,
1872 ReduceXorOpConversion,
1873 BoolCastOpConversion,
1874 NotOpConversion,
1875 NegOpConversion,
1876
1877 // Patterns of binary operations.
1878 BinaryOpConversion<AddOp, comb::AddOp>,
1879 BinaryOpConversion<SubOp, comb::SubOp>,
1880 BinaryOpConversion<MulOp, comb::MulOp>,
1881 BinaryOpConversion<DivUOp, comb::DivUOp>,
1882 BinaryOpConversion<DivSOp, comb::DivSOp>,
1883 BinaryOpConversion<ModUOp, comb::ModUOp>,
1884 BinaryOpConversion<ModSOp, comb::ModSOp>,
1885 BinaryOpConversion<AndOp, comb::AndOp>,
1886 BinaryOpConversion<OrOp, comb::OrOp>,
1887 BinaryOpConversion<XorOp, comb::XorOp>,
1888
1889 // Patterns of power operations.
1890 PowUOpConversion, PowSOpConversion,
1891
1892 // Patterns of relational operations.
1893 ICmpOpConversion<UltOp, ICmpPredicate::ult>,
1894 ICmpOpConversion<SltOp, ICmpPredicate::slt>,
1895 ICmpOpConversion<UleOp, ICmpPredicate::ule>,
1896 ICmpOpConversion<SleOp, ICmpPredicate::sle>,
1897 ICmpOpConversion<UgtOp, ICmpPredicate::ugt>,
1898 ICmpOpConversion<SgtOp, ICmpPredicate::sgt>,
1899 ICmpOpConversion<UgeOp, ICmpPredicate::uge>,
1900 ICmpOpConversion<SgeOp, ICmpPredicate::sge>,
1901 ICmpOpConversion<EqOp, ICmpPredicate::eq>,
1902 ICmpOpConversion<NeOp, ICmpPredicate::ne>,
1903 ICmpOpConversion<CaseEqOp, ICmpPredicate::ceq>,
1904 ICmpOpConversion<CaseNeOp, ICmpPredicate::cne>,
1905 ICmpOpConversion<WildcardEqOp, ICmpPredicate::weq>,
1906 ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
1907 CaseXZEqOpConversion<CaseZEqOp, true>,
1908 CaseXZEqOpConversion<CaseXZEqOp, false>,
1909
1910 // Patterns of structural operations.
1911 SVModuleOpConversion,
1912 InstanceOpConversion,
1913 ProcedureOpConversion,
1914 WaitEventOpConversion,
1915
1916 // Patterns of shifting operations.
1917 ShrOpConversion,
1918 ShlOpConversion,
1919 AShrOpConversion,
1920
1921 // Patterns of assignment operations.
1922 AssignOpConversion<ContinuousAssignOp, 0, 1>,
1923 AssignOpConversion<BlockingAssignOp, 0, 1>,
1924 AssignOpConversion<NonBlockingAssignOp, 1, 0>,
1925 AssignedVariableOpConversion,
1926
1927 // Patterns of branch operations.
1928 CondBranchOpConversion,
1929 BranchOpConversion,
1930
1931 // Patterns of other operations outside Moore dialect.
1932 HWInstanceOpConversion,
1933 ReturnOpConversion,
1934 CallOpConversion,
1935 UnrealizedConversionCastConversion,
1936 InPlaceOpConversion<debug::ArrayOp>,
1937 InPlaceOpConversion<debug::StructOp>,
1938 InPlaceOpConversion<debug::VariableOp>,
1939
1940 // Patterns of assert-like operations
1941 AssertLikeOpConversion<AssertOp, verif::AssertOp>,
1942 AssertLikeOpConversion<AssumeOp, verif::AssumeOp>,
1943 AssertLikeOpConversion<CoverOp, verif::CoverOp>,
1944
1945 // Format strings.
1946 FormatLiteralOpConversion,
1947 FormatConcatOpConversion,
1948 FormatIntOpConversion,
1949 DisplayBIOpConversion
1950 >(typeConverter, patterns.getContext());
1951 // clang-format on
1952
1953 // Structural operations
1954 patterns.add<WaitDelayOp>(convert);
1955 patterns.add<UnreachableOp>(convert);
1956
1957 // Simulation control
1958 patterns.add<StopBIOp>(convert);
1959 patterns.add<FinishBIOp>(convert);
1960 patterns.add<FinishMessageBIOp>(convert);
1961
1962 mlir::populateAnyFunctionOpInterfaceTypeConversionPattern(patterns,
1963 typeConverter);
1964 hw::populateHWModuleLikeTypeConversionPattern(
1965 hw::HWModuleOp::getOperationName(), patterns, typeConverter);
1966 populateSCFToControlFlowConversionPatterns(patterns);
1967 populateArithToCombPatterns(patterns, typeConverter);
1968}
1969
1970//===----------------------------------------------------------------------===//
1971// Moore to Core Conversion Pass
1972//===----------------------------------------------------------------------===//
1973
1974namespace {
1975struct MooreToCorePass
1976 : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
1977 void runOnOperation() override;
1978};
1979} // namespace
1980
1981/// Create a Moore to core dialects conversion pass.
1982std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
1983 return std::make_unique<MooreToCorePass>();
1984}
1985
1986/// This is the main entrypoint for the Moore to Core conversion pass.
1987void MooreToCorePass::runOnOperation() {
1988 MLIRContext &context = getContext();
1989 ModuleOp module = getOperation();
1990
1991 IRRewriter rewriter(module);
1992 (void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
1993
1994 TypeConverter typeConverter;
1995 populateTypeConversion(typeConverter);
1996
1997 ConversionTarget target(context);
1998 populateLegality(target, typeConverter);
1999
2000 ConversionPatternSet patterns(&context, typeConverter);
2001 populateOpConversion(patterns, typeConverter);
2002
2003 if (failed(applyFullConversion(module, target, std::move(patterns))))
2004 signalPassFailure();
2005}
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.