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