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