CIRCT 24.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 auto loc = op.getLoc();
1402
1403 // Bit-addressed extract out of an integer into any bitcastable result.
1404 if (isa<IntegerType>(inputType)) {
1405 int32_t inputWidth = inputType.getIntOrFloatBitWidth();
1406 int32_t resultWidth = hw::getBitWidth(resultType);
1407 if (resultWidth < 0)
1408 return failure();
1409
1410 int32_t high = low + resultWidth;
1411 int32_t lsbPad = std::clamp(-low, 0, resultWidth);
1412 int32_t msbPad = std::clamp(high - inputWidth, 0, resultWidth - lsbPad);
1413 int32_t extractWidth = resultWidth - lsbPad - msbPad;
1414
1415 SmallVector<Value> sbv;
1416 if (msbPad > 0)
1417 sbv.push_back(hw::ConstantOp::create(rewriter, loc, APInt(msbPad, 0)));
1418
1419 if (extractWidth > 0)
1420 sbv.push_back(rewriter.createOrFold<comb::ExtractOp>(
1421 loc, rewriter.getIntegerType(extractWidth), input,
1422 std::max(low, 0)));
1423
1424 if (lsbPad > 0)
1425 sbv.push_back(hw::ConstantOp::create(rewriter, loc, APInt(lsbPad, 0)));
1426
1427 Value res = rewriter.createOrFold<comb::ConcatOp>(loc, sbv);
1428 if (res.getType() != resultType)
1429 res = rewriter.createOrFold<hw::BitcastOp>(loc, resultType, res);
1430
1431 rewriter.replaceOp(op, res);
1432 return success();
1433 }
1434
1435 // Element-addressed extract out of an array
1436 if (auto arrTy = dyn_cast<hw::ArrayType>(inputType)) {
1437 Type elementType = arrTy.getElementType();
1438 int32_t idxWidth = llvm::Log2_64_Ceil(arrTy.getNumElements());
1439 int32_t inputWidth = arrTy.getNumElements();
1440
1441 // Array element
1442 if (resultType == elementType) {
1443 if (low < 0 || low >= inputWidth) {
1444 auto zeros = createZeroValue(resultType, loc, rewriter);
1445 if (!zeros)
1446 return failure();
1447 rewriter.replaceOp(op, zeros);
1448 } else {
1449 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(
1450 op, input,
1451 hw::ConstantOp::create(rewriter, loc,
1452 rewriter.getIntegerType(idxWidth), low));
1453 }
1454 return success();
1455 }
1456
1457 // Array slice
1458 if (auto resArrTy = dyn_cast<hw::ArrayType>(resultType);
1459 resArrTy && resArrTy.getElementType() == elementType) {
1460 int32_t resultWidth = resArrTy.getNumElements();
1461 int32_t high = low + resultWidth;
1462
1463 int32_t lsbPad = std::clamp(-low, 0, resultWidth);
1464 int32_t msbPad = std::clamp(high - inputWidth, 0, resultWidth - lsbPad);
1465 int32_t extractWidth = resultWidth - lsbPad - msbPad;
1466
1467 SmallVector<Value> toConcat;
1468 if (msbPad > 0) {
1469 auto zeros = createZeroValue(hw::ArrayType::get(elementType, msbPad),
1470 loc, rewriter);
1471 if (!zeros)
1472 return failure();
1473 toConcat.push_back(zeros);
1474 }
1475
1476 if (extractWidth > 0)
1477 toConcat.push_back(rewriter.createOrFold<hw::ArraySliceOp>(
1478 loc, hw::ArrayType::get(elementType, extractWidth), input,
1479 hw::ConstantOp::create(rewriter, loc,
1480 rewriter.getIntegerType(idxWidth),
1481 std::max(low, 0))));
1482
1483 if (lsbPad > 0) {
1484 auto zeros = createZeroValue(hw::ArrayType::get(elementType, lsbPad),
1485 loc, rewriter);
1486 if (!zeros)
1487 return failure();
1488 toConcat.push_back(zeros);
1489 }
1490
1491 rewriter.replaceOp(
1492 op, rewriter.createOrFold<hw::ArrayConcatOp>(loc, toConcat));
1493 return success();
1494 }
1495 }
1496
1497 return failure();
1498 }
1499};
1500
1501struct ExtractRefOpConversion : public OpConversionPattern<ExtractRefOp> {
1502 using OpConversionPattern::OpConversionPattern;
1503
1504 LogicalResult
1505 matchAndRewrite(ExtractRefOp op, OpAdaptor adaptor,
1506 ConversionPatternRewriter &rewriter) const override {
1507 // TODO: properly handle out-of-bounds accesses
1508 Type resultType = typeConverter->convertType(op.getResult().getType());
1509 Type inputType =
1510 cast<llhd::RefType>(adaptor.getInput().getType()).getNestedType();
1511
1512 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1513 int64_t width = hw::getBitWidth(inputType);
1514 if (width == -1)
1515 return failure();
1516
1517 Value lowBit = hw::ConstantOp::create(
1518 rewriter, op.getLoc(),
1519 rewriter.getIntegerType(llvm::Log2_64_Ceil(width)),
1520 adaptor.getLowBit());
1521 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
1522 op, resultType, adaptor.getInput(), lowBit);
1523 return success();
1524 }
1525
1526 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1527 Value lowBit = hw::ConstantOp::create(
1528 rewriter, op.getLoc(),
1529 rewriter.getIntegerType(llvm::Log2_64_Ceil(arrType.getNumElements())),
1530 adaptor.getLowBit());
1531
1532 // If the result type is not the same as the array's element type, then
1533 // it has to be a slice.
1534 if (arrType.getElementType() !=
1535 cast<llhd::RefType>(resultType).getNestedType()) {
1536 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
1537 op, resultType, adaptor.getInput(), lowBit);
1538 return success();
1539 }
1540
1541 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
1542 lowBit);
1543 return success();
1544 }
1545
1546 return failure();
1547 }
1548};
1549
1550struct DynExtractOpConversion : public OpConversionPattern<DynExtractOp> {
1551 using OpConversionPattern::OpConversionPattern;
1552
1553 LogicalResult
1554 matchAndRewrite(DynExtractOp op, OpAdaptor adaptor,
1555 ConversionPatternRewriter &rewriter) const override {
1556 Type resultType = typeConverter->convertType(op.getResult().getType());
1557 Type inputType = adaptor.getInput().getType();
1558
1559 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1560 Value amount = adjustIntegerWidth(rewriter, adaptor.getLowBit(),
1561 intType.getWidth(), op->getLoc());
1562 Value value = comb::ShrUOp::create(rewriter, op->getLoc(),
1563 adaptor.getInput(), amount);
1564
1565 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, value, 0);
1566 return success();
1567 }
1568
1569 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1570 unsigned idxWidth = llvm::Log2_64_Ceil(arrType.getNumElements());
1571 Value idx = adjustIntegerWidth(rewriter, adaptor.getLowBit(), idxWidth,
1572 op->getLoc());
1573
1574 bool isSingleElementExtract = arrType.getElementType() == resultType;
1575
1576 if (isSingleElementExtract)
1577 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(),
1578 idx);
1579 else
1580 rewriter.replaceOpWithNewOp<hw::ArraySliceOp>(op, resultType,
1581 adaptor.getInput(), idx);
1582
1583 return success();
1584 }
1585
1586 return failure();
1587 }
1588};
1589
1590struct DynExtractRefOpConversion : public OpConversionPattern<DynExtractRefOp> {
1591 using OpConversionPattern::OpConversionPattern;
1592
1593 LogicalResult
1594 matchAndRewrite(DynExtractRefOp op, OpAdaptor adaptor,
1595 ConversionPatternRewriter &rewriter) const override {
1596 // TODO: properly handle out-of-bounds accesses
1597 Type resultType = typeConverter->convertType(op.getResult().getType());
1598 Type inputType =
1599 cast<llhd::RefType>(adaptor.getInput().getType()).getNestedType();
1600
1601 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1602 int64_t width = hw::getBitWidth(inputType);
1603 if (width == -1)
1604 return failure();
1605
1606 Value amount =
1607 adjustIntegerWidth(rewriter, adaptor.getLowBit(),
1608 llvm::Log2_64_Ceil(width), op->getLoc());
1609 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
1610 op, resultType, adaptor.getInput(), amount);
1611 return success();
1612 }
1613
1614 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1615 Value idx = adjustIntegerWidth(
1616 rewriter, adaptor.getLowBit(),
1617 llvm::Log2_64_Ceil(arrType.getNumElements()), op->getLoc());
1618
1619 auto resultNestedType = cast<llhd::RefType>(resultType).getNestedType();
1620 bool isSingleElementExtract =
1621 arrType.getElementType() == resultNestedType;
1622
1623 if (isSingleElementExtract)
1624 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
1625 idx);
1626 else
1627 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
1628 op, resultType, adaptor.getInput(), idx);
1629
1630 return success();
1631 }
1632
1633 return failure();
1634 }
1635};
1636
1637struct ArrayCreateOpConversion : public OpConversionPattern<ArrayCreateOp> {
1638 using OpConversionPattern::OpConversionPattern;
1639
1640 LogicalResult
1641 matchAndRewrite(ArrayCreateOp op, OpAdaptor adaptor,
1642 ConversionPatternRewriter &rewriter) const override {
1643 Type resultType = typeConverter->convertType(op.getResult().getType());
1644 rewriter.replaceOpWithNewOp<hw::ArrayCreateOp>(op, resultType,
1645 adaptor.getElements());
1646 return success();
1647 }
1648};
1649
1650struct StructCreateOpConversion : public OpConversionPattern<StructCreateOp> {
1651 using OpConversionPattern::OpConversionPattern;
1652
1653 LogicalResult
1654 matchAndRewrite(StructCreateOp op, OpAdaptor adaptor,
1655 ConversionPatternRewriter &rewriter) const override {
1656 Type resultType = typeConverter->convertType(op.getResult().getType());
1657 rewriter.replaceOpWithNewOp<hw::StructCreateOp>(op, resultType,
1658 adaptor.getFields());
1659 return success();
1660 }
1661};
1662
1663struct StructExtractOpConversion : public OpConversionPattern<StructExtractOp> {
1664 using OpConversionPattern::OpConversionPattern;
1665
1666 LogicalResult
1667 matchAndRewrite(StructExtractOp op, OpAdaptor adaptor,
1668 ConversionPatternRewriter &rewriter) const override {
1669 rewriter.replaceOpWithNewOp<hw::StructExtractOp>(
1670 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1671 return success();
1672 }
1673};
1674
1675struct StructExtractRefOpConversion
1676 : public OpConversionPattern<StructExtractRefOp> {
1677 using OpConversionPattern::OpConversionPattern;
1678
1679 LogicalResult
1680 matchAndRewrite(StructExtractRefOp op, OpAdaptor adaptor,
1681 ConversionPatternRewriter &rewriter) const override {
1682 rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
1683 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1684 return success();
1685 }
1686};
1687
1688struct UnionCreateOpConversion : public OpConversionPattern<UnionCreateOp> {
1689 using OpConversionPattern::OpConversionPattern;
1690
1691 LogicalResult
1692 matchAndRewrite(UnionCreateOp op, OpAdaptor adaptor,
1693 ConversionPatternRewriter &rewriter) const override {
1694 Type resultType = typeConverter->convertType(op.getResult().getType());
1695 rewriter.replaceOpWithNewOp<hw::UnionCreateOp>(
1696 op, resultType, adaptor.getFieldNameAttr(), adaptor.getInput());
1697 return success();
1698 }
1699};
1700
1701struct UnionExtractOpConversion : public OpConversionPattern<UnionExtractOp> {
1702 using OpConversionPattern::OpConversionPattern;
1703
1704 LogicalResult
1705 matchAndRewrite(UnionExtractOp op, OpAdaptor adaptor,
1706 ConversionPatternRewriter &rewriter) const override {
1707 rewriter.replaceOpWithNewOp<hw::UnionExtractOp>(op, adaptor.getInput(),
1708 adaptor.getFieldNameAttr());
1709 return success();
1710 }
1711};
1712
1713struct UnionExtractRefOpConversion
1714 : public OpConversionPattern<UnionExtractRefOp> {
1715 using OpConversionPattern::OpConversionPattern;
1716
1717 LogicalResult
1718 matchAndRewrite(UnionExtractRefOp op, OpAdaptor adaptor,
1719 ConversionPatternRewriter &rewriter) const override {
1720 rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
1721 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1722 return success();
1723 }
1724};
1725
1726struct ReduceAndOpConversion : public OpConversionPattern<ReduceAndOp> {
1727 using OpConversionPattern::OpConversionPattern;
1728 LogicalResult
1729 matchAndRewrite(ReduceAndOp op, OpAdaptor adaptor,
1730 ConversionPatternRewriter &rewriter) const override {
1731 Type resultType = typeConverter->convertType(op.getInput().getType());
1732 Value max = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, -1);
1733
1734 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::eq,
1735 adaptor.getInput(), max);
1736 return success();
1737 }
1738};
1739
1740struct ReduceOrOpConversion : public OpConversionPattern<ReduceOrOp> {
1741 using OpConversionPattern::OpConversionPattern;
1742 LogicalResult
1743 matchAndRewrite(ReduceOrOp op, OpAdaptor adaptor,
1744 ConversionPatternRewriter &rewriter) const override {
1745 Type resultType = typeConverter->convertType(op.getInput().getType());
1746 Value zero = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1747
1748 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1749 adaptor.getInput(), zero);
1750 return success();
1751 }
1752};
1753
1754struct ReduceXorOpConversion : public OpConversionPattern<ReduceXorOp> {
1755 using OpConversionPattern::OpConversionPattern;
1756 LogicalResult
1757 matchAndRewrite(ReduceXorOp op, OpAdaptor adaptor,
1758 ConversionPatternRewriter &rewriter) const override {
1759
1760 rewriter.replaceOpWithNewOp<comb::ParityOp>(op, adaptor.getInput());
1761 return success();
1762 }
1763};
1764
1765struct BoolCastOpConversion : public OpConversionPattern<BoolCastOp> {
1766 using OpConversionPattern::OpConversionPattern;
1767 LogicalResult
1768 matchAndRewrite(BoolCastOp op, OpAdaptor adaptor,
1769 ConversionPatternRewriter &rewriter) const override {
1770 Type resultType = typeConverter->convertType(op.getInput().getType());
1771 if (isa_and_nonnull<IntegerType>(resultType)) {
1772 Value zero =
1773 hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1774 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1775 adaptor.getInput(), zero);
1776 return success();
1777 }
1778 if (isa_and_nonnull<FloatType>(resultType)) {
1779 Value zero = arith::ConstantOp::create(
1780 rewriter, op->getLoc(), rewriter.getFloatAttr(resultType, 0.0));
1781 rewriter.replaceOpWithNewOp<arith::CmpFOp>(op, arith::CmpFPredicate::ONE,
1782 adaptor.getInput(), zero);
1783 return success();
1784 }
1785 if (isa_and_nonnull<llhd::TimeType>(resultType)) {
1786 Value timeInt =
1787 llhd::TimeToIntOp::create(rewriter, op->getLoc(), adaptor.getInput());
1788 Value zero = hw::ConstantOp::create(rewriter, op->getLoc(),
1789 rewriter.getI64Type(), 0);
1790 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1791 timeInt, zero);
1792 return success();
1793 }
1794 if (isa_and_nonnull<hw::StructType, hw::ArrayType, hw::UnionType>(
1795 resultType)) {
1796 int64_t width = hw::getBitWidth(resultType);
1797 if (width < 0)
1798 return failure();
1799 auto intTy = rewriter.getIntegerType(width);
1800 Value input = rewriter.createOrFold<hw::BitcastOp>(op->getLoc(), intTy,
1801 adaptor.getInput());
1802 Value zero = hw::ConstantOp::create(rewriter, op->getLoc(), intTy, 0);
1803 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1804 input, zero);
1805 return success();
1806 }
1807 return failure();
1808 }
1809};
1810
1811struct NotOpConversion : public OpConversionPattern<NotOp> {
1812 using OpConversionPattern::OpConversionPattern;
1813 LogicalResult
1814 matchAndRewrite(NotOp op, OpAdaptor adaptor,
1815 ConversionPatternRewriter &rewriter) const override {
1816 Type resultType =
1817 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1818 Value max = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, -1);
1819
1820 rewriter.replaceOpWithNewOp<comb::XorOp>(op, adaptor.getInput(), max);
1821 return success();
1822 }
1823};
1824
1825struct NegOpConversion : public OpConversionPattern<NegOp> {
1826 using OpConversionPattern::OpConversionPattern;
1827 LogicalResult
1828 matchAndRewrite(NegOp op, OpAdaptor adaptor,
1829 ConversionPatternRewriter &rewriter) const override {
1830 Type resultType =
1831 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1832 Value zero = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, 0);
1833
1834 rewriter.replaceOpWithNewOp<comb::SubOp>(op, zero, adaptor.getInput());
1835 return success();
1836 }
1837};
1838
1839struct NegRealOpConversion : public OpConversionPattern<NegRealOp> {
1840 using OpConversionPattern::OpConversionPattern;
1841 LogicalResult
1842 matchAndRewrite(NegRealOp op, OpAdaptor adaptor,
1843 ConversionPatternRewriter &rewriter) const override {
1844 rewriter.replaceOpWithNewOp<arith::NegFOp>(op, adaptor.getInput());
1845 return success();
1846 }
1847};
1848
1849template <typename SourceOp, typename TargetOp>
1850struct BinaryOpConversion : public OpConversionPattern<SourceOp> {
1852 using OpAdaptor = typename SourceOp::Adaptor;
1853
1854 LogicalResult
1855 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1856 ConversionPatternRewriter &rewriter) const override {
1857 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1858 adaptor.getRhs(), false);
1859 return success();
1860 }
1861};
1862
1863template <typename SourceOp, typename TargetOp>
1864struct BinaryRealOpConversion : public OpConversionPattern<SourceOp> {
1866 using OpAdaptor = typename SourceOp::Adaptor;
1867
1868 LogicalResult
1869 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1870 ConversionPatternRewriter &rewriter) const override {
1871 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1872 adaptor.getRhs());
1873 return success();
1874 }
1875};
1876
1877struct HypotBIOpConversion : public OpConversionPattern<HypotBIOp> {
1878 using OpConversionPattern::OpConversionPattern;
1879 LogicalResult
1880 matchAndRewrite(HypotBIOp op, OpAdaptor adaptor,
1881 ConversionPatternRewriter &rewriter) const override {
1882 Value lhs = adaptor.getLhs();
1883 Value rhs = adaptor.getRhs();
1884 ImplicitLocOpBuilder b(op->getLoc(), rewriter);
1885 auto left = arith::MulFOp::create(b, lhs, lhs);
1886 auto right = arith::MulFOp::create(b, rhs, rhs);
1887 auto sum = arith::AddFOp::create(b, left, right);
1888 auto out = math::SqrtOp::create(b, sum);
1889 rewriter.replaceOp(op, out);
1890 return success();
1891 }
1892};
1893
1894template <typename SourceOp, typename TargetOp>
1895struct RealMathFunc : public OpConversionPattern<SourceOp> {
1897 using OpAdaptor = typename SourceOp::Adaptor;
1898
1899 LogicalResult
1900 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1901 ConversionPatternRewriter &rewriter) const override {
1902 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getValue());
1903 return success();
1904 }
1905};
1906
1907template <typename SourceOp, ICmpPredicate pred>
1908struct ICmpOpConversion : public OpConversionPattern<SourceOp> {
1910 using OpAdaptor = typename SourceOp::Adaptor;
1911
1912 LogicalResult
1913 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1914 ConversionPatternRewriter &rewriter) const override {
1915 Type resultType =
1916 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1917
1918 rewriter.replaceOpWithNewOp<comb::ICmpOp>(
1919 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1920 return success();
1921 }
1922};
1923
1924struct NullOpConversion : public OpConversionPattern<NullOp> {
1925 using OpConversionPattern::OpConversionPattern;
1926
1927 LogicalResult
1928 matchAndRewrite(NullOp op, OpAdaptor adaptor,
1929 ConversionPatternRewriter &rewriter) const override {
1930 Type ptrTy = getTypeConverter()->convertType(op.getResult().getType());
1931 if (!ptrTy)
1932 return rewriter.notifyMatchFailure(op, "failed to convert null type");
1933 rewriter.replaceOpWithNewOp<LLVM::ZeroOp>(op, ptrTy);
1934 return success();
1935 }
1936};
1937
1938template <typename SourceOp, LLVM::ICmpPredicate pred>
1939struct HandleCmpOpConversion : public OpConversionPattern<SourceOp> {
1941 using OpAdaptor = typename SourceOp::Adaptor;
1942
1943 LogicalResult
1944 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1945 ConversionPatternRewriter &rewriter) const override {
1946 rewriter.replaceOpWithNewOp<LLVM::ICmpOp>(op, pred, adaptor.getLhs(),
1947 adaptor.getRhs());
1948 return success();
1949 }
1950};
1951
1952template <typename SourceOp, arith::CmpFPredicate pred>
1953struct FCmpOpConversion : public OpConversionPattern<SourceOp> {
1955 using OpAdaptor = typename SourceOp::Adaptor;
1956
1957 LogicalResult
1958 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1959 ConversionPatternRewriter &rewriter) const override {
1960 Type resultType =
1961 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1962
1963 rewriter.replaceOpWithNewOp<arith::CmpFOp>(
1964 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1965 return success();
1966 }
1967};
1968
1969template <typename SourceOp, bool withoutX>
1970struct CaseXZEqOpConversion : public OpConversionPattern<SourceOp> {
1972 using OpAdaptor = typename SourceOp::Adaptor;
1973
1974 LogicalResult
1975 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1976 ConversionPatternRewriter &rewriter) const override {
1977 // Check each operand if it is a known constant and extract the X and/or Z
1978 // bits to be ignored.
1979 // TODO: Once the core dialects support four-valued integers, we will have
1980 // to create ops that extract X and Z bits from the operands, since we also
1981 // have to do the right casez/casex comparison on non-constant inputs.
1982 unsigned bitWidth = op.getLhs().getType().getWidth();
1983 auto ignoredBits = APInt::getZero(bitWidth);
1984 auto detectIgnoredBits = [&](Value value) {
1985 auto constOp = value.getDefiningOp<ConstantOp>();
1986 if (!constOp)
1987 return;
1988 auto constValue = constOp.getValue();
1989 if (withoutX)
1990 ignoredBits |= constValue.getZBits();
1991 else
1992 ignoredBits |= constValue.getUnknownBits();
1993 };
1994 detectIgnoredBits(op.getLhs());
1995 detectIgnoredBits(op.getRhs());
1996
1997 // If we have detected any bits to be ignored, mask them in the operands for
1998 // the comparison.
1999 Value lhs = adaptor.getLhs();
2000 Value rhs = adaptor.getRhs();
2001 if (!ignoredBits.isZero()) {
2002 ignoredBits.flipAllBits();
2003 auto maskOp = hw::ConstantOp::create(rewriter, op.getLoc(), ignoredBits);
2004 lhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), lhs, maskOp);
2005 rhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), rhs, maskOp);
2006 }
2007
2008 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, ICmpPredicate::ceq, lhs, rhs);
2009 return success();
2010 }
2011};
2012
2013//===----------------------------------------------------------------------===//
2014// Conversions
2015//===----------------------------------------------------------------------===//
2016
2017struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
2018 using OpConversionPattern::OpConversionPattern;
2019
2020 LogicalResult
2021 matchAndRewrite(ConversionOp op, OpAdaptor adaptor,
2022 ConversionPatternRewriter &rewriter) const override {
2023 Location loc = op.getLoc();
2024 Type resultType = typeConverter->convertType(op.getResult().getType());
2025 if (!resultType) {
2026 op.emitError("conversion result type is not currently supported");
2027 return failure();
2028 }
2029 int64_t inputBw = hw::getBitWidth(adaptor.getInput().getType());
2030 int64_t resultBw = hw::getBitWidth(resultType);
2031 if (inputBw == -1 || resultBw == -1) {
2032 if (isSupportedDpiOpenArrayCast(op.getInput().getType(),
2033 op.getResult().getType())) {
2034 rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
2035 op, resultType, adaptor.getInput());
2036 return success();
2037 }
2038 if (hasOpenArrayBoundaryType(op.getInput().getType()) ||
2039 hasOpenArrayBoundaryType(op.getResult().getType())) {
2040 op.emitError("unsupported DPI open-array conversion from ")
2041 << op.getInput().getType() << " to " << op.getResult().getType();
2042 return failure();
2043 }
2044 return failure();
2045 }
2046
2047 Value input = rewriter.createOrFold<hw::BitcastOp>(
2048 loc, rewriter.getIntegerType(inputBw), adaptor.getInput());
2049 Value amount = adjustIntegerWidth(rewriter, input, resultBw, loc);
2050
2051 Value result =
2052 rewriter.createOrFold<hw::BitcastOp>(loc, resultType, amount);
2053 rewriter.replaceOp(op, result);
2054 return success();
2055 }
2056};
2057
2058template <typename SourceOp>
2059struct BitcastConversion : public OpConversionPattern<SourceOp> {
2061 using OpAdaptor = typename SourceOp::Adaptor;
2062 using ConversionPattern::typeConverter;
2063
2064 LogicalResult
2065 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
2066 ConversionPatternRewriter &rewriter) const override {
2067 auto type = typeConverter->convertType(op.getResult().getType());
2068 if (type == adaptor.getInput().getType())
2069 rewriter.replaceOp(op, adaptor.getInput());
2070 else
2071 rewriter.replaceOpWithNewOp<hw::BitcastOp>(op, type, adaptor.getInput());
2072 return success();
2073 }
2074};
2075
2076/// For casts that are automatically resolved by type conversion
2077template <typename SourceOp>
2078struct NoOpConversion : public OpConversionPattern<SourceOp> {
2080 using OpAdaptor = typename SourceOp::Adaptor;
2081 using ConversionPattern::typeConverter;
2082
2083 LogicalResult
2084 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
2085 ConversionPatternRewriter &rewriter) const override {
2086 rewriter.replaceOp(op, adaptor.getInput());
2087 return success();
2088 }
2089};
2090
2091struct TruncOpConversion : public OpConversionPattern<TruncOp> {
2092 using OpConversionPattern::OpConversionPattern;
2093
2094 LogicalResult
2095 matchAndRewrite(TruncOp op, OpAdaptor adaptor,
2096 ConversionPatternRewriter &rewriter) const override {
2097 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, adaptor.getInput(), 0,
2098 op.getType().getWidth());
2099 return success();
2100 }
2101};
2102
2103struct ZExtOpConversion : public OpConversionPattern<ZExtOp> {
2104 using OpConversionPattern::OpConversionPattern;
2105
2106 LogicalResult
2107 matchAndRewrite(ZExtOp op, OpAdaptor adaptor,
2108 ConversionPatternRewriter &rewriter) const override {
2109 auto targetWidth = op.getType().getWidth();
2110 auto inputWidth = op.getInput().getType().getWidth();
2111
2112 auto zeroExt = hw::ConstantOp::create(
2113 rewriter, op.getLoc(),
2114 rewriter.getIntegerType(targetWidth - inputWidth), 0);
2115
2116 rewriter.replaceOpWithNewOp<comb::ConcatOp>(
2117 op, ValueRange{zeroExt, adaptor.getInput()});
2118 return success();
2119 }
2120};
2121
2122struct SExtOpConversion : public OpConversionPattern<SExtOp> {
2123 using OpConversionPattern::OpConversionPattern;
2124
2125 LogicalResult
2126 matchAndRewrite(SExtOp op, OpAdaptor adaptor,
2127 ConversionPatternRewriter &rewriter) const override {
2128 auto type = typeConverter->convertType(op.getType());
2129 auto value =
2130 comb::createOrFoldSExt(rewriter, op.getLoc(), adaptor.getInput(), type);
2131 rewriter.replaceOp(op, value);
2132 return success();
2133 }
2134};
2135
2136struct SIntToRealOpConversion : public OpConversionPattern<SIntToRealOp> {
2137 using OpConversionPattern::OpConversionPattern;
2138
2139 LogicalResult
2140 matchAndRewrite(SIntToRealOp op, OpAdaptor adaptor,
2141 ConversionPatternRewriter &rewriter) const override {
2142 rewriter.replaceOpWithNewOp<arith::SIToFPOp>(
2143 op, typeConverter->convertType(op.getType()), adaptor.getInput());
2144 return success();
2145 }
2146};
2147
2148struct UIntToRealOpConversion : public OpConversionPattern<UIntToRealOp> {
2149 using OpConversionPattern::OpConversionPattern;
2150
2151 LogicalResult
2152 matchAndRewrite(UIntToRealOp op, OpAdaptor adaptor,
2153 ConversionPatternRewriter &rewriter) const override {
2154 rewriter.replaceOpWithNewOp<arith::UIToFPOp>(
2155 op, typeConverter->convertType(op.getType()), adaptor.getInput());
2156 return success();
2157 }
2158};
2159
2160struct IntToStringOpConversion : public OpConversionPattern<IntToStringOp> {
2161 using OpConversionPattern::OpConversionPattern;
2162
2163 LogicalResult
2164 matchAndRewrite(IntToStringOp op, OpAdaptor adaptor,
2165 ConversionPatternRewriter &rewriter) const override {
2166 rewriter.replaceOpWithNewOp<sim::IntToStringOp>(op, adaptor.getInput());
2167 return success();
2168 }
2169};
2170
2171struct StringToIntOpConversion : public OpConversionPattern<StringToIntOp> {
2172 using OpConversionPattern::OpConversionPattern;
2173
2174 LogicalResult
2175 matchAndRewrite(StringToIntOp op, OpAdaptor adaptor,
2176 ConversionPatternRewriter &rewriter) const override {
2177 Type resultTy = typeConverter->convertType(op.getType());
2178 if (!resultTy)
2179 return failure();
2180 rewriter.replaceOpWithNewOp<sim::StringToIntOp>(op, resultTy,
2181 adaptor.getInput());
2182 return success();
2183 }
2184};
2185
2186struct FormatStringToStringOpConversion
2187 : public OpConversionPattern<FormatStringToStringOp> {
2188 using OpConversionPattern::OpConversionPattern;
2189
2190 LogicalResult
2191 matchAndRewrite(FormatStringToStringOp op, OpAdaptor adaptor,
2192 ConversionPatternRewriter &rewriter) const override {
2193 rewriter.replaceOpWithNewOp<sim::FormatToStringOp>(op,
2194 adaptor.getFmtstring());
2195 return success();
2196 }
2197};
2198
2199struct RealToIntOpConversion : public OpConversionPattern<RealToIntOp> {
2200 using OpConversionPattern::OpConversionPattern;
2201
2202 LogicalResult
2203 matchAndRewrite(RealToIntOp op, OpAdaptor adaptor,
2204 ConversionPatternRewriter &rewriter) const override {
2205 rewriter.replaceOpWithNewOp<arith::FPToSIOp>(
2206 op, typeConverter->convertType(op.getType()), adaptor.getInput());
2207 return success();
2208 }
2209};
2210
2211struct ConvertRealOpConversion : public OpConversionPattern<ConvertRealOp> {
2212 using OpConversionPattern::OpConversionPattern;
2213
2214 LogicalResult
2215 matchAndRewrite(ConvertRealOp op, OpAdaptor adaptor,
2216 ConversionPatternRewriter &rewriter) const override {
2217 op.getInput().getType().getWidth() < op.getResult().getType().getWidth()
2218 ? rewriter.replaceOpWithNewOp<arith::ExtFOp>(
2219 op, typeConverter->convertType(op.getType()), adaptor.getInput())
2220 : rewriter.replaceOpWithNewOp<arith::TruncFOp>(
2221 op, typeConverter->convertType(op.getType()), adaptor.getInput());
2222 return success();
2223 }
2224};
2225
2226template <typename SourceOp>
2227struct RealBitcastOpConversion : public OpConversionPattern<SourceOp> {
2229 using OpAdaptor = typename SourceOp::Adaptor;
2230
2231 LogicalResult
2232 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
2233 ConversionPatternRewriter &rewriter) const override {
2234 Type resultTy =
2235 ConversionPattern::typeConverter->convertType(op.getResult().getType());
2236 rewriter.replaceOpWithNewOp<arith::BitcastOp>(op, resultTy,
2237 adaptor.getValue());
2238 return success();
2239 }
2240};
2241
2242//===----------------------------------------------------------------------===//
2243// Statement Conversion
2244//===----------------------------------------------------------------------===//
2245
2246struct HWInstanceOpConversion : public OpConversionPattern<hw::InstanceOp> {
2247 using OpConversionPattern::OpConversionPattern;
2248
2249 LogicalResult
2250 matchAndRewrite(hw::InstanceOp op, OpAdaptor adaptor,
2251 ConversionPatternRewriter &rewriter) const override {
2252 SmallVector<Type> convResTypes;
2253 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
2254 return failure();
2255
2256 rewriter.replaceOpWithNewOp<hw::InstanceOp>(
2257 op, convResTypes, op.getInstanceName(), op.getModuleName(),
2258 adaptor.getOperands(), op.getArgNames(),
2259 op.getResultNames(), /*Parameter*/
2260 rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr);
2261
2262 return success();
2263 }
2264};
2265
2266struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
2267 using OpConversionPattern::OpConversionPattern;
2268
2269 LogicalResult
2270 matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
2271 ConversionPatternRewriter &rewriter) const override {
2272 rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
2273 return success();
2274 }
2275};
2276
2277struct CallOpConversion : public OpConversionPattern<func::CallOp> {
2278 using OpConversionPattern::OpConversionPattern;
2279
2280 LogicalResult
2281 matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
2282 ConversionPatternRewriter &rewriter) const override {
2283 SmallVector<Type> convResTypes;
2284 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
2285 return failure();
2286 rewriter.replaceOpWithNewOp<func::CallOp>(
2287 op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
2288 return success();
2289 }
2290};
2291
2292struct FuncDPICallOpConversion
2293 : public OpConversionPattern<moore::FuncDPICallOp> {
2294 using OpConversionPattern::OpConversionPattern;
2295
2296 LogicalResult
2297 matchAndRewrite(moore::FuncDPICallOp op, OpAdaptor adaptor,
2298 ConversionPatternRewriter &rewriter) const override {
2299 SmallVector<Type> convResTypes;
2300 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
2301 return failure();
2302 rewriter.replaceOpWithNewOp<sim::DPICallOp>(
2303 op, convResTypes, op.getCalleeAttr(), /*clock=*/Value(),
2304 /*enable=*/Value(), adaptor.getInputs());
2305 return success();
2306 }
2307};
2308
2309struct DPIFuncOpConversion : public OpConversionPattern<moore::DPIFuncOp> {
2310 using OpConversionPattern::OpConversionPattern;
2311
2312 LogicalResult
2313 matchAndRewrite(moore::DPIFuncOp op, OpAdaptor adaptor,
2314 ConversionPatternRewriter &rewriter) const override {
2315 // Map Moore DPIArgDirection to sim::DPIDirection.
2316 auto toDPIDir = [](moore::DPIArgDirection dir) -> sim::DPIDirection {
2317 switch (dir) {
2318 case moore::DPIArgDirection::In:
2319 return sim::DPIDirection::Input;
2320 case moore::DPIArgDirection::Out:
2321 return sim::DPIDirection::Output;
2322 case moore::DPIArgDirection::InOut:
2323 return sim::DPIDirection::InOut;
2324 case moore::DPIArgDirection::Return:
2325 return sim::DPIDirection::Return;
2326 }
2327 llvm_unreachable("unknown DPIArgDirection");
2328 };
2329
2330 // Reconstruct sim::DPIFunctionType from Moore's argument arrays.
2331 auto dirs = op.getDpiArgDirs();
2332 auto names = op.getDpiArgNames();
2333 SmallVector<Type> argTypes;
2334 op.getDPIArgTypes(argTypes);
2335
2336 SmallVector<sim::DPIArgument> dpiArguments;
2337 for (auto [dirAttr, nameAttr, mooreType] :
2338 llvm::zip(dirs, names, argTypes)) {
2339 auto dir = toDPIDir(cast<moore::DPIArgDirectionAttr>(dirAttr).getValue());
2340 auto name = cast<StringAttr>(nameAttr);
2341 Type coreType = typeConverter->convertType(mooreType);
2342 if (!coreType)
2343 return op.emitOpError("argument '")
2344 << name << "' has unsupported type " << mooreType;
2345 dpiArguments.push_back({name, coreType, dir});
2346 }
2347
2348 auto coreDPIFuncType =
2349 sim::DPIFunctionType::get(rewriter.getContext(), dpiArguments);
2350 auto simFunc = sim::DPIFuncOp::create(
2351 rewriter, op.getLoc(), op.getSymNameAttr(), coreDPIFuncType,
2352 op.getArgumentLocsAttr(), op.getVerilogNameAttr());
2353 SymbolTable::setSymbolVisibility(simFunc,
2354 SymbolTable::getSymbolVisibility(op));
2355 rewriter.eraseOp(op);
2356 return success();
2357 }
2358};
2359
2360struct UnrealizedConversionCastConversion
2361 : public OpConversionPattern<UnrealizedConversionCastOp> {
2362 using OpConversionPattern::OpConversionPattern;
2363
2364 LogicalResult
2365 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
2366 ConversionPatternRewriter &rewriter) const override {
2367 SmallVector<Type> convResTypes;
2368 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
2369 return failure();
2370
2371 // Drop the cast if the operand and result types agree after type
2372 // conversion.
2373 if (convResTypes == adaptor.getOperands().getTypes()) {
2374 rewriter.replaceOp(op, adaptor.getOperands());
2375 return success();
2376 }
2377
2378 rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
2379 op, convResTypes, adaptor.getOperands());
2380 return success();
2381 }
2382};
2383
2384struct ShlOpConversion : public OpConversionPattern<ShlOp> {
2385 using OpConversionPattern::OpConversionPattern;
2386
2387 LogicalResult
2388 matchAndRewrite(ShlOp op, OpAdaptor adaptor,
2389 ConversionPatternRewriter &rewriter) const override {
2390 Type resultType = typeConverter->convertType(op.getResult().getType());
2391
2392 // Comb shift operations require the same bit-width for value and amount
2393 Value amount =
2394 adjustIntegerWidth(rewriter, adaptor.getAmount(),
2395 resultType.getIntOrFloatBitWidth(), op->getLoc());
2396 rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
2397 amount, false);
2398 return success();
2399 }
2400};
2401
2402struct ShrOpConversion : public OpConversionPattern<ShrOp> {
2403 using OpConversionPattern::OpConversionPattern;
2404
2405 LogicalResult
2406 matchAndRewrite(ShrOp op, OpAdaptor adaptor,
2407 ConversionPatternRewriter &rewriter) const override {
2408 Type resultType = typeConverter->convertType(op.getResult().getType());
2409
2410 // Comb shift operations require the same bit-width for value and amount
2411 Value amount =
2412 adjustIntegerWidth(rewriter, adaptor.getAmount(),
2413 resultType.getIntOrFloatBitWidth(), op->getLoc());
2414 rewriter.replaceOpWithNewOp<comb::ShrUOp>(
2415 op, resultType, adaptor.getValue(), amount, false);
2416 return success();
2417 }
2418};
2419
2420struct PowUOpConversion : public OpConversionPattern<PowUOp> {
2421 using OpConversionPattern::OpConversionPattern;
2422
2423 LogicalResult
2424 matchAndRewrite(PowUOp op, OpAdaptor adaptor,
2425 ConversionPatternRewriter &rewriter) const override {
2426 Type resultType = typeConverter->convertType(op.getResult().getType());
2427
2428 Location loc = op->getLoc();
2429
2430 Value zeroVal = hw::ConstantOp::create(rewriter, loc, APInt(1, 0));
2431 // zero extend both LHS & RHS to ensure the unsigned integers are
2432 // interpreted correctly when calculating power
2433 auto lhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getLhs());
2434 auto rhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getRhs());
2435
2436 // lower the exponentiation via MLIR's math dialect
2437 auto pow = mlir::math::IPowIOp::create(rewriter, loc, lhs, rhs);
2438
2439 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, pow, 0);
2440 return success();
2441 }
2442};
2443
2444struct PowSOpConversion : public OpConversionPattern<PowSOp> {
2445 using OpConversionPattern::OpConversionPattern;
2446
2447 LogicalResult
2448 matchAndRewrite(PowSOp op, OpAdaptor adaptor,
2449 ConversionPatternRewriter &rewriter) const override {
2450 Type resultType = typeConverter->convertType(op.getResult().getType());
2451
2452 // utilize MLIR math dialect's math.ipowi to handle the exponentiation of
2453 // expression
2454 rewriter.replaceOpWithNewOp<mlir::math::IPowIOp>(
2455 op, resultType, adaptor.getLhs(), adaptor.getRhs());
2456 return success();
2457 }
2458};
2459
2460struct Clog2BIOpConversion : public OpConversionPattern<Clog2BIOp> {
2461 using OpConversionPattern::OpConversionPattern;
2462
2463 LogicalResult
2464 matchAndRewrite(Clog2BIOp op, OpAdaptor adaptor,
2465 ConversionPatternRewriter &rewriter) const override {
2466 Type resultType = typeConverter->convertType(op.getResult().getType());
2467 Location loc = op.getLoc();
2468 unsigned width = resultType.getIntOrFloatBitWidth();
2469 Value value = adaptor.getValue();
2470
2471 // Ceiling of log2 can be computed as follows:
2472 // if (x == 0) return 0;
2473 // else bitWidth(x) - countLeadingZeros(x - 1);
2474 Value zero = hw::ConstantOp::create(rewriter, loc, APInt(width, 0));
2475 Value one = hw::ConstantOp::create(rewriter, loc, APInt(width, 1));
2476 Value bitWidth = hw::ConstantOp::create(rewriter, loc, APInt(width, width));
2477
2478 Value valueMinusOne = comb::SubOp::create(rewriter, loc, value, one, false);
2479 Value clz =
2480 mlir::math::CountLeadingZerosOp::create(rewriter, loc, valueMinusOne);
2481 Value bitLength = comb::SubOp::create(rewriter, loc, bitWidth, clz, false);
2482
2483 Value isZero = comb::ICmpOp::create(rewriter, loc, comb::ICmpPredicate::eq,
2484 value, zero, false);
2485 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, isZero, zero, bitLength,
2486 false);
2487 return success();
2488 }
2489};
2490
2491struct AShrOpConversion : public OpConversionPattern<AShrOp> {
2492 using OpConversionPattern::OpConversionPattern;
2493
2494 LogicalResult
2495 matchAndRewrite(AShrOp op, OpAdaptor adaptor,
2496 ConversionPatternRewriter &rewriter) const override {
2497 Type resultType = typeConverter->convertType(op.getResult().getType());
2498
2499 // Comb shift operations require the same bit-width for value and amount
2500 Value amount =
2501 adjustIntegerWidth(rewriter, adaptor.getAmount(),
2502 resultType.getIntOrFloatBitWidth(), op->getLoc());
2503 rewriter.replaceOpWithNewOp<comb::ShrSOp>(
2504 op, resultType, adaptor.getValue(), amount, false);
2505 return success();
2506 }
2507};
2508
2509struct ReadOpConversion : public OpConversionPattern<ReadOp> {
2510 using OpConversionPattern::OpConversionPattern;
2511
2512 LogicalResult
2513 matchAndRewrite(ReadOp op, OpAdaptor adaptor,
2514 ConversionPatternRewriter &rewriter) const override {
2515 rewriter.replaceOpWithNewOp<llhd::ProbeOp>(op, adaptor.getInput());
2516 return success();
2517 }
2518};
2519
2520struct AssignedVariableOpConversion
2521 : public OpConversionPattern<AssignedVariableOp> {
2522 using OpConversionPattern::OpConversionPattern;
2523
2524 LogicalResult
2525 matchAndRewrite(AssignedVariableOp op, OpAdaptor adaptor,
2526 ConversionPatternRewriter &rewriter) const override {
2527 rewriter.replaceOpWithNewOp<hw::WireOp>(op, adaptor.getInput(),
2528 adaptor.getNameAttr());
2529 return success();
2530 }
2531};
2532
2533// Blocking and continuous assignments get a 0ns 0d 1e delay.
2534static llhd::TimeAttr
2535getBlockingOrContinuousAssignDelay(mlir::MLIRContext *context) {
2536 return llhd::TimeAttr::get(context, 0U, "ns", 0, 1);
2537}
2538
2539template <typename OpTy>
2540struct AssignOpConversion : public OpConversionPattern<OpTy> {
2542 using OpAdaptor = typename OpTy::Adaptor;
2543
2544 LogicalResult
2545 matchAndRewrite(OpTy op, OpAdaptor adaptor,
2546 ConversionPatternRewriter &rewriter) const override {
2547 // Determine the delay for the assignment.
2548 Value delay;
2549 if constexpr (std::is_same_v<OpTy, ContinuousAssignOp> ||
2550 std::is_same_v<OpTy, BlockingAssignOp>) {
2551 delay = llhd::ConstantTimeOp::create(
2552 rewriter, op->getLoc(),
2553 getBlockingOrContinuousAssignDelay(op->getContext()));
2554 } else if constexpr (std::is_same_v<OpTy, NonBlockingAssignOp>) {
2555 // Non-blocking assignments get a 0ns 1d 0e delay.
2556 delay = llhd::ConstantTimeOp::create(
2557 rewriter, op->getLoc(),
2558 llhd::TimeAttr::get(op->getContext(), 0U, "ns", 1, 0));
2559 } else {
2560 // Delayed assignments have a delay operand.
2561 delay = adaptor.getDelay();
2562 }
2563
2564 rewriter.replaceOpWithNewOp<llhd::DriveOp>(
2565 op, adaptor.getDst(), adaptor.getSrc(), delay, Value{});
2566 return success();
2567 }
2568};
2569
2570struct ConditionalOpConversion : public OpConversionPattern<ConditionalOp> {
2571 using OpConversionPattern::OpConversionPattern;
2572
2573 LogicalResult
2574 matchAndRewrite(ConditionalOp op, OpAdaptor adaptor,
2575 ConversionPatternRewriter &rewriter) const override {
2576 // TODO: This lowering is only correct if the condition is two-valued. If
2577 // the condition is X or Z, both branches of the conditional must be
2578 // evaluated and merged with the appropriate lookup table. See documentation
2579 // for `ConditionalOp`.
2580 auto type = typeConverter->convertType(op.getType());
2581
2582 auto hasNoWriteEffect = [](Region &region) {
2583 auto result = region.walk([](Operation *operation) {
2584 if (auto memOp = dyn_cast<MemoryEffectOpInterface>(operation))
2585 if (!memOp.hasEffect<MemoryEffects::Write>() &&
2586 !memOp.hasEffect<MemoryEffects::Free>())
2587 return WalkResult::advance();
2588
2589 if (operation->hasTrait<OpTrait::HasRecursiveMemoryEffects>())
2590 return WalkResult::advance();
2591
2592 return WalkResult::interrupt();
2593 });
2594 return !result.wasInterrupted();
2595 };
2596
2597 if (hasNoWriteEffect(op.getTrueRegion()) &&
2598 hasNoWriteEffect(op.getFalseRegion())) {
2599 Operation *trueTerm = op.getTrueRegion().front().getTerminator();
2600 Operation *falseTerm = op.getFalseRegion().front().getTerminator();
2601
2602 rewriter.inlineBlockBefore(&op.getTrueRegion().front(), op);
2603 rewriter.inlineBlockBefore(&op.getFalseRegion().front(), op);
2604
2605 Value convTrueVal = typeConverter->materializeTargetConversion(
2606 rewriter, op.getLoc(), type, trueTerm->getOperand(0));
2607 Value convFalseVal = typeConverter->materializeTargetConversion(
2608 rewriter, op.getLoc(), type, falseTerm->getOperand(0));
2609
2610 rewriter.eraseOp(trueTerm);
2611 rewriter.eraseOp(falseTerm);
2612
2613 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, adaptor.getCondition(),
2614 convTrueVal, convFalseVal);
2615 return success();
2616 }
2617
2618 auto ifOp =
2619 scf::IfOp::create(rewriter, op.getLoc(), type, adaptor.getCondition());
2620 rewriter.inlineRegionBefore(op.getTrueRegion(), ifOp.getThenRegion(),
2621 ifOp.getThenRegion().end());
2622 rewriter.inlineRegionBefore(op.getFalseRegion(), ifOp.getElseRegion(),
2623 ifOp.getElseRegion().end());
2624 rewriter.replaceOp(op, ifOp);
2625 return success();
2626 }
2627};
2628
2629struct YieldOpConversion : public OpConversionPattern<YieldOp> {
2630 using OpConversionPattern::OpConversionPattern;
2631
2632 LogicalResult
2633 matchAndRewrite(YieldOp op, OpAdaptor adaptor,
2634 ConversionPatternRewriter &rewriter) const override {
2635 Operation *parent = op->getParentOp();
2636 if (isa<llhd::GlobalSignalOp>(parent))
2637 rewriter.replaceOpWithNewOp<llhd::YieldOp>(op, adaptor.getResult());
2638 else if (isa<scf::ExecuteRegionOp, scf::ForOp, scf::IfOp,
2639 scf::IndexSwitchOp, scf::WhileOp>(parent))
2640 rewriter.replaceOpWithNewOp<scf::YieldOp>(op, adaptor.getResult());
2641 else
2642 return rewriter.notifyMatchFailure(
2643 op, "yield parent has not been converted to a legal region op yet");
2644 return success();
2645 }
2646};
2647
2648template <typename SourceOp>
2649struct InPlaceOpConversion : public OpConversionPattern<SourceOp> {
2651 using OpAdaptor = typename SourceOp::Adaptor;
2652
2653 LogicalResult
2654 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
2655 ConversionPatternRewriter &rewriter) const override {
2656 rewriter.modifyOpInPlace(op,
2657 [&]() { op->setOperands(adaptor.getOperands()); });
2658 return success();
2659 }
2660};
2661
2662template <typename MooreOpTy, typename VerifOpTy>
2663struct AssertLikeOpConversion : public OpConversionPattern<MooreOpTy> {
2665 using OpAdaptor = typename MooreOpTy::Adaptor;
2666
2667 LogicalResult
2668 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
2669 ConversionPatternRewriter &rewriter) const override {
2670 StringAttr label =
2671 op.getLabel().has_value()
2672 ? StringAttr::get(op->getContext(), op.getLabel().value())
2673 : StringAttr::get(op->getContext());
2674 rewriter.replaceOpWithNewOp<VerifOpTy>(op, adaptor.getCond(), mlir::Value(),
2675 label);
2676 return success();
2677 }
2678};
2679
2680//===----------------------------------------------------------------------===//
2681// Format String Conversion
2682//===----------------------------------------------------------------------===//
2683
2684struct FormatLiteralOpConversion : public OpConversionPattern<FormatLiteralOp> {
2685 using OpConversionPattern::OpConversionPattern;
2686
2687 LogicalResult
2688 matchAndRewrite(FormatLiteralOp op, OpAdaptor adaptor,
2689 ConversionPatternRewriter &rewriter) const override {
2690 rewriter.replaceOpWithNewOp<sim::FormatLiteralOp>(op, adaptor.getLiteral());
2691 return success();
2692 }
2693};
2694
2695struct FormatStringOpConversion : public OpConversionPattern<FormatStringOp> {
2696 using OpConversionPattern::OpConversionPattern;
2697
2698 LogicalResult
2699 matchAndRewrite(FormatStringOp op, OpAdaptor adaptor,
2700 ConversionPatternRewriter &rewriter) const override {
2701 char padChar =
2702 op.getPadding().value_or(IntPadding::Space) == IntPadding::Space ? 32
2703 : 48;
2704 IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar);
2705 auto widthAttr = adaptor.getWidthAttr();
2706
2707 bool isLeftAligned =
2708 op.getAlignment().value_or(IntAlign::Right) == IntAlign::Left;
2709 BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2710
2711 rewriter.replaceOpWithNewOp<sim::FormatStringOp>(
2712 op, adaptor.getString(), isLeftAlignedAttr, padCharAttr, widthAttr);
2713 return success();
2714 }
2715};
2716
2717struct FormatConcatOpConversion : public OpConversionPattern<FormatConcatOp> {
2718 using OpConversionPattern::OpConversionPattern;
2719
2720 LogicalResult
2721 matchAndRewrite(FormatConcatOp op, OpAdaptor adaptor,
2722 ConversionPatternRewriter &rewriter) const override {
2723 rewriter.replaceOpWithNewOp<sim::FormatStringConcatOp>(op,
2724 adaptor.getInputs());
2725 return success();
2726 }
2727};
2728
2729struct FormatHierPathOpConversion
2730 : public OpConversionPattern<FormatHierPathOp> {
2731 using OpConversionPattern::OpConversionPattern;
2732
2733 LogicalResult
2734 matchAndRewrite(FormatHierPathOp op, OpAdaptor adaptor,
2735 ConversionPatternRewriter &rewriter) const override {
2736 rewriter.replaceOpWithNewOp<sim::FormatHierPathOp>(op,
2737 adaptor.getUseEscapes());
2738 return success();
2739 }
2740};
2741
2742struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
2743 using OpConversionPattern::OpConversionPattern;
2744
2745 LogicalResult
2746 matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
2747 ConversionPatternRewriter &rewriter) const override {
2748
2749 char padChar = adaptor.getPadding() == IntPadding::Space ? 32 : 48;
2750 IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar);
2751 auto widthAttr = adaptor.getSpecifierWidthAttr();
2752
2753 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2754 BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2755
2756 switch (op.getFormat()) {
2757 case IntFormat::Decimal:
2758 rewriter.replaceOpWithNewOp<sim::FormatDecOp>(
2759 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr,
2760 adaptor.getIsSignedAttr());
2761 return success();
2762 case IntFormat::Binary:
2763 rewriter.replaceOpWithNewOp<sim::FormatBinOp>(
2764 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2765 return success();
2766 case IntFormat::Octal:
2767 rewriter.replaceOpWithNewOp<sim::FormatOctOp>(
2768 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2769 return success();
2770 case IntFormat::HexLower:
2771 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2772 op, adaptor.getValue(), rewriter.getBoolAttr(false),
2773 isLeftAlignedAttr, padCharAttr, widthAttr);
2774 return success();
2775 case IntFormat::HexUpper:
2776 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2777 op, adaptor.getValue(), rewriter.getBoolAttr(true), isLeftAlignedAttr,
2778 padCharAttr, widthAttr);
2779 return success();
2780 }
2781 return rewriter.notifyMatchFailure(op, "unsupported int format");
2782 }
2783};
2784
2785struct FormatRealOpConversion : public OpConversionPattern<FormatRealOp> {
2786 using OpConversionPattern::OpConversionPattern;
2787
2788 LogicalResult
2789 matchAndRewrite(FormatRealOp op, OpAdaptor adaptor,
2790 ConversionPatternRewriter &rewriter) const override {
2791 auto fracDigitsAttr = adaptor.getFracDigitsAttr();
2792
2793 auto fieldWidthAttr = adaptor.getFieldWidthAttr();
2794 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2795 mlir::BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2796
2797 switch (op.getFormat()) {
2798 case RealFormat::General:
2799 rewriter.replaceOpWithNewOp<sim::FormatGeneralOp>(
2800 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2801 fracDigitsAttr);
2802 return success();
2803 case RealFormat::Float:
2804 rewriter.replaceOpWithNewOp<sim::FormatFloatOp>(
2805 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2806 fracDigitsAttr);
2807 return success();
2808 case RealFormat::Exponential:
2809 rewriter.replaceOpWithNewOp<sim::FormatScientificOp>(
2810 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2811 fracDigitsAttr);
2812 return success();
2813 }
2814 }
2815};
2816
2817struct FormatCharOpConversion
2818 : public OpConversionPattern<moore::FormatCharOp> {
2819 using OpConversionPattern::OpConversionPattern;
2820 LogicalResult
2821 matchAndRewrite(moore::FormatCharOp op, OpAdaptor adaptor,
2822 ConversionPatternRewriter &rewriter) const override {
2823 rewriter.replaceOpWithNewOp<sim::FormatCharOp>(op, adaptor.getValue());
2824 return success();
2825 }
2826};
2827
2828struct StringLenOpConversion : public OpConversionPattern<StringLenOp> {
2829 using OpConversionPattern::OpConversionPattern;
2830
2831 LogicalResult
2832 matchAndRewrite(StringLenOp op, OpAdaptor adaptor,
2833 ConversionPatternRewriter &rewriter) const override {
2834 rewriter.replaceOpWithNewOp<sim::StringLengthOp>(op, adaptor.getStr());
2835 return success();
2836 }
2837};
2838
2839struct StringConcatOpConversion : public OpConversionPattern<StringConcatOp> {
2840 using OpConversionPattern::OpConversionPattern;
2841
2842 LogicalResult
2843 matchAndRewrite(StringConcatOp op, OpAdaptor adaptor,
2844 ConversionPatternRewriter &rewriter) const override {
2845 rewriter.replaceOpWithNewOp<sim::StringConcatOp>(op, adaptor.getInputs());
2846 return success();
2847 }
2848};
2849
2850struct StringGetOpConversion : public OpConversionPattern<StringGetOp> {
2851 using OpConversionPattern::OpConversionPattern;
2852
2853 LogicalResult
2854 matchAndRewrite(StringGetOp op, OpAdaptor adaptor,
2855 ConversionPatternRewriter &rewriter) const override {
2856 rewriter.replaceOpWithNewOp<sim::StringGetOp>(op, adaptor.getStr(),
2857 adaptor.getIndex());
2858 return success();
2859 }
2860};
2861
2862struct QueueSizeBIOpConversion : public OpConversionPattern<QueueSizeBIOp> {
2863 using OpConversionPattern::OpConversionPattern;
2864
2865 LogicalResult
2866 matchAndRewrite(QueueSizeBIOp op, OpAdaptor adaptor,
2867 ConversionPatternRewriter &rewriter) const override {
2868 rewriter.replaceOpWithNewOp<sim::QueueSizeOp>(op, adaptor.getQueue());
2869 return success();
2870 }
2871};
2872
2873struct DynQueueExtractOpConversion
2874 : public OpConversionPattern<DynQueueExtractOp> {
2875 using OpConversionPattern::OpConversionPattern;
2876
2877 LogicalResult
2878 matchAndRewrite(DynQueueExtractOp op, OpAdaptor adaptor,
2879 ConversionPatternRewriter &rewriter) const override {
2880 bool isSingleElementExtract =
2881 op.getInput().getType().getElementType() == op.getResult().getType();
2882
2883 if (isSingleElementExtract) {
2884 rewriter.replaceOpWithNewOp<sim::QueueGetOp>(op, adaptor.getInput(),
2885 adaptor.getLowerIdx());
2886 } else {
2887 rewriter.replaceOpWithNewOp<sim::QueueSliceOp>(
2888 op, adaptor.getInput(), adaptor.getLowerIdx(), adaptor.getUpperIdx());
2889 }
2890
2891 return success();
2892 }
2893};
2894
2895// Given a reference `ref` to some Moore type, this function emits a
2896// `ProbeOp` to read the contained value, then passes it to the function `func`.
2897// It finally emits a `DriveOp` to write the result of the function back to
2898// the referenced signal.
2899//
2900// This is useful for converting impure operations (such as the Moore ops for
2901// manipulating queues) into pure operations. (Which do not mutate the source
2902// value, instead returning a modified value.)
2903static void
2904probeRefAndDriveWithResult(OpBuilder &builder, Location loc, Value ref,
2905 const std::function<Value(Value)> &func) {
2906
2907 Value v = llhd::ProbeOp::create(builder, loc, ref);
2908
2909 // Drive using the same delay as a blocking assignment
2910 Value delay = llhd::ConstantTimeOp::create(
2911 builder, loc, getBlockingOrContinuousAssignDelay(builder.getContext()));
2912
2913 llhd::DriveOp::create(builder, loc, ref, func(v), delay, Value{});
2914}
2915
2916struct QueuePushBackOpConversion : public OpConversionPattern<QueuePushBackOp> {
2917 using OpConversionPattern::OpConversionPattern;
2918
2919 LogicalResult
2920 matchAndRewrite(QueuePushBackOp op, OpAdaptor adaptor,
2921 ConversionPatternRewriter &rewriter) const override {
2922 probeRefAndDriveWithResult(
2923 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2924 return sim::QueuePushBackOp::create(rewriter, op->getLoc(), queue,
2925 adaptor.getElement());
2926 });
2927
2928 rewriter.eraseOp(op);
2929 return success();
2930 }
2931};
2932
2933struct QueuePushFrontOpConversion
2934 : public OpConversionPattern<QueuePushFrontOp> {
2935 using OpConversionPattern::OpConversionPattern;
2936
2937 LogicalResult
2938 matchAndRewrite(QueuePushFrontOp op, OpAdaptor adaptor,
2939 ConversionPatternRewriter &rewriter) const override {
2940
2941 probeRefAndDriveWithResult(
2942 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2943 return sim::QueuePushFrontOp::create(rewriter, op->getLoc(), queue,
2944 adaptor.getElement());
2945 });
2946
2947 rewriter.eraseOp(op);
2948 return success();
2949 }
2950};
2951
2952struct QueuePopBackOpConversion : public OpConversionPattern<QueuePopBackOp> {
2953 using OpConversionPattern::OpConversionPattern;
2954
2955 LogicalResult
2956 matchAndRewrite(QueuePopBackOp op, OpAdaptor adaptor,
2957 ConversionPatternRewriter &rewriter) const override {
2958 Value popped;
2959 probeRefAndDriveWithResult(
2960 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2961 auto popBack =
2962 sim::QueuePopBackOp::create(rewriter, op->getLoc(), queue);
2963 popped = popBack.getPopped();
2964 return popBack.getOutQueue();
2965 });
2966 rewriter.replaceOp(op, popped);
2967
2968 return success();
2969 }
2970};
2971
2972struct QueuePopFrontOpConversion : public OpConversionPattern<QueuePopFrontOp> {
2973 using OpConversionPattern::OpConversionPattern;
2974
2975 LogicalResult
2976 matchAndRewrite(QueuePopFrontOp op, OpAdaptor adaptor,
2977 ConversionPatternRewriter &rewriter) const override {
2978 Value popped;
2979 probeRefAndDriveWithResult(
2980 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2981 auto popFront =
2982 sim::QueuePopFrontOp::create(rewriter, op->getLoc(), queue);
2983 popped = popFront.getPopped();
2984 return popFront.getOutQueue();
2985 });
2986 rewriter.replaceOp(op, popped);
2987
2988 return success();
2989 }
2990};
2991
2992struct QueueClearOpConversion : public OpConversionPattern<QueueClearOp> {
2993 using OpConversionPattern::OpConversionPattern;
2994
2995 LogicalResult
2996 matchAndRewrite(QueueClearOp op, OpAdaptor adaptor,
2997 ConversionPatternRewriter &rewriter) const override {
2998 auto refType = cast<llhd::RefType>(adaptor.getQueue().getType());
2999 auto queueType = refType.getNestedType();
3000 Value emptyQueue =
3001 sim::QueueEmptyOp::create(rewriter, op->getLoc(), queueType);
3002
3003 // Replace with an assignment to an empty queue
3004 Value delay = llhd::ConstantTimeOp::create(
3005 rewriter, op.getLoc(),
3006 getBlockingOrContinuousAssignDelay(rewriter.getContext()));
3007
3008 llhd::DriveOp::create(rewriter, op.getLoc(), adaptor.getQueue(), emptyQueue,
3009 delay, Value{});
3010
3011 rewriter.eraseOp(op);
3012 return success();
3013 }
3014};
3015
3016struct QueueInsertOpConversion : public OpConversionPattern<QueueInsertOp> {
3017 using OpConversionPattern::OpConversionPattern;
3018
3019 LogicalResult
3020 matchAndRewrite(QueueInsertOp op, OpAdaptor adaptor,
3021 ConversionPatternRewriter &rewriter) const override {
3022 probeRefAndDriveWithResult(
3023 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
3024 auto insert =
3025 sim::QueueInsertOp::create(rewriter, op->getLoc(), queue,
3026 adaptor.getIndex(), adaptor.getItem());
3027
3028 return insert.getOutQueue();
3029 });
3030 rewriter.eraseOp(op);
3031
3032 return success();
3033 }
3034};
3035
3036struct QueueDeleteOpConversion : public OpConversionPattern<QueueDeleteOp> {
3037 using OpConversionPattern::OpConversionPattern;
3038
3039 LogicalResult
3040 matchAndRewrite(QueueDeleteOp op, OpAdaptor adaptor,
3041 ConversionPatternRewriter &rewriter) const override {
3042 probeRefAndDriveWithResult(
3043 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
3044 auto delOp = sim::QueueDeleteOp::create(rewriter, op->getLoc(), queue,
3045 adaptor.getIndex());
3046
3047 return delOp.getOutQueue();
3048 });
3049 rewriter.eraseOp(op);
3050
3051 return success();
3052 };
3053};
3054
3055struct QueueResizeOpConversion : public OpConversionPattern<QueueResizeOp> {
3056 using OpConversionPattern::OpConversionPattern;
3057
3058 LogicalResult
3059 matchAndRewrite(QueueResizeOp op, OpAdaptor adaptor,
3060 ConversionPatternRewriter &rewriter) const override {
3061
3062 rewriter.replaceOpWithNewOp<sim::QueueResizeOp>(
3063 op, getTypeConverter()->convertType(op.getResult().getType()),
3064 adaptor.getInput());
3065 return success();
3066 }
3067};
3068
3069struct QueueSetOpConversion : public OpConversionPattern<QueueSetOp> {
3070 using OpConversionPattern::OpConversionPattern;
3071 LogicalResult
3072 matchAndRewrite(QueueSetOp op, OpAdaptor adaptor,
3073 ConversionPatternRewriter &rewriter) const override {
3074 probeRefAndDriveWithResult(
3075 rewriter, op->getLoc(), adaptor.getQueue(), [&](Value queue) {
3076 auto setOp =
3077 sim::QueueSetOp::create(rewriter, op.getLoc(), queue,
3078 adaptor.getIndex(), adaptor.getItem());
3079 return setOp.getOutQueue();
3080 });
3081 rewriter.eraseOp(op);
3082 return success();
3083 }
3084};
3085
3086// SystemVerilog unpacked array elements may be of any type. Bitcast handles
3087// statically-sized elements; reals bypass it so NaN/signed zero still compare
3088// correctly. Everything else recurses or dispatches to the matching comparison
3089// op.
3090static Value buildUArrayElementEq(ConversionPatternRewriter &rewriter,
3091 Location loc, Value lhs, Value rhs, Type type,
3092 UArrayCmpPredicate pred) {
3093 bool isEq = pred == UArrayCmpPredicate::eq;
3094
3095 if (isa<mlir::FloatType>(type))
3096 return arith::CmpFOp::create(
3097 rewriter, loc,
3098 isEq ? arith::CmpFPredicate::OEQ : arith::CmpFPredicate::UNE, lhs, rhs);
3099
3100 if (int64_t width = hw::getBitWidth(type); width != -1) {
3101 auto intTy = rewriter.getIntegerType(width);
3102 Value lhsInt = hw::BitcastOp::create(rewriter, loc, intTy, lhs);
3103 Value rhsInt = hw::BitcastOp::create(rewriter, loc, intTy, rhs);
3104 return comb::ICmpOp::create(rewriter, loc,
3105 isEq ? ICmpPredicate::eq : ICmpPredicate::ne,
3106 lhsInt, rhsInt);
3107 }
3108
3109 if (isa<sim::DynamicStringType>(type))
3110 return sim::StringCmpOp::create(rewriter, loc,
3111 isEq ? sim::StringCmpPredicate::eq
3112 : sim::StringCmpPredicate::ne,
3113 lhs, rhs);
3114
3115 if (isa<sim::QueueType>(type)) {
3116 auto pred = sim::UArrayCmpPredicateAttr::get(
3117 rewriter.getContext(),
3118 isEq ? sim::UArrayCmpPredicate::eq : sim::UArrayCmpPredicate::ne);
3119 return sim::QueueCmpOp::create(rewriter, loc, pred, lhs, rhs);
3120 }
3121
3122 if (isa<sim::AssocArrayType>(type)) {
3123 auto pred = sim::UArrayCmpPredicateAttr::get(
3124 rewriter.getContext(),
3125 isEq ? sim::UArrayCmpPredicate::eq : sim::UArrayCmpPredicate::ne);
3126 return sim::AssocArrayCmpOp::create(rewriter, loc, pred, lhs, rhs);
3127 }
3128
3129 auto arrayTy = dyn_cast<hw::ArrayType>(type);
3130 if (!arrayTy)
3131 return {};
3132
3133 unsigned size = arrayTy.getNumElements();
3134 if (size == 0)
3135 return hw::ConstantOp::create(rewriter, loc, APInt(1, isEq ? 1 : 0));
3136
3137 unsigned idxWidth = size == 1 ? 1 : llvm::Log2_64_Ceil(size);
3138 auto idxTy = rewriter.getIntegerType(idxWidth);
3139
3140 SmallVector<Value> elemEqs;
3141 elemEqs.reserve(size);
3142 for (unsigned i = 0; i < size; ++i) {
3143 Value idx = hw::ConstantOp::create(rewriter, loc, idxTy, i);
3144 Value lhsElem = hw::ArrayGetOp::create(rewriter, loc, lhs, idx);
3145 Value rhsElem = hw::ArrayGetOp::create(rewriter, loc, rhs, idx);
3146 Value elemEq = buildUArrayElementEq(rewriter, loc, lhsElem, rhsElem,
3147 arrayTy.getElementType(), pred);
3148 if (!elemEq)
3149 return {};
3150 elemEqs.push_back(elemEq);
3151 }
3152
3153 return (isEq ? comb::AndOp::create(rewriter, loc, elemEqs, /*twoState=*/true)
3154 .getResult()
3155 : comb::OrOp::create(rewriter, loc, elemEqs, /*twoState=*/true)
3156 .getResult());
3157}
3158
3159struct QueueCmpOpConversion : public OpConversionPattern<QueueCmpOp> {
3160 using OpConversionPattern::OpConversionPattern;
3161
3162 LogicalResult
3163 matchAndRewrite(QueueCmpOp op, OpAdaptor adaptor,
3164 ConversionPatternRewriter &rewriter) const override {
3165 // Per IEEE 1800-2017 7.4.2, queues are themselves a form of unpacked array,
3166 // so this reuses the same element-comparison dispatch as
3167 // UArrayCmpOpConversion (see buildUArrayElementEq), for a QueueType operand
3168 // it resolves directly to `sim.queue.cmp`.
3169 Value lhs = adaptor.getLhs();
3170 Value result =
3171 buildUArrayElementEq(rewriter, op.getLoc(), lhs, adaptor.getRhs(),
3172 lhs.getType(), op.getPredicate());
3173 rewriter.replaceOp(op, result);
3174 return success();
3175 }
3176};
3177
3178struct QueueFromUnpackedArrayOpConversion
3179 : public OpConversionPattern<QueueFromUnpackedArrayOp> {
3180 using OpConversionPattern::OpConversionPattern;
3181
3182 LogicalResult
3183 matchAndRewrite(QueueFromUnpackedArrayOp op, OpAdaptor adaptor,
3184 ConversionPatternRewriter &rewriter) const override {
3185 rewriter.replaceOpWithNewOp<sim::QueueFromArrayOp>(
3186 op, getTypeConverter()->convertType(op.getResult().getType()),
3187 adaptor.getInput());
3188 return success();
3189 }
3190};
3191
3192struct QueueConcatOpConversion : public OpConversionPattern<QueueConcatOp> {
3193 using OpConversionPattern::OpConversionPattern;
3194
3195 LogicalResult
3196 matchAndRewrite(QueueConcatOp op, OpAdaptor adaptor,
3197 ConversionPatternRewriter &rewriter) const override {
3198 rewriter.replaceOpWithNewOp<sim::QueueConcatOp>(
3199 op, getTypeConverter()->convertType(op.getResult().getType()),
3200 adaptor.getInputs());
3201 return success();
3202 }
3203};
3204
3205struct AssocArrayExtractOpConversion
3206 : public OpConversionPattern<AssocArrayExtractOp> {
3207 using OpConversionPattern::OpConversionPattern;
3208 LogicalResult
3209 matchAndRewrite(AssocArrayExtractOp op, OpAdaptor adaptor,
3210 ConversionPatternRewriter &rewriter) const override {
3211 Type resultType = getTypeConverter()->convertType(op.getResult().getType());
3212 if (!resultType)
3213 return failure();
3214
3215 Value zero = createZeroValue(resultType, op.getLoc(), rewriter);
3216 if (!zero)
3217 return failure();
3218 Value raw =
3219 sim::AssocArrayGetOp::create(rewriter, op.getLoc(), resultType,
3220 adaptor.getInput(), adaptor.getIndex());
3221 Value exists = sim::AssocArrayExistsOp::create(
3222 rewriter, op.getLoc(), adaptor.getInput(), adaptor.getIndex());
3223 Value zeroI32 = hw::ConstantOp::create(rewriter, op.getLoc(), APInt(32, 0));
3224 Value existsBit = comb::ICmpOp::create(
3225 rewriter, op.getLoc(), comb::ICmpPredicate::ne, exists, zeroI32, false);
3226 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, existsBit, raw, zero, false);
3227 return success();
3228 }
3229};
3230
3231struct UArrayCmpOpConversion : public OpConversionPattern<UArrayCmpOp> {
3232 using OpConversionPattern::OpConversionPattern;
3233 LogicalResult
3234 matchAndRewrite(UArrayCmpOp op, OpAdaptor adaptor,
3235 ConversionPatternRewriter &rewriter) const override {
3236 Value lhs = adaptor.getLhs();
3237 Value rhs = adaptor.getRhs();
3238 auto pred = op.getPredicate();
3239 Value eq = buildUArrayElementEq(rewriter, op.getLoc(), lhs, rhs,
3240 lhs.getType(), pred);
3241
3242 if (!eq)
3243 return rewriter.notifyMatchFailure(
3244 op, "unpacked array element type does not support comparison");
3245
3246 rewriter.replaceOp(op, eq);
3247 return success();
3248 }
3249};
3250
3251struct AssocArraySetOpConversion : public OpConversionPattern<AssocArraySetOp> {
3252 using OpConversionPattern::OpConversionPattern;
3253 LogicalResult
3254 matchAndRewrite(AssocArraySetOp op, OpAdaptor adaptor,
3255 ConversionPatternRewriter &rewriter) const override {
3256 probeRefAndDriveWithResult(
3257 rewriter, op.getLoc(), adaptor.getAssocArray(), [&](Value array) {
3258 return sim::AssocArraySetOp::create(rewriter, op.getLoc(), array,
3259 adaptor.getIndex(),
3260 adaptor.getValue())
3261 .getOutArray();
3262 });
3263 rewriter.eraseOp(op);
3264 return success();
3265 }
3266};
3267
3268struct AssocArrayDeleteOpConversion
3269 : public OpConversionPattern<AssocArrayDeleteOp> {
3270 using OpConversionPattern::OpConversionPattern;
3271 LogicalResult
3272 matchAndRewrite(AssocArrayDeleteOp op, OpAdaptor adaptor,
3273 ConversionPatternRewriter &rewriter) const override {
3274 probeRefAndDriveWithResult(
3275 rewriter, op.getLoc(), adaptor.getAssocArray(), [&](Value array) {
3276 return sim::AssocArrayDeleteOp::create(rewriter, op.getLoc(), array,
3277 adaptor.getIndex())
3278 .getOutArray();
3279 });
3280 rewriter.eraseOp(op);
3281 return success();
3282 }
3283};
3284
3285struct AssocArrayClearOpConversion
3286 : public OpConversionPattern<AssocArrayClearOp> {
3287 using OpConversionPattern::OpConversionPattern;
3288 LogicalResult
3289 matchAndRewrite(AssocArrayClearOp op, OpAdaptor adaptor,
3290 ConversionPatternRewriter &rewriter) const override {
3291 auto refType = cast<llhd::RefType>(adaptor.getAssocArray().getType());
3292 Value emptyArray = sim::AssocArrayEmptyOp::create(rewriter, op->getLoc(),
3293 refType.getNestedType());
3294 Value delay = llhd::ConstantTimeOp::create(
3295 rewriter, op.getLoc(),
3296 getBlockingOrContinuousAssignDelay(rewriter.getContext()));
3297 llhd::DriveOp::create(rewriter, op.getLoc(), adaptor.getAssocArray(),
3298 emptyArray, delay, Value{});
3299 rewriter.eraseOp(op);
3300 return success();
3301 }
3302};
3303
3304struct AssocArraySizeOpConversion
3305 : public OpConversionPattern<AssocArraySizeOp> {
3306 using OpConversionPattern::OpConversionPattern;
3307 LogicalResult
3308 matchAndRewrite(AssocArraySizeOp op, OpAdaptor adaptor,
3309 ConversionPatternRewriter &rewriter) const override {
3310 Value array =
3311 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getAssocArray());
3312 rewriter.replaceOpWithNewOp<sim::AssocArraySizeOp>(op, array);
3313 return success();
3314 }
3315};
3316
3317struct AssocArrayExistsOpConversion
3318 : public OpConversionPattern<AssocArrayExistsOp> {
3319 using OpConversionPattern::OpConversionPattern;
3320 LogicalResult
3321 matchAndRewrite(AssocArrayExistsOp op, OpAdaptor adaptor,
3322 ConversionPatternRewriter &rewriter) const override {
3323 Value array =
3324 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getAssocArray());
3325 rewriter.replaceOpWithNewOp<sim::AssocArrayExistsOp>(op, array,
3326 adaptor.getIndex());
3327 return success();
3328 }
3329};
3330
3331template <typename MooreOpTy, typename SimOpTy>
3332struct AssocArrayEndpointOpConversion : public OpConversionPattern<MooreOpTy> {
3334 using OpAdaptor = typename MooreOpTy::Adaptor;
3335 LogicalResult
3336 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
3337 ConversionPatternRewriter &rewriter) const override {
3338 Value array =
3339 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getAssocArray());
3340 auto endpointOp = SimOpTy::create(rewriter, op.getLoc(), array);
3341 Value curIndex =
3342 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getIndex());
3343 Value zero = hw::ConstantOp::create(rewriter, op.getLoc(), APInt(32, 0));
3344 Value foundBit =
3345 comb::ICmpOp::create(rewriter, op.getLoc(), comb::ICmpPredicate::ne,
3346 endpointOp->getResult(0), zero, false);
3347 Value newIndex =
3348 comb::MuxOp::create(rewriter, op.getLoc(), foundBit,
3349 endpointOp->getResult(1), curIndex, false);
3350 Value delay = llhd::ConstantTimeOp::create(
3351 rewriter, op.getLoc(),
3352 getBlockingOrContinuousAssignDelay(rewriter.getContext()));
3353 llhd::DriveOp::create(rewriter, op.getLoc(), adaptor.getIndex(), newIndex,
3354 delay, Value{});
3355 rewriter.replaceOp(op, endpointOp->getResult(0));
3356 return success();
3357 }
3358};
3359
3360template <typename MooreOpTy, typename SimOpTy>
3361struct AssocArrayStepOpConversion : public OpConversionPattern<MooreOpTy> {
3363 using OpAdaptor = typename MooreOpTy::Adaptor;
3364 LogicalResult
3365 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
3366 ConversionPatternRewriter &rewriter) const override {
3367 Value array =
3368 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getAssocArray());
3369 Value curIndex =
3370 llhd::ProbeOp::create(rewriter, op.getLoc(), adaptor.getIndex());
3371 auto stepOp = SimOpTy::create(rewriter, op.getLoc(), array, curIndex);
3372 Value delay = llhd::ConstantTimeOp::create(
3373 rewriter, op.getLoc(),
3374 getBlockingOrContinuousAssignDelay(rewriter.getContext()));
3375 llhd::DriveOp::create(rewriter, op.getLoc(), adaptor.getIndex(),
3376 stepOp->getResult(1), delay, Value{});
3377 rewriter.replaceOp(op, stepOp->getResult(0));
3378 return success();
3379 }
3380};
3381
3382using AssocArrayFirstOpConversion =
3383 AssocArrayEndpointOpConversion<AssocArrayFirstOp, sim::AssocArrayFirstOp>;
3384using AssocArrayLastOpConversion =
3385 AssocArrayEndpointOpConversion<AssocArrayLastOp, sim::AssocArrayLastOp>;
3386using AssocArrayNextOpConversion =
3387 AssocArrayStepOpConversion<AssocArrayNextOp, sim::AssocArrayNextOp>;
3388using AssocArrayPrevOpConversion =
3389 AssocArrayStepOpConversion<AssocArrayPrevOp, sim::AssocArrayPrevOp>;
3390
3391struct DisplayBIOpConversion : public OpConversionPattern<DisplayBIOp> {
3392 using OpConversionPattern::OpConversionPattern;
3393
3394 LogicalResult
3395 matchAndRewrite(DisplayBIOp op, OpAdaptor adaptor,
3396 ConversionPatternRewriter &rewriter) const override {
3397 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
3398 op, adaptor.getMessage());
3399 return success();
3400 }
3401};
3402
3403struct FDisplayBIOpConversion : public OpConversionPattern<FDisplayBIOp> {
3404 using OpConversionPattern::OpConversionPattern;
3405 LogicalResult
3406 matchAndRewrite(FDisplayBIOp op, OpAdaptor adaptor,
3407 ConversionPatternRewriter &rewriter) const override {
3408 auto stream = sim::SVChannelToOutputStreamOp::create(rewriter, op.getLoc(),
3409 adaptor.getFd());
3410 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
3411 op, adaptor.getMessage(), stream.getStream());
3412 return success();
3413 }
3414};
3415
3416struct FOpenBIOpConversion : public OpConversionPattern<FOpenBIOp> {
3417 using OpConversionPattern::OpConversionPattern;
3418 LogicalResult
3419 matchAndRewrite(FOpenBIOp op, OpAdaptor adaptor,
3420 ConversionPatternRewriter &rewriter) const override {
3421 sim::SVFOpenModeAttr simMode;
3422 if (auto modeAttr = op.getModeAttr()) {
3423 auto mapMode = [](moore::FOpenMode m) -> sim::SVFOpenMode {
3424 switch (m) {
3425 case moore::FOpenMode::Read:
3426 return sim::SVFOpenMode::Read;
3427 case moore::FOpenMode::Write:
3428 return sim::SVFOpenMode::Write;
3429 case moore::FOpenMode::Append:
3430 return sim::SVFOpenMode::Append;
3431 case moore::FOpenMode::ReadUpdate:
3432 return sim::SVFOpenMode::ReadUpdate;
3433 case moore::FOpenMode::WriteUpdate:
3434 return sim::SVFOpenMode::WriteUpdate;
3435 case moore::FOpenMode::AppendUpdate:
3436 return sim::SVFOpenMode::AppendUpdate;
3437 }
3438 llvm_unreachable("unknown FOpenMode");
3439 };
3440 simMode = sim::SVFOpenModeAttr::get(op.getContext(),
3441 mapMode(modeAttr.getValue()));
3442 }
3443 rewriter.replaceOpWithNewOp<sim::SVFOpenOp>(op, adaptor.getFilename(),
3444 simMode);
3445 return success();
3446 }
3447};
3448
3449struct PlusArgsTestBIOpConversion
3450 : public OpConversionPattern<PlusArgsTestBIOp> {
3451 using OpConversionPattern::OpConversionPattern;
3452 LogicalResult
3453 matchAndRewrite(PlusArgsTestBIOp op, OpAdaptor adaptor,
3454 ConversionPatternRewriter &rewriter) const override {
3455 rewriter.replaceOpWithNewOp<sim::PlusArgsTestOp>(op, rewriter.getI1Type(),
3456 op.getFormatStringAttr());
3457 return success();
3458 }
3459};
3460
3461struct PlusArgsValueBIOpConversion
3462 : public OpConversionPattern<PlusArgsValueBIOp> {
3463 using OpConversionPattern::OpConversionPattern;
3464 LogicalResult
3465 matchAndRewrite(PlusArgsValueBIOp op, OpAdaptor adaptor,
3466 ConversionPatternRewriter &rewriter) const override {
3467 auto resultType = typeConverter->convertType(op.getResult().getType());
3468 if (!resultType)
3469 return rewriter.notifyMatchFailure(op, "unsupported result type");
3470 rewriter.replaceOpWithNewOp<sim::PlusArgsValueOp>(
3471 op, rewriter.getI1Type(), resultType, op.getFormatStringAttr());
3472 return success();
3473 }
3474};
3475
3476struct FCloseBIOpConversion : public OpConversionPattern<FCloseBIOp> {
3477 using OpConversionPattern::OpConversionPattern;
3478
3479 LogicalResult
3480 matchAndRewrite(FCloseBIOp op, OpAdaptor adaptor,
3481 ConversionPatternRewriter &rewriter) const override {
3482 rewriter.replaceOpWithNewOp<sim::SVFCloseOp>(op, adaptor.getFd());
3483 return success();
3484 }
3485};
3486
3487struct FFlushBIOpConversion : public OpConversionPattern<FFlushBIOp> {
3488 using OpConversionPattern::OpConversionPattern;
3489
3490 LogicalResult
3491 matchAndRewrite(FFlushBIOp op, OpAdaptor adaptor,
3492 ConversionPatternRewriter &rewriter) const override {
3493 if (!adaptor.getFd()) {
3494 rewriter.replaceOpWithNewOp<sim::SVFFlushAllOp>(op);
3495 } else {
3496 auto stream = sim::SVChannelToOutputStreamOp::create(
3497 rewriter, op.getLoc(), adaptor.getFd());
3498 rewriter.replaceOpWithNewOp<sim::FlushOp>(op, stream);
3499 }
3500 return success();
3501 }
3502};
3503
3504struct StringCmpOpConversion : public OpConversionPattern<StringCmpOp> {
3505 using OpConversionPattern::OpConversionPattern;
3506
3507 LogicalResult
3508 matchAndRewrite(StringCmpOp op, OpAdaptor adaptor,
3509 ConversionPatternRewriter &rewriter) const override {
3510 sim::StringCmpPredicate pred;
3511 switch (op.getPredicate()) {
3512 case moore::StringCmpPredicate::eq:
3513 pred = sim::StringCmpPredicate::eq;
3514 break;
3515 case moore::StringCmpPredicate::ne:
3516 pred = sim::StringCmpPredicate::ne;
3517 break;
3518 case moore::StringCmpPredicate::lt:
3519 pred = sim::StringCmpPredicate::lt;
3520 break;
3521 case moore::StringCmpPredicate::le:
3522 pred = sim::StringCmpPredicate::le;
3523 break;
3524 case moore::StringCmpPredicate::gt:
3525 pred = sim::StringCmpPredicate::gt;
3526 break;
3527 case moore::StringCmpPredicate::ge:
3528 pred = sim::StringCmpPredicate::ge;
3529 break;
3530 }
3531 rewriter.replaceOpWithNewOp<sim::StringCmpOp>(op, pred, adaptor.getLhs(),
3532 adaptor.getRhs());
3533 return success();
3534 }
3535};
3536
3537struct ReadMemBIOpConversion : public OpConversionPattern<ReadMemBIOp> {
3538 using OpConversionPattern::OpConversionPattern;
3539
3540 LogicalResult
3541 matchAndRewrite(ReadMemBIOp op, OpAdaptor adaptor,
3542 ConversionPatternRewriter &rewriter) const override {
3543 auto loc = op.getLoc();
3544 Value memory = llhd::ProbeOp::create(rewriter, loc, adaptor.getDest());
3545 Value loaded = sim::SVReadMemOp::create(
3546 rewriter, loc, memory.getType(), adaptor.getFilename(), memory,
3547 rewriter.getBoolAttr(op.getBase() == MemBase::Binary),
3548 adaptor.getStartAddr(), adaptor.getFinishAddr(), adaptor.getSliceLeft(),
3549 adaptor.getSliceRight(), op.getDimLowsAttr(), op.getDimDescendingAttr(),
3550 op.getEnumValuesAttr());
3551 Value delay = llhd::ConstantTimeOp::create(
3552 rewriter, loc,
3553 llhd::TimeAttr::get(rewriter.getContext(), 0U, "ns", 0, 1));
3554 rewriter.replaceOpWithNewOp<llhd::DriveOp>(op, adaptor.getDest(), loaded,
3555 delay, Value{});
3556 return success();
3557 }
3558};
3559
3560} // namespace
3561
3562//===----------------------------------------------------------------------===//
3563// Simulation Control Conversion
3564//===----------------------------------------------------------------------===//
3565
3566// moore.builtin.stop -> sim.pause
3567static LogicalResult convert(StopBIOp op, StopBIOp::Adaptor adaptor,
3568 ConversionPatternRewriter &rewriter) {
3569 rewriter.replaceOpWithNewOp<sim::PauseOp>(op, /*verbose=*/false);
3570 return success();
3571}
3572
3573// moore.builtin.finish -> sim.terminate
3574static LogicalResult convert(FinishBIOp op, FinishBIOp::Adaptor adaptor,
3575 ConversionPatternRewriter &rewriter) {
3576 rewriter.replaceOpWithNewOp<sim::TerminateOp>(op, op.getExitCode() == 0,
3577 /*verbose=*/false);
3578 return success();
3579}
3580
3581// moore.builtin.severity -> sim.proc.print
3582static LogicalResult convert(SeverityBIOp op, SeverityBIOp::Adaptor adaptor,
3583 ConversionPatternRewriter &rewriter) {
3584
3585 std::string severityString;
3586
3587 switch (op.getSeverity()) {
3588 case (Severity::Fatal):
3589 severityString = "Fatal: ";
3590 break;
3591 case (Severity::Error):
3592 severityString = "Error: ";
3593 break;
3594 case (Severity::Warning):
3595 severityString = "Warning: ";
3596 break;
3597 case (Severity::Info):
3598 severityString = "Info: ";
3599 break;
3600 }
3601
3602 auto prefix =
3603 sim::FormatLiteralOp::create(rewriter, op.getLoc(), severityString);
3604 auto message = sim::FormatStringConcatOp::create(
3605 rewriter, op.getLoc(), ValueRange{prefix, adaptor.getMessage()});
3606 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(op, message);
3607 return success();
3608}
3609
3610//===----------------------------------------------------------------------===//
3611// Random Builtin Conversion
3612//===----------------------------------------------------------------------===//
3613
3614/// moore.builtin.urandom_range -> call @__circt_urandom_range(i32, i32, ptr)
3615///
3616/// The seed pointer is null when no seed is provided. When a seed ref is
3617/// present, we probe the current value into an alloca before the call, and
3618/// drive the (potentially mutated) value back after.
3619static LogicalResult convert(UrandomRangeBIOp op,
3620 UrandomRangeBIOp::Adaptor adaptor,
3621 ConversionPatternRewriter &rewriter,
3622 FunctionCache &funcCache) {
3623 auto loc = op.getLoc();
3624 auto i32Ty = rewriter.getI32Type();
3625 auto ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
3626 auto fn = funcCache.getOrCreate(rewriter, "__circt_urandom_range",
3627 {i32Ty, i32Ty, ptrTy}, {i32Ty});
3628
3629 Value seedPtr;
3630 if (auto seedRef = adaptor.getSeed()) {
3631 // Allocate a temporary, probe the current seed value into it.
3632 auto one = hw::ConstantOp::create(rewriter, loc, i32Ty, 1);
3633 seedPtr = LLVM::AllocaOp::create(rewriter, loc, ptrTy, i32Ty, one);
3634 auto seedVal = llhd::ProbeOp::create(rewriter, loc, seedRef);
3635 LLVM::StoreOp::create(rewriter, loc, seedVal, seedPtr);
3636 } else {
3637 seedPtr = LLVM::ZeroOp::create(rewriter, loc, ptrTy);
3638 }
3639
3640 auto call = func::CallOp::create(
3641 rewriter, loc, fn,
3642 ValueRange{adaptor.getMinval(), adaptor.getMaxval(), seedPtr});
3643
3644 // Drive the potentially mutated seed back with an epsilon time delta.
3645 if (adaptor.getSeed()) {
3646 auto newSeed = LLVM::LoadOp::create(rewriter, loc, i32Ty, seedPtr);
3647 auto epsilon = llhd::ConstantTimeOp::create(
3648 rewriter, loc,
3649 llhd::TimeAttr::get(rewriter.getContext(), 0, "ns", 0, 1));
3650 llhd::DriveOp::create(rewriter, loc, adaptor.getSeed(), newSeed, epsilon,
3651 Value{});
3652 }
3653
3654 rewriter.replaceOp(op, call.getResult(0));
3655 return success();
3656}
3657
3658// moore.builtin.finish_message
3659static LogicalResult convert(FinishMessageBIOp op,
3660 FinishMessageBIOp::Adaptor adaptor,
3661 ConversionPatternRewriter &rewriter) {
3662 // We don't support printing termination/pause messages yet.
3663 rewriter.eraseOp(op);
3664 return success();
3665}
3666
3667//===----------------------------------------------------------------------===//
3668// Timing Control Conversion
3669//===----------------------------------------------------------------------===//
3670
3671// moore.builtin.time
3672static LogicalResult convert(TimeBIOp op, TimeBIOp::Adaptor adaptor,
3673 ConversionPatternRewriter &rewriter) {
3674 rewriter.replaceOpWithNewOp<llhd::CurrentTimeOp>(op);
3675 return success();
3676}
3677
3678// moore.logic_to_time
3679static LogicalResult convert(LogicToTimeOp op, LogicToTimeOp::Adaptor adaptor,
3680 ConversionPatternRewriter &rewriter) {
3681 rewriter.replaceOpWithNewOp<llhd::IntToTimeOp>(op, adaptor.getInput());
3682 return success();
3683}
3684
3685// moore.time_to_logic
3686static LogicalResult convert(TimeToLogicOp op, TimeToLogicOp::Adaptor adaptor,
3687 ConversionPatternRewriter &rewriter) {
3688 rewriter.replaceOpWithNewOp<llhd::TimeToIntOp>(op, adaptor.getInput());
3689 return success();
3690}
3691
3692//===----------------------------------------------------------------------===//
3693// Conversion Infrastructure
3694//===----------------------------------------------------------------------===//
3695
3696static void populateLegality(ConversionTarget &target,
3697 const TypeConverter &converter) {
3698 target.addIllegalDialect<MooreDialect>();
3699 target.addLegalDialect<comb::CombDialect>();
3700 target.addLegalDialect<hw::HWDialect>();
3701 target.addLegalDialect<seq::SeqDialect>();
3702 target.addLegalDialect<llhd::LLHDDialect>();
3703 target.addLegalDialect<ltl::LTLDialect>();
3704 target.addLegalDialect<mlir::BuiltinDialect>();
3705 target.addLegalDialect<mlir::math::MathDialect>();
3706 target.addLegalDialect<sim::SimDialect>();
3707 target.addLegalDialect<mlir::LLVM::LLVMDialect>();
3708 target.addLegalDialect<mlir::ub::UBDialect>();
3709 target.addLegalDialect<verif::VerifDialect>();
3710 target.addLegalDialect<arith::ArithDialect>();
3711
3712 target.addLegalOp<debug::ScopeOp>();
3713
3714 target.addDynamicallyLegalOp<scf::YieldOp, func::CallOp, func::ReturnOp,
3715 UnrealizedConversionCastOp, hw::OutputOp,
3716 hw::InstanceOp, debug::ArrayOp, debug::StructOp,
3717 debug::VariableOp, arith::SelectOp>(
3718 [&](Operation *op) { return converter.isLegal(op); });
3719
3720 target.addDynamicallyLegalOp<scf::IfOp, scf::ForOp, scf::ExecuteRegionOp,
3721 scf::WhileOp, scf::ForallOp>([&](Operation *op) {
3722 return converter.isLegal(op) && !op->getParentOfType<llhd::ProcessOp>();
3723 });
3724
3725 target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
3726 return converter.isSignatureLegal(op.getFunctionType());
3727 });
3728
3729 target.addDynamicallyLegalOp<hw::HWModuleOp>([&](hw::HWModuleOp op) {
3730 return converter.isSignatureLegal(op.getModuleType().getFuncType()) &&
3731 converter.isLegal(&op.getBody());
3732 });
3733}
3734
3735static void populateTypeConversion(TypeConverter &typeConverter) {
3736 typeConverter.addConversion([&](IntType type) {
3737 return IntegerType::get(type.getContext(), type.getWidth());
3738 });
3739
3740 typeConverter.addConversion([&](RealType type) -> mlir::Type {
3741 MLIRContext *ctx = type.getContext();
3742 switch (type.getWidth()) {
3743 case moore::RealWidth::f32:
3744 return mlir::Float32Type::get(ctx);
3745 case moore::RealWidth::f64:
3746 return mlir::Float64Type::get(ctx);
3747 }
3748 });
3749
3750 typeConverter.addConversion(
3751 [&](TimeType type) { return llhd::TimeType::get(type.getContext()); });
3752
3753 typeConverter.addConversion([&](FormatStringType type) {
3754 return sim::FormatStringType::get(type.getContext());
3755 });
3756
3757 typeConverter.addConversion([&](StringType type) {
3758 return sim::DynamicStringType::get(type.getContext());
3759 });
3760
3761 typeConverter.addConversion([&](QueueType type) {
3762 return sim::QueueType::get(type.getContext(),
3763 typeConverter.convertType(type.getElementType()),
3764 type.getBound());
3765 });
3766
3767 typeConverter.addConversion([&](sim::QueueType type) -> std::optional<Type> {
3768 if (auto elementType = typeConverter.convertType(type.getElementType()))
3769 return sim::QueueType::get(type.getContext(), elementType,
3770 type.getBound());
3771 return {};
3772 });
3773
3774 typeConverter.addConversion([&](ArrayType type) -> std::optional<Type> {
3775 if (auto elementType = typeConverter.convertType(type.getElementType()))
3776 return hw::ArrayType::get(elementType, type.getSize());
3777 return {};
3778 });
3779
3780 typeConverter.addConversion([&](AssocArrayType type) -> std::optional<Type> {
3781 auto elementType = typeConverter.convertType(type.getElementType());
3782 auto indexType = typeConverter.convertType(type.getIndexType());
3783 if (!elementType || !indexType)
3784 return {};
3785 return sim::AssocArrayType::get(type.getContext(), elementType, indexType);
3786 });
3787
3788 // FIXME: Unpacked arrays support more element types than their packed
3789 // variants, and as such, mapping them to hw::Array is somewhat naive. See
3790 // also the analogous note below concerning unpacked struct type conversion.
3791 typeConverter.addConversion(
3792 [&](UnpackedArrayType type) -> std::optional<Type> {
3793 if (auto elementType = typeConverter.convertType(type.getElementType()))
3794 return hw::ArrayType::get(elementType, type.getSize());
3795 return {};
3796 });
3797
3798 typeConverter.addConversion([&](OpenArrayType type) -> std::optional<Type> {
3799 return LLVM::LLVMPointerType::get(type.getContext());
3800 });
3801
3802 typeConverter.addConversion(
3803 [&](OpenUnpackedArrayType type) -> std::optional<Type> {
3804 return LLVM::LLVMPointerType::get(type.getContext());
3805 });
3806
3807 typeConverter.addConversion([&](StructType type) -> std::optional<Type> {
3808 SmallVector<hw::StructType::FieldInfo> fields;
3809 for (auto field : type.getMembers()) {
3810 hw::StructType::FieldInfo info;
3811 info.type = typeConverter.convertType(field.type);
3812 if (!info.type)
3813 return {};
3814 info.name = field.name;
3815 fields.push_back(info);
3816 }
3817 return hw::StructType::get(type.getContext(), fields);
3818 });
3819
3820 // FIXME: Mapping unpacked struct type to struct type in hw dialect may be a
3821 // plain solution. The packed and unpacked data structures have some
3822 // differences though they look similarily. The packed data structure is
3823 // contiguous in memory but another is opposite. The differences will affect
3824 // data layout and granularity of event tracking in simulation.
3825 typeConverter.addConversion(
3826 [&](UnpackedStructType type) -> std::optional<Type> {
3827 SmallVector<hw::StructType::FieldInfo> fields;
3828 for (auto field : type.getMembers()) {
3829 hw::StructType::FieldInfo info;
3830 info.type = typeConverter.convertType(field.type);
3831 if (!info.type)
3832 return {};
3833 info.name = field.name;
3834 fields.push_back(info);
3835 }
3836 return hw::StructType::get(type.getContext(), fields);
3837 });
3838
3839 // UnionType -> hw::UnionType
3840 typeConverter.addConversion([&](UnionType type) -> std::optional<Type> {
3841 SmallVector<hw::UnionType::FieldInfo> fields;
3842 for (auto field : type.getMembers()) {
3843 hw::UnionType::FieldInfo info;
3844 info.type = typeConverter.convertType(field.type);
3845 if (!info.type)
3846 return {};
3847 info.name = field.name;
3848 info.offset = 0; // packed union, all fields start at bit 0
3849 fields.push_back(info);
3850 }
3851 auto result = hw::UnionType::get(type.getContext(), fields);
3852 return result;
3853 });
3854
3855 // UnpackedUnionType -> hw::UnionType
3856 typeConverter.addConversion(
3857 [&](UnpackedUnionType type) -> std::optional<Type> {
3858 SmallVector<hw::UnionType::FieldInfo> fields;
3859 for (auto field : type.getMembers()) {
3860 hw::UnionType::FieldInfo info;
3861 info.type = typeConverter.convertType(field.type);
3862 if (!info.type)
3863 return {};
3864 info.name = field.name;
3865 info.offset = 0;
3866 fields.push_back(info);
3867 }
3868 return hw::UnionType::get(type.getContext(), fields);
3869 });
3870
3871 // Conversion of CHandle to LLVMPointerType
3872 typeConverter.addConversion([&](ChandleType type) -> std::optional<Type> {
3873 return LLVM::LLVMPointerType::get(type.getContext());
3874 });
3875
3876 // Explicitly mark LLVMPointerType as a legal target
3877 typeConverter.addConversion(
3878 [](LLVM::LLVMPointerType t) -> std::optional<Type> { return t; });
3879
3880 // ClassHandleType -> !llvm.ptr
3881 typeConverter.addConversion([&](ClassHandleType type) -> std::optional<Type> {
3882 return LLVM::LLVMPointerType::get(type.getContext());
3883 });
3884
3885 // NullType -> !llvm.ptr
3886 typeConverter.addConversion([&](NullType type) -> std::optional<Type> {
3887 return LLVM::LLVMPointerType::get(type.getContext());
3888 });
3889
3890 typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
3891 if (isa<OpenArrayType, OpenUnpackedArrayType>(type.getNestedType()))
3892 return LLVM::LLVMPointerType::get(type.getContext());
3893 if (auto innerType = typeConverter.convertType(type.getNestedType()))
3894 return llhd::RefType::get(innerType);
3895 return {};
3896 });
3897
3898 // Valid target types.
3899 typeConverter.addConversion([](IntegerType type) { return type; });
3900 typeConverter.addConversion([](FloatType type) { return type; });
3901 typeConverter.addConversion([](sim::DynamicStringType type) { return type; });
3902 typeConverter.addConversion([](sim::FormatStringType type) { return type; });
3903 typeConverter.addConversion([](sim::AssocArrayType type) { return type; });
3904 typeConverter.addConversion([](llhd::TimeType type) { return type; });
3905 typeConverter.addConversion([](debug::ArrayType type) { return type; });
3906 typeConverter.addConversion([](debug::ScopeType type) { return type; });
3907 typeConverter.addConversion([](debug::StructType type) { return type; });
3908
3909 typeConverter.addConversion([&](llhd::RefType type) -> std::optional<Type> {
3910 if (auto innerType = typeConverter.convertType(type.getNestedType()))
3911 return llhd::RefType::get(innerType);
3912 return {};
3913 });
3914
3915 typeConverter.addConversion([&](hw::ArrayType type) -> std::optional<Type> {
3916 if (auto elementType = typeConverter.convertType(type.getElementType()))
3917 return hw::ArrayType::get(elementType, type.getNumElements());
3918 return {};
3919 });
3920
3921 typeConverter.addConversion([&](hw::StructType type) -> std::optional<Type> {
3922 SmallVector<hw::StructType::FieldInfo> fields;
3923 for (auto field : type.getElements()) {
3924 hw::StructType::FieldInfo info;
3925 info.type = typeConverter.convertType(field.type);
3926 if (!info.type)
3927 return {};
3928 info.name = field.name;
3929 fields.push_back(info);
3930 }
3931 return hw::StructType::get(type.getContext(), fields);
3932 });
3933
3934 typeConverter.addConversion([&](hw::UnionType type) -> std::optional<Type> {
3935 SmallVector<hw::UnionType::FieldInfo> fields;
3936 for (auto field : type.getElements()) {
3937 hw::UnionType::FieldInfo info;
3938 info.type = typeConverter.convertType(field.type);
3939 if (!info.type)
3940 return {};
3941 info.name = field.name;
3942 info.offset = field.offset;
3943 fields.push_back(info);
3944 }
3945 return hw::UnionType::get(type.getContext(), fields);
3946 });
3947
3948 typeConverter.addTargetMaterialization(
3949 [&](mlir::OpBuilder &builder, mlir::Type resultType,
3950 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
3951 if (inputs.size() != 1 || !inputs[0])
3952 return Value();
3953 return UnrealizedConversionCastOp::create(builder, loc, resultType,
3954 inputs[0])
3955 .getResult(0);
3956 });
3957
3958 typeConverter.addSourceMaterialization(
3959 [&](mlir::OpBuilder &builder, mlir::Type resultType,
3960 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
3961 if (inputs.size() != 1)
3962 return Value();
3963 return UnrealizedConversionCastOp::create(builder, loc, resultType,
3964 inputs[0])
3965 ->getResult(0);
3966 });
3967}
3968
3970 TypeConverter &typeConverter,
3971 ClassTypeCache &classCache,
3972 FunctionCache &funcCache) {
3973
3974 patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
3975 classCache);
3976 patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
3977 classCache, funcCache);
3978 patterns.add<ClassPropertyRefOpConversion>(typeConverter,
3979 patterns.getContext(), classCache);
3980
3981 // clang-format off
3982 patterns.add<
3983 ClassUpcastOpConversion,
3984 NullOpConversion,
3985 // Patterns of declaration operations.
3986 VariableOpConversion,
3987 NetOpConversion,
3988
3989 // Patterns for conversion operations.
3990 ConversionOpConversion,
3991 BitcastConversion<PackedToSBVOp>,
3992 BitcastConversion<SBVToPackedOp>,
3993 NoOpConversion<LogicToIntOp>,
3994 NoOpConversion<IntToLogicOp>,
3995 NoOpConversion<ToBuiltinIntOp>,
3996 NoOpConversion<FromBuiltinIntOp>,
3997 TruncOpConversion,
3998 ZExtOpConversion,
3999 SExtOpConversion,
4000 SIntToRealOpConversion,
4001 UIntToRealOpConversion,
4002 IntToStringOpConversion,
4003 StringToIntOpConversion,
4004 FormatStringToStringOpConversion,
4005 RealToIntOpConversion,
4006 ConvertRealOpConversion,
4007 RealBitcastOpConversion<RealtobitsBIOp>,
4008 RealBitcastOpConversion<BitstorealBIOp>,
4009 RealBitcastOpConversion<ShortrealtobitsBIOp>,
4010 RealBitcastOpConversion<BitstoshortrealBIOp>,
4011
4012 // Patterns of miscellaneous operations.
4013 ConstantOpConv,
4014 ConstantRealOpConv,
4015 ConcatOpConversion,
4016 ReplicateOpConversion,
4017 ConstantTimeOpConv,
4018 ExtractOpConversion,
4019 DynExtractOpConversion,
4020 DynExtractRefOpConversion,
4021 ReadOpConversion,
4022 StructExtractOpConversion,
4023 StructExtractRefOpConversion,
4024 ExtractRefOpConversion,
4025 StructCreateOpConversion,
4026 UnionCreateOpConversion,
4027 UnionExtractOpConversion,
4028 UnionExtractRefOpConversion,
4029 ConditionalOpConversion,
4030 ArrayCreateOpConversion,
4031 UArrayCmpOpConversion,
4032 YieldOpConversion,
4033 OutputOpConversion,
4034 ConstantStringOpConv,
4035
4036 // Patterns of unary operations.
4037 ReduceAndOpConversion,
4038 ReduceOrOpConversion,
4039 ReduceXorOpConversion,
4040 BoolCastOpConversion,
4041 NotOpConversion,
4042 NegOpConversion,
4043
4044 // Patterns of binary operations.
4045 BinaryOpConversion<AddOp, comb::AddOp>,
4046 BinaryOpConversion<SubOp, comb::SubOp>,
4047 BinaryOpConversion<MulOp, comb::MulOp>,
4048 BinaryOpConversion<DivUOp, comb::DivUOp>,
4049 BinaryOpConversion<DivSOp, comb::DivSOp>,
4050 BinaryOpConversion<ModUOp, comb::ModUOp>,
4051 BinaryOpConversion<ModSOp, comb::ModSOp>,
4052 BinaryOpConversion<AndOp, comb::AndOp>,
4053 BinaryOpConversion<OrOp, comb::OrOp>,
4054 BinaryOpConversion<XorOp, comb::XorOp>,
4055
4056 // Patterns for unary real operations.
4057 NegRealOpConversion,
4058
4059 // Patterns for binary real operations.
4060 BinaryRealOpConversion<AddRealOp, arith::AddFOp>,
4061 BinaryRealOpConversion<SubRealOp, arith::SubFOp>,
4062 BinaryRealOpConversion<DivRealOp, arith::DivFOp>,
4063 BinaryRealOpConversion<MulRealOp, arith::MulFOp>,
4064 BinaryRealOpConversion<PowRealOp, math::PowFOp>,
4065
4066 // Pattern for Verilog standard mathematical functions
4067 RealMathFunc<LnBIOp, math::LogOp>,
4068 RealMathFunc<Log10BIOp, math::Log10Op>,
4069 RealMathFunc<ExpBIOp, math::ExpOp>,
4070 RealMathFunc<SqrtBIOp, math::SqrtOp>,
4071 BinaryRealOpConversion<MinBIOp, arith::MinimumFOp>,
4072 BinaryRealOpConversion<MaxBIOp, arith::MaximumFOp>,
4073 RealMathFunc<AbsBIOp, math::AbsFOp>,
4074 RealMathFunc<FloorBIOp, math::FloorOp>,
4075 RealMathFunc<CeilBIOp, math::CeilOp>,
4076 RealMathFunc<SinBIOp, math::SinOp>,
4077 RealMathFunc<CosBIOp, math::CosOp>,
4078 RealMathFunc<TanBIOp, math::TanOp>,
4079 RealMathFunc<AsinBIOp, math::AsinOp>,
4080 RealMathFunc<AcosBIOp, math::AcosOp>,
4081 RealMathFunc<AtanBIOp, math::AtanOp>,
4082 BinaryRealOpConversion<Atan2BIOp, math::Atan2Op>,
4083 HypotBIOpConversion,
4084 RealMathFunc<SinhBIOp, math::SinhOp>,
4085 RealMathFunc<CoshBIOp, math::CoshOp>,
4086 RealMathFunc<TanhBIOp, math::TanhOp>,
4087 RealMathFunc<AsinhBIOp, math::AsinhOp>,
4088 RealMathFunc<AcoshBIOp, math::AcoshOp>,
4089 RealMathFunc<AtanhBIOp, math::AtanhOp>,
4090
4091 // Patterns of power operations.
4092 PowUOpConversion, PowSOpConversion,
4093 Clog2BIOpConversion,
4094
4095 // Patterns of relational operations.
4096 ICmpOpConversion<UltOp, ICmpPredicate::ult>,
4097 ICmpOpConversion<SltOp, ICmpPredicate::slt>,
4098 ICmpOpConversion<UleOp, ICmpPredicate::ule>,
4099 ICmpOpConversion<SleOp, ICmpPredicate::sle>,
4100 ICmpOpConversion<UgtOp, ICmpPredicate::ugt>,
4101 ICmpOpConversion<SgtOp, ICmpPredicate::sgt>,
4102 ICmpOpConversion<UgeOp, ICmpPredicate::uge>,
4103 ICmpOpConversion<SgeOp, ICmpPredicate::sge>,
4104 ICmpOpConversion<EqOp, ICmpPredicate::eq>,
4105 ICmpOpConversion<NeOp, ICmpPredicate::ne>,
4106 ICmpOpConversion<CaseEqOp, ICmpPredicate::ceq>,
4107 ICmpOpConversion<CaseNeOp, ICmpPredicate::cne>,
4108 ICmpOpConversion<WildcardEqOp, ICmpPredicate::weq>,
4109 ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
4110 FCmpOpConversion<NeRealOp, arith::CmpFPredicate::UNE>,
4111 FCmpOpConversion<FltOp, arith::CmpFPredicate::OLT>,
4112 FCmpOpConversion<FleOp, arith::CmpFPredicate::OLE>,
4113 FCmpOpConversion<FgtOp, arith::CmpFPredicate::OGT>,
4114 FCmpOpConversion<FgeOp, arith::CmpFPredicate::OGE>,
4115 FCmpOpConversion<EqRealOp, arith::CmpFPredicate::OEQ>,
4116 CaseXZEqOpConversion<CaseZEqOp, true>,
4117 CaseXZEqOpConversion<CaseXZEqOp, false>,
4118 HandleCmpOpConversion<HandleEqOp, LLVM::ICmpPredicate::eq>,
4119 HandleCmpOpConversion<HandleNeOp, LLVM::ICmpPredicate::ne>,
4120
4121 // Patterns of structural operations.
4122 SVModuleOpConversion,
4123 InstanceOpConversion,
4124 ProcedureOpConversion,
4125 CoroutineOpConversion,
4126 CallCoroutineOpConversion,
4127 WaitEventOpConversion,
4128
4129 // Patterns of shifting operations.
4130 ShrOpConversion,
4131 ShlOpConversion,
4132 AShrOpConversion,
4133
4134 // Patterns of assignment operations.
4135 AssignOpConversion<ContinuousAssignOp>,
4136 AssignOpConversion<DelayedContinuousAssignOp>,
4137 AssignOpConversion<BlockingAssignOp>,
4138 AssignOpConversion<NonBlockingAssignOp>,
4139 AssignOpConversion<DelayedNonBlockingAssignOp>,
4140 AssignedVariableOpConversion,
4141
4142 // Patterns of other operations outside Moore dialect.
4143 HWInstanceOpConversion,
4144 ReturnOpConversion,
4145 CallOpConversion,
4146 DPIFuncOpConversion,
4147 FuncDPICallOpConversion,
4148 UnrealizedConversionCastConversion,
4149 InPlaceOpConversion<debug::ArrayOp>,
4150 InPlaceOpConversion<debug::StructOp>,
4151 InPlaceOpConversion<debug::VariableOp>,
4152
4153 // Patterns of assert-like operations
4154 AssertLikeOpConversion<AssertOp, verif::AssertOp>,
4155 AssertLikeOpConversion<AssumeOp, verif::AssumeOp>,
4156 AssertLikeOpConversion<CoverOp, verif::CoverOp>,
4157
4158 // Format strings.
4159 FormatLiteralOpConversion,
4160 FormatStringOpConversion,
4161 FormatConcatOpConversion,
4162 FormatHierPathOpConversion,
4163 FormatIntOpConversion,
4164 FormatRealOpConversion,
4165 FormatCharOpConversion,
4166 DisplayBIOpConversion,
4167 FDisplayBIOpConversion,
4168
4169 // File I/O operations
4170 FOpenBIOpConversion,
4171 FCloseBIOpConversion,
4172 FFlushBIOpConversion,
4173 ReadMemBIOpConversion,
4174
4175 // Command line input operations
4176 PlusArgsTestBIOpConversion,
4177 PlusArgsValueBIOpConversion,
4178
4179 // Dynamic string operations
4180 StringLenOpConversion,
4181 StringConcatOpConversion,
4182 StringGetOpConversion,
4183 StringCmpOpConversion,
4184
4185 // Queue operations
4186 QueueSizeBIOpConversion,
4187 QueuePushBackOpConversion,
4188 QueuePushFrontOpConversion,
4189 QueuePopBackOpConversion,
4190 QueuePopFrontOpConversion,
4191 QueueDeleteOpConversion,
4192 QueueInsertOpConversion,
4193 QueueClearOpConversion,
4194 DynQueueExtractOpConversion,
4195 QueueResizeOpConversion,
4196 QueueSetOpConversion,
4197 QueueCmpOpConversion,
4198 QueueFromUnpackedArrayOpConversion,
4199 QueueConcatOpConversion,
4200
4201 // Associative array operations
4202 AssocArrayExtractOpConversion,
4203 AssocArraySetOpConversion,
4204 AssocArrayDeleteOpConversion,
4205 AssocArrayClearOpConversion,
4206 AssocArraySizeOpConversion,
4207 AssocArrayExistsOpConversion,
4208 AssocArrayFirstOpConversion,
4209 AssocArrayLastOpConversion,
4210 AssocArrayNextOpConversion,
4211 AssocArrayPrevOpConversion
4212 >(typeConverter, patterns.getContext());
4213 // clang-format on
4214
4215 // Structural operations
4216 patterns.add<WaitDelayOp>(convert);
4217 patterns.add<UnreachableOp>(convert);
4218 patterns.add<GlobalVariableOp>(convert);
4219 patterns.add<GetGlobalVariableOp>(convert);
4220
4221 // Simulation control
4222 patterns.add<StopBIOp>(convert);
4223 patterns.add<SeverityBIOp>(convert);
4224 patterns.add<FinishBIOp>(convert);
4225 patterns.add<FinishMessageBIOp>(convert);
4226
4227 // Random builtins
4228 patterns.add<UrandomRangeBIOp>(convert, funcCache);
4229
4230 // Timing control
4231 patterns.add<TimeBIOp>(convert);
4232 patterns.add<LogicToTimeOp>(convert);
4233 patterns.add<TimeToLogicOp>(convert);
4234
4235 mlir::populateAnyFunctionOpInterfaceTypeConversionPattern(patterns,
4236 typeConverter);
4237 hw::populateHWModuleLikeTypeConversionPattern(
4238 hw::HWModuleOp::getOperationName(), patterns, typeConverter);
4239 populateSCFToControlFlowConversionPatterns(patterns);
4240 populateArithToCombPatterns(patterns, typeConverter);
4241}
4242
4243//===----------------------------------------------------------------------===//
4244// Moore to Core Conversion Pass
4245//===----------------------------------------------------------------------===//
4246
4247namespace {
4248struct MooreToCorePass
4249 : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
4250 void runOnOperation() override;
4251};
4252} // namespace
4253
4254/// Create a Moore to core dialects conversion pass.
4255std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
4256 return std::make_unique<MooreToCorePass>();
4257}
4258
4259/// This is the main entrypoint for the Moore to Core conversion pass.
4260void MooreToCorePass::runOnOperation() {
4261 MLIRContext &context = getContext();
4262 ModuleOp module = getOperation();
4263 ClassTypeCache classCache;
4264 auto &symbolTable = getAnalysis<SymbolTable>();
4265 FunctionCache funcCache(symbolTable);
4266
4267 IRRewriter rewriter(module);
4268 (void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
4269
4270 TypeConverter typeConverter;
4271 populateTypeConversion(typeConverter);
4272
4273 ConversionTarget target(context);
4274 populateLegality(target, typeConverter);
4275
4276 ConversionPatternSet patterns(&context, typeConverter);
4277 populateOpConversion(patterns, typeConverter, classCache, funcCache);
4278 mlir::cf::populateCFStructuralTypeConversionsAndLegality(typeConverter,
4279 patterns, target);
4280
4281 if (failed(applyFullConversion(module, target, std::move(patterns))))
4282 signalPassFailure();
4283}
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.