CIRCT 24.0.0git
Loading...
Searching...
No Matches
SVOps.cpp
Go to the documentation of this file.
1//===- SVOps.cpp - Implement the SV operations ----------------------------===//
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//
9// This file implement the SV ops.
10//
11//===----------------------------------------------------------------------===//
12
25#include "mlir/IR/Builders.h"
26#include "mlir/IR/BuiltinTypes.h"
27#include "mlir/IR/Matchers.h"
28#include "mlir/IR/PatternMatch.h"
29#include "mlir/Interfaces/FunctionImplementation.h"
30#include "llvm/ADT/SmallString.h"
31#include "llvm/ADT/StringExtras.h"
32#include "llvm/ADT/TypeSwitch.h"
33
34#include <optional>
35
36using namespace circt;
37using namespace sv;
38using mlir::TypedAttr;
39
40/// Return true if the specified expression is 2-state. This is determined by
41/// looking at the defining op. This can look as far through the dataflow as it
42/// wants, but for now, it is just looking at the single value.
43bool sv::is2StateExpression(Value v) {
44 if (auto *op = v.getDefiningOp()) {
45 if (auto attr = op->getAttrOfType<UnitAttr>("twoState"))
46 return (bool)attr;
47 }
48 // Plain constants are obviously safe
49 return v.getDefiningOp<hw::ConstantOp>();
50}
51
52/// Return true if the specified operation is an expression.
53bool sv::isExpression(Operation *op) {
54 return isa<VerbatimExprOp, VerbatimExprSEOp, GetModportOp,
55 ReadInterfaceSignalOp, ConstantXOp, ConstantZOp, ConstantStrOp,
56 MacroRefExprOp, MacroRefExprSEOp>(op);
57}
58
59/// Returns the operation registered with the given symbol name with the regions
60/// of 'symbolTableOp'. recurse through nested regions which don't contain the
61/// symboltable trait. Returns nullptr if no valid symbol was found.
62static Operation *lookupSymbolInNested(Operation *symbolTableOp,
63 StringRef symbol) {
64 Region &region = symbolTableOp->getRegion(0);
65 if (region.empty())
66 return nullptr;
67
68 // Look for a symbol with the given name.
69 for (Block &block : region)
70 for (Operation &nestedOp : block) {
71 if (auto symbolOp = dyn_cast<mlir::SymbolOpInterface>(&nestedOp);
72 symbolOp && symbolOp.getName() == symbol)
73 return &nestedOp;
74 if (!nestedOp.hasTrait<OpTrait::SymbolTable>() &&
75 nestedOp.getNumRegions()) {
76 if (auto *nop = lookupSymbolInNested(&nestedOp, symbol))
77 return nop;
78 }
79 }
80 return nullptr;
81}
82
83/// Verifies symbols referenced by macro identifiers.
84static LogicalResult
85verifyMacroIdentSymbolUses(Operation *op, FlatSymbolRefAttr attr,
86 SymbolTableCollection &symbolTable) {
87 auto *refOp = symbolTable.lookupNearestSymbolFrom(op, attr);
88 if (!refOp)
89 return op->emitError("references an undefined symbol: ") << attr;
90 if (!isa<MacroDeclOp>(refOp))
91 return op->emitError("must reference a macro declaration");
92 return success();
93}
94
95//===----------------------------------------------------------------------===//
96// VerbatimOp
97//===----------------------------------------------------------------------===//
98
99/// Helper function to verify inner refs in symbols array for verbatim ops.
100static LogicalResult verifyVerbatimSymbols(Operation *op, ArrayAttr symbols,
102 // Verify each symbol reference in the symbols array
103 for (auto symbol : symbols) {
104 if (auto innerRef = dyn_cast<hw::InnerRefAttr>(symbol)) {
105 if (!ns.lookup(innerRef))
106 return op->emitError() << "inner symbol reference " << innerRef
107 << " could not be found";
108 }
109 }
110 return success();
111}
112
113/// Helper function to verify flat symbol refs in symbols array for verbatim
114/// ops.
115static LogicalResult
116verifyVerbatimFlatSymbolRefs(Operation *op, ArrayAttr symbols,
117 SymbolTableCollection &symbolTable) {
118 for (auto symbol : symbols) {
119 if (auto flatRef = dyn_cast<FlatSymbolRefAttr>(symbol)) {
120 auto *referencedOp = symbolTable.lookupNearestSymbolFrom(op, flatRef);
121 if (!referencedOp)
122 return op->emitOpError("references nonexistent symbol '")
123 << flatRef.getValue() << "'";
124 }
125 }
126 return success();
127}
128
129LogicalResult VerbatimOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
130 return verifyVerbatimSymbols(getOperation(), getSymbols(), ns);
131}
132
133LogicalResult VerbatimOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
134 return verifyVerbatimFlatSymbolRefs(getOperation(), getSymbols(),
135 symbolTable);
136}
137
138//===----------------------------------------------------------------------===//
139// VerbatimExprOp
140//===----------------------------------------------------------------------===//
141
142/// Get the asm name for sv.verbatim.expr and sv.verbatim.expr.se.
143static void
145 function_ref<void(Value, StringRef)> setNameFn) {
146 // If the string is macro like, then use a pretty name. We only take the
147 // string up to a weird character (like a paren) and currently ignore
148 // parenthesized expressions.
149 auto isOkCharacter = [](char c) { return llvm::isAlnum(c) || c == '_'; };
150 auto name = op->getAttrOfType<StringAttr>("format_string").getValue();
151 // Ignore a leading ` in macro name.
152 if (name.starts_with("`"))
153 name = name.drop_front();
154 name = name.take_while(isOkCharacter);
155 if (!name.empty())
156 setNameFn(op->getResult(0), name);
157}
158
159void VerbatimExprOp::getAsmResultNames(
160 function_ref<void(Value, StringRef)> setNameFn) {
161 getVerbatimExprAsmResultNames(getOperation(), std::move(setNameFn));
162}
163
164LogicalResult VerbatimExprOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
165 return verifyVerbatimSymbols(getOperation(), getSymbols(), ns);
166}
167
168LogicalResult
169VerbatimExprOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
170 return verifyVerbatimFlatSymbolRefs(getOperation(), getSymbols(),
171 symbolTable);
172}
173
174void VerbatimExprSEOp::getAsmResultNames(
175 function_ref<void(Value, StringRef)> setNameFn) {
176 getVerbatimExprAsmResultNames(getOperation(), std::move(setNameFn));
177}
178
179LogicalResult VerbatimExprSEOp::verifyInnerRefs(hw::InnerRefNamespace &ns) {
180 return verifyVerbatimSymbols(getOperation(), getSymbols(), ns);
181}
182
183LogicalResult
184VerbatimExprSEOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
185 return verifyVerbatimFlatSymbolRefs(getOperation(), getSymbols(),
186 symbolTable);
187}
188
189//===----------------------------------------------------------------------===//
190// MacroRefExprOp
191//===----------------------------------------------------------------------===//
192
193void MacroRefExprOp::getAsmResultNames(
194 function_ref<void(Value, StringRef)> setNameFn) {
195 setNameFn(getResult(), getMacroName());
196}
197
198void MacroRefExprSEOp::getAsmResultNames(
199 function_ref<void(Value, StringRef)> setNameFn) {
200 setNameFn(getResult(), getMacroName());
201}
202
203static MacroDeclOp getReferencedMacro(const hw::HWSymbolCache *cache,
204 Operation *op,
205 FlatSymbolRefAttr macroName) {
206 if (cache)
207 if (auto *result = cache->getDefinition(macroName.getAttr()))
208 return cast<MacroDeclOp>(result);
209
210 auto topLevelModuleOp = op->getParentOfType<ModuleOp>();
211 return topLevelModuleOp.lookupSymbol<MacroDeclOp>(macroName.getValue());
212}
213
214/// Lookup the module or extmodule for the symbol. This returns null on
215/// invalid IR.
216MacroDeclOp MacroRefExprOp::getReferencedMacro(const hw::HWSymbolCache *cache) {
217 return ::getReferencedMacro(cache, *this, getMacroNameAttr());
218}
219
220MacroDeclOp
221MacroRefExprSEOp::getReferencedMacro(const hw::HWSymbolCache *cache) {
222 return ::getReferencedMacro(cache, *this, getMacroNameAttr());
223}
224
225//===----------------------------------------------------------------------===//
226// MacroErrorOp
227//===----------------------------------------------------------------------===//
228
229std::string MacroErrorOp::getMacroIdentifier() {
230 const auto *prefix = "_ERROR";
231 auto msg = getMessage();
232 if (!msg || msg->empty())
233 return prefix;
234
235 std::string id(prefix);
236 id.push_back('_');
237 for (auto c : *msg) {
238 if (llvm::isAlnum(c))
239 id.push_back(c);
240 else
241 id.push_back('_');
242 }
243 return id;
244}
245
246//===----------------------------------------------------------------------===//
247// MacroDeclOp
248//===----------------------------------------------------------------------===//
249
250MacroDeclOp MacroDefOp::getReferencedMacro(const hw::HWSymbolCache *cache) {
251 return ::getReferencedMacro(cache, *this, getMacroNameAttr());
252}
253
254MacroDeclOp MacroRefOp::getReferencedMacro(const hw::HWSymbolCache *cache) {
255 return ::getReferencedMacro(cache, *this, getMacroNameAttr());
256}
257
258/// Ensure that the symbol being instantiated exists and is a MacroDefOp.
259LogicalResult
260MacroRefExprOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
261 return verifyMacroIdentSymbolUses(*this, getMacroNameAttr(), symbolTable);
262}
263
264/// Ensure that the symbol being instantiated exists and is a MacroDefOp.
265LogicalResult
266MacroRefExprSEOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
267 return verifyMacroIdentSymbolUses(*this, getMacroNameAttr(), symbolTable);
268}
269
270/// Ensure that the symbol being instantiated exists and is a MacroDefOp.
271LogicalResult MacroDefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
272 return verifyMacroIdentSymbolUses(*this, getMacroNameAttr(), symbolTable);
273}
274
275/// Ensure that the symbol being instantiated exists and is a MacroDefOp.
276LogicalResult MacroRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
277 return verifyMacroIdentSymbolUses(*this, getMacroNameAttr(), symbolTable);
278}
279
280//===----------------------------------------------------------------------===//
281// MacroDeclOp
282//===----------------------------------------------------------------------===//
283
284StringRef MacroDeclOp::getMacroIdentifier() {
285 return getVerilogName().value_or(getSymName());
286}
287
288//===----------------------------------------------------------------------===//
289// ConstantXOp / ConstantZOp
290//===----------------------------------------------------------------------===//
291
292void ConstantXOp::getAsmResultNames(
293 function_ref<void(Value, StringRef)> setNameFn) {
294 SmallVector<char, 32> specialNameBuffer;
295 llvm::raw_svector_ostream specialName(specialNameBuffer);
296 specialName << "x_i" << getWidth();
297 setNameFn(getResult(), specialName.str());
298}
299
300LogicalResult ConstantXOp::verify() {
301 // We don't allow zero width constant or unknown width.
302 if (getWidth() <= 0)
303 return emitError("unsupported type");
304 return success();
305}
306
307void ConstantZOp::getAsmResultNames(
308 function_ref<void(Value, StringRef)> setNameFn) {
309 SmallVector<char, 32> specialNameBuffer;
310 llvm::raw_svector_ostream specialName(specialNameBuffer);
311 specialName << "z_i" << getWidth();
312 setNameFn(getResult(), specialName.str());
313}
314
315LogicalResult ConstantZOp::verify() {
316 // We don't allow zero width constant or unknown type.
317 if (getWidth() <= 0)
318 return emitError("unsupported type");
319 return success();
320}
321
322//===----------------------------------------------------------------------===//
323// ConcatStrOp
324//===----------------------------------------------------------------------===//
325
326LogicalResult ConcatStrOp::verify() {
327 // Concatenation of zero operands would emit invalid (`{}`) SystemVerilog.
328 if (getInputs().empty())
329 return emitError("sv.concat_str requires at least one operand");
330 return success();
331}
332
333OpFoldResult ConcatStrOp::fold(FoldAdaptor) {
334 if (getInputs().size() == 1)
335 return getInputs().front();
336 return {};
337}
338
339//===----------------------------------------------------------------------===//
340// LocalParamOp
341//===----------------------------------------------------------------------===//
342
343void LocalParamOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
344 // If the localparam has an optional 'name' attribute, use it.
345 auto nameAttr = (*this)->getAttrOfType<StringAttr>("name");
346 if (!nameAttr.getValue().empty())
347 setNameFn(getResult(), nameAttr.getValue());
348}
349
350LogicalResult LocalParamOp::verify() {
351 // Verify that this is a valid parameter value.
352 return hw::checkParameterInContext(
353 getValue(), (*this)->getParentOfType<hw::HWModuleOp>(), *this);
354}
355
356//===----------------------------------------------------------------------===//
357// RegOp
358//===----------------------------------------------------------------------===//
359
360static ParseResult
361parseImplicitInitType(OpAsmParser &p, mlir::Type regType,
362 std::optional<OpAsmParser::UnresolvedOperand> &initValue,
363 mlir::Type &initType) {
364 if (!initValue.has_value())
365 return success();
366
367 hw::InOutType ioType = dyn_cast<hw::InOutType>(regType);
368 if (!ioType)
369 return p.emitError(p.getCurrentLocation(), "expected inout type for reg");
370
371 initType = ioType.getElementType();
372 return success();
373}
374
375static void printImplicitInitType(OpAsmPrinter &p, Operation *op,
376 mlir::Type regType, mlir::Value initValue,
377 mlir::Type initType) {}
378
379void RegOp::build(OpBuilder &builder, OperationState &odsState,
380 Type elementType, StringAttr name, hw::InnerSymAttr innerSym,
381 mlir::Value initValue) {
382 if (!name)
383 name = builder.getStringAttr("");
384 odsState.addAttribute("name", name);
385 if (innerSym)
386 odsState.addAttribute(hw::InnerSymbolTable::getInnerSymbolAttrName(),
387 innerSym);
388 odsState.addTypes(hw::InOutType::get(elementType));
389 if (initValue)
390 odsState.addOperands(initValue);
391}
392
393/// Suggest a name for each result value based on the saved result names
394/// attribute.
395void RegOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
396 // If the wire has an optional 'name' attribute, use it.
397 auto nameAttr = (*this)->getAttrOfType<StringAttr>("name");
398 if (!nameAttr.getValue().empty())
399 setNameFn(getResult(), nameAttr.getValue());
400}
401
402std::optional<size_t> RegOp::getTargetResultIndex() { return 0; }
403
404// If this reg is only written to, delete the reg and all writers.
405LogicalResult RegOp::canonicalize(RegOp op, PatternRewriter &rewriter) {
406 // Block if op has SV attributes.
407 if (hasSVAttributes(op))
408 return failure();
409
410 // If the reg has a symbol, then we can't delete it.
411 if (op.getInnerSymAttr())
412 return failure();
413 // Check that all operations on the wire are sv.assigns. All other wire
414 // operations will have been handled by other canonicalization.
415 for (auto *user : op.getResult().getUsers())
416 if (!isa<AssignOp>(user))
417 return failure();
418
419 // Remove all uses of the wire.
420 for (auto *user : llvm::make_early_inc_range(op.getResult().getUsers()))
421 rewriter.eraseOp(user);
422
423 // Remove the wire.
424 rewriter.eraseOp(op);
425 return success();
426}
427
428//===----------------------------------------------------------------------===//
429// LogicOp
430//===----------------------------------------------------------------------===//
431
432void LogicOp::build(OpBuilder &builder, OperationState &odsState,
433 Type elementType, StringAttr name,
434 hw::InnerSymAttr innerSym) {
435 if (!name)
436 name = builder.getStringAttr("");
437 odsState.addAttribute("name", name);
438 if (innerSym)
439 odsState.addAttribute(hw::InnerSymbolTable::getInnerSymbolAttrName(),
440 innerSym);
441 odsState.addTypes(hw::InOutType::get(elementType));
442}
443
444/// Suggest a name for each result value based on the saved result names
445/// attribute.
446void LogicOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
447 // If the logic has an optional 'name' attribute, use it.
448 auto nameAttr = (*this)->getAttrOfType<StringAttr>("name");
449 if (!nameAttr.getValue().empty())
450 setNameFn(getResult(), nameAttr.getValue());
451}
452
453std::optional<size_t> LogicOp::getTargetResultIndex() { return 0; }
454
455//===----------------------------------------------------------------------===//
456// Control flow like-operations
457//===----------------------------------------------------------------------===//
458
459//===----------------------------------------------------------------------===//
460// IfDefOp
461//===----------------------------------------------------------------------===//
462
463void IfDefOp::build(OpBuilder &builder, OperationState &result, StringRef cond,
464 std::function<void()> thenCtor,
465 std::function<void()> elseCtor) {
466 build(builder, result, builder.getStringAttr(cond), std::move(thenCtor),
467 std::move(elseCtor));
468}
469
470void IfDefOp::build(OpBuilder &builder, OperationState &result, StringAttr cond,
471 std::function<void()> thenCtor,
472 std::function<void()> elseCtor) {
473 build(builder, result, FlatSymbolRefAttr::get(builder.getContext(), cond),
474 std::move(thenCtor), std::move(elseCtor));
475}
476
477void IfDefOp::build(OpBuilder &builder, OperationState &result,
478 FlatSymbolRefAttr cond, std::function<void()> thenCtor,
479 std::function<void()> elseCtor) {
480 build(builder, result, MacroIdentAttr::get(builder.getContext(), cond),
481 std::move(thenCtor), std::move(elseCtor));
482}
483
484void IfDefOp::build(OpBuilder &builder, OperationState &result,
485 MacroIdentAttr cond, std::function<void()> thenCtor,
486 std::function<void()> elseCtor) {
487 OpBuilder::InsertionGuard guard(builder);
488
489 result.addAttribute("cond", cond);
490 builder.createBlock(result.addRegion());
491
492 // Fill in the body of the #ifdef.
493 if (thenCtor)
494 thenCtor();
495
496 Region *elseRegion = result.addRegion();
497 if (elseCtor) {
498 builder.createBlock(elseRegion);
499 elseCtor();
500 }
501}
502
503LogicalResult IfDefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
504 return verifyMacroIdentSymbolUses(*this, getCond().getIdent(), symbolTable);
505}
506
507// If both thenRegion and elseRegion are empty, erase op.
508template <class Op>
509static LogicalResult canonicalizeIfDefLike(Op op, PatternRewriter &rewriter) {
510 if (!op.getThenBlock()->empty())
511 return failure();
512
513 if (op.hasElse() && !op.getElseBlock()->empty())
514 return failure();
515
516 rewriter.eraseOp(op);
517 return success();
518}
519
520LogicalResult IfDefOp::canonicalize(IfDefOp op, PatternRewriter &rewriter) {
521 return canonicalizeIfDefLike(op, rewriter);
522}
523
524//===----------------------------------------------------------------------===//
525// Helper functions
526//===----------------------------------------------------------------------===//
527
529 ArrayRef<StringAttr> macroSymbols,
530 llvm::function_ref<void(StringAttr, std::function<void()>,
531 std::function<void()>)>
532 ifdefCtor,
533 llvm::function_ref<void(size_t)> thenCtor,
534 llvm::function_ref<void()> defaultCtor) {
535
536 // Helper function to recursively build nested ifdefs
537 std::function<void(size_t)> buildNested = [&](size_t index) {
538 if (index >= macroSymbols.size()) {
539 // Base case: we've processed all macros, call the default
540 if (defaultCtor)
541 defaultCtor();
542 return;
543 }
544
545 // Create an ifdef for the current macro
546 ifdefCtor(
547 macroSymbols[index],
548 /*thenCtor=*/
549 [&, index]() {
550 if (thenCtor)
551 thenCtor(index);
552 },
553 /*elseCtor=*/
554 [&, index]() { buildNested(index + 1); });
555 };
556
557 buildNested(0);
558}
559
560//===----------------------------------------------------------------------===//
561// IfDefProceduralOp
562//===----------------------------------------------------------------------===//
563
564void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
565 StringRef cond, std::function<void()> thenCtor,
566 std::function<void()> elseCtor) {
567 build(builder, result, builder.getStringAttr(cond), std::move(thenCtor),
568 std::move(elseCtor));
569}
570
571void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
572 StringAttr cond, std::function<void()> thenCtor,
573 std::function<void()> elseCtor) {
574 build(builder, result, FlatSymbolRefAttr::get(builder.getContext(), cond),
575 std::move(thenCtor), std::move(elseCtor));
576}
577
578void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
579 FlatSymbolRefAttr cond,
580 std::function<void()> thenCtor,
581 std::function<void()> elseCtor) {
582 build(builder, result, MacroIdentAttr::get(builder.getContext(), cond),
583 std::move(thenCtor), std::move(elseCtor));
584}
585
586void IfDefProceduralOp::build(OpBuilder &builder, OperationState &result,
587 MacroIdentAttr cond,
588 std::function<void()> thenCtor,
589 std::function<void()> elseCtor) {
590 OpBuilder::InsertionGuard guard(builder);
591
592 result.addAttribute("cond", cond);
593 builder.createBlock(result.addRegion());
594
595 // Fill in the body of the #ifdef.
596 if (thenCtor)
597 thenCtor();
598
599 Region *elseRegion = result.addRegion();
600 if (elseCtor) {
601 builder.createBlock(elseRegion);
602 elseCtor();
603 }
604}
605
606LogicalResult IfDefProceduralOp::canonicalize(IfDefProceduralOp op,
607 PatternRewriter &rewriter) {
608 return canonicalizeIfDefLike(op, rewriter);
609}
610
611LogicalResult
612IfDefProceduralOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
613 return verifyMacroIdentSymbolUses(*this, getCond().getIdent(), symbolTable);
614}
615
616//===----------------------------------------------------------------------===//
617// IfOp
618//===----------------------------------------------------------------------===//
619
620void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
621 std::function<void()> thenCtor,
622 std::function<void()> elseCtor) {
623 OpBuilder::InsertionGuard guard(builder);
624
625 result.addOperands(cond);
626 builder.createBlock(result.addRegion());
627
628 // Fill in the body of the if.
629 if (thenCtor)
630 thenCtor();
631
632 Region *elseRegion = result.addRegion();
633 if (elseCtor) {
634 builder.createBlock(elseRegion);
635 elseCtor();
636 }
637}
638
639/// Replaces the given op with the contents of the given single-block region.
640static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op,
641 Region &region) {
642 assert(llvm::hasSingleElement(region) && "expected single-region block");
643 Block *fromBlock = &region.front();
644 // Merge it in above the specified operation.
645 op->getBlock()->getOperations().splice(Block::iterator(op),
646 fromBlock->getOperations());
647}
648
649LogicalResult IfOp::canonicalize(IfOp op, PatternRewriter &rewriter) {
650 // Block if op has SV attributes.
651 if (hasSVAttributes(op))
652 return failure();
653
654 if (auto constant = op.getCond().getDefiningOp<hw::ConstantOp>()) {
655
656 if (constant.getValue().isAllOnes())
657 replaceOpWithRegion(rewriter, op, op.getThenRegion());
658 else if (!op.getElseRegion().empty())
659 replaceOpWithRegion(rewriter, op, op.getElseRegion());
660
661 rewriter.eraseOp(op);
662
663 return success();
664 }
665
666 // Erase empty if-else block.
667 if (!op.getThenBlock()->empty() && op.hasElse() &&
668 op.getElseBlock()->empty()) {
669 rewriter.eraseBlock(op.getElseBlock());
670 return success();
671 }
672
673 // Erase empty if's.
674
675 // If there is stuff in the then block, leave this operation alone.
676 if (!op.getThenBlock()->empty())
677 return failure();
678
679 // If not and there is no else, then this operation is just useless.
680 if (!op.hasElse() || op.getElseBlock()->empty()) {
681 rewriter.eraseOp(op);
682 return success();
683 }
684
685 // Otherwise, invert the condition and move the 'else' block to the 'then'
686 // region if the condition is a 2-state operation. This changes x prop
687 // behavior so it needs to be guarded.
688 if (is2StateExpression(op.getCond())) {
689 auto cond = comb::createOrFoldNot(rewriter, op.getLoc(), op.getCond());
690 op.setOperand(cond);
691
692 auto *thenBlock = op.getThenBlock(), *elseBlock = op.getElseBlock();
693
694 // Move the body of the then block over to the else.
695 thenBlock->getOperations().splice(thenBlock->end(),
696 elseBlock->getOperations());
697 rewriter.eraseBlock(elseBlock);
698 return success();
699 }
700 return failure();
701}
702
703//===----------------------------------------------------------------------===//
704// AlwaysOp
705//===----------------------------------------------------------------------===//
706
707AlwaysOp::Condition AlwaysOp::getCondition(size_t idx) {
708 return Condition{EventControl(cast<IntegerAttr>(getEvents()[idx]).getInt()),
709 getOperand(idx)};
710}
711
712void AlwaysOp::build(OpBuilder &builder, OperationState &result,
713 ArrayRef<sv::EventControl> events, ArrayRef<Value> clocks,
714 std::function<void()> bodyCtor) {
715 assert(events.size() == clocks.size() &&
716 "mismatch between event and clock list");
717 OpBuilder::InsertionGuard guard(builder);
718
719 SmallVector<Attribute> eventAttrs;
720 for (auto event : events)
721 eventAttrs.push_back(
722 builder.getI32IntegerAttr(static_cast<int32_t>(event)));
723 result.addAttribute("events", builder.getArrayAttr(eventAttrs));
724 result.addOperands(clocks);
725
726 // Set up the body. Moves the insert point
727 builder.createBlock(result.addRegion());
728
729 // Fill in the body of the #ifdef.
730 if (bodyCtor)
731 bodyCtor();
732}
733
734/// Ensure that the symbol being instantiated exists and is an InterfaceOp.
735LogicalResult AlwaysOp::verify() {
736 if (getEvents().size() != getNumOperands())
737 return emitError("different number of operands and events");
738 return success();
739}
740
741static ParseResult parseEventList(
742 OpAsmParser &p, Attribute &eventsAttr,
743 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &clocksOperands) {
744
745 // Parse zero or more conditions intoevents and clocksOperands.
746 SmallVector<Attribute> events;
747
748 auto loc = p.getCurrentLocation();
749 StringRef keyword;
750 if (!p.parseOptionalKeyword(&keyword)) {
751 while (1) {
752 auto kind = sv::symbolizeEventControl(keyword);
753 if (!kind.has_value())
754 return p.emitError(loc, "expected 'posedge', 'negedge', or 'edge'");
755 auto eventEnum = static_cast<int32_t>(*kind);
756 events.push_back(p.getBuilder().getI32IntegerAttr(eventEnum));
757
758 clocksOperands.push_back({});
759 if (p.parseOperand(clocksOperands.back()))
760 return failure();
761
762 if (failed(p.parseOptionalComma()))
763 break;
764 if (p.parseKeyword(&keyword))
765 return failure();
766 }
767 }
768 eventsAttr = p.getBuilder().getArrayAttr(events);
769 return success();
770}
771
772static void printEventList(OpAsmPrinter &p, AlwaysOp op, ArrayAttr portsAttr,
773 OperandRange operands) {
774 for (size_t i = 0, e = op.getNumConditions(); i != e; ++i) {
775 if (i != 0)
776 p << ", ";
777 auto cond = op.getCondition(i);
778 p << stringifyEventControl(cond.event);
779 p << ' ';
780 p.printOperand(cond.value);
781 }
782}
783
784//===----------------------------------------------------------------------===//
785// AlwaysFFOp
786//===----------------------------------------------------------------------===//
787
788void AlwaysFFOp::build(OpBuilder &builder, OperationState &result,
789 EventControl clockEdge, Value clock,
790 std::function<void()> bodyCtor) {
791 OpBuilder::InsertionGuard guard(builder);
792
793 result.addAttribute(
794 "clockEdge", builder.getI32IntegerAttr(static_cast<int32_t>(clockEdge)));
795 result.addOperands(clock);
796 result.addAttribute(
797 "resetStyle",
798 builder.getI32IntegerAttr(static_cast<int32_t>(ResetType::NoReset)));
799
800 // Set up the body. Moves Insert Point
801 builder.createBlock(result.addRegion());
802
803 if (bodyCtor)
804 bodyCtor();
805
806 // Set up the reset region.
807 result.addRegion();
808}
809
810void AlwaysFFOp::build(OpBuilder &builder, OperationState &result,
811 EventControl clockEdge, Value clock,
812 ResetType resetStyle, EventControl resetEdge,
813 Value reset, std::function<void()> bodyCtor,
814 std::function<void()> resetCtor) {
815 OpBuilder::InsertionGuard guard(builder);
816
817 result.addAttribute(
818 "clockEdge", builder.getI32IntegerAttr(static_cast<int32_t>(clockEdge)));
819 result.addOperands(clock);
820 result.addAttribute("resetStyle", builder.getI32IntegerAttr(
821 static_cast<int32_t>(resetStyle)));
822 result.addAttribute(
823 "resetEdge", builder.getI32IntegerAttr(static_cast<int32_t>(resetEdge)));
824 result.addOperands(reset);
825
826 // Set up the body. Moves Insert Point.
827 builder.createBlock(result.addRegion());
828
829 if (bodyCtor)
830 bodyCtor();
831
832 // Set up the reset. Moves Insert Point.
833 builder.createBlock(result.addRegion());
834
835 if (resetCtor)
836 resetCtor();
837}
838
839//===----------------------------------------------------------------------===//
840// AlwaysCombOp
841//===----------------------------------------------------------------------===//
842
843void AlwaysCombOp::build(OpBuilder &builder, OperationState &result,
844 std::function<void()> bodyCtor) {
845 OpBuilder::InsertionGuard guard(builder);
846
847 builder.createBlock(result.addRegion());
848
849 if (bodyCtor)
850 bodyCtor();
851}
852
853//===----------------------------------------------------------------------===//
854// InitialOp
855//===----------------------------------------------------------------------===//
856
857void InitialOp::build(OpBuilder &builder, OperationState &result,
858 std::function<void()> bodyCtor) {
859 OpBuilder::InsertionGuard guard(builder);
860
861 builder.createBlock(result.addRegion());
862
863 // Fill in the body of the #ifdef.
864 if (bodyCtor)
865 bodyCtor();
866}
867
868//===----------------------------------------------------------------------===//
869// CaseOp
870//===----------------------------------------------------------------------===//
871
872/// Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
874 switch (bit) {
876 return '0';
878 return '1';
880 return 'x';
882 return 'z';
883 }
884 llvm_unreachable("invalid casez PatternBit");
885}
886
887/// Return the specified bit, bit 0 is the least significant bit.
888auto CaseBitPattern::getBit(size_t bitNumber) const -> CasePatternBit {
889 return CasePatternBit(unsigned(intAttr.getValue()[bitNumber * 2]) +
890 2 * unsigned(intAttr.getValue()[bitNumber * 2 + 1]));
891}
892
894 for (size_t i = 0, e = getWidth(); i != e; ++i)
895 if (getBit(i) == CasePatternBit::AnyX)
896 return true;
897 return false;
898}
899
901 for (size_t i = 0, e = getWidth(); i != e; ++i)
902 if (getBit(i) == CasePatternBit::AnyZ)
903 return true;
904 return false;
905}
906static SmallVector<CasePatternBit> getPatternBitsForValue(const APInt &value) {
907 SmallVector<CasePatternBit> result;
908 result.reserve(value.getBitWidth());
909 for (size_t i = 0, e = value.getBitWidth(); i != e; ++i)
910 result.push_back(CasePatternBit(value[i]));
911
912 return result;
913}
914
915// Get a CaseBitPattern from a specified list of PatternBits. Bits are
916// specified in most least significant order - element zero is the least
917// significant bit.
918CaseBitPattern::CaseBitPattern(const APInt &value, MLIRContext *context)
920
921// Get a CaseBitPattern from a specified list of PatternBits. Bits are
922// specified in most least significant order - element zero is the least
923// significant bit.
924CaseBitPattern::CaseBitPattern(ArrayRef<CasePatternBit> bits,
925 MLIRContext *context)
926 : CasePattern(CPK_bit) {
927 APInt pattern(bits.size() * 2, 0);
928 for (auto elt : llvm::reverse(bits)) {
929 pattern <<= 2;
930 pattern |= unsigned(elt);
931 }
932 auto patternType = IntegerType::get(context, bits.size() * 2);
933 intAttr = IntegerAttr::get(patternType, pattern);
934}
935
936auto CaseOp::getCases() -> SmallVector<CaseInfo, 4> {
937 SmallVector<CaseInfo, 4> result;
938 assert(getCasePatterns().size() == getNumRegions() &&
939 "case pattern / region count mismatch");
940 size_t nextRegion = 0;
941 for (auto elt : getCasePatterns()) {
942 llvm::TypeSwitch<Attribute>(elt)
943 .Case<hw::EnumFieldAttr>([&](auto enumAttr) {
944 result.push_back({std::make_unique<CaseEnumPattern>(enumAttr),
945 &getRegion(nextRegion++).front()});
946 })
947 .Case<CaseExprPatternAttr>([&](auto exprAttr) {
948 result.push_back({std::make_unique<CaseExprPattern>(getContext()),
949 &getRegion(nextRegion++).front()});
950 })
951 .Case<IntegerAttr>([&](auto intAttr) {
952 result.push_back({std::make_unique<CaseBitPattern>(intAttr),
953 &getRegion(nextRegion++).front()});
954 })
955 .Case<CaseDefaultPattern::AttrType>([&](auto) {
956 result.push_back({std::make_unique<CaseDefaultPattern>(getContext()),
957 &getRegion(nextRegion++).front()});
958 })
959 .Default([](auto) {
960 assert(false && "invalid case pattern attribute type");
961 });
962 }
963
964 return result;
965}
966
968 return cast<hw::EnumFieldAttr>(enumAttr).getField();
969}
970
971/// Parse case op.
972/// case op ::= `sv.case` case-style? validation-qualifier? cond `:` type
973/// attr-dict case-pattern^*
974/// case-style ::= `case` | `casex` | `casez`
975/// validation-qualifier (see SV Spec 12.5.3) ::= `unique` | `unique0`
976/// | `priority`
977/// case-pattern ::= `case` bit-pattern `:` region
978ParseResult CaseOp::parse(OpAsmParser &parser, OperationState &result) {
979 auto &builder = parser.getBuilder();
980
981 OpAsmParser::UnresolvedOperand condOperand;
982 Type condType;
983
984 auto loc = parser.getCurrentLocation();
985
986 StringRef keyword;
987 if (!parser.parseOptionalKeyword(&keyword, {"case", "casex", "casez"})) {
988 auto kind = symbolizeCaseStmtType(keyword);
989 auto caseEnum = static_cast<int32_t>(kind.value());
990 result.addAttribute("caseStyle", builder.getI32IntegerAttr(caseEnum));
991 }
992
993 // Parse validation qualifier.
994 if (!parser.parseOptionalKeyword(
995 &keyword, {"plain", "priority", "unique", "unique0"})) {
996 auto kind = symbolizeValidationQualifierTypeEnum(keyword);
997 result.addAttribute("validationQualifier",
998 ValidationQualifierTypeEnumAttr::get(
999 builder.getContext(), kind.value()));
1000 }
1001
1002 if (parser.parseOperand(condOperand) || parser.parseColonType(condType) ||
1003 parser.parseOptionalAttrDict(result.attributes) ||
1004 parser.resolveOperand(condOperand, condType, result.operands))
1005 return failure();
1006
1007 // Check the integer type.
1008 Type canonicalCondType = hw::getCanonicalType(condType);
1009 hw::EnumType enumType = dyn_cast<hw::EnumType>(canonicalCondType);
1010 unsigned condWidth = 0;
1011 if (!enumType) {
1012 if (!result.operands[0].getType().isSignlessInteger())
1013 return parser.emitError(loc, "condition must have signless integer type");
1014 condWidth = condType.getIntOrFloatBitWidth();
1015 }
1016
1017 // Parse all the cases.
1018 SmallVector<Attribute> casePatterns;
1019 SmallVector<CasePatternBit, 16> caseBits;
1020 while (1) {
1021 mlir::OptionalParseResult caseValueParseResult;
1022 OpAsmParser::UnresolvedOperand caseValueOperand;
1023 if (succeeded(parser.parseOptionalKeyword("default"))) {
1024 casePatterns.push_back(CaseDefaultPattern(parser.getContext()).attr());
1025 } else if (failed(parser.parseOptionalKeyword("case"))) {
1026 // Not default or case, must be the end of the cases.
1027 break;
1028 } else if (enumType) {
1029 // Enumerated case; parse the case value.
1030 StringRef caseVal;
1031
1032 if (parser.parseKeyword(&caseVal))
1033 return failure();
1034
1035 if (!enumType.contains(caseVal))
1036 return parser.emitError(loc)
1037 << "case value '" + caseVal + "' is not a member of enum type "
1038 << enumType;
1039 casePatterns.push_back(
1040 hw::EnumFieldAttr::get(parser.getEncodedSourceLoc(loc),
1041 builder.getStringAttr(caseVal), condType));
1042 } else if ((caseValueParseResult =
1043 parser.parseOptionalOperand(caseValueOperand))
1044 .has_value()) {
1045 if (failed(caseValueParseResult.value()) ||
1046 parser.resolveOperand(caseValueOperand, condType, result.operands))
1047 return failure();
1048 casePatterns.push_back(CaseExprPattern(parser.getContext()).attr());
1049 } else {
1050 // Parse the pattern. It always starts with b, so it is an MLIR
1051 // keyword.
1052 StringRef caseVal;
1053 loc = parser.getCurrentLocation();
1054 if (parser.parseKeyword(&caseVal))
1055 return failure();
1056
1057 if (caseVal.front() != 'b')
1058 return parser.emitError(loc, "expected case value starting with 'b'");
1059 caseVal = caseVal.drop_front();
1060
1061 // Parse and decode each bit, we reverse the list later for MSB->LSB.
1062 for (; !caseVal.empty(); caseVal = caseVal.drop_front()) {
1063 CasePatternBit bit;
1064 switch (caseVal.front()) {
1065 case '0':
1067 break;
1068 case '1':
1069 bit = CasePatternBit::One;
1070 break;
1071 case 'x':
1073 break;
1074 case 'z':
1076 break;
1077 default:
1078 return parser.emitError(loc, "unexpected case bit '")
1079 << caseVal.front() << "'";
1080 }
1081 caseBits.push_back(bit);
1082 }
1083
1084 if (caseVal.size() > condWidth)
1085 return parser.emitError(loc, "too many bits specified in pattern");
1086 std::reverse(caseBits.begin(), caseBits.end());
1087
1088 // High zeros may be missing.
1089 if (caseBits.size() < condWidth)
1090 caseBits.append(condWidth - caseBits.size(), CasePatternBit::Zero);
1091
1092 auto resultPattern = CaseBitPattern(caseBits, builder.getContext());
1093 casePatterns.push_back(resultPattern.attr());
1094 caseBits.clear();
1095 }
1096
1097 // Parse the case body.
1098 auto caseRegion = std::make_unique<Region>();
1099 if (parser.parseColon() || parser.parseRegion(*caseRegion))
1100 return failure();
1101 result.addRegion(std::move(caseRegion));
1102 }
1103
1104 result.addAttribute("casePatterns", builder.getArrayAttr(casePatterns));
1105 return success();
1106}
1107
1108void CaseOp::print(OpAsmPrinter &p) {
1109 p << ' ';
1110 if (getCaseStyle() == CaseStmtType::CaseXStmt)
1111 p << "casex ";
1112 else if (getCaseStyle() == CaseStmtType::CaseZStmt)
1113 p << "casez ";
1114
1115 if (getValidationQualifier() !=
1116 ValidationQualifierTypeEnum::ValidationQualifierPlain)
1117 p << stringifyValidationQualifierTypeEnum(getValidationQualifier()) << ' ';
1118
1119 p << getCond() << " : " << getCond().getType();
1120 p.printOptionalAttrDict(
1121 (*this)->getAttrs(),
1122 /*elidedAttrs=*/{"casePatterns", "caseStyle", "validationQualifier"});
1123
1124 size_t caseValueIndex = 0;
1125 for (auto &caseInfo : getCases()) {
1126 p.printNewline();
1127 auto &pattern = caseInfo.pattern;
1128
1129 llvm::TypeSwitch<CasePattern *>(pattern.get())
1130 .Case<CaseBitPattern>([&](auto bitPattern) {
1131 p << "case b";
1132 for (size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
1133 p << getLetter(bitPattern->getBit(e - bit - 1));
1134 })
1135 .Case<CaseEnumPattern>([&](auto enumPattern) {
1136 p << "case " << enumPattern->getFieldValue();
1137 })
1138 .Case<CaseExprPattern>([&](auto) {
1139 p << "case ";
1140 p.printOperand(getCaseValues()[caseValueIndex++]);
1141 })
1142 .Case<CaseDefaultPattern>([&](auto) { p << "default"; })
1143 .Default([&](auto) { assert(false && "unhandled case pattern"); });
1144
1145 p << ": ";
1146 p.printRegion(*caseInfo.block->getParent(), /*printEntryBlockArgs=*/false,
1147 /*printBlockTerminators=*/true);
1148 }
1149}
1150
1151LogicalResult CaseOp::verify() {
1152 if (!(hw::isHWIntegerType(getCond().getType()) ||
1153 hw::isHWEnumType(getCond().getType())))
1154 return emitError("condition must have either integer or enum type");
1155
1156 // Ensure that the number of regions and number of case values match.
1157 if (getCasePatterns().size() != getNumRegions())
1158 return emitOpError("case pattern / region count mismatch");
1159 return success();
1160}
1161
1162/// This ctor allows you to build a CaseZ with some number of cases, getting
1163/// a callback for each case.
1164void CaseOp::build(
1165 OpBuilder &builder, OperationState &result, CaseStmtType caseStyle,
1166 ValidationQualifierTypeEnum validationQualifier, Value cond,
1167 size_t numCases,
1168 std::function<std::unique_ptr<CasePattern>(size_t)> caseCtor) {
1169 result.addOperands(cond);
1170 result.addAttribute("caseStyle",
1171 CaseStmtTypeAttr::get(builder.getContext(), caseStyle));
1172 result.addAttribute("validationQualifier",
1173 ValidationQualifierTypeEnumAttr::get(
1174 builder.getContext(), validationQualifier));
1175 SmallVector<Attribute> casePatterns;
1176
1177 OpBuilder::InsertionGuard guard(builder);
1178
1179 // Fill in the cases with the callback.
1180 for (size_t i = 0, e = numCases; i != e; ++i) {
1181 builder.createBlock(result.addRegion());
1182 casePatterns.push_back(caseCtor(i)->attr());
1183 }
1184
1185 result.addAttribute("casePatterns", builder.getArrayAttr(casePatterns));
1186}
1187
1188// Strength reduce case styles based on the bit patterns.
1189LogicalResult CaseOp::canonicalize(CaseOp op, PatternRewriter &rewriter) {
1190 if (op.getCaseStyle() == CaseStmtType::CaseStmt)
1191 return failure();
1192 if (isa<hw::EnumType>(op.getCond().getType()))
1193 return failure();
1194
1195 auto caseInfo = op.getCases();
1196 bool noXZ = llvm::all_of(caseInfo, [](const CaseInfo &ci) {
1197 return !ci.pattern.get()->hasX() && !ci.pattern.get()->hasZ();
1198 });
1199 bool noX = llvm::all_of(caseInfo, [](const CaseInfo &ci) {
1200 if (isa<CaseDefaultPattern>(ci.pattern))
1201 return true;
1202 return !ci.pattern.get()->hasX();
1203 });
1204 bool noZ = llvm::all_of(caseInfo, [](const CaseInfo &ci) {
1205 if (isa<CaseDefaultPattern>(ci.pattern))
1206 return true;
1207 return !ci.pattern.get()->hasZ();
1208 });
1209
1210 if (op.getCaseStyle() == CaseStmtType::CaseXStmt) {
1211 if (noXZ) {
1212 rewriter.modifyOpInPlace(op, [&]() {
1213 op.setCaseStyleAttr(
1214 CaseStmtTypeAttr::get(op.getContext(), CaseStmtType::CaseStmt));
1215 });
1216 return success();
1217 }
1218 if (noX) {
1219 rewriter.modifyOpInPlace(op, [&]() {
1220 op.setCaseStyleAttr(
1221 CaseStmtTypeAttr::get(op.getContext(), CaseStmtType::CaseZStmt));
1222 });
1223 return success();
1224 }
1225 }
1226
1227 if (op.getCaseStyle() == CaseStmtType::CaseZStmt && noZ) {
1228 rewriter.modifyOpInPlace(op, [&]() {
1229 op.setCaseStyleAttr(
1230 CaseStmtTypeAttr::get(op.getContext(), CaseStmtType::CaseStmt));
1231 });
1232 return success();
1233 }
1234
1235 return failure();
1236}
1237
1238//===----------------------------------------------------------------------===//
1239// OrderedOutputOp
1240//===----------------------------------------------------------------------===//
1241
1242void OrderedOutputOp::build(OpBuilder &builder, OperationState &result,
1243 std::function<void()> body) {
1244 OpBuilder::InsertionGuard guard(builder);
1245
1246 builder.createBlock(result.addRegion());
1247
1248 // Fill in the body of the ordered block.
1249 if (body)
1250 body();
1251}
1252
1253//===----------------------------------------------------------------------===//
1254// ForOp
1255//===----------------------------------------------------------------------===//
1256
1257void ForOp::build(OpBuilder &builder, OperationState &result,
1258 int64_t lowerBound, int64_t upperBound, int64_t step,
1259 IntegerType type, StringRef name,
1260 llvm::function_ref<void(BlockArgument)> body) {
1261 auto lb = hw::ConstantOp::create(builder, result.location, type, lowerBound);
1262 auto ub = hw::ConstantOp::create(builder, result.location, type, upperBound);
1263 auto st = hw::ConstantOp::create(builder, result.location, type, step);
1264 build(builder, result, lb, ub, st, name, body);
1265}
1266void ForOp::build(OpBuilder &builder, OperationState &result, Value lowerBound,
1267 Value upperBound, Value step, StringRef name,
1268 llvm::function_ref<void(BlockArgument)> body) {
1269 OpBuilder::InsertionGuard guard(builder);
1270 build(builder, result, lowerBound, upperBound, step, name);
1271 auto *region = result.regions.front().get();
1272 builder.createBlock(region);
1273 BlockArgument blockArgument =
1274 region->addArgument(lowerBound.getType(), result.location);
1275
1276 if (body)
1277 body(blockArgument);
1278}
1279
1280void ForOp::getAsmBlockArgumentNames(mlir::Region &region,
1281 mlir::OpAsmSetValueNameFn setNameFn) {
1282 auto *block = &region.front();
1283 setNameFn(block->getArgument(0), getInductionVarNameAttr());
1284}
1285
1286ParseResult ForOp::parse(OpAsmParser &parser, OperationState &result) {
1287 auto &builder = parser.getBuilder();
1288 Type type;
1289
1290 OpAsmParser::Argument inductionVariable;
1291 OpAsmParser::UnresolvedOperand lb, ub, step;
1292 // Parse the optional initial iteration arguments.
1293 SmallVector<OpAsmParser::Argument, 4> regionArgs;
1294
1295 // Parse the induction variable followed by '='.
1296 if (parser.parseOperand(inductionVariable.ssaName) || parser.parseEqual() ||
1297 // Parse loop bounds.
1298 parser.parseOperand(lb) || parser.parseKeyword("to") ||
1299 parser.parseOperand(ub) || parser.parseKeyword("step") ||
1300 parser.parseOperand(step) || parser.parseColon() ||
1301 parser.parseType(type))
1302 return failure();
1303
1304 regionArgs.push_back(inductionVariable);
1305
1306 // Resolve input operands.
1307 regionArgs.front().type = type;
1308 if (parser.resolveOperand(lb, type, result.operands) ||
1309 parser.resolveOperand(ub, type, result.operands) ||
1310 parser.resolveOperand(step, type, result.operands))
1311 return failure();
1312
1313 // Parse the body region.
1314 Region *body = result.addRegion();
1315 if (parser.parseRegion(*body, regionArgs))
1316 return failure();
1317
1318 // Parse the optional attribute list.
1319 if (parser.parseOptionalAttrDict(result.attributes))
1320 return failure();
1321
1322 if (!inductionVariable.ssaName.name.empty()) {
1323 if (!isdigit(inductionVariable.ssaName.name[1]))
1324 // Retrive from its SSA name.
1325 result.attributes.append(
1326 {builder.getStringAttr("inductionVarName"),
1327 builder.getStringAttr(inductionVariable.ssaName.name.drop_front())});
1328 }
1329
1330 return success();
1331}
1332
1333void ForOp::print(OpAsmPrinter &p) {
1334 p << " " << getInductionVar() << " = " << getLowerBound() << " to "
1335 << getUpperBound() << " step " << getStep();
1336 p << " : " << getInductionVar().getType() << ' ';
1337 p.printRegion(getRegion(),
1338 /*printEntryBlockArgs=*/false,
1339 /*printBlockTerminators=*/false);
1340 p.printOptionalAttrDict((*this)->getAttrs(), {"inductionVarName"});
1341}
1342
1343LogicalResult ForOp::canonicalize(ForOp op, PatternRewriter &rewriter) {
1344 APInt lb, ub, step;
1345 if (matchPattern(op.getLowerBound(), mlir::m_ConstantInt(&lb)) &&
1346 matchPattern(op.getUpperBound(), mlir::m_ConstantInt(&ub)) &&
1347 matchPattern(op.getStep(), mlir::m_ConstantInt(&step)) &&
1348 lb + step == ub) {
1349 // Unroll the loop if it's executed only once.
1350 rewriter.replaceAllUsesWith(op.getInductionVar(), op.getLowerBound());
1351 replaceOpWithRegion(rewriter, op, op.getBodyRegion());
1352 rewriter.eraseOp(op);
1353 return success();
1354 }
1355 return failure();
1356}
1357
1358//===----------------------------------------------------------------------===//
1359// Assignment statements
1360//===----------------------------------------------------------------------===//
1361
1362LogicalResult BPAssignOp::verify() {
1363 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1364 return emitOpError(
1365 "Verilog disallows procedural assignment to a net type (did you intend "
1366 "to use a variable type, e.g., sv.reg?)");
1367 return success();
1368}
1369
1370LogicalResult PAssignOp::verify() {
1371 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1372 return emitOpError(
1373 "Verilog disallows procedural assignment to a net type (did you intend "
1374 "to use a variable type, e.g., sv.reg?)");
1375 return success();
1376}
1377
1378namespace {
1379// This represents a slice of an array.
1380struct ArraySlice {
1381 Value array;
1382 Value start;
1383 size_t size; // Represent a range array[start, start + size).
1384
1385 // Get a struct from the value. Return std::nullopt if the value doesn't
1386 // represent an array slice.
1387 static std::optional<ArraySlice> getArraySlice(Value v) {
1388 auto *op = v.getDefiningOp();
1389 if (!op)
1390 return std::nullopt;
1391 return TypeSwitch<Operation *, std::optional<ArraySlice>>(op)
1392 .Case<hw::ArrayGetOp, ArrayIndexInOutOp>(
1393 [](auto arrayIndex) -> std::optional<ArraySlice> {
1394 hw::ConstantOp constant =
1395 arrayIndex.getIndex()
1396 .template getDefiningOp<hw::ConstantOp>();
1397 if (!constant)
1398 return std::nullopt;
1399 return ArraySlice{/*array=*/arrayIndex.getInput(),
1400 /*start=*/constant,
1401 /*end=*/1};
1402 })
1403 .Case<hw::ArraySliceOp>([](hw::ArraySliceOp slice)
1404 -> std::optional<ArraySlice> {
1405 auto constant = slice.getLowIndex().getDefiningOp<hw::ConstantOp>();
1406 if (!constant)
1407 return std::nullopt;
1408 return ArraySlice{
1409 /*array=*/slice.getInput(), /*start=*/constant,
1410 /*end=*/
1411 hw::type_cast<hw::ArrayType>(slice.getType()).getNumElements()};
1412 })
1413 .Case<sv::IndexedPartSelectInOutOp>(
1414 [](sv::IndexedPartSelectInOutOp index)
1415 -> std::optional<ArraySlice> {
1416 auto constant = index.getBase().getDefiningOp<hw::ConstantOp>();
1417 if (!constant || index.getDecrement())
1418 return std::nullopt;
1419 return ArraySlice{/*array=*/index.getInput(),
1420 /*start=*/constant,
1421 /*end=*/index.getWidth()};
1422 })
1423 .Default([](auto) { return std::nullopt; });
1424 }
1425
1426 // Create a pair of ArraySlice from source and destination of assignments.
1427 static std::optional<std::pair<ArraySlice, ArraySlice>>
1428 getAssignedRange(Operation *op) {
1429 assert((isa<PAssignOp, BPAssignOp>(op) && "assignments are expected"));
1430 auto srcRange = ArraySlice::getArraySlice(op->getOperand(1));
1431 if (!srcRange)
1432 return std::nullopt;
1433 auto destRange = ArraySlice::getArraySlice(op->getOperand(0));
1434 if (!destRange)
1435 return std::nullopt;
1436
1437 return std::make_pair(*destRange, *srcRange);
1438 }
1439};
1440} // namespace
1441
1442// This canonicalization merges neiboring assignments of array elements into
1443// array slice assignments. e.g.
1444// a[0] <= b[1]
1445// a[1] <= b[2]
1446// ->
1447// a[1:0] <= b[2:1]
1448template <typename AssignTy>
1449static LogicalResult mergeNeiboringAssignments(AssignTy op,
1450 PatternRewriter &rewriter) {
1451 // Get assigned ranges of each assignment.
1452 auto assignedRangeOpt = ArraySlice::getAssignedRange(op);
1453 if (!assignedRangeOpt)
1454 return failure();
1455
1456 auto [dest, src] = *assignedRangeOpt;
1457 AssignTy nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1458 bool changed = false;
1459 SmallVector<Location> loc{op.getLoc()};
1460 // Check that a next operation is a same kind of the assignment.
1461 while (nextAssign) {
1462 auto nextAssignedRange = ArraySlice::getAssignedRange(nextAssign);
1463 if (!nextAssignedRange)
1464 break;
1465 auto [nextDest, nextSrc] = *nextAssignedRange;
1466 // Check that these assignments are mergaable.
1467 if (dest.array != nextDest.array || src.array != nextSrc.array ||
1468 !hw::isOffset(dest.start, nextDest.start, dest.size) ||
1469 !hw::isOffset(src.start, nextSrc.start, src.size))
1470 break;
1471
1472 dest.size += nextDest.size;
1473 src.size += nextSrc.size;
1474 changed = true;
1475 loc.push_back(nextAssign.getLoc());
1476 rewriter.eraseOp(nextAssign);
1477 nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1478 }
1479
1480 if (!changed)
1481 return failure();
1482
1483 // From here, construct assignments of array slices.
1484 auto resultType = hw::ArrayType::get(
1485 hw::type_cast<hw::ArrayType>(src.array.getType()).getElementType(),
1486 src.size);
1487 auto newDest = sv::IndexedPartSelectInOutOp::create(
1488 rewriter, op.getLoc(), dest.array, dest.start, dest.size);
1489 auto newSrc = hw::ArraySliceOp::create(rewriter, op.getLoc(), resultType,
1490 src.array, src.start);
1491 auto newLoc = rewriter.getFusedLoc(loc);
1492 auto newOp = rewriter.replaceOpWithNewOp<AssignTy>(op, newDest, newSrc);
1493 newOp->setLoc(newLoc);
1494 return success();
1495}
1496
1497LogicalResult PAssignOp::canonicalize(PAssignOp op, PatternRewriter &rewriter) {
1498 return mergeNeiboringAssignments(op, rewriter);
1499}
1500
1501LogicalResult BPAssignOp::canonicalize(BPAssignOp op,
1502 PatternRewriter &rewriter) {
1503 return mergeNeiboringAssignments(op, rewriter);
1504}
1505
1506//===----------------------------------------------------------------------===//
1507// TypeDecl operations
1508//===----------------------------------------------------------------------===//
1509
1510void InterfaceOp::build(OpBuilder &builder, OperationState &result,
1511 StringRef sym_name, std::function<void()> body) {
1512 OpBuilder::InsertionGuard guard(builder);
1513
1514 result.addAttribute(InterfaceOp::getSymNameAttrName(result.name),
1515 builder.getStringAttr(sym_name));
1516 builder.createBlock(result.addRegion());
1517 if (body)
1518 body();
1519}
1520
1521ModportType InterfaceOp::getModportType(StringRef modportName) {
1522 assert(lookupSymbol<InterfaceModportOp>(modportName) &&
1523 "Modport symbol not found.");
1524 auto *ctxt = getContext();
1525 return ModportType::get(
1526 getContext(),
1527 SymbolRefAttr::get(ctxt, getSymName(),
1528 {SymbolRefAttr::get(ctxt, modportName)}));
1529}
1530
1531Type InterfaceOp::getSignalType(StringRef signalName) {
1532 InterfaceSignalOp signal = lookupSymbol<InterfaceSignalOp>(signalName);
1533 assert(signal && "Interface signal symbol not found.");
1534 return signal.getType();
1535}
1536
1537static ParseResult parseModportStructs(OpAsmParser &parser,
1538 ArrayAttr &portsAttr) {
1539
1540 auto *context = parser.getBuilder().getContext();
1541
1542 SmallVector<Attribute, 8> ports;
1543 auto parseElement = [&]() -> ParseResult {
1544 auto direction = ModportDirectionAttr::parse(parser, {});
1545 if (!direction)
1546 return failure();
1547
1548 FlatSymbolRefAttr signal;
1549 if (parser.parseAttribute(signal))
1550 return failure();
1551
1552 ports.push_back(ModportStructAttr::get(
1553 context, cast<ModportDirectionAttr>(direction), signal));
1554 return success();
1555 };
1556 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
1557 parseElement))
1558 return failure();
1559
1560 portsAttr = ArrayAttr::get(context, ports);
1561 return success();
1562}
1563
1564static void printModportStructs(OpAsmPrinter &p, Operation *,
1565 ArrayAttr portsAttr) {
1566 p << "(";
1567 llvm::interleaveComma(portsAttr, p, [&](Attribute attr) {
1568 auto port = cast<ModportStructAttr>(attr);
1569 p << stringifyEnum(port.getDirection().getValue());
1570 p << ' ';
1571 p.printSymbolName(port.getSignal().getRootReference().getValue());
1572 });
1573 p << ')';
1574}
1575
1576void InterfaceSignalOp::build(mlir::OpBuilder &builder,
1577 ::mlir::OperationState &state, StringRef name,
1578 mlir::Type type) {
1579 build(builder, state, name, /*sym_visibility=*/{}, mlir::TypeAttr::get(type));
1580}
1581
1582void InterfaceModportOp::build(OpBuilder &builder, OperationState &state,
1583 StringRef name, ArrayRef<StringRef> inputs,
1584 ArrayRef<StringRef> outputs) {
1585 auto *ctxt = builder.getContext();
1586 SmallVector<Attribute, 8> directions;
1587 auto inputDir = ModportDirectionAttr::get(ctxt, ModportDirection::input);
1588 auto outputDir = ModportDirectionAttr::get(ctxt, ModportDirection::output);
1589 for (auto input : inputs)
1590 directions.push_back(ModportStructAttr::get(
1591 ctxt, inputDir, SymbolRefAttr::get(ctxt, input)));
1592 for (auto output : outputs)
1593 directions.push_back(ModportStructAttr::get(
1594 ctxt, outputDir, SymbolRefAttr::get(ctxt, output)));
1595 build(builder, state, name, /*sym_visibility=*/{},
1596 ArrayAttr::get(ctxt, directions));
1597}
1598
1599std::optional<size_t> InterfaceInstanceOp::getTargetResultIndex() {
1600 // Inner symbols on instance operations target the op not any result.
1601 return std::nullopt;
1602}
1603
1604/// Suggest a name for each result value based on the saved result names
1605/// attribute.
1606void InterfaceInstanceOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1607 setNameFn(getResult(), getName());
1608}
1609
1610/// Ensure that the symbol being instantiated exists and is an InterfaceOp.
1611LogicalResult InterfaceInstanceOp::verify() {
1612 if (getName().empty())
1613 return emitOpError("requires non-empty name");
1614 return success();
1615}
1616
1617LogicalResult
1618InterfaceInstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1619 auto *symtable = SymbolTable::getNearestSymbolTable(*this);
1620 if (!symtable)
1621 return emitError("sv.interface.instance must exist within a region "
1622 "which has a symbol table.");
1623 auto ifaceTy = getType();
1624 auto *referencedOp =
1625 symbolTable.lookupSymbolIn(symtable, ifaceTy.getInterface());
1626 if (!referencedOp)
1627 return emitError("Symbol not found: ") << ifaceTy.getInterface() << ".";
1628 if (!isa<InterfaceOp>(referencedOp))
1629 return emitError("Symbol ")
1630 << ifaceTy.getInterface() << " is not an InterfaceOp.";
1631 return success();
1632}
1633
1634/// Ensure that the symbol being instantiated exists and is an
1635/// InterfaceModportOp.
1636LogicalResult
1637GetModportOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1638 auto *symtable = SymbolTable::getNearestSymbolTable(*this);
1639 if (!symtable)
1640 return emitError("sv.interface.instance must exist within a region "
1641 "which has a symbol table.");
1642
1643 auto ifaceTy = getType();
1644 auto *referencedOp =
1645 symbolTable.lookupSymbolIn(symtable, ifaceTy.getModport());
1646 if (!referencedOp)
1647 return emitError("Symbol not found: ") << ifaceTy.getModport() << ".";
1648 if (!isa<InterfaceModportOp>(referencedOp))
1649 return emitError("Symbol ")
1650 << ifaceTy.getModport() << " is not an InterfaceModportOp.";
1651 return success();
1652}
1653
1654void GetModportOp::build(OpBuilder &builder, OperationState &state, Value value,
1655 StringRef field) {
1656 auto ifaceTy = dyn_cast<InterfaceType>(value.getType());
1657 assert(ifaceTy && "GetModportOp expects an InterfaceType.");
1658 auto fieldAttr = SymbolRefAttr::get(builder.getContext(), field);
1659 auto modportSym =
1660 SymbolRefAttr::get(ifaceTy.getInterface().getRootReference(), fieldAttr);
1661 build(builder, state, ModportType::get(builder.getContext(), modportSym),
1662 value, fieldAttr);
1663}
1664
1665/// Lookup the op for the modport declaration. This returns null on invalid
1666/// IR.
1667InterfaceModportOp
1668GetModportOp::getReferencedDecl(const hw::HWSymbolCache &cache) {
1669 return dyn_cast_or_null<InterfaceModportOp>(
1670 cache.getDefinition(getFieldAttr()));
1671}
1672
1673void ReadInterfaceSignalOp::build(OpBuilder &builder, OperationState &state,
1674 Value iface, StringRef signalName) {
1675 auto ifaceTy = dyn_cast<InterfaceType>(iface.getType());
1676 assert(ifaceTy && "ReadInterfaceSignalOp expects an InterfaceType.");
1677 auto fieldAttr = SymbolRefAttr::get(builder.getContext(), signalName);
1678 InterfaceOp ifaceDefOp = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1679 iface.getDefiningOp(), ifaceTy.getInterface());
1680 assert(ifaceDefOp &&
1681 "ReadInterfaceSignalOp could not resolve an InterfaceOp.");
1682 build(builder, state, ifaceDefOp.getSignalType(signalName), iface, fieldAttr);
1683}
1684
1685/// Lookup the op for the signal declaration. This returns null on invalid
1686/// IR.
1687InterfaceSignalOp
1688ReadInterfaceSignalOp::getReferencedDecl(const hw::HWSymbolCache &cache) {
1689 return dyn_cast_or_null<InterfaceSignalOp>(
1690 cache.getDefinition(getSignalNameAttr()));
1691}
1692
1693ParseResult parseIfaceTypeAndSignal(OpAsmParser &p, Type &ifaceTy,
1694 FlatSymbolRefAttr &signalName) {
1695 SymbolRefAttr fullSym;
1696 if (p.parseAttribute(fullSym) || fullSym.getNestedReferences().size() != 1)
1697 return failure();
1698
1699 auto *ctxt = p.getBuilder().getContext();
1700 ifaceTy = InterfaceType::get(
1701 ctxt, FlatSymbolRefAttr::get(fullSym.getRootReference()));
1702 signalName = FlatSymbolRefAttr::get(fullSym.getLeafReference());
1703 return success();
1704}
1705
1706void printIfaceTypeAndSignal(OpAsmPrinter &p, Operation *op, Type type,
1707 FlatSymbolRefAttr signalName) {
1708 InterfaceType ifaceTy = dyn_cast<InterfaceType>(type);
1709 assert(ifaceTy && "Expected an InterfaceType");
1710 auto sym = SymbolRefAttr::get(ifaceTy.getInterface().getRootReference(),
1711 {signalName});
1712 p << sym;
1713}
1714
1715LogicalResult verifySignalExists(Value ifaceVal, FlatSymbolRefAttr signalName) {
1716 auto ifaceTy = dyn_cast<InterfaceType>(ifaceVal.getType());
1717 if (!ifaceTy)
1718 return failure();
1719 InterfaceOp iface = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1720 ifaceVal.getDefiningOp(), ifaceTy.getInterface());
1721 if (!iface)
1722 return failure();
1723 InterfaceSignalOp signal = iface.lookupSymbol<InterfaceSignalOp>(signalName);
1724 if (!signal)
1725 return failure();
1726 return success();
1727}
1728
1729Operation *
1730InterfaceInstanceOp::getReferencedInterface(const hw::HWSymbolCache *cache) {
1731 FlatSymbolRefAttr interface = getInterfaceType().getInterface();
1732 if (cache)
1733 if (auto *result = cache->getDefinition(interface))
1734 return result;
1735
1736 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1737 if (!topLevelModuleOp)
1738 return nullptr;
1739
1740 return topLevelModuleOp.lookupSymbol(interface);
1741}
1742
1743LogicalResult AssignInterfaceSignalOp::verify() {
1744 return verifySignalExists(getIface(), getSignalNameAttr());
1745}
1746
1747LogicalResult ReadInterfaceSignalOp::verify() {
1748 return verifySignalExists(getIface(), getSignalNameAttr());
1749}
1750
1751//===----------------------------------------------------------------------===//
1752// WireOp
1753//===----------------------------------------------------------------------===//
1754
1755void WireOp::build(OpBuilder &builder, OperationState &odsState,
1756 Type elementType, StringAttr name,
1757 hw::InnerSymAttr innerSym) {
1758 if (!name)
1759 name = builder.getStringAttr("");
1760 if (innerSym)
1761 odsState.addAttribute(hw::InnerSymbolTable::getInnerSymbolAttrName(),
1762 innerSym);
1763
1764 odsState.addAttribute("name", name);
1765 odsState.addTypes(InOutType::get(elementType));
1766}
1767
1768/// Suggest a name for each result value based on the saved result names
1769/// attribute.
1770void WireOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1771 // If the wire has an optional 'name' attribute, use it.
1772 auto nameAttr = (*this)->getAttrOfType<StringAttr>("name");
1773 if (!nameAttr.getValue().empty())
1774 setNameFn(getResult(), nameAttr.getValue());
1775}
1776
1777std::optional<size_t> WireOp::getTargetResultIndex() { return 0; }
1778
1779// If this wire is only written to, delete the wire and all writers.
1780LogicalResult WireOp::canonicalize(WireOp wire, PatternRewriter &rewriter) {
1781 // Block if op has SV attributes.
1782 if (hasSVAttributes(wire))
1783 return failure();
1784
1785 // If the wire has a symbol, then we can't delete it.
1786 if (wire.getInnerSymAttr())
1787 return failure();
1788
1789 // Wires have inout type, so they'll have assigns and read_inout operations
1790 // that work on them. If anything unexpected is found then leave it alone.
1791 SmallVector<sv::ReadInOutOp> reads;
1793
1794 for (auto *user : wire->getUsers()) {
1795 if (auto read = dyn_cast<sv::ReadInOutOp>(user)) {
1796 reads.push_back(read);
1797 continue;
1798 }
1799
1800 // Otherwise must be an assign, and we must not have seen a write yet.
1801 auto assign = dyn_cast<sv::AssignOp>(user);
1802 // Either the wire has more than one write or another kind of Op (other than
1803 // AssignOp and ReadInOutOp), then can't optimize.
1804 if (!assign || write)
1805 return failure();
1806
1807 // If the assign op has SV attributes, we don't want to delete the
1808 // assignment.
1809 if (hasSVAttributes(assign))
1810 return failure();
1811
1812 write = assign;
1813 }
1814
1815 Value connected;
1816 if (!write) {
1817 // If no write and only reads, then replace with ZOp.
1818 // SV 6.6: "If no driver is connected to a net, its
1819 // value shall be high-impedance (z) unless the net is a trireg"
1820 connected = ConstantZOp::create(
1821 rewriter, wire.getLoc(),
1822 cast<InOutType>(wire.getResult().getType()).getElementType());
1823 } else if (isa<hw::HWModuleOp>(write->getParentOp()))
1824 connected = write.getSrc();
1825 else
1826 // If the write is happening at the module level then we don't have any
1827 // use-before-def checking to do, so we only handle that for now.
1828 return failure();
1829
1830 // If the wire has a name attribute, propagate the name to the expression.
1831 if (auto *connectedOp = connected.getDefiningOp())
1832 if (!wire.getName().empty())
1833 rewriter.modifyOpInPlace(connectedOp, [&] {
1834 connectedOp->setAttr("sv.namehint", wire.getNameAttr());
1835 });
1836
1837 // Ok, we can do this. Replace all the reads with the connected value.
1838 for (auto read : reads)
1839 rewriter.replaceOp(read, connected);
1840
1841 // And remove the write and wire itself.
1842 if (write)
1843 rewriter.eraseOp(write);
1844 rewriter.eraseOp(wire);
1845 return success();
1846}
1847
1848//===----------------------------------------------------------------------===//
1849// IndexedPartSelectInOutOp
1850//===----------------------------------------------------------------------===//
1851
1852// A helper function to infer a return type of IndexedPartSelectInOutOp.
1853static Type getElementTypeOfWidth(Type type, int32_t width) {
1854 auto elemTy = cast<hw::InOutType>(type).getElementType();
1855 if (isa<IntegerType>(elemTy))
1856 return hw::InOutType::get(IntegerType::get(type.getContext(), width));
1857 if (isa<hw::ArrayType>(elemTy))
1858 return hw::InOutType::get(hw::ArrayType::get(
1859 cast<hw::ArrayType>(elemTy).getElementType(), width));
1860 return {};
1861}
1862
1863LogicalResult IndexedPartSelectInOutOp::inferReturnTypes(
1864 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1865 DictionaryAttr attrs, mlir::PropertyRef properties,
1866 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1867 Adaptor adaptor(operands, attrs, properties, regions);
1868 auto width = adaptor.getWidthAttr();
1869 if (!width)
1870 return failure();
1871
1872 auto typ = getElementTypeOfWidth(operands[0].getType(),
1873 width.getValue().getZExtValue());
1874 if (!typ)
1875 return failure();
1876 results.push_back(typ);
1877 return success();
1878}
1879
1880LogicalResult IndexedPartSelectInOutOp::verify() {
1881 unsigned inputWidth = 0, resultWidth = 0;
1882 auto opWidth = getWidth();
1883 auto inputElemTy = cast<InOutType>(getInput().getType()).getElementType();
1884 auto resultElemTy = cast<InOutType>(getType()).getElementType();
1885 if (auto i = dyn_cast<IntegerType>(inputElemTy))
1886 inputWidth = i.getWidth();
1887 else if (auto i = hw::type_cast<hw::ArrayType>(inputElemTy))
1888 inputWidth = i.getNumElements();
1889 else
1890 return emitError("input element type must be Integer or Array");
1891
1892 if (auto resType = dyn_cast<IntegerType>(resultElemTy))
1893 resultWidth = resType.getWidth();
1894 else if (auto resType = hw::type_cast<hw::ArrayType>(resultElemTy))
1895 resultWidth = resType.getNumElements();
1896 else
1897 return emitError("result element type must be Integer or Array");
1898
1899 if (opWidth > inputWidth)
1900 return emitError("slice width should not be greater than input width");
1901 if (opWidth != resultWidth)
1902 return emitError("result width must be equal to slice width");
1903 return success();
1904}
1905
1906OpFoldResult IndexedPartSelectInOutOp::fold(FoldAdaptor) {
1907 if (getType() == getInput().getType())
1908 return getInput();
1909 return {};
1910}
1911
1912//===----------------------------------------------------------------------===//
1913// IndexedPartSelectOp
1914//===----------------------------------------------------------------------===//
1915
1916LogicalResult IndexedPartSelectOp::inferReturnTypes(
1917 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1918 DictionaryAttr attrs, mlir::PropertyRef properties,
1919 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1920 Adaptor adaptor(operands, attrs, properties, regions);
1921 auto width = adaptor.getWidthAttr();
1922 if (!width)
1923 return failure();
1924
1925 results.push_back(IntegerType::get(context, width.getInt()));
1926 return success();
1927}
1928
1929LogicalResult IndexedPartSelectOp::verify() {
1930 auto opWidth = getWidth();
1931
1932 unsigned resultWidth = cast<IntegerType>(getType()).getWidth();
1933 unsigned inputWidth = cast<IntegerType>(getInput().getType()).getWidth();
1934
1935 if (opWidth > inputWidth)
1936 return emitError("slice width should not be greater than input width");
1937 if (opWidth != resultWidth)
1938 return emitError("result width must be equal to slice width");
1939 return success();
1940}
1941
1942//===----------------------------------------------------------------------===//
1943// StructFieldInOutOp
1944//===----------------------------------------------------------------------===//
1945
1946LogicalResult StructFieldInOutOp::inferReturnTypes(
1947 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1948 DictionaryAttr attrs, mlir::PropertyRef properties,
1949 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1950 Adaptor adaptor(operands, attrs, properties, regions);
1951 auto field = adaptor.getFieldAttr();
1952 if (!field)
1953 return failure();
1954 auto structType =
1955 hw::type_cast<hw::StructType>(getInOutElementType(operands[0].getType()));
1956 auto resultType = structType.getFieldType(field);
1957 if (!resultType)
1958 return failure();
1959
1960 results.push_back(hw::InOutType::get(resultType));
1961 return success();
1962}
1963
1964//===----------------------------------------------------------------------===//
1965// Other ops.
1966//===----------------------------------------------------------------------===//
1967
1968LogicalResult AliasOp::verify() {
1969 // Must have at least two operands.
1970 if (getAliases().size() < 2)
1971 return emitOpError("alias must have at least two operands");
1972
1973 return success();
1974}
1975
1976//===----------------------------------------------------------------------===//
1977// BindOp
1978//===----------------------------------------------------------------------===//
1979
1980/// Instances must be at the top level of the hw.module (or within a `ifdef)
1981// and are typically at the end of it, so we scan backwards to find them.
1982template <class Op>
1983static Op findInstanceSymbolInBlock(StringAttr name, Block *body) {
1984 for (auto &op : llvm::reverse(body->getOperations())) {
1985 if (auto instance = dyn_cast<Op>(op)) {
1986 if (auto innerSym = instance.getInnerSym())
1987 if (innerSym->getSymName() == name)
1988 return instance;
1989 }
1990
1991 if (auto ifdef = dyn_cast<IfDefOp>(op)) {
1992 if (auto result =
1993 findInstanceSymbolInBlock<Op>(name, ifdef.getThenBlock()))
1994 return result;
1995 if (ifdef.hasElse())
1996 if (auto result =
1997 findInstanceSymbolInBlock<Op>(name, ifdef.getElseBlock()))
1998 return result;
1999 }
2000 }
2001 return {};
2002}
2003
2004hw::InstanceOp BindOp::getReferencedInstance(const hw::HWSymbolCache *cache) {
2005 // If we have a cache, directly look up the referenced instance.
2006 if (cache) {
2007 auto result = cache->getInnerDefinition(getInstance());
2008 return cast<hw::InstanceOp>(result.getOp());
2009 }
2010
2011 // Otherwise, resolve the instance by looking up the module ...
2012 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
2013 if (!topLevelModuleOp)
2014 return {};
2015
2016 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
2017 topLevelModuleOp.lookupSymbol(getInstance().getModule()));
2018 if (!hwModule)
2019 return {};
2020
2021 // ... then look up the instance within it.
2022 return findInstanceSymbolInBlock<hw::InstanceOp>(getInstance().getName(),
2023 hwModule.getBodyBlock());
2024}
2025
2026/// Ensure that the symbol being instantiated exists and is an InterfaceOp.
2027LogicalResult BindOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2028 auto module = (*this)->getParentOfType<mlir::ModuleOp>();
2029 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
2030 symbolTable.lookupSymbolIn(module, getInstance().getModule()));
2031 if (!hwModule)
2032 return emitError("Referenced module doesn't exist ")
2033 << getInstance().getModule() << "::" << getInstance().getName();
2034
2035 auto inst = findInstanceSymbolInBlock<hw::InstanceOp>(
2036 getInstance().getName(), hwModule.getBodyBlock());
2037 if (!inst)
2038 return emitError("Referenced instance doesn't exist ")
2039 << getInstance().getModule() << "::" << getInstance().getName();
2040 if (!inst.getDoNotPrint())
2041 return emitError("Referenced instance isn't marked as doNotPrint");
2042 return success();
2043}
2044
2045void BindOp::build(OpBuilder &builder, OperationState &odsState, StringAttr mod,
2046 StringAttr name) {
2047 auto ref = hw::InnerRefAttr::get(mod, name);
2048 odsState.addAttribute("instance", ref);
2049}
2050
2051//===----------------------------------------------------------------------===//
2052// SVVerbatimSourceOp
2053//===----------------------------------------------------------------------===//
2054
2055void SVVerbatimSourceOp::print(OpAsmPrinter &p) {
2056 p << ' ';
2057
2058 StringRef visibilityAttrName =
2059 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
2060 if (auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
2061 p << visibility.getValue() << ' ';
2062
2063 p.printSymbolName(getSymName());
2064
2065 // Print parameters
2066 circt::printOptionalParameterList(p, *this, getParameters());
2067
2068 // Print attributes using the helper function
2069 SmallVector<StringRef> omittedAttrs = {getSymNameAttrName(), "parameters",
2070 visibilityAttrName};
2071
2072 p.printOptionalAttrDictWithKeyword((*this)->getAttrs(), omittedAttrs);
2073}
2074
2075ParseResult SVVerbatimSourceOp::parse(OpAsmParser &parser,
2076 OperationState &result) {
2077
2078 // parse optional visibility
2079 StringRef visibilityAttrName =
2080 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
2081 StringRef visibility;
2082 if (succeeded(parser.parseOptionalKeyword(&visibility,
2083 {"public", "private", "nested"}))) {
2084 result.addAttribute(visibilityAttrName,
2085 parser.getBuilder().getStringAttr(visibility));
2086 }
2087
2088 // Parse the symbol name
2089 StringAttr nameAttr;
2090 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
2091 result.attributes))
2092 return failure();
2093
2094 // Parse optional parameters
2095 ArrayAttr parameters;
2096 if (circt::parseOptionalParameterList(parser, parameters))
2097 return failure();
2098 result.addAttribute("parameters", parameters);
2099
2100 // Parse attributes using the helper function
2101 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
2102 return failure();
2103
2104 return success();
2105}
2106
2107LogicalResult SVVerbatimSourceOp::verify() {
2108 // must have verbatim content
2109 if (getContent().empty())
2110 return emitOpError("missing or empty content attribute");
2111
2112 return success();
2113}
2114
2115LogicalResult
2116SVVerbatimSourceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2117 // Verify that all symbols in additional_files are emit.file operations
2118 if (auto additionalFiles = getAdditionalFiles()) {
2119 for (auto fileRef : *additionalFiles) {
2120 auto flatRef = dyn_cast<FlatSymbolRefAttr>(fileRef);
2121 if (!flatRef)
2122 return emitOpError(
2123 "additional_files must contain flat symbol references");
2124
2125 auto *referencedOp =
2126 symbolTable.lookupNearestSymbolFrom(getOperation(), flatRef);
2127 if (!referencedOp)
2128 return emitOpError("references nonexistent file ")
2129 << flatRef.getValue();
2130
2131 // Check that the referenced operation is an emit.file
2132 if (referencedOp->getName().getStringRef() != "emit.file")
2133 return emitOpError("references ")
2134 << flatRef.getValue() << ", which is not an emit.file";
2135 }
2136 }
2137
2138 return success();
2139}
2140
2141//===----------------------------------------------------------------------===//
2142// SVVerbatimModuleOp
2143//===----------------------------------------------------------------------===//
2144
2145SmallVector<hw::PortInfo> SVVerbatimModuleOp::getPortList() {
2146 SmallVector<hw::PortInfo> ports;
2147 auto moduleType = getModuleType();
2148 auto portLocs = getPortLocs();
2149 auto portAttrs = getPerPortAttrs();
2150
2151 for (size_t i = 0, e = moduleType.getNumPorts(); i < e; ++i) {
2152 auto port = moduleType.getPorts()[i];
2153 LocationAttr loc = portLocs && i < portLocs->size()
2154 ? cast<LocationAttr>((*portLocs)[i])
2155 : UnknownLoc::get(getContext());
2156 DictionaryAttr attrs = portAttrs && i < portAttrs->size()
2157 ? cast<DictionaryAttr>((*portAttrs)[i])
2158 : DictionaryAttr::get(getContext());
2165 size_t argNum = moduleType.isOutput(i) ? moduleType.getOutputIdForPortId(i)
2166 : moduleType.getInputIdForPortId(i);
2167 ports.push_back({{port.name, port.type, dir}, argNum, attrs, loc});
2168 }
2169 return ports;
2170}
2171
2172hw::PortInfo SVVerbatimModuleOp::getPort(size_t idx) {
2173 return getPortList()[idx];
2174}
2175
2176size_t SVVerbatimModuleOp::getPortIdForInputId(size_t idx) {
2177 return getModuleType().getPortIdForInputId(idx);
2178}
2179
2180size_t SVVerbatimModuleOp::getPortIdForOutputId(size_t idx) {
2181 return getModuleType().getPortIdForOutputId(idx);
2182}
2183
2184size_t SVVerbatimModuleOp::getNumPorts() {
2185 return getModuleType().getNumPorts();
2186}
2187
2188size_t SVVerbatimModuleOp::getNumInputPorts() {
2189 return getModuleType().getNumInputs();
2190}
2191
2192size_t SVVerbatimModuleOp::getNumOutputPorts() {
2193 return getModuleType().getNumOutputs();
2194}
2195
2196hw::ModuleType SVVerbatimModuleOp::getHWModuleType() { return getModuleType(); }
2197
2198ArrayRef<Attribute> SVVerbatimModuleOp::getAllPortAttrs() {
2199 if (auto attrs = getPerPortAttrs())
2200 return attrs->getValue();
2201 return {};
2202}
2203
2204void SVVerbatimModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
2205 setPerPortAttrsAttr(ArrayAttr::get(getContext(), attrs));
2206}
2207
2208void SVVerbatimModuleOp::removeAllPortAttrs() { removePerPortAttrsAttr(); }
2209
2210SmallVector<Location> SVVerbatimModuleOp::getAllPortLocs() {
2211 if (auto locs = getPortLocs()) {
2212 SmallVector<Location> result;
2213 result.reserve(locs->size());
2214 for (auto loc : *locs)
2215 result.push_back(cast<Location>(loc));
2216 return result;
2217 }
2218 return SmallVector<Location>(getNumPorts(), UnknownLoc::get(getContext()));
2219}
2220
2221void SVVerbatimModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
2222 setPortLocsAttr(ArrayAttr::get(getContext(), locs));
2223}
2224
2225void SVVerbatimModuleOp::setHWModuleType(hw::ModuleType type) {
2226 setModuleTypeAttr(TypeAttr::get(type));
2227}
2228
2229void SVVerbatimModuleOp::setAllPortNames(ArrayRef<Attribute> names) {
2230 // Port names are part of the module type, so we need to reconstruct it
2231 auto currentType = getModuleType();
2232 SmallVector<hw::ModulePort> ports;
2233 for (size_t i = 0, e = currentType.getNumPorts(); i < e; ++i) {
2234 auto port = currentType.getPorts()[i];
2235 if (i < names.size())
2236 port.name = cast<StringAttr>(names[i]);
2237 ports.push_back(port);
2238 }
2239 setHWModuleType(hw::ModuleType::get(getContext(), ports));
2240}
2241
2242void SVVerbatimModuleOp::print(OpAsmPrinter &p) {
2243 p << ' ';
2244
2245 StringRef visibilityAttrName =
2246 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
2247 if (auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
2248 p << visibility.getValue() << ' ';
2249
2250 p.printSymbolName(SymbolTable::getSymbolName(*this).getValue());
2251
2252 printOptionalParameterList(p, *this, getParameters());
2253
2254 Region emptyRegion;
2256 p, emptyRegion, getModuleType(), getAllPortAttrs(), getAllPortLocs());
2257
2258 SmallVector<StringRef> omittedAttrs = {
2259 getSymNameAttrName(),
2260 mlir::SymbolOpInterface::getDefaultVisibilityAttrName(),
2261 getModuleTypeAttrName().getValue(),
2262 getPerPortAttrsAttrName().getValue(),
2263 getPortLocsAttrName().getValue(),
2264 getParametersAttrName().getValue()};
2265
2266 mlir::function_interface_impl::printFunctionAttributes(p, *this,
2267 omittedAttrs);
2268}
2269
2270ParseResult SVVerbatimModuleOp::parse(OpAsmParser &parser,
2271 OperationState &result) {
2272 using namespace mlir::function_interface_impl;
2273 auto builder = parser.getBuilder();
2274
2275 // Parse the visibility attribute.
2276 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
2277
2278 // Parse the name as a symbol.
2279 StringAttr nameAttr;
2280 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
2281 result.attributes))
2282 return failure();
2283
2284 // Parse the parameters.
2285 ArrayAttr parameters;
2286 if (parseOptionalParameterList(parser, parameters))
2287 return failure();
2288
2289 SmallVector<hw::module_like_impl::PortParse> ports;
2290 TypeAttr modType;
2291 if (failed(
2292 hw::module_like_impl::parseModuleSignature(parser, ports, modType)))
2293 return failure();
2294
2295 result.addAttribute(getModuleTypeAttrName(result.name), modType);
2296 result.addAttribute("parameters", parameters);
2297
2298 // Convert the specified array of dictionary attrs (which may have null
2299 // entries) to an ArrayAttr of dictionaries.
2300 auto unknownLoc = builder.getUnknownLoc();
2301 SmallVector<Attribute> attrs, locs;
2302
2303 for (auto &port : ports) {
2304 attrs.push_back(port.attrs ? port.attrs : builder.getDictionaryAttr({}));
2305 auto loc = port.sourceLoc ? Location(*port.sourceLoc) : unknownLoc;
2306 locs.push_back(loc);
2307 }
2308
2309 if (!attrs.empty())
2310 result.addAttribute("per_port_attrs", builder.getArrayAttr(attrs));
2311 if (!locs.empty())
2312 result.addAttribute("port_locs", builder.getArrayAttr(locs));
2313
2314 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
2315 return failure();
2316
2317 // Verify required attributes exist
2318 if (!result.attributes.get("source"))
2319 return parser.emitError(parser.getCurrentLocation(),
2320 "sv.verbatim.module requires 'source' attribute");
2321
2322 return success();
2323}
2324
2325LogicalResult SVVerbatimModuleOp::verify() { return success(); }
2326
2327LogicalResult
2328SVVerbatimModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2329 // Verify that the source attribute references an sv.verbatim.source operation
2330 auto sourceOp = dyn_cast_or_null<SVVerbatimSourceOp>(
2331 symbolTable.lookupNearestSymbolFrom(*this, getSourceAttr()));
2332 if (!sourceOp)
2333 return emitError("references ") << getSourceAttr().getAttr().getValue()
2334 << ", which is not an sv.verbatim.source";
2335
2336 return success();
2337}
2338
2339//===----------------------------------------------------------------------===//
2340// BindInterfaceOp
2341//===----------------------------------------------------------------------===//
2342
2343sv::InterfaceInstanceOp
2344BindInterfaceOp::getReferencedInstance(const hw::HWSymbolCache *cache) {
2345 // If we have a cache, directly look up the referenced instance.
2346 if (cache) {
2347 auto result = cache->getInnerDefinition(getInstance());
2348 return cast<sv::InterfaceInstanceOp>(result.getOp());
2349 }
2350
2351 // Otherwise, resolve the instance by looking up the module ...
2352 auto *symbolTable = SymbolTable::getNearestSymbolTable(*this);
2353 if (!symbolTable)
2354 return {};
2355 auto *parentOp =
2356 lookupSymbolInNested(symbolTable, getInstance().getModule().getValue());
2357 if (!parentOp)
2358 return {};
2359
2360 // ... then look up the instance within it.
2361 return findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
2362 getInstance().getName(), &parentOp->getRegion(0).front());
2363}
2364
2365/// Ensure that the symbol being instantiated exists and is an InterfaceOp.
2366LogicalResult
2367BindInterfaceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2368 auto *parentOp =
2369 symbolTable.lookupNearestSymbolFrom(*this, getInstance().getModule());
2370 if (!parentOp)
2371 return emitError("Referenced module doesn't exist ")
2372 << getInstance().getModule() << "::" << getInstance().getName();
2373
2374 auto inst = findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
2375 getInstance().getName(), &parentOp->getRegion(0).front());
2376 if (!inst)
2377 return emitError("Referenced interface doesn't exist ")
2378 << getInstance().getModule() << "::" << getInstance().getName();
2379 if (!inst.getDoNotPrint())
2380 return emitError("Referenced interface isn't marked as doNotPrint");
2381 return success();
2382}
2383
2384//===----------------------------------------------------------------------===//
2385// XMROp
2386//===----------------------------------------------------------------------===//
2387
2388ParseResult parseXMRPath(::mlir::OpAsmParser &parser, ArrayAttr &pathAttr,
2389 StringAttr &terminalAttr) {
2390 SmallVector<Attribute> strings;
2391 ParseResult ret = parser.parseCommaSeparatedList([&]() {
2392 StringAttr result;
2393 StringRef keyword;
2394 if (succeeded(parser.parseOptionalKeyword(&keyword))) {
2395 strings.push_back(parser.getBuilder().getStringAttr(keyword));
2396 return success();
2397 }
2398 if (succeeded(parser.parseAttribute(
2399 result, parser.getBuilder().getType<NoneType>()))) {
2400 strings.push_back(result);
2401 return success();
2402 }
2403 return failure();
2404 });
2405 if (succeeded(ret)) {
2406 pathAttr = parser.getBuilder().getArrayAttr(
2407 ArrayRef<Attribute>(strings).drop_back());
2408 terminalAttr = cast<StringAttr>(*strings.rbegin());
2409 }
2410 return ret;
2411}
2412
2413void printXMRPath(OpAsmPrinter &p, XMROp op, ArrayAttr pathAttr,
2414 StringAttr terminalAttr) {
2415 llvm::interleaveComma(pathAttr, p);
2416 p << ", " << terminalAttr;
2417}
2418
2419/// Ensure that the symbol being instantiated exists and is a HierPathOp.
2420LogicalResult XMRRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2421 auto *table = SymbolTable::getNearestSymbolTable(*this);
2422 auto path = dyn_cast_or_null<hw::HierPathOp>(
2423 symbolTable.lookupSymbolIn(table, getRefAttr()));
2424 if (!path)
2425 return emitError("Referenced path doesn't exist ") << getRefAttr();
2426
2427 return success();
2428}
2429
2430hw::HierPathOp XMRRefOp::getReferencedPath(const hw::HWSymbolCache *cache) {
2431 if (cache)
2432 if (auto *result = cache->getDefinition(getRefAttr().getAttr()))
2433 return cast<hw::HierPathOp>(result);
2434
2435 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
2436 return topLevelModuleOp.lookupSymbol<hw::HierPathOp>(getRefAttr().getValue());
2437}
2438
2439//===----------------------------------------------------------------------===//
2440// Verification Ops.
2441//===----------------------------------------------------------------------===//
2442
2443static LogicalResult eraseIfZeroOrNotZero(Operation *op, Value value,
2444 PatternRewriter &rewriter,
2445 bool eraseIfZero) {
2446 if (auto constant = value.getDefiningOp<hw::ConstantOp>())
2447 if (constant.getValue().isZero() == eraseIfZero) {
2448 rewriter.eraseOp(op);
2449 return success();
2450 }
2451
2452 return failure();
2453}
2454
2455template <class Op, bool EraseIfZero = false>
2456static LogicalResult canonicalizeImmediateVerifOp(Op op,
2457 PatternRewriter &rewriter) {
2458 return eraseIfZeroOrNotZero(op, op.getExpression(), rewriter, EraseIfZero);
2459}
2460
2461void AssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
2462 MLIRContext *context) {
2463 results.add(canonicalizeImmediateVerifOp<AssertOp>);
2464}
2465
2466void AssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2467 MLIRContext *context) {
2468 results.add(canonicalizeImmediateVerifOp<AssumeOp>);
2469}
2470
2471void CoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
2472 MLIRContext *context) {
2473 results.add(canonicalizeImmediateVerifOp<CoverOp, /* EraseIfZero = */ true>);
2474}
2475
2476template <class Op, bool EraseIfZero = false>
2477static LogicalResult canonicalizeConcurrentVerifOp(Op op,
2478 PatternRewriter &rewriter) {
2479 return eraseIfZeroOrNotZero(op, op.getProperty(), rewriter, EraseIfZero);
2480}
2481
2482void AssertConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2483 MLIRContext *context) {
2484 results.add(canonicalizeConcurrentVerifOp<AssertConcurrentOp>);
2485}
2486
2487void AssumeConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2488 MLIRContext *context) {
2489 results.add(canonicalizeConcurrentVerifOp<AssumeConcurrentOp>);
2490}
2491
2492void CoverConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2493 MLIRContext *context) {
2494 results.add(
2495 canonicalizeConcurrentVerifOp<CoverConcurrentOp, /* EraseIfZero */ true>);
2496}
2497
2498//===----------------------------------------------------------------------===//
2499// SV generate ops
2500//===----------------------------------------------------------------------===//
2501
2502/// Parse cases formatted like:
2503/// case (pattern, "name") { ... }
2504bool parseCaseRegions(OpAsmParser &p, ArrayAttr &patternsArray,
2505 ArrayAttr &caseNamesArray,
2506 SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
2507 SmallVector<Attribute> patterns;
2508 SmallVector<Attribute> names;
2509 while (!p.parseOptionalKeyword("case")) {
2510 Attribute pattern;
2511 StringAttr name;
2512 std::unique_ptr<Region> region = std::make_unique<Region>();
2513 if (p.parseLParen() || p.parseAttribute(pattern) || p.parseComma() ||
2514 p.parseAttribute(name) || p.parseRParen() || p.parseRegion(*region))
2515 return true;
2516 patterns.push_back(pattern);
2517 names.push_back(name);
2518 if (region->empty())
2519 region->push_back(new Block());
2520 caseRegions.push_back(std::move(region));
2521 }
2522 patternsArray = p.getBuilder().getArrayAttr(patterns);
2523 caseNamesArray = p.getBuilder().getArrayAttr(names);
2524 return false;
2525}
2526
2527/// Print cases formatted like:
2528/// case (pattern, "name") { ... }
2529void printCaseRegions(OpAsmPrinter &p, Operation *, ArrayAttr patternsArray,
2530 ArrayAttr namesArray,
2531 MutableArrayRef<Region> caseRegions) {
2532 assert(patternsArray.size() == caseRegions.size());
2533 assert(patternsArray.size() == namesArray.size());
2534 for (size_t i = 0, e = caseRegions.size(); i < e; ++i) {
2535 p.printNewline();
2536 p << "case (" << patternsArray[i] << ", " << namesArray[i] << ") ";
2537 p.printRegion(caseRegions[i]);
2538 }
2539 p.printNewline();
2540}
2541
2542LogicalResult GenerateCaseOp::verify() {
2543 size_t numPatterns = getCasePatterns().size();
2544 if (getCaseRegions().size() != numPatterns ||
2545 getCaseNames().size() != numPatterns)
2546 return emitOpError(
2547 "Size of caseRegions, patterns, and caseNames must match");
2548
2549 StringSet<> usedNames;
2550 for (Attribute name : getCaseNames()) {
2551 StringAttr nameStr = dyn_cast<StringAttr>(name);
2552 if (!nameStr)
2553 return emitOpError("caseNames must all be string attributes");
2554 if (usedNames.contains(nameStr.getValue()))
2555 return emitOpError("caseNames must be unique");
2556 usedNames.insert(nameStr.getValue());
2557 }
2558
2559 // mlir::FailureOr<Type> condType = evaluateParametricType();
2560
2561 return success();
2562}
2563
2564//===----------------------------------------------------------------------===//
2565// GenerateForOp
2566//===----------------------------------------------------------------------===//
2567
2568// Parse attribute and also optional trailing type if there. This is needed
2569// primarily for integer types as when given a type, they hapily parse without
2570// consuming the colon type.
2571static ParseResult parseTypedAttrWithFallback(OpAsmParser &parser,
2572 TypedAttr &result, Type type) {
2573 Attribute attr;
2574 // Try parsing with the expected type (no type suffix).
2575 if (succeeded(parser.parseCustomAttributeWithFallback(attr, type))) {
2576 auto typedAttr = dyn_cast<TypedAttr>(attr);
2577 if (!typedAttr || typedAttr.getType() != type) {
2578 return parser.emitError(parser.getCurrentLocation(),
2579 "expected typed attribute with type ")
2580 << type;
2581 }
2582
2583 // We are being given a type to parse extra.
2584 if (succeeded(parser.parseOptionalColon())) {
2585 Type localType;
2586 if (failed(parser.parseType(localType)) || localType != type)
2587 return parser.emitError(parser.getCurrentLocation(),
2588 "expected typed attribute with type ")
2589 << type;
2590 }
2591
2592 result = typedAttr;
2593 return success();
2594 }
2595
2596 return failure();
2597}
2598
2599// Parse the header and body of a generate for loop.
2600static ParseResult parseGenerateFor(OpAsmParser &parser, TypedAttr &lowerBound,
2601 TypedAttr &upperBound, TypedAttr &step,
2602 StringAttr &inductionVarName,
2603 StringAttr &genBlockName, Region &body) {
2604 auto &builder = parser.getBuilder();
2605
2606 OpAsmParser::Argument inductionVariable;
2607 if (parser.parseArgument(inductionVariable, /*allowType=*/true))
2608 return parser.emitError(parser.getCurrentLocation(),
2609 "expected induction variable argument");
2610
2611 // Parse induction variable assignment.
2612 if (parser.parseEqual())
2613 return failure();
2614
2615 // Parse lower bound.
2616 Type type = inductionVariable.type;
2617 if (parseTypedAttrWithFallback(parser, lowerBound, type))
2618 return failure();
2619
2620 if (parser.parseKeyword("to"))
2621 return failure();
2622
2623 // Parse upper bound.
2624 if (parseTypedAttrWithFallback(parser, upperBound, type))
2625 return failure();
2626
2627 if (parser.parseKeyword("step"))
2628 return failure();
2629
2630 // Parse step.
2631 if (parseTypedAttrWithFallback(parser, step, type))
2632 return failure();
2633
2634 if (parser.parseKeyword("name"))
2635 return failure();
2636
2637 // Parse gen block name.
2638 if (parser.parseCustomAttributeWithFallback(
2639 genBlockName, parser.getBuilder().getType<NoneType>()))
2640 return failure();
2641
2642 // Store the induction variable name if it's not a number.
2643 if (!isdigit(inductionVariable.ssaName.name.front()))
2644 inductionVarName =
2645 builder.getStringAttr(inductionVariable.ssaName.name.drop_front());
2646
2647 SmallVector<OpAsmParser::Argument, 1> regionArgs = {inductionVariable};
2648 return parser.parseRegion(body, regionArgs);
2649}
2650
2651// Print the header and body of a generate for loop.
2652static void printGenerateFor(OpAsmPrinter &p, Operation *op,
2653 TypedAttr lowerBound, TypedAttr upperBound,
2654 TypedAttr step, StringAttr inductionVarName,
2655 StringAttr genBlockName, Region &body) {
2656 auto forOp = cast<GenerateForOp>(op);
2657 p << forOp.getInductionVar() << " : " << forOp.getInductionVar().getType()
2658 << " = ";
2659 p.printStrippedAttrOrType(lowerBound);
2660 p << " to ";
2661 p.printStrippedAttrOrType(upperBound);
2662 p << " step ";
2663 p.printStrippedAttrOrType(step);
2664 p << " name ";
2665 p.printAttributeWithoutType(genBlockName);
2666 p << " ";
2667 p.printRegion(body, /*printEntryBlockArgs=*/false,
2668 /*printBlockTerminators=*/true);
2669}
2670
2671LogicalResult GenerateForOp::verify() {
2672 if (getBody().getBlocks().front().getNumArguments() != 1)
2673 return emitOpError("must have exactly one block argument");
2674 Type type = getLowerBound().getType();
2675 if (getBody().getBlocks().front().getArgument(0).getType() != type)
2676 return emitOpError("block argument type must match loop bounds type");
2677 if (!isa<IntegerType>(type))
2678 return emitOpError("loop bounds must be integer types");
2679
2680 return success();
2681}
2682
2683void GenerateForOp::getAsmBlockArgumentNames(
2684 mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
2685 auto *block = &region.front();
2686 if (auto attr = getInductionVarNameAttr())
2687 setNameFn(block->getArgument(0), attr);
2688}
2689
2690ModportStructAttr ModportStructAttr::get(MLIRContext *context,
2691 ModportDirection direction,
2692 FlatSymbolRefAttr signal) {
2693 return get(context, ModportDirectionAttr::get(context, direction), signal);
2694}
2695
2696//===----------------------------------------------------------------------===//
2697// FuncOp
2698//===----------------------------------------------------------------------===//
2699
2700ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
2701 auto builder = parser.getBuilder();
2702 // Parse visibility.
2703 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
2704
2705 // Parse the name as a symbol.
2706 StringAttr nameAttr;
2707 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
2708 result.attributes))
2709 return failure();
2710
2711 SmallVector<hw::module_like_impl::PortParse> ports;
2712 TypeAttr modType;
2713 if (failed(
2714 hw::module_like_impl::parseModuleSignature(parser, ports, modType)))
2715 return failure();
2716
2717 result.addAttribute(FuncOp::getModuleTypeAttrName(result.name), modType);
2718
2719 // Convert the specified array of dictionary attrs (which may have null
2720 // entries) to an ArrayAttr of dictionaries.
2721 auto unknownLoc = builder.getUnknownLoc();
2722 SmallVector<Attribute> attrs, inputLocs, outputLocs;
2723 auto nonEmptyLocsFn = [unknownLoc](Attribute attr) {
2724 return attr && cast<Location>(attr) != unknownLoc;
2725 };
2726
2727 for (auto &port : ports) {
2728 attrs.push_back(port.attrs ? port.attrs : builder.getDictionaryAttr({}));
2729 auto loc = port.sourceLoc ? Location(*port.sourceLoc) : unknownLoc;
2730 (port.direction == hw::PortInfo::Direction::Output ? outputLocs : inputLocs)
2731 .push_back(loc);
2732 }
2733
2734 result.addAttribute(FuncOp::getPerArgumentAttrsAttrName(result.name),
2735 builder.getArrayAttr(attrs));
2736
2737 if (llvm::any_of(outputLocs, nonEmptyLocsFn))
2738 result.addAttribute(FuncOp::getResultLocsAttrName(result.name),
2739 builder.getArrayAttr(outputLocs));
2740 // Parse the attribute dict.
2741 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
2742 return failure();
2743
2744 // Add the entry block arguments.
2745 SmallVector<OpAsmParser::Argument, 4> entryArgs;
2746 for (auto &port : ports)
2747 if (port.direction != hw::ModulePort::Direction::Output)
2748 entryArgs.push_back(port);
2749
2750 // Parse the optional function body. The printer will not print the body if
2751 // its empty, so disallow parsing of empty body in the parser.
2752 auto *body = result.addRegion();
2753 llvm::SMLoc loc = parser.getCurrentLocation();
2754
2755 mlir::OptionalParseResult parseResult =
2756 parser.parseOptionalRegion(*body, entryArgs,
2757 /*enableNameShadowing=*/false);
2758 if (parseResult.has_value()) {
2759 if (failed(*parseResult))
2760 return failure();
2761 // Function body was parsed, make sure its not empty.
2762 if (body->empty())
2763 return parser.emitError(loc, "expected non-empty function body");
2764 } else {
2765 if (llvm::any_of(inputLocs, nonEmptyLocsFn))
2766 result.addAttribute(FuncOp::getInputLocsAttrName(result.name),
2767 builder.getArrayAttr(inputLocs));
2768 }
2769
2770 return success();
2771}
2772
2773void FuncOp::getAsmBlockArgumentNames(mlir::Region &region,
2774 mlir::OpAsmSetValueNameFn setNameFn) {
2775 if (region.empty())
2776 return;
2777 // Assign port names to the bbargs.
2778 auto func = cast<FuncOp>(region.getParentOp());
2779
2780 auto *block = &region.front();
2781
2782 auto names = func.getModuleType().getInputNames();
2783 for (size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
2784 // Let mlir deterministically convert names to valid identifiers
2785 setNameFn(block->getArgument(i), cast<StringAttr>(names[i]));
2786 }
2787}
2788
2789Type FuncOp::getExplicitlyReturnedType() {
2790 if (!getPerArgumentAttrs() || getNumOutputs() == 0)
2791 return {};
2792
2793 // Check if the last port is used as an explicit return.
2794 auto lastArgument = getModuleType().getPorts().back();
2795 auto lastArgumentAttr = dyn_cast<DictionaryAttr>(
2796 getPerArgumentAttrsAttr()[getPerArgumentAttrsAttr().size() - 1]);
2797
2798 if (lastArgument.dir == hw::ModulePort::Output && lastArgumentAttr &&
2799 lastArgumentAttr.getAs<UnitAttr>(getExplicitlyReturnedAttrName()))
2800 return lastArgument.type;
2801 return {};
2802}
2803
2804ArrayRef<Attribute> FuncOp::getAllPortAttrs() {
2805 if (getPerArgumentAttrs())
2806 return getPerArgumentAttrs()->getValue();
2807 return {};
2808}
2809
2810void FuncOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
2811 setPerArgumentAttrsAttr(ArrayAttr::get(getContext(), attrs));
2812}
2813
2814void FuncOp::removeAllPortAttrs() { setPerArgumentAttrsAttr({}); }
2815SmallVector<Location> FuncOp::getAllPortLocs() {
2816 SmallVector<Location> portLocs;
2817 portLocs.reserve(getNumPorts());
2818 auto resultLocs = getResultLocsAttr();
2819 unsigned inputCount = 0;
2820 auto modType = getModuleType();
2821 auto unknownLoc = UnknownLoc::get(getContext());
2822 auto *body = getBodyBlock();
2823 auto inputLocs = getInputLocsAttr();
2824 for (unsigned i = 0, e = getNumPorts(); i < e; ++i) {
2825 if (modType.isOutput(i)) {
2826 auto loc = resultLocs
2827 ? cast<Location>(
2828 resultLocs.getValue()[portLocs.size() - inputCount])
2829 : unknownLoc;
2830 portLocs.push_back(loc);
2831 } else {
2832 auto loc = body ? body->getArgument(inputCount).getLoc()
2833 : (inputLocs ? cast<Location>(inputLocs[inputCount])
2834 : unknownLoc);
2835 portLocs.push_back(loc);
2836 ++inputCount;
2837 }
2838 }
2839 return portLocs;
2840}
2841
2842void FuncOp::setAllPortLocsAttrs(llvm::ArrayRef<mlir::Attribute> locs) {
2843 SmallVector<Attribute> resultLocs, inputLocs;
2844 unsigned inputCount = 0;
2845 auto modType = getModuleType();
2846 auto *body = getBodyBlock();
2847 for (unsigned i = 0, e = getNumPorts(); i < e; ++i) {
2848 if (modType.isOutput(i))
2849 resultLocs.push_back(locs[i]);
2850 else if (body)
2851 body->getArgument(inputCount++).setLoc(cast<Location>(locs[i]));
2852 else // Need to store locations in an attribute if declaration.
2853 inputLocs.push_back(locs[i]);
2854 }
2855 setResultLocsAttr(ArrayAttr::get(getContext(), resultLocs));
2856 if (!body)
2857 setInputLocsAttr(ArrayAttr::get(getContext(), inputLocs));
2858}
2859
2860SmallVector<hw::PortInfo> FuncOp::getPortList() { return getPortList(false); }
2861
2862hw::PortInfo FuncOp::getPort(size_t idx) {
2863 auto modTy = getHWModuleType();
2864 auto emptyDict = DictionaryAttr::get(getContext());
2865 LocationAttr loc = getPortLoc(idx);
2866 DictionaryAttr attrs = dyn_cast_or_null<DictionaryAttr>(getPortAttrs(idx));
2867 if (!attrs)
2868 attrs = emptyDict;
2869 return {modTy.getPorts()[idx],
2870 modTy.isOutput(idx) ? modTy.getOutputIdForPortId(idx)
2871 : modTy.getInputIdForPortId(idx),
2872 attrs, loc};
2873}
2874
2875SmallVector<hw::PortInfo> FuncOp::getPortList(bool excludeExplicitReturn) {
2876 auto modTy = getModuleType();
2877 auto emptyDict = DictionaryAttr::get(getContext());
2878 auto skipLastArgument = getExplicitlyReturnedType() && excludeExplicitReturn;
2879 SmallVector<hw::PortInfo> retval;
2880 auto portAttr = getAllPortLocs();
2881 for (unsigned i = 0, e = skipLastArgument ? modTy.getNumPorts() - 1
2882 : modTy.getNumPorts();
2883 i < e; ++i) {
2884 DictionaryAttr attrs = emptyDict;
2885 if (auto perArgumentAttr = getPerArgumentAttrs())
2886 if (auto argumentAttr =
2887 dyn_cast_or_null<DictionaryAttr>((*perArgumentAttr)[i]))
2888 attrs = argumentAttr;
2889
2890 retval.push_back({modTy.getPorts()[i],
2891 modTy.isOutput(i) ? modTy.getOutputIdForPortId(i)
2892 : modTy.getInputIdForPortId(i),
2893 attrs, portAttr[i]});
2894 }
2895 return retval;
2896}
2897
2898void FuncOp::print(OpAsmPrinter &p) {
2899 FuncOp op = *this;
2900 // Print the operation and the function name.
2901 auto funcName = op.getName();
2902 p << ' ';
2903
2904 StringRef visibilityAttrName =
2905 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
2906 if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
2907 p << visibility.getValue() << ' ';
2908 p.printSymbolName(funcName);
2910 p, op.getBody(), op.getModuleType(),
2911 op.getPerArgumentAttrsAttr()
2912 ? ArrayRef<Attribute>(op.getPerArgumentAttrsAttr().getValue())
2913 : ArrayRef<Attribute>{},
2914 getAllPortLocs());
2915
2916 mlir::function_interface_impl::printFunctionAttributes(
2917 p, op,
2918 {visibilityAttrName, getModuleTypeAttrName(),
2919 getPerArgumentAttrsAttrName(), getInputLocsAttrName(),
2920 getResultLocsAttrName()});
2921 // Print the body if this is not an external function.
2922 Region &body = op->getRegion(0);
2923 if (!body.empty()) {
2924 p << ' ';
2925 p.printRegion(body, /*printEntryBlockArgs=*/false,
2926 /*printBlockTerminators=*/true);
2927 }
2928}
2929
2930//===----------------------------------------------------------------------===//
2931// ReturnOp
2932//===----------------------------------------------------------------------===//
2933
2934LogicalResult ReturnOp::verify() {
2935 auto func = getParentOp<sv::FuncOp>();
2936 auto funcResults = func.getResultTypes();
2937 auto returnedValues = getOperands();
2938 if (funcResults.size() != returnedValues.size())
2939 return emitOpError("must have same number of operands as region results.");
2940 // Check that the types of our operands and the region's results match.
2941 for (size_t i = 0, e = funcResults.size(); i < e; ++i) {
2942 if (funcResults[i] != returnedValues[i].getType()) {
2943 emitOpError("output types must match function. In "
2944 "operand ")
2945 << i << ", expected " << funcResults[i] << ", but got "
2946 << returnedValues[i].getType() << ".";
2947 return failure();
2948 }
2949 }
2950 return success();
2951}
2952
2953//===----------------------------------------------------------------------===//
2954// Call Ops
2955//===----------------------------------------------------------------------===//
2956
2957static Value
2959 mlir::Operation::result_range results) {
2960 if (!op.getExplicitlyReturnedType())
2961 return {};
2962 return results.back();
2963}
2964
2965Value FuncCallOp::getExplicitlyReturnedValue(sv::FuncOp op) {
2966 return getExplicitlyReturnedValueImpl(op, getResults());
2967}
2968
2969Value FuncCallProceduralOp::getExplicitlyReturnedValue(sv::FuncOp op) {
2970 return getExplicitlyReturnedValueImpl(op, getResults());
2971}
2972
2973LogicalResult
2974FuncCallProceduralOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2975 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
2976 symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr()));
2977 if (!referencedOp)
2978 return emitError("cannot find function declaration '")
2979 << getCallee() << "'";
2980 return success();
2981}
2982
2983LogicalResult FuncCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2984 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
2985 symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr()));
2986 if (!referencedOp)
2987 return emitError("cannot find function declaration '")
2988 << getCallee() << "'";
2989
2990 // Non-procedural call cannot have output arguments.
2991 if (referencedOp.getNumOutputs() != 1 ||
2992 !referencedOp.getExplicitlyReturnedType()) {
2993 auto diag = emitError()
2994 << "function called in a non-procedural region must "
2995 "return a single result";
2996 diag.attachNote(referencedOp.getLoc()) << "doesn't satisfy the constraint";
2997 return failure();
2998 }
2999 return success();
3000}
3001
3002//===----------------------------------------------------------------------===//
3003// FuncDPIImportOp
3004//===----------------------------------------------------------------------===//
3005
3006LogicalResult
3007FuncDPIImportOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
3008 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
3009 symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr()));
3010
3011 if (!referencedOp)
3012 return emitError("cannot find function declaration '")
3013 << getCallee() << "'";
3014 if (!referencedOp.isDeclaration())
3015 return emitError("imported function must be a declaration but '")
3016 << getCallee() << "' is defined";
3017 return success();
3018}
3019
3020//===----------------------------------------------------------------------===//
3021// Assert Property Like ops
3022//===----------------------------------------------------------------------===//
3023
3025// Check that a clock is never given without an event
3026// and that an event is never given with a clock.
3027static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc) {
3028 if ((!clock && eventExists) || (clock && !eventExists))
3029 return mlir::emitError(
3030 loc, "Every clock must be associated to an even and vice-versa!");
3031 return success();
3032}
3033} // namespace AssertPropertyLikeOp
3034
3035LogicalResult AssertPropertyOp::verify() {
3036 return AssertPropertyLikeOp::verify(getClock(), getEvent().has_value(),
3037 getLoc());
3038}
3039
3040LogicalResult AssumePropertyOp::verify() {
3041 return AssertPropertyLikeOp::verify(getClock(), getEvent().has_value(),
3042 getLoc());
3043}
3044
3045LogicalResult CoverPropertyOp::verify() {
3046 return AssertPropertyLikeOp::verify(getClock(), getEvent().has_value(),
3047 getLoc());
3048}
3049
3050//===----------------------------------------------------------------------===//
3051// TableGen generated logic.
3052//===----------------------------------------------------------------------===//
3053
3054// Provide the autogenerated implementation guts for the Op classes.
3055#define GET_OP_CLASSES
3056#include "circt/Dialect/SV/SV.cpp.inc"
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static bool hasSVAttributes(Operation *op)
Definition CombFolds.cpp:67
static std::unique_ptr< Context > context
#define isdigit(x)
Definition FIRLexer.cpp:26
static LogicalResult canonicalizeImmediateVerifOp(Op op, PatternRewriter &rewriter)
static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op, Region &region)
Replaces the given op with the contents of the given single-block region.
static LogicalResult eraseIfZeroOrNotZero(Operation *op, Value predicate, Value enable, PatternRewriter &rewriter, bool eraseIfZero)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
Definition HWOps.cpp:1453
static SmallVector< Location > getAllPortLocs(ModTy module)
Definition HWOps.cpp:1231
static void setHWModuleType(ModTy &mod, ModuleType type)
Definition HWOps.cpp:1374
@ Output
Definition HW.h:42
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:222
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
static Block * getBodyBlock(FModuleLike mod)
RewritePatternSet pattern
bool parseCaseRegions(OpAsmParser &p, ArrayAttr &patternsArray, ArrayAttr &caseNamesArray, SmallVectorImpl< std::unique_ptr< Region > > &caseRegions)
Parse cases formatted like: case (pattern, "name") { ... }.
Definition SVOps.cpp:2504
ParseResult parseIfaceTypeAndSignal(OpAsmParser &p, Type &ifaceTy, FlatSymbolRefAttr &signalName)
Definition SVOps.cpp:1693
static void printGenerateFor(OpAsmPrinter &p, Operation *op, TypedAttr lowerBound, TypedAttr upperBound, TypedAttr step, StringAttr inductionVarName, StringAttr genBlockName, Region &body)
Definition SVOps.cpp:2652
LogicalResult verifySignalExists(Value ifaceVal, FlatSymbolRefAttr signalName)
Definition SVOps.cpp:1715
void printCaseRegions(OpAsmPrinter &p, Operation *, ArrayAttr patternsArray, ArrayAttr namesArray, MutableArrayRef< Region > caseRegions)
Print cases formatted like: case (pattern, "name") { ... }.
Definition SVOps.cpp:2529
static Value getExplicitlyReturnedValueImpl(sv::FuncOp op, mlir::Operation::result_range results)
Definition SVOps.cpp:2958
void printIfaceTypeAndSignal(OpAsmPrinter &p, Operation *op, Type type, FlatSymbolRefAttr signalName)
Definition SVOps.cpp:1706
static void printModportStructs(OpAsmPrinter &p, Operation *, ArrayAttr portsAttr)
Definition SVOps.cpp:1564
static ParseResult parseTypedAttrWithFallback(OpAsmParser &parser, TypedAttr &result, Type type)
Definition SVOps.cpp:2571
static LogicalResult canonicalizeConcurrentVerifOp(Op op, PatternRewriter &rewriter)
Definition SVOps.cpp:2477
static ParseResult parseEventList(OpAsmParser &p, Attribute &eventsAttr, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &clocksOperands)
Definition SVOps.cpp:741
static MacroDeclOp getReferencedMacro(const hw::HWSymbolCache *cache, Operation *op, FlatSymbolRefAttr macroName)
Definition SVOps.cpp:203
static LogicalResult canonicalizeIfDefLike(Op op, PatternRewriter &rewriter)
Definition SVOps.cpp:509
static LogicalResult verifyVerbatimSymbols(Operation *op, ArrayAttr symbols, hw::InnerRefNamespace &ns)
Helper function to verify inner refs in symbols array for verbatim ops.
Definition SVOps.cpp:100
static LogicalResult verifyVerbatimFlatSymbolRefs(Operation *op, ArrayAttr symbols, SymbolTableCollection &symbolTable)
Helper function to verify flat symbol refs in symbols array for verbatim ops.
Definition SVOps.cpp:116
ParseResult parseXMRPath(::mlir::OpAsmParser &parser, ArrayAttr &pathAttr, StringAttr &terminalAttr)
Definition SVOps.cpp:2388
static Type getElementTypeOfWidth(Type type, int32_t width)
Definition SVOps.cpp:1853
static LogicalResult mergeNeiboringAssignments(AssignTy op, PatternRewriter &rewriter)
Definition SVOps.cpp:1449
static Op findInstanceSymbolInBlock(StringAttr name, Block *body)
Instances must be at the top level of the hw.module (or within a `ifdef)
Definition SVOps.cpp:1983
static void printEventList(OpAsmPrinter &p, AlwaysOp op, ArrayAttr portsAttr, OperandRange operands)
Definition SVOps.cpp:772
static SmallVector< CasePatternBit > getPatternBitsForValue(const APInt &value)
Definition SVOps.cpp:906
static ParseResult parseImplicitInitType(OpAsmParser &p, mlir::Type regType, std::optional< OpAsmParser::UnresolvedOperand > &initValue, mlir::Type &initType)
Definition SVOps.cpp:361
static LogicalResult verifyMacroIdentSymbolUses(Operation *op, FlatSymbolRefAttr attr, SymbolTableCollection &symbolTable)
Verifies symbols referenced by macro identifiers.
Definition SVOps.cpp:85
static void getVerbatimExprAsmResultNames(Operation *op, function_ref< void(Value, StringRef)> setNameFn)
Get the asm name for sv.verbatim.expr and sv.verbatim.expr.se.
Definition SVOps.cpp:144
static void printImplicitInitType(OpAsmPrinter &p, Operation *op, mlir::Type regType, mlir::Value initValue, mlir::Type initType)
Definition SVOps.cpp:375
static ParseResult parseGenerateFor(OpAsmParser &parser, TypedAttr &lowerBound, TypedAttr &upperBound, TypedAttr &step, StringAttr &inductionVarName, StringAttr &genBlockName, Region &body)
Definition SVOps.cpp:2600
static ParseResult parseModportStructs(OpAsmParser &parser, ArrayAttr &portsAttr)
Definition SVOps.cpp:1537
static Operation * lookupSymbolInNested(Operation *symbolTableOp, StringRef symbol)
Returns the operation registered with the given symbol name with the regions of 'symbolTableOp'.
Definition SVOps.cpp:62
void printXMRPath(OpAsmPrinter &p, XMROp op, ArrayAttr pathAttr, StringAttr terminalAttr)
Definition SVOps.cpp:2413
static InstancePath empty
This stores lookup tables to make manipulating and working with the IR more efficient.
Definition HWSymCache.h:28
HWSymbolCache::Item getInnerDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name) const
Definition HWSymCache.h:66
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
Definition HWSymCache.h:57
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
IntegerAttr intAttr
Definition SVOps.h:124
CasePatternBit getBit(size_t bitNumber) const
Return the specified bit, bit 0 is the least significant bit.
Definition SVOps.cpp:888
bool hasZ() const override
Return true if this pattern has an Z.
Definition SVOps.cpp:900
CaseBitPattern(ArrayRef< CasePatternBit > bits, MLIRContext *context)
Get a CasePattern from a specified list of CasePatternBit.
Definition SVOps.cpp:924
bool hasX() const override
Return true if this pattern has an X.
Definition SVOps.cpp:893
hw::EnumFieldAttr enumAttr
Definition SVOps.h:141
StringRef getFieldValue() const
Definition SVOps.cpp:967
create(array_value, low_index, ret_type)
Definition hw.py:466
create(data_type, value)
Definition hw.py:433
Definition sv.py:70
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
Definition SVOps.cpp:3027
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
Direction
The direction of a Component or Cell port.
Definition CalyxOps.h:76
Value createOrFoldNot(OpBuilder &builder, Location loc, Value value, bool twoState=false)
Create a `‘Not’' gate on a value.
Definition CombOps.cpp:102
uint64_t getWidth(Type t)
Definition ESIPasses.cpp:32
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
ParseResult parseModuleSignature(OpAsmParser &parser, SmallVectorImpl< PortParse > &args, TypeAttr &modType)
New Style parsing.
void printModuleSignatureNew(OpAsmPrinter &p, Region &body, hw::ModuleType modType, ArrayRef< Attribute > portAttrs, ArrayRef< Location > locAttrs)
bool isHWIntegerType(mlir::Type type)
Return true if the specified type is a value HW Integer type.
Definition HWTypes.cpp:60
bool isOffset(Value base, Value index, uint64_t offset)
Definition HWOps.cpp:1737
FunctionType getModuleType(Operation *module)
Return the signature for the specified module as a function type.
Definition HWOps.cpp:533
bool isHWEnumType(mlir::Type type)
Return true if the specified type is a HW Enum type.
Definition HWTypes.cpp:73
mlir::Type getCanonicalType(mlir::Type type)
Definition HWTypes.cpp:49
CasePatternBit
This describes the bit in a pattern, 0/1/x/z.
Definition SVOps.h:50
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
Definition SVOps.cpp:873
bool hasSVAttributes(mlir::Operation *op)
Helper functions to handle SV attributes.
void createNestedIfDefs(ArrayRef< StringAttr > macroSymbols, llvm::function_ref< void(StringAttr, std::function< void()>, std::function< void()>)> ifdefCtor, llvm::function_ref< void(size_t)> thenCtor, llvm::function_ref< void()> defaultCtor)
Create nested ifdef operations for a list of macro symbols.
Definition SVOps.cpp:528
bool is2StateExpression(Value v)
Returns if the expression is known to be 2-state (binary)
Definition SVOps.cpp:43
mlir::Type getInOutElementType(mlir::Type type)
Return the element type of an InOutType or null if the operand isn't an InOut type.
Definition SVTypes.cpp:42
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
ParseResult parseOptionalParameterList(OpAsmParser &parser, ArrayAttr &parameters)
Parse an parameter list if present.
void printOptionalParameterList(OpAsmPrinter &p, Operation *op, ArrayAttr parameters)
Print a parameter list for a module or instance.
Definition hw.py:1
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:193
Definition sv.py:1
write(addr, data)
Definition xrt_cosim.py:30
read(addr)
Definition xrt_cosim.py:23
This class represents the namespace in which InnerRef's can be resolved.
InnerSymTarget lookup(hw::InnerRefAttr inner) const
Resolve the InnerRef to its target within this namespace, returning empty target if no such name exis...
This holds the name, type, direction of a module's ports.