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