CIRCT 21.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, TypeRange{});
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, ValueRange{});
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, TypeRange{});
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, ValueRange{}, Value(), observedValues,
287 ValueRange{}, 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
346 // If the 'wait_event' op is empty, we can lower it to a 'llhd.wait' op
347 // without any observed values, but since the process will never wake up
348 // from suspension anyway, we can also just terminate it using the
349 // 'llhd.halt' op.
350 if (op.getBody().front().empty()) {
351 // Let the cleanup iteration after the dialect conversion clean up all
352 // remaining unreachable blocks.
353 rewriter.replaceOpWithNewOp<llhd::HaltOp>(op, ValueRange{});
354 return success();
355 }
356
357 auto *waitBlock = rewriter.createBlock(resumeBlock);
358 auto *checkBlock = rewriter.createBlock(resumeBlock);
359
360 auto loc = op.getLoc();
361 rewriter.setInsertionPoint(op);
362 rewriter.create<cf::BranchOp>(loc, waitBlock);
363
364 // We need to inline two copies of the `wait_event`'s body region: one is
365 // used to determine the values going into `detect_event` ops before the
366 // `llhd.wait`, and one will do the actual event detection after the
367 // `llhd.wait`.
368 //
369 // Create a copy of the entire `wait_event` op in the wait block, which also
370 // creates a copy of its region. Take note of all inputs to `detect_event`
371 // ops and delete the `detect_event` ops in this copy.
372 SmallVector<Value> valuesBefore;
373 rewriter.setInsertionPointToEnd(waitBlock);
374 auto clonedOp = cast<WaitEventOp>(rewriter.clone(*op));
375 bool allDetectsAreAnyChange = true;
376 for (auto detectOp :
377 llvm::make_early_inc_range(clonedOp.getOps<DetectEventOp>())) {
378 if (detectOp.getEdge() != Edge::AnyChange || detectOp.getCondition())
379 allDetectsAreAnyChange = false;
380 valuesBefore.push_back(detectOp.getInput());
381 rewriter.eraseOp(detectOp);
382 }
383
384 // Determine the values used during event detection that are defined outside
385 // the `wait_event`'s body region. We want to wait for a change on these
386 // signals before we check if any interesting event happened.
387 SmallVector<Value> observeValues;
388 auto setInsertionPointAfterDef = [&](Value value) {
389 if (auto *op = value.getDefiningOp())
390 rewriter.setInsertionPointAfter(op);
391 if (auto arg = dyn_cast<BlockArgument>(value))
392 rewriter.setInsertionPointToStart(value.getParentBlock());
393 };
394
395 getValuesToObserve(&clonedOp.getBody(), setInsertionPointAfterDef,
396 typeConverter, rewriter, observeValues);
397
398 // Create the `llhd.wait` op that suspends the current process and waits for
399 // a change in the interesting values listed in `observeValues`. When a
400 // change is detected, execution resumes in the "check" block.
401 auto waitOp = rewriter.create<llhd::WaitOp>(
402 loc, ValueRange{}, Value(), observeValues, ValueRange{}, checkBlock);
403 rewriter.inlineBlockBefore(&clonedOp.getBody().front(), waitOp);
404 rewriter.eraseOp(clonedOp);
405
406 // Collect a list of all detect ops and inline the `wait_event` body into
407 // the check block.
408 SmallVector<DetectEventOp> detectOps(op.getBody().getOps<DetectEventOp>());
409 rewriter.inlineBlockBefore(&op.getBody().front(), checkBlock,
410 checkBlock->end());
411 rewriter.eraseOp(op);
412
413 // Helper function to detect if a certain change occurred between a value
414 // before the `llhd.wait` and after.
415 auto computeTrigger = [&](Value before, Value after, Edge edge) -> Value {
416 assert(before.getType() == after.getType() &&
417 "mismatched types after clone op");
418 auto beforeType = cast<IntType>(before.getType());
419
420 // 9.4.2 IEEE 1800-2017: An edge event shall be detected only on the LSB
421 // of the expression
422 if (beforeType.getWidth() != 1 && edge != Edge::AnyChange) {
423 constexpr int LSB = 0;
424 beforeType =
425 IntType::get(rewriter.getContext(), 1, beforeType.getDomain());
426 before =
427 rewriter.create<moore::ExtractOp>(loc, beforeType, before, LSB);
428 after = rewriter.create<moore::ExtractOp>(loc, beforeType, after, LSB);
429 }
430
431 auto intType = rewriter.getIntegerType(beforeType.getWidth());
432 before = typeConverter->materializeTargetConversion(rewriter, loc,
433 intType, before);
434 after = typeConverter->materializeTargetConversion(rewriter, loc, intType,
435 after);
436
437 if (edge == Edge::AnyChange)
438 return rewriter.create<comb::ICmpOp>(loc, ICmpPredicate::ne, before,
439 after, true);
440
441 SmallVector<Value> disjuncts;
442 Value trueVal = rewriter.create<hw::ConstantOp>(loc, APInt(1, 1));
443
444 if (edge == Edge::PosEdge || edge == Edge::BothEdges) {
445 Value notOldVal =
446 rewriter.create<comb::XorOp>(loc, before, trueVal, true);
447 Value posedge =
448 rewriter.create<comb::AndOp>(loc, notOldVal, after, true);
449 disjuncts.push_back(posedge);
450 }
451
452 if (edge == Edge::NegEdge || edge == Edge::BothEdges) {
453 Value notCurrVal =
454 rewriter.create<comb::XorOp>(loc, after, trueVal, true);
455 Value posedge =
456 rewriter.create<comb::AndOp>(loc, before, notCurrVal, true);
457 disjuncts.push_back(posedge);
458 }
459
460 return rewriter.createOrFold<comb::OrOp>(loc, disjuncts, true);
461 };
462
463 // Convert all `detect_event` ops into a check for the corresponding event
464 // between the value before and after the `llhd.wait`. The "before" value
465 // has been collected into `valuesBefore` in the "wait" block; the "after"
466 // value corresponds to the detect op's input.
467 SmallVector<Value> triggers;
468 for (auto [detectOp, before] : llvm::zip(detectOps, valuesBefore)) {
469 if (!allDetectsAreAnyChange) {
470 if (!isa<IntType>(before.getType()))
471 return detectOp->emitError() << "requires int operand";
472
473 rewriter.setInsertionPoint(detectOp);
474 auto trigger =
475 computeTrigger(before, detectOp.getInput(), detectOp.getEdge());
476 if (detectOp.getCondition()) {
477 auto condition = typeConverter->materializeTargetConversion(
478 rewriter, loc, rewriter.getI1Type(), detectOp.getCondition());
479 trigger = rewriter.create<comb::AndOp>(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 rewriter.create<cf::BranchOp>(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 rewriter.create<cf::CondBranchOp>(loc, triggered, resumeBlock, waitBlock);
501 }
502
503 return success();
504 }
505};
506
507//===----------------------------------------------------------------------===//
508// Declaration Conversion
509//===----------------------------------------------------------------------===//
510
511struct VariableOpConversion : public OpConversionPattern<VariableOp> {
512 using OpConversionPattern::OpConversionPattern;
513
514 LogicalResult
515 matchAndRewrite(VariableOp op, OpAdaptor adaptor,
516 ConversionPatternRewriter &rewriter) const override {
517 Location loc = op.getLoc();
518 Type resultType = typeConverter->convertType(op.getResult().getType());
519 if (!resultType)
520 return rewriter.notifyMatchFailure(op.getLoc(), "invalid variable type");
521
522 // Determine the initial value of the signal.
523 Value init = adaptor.getInitial();
524 if (!init) {
525 Type elementType = cast<hw::InOutType>(resultType).getElementType();
526 int64_t width = hw::getBitWidth(elementType);
527 if (width == -1)
528 return failure();
529
530 // TODO: Once the core dialects support four-valued integers, this code
531 // will additionally need to generate an all-X value for four-valued
532 // variables.
533 Value constZero = rewriter.create<hw::ConstantOp>(loc, APInt(width, 0));
534 init = rewriter.createOrFold<hw::BitcastOp>(loc, elementType, constZero);
535 }
536
537 rewriter.replaceOpWithNewOp<llhd::SignalOp>(op, resultType,
538 op.getNameAttr(), init);
539 return success();
540 }
541};
542
543struct NetOpConversion : public OpConversionPattern<NetOp> {
544 using OpConversionPattern::OpConversionPattern;
545
546 LogicalResult
547 matchAndRewrite(NetOp op, OpAdaptor adaptor,
548 ConversionPatternRewriter &rewriter) const override {
549 auto loc = op.getLoc();
550 if (op.getKind() != NetKind::Wire)
551 return rewriter.notifyMatchFailure(loc, "only wire nets supported");
552
553 auto resultType = typeConverter->convertType(op.getResult().getType());
554 if (!resultType)
555 return rewriter.notifyMatchFailure(loc, "invalid net type");
556
557 // TODO: Once the core dialects support four-valued integers, this code
558 // will additionally need to generate an all-X value for four-valued nets.
559 auto elementType = cast<hw::InOutType>(resultType).getElementType();
560 int64_t width = hw::getBitWidth(elementType);
561 if (width == -1)
562 return failure();
563 auto constZero = rewriter.create<hw::ConstantOp>(loc, APInt(width, 0));
564 auto init =
565 rewriter.createOrFold<hw::BitcastOp>(loc, elementType, constZero);
566
567 auto signal = rewriter.replaceOpWithNewOp<llhd::SignalOp>(
568 op, resultType, op.getNameAttr(), init);
569
570 if (auto assignedValue = adaptor.getAssignment()) {
571 auto timeAttr = llhd::TimeAttr::get(resultType.getContext(), 0U,
572 llvm::StringRef("ns"), 0, 1);
573 auto time = rewriter.create<llhd::ConstantTimeOp>(loc, timeAttr);
574 rewriter.create<llhd::DrvOp>(loc, signal, assignedValue, time, Value{});
575 }
576
577 return success();
578 }
579};
580
581//===----------------------------------------------------------------------===//
582// Expression Conversion
583//===----------------------------------------------------------------------===//
584
585struct ConstantOpConv : public OpConversionPattern<ConstantOp> {
586 using OpConversionPattern::OpConversionPattern;
587
588 LogicalResult
589 matchAndRewrite(ConstantOp op, OpAdaptor adaptor,
590 ConversionPatternRewriter &rewriter) const override {
591 // FIXME: Discard unknown bits and map them to 0 for now.
592 auto value = op.getValue().toAPInt(false);
593 auto type = rewriter.getIntegerType(value.getBitWidth());
594 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
595 op, type, rewriter.getIntegerAttr(type, value));
596 return success();
597 }
598};
599
600struct StringConstantOpConv : public OpConversionPattern<StringConstantOp> {
601 using OpConversionPattern::OpConversionPattern;
602 LogicalResult
603 matchAndRewrite(moore::StringConstantOp op, OpAdaptor adaptor,
604 ConversionPatternRewriter &rewriter) const override {
605 const auto str = op.getValue();
606 const unsigned byteWidth = str.size() * 8;
607 const auto resultType =
608 typeConverter->convertType(op.getResult().getType());
609 if (const auto intType = mlir::dyn_cast<IntegerType>(resultType)) {
610 if (intType.getWidth() < byteWidth) {
611 return rewriter.notifyMatchFailure(op,
612 "invalid string constant type size");
613 }
614 } else {
615 return rewriter.notifyMatchFailure(op, "invalid string constant type");
616 }
617 APInt value(byteWidth, 0);
618 for (size_t i = 0; i < str.size(); ++i) {
619 const auto asciiChar = static_cast<uint8_t>(str[i]);
620 value |= APInt(byteWidth, asciiChar) << (8 * (str.size() - 1 - i));
621 }
622 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
623 op, resultType, rewriter.getIntegerAttr(resultType, value));
624 return success();
625 }
626};
627
628struct ConcatOpConversion : public OpConversionPattern<ConcatOp> {
629 using OpConversionPattern::OpConversionPattern;
630 LogicalResult
631 matchAndRewrite(ConcatOp op, OpAdaptor adaptor,
632 ConversionPatternRewriter &rewriter) const override {
633 rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, adaptor.getValues());
634 return success();
635 }
636};
637
638struct ReplicateOpConversion : public OpConversionPattern<ReplicateOp> {
639 using OpConversionPattern::OpConversionPattern;
640 LogicalResult
641 matchAndRewrite(ReplicateOp op, OpAdaptor adaptor,
642 ConversionPatternRewriter &rewriter) const override {
643 Type resultType = typeConverter->convertType(op.getResult().getType());
644
645 rewriter.replaceOpWithNewOp<comb::ReplicateOp>(op, resultType,
646 adaptor.getValue());
647 return success();
648 }
649};
650
651struct ExtractOpConversion : public OpConversionPattern<ExtractOp> {
652 using OpConversionPattern::OpConversionPattern;
653
654 LogicalResult
655 matchAndRewrite(ExtractOp op, OpAdaptor adaptor,
656 ConversionPatternRewriter &rewriter) const override {
657 // TODO: return X if the domain is four-valued for out-of-bounds accesses
658 // once we support four-valued lowering
659 Type resultType = typeConverter->convertType(op.getResult().getType());
660 Type inputType = adaptor.getInput().getType();
661 int32_t low = adaptor.getLowBit();
662
663 if (isa<IntegerType>(inputType)) {
664 int32_t inputWidth = inputType.getIntOrFloatBitWidth();
665 int32_t resultWidth = resultType.getIntOrFloatBitWidth();
666 int32_t high = low + resultWidth;
667
668 SmallVector<Value> toConcat;
669 if (low < 0)
670 toConcat.push_back(rewriter.create<hw::ConstantOp>(
671 op.getLoc(), APInt(std::min(-low, resultWidth), 0)));
672
673 if (low < inputWidth && high > 0) {
674 int32_t lowIdx = std::max(low, 0);
675 Value middle = rewriter.createOrFold<comb::ExtractOp>(
676 op.getLoc(),
677 rewriter.getIntegerType(
678 std::min(resultWidth, std::min(high, inputWidth) - lowIdx)),
679 adaptor.getInput(), lowIdx);
680 toConcat.push_back(middle);
681 }
682
683 int32_t diff = high - inputWidth;
684 if (diff > 0) {
685 Value val =
686 rewriter.create<hw::ConstantOp>(op.getLoc(), APInt(diff, 0));
687 toConcat.push_back(val);
688 }
689
690 Value concat =
691 rewriter.createOrFold<comb::ConcatOp>(op.getLoc(), toConcat);
692 rewriter.replaceOp(op, concat);
693 return success();
694 }
695
696 if (auto arrTy = dyn_cast<hw::ArrayType>(inputType)) {
697 int32_t width = llvm::Log2_64_Ceil(arrTy.getNumElements());
698 int32_t inputWidth = arrTy.getNumElements();
699
700 if (auto resArrTy = dyn_cast<hw::ArrayType>(resultType)) {
701 int32_t elementWidth = hw::getBitWidth(arrTy.getElementType());
702 if (elementWidth < 0)
703 return failure();
704
705 int32_t high = low + resArrTy.getNumElements();
706 int32_t resWidth = resArrTy.getNumElements();
707
708 SmallVector<Value> toConcat;
709 if (low < 0) {
710 Value val = rewriter.create<hw::ConstantOp>(
711 op.getLoc(),
712 APInt(std::min((-low) * elementWidth, resWidth * elementWidth),
713 0));
714 Value res = rewriter.createOrFold<hw::BitcastOp>(
715 op.getLoc(), hw::ArrayType::get(arrTy.getElementType(), -low),
716 val);
717 toConcat.push_back(res);
718 }
719
720 if (low < inputWidth && high > 0) {
721 int32_t lowIdx = std::max(0, low);
722 Value lowIdxVal = rewriter.create<hw::ConstantOp>(
723 op.getLoc(), rewriter.getIntegerType(width), lowIdx);
724 Value middle = rewriter.createOrFold<hw::ArraySliceOp>(
725 op.getLoc(),
726 hw::ArrayType::get(
727 arrTy.getElementType(),
728 std::min(resWidth, std::min(inputWidth, high) - lowIdx)),
729 adaptor.getInput(), lowIdxVal);
730 toConcat.push_back(middle);
731 }
732
733 int32_t diff = high - inputWidth;
734 if (diff > 0) {
735 Value constZero = rewriter.create<hw::ConstantOp>(
736 op.getLoc(), APInt(diff * elementWidth, 0));
737 Value val = rewriter.create<hw::BitcastOp>(
738 op.getLoc(), hw::ArrayType::get(arrTy.getElementType(), diff),
739 constZero);
740 toConcat.push_back(val);
741 }
742
743 Value concat =
744 rewriter.createOrFold<hw::ArrayConcatOp>(op.getLoc(), toConcat);
745 rewriter.replaceOp(op, concat);
746 return success();
747 }
748
749 // Otherwise, it has to be the array's element type
750 if (low < 0 || low >= inputWidth) {
751 int32_t bw = hw::getBitWidth(resultType);
752 if (bw < 0)
753 return failure();
754
755 Value val = rewriter.create<hw::ConstantOp>(op.getLoc(), APInt(bw, 0));
756 Value bitcast =
757 rewriter.createOrFold<hw::BitcastOp>(op.getLoc(), resultType, val);
758 rewriter.replaceOp(op, bitcast);
759 return success();
760 }
761
762 Value idx = rewriter.create<hw::ConstantOp>(
763 op.getLoc(), rewriter.getIntegerType(width), adaptor.getLowBit());
764 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(), idx);
765 return success();
766 }
767
768 return failure();
769 }
770};
771
772struct ExtractRefOpConversion : public OpConversionPattern<ExtractRefOp> {
773 using OpConversionPattern::OpConversionPattern;
774
775 LogicalResult
776 matchAndRewrite(ExtractRefOp op, OpAdaptor adaptor,
777 ConversionPatternRewriter &rewriter) const override {
778 // TODO: properly handle out-of-bounds accesses
779 Type resultType = typeConverter->convertType(op.getResult().getType());
780 Type inputType =
781 cast<hw::InOutType>(adaptor.getInput().getType()).getElementType();
782
783 if (auto intType = dyn_cast<IntegerType>(inputType)) {
784 int64_t width = hw::getBitWidth(inputType);
785 if (width == -1)
786 return failure();
787
788 Value lowBit = rewriter.create<hw::ConstantOp>(
789 op.getLoc(), rewriter.getIntegerType(llvm::Log2_64_Ceil(width)),
790 adaptor.getLowBit());
791 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
792 op, resultType, adaptor.getInput(), lowBit);
793 return success();
794 }
795
796 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
797 Value lowBit = rewriter.create<hw::ConstantOp>(
798 op.getLoc(),
799 rewriter.getIntegerType(llvm::Log2_64_Ceil(arrType.getNumElements())),
800 adaptor.getLowBit());
801
802 if (isa<hw::ArrayType>(
803 cast<hw::InOutType>(resultType).getElementType())) {
804 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
805 op, resultType, adaptor.getInput(), lowBit);
806 return success();
807 }
808
809 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
810 lowBit);
811 return success();
812 }
813
814 return failure();
815 }
816};
817
818struct DynExtractOpConversion : public OpConversionPattern<DynExtractOp> {
819 using OpConversionPattern::OpConversionPattern;
820
821 LogicalResult
822 matchAndRewrite(DynExtractOp op, OpAdaptor adaptor,
823 ConversionPatternRewriter &rewriter) const override {
824 Type resultType = typeConverter->convertType(op.getResult().getType());
825 Type inputType = adaptor.getInput().getType();
826
827 if (auto intType = dyn_cast<IntegerType>(inputType)) {
828 Value amount = adjustIntegerWidth(rewriter, adaptor.getLowBit(),
829 intType.getWidth(), op->getLoc());
830 Value value = rewriter.create<comb::ShrUOp>(op->getLoc(),
831 adaptor.getInput(), amount);
832
833 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, value, 0);
834 return success();
835 }
836
837 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
838 unsigned idxWidth = llvm::Log2_64_Ceil(arrType.getNumElements());
839 Value idx = adjustIntegerWidth(rewriter, adaptor.getLowBit(), idxWidth,
840 op->getLoc());
841
842 if (isa<hw::ArrayType>(resultType)) {
843 rewriter.replaceOpWithNewOp<hw::ArraySliceOp>(op, resultType,
844 adaptor.getInput(), idx);
845 return success();
846 }
847
848 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(), idx);
849 return success();
850 }
851
852 return failure();
853 }
854};
855
856struct DynExtractRefOpConversion : public OpConversionPattern<DynExtractRefOp> {
857 using OpConversionPattern::OpConversionPattern;
858
859 LogicalResult
860 matchAndRewrite(DynExtractRefOp op, OpAdaptor adaptor,
861 ConversionPatternRewriter &rewriter) const override {
862 // TODO: properly handle out-of-bounds accesses
863 Type resultType = typeConverter->convertType(op.getResult().getType());
864 Type inputType =
865 cast<hw::InOutType>(adaptor.getInput().getType()).getElementType();
866
867 if (auto intType = dyn_cast<IntegerType>(inputType)) {
868 int64_t width = hw::getBitWidth(inputType);
869 if (width == -1)
870 return failure();
871
872 Value amount =
873 adjustIntegerWidth(rewriter, adaptor.getLowBit(),
874 llvm::Log2_64_Ceil(width), op->getLoc());
875 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
876 op, resultType, adaptor.getInput(), amount);
877 return success();
878 }
879
880 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
881 Value idx = adjustIntegerWidth(
882 rewriter, adaptor.getLowBit(),
883 llvm::Log2_64_Ceil(arrType.getNumElements()), op->getLoc());
884
885 if (isa<hw::ArrayType>(
886 cast<hw::InOutType>(resultType).getElementType())) {
887 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
888 op, resultType, adaptor.getInput(), idx);
889 return success();
890 }
891
892 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
893 idx);
894 return success();
895 }
896
897 return failure();
898 }
899};
900
901struct StructCreateOpConversion : public OpConversionPattern<StructCreateOp> {
902 using OpConversionPattern::OpConversionPattern;
903
904 LogicalResult
905 matchAndRewrite(StructCreateOp op, OpAdaptor adaptor,
906 ConversionPatternRewriter &rewriter) const override {
907 Type resultType = typeConverter->convertType(op.getResult().getType());
908 rewriter.replaceOpWithNewOp<hw::StructCreateOp>(op, resultType,
909 adaptor.getFields());
910 return success();
911 }
912};
913
914struct StructExtractOpConversion : public OpConversionPattern<StructExtractOp> {
915 using OpConversionPattern::OpConversionPattern;
916
917 LogicalResult
918 matchAndRewrite(StructExtractOp op, OpAdaptor adaptor,
919 ConversionPatternRewriter &rewriter) const override {
920 rewriter.replaceOpWithNewOp<hw::StructExtractOp>(
921 op, adaptor.getInput(), adaptor.getFieldNameAttr());
922 return success();
923 }
924};
925
926struct StructExtractRefOpConversion
927 : public OpConversionPattern<StructExtractRefOp> {
928 using OpConversionPattern::OpConversionPattern;
929
930 LogicalResult
931 matchAndRewrite(StructExtractRefOp op, OpAdaptor adaptor,
932 ConversionPatternRewriter &rewriter) const override {
933 rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
934 op, adaptor.getInput(), adaptor.getFieldNameAttr());
935 return success();
936 }
937};
938
939struct ReduceAndOpConversion : public OpConversionPattern<ReduceAndOp> {
940 using OpConversionPattern::OpConversionPattern;
941 LogicalResult
942 matchAndRewrite(ReduceAndOp op, OpAdaptor adaptor,
943 ConversionPatternRewriter &rewriter) const override {
944 Type resultType = typeConverter->convertType(op.getInput().getType());
945 Value max = rewriter.create<hw::ConstantOp>(op->getLoc(), resultType, -1);
946
947 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::eq,
948 adaptor.getInput(), max);
949 return success();
950 }
951};
952
953struct ReduceOrOpConversion : public OpConversionPattern<ReduceOrOp> {
954 using OpConversionPattern::OpConversionPattern;
955 LogicalResult
956 matchAndRewrite(ReduceOrOp op, OpAdaptor adaptor,
957 ConversionPatternRewriter &rewriter) const override {
958 Type resultType = typeConverter->convertType(op.getInput().getType());
959 Value zero = rewriter.create<hw::ConstantOp>(op->getLoc(), resultType, 0);
960
961 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
962 adaptor.getInput(), zero);
963 return success();
964 }
965};
966
967struct ReduceXorOpConversion : public OpConversionPattern<ReduceXorOp> {
968 using OpConversionPattern::OpConversionPattern;
969 LogicalResult
970 matchAndRewrite(ReduceXorOp op, OpAdaptor adaptor,
971 ConversionPatternRewriter &rewriter) const override {
972
973 rewriter.replaceOpWithNewOp<comb::ParityOp>(op, adaptor.getInput());
974 return success();
975 }
976};
977
978struct BoolCastOpConversion : public OpConversionPattern<BoolCastOp> {
979 using OpConversionPattern::OpConversionPattern;
980 LogicalResult
981 matchAndRewrite(BoolCastOp op, OpAdaptor adaptor,
982 ConversionPatternRewriter &rewriter) const override {
983 Type resultType = typeConverter->convertType(op.getInput().getType());
984 if (isa_and_nonnull<IntegerType>(resultType)) {
985 Value zero = rewriter.create<hw::ConstantOp>(op->getLoc(), resultType, 0);
986 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
987 adaptor.getInput(), zero);
988 return success();
989 }
990 return failure();
991 }
992};
993
994struct NotOpConversion : public OpConversionPattern<NotOp> {
995 using OpConversionPattern::OpConversionPattern;
996 LogicalResult
997 matchAndRewrite(NotOp op, OpAdaptor adaptor,
998 ConversionPatternRewriter &rewriter) const override {
999 Type resultType =
1000 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1001 Value max = rewriter.create<hw::ConstantOp>(op.getLoc(), resultType, -1);
1002
1003 rewriter.replaceOpWithNewOp<comb::XorOp>(op, adaptor.getInput(), max);
1004 return success();
1005 }
1006};
1007
1008struct NegOpConversion : public OpConversionPattern<NegOp> {
1009 using OpConversionPattern::OpConversionPattern;
1010 LogicalResult
1011 matchAndRewrite(NegOp op, OpAdaptor adaptor,
1012 ConversionPatternRewriter &rewriter) const override {
1013 Type resultType =
1014 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1015 Value zero = rewriter.create<hw::ConstantOp>(op.getLoc(), resultType, 0);
1016
1017 rewriter.replaceOpWithNewOp<comb::SubOp>(op, zero, adaptor.getInput());
1018 return success();
1019 }
1020};
1021
1022template <typename SourceOp, typename TargetOp>
1023struct BinaryOpConversion : public OpConversionPattern<SourceOp> {
1025 using OpAdaptor = typename SourceOp::Adaptor;
1026
1027 LogicalResult
1028 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1029 ConversionPatternRewriter &rewriter) const override {
1030 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1031 adaptor.getRhs(), false);
1032 return success();
1033 }
1034};
1035
1036template <typename SourceOp, ICmpPredicate pred>
1037struct ICmpOpConversion : public OpConversionPattern<SourceOp> {
1039 using OpAdaptor = typename SourceOp::Adaptor;
1040
1041 LogicalResult
1042 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1043 ConversionPatternRewriter &rewriter) const override {
1044 Type resultType =
1045 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1046
1047 rewriter.replaceOpWithNewOp<comb::ICmpOp>(
1048 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1049 return success();
1050 }
1051};
1052
1053template <typename SourceOp, bool withoutX>
1054struct CaseXZEqOpConversion : public OpConversionPattern<SourceOp> {
1056 using OpAdaptor = typename SourceOp::Adaptor;
1057
1058 LogicalResult
1059 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1060 ConversionPatternRewriter &rewriter) const override {
1061 // Check each operand if it is a known constant and extract the X and/or Z
1062 // bits to be ignored.
1063 // TODO: Once the core dialects support four-valued integers, we will have
1064 // to create ops that extract X and Z bits from the operands, since we also
1065 // have to do the right casez/casex comparison on non-constant inputs.
1066 unsigned bitWidth = op.getLhs().getType().getWidth();
1067 auto ignoredBits = APInt::getZero(bitWidth);
1068 auto detectIgnoredBits = [&](Value value) {
1069 auto constOp = value.getDefiningOp<ConstantOp>();
1070 if (!constOp)
1071 return;
1072 auto constValue = constOp.getValue();
1073 if (withoutX)
1074 ignoredBits |= constValue.getZBits();
1075 else
1076 ignoredBits |= constValue.getUnknownBits();
1077 };
1078 detectIgnoredBits(op.getLhs());
1079 detectIgnoredBits(op.getRhs());
1080
1081 // If we have detected any bits to be ignored, mask them in the operands for
1082 // the comparison.
1083 Value lhs = adaptor.getLhs();
1084 Value rhs = adaptor.getRhs();
1085 if (!ignoredBits.isZero()) {
1086 ignoredBits.flipAllBits();
1087 auto maskOp = rewriter.create<hw::ConstantOp>(op.getLoc(), ignoredBits);
1088 lhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), lhs, maskOp);
1089 rhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), rhs, maskOp);
1090 }
1091
1092 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, ICmpPredicate::ceq, lhs, rhs);
1093 return success();
1094 }
1095};
1096
1097//===----------------------------------------------------------------------===//
1098// Conversions
1099//===----------------------------------------------------------------------===//
1100
1101struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
1102 using OpConversionPattern::OpConversionPattern;
1103
1104 LogicalResult
1105 matchAndRewrite(ConversionOp op, OpAdaptor adaptor,
1106 ConversionPatternRewriter &rewriter) const override {
1107 Location loc = op.getLoc();
1108 Type resultType = typeConverter->convertType(op.getResult().getType());
1109 int64_t inputBw = hw::getBitWidth(adaptor.getInput().getType());
1110 int64_t resultBw = hw::getBitWidth(resultType);
1111 if (inputBw == -1 || resultBw == -1)
1112 return failure();
1113
1114 Value input = rewriter.createOrFold<hw::BitcastOp>(
1115 loc, rewriter.getIntegerType(inputBw), adaptor.getInput());
1116 Value amount = adjustIntegerWidth(rewriter, input, resultBw, loc);
1117
1118 Value result =
1119 rewriter.createOrFold<hw::BitcastOp>(loc, resultType, amount);
1120 rewriter.replaceOp(op, result);
1121 return success();
1122 }
1123};
1124
1125struct TruncOpConversion : public OpConversionPattern<TruncOp> {
1126 using OpConversionPattern::OpConversionPattern;
1127
1128 LogicalResult
1129 matchAndRewrite(TruncOp op, OpAdaptor adaptor,
1130 ConversionPatternRewriter &rewriter) const override {
1131 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, adaptor.getInput(), 0,
1132 op.getType().getWidth());
1133 return success();
1134 }
1135};
1136
1137struct ZExtOpConversion : public OpConversionPattern<ZExtOp> {
1138 using OpConversionPattern::OpConversionPattern;
1139
1140 LogicalResult
1141 matchAndRewrite(ZExtOp op, OpAdaptor adaptor,
1142 ConversionPatternRewriter &rewriter) const override {
1143 auto targetWidth = op.getType().getWidth();
1144 auto inputWidth = op.getInput().getType().getWidth();
1145
1146 auto zeroExt = rewriter.create<hw::ConstantOp>(
1147 op.getLoc(), rewriter.getIntegerType(targetWidth - inputWidth), 0);
1148
1149 rewriter.replaceOpWithNewOp<comb::ConcatOp>(
1150 op, ValueRange{zeroExt, adaptor.getInput()});
1151 return success();
1152 }
1153};
1154
1155struct SExtOpConversion : public OpConversionPattern<SExtOp> {
1156 using OpConversionPattern::OpConversionPattern;
1157
1158 LogicalResult
1159 matchAndRewrite(SExtOp op, OpAdaptor adaptor,
1160 ConversionPatternRewriter &rewriter) const override {
1161 auto type = typeConverter->convertType(op.getType());
1162 auto value =
1163 comb::createOrFoldSExt(op.getLoc(), adaptor.getInput(), type, rewriter);
1164 rewriter.replaceOp(op, value);
1165 return success();
1166 }
1167};
1168
1169//===----------------------------------------------------------------------===//
1170// Statement Conversion
1171//===----------------------------------------------------------------------===//
1172
1173struct HWInstanceOpConversion : public OpConversionPattern<hw::InstanceOp> {
1174 using OpConversionPattern::OpConversionPattern;
1175
1176 LogicalResult
1177 matchAndRewrite(hw::InstanceOp op, OpAdaptor adaptor,
1178 ConversionPatternRewriter &rewriter) const override {
1179 SmallVector<Type> convResTypes;
1180 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1181 return failure();
1182
1183 rewriter.replaceOpWithNewOp<hw::InstanceOp>(
1184 op, convResTypes, op.getInstanceName(), op.getModuleName(),
1185 adaptor.getOperands(), op.getArgNames(),
1186 op.getResultNames(), /*Parameter*/
1187 rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr);
1188
1189 return success();
1190 }
1191};
1192
1193struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
1194 using OpConversionPattern::OpConversionPattern;
1195
1196 LogicalResult
1197 matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
1198 ConversionPatternRewriter &rewriter) const override {
1199 rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
1200 return success();
1201 }
1202};
1203
1204struct CondBranchOpConversion : public OpConversionPattern<cf::CondBranchOp> {
1205 using OpConversionPattern::OpConversionPattern;
1206
1207 LogicalResult
1208 matchAndRewrite(cf::CondBranchOp op, OpAdaptor adaptor,
1209 ConversionPatternRewriter &rewriter) const override {
1210 rewriter.replaceOpWithNewOp<cf::CondBranchOp>(
1211 op, adaptor.getCondition(), adaptor.getTrueDestOperands(),
1212 adaptor.getFalseDestOperands(), op.getTrueDest(), op.getFalseDest());
1213 return success();
1214 }
1215};
1216
1217struct BranchOpConversion : public OpConversionPattern<cf::BranchOp> {
1218 using OpConversionPattern::OpConversionPattern;
1219
1220 LogicalResult
1221 matchAndRewrite(cf::BranchOp op, OpAdaptor adaptor,
1222 ConversionPatternRewriter &rewriter) const override {
1223 rewriter.replaceOpWithNewOp<cf::BranchOp>(op, op.getDest(),
1224 adaptor.getDestOperands());
1225 return success();
1226 }
1227};
1228
1229struct CallOpConversion : public OpConversionPattern<func::CallOp> {
1230 using OpConversionPattern::OpConversionPattern;
1231
1232 LogicalResult
1233 matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
1234 ConversionPatternRewriter &rewriter) const override {
1235 SmallVector<Type> convResTypes;
1236 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1237 return failure();
1238 rewriter.replaceOpWithNewOp<func::CallOp>(
1239 op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
1240 return success();
1241 }
1242};
1243
1244struct UnrealizedConversionCastConversion
1245 : public OpConversionPattern<UnrealizedConversionCastOp> {
1246 using OpConversionPattern::OpConversionPattern;
1247
1248 LogicalResult
1249 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
1250 ConversionPatternRewriter &rewriter) const override {
1251 SmallVector<Type> convResTypes;
1252 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1253 return failure();
1254
1255 // Drop the cast if the operand and result types agree after type
1256 // conversion.
1257 if (convResTypes == adaptor.getOperands().getTypes()) {
1258 rewriter.replaceOp(op, adaptor.getOperands());
1259 return success();
1260 }
1261
1262 rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
1263 op, convResTypes, adaptor.getOperands());
1264 return success();
1265 }
1266};
1267
1268struct ShlOpConversion : public OpConversionPattern<ShlOp> {
1269 using OpConversionPattern::OpConversionPattern;
1270
1271 LogicalResult
1272 matchAndRewrite(ShlOp op, OpAdaptor adaptor,
1273 ConversionPatternRewriter &rewriter) const override {
1274 Type resultType = typeConverter->convertType(op.getResult().getType());
1275
1276 // Comb shift operations require the same bit-width for value and amount
1277 Value amount =
1278 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1279 resultType.getIntOrFloatBitWidth(), op->getLoc());
1280 rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
1281 amount, false);
1282 return success();
1283 }
1284};
1285
1286struct ShrOpConversion : public OpConversionPattern<ShrOp> {
1287 using OpConversionPattern::OpConversionPattern;
1288
1289 LogicalResult
1290 matchAndRewrite(ShrOp op, OpAdaptor adaptor,
1291 ConversionPatternRewriter &rewriter) const override {
1292 Type resultType = typeConverter->convertType(op.getResult().getType());
1293
1294 // Comb shift operations require the same bit-width for value and amount
1295 Value amount =
1296 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1297 resultType.getIntOrFloatBitWidth(), op->getLoc());
1298 rewriter.replaceOpWithNewOp<comb::ShrUOp>(
1299 op, resultType, adaptor.getValue(), amount, false);
1300 return success();
1301 }
1302};
1303
1304struct PowUOpConversion : public OpConversionPattern<PowUOp> {
1305 using OpConversionPattern::OpConversionPattern;
1306
1307 LogicalResult
1308 matchAndRewrite(PowUOp op, OpAdaptor adaptor,
1309 ConversionPatternRewriter &rewriter) const override {
1310 Type resultType = typeConverter->convertType(op.getResult().getType());
1311
1312 Location loc = op.getLoc();
1313 auto intType = cast<IntType>(op.getRhs().getType());
1314
1315 // transform a ** b into scf.for 0 to b step 1 { init *= a }, init = 1
1316 Type integerType = rewriter.getIntegerType(intType.getWidth());
1317 Value lowerBound = rewriter.create<hw::ConstantOp>(loc, integerType, 0);
1318 Value upperBound =
1319 rewriter.create<ConversionOp>(loc, integerType, op.getRhs());
1320 Value step = rewriter.create<hw::ConstantOp>(loc, integerType, 1);
1321
1322 Value initVal = rewriter.create<hw::ConstantOp>(loc, resultType, 1);
1323 Value lhsVal = rewriter.create<ConversionOp>(loc, resultType, op.getLhs());
1324
1325 auto forOp = rewriter.create<scf::ForOp>(
1326 loc, lowerBound, upperBound, step, ValueRange(initVal),
1327 [&](OpBuilder &builder, Location loc, Value i, ValueRange iterArgs) {
1328 Value loopVar = iterArgs.front();
1329 Value mul = rewriter.create<comb::MulOp>(loc, lhsVal, loopVar);
1330 rewriter.create<scf::YieldOp>(loc, ValueRange(mul));
1331 });
1332
1333 rewriter.replaceOp(op, forOp.getResult(0));
1334
1335 return success();
1336 }
1337};
1338
1339struct PowSOpConversion : public OpConversionPattern<PowSOp> {
1340 using OpConversionPattern::OpConversionPattern;
1341
1342 LogicalResult
1343 matchAndRewrite(PowSOp op, OpAdaptor adaptor,
1344 ConversionPatternRewriter &rewriter) const override {
1345 Type resultType = typeConverter->convertType(op.getResult().getType());
1346
1347 Location loc = op.getLoc();
1348 auto intType = cast<IntType>(op.getRhs().getType());
1349 // transform a ** b into scf.for 0 to b step 1 { init *= a }, init = 1
1350 Type integerType = rewriter.getIntegerType(intType.getWidth());
1351 Value lhsVal = rewriter.create<ConversionOp>(loc, resultType, op.getLhs());
1352 Value rhsVal = rewriter.create<ConversionOp>(loc, integerType, op.getRhs());
1353 Value constZero = rewriter.create<hw::ConstantOp>(loc, integerType, 0);
1354 Value constZeroResult = rewriter.create<hw::ConstantOp>(loc, resultType, 0);
1355 Value isNegative = rewriter.create<comb::ICmpOp>(loc, ICmpPredicate::slt,
1356 rhsVal, constZero);
1357
1358 // if the exponent is negative, return 0
1359 lhsVal =
1360 rewriter.create<comb::MuxOp>(loc, isNegative, constZeroResult, lhsVal);
1361 Value upperBound =
1362 rewriter.create<comb::MuxOp>(loc, isNegative, constZero, rhsVal);
1363
1364 Value lowerBound = constZero;
1365 Value step = rewriter.create<hw::ConstantOp>(loc, integerType, 1);
1366 Value initVal = rewriter.create<hw::ConstantOp>(loc, resultType, 1);
1367
1368 auto forOp = rewriter.create<scf::ForOp>(
1369 loc, lowerBound, upperBound, step, ValueRange(initVal),
1370 [&](OpBuilder &builder, Location loc, Value i, ValueRange iterArgs) {
1371 auto loopVar = iterArgs.front();
1372 auto mul = rewriter.create<comb::MulOp>(loc, lhsVal, loopVar);
1373 rewriter.create<scf::YieldOp>(loc, ValueRange(mul));
1374 });
1375
1376 rewriter.replaceOp(op, forOp.getResult(0));
1377
1378 return success();
1379 }
1380};
1381
1382struct AShrOpConversion : public OpConversionPattern<AShrOp> {
1383 using OpConversionPattern::OpConversionPattern;
1384
1385 LogicalResult
1386 matchAndRewrite(AShrOp op, OpAdaptor adaptor,
1387 ConversionPatternRewriter &rewriter) const override {
1388 Type resultType = typeConverter->convertType(op.getResult().getType());
1389
1390 // Comb shift operations require the same bit-width for value and amount
1391 Value amount =
1392 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1393 resultType.getIntOrFloatBitWidth(), op->getLoc());
1394 rewriter.replaceOpWithNewOp<comb::ShrSOp>(
1395 op, resultType, adaptor.getValue(), amount, false);
1396 return success();
1397 }
1398};
1399
1400struct ReadOpConversion : public OpConversionPattern<ReadOp> {
1401 using OpConversionPattern::OpConversionPattern;
1402
1403 LogicalResult
1404 matchAndRewrite(ReadOp op, OpAdaptor adaptor,
1405 ConversionPatternRewriter &rewriter) const override {
1406 rewriter.replaceOpWithNewOp<llhd::PrbOp>(op, adaptor.getInput());
1407 return success();
1408 }
1409};
1410
1411struct AssignedVariableOpConversion
1412 : public OpConversionPattern<AssignedVariableOp> {
1413 using OpConversionPattern::OpConversionPattern;
1414
1415 LogicalResult
1416 matchAndRewrite(AssignedVariableOp op, OpAdaptor adaptor,
1417 ConversionPatternRewriter &rewriter) const override {
1418 rewriter.replaceOpWithNewOp<hw::WireOp>(op, adaptor.getInput(),
1419 adaptor.getNameAttr());
1420 return success();
1421 }
1422};
1423
1424template <typename OpTy, unsigned DeltaTime, unsigned EpsilonTime>
1425struct AssignOpConversion : public OpConversionPattern<OpTy> {
1427 using OpAdaptor = typename OpTy::Adaptor;
1428
1429 LogicalResult
1430 matchAndRewrite(OpTy op, OpAdaptor adaptor,
1431 ConversionPatternRewriter &rewriter) const override {
1432 // TODO: When we support delay control in Moore dialect, we need to update
1433 // this conversion.
1434 auto timeAttr = llhd::TimeAttr::get(
1435 op->getContext(), 0U, llvm::StringRef("ns"), DeltaTime, EpsilonTime);
1436 auto time = rewriter.create<llhd::ConstantTimeOp>(op->getLoc(), timeAttr);
1437 rewriter.replaceOpWithNewOp<llhd::DrvOp>(op, adaptor.getDst(),
1438 adaptor.getSrc(), time, Value{});
1439 return success();
1440 }
1441};
1442
1443struct ConditionalOpConversion : public OpConversionPattern<ConditionalOp> {
1444 using OpConversionPattern::OpConversionPattern;
1445
1446 LogicalResult
1447 matchAndRewrite(ConditionalOp op, OpAdaptor adaptor,
1448 ConversionPatternRewriter &rewriter) const override {
1449 // TODO: This lowering is only correct if the condition is two-valued. If
1450 // the condition is X or Z, both branches of the conditional must be
1451 // evaluated and merged with the appropriate lookup table. See documentation
1452 // for `ConditionalOp`.
1453 auto type = typeConverter->convertType(op.getType());
1454
1455 auto hasNoWriteEffect = [](Region &region) {
1456 auto result = region.walk([](Operation *operation) {
1457 if (auto memOp = dyn_cast<MemoryEffectOpInterface>(operation))
1458 if (!memOp.hasEffect<MemoryEffects::Write>() &&
1459 !memOp.hasEffect<MemoryEffects::Free>())
1460 return WalkResult::advance();
1461
1462 if (operation->hasTrait<OpTrait::HasRecursiveMemoryEffects>())
1463 return WalkResult::advance();
1464
1465 return WalkResult::interrupt();
1466 });
1467 return !result.wasInterrupted();
1468 };
1469
1470 if (hasNoWriteEffect(op.getTrueRegion()) &&
1471 hasNoWriteEffect(op.getFalseRegion())) {
1472 Operation *trueTerm = op.getTrueRegion().front().getTerminator();
1473 Operation *falseTerm = op.getFalseRegion().front().getTerminator();
1474
1475 rewriter.inlineBlockBefore(&op.getTrueRegion().front(), op);
1476 rewriter.inlineBlockBefore(&op.getFalseRegion().front(), op);
1477
1478 Value convTrueVal = typeConverter->materializeTargetConversion(
1479 rewriter, op.getLoc(), type, trueTerm->getOperand(0));
1480 Value convFalseVal = typeConverter->materializeTargetConversion(
1481 rewriter, op.getLoc(), type, falseTerm->getOperand(0));
1482
1483 rewriter.eraseOp(trueTerm);
1484 rewriter.eraseOp(falseTerm);
1485
1486 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, adaptor.getCondition(),
1487 convTrueVal, convFalseVal);
1488 return success();
1489 }
1490
1491 auto ifOp =
1492 rewriter.create<scf::IfOp>(op.getLoc(), type, adaptor.getCondition());
1493 rewriter.inlineRegionBefore(op.getTrueRegion(), ifOp.getThenRegion(),
1494 ifOp.getThenRegion().end());
1495 rewriter.inlineRegionBefore(op.getFalseRegion(), ifOp.getElseRegion(),
1496 ifOp.getElseRegion().end());
1497 rewriter.replaceOp(op, ifOp);
1498 return success();
1499 }
1500};
1501
1502struct YieldOpConversion : public OpConversionPattern<YieldOp> {
1503 using OpConversionPattern::OpConversionPattern;
1504
1505 LogicalResult
1506 matchAndRewrite(YieldOp op, OpAdaptor adaptor,
1507 ConversionPatternRewriter &rewriter) const override {
1508 rewriter.replaceOpWithNewOp<scf::YieldOp>(op, adaptor.getResult());
1509 return success();
1510 }
1511};
1512
1513template <typename SourceOp>
1514struct InPlaceOpConversion : public OpConversionPattern<SourceOp> {
1516 using OpAdaptor = typename SourceOp::Adaptor;
1517
1518 LogicalResult
1519 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1520 ConversionPatternRewriter &rewriter) const override {
1521 rewriter.modifyOpInPlace(op,
1522 [&]() { op->setOperands(adaptor.getOperands()); });
1523 return success();
1524 }
1525};
1526
1527template <typename MooreOpTy, typename VerifOpTy>
1528struct AssertLikeOpConversion : public OpConversionPattern<MooreOpTy> {
1530 using OpAdaptor = typename MooreOpTy::Adaptor;
1531
1532 LogicalResult
1533 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
1534 ConversionPatternRewriter &rewriter) const override {
1535 StringAttr label =
1536 op.getLabel().has_value()
1537 ? StringAttr::get(op->getContext(), op.getLabel().value())
1538 : StringAttr::get(op->getContext());
1539 rewriter.replaceOpWithNewOp<VerifOpTy>(op, adaptor.getCond(), mlir::Value(),
1540 label);
1541 return success();
1542 }
1543};
1544
1545//===----------------------------------------------------------------------===//
1546// Format String Conversion
1547//===----------------------------------------------------------------------===//
1548
1549struct FormatLiteralOpConversion : public OpConversionPattern<FormatLiteralOp> {
1550 using OpConversionPattern::OpConversionPattern;
1551
1552 LogicalResult
1553 matchAndRewrite(FormatLiteralOp op, OpAdaptor adaptor,
1554 ConversionPatternRewriter &rewriter) const override {
1555 rewriter.replaceOpWithNewOp<sim::FormatLitOp>(op, adaptor.getLiteral());
1556 return success();
1557 }
1558};
1559
1560struct FormatConcatOpConversion : public OpConversionPattern<FormatConcatOp> {
1561 using OpConversionPattern::OpConversionPattern;
1562
1563 LogicalResult
1564 matchAndRewrite(FormatConcatOp op, OpAdaptor adaptor,
1565 ConversionPatternRewriter &rewriter) const override {
1566 rewriter.replaceOpWithNewOp<sim::FormatStringConcatOp>(op,
1567 adaptor.getInputs());
1568 return success();
1569 }
1570};
1571
1572struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
1573 using OpConversionPattern::OpConversionPattern;
1574
1575 LogicalResult
1576 matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
1577 ConversionPatternRewriter &rewriter) const override {
1578 // TODO: These should honor the width, alignment, and padding.
1579 switch (op.getFormat()) {
1580 case IntFormat::Decimal:
1581 rewriter.replaceOpWithNewOp<sim::FormatDecOp>(op, adaptor.getValue());
1582 return success();
1583 case IntFormat::Binary:
1584 rewriter.replaceOpWithNewOp<sim::FormatBinOp>(op, adaptor.getValue());
1585 return success();
1586 case IntFormat::HexLower:
1587 case IntFormat::HexUpper:
1588 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(op, adaptor.getValue());
1589 return success();
1590 default:
1591 return rewriter.notifyMatchFailure(op, "unsupported int format");
1592 }
1593 }
1594};
1595
1596struct DisplayBIOpConversion : public OpConversionPattern<DisplayBIOp> {
1597 using OpConversionPattern::OpConversionPattern;
1598
1599 LogicalResult
1600 matchAndRewrite(DisplayBIOp op, OpAdaptor adaptor,
1601 ConversionPatternRewriter &rewriter) const override {
1602 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
1603 op, adaptor.getMessage());
1604 return success();
1605 }
1606};
1607
1608} // namespace
1609
1610//===----------------------------------------------------------------------===//
1611// Conversion Infrastructure
1612//===----------------------------------------------------------------------===//
1613
1614static void populateLegality(ConversionTarget &target,
1615 const TypeConverter &converter) {
1616 target.addIllegalDialect<MooreDialect>();
1617 target.addLegalDialect<comb::CombDialect>();
1618 target.addLegalDialect<hw::HWDialect>();
1619 target.addLegalDialect<llhd::LLHDDialect>();
1620 target.addLegalDialect<mlir::BuiltinDialect>();
1621 target.addLegalDialect<sim::SimDialect>();
1622 target.addLegalDialect<verif::VerifDialect>();
1623
1624 target.addLegalOp<debug::ScopeOp>();
1625
1626 target.addDynamicallyLegalOp<
1627 cf::CondBranchOp, cf::BranchOp, scf::YieldOp, func::CallOp,
1628 func::ReturnOp, UnrealizedConversionCastOp, hw::OutputOp, hw::InstanceOp,
1629 debug::ArrayOp, debug::StructOp, debug::VariableOp>(
1630 [&](Operation *op) { return converter.isLegal(op); });
1631
1632 target.addDynamicallyLegalOp<scf::IfOp, scf::ForOp, scf::ExecuteRegionOp,
1633 scf::WhileOp, scf::ForallOp>([&](Operation *op) {
1634 return converter.isLegal(op) && !op->getParentOfType<llhd::ProcessOp>();
1635 });
1636
1637 target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
1638 return converter.isSignatureLegal(op.getFunctionType()) &&
1639 converter.isLegal(&op.getFunctionBody());
1640 });
1641
1642 target.addDynamicallyLegalOp<hw::HWModuleOp>([&](hw::HWModuleOp op) {
1643 return converter.isSignatureLegal(op.getModuleType().getFuncType()) &&
1644 converter.isLegal(&op.getBody());
1645 });
1646}
1647
1648static void populateTypeConversion(TypeConverter &typeConverter) {
1649 typeConverter.addConversion([&](IntType type) {
1650 return IntegerType::get(type.getContext(), type.getWidth());
1651 });
1652
1653 typeConverter.addConversion([&](FormatStringType type) {
1654 return sim::FormatStringType::get(type.getContext());
1655 });
1656
1657 typeConverter.addConversion([&](ArrayType type) -> std::optional<Type> {
1658 if (auto elementType = typeConverter.convertType(type.getElementType()))
1659 return hw::ArrayType::get(elementType, type.getSize());
1660 return {};
1661 });
1662
1663 // FIXME: Unpacked arrays support more element types than their packed
1664 // variants, and as such, mapping them to hw::Array is somewhat naive. See
1665 // also the analogous note below concerning unpacked struct type conversion.
1666 typeConverter.addConversion(
1667 [&](UnpackedArrayType type) -> std::optional<Type> {
1668 if (auto elementType = typeConverter.convertType(type.getElementType()))
1669 return hw::ArrayType::get(elementType, type.getSize());
1670 return {};
1671 });
1672
1673 typeConverter.addConversion([&](StructType type) -> std::optional<Type> {
1674 SmallVector<hw::StructType::FieldInfo> fields;
1675 for (auto field : type.getMembers()) {
1676 hw::StructType::FieldInfo info;
1677 info.type = typeConverter.convertType(field.type);
1678 if (!info.type)
1679 return {};
1680 info.name = field.name;
1681 fields.push_back(info);
1682 }
1683 return hw::StructType::get(type.getContext(), fields);
1684 });
1685
1686 // FIXME: Mapping unpacked struct type to struct type in hw dialect may be a
1687 // plain solution. The packed and unpacked data structures have some
1688 // differences though they look similarily. The packed data structure is
1689 // contiguous in memory but another is opposite. The differences will affect
1690 // data layout and granularity of event tracking in simulation.
1691 typeConverter.addConversion(
1692 [&](UnpackedStructType type) -> std::optional<Type> {
1693 SmallVector<hw::StructType::FieldInfo> fields;
1694 for (auto field : type.getMembers()) {
1695 hw::StructType::FieldInfo info;
1696 info.type = typeConverter.convertType(field.type);
1697 if (!info.type)
1698 return {};
1699 info.name = field.name;
1700 fields.push_back(info);
1701 }
1702 return hw::StructType::get(type.getContext(), fields);
1703 });
1704
1705 typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
1706 if (auto innerType = typeConverter.convertType(type.getNestedType()))
1707 return hw::InOutType::get(innerType);
1708 return {};
1709 });
1710
1711 // Valid target types.
1712 typeConverter.addConversion([](IntegerType type) { return type; });
1713 typeConverter.addConversion([](debug::ArrayType type) { return type; });
1714 typeConverter.addConversion([](debug::ScopeType type) { return type; });
1715 typeConverter.addConversion([](debug::StructType type) { return type; });
1716
1717 typeConverter.addConversion([&](hw::InOutType type) -> std::optional<Type> {
1718 if (auto innerType = typeConverter.convertType(type.getElementType()))
1719 return hw::InOutType::get(innerType);
1720 return {};
1721 });
1722
1723 typeConverter.addConversion([&](hw::ArrayType type) -> std::optional<Type> {
1724 if (auto elementType = typeConverter.convertType(type.getElementType()))
1725 return hw::ArrayType::get(elementType, type.getNumElements());
1726 return {};
1727 });
1728
1729 typeConverter.addConversion([&](hw::StructType type) -> std::optional<Type> {
1730 SmallVector<hw::StructType::FieldInfo> fields;
1731 for (auto field : type.getElements()) {
1732 hw::StructType::FieldInfo info;
1733 info.type = typeConverter.convertType(field.type);
1734 if (!info.type)
1735 return {};
1736 info.name = field.name;
1737 fields.push_back(info);
1738 }
1739 return hw::StructType::get(type.getContext(), fields);
1740 });
1741
1742 typeConverter.addTargetMaterialization(
1743 [&](mlir::OpBuilder &builder, mlir::Type resultType,
1744 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
1745 if (inputs.size() != 1 || !inputs[0])
1746 return Value();
1747 return builder
1748 .create<UnrealizedConversionCastOp>(loc, resultType, inputs[0])
1749 .getResult(0);
1750 });
1751
1752 typeConverter.addSourceMaterialization(
1753 [&](mlir::OpBuilder &builder, mlir::Type resultType,
1754 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
1755 if (inputs.size() != 1)
1756 return Value();
1757 return builder
1758 .create<UnrealizedConversionCastOp>(loc, resultType, inputs[0])
1759 ->getResult(0);
1760 });
1761}
1762
1763static void populateOpConversion(RewritePatternSet &patterns,
1764 TypeConverter &typeConverter) {
1765 auto *context = patterns.getContext();
1766 // clang-format off
1767 patterns.add<
1768 // Patterns of declaration operations.
1769 VariableOpConversion,
1770 NetOpConversion,
1771
1772 // Patterns for conversion operations.
1773 ConversionOpConversion,
1774 TruncOpConversion,
1775 ZExtOpConversion,
1776 SExtOpConversion,
1777
1778 // Patterns of miscellaneous operations.
1779 ConstantOpConv, ConcatOpConversion, ReplicateOpConversion,
1780 ExtractOpConversion, DynExtractOpConversion, DynExtractRefOpConversion,
1781 ReadOpConversion,
1782 StructExtractOpConversion, StructExtractRefOpConversion,
1783 ExtractRefOpConversion, StructCreateOpConversion, ConditionalOpConversion,
1784 YieldOpConversion, OutputOpConversion, StringConstantOpConv,
1785
1786 // Patterns of unary operations.
1787 ReduceAndOpConversion, ReduceOrOpConversion, ReduceXorOpConversion,
1788 BoolCastOpConversion, NotOpConversion, NegOpConversion,
1789
1790 // Patterns of binary operations.
1791 BinaryOpConversion<AddOp, comb::AddOp>,
1792 BinaryOpConversion<SubOp, comb::SubOp>,
1793 BinaryOpConversion<MulOp, comb::MulOp>,
1794 BinaryOpConversion<DivUOp, comb::DivUOp>,
1795 BinaryOpConversion<DivSOp, comb::DivSOp>,
1796 BinaryOpConversion<ModUOp, comb::ModUOp>,
1797 BinaryOpConversion<ModSOp, comb::ModSOp>,
1798 BinaryOpConversion<AndOp, comb::AndOp>,
1799 BinaryOpConversion<OrOp, comb::OrOp>,
1800 BinaryOpConversion<XorOp, comb::XorOp>,
1801
1802 // Patterns of power operations.
1803 PowUOpConversion, PowSOpConversion,
1804
1805 // Patterns of relational operations.
1806 ICmpOpConversion<UltOp, ICmpPredicate::ult>,
1807 ICmpOpConversion<SltOp, ICmpPredicate::slt>,
1808 ICmpOpConversion<UleOp, ICmpPredicate::ule>,
1809 ICmpOpConversion<SleOp, ICmpPredicate::sle>,
1810 ICmpOpConversion<UgtOp, ICmpPredicate::ugt>,
1811 ICmpOpConversion<SgtOp, ICmpPredicate::sgt>,
1812 ICmpOpConversion<UgeOp, ICmpPredicate::uge>,
1813 ICmpOpConversion<SgeOp, ICmpPredicate::sge>,
1814 ICmpOpConversion<EqOp, ICmpPredicate::eq>,
1815 ICmpOpConversion<NeOp, ICmpPredicate::ne>,
1816 ICmpOpConversion<CaseEqOp, ICmpPredicate::ceq>,
1817 ICmpOpConversion<CaseNeOp, ICmpPredicate::cne>,
1818 ICmpOpConversion<WildcardEqOp, ICmpPredicate::weq>,
1819 ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
1820 CaseXZEqOpConversion<CaseZEqOp, true>,
1821 CaseXZEqOpConversion<CaseXZEqOp, false>,
1822
1823 // Patterns of structural operations.
1824 SVModuleOpConversion, InstanceOpConversion, ProcedureOpConversion, WaitEventOpConversion,
1825
1826 // Patterns of shifting operations.
1827 ShrOpConversion, ShlOpConversion, AShrOpConversion,
1828
1829 // Patterns of assignment operations.
1830 AssignOpConversion<ContinuousAssignOp, 0, 1>,
1831 AssignOpConversion<BlockingAssignOp, 0, 1>,
1832 AssignOpConversion<NonBlockingAssignOp, 1, 0>,
1833 AssignedVariableOpConversion,
1834
1835 // Patterns of branch operations.
1836 CondBranchOpConversion, BranchOpConversion,
1837
1838 // Patterns of other operations outside Moore dialect.
1839 HWInstanceOpConversion, ReturnOpConversion,
1840 CallOpConversion, UnrealizedConversionCastConversion,
1841 InPlaceOpConversion<debug::ArrayOp>,
1842 InPlaceOpConversion<debug::StructOp>,
1843 InPlaceOpConversion<debug::VariableOp>,
1844
1845 // Patterns of assert-like operations
1846 AssertLikeOpConversion<AssertOp, verif::AssertOp>,
1847 AssertLikeOpConversion<AssumeOp, verif::AssumeOp>,
1848 AssertLikeOpConversion<CoverOp, verif::CoverOp>,
1849
1850 // Format strings.
1851 FormatLiteralOpConversion,
1852 FormatConcatOpConversion,
1853 FormatIntOpConversion,
1854 DisplayBIOpConversion
1855 >(typeConverter, context);
1856 // clang-format on
1857
1858 mlir::populateAnyFunctionOpInterfaceTypeConversionPattern(patterns,
1859 typeConverter);
1860 hw::populateHWModuleLikeTypeConversionPattern(
1861 hw::HWModuleOp::getOperationName(), patterns, typeConverter);
1862 populateSCFToControlFlowConversionPatterns(patterns);
1863 populateArithToCombPatterns(patterns, typeConverter);
1864}
1865
1866//===----------------------------------------------------------------------===//
1867// Moore to Core Conversion Pass
1868//===----------------------------------------------------------------------===//
1869
1870namespace {
1871struct MooreToCorePass
1872 : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
1873 void runOnOperation() override;
1874};
1875} // namespace
1876
1877/// Create a Moore to core dialects conversion pass.
1878std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
1879 return std::make_unique<MooreToCorePass>();
1880}
1881
1882/// This is the main entrypoint for the Moore to Core conversion pass.
1883void MooreToCorePass::runOnOperation() {
1884 MLIRContext &context = getContext();
1885 ModuleOp module = getOperation();
1886
1887 IRRewriter rewriter(module);
1888 (void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
1889
1890 ConversionTarget target(context);
1891 TypeConverter typeConverter;
1892 RewritePatternSet patterns(&context);
1893 populateTypeConversion(typeConverter);
1894 populateLegality(target, typeConverter);
1895 populateOpConversion(patterns, typeConverter);
1896
1897 if (failed(applyFullConversion(module, target, std::move(patterns))))
1898 signalPassFailure();
1899}
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.