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