CIRCT 22.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/// Ensure we have `declare i8* @malloc(i64)` (opaque ptr prints as !llvm.ptr).
100static LLVM::LLVMFuncOp getOrCreateMalloc(ModuleOp mod, OpBuilder &b) {
101 if (auto f = mod.lookupSymbol<LLVM::LLVMFuncOp>("malloc"))
102 return f;
103
104 OpBuilder::InsertionGuard g(b);
105 b.setInsertionPointToStart(mod.getBody());
106
107 auto i64Ty = IntegerType::get(mod.getContext(), 64);
108 auto ptrTy = LLVM::LLVMPointerType::get(mod.getContext()); // opaque pointer
109 auto fnTy = LLVM::LLVMFunctionType::get(ptrTy, {i64Ty}, false);
110
111 auto fn = LLVM::LLVMFuncOp::create(b, mod.getLoc(), "malloc", fnTy);
112 // Link this in from somewhere else.
113 fn.setLinkage(LLVM::Linkage::External);
114 return fn;
115}
116
117/// Helper function to create an opaque LLVM Struct Type which corresponds
118/// to the sym
119static LLVM::LLVMStructType getOrCreateOpaqueStruct(MLIRContext *ctx,
120 SymbolRefAttr className) {
121 return LLVM::LLVMStructType::getIdentified(ctx, className.getRootReference());
122}
123
124static LogicalResult resolveClassStructBody(ClassDeclOp op,
125 TypeConverter const &typeConverter,
126 ClassTypeCache &cache) {
127
128 auto classSym = SymbolRefAttr::get(op.getSymNameAttr());
129 auto structInfo = cache.getStructInfo(classSym);
130 if (structInfo)
131 // We already have a resolved class struct body.
132 return success();
133
134 // Otherwise we need to resolve.
135 ClassTypeCache::ClassStructInfo structBody;
136 SmallVector<Type> structBodyMembers;
137
138 // Base-first (prefix) layout for single inheritance.
139 unsigned derivedStartIdx = 0;
140
141 if (auto baseClass = op.getBaseAttr()) {
142
143 ModuleOp mod = op->getParentOfType<ModuleOp>();
144 auto *opSym = mod.lookupSymbol(baseClass);
145 auto classDeclOp = cast<ClassDeclOp>(opSym);
146
147 if (failed(resolveClassStructBody(classDeclOp, typeConverter, cache)))
148 return failure();
149
150 // Process base class' struct layout first
151 auto baseClassStruct = cache.getStructInfo(baseClass);
152 structBodyMembers.push_back(baseClassStruct->classBody);
153 derivedStartIdx = 1;
154
155 // Inherit base field paths with a leading 0.
156 for (auto &kv : baseClassStruct->propertyPath) {
157 SmallVector<unsigned, 2> path;
158 path.push_back(0); // into base subobject
159 path.append(kv.second.begin(), kv.second.end());
160 structBody.setFieldPath(kv.first, path);
161 }
162 }
163
164 // Properties in source order.
165 unsigned iterator = derivedStartIdx;
166 auto &block = op.getBody().front();
167 for (Operation &child : block) {
168 if (auto prop = dyn_cast<ClassPropertyDeclOp>(child)) {
169 Type mooreTy = prop.getPropertyType();
170 Type llvmTy = typeConverter.convertType(mooreTy);
171 if (!llvmTy)
172 return prop.emitOpError()
173 << "failed to convert property type " << mooreTy;
174
175 structBodyMembers.push_back(llvmTy);
176
177 // Derived field path: either {i} or {1+i} if base is present.
178 SmallVector<unsigned, 2> path{iterator};
179 structBody.setFieldPath(prop.getSymName(), path);
180 ++iterator;
181 }
182 }
183
184 // TODO: Handle vtable generation over ClassMethodDeclOp here.
185 auto llvmStructTy = getOrCreateOpaqueStruct(op.getContext(), classSym);
186 // Empty structs may be kept opaque
187 if (!structBodyMembers.empty() &&
188 failed(llvmStructTy.setBody(structBodyMembers, false)))
189 return op.emitOpError() << "Failed to set LLVM Struct body";
190
191 structBody.classBody = llvmStructTy;
192 cache.setClassInfo(classSym, structBody);
193
194 return success();
195}
196
197/// Convenience overload that looks up ClassDeclOp
198static LogicalResult resolveClassStructBody(ModuleOp mod, SymbolRefAttr op,
199 TypeConverter const &typeConverter,
200 ClassTypeCache &cache) {
201 auto classDeclOp = cast<ClassDeclOp>(*mod.lookupSymbol(op));
202 return resolveClassStructBody(classDeclOp, typeConverter, cache);
203}
204
205/// Returns the passed value if the integer width is already correct.
206/// Zero-extends if it is too narrow.
207/// Truncates if the integer is too wide and the truncated part is zero, if it
208/// is not zero it returns the max value integer of target-width.
209static Value adjustIntegerWidth(OpBuilder &builder, Value value,
210 uint32_t targetWidth, Location loc) {
211 uint32_t intWidth = value.getType().getIntOrFloatBitWidth();
212 if (intWidth == targetWidth)
213 return value;
214
215 if (intWidth < targetWidth) {
216 Value zeroExt = hw::ConstantOp::create(
217 builder, loc, builder.getIntegerType(targetWidth - intWidth), 0);
218 return comb::ConcatOp::create(builder, loc, ValueRange{zeroExt, value});
219 }
220
221 Value hi = comb::ExtractOp::create(builder, loc, value, targetWidth,
222 intWidth - targetWidth);
223 Value zero = hw::ConstantOp::create(
224 builder, loc, builder.getIntegerType(intWidth - targetWidth), 0);
225 Value isZero = comb::ICmpOp::create(builder, loc, comb::ICmpPredicate::eq, hi,
226 zero, false);
227 Value lo = comb::ExtractOp::create(builder, loc, value, 0, targetWidth);
228 Value max = hw::ConstantOp::create(builder, loc,
229 builder.getIntegerType(targetWidth), -1);
230 return comb::MuxOp::create(builder, loc, isZero, lo, max, false);
231}
232
233/// Get the ModulePortInfo from a SVModuleOp.
234static hw::ModulePortInfo getModulePortInfo(const TypeConverter &typeConverter,
235 SVModuleOp op) {
236 size_t inputNum = 0;
237 size_t resultNum = 0;
238 auto moduleTy = op.getModuleType();
239 SmallVector<hw::PortInfo> ports;
240 ports.reserve(moduleTy.getNumPorts());
241
242 for (auto port : moduleTy.getPorts()) {
243 Type portTy = typeConverter.convertType(port.type);
244 if (port.dir == hw::ModulePort::Direction::Output) {
245 ports.push_back(
246 hw::PortInfo({{port.name, portTy, port.dir}, resultNum++, {}}));
247 } else {
248 // FIXME: Once we support net<...>, ref<...> type to represent type of
249 // special port like inout or ref port which is not a input or output
250 // port. It can change to generate corresponding types for direction of
251 // port or do specified operation to it. Now inout and ref port is treated
252 // as input port.
253 ports.push_back(
254 hw::PortInfo({{port.name, portTy, port.dir}, inputNum++, {}}));
255 }
256 }
257
258 return hw::ModulePortInfo(ports);
259}
260
261//===----------------------------------------------------------------------===//
262// Structural Conversion
263//===----------------------------------------------------------------------===//
264
265struct SVModuleOpConversion : public OpConversionPattern<SVModuleOp> {
266 using OpConversionPattern::OpConversionPattern;
267
268 LogicalResult
269 matchAndRewrite(SVModuleOp op, OpAdaptor adaptor,
270 ConversionPatternRewriter &rewriter) const override {
271 rewriter.setInsertionPoint(op);
272
273 // Create the hw.module to replace moore.module
274 auto hwModuleOp =
275 hw::HWModuleOp::create(rewriter, op.getLoc(), op.getSymNameAttr(),
276 getModulePortInfo(*typeConverter, op));
277 // Make hw.module have the same visibility as the moore.module.
278 // The entry/top level module is public, otherwise is private.
279 SymbolTable::setSymbolVisibility(hwModuleOp,
280 SymbolTable::getSymbolVisibility(op));
281 rewriter.eraseBlock(hwModuleOp.getBodyBlock());
282 if (failed(
283 rewriter.convertRegionTypes(&op.getBodyRegion(), *typeConverter)))
284 return failure();
285 rewriter.inlineRegionBefore(op.getBodyRegion(), hwModuleOp.getBodyRegion(),
286 hwModuleOp.getBodyRegion().end());
287
288 // Erase the original op
289 rewriter.eraseOp(op);
290 return success();
291 }
292};
293
294struct OutputOpConversion : public OpConversionPattern<OutputOp> {
295 using OpConversionPattern::OpConversionPattern;
296
297 LogicalResult
298 matchAndRewrite(OutputOp op, OpAdaptor adaptor,
299 ConversionPatternRewriter &rewriter) const override {
300 rewriter.replaceOpWithNewOp<hw::OutputOp>(op, adaptor.getOperands());
301 return success();
302 }
303};
304
305struct InstanceOpConversion : public OpConversionPattern<InstanceOp> {
306 using OpConversionPattern::OpConversionPattern;
307
308 LogicalResult
309 matchAndRewrite(InstanceOp op, OpAdaptor adaptor,
310 ConversionPatternRewriter &rewriter) const override {
311 auto instName = op.getInstanceNameAttr();
312 auto moduleName = op.getModuleNameAttr();
313
314 // Create the new hw instanceOp to replace the original one.
315 rewriter.setInsertionPoint(op);
316 auto instOp = hw::InstanceOp::create(
317 rewriter, op.getLoc(), op.getResultTypes(), instName, moduleName,
318 op.getInputs(), op.getInputNamesAttr(), op.getOutputNamesAttr(),
319 /*Parameter*/ rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr,
320 /*doNotPrint*/ nullptr);
321
322 // Replace uses chain and erase the original op.
323 op.replaceAllUsesWith(instOp.getResults());
324 rewriter.eraseOp(op);
325 return success();
326 }
327};
328
329static void getValuesToObserve(Region *region,
330 function_ref<void(Value)> setInsertionPoint,
331 const TypeConverter *typeConverter,
332 ConversionPatternRewriter &rewriter,
333 SmallVector<Value> &observeValues) {
334 SmallDenseSet<Value> alreadyObserved;
335 Location loc = region->getLoc();
336
337 auto probeIfSignal = [&](Value value) -> Value {
338 if (!isa<llhd::RefType>(value.getType()))
339 return value;
340 return llhd::ProbeOp::create(rewriter, loc, value);
341 };
342
343 region->getParentOp()->walk<WalkOrder::PreOrder, ForwardDominanceIterator<>>(
344 [&](Operation *operation) {
345 for (auto value : operation->getOperands()) {
346 if (isa<BlockArgument>(value))
347 value = rewriter.getRemappedValue(value);
348
349 if (region->isAncestor(value.getParentRegion()))
350 continue;
351 if (auto *defOp = value.getDefiningOp();
352 defOp && defOp->hasTrait<OpTrait::ConstantLike>())
353 continue;
354 if (!alreadyObserved.insert(value).second)
355 continue;
356
357 OpBuilder::InsertionGuard g(rewriter);
358 if (auto remapped = rewriter.getRemappedValue(value)) {
359 setInsertionPoint(remapped);
360 observeValues.push_back(probeIfSignal(remapped));
361 } else {
362 setInsertionPoint(value);
363 auto type = typeConverter->convertType(value.getType());
364 auto converted = typeConverter->materializeTargetConversion(
365 rewriter, loc, type, value);
366 observeValues.push_back(probeIfSignal(converted));
367 }
368 }
369 });
370}
371
372struct ProcedureOpConversion : public OpConversionPattern<ProcedureOp> {
373 using OpConversionPattern::OpConversionPattern;
374
375 LogicalResult
376 matchAndRewrite(ProcedureOp op, OpAdaptor adaptor,
377 ConversionPatternRewriter &rewriter) const override {
378 // Collect values to observe before we do any modifications to the region.
379 SmallVector<Value> observedValues;
380 if (op.getKind() == ProcedureKind::AlwaysComb ||
381 op.getKind() == ProcedureKind::AlwaysLatch) {
382 auto setInsertionPoint = [&](Value value) {
383 rewriter.setInsertionPoint(op);
384 };
385 getValuesToObserve(&op.getBody(), setInsertionPoint, typeConverter,
386 rewriter, observedValues);
387 }
388
389 auto loc = op.getLoc();
390 if (failed(rewriter.convertRegionTypes(&op.getBody(), *typeConverter)))
391 return failure();
392
393 // Handle initial and final procedures. These lower to a corresponding
394 // `llhd.process` or `llhd.final` op that executes the body and then halts.
395 if (op.getKind() == ProcedureKind::Initial ||
396 op.getKind() == ProcedureKind::Final) {
397 Operation *newOp;
398 if (op.getKind() == ProcedureKind::Initial)
399 newOp = llhd::ProcessOp::create(rewriter, loc, TypeRange{});
400 else
401 newOp = llhd::FinalOp::create(rewriter, loc);
402 auto &body = newOp->getRegion(0);
403 rewriter.inlineRegionBefore(op.getBody(), body, body.end());
404 for (auto returnOp :
405 llvm::make_early_inc_range(body.getOps<ReturnOp>())) {
406 rewriter.setInsertionPoint(returnOp);
407 rewriter.replaceOpWithNewOp<llhd::HaltOp>(returnOp, ValueRange{});
408 }
409 rewriter.eraseOp(op);
410 return success();
411 }
412
413 // All other procedures lower to a an `llhd.process`.
414 auto newOp = llhd::ProcessOp::create(rewriter, loc, TypeRange{});
415
416 // We need to add an empty entry block because it is not allowed in MLIR to
417 // branch back to the entry block. Instead we put the logic in the second
418 // block and branch to that.
419 rewriter.createBlock(&newOp.getBody());
420 auto *block = &op.getBody().front();
421 cf::BranchOp::create(rewriter, loc, block);
422 rewriter.inlineRegionBefore(op.getBody(), newOp.getBody(),
423 newOp.getBody().end());
424
425 // Add special handling for `always_comb` and `always_latch` procedures.
426 // These run once at simulation startup and then implicitly wait for any of
427 // the values they access to change before running again. To implement this,
428 // we create another basic block that contains the implicit wait, and make
429 // all `moore.return` ops branch to that wait block instead of immediately
430 // jumping back up to the body.
431 if (op.getKind() == ProcedureKind::AlwaysComb ||
432 op.getKind() == ProcedureKind::AlwaysLatch) {
433 Block *waitBlock = rewriter.createBlock(&newOp.getBody());
434 llhd::WaitOp::create(rewriter, loc, ValueRange{}, Value(), observedValues,
435 ValueRange{}, block);
436 block = waitBlock;
437 }
438
439 // Make all `moore.return` ops branch back up to the beginning of the
440 // process, or the wait block created above for `always_comb` and
441 // `always_latch` procedures.
442 for (auto returnOp : llvm::make_early_inc_range(newOp.getOps<ReturnOp>())) {
443 rewriter.setInsertionPoint(returnOp);
444 cf::BranchOp::create(rewriter, loc, block);
445 rewriter.eraseOp(returnOp);
446 }
447
448 rewriter.eraseOp(op);
449 return success();
450 }
451};
452
453struct WaitEventOpConversion : public OpConversionPattern<WaitEventOp> {
454 using OpConversionPattern::OpConversionPattern;
455
456 LogicalResult
457 matchAndRewrite(WaitEventOp op, OpAdaptor adaptor,
458 ConversionPatternRewriter &rewriter) const override {
459 // In order to convert the `wait_event` op we need to create three separate
460 // blocks at the location of the op:
461 //
462 // - A "wait" block that reads the current state of any values used to
463 // detect events and then waits until any of those values change. When a
464 // change occurs, control transfers to the "check" block.
465 // - A "check" block which is executed after any interesting signal has
466 // changed. This is where any `detect_event` ops read the current state of
467 // interesting values and compare them against their state before the wait
468 // in order to detect an event. If any events were detected, control
469 // transfers to the "resume" block; otherwise control goes back to the
470 // "wait" block.
471 // - A "resume" block which holds any ops after the `wait_event` op. This is
472 // where control is expected to resume after an event has happened.
473 //
474 // Block structure before:
475 // opA
476 // moore.wait_event { ... }
477 // opB
478 //
479 // Block structure after:
480 // opA
481 // cf.br ^wait
482 // ^wait:
483 // <read "before" values>
484 // llhd.wait ^check, ...
485 // ^check:
486 // <read "after" values>
487 // <detect edges>
488 // cf.cond_br %event, ^resume, ^wait
489 // ^resume:
490 // opB
491 auto *resumeBlock =
492 rewriter.splitBlock(op->getBlock(), ++Block::iterator(op));
493
494 // If the 'wait_event' op is empty, we can lower it to a 'llhd.wait' op
495 // without any observed values, but since the process will never wake up
496 // from suspension anyway, we can also just terminate it using the
497 // 'llhd.halt' op.
498 if (op.getBody().front().empty()) {
499 // Let the cleanup iteration after the dialect conversion clean up all
500 // remaining unreachable blocks.
501 rewriter.replaceOpWithNewOp<llhd::HaltOp>(op, ValueRange{});
502 return success();
503 }
504
505 auto *waitBlock = rewriter.createBlock(resumeBlock);
506 auto *checkBlock = rewriter.createBlock(resumeBlock);
507
508 auto loc = op.getLoc();
509 rewriter.setInsertionPoint(op);
510 cf::BranchOp::create(rewriter, loc, waitBlock);
511
512 // We need to inline two copies of the `wait_event`'s body region: one is
513 // used to determine the values going into `detect_event` ops before the
514 // `llhd.wait`, and one will do the actual event detection after the
515 // `llhd.wait`.
516 //
517 // Create a copy of the entire `wait_event` op in the wait block, which also
518 // creates a copy of its region. Take note of all inputs to `detect_event`
519 // ops and delete the `detect_event` ops in this copy.
520 SmallVector<Value> valuesBefore;
521 rewriter.setInsertionPointToEnd(waitBlock);
522 auto clonedOp = cast<WaitEventOp>(rewriter.clone(*op));
523 bool allDetectsAreAnyChange = true;
524 for (auto detectOp :
525 llvm::make_early_inc_range(clonedOp.getOps<DetectEventOp>())) {
526 if (detectOp.getEdge() != Edge::AnyChange || detectOp.getCondition())
527 allDetectsAreAnyChange = false;
528 valuesBefore.push_back(detectOp.getInput());
529 rewriter.eraseOp(detectOp);
530 }
531
532 // Determine the values used during event detection that are defined outside
533 // the `wait_event`'s body region. We want to wait for a change on these
534 // signals before we check if any interesting event happened.
535 SmallVector<Value> observeValues;
536 auto setInsertionPointAfterDef = [&](Value value) {
537 if (auto *op = value.getDefiningOp())
538 rewriter.setInsertionPointAfter(op);
539 if (auto arg = dyn_cast<BlockArgument>(value))
540 rewriter.setInsertionPointToStart(value.getParentBlock());
541 };
542
543 getValuesToObserve(&clonedOp.getBody(), setInsertionPointAfterDef,
544 typeConverter, rewriter, observeValues);
545
546 // Create the `llhd.wait` op that suspends the current process and waits for
547 // a change in the interesting values listed in `observeValues`. When a
548 // change is detected, execution resumes in the "check" block.
549 auto waitOp = llhd::WaitOp::create(rewriter, loc, ValueRange{}, Value(),
550 observeValues, ValueRange{}, checkBlock);
551 rewriter.inlineBlockBefore(&clonedOp.getBody().front(), waitOp);
552 rewriter.eraseOp(clonedOp);
553
554 // Collect a list of all detect ops and inline the `wait_event` body into
555 // the check block.
556 SmallVector<DetectEventOp> detectOps(op.getBody().getOps<DetectEventOp>());
557 rewriter.inlineBlockBefore(&op.getBody().front(), checkBlock,
558 checkBlock->end());
559 rewriter.eraseOp(op);
560
561 // Helper function to detect if a certain change occurred between a value
562 // before the `llhd.wait` and after.
563 auto computeTrigger = [&](Value before, Value after, Edge edge) -> Value {
564 assert(before.getType() == after.getType() &&
565 "mismatched types after clone op");
566 auto beforeType = cast<IntType>(before.getType());
567
568 // 9.4.2 IEEE 1800-2017: An edge event shall be detected only on the LSB
569 // of the expression
570 if (beforeType.getWidth() != 1 && edge != Edge::AnyChange) {
571 constexpr int LSB = 0;
572 beforeType =
573 IntType::get(rewriter.getContext(), 1, beforeType.getDomain());
574 before =
575 moore::ExtractOp::create(rewriter, loc, beforeType, before, LSB);
576 after = moore::ExtractOp::create(rewriter, loc, beforeType, after, LSB);
577 }
578
579 auto intType = rewriter.getIntegerType(beforeType.getWidth());
580 before = typeConverter->materializeTargetConversion(rewriter, loc,
581 intType, before);
582 after = typeConverter->materializeTargetConversion(rewriter, loc, intType,
583 after);
584
585 if (edge == Edge::AnyChange)
586 return comb::ICmpOp::create(rewriter, loc, ICmpPredicate::ne, before,
587 after, true);
588
589 SmallVector<Value> disjuncts;
590 Value trueVal = hw::ConstantOp::create(rewriter, loc, APInt(1, 1));
591
592 if (edge == Edge::PosEdge || edge == Edge::BothEdges) {
593 Value notOldVal =
594 comb::XorOp::create(rewriter, loc, before, trueVal, true);
595 Value posedge =
596 comb::AndOp::create(rewriter, loc, notOldVal, after, true);
597 disjuncts.push_back(posedge);
598 }
599
600 if (edge == Edge::NegEdge || edge == Edge::BothEdges) {
601 Value notCurrVal =
602 comb::XorOp::create(rewriter, loc, after, trueVal, true);
603 Value posedge =
604 comb::AndOp::create(rewriter, loc, before, notCurrVal, true);
605 disjuncts.push_back(posedge);
606 }
607
608 return rewriter.createOrFold<comb::OrOp>(loc, disjuncts, true);
609 };
610
611 // Convert all `detect_event` ops into a check for the corresponding event
612 // between the value before and after the `llhd.wait`. The "before" value
613 // has been collected into `valuesBefore` in the "wait" block; the "after"
614 // value corresponds to the detect op's input.
615 SmallVector<Value> triggers;
616 for (auto [detectOp, before] : llvm::zip(detectOps, valuesBefore)) {
617 if (!allDetectsAreAnyChange) {
618 if (!isa<IntType>(before.getType()))
619 return detectOp->emitError() << "requires int operand";
620
621 rewriter.setInsertionPoint(detectOp);
622 auto trigger =
623 computeTrigger(before, detectOp.getInput(), detectOp.getEdge());
624 if (detectOp.getCondition()) {
625 auto condition = typeConverter->materializeTargetConversion(
626 rewriter, loc, rewriter.getI1Type(), detectOp.getCondition());
627 trigger =
628 comb::AndOp::create(rewriter, loc, trigger, condition, true);
629 }
630 triggers.push_back(trigger);
631 }
632
633 rewriter.eraseOp(detectOp);
634 }
635
636 rewriter.setInsertionPointToEnd(checkBlock);
637 if (triggers.empty()) {
638 // If there are no triggers to check, we always branch to the resume
639 // block. If there are no detect_event operations in the wait event, the
640 // 'llhd.wait' operation will not have any observed values and thus the
641 // process will hang there forever.
642 cf::BranchOp::create(rewriter, loc, resumeBlock);
643 } else {
644 // If any `detect_event` op detected an event, branch to the "resume"
645 // block which contains any code after the `wait_event` op. If no events
646 // were detected, branch back to the "wait" block to wait for the next
647 // change on the interesting signals.
648 auto triggered = rewriter.createOrFold<comb::OrOp>(loc, triggers, true);
649 cf::CondBranchOp::create(rewriter, loc, triggered, resumeBlock,
650 waitBlock);
651 }
652
653 return success();
654 }
655};
656
657// moore.wait_delay -> llhd.wait
658static LogicalResult convert(WaitDelayOp op, WaitDelayOp::Adaptor adaptor,
659 ConversionPatternRewriter &rewriter) {
660 auto *resumeBlock =
661 rewriter.splitBlock(op->getBlock(), ++Block::iterator(op));
662 rewriter.setInsertionPoint(op);
663 rewriter.replaceOpWithNewOp<llhd::WaitOp>(op, ValueRange{},
664 adaptor.getDelay(), ValueRange{},
665 ValueRange{}, resumeBlock);
666 rewriter.setInsertionPointToStart(resumeBlock);
667 return success();
668}
669
670// moore.unreachable -> llhd.halt
671static LogicalResult convert(UnreachableOp op, UnreachableOp::Adaptor adaptor,
672 ConversionPatternRewriter &rewriter) {
673 rewriter.replaceOpWithNewOp<llhd::HaltOp>(op, ValueRange{});
674 return success();
675}
676
677//===----------------------------------------------------------------------===//
678// Declaration Conversion
679//===----------------------------------------------------------------------===//
680
681static Value createZeroValue(Type type, Location loc,
682 ConversionPatternRewriter &rewriter) {
683 // Handle pointers.
684 if (isa<mlir::LLVM::LLVMPointerType>(type))
685 return mlir::LLVM::ZeroOp::create(rewriter, loc, type);
686
687 // Handle time values.
688 if (isa<llhd::TimeType>(type)) {
689 auto timeAttr =
690 llhd::TimeAttr::get(type.getContext(), 0U, llvm::StringRef("ns"), 0, 0);
691 return llhd::ConstantTimeOp::create(rewriter, loc, timeAttr);
692 }
693
694 // Handle real values.
695 if (auto floatType = dyn_cast<FloatType>(type)) {
696 auto floatAttr = rewriter.getFloatAttr(floatType, 0.0);
697 return mlir::arith::ConstantOp::create(rewriter, loc, floatAttr);
698 }
699
700 // Otherwise try to create a zero integer and bitcast it to the result type.
701 int64_t width = hw::getBitWidth(type);
702 if (width == -1)
703 return {};
704
705 // TODO: Once the core dialects support four-valued integers, this code
706 // will additionally need to generate an all-X value for four-valued
707 // variables.
708 Value constZero = hw::ConstantOp::create(rewriter, loc, APInt(width, 0));
709 return rewriter.createOrFold<hw::BitcastOp>(loc, type, constZero);
710}
711
712struct ClassPropertyRefOpConversion
713 : public OpConversionPattern<circt::moore::ClassPropertyRefOp> {
714 ClassPropertyRefOpConversion(TypeConverter &tc, MLIRContext *ctx,
715 ClassTypeCache &cache)
716 : OpConversionPattern(tc, ctx), cache(cache) {}
717
718 LogicalResult
719 matchAndRewrite(circt::moore::ClassPropertyRefOp op, OpAdaptor adaptor,
720 ConversionPatternRewriter &rewriter) const override {
721 Location loc = op.getLoc();
722 MLIRContext *ctx = rewriter.getContext();
723
724 // Convert result type; we expect !llhd.ref<someT>.
725 Type dstTy = getTypeConverter()->convertType(op.getPropertyRef().getType());
726 // Operand is a !llvm.ptr
727 Value instRef = adaptor.getInstance();
728
729 // Resolve identified struct from cache.
730 auto classRefTy =
731 cast<circt::moore::ClassHandleType>(op.getInstance().getType());
732 SymbolRefAttr classSym = classRefTy.getClassSym();
733 ModuleOp mod = op->getParentOfType<ModuleOp>();
734 if (failed(resolveClassStructBody(mod, classSym, *typeConverter, cache)))
735 return rewriter.notifyMatchFailure(op,
736 "Could not resolve class struct for " +
737 classSym.getRootReference().str());
738
739 auto structInfo = cache.getStructInfo(classSym);
740 assert(structInfo && "class struct info must exist");
741 auto structTy = structInfo->classBody;
742
743 // Look up cached GEP path for the property.
744 auto propSym = op.getProperty();
745 auto pathOpt = structInfo->getFieldPath(propSym);
746 if (!pathOpt)
747 return rewriter.notifyMatchFailure(op,
748 "no GEP path for property " + propSym);
749
750 auto i32Ty = IntegerType::get(ctx, 32);
751 SmallVector<Value> idxVals;
752 for (unsigned idx : *pathOpt)
753 idxVals.push_back(LLVM::ConstantOp::create(
754 rewriter, loc, i32Ty, rewriter.getI32IntegerAttr(idx)));
755
756 // GEP to the field (opaque ptr mode requires element type).
757 auto ptrTy = LLVM::LLVMPointerType::get(ctx);
758 auto gep =
759 LLVM::GEPOp::create(rewriter, loc, ptrTy, structTy, instRef, idxVals);
760
761 // Wrap pointer back to !llhd.ref<someT>.
762 Value fieldRef = UnrealizedConversionCastOp::create(rewriter, loc, dstTy,
763 gep.getResult())
764 .getResult(0);
765
766 rewriter.replaceOp(op, fieldRef);
767 return success();
768 }
769
770private:
771 ClassTypeCache &cache;
772};
773
774struct ClassUpcastOpConversion : public OpConversionPattern<ClassUpcastOp> {
775 using OpConversionPattern::OpConversionPattern;
776
777 LogicalResult
778 matchAndRewrite(ClassUpcastOp op, OpAdaptor adaptor,
779 ConversionPatternRewriter &rewriter) const override {
780 // Expect lowered types like !llvm.ptr
781 Type dstTy = getTypeConverter()->convertType(op.getResult().getType());
782 Type srcTy = adaptor.getInstance().getType();
783
784 if (!dstTy)
785 return rewriter.notifyMatchFailure(op, "failed to convert result type");
786
787 // If the types are already identical (opaque pointer mode), just forward.
788 if (dstTy == srcTy && isa<LLVM::LLVMPointerType>(srcTy)) {
789 rewriter.replaceOp(op, adaptor.getInstance());
790 return success();
791 }
792 return rewriter.notifyMatchFailure(
793 op, "Upcast applied to non-opaque pointers!");
794 }
795};
796
797/// moore.class.new lowering: heap-allocate storage for the class object.
798struct ClassNewOpConversion : public OpConversionPattern<ClassNewOp> {
799 ClassNewOpConversion(TypeConverter &tc, MLIRContext *ctx,
800 ClassTypeCache &cache)
801 : OpConversionPattern<ClassNewOp>(tc, ctx), cache(cache) {}
802
803 LogicalResult
804 matchAndRewrite(ClassNewOp op, OpAdaptor adaptor,
805 ConversionPatternRewriter &rewriter) const override {
806 Location loc = op.getLoc();
807 MLIRContext *ctx = rewriter.getContext();
808
809 auto handleTy = cast<ClassHandleType>(op.getResult().getType());
810 auto sym = handleTy.getClassSym();
811
812 ModuleOp mod = op->getParentOfType<ModuleOp>();
813
814 if (failed(resolveClassStructBody(mod, sym, *typeConverter, cache)))
815 return op.emitError() << "Could not resolve class struct for " << sym;
816
817 auto structTy = cache.getStructInfo(sym)->classBody;
818
819 DataLayout dl(mod);
820 // DataLayout::getTypeSize gives a byte count for LLVM types.
821 uint64_t byteSize = dl.getTypeSize(structTy);
822 auto i64Ty = IntegerType::get(ctx, 64);
823 auto cSize = LLVM::ConstantOp::create(rewriter, loc, i64Ty,
824 rewriter.getI64IntegerAttr(byteSize));
825
826 // Get or declare malloc and call it.
827 auto mallocFn = getOrCreateMalloc(mod, rewriter);
828 auto ptrTy = LLVM::LLVMPointerType::get(ctx); // opaque pointer result
829 auto call =
830 LLVM::CallOp::create(rewriter, loc, TypeRange{ptrTy},
831 SymbolRefAttr::get(mallocFn), ValueRange{cSize});
832
833 // Replace the new op with the malloc pointer (no cast needed with opaque
834 // ptrs).
835 rewriter.replaceOp(op, call.getResult());
836 return success();
837 }
838
839private:
840 ClassTypeCache &cache; // shared, owned by the pass
841};
842
843struct ClassDeclOpConversion : public OpConversionPattern<ClassDeclOp> {
844 ClassDeclOpConversion(TypeConverter &tc, MLIRContext *ctx,
845 ClassTypeCache &cache)
846 : OpConversionPattern<ClassDeclOp>(tc, ctx), cache(cache) {}
847
848 LogicalResult
849 matchAndRewrite(ClassDeclOp op, OpAdaptor,
850 ConversionPatternRewriter &rewriter) const override {
851
852 if (failed(resolveClassStructBody(op, *typeConverter, cache)))
853 return failure();
854 // The declaration itself is a no-op
855 rewriter.eraseOp(op);
856 return success();
857 }
858
859private:
860 ClassTypeCache &cache; // shared, owned by the pass
861};
862
863struct VariableOpConversion : public OpConversionPattern<VariableOp> {
864 using OpConversionPattern::OpConversionPattern;
865
866 LogicalResult
867 matchAndRewrite(VariableOp op, OpAdaptor adaptor,
868 ConversionPatternRewriter &rewriter) const override {
869 auto loc = op.getLoc();
870 auto resultType = typeConverter->convertType(op.getResult().getType());
871 if (!resultType)
872 return rewriter.notifyMatchFailure(op.getLoc(), "invalid variable type");
873
874 // Determine the initial value of the signal.
875 Value init = adaptor.getInitial();
876 if (!init) {
877 auto elementType = cast<llhd::RefType>(resultType).getNestedType();
878 init = createZeroValue(elementType, loc, rewriter);
879 if (!init)
880 return failure();
881 }
882
883 rewriter.replaceOpWithNewOp<llhd::SignalOp>(op, resultType,
884 op.getNameAttr(), init);
885 return success();
886 }
887};
888
889struct NetOpConversion : public OpConversionPattern<NetOp> {
890 using OpConversionPattern::OpConversionPattern;
891
892 LogicalResult
893 matchAndRewrite(NetOp op, OpAdaptor adaptor,
894 ConversionPatternRewriter &rewriter) const override {
895 auto loc = op.getLoc();
896 if (op.getKind() != NetKind::Wire)
897 return rewriter.notifyMatchFailure(loc, "only wire nets supported");
898
899 auto resultType = typeConverter->convertType(op.getResult().getType());
900 if (!resultType)
901 return rewriter.notifyMatchFailure(loc, "invalid net type");
902
903 // TODO: Once the core dialects support four-valued integers, this code
904 // will additionally need to generate an all-X value for four-valued nets.
905 auto elementType = cast<llhd::RefType>(resultType).getNestedType();
906 int64_t width = hw::getBitWidth(elementType);
907 if (width == -1)
908 return failure();
909 auto constZero = hw::ConstantOp::create(rewriter, loc, APInt(width, 0));
910 auto init =
911 rewriter.createOrFold<hw::BitcastOp>(loc, elementType, constZero);
912
913 auto signal = rewriter.replaceOpWithNewOp<llhd::SignalOp>(
914 op, resultType, op.getNameAttr(), init);
915
916 if (auto assignedValue = adaptor.getAssignment()) {
917 auto timeAttr = llhd::TimeAttr::get(resultType.getContext(), 0U,
918 llvm::StringRef("ns"), 0, 1);
919 auto time = llhd::ConstantTimeOp::create(rewriter, loc, timeAttr);
920 llhd::DriveOp::create(rewriter, loc, signal, assignedValue, time,
921 Value{});
922 }
923
924 return success();
925 }
926};
927
928//===----------------------------------------------------------------------===//
929// Expression Conversion
930//===----------------------------------------------------------------------===//
931
932struct ConstantOpConv : public OpConversionPattern<ConstantOp> {
933 using OpConversionPattern::OpConversionPattern;
934
935 LogicalResult
936 matchAndRewrite(ConstantOp op, OpAdaptor adaptor,
937 ConversionPatternRewriter &rewriter) const override {
938 // FIXME: Discard unknown bits and map them to 0 for now.
939 auto value = op.getValue().toAPInt(false);
940 auto type = rewriter.getIntegerType(value.getBitWidth());
941 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
942 op, type, rewriter.getIntegerAttr(type, value));
943 return success();
944 }
945};
946
947struct ConstantRealOpConv : public OpConversionPattern<ConstantRealOp> {
948 using OpConversionPattern::OpConversionPattern;
949
950 LogicalResult
951 matchAndRewrite(ConstantRealOp op, OpAdaptor adaptor,
952 ConversionPatternRewriter &rewriter) const override {
953 rewriter.replaceOpWithNewOp<arith::ConstantOp>(op, op.getValueAttr());
954 return success();
955 }
956};
957
958struct ConstantTimeOpConv : public OpConversionPattern<ConstantTimeOp> {
959 using OpConversionPattern::OpConversionPattern;
960
961 LogicalResult
962 matchAndRewrite(ConstantTimeOp op, OpAdaptor adaptor,
963 ConversionPatternRewriter &rewriter) const override {
964 rewriter.replaceOpWithNewOp<llhd::ConstantTimeOp>(
965 op, llhd::TimeAttr::get(op->getContext(), op.getValue(),
966 StringRef("fs"), 0, 0));
967 return success();
968 }
969};
970
971struct ConstantStringOpConv : public OpConversionPattern<ConstantStringOp> {
972 using OpConversionPattern::OpConversionPattern;
973 LogicalResult
974 matchAndRewrite(moore::ConstantStringOp op, OpAdaptor adaptor,
975 ConversionPatternRewriter &rewriter) const override {
976 const auto resultType =
977 typeConverter->convertType(op.getResult().getType());
978 const auto intType = mlir::cast<IntegerType>(resultType);
979
980 const auto str = op.getValue();
981 const unsigned byteWidth = intType.getWidth();
982 APInt value(byteWidth, 0);
983
984 // Pack ascii chars from the end of the string, until it fits.
985 const size_t maxChars =
986 std::min(str.size(), static_cast<size_t>(byteWidth / 8));
987 for (size_t i = 0; i < maxChars; i++) {
988 const size_t pos = str.size() - 1 - i;
989 const auto asciiChar = static_cast<uint8_t>(str[pos]);
990 value |= APInt(byteWidth, asciiChar) << (8 * i);
991 }
992
993 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
994 op, resultType, rewriter.getIntegerAttr(resultType, value));
995 return success();
996 }
997};
998
999struct ConcatOpConversion : public OpConversionPattern<ConcatOp> {
1000 using OpConversionPattern::OpConversionPattern;
1001 LogicalResult
1002 matchAndRewrite(ConcatOp op, OpAdaptor adaptor,
1003 ConversionPatternRewriter &rewriter) const override {
1004 rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, adaptor.getValues());
1005 return success();
1006 }
1007};
1008
1009struct ReplicateOpConversion : public OpConversionPattern<ReplicateOp> {
1010 using OpConversionPattern::OpConversionPattern;
1011 LogicalResult
1012 matchAndRewrite(ReplicateOp op, OpAdaptor adaptor,
1013 ConversionPatternRewriter &rewriter) const override {
1014 Type resultType = typeConverter->convertType(op.getResult().getType());
1015
1016 rewriter.replaceOpWithNewOp<comb::ReplicateOp>(op, resultType,
1017 adaptor.getValue());
1018 return success();
1019 }
1020};
1021
1022struct ExtractOpConversion : public OpConversionPattern<ExtractOp> {
1023 using OpConversionPattern::OpConversionPattern;
1024
1025 LogicalResult
1026 matchAndRewrite(ExtractOp op, OpAdaptor adaptor,
1027 ConversionPatternRewriter &rewriter) const override {
1028 // TODO: return X if the domain is four-valued for out-of-bounds accesses
1029 // once we support four-valued lowering
1030 Type resultType = typeConverter->convertType(op.getResult().getType());
1031 Type inputType = adaptor.getInput().getType();
1032 int32_t low = adaptor.getLowBit();
1033
1034 if (isa<IntegerType>(inputType)) {
1035 int32_t inputWidth = inputType.getIntOrFloatBitWidth();
1036 int32_t resultWidth = hw::getBitWidth(resultType);
1037 int32_t high = low + resultWidth;
1038
1039 SmallVector<Value> toConcat;
1040 if (low < 0)
1041 toConcat.push_back(hw::ConstantOp::create(
1042 rewriter, op.getLoc(), APInt(std::min(-low, resultWidth), 0)));
1043
1044 if (low < inputWidth && high > 0) {
1045 int32_t lowIdx = std::max(low, 0);
1046 Value middle = rewriter.createOrFold<comb::ExtractOp>(
1047 op.getLoc(),
1048 rewriter.getIntegerType(
1049 std::min(resultWidth, std::min(high, inputWidth) - lowIdx)),
1050 adaptor.getInput(), lowIdx);
1051 toConcat.push_back(middle);
1052 }
1053
1054 int32_t diff = high - inputWidth;
1055 if (diff > 0) {
1056 Value val =
1057 hw::ConstantOp::create(rewriter, op.getLoc(), APInt(diff, 0));
1058 toConcat.push_back(val);
1059 }
1060
1061 Value concat =
1062 rewriter.createOrFold<comb::ConcatOp>(op.getLoc(), toConcat);
1063 rewriter.replaceOp(op, concat);
1064 return success();
1065 }
1066
1067 if (auto arrTy = dyn_cast<hw::ArrayType>(inputType)) {
1068 int32_t width = llvm::Log2_64_Ceil(arrTy.getNumElements());
1069 int32_t inputWidth = arrTy.getNumElements();
1070
1071 if (auto resArrTy = dyn_cast<hw::ArrayType>(resultType);
1072 resArrTy && resArrTy != arrTy.getElementType()) {
1073 int32_t elementWidth = hw::getBitWidth(arrTy.getElementType());
1074 if (elementWidth < 0)
1075 return failure();
1076
1077 int32_t high = low + resArrTy.getNumElements();
1078 int32_t resWidth = resArrTy.getNumElements();
1079
1080 SmallVector<Value> toConcat;
1081 if (low < 0) {
1082 Value val = hw::ConstantOp::create(
1083 rewriter, op.getLoc(),
1084 APInt(std::min((-low) * elementWidth, resWidth * elementWidth),
1085 0));
1086 Value res = rewriter.createOrFold<hw::BitcastOp>(
1087 op.getLoc(), hw::ArrayType::get(arrTy.getElementType(), -low),
1088 val);
1089 toConcat.push_back(res);
1090 }
1091
1092 if (low < inputWidth && high > 0) {
1093 int32_t lowIdx = std::max(0, low);
1094 Value lowIdxVal = hw::ConstantOp::create(
1095 rewriter, op.getLoc(), rewriter.getIntegerType(width), lowIdx);
1096 Value middle = rewriter.createOrFold<hw::ArraySliceOp>(
1097 op.getLoc(),
1098 hw::ArrayType::get(
1099 arrTy.getElementType(),
1100 std::min(resWidth, std::min(inputWidth, high) - lowIdx)),
1101 adaptor.getInput(), lowIdxVal);
1102 toConcat.push_back(middle);
1103 }
1104
1105 int32_t diff = high - inputWidth;
1106 if (diff > 0) {
1107 Value constZero = hw::ConstantOp::create(
1108 rewriter, op.getLoc(), APInt(diff * elementWidth, 0));
1109 Value val = hw::BitcastOp::create(
1110 rewriter, op.getLoc(),
1111 hw::ArrayType::get(arrTy.getElementType(), diff), constZero);
1112 toConcat.push_back(val);
1113 }
1114
1115 Value concat =
1116 rewriter.createOrFold<hw::ArrayConcatOp>(op.getLoc(), toConcat);
1117 rewriter.replaceOp(op, concat);
1118 return success();
1119 }
1120
1121 // Otherwise, it has to be the array's element type
1122 if (low < 0 || low >= inputWidth) {
1123 int32_t bw = hw::getBitWidth(resultType);
1124 if (bw < 0)
1125 return failure();
1126
1127 Value val = hw::ConstantOp::create(rewriter, op.getLoc(), APInt(bw, 0));
1128 Value bitcast =
1129 rewriter.createOrFold<hw::BitcastOp>(op.getLoc(), resultType, val);
1130 rewriter.replaceOp(op, bitcast);
1131 return success();
1132 }
1133
1134 Value idx = hw::ConstantOp::create(rewriter, op.getLoc(),
1135 rewriter.getIntegerType(width),
1136 adaptor.getLowBit());
1137 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(), idx);
1138 return success();
1139 }
1140
1141 return failure();
1142 }
1143};
1144
1145struct ExtractRefOpConversion : public OpConversionPattern<ExtractRefOp> {
1146 using OpConversionPattern::OpConversionPattern;
1147
1148 LogicalResult
1149 matchAndRewrite(ExtractRefOp op, OpAdaptor adaptor,
1150 ConversionPatternRewriter &rewriter) const override {
1151 // TODO: properly handle out-of-bounds accesses
1152 Type resultType = typeConverter->convertType(op.getResult().getType());
1153 Type inputType =
1154 cast<llhd::RefType>(adaptor.getInput().getType()).getNestedType();
1155
1156 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1157 int64_t width = hw::getBitWidth(inputType);
1158 if (width == -1)
1159 return failure();
1160
1161 Value lowBit = hw::ConstantOp::create(
1162 rewriter, op.getLoc(),
1163 rewriter.getIntegerType(llvm::Log2_64_Ceil(width)),
1164 adaptor.getLowBit());
1165 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
1166 op, resultType, adaptor.getInput(), lowBit);
1167 return success();
1168 }
1169
1170 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1171 Value lowBit = hw::ConstantOp::create(
1172 rewriter, op.getLoc(),
1173 rewriter.getIntegerType(llvm::Log2_64_Ceil(arrType.getNumElements())),
1174 adaptor.getLowBit());
1175
1176 // If the result type is not the same as the array's element type, then
1177 // it has to be a slice.
1178 if (arrType.getElementType() !=
1179 cast<llhd::RefType>(resultType).getNestedType()) {
1180 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
1181 op, resultType, adaptor.getInput(), lowBit);
1182 return success();
1183 }
1184
1185 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
1186 lowBit);
1187 return success();
1188 }
1189
1190 return failure();
1191 }
1192};
1193
1194struct DynExtractOpConversion : public OpConversionPattern<DynExtractOp> {
1195 using OpConversionPattern::OpConversionPattern;
1196
1197 LogicalResult
1198 matchAndRewrite(DynExtractOp op, OpAdaptor adaptor,
1199 ConversionPatternRewriter &rewriter) const override {
1200 Type resultType = typeConverter->convertType(op.getResult().getType());
1201 Type inputType = adaptor.getInput().getType();
1202
1203 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1204 Value amount = adjustIntegerWidth(rewriter, adaptor.getLowBit(),
1205 intType.getWidth(), op->getLoc());
1206 Value value = comb::ShrUOp::create(rewriter, op->getLoc(),
1207 adaptor.getInput(), amount);
1208
1209 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, value, 0);
1210 return success();
1211 }
1212
1213 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1214 unsigned idxWidth = llvm::Log2_64_Ceil(arrType.getNumElements());
1215 Value idx = adjustIntegerWidth(rewriter, adaptor.getLowBit(), idxWidth,
1216 op->getLoc());
1217
1218 bool isSingleElementExtract = arrType.getElementType() == resultType;
1219
1220 if (isSingleElementExtract)
1221 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(),
1222 idx);
1223 else
1224 rewriter.replaceOpWithNewOp<hw::ArraySliceOp>(op, resultType,
1225 adaptor.getInput(), idx);
1226
1227 return success();
1228 }
1229
1230 return failure();
1231 }
1232};
1233
1234struct DynExtractRefOpConversion : public OpConversionPattern<DynExtractRefOp> {
1235 using OpConversionPattern::OpConversionPattern;
1236
1237 LogicalResult
1238 matchAndRewrite(DynExtractRefOp op, OpAdaptor adaptor,
1239 ConversionPatternRewriter &rewriter) const override {
1240 // TODO: properly handle out-of-bounds accesses
1241 Type resultType = typeConverter->convertType(op.getResult().getType());
1242 Type inputType =
1243 cast<llhd::RefType>(adaptor.getInput().getType()).getNestedType();
1244
1245 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1246 int64_t width = hw::getBitWidth(inputType);
1247 if (width == -1)
1248 return failure();
1249
1250 Value amount =
1251 adjustIntegerWidth(rewriter, adaptor.getLowBit(),
1252 llvm::Log2_64_Ceil(width), op->getLoc());
1253 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
1254 op, resultType, adaptor.getInput(), amount);
1255 return success();
1256 }
1257
1258 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1259 Value idx = adjustIntegerWidth(
1260 rewriter, adaptor.getLowBit(),
1261 llvm::Log2_64_Ceil(arrType.getNumElements()), op->getLoc());
1262
1263 auto resultNestedType = cast<llhd::RefType>(resultType).getNestedType();
1264 bool isSingleElementExtract =
1265 arrType.getElementType() == resultNestedType;
1266
1267 if (isSingleElementExtract)
1268 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
1269 idx);
1270 else
1271 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
1272 op, resultType, adaptor.getInput(), idx);
1273
1274 return success();
1275 }
1276
1277 return failure();
1278 }
1279};
1280
1281struct ArrayCreateOpConversion : public OpConversionPattern<ArrayCreateOp> {
1282 using OpConversionPattern::OpConversionPattern;
1283
1284 LogicalResult
1285 matchAndRewrite(ArrayCreateOp op, OpAdaptor adaptor,
1286 ConversionPatternRewriter &rewriter) const override {
1287 Type resultType = typeConverter->convertType(op.getResult().getType());
1288 rewriter.replaceOpWithNewOp<hw::ArrayCreateOp>(op, resultType,
1289 adaptor.getElements());
1290 return success();
1291 }
1292};
1293
1294struct StructCreateOpConversion : public OpConversionPattern<StructCreateOp> {
1295 using OpConversionPattern::OpConversionPattern;
1296
1297 LogicalResult
1298 matchAndRewrite(StructCreateOp op, OpAdaptor adaptor,
1299 ConversionPatternRewriter &rewriter) const override {
1300 Type resultType = typeConverter->convertType(op.getResult().getType());
1301 rewriter.replaceOpWithNewOp<hw::StructCreateOp>(op, resultType,
1302 adaptor.getFields());
1303 return success();
1304 }
1305};
1306
1307struct StructExtractOpConversion : public OpConversionPattern<StructExtractOp> {
1308 using OpConversionPattern::OpConversionPattern;
1309
1310 LogicalResult
1311 matchAndRewrite(StructExtractOp op, OpAdaptor adaptor,
1312 ConversionPatternRewriter &rewriter) const override {
1313 rewriter.replaceOpWithNewOp<hw::StructExtractOp>(
1314 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1315 return success();
1316 }
1317};
1318
1319struct StructExtractRefOpConversion
1320 : public OpConversionPattern<StructExtractRefOp> {
1321 using OpConversionPattern::OpConversionPattern;
1322
1323 LogicalResult
1324 matchAndRewrite(StructExtractRefOp op, OpAdaptor adaptor,
1325 ConversionPatternRewriter &rewriter) const override {
1326 rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
1327 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1328 return success();
1329 }
1330};
1331
1332struct ReduceAndOpConversion : public OpConversionPattern<ReduceAndOp> {
1333 using OpConversionPattern::OpConversionPattern;
1334 LogicalResult
1335 matchAndRewrite(ReduceAndOp op, OpAdaptor adaptor,
1336 ConversionPatternRewriter &rewriter) const override {
1337 Type resultType = typeConverter->convertType(op.getInput().getType());
1338 Value max = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, -1);
1339
1340 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::eq,
1341 adaptor.getInput(), max);
1342 return success();
1343 }
1344};
1345
1346struct ReduceOrOpConversion : public OpConversionPattern<ReduceOrOp> {
1347 using OpConversionPattern::OpConversionPattern;
1348 LogicalResult
1349 matchAndRewrite(ReduceOrOp op, OpAdaptor adaptor,
1350 ConversionPatternRewriter &rewriter) const override {
1351 Type resultType = typeConverter->convertType(op.getInput().getType());
1352 Value zero = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1353
1354 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1355 adaptor.getInput(), zero);
1356 return success();
1357 }
1358};
1359
1360struct ReduceXorOpConversion : public OpConversionPattern<ReduceXorOp> {
1361 using OpConversionPattern::OpConversionPattern;
1362 LogicalResult
1363 matchAndRewrite(ReduceXorOp op, OpAdaptor adaptor,
1364 ConversionPatternRewriter &rewriter) const override {
1365
1366 rewriter.replaceOpWithNewOp<comb::ParityOp>(op, adaptor.getInput());
1367 return success();
1368 }
1369};
1370
1371struct BoolCastOpConversion : public OpConversionPattern<BoolCastOp> {
1372 using OpConversionPattern::OpConversionPattern;
1373 LogicalResult
1374 matchAndRewrite(BoolCastOp op, OpAdaptor adaptor,
1375 ConversionPatternRewriter &rewriter) const override {
1376 Type resultType = typeConverter->convertType(op.getInput().getType());
1377 if (isa_and_nonnull<IntegerType>(resultType)) {
1378 Value zero =
1379 hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1380 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1381 adaptor.getInput(), zero);
1382 return success();
1383 }
1384 return failure();
1385 }
1386};
1387
1388struct NotOpConversion : public OpConversionPattern<NotOp> {
1389 using OpConversionPattern::OpConversionPattern;
1390 LogicalResult
1391 matchAndRewrite(NotOp op, OpAdaptor adaptor,
1392 ConversionPatternRewriter &rewriter) const override {
1393 Type resultType =
1394 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1395 Value max = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, -1);
1396
1397 rewriter.replaceOpWithNewOp<comb::XorOp>(op, adaptor.getInput(), max);
1398 return success();
1399 }
1400};
1401
1402struct NegOpConversion : public OpConversionPattern<NegOp> {
1403 using OpConversionPattern::OpConversionPattern;
1404 LogicalResult
1405 matchAndRewrite(NegOp op, OpAdaptor adaptor,
1406 ConversionPatternRewriter &rewriter) const override {
1407 Type resultType =
1408 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1409 Value zero = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, 0);
1410
1411 rewriter.replaceOpWithNewOp<comb::SubOp>(op, zero, adaptor.getInput());
1412 return success();
1413 }
1414};
1415
1416template <typename SourceOp, typename TargetOp>
1417struct BinaryOpConversion : public OpConversionPattern<SourceOp> {
1419 using OpAdaptor = typename SourceOp::Adaptor;
1420
1421 LogicalResult
1422 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1423 ConversionPatternRewriter &rewriter) const override {
1424 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1425 adaptor.getRhs(), false);
1426 return success();
1427 }
1428};
1429
1430template <typename SourceOp, typename TargetOp>
1431struct BinaryRealOpConversion : public OpConversionPattern<SourceOp> {
1433 using OpAdaptor = typename SourceOp::Adaptor;
1434
1435 LogicalResult
1436 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1437 ConversionPatternRewriter &rewriter) const override {
1438 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1439 adaptor.getRhs());
1440 return success();
1441 }
1442};
1443
1444template <typename SourceOp, ICmpPredicate pred>
1445struct ICmpOpConversion : public OpConversionPattern<SourceOp> {
1447 using OpAdaptor = typename SourceOp::Adaptor;
1448
1449 LogicalResult
1450 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1451 ConversionPatternRewriter &rewriter) const override {
1452 Type resultType =
1453 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1454
1455 rewriter.replaceOpWithNewOp<comb::ICmpOp>(
1456 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1457 return success();
1458 }
1459};
1460
1461template <typename SourceOp, arith::CmpFPredicate pred>
1462struct FCmpOpConversion : public OpConversionPattern<SourceOp> {
1464 using OpAdaptor = typename SourceOp::Adaptor;
1465
1466 LogicalResult
1467 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1468 ConversionPatternRewriter &rewriter) const override {
1469 Type resultType =
1470 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1471
1472 rewriter.replaceOpWithNewOp<arith::CmpFOp>(
1473 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1474 return success();
1475 }
1476};
1477
1478template <typename SourceOp, bool withoutX>
1479struct CaseXZEqOpConversion : public OpConversionPattern<SourceOp> {
1481 using OpAdaptor = typename SourceOp::Adaptor;
1482
1483 LogicalResult
1484 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1485 ConversionPatternRewriter &rewriter) const override {
1486 // Check each operand if it is a known constant and extract the X and/or Z
1487 // bits to be ignored.
1488 // TODO: Once the core dialects support four-valued integers, we will have
1489 // to create ops that extract X and Z bits from the operands, since we also
1490 // have to do the right casez/casex comparison on non-constant inputs.
1491 unsigned bitWidth = op.getLhs().getType().getWidth();
1492 auto ignoredBits = APInt::getZero(bitWidth);
1493 auto detectIgnoredBits = [&](Value value) {
1494 auto constOp = value.getDefiningOp<ConstantOp>();
1495 if (!constOp)
1496 return;
1497 auto constValue = constOp.getValue();
1498 if (withoutX)
1499 ignoredBits |= constValue.getZBits();
1500 else
1501 ignoredBits |= constValue.getUnknownBits();
1502 };
1503 detectIgnoredBits(op.getLhs());
1504 detectIgnoredBits(op.getRhs());
1505
1506 // If we have detected any bits to be ignored, mask them in the operands for
1507 // the comparison.
1508 Value lhs = adaptor.getLhs();
1509 Value rhs = adaptor.getRhs();
1510 if (!ignoredBits.isZero()) {
1511 ignoredBits.flipAllBits();
1512 auto maskOp = hw::ConstantOp::create(rewriter, op.getLoc(), ignoredBits);
1513 lhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), lhs, maskOp);
1514 rhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), rhs, maskOp);
1515 }
1516
1517 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, ICmpPredicate::ceq, lhs, rhs);
1518 return success();
1519 }
1520};
1521
1522//===----------------------------------------------------------------------===//
1523// Conversions
1524//===----------------------------------------------------------------------===//
1525
1526struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
1527 using OpConversionPattern::OpConversionPattern;
1528
1529 LogicalResult
1530 matchAndRewrite(ConversionOp op, OpAdaptor adaptor,
1531 ConversionPatternRewriter &rewriter) const override {
1532 Location loc = op.getLoc();
1533 Type resultType = typeConverter->convertType(op.getResult().getType());
1534 if (!resultType) {
1535 op.emitError("conversion result type is not currently supported");
1536 return failure();
1537 }
1538 int64_t inputBw = hw::getBitWidth(adaptor.getInput().getType());
1539 int64_t resultBw = hw::getBitWidth(resultType);
1540 if (inputBw == -1 || resultBw == -1)
1541 return failure();
1542
1543 Value input = rewriter.createOrFold<hw::BitcastOp>(
1544 loc, rewriter.getIntegerType(inputBw), adaptor.getInput());
1545 Value amount = adjustIntegerWidth(rewriter, input, resultBw, loc);
1546
1547 Value result =
1548 rewriter.createOrFold<hw::BitcastOp>(loc, resultType, amount);
1549 rewriter.replaceOp(op, result);
1550 return success();
1551 }
1552};
1553
1554template <typename SourceOp>
1555struct BitcastConversion : public OpConversionPattern<SourceOp> {
1557 using OpAdaptor = typename SourceOp::Adaptor;
1558 using ConversionPattern::typeConverter;
1559
1560 LogicalResult
1561 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1562 ConversionPatternRewriter &rewriter) const override {
1563 auto type = typeConverter->convertType(op.getResult().getType());
1564 if (type == adaptor.getInput().getType())
1565 rewriter.replaceOp(op, adaptor.getInput());
1566 else
1567 rewriter.replaceOpWithNewOp<hw::BitcastOp>(op, type, adaptor.getInput());
1568 return success();
1569 }
1570};
1571
1572struct TruncOpConversion : public OpConversionPattern<TruncOp> {
1573 using OpConversionPattern::OpConversionPattern;
1574
1575 LogicalResult
1576 matchAndRewrite(TruncOp op, OpAdaptor adaptor,
1577 ConversionPatternRewriter &rewriter) const override {
1578 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, adaptor.getInput(), 0,
1579 op.getType().getWidth());
1580 return success();
1581 }
1582};
1583
1584struct ZExtOpConversion : public OpConversionPattern<ZExtOp> {
1585 using OpConversionPattern::OpConversionPattern;
1586
1587 LogicalResult
1588 matchAndRewrite(ZExtOp op, OpAdaptor adaptor,
1589 ConversionPatternRewriter &rewriter) const override {
1590 auto targetWidth = op.getType().getWidth();
1591 auto inputWidth = op.getInput().getType().getWidth();
1592
1593 auto zeroExt = hw::ConstantOp::create(
1594 rewriter, op.getLoc(),
1595 rewriter.getIntegerType(targetWidth - inputWidth), 0);
1596
1597 rewriter.replaceOpWithNewOp<comb::ConcatOp>(
1598 op, ValueRange{zeroExt, adaptor.getInput()});
1599 return success();
1600 }
1601};
1602
1603struct SExtOpConversion : public OpConversionPattern<SExtOp> {
1604 using OpConversionPattern::OpConversionPattern;
1605
1606 LogicalResult
1607 matchAndRewrite(SExtOp op, OpAdaptor adaptor,
1608 ConversionPatternRewriter &rewriter) const override {
1609 auto type = typeConverter->convertType(op.getType());
1610 auto value =
1611 comb::createOrFoldSExt(op.getLoc(), adaptor.getInput(), type, rewriter);
1612 rewriter.replaceOp(op, value);
1613 return success();
1614 }
1615};
1616
1617struct SIntToRealOpConversion : public OpConversionPattern<SIntToRealOp> {
1618 using OpConversionPattern::OpConversionPattern;
1619
1620 LogicalResult
1621 matchAndRewrite(SIntToRealOp op, OpAdaptor adaptor,
1622 ConversionPatternRewriter &rewriter) const override {
1623 rewriter.replaceOpWithNewOp<arith::SIToFPOp>(
1624 op, typeConverter->convertType(op.getType()), adaptor.getInput());
1625 return success();
1626 }
1627};
1628
1629struct UIntToRealOpConversion : public OpConversionPattern<UIntToRealOp> {
1630 using OpConversionPattern::OpConversionPattern;
1631
1632 LogicalResult
1633 matchAndRewrite(UIntToRealOp op, OpAdaptor adaptor,
1634 ConversionPatternRewriter &rewriter) const override {
1635 rewriter.replaceOpWithNewOp<arith::UIToFPOp>(
1636 op, typeConverter->convertType(op.getType()), adaptor.getInput());
1637 return success();
1638 }
1639};
1640
1641struct RealToIntOpConversion : public OpConversionPattern<RealToIntOp> {
1642 using OpConversionPattern::OpConversionPattern;
1643
1644 LogicalResult
1645 matchAndRewrite(RealToIntOp op, OpAdaptor adaptor,
1646 ConversionPatternRewriter &rewriter) const override {
1647 rewriter.replaceOpWithNewOp<arith::FPToSIOp>(
1648 op, typeConverter->convertType(op.getType()), adaptor.getInput());
1649 return success();
1650 }
1651};
1652
1653//===----------------------------------------------------------------------===//
1654// Statement Conversion
1655//===----------------------------------------------------------------------===//
1656
1657struct HWInstanceOpConversion : public OpConversionPattern<hw::InstanceOp> {
1658 using OpConversionPattern::OpConversionPattern;
1659
1660 LogicalResult
1661 matchAndRewrite(hw::InstanceOp op, OpAdaptor adaptor,
1662 ConversionPatternRewriter &rewriter) const override {
1663 SmallVector<Type> convResTypes;
1664 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1665 return failure();
1666
1667 rewriter.replaceOpWithNewOp<hw::InstanceOp>(
1668 op, convResTypes, op.getInstanceName(), op.getModuleName(),
1669 adaptor.getOperands(), op.getArgNames(),
1670 op.getResultNames(), /*Parameter*/
1671 rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr);
1672
1673 return success();
1674 }
1675};
1676
1677struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
1678 using OpConversionPattern::OpConversionPattern;
1679
1680 LogicalResult
1681 matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
1682 ConversionPatternRewriter &rewriter) const override {
1683 rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
1684 return success();
1685 }
1686};
1687
1688struct CallOpConversion : public OpConversionPattern<func::CallOp> {
1689 using OpConversionPattern::OpConversionPattern;
1690
1691 LogicalResult
1692 matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
1693 ConversionPatternRewriter &rewriter) const override {
1694 SmallVector<Type> convResTypes;
1695 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1696 return failure();
1697 rewriter.replaceOpWithNewOp<func::CallOp>(
1698 op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
1699 return success();
1700 }
1701};
1702
1703struct UnrealizedConversionCastConversion
1704 : public OpConversionPattern<UnrealizedConversionCastOp> {
1705 using OpConversionPattern::OpConversionPattern;
1706
1707 LogicalResult
1708 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
1709 ConversionPatternRewriter &rewriter) const override {
1710 SmallVector<Type> convResTypes;
1711 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1712 return failure();
1713
1714 // Drop the cast if the operand and result types agree after type
1715 // conversion.
1716 if (convResTypes == adaptor.getOperands().getTypes()) {
1717 rewriter.replaceOp(op, adaptor.getOperands());
1718 return success();
1719 }
1720
1721 rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
1722 op, convResTypes, adaptor.getOperands());
1723 return success();
1724 }
1725};
1726
1727struct ShlOpConversion : public OpConversionPattern<ShlOp> {
1728 using OpConversionPattern::OpConversionPattern;
1729
1730 LogicalResult
1731 matchAndRewrite(ShlOp op, OpAdaptor adaptor,
1732 ConversionPatternRewriter &rewriter) const override {
1733 Type resultType = typeConverter->convertType(op.getResult().getType());
1734
1735 // Comb shift operations require the same bit-width for value and amount
1736 Value amount =
1737 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1738 resultType.getIntOrFloatBitWidth(), op->getLoc());
1739 rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
1740 amount, false);
1741 return success();
1742 }
1743};
1744
1745struct ShrOpConversion : public OpConversionPattern<ShrOp> {
1746 using OpConversionPattern::OpConversionPattern;
1747
1748 LogicalResult
1749 matchAndRewrite(ShrOp op, OpAdaptor adaptor,
1750 ConversionPatternRewriter &rewriter) const override {
1751 Type resultType = typeConverter->convertType(op.getResult().getType());
1752
1753 // Comb shift operations require the same bit-width for value and amount
1754 Value amount =
1755 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1756 resultType.getIntOrFloatBitWidth(), op->getLoc());
1757 rewriter.replaceOpWithNewOp<comb::ShrUOp>(
1758 op, resultType, adaptor.getValue(), amount, false);
1759 return success();
1760 }
1761};
1762
1763struct PowUOpConversion : public OpConversionPattern<PowUOp> {
1764 using OpConversionPattern::OpConversionPattern;
1765
1766 LogicalResult
1767 matchAndRewrite(PowUOp op, OpAdaptor adaptor,
1768 ConversionPatternRewriter &rewriter) const override {
1769 Type resultType = typeConverter->convertType(op.getResult().getType());
1770
1771 Location loc = op->getLoc();
1772
1773 Value zeroVal = hw::ConstantOp::create(rewriter, loc, APInt(1, 0));
1774 // zero extend both LHS & RHS to ensure the unsigned integers are
1775 // interpreted correctly when calculating power
1776 auto lhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getLhs());
1777 auto rhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getRhs());
1778
1779 // lower the exponentiation via MLIR's math dialect
1780 auto pow = mlir::math::IPowIOp::create(rewriter, loc, lhs, rhs);
1781
1782 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, pow, 0);
1783 return success();
1784 }
1785};
1786
1787struct PowSOpConversion : public OpConversionPattern<PowSOp> {
1788 using OpConversionPattern::OpConversionPattern;
1789
1790 LogicalResult
1791 matchAndRewrite(PowSOp op, OpAdaptor adaptor,
1792 ConversionPatternRewriter &rewriter) const override {
1793 Type resultType = typeConverter->convertType(op.getResult().getType());
1794
1795 // utilize MLIR math dialect's math.ipowi to handle the exponentiation of
1796 // expression
1797 rewriter.replaceOpWithNewOp<mlir::math::IPowIOp>(
1798 op, resultType, adaptor.getLhs(), adaptor.getRhs());
1799 return success();
1800 }
1801};
1802
1803struct AShrOpConversion : public OpConversionPattern<AShrOp> {
1804 using OpConversionPattern::OpConversionPattern;
1805
1806 LogicalResult
1807 matchAndRewrite(AShrOp op, OpAdaptor adaptor,
1808 ConversionPatternRewriter &rewriter) const override {
1809 Type resultType = typeConverter->convertType(op.getResult().getType());
1810
1811 // Comb shift operations require the same bit-width for value and amount
1812 Value amount =
1813 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1814 resultType.getIntOrFloatBitWidth(), op->getLoc());
1815 rewriter.replaceOpWithNewOp<comb::ShrSOp>(
1816 op, resultType, adaptor.getValue(), amount, false);
1817 return success();
1818 }
1819};
1820
1821struct ReadOpConversion : public OpConversionPattern<ReadOp> {
1822 using OpConversionPattern::OpConversionPattern;
1823
1824 LogicalResult
1825 matchAndRewrite(ReadOp op, OpAdaptor adaptor,
1826 ConversionPatternRewriter &rewriter) const override {
1827 rewriter.replaceOpWithNewOp<llhd::ProbeOp>(op, adaptor.getInput());
1828 return success();
1829 }
1830};
1831
1832struct AssignedVariableOpConversion
1833 : public OpConversionPattern<AssignedVariableOp> {
1834 using OpConversionPattern::OpConversionPattern;
1835
1836 LogicalResult
1837 matchAndRewrite(AssignedVariableOp op, OpAdaptor adaptor,
1838 ConversionPatternRewriter &rewriter) const override {
1839 rewriter.replaceOpWithNewOp<hw::WireOp>(op, adaptor.getInput(),
1840 adaptor.getNameAttr());
1841 return success();
1842 }
1843};
1844
1845template <typename OpTy>
1846struct AssignOpConversion : public OpConversionPattern<OpTy> {
1848 using OpAdaptor = typename OpTy::Adaptor;
1849
1850 LogicalResult
1851 matchAndRewrite(OpTy op, OpAdaptor adaptor,
1852 ConversionPatternRewriter &rewriter) const override {
1853 // Determine the delay for the assignment.
1854 Value delay;
1855 if constexpr (std::is_same_v<OpTy, ContinuousAssignOp> ||
1856 std::is_same_v<OpTy, BlockingAssignOp>) {
1857 // Blocking and continuous assignments get a 0ns 0d 1e delay.
1858 delay = llhd::ConstantTimeOp::create(
1859 rewriter, op->getLoc(),
1860 llhd::TimeAttr::get(op->getContext(), 0U, "ns", 0, 1));
1861 } else if constexpr (std::is_same_v<OpTy, NonBlockingAssignOp>) {
1862 // Non-blocking assignments get a 0ns 1d 0e delay.
1863 delay = llhd::ConstantTimeOp::create(
1864 rewriter, op->getLoc(),
1865 llhd::TimeAttr::get(op->getContext(), 0U, "ns", 1, 0));
1866 } else {
1867 // Delayed assignments have a delay operand.
1868 delay = adaptor.getDelay();
1869 }
1870
1871 rewriter.replaceOpWithNewOp<llhd::DriveOp>(
1872 op, adaptor.getDst(), adaptor.getSrc(), delay, Value{});
1873 return success();
1874 }
1875};
1876
1877struct ConditionalOpConversion : public OpConversionPattern<ConditionalOp> {
1878 using OpConversionPattern::OpConversionPattern;
1879
1880 LogicalResult
1881 matchAndRewrite(ConditionalOp op, OpAdaptor adaptor,
1882 ConversionPatternRewriter &rewriter) const override {
1883 // TODO: This lowering is only correct if the condition is two-valued. If
1884 // the condition is X or Z, both branches of the conditional must be
1885 // evaluated and merged with the appropriate lookup table. See documentation
1886 // for `ConditionalOp`.
1887 auto type = typeConverter->convertType(op.getType());
1888
1889 auto hasNoWriteEffect = [](Region &region) {
1890 auto result = region.walk([](Operation *operation) {
1891 if (auto memOp = dyn_cast<MemoryEffectOpInterface>(operation))
1892 if (!memOp.hasEffect<MemoryEffects::Write>() &&
1893 !memOp.hasEffect<MemoryEffects::Free>())
1894 return WalkResult::advance();
1895
1896 if (operation->hasTrait<OpTrait::HasRecursiveMemoryEffects>())
1897 return WalkResult::advance();
1898
1899 return WalkResult::interrupt();
1900 });
1901 return !result.wasInterrupted();
1902 };
1903
1904 if (hasNoWriteEffect(op.getTrueRegion()) &&
1905 hasNoWriteEffect(op.getFalseRegion())) {
1906 Operation *trueTerm = op.getTrueRegion().front().getTerminator();
1907 Operation *falseTerm = op.getFalseRegion().front().getTerminator();
1908
1909 rewriter.inlineBlockBefore(&op.getTrueRegion().front(), op);
1910 rewriter.inlineBlockBefore(&op.getFalseRegion().front(), op);
1911
1912 Value convTrueVal = typeConverter->materializeTargetConversion(
1913 rewriter, op.getLoc(), type, trueTerm->getOperand(0));
1914 Value convFalseVal = typeConverter->materializeTargetConversion(
1915 rewriter, op.getLoc(), type, falseTerm->getOperand(0));
1916
1917 rewriter.eraseOp(trueTerm);
1918 rewriter.eraseOp(falseTerm);
1919
1920 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, adaptor.getCondition(),
1921 convTrueVal, convFalseVal);
1922 return success();
1923 }
1924
1925 auto ifOp =
1926 scf::IfOp::create(rewriter, op.getLoc(), type, adaptor.getCondition());
1927 rewriter.inlineRegionBefore(op.getTrueRegion(), ifOp.getThenRegion(),
1928 ifOp.getThenRegion().end());
1929 rewriter.inlineRegionBefore(op.getFalseRegion(), ifOp.getElseRegion(),
1930 ifOp.getElseRegion().end());
1931 rewriter.replaceOp(op, ifOp);
1932 return success();
1933 }
1934};
1935
1936struct YieldOpConversion : public OpConversionPattern<YieldOp> {
1937 using OpConversionPattern::OpConversionPattern;
1938
1939 LogicalResult
1940 matchAndRewrite(YieldOp op, OpAdaptor adaptor,
1941 ConversionPatternRewriter &rewriter) const override {
1942 rewriter.replaceOpWithNewOp<scf::YieldOp>(op, adaptor.getResult());
1943 return success();
1944 }
1945};
1946
1947template <typename SourceOp>
1948struct InPlaceOpConversion : public OpConversionPattern<SourceOp> {
1950 using OpAdaptor = typename SourceOp::Adaptor;
1951
1952 LogicalResult
1953 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1954 ConversionPatternRewriter &rewriter) const override {
1955 rewriter.modifyOpInPlace(op,
1956 [&]() { op->setOperands(adaptor.getOperands()); });
1957 return success();
1958 }
1959};
1960
1961template <typename MooreOpTy, typename VerifOpTy>
1962struct AssertLikeOpConversion : public OpConversionPattern<MooreOpTy> {
1964 using OpAdaptor = typename MooreOpTy::Adaptor;
1965
1966 LogicalResult
1967 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
1968 ConversionPatternRewriter &rewriter) const override {
1969 StringAttr label =
1970 op.getLabel().has_value()
1971 ? StringAttr::get(op->getContext(), op.getLabel().value())
1972 : StringAttr::get(op->getContext());
1973 rewriter.replaceOpWithNewOp<VerifOpTy>(op, adaptor.getCond(), mlir::Value(),
1974 label);
1975 return success();
1976 }
1977};
1978
1979//===----------------------------------------------------------------------===//
1980// Format String Conversion
1981//===----------------------------------------------------------------------===//
1982
1983struct FormatLiteralOpConversion : public OpConversionPattern<FormatLiteralOp> {
1984 using OpConversionPattern::OpConversionPattern;
1985
1986 LogicalResult
1987 matchAndRewrite(FormatLiteralOp op, OpAdaptor adaptor,
1988 ConversionPatternRewriter &rewriter) const override {
1989 rewriter.replaceOpWithNewOp<sim::FormatLiteralOp>(op, adaptor.getLiteral());
1990 return success();
1991 }
1992};
1993
1994struct FormatConcatOpConversion : public OpConversionPattern<FormatConcatOp> {
1995 using OpConversionPattern::OpConversionPattern;
1996
1997 LogicalResult
1998 matchAndRewrite(FormatConcatOp op, OpAdaptor adaptor,
1999 ConversionPatternRewriter &rewriter) const override {
2000 rewriter.replaceOpWithNewOp<sim::FormatStringConcatOp>(op,
2001 adaptor.getInputs());
2002 return success();
2003 }
2004};
2005
2006struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
2007 using OpConversionPattern::OpConversionPattern;
2008
2009 LogicalResult
2010 matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
2011 ConversionPatternRewriter &rewriter) const override {
2012
2013 char padChar = adaptor.getPadding() == IntPadding::Space ? 32 : 48;
2014 IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar);
2015 auto widthAttr = adaptor.getSpecifierWidthAttr();
2016
2017 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2018 BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2019
2020 switch (op.getFormat()) {
2021 case IntFormat::Decimal:
2022 rewriter.replaceOpWithNewOp<sim::FormatDecOp>(
2023 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr,
2024 adaptor.getIsSignedAttr());
2025 return success();
2026 case IntFormat::Binary:
2027 rewriter.replaceOpWithNewOp<sim::FormatBinOp>(
2028 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2029 return success();
2030 case IntFormat::Octal:
2031 rewriter.replaceOpWithNewOp<sim::FormatOctOp>(
2032 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2033 return success();
2034 case IntFormat::HexLower:
2035 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2036 op, adaptor.getValue(), rewriter.getBoolAttr(false),
2037 isLeftAlignedAttr, padCharAttr, widthAttr);
2038 return success();
2039 case IntFormat::HexUpper:
2040 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2041 op, adaptor.getValue(), rewriter.getBoolAttr(true), isLeftAlignedAttr,
2042 padCharAttr, widthAttr);
2043 return success();
2044 }
2045 return rewriter.notifyMatchFailure(op, "unsupported int format");
2046 }
2047};
2048
2049struct FormatRealOpConversion : public OpConversionPattern<FormatRealOp> {
2050 using OpConversionPattern::OpConversionPattern;
2051
2052 LogicalResult
2053 matchAndRewrite(FormatRealOp op, OpAdaptor adaptor,
2054 ConversionPatternRewriter &rewriter) const override {
2055 auto fracDigitsAttr = adaptor.getFracDigitsAttr();
2056
2057 auto fieldWidthAttr = adaptor.getFieldWidthAttr();
2058 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2059 mlir::BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2060
2061 switch (op.getFormat()) {
2062 case RealFormat::General:
2063 rewriter.replaceOpWithNewOp<sim::FormatGeneralOp>(
2064 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2065 fracDigitsAttr);
2066 return success();
2067 case RealFormat::Float:
2068 rewriter.replaceOpWithNewOp<sim::FormatFloatOp>(
2069 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2070 fracDigitsAttr);
2071 return success();
2072 case RealFormat::Exponential:
2073 rewriter.replaceOpWithNewOp<sim::FormatScientificOp>(
2074 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2075 fracDigitsAttr);
2076 return success();
2077 default:
2078 return rewriter.notifyMatchFailure(op, "unsupported real format");
2079 }
2080 }
2081};
2082
2083struct DisplayBIOpConversion : public OpConversionPattern<DisplayBIOp> {
2084 using OpConversionPattern::OpConversionPattern;
2085
2086 LogicalResult
2087 matchAndRewrite(DisplayBIOp op, OpAdaptor adaptor,
2088 ConversionPatternRewriter &rewriter) const override {
2089 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
2090 op, adaptor.getMessage());
2091 return success();
2092 }
2093};
2094
2095} // namespace
2096
2097//===----------------------------------------------------------------------===//
2098// Simulation Control Conversion
2099//===----------------------------------------------------------------------===//
2100
2101// moore.builtin.stop -> sim.pause
2102static LogicalResult convert(StopBIOp op, StopBIOp::Adaptor adaptor,
2103 ConversionPatternRewriter &rewriter) {
2104 rewriter.replaceOpWithNewOp<sim::PauseOp>(op, /*verbose=*/false);
2105 return success();
2106}
2107
2108// moore.builtin.finish -> sim.terminate
2109static LogicalResult convert(FinishBIOp op, FinishBIOp::Adaptor adaptor,
2110 ConversionPatternRewriter &rewriter) {
2111 rewriter.replaceOpWithNewOp<sim::TerminateOp>(op, op.getExitCode() == 0,
2112 /*verbose=*/false);
2113 return success();
2114}
2115
2116// moore.builtin.severity -> sim.proc.print
2117static LogicalResult convert(SeverityBIOp op, SeverityBIOp::Adaptor adaptor,
2118 ConversionPatternRewriter &rewriter) {
2119
2120 std::string severityString;
2121
2122 switch (op.getSeverity()) {
2123 case (Severity::Fatal):
2124 severityString = "Fatal: ";
2125 break;
2126 case (Severity::Error):
2127 severityString = "Error: ";
2128 break;
2129 case (Severity::Warning):
2130 severityString = "Warning: ";
2131 break;
2132 default:
2133 return failure();
2134 }
2135
2136 auto prefix =
2137 sim::FormatLiteralOp::create(rewriter, op.getLoc(), severityString);
2138 auto message = sim::FormatStringConcatOp::create(
2139 rewriter, op.getLoc(), ValueRange{prefix, adaptor.getMessage()});
2140 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(op, message);
2141 return success();
2142}
2143
2144// moore.builtin.finish_message
2145static LogicalResult convert(FinishMessageBIOp op,
2146 FinishMessageBIOp::Adaptor adaptor,
2147 ConversionPatternRewriter &rewriter) {
2148 // We don't support printing termination/pause messages yet.
2149 rewriter.eraseOp(op);
2150 return success();
2151}
2152
2153//===----------------------------------------------------------------------===//
2154// Timing Control Conversion
2155//===----------------------------------------------------------------------===//
2156
2157// moore.builtin.time
2158static LogicalResult convert(TimeBIOp op, TimeBIOp::Adaptor adaptor,
2159 ConversionPatternRewriter &rewriter) {
2160 rewriter.replaceOpWithNewOp<llhd::CurrentTimeOp>(op);
2161 return success();
2162}
2163
2164// moore.logic_to_time
2165static LogicalResult convert(LogicToTimeOp op, LogicToTimeOp::Adaptor adaptor,
2166 ConversionPatternRewriter &rewriter) {
2167 rewriter.replaceOpWithNewOp<llhd::IntToTimeOp>(op, adaptor.getInput());
2168 return success();
2169}
2170
2171// moore.time_to_logic
2172static LogicalResult convert(TimeToLogicOp op, TimeToLogicOp::Adaptor adaptor,
2173 ConversionPatternRewriter &rewriter) {
2174 rewriter.replaceOpWithNewOp<llhd::TimeToIntOp>(op, adaptor.getInput());
2175 return success();
2176}
2177
2178//===----------------------------------------------------------------------===//
2179// Conversion Infrastructure
2180//===----------------------------------------------------------------------===//
2181
2182static void populateLegality(ConversionTarget &target,
2183 const TypeConverter &converter) {
2184 target.addIllegalDialect<MooreDialect>();
2185 target.addLegalDialect<comb::CombDialect>();
2186 target.addLegalDialect<hw::HWDialect>();
2187 target.addLegalDialect<seq::SeqDialect>();
2188 target.addLegalDialect<llhd::LLHDDialect>();
2189 target.addLegalDialect<ltl::LTLDialect>();
2190 target.addLegalDialect<mlir::BuiltinDialect>();
2191 target.addLegalDialect<mlir::math::MathDialect>();
2192 target.addLegalDialect<sim::SimDialect>();
2193 target.addLegalDialect<mlir::LLVM::LLVMDialect>();
2194 target.addLegalDialect<verif::VerifDialect>();
2195 target.addLegalDialect<arith::ArithDialect>();
2196
2197 target.addLegalOp<debug::ScopeOp>();
2198
2199 target.addDynamicallyLegalOp<scf::YieldOp, func::CallOp, func::ReturnOp,
2200 UnrealizedConversionCastOp, hw::OutputOp,
2201 hw::InstanceOp, debug::ArrayOp, debug::StructOp,
2202 debug::VariableOp>(
2203 [&](Operation *op) { return converter.isLegal(op); });
2204
2205 target.addDynamicallyLegalOp<scf::IfOp, scf::ForOp, scf::ExecuteRegionOp,
2206 scf::WhileOp, scf::ForallOp>([&](Operation *op) {
2207 return converter.isLegal(op) && !op->getParentOfType<llhd::ProcessOp>();
2208 });
2209
2210 target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
2211 return converter.isSignatureLegal(op.getFunctionType());
2212 });
2213
2214 target.addDynamicallyLegalOp<hw::HWModuleOp>([&](hw::HWModuleOp op) {
2215 return converter.isSignatureLegal(op.getModuleType().getFuncType()) &&
2216 converter.isLegal(&op.getBody());
2217 });
2218}
2219
2220static void populateTypeConversion(TypeConverter &typeConverter) {
2221 typeConverter.addConversion([&](IntType type) {
2222 return IntegerType::get(type.getContext(), type.getWidth());
2223 });
2224
2225 typeConverter.addConversion([&](RealType type) -> mlir::Type {
2226 MLIRContext *ctx = type.getContext();
2227 switch (type.getWidth()) {
2228 case moore::RealWidth::f32:
2229 return mlir::Float32Type::get(ctx);
2230 case moore::RealWidth::f64:
2231 return mlir::Float64Type::get(ctx);
2232 }
2233 });
2234
2235 typeConverter.addConversion(
2236 [&](TimeType type) { return llhd::TimeType::get(type.getContext()); });
2237
2238 typeConverter.addConversion([&](FormatStringType type) {
2239 return sim::FormatStringType::get(type.getContext());
2240 });
2241
2242 typeConverter.addConversion([&](ArrayType type) -> std::optional<Type> {
2243 if (auto elementType = typeConverter.convertType(type.getElementType()))
2244 return hw::ArrayType::get(elementType, type.getSize());
2245 return {};
2246 });
2247
2248 // FIXME: Unpacked arrays support more element types than their packed
2249 // variants, and as such, mapping them to hw::Array is somewhat naive. See
2250 // also the analogous note below concerning unpacked struct type conversion.
2251 typeConverter.addConversion(
2252 [&](UnpackedArrayType type) -> std::optional<Type> {
2253 if (auto elementType = typeConverter.convertType(type.getElementType()))
2254 return hw::ArrayType::get(elementType, type.getSize());
2255 return {};
2256 });
2257
2258 typeConverter.addConversion([&](StructType type) -> std::optional<Type> {
2259 SmallVector<hw::StructType::FieldInfo> fields;
2260 for (auto field : type.getMembers()) {
2261 hw::StructType::FieldInfo info;
2262 info.type = typeConverter.convertType(field.type);
2263 if (!info.type)
2264 return {};
2265 info.name = field.name;
2266 fields.push_back(info);
2267 }
2268 return hw::StructType::get(type.getContext(), fields);
2269 });
2270
2271 // FIXME: Mapping unpacked struct type to struct type in hw dialect may be a
2272 // plain solution. The packed and unpacked data structures have some
2273 // differences though they look similarily. The packed data structure is
2274 // contiguous in memory but another is opposite. The differences will affect
2275 // data layout and granularity of event tracking in simulation.
2276 typeConverter.addConversion(
2277 [&](UnpackedStructType type) -> std::optional<Type> {
2278 SmallVector<hw::StructType::FieldInfo> fields;
2279 for (auto field : type.getMembers()) {
2280 hw::StructType::FieldInfo info;
2281 info.type = typeConverter.convertType(field.type);
2282 if (!info.type)
2283 return {};
2284 info.name = field.name;
2285 fields.push_back(info);
2286 }
2287 return hw::StructType::get(type.getContext(), fields);
2288 });
2289
2290 // Conversion of CHandle to LLVMPointerType
2291 typeConverter.addConversion([&](ChandleType type) -> std::optional<Type> {
2292 return LLVM::LLVMPointerType::get(type.getContext());
2293 });
2294
2295 // Explicitly mark LLVMPointerType as a legal target
2296 typeConverter.addConversion(
2297 [](LLVM::LLVMPointerType t) -> std::optional<Type> { return t; });
2298
2299 // ClassHandleType -> !llvm.ptr
2300 typeConverter.addConversion([&](ClassHandleType type) -> std::optional<Type> {
2301 return LLVM::LLVMPointerType::get(type.getContext());
2302 });
2303
2304 typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
2305 if (auto innerType = typeConverter.convertType(type.getNestedType()))
2306 return llhd::RefType::get(innerType);
2307 return {};
2308 });
2309
2310 // Valid target types.
2311 typeConverter.addConversion([](IntegerType type) { return type; });
2312 typeConverter.addConversion([](FloatType type) { return type; });
2313 typeConverter.addConversion([](llhd::TimeType type) { return type; });
2314 typeConverter.addConversion([](debug::ArrayType type) { return type; });
2315 typeConverter.addConversion([](debug::ScopeType type) { return type; });
2316 typeConverter.addConversion([](debug::StructType type) { return type; });
2317
2318 typeConverter.addConversion([&](llhd::RefType type) -> std::optional<Type> {
2319 if (auto innerType = typeConverter.convertType(type.getNestedType()))
2320 return llhd::RefType::get(innerType);
2321 return {};
2322 });
2323
2324 typeConverter.addConversion([&](hw::ArrayType type) -> std::optional<Type> {
2325 if (auto elementType = typeConverter.convertType(type.getElementType()))
2326 return hw::ArrayType::get(elementType, type.getNumElements());
2327 return {};
2328 });
2329
2330 typeConverter.addConversion([&](hw::StructType type) -> std::optional<Type> {
2331 SmallVector<hw::StructType::FieldInfo> fields;
2332 for (auto field : type.getElements()) {
2333 hw::StructType::FieldInfo info;
2334 info.type = typeConverter.convertType(field.type);
2335 if (!info.type)
2336 return {};
2337 info.name = field.name;
2338 fields.push_back(info);
2339 }
2340 return hw::StructType::get(type.getContext(), fields);
2341 });
2342
2343 typeConverter.addTargetMaterialization(
2344 [&](mlir::OpBuilder &builder, mlir::Type resultType,
2345 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
2346 if (inputs.size() != 1 || !inputs[0])
2347 return Value();
2348 return UnrealizedConversionCastOp::create(builder, loc, resultType,
2349 inputs[0])
2350 .getResult(0);
2351 });
2352
2353 typeConverter.addSourceMaterialization(
2354 [&](mlir::OpBuilder &builder, mlir::Type resultType,
2355 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
2356 if (inputs.size() != 1)
2357 return Value();
2358 return UnrealizedConversionCastOp::create(builder, loc, resultType,
2359 inputs[0])
2360 ->getResult(0);
2361 });
2362}
2363
2365 TypeConverter &typeConverter,
2366 ClassTypeCache &classCache) {
2367
2368 patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
2369 classCache);
2370 patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
2371 classCache);
2372 patterns.add<ClassPropertyRefOpConversion>(typeConverter,
2373 patterns.getContext(), classCache);
2374
2375 // clang-format off
2376 patterns.add<
2377 ClassUpcastOpConversion,
2378 // Patterns of declaration operations.
2379 VariableOpConversion,
2380 NetOpConversion,
2381
2382 // Patterns for conversion operations.
2383 ConversionOpConversion,
2384 BitcastConversion<PackedToSBVOp>,
2385 BitcastConversion<SBVToPackedOp>,
2386 BitcastConversion<LogicToIntOp>,
2387 BitcastConversion<IntToLogicOp>,
2388 BitcastConversion<ToBuiltinBoolOp>,
2389 TruncOpConversion,
2390 ZExtOpConversion,
2391 SExtOpConversion,
2392 SIntToRealOpConversion,
2393 UIntToRealOpConversion,
2394 RealToIntOpConversion,
2395
2396 // Patterns of miscellaneous operations.
2397 ConstantOpConv,
2398 ConstantRealOpConv,
2399 ConcatOpConversion,
2400 ReplicateOpConversion,
2401 ConstantTimeOpConv,
2402 ExtractOpConversion,
2403 DynExtractOpConversion,
2404 DynExtractRefOpConversion,
2405 ReadOpConversion,
2406 StructExtractOpConversion,
2407 StructExtractRefOpConversion,
2408 ExtractRefOpConversion,
2409 StructCreateOpConversion,
2410 ConditionalOpConversion,
2411 ArrayCreateOpConversion,
2412 YieldOpConversion,
2413 OutputOpConversion,
2414 ConstantStringOpConv,
2415
2416 // Patterns of unary operations.
2417 ReduceAndOpConversion,
2418 ReduceOrOpConversion,
2419 ReduceXorOpConversion,
2420 BoolCastOpConversion,
2421 NotOpConversion,
2422 NegOpConversion,
2423
2424 // Patterns of binary operations.
2425 BinaryOpConversion<AddOp, comb::AddOp>,
2426 BinaryOpConversion<SubOp, comb::SubOp>,
2427 BinaryOpConversion<MulOp, comb::MulOp>,
2428 BinaryOpConversion<DivUOp, comb::DivUOp>,
2429 BinaryOpConversion<DivSOp, comb::DivSOp>,
2430 BinaryOpConversion<ModUOp, comb::ModUOp>,
2431 BinaryOpConversion<ModSOp, comb::ModSOp>,
2432 BinaryOpConversion<AndOp, comb::AndOp>,
2433 BinaryOpConversion<OrOp, comb::OrOp>,
2434 BinaryOpConversion<XorOp, comb::XorOp>,
2435
2436 // Patterns for binary real operations.
2437 BinaryRealOpConversion<AddRealOp, arith::AddFOp>,
2438 BinaryRealOpConversion<SubRealOp, arith::SubFOp>,
2439 BinaryRealOpConversion<DivRealOp, arith::DivFOp>,
2440 BinaryRealOpConversion<MulRealOp, arith::MulFOp>,
2441 BinaryRealOpConversion<PowRealOp, math::PowFOp>,
2442
2443 // Patterns of power operations.
2444 PowUOpConversion, PowSOpConversion,
2445
2446 // Patterns of relational operations.
2447 ICmpOpConversion<UltOp, ICmpPredicate::ult>,
2448 ICmpOpConversion<SltOp, ICmpPredicate::slt>,
2449 ICmpOpConversion<UleOp, ICmpPredicate::ule>,
2450 ICmpOpConversion<SleOp, ICmpPredicate::sle>,
2451 ICmpOpConversion<UgtOp, ICmpPredicate::ugt>,
2452 ICmpOpConversion<SgtOp, ICmpPredicate::sgt>,
2453 ICmpOpConversion<UgeOp, ICmpPredicate::uge>,
2454 ICmpOpConversion<SgeOp, ICmpPredicate::sge>,
2455 ICmpOpConversion<EqOp, ICmpPredicate::eq>,
2456 ICmpOpConversion<NeOp, ICmpPredicate::ne>,
2457 ICmpOpConversion<CaseEqOp, ICmpPredicate::ceq>,
2458 ICmpOpConversion<CaseNeOp, ICmpPredicate::cne>,
2459 ICmpOpConversion<WildcardEqOp, ICmpPredicate::weq>,
2460 ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
2461 FCmpOpConversion<NeRealOp, arith::CmpFPredicate::ONE>,
2462 FCmpOpConversion<FltOp, arith::CmpFPredicate::OLT>,
2463 FCmpOpConversion<FleOp, arith::CmpFPredicate::OLE>,
2464 FCmpOpConversion<FgtOp, arith::CmpFPredicate::OGT>,
2465 FCmpOpConversion<FgeOp, arith::CmpFPredicate::OGE>,
2466 FCmpOpConversion<EqRealOp, arith::CmpFPredicate::OEQ>,
2467 CaseXZEqOpConversion<CaseZEqOp, true>,
2468 CaseXZEqOpConversion<CaseXZEqOp, false>,
2469
2470 // Patterns of structural operations.
2471 SVModuleOpConversion,
2472 InstanceOpConversion,
2473 ProcedureOpConversion,
2474 WaitEventOpConversion,
2475
2476 // Patterns of shifting operations.
2477 ShrOpConversion,
2478 ShlOpConversion,
2479 AShrOpConversion,
2480
2481 // Patterns of assignment operations.
2482 AssignOpConversion<ContinuousAssignOp>,
2483 AssignOpConversion<DelayedContinuousAssignOp>,
2484 AssignOpConversion<BlockingAssignOp>,
2485 AssignOpConversion<NonBlockingAssignOp>,
2486 AssignOpConversion<DelayedNonBlockingAssignOp>,
2487 AssignedVariableOpConversion,
2488
2489 // Patterns of other operations outside Moore dialect.
2490 HWInstanceOpConversion,
2491 ReturnOpConversion,
2492 CallOpConversion,
2493 UnrealizedConversionCastConversion,
2494 InPlaceOpConversion<debug::ArrayOp>,
2495 InPlaceOpConversion<debug::StructOp>,
2496 InPlaceOpConversion<debug::VariableOp>,
2497
2498 // Patterns of assert-like operations
2499 AssertLikeOpConversion<AssertOp, verif::AssertOp>,
2500 AssertLikeOpConversion<AssumeOp, verif::AssumeOp>,
2501 AssertLikeOpConversion<CoverOp, verif::CoverOp>,
2502
2503 // Format strings.
2504 FormatLiteralOpConversion,
2505 FormatConcatOpConversion,
2506 FormatIntOpConversion,
2507 FormatRealOpConversion,
2508 DisplayBIOpConversion
2509 >(typeConverter, patterns.getContext());
2510 // clang-format on
2511
2512 // Structural operations
2513 patterns.add<WaitDelayOp>(convert);
2514 patterns.add<UnreachableOp>(convert);
2515
2516 // Simulation control
2517 patterns.add<StopBIOp>(convert);
2518 patterns.add<SeverityBIOp>(convert);
2519 patterns.add<FinishBIOp>(convert);
2520 patterns.add<FinishMessageBIOp>(convert);
2521
2522 // Timing control
2523 patterns.add<TimeBIOp>(convert);
2524 patterns.add<LogicToTimeOp>(convert);
2525 patterns.add<TimeToLogicOp>(convert);
2526
2527 mlir::populateAnyFunctionOpInterfaceTypeConversionPattern(patterns,
2528 typeConverter);
2529 hw::populateHWModuleLikeTypeConversionPattern(
2530 hw::HWModuleOp::getOperationName(), patterns, typeConverter);
2531 populateSCFToControlFlowConversionPatterns(patterns);
2532 populateArithToCombPatterns(patterns, typeConverter);
2533}
2534
2535//===----------------------------------------------------------------------===//
2536// Moore to Core Conversion Pass
2537//===----------------------------------------------------------------------===//
2538
2539namespace {
2540struct MooreToCorePass
2541 : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
2542 void runOnOperation() override;
2543};
2544} // namespace
2545
2546/// Create a Moore to core dialects conversion pass.
2547std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
2548 return std::make_unique<MooreToCorePass>();
2549}
2550
2551/// This is the main entrypoint for the Moore to Core conversion pass.
2552void MooreToCorePass::runOnOperation() {
2553 MLIRContext &context = getContext();
2554 ModuleOp module = getOperation();
2555 ClassTypeCache classCache;
2556
2557 IRRewriter rewriter(module);
2558 (void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
2559
2560 TypeConverter typeConverter;
2561 populateTypeConversion(typeConverter);
2562
2563 ConversionTarget target(context);
2564 populateLegality(target, typeConverter);
2565
2566 ConversionPatternSet patterns(&context, typeConverter);
2567 populateOpConversion(patterns, typeConverter, classCache);
2568 mlir::cf::populateCFStructuralTypeConversionsAndLegality(typeConverter,
2569 patterns, target);
2570
2571 if (failed(applyFullConversion(module, target, std::move(patterns))))
2572 signalPassFailure();
2573}
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
Definition CalyxOps.cpp:540
static std::unique_ptr< Context > context
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 populateLegality(ConversionTarget &target, const TypeConverter &converter)
static void populateOpConversion(ConversionPatternSet &patterns, TypeConverter &typeConverter, ClassTypeCache &classCache)
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.