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