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