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