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,
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".
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)
969 if (getBit(i) == CasePatternBit::AnyX)
970 return true;
971 return false;
972}
973
975 for (size_t i = 0, e = getWidth(); i != e; ++i)
976 if (getBit(i) == CasePatternBit::AnyZ)
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
1436LogicalResult BPAssignOp::verify() {
1437 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1438 return emitOpError(
1439 "Verilog disallows procedural assignment to a net type (did you intend "
1440 "to use a variable type, e.g., sv.reg?)");
1441 return success();
1442}
1443
1444LogicalResult PAssignOp::verify() {
1445 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1446 return emitOpError(
1447 "Verilog disallows procedural assignment to a net type (did you intend "
1448 "to use a variable type, e.g., sv.reg?)");
1449 return success();
1450}
1451
1452namespace {
1453// This represents a slice of an array.
1454struct ArraySlice {
1455 Value array;
1456 Value start;
1457 size_t size; // Represent a range array[start, start + size).
1458
1459 // Get a struct from the value. Return std::nullopt if the value doesn't
1460 // represent an array slice.
1461 static std::optional<ArraySlice> getArraySlice(Value v) {
1462 auto *op = v.getDefiningOp();
1463 if (!op)
1464 return std::nullopt;
1465 return TypeSwitch<Operation *, std::optional<ArraySlice>>(op)
1466 .Case<hw::ArrayGetOp, ArrayIndexInOutOp>(
1467 [](auto arrayIndex) -> std::optional<ArraySlice> {
1468 hw::ConstantOp constant =
1469 arrayIndex.getIndex()
1470 .template getDefiningOp<hw::ConstantOp>();
1471 if (!constant)
1472 return std::nullopt;
1473 return ArraySlice{/*array=*/arrayIndex.getInput(),
1474 /*start=*/constant,
1475 /*end=*/1};
1476 })
1477 .Case<hw::ArraySliceOp>([](hw::ArraySliceOp slice)
1478 -> std::optional<ArraySlice> {
1479 auto constant = slice.getLowIndex().getDefiningOp<hw::ConstantOp>();
1480 if (!constant)
1481 return std::nullopt;
1482 return ArraySlice{
1483 /*array=*/slice.getInput(), /*start=*/constant,
1484 /*end=*/
1485 hw::type_cast<hw::ArrayType>(slice.getType()).getNumElements()};
1486 })
1487 .Case<sv::IndexedPartSelectInOutOp>(
1488 [](sv::IndexedPartSelectInOutOp index)
1489 -> std::optional<ArraySlice> {
1490 auto constant = index.getBase().getDefiningOp<hw::ConstantOp>();
1491 if (!constant || index.getDecrement())
1492 return std::nullopt;
1493 return ArraySlice{/*array=*/index.getInput(),
1494 /*start=*/constant,
1495 /*end=*/index.getWidth()};
1496 })
1497 .Default([](auto) { return std::nullopt; });
1498 }
1499
1500 // Create a pair of ArraySlice from source and destination of assignments.
1501 static std::optional<std::pair<ArraySlice, ArraySlice>>
1502 getAssignedRange(Operation *op) {
1503 assert((isa<PAssignOp, BPAssignOp>(op) && "assignments are expected"));
1504 auto srcRange = ArraySlice::getArraySlice(op->getOperand(1));
1505 if (!srcRange)
1506 return std::nullopt;
1507 auto destRange = ArraySlice::getArraySlice(op->getOperand(0));
1508 if (!destRange)
1509 return std::nullopt;
1510
1511 return std::make_pair(*destRange, *srcRange);
1512 }
1513};
1514} // namespace
1515
1516// This canonicalization merges neiboring assignments of array elements into
1517// array slice assignments. e.g.
1518// a[0] <= b[1]
1519// a[1] <= b[2]
1520// ->
1521// a[1:0] <= b[2:1]
1522template <typename AssignTy>
1523static LogicalResult mergeNeiboringAssignments(AssignTy op,
1524 PatternRewriter &rewriter) {
1525 // Get assigned ranges of each assignment.
1526 auto assignedRangeOpt = ArraySlice::getAssignedRange(op);
1527 if (!assignedRangeOpt)
1528 return failure();
1529
1530 auto [dest, src] = *assignedRangeOpt;
1531 AssignTy nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1532 bool changed = false;
1533 SmallVector<Location> loc{op.getLoc()};
1534 // Check that a next operation is a same kind of the assignment.
1535 while (nextAssign) {
1536 auto nextAssignedRange = ArraySlice::getAssignedRange(nextAssign);
1537 if (!nextAssignedRange)
1538 break;
1539 auto [nextDest, nextSrc] = *nextAssignedRange;
1540 // Check that these assignments are mergaable.
1541 if (dest.array != nextDest.array || src.array != nextSrc.array ||
1542 !hw::isOffset(dest.start, nextDest.start, dest.size) ||
1543 !hw::isOffset(src.start, nextSrc.start, src.size))
1544 break;
1545
1546 dest.size += nextDest.size;
1547 src.size += nextSrc.size;
1548 changed = true;
1549 loc.push_back(nextAssign.getLoc());
1550 rewriter.eraseOp(nextAssign);
1551 nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1552 }
1553
1554 if (!changed)
1555 return failure();
1556
1557 // From here, construct assignments of array slices.
1558 auto resultType = hw::ArrayType::get(
1559 hw::type_cast<hw::ArrayType>(src.array.getType()).getElementType(),
1560 src.size);
1561 auto newDest = sv::IndexedPartSelectInOutOp::create(
1562 rewriter, op.getLoc(), dest.array, dest.start, dest.size);
1563 auto newSrc = hw::ArraySliceOp::create(rewriter, op.getLoc(), resultType,
1564 src.array, src.start);
1565 auto newLoc = rewriter.getFusedLoc(loc);
1566 auto newOp = rewriter.replaceOpWithNewOp<AssignTy>(op, newDest, newSrc);
1567 newOp->setLoc(newLoc);
1568 return success();
1569}
1570
1571LogicalResult PAssignOp::canonicalize(PAssignOp op, PatternRewriter &rewriter) {
1572 return mergeNeiboringAssignments(op, rewriter);
1573}
1574
1575LogicalResult BPAssignOp::canonicalize(BPAssignOp op,
1576 PatternRewriter &rewriter) {
1577 return mergeNeiboringAssignments(op, rewriter);
1578}
1579
1580//===----------------------------------------------------------------------===//
1581// TypeDecl operations
1582//===----------------------------------------------------------------------===//
1583
1584void InterfaceOp::build(OpBuilder &builder, OperationState &result,
1585 StringRef sym_name, std::function<void()> body) {
1586 OpBuilder::InsertionGuard guard(builder);
1587
1588 result.addAttribute(InterfaceOp::getSymNameAttrName(result.name),
1589 builder.getStringAttr(sym_name));
1590 builder.createBlock(result.addRegion());
1591 if (body)
1592 body();
1593}
1594
1595ModportType InterfaceOp::getModportType(StringRef modportName) {
1596 assert(lookupSymbol<InterfaceModportOp>(modportName) &&
1597 "Modport symbol not found.");
1598 auto *ctxt = getContext();
1599 return ModportType::get(
1600 getContext(),
1601 SymbolRefAttr::get(ctxt, getSymName(),
1602 {SymbolRefAttr::get(ctxt, modportName)}));
1603}
1604
1605Type InterfaceOp::getSignalType(StringRef signalName) {
1606 InterfaceSignalOp signal = lookupSymbol<InterfaceSignalOp>(signalName);
1607 assert(signal && "Interface signal symbol not found.");
1608 return signal.getType();
1609}
1610
1611static ParseResult parseModportStructs(OpAsmParser &parser,
1612 ArrayAttr &portsAttr) {
1613
1614 auto *context = parser.getBuilder().getContext();
1615
1616 SmallVector<Attribute, 8> ports;
1617 auto parseElement = [&]() -> ParseResult {
1618 auto direction = ModportDirectionAttr::parse(parser, {});
1619 if (!direction)
1620 return failure();
1621
1622 FlatSymbolRefAttr signal;
1623 if (parser.parseAttribute(signal))
1624 return failure();
1625
1626 ports.push_back(ModportStructAttr::get(
1627 context, cast<ModportDirectionAttr>(direction), signal));
1628 return success();
1629 };
1630 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
1631 parseElement))
1632 return failure();
1633
1634 portsAttr = ArrayAttr::get(context, ports);
1635 return success();
1636}
1637
1638static void printModportStructs(OpAsmPrinter &p, Operation *,
1639 ArrayAttr portsAttr) {
1640 p << "(";
1641 llvm::interleaveComma(portsAttr, p, [&](Attribute attr) {
1642 auto port = cast<ModportStructAttr>(attr);
1643 p << stringifyEnum(port.getDirection().getValue());
1644 p << ' ';
1645 p.printSymbolName(port.getSignal().getRootReference().getValue());
1646 });
1647 p << ')';
1648}
1649
1650void InterfaceSignalOp::build(mlir::OpBuilder &builder,
1651 ::mlir::OperationState &state, StringRef name,
1652 mlir::Type type) {
1653 build(builder, state, name, /*sym_visibility=*/{}, mlir::TypeAttr::get(type));
1654}
1655
1656void InterfaceModportOp::build(OpBuilder &builder, OperationState &state,
1657 StringRef name, ArrayRef<StringRef> inputs,
1658 ArrayRef<StringRef> outputs) {
1659 auto *ctxt = builder.getContext();
1660 SmallVector<Attribute, 8> directions;
1661 auto inputDir = ModportDirectionAttr::get(ctxt, ModportDirection::input);
1662 auto outputDir = ModportDirectionAttr::get(ctxt, ModportDirection::output);
1663 for (auto input : inputs)
1664 directions.push_back(ModportStructAttr::get(
1665 ctxt, inputDir, SymbolRefAttr::get(ctxt, input)));
1666 for (auto output : outputs)
1667 directions.push_back(ModportStructAttr::get(
1668 ctxt, outputDir, SymbolRefAttr::get(ctxt, output)));
1669 build(builder, state, name, /*sym_visibility=*/{},
1670 ArrayAttr::get(ctxt, directions));
1671}
1672
1673std::optional<size_t> InterfaceInstanceOp::getTargetResultIndex() {
1674 // Inner symbols on instance operations target the op not any result.
1675 return std::nullopt;
1676}
1677
1678/// Suggest a name for each result value based on the saved result names
1679/// attribute.
1680void InterfaceInstanceOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1681 setNameFn(getResult(), getName());
1682}
1683
1684/// Ensure that the symbol being instantiated exists and is an InterfaceOp.
1685LogicalResult InterfaceInstanceOp::verify() {
1686 if (getName().empty())
1687 return emitOpError("requires non-empty name");
1688 return success();
1689}
1690
1691LogicalResult
1692InterfaceInstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1693 auto *symtable = SymbolTable::getNearestSymbolTable(*this);
1694 if (!symtable)
1695 return emitError("sv.interface.instance must exist within a region "
1696 "which has a symbol table.");
1697 auto ifaceTy = getType();
1698 auto *referencedOp =
1699 symbolTable.lookupSymbolIn(symtable, ifaceTy.getInterface());
1700 if (!referencedOp)
1701 return emitError("Symbol not found: ") << ifaceTy.getInterface() << ".";
1702 if (!isa<InterfaceOp>(referencedOp))
1703 return emitError("Symbol ")
1704 << ifaceTy.getInterface() << " is not an InterfaceOp.";
1705 return success();
1706}
1707
1708/// Ensure that the symbol being instantiated exists and is an
1709/// InterfaceModportOp.
1710LogicalResult
1711GetModportOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1712 auto *symtable = SymbolTable::getNearestSymbolTable(*this);
1713 if (!symtable)
1714 return emitError("sv.interface.instance must exist within a region "
1715 "which has a symbol table.");
1716
1717 auto ifaceTy = getType();
1718 auto *referencedOp =
1719 symbolTable.lookupSymbolIn(symtable, ifaceTy.getModport());
1720 if (!referencedOp)
1721 return emitError("Symbol not found: ") << ifaceTy.getModport() << ".";
1722 if (!isa<InterfaceModportOp>(referencedOp))
1723 return emitError("Symbol ")
1724 << ifaceTy.getModport() << " is not an InterfaceModportOp.";
1725 return success();
1726}
1727
1728void GetModportOp::build(OpBuilder &builder, OperationState &state, Value value,
1729 StringRef field) {
1730 auto ifaceTy = dyn_cast<InterfaceType>(value.getType());
1731 assert(ifaceTy && "GetModportOp expects an InterfaceType.");
1732 auto fieldAttr = SymbolRefAttr::get(builder.getContext(), field);
1733 auto modportSym =
1734 SymbolRefAttr::get(ifaceTy.getInterface().getRootReference(), fieldAttr);
1735 build(builder, state, ModportType::get(builder.getContext(), modportSym),
1736 value, fieldAttr);
1737}
1738
1739/// Lookup the op for the modport declaration. This returns null on invalid
1740/// IR.
1741InterfaceModportOp
1742GetModportOp::getReferencedDecl(const hw::HWSymbolCache &cache) {
1743 return dyn_cast_or_null<InterfaceModportOp>(
1744 cache.getDefinition(getFieldAttr()));
1745}
1746
1747void ReadInterfaceSignalOp::build(OpBuilder &builder, OperationState &state,
1748 Value iface, StringRef signalName) {
1749 auto ifaceTy = dyn_cast<InterfaceType>(iface.getType());
1750 assert(ifaceTy && "ReadInterfaceSignalOp expects an InterfaceType.");
1751 auto fieldAttr = SymbolRefAttr::get(builder.getContext(), signalName);
1752 InterfaceOp ifaceDefOp = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1753 iface.getDefiningOp(), ifaceTy.getInterface());
1754 assert(ifaceDefOp &&
1755 "ReadInterfaceSignalOp could not resolve an InterfaceOp.");
1756 build(builder, state, ifaceDefOp.getSignalType(signalName), iface, fieldAttr);
1757}
1758
1759/// Lookup the op for the signal declaration. This returns null on invalid
1760/// IR.
1761InterfaceSignalOp
1762ReadInterfaceSignalOp::getReferencedDecl(const hw::HWSymbolCache &cache) {
1763 return dyn_cast_or_null<InterfaceSignalOp>(
1764 cache.getDefinition(getSignalNameAttr()));
1765}
1766
1767ParseResult parseIfaceTypeAndSignal(OpAsmParser &p, Type &ifaceTy,
1768 FlatSymbolRefAttr &signalName) {
1769 SymbolRefAttr fullSym;
1770 if (p.parseAttribute(fullSym) || fullSym.getNestedReferences().size() != 1)
1771 return failure();
1772
1773 auto *ctxt = p.getBuilder().getContext();
1774 ifaceTy = InterfaceType::get(
1775 ctxt, FlatSymbolRefAttr::get(fullSym.getRootReference()));
1776 signalName = FlatSymbolRefAttr::get(fullSym.getLeafReference());
1777 return success();
1778}
1779
1780void printIfaceTypeAndSignal(OpAsmPrinter &p, Operation *op, Type type,
1781 FlatSymbolRefAttr signalName) {
1782 InterfaceType ifaceTy = dyn_cast<InterfaceType>(type);
1783 assert(ifaceTy && "Expected an InterfaceType");
1784 auto sym = SymbolRefAttr::get(ifaceTy.getInterface().getRootReference(),
1785 {signalName});
1786 p << sym;
1787}
1788
1789LogicalResult verifySignalExists(Value ifaceVal, FlatSymbolRefAttr signalName) {
1790 auto ifaceTy = dyn_cast<InterfaceType>(ifaceVal.getType());
1791 if (!ifaceTy)
1792 return failure();
1793 InterfaceOp iface = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1794 ifaceVal.getDefiningOp(), ifaceTy.getInterface());
1795 if (!iface)
1796 return failure();
1797 InterfaceSignalOp signal = iface.lookupSymbol<InterfaceSignalOp>(signalName);
1798 if (!signal)
1799 return failure();
1800 return success();
1801}
1802
1803Operation *
1804InterfaceInstanceOp::getReferencedInterface(const hw::HWSymbolCache *cache) {
1805 FlatSymbolRefAttr interface = getInterfaceType().getInterface();
1806 if (cache)
1807 if (auto *result = cache->getDefinition(interface))
1808 return result;
1809
1810 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1811 if (!topLevelModuleOp)
1812 return nullptr;
1813
1814 return topLevelModuleOp.lookupSymbol(interface);
1815}
1816
1817LogicalResult AssignInterfaceSignalOp::verify() {
1818 return verifySignalExists(getIface(), getSignalNameAttr());
1819}
1820
1821LogicalResult ReadInterfaceSignalOp::verify() {
1822 return verifySignalExists(getIface(), getSignalNameAttr());
1823}
1824
1825//===----------------------------------------------------------------------===//
1826// WireOp
1827//===----------------------------------------------------------------------===//
1828
1829void WireOp::build(OpBuilder &builder, OperationState &odsState,
1830 Type elementType, StringAttr name,
1831 hw::InnerSymAttr innerSym) {
1832 if (!name)
1833 name = builder.getStringAttr("");
1834 if (innerSym)
1835 odsState.addAttribute(hw::InnerSymbolTable::getInnerSymbolAttrName(),
1836 innerSym);
1837
1838 odsState.addAttribute("name", name);
1839 odsState.addTypes(InOutType::get(elementType));
1840}
1841
1842/// Suggest a name for each result value based on the saved result names
1843/// attribute.
1844void WireOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1845 // If the wire has an optional 'name' attribute, use it.
1846 auto nameAttr = (*this)->getAttrOfType<StringAttr>("name");
1847 if (!nameAttr.getValue().empty())
1848 setNameFn(getResult(), nameAttr.getValue());
1849}
1850
1851std::optional<size_t> WireOp::getTargetResultIndex() { return 0; }
1852
1853// If this wire is only written to, delete the wire and all writers.
1854LogicalResult WireOp::canonicalize(WireOp wire, PatternRewriter &rewriter) {
1855 // Block if op has SV attributes.
1856 if (hasSVAttributes(wire))
1857 return failure();
1858
1859 // If the wire has a symbol, then we can't delete it.
1860 if (wire.getInnerSymAttr())
1861 return failure();
1862
1863 // Wires have inout type, so they'll have assigns and read_inout operations
1864 // that work on them. If anything unexpected is found then leave it alone.
1865 SmallVector<sv::ReadInOutOp> reads;
1867
1868 for (auto *user : wire->getUsers()) {
1869 if (auto read = dyn_cast<sv::ReadInOutOp>(user)) {
1870 reads.push_back(read);
1871 continue;
1872 }
1873
1874 // Otherwise must be an assign, and we must not have seen a write yet.
1875 auto assign = dyn_cast<sv::AssignOp>(user);
1876 // Either the wire has more than one write or another kind of Op (other than
1877 // AssignOp and ReadInOutOp), then can't optimize.
1878 if (!assign || write)
1879 return failure();
1880
1881 // If the assign op has SV attributes, we don't want to delete the
1882 // assignment.
1883 if (hasSVAttributes(assign))
1884 return failure();
1885
1886 write = assign;
1887 }
1888
1889 Value connected;
1890 if (!write) {
1891 // If no write and only reads, then replace with ZOp.
1892 // SV 6.6: "If no driver is connected to a net, its
1893 // value shall be high-impedance (z) unless the net is a trireg"
1894 connected = ConstantZOp::create(
1895 rewriter, wire.getLoc(),
1896 cast<InOutType>(wire.getResult().getType()).getElementType());
1897 } else if (isa<hw::HWModuleOp>(write->getParentOp()))
1898 connected = write.getSrc();
1899 else
1900 // If the write is happening at the module level then we don't have any
1901 // use-before-def checking to do, so we only handle that for now.
1902 return failure();
1903
1904 // If the wire has a name attribute, propagate the name to the expression.
1905 if (auto *connectedOp = connected.getDefiningOp())
1906 if (!wire.getName().empty())
1907 rewriter.modifyOpInPlace(connectedOp, [&] {
1908 connectedOp->setAttr("sv.namehint", wire.getNameAttr());
1909 });
1910
1911 // Ok, we can do this. Replace all the reads with the connected value.
1912 for (auto read : reads)
1913 rewriter.replaceOp(read, connected);
1914
1915 // And remove the write and wire itself.
1916 if (write)
1917 rewriter.eraseOp(write);
1918 rewriter.eraseOp(wire);
1919 return success();
1920}
1921
1922//===----------------------------------------------------------------------===//
1923// IndexedPartSelectInOutOp
1924//===----------------------------------------------------------------------===//
1925
1926// A helper function to infer a return type of IndexedPartSelectInOutOp.
1927static Type getElementTypeOfWidth(Type type, int32_t width) {
1928 auto elemTy = cast<hw::InOutType>(type).getElementType();
1929 if (isa<IntegerType>(elemTy))
1930 return hw::InOutType::get(IntegerType::get(type.getContext(), width));
1931 if (isa<hw::ArrayType>(elemTy))
1932 return hw::InOutType::get(hw::ArrayType::get(
1933 cast<hw::ArrayType>(elemTy).getElementType(), width));
1934 return {};
1935}
1936
1937LogicalResult IndexedPartSelectInOutOp::inferReturnTypes(
1938 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1939 DictionaryAttr attrs, mlir::PropertyRef properties,
1940 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1941 Adaptor adaptor(operands, attrs, properties, regions);
1942 auto width = adaptor.getWidthAttr();
1943 if (!width)
1944 return failure();
1945
1946 auto typ = getElementTypeOfWidth(operands[0].getType(),
1947 width.getValue().getZExtValue());
1948 if (!typ)
1949 return failure();
1950 results.push_back(typ);
1951 return success();
1952}
1953
1954LogicalResult IndexedPartSelectInOutOp::verify() {
1955 unsigned inputWidth = 0, resultWidth = 0;
1956 auto opWidth = getWidth();
1957 auto inputElemTy = cast<InOutType>(getInput().getType()).getElementType();
1958 auto resultElemTy = cast<InOutType>(getType()).getElementType();
1959 if (auto i = dyn_cast<IntegerType>(inputElemTy))
1960 inputWidth = i.getWidth();
1961 else if (auto i = hw::type_cast<hw::ArrayType>(inputElemTy))
1962 inputWidth = i.getNumElements();
1963 else
1964 return emitError("input element type must be Integer or Array");
1965
1966 if (auto resType = dyn_cast<IntegerType>(resultElemTy))
1967 resultWidth = resType.getWidth();
1968 else if (auto resType = hw::type_cast<hw::ArrayType>(resultElemTy))
1969 resultWidth = resType.getNumElements();
1970 else
1971 return emitError("result element type must be Integer or Array");
1972
1973 if (opWidth > inputWidth)
1974 return emitError("slice width should not be greater than input width");
1975 if (opWidth != resultWidth)
1976 return emitError("result width must be equal to slice width");
1977 return success();
1978}
1979
1980OpFoldResult IndexedPartSelectInOutOp::fold(FoldAdaptor) {
1981 if (getType() == getInput().getType())
1982 return getInput();
1983 return {};
1984}
1985
1986//===----------------------------------------------------------------------===//
1987// IndexedPartSelectOp
1988//===----------------------------------------------------------------------===//
1989
1990LogicalResult IndexedPartSelectOp::inferReturnTypes(
1991 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1992 DictionaryAttr attrs, mlir::PropertyRef properties,
1993 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1994 Adaptor adaptor(operands, attrs, properties, regions);
1995 auto width = adaptor.getWidthAttr();
1996 if (!width)
1997 return failure();
1998
1999 results.push_back(IntegerType::get(context, width.getInt()));
2000 return success();
2001}
2002
2003LogicalResult IndexedPartSelectOp::verify() {
2004 auto opWidth = getWidth();
2005
2006 unsigned resultWidth = cast<IntegerType>(getType()).getWidth();
2007 unsigned inputWidth = cast<IntegerType>(getInput().getType()).getWidth();
2008
2009 if (opWidth > inputWidth)
2010 return emitError("slice width should not be greater than input width");
2011 if (opWidth != resultWidth)
2012 return emitError("result width must be equal to slice width");
2013 return success();
2014}
2015
2016//===----------------------------------------------------------------------===//
2017// StructFieldInOutOp
2018//===----------------------------------------------------------------------===//
2019
2020LogicalResult StructFieldInOutOp::inferReturnTypes(
2021 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
2022 DictionaryAttr attrs, mlir::PropertyRef properties,
2023 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
2024 Adaptor adaptor(operands, attrs, properties, regions);
2025 auto field = adaptor.getFieldAttr();
2026 if (!field)
2027 return failure();
2028 auto structType =
2029 hw::type_cast<hw::StructType>(getInOutElementType(operands[0].getType()));
2030 auto resultType = structType.getFieldType(field);
2031 if (!resultType)
2032 return failure();
2033
2034 results.push_back(hw::InOutType::get(resultType));
2035 return success();
2036}
2037
2038//===----------------------------------------------------------------------===//
2039// Other ops.
2040//===----------------------------------------------------------------------===//
2041
2042LogicalResult AliasOp::verify() {
2043 // Must have at least two operands.
2044 if (getAliases().size() < 2)
2045 return emitOpError("alias must have at least two operands");
2046
2047 return success();
2048}
2049
2050//===----------------------------------------------------------------------===//
2051// BindOp
2052//===----------------------------------------------------------------------===//
2053
2054/// Instances must be at the top level of the hw.module (or within a `ifdef)
2055// and are typically at the end of it, so we scan backwards to find them.
2056template <class Op>
2057static Op findInstanceSymbolInBlock(StringAttr name, Block *body) {
2058 for (auto &op : llvm::reverse(body->getOperations())) {
2059 if (auto instance = dyn_cast<Op>(op)) {
2060 if (auto innerSym = instance.getInnerSym())
2061 if (innerSym->getSymName() == name)
2062 return instance;
2063 }
2064
2065 if (auto ifdef = dyn_cast<IfDefOp>(op)) {
2066 if (auto result =
2067 findInstanceSymbolInBlock<Op>(name, ifdef.getThenBlock()))
2068 return result;
2069 if (ifdef.hasElse())
2070 if (auto result =
2071 findInstanceSymbolInBlock<Op>(name, ifdef.getElseBlock()))
2072 return result;
2073 }
2074 }
2075 return {};
2076}
2077
2078hw::InstanceOp BindOp::getReferencedInstance(const hw::HWSymbolCache *cache) {
2079 // If we have a cache, directly look up the referenced instance.
2080 if (cache) {
2081 auto result = cache->getInnerDefinition(getInstance());
2082 return cast<hw::InstanceOp>(result.getOp());
2083 }
2084
2085 // Otherwise, resolve the instance by looking up the module ...
2086 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
2087 if (!topLevelModuleOp)
2088 return {};
2089
2090 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
2091 topLevelModuleOp.lookupSymbol(getInstance().getModule()));
2092 if (!hwModule)
2093 return {};
2094
2095 // ... then look up the instance within it.
2096 return findInstanceSymbolInBlock<hw::InstanceOp>(getInstance().getName(),
2097 hwModule.getBodyBlock());
2098}
2099
2100/// Ensure that the symbol being instantiated exists and is an InterfaceOp.
2101LogicalResult BindOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2102 auto module = (*this)->getParentOfType<mlir::ModuleOp>();
2103 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
2104 symbolTable.lookupSymbolIn(module, getInstance().getModule()));
2105 if (!hwModule)
2106 return emitError("Referenced module doesn't exist ")
2107 << getInstance().getModule() << "::" << getInstance().getName();
2108
2109 auto inst = findInstanceSymbolInBlock<hw::InstanceOp>(
2110 getInstance().getName(), hwModule.getBodyBlock());
2111 if (!inst)
2112 return emitError("Referenced instance doesn't exist ")
2113 << getInstance().getModule() << "::" << getInstance().getName();
2114 if (!inst.getDoNotPrint())
2115 return emitError("Referenced instance isn't marked as doNotPrint");
2116 return success();
2117}
2118
2119void BindOp::build(OpBuilder &builder, OperationState &odsState, StringAttr mod,
2120 StringAttr name) {
2121 auto ref = hw::InnerRefAttr::get(mod, name);
2122 odsState.addAttribute("instance", ref);
2123}
2124
2125//===----------------------------------------------------------------------===//
2126// SVVerbatimSourceOp
2127//===----------------------------------------------------------------------===//
2128
2129void SVVerbatimSourceOp::print(OpAsmPrinter &p) {
2130 p << ' ';
2131
2132 StringRef visibilityAttrName =
2133 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
2134 if (auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
2135 p << visibility.getValue() << ' ';
2136
2137 p.printSymbolName(getSymName());
2138
2139 // Print parameters
2140 circt::printOptionalParameterList(p, *this, getParameters());
2141
2142 // Print attributes using the helper function
2143 SmallVector<StringRef> omittedAttrs = {getSymNameAttrName(), "parameters",
2144 visibilityAttrName};
2145
2146 p.printOptionalAttrDictWithKeyword((*this)->getAttrs(), omittedAttrs);
2147}
2148
2149ParseResult SVVerbatimSourceOp::parse(OpAsmParser &parser,
2150 OperationState &result) {
2151
2152 // parse optional visibility
2153 StringRef visibilityAttrName =
2154 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
2155 StringRef visibility;
2156 if (succeeded(parser.parseOptionalKeyword(&visibility,
2157 {"public", "private", "nested"}))) {
2158 result.addAttribute(visibilityAttrName,
2159 parser.getBuilder().getStringAttr(visibility));
2160 }
2161
2162 // Parse the symbol name
2163 StringAttr nameAttr;
2164 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
2165 result.attributes))
2166 return failure();
2167
2168 // Parse optional parameters
2169 ArrayAttr parameters;
2170 if (circt::parseOptionalParameterList(parser, parameters))
2171 return failure();
2172 result.addAttribute("parameters", parameters);
2173
2174 // Parse attributes using the helper function
2175 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
2176 return failure();
2177
2178 return success();
2179}
2180
2181LogicalResult SVVerbatimSourceOp::verify() {
2182 // must have verbatim content
2183 if (getContent().empty())
2184 return emitOpError("missing or empty content attribute");
2185
2186 return success();
2187}
2188
2189LogicalResult
2190SVVerbatimSourceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2191 // Verify that all symbols in additional_files are emit.file operations
2192 if (auto additionalFiles = getAdditionalFiles()) {
2193 for (auto fileRef : *additionalFiles) {
2194 auto flatRef = dyn_cast<FlatSymbolRefAttr>(fileRef);
2195 if (!flatRef)
2196 return emitOpError(
2197 "additional_files must contain flat symbol references");
2198
2199 auto *referencedOp =
2200 symbolTable.lookupNearestSymbolFrom(getOperation(), flatRef);
2201 if (!referencedOp)
2202 return emitOpError("references nonexistent file ")
2203 << flatRef.getValue();
2204
2205 // Check that the referenced operation is an emit.file
2206 if (referencedOp->getName().getStringRef() != "emit.file")
2207 return emitOpError("references ")
2208 << flatRef.getValue() << ", which is not an emit.file";
2209 }
2210 }
2211
2212 return success();
2213}
2214
2215//===----------------------------------------------------------------------===//
2216// SVVerbatimModuleOp
2217//===----------------------------------------------------------------------===//
2218
2219SmallVector<hw::PortInfo> SVVerbatimModuleOp::getPortList() {
2220 SmallVector<hw::PortInfo> ports;
2221 auto moduleType = getModuleType();
2222 auto portLocs = getPortLocs();
2223 auto portAttrs = getPerPortAttrs();
2224
2225 for (size_t i = 0, e = moduleType.getNumPorts(); i < e; ++i) {
2226 auto port = moduleType.getPorts()[i];
2227 LocationAttr loc = portLocs && i < portLocs->size()
2228 ? cast<LocationAttr>((*portLocs)[i])
2229 : UnknownLoc::get(getContext());
2230 DictionaryAttr attrs = portAttrs && i < portAttrs->size()
2231 ? cast<DictionaryAttr>((*portAttrs)[i])
2232 : DictionaryAttr::get(getContext());
2239 size_t argNum = moduleType.isOutput(i) ? moduleType.getOutputIdForPortId(i)
2240 : moduleType.getInputIdForPortId(i);
2241 ports.push_back({{port.name, port.type, dir}, argNum, attrs, loc});
2242 }
2243 return ports;
2244}
2245
2246hw::PortInfo SVVerbatimModuleOp::getPort(size_t idx) {
2247 return getPortList()[idx];
2248}
2249
2250size_t SVVerbatimModuleOp::getPortIdForInputId(size_t idx) {
2251 return getModuleType().getPortIdForInputId(idx);
2252}
2253
2254size_t SVVerbatimModuleOp::getPortIdForOutputId(size_t idx) {
2255 return getModuleType().getPortIdForOutputId(idx);
2256}
2257
2258size_t SVVerbatimModuleOp::getNumPorts() {
2259 return getModuleType().getNumPorts();
2260}
2261
2262size_t SVVerbatimModuleOp::getNumInputPorts() {
2263 return getModuleType().getNumInputs();
2264}
2265
2266size_t SVVerbatimModuleOp::getNumOutputPorts() {
2267 return getModuleType().getNumOutputs();
2268}
2269
2270hw::ModuleType SVVerbatimModuleOp::getHWModuleType() { return getModuleType(); }
2271
2272ArrayRef<Attribute> SVVerbatimModuleOp::getAllPortAttrs() {
2273 if (auto attrs = getPerPortAttrs())
2274 return attrs->getValue();
2275 return {};
2276}
2277
2278void SVVerbatimModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
2279 setPerPortAttrsAttr(ArrayAttr::get(getContext(), attrs));
2280}
2281
2282void SVVerbatimModuleOp::removeAllPortAttrs() { removePerPortAttrsAttr(); }
2283
2284SmallVector<Location> SVVerbatimModuleOp::getAllPortLocs() {
2285 if (auto locs = getPortLocs()) {
2286 SmallVector<Location> result;
2287 result.reserve(locs->size());
2288 for (auto loc : *locs)
2289 result.push_back(cast<Location>(loc));
2290 return result;
2291 }
2292 return SmallVector<Location>(getNumPorts(), UnknownLoc::get(getContext()));
2293}
2294
2295void SVVerbatimModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
2296 setPortLocsAttr(ArrayAttr::get(getContext(), locs));
2297}
2298
2299void SVVerbatimModuleOp::setHWModuleType(hw::ModuleType type) {
2300 setModuleTypeAttr(TypeAttr::get(type));
2301}
2302
2303void SVVerbatimModuleOp::setAllPortNames(ArrayRef<Attribute> names) {
2304 // Port names are part of the module type, so we need to reconstruct it
2305 auto currentType = getModuleType();
2306 SmallVector<hw::ModulePort> ports;
2307 for (size_t i = 0, e = currentType.getNumPorts(); i < e; ++i) {
2308 auto port = currentType.getPorts()[i];
2309 if (i < names.size())
2310 port.name = cast<StringAttr>(names[i]);
2311 ports.push_back(port);
2312 }
2313 setHWModuleType(hw::ModuleType::get(getContext(), ports));
2314}
2315
2316void SVVerbatimModuleOp::print(OpAsmPrinter &p) {
2317 p << ' ';
2318
2319 StringRef visibilityAttrName =
2320 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
2321 if (auto visibility = (*this)->getAttrOfType<StringAttr>(visibilityAttrName))
2322 p << visibility.getValue() << ' ';
2323
2324 p.printSymbolName(SymbolTable::getSymbolName(*this).getValue());
2325
2326 printOptionalParameterList(p, *this, getParameters());
2327
2328 Region emptyRegion;
2330 p, emptyRegion, getModuleType(), getAllPortAttrs(), getAllPortLocs());
2331
2332 SmallVector<StringRef> omittedAttrs = {
2333 getSymNameAttrName(),
2334 mlir::SymbolOpInterface::getDefaultVisibilityAttrName(),
2335 getModuleTypeAttrName().getValue(),
2336 getPerPortAttrsAttrName().getValue(),
2337 getPortLocsAttrName().getValue(),
2338 getParametersAttrName().getValue()};
2339
2340 mlir::function_interface_impl::printFunctionAttributes(p, *this,
2341 omittedAttrs);
2342}
2343
2344ParseResult SVVerbatimModuleOp::parse(OpAsmParser &parser,
2345 OperationState &result) {
2346 using namespace mlir::function_interface_impl;
2347 auto builder = parser.getBuilder();
2348
2349 // Parse the visibility attribute.
2350 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
2351
2352 // Parse the name as a symbol.
2353 StringAttr nameAttr;
2354 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
2355 result.attributes))
2356 return failure();
2357
2358 // Parse the parameters.
2359 ArrayAttr parameters;
2360 if (parseOptionalParameterList(parser, parameters))
2361 return failure();
2362
2363 SmallVector<hw::module_like_impl::PortParse> ports;
2364 TypeAttr modType;
2365 if (failed(
2366 hw::module_like_impl::parseModuleSignature(parser, ports, modType)))
2367 return failure();
2368
2369 result.addAttribute(getModuleTypeAttrName(result.name), modType);
2370 result.addAttribute("parameters", parameters);
2371
2372 // Convert the specified array of dictionary attrs (which may have null
2373 // entries) to an ArrayAttr of dictionaries.
2374 auto unknownLoc = builder.getUnknownLoc();
2375 SmallVector<Attribute> attrs, locs;
2376
2377 for (auto &port : ports) {
2378 attrs.push_back(port.attrs ? port.attrs : builder.getDictionaryAttr({}));
2379 auto loc = port.sourceLoc ? Location(*port.sourceLoc) : unknownLoc;
2380 locs.push_back(loc);
2381 }
2382
2383 if (!attrs.empty())
2384 result.addAttribute("per_port_attrs", builder.getArrayAttr(attrs));
2385 if (!locs.empty())
2386 result.addAttribute("port_locs", builder.getArrayAttr(locs));
2387
2388 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
2389 return failure();
2390
2391 // Verify required attributes exist
2392 if (!result.attributes.get("source"))
2393 return parser.emitError(parser.getCurrentLocation(),
2394 "sv.verbatim.module requires 'source' attribute");
2395
2396 return success();
2397}
2398
2399LogicalResult SVVerbatimModuleOp::verify() { return success(); }
2400
2401LogicalResult
2402SVVerbatimModuleOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2403 // Verify that the source attribute references an sv.verbatim.source operation
2404 auto sourceOp = dyn_cast_or_null<SVVerbatimSourceOp>(
2405 symbolTable.lookupNearestSymbolFrom(*this, getSourceAttr()));
2406 if (!sourceOp)
2407 return emitError("references ") << getSourceAttr().getAttr().getValue()
2408 << ", which is not an sv.verbatim.source";
2409
2410 return success();
2411}
2412
2413//===----------------------------------------------------------------------===//
2414// BindInterfaceOp
2415//===----------------------------------------------------------------------===//
2416
2417sv::InterfaceInstanceOp
2418BindInterfaceOp::getReferencedInstance(const hw::HWSymbolCache *cache) {
2419 // If we have a cache, directly look up the referenced instance.
2420 if (cache) {
2421 auto result = cache->getInnerDefinition(getInstance());
2422 return cast<sv::InterfaceInstanceOp>(result.getOp());
2423 }
2424
2425 // Otherwise, resolve the instance by looking up the module ...
2426 auto *symbolTable = SymbolTable::getNearestSymbolTable(*this);
2427 if (!symbolTable)
2428 return {};
2429 auto *parentOp =
2430 lookupSymbolInNested(symbolTable, getInstance().getModule().getValue());
2431 if (!parentOp)
2432 return {};
2433
2434 // ... then look up the instance within it.
2435 return findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
2436 getInstance().getName(), &parentOp->getRegion(0).front());
2437}
2438
2439/// Ensure that the symbol being instantiated exists and is an InterfaceOp.
2440LogicalResult
2441BindInterfaceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2442 auto *parentOp =
2443 symbolTable.lookupNearestSymbolFrom(*this, getInstance().getModule());
2444 if (!parentOp)
2445 return emitError("Referenced module doesn't exist ")
2446 << getInstance().getModule() << "::" << getInstance().getName();
2447
2448 auto inst = findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
2449 getInstance().getName(), &parentOp->getRegion(0).front());
2450 if (!inst)
2451 return emitError("Referenced interface doesn't exist ")
2452 << getInstance().getModule() << "::" << getInstance().getName();
2453 if (!inst.getDoNotPrint())
2454 return emitError("Referenced interface isn't marked as doNotPrint");
2455 return success();
2456}
2457
2458//===----------------------------------------------------------------------===//
2459// XMROp
2460//===----------------------------------------------------------------------===//
2461
2462ParseResult parseXMRPath(::mlir::OpAsmParser &parser, ArrayAttr &pathAttr,
2463 StringAttr &terminalAttr) {
2464 SmallVector<Attribute> strings;
2465 ParseResult ret = parser.parseCommaSeparatedList([&]() {
2466 StringAttr result;
2467 StringRef keyword;
2468 if (succeeded(parser.parseOptionalKeyword(&keyword))) {
2469 strings.push_back(parser.getBuilder().getStringAttr(keyword));
2470 return success();
2471 }
2472 if (succeeded(parser.parseAttribute(
2473 result, parser.getBuilder().getType<NoneType>()))) {
2474 strings.push_back(result);
2475 return success();
2476 }
2477 return failure();
2478 });
2479 if (succeeded(ret)) {
2480 pathAttr = parser.getBuilder().getArrayAttr(
2481 ArrayRef<Attribute>(strings).drop_back());
2482 terminalAttr = cast<StringAttr>(*strings.rbegin());
2483 }
2484 return ret;
2485}
2486
2487void printXMRPath(OpAsmPrinter &p, XMROp op, ArrayAttr pathAttr,
2488 StringAttr terminalAttr) {
2489 llvm::interleaveComma(pathAttr, p);
2490 p << ", " << terminalAttr;
2491}
2492
2493/// Ensure that the symbol being instantiated exists and is a HierPathOp.
2494LogicalResult XMRRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2495 auto *table = SymbolTable::getNearestSymbolTable(*this);
2496 auto path = dyn_cast_or_null<hw::HierPathOp>(
2497 symbolTable.lookupSymbolIn(table, getRefAttr()));
2498 if (!path)
2499 return emitError("Referenced path doesn't exist ") << getRefAttr();
2500
2501 return success();
2502}
2503
2504hw::HierPathOp XMRRefOp::getReferencedPath(const hw::HWSymbolCache *cache) {
2505 if (cache)
2506 if (auto *result = cache->getDefinition(getRefAttr().getAttr()))
2507 return cast<hw::HierPathOp>(result);
2508
2509 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
2510 return topLevelModuleOp.lookupSymbol<hw::HierPathOp>(getRefAttr().getValue());
2511}
2512
2513//===----------------------------------------------------------------------===//
2514// Verification Ops.
2515//===----------------------------------------------------------------------===//
2516
2517static LogicalResult eraseIfZeroOrNotZero(Operation *op, Value value,
2518 PatternRewriter &rewriter,
2519 bool eraseIfZero) {
2520 if (auto constant = value.getDefiningOp<hw::ConstantOp>())
2521 if (constant.getValue().isZero() == eraseIfZero) {
2522 rewriter.eraseOp(op);
2523 return success();
2524 }
2525
2526 return failure();
2527}
2528
2529template <class Op, bool EraseIfZero = false>
2530static LogicalResult canonicalizeImmediateVerifOp(Op op,
2531 PatternRewriter &rewriter) {
2532 return eraseIfZeroOrNotZero(op, op.getExpression(), rewriter, EraseIfZero);
2533}
2534
2535void AssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
2536 MLIRContext *context) {
2537 results.add(canonicalizeImmediateVerifOp<AssertOp>);
2538}
2539
2540void AssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2541 MLIRContext *context) {
2542 results.add(canonicalizeImmediateVerifOp<AssumeOp>);
2543}
2544
2545void CoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
2546 MLIRContext *context) {
2547 results.add(canonicalizeImmediateVerifOp<CoverOp, /* EraseIfZero = */ true>);
2548}
2549
2550template <class Op, bool EraseIfZero = false>
2551static LogicalResult canonicalizeConcurrentVerifOp(Op op,
2552 PatternRewriter &rewriter) {
2553 return eraseIfZeroOrNotZero(op, op.getProperty(), rewriter, EraseIfZero);
2554}
2555
2556void AssertConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2557 MLIRContext *context) {
2558 results.add(canonicalizeConcurrentVerifOp<AssertConcurrentOp>);
2559}
2560
2561void AssumeConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2562 MLIRContext *context) {
2563 results.add(canonicalizeConcurrentVerifOp<AssumeConcurrentOp>);
2564}
2565
2566void CoverConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2567 MLIRContext *context) {
2568 results.add(
2569 canonicalizeConcurrentVerifOp<CoverConcurrentOp, /* EraseIfZero */ true>);
2570}
2571
2572//===----------------------------------------------------------------------===//
2573// SV generate ops
2574//===----------------------------------------------------------------------===//
2575
2576/// Parse cases formatted like:
2577/// case (pattern, "name") { ... }
2578bool parseCaseRegions(OpAsmParser &p, ArrayAttr &patternsArray,
2579 ArrayAttr &caseNamesArray,
2580 SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
2581 SmallVector<Attribute> patterns;
2582 SmallVector<Attribute> names;
2583 while (!p.parseOptionalKeyword("case")) {
2584 Attribute pattern;
2585 StringAttr name;
2586 std::unique_ptr<Region> region = std::make_unique<Region>();
2587 if (p.parseLParen() || p.parseAttribute(pattern) || p.parseComma() ||
2588 p.parseAttribute(name) || p.parseRParen() || p.parseRegion(*region))
2589 return true;
2590 patterns.push_back(pattern);
2591 names.push_back(name);
2592 if (region->empty())
2593 region->push_back(new Block());
2594 caseRegions.push_back(std::move(region));
2595 }
2596 patternsArray = p.getBuilder().getArrayAttr(patterns);
2597 caseNamesArray = p.getBuilder().getArrayAttr(names);
2598 return false;
2599}
2600
2601/// Print cases formatted like:
2602/// case (pattern, "name") { ... }
2603void printCaseRegions(OpAsmPrinter &p, Operation *, ArrayAttr patternsArray,
2604 ArrayAttr namesArray,
2605 MutableArrayRef<Region> caseRegions) {
2606 assert(patternsArray.size() == caseRegions.size());
2607 assert(patternsArray.size() == namesArray.size());
2608 for (size_t i = 0, e = caseRegions.size(); i < e; ++i) {
2609 p.printNewline();
2610 p << "case (" << patternsArray[i] << ", " << namesArray[i] << ") ";
2611 p.printRegion(caseRegions[i]);
2612 }
2613 p.printNewline();
2614}
2615
2616LogicalResult GenerateCaseOp::verify() {
2617 size_t numPatterns = getCasePatterns().size();
2618 if (getCaseRegions().size() != numPatterns ||
2619 getCaseNames().size() != numPatterns)
2620 return emitOpError(
2621 "Size of caseRegions, patterns, and caseNames must match");
2622
2623 StringSet<> usedNames;
2624 for (Attribute name : getCaseNames()) {
2625 StringAttr nameStr = dyn_cast<StringAttr>(name);
2626 if (!nameStr)
2627 return emitOpError("caseNames must all be string attributes");
2628 if (usedNames.contains(nameStr.getValue()))
2629 return emitOpError("caseNames must be unique");
2630 usedNames.insert(nameStr.getValue());
2631 }
2632
2633 // mlir::FailureOr<Type> condType = evaluateParametricType();
2634
2635 return success();
2636}
2637
2638//===----------------------------------------------------------------------===//
2639// GenerateForOp
2640//===----------------------------------------------------------------------===//
2641
2642// Parse attribute and also optional trailing type if there. This is needed
2643// primarily for integer types as when given a type, they hapily parse without
2644// consuming the colon type.
2645static ParseResult parseTypedAttrWithFallback(OpAsmParser &parser,
2646 TypedAttr &result, Type type) {
2647 Attribute attr;
2648 // Try parsing with the expected type (no type suffix).
2649 if (succeeded(parser.parseCustomAttributeWithFallback(attr, type))) {
2650 auto typedAttr = dyn_cast<TypedAttr>(attr);
2651 if (!typedAttr || typedAttr.getType() != type) {
2652 return parser.emitError(parser.getCurrentLocation(),
2653 "expected typed attribute with type ")
2654 << type;
2655 }
2656
2657 // We are being given a type to parse extra.
2658 if (succeeded(parser.parseOptionalColon())) {
2659 Type localType;
2660 if (failed(parser.parseType(localType)) || localType != type)
2661 return parser.emitError(parser.getCurrentLocation(),
2662 "expected typed attribute with type ")
2663 << type;
2664 }
2665
2666 result = typedAttr;
2667 return success();
2668 }
2669
2670 return failure();
2671}
2672
2673// Parse the header and body of a generate for loop.
2674static ParseResult parseGenerateFor(OpAsmParser &parser, TypedAttr &lowerBound,
2675 TypedAttr &upperBound, TypedAttr &step,
2676 StringAttr &inductionVarName,
2677 StringAttr &genBlockName, Region &body) {
2678 auto &builder = parser.getBuilder();
2679
2680 OpAsmParser::Argument inductionVariable;
2681 if (parser.parseArgument(inductionVariable, /*allowType=*/true))
2682 return parser.emitError(parser.getCurrentLocation(),
2683 "expected induction variable argument");
2684
2685 // Parse induction variable assignment.
2686 if (parser.parseEqual())
2687 return failure();
2688
2689 // Parse lower bound.
2690 Type type = inductionVariable.type;
2691 if (parseTypedAttrWithFallback(parser, lowerBound, type))
2692 return failure();
2693
2694 if (parser.parseKeyword("to"))
2695 return failure();
2696
2697 // Parse upper bound.
2698 if (parseTypedAttrWithFallback(parser, upperBound, type))
2699 return failure();
2700
2701 if (parser.parseKeyword("step"))
2702 return failure();
2703
2704 // Parse step.
2705 if (parseTypedAttrWithFallback(parser, step, type))
2706 return failure();
2707
2708 if (parser.parseKeyword("name"))
2709 return failure();
2710
2711 // Parse gen block name.
2712 if (parser.parseCustomAttributeWithFallback(
2713 genBlockName, parser.getBuilder().getType<NoneType>()))
2714 return failure();
2715
2716 // Store the induction variable name if it's not a number.
2717 if (!isdigit(inductionVariable.ssaName.name.front()))
2718 inductionVarName =
2719 builder.getStringAttr(inductionVariable.ssaName.name.drop_front());
2720
2721 SmallVector<OpAsmParser::Argument, 1> regionArgs = {inductionVariable};
2722 return parser.parseRegion(body, regionArgs);
2723}
2724
2725// Print the header and body of a generate for loop.
2726static void printGenerateFor(OpAsmPrinter &p, Operation *op,
2727 TypedAttr lowerBound, TypedAttr upperBound,
2728 TypedAttr step, StringAttr inductionVarName,
2729 StringAttr genBlockName, Region &body) {
2730 auto forOp = cast<GenerateForOp>(op);
2731 p << forOp.getInductionVar() << " : " << forOp.getInductionVar().getType()
2732 << " = ";
2733 p.printStrippedAttrOrType(lowerBound);
2734 p << " to ";
2735 p.printStrippedAttrOrType(upperBound);
2736 p << " step ";
2737 p.printStrippedAttrOrType(step);
2738 p << " name ";
2739 p.printAttributeWithoutType(genBlockName);
2740 p << " ";
2741 p.printRegion(body, /*printEntryBlockArgs=*/false,
2742 /*printBlockTerminators=*/true);
2743}
2744
2745LogicalResult GenerateForOp::verify() {
2746 if (getBody().getBlocks().front().getNumArguments() != 1)
2747 return emitOpError("must have exactly one block argument");
2748 Type type = getLowerBound().getType();
2749 if (getBody().getBlocks().front().getArgument(0).getType() != type)
2750 return emitOpError("block argument type must match loop bounds type");
2751 if (!isa<IntegerType>(type))
2752 return emitOpError("loop bounds must be integer types");
2753
2754 return success();
2755}
2756
2757void GenerateForOp::getAsmBlockArgumentNames(
2758 mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
2759 auto *block = &region.front();
2760 if (auto attr = getInductionVarNameAttr())
2761 setNameFn(block->getArgument(0), attr);
2762}
2763
2764ModportStructAttr ModportStructAttr::get(MLIRContext *context,
2765 ModportDirection direction,
2766 FlatSymbolRefAttr signal) {
2767 return get(context, ModportDirectionAttr::get(context, direction), signal);
2768}
2769
2770//===----------------------------------------------------------------------===//
2771// FuncOp
2772//===----------------------------------------------------------------------===//
2773
2774ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
2775 auto builder = parser.getBuilder();
2776 // Parse visibility.
2777 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
2778
2779 // Parse the name as a symbol.
2780 StringAttr nameAttr;
2781 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
2782 result.attributes))
2783 return failure();
2784
2785 SmallVector<hw::module_like_impl::PortParse> ports;
2786 TypeAttr modType;
2787 if (failed(
2788 hw::module_like_impl::parseModuleSignature(parser, ports, modType)))
2789 return failure();
2790
2791 result.addAttribute(FuncOp::getModuleTypeAttrName(result.name), modType);
2792
2793 // Convert the specified array of dictionary attrs (which may have null
2794 // entries) to an ArrayAttr of dictionaries.
2795 auto unknownLoc = builder.getUnknownLoc();
2796 SmallVector<Attribute> attrs, inputLocs, outputLocs;
2797 auto nonEmptyLocsFn = [unknownLoc](Attribute attr) {
2798 return attr && cast<Location>(attr) != unknownLoc;
2799 };
2800
2801 for (auto &port : ports) {
2802 attrs.push_back(port.attrs ? port.attrs : builder.getDictionaryAttr({}));
2803 auto loc = port.sourceLoc ? Location(*port.sourceLoc) : unknownLoc;
2804 (port.direction == hw::PortInfo::Direction::Output ? outputLocs : inputLocs)
2805 .push_back(loc);
2806 }
2807
2808 result.addAttribute(FuncOp::getPerArgumentAttrsAttrName(result.name),
2809 builder.getArrayAttr(attrs));
2810
2811 if (llvm::any_of(outputLocs, nonEmptyLocsFn))
2812 result.addAttribute(FuncOp::getResultLocsAttrName(result.name),
2813 builder.getArrayAttr(outputLocs));
2814 // Parse the attribute dict.
2815 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
2816 return failure();
2817
2818 // Add the entry block arguments.
2819 SmallVector<OpAsmParser::Argument, 4> entryArgs;
2820 for (auto &port : ports)
2821 if (port.direction != hw::ModulePort::Direction::Output)
2822 entryArgs.push_back(port);
2823
2824 // Parse the optional function body. The printer will not print the body if
2825 // its empty, so disallow parsing of empty body in the parser.
2826 auto *body = result.addRegion();
2827 llvm::SMLoc loc = parser.getCurrentLocation();
2828
2829 mlir::OptionalParseResult parseResult =
2830 parser.parseOptionalRegion(*body, entryArgs,
2831 /*enableNameShadowing=*/false);
2832 if (parseResult.has_value()) {
2833 if (failed(*parseResult))
2834 return failure();
2835 // Function body was parsed, make sure its not empty.
2836 if (body->empty())
2837 return parser.emitError(loc, "expected non-empty function body");
2838 } else {
2839 if (llvm::any_of(inputLocs, nonEmptyLocsFn))
2840 result.addAttribute(FuncOp::getInputLocsAttrName(result.name),
2841 builder.getArrayAttr(inputLocs));
2842 }
2843
2844 return success();
2845}
2846
2847void FuncOp::getAsmBlockArgumentNames(mlir::Region &region,
2848 mlir::OpAsmSetValueNameFn setNameFn) {
2849 if (region.empty())
2850 return;
2851 // Assign port names to the bbargs.
2852 auto func = cast<FuncOp>(region.getParentOp());
2853
2854 auto *block = &region.front();
2855
2856 auto names = func.getModuleType().getInputNames();
2857 for (size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
2858 // Let mlir deterministically convert names to valid identifiers
2859 setNameFn(block->getArgument(i), cast<StringAttr>(names[i]));
2860 }
2861}
2862
2863Type FuncOp::getExplicitlyReturnedType() {
2864 if (!getPerArgumentAttrs() || getNumOutputs() == 0)
2865 return {};
2866
2867 // Check if the last port is used as an explicit return.
2868 auto lastArgument = getModuleType().getPorts().back();
2869 auto lastArgumentAttr = dyn_cast<DictionaryAttr>(
2870 getPerArgumentAttrsAttr()[getPerArgumentAttrsAttr().size() - 1]);
2871
2872 if (lastArgument.dir == hw::ModulePort::Output && lastArgumentAttr &&
2873 lastArgumentAttr.getAs<UnitAttr>(getExplicitlyReturnedAttrName()))
2874 return lastArgument.type;
2875 return {};
2876}
2877
2878ArrayRef<Attribute> FuncOp::getAllPortAttrs() {
2879 if (getPerArgumentAttrs())
2880 return getPerArgumentAttrs()->getValue();
2881 return {};
2882}
2883
2884void FuncOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
2885 setPerArgumentAttrsAttr(ArrayAttr::get(getContext(), attrs));
2886}
2887
2888void FuncOp::removeAllPortAttrs() { setPerArgumentAttrsAttr({}); }
2889SmallVector<Location> FuncOp::getAllPortLocs() {
2890 SmallVector<Location> portLocs;
2891 portLocs.reserve(getNumPorts());
2892 auto resultLocs = getResultLocsAttr();
2893 unsigned inputCount = 0;
2894 auto modType = getModuleType();
2895 auto unknownLoc = UnknownLoc::get(getContext());
2896 auto *body = getBodyBlock();
2897 auto inputLocs = getInputLocsAttr();
2898 for (unsigned i = 0, e = getNumPorts(); i < e; ++i) {
2899 if (modType.isOutput(i)) {
2900 auto loc = resultLocs
2901 ? cast<Location>(
2902 resultLocs.getValue()[portLocs.size() - inputCount])
2903 : unknownLoc;
2904 portLocs.push_back(loc);
2905 } else {
2906 auto loc = body ? body->getArgument(inputCount).getLoc()
2907 : (inputLocs ? cast<Location>(inputLocs[inputCount])
2908 : unknownLoc);
2909 portLocs.push_back(loc);
2910 ++inputCount;
2911 }
2912 }
2913 return portLocs;
2914}
2915
2916void FuncOp::setAllPortLocsAttrs(llvm::ArrayRef<mlir::Attribute> locs) {
2917 SmallVector<Attribute> resultLocs, inputLocs;
2918 unsigned inputCount = 0;
2919 auto modType = getModuleType();
2920 auto *body = getBodyBlock();
2921 for (unsigned i = 0, e = getNumPorts(); i < e; ++i) {
2922 if (modType.isOutput(i))
2923 resultLocs.push_back(locs[i]);
2924 else if (body)
2925 body->getArgument(inputCount++).setLoc(cast<Location>(locs[i]));
2926 else // Need to store locations in an attribute if declaration.
2927 inputLocs.push_back(locs[i]);
2928 }
2929 setResultLocsAttr(ArrayAttr::get(getContext(), resultLocs));
2930 if (!body)
2931 setInputLocsAttr(ArrayAttr::get(getContext(), inputLocs));
2932}
2933
2934SmallVector<hw::PortInfo> FuncOp::getPortList() { return getPortList(false); }
2935
2936hw::PortInfo FuncOp::getPort(size_t idx) {
2937 auto modTy = getHWModuleType();
2938 auto emptyDict = DictionaryAttr::get(getContext());
2939 LocationAttr loc = getPortLoc(idx);
2940 DictionaryAttr attrs = dyn_cast_or_null<DictionaryAttr>(getPortAttrs(idx));
2941 if (!attrs)
2942 attrs = emptyDict;
2943 return {modTy.getPorts()[idx],
2944 modTy.isOutput(idx) ? modTy.getOutputIdForPortId(idx)
2945 : modTy.getInputIdForPortId(idx),
2946 attrs, loc};
2947}
2948
2949SmallVector<hw::PortInfo> FuncOp::getPortList(bool excludeExplicitReturn) {
2950 auto modTy = getModuleType();
2951 auto emptyDict = DictionaryAttr::get(getContext());
2952 auto skipLastArgument = getExplicitlyReturnedType() && excludeExplicitReturn;
2953 SmallVector<hw::PortInfo> retval;
2954 auto portAttr = getAllPortLocs();
2955 for (unsigned i = 0, e = skipLastArgument ? modTy.getNumPorts() - 1
2956 : modTy.getNumPorts();
2957 i < e; ++i) {
2958 DictionaryAttr attrs = emptyDict;
2959 if (auto perArgumentAttr = getPerArgumentAttrs())
2960 if (auto argumentAttr =
2961 dyn_cast_or_null<DictionaryAttr>((*perArgumentAttr)[i]))
2962 attrs = argumentAttr;
2963
2964 retval.push_back({modTy.getPorts()[i],
2965 modTy.isOutput(i) ? modTy.getOutputIdForPortId(i)
2966 : modTy.getInputIdForPortId(i),
2967 attrs, portAttr[i]});
2968 }
2969 return retval;
2970}
2971
2972void FuncOp::print(OpAsmPrinter &p) {
2973 FuncOp op = *this;
2974 // Print the operation and the function name.
2975 auto funcName = op.getName();
2976 p << ' ';
2977
2978 StringRef visibilityAttrName =
2979 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
2980 if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
2981 p << visibility.getValue() << ' ';
2982 p.printSymbolName(funcName);
2984 p, op.getBody(), op.getModuleType(),
2985 op.getPerArgumentAttrsAttr()
2986 ? ArrayRef<Attribute>(op.getPerArgumentAttrsAttr().getValue())
2987 : ArrayRef<Attribute>{},
2988 getAllPortLocs());
2989
2990 mlir::function_interface_impl::printFunctionAttributes(
2991 p, op,
2992 {visibilityAttrName, getModuleTypeAttrName(),
2993 getPerArgumentAttrsAttrName(), getInputLocsAttrName(),
2994 getResultLocsAttrName()});
2995 // Print the body if this is not an external function.
2996 Region &body = op->getRegion(0);
2997 if (!body.empty()) {
2998 p << ' ';
2999 p.printRegion(body, /*printEntryBlockArgs=*/false,
3000 /*printBlockTerminators=*/true);
3001 }
3002}
3003
3004//===----------------------------------------------------------------------===//
3005// ReturnOp
3006//===----------------------------------------------------------------------===//
3007
3008LogicalResult ReturnOp::verify() {
3009 auto func = getParentOp<sv::FuncOp>();
3010 auto funcResults = func.getResultTypes();
3011 auto returnedValues = getOperands();
3012 if (funcResults.size() != returnedValues.size())
3013 return emitOpError("must have same number of operands as region results.");
3014 // Check that the types of our operands and the region's results match.
3015 for (size_t i = 0, e = funcResults.size(); i < e; ++i) {
3016 if (funcResults[i] != returnedValues[i].getType()) {
3017 emitOpError("output types must match function. In "
3018 "operand ")
3019 << i << ", expected " << funcResults[i] << ", but got "
3020 << returnedValues[i].getType() << ".";
3021 return failure();
3022 }
3023 }
3024 return success();
3025}
3026
3027//===----------------------------------------------------------------------===//
3028// Call Ops
3029//===----------------------------------------------------------------------===//
3030
3031static Value
3033 mlir::Operation::result_range results) {
3034 if (!op.getExplicitlyReturnedType())
3035 return {};
3036 return results.back();
3037}
3038
3039Value FuncCallOp::getExplicitlyReturnedValue(sv::FuncOp op) {
3040 return getExplicitlyReturnedValueImpl(op, getResults());
3041}
3042
3043Value FuncCallProceduralOp::getExplicitlyReturnedValue(sv::FuncOp op) {
3044 return getExplicitlyReturnedValueImpl(op, getResults());
3045}
3046
3047LogicalResult
3048FuncCallProceduralOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
3049 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
3050 symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr()));
3051 if (!referencedOp)
3052 return emitError("cannot find function declaration '")
3053 << getCallee() << "'";
3054 return success();
3055}
3056
3057LogicalResult FuncCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
3058 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
3059 symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr()));
3060 if (!referencedOp)
3061 return emitError("cannot find function declaration '")
3062 << getCallee() << "'";
3063
3064 // Non-procedural call cannot have output arguments.
3065 if (referencedOp.getNumOutputs() != 1 ||
3066 !referencedOp.getExplicitlyReturnedType()) {
3067 auto diag = emitError()
3068 << "function called in a non-procedural region must "
3069 "return a single result";
3070 diag.attachNote(referencedOp.getLoc()) << "doesn't satisfy the constraint";
3071 return failure();
3072 }
3073 return success();
3074}
3075
3076//===----------------------------------------------------------------------===//
3077// FuncDPIImportOp
3078//===----------------------------------------------------------------------===//
3079
3080LogicalResult
3081FuncDPIImportOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
3082 auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
3083 symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr()));
3084
3085 if (!referencedOp)
3086 return emitError("cannot find function declaration '")
3087 << getCallee() << "'";
3088 if (!referencedOp.isDeclaration())
3089 return emitError("imported function must be a declaration but '")
3090 << getCallee() << "' is defined";
3091 return success();
3092}
3093
3094//===----------------------------------------------------------------------===//
3095// Assert Property Like ops
3096//===----------------------------------------------------------------------===//
3097
3099// Check that a clock is never given without an event
3100// and that an event is never given with a clock.
3101static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc) {
3102 if ((!clock && eventExists) || (clock && !eventExists))
3103 return mlir::emitError(
3104 loc, "Every clock must be associated to an even and vice-versa!");
3105 return success();
3106}
3107} // namespace AssertPropertyLikeOp
3108
3109LogicalResult AssertPropertyOp::verify() {
3110 return AssertPropertyLikeOp::verify(getClock(), getEvent().has_value(),
3111 getLoc());
3112}
3113
3114LogicalResult AssumePropertyOp::verify() {
3115 return AssertPropertyLikeOp::verify(getClock(), getEvent().has_value(),
3116 getLoc());
3117}
3118
3119LogicalResult CoverPropertyOp::verify() {
3120 return AssertPropertyLikeOp::verify(getClock(), getEvent().has_value(),
3121 getLoc());
3122}
3123
3124//===----------------------------------------------------------------------===//
3125// TableGen generated logic.
3126//===----------------------------------------------------------------------===//
3127
3128// Provide the autogenerated implementation guts for the Op classes.
3129#define GET_OP_CLASSES
3130#include "circt/Dialect/SV/SV.cpp.inc"
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static bool hasSVAttributes(Operation *op)
Definition CombFolds.cpp:67
static std::unique_ptr< Context > context
#define isdigit(x)
Definition FIRLexer.cpp:26
static LogicalResult canonicalizeImmediateVerifOp(Op op, PatternRewriter &rewriter)
static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op, Region &region)
Replaces the given op with the contents of the given single-block region.
static LogicalResult eraseIfZeroOrNotZero(Operation *op, Value predicate, Value enable, PatternRewriter &rewriter, bool eraseIfZero)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
Definition HWOps.cpp:1495
static SmallVector< Location > getAllPortLocs(ModTy module)
Definition HWOps.cpp:1273
static void setHWModuleType(ModTy &mod, ModuleType type)
Definition HWOps.cpp:1416
@ Output
Definition HW.h:42
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:222
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
static Block * getBodyBlock(FModuleLike mod)
RewritePatternSet pattern
bool parseCaseRegions(OpAsmParser &p, ArrayAttr &patternsArray, ArrayAttr &caseNamesArray, SmallVectorImpl< std::unique_ptr< Region > > &caseRegions)
Parse cases formatted like: case (pattern, "name") { ... }.
Definition SVOps.cpp:2578
ParseResult parseIfaceTypeAndSignal(OpAsmParser &p, Type &ifaceTy, FlatSymbolRefAttr &signalName)
Definition SVOps.cpp:1767
static void printGenerateFor(OpAsmPrinter &p, Operation *op, TypedAttr lowerBound, TypedAttr upperBound, TypedAttr step, StringAttr inductionVarName, StringAttr genBlockName, Region &body)
Definition SVOps.cpp:2726
LogicalResult verifySignalExists(Value ifaceVal, FlatSymbolRefAttr signalName)
Definition SVOps.cpp:1789
void printCaseRegions(OpAsmPrinter &p, Operation *, ArrayAttr patternsArray, ArrayAttr namesArray, MutableArrayRef< Region > caseRegions)
Print cases formatted like: case (pattern, "name") { ... }.
Definition SVOps.cpp:2603
static Value getExplicitlyReturnedValueImpl(sv::FuncOp op, mlir::Operation::result_range results)
Definition SVOps.cpp:3032
void printIfaceTypeAndSignal(OpAsmPrinter &p, Operation *op, Type type, FlatSymbolRefAttr signalName)
Definition SVOps.cpp:1780
static void printModportStructs(OpAsmPrinter &p, Operation *, ArrayAttr portsAttr)
Definition SVOps.cpp:1638
static ParseResult parseTypedAttrWithFallback(OpAsmParser &parser, TypedAttr &result, Type type)
Definition SVOps.cpp:2645
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:2551
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:2462
static Type getElementTypeOfWidth(Type type, int32_t width)
Definition SVOps.cpp:1927
static LogicalResult mergeNeiboringAssignments(AssignTy op, PatternRewriter &rewriter)
Definition SVOps.cpp:1523
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:2057
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:2674
static ParseResult parseModportStructs(OpAsmParser &parser, ArrayAttr &portsAttr)
Definition SVOps.cpp:1611
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
void printXMRPath(OpAsmPrinter &p, XMROp op, ArrayAttr pathAttr, StringAttr terminalAttr)
Definition SVOps.cpp:2487
static InstancePath empty
This stores lookup tables to make manipulating and working with the IR more efficient.
Definition HWSymCache.h:28
HWSymbolCache::Item getInnerDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name) const
Definition HWSymCache.h:66
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
Definition HWSymCache.h:57
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
IntegerAttr intAttr
Definition SVOps.h:124
CasePatternBit getBit(size_t bitNumber) const
Return the specified bit, bit 0 is the least significant bit.
Definition SVOps.cpp: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
hw::EnumFieldAttr enumAttr
Definition SVOps.h:141
StringRef getFieldValue() const
Definition SVOps.cpp:1041
create(array_value, low_index, ret_type)
Definition hw.py:469
create(data_type, value)
Definition hw.py:433
Definition sv.py:70
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
Definition SVOps.cpp:3101
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
Direction
The direction of a Component or Cell port.
Definition CalyxOps.h:76
Value createOrFoldNot(OpBuilder &builder, Location loc, Value value, bool twoState=false)
Create a `‘Not’' gate on a value.
Definition CombOps.cpp:112
uint64_t getWidth(Type t)
Definition ESIPasses.cpp:32
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
ParseResult parseModuleSignature(OpAsmParser &parser, SmallVectorImpl< PortParse > &args, TypeAttr &modType)
New Style parsing.
void printModuleSignatureNew(OpAsmPrinter &p, Region &body, hw::ModuleType modType, ArrayRef< Attribute > portAttrs, ArrayRef< Location > locAttrs)
bool isHWIntegerType(mlir::Type type)
Return true if the specified type is a value HW Integer type.
Definition HWTypes.cpp:59
bool isOffset(Value base, Value index, uint64_t offset)
Definition HWOps.cpp:1779
FunctionType getModuleType(Operation *module)
Return the signature for the specified module as a function type.
Definition HWOps.cpp:575
bool isHWEnumType(mlir::Type type)
Return true if the specified type is a HW Enum type.
Definition HWTypes.cpp:72
mlir::Type getCanonicalType(mlir::Type type)
Recursively remove HW type aliases from a type and its subelements.
Definition HWTypes.cpp:50
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.
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 getInOutElementType(mlir::Type type)
Return the element type of an InOutType or null if the operand isn't an InOut type.
Definition SVTypes.cpp:67
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
ParseResult parseOptionalParameterList(OpAsmParser &parser, ArrayAttr &parameters)
Parse an parameter list if present.
void printOptionalParameterList(OpAsmPrinter &p, Operation *op, ArrayAttr parameters)
Print a parameter list for a module or instance.
Definition hw.py:1
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:193
Definition sv.py:1
write(addr, data)
Definition xrt_cosim.py:30
read(addr)
Definition xrt_cosim.py:23
This class represents the namespace in which InnerRef's can be resolved.
InnerSymTarget lookup(hw::InnerRefAttr inner) const
Resolve the InnerRef to its target within this namespace, returning empty target if no such name exis...
This holds the name, type, direction of a module's ports.