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
1585//===----------------------------------------------------------------------===//
1586// Statement Conversion
1587//===----------------------------------------------------------------------===//
1588
1589struct HWInstanceOpConversion : public OpConversionPattern<hw::InstanceOp> {
1590 using OpConversionPattern::OpConversionPattern;
1591
1592 LogicalResult
1593 matchAndRewrite(hw::InstanceOp op, OpAdaptor adaptor,
1594 ConversionPatternRewriter &rewriter) const override {
1595 SmallVector<Type> convResTypes;
1596 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1597 return failure();
1598
1599 rewriter.replaceOpWithNewOp<hw::InstanceOp>(
1600 op, convResTypes, op.getInstanceName(), op.getModuleName(),
1601 adaptor.getOperands(), op.getArgNames(),
1602 op.getResultNames(), /*Parameter*/
1603 rewriter.getArrayAttr({}), /*InnerSymbol*/ nullptr);
1604
1605 return success();
1606 }
1607};
1608
1609struct ReturnOpConversion : public OpConversionPattern<func::ReturnOp> {
1610 using OpConversionPattern::OpConversionPattern;
1611
1612 LogicalResult
1613 matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
1614 ConversionPatternRewriter &rewriter) const override {
1615 rewriter.replaceOpWithNewOp<func::ReturnOp>(op, adaptor.getOperands());
1616 return success();
1617 }
1618};
1619
1620struct CallOpConversion : public OpConversionPattern<func::CallOp> {
1621 using OpConversionPattern::OpConversionPattern;
1622
1623 LogicalResult
1624 matchAndRewrite(func::CallOp op, OpAdaptor adaptor,
1625 ConversionPatternRewriter &rewriter) const override {
1626 SmallVector<Type> convResTypes;
1627 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1628 return failure();
1629 rewriter.replaceOpWithNewOp<func::CallOp>(
1630 op, adaptor.getCallee(), convResTypes, adaptor.getOperands());
1631 return success();
1632 }
1633};
1634
1635struct UnrealizedConversionCastConversion
1636 : public OpConversionPattern<UnrealizedConversionCastOp> {
1637 using OpConversionPattern::OpConversionPattern;
1638
1639 LogicalResult
1640 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
1641 ConversionPatternRewriter &rewriter) const override {
1642 SmallVector<Type> convResTypes;
1643 if (typeConverter->convertTypes(op.getResultTypes(), convResTypes).failed())
1644 return failure();
1645
1646 // Drop the cast if the operand and result types agree after type
1647 // conversion.
1648 if (convResTypes == adaptor.getOperands().getTypes()) {
1649 rewriter.replaceOp(op, adaptor.getOperands());
1650 return success();
1651 }
1652
1653 rewriter.replaceOpWithNewOp<UnrealizedConversionCastOp>(
1654 op, convResTypes, adaptor.getOperands());
1655 return success();
1656 }
1657};
1658
1659struct ShlOpConversion : public OpConversionPattern<ShlOp> {
1660 using OpConversionPattern::OpConversionPattern;
1661
1662 LogicalResult
1663 matchAndRewrite(ShlOp op, OpAdaptor adaptor,
1664 ConversionPatternRewriter &rewriter) const override {
1665 Type resultType = typeConverter->convertType(op.getResult().getType());
1666
1667 // Comb shift operations require the same bit-width for value and amount
1668 Value amount =
1669 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1670 resultType.getIntOrFloatBitWidth(), op->getLoc());
1671 rewriter.replaceOpWithNewOp<comb::ShlOp>(op, resultType, adaptor.getValue(),
1672 amount, false);
1673 return success();
1674 }
1675};
1676
1677struct ShrOpConversion : public OpConversionPattern<ShrOp> {
1678 using OpConversionPattern::OpConversionPattern;
1679
1680 LogicalResult
1681 matchAndRewrite(ShrOp op, OpAdaptor adaptor,
1682 ConversionPatternRewriter &rewriter) const override {
1683 Type resultType = typeConverter->convertType(op.getResult().getType());
1684
1685 // Comb shift operations require the same bit-width for value and amount
1686 Value amount =
1687 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1688 resultType.getIntOrFloatBitWidth(), op->getLoc());
1689 rewriter.replaceOpWithNewOp<comb::ShrUOp>(
1690 op, resultType, adaptor.getValue(), amount, false);
1691 return success();
1692 }
1693};
1694
1695struct PowUOpConversion : public OpConversionPattern<PowUOp> {
1696 using OpConversionPattern::OpConversionPattern;
1697
1698 LogicalResult
1699 matchAndRewrite(PowUOp op, OpAdaptor adaptor,
1700 ConversionPatternRewriter &rewriter) const override {
1701 Type resultType = typeConverter->convertType(op.getResult().getType());
1702
1703 Location loc = op->getLoc();
1704
1705 Value zeroVal = hw::ConstantOp::create(rewriter, loc, APInt(1, 0));
1706 // zero extend both LHS & RHS to ensure the unsigned integers are
1707 // interpreted correctly when calculating power
1708 auto lhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getLhs());
1709 auto rhs = comb::ConcatOp::create(rewriter, loc, zeroVal, adaptor.getRhs());
1710
1711 // lower the exponentiation via MLIR's math dialect
1712 auto pow = mlir::math::IPowIOp::create(rewriter, loc, lhs, rhs);
1713
1714 rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, resultType, pow, 0);
1715 return success();
1716 }
1717};
1718
1719struct PowSOpConversion : public OpConversionPattern<PowSOp> {
1720 using OpConversionPattern::OpConversionPattern;
1721
1722 LogicalResult
1723 matchAndRewrite(PowSOp op, OpAdaptor adaptor,
1724 ConversionPatternRewriter &rewriter) const override {
1725 Type resultType = typeConverter->convertType(op.getResult().getType());
1726
1727 // utilize MLIR math dialect's math.ipowi to handle the exponentiation of
1728 // expression
1729 rewriter.replaceOpWithNewOp<mlir::math::IPowIOp>(
1730 op, resultType, adaptor.getLhs(), adaptor.getRhs());
1731 return success();
1732 }
1733};
1734
1735struct AShrOpConversion : public OpConversionPattern<AShrOp> {
1736 using OpConversionPattern::OpConversionPattern;
1737
1738 LogicalResult
1739 matchAndRewrite(AShrOp op, OpAdaptor adaptor,
1740 ConversionPatternRewriter &rewriter) const override {
1741 Type resultType = typeConverter->convertType(op.getResult().getType());
1742
1743 // Comb shift operations require the same bit-width for value and amount
1744 Value amount =
1745 adjustIntegerWidth(rewriter, adaptor.getAmount(),
1746 resultType.getIntOrFloatBitWidth(), op->getLoc());
1747 rewriter.replaceOpWithNewOp<comb::ShrSOp>(
1748 op, resultType, adaptor.getValue(), amount, false);
1749 return success();
1750 }
1751};
1752
1753struct ReadOpConversion : public OpConversionPattern<ReadOp> {
1754 using OpConversionPattern::OpConversionPattern;
1755
1756 LogicalResult
1757 matchAndRewrite(ReadOp op, OpAdaptor adaptor,
1758 ConversionPatternRewriter &rewriter) const override {
1759 rewriter.replaceOpWithNewOp<llhd::ProbeOp>(op, adaptor.getInput());
1760 return success();
1761 }
1762};
1763
1764struct AssignedVariableOpConversion
1765 : public OpConversionPattern<AssignedVariableOp> {
1766 using OpConversionPattern::OpConversionPattern;
1767
1768 LogicalResult
1769 matchAndRewrite(AssignedVariableOp op, OpAdaptor adaptor,
1770 ConversionPatternRewriter &rewriter) const override {
1771 rewriter.replaceOpWithNewOp<hw::WireOp>(op, adaptor.getInput(),
1772 adaptor.getNameAttr());
1773 return success();
1774 }
1775};
1776
1777template <typename OpTy>
1778struct AssignOpConversion : public OpConversionPattern<OpTy> {
1780 using OpAdaptor = typename OpTy::Adaptor;
1781
1782 LogicalResult
1783 matchAndRewrite(OpTy op, OpAdaptor adaptor,
1784 ConversionPatternRewriter &rewriter) const override {
1785 // Determine the delay for the assignment.
1786 Value delay;
1787 if constexpr (std::is_same_v<OpTy, ContinuousAssignOp> ||
1788 std::is_same_v<OpTy, BlockingAssignOp>) {
1789 // Blocking and continuous assignments get a 0ns 0d 1e delay.
1790 delay = llhd::ConstantTimeOp::create(
1791 rewriter, op->getLoc(),
1792 llhd::TimeAttr::get(op->getContext(), 0U, "ns", 0, 1));
1793 } else if constexpr (std::is_same_v<OpTy, NonBlockingAssignOp>) {
1794 // Non-blocking assignments get a 0ns 1d 0e delay.
1795 delay = llhd::ConstantTimeOp::create(
1796 rewriter, op->getLoc(),
1797 llhd::TimeAttr::get(op->getContext(), 0U, "ns", 1, 0));
1798 } else {
1799 // Delayed assignments have a delay operand.
1800 delay = adaptor.getDelay();
1801 }
1802
1803 rewriter.replaceOpWithNewOp<llhd::DriveOp>(
1804 op, adaptor.getDst(), adaptor.getSrc(), delay, Value{});
1805 return success();
1806 }
1807};
1808
1809struct ConditionalOpConversion : public OpConversionPattern<ConditionalOp> {
1810 using OpConversionPattern::OpConversionPattern;
1811
1812 LogicalResult
1813 matchAndRewrite(ConditionalOp op, OpAdaptor adaptor,
1814 ConversionPatternRewriter &rewriter) const override {
1815 // TODO: This lowering is only correct if the condition is two-valued. If
1816 // the condition is X or Z, both branches of the conditional must be
1817 // evaluated and merged with the appropriate lookup table. See documentation
1818 // for `ConditionalOp`.
1819 auto type = typeConverter->convertType(op.getType());
1820
1821 auto hasNoWriteEffect = [](Region &region) {
1822 auto result = region.walk([](Operation *operation) {
1823 if (auto memOp = dyn_cast<MemoryEffectOpInterface>(operation))
1824 if (!memOp.hasEffect<MemoryEffects::Write>() &&
1825 !memOp.hasEffect<MemoryEffects::Free>())
1826 return WalkResult::advance();
1827
1828 if (operation->hasTrait<OpTrait::HasRecursiveMemoryEffects>())
1829 return WalkResult::advance();
1830
1831 return WalkResult::interrupt();
1832 });
1833 return !result.wasInterrupted();
1834 };
1835
1836 if (hasNoWriteEffect(op.getTrueRegion()) &&
1837 hasNoWriteEffect(op.getFalseRegion())) {
1838 Operation *trueTerm = op.getTrueRegion().front().getTerminator();
1839 Operation *falseTerm = op.getFalseRegion().front().getTerminator();
1840
1841 rewriter.inlineBlockBefore(&op.getTrueRegion().front(), op);
1842 rewriter.inlineBlockBefore(&op.getFalseRegion().front(), op);
1843
1844 Value convTrueVal = typeConverter->materializeTargetConversion(
1845 rewriter, op.getLoc(), type, trueTerm->getOperand(0));
1846 Value convFalseVal = typeConverter->materializeTargetConversion(
1847 rewriter, op.getLoc(), type, falseTerm->getOperand(0));
1848
1849 rewriter.eraseOp(trueTerm);
1850 rewriter.eraseOp(falseTerm);
1851
1852 rewriter.replaceOpWithNewOp<comb::MuxOp>(op, adaptor.getCondition(),
1853 convTrueVal, convFalseVal);
1854 return success();
1855 }
1856
1857 auto ifOp =
1858 scf::IfOp::create(rewriter, op.getLoc(), type, adaptor.getCondition());
1859 rewriter.inlineRegionBefore(op.getTrueRegion(), ifOp.getThenRegion(),
1860 ifOp.getThenRegion().end());
1861 rewriter.inlineRegionBefore(op.getFalseRegion(), ifOp.getElseRegion(),
1862 ifOp.getElseRegion().end());
1863 rewriter.replaceOp(op, ifOp);
1864 return success();
1865 }
1866};
1867
1868struct YieldOpConversion : public OpConversionPattern<YieldOp> {
1869 using OpConversionPattern::OpConversionPattern;
1870
1871 LogicalResult
1872 matchAndRewrite(YieldOp op, OpAdaptor adaptor,
1873 ConversionPatternRewriter &rewriter) const override {
1874 rewriter.replaceOpWithNewOp<scf::YieldOp>(op, adaptor.getResult());
1875 return success();
1876 }
1877};
1878
1879template <typename SourceOp>
1880struct InPlaceOpConversion : public OpConversionPattern<SourceOp> {
1882 using OpAdaptor = typename SourceOp::Adaptor;
1883
1884 LogicalResult
1885 matchAndRewrite(SourceOp op, OpAdaptor adaptor,
1886 ConversionPatternRewriter &rewriter) const override {
1887 rewriter.modifyOpInPlace(op,
1888 [&]() { op->setOperands(adaptor.getOperands()); });
1889 return success();
1890 }
1891};
1892
1893template <typename MooreOpTy, typename VerifOpTy>
1894struct AssertLikeOpConversion : public OpConversionPattern<MooreOpTy> {
1896 using OpAdaptor = typename MooreOpTy::Adaptor;
1897
1898 LogicalResult
1899 matchAndRewrite(MooreOpTy op, OpAdaptor adaptor,
1900 ConversionPatternRewriter &rewriter) const override {
1901 StringAttr label =
1902 op.getLabel().has_value()
1903 ? StringAttr::get(op->getContext(), op.getLabel().value())
1904 : StringAttr::get(op->getContext());
1905 rewriter.replaceOpWithNewOp<VerifOpTy>(op, adaptor.getCond(), mlir::Value(),
1906 label);
1907 return success();
1908 }
1909};
1910
1911//===----------------------------------------------------------------------===//
1912// Format String Conversion
1913//===----------------------------------------------------------------------===//
1914
1915struct FormatLiteralOpConversion : public OpConversionPattern<FormatLiteralOp> {
1916 using OpConversionPattern::OpConversionPattern;
1917
1918 LogicalResult
1919 matchAndRewrite(FormatLiteralOp op, OpAdaptor adaptor,
1920 ConversionPatternRewriter &rewriter) const override {
1921 rewriter.replaceOpWithNewOp<sim::FormatLiteralOp>(op, adaptor.getLiteral());
1922 return success();
1923 }
1924};
1925
1926struct FormatConcatOpConversion : public OpConversionPattern<FormatConcatOp> {
1927 using OpConversionPattern::OpConversionPattern;
1928
1929 LogicalResult
1930 matchAndRewrite(FormatConcatOp op, OpAdaptor adaptor,
1931 ConversionPatternRewriter &rewriter) const override {
1932 rewriter.replaceOpWithNewOp<sim::FormatStringConcatOp>(op,
1933 adaptor.getInputs());
1934 return success();
1935 }
1936};
1937
1938struct FormatIntOpConversion : public OpConversionPattern<FormatIntOp> {
1939 using OpConversionPattern::OpConversionPattern;
1940
1941 LogicalResult
1942 matchAndRewrite(FormatIntOp op, OpAdaptor adaptor,
1943 ConversionPatternRewriter &rewriter) const override {
1944 // TODO: These should honor the width, alignment, and padding.
1945 switch (op.getFormat()) {
1946 case IntFormat::Decimal:
1947 rewriter.replaceOpWithNewOp<sim::FormatDecOp>(op, adaptor.getValue());
1948 return success();
1949 case IntFormat::Binary:
1950 rewriter.replaceOpWithNewOp<sim::FormatBinOp>(op, adaptor.getValue());
1951 return success();
1952 case IntFormat::HexLower:
1953 case IntFormat::HexUpper:
1954 rewriter.replaceOpWithNewOp<sim::FormatHexOp>(op, adaptor.getValue());
1955 return success();
1956 default:
1957 return rewriter.notifyMatchFailure(op, "unsupported int format");
1958 }
1959 }
1960};
1961
1962struct DisplayBIOpConversion : public OpConversionPattern<DisplayBIOp> {
1963 using OpConversionPattern::OpConversionPattern;
1964
1965 LogicalResult
1966 matchAndRewrite(DisplayBIOp op, OpAdaptor adaptor,
1967 ConversionPatternRewriter &rewriter) const override {
1968 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(
1969 op, adaptor.getMessage());
1970 return success();
1971 }
1972};
1973
1974} // namespace
1975
1976//===----------------------------------------------------------------------===//
1977// Simulation Control Conversion
1978//===----------------------------------------------------------------------===//
1979
1980// moore.builtin.stop -> sim.pause
1981static LogicalResult convert(StopBIOp op, StopBIOp::Adaptor adaptor,
1982 ConversionPatternRewriter &rewriter) {
1983 rewriter.replaceOpWithNewOp<sim::PauseOp>(op, /*verbose=*/false);
1984 return success();
1985}
1986
1987// moore.builtin.finish -> sim.terminate
1988static LogicalResult convert(FinishBIOp op, FinishBIOp::Adaptor adaptor,
1989 ConversionPatternRewriter &rewriter) {
1990 rewriter.replaceOpWithNewOp<sim::TerminateOp>(op, op.getExitCode() == 0,
1991 /*verbose=*/false);
1992 return success();
1993}
1994
1995// moore.builtin.severity -> sim.proc.print
1996static LogicalResult convert(SeverityBIOp op, SeverityBIOp::Adaptor adaptor,
1997 ConversionPatternRewriter &rewriter) {
1998
1999 std::string severityString;
2000
2001 switch (op.getSeverity()) {
2002 case (Severity::Fatal):
2003 severityString = "Fatal: ";
2004 break;
2005 case (Severity::Error):
2006 severityString = "Error: ";
2007 break;
2008 case (Severity::Warning):
2009 severityString = "Warning: ";
2010 break;
2011 default:
2012 return failure();
2013 }
2014
2015 auto prefix =
2016 sim::FormatLiteralOp::create(rewriter, op.getLoc(), severityString);
2017 auto message = sim::FormatStringConcatOp::create(
2018 rewriter, op.getLoc(), ValueRange{prefix, adaptor.getMessage()});
2019 rewriter.replaceOpWithNewOp<sim::PrintFormattedProcOp>(op, message);
2020 return success();
2021}
2022
2023// moore.builtin.finish_message
2024static LogicalResult convert(FinishMessageBIOp op,
2025 FinishMessageBIOp::Adaptor adaptor,
2026 ConversionPatternRewriter &rewriter) {
2027 // We don't support printing termination/pause messages yet.
2028 rewriter.eraseOp(op);
2029 return success();
2030}
2031
2032//===----------------------------------------------------------------------===//
2033// Conversion Infrastructure
2034//===----------------------------------------------------------------------===//
2035
2036static void populateLegality(ConversionTarget &target,
2037 const TypeConverter &converter) {
2038 target.addIllegalDialect<MooreDialect>();
2039 target.addLegalDialect<comb::CombDialect>();
2040 target.addLegalDialect<hw::HWDialect>();
2041 target.addLegalDialect<seq::SeqDialect>();
2042 target.addLegalDialect<llhd::LLHDDialect>();
2043 target.addLegalDialect<ltl::LTLDialect>();
2044 target.addLegalDialect<mlir::BuiltinDialect>();
2045 target.addLegalDialect<mlir::math::MathDialect>();
2046 target.addLegalDialect<sim::SimDialect>();
2047 target.addLegalDialect<mlir::LLVM::LLVMDialect>();
2048 target.addLegalDialect<verif::VerifDialect>();
2049 target.addLegalDialect<arith::ArithDialect>();
2050
2051 target.addLegalOp<debug::ScopeOp>();
2052
2053 target.addDynamicallyLegalOp<scf::YieldOp, func::CallOp, func::ReturnOp,
2054 UnrealizedConversionCastOp, hw::OutputOp,
2055 hw::InstanceOp, debug::ArrayOp, debug::StructOp,
2056 debug::VariableOp>(
2057 [&](Operation *op) { return converter.isLegal(op); });
2058
2059 target.addDynamicallyLegalOp<scf::IfOp, scf::ForOp, scf::ExecuteRegionOp,
2060 scf::WhileOp, scf::ForallOp>([&](Operation *op) {
2061 return converter.isLegal(op) && !op->getParentOfType<llhd::ProcessOp>();
2062 });
2063
2064 target.addDynamicallyLegalOp<func::FuncOp>([&](func::FuncOp op) {
2065 return converter.isSignatureLegal(op.getFunctionType());
2066 });
2067
2068 target.addDynamicallyLegalOp<hw::HWModuleOp>([&](hw::HWModuleOp op) {
2069 return converter.isSignatureLegal(op.getModuleType().getFuncType()) &&
2070 converter.isLegal(&op.getBody());
2071 });
2072}
2073
2074static void populateTypeConversion(TypeConverter &typeConverter) {
2075 typeConverter.addConversion([&](IntType type) {
2076 return IntegerType::get(type.getContext(), type.getWidth());
2077 });
2078
2079 typeConverter.addConversion([&](RealType type) -> mlir::Type {
2080 MLIRContext *ctx = type.getContext();
2081 switch (type.getWidth()) {
2082 case moore::RealWidth::f32:
2083 return mlir::Float32Type::get(ctx);
2084 case moore::RealWidth::f64:
2085 return mlir::Float64Type::get(ctx);
2086 }
2087 });
2088
2089 typeConverter.addConversion(
2090 [&](TimeType type) { return llhd::TimeType::get(type.getContext()); });
2091
2092 typeConverter.addConversion([&](FormatStringType type) {
2093 return sim::FormatStringType::get(type.getContext());
2094 });
2095
2096 typeConverter.addConversion([&](ArrayType type) -> std::optional<Type> {
2097 if (auto elementType = typeConverter.convertType(type.getElementType()))
2098 return hw::ArrayType::get(elementType, type.getSize());
2099 return {};
2100 });
2101
2102 // FIXME: Unpacked arrays support more element types than their packed
2103 // variants, and as such, mapping them to hw::Array is somewhat naive. See
2104 // also the analogous note below concerning unpacked struct type conversion.
2105 typeConverter.addConversion(
2106 [&](UnpackedArrayType type) -> std::optional<Type> {
2107 if (auto elementType = typeConverter.convertType(type.getElementType()))
2108 return hw::ArrayType::get(elementType, type.getSize());
2109 return {};
2110 });
2111
2112 typeConverter.addConversion([&](StructType type) -> std::optional<Type> {
2113 SmallVector<hw::StructType::FieldInfo> fields;
2114 for (auto field : type.getMembers()) {
2115 hw::StructType::FieldInfo info;
2116 info.type = typeConverter.convertType(field.type);
2117 if (!info.type)
2118 return {};
2119 info.name = field.name;
2120 fields.push_back(info);
2121 }
2122 return hw::StructType::get(type.getContext(), fields);
2123 });
2124
2125 // FIXME: Mapping unpacked struct type to struct type in hw dialect may be a
2126 // plain solution. The packed and unpacked data structures have some
2127 // differences though they look similarily. The packed data structure is
2128 // contiguous in memory but another is opposite. The differences will affect
2129 // data layout and granularity of event tracking in simulation.
2130 typeConverter.addConversion(
2131 [&](UnpackedStructType type) -> std::optional<Type> {
2132 SmallVector<hw::StructType::FieldInfo> fields;
2133 for (auto field : type.getMembers()) {
2134 hw::StructType::FieldInfo info;
2135 info.type = typeConverter.convertType(field.type);
2136 if (!info.type)
2137 return {};
2138 info.name = field.name;
2139 fields.push_back(info);
2140 }
2141 return hw::StructType::get(type.getContext(), fields);
2142 });
2143
2144 // Conversion of CHandle to LLVMPointerType
2145 typeConverter.addConversion([&](ChandleType type) -> std::optional<Type> {
2146 return LLVM::LLVMPointerType::get(type.getContext());
2147 });
2148
2149 // Explicitly mark LLVMPointerType as a legal target
2150 typeConverter.addConversion(
2151 [](LLVM::LLVMPointerType t) -> std::optional<Type> { return t; });
2152
2153 // ClassHandleType -> !llvm.ptr
2154 typeConverter.addConversion([&](ClassHandleType type) -> std::optional<Type> {
2155 return LLVM::LLVMPointerType::get(type.getContext());
2156 });
2157
2158 typeConverter.addConversion([&](RefType type) -> std::optional<Type> {
2159 if (auto innerType = typeConverter.convertType(type.getNestedType()))
2160 return llhd::RefType::get(innerType);
2161 return {};
2162 });
2163
2164 // Valid target types.
2165 typeConverter.addConversion([](IntegerType type) { return type; });
2166 typeConverter.addConversion([](llhd::TimeType type) { return type; });
2167 typeConverter.addConversion([](debug::ArrayType type) { return type; });
2168 typeConverter.addConversion([](debug::ScopeType type) { return type; });
2169 typeConverter.addConversion([](debug::StructType type) { return type; });
2170
2171 typeConverter.addConversion([&](llhd::RefType type) -> std::optional<Type> {
2172 if (auto innerType = typeConverter.convertType(type.getNestedType()))
2173 return llhd::RefType::get(innerType);
2174 return {};
2175 });
2176
2177 typeConverter.addConversion([&](hw::ArrayType type) -> std::optional<Type> {
2178 if (auto elementType = typeConverter.convertType(type.getElementType()))
2179 return hw::ArrayType::get(elementType, type.getNumElements());
2180 return {};
2181 });
2182
2183 typeConverter.addConversion([&](hw::StructType type) -> std::optional<Type> {
2184 SmallVector<hw::StructType::FieldInfo> fields;
2185 for (auto field : type.getElements()) {
2186 hw::StructType::FieldInfo info;
2187 info.type = typeConverter.convertType(field.type);
2188 if (!info.type)
2189 return {};
2190 info.name = field.name;
2191 fields.push_back(info);
2192 }
2193 return hw::StructType::get(type.getContext(), fields);
2194 });
2195
2196 typeConverter.addTargetMaterialization(
2197 [&](mlir::OpBuilder &builder, mlir::Type resultType,
2198 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
2199 if (inputs.size() != 1 || !inputs[0])
2200 return Value();
2201 return UnrealizedConversionCastOp::create(builder, loc, resultType,
2202 inputs[0])
2203 .getResult(0);
2204 });
2205
2206 typeConverter.addSourceMaterialization(
2207 [&](mlir::OpBuilder &builder, mlir::Type resultType,
2208 mlir::ValueRange inputs, mlir::Location loc) -> mlir::Value {
2209 if (inputs.size() != 1)
2210 return Value();
2211 return UnrealizedConversionCastOp::create(builder, loc, resultType,
2212 inputs[0])
2213 ->getResult(0);
2214 });
2215}
2216
2218 TypeConverter &typeConverter,
2219 ClassTypeCache &classCache) {
2220
2221 patterns.add<ClassDeclOpConversion>(typeConverter, patterns.getContext(),
2222 classCache);
2223 patterns.add<ClassNewOpConversion>(typeConverter, patterns.getContext(),
2224 classCache);
2225 patterns.add<ClassPropertyRefOpConversion>(typeConverter,
2226 patterns.getContext(), classCache);
2227
2228 // clang-format off
2229 patterns.add<
2230 ClassUpcastOpConversion,
2231 // Patterns of declaration operations.
2232 VariableOpConversion,
2233 NetOpConversion,
2234
2235 // Patterns for conversion operations.
2236 ConversionOpConversion,
2237 BitcastConversion<PackedToSBVOp>,
2238 BitcastConversion<SBVToPackedOp>,
2239 BitcastConversion<LogicToIntOp>,
2240 BitcastConversion<IntToLogicOp>,
2241 BitcastConversion<ToBuiltinBoolOp>,
2242 TruncOpConversion,
2243 ZExtOpConversion,
2244 SExtOpConversion,
2245
2246 // Patterns of miscellaneous operations.
2247 ConstantOpConv,
2248 ConstantRealOpConv,
2249 ConcatOpConversion,
2250 ReplicateOpConversion,
2251 ConstantTimeOpConv,
2252 ExtractOpConversion,
2253 DynExtractOpConversion,
2254 DynExtractRefOpConversion,
2255 ReadOpConversion,
2256 StructExtractOpConversion,
2257 StructExtractRefOpConversion,
2258 ExtractRefOpConversion,
2259 StructCreateOpConversion,
2260 ConditionalOpConversion,
2261 ArrayCreateOpConversion,
2262 YieldOpConversion,
2263 OutputOpConversion,
2264 ConstantStringOpConv,
2265
2266 // Patterns of unary operations.
2267 ReduceAndOpConversion,
2268 ReduceOrOpConversion,
2269 ReduceXorOpConversion,
2270 BoolCastOpConversion,
2271 NotOpConversion,
2272 NegOpConversion,
2273
2274 // Patterns of binary operations.
2275 BinaryOpConversion<AddOp, comb::AddOp>,
2276 BinaryOpConversion<SubOp, comb::SubOp>,
2277 BinaryOpConversion<MulOp, comb::MulOp>,
2278 BinaryOpConversion<DivUOp, comb::DivUOp>,
2279 BinaryOpConversion<DivSOp, comb::DivSOp>,
2280 BinaryOpConversion<ModUOp, comb::ModUOp>,
2281 BinaryOpConversion<ModSOp, comb::ModSOp>,
2282 BinaryOpConversion<AndOp, comb::AndOp>,
2283 BinaryOpConversion<OrOp, comb::OrOp>,
2284 BinaryOpConversion<XorOp, comb::XorOp>,
2285
2286 // Patterns of power operations.
2287 PowUOpConversion, PowSOpConversion,
2288
2289 // Patterns of relational operations.
2290 ICmpOpConversion<UltOp, ICmpPredicate::ult>,
2291 ICmpOpConversion<SltOp, ICmpPredicate::slt>,
2292 ICmpOpConversion<UleOp, ICmpPredicate::ule>,
2293 ICmpOpConversion<SleOp, ICmpPredicate::sle>,
2294 ICmpOpConversion<UgtOp, ICmpPredicate::ugt>,
2295 ICmpOpConversion<SgtOp, ICmpPredicate::sgt>,
2296 ICmpOpConversion<UgeOp, ICmpPredicate::uge>,
2297 ICmpOpConversion<SgeOp, ICmpPredicate::sge>,
2298 ICmpOpConversion<EqOp, ICmpPredicate::eq>,
2299 ICmpOpConversion<NeOp, ICmpPredicate::ne>,
2300 ICmpOpConversion<CaseEqOp, ICmpPredicate::ceq>,
2301 ICmpOpConversion<CaseNeOp, ICmpPredicate::cne>,
2302 ICmpOpConversion<WildcardEqOp, ICmpPredicate::weq>,
2303 ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
2304 CaseXZEqOpConversion<CaseZEqOp, true>,
2305 CaseXZEqOpConversion<CaseXZEqOp, false>,
2306
2307 // Patterns of structural operations.
2308 SVModuleOpConversion,
2309 InstanceOpConversion,
2310 ProcedureOpConversion,
2311 WaitEventOpConversion,
2312
2313 // Patterns of shifting operations.
2314 ShrOpConversion,
2315 ShlOpConversion,
2316 AShrOpConversion,
2317
2318 // Patterns of assignment operations.
2319 AssignOpConversion<ContinuousAssignOp>,
2320 AssignOpConversion<DelayedContinuousAssignOp>,
2321 AssignOpConversion<BlockingAssignOp>,
2322 AssignOpConversion<NonBlockingAssignOp>,
2323 AssignOpConversion<DelayedNonBlockingAssignOp>,
2324 AssignedVariableOpConversion,
2325
2326 // Patterns of other operations outside Moore dialect.
2327 HWInstanceOpConversion,
2328 ReturnOpConversion,
2329 CallOpConversion,
2330 UnrealizedConversionCastConversion,
2331 InPlaceOpConversion<debug::ArrayOp>,
2332 InPlaceOpConversion<debug::StructOp>,
2333 InPlaceOpConversion<debug::VariableOp>,
2334
2335 // Patterns of assert-like operations
2336 AssertLikeOpConversion<AssertOp, verif::AssertOp>,
2337 AssertLikeOpConversion<AssumeOp, verif::AssumeOp>,
2338 AssertLikeOpConversion<CoverOp, verif::CoverOp>,
2339
2340 // Format strings.
2341 FormatLiteralOpConversion,
2342 FormatConcatOpConversion,
2343 FormatIntOpConversion,
2344 DisplayBIOpConversion
2345 >(typeConverter, patterns.getContext());
2346 // clang-format on
2347
2348 // Structural operations
2349 patterns.add<WaitDelayOp>(convert);
2350 patterns.add<UnreachableOp>(convert);
2351
2352 // Simulation control
2353 patterns.add<StopBIOp>(convert);
2354 patterns.add<SeverityBIOp>(convert);
2355 patterns.add<FinishBIOp>(convert);
2356 patterns.add<FinishMessageBIOp>(convert);
2357
2358 mlir::populateAnyFunctionOpInterfaceTypeConversionPattern(patterns,
2359 typeConverter);
2360 hw::populateHWModuleLikeTypeConversionPattern(
2361 hw::HWModuleOp::getOperationName(), patterns, typeConverter);
2362 populateSCFToControlFlowConversionPatterns(patterns);
2363 populateArithToCombPatterns(patterns, typeConverter);
2364}
2365
2366//===----------------------------------------------------------------------===//
2367// Moore to Core Conversion Pass
2368//===----------------------------------------------------------------------===//
2369
2370namespace {
2371struct MooreToCorePass
2372 : public circt::impl::ConvertMooreToCoreBase<MooreToCorePass> {
2373 void runOnOperation() override;
2374};
2375} // namespace
2376
2377/// Create a Moore to core dialects conversion pass.
2378std::unique_ptr<OperationPass<ModuleOp>> circt::createConvertMooreToCorePass() {
2379 return std::make_unique<MooreToCorePass>();
2380}
2381
2382/// This is the main entrypoint for the Moore to Core conversion pass.
2383void MooreToCorePass::runOnOperation() {
2384 MLIRContext &context = getContext();
2385 ModuleOp module = getOperation();
2386 ClassTypeCache classCache;
2387
2388 IRRewriter rewriter(module);
2389 (void)mlir::eraseUnreachableBlocks(rewriter, module->getRegions());
2390
2391 TypeConverter typeConverter;
2392 populateTypeConversion(typeConverter);
2393
2394 ConversionTarget target(context);
2395 populateLegality(target, typeConverter);
2396
2397 ConversionPatternSet patterns(&context, typeConverter);
2398 populateOpConversion(patterns, typeConverter, classCache);
2399 mlir::cf::populateCFStructuralTypeConversionsAndLegality(typeConverter,
2400 patterns, target);
2401
2402 if (failed(applyFullConversion(module, target, std::move(patterns))))
2403 signalPassFailure();
2404}
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 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.