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