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