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