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