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