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