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