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