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