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 FormatConcatOpConversion : public OpConversionPattern<FormatConcatOp> {
2506 using OpConversionPattern::OpConversionPattern;
2507
2508 LogicalResult
2509 matchAndRewrite(FormatConcatOp op, OpAdaptor adaptor,
2510 ConversionPatternRewriter &rewriter) const override {
2511 rewriter.replaceOpWithNewOp<sim::FormatStringConcatOp>(op,
2512 adaptor.getInputs());
2513 return success();
2514 }
2515};
2516
2517struct FormatHierPathOpConversion
2518 : public OpConversionPattern<FormatHierPathOp> {
2519 using OpConversionPattern::OpConversionPattern;
2520
2521 LogicalResult
2522 matchAndRewrite(FormatHierPathOp op, OpAdaptor adaptor,
2523 ConversionPatternRewriter &rewriter) const override {
2524 rewriter.replaceOpWithNewOp<sim::FormatHierPathOp>(op,
2525 adaptor.getUseEscapes());
2526 return success();
2527 }
2528};
2529
2530struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
2531 using OpConversionPattern::OpConversionPattern;
2532
2533 LogicalResult
2534 matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
2535 ConversionPatternRewriter &rewriter) const override {
2536
2537 char padChar = adaptor.getPadding() == IntPadding::Space ? 32 : 48;
2538 IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar);
2539 auto widthAttr = adaptor.getSpecifierWidthAttr();
2540
2541 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2542 BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2543
2544 switch (op.getFormat()) {
2545 case IntFormat::Decimal:
2546 rewriter.replaceOpWithNewOp<sim::FormatDecOp>(
2547 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr,
2548 adaptor.getIsSignedAttr());
2549 return success();
2550 case IntFormat::Binary:
2551 rewriter.replaceOpWithNewOp<sim::FormatBinOp>(
2552 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2553 return success();
2554 case IntFormat::Octal:
2555 rewriter.replaceOpWithNewOp<sim::FormatOctOp>(
2556 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2557 return success();
2558 case IntFormat::HexLower:
2559 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2560 op, adaptor.getValue(), rewriter.getBoolAttr(false),
2561 isLeftAlignedAttr, padCharAttr, widthAttr);
2562 return success();
2563 case IntFormat::HexUpper:
2564 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2565 op, adaptor.getValue(), rewriter.getBoolAttr(true), isLeftAlignedAttr,
2566 padCharAttr, widthAttr);
2567 return success();
2568 }
2569 return rewriter.notifyMatchFailure(op, "unsupported int format");
2570 }
2571};
2572
2573struct FormatRealOpConversion : public OpConversionPattern<FormatRealOp> {
2574 using OpConversionPattern::OpConversionPattern;
2575
2576 LogicalResult
2577 matchAndRewrite(FormatRealOp op, OpAdaptor adaptor,
2578 ConversionPatternRewriter &rewriter) const override {
2579 auto fracDigitsAttr = adaptor.getFracDigitsAttr();
2580
2581 auto fieldWidthAttr = adaptor.getFieldWidthAttr();
2582 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2583 mlir::BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2584
2585 switch (op.getFormat()) {
2586 case RealFormat::General:
2587 rewriter.replaceOpWithNewOp<sim::FormatGeneralOp>(
2588 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2589 fracDigitsAttr);
2590 return success();
2591 case RealFormat::Float:
2592 rewriter.replaceOpWithNewOp<sim::FormatFloatOp>(
2593 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2594 fracDigitsAttr);
2595 return success();
2596 case RealFormat::Exponential:
2597 rewriter.replaceOpWithNewOp<sim::FormatScientificOp>(
2598 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2599 fracDigitsAttr);
2600 return success();
2601 }
2602 }
2603};
2604
2605struct StringLenOpConversion : public OpConversionPattern<StringLenOp> {
2606 using OpConversionPattern::OpConversionPattern;
2607
2608 LogicalResult
2609 matchAndRewrite(StringLenOp op, OpAdaptor adaptor,
2610 ConversionPatternRewriter &rewriter) const override {
2611 rewriter.replaceOpWithNewOp<sim::StringLengthOp>(op, adaptor.getStr());
2612 return success();
2613 }
2614};
2615
2616struct StringConcatOpConversion : public OpConversionPattern<StringConcatOp> {
2617 using OpConversionPattern::OpConversionPattern;
2618
2619 LogicalResult
2620 matchAndRewrite(StringConcatOp op, OpAdaptor adaptor,
2621 ConversionPatternRewriter &rewriter) const override {
2622 rewriter.replaceOpWithNewOp<sim::StringConcatOp>(op, adaptor.getInputs());
2623 return success();
2624 }
2625};
2626
2627struct StringGetOpConversion : public OpConversionPattern<StringGetOp> {
2628 using OpConversionPattern::OpConversionPattern;
2629
2630 LogicalResult
2631 matchAndRewrite(StringGetOp op, OpAdaptor adaptor,
2632 ConversionPatternRewriter &rewriter) const override {
2633 rewriter.replaceOpWithNewOp<sim::StringGetOp>(op, adaptor.getStr(),
2634 adaptor.getIndex());
2635 return success();
2636 }
2637};
2638
2639struct QueueSizeBIOpConversion : public OpConversionPattern<QueueSizeBIOp> {
2640 using OpConversionPattern::OpConversionPattern;
2641
2642 LogicalResult
2643 matchAndRewrite(QueueSizeBIOp op, OpAdaptor adaptor,
2644 ConversionPatternRewriter &rewriter) const override {
2645 rewriter.replaceOpWithNewOp<sim::QueueSizeOp>(op, adaptor.getQueue());
2646 return success();
2647 }
2648};
2649
2650struct DynQueueExtractOpConversion
2651 : public OpConversionPattern<DynQueueExtractOp> {
2652 using OpConversionPattern::OpConversionPattern;
2653
2654 LogicalResult
2655 matchAndRewrite(DynQueueExtractOp op, OpAdaptor adaptor,
2656 ConversionPatternRewriter &rewriter) const override {
2657 bool isSingleElementExtract =
2658 op.getInput().getType().getElementType() == op.getResult().getType();
2659
2660 if (isSingleElementExtract) {
2661 rewriter.replaceOpWithNewOp<sim::QueueGetOp>(op, adaptor.getInput(),
2662 adaptor.getLowerIdx());
2663 } else {
2664 rewriter.replaceOpWithNewOp<sim::QueueSliceOp>(
2665 op, adaptor.getInput(), adaptor.getLowerIdx(), adaptor.getUpperIdx());
2666 }
2667
2668 return success();
2669 }
2670};
2671
2672// Given a reference `ref` to some Moore type, this function emits a
2673// `ProbeOp` to read the contained value, then passes it to the function `func`.
2674// It finally emits a `DriveOp` to write the result of the function back to
2675// the referenced signal.
2676//
2677// This is useful for converting impure operations (such as the Moore ops for
2678// manipulating queues) into pure operations. (Which do not mutate the source
2679// value, instead returning a modified value.)
2680static void
2681probeRefAndDriveWithResult(OpBuilder &builder, Location loc, Value ref,
2682 const std::function<Value(Value)> &func) {
2683
2684 Value v = llhd::ProbeOp::create(builder, loc, ref);
2685
2686 // Drive using the same delay as a blocking assignment
2687 Value delay = llhd::ConstantTimeOp::create(
2688 builder, loc, getBlockingOrContinuousAssignDelay(builder.getContext()));
2689
2690 llhd::DriveOp::create(builder, loc, ref, func(v), delay, Value{});
2691}
2692
2693struct QueuePushBackOpConversion : public OpConversionPattern<QueuePushBackOp> {
2694 using OpConversionPattern::OpConversionPattern;
2695
2696 LogicalResult
2697 matchAndRewrite(QueuePushBackOp op, OpAdaptor adaptor,
2698 ConversionPatternRewriter &rewriter) const override {
2699 probeRefAndDriveWithResult(
2700 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2701 return sim::QueuePushBackOp::create(rewriter, op->getLoc(), queue,
2702 adaptor.getElement());
2703 });
2704
2705 rewriter.eraseOp(op);
2706 return success();
2707 }
2708};
2709
2710struct QueuePushFrontOpConversion
2711 : public OpConversionPattern<QueuePushFrontOp> {
2712 using OpConversionPattern::OpConversionPattern;
2713
2714 LogicalResult
2715 matchAndRewrite(QueuePushFrontOp op, OpAdaptor adaptor,
2716 ConversionPatternRewriter &rewriter) const override {
2717
2718 probeRefAndDriveWithResult(
2719 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2720 return sim::QueuePushFrontOp::create(rewriter, op->getLoc(), queue,
2721 adaptor.getElement());
2722 });
2723
2724 rewriter.eraseOp(op);
2725 return success();
2726 }
2727};
2728
2729struct QueuePopBackOpConversion : public OpConversionPattern<QueuePopBackOp> {
2730 using OpConversionPattern::OpConversionPattern;
2731
2732 LogicalResult
2733 matchAndRewrite(QueuePopBackOp op, OpAdaptor adaptor,
2734 ConversionPatternRewriter &rewriter) const override {
2735 Value popped;
2736 probeRefAndDriveWithResult(
2737 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2738 auto popBack =
2739 sim::QueuePopBackOp::create(rewriter, op->getLoc(), queue);
2740 popped = popBack.getPopped();
2741 return popBack.getOutQueue();
2742 });
2743 rewriter.replaceOp(op, popped);
2744
2745 return success();
2746 }
2747};
2748
2749struct QueuePopFrontOpConversion : public OpConversionPattern<QueuePopFrontOp> {
2750 using OpConversionPattern::OpConversionPattern;
2751
2752 LogicalResult
2753 matchAndRewrite(QueuePopFrontOp op, OpAdaptor adaptor,
2754 ConversionPatternRewriter &rewriter) const override {
2755 Value popped;
2756 probeRefAndDriveWithResult(
2757 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2758 auto popFront =
2759 sim::QueuePopFrontOp::create(rewriter, op->getLoc(), queue);
2760 popped = popFront.getPopped();
2761 return popFront.getOutQueue();
2762 });
2763 rewriter.replaceOp(op, popped);
2764
2765 return success();
2766 }
2767};
2768
2769struct QueueClearOpConversion : public OpConversionPattern<QueueClearOp> {
2770 using OpConversionPattern::OpConversionPattern;
2771
2772 LogicalResult
2773 matchAndRewrite(QueueClearOp op, OpAdaptor adaptor,
2774 ConversionPatternRewriter &rewriter) const override {
2775 auto refType = cast<llhd::RefType>(adaptor.getQueue().getType());
2776 auto queueType = refType.getNestedType();
2777 Value emptyQueue =
2778 sim::QueueEmptyOp::create(rewriter, op->getLoc(), queueType);
2779
2780 // Replace with an assignment to an empty queue
2781 Value delay = llhd::ConstantTimeOp::create(
2782 rewriter, op.getLoc(),
2783 getBlockingOrContinuousAssignDelay(rewriter.getContext()));
2784
2785 llhd::DriveOp::create(rewriter, op.getLoc(), adaptor.getQueue(), emptyQueue,
2786 delay, Value{});
2787
2788 rewriter.eraseOp(op);
2789 return success();
2790 }
2791};
2792
2793struct QueueInsertOpConversion : public OpConversionPattern<QueueInsertOp> {
2794 using OpConversionPattern::OpConversionPattern;
2795
2796 LogicalResult
2797 matchAndRewrite(QueueInsertOp op, OpAdaptor adaptor,
2798 ConversionPatternRewriter &rewriter) const override {
2799 probeRefAndDriveWithResult(
2800 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2801 auto insert =
2802 sim::QueueInsertOp::create(rewriter, op->getLoc(), queue,
2803 adaptor.getIndex(), adaptor.getItem());
2804
2805 return insert.getOutQueue();
2806 });
2807 rewriter.eraseOp(op);
2808
2809 return success();
2810 }
2811};
2812
2813struct QueueDeleteOpConversion : public OpConversionPattern<QueueDeleteOp> {
2814 using OpConversionPattern::OpConversionPattern;
2815
2816 LogicalResult
2817 matchAndRewrite(QueueDeleteOp op, OpAdaptor adaptor,
2818 ConversionPatternRewriter &rewriter) const override {
2819 probeRefAndDriveWithResult(
2820 rewriter, op.getLoc(), adaptor.getQueue(), [&](Value queue) {
2821 auto delOp = sim::QueueDeleteOp::create(rewriter, op->getLoc(), queue,
2822 adaptor.getIndex());
2823
2824 return delOp.getOutQueue();
2825 });
2826 rewriter.eraseOp(op);
2827
2828 return success();
2829 };
2830};
2831
2832struct QueueResizeOpConversion : public OpConversionPattern<QueueResizeOp> {
2833 using OpConversionPattern::OpConversionPattern;
2834
2835 LogicalResult
2836 matchAndRewrite(QueueResizeOp op, OpAdaptor adaptor,
2837 ConversionPatternRewriter &rewriter) const override {
2838
2839 rewriter.replaceOpWithNewOp<sim::QueueResizeOp>(
2840 op, getTypeConverter()->convertType(op.getResult().getType()),
2841 adaptor.getInput());
2842 return success();
2843 }
2844};
2845
2846struct QueueSetOpConversion : public OpConversionPattern<QueueSetOp> {
2847 using OpConversionPattern::OpConversionPattern;
2848 LogicalResult
2849 matchAndRewrite(QueueSetOp op, OpAdaptor adaptor,
2850 ConversionPatternRewriter &rewriter) const override {
2851 probeRefAndDriveWithResult(
2852 rewriter, op->getLoc(), adaptor.getQueue(), [&](Value queue) {
2853 auto setOp =
2854 sim::QueueSetOp::create(rewriter, op.getLoc(), queue,
2855 adaptor.getIndex(), adaptor.getItem());
2856 return setOp.getOutQueue();
2857 });
2858 rewriter.eraseOp(op);
2859 return success();
2860 }
2861};
2862
2863struct QueueCmpOpConversion : public OpConversionPattern<QueueCmpOp> {
2864 using OpConversionPattern::OpConversionPattern;
2865
2866 LogicalResult
2867 matchAndRewrite(QueueCmpOp op, OpAdaptor adaptor,
2868 ConversionPatternRewriter &rewriter) const override {
2869 // TODO: Right now Moore uses `UArrayCmpPredicate` for both queues/unpacked
2870 // arrays - reasonable because, per SV spec, queues *are* a type of unpacked
2871 // array). Once we support comparing unpacked arrays in core, it will make
2872 // sense to rename `QueueCmpPredicate` to `UArrayCmpPredicate` and use it
2873 // for both forms of comparisons.
2874
2875 // Convert the UArrayCmpPredicate into a QueueCmpPredicate
2876 auto unpackedPred = adaptor.getPredicateAttr().getValue();
2877 sim::QueueCmpPredicate queuePred;
2878 switch (unpackedPred) {
2879 case circt::moore::UArrayCmpPredicate::eq:
2880 queuePred = sim::QueueCmpPredicate::eq;
2881 break;
2882 case circt::moore::UArrayCmpPredicate::ne:
2883 queuePred = sim::QueueCmpPredicate::ne;
2884 break;
2885 }
2886
2887 auto cmpPred = sim::QueueCmpPredicateAttr::get(getContext(), queuePred);
2888
2889 rewriter.replaceOpWithNewOp<sim::QueueCmpOp>(op, cmpPred, adaptor.getLhs(),
2890 adaptor.getRhs());
2891 return success();
2892 }
2893};
2894
2895struct QueueFromUnpackedArrayOpConversion
2896 : public OpConversionPattern<QueueFromUnpackedArrayOp> {
2897 using OpConversionPattern::OpConversionPattern;
2898
2899 LogicalResult
2900 matchAndRewrite(QueueFromUnpackedArrayOp op, OpAdaptor adaptor,
2901 ConversionPatternRewriter &rewriter) const override {
2902 rewriter.replaceOpWithNewOp<sim::QueueFromArrayOp>(
2903 op, getTypeConverter()->convertType(op.getResult().getType()),
2904 adaptor.getInput());
2905 return success();
2906 }
2907};
2908
2909struct QueueConcatOpConversion : public OpConversionPattern<QueueConcatOp> {
2910 using OpConversionPattern::OpConversionPattern;
2911
2912 LogicalResult
2913 matchAndRewrite(QueueConcatOp op, OpAdaptor adaptor,
2914 ConversionPatternRewriter &rewriter) const override {
2915 rewriter.replaceOpWithNewOp<sim::QueueConcatOp>(
2916 op, getTypeConverter()->convertType(op.getResult().getType()),
2917 adaptor.getInputs());
2918 return success();
2919 }
2920};
2921
2922struct DisplayBIOpConversion : public OpConversionPattern<DisplayBIOp> {
2923 using OpConversionPattern::OpConversionPattern;
2924
2925 LogicalResult
2926 matchAndRewrite(DisplayBIOp op, OpAdaptor adaptor,
2927 ConversionPatternRewriter &rewriter) const override {
2928 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
2929 op, adaptor.getMessage());
2930 return success();
2931 }
2932};
2933
2934} // namespace
2935
2936//===----------------------------------------------------------------------===//
2937// Simulation Control Conversion
2938//===----------------------------------------------------------------------===//
2939
2940// moore.builtin.stop -> sim.pause
2941static LogicalResult convert(StopBIOp op, StopBIOp::Adaptor adaptor,
2942 ConversionPatternRewriter &rewriter) {
2943 rewriter.replaceOpWithNewOp<sim::PauseOp>(op, /*verbose=*/false);
2944 return success();
2945}
2946
2947// moore.builtin.finish -> sim.terminate
2948static LogicalResult convert(FinishBIOp op, FinishBIOp::Adaptor adaptor,
2949 ConversionPatternRewriter &rewriter) {
2950 rewriter.replaceOpWithNewOp<sim::TerminateOp>(op, op.getExitCode() == 0,
2951 /*verbose=*/false);
2952 return success();
2953}
2954
2955// moore.builtin.severity -> sim.proc.print
2956static LogicalResult convert(SeverityBIOp op, SeverityBIOp::Adaptor adaptor,
2957 ConversionPatternRewriter &rewriter) {
2958
2959 std::string severityString;
2960
2961 switch (op.getSeverity()) {
2962 case (Severity::Fatal):
2963 severityString = "Fatal: ";
2964 break;
2965 case (Severity::Error):
2966 severityString = "Error: ";
2967 break;
2968 case (Severity::Warning):
2969 severityString = "Warning: ";
2970 break;
2971 case (Severity::Info):
2972 severityString = "Info: ";
2973 break;
2974 }
2975
2976 auto prefix =
2977 sim::FormatLiteralOp::create(rewriter, op.getLoc(), severityString);
2978 auto message = sim::FormatStringConcatOp::create(
2979 rewriter, op.getLoc(), ValueRange{prefix, adaptor.getMessage()});
2980 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(op, message);
2981 return success();
2982}
2983
2984//===----------------------------------------------------------------------===//
2985// Random Builtin Conversion
2986//===----------------------------------------------------------------------===//
2987
2988/// moore.builtin.urandom_range -> call @__circt_urandom_range(i32, i32, ptr)
2989///
2990/// The seed pointer is null when no seed is provided. When a seed ref is
2991/// present, we probe the current value into an alloca before the call, and
2992/// drive the (potentially mutated) value back after.
2993static LogicalResult convert(UrandomRangeBIOp op,
2994 UrandomRangeBIOp::Adaptor adaptor,
2995 ConversionPatternRewriter &rewriter,
2996 FunctionCache &funcCache) {
2997 auto loc = op.getLoc();
2998 auto i32Ty = rewriter.getI32Type();
2999 auto ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
3000 auto fn = funcCache.getOrCreate(rewriter, "__circt_urandom_range",
3001 {i32Ty, i32Ty, ptrTy}, {i32Ty});
3002
3003 Value seedPtr;
3004 if (auto seedRef = adaptor.getSeed()) {
3005 // Allocate a temporary, probe the current seed value into it.
3006 auto one = hw::ConstantOp::create(rewriter, loc, i32Ty, 1);
3007 seedPtr = LLVM::AllocaOp::create(rewriter, loc, ptrTy, i32Ty, one);
3008 auto seedVal = llhd::ProbeOp::create(rewriter, loc, seedRef);
3009 LLVM::StoreOp::create(rewriter, loc, seedVal, seedPtr);
3010 } else {
3011 seedPtr = LLVM::ZeroOp::create(rewriter, loc, ptrTy);
3012 }
3013
3014 auto call = func::CallOp::create(
3015 rewriter, loc, fn,
3016 ValueRange{adaptor.getMinval(), adaptor.getMaxval(), seedPtr});
3017
3018 // Drive the potentially mutated seed back with an epsilon time delta.
3019 if (adaptor.getSeed()) {
3020 auto newSeed = LLVM::LoadOp::create(rewriter, loc, i32Ty, seedPtr);
3021 auto epsilon = llhd::ConstantTimeOp::create(
3022 rewriter, loc,
3023 llhd::TimeAttr::get(rewriter.getContext(), 0, "ns", 0, 1));
3024 llhd::DriveOp::create(rewriter, loc, adaptor.getSeed(), newSeed, epsilon,
3025 Value{});
3026 }
3027
3028 rewriter.replaceOp(op, call.getResult(0));
3029 return success();
3030}
3031
3032// moore.builtin.finish_message
3033static LogicalResult convert(FinishMessageBIOp op,
3034 FinishMessageBIOp::Adaptor adaptor,
3035 ConversionPatternRewriter &rewriter) {
3036 // We don't support printing termination/pause messages yet.
3037 rewriter.eraseOp(op);
3038 return success();
3039}
3040
3041//===----------------------------------------------------------------------===//
3042// Timing Control Conversion
3043//===----------------------------------------------------------------------===//
3044
3045// moore.builtin.time
3046static LogicalResult convert(TimeBIOp op, TimeBIOp::Adaptor adaptor,
3047 ConversionPatternRewriter &rewriter) {
3048 rewriter.replaceOpWithNewOp<llhd::CurrentTimeOp>(op);
3049 return success();
3050}
3051
3052// moore.logic_to_time
3053static LogicalResult convert(LogicToTimeOp op, LogicToTimeOp::Adaptor adaptor,
3054 ConversionPatternRewriter &rewriter) {
3055 rewriter.replaceOpWithNewOp<llhd::IntToTimeOp>(op, adaptor.getInput());
3056 return success();
3057}
3058
3059// moore.time_to_logic
3060static LogicalResult convert(TimeToLogicOp op, TimeToLogicOp::Adaptor adaptor,
3061 ConversionPatternRewriter &rewriter) {
3062 rewriter.replaceOpWithNewOp<llhd::TimeToIntOp>(op, adaptor.getInput());
3063 return success();
3064}
3065
3066//===----------------------------------------------------------------------===//
3067// Conversion Infrastructure
3068//===----------------------------------------------------------------------===//
3069
3070static void populateLegality(ConversionTarget &target,
3071 const TypeConverter &converter) {
3072 target.addIllegalDialect<MooreDialect>();
3073 target.addLegalDialect<comb::CombDialect>();
3074 target.addLegalDialect<hw::HWDialect>();
3075 target.addLegalDialect<seq::SeqDialect>();
3076 target.addLegalDialect<llhd::LLHDDialect>();
3077 target.addLegalDialect<ltl::LTLDialect>();
3078 target.addLegalDialect<mlir::BuiltinDialect>();
3079 target.addLegalDialect<mlir::math::MathDialect>();
3080 target.addLegalDialect<sim::SimDialect>();
3081 target.addLegalDialect<mlir::LLVM::LLVMDialect>();
3082 target.addLegalDialect<verif::VerifDialect>();
3083 target.addLegalDialect<arith::ArithDialect>();
3084
3085 target.addLegalOp<debug::ScopeOp>();
3086
3087 target.addDynamicallyLegalOp<scf::YieldOp, func::CallOp, func::ReturnOp,
3088 UnrealizedConversionCastOp, hw::OutputOp,
3089 hw::InstanceOp, debug::ArrayOp, debug::StructOp,
3090 debug::VariableOp>(
3091 [&](Operation *op) { return converter.isLegal(op); });
3092
3093 target.addDynamicallyLegalOp<scf::IfOp, scf::ForOp, scf::ExecuteRegionOp,
3094 scf::WhileOp, scf::ForallOp>([&](Operation *op) {
3095 return converter.isLegal(op) && !op->getParentOfType<llhd::ProcessOp>();
3096 });
3097
3098 target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
3099 return converter.isSignatureLegal(op.getFunctionType());
3100 });
3101
3102 target.addDynamicallyLegalOp<hw::HWModuleOp>([&](hw::HWModuleOp op) {
3103 return converter.isSignatureLegal(op.getModuleType().getFuncType()) &&
3104 converter.isLegal(&op.getBody());
3105 });
3106}
3107
3108static void populateTypeConversion(TypeConverter &typeConverter) {
3109 typeConverter.addConversion([&](IntType type) {
3110 return IntegerType::get(type.getContext(), type.getWidth());
3111 });
3112
3113 typeConverter.addConversion([&](RealType type) -> mlir::Type {
3114 MLIRContext *ctx = type.getContext();
3115 switch (type.getWidth()) {
3116 case moore::RealWidth::f32:
3117 return mlir::Float32Type::get(ctx);
3118 case moore::RealWidth::f64:
3119 return mlir::Float64Type::get(ctx);
3120 }
3121 });
3122
3123 typeConverter.addConversion(
3124 [&](TimeType type) { return llhd::TimeType::get(type.getContext()); });
3125
3126 typeConverter.addConversion([&](FormatStringType type) {
3127 return sim::FormatStringType::get(type.getContext());
3128 });
3129
3130 typeConverter.addConversion([&](StringType type) {
3131 return sim::DynamicStringType::get(type.getContext());
3132 });
3133
3134 typeConverter.addConversion([&](QueueType type) {
3135 return sim::QueueType::get(type.getContext(),
3136 typeConverter.convertType(type.getElementType()),
3137 type.getBound());
3138 });
3139
3140 typeConverter.addConversion([&](ArrayType type) -> std::optional<Type> {
3141 if (auto elementType = typeConverter.convertType(type.getElementType()))
3142 return hw::ArrayType::get(elementType, type.getSize());
3143 return {};
3144 });
3145
3146 // FIXME: Unpacked arrays support more element types than their packed
3147 // variants, and as such, mapping them to hw::Array is somewhat naive. See
3148 // also the analogous note below concerning unpacked struct type conversion.
3149 typeConverter.addConversion(
3150 [&](UnpackedArrayType type) -> std::optional<Type> {
3151 if (auto elementType = typeConverter.convertType(type.getElementType()))
3152 return hw::ArrayType::get(elementType, type.getSize());
3153 return {};
3154 });
3155
3156 typeConverter.addConversion([&](OpenArrayType type) -> std::optional<Type> {
3157 return LLVM::LLVMPointerType::get(type.getContext());
3158 });
3159
3160 typeConverter.addConversion(
3161 [&](OpenUnpackedArrayType type) -> std::optional<Type> {
3162 return LLVM::LLVMPointerType::get(type.getContext());
3163 });
3164
3165 typeConverter.addConversion([&](StructType type) -> std::optional<Type> {
3166 SmallVector<hw::StructType::FieldInfo> fields;
3167 for (auto field : type.getMembers()) {
3168 hw::StructType::FieldInfo info;
3169 info.type = typeConverter.convertType(field.type);
3170 if (!info.type)
3171 return {};
3172 info.name = field.name;
3173 fields.push_back(info);
3174 }
3175 return hw::StructType::get(type.getContext(), fields);
3176 });
3177
3178 // FIXME: Mapping unpacked struct type to struct type in hw dialect may be a
3179 // plain solution. The packed and unpacked data structures have some
3180 // differences though they look similarily. The packed data structure is
3181 // contiguous in memory but another is opposite. The differences will affect
3182 // data layout and granularity of event tracking in simulation.
3183 typeConverter.addConversion(
3184 [&](UnpackedStructType type) -> std::optional<Type> {
3185 SmallVector<hw::StructType::FieldInfo> fields;
3186 for (auto field : type.getMembers()) {
3187 hw::StructType::FieldInfo info;
3188 info.type = typeConverter.convertType(field.type);
3189 if (!info.type)
3190 return {};
3191 info.name = field.name;
3192 fields.push_back(info);
3193 }
3194 return hw::StructType::get(type.getContext(), fields);
3195 });
3196
3197 // UnionType -> hw::UnionType
3198 typeConverter.addConversion([&](UnionType type) -> std::optional<Type> {
3199 SmallVector<hw::UnionType::FieldInfo> fields;
3200 for (auto field : type.getMembers()) {
3201 hw::UnionType::FieldInfo info;
3202 info.type = typeConverter.convertType(field.type);
3203 if (!info.type)
3204 return {};
3205 info.name = field.name;
3206 info.offset = 0; // packed union, all fields start at bit 0
3207 fields.push_back(info);
3208 }
3209 auto result = hw::UnionType::get(type.getContext(), fields);
3210 return result;
3211 });
3212
3213 // UnpackedUnionType -> hw::UnionType
3214 typeConverter.addConversion(
3215 [&](UnpackedUnionType type) -> std::optional<Type> {
3216 SmallVector<hw::UnionType::FieldInfo> fields;
3217 for (auto field : type.getMembers()) {
3218 hw::UnionType::FieldInfo info;
3219 info.type = typeConverter.convertType(field.type);
3220 if (!info.type)
3221 return {};
3222 info.name = field.name;
3223 info.offset = 0;
3224 fields.push_back(info);
3225 }
3226 return hw::UnionType::get(type.getContext(), fields);
3227 });
3228
3229 // Conversion of CHandle to LLVMPointerType
3230 typeConverter.addConversion([&](ChandleType type) -> std::optional<Type> {
3231 return LLVM::LLVMPointerType::get(type.getContext());
3232 });
3233
3234 // Explicitly mark LLVMPointerType as a legal target
3235 typeConverter.addConversion(
3236 [](LLVM::LLVMPointerType t) -> std::optional<Type> { return t; });
3237
3238 // ClassHandleType -> !llvm.ptr
3239 typeConverter.addConversion([&](ClassHandleType type) -> std::optional<Type> {
3240 return LLVM::LLVMPointerType::get(type.getContext());
3241 });
3242
3243 typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
3244 if (isa<OpenArrayType, OpenUnpackedArrayType>(type.getNestedType()))
3245 return LLVM::LLVMPointerType::get(type.getContext());
3246 if (auto innerType = typeConverter.convertType(type.getNestedType()))
3247 return llhd::RefType::get(innerType);
3248 return {};
3249 });
3250
3251 // Valid target types.
3252 typeConverter.addConversion([](IntegerType type) { return type; });
3253 typeConverter.addConversion([](FloatType type) { return type; });
3254 typeConverter.addConversion([](sim::DynamicStringType type) { return type; });
3255 typeConverter.addConversion([](llhd::TimeType type) { return type; });
3256 typeConverter.addConversion([](debug::ArrayType type) { return type; });
3257 typeConverter.addConversion([](debug::ScopeType type) { return type; });
3258 typeConverter.addConversion([](debug::StructType type) { return type; });
3259
3260 typeConverter.addConversion([&](llhd::RefType type) -> std::optional<Type> {
3261 if (auto innerType = typeConverter.convertType(type.getNestedType()))
3262 return llhd::RefType::get(innerType);
3263 return {};
3264 });
3265
3266 typeConverter.addConversion([&](hw::ArrayType type) -> std::optional<Type> {
3267 if (auto elementType = typeConverter.convertType(type.getElementType()))
3268 return hw::ArrayType::get(elementType, type.getNumElements());
3269 return {};
3270 });
3271
3272 typeConverter.addConversion([&](hw::StructType type) -> std::optional<Type> {
3273 SmallVector<hw::StructType::FieldInfo> fields;
3274 for (auto field : type.getElements()) {
3275 hw::StructType::FieldInfo info;
3276 info.type = typeConverter.convertType(field.type);
3277 if (!info.type)
3278 return {};
3279 info.name = field.name;
3280 fields.push_back(info);
3281 }
3282 return hw::StructType::get(type.getContext(), fields);
3283 });
3284
3285 typeConverter.addConversion([&](hw::UnionType type) -> std::optional<Type> {
3286 SmallVector<hw::UnionType::FieldInfo> fields;
3287 for (auto field : type.getElements()) {
3288 hw::UnionType::FieldInfo info;
3289 info.type = typeConverter.convertType(field.type);
3290 if (!info.type)
3291 return {};
3292 info.name = field.name;
3293 info.offset = field.offset;
3294 fields.push_back(info);
3295 }
3296 return hw::UnionType::get(type.getContext(), fields);
3297 });
3298
3299 typeConverter.addTargetMaterialization(
3300 [&](mlir::OpBuilder &builder, mlir::Type resultType,
3301 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
3302 if (inputs.size() != 1 || !inputs[0])
3303 return Value();
3304 return UnrealizedConversionCastOp::create(builder, loc, resultType,
3305 inputs[0])
3306 .getResult(0);
3307 });
3308
3309 typeConverter.addSourceMaterialization(
3310 [&](mlir::OpBuilder &builder, mlir::Type resultType,
3311 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
3312 if (inputs.size() != 1)
3313 return Value();
3314 return UnrealizedConversionCastOp::create(builder, loc, resultType,
3315 inputs[0])
3316 ->getResult(0);
3317 });
3318}
3319
3321 TypeConverter &typeConverter,
3322 ClassTypeCache &classCache,
3323 FunctionCache &funcCache) {
3324
3325 patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
3326 classCache);
3327 patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
3328 classCache, funcCache);
3329 patterns.add<ClassPropertyRefOpConversion>(typeConverter,
3330 patterns.getContext(), classCache);
3331
3332 // clang-format off
3333 patterns.add<
3334 ClassUpcastOpConversion,
3335 // Patterns of declaration operations.
3336 VariableOpConversion,
3337 NetOpConversion,
3338
3339 // Patterns for conversion operations.
3340 ConversionOpConversion,
3341 BitcastConversion<PackedToSBVOp>,
3342 BitcastConversion<SBVToPackedOp>,
3343 NoOpConversion<LogicToIntOp>,
3344 NoOpConversion<IntToLogicOp>,
3345 NoOpConversion<ToBuiltinIntOp>,
3346 NoOpConversion<FromBuiltinIntOp>,
3347 TruncOpConversion,
3348 ZExtOpConversion,
3349 SExtOpConversion,
3350 SIntToRealOpConversion,
3351 UIntToRealOpConversion,
3352 IntToStringOpConversion,
3353 RealToIntOpConversion,
3354 ConvertRealOpConversion,
3355
3356 // Patterns of miscellaneous operations.
3357 ConstantOpConv,
3358 ConstantRealOpConv,
3359 ConcatOpConversion,
3360 ReplicateOpConversion,
3361 ConstantTimeOpConv,
3362 ExtractOpConversion,
3363 DynExtractOpConversion,
3364 DynExtractRefOpConversion,
3365 ReadOpConversion,
3366 StructExtractOpConversion,
3367 StructExtractRefOpConversion,
3368 ExtractRefOpConversion,
3369 StructCreateOpConversion,
3370 UnionCreateOpConversion,
3371 UnionExtractOpConversion,
3372 UnionExtractRefOpConversion,
3373 ConditionalOpConversion,
3374 ArrayCreateOpConversion,
3375 YieldOpConversion,
3376 OutputOpConversion,
3377 ConstantStringOpConv,
3378
3379 // Patterns of unary operations.
3380 ReduceAndOpConversion,
3381 ReduceOrOpConversion,
3382 ReduceXorOpConversion,
3383 BoolCastOpConversion,
3384 NotOpConversion,
3385 NegOpConversion,
3386
3387 // Patterns of binary operations.
3388 BinaryOpConversion<AddOp, comb::AddOp>,
3389 BinaryOpConversion<SubOp, comb::SubOp>,
3390 BinaryOpConversion<MulOp, comb::MulOp>,
3391 BinaryOpConversion<DivUOp, comb::DivUOp>,
3392 BinaryOpConversion<DivSOp, comb::DivSOp>,
3393 BinaryOpConversion<ModUOp, comb::ModUOp>,
3394 BinaryOpConversion<ModSOp, comb::ModSOp>,
3395 BinaryOpConversion<AndOp, comb::AndOp>,
3396 BinaryOpConversion<OrOp, comb::OrOp>,
3397 BinaryOpConversion<XorOp, comb::XorOp>,
3398
3399 // Patterns for unary real operations.
3400 NegRealOpConversion,
3401
3402 // Patterns for binary real operations.
3403 BinaryRealOpConversion<AddRealOp, arith::AddFOp>,
3404 BinaryRealOpConversion<SubRealOp, arith::SubFOp>,
3405 BinaryRealOpConversion<DivRealOp, arith::DivFOp>,
3406 BinaryRealOpConversion<MulRealOp, arith::MulFOp>,
3407 BinaryRealOpConversion<PowRealOp, math::PowFOp>,
3408
3409 // Patterns of power operations.
3410 PowUOpConversion, PowSOpConversion,
3411
3412 // Patterns of relational operations.
3413 ICmpOpConversion<UltOp, ICmpPredicate::ult>,
3414 ICmpOpConversion<SltOp, ICmpPredicate::slt>,
3415 ICmpOpConversion<UleOp, ICmpPredicate::ule>,
3416 ICmpOpConversion<SleOp, ICmpPredicate::sle>,
3417 ICmpOpConversion<UgtOp, ICmpPredicate::ugt>,
3418 ICmpOpConversion<SgtOp, ICmpPredicate::sgt>,
3419 ICmpOpConversion<UgeOp, ICmpPredicate::uge>,
3420 ICmpOpConversion<SgeOp, ICmpPredicate::sge>,
3421 ICmpOpConversion<EqOp, ICmpPredicate::eq>,
3422 ICmpOpConversion<NeOp, ICmpPredicate::ne>,
3423 ICmpOpConversion<CaseEqOp, ICmpPredicate::ceq>,
3424 ICmpOpConversion<CaseNeOp, ICmpPredicate::cne>,
3425 ICmpOpConversion<WildcardEqOp, ICmpPredicate::weq>,
3426 ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
3427 FCmpOpConversion<NeRealOp, arith::CmpFPredicate::ONE>,
3428 FCmpOpConversion<FltOp, arith::CmpFPredicate::OLT>,
3429 FCmpOpConversion<FleOp, arith::CmpFPredicate::OLE>,
3430 FCmpOpConversion<FgtOp, arith::CmpFPredicate::OGT>,
3431 FCmpOpConversion<FgeOp, arith::CmpFPredicate::OGE>,
3432 FCmpOpConversion<EqRealOp, arith::CmpFPredicate::OEQ>,
3433 CaseXZEqOpConversion<CaseZEqOp, true>,
3434 CaseXZEqOpConversion<CaseXZEqOp, false>,
3435
3436 // Patterns of structural operations.
3437 SVModuleOpConversion,
3438 InstanceOpConversion,
3439 ProcedureOpConversion,
3440 CoroutineOpConversion,
3441 CallCoroutineOpConversion,
3442 WaitEventOpConversion,
3443
3444 // Patterns of shifting operations.
3445 ShrOpConversion,
3446 ShlOpConversion,
3447 AShrOpConversion,
3448
3449 // Patterns of assignment operations.
3450 AssignOpConversion<ContinuousAssignOp>,
3451 AssignOpConversion<DelayedContinuousAssignOp>,
3452 AssignOpConversion<BlockingAssignOp>,
3453 AssignOpConversion<NonBlockingAssignOp>,
3454 AssignOpConversion<DelayedNonBlockingAssignOp>,
3455 AssignedVariableOpConversion,
3456
3457 // Patterns of other operations outside Moore dialect.
3458 HWInstanceOpConversion,
3459 ReturnOpConversion,
3460 CallOpConversion,
3461 DPIFuncOpConversion,
3462 FuncDPICallOpConversion,
3463 UnrealizedConversionCastConversion,
3464 InPlaceOpConversion<debug::ArrayOp>,
3465 InPlaceOpConversion<debug::StructOp>,
3466 InPlaceOpConversion<debug::VariableOp>,
3467
3468 // Patterns of assert-like operations
3469 AssertLikeOpConversion<AssertOp, verif::AssertOp>,
3470 AssertLikeOpConversion<AssumeOp, verif::AssumeOp>,
3471 AssertLikeOpConversion<CoverOp, verif::CoverOp>,
3472
3473 // Format strings.
3474 FormatLiteralOpConversion,
3475 FormatConcatOpConversion,
3476 FormatHierPathOpConversion,
3477 FormatIntOpConversion,
3478 FormatRealOpConversion,
3479 DisplayBIOpConversion,
3480
3481 // Dynamic string operations
3482 StringLenOpConversion,
3483 StringConcatOpConversion,
3484 StringGetOpConversion,
3485
3486 // Queue operations
3487 QueueSizeBIOpConversion,
3488 QueuePushBackOpConversion,
3489 QueuePushFrontOpConversion,
3490 QueuePopBackOpConversion,
3491 QueuePopFrontOpConversion,
3492 QueueDeleteOpConversion,
3493 QueueInsertOpConversion,
3494 QueueClearOpConversion,
3495 DynQueueExtractOpConversion,
3496 QueueResizeOpConversion,
3497 QueueSetOpConversion,
3498 QueueCmpOpConversion,
3499 QueueFromUnpackedArrayOpConversion,
3500 QueueConcatOpConversion
3501 >(typeConverter, patterns.getContext());
3502 // clang-format on
3503
3504 // Structural operations
3505 patterns.add<WaitDelayOp>(convert);
3506 patterns.add<UnreachableOp>(convert);
3507 patterns.add<GlobalVariableOp>(convert);
3508 patterns.add<GetGlobalVariableOp>(convert);
3509
3510 // Simulation control
3511 patterns.add<StopBIOp>(convert);
3512 patterns.add<SeverityBIOp>(convert);
3513 patterns.add<FinishBIOp>(convert);
3514 patterns.add<FinishMessageBIOp>(convert);
3515
3516 // Random builtins
3517 patterns.add<UrandomRangeBIOp>(convert, funcCache);
3518
3519 // Timing control
3520 patterns.add<TimeBIOp>(convert);
3521 patterns.add<LogicToTimeOp>(convert);
3522 patterns.add<TimeToLogicOp>(convert);
3523
3524 mlir::populateAnyFunctionOpInterfaceTypeConversionPattern(patterns,
3525 typeConverter);
3526 hw::populateHWModuleLikeTypeConversionPattern(
3527 hw::HWModuleOp::getOperationName(), patterns, typeConverter);
3528 populateSCFToControlFlowConversionPatterns(patterns);
3529 populateArithToCombPatterns(patterns, typeConverter);
3530}
3531
3532//===----------------------------------------------------------------------===//
3533// Moore to Core Conversion Pass
3534//===----------------------------------------------------------------------===//
3535
3536namespace {
3537struct MooreToCorePass
3538 : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
3539 void runOnOperation() override;
3540};
3541} // namespace
3542
3543/// Create a Moore to core dialects conversion pass.
3544std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
3545 return std::make_unique<MooreToCorePass>();
3546}
3547
3548/// This is the main entrypoint for the Moore to Core conversion pass.
3549void MooreToCorePass::runOnOperation() {
3550 MLIRContext &context = getContext();
3551 ModuleOp module = getOperation();
3552 ClassTypeCache classCache;
3553 auto &symbolTable = getAnalysis<SymbolTable>();
3554 FunctionCache funcCache(symbolTable);
3555
3556 IRRewriter rewriter(module);
3557 (void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
3558
3559 TypeConverter typeConverter;
3560 populateTypeConversion(typeConverter);
3561
3562 ConversionTarget target(context);
3563 populateLegality(target, typeConverter);
3564
3565 ConversionPatternSet patterns(&context, typeConverter);
3566 populateOpConversion(patterns, typeConverter, classCache, funcCache);
3567 mlir::cf::populateCFStructuralTypeConversionsAndLegality(typeConverter,
3568 patterns, target);
3569
3570 if (failed(applyFullConversion(module, target, std::move(patterns))))
3571 signalPassFailure();
3572}
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.