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