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