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