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