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