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