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