CIRCT 23.0.0git
Loading...
Searching...
No Matches
MooreToCore.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
21#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
22#include "mlir/Dialect/Arith/IR/Arith.h"
23#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
24#include "mlir/Dialect/ControlFlow/Transforms/StructuralTypeConversions.h"
25#include "mlir/Dialect/Func/IR/FuncOps.h"
26#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
27#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
28#include "mlir/Dialect/Math/IR/Math.h"
29#include "mlir/Dialect/SCF/IR/SCF.h"
30#include "mlir/IR/BuiltinDialect.h"
31#include "mlir/IR/Iterators.h"
32#include "mlir/Interfaces/SideEffectInterfaces.h"
33#include "mlir/Pass/Pass.h"
34#include "mlir/Transforms/DialectConversion.h"
35#include "mlir/Transforms/RegionUtils.h"
36#include "llvm/ADT/TypeSwitch.h"
37#include "llvm/IR/DerivedTypes.h"
38
39namespace circt {
40#define GEN_PASS_DEF_CONVERTMOORETOCORE
41#include "circt/Conversion/Passes.h.inc"
42} // namespace circt
43
44using namespace mlir;
45using namespace circt;
46using namespace moore;
47
48using comb::ICmpPredicate;
49using llvm::SmallDenseSet;
50
51namespace {
52
53/// Cache for identified structs and field GEP paths keyed by class symbol.
54struct ClassTypeCache {
55 struct ClassStructInfo {
56 LLVM::LLVMStructType classBody;
57
58 // field name -> GEP path inside ident (excluding the leading pointer index)
59 DenseMap<StringRef, SmallVector<unsigned, 2>> propertyPath;
60
61 // TODO: Add classVTable in here.
62 /// Record/overwrite the field path to a single property for a class.
63 void setFieldPath(StringRef propertyName, ArrayRef<unsigned> path) {
64 this->propertyPath[propertyName] =
65 SmallVector<unsigned, 2>(path.begin(), path.end());
66 }
67
68 /// Lookup the full GEP path for a (class, field).
69 std::optional<ArrayRef<unsigned>>
70 getFieldPath(StringRef propertySym) const {
71 if (auto prop = this->propertyPath.find(propertySym);
72 prop != this->propertyPath.end())
73 return ArrayRef<unsigned>(prop->second);
74 return std::nullopt;
75 }
76 };
77
78 /// Record the identified struct body for a class.
79 /// Implicitly finalizes the class to struct conversion.
80 void setClassInfo(SymbolRefAttr classSym, const ClassStructInfo &info) {
81 auto &dst = classToStructMap[classSym];
82 dst = info;
83 }
84
85 /// Lookup the identified struct body for a class.
86 std::optional<ClassStructInfo> getStructInfo(SymbolRefAttr classSym) const {
87 if (auto it = classToStructMap.find(classSym); it != classToStructMap.end())
88 return it->second;
89 return std::nullopt;
90 }
91
92private:
93 // Keyed by the SymbolRefAttr of the class.
94 // Kept private so all accesses are done with helpers which preserve
95 // invariants
96 DenseMap<Attribute, ClassStructInfo> classToStructMap;
97};
98
99/// Ensure we have `declare i8* @malloc(i64)` (opaque ptr prints as !llvm.ptr).
100static LLVM::LLVMFuncOp getOrCreateMalloc(ModuleOp mod, OpBuilder &b) {
101 if (auto f = mod.lookupSymbol<LLVM::LLVMFuncOp>("malloc"))
102 return f;
103
104 OpBuilder::InsertionGuard g(b);
105 b.setInsertionPointToStart(mod.getBody());
106
107 auto i64Ty = IntegerType::get(mod.getContext(), 64);
108 auto ptrTy = LLVM::LLVMPointerType::get(mod.getContext()); // opaque pointer
109 auto fnTy = LLVM::LLVMFunctionType::get(ptrTy, {i64Ty}, false);
110
111 auto fn = LLVM::LLVMFuncOp::create(b, mod.getLoc(), "malloc", fnTy);
112 // Link this in from somewhere else.
113 fn.setLinkage(LLVM::Linkage::External);
114 return fn;
115}
116
117/// Helper function to create an opaque LLVM Struct Type which corresponds
118/// to the sym
119static LLVM::LLVMStructType getOrCreateOpaqueStruct(MLIRContext *ctx,
120 SymbolRefAttr className) {
121 return LLVM::LLVMStructType::getIdentified(ctx, className.getRootReference());
122}
123
124static LogicalResult resolveClassStructBody(ClassDeclOp op,
125 TypeConverter const &typeConverter,
126 ClassTypeCache &cache) {
127
128 auto classSym = SymbolRefAttr::get(op.getSymNameAttr());
129 auto structInfo = cache.getStructInfo(classSym);
130 if (structInfo)
131 // We already have a resolved class struct body.
132 return success();
133
134 // Otherwise we need to resolve.
135 ClassTypeCache::ClassStructInfo structBody;
136 SmallVector<Type> structBodyMembers;
137
138 // Base-first (prefix) layout for single inheritance.
139 unsigned derivedStartIdx = 0;
140
141 if (auto baseClass = op.getBaseAttr()) {
142
143 ModuleOp mod = op->getParentOfType<ModuleOp>();
144 auto *opSym = mod.lookupSymbol(baseClass);
145 auto classDeclOp = cast<ClassDeclOp>(opSym);
146
147 if (failed(resolveClassStructBody(classDeclOp, typeConverter, cache)))
148 return failure();
149
150 // Process base class' struct layout first
151 auto baseClassStruct = cache.getStructInfo(baseClass);
152 structBodyMembers.push_back(baseClassStruct->classBody);
153 derivedStartIdx = 1;
154
155 // Inherit base field paths with a leading 0.
156 for (auto &kv : baseClassStruct->propertyPath) {
157 SmallVector<unsigned, 2> path;
158 path.push_back(0); // into base subobject
159 path.append(kv.second.begin(), kv.second.end());
160 structBody.setFieldPath(kv.first, path);
161 }
162 }
163
164 // Properties in source order.
165 unsigned iterator = derivedStartIdx;
166 auto &block = op.getBody().front();
167 for (Operation &child : block) {
168 if (auto prop = dyn_cast<ClassPropertyDeclOp>(child)) {
169 Type mooreTy = prop.getPropertyType();
170 Type llvmTy = typeConverter.convertType(mooreTy);
171 if (!llvmTy)
172 return prop.emitOpError()
173 << "failed to convert property type " << mooreTy;
174
175 structBodyMembers.push_back(llvmTy);
176
177 // Derived field path: either {i} or {1+i} if base is present.
178 SmallVector<unsigned, 2> path{iterator};
179 structBody.setFieldPath(prop.getSymName(), path);
180 ++iterator;
181 }
182 }
183
184 // TODO: Handle vtable generation over ClassMethodDeclOp here.
185 auto llvmStructTy = getOrCreateOpaqueStruct(op.getContext(), classSym);
186 // Empty structs may be kept opaque
187 if (!structBodyMembers.empty() &&
188 failed(llvmStructTy.setBody(structBodyMembers, false)))
189 return op.emitOpError() << "Failed to set LLVM Struct body";
190
191 structBody.classBody = llvmStructTy;
192 cache.setClassInfo(classSym, structBody);
193
194 return success();
195}
196
197/// Convenience overload that looks up ClassDeclOp
198static LogicalResult resolveClassStructBody(ModuleOp mod, SymbolRefAttr op,
199 TypeConverter const &typeConverter,
200 ClassTypeCache &cache) {
201 auto classDeclOp = cast<ClassDeclOp>(*mod.lookupSymbol(op));
202 return resolveClassStructBody(classDeclOp, typeConverter, cache);
203}
204
205/// Returns the passed value if the integer width is already correct.
206/// Zero-extends if it is too narrow.
207/// Truncates if the integer is too wide and the truncated part is zero, if it
208/// is not zero it returns the max value integer of target-width.
209static Value adjustIntegerWidth(OpBuilder &builder, Value value,
210 uint32_t targetWidth, Location loc) {
211 uint32_t intWidth = value.getType().getIntOrFloatBitWidth();
212 if (intWidth == targetWidth)
213 return value;
214
215 if (intWidth < targetWidth) {
216 Value zeroExt = hw::ConstantOp::create(
217 builder, loc, builder.getIntegerType(targetWidth - intWidth), 0);
218 return comb::ConcatOp::create(builder, loc, ValueRange{zeroExt, value});
219 }
220
221 Value hi = comb::ExtractOp::create(builder, loc, value, targetWidth,
222 intWidth - targetWidth);
223 Value zero = hw::ConstantOp::create(
224 builder, loc, builder.getIntegerType(intWidth - targetWidth), 0);
225 Value isZero = comb::ICmpOp::create(builder, loc, comb::ICmpPredicate::eq, hi,
226 zero, false);
227 Value lo = comb::ExtractOp::create(builder, loc, value, 0, targetWidth);
228 Value max = hw::ConstantOp::create(builder, loc,
229 builder.getIntegerType(targetWidth), -1);
230 return comb::MuxOp::create(builder, loc, isZero, lo, max, false);
231}
232
233/// Get the ModulePortInfo from a SVModuleOp.
234static FailureOr<hw::ModulePortInfo>
235getModulePortInfo(const TypeConverter &typeConverter, SVModuleOp op) {
236 size_t inputNum = 0;
237 size_t resultNum = 0;
238 auto moduleTy = op.getModuleType();
239 SmallVector<hw::PortInfo> ports;
240 ports.reserve(moduleTy.getNumPorts());
241
242 for (auto port : moduleTy.getPorts()) {
243 Type portTy = typeConverter.convertType(port.type);
244 if (!portTy) {
245 return op.emitOpError("port '")
246 << port.name << "' has unsupported type " << port.type
247 << " that cannot be converted to hardware type";
248 }
249 if (port.dir == hw::ModulePort::Direction::Output) {
250 ports.push_back(
251 hw::PortInfo({{port.name, portTy, port.dir}, resultNum++, {}}));
252 } else {
253 // FIXME: Once we support net<...>, ref<...> type to represent type of
254 // special port like inout or ref port which is not a input or output
255 // port. It can change to generate corresponding types for direction of
256 // port or do specified operation to it. Now inout and ref port is treated
257 // as input port.
258 ports.push_back(
259 hw::PortInfo({{port.name, portTy, port.dir}, inputNum++, {}}));
260 }
261 }
262
263 return hw::ModulePortInfo(ports);
264}
265
266//===----------------------------------------------------------------------===//
267// Structural Conversion
268//===----------------------------------------------------------------------===//
269
270struct SVModuleOpConversion : public OpConversionPattern<SVModuleOp> {
271 using OpConversionPattern::OpConversionPattern;
272
273 LogicalResult
274 matchAndRewrite(SVModuleOp op, OpAdaptor adaptor,
275 ConversionPatternRewriter &rewriter) const override {
276 rewriter.setInsertionPoint(op);
277
278 // Create the hw.module to replace moore.module
279 auto portInfo = getModulePortInfo(*typeConverter, op);
280 if (failed(portInfo))
281 return failure();
282
283 auto hwModuleOp = hw::HWModuleOp::create(rewriter, op.getLoc(),
284 op.getSymNameAttr(), *portInfo);
285 // Make hw.module have the same visibility as the moore.module.
286 // The entry/top level module is public, otherwise is private.
287 SymbolTable::setSymbolVisibility(hwModuleOp,
288 SymbolTable::getSymbolVisibility(op));
289 rewriter.eraseBlock(hwModuleOp.getBodyBlock());
290 if (failed(
291 rewriter.convertRegionTypes(&op.getBodyRegion(), *typeConverter)))
292 return failure();
293 rewriter.inlineRegionBefore(op.getBodyRegion(), hwModuleOp.getBodyRegion(),
294 hwModuleOp.getBodyRegion().end());
295
296 // Erase the original op
297 rewriter.eraseOp(op);
298 return success();
299 }
300};
301
302struct OutputOpConversion : public OpConversionPattern<OutputOp> {
303 using OpConversionPattern::OpConversionPattern;
304
305 LogicalResult
306 matchAndRewrite(OutputOp op, OpAdaptor adaptor,
307 ConversionPatternRewriter &rewriter) const override {
308 rewriter.replaceOpWithNewOp<hw::OutputOp>(op, adaptor.getOperands());
309 return success();
310 }
311};
312
313struct InstanceOpConversion : public OpConversionPattern<InstanceOp> {
314 using OpConversionPattern::OpConversionPattern;
315
316 LogicalResult
317 matchAndRewrite(InstanceOp op, OpAdaptor adaptor,
318 ConversionPatternRewriter &rewriter) const override {
319 auto instName = op.getInstanceNameAttr();
320 auto moduleName = op.getModuleNameAttr();
321
322 // Create the new hw instanceOp to replace the original one.
323 rewriter.setInsertionPoint(op);
324 auto instOp = hw::InstanceOp::create(
325 rewriter, op.getLoc(), op.getResultTypes(), instName, moduleName,
326 op.getInputs(), op.getInputNamesAttr(), op.getOutputNamesAttr(),
327 /*Parameter*/ rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr,
328 /*doNotPrint*/ nullptr);
329
330 // Replace uses chain and erase the original op.
331 op.replaceAllUsesWith(instOp.getResults());
332 rewriter.eraseOp(op);
333 return success();
334 }
335};
336
337static void getValuesToObserve(Region *region,
338 function_ref<void(Value)> setInsertionPoint,
339 const TypeConverter *typeConverter,
340 ConversionPatternRewriter &rewriter,
341 SmallVector<Value> &observeValues) {
342 SmallDenseSet<Value> alreadyObserved;
343 Location loc = region->getLoc();
344
345 auto probeIfSignal = [&](Value value) -> Value {
346 if (!isa<llhd::RefType>(value.getType()))
347 return value;
348 return llhd::ProbeOp::create(rewriter, loc, value);
349 };
350
351 region->getParentOp()->walk<WalkOrder::PreOrder, ForwardDominanceIterator<>>(
352 [&](Operation *operation) {
353 for (auto value : operation->getOperands()) {
354 if (isa<BlockArgument>(value))
355 value = rewriter.getRemappedValue(value);
356
357 if (region->isAncestor(value.getParentRegion()))
358 continue;
359 if (auto *defOp = value.getDefiningOp();
360 defOp && defOp->hasTrait<OpTrait::ConstantLike>())
361 continue;
362 if (!alreadyObserved.insert(value).second)
363 continue;
364
365 OpBuilder::InsertionGuard g(rewriter);
366 if (auto remapped = rewriter.getRemappedValue(value)) {
367 setInsertionPoint(remapped);
368 observeValues.push_back(probeIfSignal(remapped));
369 } else {
370 setInsertionPoint(value);
371 auto type = typeConverter->convertType(value.getType());
372 auto converted = typeConverter->materializeTargetConversion(
373 rewriter, loc, type, value);
374 observeValues.push_back(probeIfSignal(converted));
375 }
376 }
377 });
378}
379
380struct ProcedureOpConversion : public OpConversionPattern<ProcedureOp> {
381 using OpConversionPattern::OpConversionPattern;
382
383 LogicalResult
384 matchAndRewrite(ProcedureOp op, OpAdaptor adaptor,
385 ConversionPatternRewriter &rewriter) const override {
386 // Collect values to observe before we do any modifications to the region.
387 SmallVector<Value> observedValues;
388 if (op.getKind() == ProcedureKind::AlwaysComb ||
389 op.getKind() == ProcedureKind::AlwaysLatch) {
390 auto setInsertionPoint = [&](Value value) {
391 rewriter.setInsertionPoint(op);
392 };
393 getValuesToObserve(&op.getBody(), setInsertionPoint, typeConverter,
394 rewriter, observedValues);
395 }
396
397 auto loc = op.getLoc();
398 if (failed(rewriter.convertRegionTypes(&op.getBody(), *typeConverter)))
399 return failure();
400
401 // Handle initial and final procedures. These lower to a corresponding
402 // `llhd.process` or `llhd.final` op that executes the body and then halts.
403 if (op.getKind() == ProcedureKind::Initial ||
404 op.getKind() == ProcedureKind::Final) {
405 Operation *newOp;
406 if (op.getKind() == ProcedureKind::Initial)
407 newOp = llhd::ProcessOp::create(rewriter, loc, TypeRange{});
408 else
409 newOp = llhd::FinalOp::create(rewriter, loc);
410 auto &body = newOp->getRegion(0);
411 rewriter.inlineRegionBefore(op.getBody(), body, body.end());
412 for (auto returnOp :
413 llvm::make_early_inc_range(body.getOps<ReturnOp>())) {
414 rewriter.setInsertionPoint(returnOp);
415 rewriter.replaceOpWithNewOp<llhd::HaltOp>(returnOp, ValueRange{});
416 }
417 rewriter.eraseOp(op);
418 return success();
419 }
420
421 // All other procedures lower to a an `llhd.process`.
422 auto newOp = llhd::ProcessOp::create(rewriter, loc, TypeRange{});
423
424 // We need to add an empty entry block because it is not allowed in MLIR to
425 // branch back to the entry block. Instead we put the logic in the second
426 // block and branch to that.
427 rewriter.createBlock(&newOp.getBody());
428 auto *block = &op.getBody().front();
429 cf::BranchOp::create(rewriter, loc, block);
430 rewriter.inlineRegionBefore(op.getBody(), newOp.getBody(),
431 newOp.getBody().end());
432
433 // Add special handling for `always_comb` and `always_latch` procedures.
434 // These run once at simulation startup and then implicitly wait for any of
435 // the values they access to change before running again. To implement this,
436 // we create another basic block that contains the implicit wait, and make
437 // all `moore.return` ops branch to that wait block instead of immediately
438 // jumping back up to the body.
439 if (op.getKind() == ProcedureKind::AlwaysComb ||
440 op.getKind() == ProcedureKind::AlwaysLatch) {
441 Block *waitBlock = rewriter.createBlock(&newOp.getBody());
442 llhd::WaitOp::create(rewriter, loc, ValueRange{}, Value(), observedValues,
443 ValueRange{}, block);
444 block = waitBlock;
445 }
446
447 // Make all `moore.return` ops branch back up to the beginning of the
448 // process, or the wait block created above for `always_comb` and
449 // `always_latch` procedures.
450 for (auto returnOp : llvm::make_early_inc_range(newOp.getOps<ReturnOp>())) {
451 rewriter.setInsertionPoint(returnOp);
452 cf::BranchOp::create(rewriter, loc, block);
453 rewriter.eraseOp(returnOp);
454 }
455
456 rewriter.eraseOp(op);
457 return success();
458 }
459};
460
461struct WaitEventOpConversion : public OpConversionPattern<WaitEventOp> {
462 using OpConversionPattern::OpConversionPattern;
463
464 LogicalResult
465 matchAndRewrite(WaitEventOp op, OpAdaptor adaptor,
466 ConversionPatternRewriter &rewriter) const override {
467 // In order to convert the `wait_event` op we need to create three separate
468 // blocks at the location of the op:
469 //
470 // - A "wait" block that reads the current state of any values used to
471 // detect events and then waits until any of those values change. When a
472 // change occurs, control transfers to the "check" block.
473 // - A "check" block which is executed after any interesting signal has
474 // changed. This is where any `detect_event` ops read the current state of
475 // interesting values and compare them against their state before the wait
476 // in order to detect an event. If any events were detected, control
477 // transfers to the "resume" block; otherwise control goes back to the
478 // "wait" block.
479 // - A "resume" block which holds any ops after the `wait_event` op. This is
480 // where control is expected to resume after an event has happened.
481 //
482 // Block structure before:
483 // opA
484 // moore.wait_event { ... }
485 // opB
486 //
487 // Block structure after:
488 // opA
489 // cf.br ^wait
490 // ^wait:
491 // <read "before" values>
492 // llhd.wait ^check, ...
493 // ^check:
494 // <read "after" values>
495 // <detect edges>
496 // cf.cond_br %event, ^resume, ^wait
497 // ^resume:
498 // opB
499 auto *resumeBlock =
500 rewriter.splitBlock(op->getBlock(), ++Block::iterator(op));
501
502 // If the 'wait_event' op is empty, we can lower it to a 'llhd.wait' op
503 // without any observed values, but since the process will never wake up
504 // from suspension anyway, we can also just terminate it using the
505 // 'llhd.halt' op.
506 if (op.getBody().front().empty()) {
507 // Let the cleanup iteration after the dialect conversion clean up all
508 // remaining unreachable blocks.
509 rewriter.replaceOpWithNewOp<llhd::HaltOp>(op, ValueRange{});
510 return success();
511 }
512
513 auto *waitBlock = rewriter.createBlock(resumeBlock);
514 auto *checkBlock = rewriter.createBlock(resumeBlock);
515
516 auto loc = op.getLoc();
517 rewriter.setInsertionPoint(op);
518 cf::BranchOp::create(rewriter, loc, waitBlock);
519
520 // We need to inline two copies of the `wait_event`'s body region: one is
521 // used to determine the values going into `detect_event` ops before the
522 // `llhd.wait`, and one will do the actual event detection after the
523 // `llhd.wait`.
524 //
525 // Create a copy of the entire `wait_event` op in the wait block, which also
526 // creates a copy of its region. Take note of all inputs to `detect_event`
527 // ops and delete the `detect_event` ops in this copy.
528 SmallVector<Value> valuesBefore;
529 rewriter.setInsertionPointToEnd(waitBlock);
530 auto clonedOp = cast<WaitEventOp>(rewriter.clone(*op));
531 bool allDetectsAreAnyChange = true;
532 for (auto detectOp :
533 llvm::make_early_inc_range(clonedOp.getOps<DetectEventOp>())) {
534 if (detectOp.getEdge() != Edge::AnyChange || detectOp.getCondition())
535 allDetectsAreAnyChange = false;
536 valuesBefore.push_back(detectOp.getInput());
537 rewriter.eraseOp(detectOp);
538 }
539
540 // Determine the values used during event detection that are defined outside
541 // the `wait_event`'s body region. We want to wait for a change on these
542 // signals before we check if any interesting event happened.
543 SmallVector<Value> observeValues;
544 auto setInsertionPointAfterDef = [&](Value value) {
545 if (auto *op = value.getDefiningOp())
546 rewriter.setInsertionPointAfter(op);
547 if (auto arg = dyn_cast<BlockArgument>(value))
548 rewriter.setInsertionPointToStart(value.getParentBlock());
549 };
550
551 getValuesToObserve(&clonedOp.getBody(), setInsertionPointAfterDef,
552 typeConverter, rewriter, observeValues);
553
554 // Create the `llhd.wait` op that suspends the current process and waits for
555 // a change in the interesting values listed in `observeValues`. When a
556 // change is detected, execution resumes in the "check" block.
557 auto waitOp = llhd::WaitOp::create(rewriter, loc, ValueRange{}, Value(),
558 observeValues, ValueRange{}, checkBlock);
559 rewriter.inlineBlockBefore(&clonedOp.getBody().front(), waitOp);
560 rewriter.eraseOp(clonedOp);
561
562 // Collect a list of all detect ops and inline the `wait_event` body into
563 // the check block.
564 SmallVector<DetectEventOp> detectOps(op.getBody().getOps<DetectEventOp>());
565 rewriter.inlineBlockBefore(&op.getBody().front(), checkBlock,
566 checkBlock->end());
567 rewriter.eraseOp(op);
568
569 // Helper function to detect if a certain change occurred between a value
570 // before the `llhd.wait` and after.
571 auto computeTrigger = [&](Value before, Value after, Edge edge) -> Value {
572 assert(before.getType() == after.getType() &&
573 "mismatched types after clone op");
574 auto beforeType = cast<IntType>(before.getType());
575
576 // 9.4.2 IEEE 1800-2017: An edge event shall be detected only on the LSB
577 // of the expression
578 if (beforeType.getWidth() != 1 && edge != Edge::AnyChange) {
579 constexpr int LSB = 0;
580 beforeType =
581 IntType::get(rewriter.getContext(), 1, beforeType.getDomain());
582 before =
583 moore::ExtractOp::create(rewriter, loc, beforeType, before, LSB);
584 after = moore::ExtractOp::create(rewriter, loc, beforeType, after, LSB);
585 }
586
587 auto intType = rewriter.getIntegerType(beforeType.getWidth());
588 before = typeConverter->materializeTargetConversion(rewriter, loc,
589 intType, before);
590 after = typeConverter->materializeTargetConversion(rewriter, loc, intType,
591 after);
592
593 if (edge == Edge::AnyChange)
594 return comb::ICmpOp::create(rewriter, loc, ICmpPredicate::ne, before,
595 after, true);
596
597 SmallVector<Value> disjuncts;
598 Value trueVal = hw::ConstantOp::create(rewriter, loc, APInt(1, 1));
599
600 if (edge == Edge::PosEdge || edge == Edge::BothEdges) {
601 Value notOldVal =
602 comb::XorOp::create(rewriter, loc, before, trueVal, true);
603 Value posedge =
604 comb::AndOp::create(rewriter, loc, notOldVal, after, true);
605 disjuncts.push_back(posedge);
606 }
607
608 if (edge == Edge::NegEdge || edge == Edge::BothEdges) {
609 Value notCurrVal =
610 comb::XorOp::create(rewriter, loc, after, trueVal, true);
611 Value posedge =
612 comb::AndOp::create(rewriter, loc, before, notCurrVal, true);
613 disjuncts.push_back(posedge);
614 }
615
616 return rewriter.createOrFold<comb::OrOp>(loc, disjuncts, true);
617 };
618
619 // Convert all `detect_event` ops into a check for the corresponding event
620 // between the value before and after the `llhd.wait`. The "before" value
621 // has been collected into `valuesBefore` in the "wait" block; the "after"
622 // value corresponds to the detect op's input.
623 SmallVector<Value> triggers;
624 for (auto [detectOp, before] : llvm::zip(detectOps, valuesBefore)) {
625 if (!allDetectsAreAnyChange) {
626 if (!isa<IntType>(before.getType()))
627 return detectOp->emitError() << "requires int operand";
628
629 rewriter.setInsertionPoint(detectOp);
630 auto trigger =
631 computeTrigger(before, detectOp.getInput(), detectOp.getEdge());
632 if (detectOp.getCondition()) {
633 auto condition = typeConverter->materializeTargetConversion(
634 rewriter, loc, rewriter.getI1Type(), detectOp.getCondition());
635 trigger =
636 comb::AndOp::create(rewriter, loc, trigger, condition, true);
637 }
638 triggers.push_back(trigger);
639 }
640
641 rewriter.eraseOp(detectOp);
642 }
643
644 rewriter.setInsertionPointToEnd(checkBlock);
645 if (triggers.empty()) {
646 // If there are no triggers to check, we always branch to the resume
647 // block. If there are no detect_event operations in the wait event, the
648 // 'llhd.wait' operation will not have any observed values and thus the
649 // process will hang there forever.
650 cf::BranchOp::create(rewriter, loc, resumeBlock);
651 } else {
652 // If any `detect_event` op detected an event, branch to the "resume"
653 // block which contains any code after the `wait_event` op. If no events
654 // were detected, branch back to the "wait" block to wait for the next
655 // change on the interesting signals.
656 auto triggered = rewriter.createOrFold<comb::OrOp>(loc, triggers, true);
657 cf::CondBranchOp::create(rewriter, loc, triggered, resumeBlock,
658 waitBlock);
659 }
660
661 return success();
662 }
663};
664
665// moore.wait_delay -> llhd.wait
666static LogicalResult convert(WaitDelayOp op, WaitDelayOp::Adaptor adaptor,
667 ConversionPatternRewriter &rewriter) {
668 auto *resumeBlock =
669 rewriter.splitBlock(op->getBlock(), ++Block::iterator(op));
670 rewriter.setInsertionPoint(op);
671 rewriter.replaceOpWithNewOp<llhd::WaitOp>(op, ValueRange{},
672 adaptor.getDelay(), ValueRange{},
673 ValueRange{}, resumeBlock);
674 rewriter.setInsertionPointToStart(resumeBlock);
675 return success();
676}
677
678// moore.unreachable -> llhd.halt
679static LogicalResult convert(UnreachableOp op, UnreachableOp::Adaptor adaptor,
680 ConversionPatternRewriter &rewriter) {
681 rewriter.replaceOpWithNewOp<llhd::HaltOp>(op, ValueRange{});
682 return success();
683}
684
685//===----------------------------------------------------------------------===//
686// Declaration Conversion
687//===----------------------------------------------------------------------===//
688
689static Value createZeroValue(Type type, Location loc,
690 ConversionPatternRewriter &rewriter) {
691 // Handle pointers.
692 if (isa<mlir::LLVM::LLVMPointerType>(type))
693 return mlir::LLVM::ZeroOp::create(rewriter, loc, type);
694
695 // Handle time values.
696 if (isa<llhd::TimeType>(type)) {
697 auto timeAttr =
698 llhd::TimeAttr::get(type.getContext(), 0U, llvm::StringRef("ns"), 0, 0);
699 return llhd::ConstantTimeOp::create(rewriter, loc, timeAttr);
700 }
701
702 // Handle real values.
703 if (auto floatType = dyn_cast<FloatType>(type)) {
704 auto floatAttr = rewriter.getFloatAttr(floatType, 0.0);
705 return mlir::arith::ConstantOp::create(rewriter, loc, floatAttr);
706 }
707
708 // Handle dynamic strings
709 if (auto strType = dyn_cast<sim::DynamicStringType>(type))
710 return sim::StringConstantOp::create(rewriter, loc, strType, "");
711
712 // Handle queues
713 if (auto queueType = dyn_cast<sim::QueueType>(type))
714 return sim::QueueEmptyOp::create(rewriter, loc, queueType);
715
716 // Otherwise try to create a zero integer and bitcast it to the result type.
717 int64_t width = hw::getBitWidth(type);
718 if (width == -1)
719 return {};
720
721 // TODO: Once the core dialects support four-valued integers, this code
722 // will additionally need to generate an all-X value for four-valued
723 // variables.
724 Value constZero = hw::ConstantOp::create(rewriter, loc, APInt(width, 0));
725 return rewriter.createOrFold<hw::BitcastOp>(loc, type, constZero);
726}
727
728struct ClassPropertyRefOpConversion
729 : public OpConversionPattern<circt::moore::ClassPropertyRefOp> {
730 ClassPropertyRefOpConversion(TypeConverter &tc, MLIRContext *ctx,
731 ClassTypeCache &cache)
732 : OpConversionPattern(tc, ctx), cache(cache) {}
733
734 LogicalResult
735 matchAndRewrite(circt::moore::ClassPropertyRefOp op, OpAdaptor adaptor,
736 ConversionPatternRewriter &rewriter) const override {
737 Location loc = op.getLoc();
738 MLIRContext *ctx = rewriter.getContext();
739
740 // Convert result type; we expect !llhd.ref<someT>.
741 Type dstTy = getTypeConverter()->convertType(op.getPropertyRef().getType());
742 // Operand is a !llvm.ptr
743 Value instRef = adaptor.getInstance();
744
745 // Resolve identified struct from cache.
746 auto classRefTy =
747 cast<circt::moore::ClassHandleType>(op.getInstance().getType());
748 SymbolRefAttr classSym = classRefTy.getClassSym();
749 ModuleOp mod = op->getParentOfType<ModuleOp>();
750 if (failed(resolveClassStructBody(mod, classSym, *typeConverter, cache)))
751 return rewriter.notifyMatchFailure(op,
752 "Could not resolve class struct for " +
753 classSym.getRootReference().str());
754
755 auto structInfo = cache.getStructInfo(classSym);
756 assert(structInfo && "class struct info must exist");
757 auto structTy = structInfo->classBody;
758
759 // Look up cached GEP path for the property.
760 auto propSym = op.getProperty();
761 auto pathOpt = structInfo->getFieldPath(propSym);
762 if (!pathOpt)
763 return rewriter.notifyMatchFailure(op,
764 "no GEP path for property " + propSym);
765
766 auto i32Ty = IntegerType::get(ctx, 32);
767 SmallVector<Value> idxVals;
768 for (unsigned idx : *pathOpt)
769 idxVals.push_back(LLVM::ConstantOp::create(
770 rewriter, loc, i32Ty, rewriter.getI32IntegerAttr(idx)));
771
772 // GEP to the field (opaque ptr mode requires element type).
773 auto ptrTy = LLVM::LLVMPointerType::get(ctx);
774 auto gep =
775 LLVM::GEPOp::create(rewriter, loc, ptrTy, structTy, instRef, idxVals);
776
777 // Wrap pointer back to !llhd.ref<someT>.
778 Value fieldRef = UnrealizedConversionCastOp::create(rewriter, loc, dstTy,
779 gep.getResult())
780 .getResult(0);
781
782 rewriter.replaceOp(op, fieldRef);
783 return success();
784 }
785
786private:
787 ClassTypeCache &cache;
788};
789
790struct ClassUpcastOpConversion : public OpConversionPattern<ClassUpcastOp> {
791 using OpConversionPattern::OpConversionPattern;
792
793 LogicalResult
794 matchAndRewrite(ClassUpcastOp op, OpAdaptor adaptor,
795 ConversionPatternRewriter &rewriter) const override {
796 // Expect lowered types like !llvm.ptr
797 Type dstTy = getTypeConverter()->convertType(op.getResult().getType());
798 Type srcTy = adaptor.getInstance().getType();
799
800 if (!dstTy)
801 return rewriter.notifyMatchFailure(op, "failed to convert result type");
802
803 // If the types are already identical (opaque pointer mode), just forward.
804 if (dstTy == srcTy && isa<LLVM::LLVMPointerType>(srcTy)) {
805 rewriter.replaceOp(op, adaptor.getInstance());
806 return success();
807 }
808 return rewriter.notifyMatchFailure(
809 op, "Upcast applied to non-opaque pointers!");
810 }
811};
812
813/// moore.class.new lowering: heap-allocate storage for the class object.
814struct ClassNewOpConversion : public OpConversionPattern<ClassNewOp> {
815 ClassNewOpConversion(TypeConverter &tc, MLIRContext *ctx,
816 ClassTypeCache &cache)
817 : OpConversionPattern<ClassNewOp>(tc, ctx), cache(cache) {}
818
819 LogicalResult
820 matchAndRewrite(ClassNewOp op, OpAdaptor adaptor,
821 ConversionPatternRewriter &rewriter) const override {
822 Location loc = op.getLoc();
823 MLIRContext *ctx = rewriter.getContext();
824
825 auto handleTy = cast<ClassHandleType>(op.getResult().getType());
826 auto sym = handleTy.getClassSym();
827
828 ModuleOp mod = op->getParentOfType<ModuleOp>();
829
830 if (failed(resolveClassStructBody(mod, sym, *typeConverter, cache)))
831 return op.emitError() << "Could not resolve class struct for " << sym;
832
833 auto structTy = cache.getStructInfo(sym)->classBody;
834
835 DataLayout dl(mod);
836 // DataLayout::getTypeSize gives a byte count for LLVM types.
837 uint64_t byteSize = dl.getTypeSize(structTy);
838 auto i64Ty = IntegerType::get(ctx, 64);
839 auto cSize = LLVM::ConstantOp::create(rewriter, loc, i64Ty,
840 rewriter.getI64IntegerAttr(byteSize));
841
842 // Get or declare malloc and call it.
843 auto mallocFn = getOrCreateMalloc(mod, rewriter);
844 auto ptrTy = LLVM::LLVMPointerType::get(ctx); // opaque pointer result
845 auto call =
846 LLVM::CallOp::create(rewriter, loc, TypeRange{ptrTy},
847 SymbolRefAttr::get(mallocFn), ValueRange{cSize});
848
849 // Replace the new op with the malloc pointer (no cast needed with opaque
850 // ptrs).
851 rewriter.replaceOp(op, call.getResult());
852 return success();
853 }
854
855private:
856 ClassTypeCache &cache; // shared, owned by the pass
857};
858
859struct ClassDeclOpConversion : public OpConversionPattern<ClassDeclOp> {
860 ClassDeclOpConversion(TypeConverter &tc, MLIRContext *ctx,
861 ClassTypeCache &cache)
862 : OpConversionPattern<ClassDeclOp>(tc, ctx), cache(cache) {}
863
864 LogicalResult
865 matchAndRewrite(ClassDeclOp op, OpAdaptor,
866 ConversionPatternRewriter &rewriter) const override {
867
868 if (failed(resolveClassStructBody(op, *typeConverter, cache)))
869 return failure();
870 // The declaration itself is a no-op
871 rewriter.eraseOp(op);
872 return success();
873 }
874
875private:
876 ClassTypeCache &cache; // shared, owned by the pass
877};
878
879struct VariableOpConversion : public OpConversionPattern<VariableOp> {
880 using OpConversionPattern::OpConversionPattern;
881
882 LogicalResult
883 matchAndRewrite(VariableOp op, OpAdaptor adaptor,
884 ConversionPatternRewriter &rewriter) const override {
885 auto loc = op.getLoc();
886 auto resultType = typeConverter->convertType(op.getResult().getType());
887 if (!resultType)
888 return rewriter.notifyMatchFailure(op.getLoc(), "invalid variable type");
889
890 // Determine the initial value of the signal.
891 Value init = adaptor.getInitial();
892 if (!init) {
893 auto elementType = cast<llhd::RefType>(resultType).getNestedType();
894 init = createZeroValue(elementType, loc, rewriter);
895 if (!init)
896 return failure();
897 }
898
899 rewriter.replaceOpWithNewOp<llhd::SignalOp>(op, resultType,
900 op.getNameAttr(), init);
901 return success();
902 }
903};
904
905struct NetOpConversion : public OpConversionPattern<NetOp> {
906 using OpConversionPattern::OpConversionPattern;
907
908 LogicalResult
909 matchAndRewrite(NetOp op, OpAdaptor adaptor,
910 ConversionPatternRewriter &rewriter) const override {
911 auto loc = op.getLoc();
912
913 auto resultType = typeConverter->convertType(op.getResult().getType());
914 if (!resultType)
915 return rewriter.notifyMatchFailure(loc, "invalid net type");
916
917 auto elementType = cast<llhd::RefType>(resultType).getNestedType();
918 int64_t width = hw::getBitWidth(elementType);
919 if (width == -1)
920 return failure();
921
922 auto init =
923 createInitialValue(op.getKind(), rewriter, loc, width, elementType);
924 auto signal = rewriter.replaceOpWithNewOp<llhd::SignalOp>(
925 op, resultType, op.getNameAttr(), init);
926
927 if (auto assignedValue = adaptor.getAssignment()) {
928 auto timeAttr = llhd::TimeAttr::get(resultType.getContext(), 0U,
929 llvm::StringRef("ns"), 0, 1);
930 auto time = llhd::ConstantTimeOp::create(rewriter, loc, timeAttr);
931 llhd::DriveOp::create(rewriter, loc, signal, assignedValue, time,
932 Value{});
933 }
934
935 return success();
936 }
937
938 static mlir::Value createInitialValue(NetKind kind,
939 ConversionPatternRewriter &rewriter,
940 Location loc, int64_t width,
941 Type elementType) {
942 // TODO: Once the core dialects support four-valued integers, this code
943 // will additionally need to generate an all-X value for four-valued nets.
944 //
945 // If no driver is connected to a net, its value shall be high-impedance (z)
946 // unless the net is a trireg, in which case it shall hold the previously
947 // driven value.
948 //
949 // See IEEE 1800-2017 § 6.6 "Net types".
950 auto theInt = [&] {
951 if (kind == NetKind::Supply1 || kind == NetKind::Tri1)
952 return APInt::getAllOnes(width);
953 return APInt::getZero(width);
954 }();
955 auto theConst = hw::ConstantOp::create(rewriter, loc, theInt);
956 return rewriter.createOrFold<hw::BitcastOp>(loc, elementType, theConst);
957 }
958};
959
960// moore.global_variable -> llhd.global_signal
961static LogicalResult convert(GlobalVariableOp op,
962 GlobalVariableOp::Adaptor adaptor,
963 ConversionPatternRewriter &rewriter,
964 const TypeConverter &typeConverter) {
965 auto type = typeConverter.convertType(op.getType());
966 auto sig = llhd::GlobalSignalOp::create(rewriter, op.getLoc(),
967 op.getSymNameAttr(), type);
968 sig.getInitRegion().takeBody(op.getInitRegion());
969 rewriter.eraseOp(op);
970 return success();
971}
972
973// moore.get_global_variable -> llhd.get_global_signal
974static LogicalResult convert(GetGlobalVariableOp op,
975 GetGlobalVariableOp::Adaptor adaptor,
976 ConversionPatternRewriter &rewriter,
977 const TypeConverter &typeConverter) {
978 auto type = typeConverter.convertType(op.getType());
979 rewriter.replaceOpWithNewOp<llhd::GetGlobalSignalOp>(op, type,
980 op.getGlobalNameAttr());
981 return success();
982}
983
984//===----------------------------------------------------------------------===//
985// Expression Conversion
986//===----------------------------------------------------------------------===//
987
988struct ConstantOpConv : public OpConversionPattern<ConstantOp> {
989 using OpConversionPattern::OpConversionPattern;
990
991 LogicalResult
992 matchAndRewrite(ConstantOp op, OpAdaptor adaptor,
993 ConversionPatternRewriter &rewriter) const override {
994 // FIXME: Discard unknown bits and map them to 0 for now.
995 auto value = op.getValue().toAPInt(false);
996 auto type = rewriter.getIntegerType(value.getBitWidth());
997 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
998 op, type, rewriter.getIntegerAttr(type, value));
999 return success();
1000 }
1001};
1002
1003struct ConstantRealOpConv : public OpConversionPattern<ConstantRealOp> {
1004 using OpConversionPattern::OpConversionPattern;
1005
1006 LogicalResult
1007 matchAndRewrite(ConstantRealOp op, OpAdaptor adaptor,
1008 ConversionPatternRewriter &rewriter) const override {
1009 rewriter.replaceOpWithNewOp<arith::ConstantOp>(op, op.getValueAttr());
1010 return success();
1011 }
1012};
1013
1014struct ConstantTimeOpConv : public OpConversionPattern<ConstantTimeOp> {
1015 using OpConversionPattern::OpConversionPattern;
1016
1017 LogicalResult
1018 matchAndRewrite(ConstantTimeOp op, OpAdaptor adaptor,
1019 ConversionPatternRewriter &rewriter) const override {
1020 rewriter.replaceOpWithNewOp<llhd::ConstantTimeOp>(
1021 op, llhd::TimeAttr::get(op->getContext(), op.getValue(),
1022 StringRef("fs"), 0, 0));
1023 return success();
1024 }
1025};
1026
1027struct ConstantStringOpConv : public OpConversionPattern<ConstantStringOp> {
1028 using OpConversionPattern::OpConversionPattern;
1029 LogicalResult
1030 matchAndRewrite(moore::ConstantStringOp op, OpAdaptor adaptor,
1031 ConversionPatternRewriter &rewriter) const override {
1032 const auto resultType =
1033 typeConverter->convertType(op.getResult().getType());
1034 const auto intType = mlir::cast<IntegerType>(resultType);
1035
1036 const auto str = op.getValue();
1037 const unsigned byteWidth = intType.getWidth();
1038 APInt value(byteWidth, 0);
1039
1040 // Pack ascii chars from the end of the string, until it fits.
1041 const size_t maxChars =
1042 std::min(str.size(), static_cast<size_t>(byteWidth / 8));
1043 for (size_t i = 0; i < maxChars; i++) {
1044 const size_t pos = str.size() - 1 - i;
1045 const auto asciiChar = static_cast<uint8_t>(str[pos]);
1046 value |= APInt(byteWidth, asciiChar) << (8 * i);
1047 }
1048
1049 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
1050 op, resultType, rewriter.getIntegerAttr(resultType, value));
1051 return success();
1052 }
1053};
1054
1055struct ConcatOpConversion : public OpConversionPattern<ConcatOp> {
1056 using OpConversionPattern::OpConversionPattern;
1057 LogicalResult
1058 matchAndRewrite(ConcatOp op, OpAdaptor adaptor,
1059 ConversionPatternRewriter &rewriter) const override {
1060 rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, adaptor.getValues());
1061 return success();
1062 }
1063};
1064
1065struct ReplicateOpConversion : public OpConversionPattern<ReplicateOp> {
1066 using OpConversionPattern::OpConversionPattern;
1067 LogicalResult
1068 matchAndRewrite(ReplicateOp op, OpAdaptor adaptor,
1069 ConversionPatternRewriter &rewriter) const override {
1070 Type resultType = typeConverter->convertType(op.getResult().getType());
1071
1072 rewriter.replaceOpWithNewOp<comb::ReplicateOp>(op, resultType,
1073 adaptor.getValue());
1074 return success();
1075 }
1076};
1077
1078struct ExtractOpConversion : public OpConversionPattern<ExtractOp> {
1079 using OpConversionPattern::OpConversionPattern;
1080
1081 LogicalResult
1082 matchAndRewrite(ExtractOp op, OpAdaptor adaptor,
1083 ConversionPatternRewriter &rewriter) const override {
1084 // TODO: return X if the domain is four-valued for out-of-bounds accesses
1085 // once we support four-valued lowering
1086 Type resultType = typeConverter->convertType(op.getResult().getType());
1087 Type inputType = adaptor.getInput().getType();
1088 int32_t low = adaptor.getLowBit();
1089
1090 if (isa<IntegerType>(inputType)) {
1091 int32_t inputWidth = inputType.getIntOrFloatBitWidth();
1092 int32_t resultWidth = hw::getBitWidth(resultType);
1093 int32_t high = low + resultWidth;
1094
1095 SmallVector<Value> toConcat;
1096 if (low < 0)
1097 toConcat.push_back(hw::ConstantOp::create(
1098 rewriter, op.getLoc(), APInt(std::min(-low, resultWidth), 0)));
1099
1100 if (low < inputWidth && high > 0) {
1101 int32_t lowIdx = std::max(low, 0);
1102 Value middle = rewriter.createOrFold<comb::ExtractOp>(
1103 op.getLoc(),
1104 rewriter.getIntegerType(
1105 std::min(resultWidth, std::min(high, inputWidth) - lowIdx)),
1106 adaptor.getInput(), lowIdx);
1107 toConcat.push_back(middle);
1108 }
1109
1110 int32_t diff = high - inputWidth;
1111 if (diff > 0) {
1112 Value val =
1113 hw::ConstantOp::create(rewriter, op.getLoc(), APInt(diff, 0));
1114 toConcat.push_back(val);
1115 }
1116
1117 Value concat =
1118 rewriter.createOrFold<comb::ConcatOp>(op.getLoc(), toConcat);
1119 rewriter.replaceOp(op, concat);
1120 return success();
1121 }
1122
1123 if (auto arrTy = dyn_cast<hw::ArrayType>(inputType)) {
1124 int32_t width = llvm::Log2_64_Ceil(arrTy.getNumElements());
1125 int32_t inputWidth = arrTy.getNumElements();
1126
1127 if (auto resArrTy = dyn_cast<hw::ArrayType>(resultType);
1128 resArrTy && resArrTy != arrTy.getElementType()) {
1129 int32_t elementWidth = hw::getBitWidth(arrTy.getElementType());
1130 if (elementWidth < 0)
1131 return failure();
1132
1133 int32_t high = low + resArrTy.getNumElements();
1134 int32_t resWidth = resArrTy.getNumElements();
1135
1136 SmallVector<Value> toConcat;
1137 if (low < 0) {
1138 Value val = hw::ConstantOp::create(
1139 rewriter, op.getLoc(),
1140 APInt(std::min((-low) * elementWidth, resWidth * elementWidth),
1141 0));
1142 Value res = rewriter.createOrFold<hw::BitcastOp>(
1143 op.getLoc(), hw::ArrayType::get(arrTy.getElementType(), -low),
1144 val);
1145 toConcat.push_back(res);
1146 }
1147
1148 if (low < inputWidth && high > 0) {
1149 int32_t lowIdx = std::max(0, low);
1150 Value lowIdxVal = hw::ConstantOp::create(
1151 rewriter, op.getLoc(), rewriter.getIntegerType(width), lowIdx);
1152 Value middle = rewriter.createOrFold<hw::ArraySliceOp>(
1153 op.getLoc(),
1154 hw::ArrayType::get(
1155 arrTy.getElementType(),
1156 std::min(resWidth, std::min(inputWidth, high) - lowIdx)),
1157 adaptor.getInput(), lowIdxVal);
1158 toConcat.push_back(middle);
1159 }
1160
1161 int32_t diff = high - inputWidth;
1162 if (diff > 0) {
1163 Value constZero = hw::ConstantOp::create(
1164 rewriter, op.getLoc(), APInt(diff * elementWidth, 0));
1165 Value val = hw::BitcastOp::create(
1166 rewriter, op.getLoc(),
1167 hw::ArrayType::get(arrTy.getElementType(), diff), constZero);
1168 toConcat.push_back(val);
1169 }
1170
1171 Value concat =
1172 rewriter.createOrFold<hw::ArrayConcatOp>(op.getLoc(), toConcat);
1173 rewriter.replaceOp(op, concat);
1174 return success();
1175 }
1176
1177 // Otherwise, it has to be the array's element type
1178 if (low < 0 || low >= inputWidth) {
1179 int32_t bw = hw::getBitWidth(resultType);
1180 if (bw < 0)
1181 return failure();
1182
1183 Value val = hw::ConstantOp::create(rewriter, op.getLoc(), APInt(bw, 0));
1184 Value bitcast =
1185 rewriter.createOrFold<hw::BitcastOp>(op.getLoc(), resultType, val);
1186 rewriter.replaceOp(op, bitcast);
1187 return success();
1188 }
1189
1190 Value idx = hw::ConstantOp::create(rewriter, op.getLoc(),
1191 rewriter.getIntegerType(width),
1192 adaptor.getLowBit());
1193 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(), idx);
1194 return success();
1195 }
1196
1197 return failure();
1198 }
1199};
1200
1201struct ExtractRefOpConversion : public OpConversionPattern<ExtractRefOp> {
1202 using OpConversionPattern::OpConversionPattern;
1203
1204 LogicalResult
1205 matchAndRewrite(ExtractRefOp op, OpAdaptor adaptor,
1206 ConversionPatternRewriter &rewriter) const override {
1207 // TODO: properly handle out-of-bounds accesses
1208 Type resultType = typeConverter->convertType(op.getResult().getType());
1209 Type inputType =
1210 cast<llhd::RefType>(adaptor.getInput().getType()).getNestedType();
1211
1212 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1213 int64_t width = hw::getBitWidth(inputType);
1214 if (width == -1)
1215 return failure();
1216
1217 Value lowBit = hw::ConstantOp::create(
1218 rewriter, op.getLoc(),
1219 rewriter.getIntegerType(llvm::Log2_64_Ceil(width)),
1220 adaptor.getLowBit());
1221 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
1222 op, resultType, adaptor.getInput(), lowBit);
1223 return success();
1224 }
1225
1226 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1227 Value lowBit = hw::ConstantOp::create(
1228 rewriter, op.getLoc(),
1229 rewriter.getIntegerType(llvm::Log2_64_Ceil(arrType.getNumElements())),
1230 adaptor.getLowBit());
1231
1232 // If the result type is not the same as the array's element type, then
1233 // it has to be a slice.
1234 if (arrType.getElementType() !=
1235 cast<llhd::RefType>(resultType).getNestedType()) {
1236 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
1237 op, resultType, adaptor.getInput(), lowBit);
1238 return success();
1239 }
1240
1241 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
1242 lowBit);
1243 return success();
1244 }
1245
1246 return failure();
1247 }
1248};
1249
1250struct DynExtractOpConversion : public OpConversionPattern<DynExtractOp> {
1251 using OpConversionPattern::OpConversionPattern;
1252
1253 LogicalResult
1254 matchAndRewrite(DynExtractOp op, OpAdaptor adaptor,
1255 ConversionPatternRewriter &rewriter) const override {
1256 Type resultType = typeConverter->convertType(op.getResult().getType());
1257 Type inputType = adaptor.getInput().getType();
1258
1259 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1260 Value amount = adjustIntegerWidth(rewriter, adaptor.getLowBit(),
1261 intType.getWidth(), op->getLoc());
1262 Value value = comb::ShrUOp::create(rewriter, op->getLoc(),
1263 adaptor.getInput(), amount);
1264
1265 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, value, 0);
1266 return success();
1267 }
1268
1269 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1270 unsigned idxWidth = llvm::Log2_64_Ceil(arrType.getNumElements());
1271 Value idx = adjustIntegerWidth(rewriter, adaptor.getLowBit(), idxWidth,
1272 op->getLoc());
1273
1274 bool isSingleElementExtract = arrType.getElementType() == resultType;
1275
1276 if (isSingleElementExtract)
1277 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(),
1278 idx);
1279 else
1280 rewriter.replaceOpWithNewOp<hw::ArraySliceOp>(op, resultType,
1281 adaptor.getInput(), idx);
1282
1283 return success();
1284 }
1285
1286 return failure();
1287 }
1288};
1289
1290struct DynExtractRefOpConversion : public OpConversionPattern<DynExtractRefOp> {
1291 using OpConversionPattern::OpConversionPattern;
1292
1293 LogicalResult
1294 matchAndRewrite(DynExtractRefOp op, OpAdaptor adaptor,
1295 ConversionPatternRewriter &rewriter) const override {
1296 // TODO: properly handle out-of-bounds accesses
1297 Type resultType = typeConverter->convertType(op.getResult().getType());
1298 Type inputType =
1299 cast<llhd::RefType>(adaptor.getInput().getType()).getNestedType();
1300
1301 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1302 int64_t width = hw::getBitWidth(inputType);
1303 if (width == -1)
1304 return failure();
1305
1306 Value amount =
1307 adjustIntegerWidth(rewriter, adaptor.getLowBit(),
1308 llvm::Log2_64_Ceil(width), op->getLoc());
1309 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
1310 op, resultType, adaptor.getInput(), amount);
1311 return success();
1312 }
1313
1314 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1315 Value idx = adjustIntegerWidth(
1316 rewriter, adaptor.getLowBit(),
1317 llvm::Log2_64_Ceil(arrType.getNumElements()), op->getLoc());
1318
1319 auto resultNestedType = cast<llhd::RefType>(resultType).getNestedType();
1320 bool isSingleElementExtract =
1321 arrType.getElementType() == resultNestedType;
1322
1323 if (isSingleElementExtract)
1324 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
1325 idx);
1326 else
1327 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
1328 op, resultType, adaptor.getInput(), idx);
1329
1330 return success();
1331 }
1332
1333 return failure();
1334 }
1335};
1336
1337struct ArrayCreateOpConversion : public OpConversionPattern<ArrayCreateOp> {
1338 using OpConversionPattern::OpConversionPattern;
1339
1340 LogicalResult
1341 matchAndRewrite(ArrayCreateOp op, OpAdaptor adaptor,
1342 ConversionPatternRewriter &rewriter) const override {
1343 Type resultType = typeConverter->convertType(op.getResult().getType());
1344 rewriter.replaceOpWithNewOp<hw::ArrayCreateOp>(op, resultType,
1345 adaptor.getElements());
1346 return success();
1347 }
1348};
1349
1350struct StructCreateOpConversion : public OpConversionPattern<StructCreateOp> {
1351 using OpConversionPattern::OpConversionPattern;
1352
1353 LogicalResult
1354 matchAndRewrite(StructCreateOp op, OpAdaptor adaptor,
1355 ConversionPatternRewriter &rewriter) const override {
1356 Type resultType = typeConverter->convertType(op.getResult().getType());
1357 rewriter.replaceOpWithNewOp<hw::StructCreateOp>(op, resultType,
1358 adaptor.getFields());
1359 return success();
1360 }
1361};
1362
1363struct StructExtractOpConversion : public OpConversionPattern<StructExtractOp> {
1364 using OpConversionPattern::OpConversionPattern;
1365
1366 LogicalResult
1367 matchAndRewrite(StructExtractOp op, OpAdaptor adaptor,
1368 ConversionPatternRewriter &rewriter) const override {
1369 rewriter.replaceOpWithNewOp<hw::StructExtractOp>(
1370 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1371 return success();
1372 }
1373};
1374
1375struct StructExtractRefOpConversion
1376 : public OpConversionPattern<StructExtractRefOp> {
1377 using OpConversionPattern::OpConversionPattern;
1378
1379 LogicalResult
1380 matchAndRewrite(StructExtractRefOp op, OpAdaptor adaptor,
1381 ConversionPatternRewriter &rewriter) const override {
1382 rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
1383 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1384 return success();
1385 }
1386};
1387
1388struct UnionCreateOpConversion : public OpConversionPattern<UnionCreateOp> {
1389 using OpConversionPattern::OpConversionPattern;
1390
1391 LogicalResult
1392 matchAndRewrite(UnionCreateOp op, OpAdaptor adaptor,
1393 ConversionPatternRewriter &rewriter) const override {
1394 Type resultType = typeConverter->convertType(op.getResult().getType());
1395 rewriter.replaceOpWithNewOp<hw::UnionCreateOp>(
1396 op, resultType, adaptor.getFieldNameAttr(), adaptor.getInput());
1397 return success();
1398 }
1399};
1400
1401struct UnionExtractOpConversion : public OpConversionPattern<UnionExtractOp> {
1402 using OpConversionPattern::OpConversionPattern;
1403
1404 LogicalResult
1405 matchAndRewrite(UnionExtractOp op, OpAdaptor adaptor,
1406 ConversionPatternRewriter &rewriter) const override {
1407 rewriter.replaceOpWithNewOp<hw::UnionExtractOp>(op, adaptor.getInput(),
1408 adaptor.getFieldNameAttr());
1409 return success();
1410 }
1411};
1412
1413struct UnionExtractRefOpConversion
1414 : public OpConversionPattern<UnionExtractRefOp> {
1415 using OpConversionPattern::OpConversionPattern;
1416
1417 LogicalResult
1418 matchAndRewrite(UnionExtractRefOp op, OpAdaptor adaptor,
1419 ConversionPatternRewriter &rewriter) const override {
1420 rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
1421 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1422 return success();
1423 }
1424};
1425
1426struct ReduceAndOpConversion : public OpConversionPattern<ReduceAndOp> {
1427 using OpConversionPattern::OpConversionPattern;
1428 LogicalResult
1429 matchAndRewrite(ReduceAndOp op, OpAdaptor adaptor,
1430 ConversionPatternRewriter &rewriter) const override {
1431 Type resultType = typeConverter->convertType(op.getInput().getType());
1432 Value max = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, -1);
1433
1434 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::eq,
1435 adaptor.getInput(), max);
1436 return success();
1437 }
1438};
1439
1440struct ReduceOrOpConversion : public OpConversionPattern<ReduceOrOp> {
1441 using OpConversionPattern::OpConversionPattern;
1442 LogicalResult
1443 matchAndRewrite(ReduceOrOp op, OpAdaptor adaptor,
1444 ConversionPatternRewriter &rewriter) const override {
1445 Type resultType = typeConverter->convertType(op.getInput().getType());
1446 Value zero = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1447
1448 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1449 adaptor.getInput(), zero);
1450 return success();
1451 }
1452};
1453
1454struct ReduceXorOpConversion : public OpConversionPattern<ReduceXorOp> {
1455 using OpConversionPattern::OpConversionPattern;
1456 LogicalResult
1457 matchAndRewrite(ReduceXorOp op, OpAdaptor adaptor,
1458 ConversionPatternRewriter &rewriter) const override {
1459
1460 rewriter.replaceOpWithNewOp<comb::ParityOp>(op, adaptor.getInput());
1461 return success();
1462 }
1463};
1464
1465struct BoolCastOpConversion : public OpConversionPattern<BoolCastOp> {
1466 using OpConversionPattern::OpConversionPattern;
1467 LogicalResult
1468 matchAndRewrite(BoolCastOp op, OpAdaptor adaptor,
1469 ConversionPatternRewriter &rewriter) const override {
1470 Type resultType = typeConverter->convertType(op.getInput().getType());
1471 if (isa_and_nonnull<IntegerType>(resultType)) {
1472 Value zero =
1473 hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1474 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1475 adaptor.getInput(), zero);
1476 return success();
1477 }
1478 return failure();
1479 }
1480};
1481
1482struct NotOpConversion : public OpConversionPattern<NotOp> {
1483 using OpConversionPattern::OpConversionPattern;
1484 LogicalResult
1485 matchAndRewrite(NotOp op, OpAdaptor adaptor,
1486 ConversionPatternRewriter &rewriter) const override {
1487 Type resultType =
1488 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1489 Value max = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, -1);
1490
1491 rewriter.replaceOpWithNewOp<comb::XorOp>(op, adaptor.getInput(), max);
1492 return success();
1493 }
1494};
1495
1496struct NegOpConversion : public OpConversionPattern<NegOp> {
1497 using OpConversionPattern::OpConversionPattern;
1498 LogicalResult
1499 matchAndRewrite(NegOp op, OpAdaptor adaptor,
1500 ConversionPatternRewriter &rewriter) const override {
1501 Type resultType =
1502 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1503 Value zero = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, 0);
1504
1505 rewriter.replaceOpWithNewOp<comb::SubOp>(op, zero, adaptor.getInput());
1506 return success();
1507 }
1508};
1509
1510struct NegRealOpConversion : public OpConversionPattern<NegRealOp> {
1511 using OpConversionPattern::OpConversionPattern;
1512 LogicalResult
1513 matchAndRewrite(NegRealOp op, OpAdaptor adaptor,
1514 ConversionPatternRewriter &rewriter) const override {
1515 rewriter.replaceOpWithNewOp<arith::NegFOp>(op, adaptor.getInput());
1516 return success();
1517 }
1518};
1519
1520template <typename SourceOp, typename TargetOp>
1521struct BinaryOpConversion : public OpConversionPattern<SourceOp> {
1523 using OpAdaptor = typename SourceOp::Adaptor;
1524
1525 LogicalResult
1526 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1527 ConversionPatternRewriter &rewriter) const override {
1528 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1529 adaptor.getRhs(), false);
1530 return success();
1531 }
1532};
1533
1534template <typename SourceOp, typename TargetOp>
1535struct BinaryRealOpConversion : public OpConversionPattern<SourceOp> {
1537 using OpAdaptor = typename SourceOp::Adaptor;
1538
1539 LogicalResult
1540 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1541 ConversionPatternRewriter &rewriter) const override {
1542 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1543 adaptor.getRhs());
1544 return success();
1545 }
1546};
1547
1548template <typename SourceOp, ICmpPredicate pred>
1549struct ICmpOpConversion : public OpConversionPattern<SourceOp> {
1551 using OpAdaptor = typename SourceOp::Adaptor;
1552
1553 LogicalResult
1554 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1555 ConversionPatternRewriter &rewriter) const override {
1556 Type resultType =
1557 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1558
1559 rewriter.replaceOpWithNewOp<comb::ICmpOp>(
1560 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1561 return success();
1562 }
1563};
1564
1565template <typename SourceOp, arith::CmpFPredicate pred>
1566struct FCmpOpConversion : public OpConversionPattern<SourceOp> {
1568 using OpAdaptor = typename SourceOp::Adaptor;
1569
1570 LogicalResult
1571 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1572 ConversionPatternRewriter &rewriter) const override {
1573 Type resultType =
1574 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1575
1576 rewriter.replaceOpWithNewOp<arith::CmpFOp>(
1577 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1578 return success();
1579 }
1580};
1581
1582template <typename SourceOp, bool withoutX>
1583struct CaseXZEqOpConversion : public OpConversionPattern<SourceOp> {
1585 using OpAdaptor = typename SourceOp::Adaptor;
1586
1587 LogicalResult
1588 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1589 ConversionPatternRewriter &rewriter) const override {
1590 // Check each operand if it is a known constant and extract the X and/or Z
1591 // bits to be ignored.
1592 // TODO: Once the core dialects support four-valued integers, we will have
1593 // to create ops that extract X and Z bits from the operands, since we also
1594 // have to do the right casez/casex comparison on non-constant inputs.
1595 unsigned bitWidth = op.getLhs().getType().getWidth();
1596 auto ignoredBits = APInt::getZero(bitWidth);
1597 auto detectIgnoredBits = [&](Value value) {
1598 auto constOp = value.getDefiningOp<ConstantOp>();
1599 if (!constOp)
1600 return;
1601 auto constValue = constOp.getValue();
1602 if (withoutX)
1603 ignoredBits |= constValue.getZBits();
1604 else
1605 ignoredBits |= constValue.getUnknownBits();
1606 };
1607 detectIgnoredBits(op.getLhs());
1608 detectIgnoredBits(op.getRhs());
1609
1610 // If we have detected any bits to be ignored, mask them in the operands for
1611 // the comparison.
1612 Value lhs = adaptor.getLhs();
1613 Value rhs = adaptor.getRhs();
1614 if (!ignoredBits.isZero()) {
1615 ignoredBits.flipAllBits();
1616 auto maskOp = hw::ConstantOp::create(rewriter, op.getLoc(), ignoredBits);
1617 lhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), lhs, maskOp);
1618 rhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), rhs, maskOp);
1619 }
1620
1621 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, ICmpPredicate::ceq, lhs, rhs);
1622 return success();
1623 }
1624};
1625
1626//===----------------------------------------------------------------------===//
1627// Conversions
1628//===----------------------------------------------------------------------===//
1629
1630struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
1631 using OpConversionPattern::OpConversionPattern;
1632
1633 LogicalResult
1634 matchAndRewrite(ConversionOp op, OpAdaptor adaptor,
1635 ConversionPatternRewriter &rewriter) const override {
1636 Location loc = op.getLoc();
1637 Type resultType = typeConverter->convertType(op.getResult().getType());
1638 if (!resultType) {
1639 op.emitError("conversion result type is not currently supported");
1640 return failure();
1641 }
1642 int64_t inputBw = hw::getBitWidth(adaptor.getInput().getType());
1643 int64_t resultBw = hw::getBitWidth(resultType);
1644 if (inputBw == -1 || resultBw == -1)
1645 return failure();
1646
1647 Value input = rewriter.createOrFold<hw::BitcastOp>(
1648 loc, rewriter.getIntegerType(inputBw), adaptor.getInput());
1649 Value amount = adjustIntegerWidth(rewriter, input, resultBw, loc);
1650
1651 Value result =
1652 rewriter.createOrFold<hw::BitcastOp>(loc, resultType, amount);
1653 rewriter.replaceOp(op, result);
1654 return success();
1655 }
1656};
1657
1658template <typename SourceOp>
1659struct BitcastConversion : public OpConversionPattern<SourceOp> {
1661 using OpAdaptor = typename SourceOp::Adaptor;
1662 using ConversionPattern::typeConverter;
1663
1664 LogicalResult
1665 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1666 ConversionPatternRewriter &rewriter) const override {
1667 auto type = typeConverter->convertType(op.getResult().getType());
1668 if (type == adaptor.getInput().getType())
1669 rewriter.replaceOp(op, adaptor.getInput());
1670 else
1671 rewriter.replaceOpWithNewOp<hw::BitcastOp>(op, type, adaptor.getInput());
1672 return success();
1673 }
1674};
1675
1676/// For casts that are automatically resolved by type conversion
1677template <typename SourceOp>
1678struct NoOpConversion : public OpConversionPattern<SourceOp> {
1680 using OpAdaptor = typename SourceOp::Adaptor;
1681 using ConversionPattern::typeConverter;
1682
1683 LogicalResult
1684 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1685 ConversionPatternRewriter &rewriter) const override {
1686 rewriter.replaceOp(op, adaptor.getInput());
1687 return success();
1688 }
1689};
1690
1691struct TruncOpConversion : public OpConversionPattern<TruncOp> {
1692 using OpConversionPattern::OpConversionPattern;
1693
1694 LogicalResult
1695 matchAndRewrite(TruncOp op, OpAdaptor adaptor,
1696 ConversionPatternRewriter &rewriter) const override {
1697 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, adaptor.getInput(), 0,
1698 op.getType().getWidth());
1699 return success();
1700 }
1701};
1702
1703struct ZExtOpConversion : public OpConversionPattern<ZExtOp> {
1704 using OpConversionPattern::OpConversionPattern;
1705
1706 LogicalResult
1707 matchAndRewrite(ZExtOp op, OpAdaptor adaptor,
1708 ConversionPatternRewriter &rewriter) const override {
1709 auto targetWidth = op.getType().getWidth();
1710 auto inputWidth = op.getInput().getType().getWidth();
1711
1712 auto zeroExt = hw::ConstantOp::create(
1713 rewriter, op.getLoc(),
1714 rewriter.getIntegerType(targetWidth - inputWidth), 0);
1715
1716 rewriter.replaceOpWithNewOp<comb::ConcatOp>(
1717 op, ValueRange{zeroExt, adaptor.getInput()});
1718 return success();
1719 }
1720};
1721
1722struct SExtOpConversion : public OpConversionPattern<SExtOp> {
1723 using OpConversionPattern::OpConversionPattern;
1724
1725 LogicalResult
1726 matchAndRewrite(SExtOp op, OpAdaptor adaptor,
1727 ConversionPatternRewriter &rewriter) const override {
1728 auto type = typeConverter->convertType(op.getType());
1729 auto value =
1730 comb::createOrFoldSExt(op.getLoc(), adaptor.getInput(), type, rewriter);
1731 rewriter.replaceOp(op, value);
1732 return success();
1733 }
1734};
1735
1736struct SIntToRealOpConversion : public OpConversionPattern<SIntToRealOp> {
1737 using OpConversionPattern::OpConversionPattern;
1738
1739 LogicalResult
1740 matchAndRewrite(SIntToRealOp op, OpAdaptor adaptor,
1741 ConversionPatternRewriter &rewriter) const override {
1742 rewriter.replaceOpWithNewOp<arith::SIToFPOp>(
1743 op, typeConverter->convertType(op.getType()), adaptor.getInput());
1744 return success();
1745 }
1746};
1747
1748struct UIntToRealOpConversion : public OpConversionPattern<UIntToRealOp> {
1749 using OpConversionPattern::OpConversionPattern;
1750
1751 LogicalResult
1752 matchAndRewrite(UIntToRealOp op, OpAdaptor adaptor,
1753 ConversionPatternRewriter &rewriter) const override {
1754 rewriter.replaceOpWithNewOp<arith::UIToFPOp>(
1755 op, typeConverter->convertType(op.getType()), adaptor.getInput());
1756 return success();
1757 }
1758};
1759
1760struct IntToStringOpConversion : public OpConversionPattern<IntToStringOp> {
1761 using OpConversionPattern::OpConversionPattern;
1762
1763 LogicalResult
1764 matchAndRewrite(IntToStringOp op, OpAdaptor adaptor,
1765 ConversionPatternRewriter &rewriter) const override {
1766 rewriter.replaceOpWithNewOp<sim::IntToStringOp>(op, adaptor.getInput());
1767 return success();
1768 }
1769};
1770
1771struct RealToIntOpConversion : public OpConversionPattern<RealToIntOp> {
1772 using OpConversionPattern::OpConversionPattern;
1773
1774 LogicalResult
1775 matchAndRewrite(RealToIntOp op, OpAdaptor adaptor,
1776 ConversionPatternRewriter &rewriter) const override {
1777 rewriter.replaceOpWithNewOp<arith::FPToSIOp>(
1778 op, typeConverter->convertType(op.getType()), adaptor.getInput());
1779 return success();
1780 }
1781};
1782
1783//===----------------------------------------------------------------------===//
1784// Statement Conversion
1785//===----------------------------------------------------------------------===//
1786
1787struct HWInstanceOpConversion : public OpConversionPattern<hw::InstanceOp> {
1788 using OpConversionPattern::OpConversionPattern;
1789
1790 LogicalResult
1791 matchAndRewrite(hw::InstanceOp op, OpAdaptor adaptor,
1792 ConversionPatternRewriter &rewriter) const override {
1793 SmallVector<Type> convResTypes;
1794 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1795 return failure();
1796
1797 rewriter.replaceOpWithNewOp<hw::InstanceOp>(
1798 op, convResTypes, op.getInstanceName(), op.getModuleName(),
1799 adaptor.getOperands(), op.getArgNames(),
1800 op.getResultNames(), /*Parameter*/
1801 rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr);
1802
1803 return success();
1804 }
1805};
1806
1807struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
1808 using OpConversionPattern::OpConversionPattern;
1809
1810 LogicalResult
1811 matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
1812 ConversionPatternRewriter &rewriter) const override {
1813 rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
1814 return success();
1815 }
1816};
1817
1818struct CallOpConversion : public OpConversionPattern<func::CallOp> {
1819 using OpConversionPattern::OpConversionPattern;
1820
1821 LogicalResult
1822 matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
1823 ConversionPatternRewriter &rewriter) const override {
1824 SmallVector<Type> convResTypes;
1825 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1826 return failure();
1827 rewriter.replaceOpWithNewOp<func::CallOp>(
1828 op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
1829 return success();
1830 }
1831};
1832
1833struct UnrealizedConversionCastConversion
1834 : public OpConversionPattern<UnrealizedConversionCastOp> {
1835 using OpConversionPattern::OpConversionPattern;
1836
1837 LogicalResult
1838 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
1839 ConversionPatternRewriter &rewriter) const override {
1840 SmallVector<Type> convResTypes;
1841 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1842 return failure();
1843
1844 // Drop the cast if the operand and result types agree after type
1845 // conversion.
1846 if (convResTypes == adaptor.getOperands().getTypes()) {
1847 rewriter.replaceOp(op, adaptor.getOperands());
1848 return success();
1849 }
1850
1851 rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
1852 op, convResTypes, adaptor.getOperands());
1853 return success();
1854 }
1855};
1856
1857struct ShlOpConversion : public OpConversionPattern<ShlOp> {
1858 using OpConversionPattern::OpConversionPattern;
1859
1860 LogicalResult
1861 matchAndRewrite(ShlOp op, OpAdaptor adaptor,
1862 ConversionPatternRewriter &rewriter) const override {
1863 Type resultType = typeConverter->convertType(op.getResult().getType());
1864
1865 // Comb shift operations require the same bit-width for value and amount
1866 Value amount =
1867 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1868 resultType.getIntOrFloatBitWidth(), op->getLoc());
1869 rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
1870 amount, false);
1871 return success();
1872 }
1873};
1874
1875struct ShrOpConversion : public OpConversionPattern<ShrOp> {
1876 using OpConversionPattern::OpConversionPattern;
1877
1878 LogicalResult
1879 matchAndRewrite(ShrOp op, OpAdaptor adaptor,
1880 ConversionPatternRewriter &rewriter) const override {
1881 Type resultType = typeConverter->convertType(op.getResult().getType());
1882
1883 // Comb shift operations require the same bit-width for value and amount
1884 Value amount =
1885 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1886 resultType.getIntOrFloatBitWidth(), op->getLoc());
1887 rewriter.replaceOpWithNewOp<comb::ShrUOp>(
1888 op, resultType, adaptor.getValue(), amount, false);
1889 return success();
1890 }
1891};
1892
1893struct PowUOpConversion : public OpConversionPattern<PowUOp> {
1894 using OpConversionPattern::OpConversionPattern;
1895
1896 LogicalResult
1897 matchAndRewrite(PowUOp op, OpAdaptor adaptor,
1898 ConversionPatternRewriter &rewriter) const override {
1899 Type resultType = typeConverter->convertType(op.getResult().getType());
1900
1901 Location loc = op->getLoc();
1902
1903 Value zeroVal = hw::ConstantOp::create(rewriter, loc, APInt(1, 0));
1904 // zero extend both LHS & RHS to ensure the unsigned integers are
1905 // interpreted correctly when calculating power
1906 auto lhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getLhs());
1907 auto rhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getRhs());
1908
1909 // lower the exponentiation via MLIR's math dialect
1910 auto pow = mlir::math::IPowIOp::create(rewriter, loc, lhs, rhs);
1911
1912 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, pow, 0);
1913 return success();
1914 }
1915};
1916
1917struct PowSOpConversion : public OpConversionPattern<PowSOp> {
1918 using OpConversionPattern::OpConversionPattern;
1919
1920 LogicalResult
1921 matchAndRewrite(PowSOp op, OpAdaptor adaptor,
1922 ConversionPatternRewriter &rewriter) const override {
1923 Type resultType = typeConverter->convertType(op.getResult().getType());
1924
1925 // utilize MLIR math dialect's math.ipowi to handle the exponentiation of
1926 // expression
1927 rewriter.replaceOpWithNewOp<mlir::math::IPowIOp>(
1928 op, resultType, adaptor.getLhs(), adaptor.getRhs());
1929 return success();
1930 }
1931};
1932
1933struct AShrOpConversion : public OpConversionPattern<AShrOp> {
1934 using OpConversionPattern::OpConversionPattern;
1935
1936 LogicalResult
1937 matchAndRewrite(AShrOp op, OpAdaptor adaptor,
1938 ConversionPatternRewriter &rewriter) const override {
1939 Type resultType = typeConverter->convertType(op.getResult().getType());
1940
1941 // Comb shift operations require the same bit-width for value and amount
1942 Value amount =
1943 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1944 resultType.getIntOrFloatBitWidth(), op->getLoc());
1945 rewriter.replaceOpWithNewOp<comb::ShrSOp>(
1946 op, resultType, adaptor.getValue(), amount, false);
1947 return success();
1948 }
1949};
1950
1951struct ReadOpConversion : public OpConversionPattern<ReadOp> {
1952 using OpConversionPattern::OpConversionPattern;
1953
1954 LogicalResult
1955 matchAndRewrite(ReadOp op, OpAdaptor adaptor,
1956 ConversionPatternRewriter &rewriter) const override {
1957 rewriter.replaceOpWithNewOp<llhd::ProbeOp>(op, adaptor.getInput());
1958 return success();
1959 }
1960};
1961
1962struct AssignedVariableOpConversion
1963 : public OpConversionPattern<AssignedVariableOp> {
1964 using OpConversionPattern::OpConversionPattern;
1965
1966 LogicalResult
1967 matchAndRewrite(AssignedVariableOp op, OpAdaptor adaptor,
1968 ConversionPatternRewriter &rewriter) const override {
1969 rewriter.replaceOpWithNewOp<hw::WireOp>(op, adaptor.getInput(),
1970 adaptor.getNameAttr());
1971 return success();
1972 }
1973};
1974
1975// Blocking and continuous assignments get a 0ns 0d 1e delay.
1976static llhd::TimeAttr
1977getBlockingOrContinuousAssignDelay(mlir::MLIRContext *context) {
1978 return llhd::TimeAttr::get(context, 0U, "ns", 0, 1);
1979}
1980
1981template <typename OpTy>
1982struct AssignOpConversion : public OpConversionPattern<OpTy> {
1984 using OpAdaptor = typename OpTy::Adaptor;
1985
1986 LogicalResult
1987 matchAndRewrite(OpTy op, OpAdaptor adaptor,
1988 ConversionPatternRewriter &rewriter) const override {
1989 // Determine the delay for the assignment.
1990 Value delay;
1991 if constexpr (std::is_same_v<OpTy, ContinuousAssignOp> ||
1992 std::is_same_v<OpTy, BlockingAssignOp>) {
1993 delay = llhd::ConstantTimeOp::create(
1994 rewriter, op->getLoc(),
1995 getBlockingOrContinuousAssignDelay(op->getContext()));
1996 } else if constexpr (std::is_same_v<OpTy, NonBlockingAssignOp>) {
1997 // Non-blocking assignments get a 0ns 1d 0e delay.
1998 delay = llhd::ConstantTimeOp::create(
1999 rewriter, op->getLoc(),
2000 llhd::TimeAttr::get(op->getContext(), 0U, "ns", 1, 0));
2001 } else {
2002 // Delayed assignments have a delay operand.
2003 delay = adaptor.getDelay();
2004 }
2005
2006 rewriter.replaceOpWithNewOp<llhd::DriveOp>(
2007 op, adaptor.getDst(), adaptor.getSrc(), delay, Value{});
2008 return success();
2009 }
2010};
2011
2012struct ConditionalOpConversion : public OpConversionPattern<ConditionalOp> {
2013 using OpConversionPattern::OpConversionPattern;
2014
2015 LogicalResult
2016 matchAndRewrite(ConditionalOp op, OpAdaptor adaptor,
2017 ConversionPatternRewriter &rewriter) const override {
2018 // TODO: This lowering is only correct if the condition is two-valued. If
2019 // the condition is X or Z, both branches of the conditional must be
2020 // evaluated and merged with the appropriate lookup table. See documentation
2021 // for `ConditionalOp`.
2022 auto type = typeConverter->convertType(op.getType());
2023
2024 auto hasNoWriteEffect = [](Region &region) {
2025 auto result = region.walk([](Operation *operation) {
2026 if (auto memOp = dyn_cast<MemoryEffectOpInterface>(operation))
2027 if (!memOp.hasEffect<MemoryEffects::Write>() &&
2028 !memOp.hasEffect<MemoryEffects::Free>())
2029 return WalkResult::advance();
2030
2031 if (operation->hasTrait<OpTrait::HasRecursiveMemoryEffects>())
2032 return WalkResult::advance();
2033
2034 return WalkResult::interrupt();
2035 });
2036 return !result.wasInterrupted();
2037 };
2038
2039 if (hasNoWriteEffect(op.getTrueRegion()) &&
2040 hasNoWriteEffect(op.getFalseRegion())) {
2041 Operation *trueTerm = op.getTrueRegion().front().getTerminator();
2042 Operation *falseTerm = op.getFalseRegion().front().getTerminator();
2043
2044 rewriter.inlineBlockBefore(&op.getTrueRegion().front(), op);
2045 rewriter.inlineBlockBefore(&op.getFalseRegion().front(), op);
2046
2047 Value convTrueVal = typeConverter->materializeTargetConversion(
2048 rewriter, op.getLoc(), type, trueTerm->getOperand(0));
2049 Value convFalseVal = typeConverter->materializeTargetConversion(
2050 rewriter, op.getLoc(), type, falseTerm->getOperand(0));
2051
2052 rewriter.eraseOp(trueTerm);
2053 rewriter.eraseOp(falseTerm);
2054
2055 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, adaptor.getCondition(),
2056 convTrueVal, convFalseVal);
2057 return success();
2058 }
2059
2060 auto ifOp =
2061 scf::IfOp::create(rewriter, op.getLoc(), type, adaptor.getCondition());
2062 rewriter.inlineRegionBefore(op.getTrueRegion(), ifOp.getThenRegion(),
2063 ifOp.getThenRegion().end());
2064 rewriter.inlineRegionBefore(op.getFalseRegion(), ifOp.getElseRegion(),
2065 ifOp.getElseRegion().end());
2066 rewriter.replaceOp(op, ifOp);
2067 return success();
2068 }
2069};
2070
2071struct YieldOpConversion : public OpConversionPattern<YieldOp> {
2072 using OpConversionPattern::OpConversionPattern;
2073
2074 LogicalResult
2075 matchAndRewrite(YieldOp op, OpAdaptor adaptor,
2076 ConversionPatternRewriter &rewriter) const override {
2077 if (isa<llhd::GlobalSignalOp>(op->getParentOp()))
2078 rewriter.replaceOpWithNewOp<llhd::YieldOp>(op, adaptor.getResult());
2079 else
2080 rewriter.replaceOpWithNewOp<scf::YieldOp>(op, adaptor.getResult());
2081 return success();
2082 }
2083};
2084
2085template <typename SourceOp>
2086struct InPlaceOpConversion : public OpConversionPattern<SourceOp> {
2088 using OpAdaptor = typename SourceOp::Adaptor;
2089
2090 LogicalResult
2091 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
2092 ConversionPatternRewriter &rewriter) const override {
2093 rewriter.modifyOpInPlace(op,
2094 [&]() { op->setOperands(adaptor.getOperands()); });
2095 return success();
2096 }
2097};
2098
2099template <typename MooreOpTy, typename VerifOpTy>
2100struct AssertLikeOpConversion : public OpConversionPattern<MooreOpTy> {
2102 using OpAdaptor = typename MooreOpTy::Adaptor;
2103
2104 LogicalResult
2105 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
2106 ConversionPatternRewriter &rewriter) const override {
2107 StringAttr label =
2108 op.getLabel().has_value()
2109 ? StringAttr::get(op->getContext(), op.getLabel().value())
2110 : StringAttr::get(op->getContext());
2111 rewriter.replaceOpWithNewOp<VerifOpTy>(op, adaptor.getCond(), mlir::Value(),
2112 label);
2113 return success();
2114 }
2115};
2116
2117//===----------------------------------------------------------------------===//
2118// Format String Conversion
2119//===----------------------------------------------------------------------===//
2120
2121struct FormatLiteralOpConversion : public OpConversionPattern<FormatLiteralOp> {
2122 using OpConversionPattern::OpConversionPattern;
2123
2124 LogicalResult
2125 matchAndRewrite(FormatLiteralOp op, OpAdaptor adaptor,
2126 ConversionPatternRewriter &rewriter) const override {
2127 rewriter.replaceOpWithNewOp<sim::FormatLiteralOp>(op, adaptor.getLiteral());
2128 return success();
2129 }
2130};
2131
2132struct FormatConcatOpConversion : public OpConversionPattern<FormatConcatOp> {
2133 using OpConversionPattern::OpConversionPattern;
2134
2135 LogicalResult
2136 matchAndRewrite(FormatConcatOp op, OpAdaptor adaptor,
2137 ConversionPatternRewriter &rewriter) const override {
2138 rewriter.replaceOpWithNewOp<sim::FormatStringConcatOp>(op,
2139 adaptor.getInputs());
2140 return success();
2141 }
2142};
2143
2144struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
2145 using OpConversionPattern::OpConversionPattern;
2146
2147 LogicalResult
2148 matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
2149 ConversionPatternRewriter &rewriter) const override {
2150
2151 char padChar = adaptor.getPadding() == IntPadding::Space ? 32 : 48;
2152 IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar);
2153 auto widthAttr = adaptor.getSpecifierWidthAttr();
2154
2155 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2156 BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2157
2158 switch (op.getFormat()) {
2159 case IntFormat::Decimal:
2160 rewriter.replaceOpWithNewOp<sim::FormatDecOp>(
2161 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr,
2162 adaptor.getIsSignedAttr());
2163 return success();
2164 case IntFormat::Binary:
2165 rewriter.replaceOpWithNewOp<sim::FormatBinOp>(
2166 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2167 return success();
2168 case IntFormat::Octal:
2169 rewriter.replaceOpWithNewOp<sim::FormatOctOp>(
2170 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2171 return success();
2172 case IntFormat::HexLower:
2173 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2174 op, adaptor.getValue(), rewriter.getBoolAttr(false),
2175 isLeftAlignedAttr, padCharAttr, widthAttr);
2176 return success();
2177 case IntFormat::HexUpper:
2178 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2179 op, adaptor.getValue(), rewriter.getBoolAttr(true), isLeftAlignedAttr,
2180 padCharAttr, widthAttr);
2181 return success();
2182 }
2183 return rewriter.notifyMatchFailure(op, "unsupported int format");
2184 }
2185};
2186
2187struct FormatRealOpConversion : public OpConversionPattern<FormatRealOp> {
2188 using OpConversionPattern::OpConversionPattern;
2189
2190 LogicalResult
2191 matchAndRewrite(FormatRealOp op, OpAdaptor adaptor,
2192 ConversionPatternRewriter &rewriter) const override {
2193 auto fracDigitsAttr = adaptor.getFracDigitsAttr();
2194
2195 auto fieldWidthAttr = adaptor.getFieldWidthAttr();
2196 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2197 mlir::BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2198
2199 switch (op.getFormat()) {
2200 case RealFormat::General:
2201 rewriter.replaceOpWithNewOp<sim::FormatGeneralOp>(
2202 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2203 fracDigitsAttr);
2204 return success();
2205 case RealFormat::Float:
2206 rewriter.replaceOpWithNewOp<sim::FormatFloatOp>(
2207 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2208 fracDigitsAttr);
2209 return success();
2210 case RealFormat::Exponential:
2211 rewriter.replaceOpWithNewOp<sim::FormatScientificOp>(
2212 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2213 fracDigitsAttr);
2214 return success();
2215 }
2216 }
2217};
2218
2219struct StringLenOpConversion : public OpConversionPattern<StringLenOp> {
2220 using OpConversionPattern::OpConversionPattern;
2221
2222 LogicalResult
2223 matchAndRewrite(StringLenOp op, OpAdaptor adaptor,
2224 ConversionPatternRewriter &rewriter) const override {
2225 rewriter.replaceOpWithNewOp<sim::StringLengthOp>(op, adaptor.getStr());
2226 return success();
2227 }
2228};
2229
2230struct StringConcatOpConversion : public OpConversionPattern<StringConcatOp> {
2231 using OpConversionPattern::OpConversionPattern;
2232
2233 LogicalResult
2234 matchAndRewrite(StringConcatOp op, OpAdaptor adaptor,
2235 ConversionPatternRewriter &rewriter) const override {
2236 rewriter.replaceOpWithNewOp<sim::StringConcatOp>(op, adaptor.getInputs());
2237 return success();
2238 }
2239};
2240
2241struct QueueSizeBIOpConversion : public OpConversionPattern<QueueSizeBIOp> {
2242 using OpConversionPattern::OpConversionPattern;
2243
2244 LogicalResult
2245 matchAndRewrite(QueueSizeBIOp op, OpAdaptor adaptor,
2246 ConversionPatternRewriter &rewriter) const override {
2247 auto readQueue =
2248 llhd::ProbeOp::create(rewriter, op->getLoc(), adaptor.getQueue());
2249
2250 rewriter.replaceOpWithNewOp<sim::QueueSizeOp>(op, readQueue);
2251 return success();
2252 }
2253};
2254
2255// Given a reference `ref` to some Moore type, this function emits a
2256// `ProbeOp` to read the contained value, then passes it to the function `func`.
2257// It finally emits a `DriveOp` to write the result of the function back to
2258// the referenced signal.
2259//
2260// This is useful for converting impure operations (such as the Moore ops for
2261// manipulating queues) into pure operations. (Which do not mutate the source
2262// value, instead returning a modified value.)
2263static void
2264probeRefAndDriveWithResult(OpBuilder &builder, Location loc, Value ref,
2265 const std::function<Value(Value)> &func) {
2266
2267 Value v = llhd::ProbeOp::create(builder, loc, ref);
2268
2269 // Drive using the same delay as a blocking assignment
2270 Value delay = llhd::ConstantTimeOp::create(
2271 builder, loc, getBlockingOrContinuousAssignDelay(builder.getContext()));
2272
2273 llhd::DriveOp::create(builder, loc, ref, func(v), delay, Value{});
2274}
2275
2276struct QueuePushBackOpConversion : public OpConversionPattern<QueuePushBackOp> {
2277 using OpConversionPattern::OpConversionPattern;
2278
2279 LogicalResult
2280 matchAndRewrite(QueuePushBackOp op, OpAdaptor adaptor,
2281 ConversionPatternRewriter &rewriter) const override {
2282 probeRefAndDriveWithResult(
2283 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2284 return sim::QueuePushBackOp::create(rewriter, op->getLoc(), queue,
2285 adaptor.getElement());
2286 });
2287
2288 rewriter.eraseOp(op);
2289 return success();
2290 }
2291};
2292
2293struct QueuePushFrontOpConversion
2294 : public OpConversionPattern<QueuePushFrontOp> {
2295 using OpConversionPattern::OpConversionPattern;
2296
2297 LogicalResult
2298 matchAndRewrite(QueuePushFrontOp op, OpAdaptor adaptor,
2299 ConversionPatternRewriter &rewriter) const override {
2300
2301 probeRefAndDriveWithResult(
2302 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2303 return sim::QueuePushFrontOp::create(rewriter, op->getLoc(), queue,
2304 adaptor.getElement());
2305 });
2306
2307 rewriter.eraseOp(op);
2308 return success();
2309 }
2310};
2311
2312struct QueuePopBackOpConversion : public OpConversionPattern<QueuePopBackOp> {
2313 using OpConversionPattern::OpConversionPattern;
2314
2315 LogicalResult
2316 matchAndRewrite(QueuePopBackOp op, OpAdaptor adaptor,
2317 ConversionPatternRewriter &rewriter) const override {
2318 probeRefAndDriveWithResult(
2319 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2320 auto popBack =
2321 sim::QueuePopBackOp::create(rewriter, op->getLoc(), queue);
2322
2323 op.replaceAllUsesWith(popBack.getPopped());
2324 return popBack.getOutQueue();
2325 });
2326 rewriter.eraseOp(op);
2327
2328 return success();
2329 }
2330};
2331
2332struct QueuePopFrontOpConversion : public OpConversionPattern<QueuePopFrontOp> {
2333 using OpConversionPattern::OpConversionPattern;
2334
2335 LogicalResult
2336 matchAndRewrite(QueuePopFrontOp op, OpAdaptor adaptor,
2337 ConversionPatternRewriter &rewriter) const override {
2338 probeRefAndDriveWithResult(
2339 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2340 auto popFront =
2341 sim::QueuePopFrontOp::create(rewriter, op->getLoc(), queue);
2342
2343 op.replaceAllUsesWith(popFront.getPopped());
2344 return popFront.getOutQueue();
2345 });
2346 rewriter.eraseOp(op);
2347
2348 return success();
2349 }
2350};
2351
2352struct QueueClearOpConversion : public OpConversionPattern<QueueClearOp> {
2353 using OpConversionPattern::OpConversionPattern;
2354
2355 LogicalResult
2356 matchAndRewrite(QueueClearOp op, OpAdaptor adaptor,
2357 ConversionPatternRewriter &rewriter) const override {
2358 auto refType = cast<llhd::RefType>(adaptor.getQueue().getType());
2359 auto queueType = refType.getNestedType();
2360 Value emptyQueue =
2361 sim::QueueEmptyOp::create(rewriter, op->getLoc(), queueType);
2362
2363 // Replace with an assignment to an empty queue
2364 Value delay = llhd::ConstantTimeOp::create(
2365 rewriter, op.getLoc(),
2366 getBlockingOrContinuousAssignDelay(rewriter.getContext()));
2367
2368 llhd::DriveOp::create(rewriter, op.getLoc(), adaptor.getQueue(), emptyQueue,
2369 delay, Value{});
2370
2371 rewriter.eraseOp(op);
2372 return success();
2373 }
2374};
2375
2376struct QueueInsertOpConversion : public OpConversionPattern<QueueInsertOp> {
2377 using OpConversionPattern::OpConversionPattern;
2378
2379 LogicalResult
2380 matchAndRewrite(QueueInsertOp op, OpAdaptor adaptor,
2381 ConversionPatternRewriter &rewriter) const override {
2382 probeRefAndDriveWithResult(
2383 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2384 auto insert =
2385 sim::QueueInsertOp::create(rewriter, op->getLoc(), queue,
2386 adaptor.getIndex(), adaptor.getItem());
2387
2388 return insert.getOutQueue();
2389 });
2390 rewriter.eraseOp(op);
2391
2392 return success();
2393 }
2394};
2395
2396struct QueueDeleteOpConversion : public OpConversionPattern<QueueDeleteOp> {
2397 using OpConversionPattern::OpConversionPattern;
2398
2399 LogicalResult
2400 matchAndRewrite(QueueDeleteOp op, OpAdaptor adaptor,
2401 ConversionPatternRewriter &rewriter) const override {
2402 probeRefAndDriveWithResult(
2403 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2404 auto delOp = sim::QueueDeleteOp::create(rewriter, op->getLoc(), queue,
2405 adaptor.getIndex());
2406
2407 return delOp.getOutQueue();
2408 });
2409 rewriter.eraseOp(op);
2410
2411 return success();
2412 };
2413};
2414
2415struct DisplayBIOpConversion : public OpConversionPattern<DisplayBIOp> {
2416 using OpConversionPattern::OpConversionPattern;
2417
2418 LogicalResult
2419 matchAndRewrite(DisplayBIOp op, OpAdaptor adaptor,
2420 ConversionPatternRewriter &rewriter) const override {
2421 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
2422 op, adaptor.getMessage());
2423 return success();
2424 }
2425};
2426
2427} // namespace
2428
2429//===----------------------------------------------------------------------===//
2430// Simulation Control Conversion
2431//===----------------------------------------------------------------------===//
2432
2433// moore.builtin.stop -> sim.pause
2434static LogicalResult convert(StopBIOp op, StopBIOp::Adaptor adaptor,
2435 ConversionPatternRewriter &rewriter) {
2436 rewriter.replaceOpWithNewOp<sim::PauseOp>(op, /*verbose=*/false);
2437 return success();
2438}
2439
2440// moore.builtin.finish -> sim.terminate
2441static LogicalResult convert(FinishBIOp op, FinishBIOp::Adaptor adaptor,
2442 ConversionPatternRewriter &rewriter) {
2443 rewriter.replaceOpWithNewOp<sim::TerminateOp>(op, op.getExitCode() == 0,
2444 /*verbose=*/false);
2445 return success();
2446}
2447
2448// moore.builtin.severity -> sim.proc.print
2449static LogicalResult convert(SeverityBIOp op, SeverityBIOp::Adaptor adaptor,
2450 ConversionPatternRewriter &rewriter) {
2451
2452 std::string severityString;
2453
2454 switch (op.getSeverity()) {
2455 case (Severity::Fatal):
2456 severityString = "Fatal: ";
2457 break;
2458 case (Severity::Error):
2459 severityString = "Error: ";
2460 break;
2461 case (Severity::Warning):
2462 severityString = "Warning: ";
2463 break;
2464 case (Severity::Info):
2465 severityString = "Info: ";
2466 break;
2467 default:
2468 return failure();
2469 }
2470
2471 auto prefix =
2472 sim::FormatLiteralOp::create(rewriter, op.getLoc(), severityString);
2473 auto message = sim::FormatStringConcatOp::create(
2474 rewriter, op.getLoc(), ValueRange{prefix, adaptor.getMessage()});
2475 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(op, message);
2476 return success();
2477}
2478
2479// moore.builtin.finish_message
2480static LogicalResult convert(FinishMessageBIOp op,
2481 FinishMessageBIOp::Adaptor adaptor,
2482 ConversionPatternRewriter &rewriter) {
2483 // We don't support printing termination/pause messages yet.
2484 rewriter.eraseOp(op);
2485 return success();
2486}
2487
2488//===----------------------------------------------------------------------===//
2489// Timing Control Conversion
2490//===----------------------------------------------------------------------===//
2491
2492// moore.builtin.time
2493static LogicalResult convert(TimeBIOp op, TimeBIOp::Adaptor adaptor,
2494 ConversionPatternRewriter &rewriter) {
2495 rewriter.replaceOpWithNewOp<llhd::CurrentTimeOp>(op);
2496 return success();
2497}
2498
2499// moore.logic_to_time
2500static LogicalResult convert(LogicToTimeOp op, LogicToTimeOp::Adaptor adaptor,
2501 ConversionPatternRewriter &rewriter) {
2502 rewriter.replaceOpWithNewOp<llhd::IntToTimeOp>(op, adaptor.getInput());
2503 return success();
2504}
2505
2506// moore.time_to_logic
2507static LogicalResult convert(TimeToLogicOp op, TimeToLogicOp::Adaptor adaptor,
2508 ConversionPatternRewriter &rewriter) {
2509 rewriter.replaceOpWithNewOp<llhd::TimeToIntOp>(op, adaptor.getInput());
2510 return success();
2511}
2512
2513//===----------------------------------------------------------------------===//
2514// Conversion Infrastructure
2515//===----------------------------------------------------------------------===//
2516
2517static void populateLegality(ConversionTarget &target,
2518 const TypeConverter &converter) {
2519 target.addIllegalDialect<MooreDialect>();
2520 target.addLegalDialect<comb::CombDialect>();
2521 target.addLegalDialect<hw::HWDialect>();
2522 target.addLegalDialect<seq::SeqDialect>();
2523 target.addLegalDialect<llhd::LLHDDialect>();
2524 target.addLegalDialect<ltl::LTLDialect>();
2525 target.addLegalDialect<mlir::BuiltinDialect>();
2526 target.addLegalDialect<mlir::math::MathDialect>();
2527 target.addLegalDialect<sim::SimDialect>();
2528 target.addLegalDialect<mlir::LLVM::LLVMDialect>();
2529 target.addLegalDialect<verif::VerifDialect>();
2530 target.addLegalDialect<arith::ArithDialect>();
2531
2532 target.addLegalOp<debug::ScopeOp>();
2533
2534 target.addDynamicallyLegalOp<scf::YieldOp, func::CallOp, func::ReturnOp,
2535 UnrealizedConversionCastOp, hw::OutputOp,
2536 hw::InstanceOp, debug::ArrayOp, debug::StructOp,
2537 debug::VariableOp>(
2538 [&](Operation *op) { return converter.isLegal(op); });
2539
2540 target.addDynamicallyLegalOp<scf::IfOp, scf::ForOp, scf::ExecuteRegionOp,
2541 scf::WhileOp, scf::ForallOp>([&](Operation *op) {
2542 return converter.isLegal(op) && !op->getParentOfType<llhd::ProcessOp>();
2543 });
2544
2545 target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
2546 return converter.isSignatureLegal(op.getFunctionType());
2547 });
2548
2549 target.addDynamicallyLegalOp<hw::HWModuleOp>([&](hw::HWModuleOp op) {
2550 return converter.isSignatureLegal(op.getModuleType().getFuncType()) &&
2551 converter.isLegal(&op.getBody());
2552 });
2553}
2554
2555static void populateTypeConversion(TypeConverter &typeConverter) {
2556 typeConverter.addConversion([&](IntType type) {
2557 return IntegerType::get(type.getContext(), type.getWidth());
2558 });
2559
2560 typeConverter.addConversion([&](RealType type) -> mlir::Type {
2561 MLIRContext *ctx = type.getContext();
2562 switch (type.getWidth()) {
2563 case moore::RealWidth::f32:
2564 return mlir::Float32Type::get(ctx);
2565 case moore::RealWidth::f64:
2566 return mlir::Float64Type::get(ctx);
2567 }
2568 });
2569
2570 typeConverter.addConversion(
2571 [&](TimeType type) { return llhd::TimeType::get(type.getContext()); });
2572
2573 typeConverter.addConversion([&](FormatStringType type) {
2574 return sim::FormatStringType::get(type.getContext());
2575 });
2576
2577 typeConverter.addConversion([&](StringType type) {
2578 return sim::DynamicStringType::get(type.getContext());
2579 });
2580
2581 typeConverter.addConversion([&](QueueType type) {
2582 return sim::QueueType::get(type.getContext(),
2583 typeConverter.convertType(type.getElementType()),
2584 type.getBound());
2585 });
2586
2587 typeConverter.addConversion([&](ArrayType type) -> std::optional<Type> {
2588 if (auto elementType = typeConverter.convertType(type.getElementType()))
2589 return hw::ArrayType::get(elementType, type.getSize());
2590 return {};
2591 });
2592
2593 // FIXME: Unpacked arrays support more element types than their packed
2594 // variants, and as such, mapping them to hw::Array is somewhat naive. See
2595 // also the analogous note below concerning unpacked struct type conversion.
2596 typeConverter.addConversion(
2597 [&](UnpackedArrayType type) -> std::optional<Type> {
2598 if (auto elementType = typeConverter.convertType(type.getElementType()))
2599 return hw::ArrayType::get(elementType, type.getSize());
2600 return {};
2601 });
2602
2603 typeConverter.addConversion([&](StructType type) -> std::optional<Type> {
2604 SmallVector<hw::StructType::FieldInfo> fields;
2605 for (auto field : type.getMembers()) {
2606 hw::StructType::FieldInfo info;
2607 info.type = typeConverter.convertType(field.type);
2608 if (!info.type)
2609 return {};
2610 info.name = field.name;
2611 fields.push_back(info);
2612 }
2613 return hw::StructType::get(type.getContext(), fields);
2614 });
2615
2616 // FIXME: Mapping unpacked struct type to struct type in hw dialect may be a
2617 // plain solution. The packed and unpacked data structures have some
2618 // differences though they look similarily. The packed data structure is
2619 // contiguous in memory but another is opposite. The differences will affect
2620 // data layout and granularity of event tracking in simulation.
2621 typeConverter.addConversion(
2622 [&](UnpackedStructType type) -> std::optional<Type> {
2623 SmallVector<hw::StructType::FieldInfo> fields;
2624 for (auto field : type.getMembers()) {
2625 hw::StructType::FieldInfo info;
2626 info.type = typeConverter.convertType(field.type);
2627 if (!info.type)
2628 return {};
2629 info.name = field.name;
2630 fields.push_back(info);
2631 }
2632 return hw::StructType::get(type.getContext(), fields);
2633 });
2634
2635 // UnionType -> hw::UnionType
2636 typeConverter.addConversion([&](UnionType type) -> std::optional<Type> {
2637 SmallVector<hw::UnionType::FieldInfo> fields;
2638 for (auto field : type.getMembers()) {
2639 hw::UnionType::FieldInfo info;
2640 info.type = typeConverter.convertType(field.type);
2641 if (!info.type)
2642 return {};
2643 info.name = field.name;
2644 info.offset = 0; // packed union, all fields start at bit 0
2645 fields.push_back(info);
2646 }
2647 auto result = hw::UnionType::get(type.getContext(), fields);
2648 return result;
2649 });
2650
2651 // UnpackedUnionType -> hw::UnionType
2652 typeConverter.addConversion(
2653 [&](UnpackedUnionType type) -> std::optional<Type> {
2654 SmallVector<hw::UnionType::FieldInfo> fields;
2655 for (auto field : type.getMembers()) {
2656 hw::UnionType::FieldInfo info;
2657 info.type = typeConverter.convertType(field.type);
2658 if (!info.type)
2659 return {};
2660 info.name = field.name;
2661 info.offset = 0;
2662 fields.push_back(info);
2663 }
2664 return hw::UnionType::get(type.getContext(), fields);
2665 });
2666
2667 // Conversion of CHandle to LLVMPointerType
2668 typeConverter.addConversion([&](ChandleType type) -> std::optional<Type> {
2669 return LLVM::LLVMPointerType::get(type.getContext());
2670 });
2671
2672 // Explicitly mark LLVMPointerType as a legal target
2673 typeConverter.addConversion(
2674 [](LLVM::LLVMPointerType t) -> std::optional<Type> { return t; });
2675
2676 // ClassHandleType -> !llvm.ptr
2677 typeConverter.addConversion([&](ClassHandleType type) -> std::optional<Type> {
2678 return LLVM::LLVMPointerType::get(type.getContext());
2679 });
2680
2681 typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
2682 if (auto innerType = typeConverter.convertType(type.getNestedType()))
2683 return llhd::RefType::get(innerType);
2684 return {};
2685 });
2686
2687 // Valid target types.
2688 typeConverter.addConversion([](IntegerType type) { return type; });
2689 typeConverter.addConversion([](FloatType type) { return type; });
2690 typeConverter.addConversion([](sim::DynamicStringType type) { return type; });
2691 typeConverter.addConversion([](llhd::TimeType type) { return type; });
2692 typeConverter.addConversion([](debug::ArrayType type) { return type; });
2693 typeConverter.addConversion([](debug::ScopeType type) { return type; });
2694 typeConverter.addConversion([](debug::StructType type) { return type; });
2695
2696 typeConverter.addConversion([&](llhd::RefType type) -> std::optional<Type> {
2697 if (auto innerType = typeConverter.convertType(type.getNestedType()))
2698 return llhd::RefType::get(innerType);
2699 return {};
2700 });
2701
2702 typeConverter.addConversion([&](hw::ArrayType type) -> std::optional<Type> {
2703 if (auto elementType = typeConverter.convertType(type.getElementType()))
2704 return hw::ArrayType::get(elementType, type.getNumElements());
2705 return {};
2706 });
2707
2708 typeConverter.addConversion([&](hw::StructType type) -> std::optional<Type> {
2709 SmallVector<hw::StructType::FieldInfo> fields;
2710 for (auto field : type.getElements()) {
2711 hw::StructType::FieldInfo info;
2712 info.type = typeConverter.convertType(field.type);
2713 if (!info.type)
2714 return {};
2715 info.name = field.name;
2716 fields.push_back(info);
2717 }
2718 return hw::StructType::get(type.getContext(), fields);
2719 });
2720
2721 typeConverter.addConversion([&](hw::UnionType type) -> std::optional<Type> {
2722 SmallVector<hw::UnionType::FieldInfo> fields;
2723 for (auto field : type.getElements()) {
2724 hw::UnionType::FieldInfo info;
2725 info.type = typeConverter.convertType(field.type);
2726 if (!info.type)
2727 return {};
2728 info.name = field.name;
2729 info.offset = field.offset;
2730 fields.push_back(info);
2731 }
2732 return hw::UnionType::get(type.getContext(), fields);
2733 });
2734
2735 typeConverter.addTargetMaterialization(
2736 [&](mlir::OpBuilder &builder, mlir::Type resultType,
2737 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
2738 if (inputs.size() != 1 || !inputs[0])
2739 return Value();
2740 return UnrealizedConversionCastOp::create(builder, loc, resultType,
2741 inputs[0])
2742 .getResult(0);
2743 });
2744
2745 typeConverter.addSourceMaterialization(
2746 [&](mlir::OpBuilder &builder, mlir::Type resultType,
2747 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
2748 if (inputs.size() != 1)
2749 return Value();
2750 return UnrealizedConversionCastOp::create(builder, loc, resultType,
2751 inputs[0])
2752 ->getResult(0);
2753 });
2754}
2755
2757 TypeConverter &typeConverter,
2758 ClassTypeCache &classCache) {
2759
2760 patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
2761 classCache);
2762 patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
2763 classCache);
2764 patterns.add<ClassPropertyRefOpConversion>(typeConverter,
2765 patterns.getContext(), classCache);
2766
2767 // clang-format off
2768 patterns.add<
2769 ClassUpcastOpConversion,
2770 // Patterns of declaration operations.
2771 VariableOpConversion,
2772 NetOpConversion,
2773
2774 // Patterns for conversion operations.
2775 ConversionOpConversion,
2776 BitcastConversion<PackedToSBVOp>,
2777 BitcastConversion<SBVToPackedOp>,
2778 NoOpConversion<LogicToIntOp>,
2779 NoOpConversion<IntToLogicOp>,
2780 NoOpConversion<ToBuiltinIntOp>,
2781 NoOpConversion<FromBuiltinIntOp>,
2782 TruncOpConversion,
2783 ZExtOpConversion,
2784 SExtOpConversion,
2785 SIntToRealOpConversion,
2786 UIntToRealOpConversion,
2787 IntToStringOpConversion,
2788 RealToIntOpConversion,
2789
2790 // Patterns of miscellaneous operations.
2791 ConstantOpConv,
2792 ConstantRealOpConv,
2793 ConcatOpConversion,
2794 ReplicateOpConversion,
2795 ConstantTimeOpConv,
2796 ExtractOpConversion,
2797 DynExtractOpConversion,
2798 DynExtractRefOpConversion,
2799 ReadOpConversion,
2800 StructExtractOpConversion,
2801 StructExtractRefOpConversion,
2802 ExtractRefOpConversion,
2803 StructCreateOpConversion,
2804 UnionCreateOpConversion,
2805 UnionExtractOpConversion,
2806 UnionExtractRefOpConversion,
2807 ConditionalOpConversion,
2808 ArrayCreateOpConversion,
2809 YieldOpConversion,
2810 OutputOpConversion,
2811 ConstantStringOpConv,
2812
2813 // Patterns of unary operations.
2814 ReduceAndOpConversion,
2815 ReduceOrOpConversion,
2816 ReduceXorOpConversion,
2817 BoolCastOpConversion,
2818 NotOpConversion,
2819 NegOpConversion,
2820
2821 // Patterns of binary operations.
2822 BinaryOpConversion<AddOp, comb::AddOp>,
2823 BinaryOpConversion<SubOp, comb::SubOp>,
2824 BinaryOpConversion<MulOp, comb::MulOp>,
2825 BinaryOpConversion<DivUOp, comb::DivUOp>,
2826 BinaryOpConversion<DivSOp, comb::DivSOp>,
2827 BinaryOpConversion<ModUOp, comb::ModUOp>,
2828 BinaryOpConversion<ModSOp, comb::ModSOp>,
2829 BinaryOpConversion<AndOp, comb::AndOp>,
2830 BinaryOpConversion<OrOp, comb::OrOp>,
2831 BinaryOpConversion<XorOp, comb::XorOp>,
2832
2833 // Patterns for unary real operations.
2834 NegRealOpConversion,
2835
2836 // Patterns for binary real operations.
2837 BinaryRealOpConversion<AddRealOp, arith::AddFOp>,
2838 BinaryRealOpConversion<SubRealOp, arith::SubFOp>,
2839 BinaryRealOpConversion<DivRealOp, arith::DivFOp>,
2840 BinaryRealOpConversion<MulRealOp, arith::MulFOp>,
2841 BinaryRealOpConversion<PowRealOp, math::PowFOp>,
2842
2843 // Patterns of power operations.
2844 PowUOpConversion, PowSOpConversion,
2845
2846 // Patterns of relational operations.
2847 ICmpOpConversion<UltOp, ICmpPredicate::ult>,
2848 ICmpOpConversion<SltOp, ICmpPredicate::slt>,
2849 ICmpOpConversion<UleOp, ICmpPredicate::ule>,
2850 ICmpOpConversion<SleOp, ICmpPredicate::sle>,
2851 ICmpOpConversion<UgtOp, ICmpPredicate::ugt>,
2852 ICmpOpConversion<SgtOp, ICmpPredicate::sgt>,
2853 ICmpOpConversion<UgeOp, ICmpPredicate::uge>,
2854 ICmpOpConversion<SgeOp, ICmpPredicate::sge>,
2855 ICmpOpConversion<EqOp, ICmpPredicate::eq>,
2856 ICmpOpConversion<NeOp, ICmpPredicate::ne>,
2857 ICmpOpConversion<CaseEqOp, ICmpPredicate::ceq>,
2858 ICmpOpConversion<CaseNeOp, ICmpPredicate::cne>,
2859 ICmpOpConversion<WildcardEqOp, ICmpPredicate::weq>,
2860 ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
2861 FCmpOpConversion<NeRealOp, arith::CmpFPredicate::ONE>,
2862 FCmpOpConversion<FltOp, arith::CmpFPredicate::OLT>,
2863 FCmpOpConversion<FleOp, arith::CmpFPredicate::OLE>,
2864 FCmpOpConversion<FgtOp, arith::CmpFPredicate::OGT>,
2865 FCmpOpConversion<FgeOp, arith::CmpFPredicate::OGE>,
2866 FCmpOpConversion<EqRealOp, arith::CmpFPredicate::OEQ>,
2867 CaseXZEqOpConversion<CaseZEqOp, true>,
2868 CaseXZEqOpConversion<CaseXZEqOp, false>,
2869
2870 // Patterns of structural operations.
2871 SVModuleOpConversion,
2872 InstanceOpConversion,
2873 ProcedureOpConversion,
2874 WaitEventOpConversion,
2875
2876 // Patterns of shifting operations.
2877 ShrOpConversion,
2878 ShlOpConversion,
2879 AShrOpConversion,
2880
2881 // Patterns of assignment operations.
2882 AssignOpConversion<ContinuousAssignOp>,
2883 AssignOpConversion<DelayedContinuousAssignOp>,
2884 AssignOpConversion<BlockingAssignOp>,
2885 AssignOpConversion<NonBlockingAssignOp>,
2886 AssignOpConversion<DelayedNonBlockingAssignOp>,
2887 AssignedVariableOpConversion,
2888
2889 // Patterns of other operations outside Moore dialect.
2890 HWInstanceOpConversion,
2891 ReturnOpConversion,
2892 CallOpConversion,
2893 UnrealizedConversionCastConversion,
2894 InPlaceOpConversion<debug::ArrayOp>,
2895 InPlaceOpConversion<debug::StructOp>,
2896 InPlaceOpConversion<debug::VariableOp>,
2897
2898 // Patterns of assert-like operations
2899 AssertLikeOpConversion<AssertOp, verif::AssertOp>,
2900 AssertLikeOpConversion<AssumeOp, verif::AssumeOp>,
2901 AssertLikeOpConversion<CoverOp, verif::CoverOp>,
2902
2903 // Format strings.
2904 FormatLiteralOpConversion,
2905 FormatConcatOpConversion,
2906 FormatIntOpConversion,
2907 FormatRealOpConversion,
2908 DisplayBIOpConversion,
2909
2910 // Dynamic string operations
2911 StringLenOpConversion,
2912 StringConcatOpConversion,
2913
2914 // Queue operations
2915 QueueSizeBIOpConversion,
2916 QueuePushBackOpConversion,
2917 QueuePushFrontOpConversion,
2918 QueuePopBackOpConversion,
2919 QueuePopFrontOpConversion,
2920 QueueDeleteOpConversion,
2921 QueueInsertOpConversion,
2922 QueueClearOpConversion
2923 >(typeConverter, patterns.getContext());
2924 // clang-format on
2925
2926 // Structural operations
2927 patterns.add<WaitDelayOp>(convert);
2928 patterns.add<UnreachableOp>(convert);
2929 patterns.add<GlobalVariableOp>(convert);
2930 patterns.add<GetGlobalVariableOp>(convert);
2931
2932 // Simulation control
2933 patterns.add<StopBIOp>(convert);
2934 patterns.add<SeverityBIOp>(convert);
2935 patterns.add<FinishBIOp>(convert);
2936 patterns.add<FinishMessageBIOp>(convert);
2937
2938 // Timing control
2939 patterns.add<TimeBIOp>(convert);
2940 patterns.add<LogicToTimeOp>(convert);
2941 patterns.add<TimeToLogicOp>(convert);
2942
2943 mlir::populateAnyFunctionOpInterfaceTypeConversionPattern(patterns,
2944 typeConverter);
2945 hw::populateHWModuleLikeTypeConversionPattern(
2946 hw::HWModuleOp::getOperationName(), patterns, typeConverter);
2947 populateSCFToControlFlowConversionPatterns(patterns);
2948 populateArithToCombPatterns(patterns, typeConverter);
2949}
2950
2951//===----------------------------------------------------------------------===//
2952// Moore to Core Conversion Pass
2953//===----------------------------------------------------------------------===//
2954
2955namespace {
2956struct MooreToCorePass
2957 : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
2958 void runOnOperation() override;
2959};
2960} // namespace
2961
2962/// Create a Moore to core dialects conversion pass.
2963std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
2964 return std::make_unique<MooreToCorePass>();
2965}
2966
2967/// This is the main entrypoint for the Moore to Core conversion pass.
2968void MooreToCorePass::runOnOperation() {
2969 MLIRContext &context = getContext();
2970 ModuleOp module = getOperation();
2971 ClassTypeCache classCache;
2972
2973 IRRewriter rewriter(module);
2974 (void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
2975
2976 TypeConverter typeConverter;
2977 populateTypeConversion(typeConverter);
2978
2979 ConversionTarget target(context);
2980 populateLegality(target, typeConverter);
2981
2982 ConversionPatternSet patterns(&context, typeConverter);
2983 populateOpConversion(patterns, typeConverter, classCache);
2984 mlir::cf::populateCFStructuralTypeConversionsAndLegality(typeConverter,
2985 patterns, target);
2986
2987 if (failed(applyFullConversion(module, target, std::move(patterns))))
2988 signalPassFailure();
2989}
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static std::unique_ptr< Context > context
static Value createZeroValue(ImplicitLocOpBuilder &builder, FIRRTLBaseType type, SmallDenseMap< FIRRTLBaseType, Value > &cache)
Construct a zero value of the given type using the given builder.
static LogicalResult convert(StopBIOp op, StopBIOp::Adaptor adaptor, ConversionPatternRewriter &rewriter)
static void populateLegality(ConversionTarget &target, const TypeConverter &converter)
static void populateOpConversion(ConversionPatternSet &patterns, TypeConverter &typeConverter, ClassTypeCache &classCache)
static void populateTypeConversion(TypeConverter &typeConverter)
Extension of RewritePatternSet that allows adding matchAndRewrite functions with op adaptors and Conv...
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
void info(Twine message)
Definition LSPUtils.cpp:20
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.