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