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 // Handle dynamic strings
701 if (auto strType = dyn_cast<sim::DynamicStringType>(type))
702 return sim::StringConstantOp::create(rewriter, loc, strType, "");
703
704 // Otherwise try to create a zero integer and bitcast it to the result type.
705 int64_t width = hw::getBitWidth(type);
706 if (width == -1)
707 return {};
708
709 // TODO: Once the core dialects support four-valued integers, this code
710 // will additionally need to generate an all-X value for four-valued
711 // variables.
712 Value constZero = hw::ConstantOp::create(rewriter, loc, APInt(width, 0));
713 return rewriter.createOrFold<hw::BitcastOp>(loc, type, constZero);
714}
715
716struct ClassPropertyRefOpConversion
717 : public OpConversionPattern<circt::moore::ClassPropertyRefOp> {
718 ClassPropertyRefOpConversion(TypeConverter &tc, MLIRContext *ctx,
719 ClassTypeCache &cache)
720 : OpConversionPattern(tc, ctx), cache(cache) {}
721
722 LogicalResult
723 matchAndRewrite(circt::moore::ClassPropertyRefOp op, OpAdaptor adaptor,
724 ConversionPatternRewriter &rewriter) const override {
725 Location loc = op.getLoc();
726 MLIRContext *ctx = rewriter.getContext();
727
728 // Convert result type; we expect !llhd.ref<someT>.
729 Type dstTy = getTypeConverter()->convertType(op.getPropertyRef().getType());
730 // Operand is a !llvm.ptr
731 Value instRef = adaptor.getInstance();
732
733 // Resolve identified struct from cache.
734 auto classRefTy =
735 cast<circt::moore::ClassHandleType>(op.getInstance().getType());
736 SymbolRefAttr classSym = classRefTy.getClassSym();
737 ModuleOp mod = op->getParentOfType<ModuleOp>();
738 if (failed(resolveClassStructBody(mod, classSym, *typeConverter, cache)))
739 return rewriter.notifyMatchFailure(op,
740 "Could not resolve class struct for " +
741 classSym.getRootReference().str());
742
743 auto structInfo = cache.getStructInfo(classSym);
744 assert(structInfo && "class struct info must exist");
745 auto structTy = structInfo->classBody;
746
747 // Look up cached GEP path for the property.
748 auto propSym = op.getProperty();
749 auto pathOpt = structInfo->getFieldPath(propSym);
750 if (!pathOpt)
751 return rewriter.notifyMatchFailure(op,
752 "no GEP path for property " + propSym);
753
754 auto i32Ty = IntegerType::get(ctx, 32);
755 SmallVector<Value> idxVals;
756 for (unsigned idx : *pathOpt)
757 idxVals.push_back(LLVM::ConstantOp::create(
758 rewriter, loc, i32Ty, rewriter.getI32IntegerAttr(idx)));
759
760 // GEP to the field (opaque ptr mode requires element type).
761 auto ptrTy = LLVM::LLVMPointerType::get(ctx);
762 auto gep =
763 LLVM::GEPOp::create(rewriter, loc, ptrTy, structTy, instRef, idxVals);
764
765 // Wrap pointer back to !llhd.ref<someT>.
766 Value fieldRef = UnrealizedConversionCastOp::create(rewriter, loc, dstTy,
767 gep.getResult())
768 .getResult(0);
769
770 rewriter.replaceOp(op, fieldRef);
771 return success();
772 }
773
774private:
775 ClassTypeCache &cache;
776};
777
778struct ClassUpcastOpConversion : public OpConversionPattern<ClassUpcastOp> {
779 using OpConversionPattern::OpConversionPattern;
780
781 LogicalResult
782 matchAndRewrite(ClassUpcastOp op, OpAdaptor adaptor,
783 ConversionPatternRewriter &rewriter) const override {
784 // Expect lowered types like !llvm.ptr
785 Type dstTy = getTypeConverter()->convertType(op.getResult().getType());
786 Type srcTy = adaptor.getInstance().getType();
787
788 if (!dstTy)
789 return rewriter.notifyMatchFailure(op, "failed to convert result type");
790
791 // If the types are already identical (opaque pointer mode), just forward.
792 if (dstTy == srcTy && isa<LLVM::LLVMPointerType>(srcTy)) {
793 rewriter.replaceOp(op, adaptor.getInstance());
794 return success();
795 }
796 return rewriter.notifyMatchFailure(
797 op, "Upcast applied to non-opaque pointers!");
798 }
799};
800
801/// moore.class.new lowering: heap-allocate storage for the class object.
802struct ClassNewOpConversion : public OpConversionPattern<ClassNewOp> {
803 ClassNewOpConversion(TypeConverter &tc, MLIRContext *ctx,
804 ClassTypeCache &cache)
805 : OpConversionPattern<ClassNewOp>(tc, ctx), cache(cache) {}
806
807 LogicalResult
808 matchAndRewrite(ClassNewOp op, OpAdaptor adaptor,
809 ConversionPatternRewriter &rewriter) const override {
810 Location loc = op.getLoc();
811 MLIRContext *ctx = rewriter.getContext();
812
813 auto handleTy = cast<ClassHandleType>(op.getResult().getType());
814 auto sym = handleTy.getClassSym();
815
816 ModuleOp mod = op->getParentOfType<ModuleOp>();
817
818 if (failed(resolveClassStructBody(mod, sym, *typeConverter, cache)))
819 return op.emitError() << "Could not resolve class struct for " << sym;
820
821 auto structTy = cache.getStructInfo(sym)->classBody;
822
823 DataLayout dl(mod);
824 // DataLayout::getTypeSize gives a byte count for LLVM types.
825 uint64_t byteSize = dl.getTypeSize(structTy);
826 auto i64Ty = IntegerType::get(ctx, 64);
827 auto cSize = LLVM::ConstantOp::create(rewriter, loc, i64Ty,
828 rewriter.getI64IntegerAttr(byteSize));
829
830 // Get or declare malloc and call it.
831 auto mallocFn = getOrCreateMalloc(mod, rewriter);
832 auto ptrTy = LLVM::LLVMPointerType::get(ctx); // opaque pointer result
833 auto call =
834 LLVM::CallOp::create(rewriter, loc, TypeRange{ptrTy},
835 SymbolRefAttr::get(mallocFn), ValueRange{cSize});
836
837 // Replace the new op with the malloc pointer (no cast needed with opaque
838 // ptrs).
839 rewriter.replaceOp(op, call.getResult());
840 return success();
841 }
842
843private:
844 ClassTypeCache &cache; // shared, owned by the pass
845};
846
847struct ClassDeclOpConversion : public OpConversionPattern<ClassDeclOp> {
848 ClassDeclOpConversion(TypeConverter &tc, MLIRContext *ctx,
849 ClassTypeCache &cache)
850 : OpConversionPattern<ClassDeclOp>(tc, ctx), cache(cache) {}
851
852 LogicalResult
853 matchAndRewrite(ClassDeclOp op, OpAdaptor,
854 ConversionPatternRewriter &rewriter) const override {
855
856 if (failed(resolveClassStructBody(op, *typeConverter, cache)))
857 return failure();
858 // The declaration itself is a no-op
859 rewriter.eraseOp(op);
860 return success();
861 }
862
863private:
864 ClassTypeCache &cache; // shared, owned by the pass
865};
866
867struct VariableOpConversion : public OpConversionPattern<VariableOp> {
868 using OpConversionPattern::OpConversionPattern;
869
870 LogicalResult
871 matchAndRewrite(VariableOp op, OpAdaptor adaptor,
872 ConversionPatternRewriter &rewriter) const override {
873 auto loc = op.getLoc();
874 auto resultType = typeConverter->convertType(op.getResult().getType());
875 if (!resultType)
876 return rewriter.notifyMatchFailure(op.getLoc(), "invalid variable type");
877
878 // Determine the initial value of the signal.
879 Value init = adaptor.getInitial();
880 if (!init) {
881 auto elementType = cast<llhd::RefType>(resultType).getNestedType();
882 init = createZeroValue(elementType, loc, rewriter);
883 if (!init)
884 return failure();
885 }
886
887 rewriter.replaceOpWithNewOp<llhd::SignalOp>(op, resultType,
888 op.getNameAttr(), init);
889 return success();
890 }
891};
892
893struct NetOpConversion : public OpConversionPattern<NetOp> {
894 using OpConversionPattern::OpConversionPattern;
895
896 LogicalResult
897 matchAndRewrite(NetOp op, OpAdaptor adaptor,
898 ConversionPatternRewriter &rewriter) const override {
899 auto loc = op.getLoc();
900 if (op.getKind() != NetKind::Wire)
901 return rewriter.notifyMatchFailure(loc, "only wire nets supported");
902
903 auto resultType = typeConverter->convertType(op.getResult().getType());
904 if (!resultType)
905 return rewriter.notifyMatchFailure(loc, "invalid net type");
906
907 // TODO: Once the core dialects support four-valued integers, this code
908 // will additionally need to generate an all-X value for four-valued nets.
909 auto elementType = cast<llhd::RefType>(resultType).getNestedType();
910 int64_t width = hw::getBitWidth(elementType);
911 if (width == -1)
912 return failure();
913 auto constZero = hw::ConstantOp::create(rewriter, loc, APInt(width, 0));
914 auto init =
915 rewriter.createOrFold<hw::BitcastOp>(loc, elementType, constZero);
916
917 auto signal = rewriter.replaceOpWithNewOp<llhd::SignalOp>(
918 op, resultType, op.getNameAttr(), init);
919
920 if (auto assignedValue = adaptor.getAssignment()) {
921 auto timeAttr = llhd::TimeAttr::get(resultType.getContext(), 0U,
922 llvm::StringRef("ns"), 0, 1);
923 auto time = llhd::ConstantTimeOp::create(rewriter, loc, timeAttr);
924 llhd::DriveOp::create(rewriter, loc, signal, assignedValue, time,
925 Value{});
926 }
927
928 return success();
929 }
930};
931
932//===----------------------------------------------------------------------===//
933// Expression Conversion
934//===----------------------------------------------------------------------===//
935
936struct ConstantOpConv : public OpConversionPattern<ConstantOp> {
937 using OpConversionPattern::OpConversionPattern;
938
939 LogicalResult
940 matchAndRewrite(ConstantOp op, OpAdaptor adaptor,
941 ConversionPatternRewriter &rewriter) const override {
942 // FIXME: Discard unknown bits and map them to 0 for now.
943 auto value = op.getValue().toAPInt(false);
944 auto type = rewriter.getIntegerType(value.getBitWidth());
945 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
946 op, type, rewriter.getIntegerAttr(type, value));
947 return success();
948 }
949};
950
951struct ConstantRealOpConv : public OpConversionPattern<ConstantRealOp> {
952 using OpConversionPattern::OpConversionPattern;
953
954 LogicalResult
955 matchAndRewrite(ConstantRealOp op, OpAdaptor adaptor,
956 ConversionPatternRewriter &rewriter) const override {
957 rewriter.replaceOpWithNewOp<arith::ConstantOp>(op, op.getValueAttr());
958 return success();
959 }
960};
961
962struct ConstantTimeOpConv : public OpConversionPattern<ConstantTimeOp> {
963 using OpConversionPattern::OpConversionPattern;
964
965 LogicalResult
966 matchAndRewrite(ConstantTimeOp op, OpAdaptor adaptor,
967 ConversionPatternRewriter &rewriter) const override {
968 rewriter.replaceOpWithNewOp<llhd::ConstantTimeOp>(
969 op, llhd::TimeAttr::get(op->getContext(), op.getValue(),
970 StringRef("fs"), 0, 0));
971 return success();
972 }
973};
974
975struct ConstantStringOpConv : public OpConversionPattern<ConstantStringOp> {
976 using OpConversionPattern::OpConversionPattern;
977 LogicalResult
978 matchAndRewrite(moore::ConstantStringOp op, OpAdaptor adaptor,
979 ConversionPatternRewriter &rewriter) const override {
980 const auto resultType =
981 typeConverter->convertType(op.getResult().getType());
982 const auto intType = mlir::cast<IntegerType>(resultType);
983
984 const auto str = op.getValue();
985 const unsigned byteWidth = intType.getWidth();
986 APInt value(byteWidth, 0);
987
988 // Pack ascii chars from the end of the string, until it fits.
989 const size_t maxChars =
990 std::min(str.size(), static_cast<size_t>(byteWidth / 8));
991 for (size_t i = 0; i < maxChars; i++) {
992 const size_t pos = str.size() - 1 - i;
993 const auto asciiChar = static_cast<uint8_t>(str[pos]);
994 value |= APInt(byteWidth, asciiChar) << (8 * i);
995 }
996
997 rewriter.replaceOpWithNewOp<hw::ConstantOp>(
998 op, resultType, rewriter.getIntegerAttr(resultType, value));
999 return success();
1000 }
1001};
1002
1003struct ConcatOpConversion : public OpConversionPattern<ConcatOp> {
1004 using OpConversionPattern::OpConversionPattern;
1005 LogicalResult
1006 matchAndRewrite(ConcatOp op, OpAdaptor adaptor,
1007 ConversionPatternRewriter &rewriter) const override {
1008 rewriter.replaceOpWithNewOp<comb::ConcatOp>(op, adaptor.getValues());
1009 return success();
1010 }
1011};
1012
1013struct ReplicateOpConversion : public OpConversionPattern<ReplicateOp> {
1014 using OpConversionPattern::OpConversionPattern;
1015 LogicalResult
1016 matchAndRewrite(ReplicateOp op, OpAdaptor adaptor,
1017 ConversionPatternRewriter &rewriter) const override {
1018 Type resultType = typeConverter->convertType(op.getResult().getType());
1019
1020 rewriter.replaceOpWithNewOp<comb::ReplicateOp>(op, resultType,
1021 adaptor.getValue());
1022 return success();
1023 }
1024};
1025
1026struct ExtractOpConversion : public OpConversionPattern<ExtractOp> {
1027 using OpConversionPattern::OpConversionPattern;
1028
1029 LogicalResult
1030 matchAndRewrite(ExtractOp op, OpAdaptor adaptor,
1031 ConversionPatternRewriter &rewriter) const override {
1032 // TODO: return X if the domain is four-valued for out-of-bounds accesses
1033 // once we support four-valued lowering
1034 Type resultType = typeConverter->convertType(op.getResult().getType());
1035 Type inputType = adaptor.getInput().getType();
1036 int32_t low = adaptor.getLowBit();
1037
1038 if (isa<IntegerType>(inputType)) {
1039 int32_t inputWidth = inputType.getIntOrFloatBitWidth();
1040 int32_t resultWidth = hw::getBitWidth(resultType);
1041 int32_t high = low + resultWidth;
1042
1043 SmallVector<Value> toConcat;
1044 if (low < 0)
1045 toConcat.push_back(hw::ConstantOp::create(
1046 rewriter, op.getLoc(), APInt(std::min(-low, resultWidth), 0)));
1047
1048 if (low < inputWidth && high > 0) {
1049 int32_t lowIdx = std::max(low, 0);
1050 Value middle = rewriter.createOrFold<comb::ExtractOp>(
1051 op.getLoc(),
1052 rewriter.getIntegerType(
1053 std::min(resultWidth, std::min(high, inputWidth) - lowIdx)),
1054 adaptor.getInput(), lowIdx);
1055 toConcat.push_back(middle);
1056 }
1057
1058 int32_t diff = high - inputWidth;
1059 if (diff > 0) {
1060 Value val =
1061 hw::ConstantOp::create(rewriter, op.getLoc(), APInt(diff, 0));
1062 toConcat.push_back(val);
1063 }
1064
1065 Value concat =
1066 rewriter.createOrFold<comb::ConcatOp>(op.getLoc(), toConcat);
1067 rewriter.replaceOp(op, concat);
1068 return success();
1069 }
1070
1071 if (auto arrTy = dyn_cast<hw::ArrayType>(inputType)) {
1072 int32_t width = llvm::Log2_64_Ceil(arrTy.getNumElements());
1073 int32_t inputWidth = arrTy.getNumElements();
1074
1075 if (auto resArrTy = dyn_cast<hw::ArrayType>(resultType);
1076 resArrTy && resArrTy != arrTy.getElementType()) {
1077 int32_t elementWidth = hw::getBitWidth(arrTy.getElementType());
1078 if (elementWidth < 0)
1079 return failure();
1080
1081 int32_t high = low + resArrTy.getNumElements();
1082 int32_t resWidth = resArrTy.getNumElements();
1083
1084 SmallVector<Value> toConcat;
1085 if (low < 0) {
1086 Value val = hw::ConstantOp::create(
1087 rewriter, op.getLoc(),
1088 APInt(std::min((-low) * elementWidth, resWidth * elementWidth),
1089 0));
1090 Value res = rewriter.createOrFold<hw::BitcastOp>(
1091 op.getLoc(), hw::ArrayType::get(arrTy.getElementType(), -low),
1092 val);
1093 toConcat.push_back(res);
1094 }
1095
1096 if (low < inputWidth && high > 0) {
1097 int32_t lowIdx = std::max(0, low);
1098 Value lowIdxVal = hw::ConstantOp::create(
1099 rewriter, op.getLoc(), rewriter.getIntegerType(width), lowIdx);
1100 Value middle = rewriter.createOrFold<hw::ArraySliceOp>(
1101 op.getLoc(),
1102 hw::ArrayType::get(
1103 arrTy.getElementType(),
1104 std::min(resWidth, std::min(inputWidth, high) - lowIdx)),
1105 adaptor.getInput(), lowIdxVal);
1106 toConcat.push_back(middle);
1107 }
1108
1109 int32_t diff = high - inputWidth;
1110 if (diff > 0) {
1111 Value constZero = hw::ConstantOp::create(
1112 rewriter, op.getLoc(), APInt(diff * elementWidth, 0));
1113 Value val = hw::BitcastOp::create(
1114 rewriter, op.getLoc(),
1115 hw::ArrayType::get(arrTy.getElementType(), diff), constZero);
1116 toConcat.push_back(val);
1117 }
1118
1119 Value concat =
1120 rewriter.createOrFold<hw::ArrayConcatOp>(op.getLoc(), toConcat);
1121 rewriter.replaceOp(op, concat);
1122 return success();
1123 }
1124
1125 // Otherwise, it has to be the array's element type
1126 if (low < 0 || low >= inputWidth) {
1127 int32_t bw = hw::getBitWidth(resultType);
1128 if (bw < 0)
1129 return failure();
1130
1131 Value val = hw::ConstantOp::create(rewriter, op.getLoc(), APInt(bw, 0));
1132 Value bitcast =
1133 rewriter.createOrFold<hw::BitcastOp>(op.getLoc(), resultType, val);
1134 rewriter.replaceOp(op, bitcast);
1135 return success();
1136 }
1137
1138 Value idx = hw::ConstantOp::create(rewriter, op.getLoc(),
1139 rewriter.getIntegerType(width),
1140 adaptor.getLowBit());
1141 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(), idx);
1142 return success();
1143 }
1144
1145 return failure();
1146 }
1147};
1148
1149struct ExtractRefOpConversion : public OpConversionPattern<ExtractRefOp> {
1150 using OpConversionPattern::OpConversionPattern;
1151
1152 LogicalResult
1153 matchAndRewrite(ExtractRefOp op, OpAdaptor adaptor,
1154 ConversionPatternRewriter &rewriter) const override {
1155 // TODO: properly handle out-of-bounds accesses
1156 Type resultType = typeConverter->convertType(op.getResult().getType());
1157 Type inputType =
1158 cast<llhd::RefType>(adaptor.getInput().getType()).getNestedType();
1159
1160 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1161 int64_t width = hw::getBitWidth(inputType);
1162 if (width == -1)
1163 return failure();
1164
1165 Value lowBit = hw::ConstantOp::create(
1166 rewriter, op.getLoc(),
1167 rewriter.getIntegerType(llvm::Log2_64_Ceil(width)),
1168 adaptor.getLowBit());
1169 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
1170 op, resultType, adaptor.getInput(), lowBit);
1171 return success();
1172 }
1173
1174 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1175 Value lowBit = hw::ConstantOp::create(
1176 rewriter, op.getLoc(),
1177 rewriter.getIntegerType(llvm::Log2_64_Ceil(arrType.getNumElements())),
1178 adaptor.getLowBit());
1179
1180 // If the result type is not the same as the array's element type, then
1181 // it has to be a slice.
1182 if (arrType.getElementType() !=
1183 cast<llhd::RefType>(resultType).getNestedType()) {
1184 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
1185 op, resultType, adaptor.getInput(), lowBit);
1186 return success();
1187 }
1188
1189 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
1190 lowBit);
1191 return success();
1192 }
1193
1194 return failure();
1195 }
1196};
1197
1198struct DynExtractOpConversion : public OpConversionPattern<DynExtractOp> {
1199 using OpConversionPattern::OpConversionPattern;
1200
1201 LogicalResult
1202 matchAndRewrite(DynExtractOp op, OpAdaptor adaptor,
1203 ConversionPatternRewriter &rewriter) const override {
1204 Type resultType = typeConverter->convertType(op.getResult().getType());
1205 Type inputType = adaptor.getInput().getType();
1206
1207 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1208 Value amount = adjustIntegerWidth(rewriter, adaptor.getLowBit(),
1209 intType.getWidth(), op->getLoc());
1210 Value value = comb::ShrUOp::create(rewriter, op->getLoc(),
1211 adaptor.getInput(), amount);
1212
1213 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, value, 0);
1214 return success();
1215 }
1216
1217 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1218 unsigned idxWidth = llvm::Log2_64_Ceil(arrType.getNumElements());
1219 Value idx = adjustIntegerWidth(rewriter, adaptor.getLowBit(), idxWidth,
1220 op->getLoc());
1221
1222 bool isSingleElementExtract = arrType.getElementType() == resultType;
1223
1224 if (isSingleElementExtract)
1225 rewriter.replaceOpWithNewOp<hw::ArrayGetOp>(op, adaptor.getInput(),
1226 idx);
1227 else
1228 rewriter.replaceOpWithNewOp<hw::ArraySliceOp>(op, resultType,
1229 adaptor.getInput(), idx);
1230
1231 return success();
1232 }
1233
1234 return failure();
1235 }
1236};
1237
1238struct DynExtractRefOpConversion : public OpConversionPattern<DynExtractRefOp> {
1239 using OpConversionPattern::OpConversionPattern;
1240
1241 LogicalResult
1242 matchAndRewrite(DynExtractRefOp op, OpAdaptor adaptor,
1243 ConversionPatternRewriter &rewriter) const override {
1244 // TODO: properly handle out-of-bounds accesses
1245 Type resultType = typeConverter->convertType(op.getResult().getType());
1246 Type inputType =
1247 cast<llhd::RefType>(adaptor.getInput().getType()).getNestedType();
1248
1249 if (auto intType = dyn_cast<IntegerType>(inputType)) {
1250 int64_t width = hw::getBitWidth(inputType);
1251 if (width == -1)
1252 return failure();
1253
1254 Value amount =
1255 adjustIntegerWidth(rewriter, adaptor.getLowBit(),
1256 llvm::Log2_64_Ceil(width), op->getLoc());
1257 rewriter.replaceOpWithNewOp<llhd::SigExtractOp>(
1258 op, resultType, adaptor.getInput(), amount);
1259 return success();
1260 }
1261
1262 if (auto arrType = dyn_cast<hw::ArrayType>(inputType)) {
1263 Value idx = adjustIntegerWidth(
1264 rewriter, adaptor.getLowBit(),
1265 llvm::Log2_64_Ceil(arrType.getNumElements()), op->getLoc());
1266
1267 auto resultNestedType = cast<llhd::RefType>(resultType).getNestedType();
1268 bool isSingleElementExtract =
1269 arrType.getElementType() == resultNestedType;
1270
1271 if (isSingleElementExtract)
1272 rewriter.replaceOpWithNewOp<llhd::SigArrayGetOp>(op, adaptor.getInput(),
1273 idx);
1274 else
1275 rewriter.replaceOpWithNewOp<llhd::SigArraySliceOp>(
1276 op, resultType, adaptor.getInput(), idx);
1277
1278 return success();
1279 }
1280
1281 return failure();
1282 }
1283};
1284
1285struct ArrayCreateOpConversion : public OpConversionPattern<ArrayCreateOp> {
1286 using OpConversionPattern::OpConversionPattern;
1287
1288 LogicalResult
1289 matchAndRewrite(ArrayCreateOp op, OpAdaptor adaptor,
1290 ConversionPatternRewriter &rewriter) const override {
1291 Type resultType = typeConverter->convertType(op.getResult().getType());
1292 rewriter.replaceOpWithNewOp<hw::ArrayCreateOp>(op, resultType,
1293 adaptor.getElements());
1294 return success();
1295 }
1296};
1297
1298struct StructCreateOpConversion : public OpConversionPattern<StructCreateOp> {
1299 using OpConversionPattern::OpConversionPattern;
1300
1301 LogicalResult
1302 matchAndRewrite(StructCreateOp op, OpAdaptor adaptor,
1303 ConversionPatternRewriter &rewriter) const override {
1304 Type resultType = typeConverter->convertType(op.getResult().getType());
1305 rewriter.replaceOpWithNewOp<hw::StructCreateOp>(op, resultType,
1306 adaptor.getFields());
1307 return success();
1308 }
1309};
1310
1311struct StructExtractOpConversion : public OpConversionPattern<StructExtractOp> {
1312 using OpConversionPattern::OpConversionPattern;
1313
1314 LogicalResult
1315 matchAndRewrite(StructExtractOp op, OpAdaptor adaptor,
1316 ConversionPatternRewriter &rewriter) const override {
1317 rewriter.replaceOpWithNewOp<hw::StructExtractOp>(
1318 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1319 return success();
1320 }
1321};
1322
1323struct StructExtractRefOpConversion
1324 : public OpConversionPattern<StructExtractRefOp> {
1325 using OpConversionPattern::OpConversionPattern;
1326
1327 LogicalResult
1328 matchAndRewrite(StructExtractRefOp op, OpAdaptor adaptor,
1329 ConversionPatternRewriter &rewriter) const override {
1330 rewriter.replaceOpWithNewOp<llhd::SigStructExtractOp>(
1331 op, adaptor.getInput(), adaptor.getFieldNameAttr());
1332 return success();
1333 }
1334};
1335
1336struct ReduceAndOpConversion : public OpConversionPattern<ReduceAndOp> {
1337 using OpConversionPattern::OpConversionPattern;
1338 LogicalResult
1339 matchAndRewrite(ReduceAndOp op, OpAdaptor adaptor,
1340 ConversionPatternRewriter &rewriter) const override {
1341 Type resultType = typeConverter->convertType(op.getInput().getType());
1342 Value max = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, -1);
1343
1344 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::eq,
1345 adaptor.getInput(), max);
1346 return success();
1347 }
1348};
1349
1350struct ReduceOrOpConversion : public OpConversionPattern<ReduceOrOp> {
1351 using OpConversionPattern::OpConversionPattern;
1352 LogicalResult
1353 matchAndRewrite(ReduceOrOp op, OpAdaptor adaptor,
1354 ConversionPatternRewriter &rewriter) const override {
1355 Type resultType = typeConverter->convertType(op.getInput().getType());
1356 Value zero = hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1357
1358 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1359 adaptor.getInput(), zero);
1360 return success();
1361 }
1362};
1363
1364struct ReduceXorOpConversion : public OpConversionPattern<ReduceXorOp> {
1365 using OpConversionPattern::OpConversionPattern;
1366 LogicalResult
1367 matchAndRewrite(ReduceXorOp op, OpAdaptor adaptor,
1368 ConversionPatternRewriter &rewriter) const override {
1369
1370 rewriter.replaceOpWithNewOp<comb::ParityOp>(op, adaptor.getInput());
1371 return success();
1372 }
1373};
1374
1375struct BoolCastOpConversion : public OpConversionPattern<BoolCastOp> {
1376 using OpConversionPattern::OpConversionPattern;
1377 LogicalResult
1378 matchAndRewrite(BoolCastOp op, OpAdaptor adaptor,
1379 ConversionPatternRewriter &rewriter) const override {
1380 Type resultType = typeConverter->convertType(op.getInput().getType());
1381 if (isa_and_nonnull<IntegerType>(resultType)) {
1382 Value zero =
1383 hw::ConstantOp::create(rewriter, op->getLoc(), resultType, 0);
1384 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, comb::ICmpPredicate::ne,
1385 adaptor.getInput(), zero);
1386 return success();
1387 }
1388 return failure();
1389 }
1390};
1391
1392struct NotOpConversion : public OpConversionPattern<NotOp> {
1393 using OpConversionPattern::OpConversionPattern;
1394 LogicalResult
1395 matchAndRewrite(NotOp op, OpAdaptor adaptor,
1396 ConversionPatternRewriter &rewriter) const override {
1397 Type resultType =
1398 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1399 Value max = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, -1);
1400
1401 rewriter.replaceOpWithNewOp<comb::XorOp>(op, adaptor.getInput(), max);
1402 return success();
1403 }
1404};
1405
1406struct NegOpConversion : public OpConversionPattern<NegOp> {
1407 using OpConversionPattern::OpConversionPattern;
1408 LogicalResult
1409 matchAndRewrite(NegOp op, OpAdaptor adaptor,
1410 ConversionPatternRewriter &rewriter) const override {
1411 Type resultType =
1412 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1413 Value zero = hw::ConstantOp::create(rewriter, op.getLoc(), resultType, 0);
1414
1415 rewriter.replaceOpWithNewOp<comb::SubOp>(op, zero, adaptor.getInput());
1416 return success();
1417 }
1418};
1419
1420template <typename SourceOp, typename TargetOp>
1421struct BinaryOpConversion : public OpConversionPattern<SourceOp> {
1423 using OpAdaptor = typename SourceOp::Adaptor;
1424
1425 LogicalResult
1426 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1427 ConversionPatternRewriter &rewriter) const override {
1428 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1429 adaptor.getRhs(), false);
1430 return success();
1431 }
1432};
1433
1434template <typename SourceOp, typename TargetOp>
1435struct BinaryRealOpConversion : public OpConversionPattern<SourceOp> {
1437 using OpAdaptor = typename SourceOp::Adaptor;
1438
1439 LogicalResult
1440 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1441 ConversionPatternRewriter &rewriter) const override {
1442 rewriter.replaceOpWithNewOp<TargetOp>(op, adaptor.getLhs(),
1443 adaptor.getRhs());
1444 return success();
1445 }
1446};
1447
1448template <typename SourceOp, ICmpPredicate pred>
1449struct ICmpOpConversion : public OpConversionPattern<SourceOp> {
1451 using OpAdaptor = typename SourceOp::Adaptor;
1452
1453 LogicalResult
1454 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1455 ConversionPatternRewriter &rewriter) const override {
1456 Type resultType =
1457 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1458
1459 rewriter.replaceOpWithNewOp<comb::ICmpOp>(
1460 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1461 return success();
1462 }
1463};
1464
1465template <typename SourceOp, arith::CmpFPredicate pred>
1466struct FCmpOpConversion : public OpConversionPattern<SourceOp> {
1468 using OpAdaptor = typename SourceOp::Adaptor;
1469
1470 LogicalResult
1471 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1472 ConversionPatternRewriter &rewriter) const override {
1473 Type resultType =
1474 ConversionPattern::typeConverter->convertType(op.getResult().getType());
1475
1476 rewriter.replaceOpWithNewOp<arith::CmpFOp>(
1477 op, resultType, pred, adaptor.getLhs(), adaptor.getRhs());
1478 return success();
1479 }
1480};
1481
1482template <typename SourceOp, bool withoutX>
1483struct CaseXZEqOpConversion : public OpConversionPattern<SourceOp> {
1485 using OpAdaptor = typename SourceOp::Adaptor;
1486
1487 LogicalResult
1488 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1489 ConversionPatternRewriter &rewriter) const override {
1490 // Check each operand if it is a known constant and extract the X and/or Z
1491 // bits to be ignored.
1492 // TODO: Once the core dialects support four-valued integers, we will have
1493 // to create ops that extract X and Z bits from the operands, since we also
1494 // have to do the right casez/casex comparison on non-constant inputs.
1495 unsigned bitWidth = op.getLhs().getType().getWidth();
1496 auto ignoredBits = APInt::getZero(bitWidth);
1497 auto detectIgnoredBits = [&](Value value) {
1498 auto constOp = value.getDefiningOp<ConstantOp>();
1499 if (!constOp)
1500 return;
1501 auto constValue = constOp.getValue();
1502 if (withoutX)
1503 ignoredBits |= constValue.getZBits();
1504 else
1505 ignoredBits |= constValue.getUnknownBits();
1506 };
1507 detectIgnoredBits(op.getLhs());
1508 detectIgnoredBits(op.getRhs());
1509
1510 // If we have detected any bits to be ignored, mask them in the operands for
1511 // the comparison.
1512 Value lhs = adaptor.getLhs();
1513 Value rhs = adaptor.getRhs();
1514 if (!ignoredBits.isZero()) {
1515 ignoredBits.flipAllBits();
1516 auto maskOp = hw::ConstantOp::create(rewriter, op.getLoc(), ignoredBits);
1517 lhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), lhs, maskOp);
1518 rhs = rewriter.createOrFold<comb::AndOp>(op.getLoc(), rhs, maskOp);
1519 }
1520
1521 rewriter.replaceOpWithNewOp<comb::ICmpOp>(op, ICmpPredicate::ceq, lhs, rhs);
1522 return success();
1523 }
1524};
1525
1526//===----------------------------------------------------------------------===//
1527// Conversions
1528//===----------------------------------------------------------------------===//
1529
1530struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
1531 using OpConversionPattern::OpConversionPattern;
1532
1533 LogicalResult
1534 matchAndRewrite(ConversionOp op, OpAdaptor adaptor,
1535 ConversionPatternRewriter &rewriter) const override {
1536 Location loc = op.getLoc();
1537 Type resultType = typeConverter->convertType(op.getResult().getType());
1538 if (!resultType) {
1539 op.emitError("conversion result type is not currently supported");
1540 return failure();
1541 }
1542 int64_t inputBw = hw::getBitWidth(adaptor.getInput().getType());
1543 int64_t resultBw = hw::getBitWidth(resultType);
1544 if (inputBw == -1 || resultBw == -1)
1545 return failure();
1546
1547 Value input = rewriter.createOrFold<hw::BitcastOp>(
1548 loc, rewriter.getIntegerType(inputBw), adaptor.getInput());
1549 Value amount = adjustIntegerWidth(rewriter, input, resultBw, loc);
1550
1551 Value result =
1552 rewriter.createOrFold<hw::BitcastOp>(loc, resultType, amount);
1553 rewriter.replaceOp(op, result);
1554 return success();
1555 }
1556};
1557
1558template <typename SourceOp>
1559struct BitcastConversion : public OpConversionPattern<SourceOp> {
1561 using OpAdaptor = typename SourceOp::Adaptor;
1562 using ConversionPattern::typeConverter;
1563
1564 LogicalResult
1565 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1566 ConversionPatternRewriter &rewriter) const override {
1567 auto type = typeConverter->convertType(op.getResult().getType());
1568 if (type == adaptor.getInput().getType())
1569 rewriter.replaceOp(op, adaptor.getInput());
1570 else
1571 rewriter.replaceOpWithNewOp<hw::BitcastOp>(op, type, adaptor.getInput());
1572 return success();
1573 }
1574};
1575
1576struct TruncOpConversion : public OpConversionPattern<TruncOp> {
1577 using OpConversionPattern::OpConversionPattern;
1578
1579 LogicalResult
1580 matchAndRewrite(TruncOp op, OpAdaptor adaptor,
1581 ConversionPatternRewriter &rewriter) const override {
1582 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, adaptor.getInput(), 0,
1583 op.getType().getWidth());
1584 return success();
1585 }
1586};
1587
1588struct ZExtOpConversion : public OpConversionPattern<ZExtOp> {
1589 using OpConversionPattern::OpConversionPattern;
1590
1591 LogicalResult
1592 matchAndRewrite(ZExtOp op, OpAdaptor adaptor,
1593 ConversionPatternRewriter &rewriter) const override {
1594 auto targetWidth = op.getType().getWidth();
1595 auto inputWidth = op.getInput().getType().getWidth();
1596
1597 auto zeroExt = hw::ConstantOp::create(
1598 rewriter, op.getLoc(),
1599 rewriter.getIntegerType(targetWidth - inputWidth), 0);
1600
1601 rewriter.replaceOpWithNewOp<comb::ConcatOp>(
1602 op, ValueRange{zeroExt, adaptor.getInput()});
1603 return success();
1604 }
1605};
1606
1607struct SExtOpConversion : public OpConversionPattern<SExtOp> {
1608 using OpConversionPattern::OpConversionPattern;
1609
1610 LogicalResult
1611 matchAndRewrite(SExtOp op, OpAdaptor adaptor,
1612 ConversionPatternRewriter &rewriter) const override {
1613 auto type = typeConverter->convertType(op.getType());
1614 auto value =
1615 comb::createOrFoldSExt(op.getLoc(), adaptor.getInput(), type, rewriter);
1616 rewriter.replaceOp(op, value);
1617 return success();
1618 }
1619};
1620
1621struct SIntToRealOpConversion : public OpConversionPattern<SIntToRealOp> {
1622 using OpConversionPattern::OpConversionPattern;
1623
1624 LogicalResult
1625 matchAndRewrite(SIntToRealOp op, OpAdaptor adaptor,
1626 ConversionPatternRewriter &rewriter) const override {
1627 rewriter.replaceOpWithNewOp<arith::SIToFPOp>(
1628 op, typeConverter->convertType(op.getType()), adaptor.getInput());
1629 return success();
1630 }
1631};
1632
1633struct UIntToRealOpConversion : public OpConversionPattern<UIntToRealOp> {
1634 using OpConversionPattern::OpConversionPattern;
1635
1636 LogicalResult
1637 matchAndRewrite(UIntToRealOp op, OpAdaptor adaptor,
1638 ConversionPatternRewriter &rewriter) const override {
1639 rewriter.replaceOpWithNewOp<arith::UIToFPOp>(
1640 op, typeConverter->convertType(op.getType()), adaptor.getInput());
1641 return success();
1642 }
1643};
1644
1645struct IntToStringOpConversion : public OpConversionPattern<IntToStringOp> {
1646 using OpConversionPattern::OpConversionPattern;
1647
1648 LogicalResult
1649 matchAndRewrite(IntToStringOp op, OpAdaptor adaptor,
1650 ConversionPatternRewriter &rewriter) const override {
1651 rewriter.replaceOpWithNewOp<sim::IntToStringOp>(op, adaptor.getInput());
1652 return success();
1653 }
1654};
1655
1656struct RealToIntOpConversion : public OpConversionPattern<RealToIntOp> {
1657 using OpConversionPattern::OpConversionPattern;
1658
1659 LogicalResult
1660 matchAndRewrite(RealToIntOp op, OpAdaptor adaptor,
1661 ConversionPatternRewriter &rewriter) const override {
1662 rewriter.replaceOpWithNewOp<arith::FPToSIOp>(
1663 op, typeConverter->convertType(op.getType()), adaptor.getInput());
1664 return success();
1665 }
1666};
1667
1668//===----------------------------------------------------------------------===//
1669// Statement Conversion
1670//===----------------------------------------------------------------------===//
1671
1672struct HWInstanceOpConversion : public OpConversionPattern<hw::InstanceOp> {
1673 using OpConversionPattern::OpConversionPattern;
1674
1675 LogicalResult
1676 matchAndRewrite(hw::InstanceOp op, OpAdaptor adaptor,
1677 ConversionPatternRewriter &rewriter) const override {
1678 SmallVector<Type> convResTypes;
1679 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1680 return failure();
1681
1682 rewriter.replaceOpWithNewOp<hw::InstanceOp>(
1683 op, convResTypes, op.getInstanceName(), op.getModuleName(),
1684 adaptor.getOperands(), op.getArgNames(),
1685 op.getResultNames(), /*Parameter*/
1686 rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr);
1687
1688 return success();
1689 }
1690};
1691
1692struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
1693 using OpConversionPattern::OpConversionPattern;
1694
1695 LogicalResult
1696 matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
1697 ConversionPatternRewriter &rewriter) const override {
1698 rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
1699 return success();
1700 }
1701};
1702
1703struct CallOpConversion : public OpConversionPattern<func::CallOp> {
1704 using OpConversionPattern::OpConversionPattern;
1705
1706 LogicalResult
1707 matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
1708 ConversionPatternRewriter &rewriter) const override {
1709 SmallVector<Type> convResTypes;
1710 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1711 return failure();
1712 rewriter.replaceOpWithNewOp<func::CallOp>(
1713 op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
1714 return success();
1715 }
1716};
1717
1718struct UnrealizedConversionCastConversion
1719 : public OpConversionPattern<UnrealizedConversionCastOp> {
1720 using OpConversionPattern::OpConversionPattern;
1721
1722 LogicalResult
1723 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
1724 ConversionPatternRewriter &rewriter) const override {
1725 SmallVector<Type> convResTypes;
1726 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1727 return failure();
1728
1729 // Drop the cast if the operand and result types agree after type
1730 // conversion.
1731 if (convResTypes == adaptor.getOperands().getTypes()) {
1732 rewriter.replaceOp(op, adaptor.getOperands());
1733 return success();
1734 }
1735
1736 rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
1737 op, convResTypes, adaptor.getOperands());
1738 return success();
1739 }
1740};
1741
1742struct ShlOpConversion : public OpConversionPattern<ShlOp> {
1743 using OpConversionPattern::OpConversionPattern;
1744
1745 LogicalResult
1746 matchAndRewrite(ShlOp op, OpAdaptor adaptor,
1747 ConversionPatternRewriter &rewriter) const override {
1748 Type resultType = typeConverter->convertType(op.getResult().getType());
1749
1750 // Comb shift operations require the same bit-width for value and amount
1751 Value amount =
1752 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1753 resultType.getIntOrFloatBitWidth(), op->getLoc());
1754 rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
1755 amount, false);
1756 return success();
1757 }
1758};
1759
1760struct ShrOpConversion : public OpConversionPattern<ShrOp> {
1761 using OpConversionPattern::OpConversionPattern;
1762
1763 LogicalResult
1764 matchAndRewrite(ShrOp op, OpAdaptor adaptor,
1765 ConversionPatternRewriter &rewriter) const override {
1766 Type resultType = typeConverter->convertType(op.getResult().getType());
1767
1768 // Comb shift operations require the same bit-width for value and amount
1769 Value amount =
1770 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1771 resultType.getIntOrFloatBitWidth(), op->getLoc());
1772 rewriter.replaceOpWithNewOp<comb::ShrUOp>(
1773 op, resultType, adaptor.getValue(), amount, false);
1774 return success();
1775 }
1776};
1777
1778struct PowUOpConversion : public OpConversionPattern<PowUOp> {
1779 using OpConversionPattern::OpConversionPattern;
1780
1781 LogicalResult
1782 matchAndRewrite(PowUOp op, OpAdaptor adaptor,
1783 ConversionPatternRewriter &rewriter) const override {
1784 Type resultType = typeConverter->convertType(op.getResult().getType());
1785
1786 Location loc = op->getLoc();
1787
1788 Value zeroVal = hw::ConstantOp::create(rewriter, loc, APInt(1, 0));
1789 // zero extend both LHS & RHS to ensure the unsigned integers are
1790 // interpreted correctly when calculating power
1791 auto lhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getLhs());
1792 auto rhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getRhs());
1793
1794 // lower the exponentiation via MLIR's math dialect
1795 auto pow = mlir::math::IPowIOp::create(rewriter, loc, lhs, rhs);
1796
1797 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, pow, 0);
1798 return success();
1799 }
1800};
1801
1802struct PowSOpConversion : public OpConversionPattern<PowSOp> {
1803 using OpConversionPattern::OpConversionPattern;
1804
1805 LogicalResult
1806 matchAndRewrite(PowSOp op, OpAdaptor adaptor,
1807 ConversionPatternRewriter &rewriter) const override {
1808 Type resultType = typeConverter->convertType(op.getResult().getType());
1809
1810 // utilize MLIR math dialect's math.ipowi to handle the exponentiation of
1811 // expression
1812 rewriter.replaceOpWithNewOp<mlir::math::IPowIOp>(
1813 op, resultType, adaptor.getLhs(), adaptor.getRhs());
1814 return success();
1815 }
1816};
1817
1818struct AShrOpConversion : public OpConversionPattern<AShrOp> {
1819 using OpConversionPattern::OpConversionPattern;
1820
1821 LogicalResult
1822 matchAndRewrite(AShrOp op, OpAdaptor adaptor,
1823 ConversionPatternRewriter &rewriter) const override {
1824 Type resultType = typeConverter->convertType(op.getResult().getType());
1825
1826 // Comb shift operations require the same bit-width for value and amount
1827 Value amount =
1828 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1829 resultType.getIntOrFloatBitWidth(), op->getLoc());
1830 rewriter.replaceOpWithNewOp<comb::ShrSOp>(
1831 op, resultType, adaptor.getValue(), amount, false);
1832 return success();
1833 }
1834};
1835
1836struct ReadOpConversion : public OpConversionPattern<ReadOp> {
1837 using OpConversionPattern::OpConversionPattern;
1838
1839 LogicalResult
1840 matchAndRewrite(ReadOp op, OpAdaptor adaptor,
1841 ConversionPatternRewriter &rewriter) const override {
1842 rewriter.replaceOpWithNewOp<llhd::ProbeOp>(op, adaptor.getInput());
1843 return success();
1844 }
1845};
1846
1847struct AssignedVariableOpConversion
1848 : public OpConversionPattern<AssignedVariableOp> {
1849 using OpConversionPattern::OpConversionPattern;
1850
1851 LogicalResult
1852 matchAndRewrite(AssignedVariableOp op, OpAdaptor adaptor,
1853 ConversionPatternRewriter &rewriter) const override {
1854 rewriter.replaceOpWithNewOp<hw::WireOp>(op, adaptor.getInput(),
1855 adaptor.getNameAttr());
1856 return success();
1857 }
1858};
1859
1860template <typename OpTy>
1861struct AssignOpConversion : public OpConversionPattern<OpTy> {
1863 using OpAdaptor = typename OpTy::Adaptor;
1864
1865 LogicalResult
1866 matchAndRewrite(OpTy op, OpAdaptor adaptor,
1867 ConversionPatternRewriter &rewriter) const override {
1868 // Determine the delay for the assignment.
1869 Value delay;
1870 if constexpr (std::is_same_v<OpTy, ContinuousAssignOp> ||
1871 std::is_same_v<OpTy, BlockingAssignOp>) {
1872 // Blocking and continuous assignments get a 0ns 0d 1e delay.
1873 delay = llhd::ConstantTimeOp::create(
1874 rewriter, op->getLoc(),
1875 llhd::TimeAttr::get(op->getContext(), 0U, "ns", 0, 1));
1876 } else if constexpr (std::is_same_v<OpTy, NonBlockingAssignOp>) {
1877 // Non-blocking assignments get a 0ns 1d 0e delay.
1878 delay = llhd::ConstantTimeOp::create(
1879 rewriter, op->getLoc(),
1880 llhd::TimeAttr::get(op->getContext(), 0U, "ns", 1, 0));
1881 } else {
1882 // Delayed assignments have a delay operand.
1883 delay = adaptor.getDelay();
1884 }
1885
1886 rewriter.replaceOpWithNewOp<llhd::DriveOp>(
1887 op, adaptor.getDst(), adaptor.getSrc(), delay, Value{});
1888 return success();
1889 }
1890};
1891
1892struct ConditionalOpConversion : public OpConversionPattern<ConditionalOp> {
1893 using OpConversionPattern::OpConversionPattern;
1894
1895 LogicalResult
1896 matchAndRewrite(ConditionalOp op, OpAdaptor adaptor,
1897 ConversionPatternRewriter &rewriter) const override {
1898 // TODO: This lowering is only correct if the condition is two-valued. If
1899 // the condition is X or Z, both branches of the conditional must be
1900 // evaluated and merged with the appropriate lookup table. See documentation
1901 // for `ConditionalOp`.
1902 auto type = typeConverter->convertType(op.getType());
1903
1904 auto hasNoWriteEffect = [](Region &region) {
1905 auto result = region.walk([](Operation *operation) {
1906 if (auto memOp = dyn_cast<MemoryEffectOpInterface>(operation))
1907 if (!memOp.hasEffect<MemoryEffects::Write>() &&
1908 !memOp.hasEffect<MemoryEffects::Free>())
1909 return WalkResult::advance();
1910
1911 if (operation->hasTrait<OpTrait::HasRecursiveMemoryEffects>())
1912 return WalkResult::advance();
1913
1914 return WalkResult::interrupt();
1915 });
1916 return !result.wasInterrupted();
1917 };
1918
1919 if (hasNoWriteEffect(op.getTrueRegion()) &&
1920 hasNoWriteEffect(op.getFalseRegion())) {
1921 Operation *trueTerm = op.getTrueRegion().front().getTerminator();
1922 Operation *falseTerm = op.getFalseRegion().front().getTerminator();
1923
1924 rewriter.inlineBlockBefore(&op.getTrueRegion().front(), op);
1925 rewriter.inlineBlockBefore(&op.getFalseRegion().front(), op);
1926
1927 Value convTrueVal = typeConverter->materializeTargetConversion(
1928 rewriter, op.getLoc(), type, trueTerm->getOperand(0));
1929 Value convFalseVal = typeConverter->materializeTargetConversion(
1930 rewriter, op.getLoc(), type, falseTerm->getOperand(0));
1931
1932 rewriter.eraseOp(trueTerm);
1933 rewriter.eraseOp(falseTerm);
1934
1935 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, adaptor.getCondition(),
1936 convTrueVal, convFalseVal);
1937 return success();
1938 }
1939
1940 auto ifOp =
1941 scf::IfOp::create(rewriter, op.getLoc(), type, adaptor.getCondition());
1942 rewriter.inlineRegionBefore(op.getTrueRegion(), ifOp.getThenRegion(),
1943 ifOp.getThenRegion().end());
1944 rewriter.inlineRegionBefore(op.getFalseRegion(), ifOp.getElseRegion(),
1945 ifOp.getElseRegion().end());
1946 rewriter.replaceOp(op, ifOp);
1947 return success();
1948 }
1949};
1950
1951struct YieldOpConversion : public OpConversionPattern<YieldOp> {
1952 using OpConversionPattern::OpConversionPattern;
1953
1954 LogicalResult
1955 matchAndRewrite(YieldOp op, OpAdaptor adaptor,
1956 ConversionPatternRewriter &rewriter) const override {
1957 rewriter.replaceOpWithNewOp<scf::YieldOp>(op, adaptor.getResult());
1958 return success();
1959 }
1960};
1961
1962template <typename SourceOp>
1963struct InPlaceOpConversion : public OpConversionPattern<SourceOp> {
1965 using OpAdaptor = typename SourceOp::Adaptor;
1966
1967 LogicalResult
1968 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1969 ConversionPatternRewriter &rewriter) const override {
1970 rewriter.modifyOpInPlace(op,
1971 [&]() { op->setOperands(adaptor.getOperands()); });
1972 return success();
1973 }
1974};
1975
1976template <typename MooreOpTy, typename VerifOpTy>
1977struct AssertLikeOpConversion : public OpConversionPattern<MooreOpTy> {
1979 using OpAdaptor = typename MooreOpTy::Adaptor;
1980
1981 LogicalResult
1982 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
1983 ConversionPatternRewriter &rewriter) const override {
1984 StringAttr label =
1985 op.getLabel().has_value()
1986 ? StringAttr::get(op->getContext(), op.getLabel().value())
1987 : StringAttr::get(op->getContext());
1988 rewriter.replaceOpWithNewOp<VerifOpTy>(op, adaptor.getCond(), mlir::Value(),
1989 label);
1990 return success();
1991 }
1992};
1993
1994//===----------------------------------------------------------------------===//
1995// Format String Conversion
1996//===----------------------------------------------------------------------===//
1997
1998struct FormatLiteralOpConversion : public OpConversionPattern<FormatLiteralOp> {
1999 using OpConversionPattern::OpConversionPattern;
2000
2001 LogicalResult
2002 matchAndRewrite(FormatLiteralOp op, OpAdaptor adaptor,
2003 ConversionPatternRewriter &rewriter) const override {
2004 rewriter.replaceOpWithNewOp<sim::FormatLiteralOp>(op, adaptor.getLiteral());
2005 return success();
2006 }
2007};
2008
2009struct FormatConcatOpConversion : public OpConversionPattern<FormatConcatOp> {
2010 using OpConversionPattern::OpConversionPattern;
2011
2012 LogicalResult
2013 matchAndRewrite(FormatConcatOp op, OpAdaptor adaptor,
2014 ConversionPatternRewriter &rewriter) const override {
2015 rewriter.replaceOpWithNewOp<sim::FormatStringConcatOp>(op,
2016 adaptor.getInputs());
2017 return success();
2018 }
2019};
2020
2021struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
2022 using OpConversionPattern::OpConversionPattern;
2023
2024 LogicalResult
2025 matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
2026 ConversionPatternRewriter &rewriter) const override {
2027
2028 char padChar = adaptor.getPadding() == IntPadding::Space ? 32 : 48;
2029 IntegerAttr padCharAttr = rewriter.getI8IntegerAttr(padChar);
2030 auto widthAttr = adaptor.getSpecifierWidthAttr();
2031
2032 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2033 BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2034
2035 switch (op.getFormat()) {
2036 case IntFormat::Decimal:
2037 rewriter.replaceOpWithNewOp<sim::FormatDecOp>(
2038 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr,
2039 adaptor.getIsSignedAttr());
2040 return success();
2041 case IntFormat::Binary:
2042 rewriter.replaceOpWithNewOp<sim::FormatBinOp>(
2043 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2044 return success();
2045 case IntFormat::Octal:
2046 rewriter.replaceOpWithNewOp<sim::FormatOctOp>(
2047 op, adaptor.getValue(), isLeftAlignedAttr, padCharAttr, widthAttr);
2048 return success();
2049 case IntFormat::HexLower:
2050 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2051 op, adaptor.getValue(), rewriter.getBoolAttr(false),
2052 isLeftAlignedAttr, padCharAttr, widthAttr);
2053 return success();
2054 case IntFormat::HexUpper:
2055 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(
2056 op, adaptor.getValue(), rewriter.getBoolAttr(true), isLeftAlignedAttr,
2057 padCharAttr, widthAttr);
2058 return success();
2059 }
2060 return rewriter.notifyMatchFailure(op, "unsupported int format");
2061 }
2062};
2063
2064struct FormatRealOpConversion : public OpConversionPattern<FormatRealOp> {
2065 using OpConversionPattern::OpConversionPattern;
2066
2067 LogicalResult
2068 matchAndRewrite(FormatRealOp op, OpAdaptor adaptor,
2069 ConversionPatternRewriter &rewriter) const override {
2070 auto fracDigitsAttr = adaptor.getFracDigitsAttr();
2071
2072 auto fieldWidthAttr = adaptor.getFieldWidthAttr();
2073 bool isLeftAligned = adaptor.getAlignment() == IntAlign::Left;
2074 mlir::BoolAttr isLeftAlignedAttr = rewriter.getBoolAttr(isLeftAligned);
2075
2076 switch (op.getFormat()) {
2077 case RealFormat::General:
2078 rewriter.replaceOpWithNewOp<sim::FormatGeneralOp>(
2079 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2080 fracDigitsAttr);
2081 return success();
2082 case RealFormat::Float:
2083 rewriter.replaceOpWithNewOp<sim::FormatFloatOp>(
2084 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2085 fracDigitsAttr);
2086 return success();
2087 case RealFormat::Exponential:
2088 rewriter.replaceOpWithNewOp<sim::FormatScientificOp>(
2089 op, adaptor.getValue(), isLeftAlignedAttr, fieldWidthAttr,
2090 fracDigitsAttr);
2091 return success();
2092 }
2093 }
2094};
2095
2096struct StringLenOpConversion : public OpConversionPattern<StringLenOp> {
2097 using OpConversionPattern::OpConversionPattern;
2098
2099 LogicalResult
2100 matchAndRewrite(StringLenOp op, OpAdaptor adaptor,
2101 ConversionPatternRewriter &rewriter) const override {
2102 rewriter.replaceOpWithNewOp<sim::StringLengthOp>(op, adaptor.getStr());
2103 return success();
2104 }
2105};
2106
2107struct StringConcatOpConversion : public OpConversionPattern<StringConcatOp> {
2108 using OpConversionPattern::OpConversionPattern;
2109
2110 LogicalResult
2111 matchAndRewrite(StringConcatOp op, OpAdaptor adaptor,
2112 ConversionPatternRewriter &rewriter) const override {
2113 rewriter.replaceOpWithNewOp<sim::StringConcatOp>(op, adaptor.getInputs());
2114 return success();
2115 }
2116};
2117
2118struct DisplayBIOpConversion : public OpConversionPattern<DisplayBIOp> {
2119 using OpConversionPattern::OpConversionPattern;
2120
2121 LogicalResult
2122 matchAndRewrite(DisplayBIOp op, OpAdaptor adaptor,
2123 ConversionPatternRewriter &rewriter) const override {
2124 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
2125 op, adaptor.getMessage());
2126 return success();
2127 }
2128};
2129
2130} // namespace
2131
2132//===----------------------------------------------------------------------===//
2133// Simulation Control Conversion
2134//===----------------------------------------------------------------------===//
2135
2136// moore.builtin.stop -> sim.pause
2137static LogicalResult convert(StopBIOp op, StopBIOp::Adaptor adaptor,
2138 ConversionPatternRewriter &rewriter) {
2139 rewriter.replaceOpWithNewOp<sim::PauseOp>(op, /*verbose=*/false);
2140 return success();
2141}
2142
2143// moore.builtin.finish -> sim.terminate
2144static LogicalResult convert(FinishBIOp op, FinishBIOp::Adaptor adaptor,
2145 ConversionPatternRewriter &rewriter) {
2146 rewriter.replaceOpWithNewOp<sim::TerminateOp>(op, op.getExitCode() == 0,
2147 /*verbose=*/false);
2148 return success();
2149}
2150
2151// moore.builtin.severity -> sim.proc.print
2152static LogicalResult convert(SeverityBIOp op, SeverityBIOp::Adaptor adaptor,
2153 ConversionPatternRewriter &rewriter) {
2154
2155 std::string severityString;
2156
2157 switch (op.getSeverity()) {
2158 case (Severity::Fatal):
2159 severityString = "Fatal: ";
2160 break;
2161 case (Severity::Error):
2162 severityString = "Error: ";
2163 break;
2164 case (Severity::Warning):
2165 severityString = "Warning: ";
2166 break;
2167 default:
2168 return failure();
2169 }
2170
2171 auto prefix =
2172 sim::FormatLiteralOp::create(rewriter, op.getLoc(), severityString);
2173 auto message = sim::FormatStringConcatOp::create(
2174 rewriter, op.getLoc(), ValueRange{prefix, adaptor.getMessage()});
2175 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(op, message);
2176 return success();
2177}
2178
2179// moore.builtin.finish_message
2180static LogicalResult convert(FinishMessageBIOp op,
2181 FinishMessageBIOp::Adaptor adaptor,
2182 ConversionPatternRewriter &rewriter) {
2183 // We don't support printing termination/pause messages yet.
2184 rewriter.eraseOp(op);
2185 return success();
2186}
2187
2188//===----------------------------------------------------------------------===//
2189// Timing Control Conversion
2190//===----------------------------------------------------------------------===//
2191
2192// moore.builtin.time
2193static LogicalResult convert(TimeBIOp op, TimeBIOp::Adaptor adaptor,
2194 ConversionPatternRewriter &rewriter) {
2195 rewriter.replaceOpWithNewOp<llhd::CurrentTimeOp>(op);
2196 return success();
2197}
2198
2199// moore.logic_to_time
2200static LogicalResult convert(LogicToTimeOp op, LogicToTimeOp::Adaptor adaptor,
2201 ConversionPatternRewriter &rewriter) {
2202 rewriter.replaceOpWithNewOp<llhd::IntToTimeOp>(op, adaptor.getInput());
2203 return success();
2204}
2205
2206// moore.time_to_logic
2207static LogicalResult convert(TimeToLogicOp op, TimeToLogicOp::Adaptor adaptor,
2208 ConversionPatternRewriter &rewriter) {
2209 rewriter.replaceOpWithNewOp<llhd::TimeToIntOp>(op, adaptor.getInput());
2210 return success();
2211}
2212
2213//===----------------------------------------------------------------------===//
2214// Conversion Infrastructure
2215//===----------------------------------------------------------------------===//
2216
2217static void populateLegality(ConversionTarget &target,
2218 const TypeConverter &converter) {
2219 target.addIllegalDialect<MooreDialect>();
2220 target.addLegalDialect<comb::CombDialect>();
2221 target.addLegalDialect<hw::HWDialect>();
2222 target.addLegalDialect<seq::SeqDialect>();
2223 target.addLegalDialect<llhd::LLHDDialect>();
2224 target.addLegalDialect<ltl::LTLDialect>();
2225 target.addLegalDialect<mlir::BuiltinDialect>();
2226 target.addLegalDialect<mlir::math::MathDialect>();
2227 target.addLegalDialect<sim::SimDialect>();
2228 target.addLegalDialect<mlir::LLVM::LLVMDialect>();
2229 target.addLegalDialect<verif::VerifDialect>();
2230 target.addLegalDialect<arith::ArithDialect>();
2231
2232 target.addLegalOp<debug::ScopeOp>();
2233
2234 target.addDynamicallyLegalOp<scf::YieldOp, func::CallOp, func::ReturnOp,
2235 UnrealizedConversionCastOp, hw::OutputOp,
2236 hw::InstanceOp, debug::ArrayOp, debug::StructOp,
2237 debug::VariableOp>(
2238 [&](Operation *op) { return converter.isLegal(op); });
2239
2240 target.addDynamicallyLegalOp<scf::IfOp, scf::ForOp, scf::ExecuteRegionOp,
2241 scf::WhileOp, scf::ForallOp>([&](Operation *op) {
2242 return converter.isLegal(op) && !op->getParentOfType<llhd::ProcessOp>();
2243 });
2244
2245 target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
2246 return converter.isSignatureLegal(op.getFunctionType());
2247 });
2248
2249 target.addDynamicallyLegalOp<hw::HWModuleOp>([&](hw::HWModuleOp op) {
2250 return converter.isSignatureLegal(op.getModuleType().getFuncType()) &&
2251 converter.isLegal(&op.getBody());
2252 });
2253}
2254
2255static void populateTypeConversion(TypeConverter &typeConverter) {
2256 typeConverter.addConversion([&](IntType type) {
2257 return IntegerType::get(type.getContext(), type.getWidth());
2258 });
2259
2260 typeConverter.addConversion([&](RealType type) -> mlir::Type {
2261 MLIRContext *ctx = type.getContext();
2262 switch (type.getWidth()) {
2263 case moore::RealWidth::f32:
2264 return mlir::Float32Type::get(ctx);
2265 case moore::RealWidth::f64:
2266 return mlir::Float64Type::get(ctx);
2267 }
2268 });
2269
2270 typeConverter.addConversion(
2271 [&](TimeType type) { return llhd::TimeType::get(type.getContext()); });
2272
2273 typeConverter.addConversion([&](FormatStringType type) {
2274 return sim::FormatStringType::get(type.getContext());
2275 });
2276
2277 typeConverter.addConversion([&](StringType type) {
2278 return sim::DynamicStringType::get(type.getContext());
2279 });
2280
2281 typeConverter.addConversion([&](ArrayType type) -> std::optional<Type> {
2282 if (auto elementType = typeConverter.convertType(type.getElementType()))
2283 return hw::ArrayType::get(elementType, type.getSize());
2284 return {};
2285 });
2286
2287 // FIXME: Unpacked arrays support more element types than their packed
2288 // variants, and as such, mapping them to hw::Array is somewhat naive. See
2289 // also the analogous note below concerning unpacked struct type conversion.
2290 typeConverter.addConversion(
2291 [&](UnpackedArrayType type) -> std::optional<Type> {
2292 if (auto elementType = typeConverter.convertType(type.getElementType()))
2293 return hw::ArrayType::get(elementType, type.getSize());
2294 return {};
2295 });
2296
2297 typeConverter.addConversion([&](StructType type) -> std::optional<Type> {
2298 SmallVector<hw::StructType::FieldInfo> fields;
2299 for (auto field : type.getMembers()) {
2300 hw::StructType::FieldInfo info;
2301 info.type = typeConverter.convertType(field.type);
2302 if (!info.type)
2303 return {};
2304 info.name = field.name;
2305 fields.push_back(info);
2306 }
2307 return hw::StructType::get(type.getContext(), fields);
2308 });
2309
2310 // FIXME: Mapping unpacked struct type to struct type in hw dialect may be a
2311 // plain solution. The packed and unpacked data structures have some
2312 // differences though they look similarily. The packed data structure is
2313 // contiguous in memory but another is opposite. The differences will affect
2314 // data layout and granularity of event tracking in simulation.
2315 typeConverter.addConversion(
2316 [&](UnpackedStructType type) -> std::optional<Type> {
2317 SmallVector<hw::StructType::FieldInfo> fields;
2318 for (auto field : type.getMembers()) {
2319 hw::StructType::FieldInfo info;
2320 info.type = typeConverter.convertType(field.type);
2321 if (!info.type)
2322 return {};
2323 info.name = field.name;
2324 fields.push_back(info);
2325 }
2326 return hw::StructType::get(type.getContext(), fields);
2327 });
2328
2329 // Conversion of CHandle to LLVMPointerType
2330 typeConverter.addConversion([&](ChandleType type) -> std::optional<Type> {
2331 return LLVM::LLVMPointerType::get(type.getContext());
2332 });
2333
2334 // Explicitly mark LLVMPointerType as a legal target
2335 typeConverter.addConversion(
2336 [](LLVM::LLVMPointerType t) -> std::optional<Type> { return t; });
2337
2338 // ClassHandleType -> !llvm.ptr
2339 typeConverter.addConversion([&](ClassHandleType type) -> std::optional<Type> {
2340 return LLVM::LLVMPointerType::get(type.getContext());
2341 });
2342
2343 typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
2344 if (auto innerType = typeConverter.convertType(type.getNestedType()))
2345 return llhd::RefType::get(innerType);
2346 return {};
2347 });
2348
2349 // Valid target types.
2350 typeConverter.addConversion([](IntegerType type) { return type; });
2351 typeConverter.addConversion([](FloatType type) { return type; });
2352 typeConverter.addConversion([](sim::DynamicStringType type) { return type; });
2353 typeConverter.addConversion([](llhd::TimeType type) { return type; });
2354 typeConverter.addConversion([](debug::ArrayType type) { return type; });
2355 typeConverter.addConversion([](debug::ScopeType type) { return type; });
2356 typeConverter.addConversion([](debug::StructType type) { return type; });
2357
2358 typeConverter.addConversion([&](llhd::RefType type) -> std::optional<Type> {
2359 if (auto innerType = typeConverter.convertType(type.getNestedType()))
2360 return llhd::RefType::get(innerType);
2361 return {};
2362 });
2363
2364 typeConverter.addConversion([&](hw::ArrayType type) -> std::optional<Type> {
2365 if (auto elementType = typeConverter.convertType(type.getElementType()))
2366 return hw::ArrayType::get(elementType, type.getNumElements());
2367 return {};
2368 });
2369
2370 typeConverter.addConversion([&](hw::StructType type) -> std::optional<Type> {
2371 SmallVector<hw::StructType::FieldInfo> fields;
2372 for (auto field : type.getElements()) {
2373 hw::StructType::FieldInfo info;
2374 info.type = typeConverter.convertType(field.type);
2375 if (!info.type)
2376 return {};
2377 info.name = field.name;
2378 fields.push_back(info);
2379 }
2380 return hw::StructType::get(type.getContext(), fields);
2381 });
2382
2383 typeConverter.addTargetMaterialization(
2384 [&](mlir::OpBuilder &builder, mlir::Type resultType,
2385 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
2386 if (inputs.size() != 1 || !inputs[0])
2387 return Value();
2388 return UnrealizedConversionCastOp::create(builder, loc, resultType,
2389 inputs[0])
2390 .getResult(0);
2391 });
2392
2393 typeConverter.addSourceMaterialization(
2394 [&](mlir::OpBuilder &builder, mlir::Type resultType,
2395 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
2396 if (inputs.size() != 1)
2397 return Value();
2398 return UnrealizedConversionCastOp::create(builder, loc, resultType,
2399 inputs[0])
2400 ->getResult(0);
2401 });
2402}
2403
2405 TypeConverter &typeConverter,
2406 ClassTypeCache &classCache) {
2407
2408 patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
2409 classCache);
2410 patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
2411 classCache);
2412 patterns.add<ClassPropertyRefOpConversion>(typeConverter,
2413 patterns.getContext(), classCache);
2414
2415 // clang-format off
2416 patterns.add<
2417 ClassUpcastOpConversion,
2418 // Patterns of declaration operations.
2419 VariableOpConversion,
2420 NetOpConversion,
2421
2422 // Patterns for conversion operations.
2423 ConversionOpConversion,
2424 BitcastConversion<PackedToSBVOp>,
2425 BitcastConversion<SBVToPackedOp>,
2426 BitcastConversion<LogicToIntOp>,
2427 BitcastConversion<IntToLogicOp>,
2428 BitcastConversion<ToBuiltinBoolOp>,
2429 TruncOpConversion,
2430 ZExtOpConversion,
2431 SExtOpConversion,
2432 SIntToRealOpConversion,
2433 UIntToRealOpConversion,
2434 IntToStringOpConversion,
2435 RealToIntOpConversion,
2436
2437 // Patterns of miscellaneous operations.
2438 ConstantOpConv,
2439 ConstantRealOpConv,
2440 ConcatOpConversion,
2441 ReplicateOpConversion,
2442 ConstantTimeOpConv,
2443 ExtractOpConversion,
2444 DynExtractOpConversion,
2445 DynExtractRefOpConversion,
2446 ReadOpConversion,
2447 StructExtractOpConversion,
2448 StructExtractRefOpConversion,
2449 ExtractRefOpConversion,
2450 StructCreateOpConversion,
2451 ConditionalOpConversion,
2452 ArrayCreateOpConversion,
2453 YieldOpConversion,
2454 OutputOpConversion,
2455 ConstantStringOpConv,
2456
2457 // Patterns of unary operations.
2458 ReduceAndOpConversion,
2459 ReduceOrOpConversion,
2460 ReduceXorOpConversion,
2461 BoolCastOpConversion,
2462 NotOpConversion,
2463 NegOpConversion,
2464
2465 // Patterns of binary operations.
2466 BinaryOpConversion<AddOp, comb::AddOp>,
2467 BinaryOpConversion<SubOp, comb::SubOp>,
2468 BinaryOpConversion<MulOp, comb::MulOp>,
2469 BinaryOpConversion<DivUOp, comb::DivUOp>,
2470 BinaryOpConversion<DivSOp, comb::DivSOp>,
2471 BinaryOpConversion<ModUOp, comb::ModUOp>,
2472 BinaryOpConversion<ModSOp, comb::ModSOp>,
2473 BinaryOpConversion<AndOp, comb::AndOp>,
2474 BinaryOpConversion<OrOp, comb::OrOp>,
2475 BinaryOpConversion<XorOp, comb::XorOp>,
2476
2477 // Patterns for binary real operations.
2478 BinaryRealOpConversion<AddRealOp, arith::AddFOp>,
2479 BinaryRealOpConversion<SubRealOp, arith::SubFOp>,
2480 BinaryRealOpConversion<DivRealOp, arith::DivFOp>,
2481 BinaryRealOpConversion<MulRealOp, arith::MulFOp>,
2482 BinaryRealOpConversion<PowRealOp, math::PowFOp>,
2483
2484 // Patterns of power operations.
2485 PowUOpConversion, PowSOpConversion,
2486
2487 // Patterns of relational operations.
2488 ICmpOpConversion<UltOp, ICmpPredicate::ult>,
2489 ICmpOpConversion<SltOp, ICmpPredicate::slt>,
2490 ICmpOpConversion<UleOp, ICmpPredicate::ule>,
2491 ICmpOpConversion<SleOp, ICmpPredicate::sle>,
2492 ICmpOpConversion<UgtOp, ICmpPredicate::ugt>,
2493 ICmpOpConversion<SgtOp, ICmpPredicate::sgt>,
2494 ICmpOpConversion<UgeOp, ICmpPredicate::uge>,
2495 ICmpOpConversion<SgeOp, ICmpPredicate::sge>,
2496 ICmpOpConversion<EqOp, ICmpPredicate::eq>,
2497 ICmpOpConversion<NeOp, ICmpPredicate::ne>,
2498 ICmpOpConversion<CaseEqOp, ICmpPredicate::ceq>,
2499 ICmpOpConversion<CaseNeOp, ICmpPredicate::cne>,
2500 ICmpOpConversion<WildcardEqOp, ICmpPredicate::weq>,
2501 ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
2502 FCmpOpConversion<NeRealOp, arith::CmpFPredicate::ONE>,
2503 FCmpOpConversion<FltOp, arith::CmpFPredicate::OLT>,
2504 FCmpOpConversion<FleOp, arith::CmpFPredicate::OLE>,
2505 FCmpOpConversion<FgtOp, arith::CmpFPredicate::OGT>,
2506 FCmpOpConversion<FgeOp, arith::CmpFPredicate::OGE>,
2507 FCmpOpConversion<EqRealOp, arith::CmpFPredicate::OEQ>,
2508 CaseXZEqOpConversion<CaseZEqOp, true>,
2509 CaseXZEqOpConversion<CaseXZEqOp, false>,
2510
2511 // Patterns of structural operations.
2512 SVModuleOpConversion,
2513 InstanceOpConversion,
2514 ProcedureOpConversion,
2515 WaitEventOpConversion,
2516
2517 // Patterns of shifting operations.
2518 ShrOpConversion,
2519 ShlOpConversion,
2520 AShrOpConversion,
2521
2522 // Patterns of assignment operations.
2523 AssignOpConversion<ContinuousAssignOp>,
2524 AssignOpConversion<DelayedContinuousAssignOp>,
2525 AssignOpConversion<BlockingAssignOp>,
2526 AssignOpConversion<NonBlockingAssignOp>,
2527 AssignOpConversion<DelayedNonBlockingAssignOp>,
2528 AssignedVariableOpConversion,
2529
2530 // Patterns of other operations outside Moore dialect.
2531 HWInstanceOpConversion,
2532 ReturnOpConversion,
2533 CallOpConversion,
2534 UnrealizedConversionCastConversion,
2535 InPlaceOpConversion<debug::ArrayOp>,
2536 InPlaceOpConversion<debug::StructOp>,
2537 InPlaceOpConversion<debug::VariableOp>,
2538
2539 // Patterns of assert-like operations
2540 AssertLikeOpConversion<AssertOp, verif::AssertOp>,
2541 AssertLikeOpConversion<AssumeOp, verif::AssumeOp>,
2542 AssertLikeOpConversion<CoverOp, verif::CoverOp>,
2543
2544 // Format strings.
2545 FormatLiteralOpConversion,
2546 FormatConcatOpConversion,
2547 FormatIntOpConversion,
2548 FormatRealOpConversion,
2549 DisplayBIOpConversion,
2550
2551 // Dynamic string operations
2552 StringLenOpConversion,
2553 StringConcatOpConversion
2554 >(typeConverter, patterns.getContext());
2555 // clang-format on
2556
2557 // Structural operations
2558 patterns.add<WaitDelayOp>(convert);
2559 patterns.add<UnreachableOp>(convert);
2560
2561 // Simulation control
2562 patterns.add<StopBIOp>(convert);
2563 patterns.add<SeverityBIOp>(convert);
2564 patterns.add<FinishBIOp>(convert);
2565 patterns.add<FinishMessageBIOp>(convert);
2566
2567 // Timing control
2568 patterns.add<TimeBIOp>(convert);
2569 patterns.add<LogicToTimeOp>(convert);
2570 patterns.add<TimeToLogicOp>(convert);
2571
2572 mlir::populateAnyFunctionOpInterfaceTypeConversionPattern(patterns,
2573 typeConverter);
2574 hw::populateHWModuleLikeTypeConversionPattern(
2575 hw::HWModuleOp::getOperationName(), patterns, typeConverter);
2576 populateSCFToControlFlowConversionPatterns(patterns);
2577 populateArithToCombPatterns(patterns, typeConverter);
2578}
2579
2580//===----------------------------------------------------------------------===//
2581// Moore to Core Conversion Pass
2582//===----------------------------------------------------------------------===//
2583
2584namespace {
2585struct MooreToCorePass
2586 : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
2587 void runOnOperation() override;
2588};
2589} // namespace
2590
2591/// Create a Moore to core dialects conversion pass.
2592std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
2593 return std::make_unique<MooreToCorePass>();
2594}
2595
2596/// This is the main entrypoint for the Moore to Core conversion pass.
2597void MooreToCorePass::runOnOperation() {
2598 MLIRContext &context = getContext();
2599 ModuleOp module = getOperation();
2600 ClassTypeCache classCache;
2601
2602 IRRewriter rewriter(module);
2603 (void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
2604
2605 TypeConverter typeConverter;
2606 populateTypeConversion(typeConverter);
2607
2608 ConversionTarget target(context);
2609 populateLegality(target, typeConverter);
2610
2611 ConversionPatternSet patterns(&context, typeConverter);
2612 populateOpConversion(patterns, typeConverter, classCache);
2613 mlir::cf::populateCFStructuralTypeConversionsAndLegality(typeConverter,
2614 patterns, target);
2615
2616 if (failed(applyFullConversion(module, target, std::move(patterns))))
2617 signalPassFailure();
2618}
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.