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