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