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