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