CIRCT  18.0.0git
PrepareForEmission.cpp
Go to the documentation of this file.
1 //===- PrepareForEmission.cpp - IR Prepass for Emitter --------------------===//
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 implements the "prepare" pass that walks the IR before the emitter
10 // gets involved. This allows us to do some transformations that would be
11 // awkward to implement inline in the emitter.
12 //
13 // NOTE: This file covers the preparation phase of `ExportVerilog` which mainly
14 // legalizes the IR and makes adjustments necessary for emission. This is the
15 // place to mutate the IR if emission needs it. The IR cannot be modified during
16 // emission itself, which happens in parallel.
17 //
18 //===----------------------------------------------------------------------===//
19 
20 #include "../PassDetail.h"
21 #include "ExportVerilogInternals.h"
26 #include "mlir/IR/ImplicitLocOpBuilder.h"
27 #include "llvm/ADT/DenseSet.h"
28 #include "llvm/ADT/SmallPtrSet.h"
29 #include "llvm/ADT/TypeSwitch.h"
30 #include "llvm/Support/CommandLine.h"
31 #include "llvm/Support/Debug.h"
32 
33 #define DEBUG_TYPE "prepare-for-emission"
34 
35 using namespace circt;
36 using namespace comb;
37 using namespace hw;
38 using namespace sv;
39 using namespace ExportVerilog;
40 
41 // Check if the value is from read of a wire or reg or is a port.
43  if (v.isa<BlockArgument>())
44  return true;
45  auto vOp = v.getDefiningOp();
46  if (!vOp)
47  return false;
48  if (v.getType().isa<sv::InOutType>() && isa<sv::WireOp>(vOp))
49  return true;
50  auto read = dyn_cast<ReadInOutOp>(vOp);
51  if (!read)
52  return false;
53  auto readSrc = read.getInput().getDefiningOp();
54  if (!readSrc)
55  return false;
56  return isa<sv::WireOp, RegOp, LogicOp, XMROp, XMRRefOp>(readSrc);
57 }
58 
59 // Check if the value is deemed worth spilling into a wire.
60 static bool shouldSpillWire(Operation &op, const LoweringOptions &options) {
61  if (!isVerilogExpression(&op))
62  return false;
63 
64  // Spill temporary wires if it is not possible to inline.
65  return !ExportVerilog::isExpressionEmittedInline(&op, options);
66 }
67 
68 // Given an instance, make sure all inputs are driven from wires or ports.
69 static void spillWiresForInstanceInputs(InstanceOp op) {
70  Block *block = op->getParentOfType<HWModuleOp>().getBodyBlock();
71  auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
72 
73  SmallString<32> nameTmp{"_", op.getInstanceName(), "_"};
74  auto namePrefixSize = nameTmp.size();
75 
76  size_t nextOpNo = 0;
77  auto ports = op.getPortList();
78  for (auto &port : ports.getInputs()) {
79  auto src = op.getOperand(nextOpNo);
80  ++nextOpNo;
81 
82  if (isSimpleReadOrPort(src))
83  continue;
84 
85  nameTmp.resize(namePrefixSize);
86  if (port.name)
87  nameTmp += port.name.getValue().str();
88  else
89  nameTmp += std::to_string(nextOpNo - 1);
90 
91  auto newWire = builder.create<sv::WireOp>(src.getType(), nameTmp);
92  auto newWireRead = builder.create<ReadInOutOp>(newWire);
93  auto connect = builder.create<AssignOp>(newWire, src);
94  newWireRead->moveBefore(op);
95  connect->moveBefore(op);
96  op.setOperand(nextOpNo - 1, newWireRead);
97  }
98 }
99 
100 // Ensure that each output of an instance are used only by a wire
101 static void lowerInstanceResults(InstanceOp op) {
102  Block *block = op->getParentOfType<HWModuleOp>().getBodyBlock();
103  auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
104 
105  SmallString<32> nameTmp{"_", op.getInstanceName(), "_"};
106  auto namePrefixSize = nameTmp.size();
107 
108  size_t nextResultNo = 0;
109  auto ports = op.getPortList();
110  for (auto &port : ports.getOutputs()) {
111  auto result = op.getResult(nextResultNo);
112  ++nextResultNo;
113 
114  // If the result doesn't have a user, the connection won't be emitted by
115  // Emitter, so there's no need to create a wire for it. However, if the
116  // result is a zero bit value, the normal emission code path should be used,
117  // as zero bit values require special handling by the emitter.
118  if (result.use_empty() && !ExportVerilog::isZeroBitType(result.getType()))
119  continue;
120 
121  if (result.hasOneUse()) {
122  OpOperand &use = *result.getUses().begin();
123  if (dyn_cast_or_null<OutputOp>(use.getOwner()))
124  continue;
125  if (auto assign = dyn_cast_or_null<AssignOp>(use.getOwner())) {
126  // Move assign op after instance to resolve cyclic dependencies.
127  assign->moveAfter(op);
128  continue;
129  }
130  }
131 
132  nameTmp.resize(namePrefixSize);
133  if (port.name)
134  nameTmp += port.name.getValue().str();
135  else
136  nameTmp += std::to_string(nextResultNo - 1);
137  Value newWire = builder.create<sv::WireOp>(result.getType(), nameTmp);
138 
139  while (!result.use_empty()) {
140  auto newWireRead = builder.create<ReadInOutOp>(newWire);
141  OpOperand &use = *result.getUses().begin();
142  use.set(newWireRead);
143  newWireRead->moveBefore(use.getOwner());
144  }
145 
146  auto connect = builder.create<AssignOp>(newWire, result);
147  connect->moveAfter(op);
148  }
149 }
150 
151 /// Emit an explicit wire or logic to assign operation's result. This function
152 /// is used to create a temporary to legalize a verilog expression or to
153 /// resolve use-before-def in a graph region. If `emitWireAtBlockBegin` is true,
154 /// a temporary wire will be created at the beginning of the block. Otherwise,
155 /// a wire is created just after op's position so that we can inline the
156 /// assignment into its wire declaration.
157 static void lowerUsersToTemporaryWire(Operation &op,
158  bool emitWireAtBlockBegin = false) {
159  Block *block = op.getBlock();
160  auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
161  bool isProceduralRegion = op.getParentOp()->hasTrait<ProceduralRegion>();
162 
163  auto createWireForResult = [&](Value result, StringAttr name) {
164  Value newWire;
165  // If the op is in a procedural region, use logic op.
166  if (isProceduralRegion)
167  newWire = builder.create<LogicOp>(result.getType(), name);
168  else
169  newWire = builder.create<sv::WireOp>(result.getType(), name);
170 
171  while (!result.use_empty()) {
172  auto newWireRead = builder.create<ReadInOutOp>(newWire);
173  OpOperand &use = *result.getUses().begin();
174  use.set(newWireRead);
175  newWireRead->moveBefore(use.getOwner());
176  }
177 
178  Operation *connect;
179  if (isProceduralRegion)
180  connect = builder.create<BPAssignOp>(newWire, result);
181  else
182  connect = builder.create<AssignOp>(newWire, result);
183  connect->moveAfter(&op);
184 
185  // Move the temporary to the appropriate place.
186  if (!emitWireAtBlockBegin) {
187  // `emitWireAtBlockBegin` is intendend to be used for resovling cyclic
188  // dependencies. So when `emitWireAtBlockBegin` is true, we keep the
189  // position of the wire. Otherwise, we move the wire to immediately after
190  // the expression so that the wire and assignment are next to each other.
191  // This ordering will be used by the heurstic to inline assignments.
192  newWire.getDefiningOp()->moveAfter(&op);
193  }
194  };
195 
196  // If the op has a single result, infer a meaningful name from the
197  // value.
198  if (op.getNumResults() == 1) {
199  auto namehint = inferStructuralNameForTemporary(op.getResult(0));
200  op.removeAttr("sv.namehint");
201  createWireForResult(op.getResult(0), namehint);
202  return;
203  }
204 
205  // If the op has multiple results, create wires for each result.
206  for (auto result : op.getResults())
207  createWireForResult(result, StringAttr());
208 }
209 
210 // Given a side effect free "always inline" operation, make sure that it
211 // exists in the same block as its users and that it has one use for each one.
212 static void lowerAlwaysInlineOperation(Operation *op,
213  const LoweringOptions &options) {
214  assert(op->getNumResults() == 1 &&
215  "only support 'always inline' ops with one result");
216 
217  // Moving/cloning an op should pull along its operand tree with it if they are
218  // always inline. This happens when an array index has a constant operand for
219  // example. If the operand is not always inline, then evaluate it to see if
220  // it should be spilled to a wire.
221  auto recursivelyHandleOperands = [&](Operation *op) {
222  for (auto operand : op->getOperands()) {
223  if (auto *operandOp = operand.getDefiningOp()) {
224  if (isExpressionAlwaysInline(operandOp))
225  lowerAlwaysInlineOperation(operandOp, options);
226  else if (shouldSpillWire(*operandOp, options))
227  lowerUsersToTemporaryWire(*operandOp);
228  }
229  }
230  };
231 
232  // If an operation is an assignment that immediately follows the declaration
233  // of its wire, return that wire. Otherwise return the original op. This
234  // ensures that the declaration and assignment don't get split apart by
235  // inlined operations, which allows `ExportVerilog` to trivially emit the
236  // expression inline in the declaration.
237  auto skipToWireImmediatelyBefore = [](Operation *user) {
238  if (!isa<BPAssignOp, AssignOp>(user))
239  return user;
240  auto *wireOp = user->getOperand(0).getDefiningOp();
241  if (wireOp && wireOp->getNextNode() == user)
242  return wireOp;
243  return user;
244  };
245 
246  // If this operation has multiple uses, duplicate it into N-1 of them in
247  // turn.
248  while (!op->hasOneUse()) {
249  OpOperand &use = *op->getUses().begin();
250  Operation *user = skipToWireImmediatelyBefore(use.getOwner());
251 
252  // Clone the op before the user.
253  auto *newOp = op->clone();
254  user->getBlock()->getOperations().insert(Block::iterator(user), newOp);
255  // Change the user to use the new op.
256  use.set(newOp->getResult(0));
257 
258  // If any of the operations of the moved op are always inline, recursively
259  // handle them too.
260  recursivelyHandleOperands(newOp);
261  }
262 
263  // Finally, ensures the op is in the same block as its user so it can be
264  // inlined.
265  Operation *user = skipToWireImmediatelyBefore(*op->getUsers().begin());
266  op->moveBefore(user);
267 
268  // If any of the operations of the moved op are always inline, recursively
269  // move/clone them too.
270  recursivelyHandleOperands(op);
271  return;
272 }
273 
274 // Find a nearest insertion point where logic op can be declared.
275 // Logic ops are emitted as "automatic logic" in procedural regions, but
276 // they must be declared at beginning of blocks.
277 static std::pair<Block *, Block::iterator>
279  // We have to skip `ifdef.procedural` because it is a just macro.
280  if (isa<IfDefProceduralOp>(op->getParentOp()))
281  return findLogicOpInsertionPoint(op->getParentOp());
282  return {op->getBlock(), op->getBlock()->begin()};
283 }
284 
285 /// Lower a variadic fully-associative operation into an expression tree. This
286 /// enables long-line splitting to work with them.
287 /// NOLINTNEXTLINE(misc-no-recursion)
288 static Value lowerFullyAssociativeOp(Operation &op, OperandRange operands,
289  SmallVector<Operation *> &newOps) {
290  // save the top level name
291  auto name = op.getAttr("sv.namehint");
292  if (name)
293  op.removeAttr("sv.namehint");
294  Value lhs, rhs;
295  switch (operands.size()) {
296  case 0:
297  assert(0 && "cannot be called with empty operand range");
298  break;
299  case 1:
300  return operands[0];
301  case 2:
302  lhs = operands[0];
303  rhs = operands[1];
304  break;
305  default:
306  auto firstHalf = operands.size() / 2;
307  lhs = lowerFullyAssociativeOp(op, operands.take_front(firstHalf), newOps);
308  rhs = lowerFullyAssociativeOp(op, operands.drop_front(firstHalf), newOps);
309  break;
310  }
311 
312  OperationState state(op.getLoc(), op.getName());
313  state.addOperands(ValueRange{lhs, rhs});
314  state.addTypes(op.getResult(0).getType());
315  auto *newOp = Operation::create(state);
316  op.getBlock()->getOperations().insert(Block::iterator(&op), newOp);
317  newOps.push_back(newOp);
318  if (name)
319  newOp->setAttr("sv.namehint", name);
320  if (auto twoState = op.getAttr("twoState"))
321  newOp->setAttr("twoState", twoState);
322  return newOp->getResult(0);
323 }
324 
325 /// Transform "a + -cst" ==> "a - cst" for prettier output. This returns the
326 /// first operation emitted.
327 static Operation *rewriteAddWithNegativeConstant(comb::AddOp add,
328  hw::ConstantOp rhsCst) {
329  ImplicitLocOpBuilder builder(add.getLoc(), add);
330 
331  // Get the positive constant.
332  auto negCst = builder.create<hw::ConstantOp>(-rhsCst.getValue());
333  auto sub =
334  builder.create<comb::SubOp>(add.getOperand(0), negCst, add.getTwoState());
335  add.getResult().replaceAllUsesWith(sub);
336  add.erase();
337  if (rhsCst.use_empty())
338  rhsCst.erase();
339  return negCst;
340 }
341 
342 // Transforms a hw.struct_explode operation into a set of hw.struct_extract
343 // operations, and returns the first op generated.
344 static Operation *lowerStructExplodeOp(hw::StructExplodeOp op) {
345  Operation *firstOp = nullptr;
346  ImplicitLocOpBuilder builder(op.getLoc(), op);
347  StructType structType = op.getInput().getType().cast<StructType>();
348  for (auto [res, field] :
349  llvm::zip(op.getResults(), structType.getElements())) {
350  auto extract =
351  builder.create<hw::StructExtractOp>(op.getInput(), field.name);
352  if (!firstOp)
353  firstOp = extract;
354  res.replaceAllUsesWith(extract);
355  }
356  op.erase();
357  return firstOp;
358 }
359 
360 /// Given an operation in a procedural region, scan up the region tree to find
361 /// the first operation in a graph region (typically an always or initial op).
362 ///
363 /// By looking for a graph region, we will stop at graph-region #ifdef's that
364 /// may enclose this operation.
365 static Operation *findParentInNonProceduralRegion(Operation *op) {
366  Operation *parentOp = op->getParentOp();
367  assert(parentOp->hasTrait<ProceduralRegion>() &&
368  "we should only be hoisting from procedural");
369  while (parentOp->getParentOp()->hasTrait<ProceduralRegion>())
370  parentOp = parentOp->getParentOp();
371  return parentOp;
372 }
373 
374 /// This function is invoked on side effecting Verilog expressions when we're in
375 /// 'disallowLocalVariables' mode for old Verilog clients. This ensures that
376 /// any side effecting expressions are only used by a single BPAssign to a
377 /// sv.reg or sv.logic operation. This ensures that the verilog emitter doesn't
378 /// have to worry about spilling them.
379 ///
380 /// This returns true if the op was rewritten, false otherwise.
381 static bool rewriteSideEffectingExpr(Operation *op) {
382  assert(op->getNumResults() == 1 && "isn't a verilog expression");
383 
384  // Check to see if this is already rewritten.
385  if (op->hasOneUse()) {
386  if (auto assign = dyn_cast<BPAssignOp>(*op->user_begin()))
387  return false;
388  }
389 
390  // Otherwise, we have to transform it. Insert a reg at the top level, make
391  // everything using the side effecting expression read the reg, then assign to
392  // it after the side effecting operation.
393  Value opValue = op->getResult(0);
394 
395  // Scan to the top of the region tree to find out where to insert the reg.
396  Operation *parentOp = findParentInNonProceduralRegion(op);
397  OpBuilder builder(parentOp);
398  auto reg = builder.create<RegOp>(op->getLoc(), opValue.getType());
399 
400  // Everything using the expr now uses a read_inout of the reg.
401  auto value = builder.create<ReadInOutOp>(op->getLoc(), reg);
402  opValue.replaceAllUsesWith(value);
403 
404  // We assign the side effect expr to the reg immediately after that expression
405  // is computed.
406  builder.setInsertionPointAfter(op);
407  builder.create<BPAssignOp>(op->getLoc(), reg, opValue);
408  return true;
409 }
410 
411 /// This function is called for non-side-effecting Verilog expressions when
412 /// we're in 'disallowLocalVariables' mode for old Verilog clients. It hoists
413 /// non-constant expressions out to the top level so they don't turn into local
414 /// variable declarations.
415 static bool hoistNonSideEffectExpr(Operation *op) {
416  // Never hoist "always inline" expressions except for inout stuffs - they will
417  // never generate a temporary and in fact must always be emitted inline.
418  if (isExpressionAlwaysInline(op) &&
419  !(isa<sv::ReadInOutOp>(op) ||
420  op->getResult(0).getType().isa<hw::InOutType>()))
421  return false;
422 
423  // Scan to the top of the region tree to find out where to move the op.
424  Operation *parentOp = findParentInNonProceduralRegion(op);
425 
426  // We can typically hoist all the way out to the top level in one step, but
427  // there may be intermediate operands that aren't hoistable. If so, just
428  // hoist one level.
429  bool cantHoist = false;
430  if (llvm::any_of(op->getOperands(), [&](Value operand) -> bool {
431  // The operand value dominates the original operation, but may be
432  // defined in one of the procedural regions between the operation and
433  // the top level of the module. We can tell this quite efficiently by
434  // looking for ops in a procedural region - because procedural regions
435  // live in graph regions but not visa-versa.
436  if (BlockArgument block = operand.dyn_cast<BlockArgument>()) {
437  // References to ports are always ok.
438  if (isa<hw::HWModuleOp>(block.getParentBlock()->getParentOp()))
439  return false;
440 
441  cantHoist = true;
442  return true;
443  }
444  Operation *operandOp = operand.getDefiningOp();
445 
446  if (operandOp->getParentOp()->hasTrait<ProceduralRegion>()) {
447  cantHoist |= operandOp->getBlock() == op->getBlock();
448  return true;
449  }
450  return false;
451  })) {
452 
453  // If the operand is in the same block as the expression then we can't hoist
454  // this out at all.
455  if (cantHoist)
456  return false;
457 
458  // Otherwise, we can hoist it, but not all the way out in one step. Just
459  // hoist one level out.
460  parentOp = op->getParentOp();
461  }
462 
463  op->moveBefore(parentOp);
464  return true;
465 }
466 
467 /// Check whether an op is a declaration that can be moved.
468 static bool isMovableDeclaration(Operation *op) {
469  return op->getNumResults() == 1 &&
470  op->getResult(0).getType().isa<InOutType, sv::InterfaceType>() &&
471  op->getNumOperands() == 0;
472 }
473 
474 //===----------------------------------------------------------------------===//
475 // EmittedExpressionStateManager
476 //===----------------------------------------------------------------------===//
477 
479  size_t size = 0;
481  return EmittedExpressionState{1};
482  }
483  void mergeState(const EmittedExpressionState &state) { size += state.size; }
484 };
485 
486 /// This class handles information about AST structures of each expressions.
488  : public hw::TypeOpVisitor<EmittedExpressionStateManager,
489  EmittedExpressionState>,
490  public comb::CombinationalVisitor<EmittedExpressionStateManager,
491  EmittedExpressionState>,
492  public sv::Visitor<EmittedExpressionStateManager,
493  EmittedExpressionState> {
494 public:
496  : options(options){};
497 
498  // Get or caluculate an emitted expression state.
499  EmittedExpressionState getExpressionState(Value value);
500 
501  bool dispatchHeuristic(Operation &op);
502 
503  // Return true if the operation is worth spilling based on the expression
504  // state of the op.
505  bool shouldSpillWireBasedOnState(Operation &op);
506 
507 private:
508  friend class TypeOpVisitor<EmittedExpressionStateManager,
510  friend class CombinationalVisitor<EmittedExpressionStateManager,
514  LLVM_DEBUG(op->emitWarning() << "unhandled by EmittedExpressionState");
515  if (op->getNumOperands() == 0)
517  return mergeOperandsStates(op);
518  }
520  return dispatchTypeOpVisitor(op);
521  }
523  return visitUnhandledExpr(op);
524  }
526  return dispatchSVVisitor(op);
527  }
529  return visitUnhandledExpr(op);
530  }
532  return visitUnhandledExpr(op);
533  }
534 
535  using CombinationalVisitor::visitComb;
536  using Visitor::visitSV;
537 
539 
540  // A helper function to accumulate states.
541  EmittedExpressionState mergeOperandsStates(Operation *op);
542 
543  // This caches the expression states in the module scope.
544  DenseMap<Value, EmittedExpressionState> expressionStates;
545 };
546 
549  auto it = expressionStates.find(v);
550  if (it != expressionStates.end())
551  return it->second;
552 
553  // Ports.
554  if (auto blockArg = v.dyn_cast<BlockArgument>())
556 
557  EmittedExpressionState state =
558  dispatchCombinationalVisitor(v.getDefiningOp());
559  expressionStates.insert({v, state});
560  return state;
561 }
562 
566  for (auto operand : op->getOperands())
567  state.mergeState(getExpressionState(operand));
568 
569  return state;
570 }
571 
572 /// If exactly one use of this op is an assign, replace the other uses with a
573 /// read from the assigned wire or reg. This assumes the preconditions for doing
574 /// so are met: op must be an expression in a non-procedural region.
575 static bool reuseExistingInOut(Operation *op, const LoweringOptions &options) {
576  // Try to collect a single assign and all the other uses of op.
577  sv::AssignOp assign;
578  SmallVector<OpOperand *> uses;
579 
580  // Look at each use.
581  for (OpOperand &use : op->getUses()) {
582  // If it's an assign, try to save it.
583  if (auto assignUse = dyn_cast<AssignOp>(use.getOwner())) {
584  // If there are multiple assigns, bail out.
585  if (assign)
586  return false;
587 
588  // If the assign is not at the top level, it might be conditionally
589  // executed. So bail out.
590  if (!isa<HWModuleOp>(assignUse->getParentOp()))
591  return false;
592 
593  // Remember this assign for later.
594  assign = assignUse;
595  continue;
596  }
597 
598  // If not an assign, remember this use for later.
599  uses.push_back(&use);
600  }
601 
602  // If we didn't find anything, bail out.
603  if (!assign || uses.empty())
604  return false;
605 
606  if (auto *cop = assign.getSrc().getDefiningOp())
607  if (isa<ConstantOp>(cop))
608  return false;
609 
610  // Replace all saved uses with a read from the assigned destination.
611  ImplicitLocOpBuilder builder(assign.getDest().getLoc(), op->getContext());
612  for (OpOperand *use : uses) {
613  builder.setInsertionPoint(use->getOwner());
614  auto read = builder.create<ReadInOutOp>(assign.getDest());
615  use->set(read);
616  }
617  if (auto *destOp = assign.getDest().getDefiningOp())
618  if (isExpressionAlwaysInline(destOp))
619  lowerAlwaysInlineOperation(destOp, options);
620  return true;
621 }
622 
624  // TODO: Consider using virtual functions.
625  if (options.isWireSpillingHeuristicEnabled(
627  if (auto hint = op.getAttrOfType<StringAttr>("sv.namehint")) {
628  // Spill wires if the name doesn't have a prefix "_".
629  if (!hint.getValue().startswith("_"))
630  return true;
631  // If the name has prefix "_", spill if the size is greater than the
632  // threshould.
633  if (getExpressionState(op.getResult(0)).size >=
634  options.wireSpillingNamehintTermLimit)
635  return true;
636  }
637 
638  return false;
639 }
640 
641 /// Return true if it is beneficial to spill the operation under the specified
642 /// spilling heuristic.
644  // Don't spill wires for inout operations and simple expressions such as read
645  // or constant.
646  if (op.getNumResults() == 0 ||
647  op.getResult(0).getType().isa<hw::InOutType>() ||
648  isa<ReadInOutOp, ConstantOp>(op))
649  return false;
650 
651  // If the operation is only used by an assignment, the op is already spilled
652  // to a wire.
653  if (op.hasOneUse()) {
654  auto *singleUser = *op.getUsers().begin();
655  if (isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, hw::InstanceOp>(
656  singleUser))
657  return false;
658 
659  // If the single user is bitcast, we check the same property for the bitcast
660  // op since bitcast op is no-op in system verilog.
661  if (singleUser->hasOneUse() && isa<hw::BitcastOp>(singleUser) &&
662  isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp>(
663  *singleUser->getUsers().begin()))
664  return false;
665  }
666 
667  // If the term size is greater than `maximumNumberOfTermsPerExpression`,
668  // we have to spill the wire.
669  if (options.maximumNumberOfTermsPerExpression <
670  getExpressionState(op.getResult(0)).size)
671  return true;
672  return dispatchHeuristic(op);
673 }
674 
675 /// After the legalization, we are able to know accurate verilog AST structures.
676 /// So this function walks and prettifies verilog IR with a heuristic method
677 /// specified by `options.wireSpillingHeuristic` based on the structures.
679  Block &block, EmittedExpressionStateManager &expressionStateManager) {
680  // TODO: Handle procedural regions as well.
681  if (block.getParentOp()->hasTrait<ProceduralRegion>())
682  return;
683 
684  for (auto &op : llvm::make_early_inc_range(block)) {
685  if (!isVerilogExpression(&op))
686  continue;
687  if (expressionStateManager.shouldSpillWireBasedOnState(op)) {
689  continue;
690  }
691  }
692 
693  for (auto &op : block) {
694  // If the operations has regions, visit each of the region bodies.
695  for (auto &region : op.getRegions()) {
696  if (!region.empty())
697  prettifyAfterLegalization(region.front(), expressionStateManager);
698  }
699  }
700 }
701 
702 struct WireLowering {
703  hw::WireOp wireOp;
704  Block::iterator declarePoint;
705  Block::iterator assignPoint;
706 };
707 
708 /// Determine the insertion location of declaration and assignment ops for
709 /// `hw::WireOp`s in a block. This function tries to place the declaration point
710 /// as late as possible, right before the very first use of the wire. It also
711 /// tries to place the assign point as early as possible, right after the
712 /// assigned value becomes available.
713 static void buildWireLowerings(Block &block,
714  SmallVectorImpl<WireLowering> &wireLowerings) {
715  for (auto hwWireOp : block.getOps<hw::WireOp>()) {
716  // Find the earliest point in the block at which the input operand is
717  // available. The assign operation will have to go after that to not create
718  // a use-before-def situation.
719  Block::iterator assignPoint = block.begin();
720  if (auto *defOp = hwWireOp.getInput().getDefiningOp())
721  if (defOp && defOp->getBlock() == &block)
722  assignPoint = ++Block::iterator(defOp);
723 
724  // Find the earliest use of the wire within the block. The wire declaration
725  // will have to go somewhere before this point to not create a
726  // use-before-def situation.
727  Block::iterator declarePoint = assignPoint;
728  for (auto *user : hwWireOp->getUsers()) {
729  while (user->getBlock() != &block)
730  user = user->getParentOp();
731  if (user->isBeforeInBlock(&*declarePoint))
732  declarePoint = Block::iterator(user);
733  }
734 
735  wireLowerings.push_back({hwWireOp, declarePoint, assignPoint});
736  }
737 }
738 
739 /// Materialize the SV wire declaration and assignment operations in the
740 /// locations previously determined by a call to `buildWireLowerings`. This
741 /// replaces all `hw::WireOp`s with their appropriate SV counterpart.
742 static void applyWireLowerings(Block &block,
743  ArrayRef<WireLowering> wireLowerings) {
744  bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
745 
746  for (auto [hwWireOp, declarePoint, assignPoint] : wireLowerings) {
747  // Create the declaration.
748  ImplicitLocOpBuilder builder(hwWireOp.getLoc(), &block, declarePoint);
749  Value decl;
750  if (isProceduralRegion) {
751  decl = builder.create<LogicOp>(hwWireOp.getType(), hwWireOp.getNameAttr(),
752  hwWireOp.getInnerSymAttr());
753  } else {
754  decl =
755  builder.create<sv::WireOp>(hwWireOp.getType(), hwWireOp.getNameAttr(),
756  hwWireOp.getInnerSymAttr());
757  }
758 
759  // Carry attributes over to the lowered operation.
760  auto defaultAttrNames = hwWireOp.getAttributeNames();
761  for (auto namedAttr : hwWireOp->getAttrs())
762  if (!llvm::is_contained(defaultAttrNames, namedAttr.getName()))
763  decl.getDefiningOp()->setAttr(namedAttr.getName(),
764  namedAttr.getValue());
765 
766  // Create the assignment. If it is supposed to be at a different point than
767  // the declaration, reposition the builder there. Otherwise we'll just build
768  // the assignment immediately after the declaration.
769  if (assignPoint != declarePoint)
770  builder.setInsertionPoint(&block, assignPoint);
771  if (isProceduralRegion)
772  builder.create<BPAssignOp>(decl, hwWireOp.getInput());
773  else
774  builder.create<AssignOp>(decl, hwWireOp.getInput());
775 
776  // Create the read. If we have created the assignment at a different point
777  // than the declaration, reposition the builder to immediately after the
778  // declaration. Otherwise we'll just build the read immediately after the
779  // assignment.
780  if (assignPoint != declarePoint)
781  builder.setInsertionPointAfterValue(decl);
782  auto readOp = builder.create<sv::ReadInOutOp>(decl);
783 
784  // Replace the HW wire.
785  hwWireOp.replaceAllUsesWith(readOp.getResult());
786  }
787 
788  for (auto [hwWireOp, declarePoint, assignPoint] : wireLowerings)
789  hwWireOp.erase();
790 }
791 
792 /// For each module we emit, do a prepass over the structure, pre-lowering and
793 /// otherwise rewriting operations we don't want to emit.
794 static LogicalResult legalizeHWModule(Block &block,
795  const LoweringOptions &options) {
796 
797  // First step, check any nested blocks that exist in this region. This walk
798  // can pull things out to our level of the hierarchy.
799  for (auto &op : block) {
800  // If the operations has regions, prepare each of the region bodies.
801  for (auto &region : op.getRegions()) {
802  if (!region.empty())
803  if (failed(legalizeHWModule(region.front(), options)))
804  return failure();
805  }
806  }
807 
808  // Lower HW wires into SV wires in the appropriate spots. Try to put the
809  // declaration close to the first use in the block, and the assignment right
810  // after the assigned value becomes available.
811  {
812  SmallVector<WireLowering> wireLowerings;
813  buildWireLowerings(block, wireLowerings);
814  applyWireLowerings(block, wireLowerings);
815  }
816 
817  // Next, walk all of the operations at this level.
818 
819  // True if these operations are in a procedural region.
820  bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
821 
822  // This tracks "always inline" operation already visited in the iterations to
823  // avoid processing same operations infinitely.
824  DenseSet<Operation *> visitedAlwaysInlineOperations;
825 
826  for (Block::iterator opIterator = block.begin(), e = block.end();
827  opIterator != e;) {
828  auto &op = *opIterator++;
829 
830  if (!isa<CombDialect, SVDialect, HWDialect, ltl::LTLDialect,
831  verif::VerifDialect>(op.getDialect())) {
832  auto d = op.emitError() << "dialect \"" << op.getDialect()->getNamespace()
833  << "\" not supported for direct Verilog emission";
834  d.attachNote() << "ExportVerilog cannot emit this operation; it needs "
835  "to be lowered before running ExportVerilog";
836  return failure();
837  }
838 
839  // Do not reorder LTL expressions, which are always emitted inline.
840  if (isa<ltl::LTLDialect>(op.getDialect()))
841  continue;
842 
843  // Name legalization should have happened in a different pass for these sv
844  // elements and we don't want to change their name through re-legalization
845  // (e.g. letting a temporary take the name of an unvisited wire). Adding
846  // them now ensures any temporary generated will not use one of the names
847  // previously declared.
848  if (auto instance = dyn_cast<InstanceOp>(op)) {
849  // Anchor return values to wires early
850  lowerInstanceResults(instance);
851  // Anchor ports of instances when `disallowExpressionInliningInPorts` is
852  // enabled.
854  spillWiresForInstanceInputs(instance);
855  }
856 
857  // If logic op is located in a procedural region, we have to move the logic
858  // op declaration to a valid program point.
859  if (isProceduralRegion && isa<LogicOp>(op)) {
860  if (options.disallowLocalVariables) {
861  // When `disallowLocalVariables` is enabled, "automatic logic" is
862  // prohibited so hoist the op to a non-procedural region.
863  auto *parentOp = findParentInNonProceduralRegion(&op);
864  op.moveBefore(parentOp);
865  }
866  }
867 
868  // If the target doesn't support local variables, hoist all the expressions
869  // out to the nearest non-procedural region.
870  if (options.disallowLocalVariables && isVerilogExpression(&op) &&
871  isProceduralRegion) {
872 
873  // Force any side-effecting expressions in nested regions into a sv.reg
874  // if we aren't allowing local variable declarations. The Verilog emitter
875  // doesn't want to have to have to know how to synthesize a reg in the
876  // case they have to be spilled for whatever reason.
877  if (!mlir::isMemoryEffectFree(&op)) {
878  if (rewriteSideEffectingExpr(&op))
879  continue;
880  }
881 
882  // Hoist other expressions out to the parent region.
883  //
884  // NOTE: This effectively disables inlining of expressions into if
885  // conditions, $fwrite statements, and instance inputs. We could be
886  // smarter in ExportVerilog itself, but we'd have to teach it to put
887  // spilled expressions (due to line length, multiple-uses, and
888  // non-inlinable expressions) in the outer scope.
889  if (hoistNonSideEffectExpr(&op))
890  continue;
891  }
892 
893  // Duplicate "always inline" expression for each of their users and move
894  // them to be next to their users.
895  if (isExpressionAlwaysInline(&op)) {
896  // Nuke use-less operations.
897  if (op.use_empty()) {
898  op.erase();
899  continue;
900  }
901  // Process the op only when the op is never processed from the top-level
902  // loop.
903  if (visitedAlwaysInlineOperations.insert(&op).second)
904  lowerAlwaysInlineOperation(&op, options);
905 
906  continue;
907  }
908 
909  if (auto aggregateConstantOp = dyn_cast<hw::AggregateConstantOp>(op);
910  options.disallowPackedStructAssignments && aggregateConstantOp) {
911  if (hw::StructType structType =
912  type_dyn_cast<hw::StructType>(aggregateConstantOp.getType())) {
913  // Create hw struct create op and apply the legalization again.
914  SmallVector<Value> operands;
915  ImplicitLocOpBuilder builder(op.getLoc(), op.getContext());
916  builder.setInsertionPointAfter(&op);
917  for (auto [value, field] :
918  llvm::zip(aggregateConstantOp.getFieldsAttr(),
919  structType.getElements())) {
920  if (auto arrayAttr = dyn_cast<mlir::ArrayAttr>(value))
921  operands.push_back(
922  builder.create<hw::AggregateConstantOp>(field.type, arrayAttr));
923  else
924  operands.push_back(builder.create<hw::ConstantOp>(
925  field.type, cast<mlir::IntegerAttr>(value)));
926  }
927 
928  auto structCreate =
929  builder.create<hw::StructCreateOp>(structType, operands);
930  aggregateConstantOp.getResult().replaceAllUsesWith(structCreate);
931  // Reset the iterator.
932  opIterator = std::next(op.getIterator());
933 
934  op.erase();
935  continue;
936  }
937  }
938 
939  if (auto structCreateOp = dyn_cast<hw::StructCreateOp>(op);
940  options.disallowPackedStructAssignments && structCreateOp) {
941  // Force packed struct assignment to be a wire + assignments to each
942  // field.
943  Value wireOp;
944  ImplicitLocOpBuilder builder(op.getLoc(), &op);
945  hw::StructType structType =
946  structCreateOp.getResult().getType().cast<hw::StructType>();
947  bool procedural = op.getParentOp()->hasTrait<ProceduralRegion>();
948  if (procedural)
949  wireOp = builder.create<LogicOp>(structType);
950  else
951  wireOp = builder.create<sv::WireOp>(structType);
952 
953  for (auto [input, field] :
954  llvm::zip(structCreateOp.getInput(), structType.getElements())) {
955  auto target =
956  builder.create<sv::StructFieldInOutOp>(wireOp, field.name);
957  if (procedural)
958  builder.create<BPAssignOp>(target, input);
959  else
960  builder.create<AssignOp>(target, input);
961  }
962  // Have to create a separate read for each use to keep things legal.
963  for (auto &use :
964  llvm::make_early_inc_range(structCreateOp.getResult().getUses()))
965  use.set(builder.create<ReadInOutOp>(wireOp));
966 
967  structCreateOp.erase();
968  continue;
969  }
970 
971  // Force array index expressions to be a simple name when the
972  // `mitigateVivadoArrayIndexConstPropBug` option is set.
974  isa<ArraySliceOp, ArrayGetOp, ArrayIndexInOutOp,
975  IndexedPartSelectInOutOp, IndexedPartSelectOp>(&op)) {
976 
977  // Check if the index expression is already a wire.
978  Value wireOp;
979  Value readOp;
980  if (auto maybeReadOp =
981  op.getOperand(1).getDefiningOp<sv::ReadInOutOp>()) {
982  if (isa_and_nonnull<sv::WireOp, LogicOp>(
983  maybeReadOp.getInput().getDefiningOp())) {
984  wireOp = maybeReadOp.getInput();
985  readOp = maybeReadOp;
986  }
987  }
988 
989  // Insert a wire and read if necessary.
990  ImplicitLocOpBuilder builder(op.getLoc(), &op);
991  if (!wireOp) {
992  auto type = op.getOperand(1).getType();
993  const auto *name = "_GEN_ARRAY_IDX";
994  if (op.getParentOp()->hasTrait<ProceduralRegion>()) {
995  wireOp = builder.create<LogicOp>(type, name);
996  builder.create<BPAssignOp>(wireOp, op.getOperand(1));
997  } else {
998  wireOp = builder.create<sv::WireOp>(type, name);
999  builder.create<AssignOp>(wireOp, op.getOperand(1));
1000  }
1001  readOp = builder.create<ReadInOutOp>(wireOp);
1002  }
1003  op.setOperand(1, readOp);
1004 
1005  // Add a `(* keep = "true" *)` SV attribute to the wire.
1006  sv::addSVAttributes(wireOp.getDefiningOp(),
1007  SVAttributeAttr::get(wireOp.getContext(), "keep",
1008  R"("true")",
1009  /*emitAsComment=*/false));
1010  continue;
1011  }
1012 
1013  // If this expression is deemed worth spilling into a wire, do it here.
1014  if (shouldSpillWire(op, options)) {
1015  // We first check that it is possible to reuse existing wires as a spilled
1016  // wire. Otherwise, create a new wire op.
1017  if (isProceduralRegion || !reuseExistingInOut(&op, options)) {
1018  if (options.disallowLocalVariables) {
1019  // If we're not in a procedural region, or we are, but we can hoist
1020  // out of it, we are good to generate a wire.
1021  if (!isProceduralRegion ||
1022  (isProceduralRegion && hoistNonSideEffectExpr(&op))) {
1023  // If op is moved to a non-procedural region, create a temporary
1024  // wire.
1025  if (!op.getParentOp()->hasTrait<ProceduralRegion>())
1027 
1028  // If we're in a procedural region, we move on to the next op in the
1029  // block. The expression splitting and canonicalization below will
1030  // happen after we recurse back up. If we're not in a procedural
1031  // region, the expression can continue being worked on.
1032  if (isProceduralRegion)
1033  continue;
1034  }
1035  } else {
1036  // If `disallowLocalVariables` is not enabled, we can spill the
1037  // expression to automatic logic declarations even when the op is in a
1038  // procedural region.
1040  }
1041  }
1042  }
1043 
1044  // Lower variadic fully-associative operations with more than two operands
1045  // into balanced operand trees so we can split long lines across multiple
1046  // statements.
1047  // TODO: This is checking the Commutative property, which doesn't seem
1048  // right in general. MLIR doesn't have a "fully associative" property.
1049  if (op.getNumOperands() > 2 && op.getNumResults() == 1 &&
1050  op.hasTrait<mlir::OpTrait::IsCommutative>() &&
1051  mlir::isMemoryEffectFree(&op) && op.getNumRegions() == 0 &&
1052  op.getNumSuccessors() == 0 &&
1053  llvm::all_of(op.getAttrs(), [](NamedAttribute attr) {
1054  return attr.getNameDialect() != nullptr ||
1055  attr.getName() == "twoState";
1056  })) {
1057  // Lower this operation to a balanced binary tree of the same operation.
1058  SmallVector<Operation *> newOps;
1059  auto result = lowerFullyAssociativeOp(op, op.getOperands(), newOps);
1060  op.getResult(0).replaceAllUsesWith(result);
1061  op.erase();
1062 
1063  // Make sure we revisit the newly inserted operations.
1064  opIterator = Block::iterator(newOps.front());
1065  continue;
1066  }
1067 
1068  // Turn a + -cst ==> a - cst
1069  if (auto addOp = dyn_cast<comb::AddOp>(op)) {
1070  if (auto cst = addOp.getOperand(1).getDefiningOp<hw::ConstantOp>()) {
1071  assert(addOp.getNumOperands() == 2 && "commutative lowering is done");
1072  if (cst.getValue().isNegative()) {
1073  Operation *firstOp = rewriteAddWithNegativeConstant(addOp, cst);
1074  opIterator = Block::iterator(firstOp);
1075  continue;
1076  }
1077  }
1078  }
1079 
1080  // Lower hw.struct_explode ops into a set of hw.struct_extract ops which
1081  // have well-defined SV emission semantics.
1082  if (auto structExplodeOp = dyn_cast<hw::StructExplodeOp>(op)) {
1083  Operation *firstOp = lowerStructExplodeOp(structExplodeOp);
1084  opIterator = Block::iterator(firstOp);
1085  continue;
1086  }
1087 
1088  // Try to anticipate expressions that ExportVerilog may spill to a temporary
1089  // inout, and re-use an existing inout when possible. This is legal when op
1090  // is an expression in a non-procedural region.
1091  if (!isProceduralRegion && isVerilogExpression(&op))
1092  (void)reuseExistingInOut(&op, options);
1093  }
1094 
1095  if (isProceduralRegion) {
1096  // If there is no operation, there is nothing to do.
1097  if (block.empty())
1098  return success();
1099 
1100  // In a procedural region, logic operations needs to be top of blocks so
1101  // mvoe logic operations to valid program points.
1102 
1103  // This keeps tracks of an insertion point of logic op.
1104  std::pair<Block *, Block::iterator> logicOpInsertionPoint =
1105  findLogicOpInsertionPoint(&block.front());
1106  for (auto &op : llvm::make_early_inc_range(block)) {
1107  if (auto logic = dyn_cast<LogicOp>(&op)) {
1108  // If the logic op is already located at the given point, increment the
1109  // iterator to keep the order of logic operations in the block.
1110  if (logicOpInsertionPoint.second == logic->getIterator()) {
1111  ++logicOpInsertionPoint.second;
1112  continue;
1113  }
1114  // Otherwise, move the op to the insertion point.
1115  logic->moveBefore(logicOpInsertionPoint.first,
1116  logicOpInsertionPoint.second);
1117  }
1118  }
1119  return success();
1120  }
1121 
1122  // Now that all the basic ops are settled, check for any use-before def issues
1123  // in graph regions. Lower these into explicit wires to keep the emitter
1124  // simple.
1125 
1126  SmallPtrSet<Operation *, 32> seenOperations;
1127 
1128  for (auto &op : llvm::make_early_inc_range(block)) {
1129  // Do not reorder LTL expressions, which are always emitted inline.
1130  if (isa<ltl::LTLDialect>(op.getDialect()))
1131  continue;
1132 
1133  // Check the users of any expressions to see if they are
1134  // lexically below the operation itself. If so, it is being used out
1135  // of order.
1136  bool haveAnyOutOfOrderUses = false;
1137  for (auto *userOp : op.getUsers()) {
1138  // If the user is in a suboperation like an always block, then zip up
1139  // to the operation that uses it.
1140  while (&block != &userOp->getParentRegion()->front())
1141  userOp = userOp->getParentOp();
1142 
1143  if (seenOperations.count(userOp)) {
1144  haveAnyOutOfOrderUses = true;
1145  break;
1146  }
1147  }
1148 
1149  // Remember that we've seen this operation.
1150  seenOperations.insert(&op);
1151 
1152  // If all the uses of the operation are below this, then we're ok.
1153  if (!haveAnyOutOfOrderUses)
1154  continue;
1155 
1156  // If this is a reg/wire declaration, then we move it to the top of the
1157  // block. We can't abstract the inout result.
1158  if (isMovableDeclaration(&op)) {
1159  op.moveBefore(&block.front());
1160  continue;
1161  }
1162 
1163  // If this is a constant, then we move it to the top of the block.
1164  if (isConstantExpression(&op)) {
1165  op.moveBefore(&block.front());
1166  continue;
1167  }
1168 
1169  // If this is a an operation reading from a declaration, move it up,
1170  // along with the corresponding declaration.
1171  if (auto readInOut = dyn_cast<ReadInOutOp>(op)) {
1172  auto *def = readInOut.getInput().getDefiningOp();
1173  if (isMovableDeclaration(def)) {
1174  op.moveBefore(&block.front());
1175  def->moveBefore(&block.front());
1176  continue;
1177  }
1178  }
1179 
1180  // Otherwise, we need to lower this to a wire to resolve this.
1182  /*emitWireAtBlockBegin=*/true);
1183  }
1184  return success();
1185 }
1186 
1187 // NOLINTNEXTLINE(misc-no-recursion)
1188 LogicalResult ExportVerilog::prepareHWModule(hw::HWModuleOp module,
1189  const LoweringOptions &options) {
1190  // Zero-valued logic pruning.
1191  pruneZeroValuedLogic(module);
1192 
1193  // Legalization.
1194  if (failed(legalizeHWModule(*module.getBodyBlock(), options)))
1195  return failure();
1196 
1197  EmittedExpressionStateManager expressionStateManager(options);
1198  // Spill wires to prettify verilog outputs.
1199  prettifyAfterLegalization(*module.getBodyBlock(), expressionStateManager);
1200  return success();
1201 }
1202 
1203 namespace {
1204 
1205 struct PrepareForEmissionPass
1206  : public PrepareForEmissionBase<PrepareForEmissionPass> {
1207  void runOnOperation() override {
1208  HWModuleOp module = getOperation();
1209  LoweringOptions options(cast<mlir::ModuleOp>(module->getParentOp()));
1210  if (failed(prepareHWModule(module, options)))
1211  signalPassFailure();
1212  }
1213 };
1214 
1215 } // end anonymous namespace
1216 
1217 std::unique_ptr<mlir::Pass> circt::createPrepareForEmissionPass() {
1218  return std::make_unique<PrepareForEmissionPass>();
1219 }
assert(baseType &&"element must be base type")
Builder builder
static void lowerUsersToTemporaryWire(Operation &op, bool emitWireAtBlockBegin=false)
Emit an explicit wire or logic to assign operation's result.
static std::pair< Block *, Block::iterator > findLogicOpInsertionPoint(Operation *op)
static bool rewriteSideEffectingExpr(Operation *op)
This function is invoked on side effecting Verilog expressions when we're in 'disallowLocalVariables'...
static Operation * lowerStructExplodeOp(hw::StructExplodeOp op)
static void buildWireLowerings(Block &block, SmallVectorImpl< WireLowering > &wireLowerings)
Determine the insertion location of declaration and assignment ops for hw::WireOps in a block.
static void prettifyAfterLegalization(Block &block, EmittedExpressionStateManager &expressionStateManager)
After the legalization, we are able to know accurate verilog AST structures.
static bool hoistNonSideEffectExpr(Operation *op)
This function is called for non-side-effecting Verilog expressions when we're in 'disallowLocalVariab...
static Operation * findParentInNonProceduralRegion(Operation *op)
Given an operation in a procedural region, scan up the region tree to find the first operation in a g...
static Operation * rewriteAddWithNegativeConstant(comb::AddOp add, hw::ConstantOp rhsCst)
Transform "a + -cst" ==> "a - cst" for prettier output.
static void lowerAlwaysInlineOperation(Operation *op, const LoweringOptions &options)
static bool reuseExistingInOut(Operation *op, const LoweringOptions &options)
If exactly one use of this op is an assign, replace the other uses with a read from the assigned wire...
static void spillWiresForInstanceInputs(InstanceOp op)
static void lowerInstanceResults(InstanceOp op)
static bool isMovableDeclaration(Operation *op)
Check whether an op is a declaration that can be moved.
static LogicalResult legalizeHWModule(Block &block, const LoweringOptions &options)
For each module we emit, do a prepass over the structure, pre-lowering and otherwise rewriting operat...
static Value lowerFullyAssociativeOp(Operation &op, OperandRange operands, SmallVector< Operation * > &newOps)
Lower a variadic fully-associative operation into an expression tree.
static bool shouldSpillWire(Operation &op, const LoweringOptions &options)
static void applyWireLowerings(Block &block, ArrayRef< WireLowering > wireLowerings)
Materialize the SV wire declaration and assignment operations in the locations previously determined ...
static int64_t size(hw::ArrayType mType, capnp::schema::Field::Reader cField)
Returns the expected size of an array (capnp list) in 64-bit words.
Definition: Schema.cpp:193
This class handles information about AST structures of each expressions.
DenseMap< Value, EmittedExpressionState > expressionStates
EmittedExpressionState visitUnhandledSV(Operation *op)
EmittedExpressionStateManager(const LoweringOptions &options)
EmittedExpressionState visitInvalidComb(Operation *op)
EmittedExpressionState visitUnhandledTypeOp(Operation *op)
EmittedExpressionState getExpressionState(Value value)
const LoweringOptions & options
EmittedExpressionState visitUnhandledComb(Operation *op)
EmittedExpressionState mergeOperandsStates(Operation *op)
EmittedExpressionState visitUnhandledExpr(Operation *op)
bool shouldSpillWireBasedOnState(Operation &op)
Return true if it is beneficial to spill the operation under the specified spilling heuristic.
EmittedExpressionState visitInvalidTypeOp(Operation *op)
def connect(destination, source)
Definition: support.py:37
LogicalResult prepareHWModule(Block &block, const LoweringOptions &options)
For each module we emit, do a prepass over the structure, pre-lowering and otherwise rewriting operat...
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
void pruneZeroValuedLogic(hw::HWModuleOp module)
bool isSimpleReadOrPort(Value v)
Check if the value is from read of a wire or reg or is a port.
static bool isExpressionAlwaysInline(Operation *op)
Return true for operations that must always be inlined into a containing expression for correctness.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
circt::hw::InOutType InOutType
Definition: SVTypes.h:25
unsigned addSVAttributes(mlir::Operation *op, llvm::ArrayRef< SVAttributeAttr > attrs)
Add a list of SV attributes to an operation.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
std::unique_ptr< mlir::Pass > createPrepareForEmissionPass()
Definition: comb.py:1
Definition: hw.py:1
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:16
Definition: sv.py:1
static EmittedExpressionState getBaseState()
void mergeState(const EmittedExpressionState &state)
Block::iterator declarePoint
Block::iterator assignPoint
Options which control the emission from CIRCT to Verilog.
bool mitigateVivadoArrayIndexConstPropBug
If true, every expression used as an array index is driven by a wire, and the wire is marked as (* ke...
bool disallowLocalVariables
If true, do not emit SystemVerilog locally scoped "automatic" or logic declarations - emit top level ...
bool disallowExpressionInliningInPorts
If true, every expression passed to an instance port is driven by a wire.
bool disallowPackedStructAssignments
If true, eliminate packed struct assignments in favor of a wire + assignments to the individual field...