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