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/Dialect/UB/IR/UBOps.h"
31#include "mlir/IR/BuiltinDialect.h"
32#include "mlir/IR/Iterators.h"
33#include "mlir/Interfaces/SideEffectInterfaces.h"
34#include "mlir/Pass/Pass.h"
35#include "mlir/Transforms/DialectConversion.h"
36#include "mlir/Transforms/RegionUtils.h"
37#include "llvm/ADT/TypeSwitch.h"
38#include "llvm/IR/DerivedTypes.h"
39
40namespace circt {
41#define GEN_PASS_DEF_CONVERTMOORETOCORE
42#include "circt/Conversion/Passes.h.inc"
43} // namespace circt
44
45using namespace mlir;
46using namespace circt;
47using namespace moore;
48
49using comb::ICmpPredicate;
50using llvm::SmallDenseSet;
51
52namespace {
53
54/// Cache for identified structs and field GEP paths keyed by class symbol.
55struct ClassTypeCache {
56 struct TypeInfoInfo {
57 LLVM::GlobalOp global;
58 };
59
60 struct ClassStructInfo {
61 LLVM::LLVMStructType classBody;
62 LLVM::LLVMStructType headerTy;
63 TypeInfoInfo typeInfo;
64
65 unsigned headerFieldIndex = 0;
66 unsigned typeInfoFieldIndex = 0;
67 unsigned vtableFieldIndex = 1;
68
69 // field name -> GEP path inside ident (excluding the leading pointer index)
70 DenseMap<StringRef, SmallVector<unsigned, 2>> propertyPath;
71
72 // TODO: Add classVTable in here.
73 /// Record/overwrite the field path to a single property for a class.
74 void setFieldPath(StringRef propertyName, ArrayRef<unsigned> path) {
75 this->propertyPath[propertyName] =
76 SmallVector<unsigned, 2>(path.begin(), path.end());
77 }
78
79 /// Lookup the full GEP path for a (class, field).
80 std::optional<ArrayRef<unsigned>>
81 getFieldPath(StringRef propertySym) const {
82 if (auto prop = this->propertyPath.find(propertySym);
83 prop != this->propertyPath.end())
84 return ArrayRef<unsigned>(prop->second);
85 return std::nullopt;
86 }
87 };
88
89 /// Record the identified struct body for a class.
90 /// Implicitly finalizes the class to struct conversion.
91 void setClassInfo(SymbolRefAttr classSym, const ClassStructInfo &info) {
92 auto &dst = classToStructMap[classSym];
93 dst = info;
94 }
95
96 /// Lookup the identified struct body for a class.
97 std::optional<ClassStructInfo> getStructInfo(SymbolRefAttr classSym) const {
98 if (auto it = classToStructMap.find(classSym); it != classToStructMap.end())
99 return it->second;
100 return std::nullopt;
101 }
102
103 std::optional<TypeInfoInfo> getTypeInfo(SymbolRefAttr classSym) const {
104 if (auto it = classToTypeInfoMap.find(classSym);
105 it != classToTypeInfoMap.end())
106 return it->second;
107 return std::nullopt;
108 }
109
110 void setTypeInfo(SymbolRefAttr classSym, const TypeInfoInfo &info) {
111 classToTypeInfoMap[classSym] = info;
112 }
113
114private:
115 // Keyed by the SymbolRefAttr of the class.
116 // Kept private so all accesses are done with helpers which preserve
117 // invariants
118 DenseMap<Attribute, ClassStructInfo> classToStructMap;
119 DenseMap<Attribute, TypeInfoInfo> classToTypeInfoMap;
120};
121
122/// Cache for external function declarations. Avoids redundant symbol table
123/// lookups and ensures each function is declared at most once.
124struct FunctionCache {
125 FunctionCache(SymbolTable &symbolTable) : symbolTable(symbolTable) {}
126
127 /// Look up a function by name. If it doesn't exist, invoke the callback to
128 /// create it. The builder is repositioned to the start of the module body
129 /// before the callback is invoked. The result is inserted into the symbol
130 /// table and cache.
131 func::FuncOp getOrCreate(OpBuilder &builder, StringRef name,
132 function_ref<func::FuncOp()> createFn) {
133 auto &slot = map[name];
134 if (slot)
135 return slot;
136 if (auto fn = symbolTable.lookup<func::FuncOp>(name))
137 return slot = fn;
138 auto mod = cast<ModuleOp>(symbolTable.getOp());
139 OpBuilder::InsertionGuard g(builder);
140 builder.setInsertionPointToStart(mod.getBody());
141 slot = createFn();
142 symbolTable.insert(slot);
143 return slot;
144 }
145
146 /// Convenience wrapper that creates a private external function declaration
147 /// with the given argument and result types.
148 func::FuncOp getOrCreate(OpBuilder &builder, StringRef name,
149 TypeRange argTypes, TypeRange resultTypes) {
150 return getOrCreate(builder, name, [&] {
151 auto mod = cast<ModuleOp>(symbolTable.getOp());
152 auto fnTy = builder.getFunctionType(argTypes, resultTypes);
153 auto fn = func::FuncOp::create(builder, mod.getLoc(), name, fnTy);
154 fn.setPrivate();
155 return fn;
156 });
157 }
158
159private:
160 SymbolTable &symbolTable;
161 llvm::StringMap<func::FuncOp> map;
162};
163
164/// Helper function to create an opaque LLVM Struct Type which corresponds
165/// to the sym
166static LLVM::LLVMStructType getOrCreateOpaqueStruct(MLIRContext *ctx,
167 SymbolRefAttr className) {
168 return LLVM::LLVMStructType::getIdentified(ctx, className.getRootReference());
169}
170
171/// Create the canonical object header for lowered Moore class objects.
172static LLVM::LLVMStructType getClassObjectHeaderType(MLIRContext *ctx) {
173 return LLVM::LLVMStructType::getLiteral(
174 ctx, SmallVector<Type>{LLVM::LLVMPointerType::get(ctx),
175 LLVM::LLVMPointerType::get(ctx)});
176}
177
178static std::string getTypeInfoName(SymbolRefAttr className) {
179 return className.getRootReference().str() + "::typeinfo";
180}
181
182static FailureOr<ClassTypeCache::TypeInfoInfo>
183getOrCreateTypeInfo(ModuleOp mod, SymbolRefAttr classSym,
184 ClassTypeCache &cache) {
185 if (auto info = cache.getTypeInfo(classSym))
186 return *info;
187
188 MLIRContext *ctx = mod.getContext();
189 auto ptrTy = LLVM::LLVMPointerType::get(ctx);
190 auto typeInfoTy = LLVM::LLVMStructType::getLiteral(ctx, {ptrTy});
191
192 auto globalName = getTypeInfoName(classSym);
193 auto global = mod.lookupSymbol<LLVM::GlobalOp>(globalName);
194 if (!global) {
195 OpBuilder builder = OpBuilder::atBlockBegin(mod.getBody());
196 global = LLVM::GlobalOp::create(
197 builder, mod.getLoc(), typeInfoTy,
198 /*isConstant=*/true, LLVM::Linkage::Internal, globalName, Attribute());
199
200 Block *block = new Block();
201 global.getInitializerRegion().push_back(block);
202 builder.setInsertionPointToStart(block);
203
204 if (auto *classOp = mod.lookupSymbol(classSym)) {
205 auto classDecl = dyn_cast<ClassDeclOp>(classOp);
206 if (classDecl && classDecl.getBaseAttr()) {
207 auto baseInfo =
208 getOrCreateTypeInfo(mod, classDecl.getBaseAttr(), cache);
209 if (failed(baseInfo))
210 return failure();
211 auto baseAddr =
212 LLVM::AddressOfOp::create(builder, mod.getLoc(), baseInfo->global);
213 auto undef = LLVM::UndefOp::create(builder, mod.getLoc(), typeInfoTy)
214 .getResult();
215 auto init = LLVM::InsertValueOp::create(builder, mod.getLoc(), undef,
216 baseAddr.getResult(),
217 ArrayRef<int64_t>{0});
218 LLVM::ReturnOp::create(builder, mod.getLoc(), init);
219 ClassTypeCache::TypeInfoInfo info{global};
220 cache.setTypeInfo(classSym, info);
221 return info;
222 }
223 }
224
225 auto undef =
226 LLVM::UndefOp::create(builder, mod.getLoc(), typeInfoTy).getResult();
227 auto nullPtr =
228 LLVM::ZeroOp::create(builder, mod.getLoc(), ptrTy).getResult();
229 auto init = LLVM::InsertValueOp::create(builder, mod.getLoc(), undef,
230 nullPtr, ArrayRef<int64_t>{0});
231 LLVM::ReturnOp::create(builder, mod.getLoc(), init);
232 }
233
234 ClassTypeCache::TypeInfoInfo info{global};
235 cache.setTypeInfo(classSym, info);
236 return info;
237}
238static LogicalResult resolveClassStructBody(ClassDeclOp op,
239 TypeConverter const &typeConverter,
240 ClassTypeCache &cache) {
241
242 auto classSym = SymbolRefAttr::get(op.getSymNameAttr());
243 auto structInfo = cache.getStructInfo(classSym);
244 if (structInfo)
245 // We already have a resolved class struct body.
246 return success();
247
248 if (failed(getOrCreateTypeInfo(op->getParentOfType<ModuleOp>(), classSym,
249 cache)))
250 return op.emitOpError() << "Failed to create RTTI for class";
251
252 // Otherwise we need to resolve.
253 ClassTypeCache::ClassStructInfo structBody;
254 SmallVector<Type> structBodyMembers;
255 structBody.headerTy = getClassObjectHeaderType(op.getContext());
256 structBody.typeInfo = *cache.getTypeInfo(classSym);
257 structBodyMembers.push_back(structBody.headerTy);
258
259 // Base-first (prefix) layout for single inheritance.
260 unsigned derivedStartIdx = 1;
261
262 if (auto baseClass = op.getBaseAttr()) {
263
264 ModuleOp mod = op->getParentOfType<ModuleOp>();
265 auto *opSym = mod.lookupSymbol(baseClass);
266 auto classDeclOp = cast<ClassDeclOp>(opSym);
267
268 if (failed(resolveClassStructBody(classDeclOp, typeConverter, cache)))
269 return failure();
270
271 // Process base class' struct layout first
272 auto baseClassStruct = cache.getStructInfo(baseClass);
273 structBodyMembers.push_back(baseClassStruct->classBody);
274 derivedStartIdx = 2;
275
276 // Inherit base field paths with a leading 1 to index into the base
277 // subobject after the object header.
278 for (auto &kv : baseClassStruct->propertyPath) {
279 SmallVector<unsigned, 2> path;
280 path.push_back(1); // into base subobject
281 path.append(kv.second.begin(), kv.second.end());
282 structBody.setFieldPath(kv.first, path);
283 }
284 }
285
286 // Properties in source order.
287 unsigned iterator = derivedStartIdx;
288 auto &block = op.getBody().front();
289 for (Operation &child : block) {
290 if (auto prop = dyn_cast<ClassPropertyDeclOp>(child)) {
291 Type mooreTy = prop.getPropertyType();
292 Type llvmTy = typeConverter.convertType(mooreTy);
293 if (!llvmTy)
294 return prop.emitOpError()
295 << "failed to convert property type " << mooreTy;
296
297 structBodyMembers.push_back(llvmTy);
298
299 // Derived field path: either {i} or {1+i} if base is present.
300 SmallVector<unsigned, 2> path{iterator};
301 structBody.setFieldPath(prop.getSymName(), path);
302 ++iterator;
303 }
304 }
305
306 // TODO: Handle vtable generation over ClassMethodDeclOp here.
307 auto llvmStructTy = getOrCreateOpaqueStruct(op.getContext(), classSym);
308 // Empty structs may be kept opaque
309 if (!structBodyMembers.empty() &&
310 failed(llvmStructTy.setBody(structBodyMembers, false)))
311 return op.emitOpError() << "Failed to set LLVM Struct body";
312
313 structBody.classBody = llvmStructTy;
314 cache.setClassInfo(classSym, structBody);
315
316 return success();
317}
318
319/// Convenience overload that looks up ClassDeclOp
320static LogicalResult resolveClassStructBody(ModuleOp mod, SymbolRefAttr op,
321 TypeConverter const &typeConverter,
322 ClassTypeCache &cache) {
323 auto classDeclOp = cast<ClassDeclOp>(*mod.lookupSymbol(op));
324 return resolveClassStructBody(classDeclOp, typeConverter, cache);
325}
326
327/// Returns the passed value if the integer width is already correct.
328/// Zero-extends if it is too narrow.
329/// Truncates if the integer is too wide and the truncated part is zero, if it
330/// is not zero it returns the max value integer of target-width.
331static Value adjustIntegerWidth(OpBuilder &builder, Value value,
332 uint32_t targetWidth, Location loc) {
333 uint32_t intWidth = value.getType().getIntOrFloatBitWidth();
334 if (intWidth == targetWidth)
335 return value;
336
337 if (intWidth < targetWidth) {
338 Value zeroExt = hw::ConstantOp::create(
339 builder, loc, builder.getIntegerType(targetWidth - intWidth), 0);
340 return comb::ConcatOp::create(builder, loc, ValueRange{zeroExt, value});
341 }
342
343 Value hi = comb::ExtractOp::create(builder, loc, value, targetWidth,
344 intWidth - targetWidth);
345 Value zero = hw::ConstantOp::create(
346 builder, loc, builder.getIntegerType(intWidth - targetWidth), 0);
347 Value isZero = comb::ICmpOp::create(builder, loc, comb::ICmpPredicate::eq, hi,
348 zero, false);
349 Value lo = comb::ExtractOp::create(builder, loc, value, 0, targetWidth);
350 Value max = hw::ConstantOp::create(builder, loc,
351 builder.getIntegerType(targetWidth), -1);
352 return comb::MuxOp::create(builder, loc, isZero, lo, max, false);
353}
354
355/// Get the ModulePortInfo from a SVModuleOp.
356static FailureOr<hw::ModulePortInfo>
357getModulePortInfo(const TypeConverter &typeConverter, SVModuleOp op) {
358 size_t inputNum = 0;
359 size_t resultNum = 0;
360 auto moduleTy = op.getModuleType();
361 SmallVector<hw::PortInfo> ports;
362 ports.reserve(moduleTy.getNumPorts());
363
364 for (auto port : moduleTy.getPorts()) {
365 Type portTy = typeConverter.convertType(port.type);
366 if (!portTy) {
367 return op.emitOpError("port '")
368 << port.name << "' has unsupported type " << port.type
369 << " that cannot be converted to hardware type";
370 }
371 if (port.dir == hw::ModulePort::Direction::Output) {
372 ports.push_back(
373 hw::PortInfo({{port.name, portTy, port.dir}, resultNum++, {}}));
374 } else {
375 // FIXME: Once we support net<...>, ref<...> type to represent type of
376 // special port like inout or ref port which is not a input or output
377 // port. It can change to generate corresponding types for direction of
378 // port or do specified operation to it. Now inout and ref port is treated
379 // as input port.
380 ports.push_back(
381 hw::PortInfo({{port.name, portTy, port.dir}, inputNum++, {}}));
382 }
383 }
384 return hw::ModulePortInfo(ports);
385}
386
387struct DpiArrayCastInfo {
388 bool isRef = false;
389 bool isOpen = false;
390 bool isPacked = false;
391 Type elementType;
392};
393
394static std::optional<DpiArrayCastInfo> getDpiArrayCastInfo(Type type) {
395 DpiArrayCastInfo info;
396 if (auto refType = dyn_cast<RefType>(type)) {
397 info.isRef = true;
398 type = refType.getNestedType();
399 }
400
401 if (auto arrayType = dyn_cast<ArrayType>(type)) {
402 info.isPacked = true;
403 info.elementType = arrayType.getElementType();
404 return info;
405 }
406 if (auto arrayType = dyn_cast<OpenArrayType>(type)) {
407 info.isOpen = true;
408 info.isPacked = true;
409 info.elementType = arrayType.getElementType();
410 return info;
411 }
412 if (auto arrayType = dyn_cast<UnpackedArrayType>(type)) {
413 info.elementType = arrayType.getElementType();
414 return info;
415 }
416 if (auto arrayType = dyn_cast<OpenUnpackedArrayType>(type)) {
417 info.isOpen = true;
418 info.elementType = arrayType.getElementType();
419 return info;
420 }
421 return std::nullopt;
422}
423
424static bool hasOpenArrayBoundaryType(Type type) {
425 if (isa<OpenArrayType, OpenUnpackedArrayType>(type))
426 return true;
427 if (auto refType = dyn_cast<RefType>(type))
428 return isa<OpenArrayType, OpenUnpackedArrayType>(refType.getNestedType());
429 return false;
430}
431
432static bool isSupportedDpiOpenArrayCast(Type source, Type target) {
433 auto sourceInfo = getDpiArrayCastInfo(source);
434 auto targetInfo = getDpiArrayCastInfo(target);
435 if (!sourceInfo || !targetInfo)
436 return false;
437 // note: We currently don't support converting from open array to non-open
438 // array, even if the element types match, because there is no size
439 // information for the open array.
440 return sourceInfo->isRef == targetInfo->isRef &&
441 sourceInfo->isPacked == targetInfo->isPacked &&
442 sourceInfo->elementType == targetInfo->elementType &&
443 (targetInfo->isOpen && !sourceInfo->isOpen);
444}
445
446//===----------------------------------------------------------------------===//
447// Structural Conversion
448//===----------------------------------------------------------------------===//
449
450struct SVModuleOpConversion : public OpConversionPattern<SVModuleOp> {
451 using OpConversionPattern::OpConversionPattern;
452
453 LogicalResult
454 matchAndRewrite(SVModuleOp op, OpAdaptor adaptor,
455 ConversionPatternRewriter &rewriter) const override {
456 rewriter.setInsertionPoint(op);
457
458 // Create the hw.module to replace moore.module
459 auto portInfo = getModulePortInfo(*typeConverter, op);
460 if (failed(portInfo))
461 return failure();
462
463 auto hwModuleOp = hw::HWModuleOp::create(rewriter, op.getLoc(),
464 op.getSymNameAttr(), *portInfo);
465 // Make hw.module have the same visibility as the moore.module.
466 // The entry/top level module is public, otherwise is private.
467 SymbolTable::setSymbolVisibility(hwModuleOp,
468 SymbolTable::getSymbolVisibility(op));
469 rewriter.eraseBlock(hwModuleOp.getBodyBlock());
470 if (failed(
471 rewriter.convertRegionTypes(&op.getBodyRegion(), *typeConverter)))
472 return failure();
473 rewriter.inlineRegionBefore(op.getBodyRegion(), hwModuleOp.getBodyRegion(),
474 hwModuleOp.getBodyRegion().end());
475
476 // Erase the original op
477 rewriter.eraseOp(op);
478 return success();
479 }
480};
481
482struct OutputOpConversion : public OpConversionPattern<OutputOp> {
483 using OpConversionPattern::OpConversionPattern;
484
485 LogicalResult
486 matchAndRewrite(OutputOp op, OpAdaptor adaptor,
487 ConversionPatternRewriter &rewriter) const override {
488 rewriter.replaceOpWithNewOp<hw::OutputOp>(op, adaptor.getOperands());
489 return success();
490 }
491};
492
493struct InstanceOpConversion : public OpConversionPattern<InstanceOp> {
494 using OpConversionPattern::OpConversionPattern;
495
496 LogicalResult
497 matchAndRewrite(InstanceOp op, OpAdaptor adaptor,
498 ConversionPatternRewriter &rewriter) const override {
499 auto instName = op.getInstanceNameAttr();
500 auto moduleName = op.getModuleNameAttr();
501
502 // Create the new hw instanceOp to replace the original one.
503 rewriter.setInsertionPoint(op);
504 auto instOp = hw::InstanceOp::create(
505 rewriter, op.getLoc(), op.getResultTypes(), instName, moduleName,
506 op.getInputs(), op.getInputNamesAttr(), op.getOutputNamesAttr(),
507 /*Parameter*/ rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr,
508 /*doNotPrint*/ nullptr);
509
510 // Replace uses chain and erase the original op.
511 op.replaceAllUsesWith(instOp.getResults());
512 rewriter.eraseOp(op);
513 return success();
514 }
515};
516
517static void getValuesToObserve(Region *region,
518 function_ref<void(Value)> setInsertionPoint,
519 const TypeConverter *typeConverter,
520 ConversionPatternRewriter &rewriter,
521 SmallVector<Value> &observeValues) {
522 SmallDenseSet<Value> alreadyObserved;
523 Location loc = region->getLoc();
524
525 auto probeIfSignal = [&](Value value) -> Value {
526 Type type = value.getType();
527 if (auto refType = dyn_cast<llhd::RefType>(type)) {
528 if (!hw::isHWValueType(refType.getNestedType()))
529 return {};
530 return llhd::ProbeOp::create(rewriter, loc, value);
531 }
532 if (!hw::isHWValueType(type))
533 return {};
534 return value;
535 };
536
537 region->getParentOp()->walk<WalkOrder::PreOrder, ForwardDominanceIterator<>>(
538 [&](Operation *operation) {
539 for (auto value : operation->getOperands()) {
540 if (isa<BlockArgument>(value))
541 value = rewriter.getRemappedValue(value);
542
543 if (region->isAncestor(value.getParentRegion()))
544 continue;
545 if (auto *defOp = value.getDefiningOp();
546 defOp && defOp->hasTrait<OpTrait::ConstantLike>())
547 continue;
548 if (!alreadyObserved.insert(value).second)
549 continue;
550
551 OpBuilder::InsertionGuard g(rewriter);
552 if (auto remapped = rewriter.getRemappedValue(value)) {
553 setInsertionPoint(remapped);
554 if (auto observed = probeIfSignal(remapped))
555 observeValues.push_back(observed);
556 } else {
557 setInsertionPoint(value);
558 auto type = typeConverter->convertType(value.getType());
559 auto converted = typeConverter->materializeTargetConversion(
560 rewriter, loc, type, value);
561 if (auto observed = probeIfSignal(converted))
562 observeValues.push_back(observed);
563 }
564 }
565 });
566}
567
568struct ProcedureOpConversion : public OpConversionPattern<ProcedureOp> {
569 using OpConversionPattern::OpConversionPattern;
570
571 LogicalResult
572 matchAndRewrite(ProcedureOp op, OpAdaptor adaptor,
573 ConversionPatternRewriter &rewriter) const override {
574 // Collect values to observe before we do any modifications to the region.
575 SmallVector<Value> observedValues;
576 if (op.getKind() == ProcedureKind::AlwaysComb ||
577 op.getKind() == ProcedureKind::AlwaysLatch) {
578 auto setInsertionPoint = [&](Value value) {
579 rewriter.setInsertionPoint(op);
580 };
581 getValuesToObserve(&op.getBody(), setInsertionPoint, typeConverter,
582 rewriter, observedValues);
583 }
584
585 auto loc = op.getLoc();
586 if (failed(rewriter.convertRegionTypes(&op.getBody(), *typeConverter)))
587 return failure();
588
589 // Handle initial and final procedures. These lower to a corresponding
590 // `llhd.process` or `llhd.final` op that executes the body and then halts.
591 if (op.getKind() == ProcedureKind::Initial ||
592 op.getKind() == ProcedureKind::Final) {
593 Operation *newOp;
594 if (op.getKind() == ProcedureKind::Initial)
595 newOp = llhd::ProcessOp::create(rewriter, loc, TypeRange{});
596 else
597 newOp = llhd::FinalOp::create(rewriter, loc);
598 auto &body = newOp->getRegion(0);
599 rewriter.inlineRegionBefore(op.getBody(), body, body.end());
600 for (auto returnOp :
601 llvm::make_early_inc_range(body.getOps<ReturnOp>())) {
602 rewriter.setInsertionPoint(returnOp);
603 rewriter.replaceOpWithNewOp<llhd::HaltOp>(returnOp, ValueRange{});
604 }
605 rewriter.eraseOp(op);
606 return success();
607 }
608
609 // All other procedures lower to a an `llhd.process`.
610 auto newOp = llhd::ProcessOp::create(rewriter, loc, TypeRange{});
611
612 // We need to add an empty entry block because it is not allowed in MLIR to
613 // branch back to the entry block. Instead we put the logic in the second
614 // block and branch to that.
615 rewriter.createBlock(&newOp.getBody());
616 auto *block = &op.getBody().front();
617 cf::BranchOp::create(rewriter, loc, block);
618 rewriter.inlineRegionBefore(op.getBody(), newOp.getBody(),
619 newOp.getBody().end());
620
621 // Add special handling for `always_comb` and `always_latch` procedures.
622 // These run once at simulation startup and then implicitly wait for any of
623 // the values they access to change before running again. To implement this,
624 // we create another basic block that contains the implicit wait, and make
625 // all `moore.return` ops branch to that wait block instead of immediately
626 // jumping back up to the body.
627 if (op.getKind() == ProcedureKind::AlwaysComb ||
628 op.getKind() == ProcedureKind::AlwaysLatch) {
629 Block *waitBlock = rewriter.createBlock(&newOp.getBody());
630 llhd::WaitOp::create(rewriter, loc, ValueRange{}, Value(), observedValues,
631 ValueRange{}, block);
632 block = waitBlock;
633 }
634
635 // Make all `moore.return` ops branch back up to the beginning of the
636 // process, or the wait block created above for `always_comb` and
637 // `always_latch` procedures.
638 for (auto returnOp : llvm::make_early_inc_range(newOp.getOps<ReturnOp>())) {
639 rewriter.setInsertionPoint(returnOp);
640 cf::BranchOp::create(rewriter, loc, block);
641 rewriter.eraseOp(returnOp);
642 }
643
644 rewriter.eraseOp(op);
645 return success();
646 }
647};
648
649//===----------------------------------------------------------------------===//
650// Coroutine Conversion
651//===----------------------------------------------------------------------===//
652
653struct CoroutineOpConversion : public OpConversionPattern<CoroutineOp> {
654 using OpConversionPattern::OpConversionPattern;
655
656 LogicalResult
657 matchAndRewrite(CoroutineOp op, OpAdaptor adaptor,
658 ConversionPatternRewriter &rewriter) const override {
659 auto funcType = op.getFunctionType();
660 TypeConverter::SignatureConversion sigConversion(funcType.getNumInputs());
661 for (auto [i, type] : llvm::enumerate(funcType.getInputs())) {
662 auto converted = typeConverter->convertType(type);
663 if (!converted)
664 return failure();
665 sigConversion.addInputs(i, converted);
666 }
667 SmallVector<Type> resultTypes;
668 if (failed(typeConverter->convertTypes(funcType.getResults(), resultTypes)))
669 return failure();
670
671 auto newFuncType = FunctionType::get(
672 rewriter.getContext(), sigConversion.getConvertedTypes(), resultTypes);
673 auto newOp = llhd::CoroutineOp::create(rewriter, op.getLoc(),
674 op.getSymName(), newFuncType);
675 newOp.setSymVisibilityAttr(op.getSymVisibilityAttr());
676 if (auto dpiExport = op->getAttr("circt.dpi.export"))
677 newOp->setAttr("circt.dpi.export", dpiExport);
678 rewriter.inlineRegionBefore(op.getBody(), newOp.getBody(),
679 newOp.getBody().end());
680 if (failed(rewriter.convertRegionTypes(&newOp.getBody(), *typeConverter,
681 &sigConversion)))
682 return failure();
683
684 // Replace moore.return with llhd.return inside the coroutine body.
685 for (auto returnOp :
686 llvm::make_early_inc_range(newOp.getBody().getOps<ReturnOp>())) {
687 rewriter.setInsertionPoint(returnOp);
688 rewriter.replaceOpWithNewOp<llhd::ReturnOp>(returnOp, ValueRange{});
689 }
690
691 rewriter.eraseOp(op);
692 return success();
693 }
694};
695
696struct CallCoroutineOpConversion : public OpConversionPattern<CallCoroutineOp> {
697 using OpConversionPattern::OpConversionPattern;
698
699 LogicalResult
700 matchAndRewrite(CallCoroutineOp op, OpAdaptor adaptor,
701 ConversionPatternRewriter &rewriter) const override {
702 SmallVector<Type> convResTypes;
703 if (failed(typeConverter->convertTypes(op.getResultTypes(), convResTypes)))
704 return failure();
705 rewriter.replaceOpWithNewOp<llhd::CallCoroutineOp>(
706 op, convResTypes, adaptor.getCallee(), adaptor.getOperands());
707 return success();
708 }
709};
710
711struct WaitEventOpConversion : public OpConversionPattern<WaitEventOp> {
712 using OpConversionPattern::OpConversionPattern;
713
714 LogicalResult
715 matchAndRewrite(WaitEventOp op, OpAdaptor adaptor,
716 ConversionPatternRewriter &rewriter) const override {
717 // In order to convert the `wait_event` op we need to create three separate
718 // blocks at the location of the op:
719 //
720 // - A "wait" block that reads the current state of any values used to
721 // detect events and then waits until any of those values change. When a
722 // change occurs, control transfers to the "check" block.
723 // - A "check" block which is executed after any interesting signal has
724 // changed. This is where any `detect_event` ops read the current state of
725 // interesting values and compare them against their state before the wait
726 // in order to detect an event. If any events were detected, control
727 // transfers to the "resume" block; otherwise control goes back to the
728 // "wait" block.
729 // - A "resume" block which holds any ops after the `wait_event` op. This is
730 // where control is expected to resume after an event has happened.
731 //
732 // Block structure before:
733 // opA
734 // moore.wait_event { ... }
735 // opB
736 //
737 // Block structure after:
738 // opA
739 // cf.br ^wait
740 // ^wait:
741 // <read "before" values>
742 // llhd.wait ^check, ...
743 // ^check:
744 // <read "after" values>
745 // <detect edges>
746 // cf.cond_br %event, ^resume, ^wait
747 // ^resume:
748 // opB
749 auto *resumeBlock =
750 rewriter.splitBlock(op->getBlock(), ++Block::iterator(op));
751
752 // If the 'wait_event' op is empty, we can lower it to a 'llhd.wait' op
753 // without any observed values, but since the process will never wake up
754 // from suspension anyway, we can also just terminate it using the
755 // 'llhd.halt' op.
756 if (op.getBody().front().empty()) {
757 // Let the cleanup iteration after the dialect conversion clean up all
758 // remaining unreachable blocks.
759 rewriter.replaceOpWithNewOp<llhd::HaltOp>(op, ValueRange{});
760 return success();
761 }
762
763 auto *waitBlock = rewriter.createBlock(resumeBlock);
764 auto *checkBlock = rewriter.createBlock(resumeBlock);
765
766 auto loc = op.getLoc();
767 rewriter.setInsertionPoint(op);
768 cf::BranchOp::create(rewriter, loc, waitBlock);
769
770 // We need to inline two copies of the `wait_event`'s body region: one is
771 // used to determine the values going into `detect_event` ops before the
772 // `llhd.wait`, and one will do the actual event detection after the
773 // `llhd.wait`.
774 //
775 // Create a copy of the entire `wait_event` op in the wait block, which also
776 // creates a copy of its region. Take note of all inputs to `detect_event`
777 // ops and delete the `detect_event` ops in this copy.
778 SmallVector<Value> valuesBefore;
779 rewriter.setInsertionPointToEnd(waitBlock);
780 auto clonedOp = cast<WaitEventOp>(rewriter.clone(*op));
781 bool allDetectsAreAnyChange = true;
782 for (auto detectOp :
783 llvm::make_early_inc_range(clonedOp.getOps<DetectEventOp>())) {
784 if (detectOp.getEdge() != Edge::AnyChange || detectOp.getCondition())
785 allDetectsAreAnyChange = false;
786 valuesBefore.push_back(detectOp.getInput());
787 rewriter.eraseOp(detectOp);
788 }
789
790 // Determine the values used during event detection that are defined outside
791 // the `wait_event`'s body region. We want to wait for a change on these
792 // signals before we check if any interesting event happened.
793 SmallVector<Value> observeValues;
794 auto setInsertionPointAfterDef = [&](Value value) {
795 if (auto *op = value.getDefiningOp())
796 rewriter.setInsertionPointAfter(op);
797 if (auto arg = dyn_cast<BlockArgument>(value))
798 rewriter.setInsertionPointToStart(value.getParentBlock());
799 };
800
801 getValuesToObserve(&clonedOp.getBody(), setInsertionPointAfterDef,
802 typeConverter, rewriter, observeValues);
803
804 // Create the `llhd.wait` op that suspends the current process and waits for
805 // a change in the interesting values listed in `observeValues`. When a
806 // change is detected, execution resumes in the "check" block.
807 auto waitOp = llhd::WaitOp::create(rewriter, loc, ValueRange{}, Value(),
808 observeValues, ValueRange{}, checkBlock);
809 rewriter.inlineBlockBefore(&clonedOp.getBody().front(), waitOp);
810 rewriter.eraseOp(clonedOp);
811
812 // Collect a list of all detect ops and inline the `wait_event` body into
813 // the check block.
814 SmallVector<DetectEventOp> detectOps(op.getBody().getOps<DetectEventOp>());
815 rewriter.inlineBlockBefore(&op.getBody().front(), checkBlock,
816 checkBlock->end());
817 rewriter.eraseOp(op);
818
819 // Helper function to detect if a certain change occurred between a value
820 // before the `llhd.wait` and after.
821 auto computeTrigger = [&](Value before, Value after, Edge edge) -> Value {
822 assert(before.getType() == after.getType() &&
823 "mismatched types after clone op");
824 auto beforeType = cast<IntType>(before.getType());
825
826 // 9.4.2 IEEE 1800-2017: An edge event shall be detected only on the LSB
827 // of the expression
828 if (beforeType.getWidth() != 1 && edge != Edge::AnyChange) {
829 constexpr int LSB = 0;
830 beforeType =
831 IntType::get(rewriter.getContext(), 1, beforeType.getDomain());
832 before =
833 moore::ExtractOp::create(rewriter, loc, beforeType, before, LSB);
834 after = moore::ExtractOp::create(rewriter, loc, beforeType, after, LSB);
835 }
836
837 auto intType = rewriter.getIntegerType(beforeType.getWidth());
838 before = typeConverter->materializeTargetConversion(rewriter, loc,
839 intType, before);
840 after = typeConverter->materializeTargetConversion(rewriter, loc, intType,
841 after);
842
843 if (edge == Edge::AnyChange)
844 return comb::ICmpOp::create(rewriter, loc, ICmpPredicate::ne, before,
845 after, true);
846
847 SmallVector<Value> disjuncts;
848 Value trueVal = hw::ConstantOp::create(rewriter, loc, APInt(1, 1));
849
850 if (edge == Edge::PosEdge || edge == Edge::BothEdges) {
851 Value notOldVal =
852 comb::XorOp::create(rewriter, loc, before, trueVal, true);
853 Value posedge =
854 comb::AndOp::create(rewriter, loc, notOldVal, after, true);
855 disjuncts.push_back(posedge);
856 }
857
858 if (edge == Edge::NegEdge || edge == Edge::BothEdges) {
859 Value notCurrVal =
860 comb::XorOp::create(rewriter, loc, after, trueVal, true);
861 Value posedge =
862 comb::AndOp::create(rewriter, loc, before, notCurrVal, true);
863 disjuncts.push_back(posedge);
864 }
865
866 return rewriter.createOrFold<comb::OrOp>(loc, disjuncts, true);
867 };
868
869 // Convert all `detect_event` ops into a check for the corresponding event
870 // between the value before and after the `llhd.wait`. The "before" value
871 // has been collected into `valuesBefore` in the "wait" block; the "after"
872 // value corresponds to the detect op's input.
873 SmallVector<Value> triggers;
874 for (auto [detectOp, before] : llvm::zip(detectOps, valuesBefore)) {
875 if (!allDetectsAreAnyChange) {
876 if (!isa<IntType>(before.getType()))
877 return detectOp->emitError() << "requires int operand";
878
879 rewriter.setInsertionPoint(detectOp);
880 auto trigger =
881 computeTrigger(before, detectOp.getInput(), detectOp.getEdge());
882 if (detectOp.getCondition()) {
883 auto condition = typeConverter->materializeTargetConversion(
884 rewriter, loc, rewriter.getI1Type(), detectOp.getCondition());
885 trigger =
886 comb::AndOp::create(rewriter, loc, trigger, condition, true);
887 }
888 triggers.push_back(trigger);
889 }
890
891 rewriter.eraseOp(detectOp);
892 }
893
894 rewriter.setInsertionPointToEnd(checkBlock);
895 if (triggers.empty()) {
896 // If there are no triggers to check, we always branch to the resume
897 // block. If there are no detect_event operations in the wait event, the
898 // 'llhd.wait' operation will not have any observed values and thus the
899 // process will hang there forever.
900 cf::BranchOp::create(rewriter, loc, resumeBlock);
901 } else {
902 // If any `detect_event` op detected an event, branch to the "resume"
903 // block which contains any code after the `wait_event` op. If no events
904 // were detected, branch back to the "wait" block to wait for the next
905 // change on the interesting signals.
906 auto triggered = rewriter.createOrFold<comb::OrOp>(loc, triggers, true);
907 cf::CondBranchOp::create(rewriter, loc, triggered, resumeBlock,
908 waitBlock);
909 }
910
911 return success();
912 }
913};
914
915// moore.wait_delay -> llhd.wait
916static LogicalResult convert(WaitDelayOp op, WaitDelayOp::Adaptor adaptor,
917 ConversionPatternRewriter &rewriter) {
918 auto *resumeBlock =
919 rewriter.splitBlock(op->getBlock(), ++Block::iterator(op));
920 rewriter.setInsertionPoint(op);
921 rewriter.replaceOpWithNewOp<llhd::WaitOp>(op, ValueRange{},
922 adaptor.getDelay(), ValueRange{},
923 ValueRange{}, resumeBlock);
924 rewriter.setInsertionPointToStart(resumeBlock);
925 return success();
926}
927
928// moore.unreachable -> ub.unreachable
929static LogicalResult convert(UnreachableOp op, UnreachableOp::Adaptor adaptor,
930 ConversionPatternRewriter &rewriter) {
931 rewriter.replaceOpWithNewOp<ub::UnreachableOp>(op);
932 return success();
933}
934
935//===----------------------------------------------------------------------===//
936// Declaration Conversion
937//===----------------------------------------------------------------------===//
938
939static Value createZeroValue(Type type, Location loc,
940 ConversionPatternRewriter &rewriter) {
941 // Handle pointers.
942 if (isa<mlir::LLVM::LLVMPointerType>(type))
943 return mlir::LLVM::ZeroOp::create(rewriter, loc, type);
944
945 // Handle time values.
946 if (isa<llhd::TimeType>(type)) {
947 auto timeAttr =
948 llhd::TimeAttr::get(type.getContext(), 0U, llvm::StringRef("ns"), 0, 0);
949 return llhd::ConstantTimeOp::create(rewriter, loc, timeAttr);
950 }
951
952 // Handle real values.
953 if (auto floatType = dyn_cast<FloatType>(type)) {
954 auto floatAttr = rewriter.getFloatAttr(floatType, 0.0);
955 return mlir::arith::ConstantOp::create(rewriter, loc, floatAttr);
956 }
957
958 // Handle dynamic strings
959 if (auto strType = dyn_cast<sim::DynamicStringType>(type))
960 return sim::StringConstantOp::create(rewriter, loc, strType, "");
961
962 // Handle associative arrays
963 if (auto assocArrayType = dyn_cast<sim::AssocArrayType>(type))
964 return sim::AssocArrayEmptyOp::create(rewriter, loc, assocArrayType);
965
966 // Handle queues
967 if (auto queueType = dyn_cast<sim::QueueType>(type))
968 return sim::QueueEmptyOp::create(rewriter, loc, queueType);
969
970 // Handle aggregate
971 if (auto arrayType = dyn_cast<hw::ArrayType>(type)) {
972 if (hw::getBitWidth(type) == -1) {
973 Value zeroElement =
974 createZeroValue(arrayType.getElementType(), loc, rewriter);
975 if (!zeroElement)
976 return {};
977 SmallVector<Value> elements(arrayType.getNumElements(), zeroElement);
978 return hw::ArrayCreateOp::create(rewriter, loc, elements);
979 }
980 }
981
982 if (auto structType = dyn_cast<hw::StructType>(type)) {
983 if (hw::getBitWidth(type) == -1) {
984 SmallVector<Value> fields;
985 for (auto field : structType.getElements()) {
986 Value zeroField = createZeroValue(field.type, loc, rewriter);
987 if (!zeroField)
988 return {};
989 fields.push_back(zeroField);
990 }
991 return hw::StructCreateOp::create(rewriter, loc, structType, fields);
992 }
993 }
994
995 // Otherwise try to create a zero integer and bitcast it to the result type.
996 int64_t width = hw::getBitWidth(type);
997 if (width == -1)
998 return {};
999
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
1002 // variables.
1003 Value constZero = hw::ConstantOp::create(rewriter, loc, APInt(width, 0));
1004 return rewriter.createOrFold<hw::BitcastOp>(loc, type, constZero);
1005}
1006
1007struct ClassPropertyRefOpConversion
1008 : public OpConversionPattern<circt::moore::ClassPropertyRefOp> {
1009 ClassPropertyRefOpConversion(TypeConverter &tc, MLIRContext *ctx,
1010 ClassTypeCache &cache)
1011 : OpConversionPattern(tc, ctx), cache(cache) {}
1012
1013 LogicalResult
1014 matchAndRewrite(circt::moore::ClassPropertyRefOp op, OpAdaptor adaptor,
1015 ConversionPatternRewriter &rewriter) const override {
1016 Location loc = op.getLoc();
1017 MLIRContext *ctx = rewriter.getContext();
1018
1019 // Convert result type; we expect !llhd.ref<someT>.
1020 Type dstTy = getTypeConverter()->convertType(op.getPropertyRef().getType());
1021 // Operand is a !llvm.ptr
1022 Value instRef = adaptor.getInstance();
1023
1024 // Resolve identified struct from cache.
1025 auto classRefTy =
1026 cast<circt::moore::ClassHandleType>(op.getInstance().getType());
1027 SymbolRefAttr classSym = classRefTy.getClassSym();
1028 ModuleOp mod = op->getParentOfType<ModuleOp>();
1029 if (failed(resolveClassStructBody(mod, classSym, *typeConverter, cache)))
1030 return rewriter.notifyMatchFailure(op,
1031 "Could not resolve class struct for " +
1032 classSym.getRootReference().str());
1033
1034 auto structInfo = cache.getStructInfo(classSym);
1035 assert(structInfo && "class struct info must exist");
1036 auto structTy = structInfo->classBody;
1037
1038 // Look up cached GEP path for the property.
1039 auto propSym = op.getProperty();
1040 auto pathOpt = structInfo->getFieldPath(propSym);
1041 if (!pathOpt)
1042 return rewriter.notifyMatchFailure(op,
1043 "no GEP path for property " + propSym);
1044
1045 auto i32Ty = IntegerType::get(ctx, 32);
1046 SmallVector<Value> idxVals;
1047 for (unsigned idx : *pathOpt)
1048 idxVals.push_back(LLVM::ConstantOp::create(
1049 rewriter, loc, i32Ty, rewriter.getI32IntegerAttr(idx)));
1050
1051 // GEP to the field (opaque ptr mode requires element type).
1052 auto ptrTy = LLVM::LLVMPointerType::get(ctx);
1053 auto gep =
1054 LLVM::GEPOp::create(rewriter, loc, ptrTy, structTy, instRef, idxVals);
1055
1056 // Wrap pointer back to !llhd.ref<someT>.
1057 Value fieldRef = UnrealizedConversionCastOp::create(rewriter, loc, dstTy,
1058 gep.getResult())
1059 .getResult(0);
1060
1061 rewriter.replaceOp(op, fieldRef);
1062 return success();
1063 }
1064
1065private:
1066 ClassTypeCache &cache;
1067};
1068
1069struct ClassUpcastOpConversion : public OpConversionPattern<ClassUpcastOp> {
1070 using OpConversionPattern::OpConversionPattern;
1071
1072 LogicalResult
1073 matchAndRewrite(ClassUpcastOp op, OpAdaptor adaptor,
1074 ConversionPatternRewriter &rewriter) const override {
1075 // Expect lowered types like !llvm.ptr
1076 Type dstTy = getTypeConverter()->convertType(op.getResult().getType());
1077 Type srcTy = adaptor.getInstance().getType();
1078
1079 if (!dstTy)
1080 return rewriter.notifyMatchFailure(op, "failed to convert result type");
1081
1082 // If the types are already identical (opaque pointer mode), just forward.
1083 if (dstTy == srcTy && isa<LLVM::LLVMPointerType>(srcTy)) {
1084 rewriter.replaceOp(op, adaptor.getInstance());
1085 return success();
1086 }
1087 return rewriter.notifyMatchFailure(
1088 op, "Upcast applied to non-opaque pointers!");
1089 }
1090};
1091
1092/// moore.class.new lowering: heap-allocate storage for the class object.
1093struct ClassNewOpConversion : public OpConversionPattern<ClassNewOp> {
1094 ClassNewOpConversion(TypeConverter &tc, MLIRContext *ctx,
1095 ClassTypeCache &cache, FunctionCache &funcCache)
1096 : OpConversionPattern<ClassNewOp>(tc, ctx), cache(cache),
1097 funcCache(funcCache) {}
1098
1099 LogicalResult
1100 matchAndRewrite(ClassNewOp op, OpAdaptor adaptor,
1101 ConversionPatternRewriter &rewriter) const override {
1102 Location loc = op.getLoc();
1103 MLIRContext *ctx = rewriter.getContext();
1104
1105 auto handleTy = cast<ClassHandleType>(op.getResult().getType());
1106 auto sym = handleTy.getClassSym();
1107
1108 ModuleOp mod = op->getParentOfType<ModuleOp>();
1109
1110 if (failed(resolveClassStructBody(mod, sym, *typeConverter, cache)))
1111 return op.emitError() << "Could not resolve class struct for " << sym;
1112
1113 auto structTy = cache.getStructInfo(sym)->classBody;
1114 auto typeInfo = cache.getStructInfo(sym)->typeInfo;
1115
1116 // Check that all struct members have data layout support. Types like
1117 // !sim.dstring or !sim.queue don't have a known size, which would cause
1118 // a fatal error in DataLayout::getTypeSize below.
1119 for (auto memberTy : structTy.getBody()) {
1120 if (!LLVM::isCompatibleType(memberTy) &&
1121 !memberTy.hasTrait<DataLayoutTypeInterface::Trait>()) {
1122 return op.emitError()
1123 << "class struct has member types with no data layout";
1124 }
1125 }
1126
1127 DataLayout dl(mod);
1128 // DataLayout::getTypeSize gives a byte count for LLVM types.
1129 uint64_t byteSize = dl.getTypeSize(structTy);
1130 auto i64Ty = IntegerType::get(ctx, 64);
1131 auto cSize = LLVM::ConstantOp::create(rewriter, loc, i64Ty,
1132 rewriter.getI64IntegerAttr(byteSize));
1133
1134 // Get or declare malloc and call it.
1135 auto ptrTy = LLVM::LLVMPointerType::get(ctx); // opaque pointer result
1136 auto mallocFn = funcCache.getOrCreate(rewriter, "malloc", {i64Ty}, {ptrTy});
1137 auto call =
1138 func::CallOp::create(rewriter, loc, mallocFn, ValueRange{cSize});
1139
1140 auto typeInfoAddr =
1141 LLVM::AddressOfOp::create(rewriter, loc, typeInfo.global);
1142 auto i32Ty = IntegerType::get(ctx, 32);
1143 auto headerIdx = LLVM::ConstantOp::create(
1144 rewriter, loc, i32Ty,
1145 rewriter.getI32IntegerAttr(cache.getStructInfo(sym)->headerFieldIndex));
1146 auto typeInfoIdx = LLVM::ConstantOp::create(
1147 rewriter, loc, i32Ty,
1148 rewriter.getI32IntegerAttr(
1149 cache.getStructInfo(sym)->typeInfoFieldIndex));
1150 auto headerPtr =
1151 LLVM::GEPOp::create(rewriter, loc, ptrTy, structTy, call.getResult(0),
1152 ValueRange{headerIdx, typeInfoIdx});
1153 LLVM::StoreOp::create(rewriter, loc, typeInfoAddr, headerPtr);
1154
1155 // Replace the new op with the malloc pointer (no cast needed with opaque
1156 // ptrs).
1157 rewriter.replaceOp(op, call.getResult(0));
1158 return success();
1159 }
1160
1161private:
1162 ClassTypeCache &cache; // shared, owned by the pass
1163 FunctionCache &funcCache;
1164};
1165
1166struct ClassDeclOpConversion : public OpConversionPattern<ClassDeclOp> {
1167 ClassDeclOpConversion(TypeConverter &tc, MLIRContext *ctx,
1168 ClassTypeCache &cache)
1169 : OpConversionPattern<ClassDeclOp>(tc, ctx), cache(cache) {}
1170
1171 LogicalResult
1172 matchAndRewrite(ClassDeclOp op, OpAdaptor,
1173 ConversionPatternRewriter &rewriter) const override {
1174
1175 if (failed(resolveClassStructBody(op, *typeConverter, cache)))
1176 return failure();
1177 // The declaration itself is a no-op
1178 rewriter.eraseOp(op);
1179 return success();
1180 }
1181
1182private:
1183 ClassTypeCache &cache; // shared, owned by the pass
1184};
1185
1186struct VariableOpConversion : public OpConversionPattern<VariableOp> {
1187 using OpConversionPattern::OpConversionPattern;
1188
1189 LogicalResult
1190 matchAndRewrite(VariableOp op, OpAdaptor adaptor,
1191 ConversionPatternRewriter &rewriter) const override {
1192 auto loc = op.getLoc();
1193 auto resultType = typeConverter->convertType(op.getResult().getType());
1194 if (!resultType)
1195 return rewriter.notifyMatchFailure(op.getLoc(), "invalid variable type");
1196
1197 auto refType = dyn_cast<llhd::RefType>(resultType);
1198 if (!refType)
1199 return rewriter.notifyMatchFailure(
1200 op.getLoc(), "variable type did not convert to llhd::RefType");
1201
1202 // Determine the initial value of the signal.
1203 Value init = adaptor.getInitial();
1204 if (!init) {
1205 init = createZeroValue(refType.getNestedType(), loc, rewriter);
1206 if (!init)
1207 return failure();
1208 }
1209
1210 rewriter.replaceOpWithNewOp<llhd::SignalOp>(op, resultType,
1211 op.getNameAttr(), init);
1212 return success();
1213 }
1214};
1215
1216struct NetOpConversion : public OpConversionPattern<NetOp> {
1217 using OpConversionPattern::OpConversionPattern;
1218
1219 LogicalResult
1220 matchAndRewrite(NetOp op, OpAdaptor adaptor,
1221 ConversionPatternRewriter &rewriter) const override {
1222 auto loc = op.getLoc();
1223
1224 auto resultType = typeConverter->convertType(op.getResult().getType());
1225 if (!resultType)
1226 return rewriter.notifyMatchFailure(loc, "invalid net type");
1227
1228 auto elementType = cast<llhd::RefType>(resultType).getNestedType();
1229 int64_t width = hw::getBitWidth(elementType);
1230 if (width == -1)
1231 return failure();
1232
1233 auto init =
1234 createInitialValue(op.getKind(), rewriter, loc, width, elementType);
1235 auto signal = rewriter.replaceOpWithNewOp<llhd::SignalOp>(
1236 op, resultType, op.getNameAttr(), init);
1237
1238 if (auto assignedValue = adaptor.getAssignment()) {
1239 auto timeAttr = llhd::TimeAttr::get(resultType.getContext(), 0U,
1240 llvm::StringRef("ns"), 0, 1);
1241 auto time = llhd::ConstantTimeOp::create(rewriter, loc, timeAttr);
1242 llhd::DriveOp::create(rewriter, loc, signal, assignedValue, time,
1243 Value{});
1244 }
1245
1246 return success();
1247 }
1248
1249 static mlir::Value createInitialValue(NetKind kind,
1250 ConversionPatternRewriter &rewriter,
1251 Location loc, int64_t width,
1252 Type elementType) {
1253 // TODO: Once the core dialects support four-valued integers, this code
1254 // will additionally need to generate an all-X value for four-valued nets.
1255 //
1256 // If no driver is connected to a net, its value shall be high-impedance (z)
1257 // unless the net is a trireg, in which case it shall hold the previously
1258 // driven value.
1259 //
1260 // See IEEE 1800-2017 § 6.6 "Net types".
1261 auto theInt = [&] {
1262 if (kind == NetKind::Supply1 || kind == NetKind::Tri1)
1263 return APInt::getAllOnes(width);
1264 return APInt::getZero(width);
1265 }();
1266 auto theConst = hw::ConstantOp::create(rewriter, loc, theInt);
1267 return rewriter.createOrFold<hw::BitcastOp>(loc, elementType, theConst);
1268 }
1269};
1270
1271// moore.global_variable -> llhd.global_signal
1272static LogicalResult convert(GlobalVariableOp op,
1273 GlobalVariableOp::Adaptor adaptor,
1274 ConversionPatternRewriter &rewriter,
1275 const TypeConverter &typeConverter) {
1276 auto type = typeConverter.convertType(op.getType());
1277 auto sig = llhd::GlobalSignalOp::create(rewriter, op.getLoc(),
1278 op.getSymNameAttr(), type);
1279 sig.getInitRegion().takeBody(op.getInitRegion());
1280 rewriter.eraseOp(op);
1281 return success();
1282}
1283
1284// moore.get_global_variable -> llhd.get_global_signal
1285static LogicalResult convert(GetGlobalVariableOp op,
1286 GetGlobalVariableOp::Adaptor adaptor,
1287 ConversionPatternRewriter &rewriter,
1288 const TypeConverter &typeConverter) {
1289 auto type = typeConverter.convertType(op.getType());
1290 rewriter.replaceOpWithNewOp<llhd::GetGlobalSignalOp>(op, type,
1291 op.getGlobalNameAttr());
1292 return success();
1293}
1294
1295//===----------------------------------------------------------------------===//
1296// Expression Conversion
1297//===----------------------------------------------------------------------===//
1298
1299struct ConstantOpConv : public OpConversionPattern<ConstantOp> {
1300 using OpConversionPattern::OpConversionPattern;
1301
1302 LogicalResult
1303 matchAndRewrite(ConstantOp op, OpAdaptor adaptor,
1304 ConversionPatternRewriter &rewriter) const override {
1305 // FIXME: Discard unknown bits and map them to 0 for now.
1306 auto value = op.getValue().toAPInt(false);
1307 auto type = rewriter.getIntegerType(value.getBitWidth());
1308 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
1309 op, type, rewriter.getIntegerAttr(type, value));
1310 return success();
1311 }
1312};
1313
1314struct ConstantRealOpConv : public OpConversionPattern<ConstantRealOp> {
1315 using OpConversionPattern::OpConversionPattern;
1316
1317 LogicalResult
1318 matchAndRewrite(ConstantRealOp op, OpAdaptor adaptor,
1319 ConversionPatternRewriter &rewriter) const override {
1320 rewriter.replaceOpWithNewOp<arith::ConstantOp>(op, op.getValueAttr());
1321 return success();
1322 }
1323};
1324
1325struct ConstantTimeOpConv : public OpConversionPattern<ConstantTimeOp> {
1326 using OpConversionPattern::OpConversionPattern;
1327
1328 LogicalResult
1329 matchAndRewrite(ConstantTimeOp op, OpAdaptor adaptor,
1330 ConversionPatternRewriter &rewriter) const override {
1331 rewriter.replaceOpWithNewOp<llhd::ConstantTimeOp>(
1332 op, llhd::TimeAttr::get(op->getContext(), op.getValue(),
1333 StringRef("fs"), 0, 0));
1334 return success();
1335 }
1336};
1337
1338struct ConstantStringOpConv : public OpConversionPattern<ConstantStringOp> {
1339 using OpConversionPattern::OpConversionPattern;
1340 LogicalResult
1341 matchAndRewrite(moore::ConstantStringOp op, OpAdaptor adaptor,
1342 ConversionPatternRewriter &rewriter) const override {
1343 const auto resultType =
1344 typeConverter->convertType(op.getResult().getType());
1345 const auto intType = mlir::cast<IntegerType>(resultType);
1346
1347 const auto str = op.getValue();
1348 const unsigned byteWidth = intType.getWidth();
1349 APInt value(byteWidth, 0);
1350
1351 // Pack ascii chars from the end of the string, until it fits.
1352 const size_t maxChars =
1353 std::min(str.size(), static_cast<size_t>(byteWidth / 8));
1354 for (size_t i = 0; i < maxChars; i++) {
1355 const size_t pos = str.size() - 1 - i;
1356 const auto asciiChar = static_cast<uint8_t>(str[pos]);
1357 value |= APInt(byteWidth, asciiChar) << (8 * i);
1358 }
1359
1360 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
1361 op, resultType, rewriter.getIntegerAttr(resultType, value));
1362 return success();
1363 }
1364};
1365
1366struct ConcatOpConversion : public OpConversionPattern<ConcatOp> {
1367 using OpConversionPattern::OpConversionPattern;
1368 LogicalResult
1369 matchAndRewrite(ConcatOp op, OpAdaptor adaptor,
1370 ConversionPatternRewriter &rewriter) const override {
1371 rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, adaptor.getValues());
1372 return success();
1373 }
1374};
1375
1376struct ReplicateOpConversion : public OpConversionPattern<ReplicateOp> {
1377 using OpConversionPattern::OpConversionPattern;
1378 LogicalResult
1379 matchAndRewrite(ReplicateOp op, OpAdaptor adaptor,
1380 ConversionPatternRewriter &rewriter) const override {
1381 Type resultType = typeConverter->convertType(op.getResult().getType());
1382
1383 rewriter.replaceOpWithNewOp<comb::ReplicateOp>(op, resultType,
1384 adaptor.getValue());
1385 return success();
1386 }
1387};
1388
1389struct ExtractOpConversion : public OpConversionPattern<ExtractOp> {
1390 using OpConversionPattern::OpConversionPattern;
1391
1392 LogicalResult
1393 matchAndRewrite(ExtractOp op, OpAdaptor adaptor,
1394 ConversionPatternRewriter &rewriter) const override {
1395 // TODO: return X if the domain is four-valued for out-of-bounds accesses
1396 // once we support four-valued lowering
1397 Type resultType = typeConverter->convertType(op.getResult().getType());
1398 Value input = adaptor.getInput();
1399 Type inputType = input.getType();
1400 int32_t low = adaptor.getLowBit();
1401
1402 if (auto structTy = dyn_cast<hw::StructType>(inputType)) {
1403 int32_t width = hw::getBitWidth(structTy);
1404 if (width == -1)
1405 return failure();
1406 input = rewriter.createOrFold<hw::BitcastOp>(
1407 op.getLoc(), rewriter.getIntegerType(width), input);
1408 inputType = input.getType();
1409 }
1410
1411 if (isa<IntegerType>(inputType)) {
1412 int32_t inputWidth = inputType.getIntOrFloatBitWidth();
1413 int32_t resultWidth = hw::getBitWidth(resultType);
1414 int32_t high = low + resultWidth;
1415
1416 SmallVector<Value> toConcat;
1417 if (low < 0)
1418 toConcat.push_back(hw::ConstantOp::create(
1419 rewriter, op.getLoc(), APInt(std::min(-low, resultWidth), 0)));
1420
1421 if (low < inputWidth && high > 0) {
1422 int32_t lowIdx = std::max(low, 0);
1423 Value middle = rewriter.createOrFold<comb::ExtractOp>(
1424 op.getLoc(),
1425 rewriter.getIntegerType(
1426 std::min(resultWidth, std::min(high, inputWidth) - lowIdx)),
1427 input, lowIdx);
1428 toConcat.push_back(middle);
1429 }
1430
1431 int32_t diff = high - inputWidth;
1432 if (diff > 0) {
1433 Value val =
1434 hw::ConstantOp::create(rewriter, op.getLoc(), APInt(diff, 0));
1435 toConcat.push_back(val);
1436 }
1437
1438 Value concat =
1439 rewriter.createOrFold<comb::ConcatOp>(op.getLoc(), toConcat);
1440 rewriter.replaceOp(op, concat);
1441 return success();
1442 }
1443
1444 if (auto arrTy = dyn_cast<hw::ArrayType>(inputType)) {
1445 int32_t width = llvm::Log2_64_Ceil(arrTy.getNumElements());
1446 int32_t inputWidth = arrTy.getNumElements();
1447
1448 if (auto resArrTy = dyn_cast<hw::ArrayType>(resultType);
1449 resArrTy && resArrTy != arrTy.getElementType()) {
1450 int32_t elementWidth = hw::getBitWidth(arrTy.getElementType());
1451 if (elementWidth < 0)
1452 return failure();
1453
1454 int32_t high = low + resArrTy.getNumElements();
1455 int32_t resWidth = resArrTy.getNumElements();
1456
1457 SmallVector<Value> toConcat;
1458 if (low < 0) {
1459 Value val = hw::ConstantOp::create(
1460 rewriter, op.getLoc(),
1461 APInt(std::min((-low) * elementWidth, resWidth * elementWidth),
1462 0));
1463 Value res = rewriter.createOrFold<hw::BitcastOp>(
1464 op.getLoc(), hw::ArrayType::get(arrTy.getElementType(), -low),
1465 val);
1466 toConcat.push_back(res);
1467 }
1468
1469 if (low < inputWidth && high > 0) {
1470 int32_t lowIdx = std::max(0, low);
1471 Value lowIdxVal = hw::ConstantOp::create(
1472 rewriter, op.getLoc(), rewriter.getIntegerType(width), lowIdx);
1473 Value middle = rewriter.createOrFold<hw::ArraySliceOp>(
1474 op.getLoc(),
1475 hw::ArrayType::get(
1476 arrTy.getElementType(),
1477 std::min(resWidth, std::min(inputWidth, high) - lowIdx)),
1478 adaptor.getInput(), lowIdxVal);
1479 toConcat.push_back(middle);
1480 }
1481
1482 int32_t diff = high - inputWidth;
1483 if (diff > 0) {
1484 Value constZero = hw::ConstantOp::create(
1485 rewriter, op.getLoc(), APInt(diff * elementWidth, 0));
1486 Value val = hw::BitcastOp::create(
1487 rewriter, op.getLoc(),
1488 hw::ArrayType::get(arrTy.getElementType(), diff), constZero);
1489 toConcat.push_back(val);
1490 }
1491
1492 Value concat =
1493 rewriter.createOrFold<hw::ArrayConcatOp>(op.getLoc(), toConcat);
1494 rewriter.replaceOp(op, concat);
1495 return success();
1496 }
1497
1498 // Otherwise, it has to be the array's element type
1499 if (low < 0 || low >= inputWidth) {
1500 int32_t bw = hw::getBitWidth(resultType);
1501 if (bw < 0)
1502 return failure();
1503
1504 Value val = hw::ConstantOp::create(rewriter, op.getLoc(), APInt(bw, 0));
1505 Value bitcast =
1506 rewriter.createOrFold<hw::BitcastOp>(op.getLoc(), resultType, val);
1507 rewriter.replaceOp(op, bitcast);
1508 return success();
1509 }
1510
1511 Value idx = hw::ConstantOp::create(rewriter, op.getLoc(),
1512 rewriter.getIntegerType(width),
1513 adaptor.getLowBit());
1514 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(), idx);
1515 return success();
1516 }
1517
1518 return failure();
1519 }
1520};
1521
1522struct ExtractRefOpConversion : public OpConversionPattern<ExtractRefOp> {
1523 using OpConversionPattern::OpConversionPattern;
1524
1525 LogicalResult
1526 matchAndRewrite(ExtractRefOp op, OpAdaptor adaptor,
1527 ConversionPatternRewriter &rewriter) const override {
1528 // TODO: properly handle out-of-bounds accesses
1529 Type resultType = typeConverter->convertType(op.getResult().getType());
1530 Type inputType =
1531 cast<llhd::RefType>(adaptor.getInput().getType()).getNestedType();
1532
1533 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1534 int64_t width = hw::getBitWidth(inputType);
1535 if (width == -1)
1536 return failure();
1537
1538 Value lowBit = hw::ConstantOp::create(
1539 rewriter, op.getLoc(),
1540 rewriter.getIntegerType(llvm::Log2_64_Ceil(width)),
1541 adaptor.getLowBit());
1542 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
1543 op, resultType, adaptor.getInput(), lowBit);
1544 return success();
1545 }
1546
1547 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1548 Value lowBit = hw::ConstantOp::create(
1549 rewriter, op.getLoc(),
1550 rewriter.getIntegerType(llvm::Log2_64_Ceil(arrType.getNumElements())),
1551 adaptor.getLowBit());
1552
1553 // If the result type is not the same as the array's element type, then
1554 // it has to be a slice.
1555 if (arrType.getElementType() !=
1556 cast<llhd::RefType>(resultType).getNestedType()) {
1557 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
1558 op, resultType, adaptor.getInput(), lowBit);
1559 return success();
1560 }
1561
1562 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
1563 lowBit);
1564 return success();
1565 }
1566
1567 return failure();
1568 }
1569};
1570
1571struct DynExtractOpConversion : public OpConversionPattern<DynExtractOp> {
1572 using OpConversionPattern::OpConversionPattern;
1573
1574 LogicalResult
1575 matchAndRewrite(DynExtractOp op, OpAdaptor adaptor,
1576 ConversionPatternRewriter &rewriter) const override {
1577 Type resultType = typeConverter->convertType(op.getResult().getType());
1578 Type inputType = adaptor.getInput().getType();
1579
1580 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1581 Value amount = adjustIntegerWidth(rewriter, adaptor.getLowBit(),
1582 intType.getWidth(), op->getLoc());
1583 Value value = comb::ShrUOp::create(rewriter, op->getLoc(),
1584 adaptor.getInput(), amount);
1585
1586 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, value, 0);
1587 return success();
1588 }
1589
1590 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1591 unsigned idxWidth = llvm::Log2_64_Ceil(arrType.getNumElements());
1592 Value idx = adjustIntegerWidth(rewriter, adaptor.getLowBit(), idxWidth,
1593 op->getLoc());
1594
1595 bool isSingleElementExtract = arrType.getElementType() == resultType;
1596
1597 if (isSingleElementExtract)
1598 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(),
1599 idx);
1600 else
1601 rewriter.replaceOpWithNewOp<hw::ArraySliceOp>(op, resultType,
1602 adaptor.getInput(), idx);
1603
1604 return success();
1605 }
1606
1607 return failure();
1608 }
1609};
1610
1611struct DynExtractRefOpConversion : public OpConversionPattern<DynExtractRefOp> {
1612 using OpConversionPattern::OpConversionPattern;
1613
1614 LogicalResult
1615 matchAndRewrite(DynExtractRefOp op, OpAdaptor adaptor,
1616 ConversionPatternRewriter &rewriter) const override {
1617 // TODO: properly handle out-of-bounds accesses
1618 Type resultType = typeConverter->convertType(op.getResult().getType());
1619 Type inputType =
1620 cast<llhd::RefType>(adaptor.getInput().getType()).getNestedType();
1621
1622 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1623 int64_t width = hw::getBitWidth(inputType);
1624 if (width == -1)
1625 return failure();
1626
1627 Value amount =
1628 adjustIntegerWidth(rewriter, adaptor.getLowBit(),
1629 llvm::Log2_64_Ceil(width), op->getLoc());
1630 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
1631 op, resultType, adaptor.getInput(), amount);
1632 return success();
1633 }
1634
1635 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1636 Value idx = adjustIntegerWidth(
1637 rewriter, adaptor.getLowBit(),
1638 llvm::Log2_64_Ceil(arrType.getNumElements()), op->getLoc());
1639
1640 auto resultNestedType = cast<llhd::RefType>(resultType).getNestedType();
1641 bool isSingleElementExtract =
1642 arrType.getElementType() == resultNestedType;
1643
1644 if (isSingleElementExtract)
1645 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
1646 idx);
1647 else
1648 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
1649 op, resultType, adaptor.getInput(), idx);
1650
1651 return success();
1652 }
1653
1654 return failure();
1655 }
1656};
1657
1658struct ArrayCreateOpConversion : public OpConversionPattern<ArrayCreateOp> {
1659 using OpConversionPattern::OpConversionPattern;
1660
1661 LogicalResult
1662 matchAndRewrite(ArrayCreateOp op, OpAdaptor adaptor,
1663 ConversionPatternRewriter &rewriter) const override {
1664 Type resultType = typeConverter->convertType(op.getResult().getType());
1665 rewriter.replaceOpWithNewOp<hw::ArrayCreateOp>(op, resultType,
1666 adaptor.getElements());
1667 return success();
1668 }
1669};
1670
1671struct StructCreateOpConversion : public OpConversionPattern<StructCreateOp> {
1672 using OpConversionPattern::OpConversionPattern;
1673
1674 LogicalResult
1675 matchAndRewrite(StructCreateOp op, OpAdaptor adaptor,
1676 ConversionPatternRewriter &rewriter) const override {
1677 Type resultType = typeConverter->convertType(op.getResult().getType());
1678 rewriter.replaceOpWithNewOp<hw::StructCreateOp>(op, resultType,
1679 adaptor.getFields());
1680 return success();
1681 }
1682};
1683
1684struct StructExtractOpConversion : public OpConversionPattern<StructExtractOp> {
1685 using OpConversionPattern::OpConversionPattern;
1686
1687 LogicalResult
1688 matchAndRewrite(StructExtractOp op, OpAdaptor adaptor,
1689 ConversionPatternRewriter &rewriter) const override {
1690 rewriter.replaceOpWithNewOp<hw::StructExtractOp>(
1691 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1692 return success();
1693 }
1694};
1695
1696struct StructExtractRefOpConversion
1697 : public OpConversionPattern<StructExtractRefOp> {
1698 using OpConversionPattern::OpConversionPattern;
1699
1700 LogicalResult
1701 matchAndRewrite(StructExtractRefOp op, OpAdaptor adaptor,
1702 ConversionPatternRewriter &rewriter) const override {
1703 rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
1704 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1705 return success();
1706 }
1707};
1708
1709struct UnionCreateOpConversion : public OpConversionPattern<UnionCreateOp> {
1710 using OpConversionPattern::OpConversionPattern;
1711
1712 LogicalResult
1713 matchAndRewrite(UnionCreateOp op, OpAdaptor adaptor,
1714 ConversionPatternRewriter &rewriter) const override {
1715 Type resultType = typeConverter->convertType(op.getResult().getType());
1716 rewriter.replaceOpWithNewOp<hw::UnionCreateOp>(
1717 op, resultType, adaptor.getFieldNameAttr(), adaptor.getInput());
1718 return success();
1719 }
1720};
1721
1722struct UnionExtractOpConversion : public OpConversionPattern<UnionExtractOp> {
1723 using OpConversionPattern::OpConversionPattern;
1724
1725 LogicalResult
1726 matchAndRewrite(UnionExtractOp op, OpAdaptor adaptor,
1727 ConversionPatternRewriter &rewriter) const override {
1728 rewriter.replaceOpWithNewOp<hw::UnionExtractOp>(op, adaptor.getInput(),
1729 adaptor.getFieldNameAttr());
1730 return success();
1731 }
1732};
1733
1734struct UnionExtractRefOpConversion
1735 : public OpConversionPattern<UnionExtractRefOp> {
1736 using OpConversionPattern::OpConversionPattern;
1737
1738 LogicalResult
1739 matchAndRewrite(UnionExtractRefOp op, OpAdaptor adaptor,
1740 ConversionPatternRewriter &rewriter) const override {
1741 rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
1742 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1743 return success();
1744 }
1745};
1746
1747struct ReduceAndOpConversion : public OpConversionPattern<ReduceAndOp> {
1748 using OpConversionPattern::OpConversionPattern;
1749 LogicalResult
1750 matchAndRewrite(ReduceAndOp op, OpAdaptor adaptor,
1751 ConversionPatternRewriter &rewriter) const override {
1752 Type resultType = typeConverter->convertType(op.getInput().getType());
1753 Value max = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, -1);
1754
1755 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::eq,
1756 adaptor.getInput(), max);
1757 return success();
1758 }
1759};
1760
1761struct ReduceOrOpConversion : public OpConversionPattern<ReduceOrOp> {
1762 using OpConversionPattern::OpConversionPattern;
1763 LogicalResult
1764 matchAndRewrite(ReduceOrOp op, OpAdaptor adaptor,
1765 ConversionPatternRewriter &rewriter) const override {
1766 Type resultType = typeConverter->convertType(op.getInput().getType());
1767 Value zero = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1768
1769 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1770 adaptor.getInput(), zero);
1771 return success();
1772 }
1773};
1774
1775struct ReduceXorOpConversion : public OpConversionPattern<ReduceXorOp> {
1776 using OpConversionPattern::OpConversionPattern;
1777 LogicalResult
1778 matchAndRewrite(ReduceXorOp op, OpAdaptor adaptor,
1779 ConversionPatternRewriter &rewriter) const override {
1780
1781 rewriter.replaceOpWithNewOp<comb::ParityOp>(op, adaptor.getInput());
1782 return success();
1783 }
1784};
1785
1786struct BoolCastOpConversion : public OpConversionPattern<BoolCastOp> {
1787 using OpConversionPattern::OpConversionPattern;
1788 LogicalResult
1789 matchAndRewrite(BoolCastOp op, OpAdaptor adaptor,
1790 ConversionPatternRewriter &rewriter) const override {
1791 Type resultType = typeConverter->convertType(op.getInput().getType());
1792 if (isa_and_nonnull<IntegerType>(resultType)) {
1793 Value zero =
1794 hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1795 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1796 adaptor.getInput(), zero);
1797 return success();
1798 }
1799 if (isa_and_nonnull<FloatType>(resultType)) {
1800 Value zero = arith::ConstantOp::create(
1801 rewriter, op->getLoc(), rewriter.getFloatAttr(resultType, 0.0));
1802 rewriter.replaceOpWithNewOp<arith::CmpFOp>(op, arith::CmpFPredicate::ONE,
1803 adaptor.getInput(), zero);
1804 return success();
1805 }
1806 if (isa_and_nonnull<llhd::TimeType>(resultType)) {
1807 Value timeInt =
1808 llhd::TimeToIntOp::create(rewriter, op->getLoc(), adaptor.getInput());
1809 Value zero = hw::ConstantOp::create(rewriter, op->getLoc(),
1810 rewriter.getI64Type(), 0);
1811 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1812 timeInt, zero);
1813 return success();
1814 }
1815 if (isa_and_nonnull<hw::StructType, hw::ArrayType, hw::UnionType>(
1816 resultType)) {
1817 int64_t width = hw::getBitWidth(resultType);
1818 if (width < 0)
1819 return failure();
1820 auto intTy = rewriter.getIntegerType(width);
1821 Value input = rewriter.createOrFold<hw::BitcastOp>(op->getLoc(), intTy,
1822 adaptor.getInput());
1823 Value zero = hw::ConstantOp::create(rewriter, op->getLoc(), intTy, 0);
1824 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1825 input, zero);
1826 return success();
1827 }
1828 return failure();
1829 }
1830};
1831
1832struct NotOpConversion : public OpConversionPattern<NotOp> {
1833 using OpConversionPattern::OpConversionPattern;
1834 LogicalResult
1835 matchAndRewrite(NotOp op, OpAdaptor adaptor,
1836 ConversionPatternRewriter &rewriter) const override {
1837 Type resultType =
1838 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1839 Value max = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, -1);
1840
1841 rewriter.replaceOpWithNewOp<comb::XorOp>(op, adaptor.getInput(), max);
1842 return success();
1843 }
1844};
1845
1846struct NegOpConversion : public OpConversionPattern<NegOp> {
1847 using OpConversionPattern::OpConversionPattern;
1848 LogicalResult
1849 matchAndRewrite(NegOp op, OpAdaptor adaptor,
1850 ConversionPatternRewriter &rewriter) const override {
1851 Type resultType =
1852 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1853 Value zero = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, 0);
1854
1855 rewriter.replaceOpWithNewOp<comb::SubOp>(op, zero, adaptor.getInput());
1856 return success();
1857 }
1858};
1859
1860struct NegRealOpConversion : public OpConversionPattern<NegRealOp> {
1861 using OpConversionPattern::OpConversionPattern;
1862 LogicalResult
1863 matchAndRewrite(NegRealOp op, OpAdaptor adaptor,
1864 ConversionPatternRewriter &rewriter) const override {
1865 rewriter.replaceOpWithNewOp<arith::NegFOp>(op, adaptor.getInput());
1866 return success();
1867 }
1868};
1869
1870template <typename SourceOp, typename TargetOp>
1871struct BinaryOpConversion : public OpConversionPattern<SourceOp> {
1873 using OpAdaptor = typename SourceOp::Adaptor;
1874
1875 LogicalResult
1876 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1877 ConversionPatternRewriter &rewriter) const override {
1878 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1879 adaptor.getRhs(), false);
1880 return success();
1881 }
1882};
1883
1884template <typename SourceOp, typename TargetOp>
1885struct BinaryRealOpConversion : public OpConversionPattern<SourceOp> {
1887 using OpAdaptor = typename SourceOp::Adaptor;
1888
1889 LogicalResult
1890 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1891 ConversionPatternRewriter &rewriter) const override {
1892 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1893 adaptor.getRhs());
1894 return success();
1895 }
1896};
1897
1898struct HypotBIOpConversion : public OpConversionPattern<HypotBIOp> {
1899 using OpConversionPattern::OpConversionPattern;
1900 LogicalResult
1901 matchAndRewrite(HypotBIOp op, OpAdaptor adaptor,
1902 ConversionPatternRewriter &rewriter) const override {
1903 Value lhs = adaptor.getLhs();
1904 Value rhs = adaptor.getRhs();
1905 ImplicitLocOpBuilder b(op->getLoc(), rewriter);
1906 auto left = arith::MulFOp::create(b, lhs, lhs);
1907 auto right = arith::MulFOp::create(b, rhs, rhs);
1908 auto sum = arith::AddFOp::create(b, left, right);
1909 auto out = math::SqrtOp::create(b, sum);
1910 rewriter.replaceOp(op, out);
1911 return success();
1912 }
1913};
1914
1915template <typename SourceOp, typename TargetOp>
1916struct RealMathFunc : public OpConversionPattern<SourceOp> {
1918 using OpAdaptor = typename SourceOp::Adaptor;
1919
1920 LogicalResult
1921 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1922 ConversionPatternRewriter &rewriter) const override {
1923 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getValue());
1924 return success();
1925 }
1926};
1927
1928template <typename SourceOp, ICmpPredicate pred>
1929struct ICmpOpConversion : public OpConversionPattern<SourceOp> {
1931 using OpAdaptor = typename SourceOp::Adaptor;
1932
1933 LogicalResult
1934 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1935 ConversionPatternRewriter &rewriter) const override {
1936 Type resultType =
1937 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1938
1939 rewriter.replaceOpWithNewOp<comb::ICmpOp>(
1940 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1941 return success();
1942 }
1943};
1944
1945struct NullOpConversion : public OpConversionPattern<NullOp> {
1946 using OpConversionPattern::OpConversionPattern;
1947
1948 LogicalResult
1949 matchAndRewrite(NullOp op, OpAdaptor adaptor,
1950 ConversionPatternRewriter &rewriter) const override {
1951 Type ptrTy = getTypeConverter()->convertType(op.getResult().getType());
1952 if (!ptrTy)
1953 return rewriter.notifyMatchFailure(op, "failed to convert null type");
1954 rewriter.replaceOpWithNewOp<LLVM::ZeroOp>(op, ptrTy);
1955 return success();
1956 }
1957};
1958
1959template <typename SourceOp, LLVM::ICmpPredicate pred>
1960struct HandleCmpOpConversion : public OpConversionPattern<SourceOp> {
1962 using OpAdaptor = typename SourceOp::Adaptor;
1963
1964 LogicalResult
1965 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1966 ConversionPatternRewriter &rewriter) const override {
1967 rewriter.replaceOpWithNewOp<LLVM::ICmpOp>(op, pred, adaptor.getLhs(),
1968 adaptor.getRhs());
1969 return success();
1970 }
1971};
1972
1973template <typename SourceOp, arith::CmpFPredicate pred>
1974struct FCmpOpConversion : public OpConversionPattern<SourceOp> {
1976 using OpAdaptor = typename SourceOp::Adaptor;
1977
1978 LogicalResult
1979 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1980 ConversionPatternRewriter &rewriter) const override {
1981 Type resultType =
1982 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1983
1984 rewriter.replaceOpWithNewOp<arith::CmpFOp>(
1985 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1986 return success();
1987 }
1988};
1989
1990template <typename SourceOp, bool withoutX>
1991struct CaseXZEqOpConversion : public OpConversionPattern<SourceOp> {
1993 using OpAdaptor = typename SourceOp::Adaptor;
1994
1995 LogicalResult
1996 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1997 ConversionPatternRewriter &rewriter) const override {
1998 // Check each operand if it is a known constant and extract the X and/or Z
1999 // bits to be ignored.
2000 // TODO: Once the core dialects support four-valued integers, we will have
2001 // to create ops that extract X and Z bits from the operands, since we also
2002 // have to do the right casez/casex comparison on non-constant inputs.
2003 unsigned bitWidth = op.getLhs().getType().getWidth();
2004 auto ignoredBits = APInt::getZero(bitWidth);
2005 auto detectIgnoredBits = [&](Value value) {
2006 auto constOp = value.getDefiningOp<ConstantOp>();
2007 if (!constOp)
2008 return;
2009 auto constValue = constOp.getValue();
2010 if (withoutX)
2011 ignoredBits |= constValue.getZBits();
2012 else
2013 ignoredBits |= constValue.getUnknownBits();
2014 };
2015 detectIgnoredBits(op.getLhs());
2016 detectIgnoredBits(op.getRhs());
2017
2018 // If we have detected any bits to be ignored, mask them in the operands for
2019 // the comparison.
2020 Value lhs = adaptor.getLhs();
2021 Value rhs = adaptor.getRhs();
2022 if (!ignoredBits.isZero()) {
2023 ignoredBits.flipAllBits();
2024 auto maskOp = hw::ConstantOp::create(rewriter, op.getLoc(), ignoredBits);
2025 lhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), lhs, maskOp);
2026 rhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), rhs, maskOp);
2027 }
2028
2029 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, ICmpPredicate::ceq, lhs, rhs);
2030 return success();
2031 }
2032};
2033
2034//===----------------------------------------------------------------------===//
2035// Conversions
2036//===----------------------------------------------------------------------===//
2037
2038struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
2039 using OpConversionPattern::OpConversionPattern;
2040
2041 LogicalResult
2042 matchAndRewrite(ConversionOp op, OpAdaptor adaptor,
2043 ConversionPatternRewriter &rewriter) const override {
2044 Location loc = op.getLoc();
2045 Type resultType = typeConverter->convertType(op.getResult().getType());
2046 if (!resultType) {
2047 op.emitError("conversion result type is not currently supported");
2048 return failure();
2049 }
2050 int64_t inputBw = hw::getBitWidth(adaptor.getInput().getType());
2051 int64_t resultBw = hw::getBitWidth(resultType);
2052 if (inputBw == -1 || resultBw == -1) {
2053 if (isSupportedDpiOpenArrayCast(op.getInput().getType(),
2054 op.getResult().getType())) {
2055 rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
2056 op, resultType, adaptor.getInput());
2057 return success();
2058 }
2059 if (hasOpenArrayBoundaryType(op.getInput().getType()) ||
2060 hasOpenArrayBoundaryType(op.getResult().getType())) {
2061 op.emitError("unsupported DPI open-array conversion from ")
2062 << op.getInput().getType() << " to " << op.getResult().getType();
2063 return failure();
2064 }
2065 return failure();
2066 }
2067
2068 Value input = rewriter.createOrFold<hw::BitcastOp>(
2069 loc, rewriter.getIntegerType(inputBw), adaptor.getInput());
2070 Value amount = adjustIntegerWidth(rewriter, input, resultBw, loc);
2071
2072 Value result =
2073 rewriter.createOrFold<hw::BitcastOp>(loc, resultType, amount);
2074 rewriter.replaceOp(op, result);
2075 return success();
2076 }
2077};
2078
2079template <typename SourceOp>
2080struct BitcastConversion : public OpConversionPattern<SourceOp> {
2082 using OpAdaptor = typename SourceOp::Adaptor;
2083 using ConversionPattern::typeConverter;
2084
2085 LogicalResult
2086 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
2087 ConversionPatternRewriter &rewriter) const override {
2088 auto type = typeConverter->convertType(op.getResult().getType());
2089 if (type == adaptor.getInput().getType())
2090 rewriter.replaceOp(op, adaptor.getInput());
2091 else
2092 rewriter.replaceOpWithNewOp<hw::BitcastOp>(op, type, adaptor.getInput());
2093 return success();
2094 }
2095};
2096
2097/// For casts that are automatically resolved by type conversion
2098template <typename SourceOp>
2099struct NoOpConversion : public OpConversionPattern<SourceOp> {
2101 using OpAdaptor = typename SourceOp::Adaptor;
2102 using ConversionPattern::typeConverter;
2103
2104 LogicalResult
2105 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
2106 ConversionPatternRewriter &rewriter) const override {
2107 rewriter.replaceOp(op, adaptor.getInput());
2108 return success();
2109 }
2110};
2111
2112struct TruncOpConversion : public OpConversionPattern<TruncOp> {
2113 using OpConversionPattern::OpConversionPattern;
2114
2115 LogicalResult
2116 matchAndRewrite(TruncOp op, OpAdaptor adaptor,
2117 ConversionPatternRewriter &rewriter) const override {
2118 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, adaptor.getInput(), 0,
2119 op.getType().getWidth());
2120 return success();
2121 }
2122};
2123
2124struct ZExtOpConversion : public OpConversionPattern<ZExtOp> {
2125 using OpConversionPattern::OpConversionPattern;
2126
2127 LogicalResult
2128 matchAndRewrite(ZExtOp op, OpAdaptor adaptor,
2129 ConversionPatternRewriter &rewriter) const override {
2130 auto targetWidth = op.getType().getWidth();
2131 auto inputWidth = op.getInput().getType().getWidth();
2132
2133 auto zeroExt = hw::ConstantOp::create(
2134 rewriter, op.getLoc(),
2135 rewriter.getIntegerType(targetWidth - inputWidth), 0);
2136
2137 rewriter.replaceOpWithNewOp<comb::ConcatOp>(
2138 op, ValueRange{zeroExt, adaptor.getInput()});
2139 return success();
2140 }
2141};
2142
2143struct SExtOpConversion : public OpConversionPattern<SExtOp> {
2144 using OpConversionPattern::OpConversionPattern;
2145
2146 LogicalResult
2147 matchAndRewrite(SExtOp op, OpAdaptor adaptor,
2148 ConversionPatternRewriter &rewriter) const override {
2149 auto type = typeConverter->convertType(op.getType());
2150 auto value =
2151 comb::createOrFoldSExt(rewriter, op.getLoc(), adaptor.getInput(), type);
2152 rewriter.replaceOp(op, value);
2153 return success();
2154 }
2155};
2156
2157struct SIntToRealOpConversion : public OpConversionPattern<SIntToRealOp> {
2158 using OpConversionPattern::OpConversionPattern;
2159
2160 LogicalResult
2161 matchAndRewrite(SIntToRealOp op, OpAdaptor adaptor,
2162 ConversionPatternRewriter &rewriter) const override {
2163 rewriter.replaceOpWithNewOp<arith::SIToFPOp>(
2164 op, typeConverter->convertType(op.getType()), adaptor.getInput());
2165 return success();
2166 }
2167};
2168
2169struct UIntToRealOpConversion : public OpConversionPattern<UIntToRealOp> {
2170 using OpConversionPattern::OpConversionPattern;
2171
2172 LogicalResult
2173 matchAndRewrite(UIntToRealOp op, OpAdaptor adaptor,
2174 ConversionPatternRewriter &rewriter) const override {
2175 rewriter.replaceOpWithNewOp<arith::UIToFPOp>(
2176 op, typeConverter->convertType(op.getType()), adaptor.getInput());
2177 return success();
2178 }
2179};
2180
2181struct IntToStringOpConversion : public OpConversionPattern<IntToStringOp> {
2182 using OpConversionPattern::OpConversionPattern;
2183
2184 LogicalResult
2185 matchAndRewrite(IntToStringOp op, OpAdaptor adaptor,
2186 ConversionPatternRewriter &rewriter) const override {
2187 rewriter.replaceOpWithNewOp<sim::IntToStringOp>(op, adaptor.getInput());
2188 return success();
2189 }
2190};
2191
2192struct StringToIntOpConversion : public OpConversionPattern<StringToIntOp> {
2193 using OpConversionPattern::OpConversionPattern;
2194
2195 LogicalResult
2196 matchAndRewrite(StringToIntOp op, OpAdaptor adaptor,
2197 ConversionPatternRewriter &rewriter) const override {
2198 Type resultTy = typeConverter->convertType(op.getType());
2199 if (!resultTy)
2200 return failure();
2201 rewriter.replaceOpWithNewOp<sim::StringToIntOp>(op, resultTy,
2202 adaptor.getInput());
2203 return success();
2204 }
2205};
2206
2207struct FormatStringToStringOpConversion
2208 : public OpConversionPattern<FormatStringToStringOp> {
2209 using OpConversionPattern::OpConversionPattern;
2210
2211 LogicalResult
2212 matchAndRewrite(FormatStringToStringOp op, OpAdaptor adaptor,
2213 ConversionPatternRewriter &rewriter) const override {
2214 rewriter.replaceOpWithNewOp<sim::FormatToStringOp>(op,
2215 adaptor.getFmtstring());
2216 return success();
2217 }
2218};
2219
2220struct RealToIntOpConversion : public OpConversionPattern<RealToIntOp> {
2221 using OpConversionPattern::OpConversionPattern;
2222
2223 LogicalResult
2224 matchAndRewrite(RealToIntOp op, OpAdaptor adaptor,
2225 ConversionPatternRewriter &rewriter) const override {
2226 rewriter.replaceOpWithNewOp<arith::FPToSIOp>(
2227 op, typeConverter->convertType(op.getType()), adaptor.getInput());
2228 return success();
2229 }
2230};
2231
2232struct ConvertRealOpConversion : public OpConversionPattern<ConvertRealOp> {
2233 using OpConversionPattern::OpConversionPattern;
2234
2235 LogicalResult
2236 matchAndRewrite(ConvertRealOp op, OpAdaptor adaptor,
2237 ConversionPatternRewriter &rewriter) const override {
2238 op.getInput().getType().getWidth() < op.getResult().getType().getWidth()
2239 ? rewriter.replaceOpWithNewOp<arith::ExtFOp>(
2240 op, typeConverter->convertType(op.getType()), adaptor.getInput())
2241 : rewriter.replaceOpWithNewOp<arith::TruncFOp>(
2242 op, typeConverter->convertType(op.getType()), adaptor.getInput());
2243 return success();
2244 }
2245};
2246
2247template <typename SourceOp>
2248struct RealBitcastOpConversion : public OpConversionPattern<SourceOp> {
2250 using OpAdaptor = typename SourceOp::Adaptor;
2251
2252 LogicalResult
2253 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
2254 ConversionPatternRewriter &rewriter) const override {
2255 Type resultTy =
2256 ConversionPattern::typeConverter->convertType(op.getResult().getType());
2257 rewriter.replaceOpWithNewOp<arith::BitcastOp>(op, resultTy,
2258 adaptor.getValue());
2259 return success();
2260 }
2261};
2262
2263//===----------------------------------------------------------------------===//
2264// Statement Conversion
2265//===----------------------------------------------------------------------===//
2266
2267struct HWInstanceOpConversion : public OpConversionPattern<hw::InstanceOp> {
2268 using OpConversionPattern::OpConversionPattern;
2269
2270 LogicalResult
2271 matchAndRewrite(hw::InstanceOp op, OpAdaptor adaptor,
2272 ConversionPatternRewriter &rewriter) const override {
2273 SmallVector<Type> convResTypes;
2274 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
2275 return failure();
2276
2277 rewriter.replaceOpWithNewOp<hw::InstanceOp>(
2278 op, convResTypes, op.getInstanceName(), op.getModuleName(),
2279 adaptor.getOperands(), op.getArgNames(),
2280 op.getResultNames(), /*Parameter*/
2281 rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr);
2282
2283 return success();
2284 }
2285};
2286
2287struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
2288 using OpConversionPattern::OpConversionPattern;
2289
2290 LogicalResult
2291 matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
2292 ConversionPatternRewriter &rewriter) const override {
2293 rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
2294 return success();
2295 }
2296};
2297
2298struct CallOpConversion : public OpConversionPattern<func::CallOp> {
2299 using OpConversionPattern::OpConversionPattern;
2300
2301 LogicalResult
2302 matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
2303 ConversionPatternRewriter &rewriter) const override {
2304 SmallVector<Type> convResTypes;
2305 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
2306 return failure();
2307 rewriter.replaceOpWithNewOp<func::CallOp>(
2308 op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
2309 return success();
2310 }
2311};
2312
2313struct FuncDPICallOpConversion
2314 : public OpConversionPattern<moore::FuncDPICallOp> {
2315 using OpConversionPattern::OpConversionPattern;
2316
2317 LogicalResult
2318 matchAndRewrite(moore::FuncDPICallOp op, OpAdaptor adaptor,
2319 ConversionPatternRewriter &rewriter) const override {
2320 SmallVector<Type> convResTypes;
2321 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
2322 return failure();
2323 rewriter.replaceOpWithNewOp<sim::DPICallOp>(
2324 op, convResTypes, op.getCalleeAttr(), /*clock=*/Value(),
2325 /*enable=*/Value(), adaptor.getInputs());
2326 return success();
2327 }
2328};
2329
2330struct DPIFuncOpConversion : public OpConversionPattern<moore::DPIFuncOp> {
2331 using OpConversionPattern::OpConversionPattern;
2332
2333 LogicalResult
2334 matchAndRewrite(moore::DPIFuncOp op, OpAdaptor adaptor,
2335 ConversionPatternRewriter &rewriter) const override {
2336 // Map Moore DPIArgDirection to sim::DPIDirection.
2337 auto toDPIDir = [](moore::DPIArgDirection dir) -> sim::DPIDirection {
2338 switch (dir) {
2339 case moore::DPIArgDirection::In:
2340 return sim::DPIDirection::Input;
2341 case moore::DPIArgDirection::Out:
2342 return sim::DPIDirection::Output;
2343 case moore::DPIArgDirection::InOut:
2344 return sim::DPIDirection::InOut;
2345 case moore::DPIArgDirection::Return:
2346 return sim::DPIDirection::Return;
2347 }
2348 llvm_unreachable("unknown DPIArgDirection");
2349 };
2350
2351 // Reconstruct sim::DPIFunctionType from Moore's argument arrays.
2352 auto dirs = op.getDpiArgDirs();
2353 auto names = op.getDpiArgNames();
2354 SmallVector<Type> argTypes;
2355 op.getDPIArgTypes(argTypes);
2356
2357 SmallVector<sim::DPIArgument> dpiArguments;
2358 for (auto [dirAttr, nameAttr, mooreType] :
2359 llvm::zip(dirs, names, argTypes)) {
2360 auto dir = toDPIDir(cast<moore::DPIArgDirectionAttr>(dirAttr).getValue());
2361 auto name = cast<StringAttr>(nameAttr);
2362 Type coreType = typeConverter->convertType(mooreType);
2363 if (!coreType)
2364 return op.emitOpError("argument '")
2365 << name << "' has unsupported type " << mooreType;
2366 dpiArguments.push_back({name, coreType, dir});
2367 }
2368
2369 auto coreDPIFuncType =
2370 sim::DPIFunctionType::get(rewriter.getContext(), dpiArguments);
2371 auto simFunc = sim::DPIFuncOp::create(
2372 rewriter, op.getLoc(), op.getSymNameAttr(), coreDPIFuncType,
2373 op.getArgumentLocsAttr(), op.getVerilogNameAttr());
2374 SymbolTable::setSymbolVisibility(simFunc,
2375 SymbolTable::getSymbolVisibility(op));
2376 rewriter.eraseOp(op);
2377 return success();
2378 }
2379};
2380
2381struct UnrealizedConversionCastConversion
2382 : public OpConversionPattern<UnrealizedConversionCastOp> {
2383 using OpConversionPattern::OpConversionPattern;
2384
2385 LogicalResult
2386 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
2387 ConversionPatternRewriter &rewriter) const override {
2388 SmallVector<Type> convResTypes;
2389 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
2390 return failure();
2391
2392 // Drop the cast if the operand and result types agree after type
2393 // conversion.
2394 if (convResTypes == adaptor.getOperands().getTypes()) {
2395 rewriter.replaceOp(op, adaptor.getOperands());
2396 return success();
2397 }
2398
2399 rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
2400 op, convResTypes, adaptor.getOperands());
2401 return success();
2402 }
2403};
2404
2405struct ShlOpConversion : public OpConversionPattern<ShlOp> {
2406 using OpConversionPattern::OpConversionPattern;
2407
2408 LogicalResult
2409 matchAndRewrite(ShlOp op, OpAdaptor adaptor,
2410 ConversionPatternRewriter &rewriter) const override {
2411 Type resultType = typeConverter->convertType(op.getResult().getType());
2412
2413 // Comb shift operations require the same bit-width for value and amount
2414 Value amount =
2415 adjustIntegerWidth(rewriter, adaptor.getAmount(),
2416 resultType.getIntOrFloatBitWidth(), op->getLoc());
2417 rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
2418 amount, false);
2419 return success();
2420 }
2421};
2422
2423struct ShrOpConversion : public OpConversionPattern<ShrOp> {
2424 using OpConversionPattern::OpConversionPattern;
2425
2426 LogicalResult
2427 matchAndRewrite(ShrOp op, OpAdaptor adaptor,
2428 ConversionPatternRewriter &rewriter) const override {
2429 Type resultType = typeConverter->convertType(op.getResult().getType());
2430
2431 // Comb shift operations require the same bit-width for value and amount
2432 Value amount =
2433 adjustIntegerWidth(rewriter, adaptor.getAmount(),
2434 resultType.getIntOrFloatBitWidth(), op->getLoc());
2435 rewriter.replaceOpWithNewOp<comb::ShrUOp>(
2436 op, resultType, adaptor.getValue(), amount, false);
2437 return success();
2438 }
2439};
2440
2441struct PowUOpConversion : public OpConversionPattern<PowUOp> {
2442 using OpConversionPattern::OpConversionPattern;
2443
2444 LogicalResult
2445 matchAndRewrite(PowUOp op, OpAdaptor adaptor,
2446 ConversionPatternRewriter &rewriter) const override {
2447 Type resultType = typeConverter->convertType(op.getResult().getType());
2448
2449 Location loc = op->getLoc();
2450
2451 Value zeroVal = hw::ConstantOp::create(rewriter, loc, APInt(1, 0));
2452 // zero extend both LHS & RHS to ensure the unsigned integers are
2453 // interpreted correctly when calculating power
2454 auto lhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getLhs());
2455 auto rhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getRhs());
2456
2457 // lower the exponentiation via MLIR's math dialect
2458 auto pow = mlir::math::IPowIOp::create(rewriter, loc, lhs, rhs);
2459
2460 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, pow, 0);
2461 return success();
2462 }
2463};
2464
2465struct PowSOpConversion : public OpConversionPattern<PowSOp> {
2466 using OpConversionPattern::OpConversionPattern;
2467
2468 LogicalResult
2469 matchAndRewrite(PowSOp op, OpAdaptor adaptor,
2470 ConversionPatternRewriter &rewriter) const override {
2471 Type resultType = typeConverter->convertType(op.getResult().getType());
2472
2473 // utilize MLIR math dialect's math.ipowi to handle the exponentiation of
2474 // expression
2475 rewriter.replaceOpWithNewOp<mlir::math::IPowIOp>(
2476 op, resultType, adaptor.getLhs(), adaptor.getRhs());
2477 return success();
2478 }
2479};
2480
2481struct Clog2BIOpConversion : public OpConversionPattern<Clog2BIOp> {
2482 using OpConversionPattern::OpConversionPattern;
2483
2484 LogicalResult
2485 matchAndRewrite(Clog2BIOp op, OpAdaptor adaptor,
2486 ConversionPatternRewriter &rewriter) const override {
2487 Type resultType = typeConverter->convertType(op.getResult().getType());
2488 Location loc = op.getLoc();
2489 unsigned width = resultType.getIntOrFloatBitWidth();
2490 Value value = adaptor.getValue();
2491
2492 // Ceiling of log2 can be computed as follows:
2493 // if (x == 0) return 0;
2494 // else bitWidth(x) - countLeadingZeros(x - 1);
2495 Value zero = hw::ConstantOp::create(rewriter, loc, APInt(width, 0));
2496 Value one = hw::ConstantOp::create(rewriter, loc, APInt(width, 1));
2497 Value bitWidth = hw::ConstantOp::create(rewriter, loc, APInt(width, width));
2498
2499 Value valueMinusOne = comb::SubOp::create(rewriter, loc, value, one, false);
2500 Value clz =
2501 mlir::math::CountLeadingZerosOp::create(rewriter, loc, valueMinusOne);
2502 Value bitLength = comb::SubOp::create(rewriter, loc, bitWidth, clz, false);
2503
2504 Value isZero = comb::ICmpOp::create(rewriter, loc, comb::ICmpPredicate::eq,
2505 value, zero, false);
2506 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, isZero, zero, bitLength,
2507 false);
2508 return success();
2509 }
2510};
2511
2512struct AShrOpConversion : public OpConversionPattern<AShrOp> {
2513 using OpConversionPattern::OpConversionPattern;
2514
2515 LogicalResult
2516 matchAndRewrite(AShrOp op, OpAdaptor adaptor,
2517 ConversionPatternRewriter &rewriter) const override {
2518 Type resultType = typeConverter->convertType(op.getResult().getType());
2519
2520 // Comb shift operations require the same bit-width for value and amount
2521 Value amount =
2522 adjustIntegerWidth(rewriter, adaptor.getAmount(),
2523 resultType.getIntOrFloatBitWidth(), op->getLoc());
2524 rewriter.replaceOpWithNewOp<comb::ShrSOp>(
2525 op, resultType, adaptor.getValue(), amount, false);
2526 return success();
2527 }
2528};
2529
2530struct ReadOpConversion : public OpConversionPattern<ReadOp> {
2531 using OpConversionPattern::OpConversionPattern;
2532
2533 LogicalResult
2534 matchAndRewrite(ReadOp op, OpAdaptor adaptor,
2535 ConversionPatternRewriter &rewriter) const override {
2536 rewriter.replaceOpWithNewOp<llhd::ProbeOp>(op, adaptor.getInput());
2537 return success();
2538 }
2539};
2540
2541struct AssignedVariableOpConversion
2542 : public OpConversionPattern<AssignedVariableOp> {
2543 using OpConversionPattern::OpConversionPattern;
2544
2545 LogicalResult
2546 matchAndRewrite(AssignedVariableOp op, OpAdaptor adaptor,
2547 ConversionPatternRewriter &rewriter) const override {
2548 rewriter.replaceOpWithNewOp<hw::WireOp>(op, adaptor.getInput(),
2549 adaptor.getNameAttr());
2550 return success();
2551 }
2552};
2553
2554// Blocking and continuous assignments get a 0ns 0d 1e delay.
2555static llhd::TimeAttr
2556getBlockingOrContinuousAssignDelay(mlir::MLIRContext *context) {
2557 return llhd::TimeAttr::get(context, 0U, "ns", 0, 1);
2558}
2559
2560template <typename OpTy>
2561struct AssignOpConversion : public OpConversionPattern<OpTy> {
2563 using OpAdaptor = typename OpTy::Adaptor;
2564
2565 LogicalResult
2566 matchAndRewrite(OpTy op, OpAdaptor adaptor,
2567 ConversionPatternRewriter &rewriter) const override {
2568 // Determine the delay for the assignment.
2569 Value delay;
2570 if constexpr (std::is_same_v<OpTy, ContinuousAssignOp> ||
2571 std::is_same_v<OpTy, BlockingAssignOp>) {
2572 delay = llhd::ConstantTimeOp::create(
2573 rewriter, op->getLoc(),
2574 getBlockingOrContinuousAssignDelay(op->getContext()));
2575 } else if constexpr (std::is_same_v<OpTy, NonBlockingAssignOp>) {
2576 // Non-blocking assignments get a 0ns 1d 0e delay.
2577 delay = llhd::ConstantTimeOp::create(
2578 rewriter, op->getLoc(),
2579 llhd::TimeAttr::get(op->getContext(), 0U, "ns", 1, 0));
2580 } else {
2581 // Delayed assignments have a delay operand.
2582 delay = adaptor.getDelay();
2583 }
2584
2585 rewriter.replaceOpWithNewOp<llhd::DriveOp>(
2586 op, adaptor.getDst(), adaptor.getSrc(), delay, Value{});
2587 return success();
2588 }
2589};
2590
2591struct ConditionalOpConversion : public OpConversionPattern<ConditionalOp> {
2592 using OpConversionPattern::OpConversionPattern;
2593
2594 LogicalResult
2595 matchAndRewrite(ConditionalOp op, OpAdaptor adaptor,
2596 ConversionPatternRewriter &rewriter) const override {
2597 // TODO: This lowering is only correct if the condition is two-valued. If
2598 // the condition is X or Z, both branches of the conditional must be
2599 // evaluated and merged with the appropriate lookup table. See documentation
2600 // for `ConditionalOp`.
2601 auto type = typeConverter->convertType(op.getType());
2602
2603 auto hasNoWriteEffect = [](Region &region) {
2604 auto result = region.walk([](Operation *operation) {
2605 if (auto memOp = dyn_cast<MemoryEffectOpInterface>(operation))
2606 if (!memOp.hasEffect<MemoryEffects::Write>() &&
2607 !memOp.hasEffect<MemoryEffects::Free>())
2608 return WalkResult::advance();
2609
2610 if (operation->hasTrait<OpTrait::HasRecursiveMemoryEffects>())
2611 return WalkResult::advance();
2612
2613 return WalkResult::interrupt();
2614 });
2615 return !result.wasInterrupted();
2616 };
2617
2618 if (hasNoWriteEffect(op.getTrueRegion()) &&
2619 hasNoWriteEffect(op.getFalseRegion())) {
2620 Operation *trueTerm = op.getTrueRegion().front().getTerminator();
2621 Operation *falseTerm = op.getFalseRegion().front().getTerminator();
2622
2623 rewriter.inlineBlockBefore(&op.getTrueRegion().front(), op);
2624 rewriter.inlineBlockBefore(&op.getFalseRegion().front(), op);
2625
2626 Value convTrueVal = typeConverter->materializeTargetConversion(
2627 rewriter, op.getLoc(), type, trueTerm->getOperand(0));
2628 Value convFalseVal = typeConverter->materializeTargetConversion(
2629 rewriter, op.getLoc(), type, falseTerm->getOperand(0));
2630
2631 rewriter.eraseOp(trueTerm);
2632 rewriter.eraseOp(falseTerm);
2633
2634 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, adaptor.getCondition(),
2635 convTrueVal, convFalseVal);
2636 return success();
2637 }
2638
2639 auto ifOp =
2640 scf::IfOp::create(rewriter, op.getLoc(), type, adaptor.getCondition());
2641 rewriter.inlineRegionBefore(op.getTrueRegion(), ifOp.getThenRegion(),
2642 ifOp.getThenRegion().end());
2643 rewriter.inlineRegionBefore(op.getFalseRegion(), ifOp.getElseRegion(),
2644 ifOp.getElseRegion().end());
2645 rewriter.replaceOp(op, ifOp);
2646 return success();
2647 }
2648};
2649
2650struct YieldOpConversion : public OpConversionPattern<YieldOp> {
2651 using OpConversionPattern::OpConversionPattern;
2652
2653 LogicalResult
2654 matchAndRewrite(YieldOp op, OpAdaptor adaptor,
2655 ConversionPatternRewriter &rewriter) const override {
2656 Operation *parent = op->getParentOp();
2657 if (isa<llhd::GlobalSignalOp>(parent))
2658 rewriter.replaceOpWithNewOp<llhd::YieldOp>(op, adaptor.getResult());
2659 else if (isa<scf::ExecuteRegionOp, scf::ForOp, scf::IfOp,
2660 scf::IndexSwitchOp, scf::WhileOp>(parent))
2661 rewriter.replaceOpWithNewOp<scf::YieldOp>(op, adaptor.getResult());
2662 else
2663 return rewriter.notifyMatchFailure(
2664 op, "yield parent has not been converted to a legal region op yet");
2665 return success();
2666 }
2667};
2668
2669template <typename SourceOp>
2670struct InPlaceOpConversion : public OpConversionPattern<SourceOp> {
2672 using OpAdaptor = typename SourceOp::Adaptor;
2673
2674 LogicalResult
2675 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
2676 ConversionPatternRewriter &rewriter) const override {
2677 rewriter.modifyOpInPlace(op,
2678 [&]() { op->setOperands(adaptor.getOperands()); });
2679 return success();
2680 }
2681};
2682
2683template <typename MooreOpTy, typename VerifOpTy>
2684struct AssertLikeOpConversion : public OpConversionPattern<MooreOpTy> {
2686 using OpAdaptor = typename MooreOpTy::Adaptor;
2687
2688 LogicalResult
2689 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
2690 ConversionPatternRewriter &rewriter) const override {
2691 StringAttr label =
2692 op.getLabel().has_value()
2693 ? StringAttr::get(op->getContext(), op.getLabel().value())
2694 : StringAttr::get(op->getContext());
2695 rewriter.replaceOpWithNewOp<VerifOpTy>(op, adaptor.getCond(), mlir::Value(),
2696 label);
2697 return success();
2698 }
2699};
2700
2701//===----------------------------------------------------------------------===//
2702// Format String Conversion
2703//===----------------------------------------------------------------------===//
2704
2705struct FormatLiteralOpConversion : public OpConversionPattern<FormatLiteralOp> {
2706 using OpConversionPattern::OpConversionPattern;
2707
2708 LogicalResult
2709 matchAndRewrite(FormatLiteralOp op, OpAdaptor adaptor,
2710 ConversionPatternRewriter &rewriter) const override {
2711 rewriter.replaceOpWithNewOp<sim::FormatLiteralOp>(op, adaptor.getLiteral());
2712 return success();
2713 }
2714};
2715
2716struct FormatStringOpConversion : public OpConversionPattern<FormatStringOp> {
2717 using OpConversionPattern::OpConversionPattern;
2718
2719 LogicalResult
2720 matchAndRewrite(FormatStringOp op, OpAdaptor adaptor,
2721 ConversionPatternRewriter &rewriter) const override {
2722 char padChar =
2723 op.getPadding().value_or(IntPadding::Space) == IntPadding::Space ? 32
2724 : 48;
2725 IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar);
2726 auto widthAttr = adaptor.getWidthAttr();
2727
2728 bool isLeftAligned =
2729 op.getAlignment().value_or(IntAlign::Right) == IntAlign::Left;
2730 BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2731
2732 rewriter.replaceOpWithNewOp<sim::FormatStringOp>(
2733 op, adaptor.getString(), isLeftAlignedAttr, padCharAttr, widthAttr);
2734 return success();
2735 }
2736};
2737
2738struct FormatConcatOpConversion : public OpConversionPattern<FormatConcatOp> {
2739 using OpConversionPattern::OpConversionPattern;
2740
2741 LogicalResult
2742 matchAndRewrite(FormatConcatOp op, OpAdaptor adaptor,
2743 ConversionPatternRewriter &rewriter) const override {
2744 rewriter.replaceOpWithNewOp<sim::FormatStringConcatOp>(op,
2745 adaptor.getInputs());
2746 return success();
2747 }
2748};
2749
2750struct FormatHierPathOpConversion
2751 : public OpConversionPattern<FormatHierPathOp> {
2752 using OpConversionPattern::OpConversionPattern;
2753
2754 LogicalResult
2755 matchAndRewrite(FormatHierPathOp op, OpAdaptor adaptor,
2756 ConversionPatternRewriter &rewriter) const override {
2757 rewriter.replaceOpWithNewOp<sim::FormatHierPathOp>(op,
2758 adaptor.getUseEscapes());
2759 return success();
2760 }
2761};
2762
2763struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
2764 using OpConversionPattern::OpConversionPattern;
2765
2766 LogicalResult
2767 matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
2768 ConversionPatternRewriter &rewriter) const override {
2769
2770 char padChar = adaptor.getPadding() == IntPadding::Space ? 32 : 48;
2771 IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar);
2772 auto widthAttr = adaptor.getSpecifierWidthAttr();
2773
2774 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2775 BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2776
2777 switch (op.getFormat()) {
2778 case IntFormat::Decimal:
2779 rewriter.replaceOpWithNewOp<sim::FormatDecOp>(
2780 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr,
2781 adaptor.getIsSignedAttr());
2782 return success();
2783 case IntFormat::Binary:
2784 rewriter.replaceOpWithNewOp<sim::FormatBinOp>(
2785 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2786 return success();
2787 case IntFormat::Octal:
2788 rewriter.replaceOpWithNewOp<sim::FormatOctOp>(
2789 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2790 return success();
2791 case IntFormat::HexLower:
2792 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2793 op, adaptor.getValue(), rewriter.getBoolAttr(false),
2794 isLeftAlignedAttr, padCharAttr, widthAttr);
2795 return success();
2796 case IntFormat::HexUpper:
2797 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2798 op, adaptor.getValue(), rewriter.getBoolAttr(true), isLeftAlignedAttr,
2799 padCharAttr, widthAttr);
2800 return success();
2801 }
2802 return rewriter.notifyMatchFailure(op, "unsupported int format");
2803 }
2804};
2805
2806struct FormatRealOpConversion : public OpConversionPattern<FormatRealOp> {
2807 using OpConversionPattern::OpConversionPattern;
2808
2809 LogicalResult
2810 matchAndRewrite(FormatRealOp op, OpAdaptor adaptor,
2811 ConversionPatternRewriter &rewriter) const override {
2812 auto fracDigitsAttr = adaptor.getFracDigitsAttr();
2813
2814 auto fieldWidthAttr = adaptor.getFieldWidthAttr();
2815 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2816 mlir::BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2817
2818 switch (op.getFormat()) {
2819 case RealFormat::General:
2820 rewriter.replaceOpWithNewOp<sim::FormatGeneralOp>(
2821 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2822 fracDigitsAttr);
2823 return success();
2824 case RealFormat::Float:
2825 rewriter.replaceOpWithNewOp<sim::FormatFloatOp>(
2826 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2827 fracDigitsAttr);
2828 return success();
2829 case RealFormat::Exponential:
2830 rewriter.replaceOpWithNewOp<sim::FormatScientificOp>(
2831 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2832 fracDigitsAttr);
2833 return success();
2834 }
2835 }
2836};
2837
2838struct FormatCharOpConversion
2839 : public OpConversionPattern<moore::FormatCharOp> {
2840 using OpConversionPattern::OpConversionPattern;
2841 LogicalResult
2842 matchAndRewrite(moore::FormatCharOp op, OpAdaptor adaptor,
2843 ConversionPatternRewriter &rewriter) const override {
2844 rewriter.replaceOpWithNewOp<sim::FormatCharOp>(op, adaptor.getValue());
2845 return success();
2846 }
2847};
2848
2849struct StringLenOpConversion : public OpConversionPattern<StringLenOp> {
2850 using OpConversionPattern::OpConversionPattern;
2851
2852 LogicalResult
2853 matchAndRewrite(StringLenOp op, OpAdaptor adaptor,
2854 ConversionPatternRewriter &rewriter) const override {
2855 rewriter.replaceOpWithNewOp<sim::StringLengthOp>(op, adaptor.getStr());
2856 return success();
2857 }
2858};
2859
2860struct StringConcatOpConversion : public OpConversionPattern<StringConcatOp> {
2861 using OpConversionPattern::OpConversionPattern;
2862
2863 LogicalResult
2864 matchAndRewrite(StringConcatOp op, OpAdaptor adaptor,
2865 ConversionPatternRewriter &rewriter) const override {
2866 rewriter.replaceOpWithNewOp<sim::StringConcatOp>(op, adaptor.getInputs());
2867 return success();
2868 }
2869};
2870
2871struct StringGetOpConversion : public OpConversionPattern<StringGetOp> {
2872 using OpConversionPattern::OpConversionPattern;
2873
2874 LogicalResult
2875 matchAndRewrite(StringGetOp op, OpAdaptor adaptor,
2876 ConversionPatternRewriter &rewriter) const override {
2877 rewriter.replaceOpWithNewOp<sim::StringGetOp>(op, adaptor.getStr(),
2878 adaptor.getIndex());
2879 return success();
2880 }
2881};
2882
2883struct QueueSizeBIOpConversion : public OpConversionPattern<QueueSizeBIOp> {
2884 using OpConversionPattern::OpConversionPattern;
2885
2886 LogicalResult
2887 matchAndRewrite(QueueSizeBIOp op, OpAdaptor adaptor,
2888 ConversionPatternRewriter &rewriter) const override {
2889 rewriter.replaceOpWithNewOp<sim::QueueSizeOp>(op, adaptor.getQueue());
2890 return success();
2891 }
2892};
2893
2894struct DynQueueExtractOpConversion
2895 : public OpConversionPattern<DynQueueExtractOp> {
2896 using OpConversionPattern::OpConversionPattern;
2897
2898 LogicalResult
2899 matchAndRewrite(DynQueueExtractOp op, OpAdaptor adaptor,
2900 ConversionPatternRewriter &rewriter) const override {
2901 bool isSingleElementExtract =
2902 op.getInput().getType().getElementType() == op.getResult().getType();
2903
2904 if (isSingleElementExtract) {
2905 rewriter.replaceOpWithNewOp<sim::QueueGetOp>(op, adaptor.getInput(),
2906 adaptor.getLowerIdx());
2907 } else {
2908 rewriter.replaceOpWithNewOp<sim::QueueSliceOp>(
2909 op, adaptor.getInput(), adaptor.getLowerIdx(), adaptor.getUpperIdx());
2910 }
2911
2912 return success();
2913 }
2914};
2915
2916// Given a reference `ref` to some Moore type, this function emits a
2917// `ProbeOp` to read the contained value, then passes it to the function `func`.
2918// It finally emits a `DriveOp` to write the result of the function back to
2919// the referenced signal.
2920//
2921// This is useful for converting impure operations (such as the Moore ops for
2922// manipulating queues) into pure operations. (Which do not mutate the source
2923// value, instead returning a modified value.)
2924static void
2925probeRefAndDriveWithResult(OpBuilder &builder, Location loc, Value ref,
2926 const std::function<Value(Value)> &func) {
2927
2928 Value v = llhd::ProbeOp::create(builder, loc, ref);
2929
2930 // Drive using the same delay as a blocking assignment
2931 Value delay = llhd::ConstantTimeOp::create(
2932 builder, loc, getBlockingOrContinuousAssignDelay(builder.getContext()));
2933
2934 llhd::DriveOp::create(builder, loc, ref, func(v), delay, Value{});
2935}
2936
2937struct QueuePushBackOpConversion : public OpConversionPattern<QueuePushBackOp> {
2938 using OpConversionPattern::OpConversionPattern;
2939
2940 LogicalResult
2941 matchAndRewrite(QueuePushBackOp op, OpAdaptor adaptor,
2942 ConversionPatternRewriter &rewriter) const override {
2943 probeRefAndDriveWithResult(
2944 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2945 return sim::QueuePushBackOp::create(rewriter, op->getLoc(), queue,
2946 adaptor.getElement());
2947 });
2948
2949 rewriter.eraseOp(op);
2950 return success();
2951 }
2952};
2953
2954struct QueuePushFrontOpConversion
2955 : public OpConversionPattern<QueuePushFrontOp> {
2956 using OpConversionPattern::OpConversionPattern;
2957
2958 LogicalResult
2959 matchAndRewrite(QueuePushFrontOp op, OpAdaptor adaptor,
2960 ConversionPatternRewriter &rewriter) const override {
2961
2962 probeRefAndDriveWithResult(
2963 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2964 return sim::QueuePushFrontOp::create(rewriter, op->getLoc(), queue,
2965 adaptor.getElement());
2966 });
2967
2968 rewriter.eraseOp(op);
2969 return success();
2970 }
2971};
2972
2973struct QueuePopBackOpConversion : public OpConversionPattern<QueuePopBackOp> {
2974 using OpConversionPattern::OpConversionPattern;
2975
2976 LogicalResult
2977 matchAndRewrite(QueuePopBackOp op, OpAdaptor adaptor,
2978 ConversionPatternRewriter &rewriter) const override {
2979 Value popped;
2980 probeRefAndDriveWithResult(
2981 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2982 auto popBack =
2983 sim::QueuePopBackOp::create(rewriter, op->getLoc(), queue);
2984 popped = popBack.getPopped();
2985 return popBack.getOutQueue();
2986 });
2987 rewriter.replaceOp(op, popped);
2988
2989 return success();
2990 }
2991};
2992
2993struct QueuePopFrontOpConversion : public OpConversionPattern<QueuePopFrontOp> {
2994 using OpConversionPattern::OpConversionPattern;
2995
2996 LogicalResult
2997 matchAndRewrite(QueuePopFrontOp op, OpAdaptor adaptor,
2998 ConversionPatternRewriter &rewriter) const override {
2999 Value popped;
3000 probeRefAndDriveWithResult(
3001 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
3002 auto popFront =
3003 sim::QueuePopFrontOp::create(rewriter, op->getLoc(), queue);
3004 popped = popFront.getPopped();
3005 return popFront.getOutQueue();
3006 });
3007 rewriter.replaceOp(op, popped);
3008
3009 return success();
3010 }
3011};
3012
3013struct QueueClearOpConversion : public OpConversionPattern<QueueClearOp> {
3014 using OpConversionPattern::OpConversionPattern;
3015
3016 LogicalResult
3017 matchAndRewrite(QueueClearOp op, OpAdaptor adaptor,
3018 ConversionPatternRewriter &rewriter) const override {
3019 auto refType = cast<llhd::RefType>(adaptor.getQueue().getType());
3020 auto queueType = refType.getNestedType();
3021 Value emptyQueue =
3022 sim::QueueEmptyOp::create(rewriter, op->getLoc(), queueType);
3023
3024 // Replace with an assignment to an empty queue
3025 Value delay = llhd::ConstantTimeOp::create(
3026 rewriter, op.getLoc(),
3027 getBlockingOrContinuousAssignDelay(rewriter.getContext()));
3028
3029 llhd::DriveOp::create(rewriter, op.getLoc(), adaptor.getQueue(), emptyQueue,
3030 delay, Value{});
3031
3032 rewriter.eraseOp(op);
3033 return success();
3034 }
3035};
3036
3037struct QueueInsertOpConversion : public OpConversionPattern<QueueInsertOp> {
3038 using OpConversionPattern::OpConversionPattern;
3039
3040 LogicalResult
3041 matchAndRewrite(QueueInsertOp op, OpAdaptor adaptor,
3042 ConversionPatternRewriter &rewriter) const override {
3043 probeRefAndDriveWithResult(
3044 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
3045 auto insert =
3046 sim::QueueInsertOp::create(rewriter, op->getLoc(), queue,
3047 adaptor.getIndex(), adaptor.getItem());
3048
3049 return insert.getOutQueue();
3050 });
3051 rewriter.eraseOp(op);
3052
3053 return success();
3054 }
3055};
3056
3057struct QueueDeleteOpConversion : public OpConversionPattern<QueueDeleteOp> {
3058 using OpConversionPattern::OpConversionPattern;
3059
3060 LogicalResult
3061 matchAndRewrite(QueueDeleteOp op, OpAdaptor adaptor,
3062 ConversionPatternRewriter &rewriter) const override {
3063 probeRefAndDriveWithResult(
3064 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
3065 auto delOp = sim::QueueDeleteOp::create(rewriter, op->getLoc(), queue,
3066 adaptor.getIndex());
3067
3068 return delOp.getOutQueue();
3069 });
3070 rewriter.eraseOp(op);
3071
3072 return success();
3073 };
3074};
3075
3076struct QueueResizeOpConversion : public OpConversionPattern<QueueResizeOp> {
3077 using OpConversionPattern::OpConversionPattern;
3078
3079 LogicalResult
3080 matchAndRewrite(QueueResizeOp op, OpAdaptor adaptor,
3081 ConversionPatternRewriter &rewriter) const override {
3082
3083 rewriter.replaceOpWithNewOp<sim::QueueResizeOp>(
3084 op, getTypeConverter()->convertType(op.getResult().getType()),
3085 adaptor.getInput());
3086 return success();
3087 }
3088};
3089
3090struct QueueSetOpConversion : public OpConversionPattern<QueueSetOp> {
3091 using OpConversionPattern::OpConversionPattern;
3092 LogicalResult
3093 matchAndRewrite(QueueSetOp op, OpAdaptor adaptor,
3094 ConversionPatternRewriter &rewriter) const override {
3095 probeRefAndDriveWithResult(
3096 rewriter, op->getLoc(), adaptor.getQueue(), [&](Value queue) {
3097 auto setOp =
3098 sim::QueueSetOp::create(rewriter, op.getLoc(), queue,
3099 adaptor.getIndex(), adaptor.getItem());
3100 return setOp.getOutQueue();
3101 });
3102 rewriter.eraseOp(op);
3103 return success();
3104 }
3105};
3106
3107// SystemVerilog unpacked array elements may be of any type. Bitcast handles
3108// statically-sized elements; reals bypass it so NaN/signed zero still compare
3109// correctly. Everything else recurses or dispatches to the matching comparison
3110// op.
3111static Value buildUArrayElementEq(ConversionPatternRewriter &rewriter,
3112 Location loc, Value lhs, Value rhs, Type type,
3113 UArrayCmpPredicate pred) {
3114 bool isEq = pred == UArrayCmpPredicate::eq;
3115
3116 if (isa<mlir::FloatType>(type))
3117 return arith::CmpFOp::create(
3118 rewriter, loc,
3119 isEq ? arith::CmpFPredicate::OEQ : arith::CmpFPredicate::UNE, lhs, rhs);
3120
3121 if (int64_t width = hw::getBitWidth(type); width != -1) {
3122 auto intTy = rewriter.getIntegerType(width);
3123 Value lhsInt = hw::BitcastOp::create(rewriter, loc, intTy, lhs);
3124 Value rhsInt = hw::BitcastOp::create(rewriter, loc, intTy, rhs);
3125 return comb::ICmpOp::create(rewriter, loc,
3126 isEq ? ICmpPredicate::eq : ICmpPredicate::ne,
3127 lhsInt, rhsInt);
3128 }
3129
3130 if (isa<sim::DynamicStringType>(type))
3131 return sim::StringCmpOp::create(rewriter, loc,
3132 isEq ? sim::StringCmpPredicate::eq
3133 : sim::StringCmpPredicate::ne,
3134 lhs, rhs);
3135
3136 if (isa<sim::QueueType>(type)) {
3137 auto pred = sim::UArrayCmpPredicateAttr::get(
3138 rewriter.getContext(),
3139 isEq ? sim::UArrayCmpPredicate::eq : sim::UArrayCmpPredicate::ne);
3140 return sim::QueueCmpOp::create(rewriter, loc, pred, lhs, rhs);
3141 }
3142
3143 if (isa<sim::AssocArrayType>(type)) {
3144 auto pred = sim::UArrayCmpPredicateAttr::get(
3145 rewriter.getContext(),
3146 isEq ? sim::UArrayCmpPredicate::eq : sim::UArrayCmpPredicate::ne);
3147 return sim::AssocArrayCmpOp::create(rewriter, loc, pred, lhs, rhs);
3148 }
3149
3150 auto arrayTy = dyn_cast<hw::ArrayType>(type);
3151 if (!arrayTy)
3152 return {};
3153
3154 unsigned size = arrayTy.getNumElements();
3155 if (size == 0)
3156 return hw::ConstantOp::create(rewriter, loc, APInt(1, isEq ? 1 : 0));
3157
3158 unsigned idxWidth = size == 1 ? 1 : llvm::Log2_64_Ceil(size);
3159 auto idxTy = rewriter.getIntegerType(idxWidth);
3160
3161 SmallVector<Value> elemEqs;
3162 elemEqs.reserve(size);
3163 for (unsigned i = 0; i < size; ++i) {
3164 Value idx = hw::ConstantOp::create(rewriter, loc, idxTy, i);
3165 Value lhsElem = hw::ArrayGetOp::create(rewriter, loc, lhs, idx);
3166 Value rhsElem = hw::ArrayGetOp::create(rewriter, loc, rhs, idx);
3167 Value elemEq = buildUArrayElementEq(rewriter, loc, lhsElem, rhsElem,
3168 arrayTy.getElementType(), pred);
3169 if (!elemEq)
3170 return {};
3171 elemEqs.push_back(elemEq);
3172 }
3173
3174 return (isEq ? comb::AndOp::create(rewriter, loc, elemEqs, /*twoState=*/true)
3175 .getResult()
3176 : comb::OrOp::create(rewriter, loc, elemEqs, /*twoState=*/true)
3177 .getResult());
3178}
3179
3180struct QueueCmpOpConversion : public OpConversionPattern<QueueCmpOp> {
3181 using OpConversionPattern::OpConversionPattern;
3182
3183 LogicalResult
3184 matchAndRewrite(QueueCmpOp op, OpAdaptor adaptor,
3185 ConversionPatternRewriter &rewriter) const override {
3186 // Per IEEE 1800-2017 7.4.2, queues are themselves a form of unpacked array,
3187 // so this reuses the same element-comparison dispatch as
3188 // UArrayCmpOpConversion (see buildUArrayElementEq), for a QueueType operand
3189 // it resolves directly to `sim.queue.cmp`.
3190 Value lhs = adaptor.getLhs();
3191 Value result =
3192 buildUArrayElementEq(rewriter, op.getLoc(), lhs, adaptor.getRhs(),
3193 lhs.getType(), op.getPredicate());
3194 rewriter.replaceOp(op, result);
3195 return success();
3196 }
3197};
3198
3199struct QueueFromUnpackedArrayOpConversion
3200 : public OpConversionPattern<QueueFromUnpackedArrayOp> {
3201 using OpConversionPattern::OpConversionPattern;
3202
3203 LogicalResult
3204 matchAndRewrite(QueueFromUnpackedArrayOp op, OpAdaptor adaptor,
3205 ConversionPatternRewriter &rewriter) const override {
3206 rewriter.replaceOpWithNewOp<sim::QueueFromArrayOp>(
3207 op, getTypeConverter()->convertType(op.getResult().getType()),
3208 adaptor.getInput());
3209 return success();
3210 }
3211};
3212
3213struct QueueConcatOpConversion : public OpConversionPattern<QueueConcatOp> {
3214 using OpConversionPattern::OpConversionPattern;
3215
3216 LogicalResult
3217 matchAndRewrite(QueueConcatOp op, OpAdaptor adaptor,
3218 ConversionPatternRewriter &rewriter) const override {
3219 rewriter.replaceOpWithNewOp<sim::QueueConcatOp>(
3220 op, getTypeConverter()->convertType(op.getResult().getType()),
3221 adaptor.getInputs());
3222 return success();
3223 }
3224};
3225
3226struct AssocArrayExtractOpConversion
3227 : public OpConversionPattern<AssocArrayExtractOp> {
3228 using OpConversionPattern::OpConversionPattern;
3229 LogicalResult
3230 matchAndRewrite(AssocArrayExtractOp op, OpAdaptor adaptor,
3231 ConversionPatternRewriter &rewriter) const override {
3232 Type resultType = getTypeConverter()->convertType(op.getResult().getType());
3233 if (!resultType)
3234 return failure();
3235
3236 Value zero = createZeroValue(resultType, op.getLoc(), rewriter);
3237 if (!zero)
3238 return failure();
3239 Value raw =
3240 sim::AssocArrayGetOp::create(rewriter, op.getLoc(), resultType,
3241 adaptor.getInput(), adaptor.getIndex());
3242 Value exists = sim::AssocArrayExistsOp::create(
3243 rewriter, op.getLoc(), adaptor.getInput(), adaptor.getIndex());
3244 Value zeroI32 = hw::ConstantOp::create(rewriter, op.getLoc(), APInt(32, 0));
3245 Value existsBit = comb::ICmpOp::create(
3246 rewriter, op.getLoc(), comb::ICmpPredicate::ne, exists, zeroI32, false);
3247 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, existsBit, raw, zero, false);
3248 return success();
3249 }
3250};
3251
3252struct UArrayCmpOpConversion : public OpConversionPattern<UArrayCmpOp> {
3253 using OpConversionPattern::OpConversionPattern;
3254 LogicalResult
3255 matchAndRewrite(UArrayCmpOp op, OpAdaptor adaptor,
3256 ConversionPatternRewriter &rewriter) const override {
3257 Value lhs = adaptor.getLhs();
3258 Value rhs = adaptor.getRhs();
3259 auto pred = op.getPredicate();
3260 Value eq = buildUArrayElementEq(rewriter, op.getLoc(), lhs, rhs,
3261 lhs.getType(), pred);
3262
3263 if (!eq)
3264 return rewriter.notifyMatchFailure(
3265 op, "unpacked array element type does not support comparison");
3266
3267 rewriter.replaceOp(op, eq);
3268 return success();
3269 }
3270};
3271
3272struct AssocArraySetOpConversion : public OpConversionPattern<AssocArraySetOp> {
3273 using OpConversionPattern::OpConversionPattern;
3274 LogicalResult
3275 matchAndRewrite(AssocArraySetOp op, OpAdaptor adaptor,
3276 ConversionPatternRewriter &rewriter) const override {
3277 probeRefAndDriveWithResult(
3278 rewriter, op.getLoc(), adaptor.getAssocArray(), [&](Value array) {
3279 return sim::AssocArraySetOp::create(rewriter, op.getLoc(), array,
3280 adaptor.getIndex(),
3281 adaptor.getValue())
3282 .getOutArray();
3283 });
3284 rewriter.eraseOp(op);
3285 return success();
3286 }
3287};
3288
3289struct AssocArrayDeleteOpConversion
3290 : public OpConversionPattern<AssocArrayDeleteOp> {
3291 using OpConversionPattern::OpConversionPattern;
3292 LogicalResult
3293 matchAndRewrite(AssocArrayDeleteOp op, OpAdaptor adaptor,
3294 ConversionPatternRewriter &rewriter) const override {
3295 probeRefAndDriveWithResult(
3296 rewriter, op.getLoc(), adaptor.getAssocArray(), [&](Value array) {
3297 return sim::AssocArrayDeleteOp::create(rewriter, op.getLoc(), array,
3298 adaptor.getIndex())
3299 .getOutArray();
3300 });
3301 rewriter.eraseOp(op);
3302 return success();
3303 }
3304};
3305
3306struct AssocArrayClearOpConversion
3307 : public OpConversionPattern<AssocArrayClearOp> {
3308 using OpConversionPattern::OpConversionPattern;
3309 LogicalResult
3310 matchAndRewrite(AssocArrayClearOp op, OpAdaptor adaptor,
3311 ConversionPatternRewriter &rewriter) const override {
3312 auto refType = cast<llhd::RefType>(adaptor.getAssocArray().getType());
3313 Value emptyArray = sim::AssocArrayEmptyOp::create(rewriter, op->getLoc(),
3314 refType.getNestedType());
3315 Value delay = llhd::ConstantTimeOp::create(
3316 rewriter, op.getLoc(),
3317 getBlockingOrContinuousAssignDelay(rewriter.getContext()));
3318 llhd::DriveOp::create(rewriter, op.getLoc(), adaptor.getAssocArray(),
3319 emptyArray, delay, Value{});
3320 rewriter.eraseOp(op);
3321 return success();
3322 }
3323};
3324
3325struct AssocArraySizeOpConversion
3326 : public OpConversionPattern<AssocArraySizeOp> {
3327 using OpConversionPattern::OpConversionPattern;
3328 LogicalResult
3329 matchAndRewrite(AssocArraySizeOp op, OpAdaptor adaptor,
3330 ConversionPatternRewriter &rewriter) const override {
3331 Value array =
3332 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getAssocArray());
3333 rewriter.replaceOpWithNewOp<sim::AssocArraySizeOp>(op, array);
3334 return success();
3335 }
3336};
3337
3338struct AssocArrayExistsOpConversion
3339 : public OpConversionPattern<AssocArrayExistsOp> {
3340 using OpConversionPattern::OpConversionPattern;
3341 LogicalResult
3342 matchAndRewrite(AssocArrayExistsOp op, OpAdaptor adaptor,
3343 ConversionPatternRewriter &rewriter) const override {
3344 Value array =
3345 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getAssocArray());
3346 rewriter.replaceOpWithNewOp<sim::AssocArrayExistsOp>(op, array,
3347 adaptor.getIndex());
3348 return success();
3349 }
3350};
3351
3352template <typename MooreOpTy, typename SimOpTy>
3353struct AssocArrayEndpointOpConversion : public OpConversionPattern<MooreOpTy> {
3355 using OpAdaptor = typename MooreOpTy::Adaptor;
3356 LogicalResult
3357 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
3358 ConversionPatternRewriter &rewriter) const override {
3359 Value array =
3360 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getAssocArray());
3361 auto endpointOp = SimOpTy::create(rewriter, op.getLoc(), array);
3362 Value curIndex =
3363 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getIndex());
3364 Value zero = hw::ConstantOp::create(rewriter, op.getLoc(), APInt(32, 0));
3365 Value foundBit =
3366 comb::ICmpOp::create(rewriter, op.getLoc(), comb::ICmpPredicate::ne,
3367 endpointOp->getResult(0), zero, false);
3368 Value newIndex =
3369 comb::MuxOp::create(rewriter, op.getLoc(), foundBit,
3370 endpointOp->getResult(1), curIndex, false);
3371 Value delay = llhd::ConstantTimeOp::create(
3372 rewriter, op.getLoc(),
3373 getBlockingOrContinuousAssignDelay(rewriter.getContext()));
3374 llhd::DriveOp::create(rewriter, op.getLoc(), adaptor.getIndex(), newIndex,
3375 delay, Value{});
3376 rewriter.replaceOp(op, endpointOp->getResult(0));
3377 return success();
3378 }
3379};
3380
3381template <typename MooreOpTy, typename SimOpTy>
3382struct AssocArrayStepOpConversion : public OpConversionPattern<MooreOpTy> {
3384 using OpAdaptor = typename MooreOpTy::Adaptor;
3385 LogicalResult
3386 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
3387 ConversionPatternRewriter &rewriter) const override {
3388 Value array =
3389 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getAssocArray());
3390 Value curIndex =
3391 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getIndex());
3392 auto stepOp = SimOpTy::create(rewriter, op.getLoc(), array, curIndex);
3393 Value delay = llhd::ConstantTimeOp::create(
3394 rewriter, op.getLoc(),
3395 getBlockingOrContinuousAssignDelay(rewriter.getContext()));
3396 llhd::DriveOp::create(rewriter, op.getLoc(), adaptor.getIndex(),
3397 stepOp->getResult(1), delay, Value{});
3398 rewriter.replaceOp(op, stepOp->getResult(0));
3399 return success();
3400 }
3401};
3402
3403using AssocArrayFirstOpConversion =
3404 AssocArrayEndpointOpConversion<AssocArrayFirstOp, sim::AssocArrayFirstOp>;
3405using AssocArrayLastOpConversion =
3406 AssocArrayEndpointOpConversion<AssocArrayLastOp, sim::AssocArrayLastOp>;
3407using AssocArrayNextOpConversion =
3408 AssocArrayStepOpConversion<AssocArrayNextOp, sim::AssocArrayNextOp>;
3409using AssocArrayPrevOpConversion =
3410 AssocArrayStepOpConversion<AssocArrayPrevOp, sim::AssocArrayPrevOp>;
3411
3412struct DisplayBIOpConversion : public OpConversionPattern<DisplayBIOp> {
3413 using OpConversionPattern::OpConversionPattern;
3414
3415 LogicalResult
3416 matchAndRewrite(DisplayBIOp op, OpAdaptor adaptor,
3417 ConversionPatternRewriter &rewriter) const override {
3418 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
3419 op, adaptor.getMessage());
3420 return success();
3421 }
3422};
3423
3424struct FDisplayBIOpConversion : public OpConversionPattern<FDisplayBIOp> {
3425 using OpConversionPattern::OpConversionPattern;
3426 LogicalResult
3427 matchAndRewrite(FDisplayBIOp op, OpAdaptor adaptor,
3428 ConversionPatternRewriter &rewriter) const override {
3429 auto stream = sim::SVChannelToOutputStreamOp::create(rewriter, op.getLoc(),
3430 adaptor.getFd());
3431 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
3432 op, adaptor.getMessage(), stream.getStream());
3433 return success();
3434 }
3435};
3436
3437struct FOpenBIOpConversion : public OpConversionPattern<FOpenBIOp> {
3438 using OpConversionPattern::OpConversionPattern;
3439 LogicalResult
3440 matchAndRewrite(FOpenBIOp op, OpAdaptor adaptor,
3441 ConversionPatternRewriter &rewriter) const override {
3442 sim::SVFOpenModeAttr simMode;
3443 if (auto modeAttr = op.getModeAttr()) {
3444 auto mapMode = [](moore::FOpenMode m) -> sim::SVFOpenMode {
3445 switch (m) {
3446 case moore::FOpenMode::Read:
3447 return sim::SVFOpenMode::Read;
3448 case moore::FOpenMode::Write:
3449 return sim::SVFOpenMode::Write;
3450 case moore::FOpenMode::Append:
3451 return sim::SVFOpenMode::Append;
3452 case moore::FOpenMode::ReadUpdate:
3453 return sim::SVFOpenMode::ReadUpdate;
3454 case moore::FOpenMode::WriteUpdate:
3455 return sim::SVFOpenMode::WriteUpdate;
3456 case moore::FOpenMode::AppendUpdate:
3457 return sim::SVFOpenMode::AppendUpdate;
3458 }
3459 llvm_unreachable("unknown FOpenMode");
3460 };
3461 simMode = sim::SVFOpenModeAttr::get(op.getContext(),
3462 mapMode(modeAttr.getValue()));
3463 }
3464 rewriter.replaceOpWithNewOp<sim::SVFOpenOp>(op, adaptor.getFilename(),
3465 simMode);
3466 return success();
3467 }
3468};
3469
3470struct PlusArgsTestBIOpConversion
3471 : public OpConversionPattern<PlusArgsTestBIOp> {
3472 using OpConversionPattern::OpConversionPattern;
3473 LogicalResult
3474 matchAndRewrite(PlusArgsTestBIOp op, OpAdaptor adaptor,
3475 ConversionPatternRewriter &rewriter) const override {
3476 rewriter.replaceOpWithNewOp<sim::PlusArgsTestOp>(op, rewriter.getI1Type(),
3477 op.getFormatStringAttr());
3478 return success();
3479 }
3480};
3481
3482struct PlusArgsValueBIOpConversion
3483 : public OpConversionPattern<PlusArgsValueBIOp> {
3484 using OpConversionPattern::OpConversionPattern;
3485 LogicalResult
3486 matchAndRewrite(PlusArgsValueBIOp op, OpAdaptor adaptor,
3487 ConversionPatternRewriter &rewriter) const override {
3488 auto resultType = typeConverter->convertType(op.getResult().getType());
3489 if (!resultType)
3490 return rewriter.notifyMatchFailure(op, "unsupported result type");
3491 rewriter.replaceOpWithNewOp<sim::PlusArgsValueOp>(
3492 op, rewriter.getI1Type(), resultType, op.getFormatStringAttr());
3493 return success();
3494 }
3495};
3496
3497struct FCloseBIOpConversion : public OpConversionPattern<FCloseBIOp> {
3498 using OpConversionPattern::OpConversionPattern;
3499
3500 LogicalResult
3501 matchAndRewrite(FCloseBIOp op, OpAdaptor adaptor,
3502 ConversionPatternRewriter &rewriter) const override {
3503 rewriter.replaceOpWithNewOp<sim::SVFCloseOp>(op, adaptor.getFd());
3504 return success();
3505 }
3506};
3507
3508struct FFlushBIOpConversion : public OpConversionPattern<FFlushBIOp> {
3509 using OpConversionPattern::OpConversionPattern;
3510
3511 LogicalResult
3512 matchAndRewrite(FFlushBIOp op, OpAdaptor adaptor,
3513 ConversionPatternRewriter &rewriter) const override {
3514 if (!adaptor.getFd()) {
3515 rewriter.replaceOpWithNewOp<sim::SVFFlushAllOp>(op);
3516 } else {
3517 auto stream = sim::SVChannelToOutputStreamOp::create(
3518 rewriter, op.getLoc(), adaptor.getFd());
3519 rewriter.replaceOpWithNewOp<sim::FlushOp>(op, stream);
3520 }
3521 return success();
3522 }
3523};
3524
3525struct StringCmpOpConversion : public OpConversionPattern<StringCmpOp> {
3526 using OpConversionPattern::OpConversionPattern;
3527
3528 LogicalResult
3529 matchAndRewrite(StringCmpOp op, OpAdaptor adaptor,
3530 ConversionPatternRewriter &rewriter) const override {
3531 sim::StringCmpPredicate pred;
3532 switch (op.getPredicate()) {
3533 case moore::StringCmpPredicate::eq:
3534 pred = sim::StringCmpPredicate::eq;
3535 break;
3536 case moore::StringCmpPredicate::ne:
3537 pred = sim::StringCmpPredicate::ne;
3538 break;
3539 case moore::StringCmpPredicate::lt:
3540 pred = sim::StringCmpPredicate::lt;
3541 break;
3542 case moore::StringCmpPredicate::le:
3543 pred = sim::StringCmpPredicate::le;
3544 break;
3545 case moore::StringCmpPredicate::gt:
3546 pred = sim::StringCmpPredicate::gt;
3547 break;
3548 case moore::StringCmpPredicate::ge:
3549 pred = sim::StringCmpPredicate::ge;
3550 break;
3551 }
3552 rewriter.replaceOpWithNewOp<sim::StringCmpOp>(op, pred, adaptor.getLhs(),
3553 adaptor.getRhs());
3554 return success();
3555 }
3556};
3557
3558struct ReadMemBIOpConversion : public OpConversionPattern<ReadMemBIOp> {
3559 using OpConversionPattern::OpConversionPattern;
3560
3561 LogicalResult
3562 matchAndRewrite(ReadMemBIOp op, OpAdaptor adaptor,
3563 ConversionPatternRewriter &rewriter) const override {
3564 auto loc = op.getLoc();
3565 Value memory = llhd::ProbeOp::create(rewriter, loc, adaptor.getDest());
3566 Value loaded = sim::SVReadMemOp::create(
3567 rewriter, loc, memory.getType(), adaptor.getFilename(), memory,
3568 rewriter.getBoolAttr(op.getBase() == MemBase::Binary),
3569 adaptor.getStartAddr(), adaptor.getFinishAddr(), adaptor.getSliceLeft(),
3570 adaptor.getSliceRight(), op.getDimLowsAttr(), op.getDimDescendingAttr(),
3571 op.getEnumValuesAttr());
3572 Value delay = llhd::ConstantTimeOp::create(
3573 rewriter, loc,
3574 llhd::TimeAttr::get(rewriter.getContext(), 0U, "ns", 0, 1));
3575 rewriter.replaceOpWithNewOp<llhd::DriveOp>(op, adaptor.getDest(), loaded,
3576 delay, Value{});
3577 return success();
3578 }
3579};
3580
3581} // namespace
3582
3583//===----------------------------------------------------------------------===//
3584// Simulation Control Conversion
3585//===----------------------------------------------------------------------===//
3586
3587// moore.builtin.stop -> sim.pause
3588static LogicalResult convert(StopBIOp op, StopBIOp::Adaptor adaptor,
3589 ConversionPatternRewriter &rewriter) {
3590 rewriter.replaceOpWithNewOp<sim::PauseOp>(op, /*verbose=*/false);
3591 return success();
3592}
3593
3594// moore.builtin.finish -> sim.terminate
3595static LogicalResult convert(FinishBIOp op, FinishBIOp::Adaptor adaptor,
3596 ConversionPatternRewriter &rewriter) {
3597 rewriter.replaceOpWithNewOp<sim::TerminateOp>(op, op.getExitCode() == 0,
3598 /*verbose=*/false);
3599 return success();
3600}
3601
3602// moore.builtin.severity -> sim.proc.print
3603static LogicalResult convert(SeverityBIOp op, SeverityBIOp::Adaptor adaptor,
3604 ConversionPatternRewriter &rewriter) {
3605
3606 std::string severityString;
3607
3608 switch (op.getSeverity()) {
3609 case (Severity::Fatal):
3610 severityString = "Fatal: ";
3611 break;
3612 case (Severity::Error):
3613 severityString = "Error: ";
3614 break;
3615 case (Severity::Warning):
3616 severityString = "Warning: ";
3617 break;
3618 case (Severity::Info):
3619 severityString = "Info: ";
3620 break;
3621 }
3622
3623 auto prefix =
3624 sim::FormatLiteralOp::create(rewriter, op.getLoc(), severityString);
3625 auto message = sim::FormatStringConcatOp::create(
3626 rewriter, op.getLoc(), ValueRange{prefix, adaptor.getMessage()});
3627 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(op, message);
3628 return success();
3629}
3630
3631//===----------------------------------------------------------------------===//
3632// Random Builtin Conversion
3633//===----------------------------------------------------------------------===//
3634
3635/// moore.builtin.urandom_range -> call @__circt_urandom_range(i32, i32, ptr)
3636///
3637/// The seed pointer is null when no seed is provided. When a seed ref is
3638/// present, we probe the current value into an alloca before the call, and
3639/// drive the (potentially mutated) value back after.
3640static LogicalResult convert(UrandomRangeBIOp op,
3641 UrandomRangeBIOp::Adaptor adaptor,
3642 ConversionPatternRewriter &rewriter,
3643 FunctionCache &funcCache) {
3644 auto loc = op.getLoc();
3645 auto i32Ty = rewriter.getI32Type();
3646 auto ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
3647 auto fn = funcCache.getOrCreate(rewriter, "__circt_urandom_range",
3648 {i32Ty, i32Ty, ptrTy}, {i32Ty});
3649
3650 Value seedPtr;
3651 if (auto seedRef = adaptor.getSeed()) {
3652 // Allocate a temporary, probe the current seed value into it.
3653 auto one = hw::ConstantOp::create(rewriter, loc, i32Ty, 1);
3654 seedPtr = LLVM::AllocaOp::create(rewriter, loc, ptrTy, i32Ty, one);
3655 auto seedVal = llhd::ProbeOp::create(rewriter, loc, seedRef);
3656 LLVM::StoreOp::create(rewriter, loc, seedVal, seedPtr);
3657 } else {
3658 seedPtr = LLVM::ZeroOp::create(rewriter, loc, ptrTy);
3659 }
3660
3661 auto call = func::CallOp::create(
3662 rewriter, loc, fn,
3663 ValueRange{adaptor.getMinval(), adaptor.getMaxval(), seedPtr});
3664
3665 // Drive the potentially mutated seed back with an epsilon time delta.
3666 if (adaptor.getSeed()) {
3667 auto newSeed = LLVM::LoadOp::create(rewriter, loc, i32Ty, seedPtr);
3668 auto epsilon = llhd::ConstantTimeOp::create(
3669 rewriter, loc,
3670 llhd::TimeAttr::get(rewriter.getContext(), 0, "ns", 0, 1));
3671 llhd::DriveOp::create(rewriter, loc, adaptor.getSeed(), newSeed, epsilon,
3672 Value{});
3673 }
3674
3675 rewriter.replaceOp(op, call.getResult(0));
3676 return success();
3677}
3678
3679// moore.builtin.finish_message
3680static LogicalResult convert(FinishMessageBIOp op,
3681 FinishMessageBIOp::Adaptor adaptor,
3682 ConversionPatternRewriter &rewriter) {
3683 // We don't support printing termination/pause messages yet.
3684 rewriter.eraseOp(op);
3685 return success();
3686}
3687
3688//===----------------------------------------------------------------------===//
3689// Timing Control Conversion
3690//===----------------------------------------------------------------------===//
3691
3692// moore.builtin.time
3693static LogicalResult convert(TimeBIOp op, TimeBIOp::Adaptor adaptor,
3694 ConversionPatternRewriter &rewriter) {
3695 rewriter.replaceOpWithNewOp<llhd::CurrentTimeOp>(op);
3696 return success();
3697}
3698
3699// moore.logic_to_time
3700static LogicalResult convert(LogicToTimeOp op, LogicToTimeOp::Adaptor adaptor,
3701 ConversionPatternRewriter &rewriter) {
3702 rewriter.replaceOpWithNewOp<llhd::IntToTimeOp>(op, adaptor.getInput());
3703 return success();
3704}
3705
3706// moore.time_to_logic
3707static LogicalResult convert(TimeToLogicOp op, TimeToLogicOp::Adaptor adaptor,
3708 ConversionPatternRewriter &rewriter) {
3709 rewriter.replaceOpWithNewOp<llhd::TimeToIntOp>(op, adaptor.getInput());
3710 return success();
3711}
3712
3713//===----------------------------------------------------------------------===//
3714// Conversion Infrastructure
3715//===----------------------------------------------------------------------===//
3716
3717static void populateLegality(ConversionTarget &target,
3718 const TypeConverter &converter) {
3719 target.addIllegalDialect<MooreDialect>();
3720 target.addLegalDialect<comb::CombDialect>();
3721 target.addLegalDialect<hw::HWDialect>();
3722 target.addLegalDialect<seq::SeqDialect>();
3723 target.addLegalDialect<llhd::LLHDDialect>();
3724 target.addLegalDialect<ltl::LTLDialect>();
3725 target.addLegalDialect<mlir::BuiltinDialect>();
3726 target.addLegalDialect<mlir::math::MathDialect>();
3727 target.addLegalDialect<sim::SimDialect>();
3728 target.addLegalDialect<mlir::LLVM::LLVMDialect>();
3729 target.addLegalDialect<mlir::ub::UBDialect>();
3730 target.addLegalDialect<verif::VerifDialect>();
3731 target.addLegalDialect<arith::ArithDialect>();
3732
3733 target.addLegalOp<debug::ScopeOp>();
3734
3735 target.addDynamicallyLegalOp<scf::YieldOp, func::CallOp, func::ReturnOp,
3736 UnrealizedConversionCastOp, hw::OutputOp,
3737 hw::InstanceOp, debug::ArrayOp, debug::StructOp,
3738 debug::VariableOp, arith::SelectOp>(
3739 [&](Operation *op) { return converter.isLegal(op); });
3740
3741 target.addDynamicallyLegalOp<scf::IfOp, scf::ForOp, scf::ExecuteRegionOp,
3742 scf::WhileOp, scf::ForallOp>([&](Operation *op) {
3743 return converter.isLegal(op) && !op->getParentOfType<llhd::ProcessOp>();
3744 });
3745
3746 target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
3747 return converter.isSignatureLegal(op.getFunctionType());
3748 });
3749
3750 target.addDynamicallyLegalOp<hw::HWModuleOp>([&](hw::HWModuleOp op) {
3751 return converter.isSignatureLegal(op.getModuleType().getFuncType()) &&
3752 converter.isLegal(&op.getBody());
3753 });
3754}
3755
3756static void populateTypeConversion(TypeConverter &typeConverter) {
3757 typeConverter.addConversion([&](IntType type) {
3758 return IntegerType::get(type.getContext(), type.getWidth());
3759 });
3760
3761 typeConverter.addConversion([&](RealType type) -> mlir::Type {
3762 MLIRContext *ctx = type.getContext();
3763 switch (type.getWidth()) {
3764 case moore::RealWidth::f32:
3765 return mlir::Float32Type::get(ctx);
3766 case moore::RealWidth::f64:
3767 return mlir::Float64Type::get(ctx);
3768 }
3769 });
3770
3771 typeConverter.addConversion(
3772 [&](TimeType type) { return llhd::TimeType::get(type.getContext()); });
3773
3774 typeConverter.addConversion([&](FormatStringType type) {
3775 return sim::FormatStringType::get(type.getContext());
3776 });
3777
3778 typeConverter.addConversion([&](StringType type) {
3779 return sim::DynamicStringType::get(type.getContext());
3780 });
3781
3782 typeConverter.addConversion([&](QueueType type) {
3783 return sim::QueueType::get(type.getContext(),
3784 typeConverter.convertType(type.getElementType()),
3785 type.getBound());
3786 });
3787
3788 typeConverter.addConversion([&](sim::QueueType type) -> std::optional<Type> {
3789 if (auto elementType = typeConverter.convertType(type.getElementType()))
3790 return sim::QueueType::get(type.getContext(), elementType,
3791 type.getBound());
3792 return {};
3793 });
3794
3795 typeConverter.addConversion([&](ArrayType type) -> std::optional<Type> {
3796 if (auto elementType = typeConverter.convertType(type.getElementType()))
3797 return hw::ArrayType::get(elementType, type.getSize());
3798 return {};
3799 });
3800
3801 typeConverter.addConversion([&](AssocArrayType type) -> std::optional<Type> {
3802 auto elementType = typeConverter.convertType(type.getElementType());
3803 auto indexType = typeConverter.convertType(type.getIndexType());
3804 if (!elementType || !indexType)
3805 return {};
3806 return sim::AssocArrayType::get(type.getContext(), elementType, indexType);
3807 });
3808
3809 // FIXME: Unpacked arrays support more element types than their packed
3810 // variants, and as such, mapping them to hw::Array is somewhat naive. See
3811 // also the analogous note below concerning unpacked struct type conversion.
3812 typeConverter.addConversion(
3813 [&](UnpackedArrayType type) -> std::optional<Type> {
3814 if (auto elementType = typeConverter.convertType(type.getElementType()))
3815 return hw::ArrayType::get(elementType, type.getSize());
3816 return {};
3817 });
3818
3819 typeConverter.addConversion([&](OpenArrayType type) -> std::optional<Type> {
3820 return LLVM::LLVMPointerType::get(type.getContext());
3821 });
3822
3823 typeConverter.addConversion(
3824 [&](OpenUnpackedArrayType type) -> std::optional<Type> {
3825 return LLVM::LLVMPointerType::get(type.getContext());
3826 });
3827
3828 typeConverter.addConversion([&](StructType type) -> std::optional<Type> {
3829 SmallVector<hw::StructType::FieldInfo> fields;
3830 for (auto field : type.getMembers()) {
3831 hw::StructType::FieldInfo info;
3832 info.type = typeConverter.convertType(field.type);
3833 if (!info.type)
3834 return {};
3835 info.name = field.name;
3836 fields.push_back(info);
3837 }
3838 return hw::StructType::get(type.getContext(), fields);
3839 });
3840
3841 // FIXME: Mapping unpacked struct type to struct type in hw dialect may be a
3842 // plain solution. The packed and unpacked data structures have some
3843 // differences though they look similarily. The packed data structure is
3844 // contiguous in memory but another is opposite. The differences will affect
3845 // data layout and granularity of event tracking in simulation.
3846 typeConverter.addConversion(
3847 [&](UnpackedStructType type) -> std::optional<Type> {
3848 SmallVector<hw::StructType::FieldInfo> fields;
3849 for (auto field : type.getMembers()) {
3850 hw::StructType::FieldInfo info;
3851 info.type = typeConverter.convertType(field.type);
3852 if (!info.type)
3853 return {};
3854 info.name = field.name;
3855 fields.push_back(info);
3856 }
3857 return hw::StructType::get(type.getContext(), fields);
3858 });
3859
3860 // UnionType -> hw::UnionType
3861 typeConverter.addConversion([&](UnionType type) -> std::optional<Type> {
3862 SmallVector<hw::UnionType::FieldInfo> fields;
3863 for (auto field : type.getMembers()) {
3864 hw::UnionType::FieldInfo info;
3865 info.type = typeConverter.convertType(field.type);
3866 if (!info.type)
3867 return {};
3868 info.name = field.name;
3869 info.offset = 0; // packed union, all fields start at bit 0
3870 fields.push_back(info);
3871 }
3872 auto result = hw::UnionType::get(type.getContext(), fields);
3873 return result;
3874 });
3875
3876 // UnpackedUnionType -> hw::UnionType
3877 typeConverter.addConversion(
3878 [&](UnpackedUnionType type) -> std::optional<Type> {
3879 SmallVector<hw::UnionType::FieldInfo> fields;
3880 for (auto field : type.getMembers()) {
3881 hw::UnionType::FieldInfo info;
3882 info.type = typeConverter.convertType(field.type);
3883 if (!info.type)
3884 return {};
3885 info.name = field.name;
3886 info.offset = 0;
3887 fields.push_back(info);
3888 }
3889 return hw::UnionType::get(type.getContext(), fields);
3890 });
3891
3892 // Conversion of CHandle to LLVMPointerType
3893 typeConverter.addConversion([&](ChandleType type) -> std::optional<Type> {
3894 return LLVM::LLVMPointerType::get(type.getContext());
3895 });
3896
3897 // Explicitly mark LLVMPointerType as a legal target
3898 typeConverter.addConversion(
3899 [](LLVM::LLVMPointerType t) -> std::optional<Type> { return t; });
3900
3901 // ClassHandleType -> !llvm.ptr
3902 typeConverter.addConversion([&](ClassHandleType type) -> std::optional<Type> {
3903 return LLVM::LLVMPointerType::get(type.getContext());
3904 });
3905
3906 // NullType -> !llvm.ptr
3907 typeConverter.addConversion([&](NullType type) -> std::optional<Type> {
3908 return LLVM::LLVMPointerType::get(type.getContext());
3909 });
3910
3911 typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
3912 if (isa<OpenArrayType, OpenUnpackedArrayType>(type.getNestedType()))
3913 return LLVM::LLVMPointerType::get(type.getContext());
3914 if (auto innerType = typeConverter.convertType(type.getNestedType()))
3915 return llhd::RefType::get(innerType);
3916 return {};
3917 });
3918
3919 // Valid target types.
3920 typeConverter.addConversion([](IntegerType type) { return type; });
3921 typeConverter.addConversion([](FloatType type) { return type; });
3922 typeConverter.addConversion([](sim::DynamicStringType type) { return type; });
3923 typeConverter.addConversion([](sim::FormatStringType type) { return type; });
3924 typeConverter.addConversion([](sim::AssocArrayType type) { return type; });
3925 typeConverter.addConversion([](llhd::TimeType type) { return type; });
3926 typeConverter.addConversion([](debug::ArrayType type) { return type; });
3927 typeConverter.addConversion([](debug::ScopeType type) { return type; });
3928 typeConverter.addConversion([](debug::StructType type) { return type; });
3929
3930 typeConverter.addConversion([&](llhd::RefType type) -> std::optional<Type> {
3931 if (auto innerType = typeConverter.convertType(type.getNestedType()))
3932 return llhd::RefType::get(innerType);
3933 return {};
3934 });
3935
3936 typeConverter.addConversion([&](hw::ArrayType type) -> std::optional<Type> {
3937 if (auto elementType = typeConverter.convertType(type.getElementType()))
3938 return hw::ArrayType::get(elementType, type.getNumElements());
3939 return {};
3940 });
3941
3942 typeConverter.addConversion([&](hw::StructType type) -> std::optional<Type> {
3943 SmallVector<hw::StructType::FieldInfo> fields;
3944 for (auto field : type.getElements()) {
3945 hw::StructType::FieldInfo info;
3946 info.type = typeConverter.convertType(field.type);
3947 if (!info.type)
3948 return {};
3949 info.name = field.name;
3950 fields.push_back(info);
3951 }
3952 return hw::StructType::get(type.getContext(), fields);
3953 });
3954
3955 typeConverter.addConversion([&](hw::UnionType type) -> std::optional<Type> {
3956 SmallVector<hw::UnionType::FieldInfo> fields;
3957 for (auto field : type.getElements()) {
3958 hw::UnionType::FieldInfo info;
3959 info.type = typeConverter.convertType(field.type);
3960 if (!info.type)
3961 return {};
3962 info.name = field.name;
3963 info.offset = field.offset;
3964 fields.push_back(info);
3965 }
3966 return hw::UnionType::get(type.getContext(), fields);
3967 });
3968
3969 typeConverter.addTargetMaterialization(
3970 [&](mlir::OpBuilder &builder, mlir::Type resultType,
3971 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
3972 if (inputs.size() != 1 || !inputs[0])
3973 return Value();
3974 return UnrealizedConversionCastOp::create(builder, loc, resultType,
3975 inputs[0])
3976 .getResult(0);
3977 });
3978
3979 typeConverter.addSourceMaterialization(
3980 [&](mlir::OpBuilder &builder, mlir::Type resultType,
3981 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
3982 if (inputs.size() != 1)
3983 return Value();
3984 return UnrealizedConversionCastOp::create(builder, loc, resultType,
3985 inputs[0])
3986 ->getResult(0);
3987 });
3988}
3989
3991 TypeConverter &typeConverter,
3992 ClassTypeCache &classCache,
3993 FunctionCache &funcCache) {
3994
3995 patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
3996 classCache);
3997 patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
3998 classCache, funcCache);
3999 patterns.add<ClassPropertyRefOpConversion>(typeConverter,
4000 patterns.getContext(), classCache);
4001
4002 // clang-format off
4003 patterns.add<
4004 ClassUpcastOpConversion,
4005 NullOpConversion,
4006 // Patterns of declaration operations.
4007 VariableOpConversion,
4008 NetOpConversion,
4009
4010 // Patterns for conversion operations.
4011 ConversionOpConversion,
4012 BitcastConversion<PackedToSBVOp>,
4013 BitcastConversion<SBVToPackedOp>,
4014 NoOpConversion<LogicToIntOp>,
4015 NoOpConversion<IntToLogicOp>,
4016 NoOpConversion<ToBuiltinIntOp>,
4017 NoOpConversion<FromBuiltinIntOp>,
4018 TruncOpConversion,
4019 ZExtOpConversion,
4020 SExtOpConversion,
4021 SIntToRealOpConversion,
4022 UIntToRealOpConversion,
4023 IntToStringOpConversion,
4024 StringToIntOpConversion,
4025 FormatStringToStringOpConversion,
4026 RealToIntOpConversion,
4027 ConvertRealOpConversion,
4028 RealBitcastOpConversion<RealtobitsBIOp>,
4029 RealBitcastOpConversion<BitstorealBIOp>,
4030 RealBitcastOpConversion<ShortrealtobitsBIOp>,
4031 RealBitcastOpConversion<BitstoshortrealBIOp>,
4032
4033 // Patterns of miscellaneous operations.
4034 ConstantOpConv,
4035 ConstantRealOpConv,
4036 ConcatOpConversion,
4037 ReplicateOpConversion,
4038 ConstantTimeOpConv,
4039 ExtractOpConversion,
4040 DynExtractOpConversion,
4041 DynExtractRefOpConversion,
4042 ReadOpConversion,
4043 StructExtractOpConversion,
4044 StructExtractRefOpConversion,
4045 ExtractRefOpConversion,
4046 StructCreateOpConversion,
4047 UnionCreateOpConversion,
4048 UnionExtractOpConversion,
4049 UnionExtractRefOpConversion,
4050 ConditionalOpConversion,
4051 ArrayCreateOpConversion,
4052 UArrayCmpOpConversion,
4053 YieldOpConversion,
4054 OutputOpConversion,
4055 ConstantStringOpConv,
4056
4057 // Patterns of unary operations.
4058 ReduceAndOpConversion,
4059 ReduceOrOpConversion,
4060 ReduceXorOpConversion,
4061 BoolCastOpConversion,
4062 NotOpConversion,
4063 NegOpConversion,
4064
4065 // Patterns of binary operations.
4066 BinaryOpConversion<AddOp, comb::AddOp>,
4067 BinaryOpConversion<SubOp, comb::SubOp>,
4068 BinaryOpConversion<MulOp, comb::MulOp>,
4069 BinaryOpConversion<DivUOp, comb::DivUOp>,
4070 BinaryOpConversion<DivSOp, comb::DivSOp>,
4071 BinaryOpConversion<ModUOp, comb::ModUOp>,
4072 BinaryOpConversion<ModSOp, comb::ModSOp>,
4073 BinaryOpConversion<AndOp, comb::AndOp>,
4074 BinaryOpConversion<OrOp, comb::OrOp>,
4075 BinaryOpConversion<XorOp, comb::XorOp>,
4076
4077 // Patterns for unary real operations.
4078 NegRealOpConversion,
4079
4080 // Patterns for binary real operations.
4081 BinaryRealOpConversion<AddRealOp, arith::AddFOp>,
4082 BinaryRealOpConversion<SubRealOp, arith::SubFOp>,
4083 BinaryRealOpConversion<DivRealOp, arith::DivFOp>,
4084 BinaryRealOpConversion<MulRealOp, arith::MulFOp>,
4085 BinaryRealOpConversion<PowRealOp, math::PowFOp>,
4086
4087 // Pattern for Verilog standard mathematical functions
4088 RealMathFunc<LnBIOp, math::LogOp>,
4089 RealMathFunc<Log10BIOp, math::Log10Op>,
4090 RealMathFunc<ExpBIOp, math::ExpOp>,
4091 RealMathFunc<SqrtBIOp, math::SqrtOp>,
4092 BinaryRealOpConversion<MinBIOp, arith::MinimumFOp>,
4093 BinaryRealOpConversion<MaxBIOp, arith::MaximumFOp>,
4094 RealMathFunc<AbsBIOp, math::AbsFOp>,
4095 RealMathFunc<FloorBIOp, math::FloorOp>,
4096 RealMathFunc<CeilBIOp, math::CeilOp>,
4097 RealMathFunc<SinBIOp, math::SinOp>,
4098 RealMathFunc<CosBIOp, math::CosOp>,
4099 RealMathFunc<TanBIOp, math::TanOp>,
4100 RealMathFunc<AsinBIOp, math::AsinOp>,
4101 RealMathFunc<AcosBIOp, math::AcosOp>,
4102 RealMathFunc<AtanBIOp, math::AtanOp>,
4103 BinaryRealOpConversion<Atan2BIOp, math::Atan2Op>,
4104 HypotBIOpConversion,
4105 RealMathFunc<SinhBIOp, math::SinhOp>,
4106 RealMathFunc<CoshBIOp, math::CoshOp>,
4107 RealMathFunc<TanhBIOp, math::TanhOp>,
4108 RealMathFunc<AsinhBIOp, math::AsinhOp>,
4109 RealMathFunc<AcoshBIOp, math::AcoshOp>,
4110 RealMathFunc<AtanhBIOp, math::AtanhOp>,
4111
4112 // Patterns of power operations.
4113 PowUOpConversion, PowSOpConversion,
4114 Clog2BIOpConversion,
4115
4116 // Patterns of relational operations.
4117 ICmpOpConversion<UltOp, ICmpPredicate::ult>,
4118 ICmpOpConversion<SltOp, ICmpPredicate::slt>,
4119 ICmpOpConversion<UleOp, ICmpPredicate::ule>,
4120 ICmpOpConversion<SleOp, ICmpPredicate::sle>,
4121 ICmpOpConversion<UgtOp, ICmpPredicate::ugt>,
4122 ICmpOpConversion<SgtOp, ICmpPredicate::sgt>,
4123 ICmpOpConversion<UgeOp, ICmpPredicate::uge>,
4124 ICmpOpConversion<SgeOp, ICmpPredicate::sge>,
4125 ICmpOpConversion<EqOp, ICmpPredicate::eq>,
4126 ICmpOpConversion<NeOp, ICmpPredicate::ne>,
4127 ICmpOpConversion<CaseEqOp, ICmpPredicate::ceq>,
4128 ICmpOpConversion<CaseNeOp, ICmpPredicate::cne>,
4129 ICmpOpConversion<WildcardEqOp, ICmpPredicate::weq>,
4130 ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
4131 FCmpOpConversion<NeRealOp, arith::CmpFPredicate::UNE>,
4132 FCmpOpConversion<FltOp, arith::CmpFPredicate::OLT>,
4133 FCmpOpConversion<FleOp, arith::CmpFPredicate::OLE>,
4134 FCmpOpConversion<FgtOp, arith::CmpFPredicate::OGT>,
4135 FCmpOpConversion<FgeOp, arith::CmpFPredicate::OGE>,
4136 FCmpOpConversion<EqRealOp, arith::CmpFPredicate::OEQ>,
4137 CaseXZEqOpConversion<CaseZEqOp, true>,
4138 CaseXZEqOpConversion<CaseXZEqOp, false>,
4139 HandleCmpOpConversion<HandleEqOp, LLVM::ICmpPredicate::eq>,
4140 HandleCmpOpConversion<HandleNeOp, LLVM::ICmpPredicate::ne>,
4141
4142 // Patterns of structural operations.
4143 SVModuleOpConversion,
4144 InstanceOpConversion,
4145 ProcedureOpConversion,
4146 CoroutineOpConversion,
4147 CallCoroutineOpConversion,
4148 WaitEventOpConversion,
4149
4150 // Patterns of shifting operations.
4151 ShrOpConversion,
4152 ShlOpConversion,
4153 AShrOpConversion,
4154
4155 // Patterns of assignment operations.
4156 AssignOpConversion<ContinuousAssignOp>,
4157 AssignOpConversion<DelayedContinuousAssignOp>,
4158 AssignOpConversion<BlockingAssignOp>,
4159 AssignOpConversion<NonBlockingAssignOp>,
4160 AssignOpConversion<DelayedNonBlockingAssignOp>,
4161 AssignedVariableOpConversion,
4162
4163 // Patterns of other operations outside Moore dialect.
4164 HWInstanceOpConversion,
4165 ReturnOpConversion,
4166 CallOpConversion,
4167 DPIFuncOpConversion,
4168 FuncDPICallOpConversion,
4169 UnrealizedConversionCastConversion,
4170 InPlaceOpConversion<debug::ArrayOp>,
4171 InPlaceOpConversion<debug::StructOp>,
4172 InPlaceOpConversion<debug::VariableOp>,
4173
4174 // Patterns of assert-like operations
4175 AssertLikeOpConversion<AssertOp, verif::AssertOp>,
4176 AssertLikeOpConversion<AssumeOp, verif::AssumeOp>,
4177 AssertLikeOpConversion<CoverOp, verif::CoverOp>,
4178
4179 // Format strings.
4180 FormatLiteralOpConversion,
4181 FormatStringOpConversion,
4182 FormatConcatOpConversion,
4183 FormatHierPathOpConversion,
4184 FormatIntOpConversion,
4185 FormatRealOpConversion,
4186 FormatCharOpConversion,
4187 DisplayBIOpConversion,
4188 FDisplayBIOpConversion,
4189
4190 // File I/O operations
4191 FOpenBIOpConversion,
4192 FCloseBIOpConversion,
4193 FFlushBIOpConversion,
4194 ReadMemBIOpConversion,
4195
4196 // Command line input operations
4197 PlusArgsTestBIOpConversion,
4198 PlusArgsValueBIOpConversion,
4199
4200 // Dynamic string operations
4201 StringLenOpConversion,
4202 StringConcatOpConversion,
4203 StringGetOpConversion,
4204 StringCmpOpConversion,
4205
4206 // Queue operations
4207 QueueSizeBIOpConversion,
4208 QueuePushBackOpConversion,
4209 QueuePushFrontOpConversion,
4210 QueuePopBackOpConversion,
4211 QueuePopFrontOpConversion,
4212 QueueDeleteOpConversion,
4213 QueueInsertOpConversion,
4214 QueueClearOpConversion,
4215 DynQueueExtractOpConversion,
4216 QueueResizeOpConversion,
4217 QueueSetOpConversion,
4218 QueueCmpOpConversion,
4219 QueueFromUnpackedArrayOpConversion,
4220 QueueConcatOpConversion,
4221
4222 // Associative array operations
4223 AssocArrayExtractOpConversion,
4224 AssocArraySetOpConversion,
4225 AssocArrayDeleteOpConversion,
4226 AssocArrayClearOpConversion,
4227 AssocArraySizeOpConversion,
4228 AssocArrayExistsOpConversion,
4229 AssocArrayFirstOpConversion,
4230 AssocArrayLastOpConversion,
4231 AssocArrayNextOpConversion,
4232 AssocArrayPrevOpConversion
4233 >(typeConverter, patterns.getContext());
4234 // clang-format on
4235
4236 // Structural operations
4237 patterns.add<WaitDelayOp>(convert);
4238 patterns.add<UnreachableOp>(convert);
4239 patterns.add<GlobalVariableOp>(convert);
4240 patterns.add<GetGlobalVariableOp>(convert);
4241
4242 // Simulation control
4243 patterns.add<StopBIOp>(convert);
4244 patterns.add<SeverityBIOp>(convert);
4245 patterns.add<FinishBIOp>(convert);
4246 patterns.add<FinishMessageBIOp>(convert);
4247
4248 // Random builtins
4249 patterns.add<UrandomRangeBIOp>(convert, funcCache);
4250
4251 // Timing control
4252 patterns.add<TimeBIOp>(convert);
4253 patterns.add<LogicToTimeOp>(convert);
4254 patterns.add<TimeToLogicOp>(convert);
4255
4256 mlir::populateAnyFunctionOpInterfaceTypeConversionPattern(patterns,
4257 typeConverter);
4258 hw::populateHWModuleLikeTypeConversionPattern(
4259 hw::HWModuleOp::getOperationName(), patterns, typeConverter);
4260 populateSCFToControlFlowConversionPatterns(patterns);
4261 populateArithToCombPatterns(patterns, typeConverter);
4262}
4263
4264//===----------------------------------------------------------------------===//
4265// Moore to Core Conversion Pass
4266//===----------------------------------------------------------------------===//
4267
4268namespace {
4269struct MooreToCorePass
4270 : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
4271 void runOnOperation() override;
4272};
4273} // namespace
4274
4275/// Create a Moore to core dialects conversion pass.
4276std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
4277 return std::make_unique<MooreToCorePass>();
4278}
4279
4280/// This is the main entrypoint for the Moore to Core conversion pass.
4281void MooreToCorePass::runOnOperation() {
4282 MLIRContext &context = getContext();
4283 ModuleOp module = getOperation();
4284 ClassTypeCache classCache;
4285 auto &symbolTable = getAnalysis<SymbolTable>();
4286 FunctionCache funcCache(symbolTable);
4287
4288 IRRewriter rewriter(module);
4289 (void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
4290
4291 TypeConverter typeConverter;
4292 populateTypeConversion(typeConverter);
4293
4294 ConversionTarget target(context);
4295 populateLegality(target, typeConverter);
4296
4297 ConversionPatternSet patterns(&context, typeConverter);
4298 populateOpConversion(patterns, typeConverter, classCache, funcCache);
4299 mlir::cf::populateCFStructuralTypeConversionsAndLegality(typeConverter,
4300 patterns, target);
4301
4302 if (failed(applyFullConversion(module, target, std::move(patterns))))
4303 signalPassFailure();
4304}
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static std::unique_ptr< Context > context
static FIRRTLBaseType convertType(FIRRTLBaseType type)
Returns null type if no conversion is needed.
Definition DropConst.cpp:32
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 populateOpConversion(ConversionPatternSet &patterns, TypeConverter &typeConverter, ClassTypeCache &classCache, FunctionCache &funcCache)
static void populateLegality(ConversionTarget &target, const TypeConverter &converter)
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(elements, Type result_type=None)
Definition hw.py:483
create(array_value, idx)
Definition hw.py:450
create(data_type, value)
Definition hw.py:441
create(data_type, value)
Definition hw.py:433
create(elements, Type result_type=None)
Definition hw.py:544
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
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.
Definition comb.py:1
Definition sim.py:1
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.