20 #include "../PassDetail.h"
27 #include "mlir/IR/ImplicitLocOpBuilder.h"
28 #include "llvm/ADT/DenseSet.h"
29 #include "llvm/ADT/SmallPtrSet.h"
30 #include "llvm/ADT/TypeSwitch.h"
31 #include "llvm/Support/CommandLine.h"
32 #include "llvm/Support/Debug.h"
34 #define DEBUG_TYPE "prepare-for-emission"
36 using namespace circt;
40 using namespace ExportVerilog;
44 if (v.isa<BlockArgument>())
46 auto vOp = v.getDefiningOp();
51 auto read = dyn_cast<ReadInOutOp>(vOp);
54 auto readSrc = read.getInput().getDefiningOp();
57 return isa<sv::WireOp, RegOp, LogicOp, XMROp, XMRRefOp>(readSrc);
69 static StringAttr
getArgName(Operation *op,
size_t idx) {
70 if (
auto inst = dyn_cast<hw::InstanceOp>(op))
71 return inst.getArgumentName(idx);
72 else if (
auto inst = dyn_cast<InstanceChoiceOp>(op))
73 return inst.getArgumentName(idx);
79 Block *block = op->getParentOfType<
HWModuleOp>().getBodyBlock();
80 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
82 SmallString<32> nameTmp{
"_", op.getInstanceName(),
"_"};
83 auto namePrefixSize = nameTmp.size();
85 for (
size_t opNum = 0, e = op->getNumOperands(); opNum != e; ++opNum) {
86 auto src = op->getOperand(opNum);
91 nameTmp.resize(namePrefixSize);
93 nameTmp += n.getValue().str();
95 nameTmp += std::to_string(opNum);
100 newWireRead->moveBefore(op);
102 op->setOperand(opNum, newWireRead);
107 if (
auto inst = dyn_cast<hw::InstanceOp>(op))
108 return inst.getResultName(idx);
109 else if (
auto inst = dyn_cast<InstanceChoiceOp>(op))
110 return inst.getResultName(idx);
116 Block *block = op->getParentOfType<
HWModuleOp>().getBodyBlock();
117 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
119 SmallString<32> nameTmp{
"_", op.getInstanceName(),
"_"};
120 auto namePrefixSize = nameTmp.size();
122 for (
size_t resNum = 0, e = op->getNumResults(); resNum != e; ++resNum) {
123 auto result = op->getResult(resNum);
132 if (result.hasOneUse()) {
133 OpOperand &use = *result.getUses().begin();
134 if (dyn_cast_or_null<OutputOp>(use.getOwner()))
136 if (
auto assign = dyn_cast_or_null<AssignOp>(use.getOwner())) {
138 assign->moveAfter(op);
143 nameTmp.resize(namePrefixSize);
145 nameTmp += n.getValue().str();
147 nameTmp += std::to_string(resNum);
150 while (!result.use_empty()) {
152 OpOperand &use = *result.getUses().begin();
153 use.set(newWireRead);
154 newWireRead->moveBefore(use.getOwner());
169 bool emitWireAtBlockBegin =
false) {
170 Block *block = op.getBlock();
171 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
172 bool isProceduralRegion = op.getParentOp()->hasTrait<ProceduralRegion>();
174 auto createWireForResult = [&](Value result, StringAttr name) {
176 Type wireElementType = result.getType();
177 bool isResultInOut =
false;
180 if (
auto inoutType = hw::type_dyn_cast<hw::InOutType>(result.getType())) {
181 wireElementType = inoutType.getElementType();
182 isResultInOut =
true;
186 if (isProceduralRegion)
187 newWire =
builder.create<LogicOp>(wireElementType, name);
192 while (!result.use_empty()) {
193 OpOperand &use = *result.getUses().begin();
198 use.set(newWireRead);
199 newWireRead->moveBefore(use.getOwner());
210 if (isProceduralRegion)
212 newWire, isResultInOut ? resultRead.getResult() : result);
215 newWire, isResultInOut ? resultRead.getResult() : result);
219 resultRead->moveBefore(
connect);
222 if (!emitWireAtBlockBegin) {
228 newWire.getDefiningOp()->moveAfter(&op);
234 if (op.getNumResults() == 1) {
236 op.removeAttr(
"sv.namehint");
237 createWireForResult(op.getResult(0), namehint);
242 for (
auto result : op.getResults())
243 createWireForResult(result, StringAttr());
250 assert(op->getNumResults() == 1 &&
251 "only support 'always inline' ops with one result");
257 auto recursivelyHandleOperands = [&](Operation *op) {
258 for (
auto operand : op->getOperands()) {
259 if (
auto *operandOp = operand.getDefiningOp()) {
273 auto skipToWireImmediatelyBefore = [](Operation *user) {
274 if (!isa<BPAssignOp, AssignOp>(user))
276 auto *wireOp = user->getOperand(0).getDefiningOp();
277 if (wireOp && wireOp->getNextNode() == user)
284 while (!op->hasOneUse()) {
285 OpOperand &use = *op->getUses().begin();
286 Operation *user = skipToWireImmediatelyBefore(use.getOwner());
289 auto *newOp = op->clone();
290 user->getBlock()->getOperations().insert(Block::iterator(user), newOp);
292 use.set(newOp->getResult(0));
296 recursivelyHandleOperands(newOp);
301 Operation *user = skipToWireImmediatelyBefore(*op->getUsers().begin());
302 op->moveBefore(user);
306 recursivelyHandleOperands(op);
313 static std::pair<Block *, Block::iterator>
316 if (isa<IfDefProceduralOp>(op->getParentOp()))
318 return {op->getBlock(), op->getBlock()->begin()};
325 SmallVector<Operation *> &newOps) {
327 auto name = op.getAttr(
"sv.namehint");
329 op.removeAttr(
"sv.namehint");
331 switch (operands.size()) {
333 assert(0 &&
"cannot be called with empty operand range");
342 auto firstHalf = operands.size() / 2;
348 OperationState state(op.getLoc(), op.getName());
349 state.addOperands(ValueRange{lhs, rhs});
350 state.addTypes(op.getResult(0).getType());
351 auto *newOp = Operation::create(state);
352 op.getBlock()->getOperations().insert(Block::iterator(&op), newOp);
353 newOps.push_back(newOp);
355 newOp->setAttr(
"sv.namehint", name);
356 if (
auto twoState = op.getAttr(
"twoState"))
357 newOp->setAttr(
"twoState", twoState);
358 return newOp->getResult(0);
365 ImplicitLocOpBuilder
builder(add.getLoc(), add);
371 add.getResult().replaceAllUsesWith(sub);
373 if (rhsCst.use_empty())
381 Operation *firstOp =
nullptr;
382 ImplicitLocOpBuilder
builder(op.getLoc(), op);
383 StructType structType = op.getInput().getType().cast<StructType>();
384 for (
auto [res, field] :
385 llvm::zip(op.getResults(), structType.getElements())) {
390 res.replaceAllUsesWith(extract);
402 Operation *parentOp = op->getParentOp();
403 assert(parentOp->hasTrait<ProceduralRegion>() &&
404 "we should only be hoisting from procedural");
405 while (parentOp->getParentOp()->hasTrait<ProceduralRegion>())
406 parentOp = parentOp->getParentOp();
418 assert(op->getNumResults() == 1 &&
"isn't a verilog expression");
421 if (op->hasOneUse()) {
422 if (
auto assign = dyn_cast<BPAssignOp>(*op->user_begin()))
429 Value opValue = op->getResult(0);
438 opValue.replaceAllUsesWith(value);
442 builder.setInsertionPointAfter(op);
443 builder.create<BPAssignOp>(op->getLoc(),
reg, opValue);
455 !(isa<sv::ReadInOutOp>(op) ||
465 bool cantHoist =
false;
466 if (llvm::any_of(op->getOperands(), [&](Value operand) ->
bool {
472 if (BlockArgument block = operand.dyn_cast<BlockArgument>()) {
474 if (isa<hw::HWModuleOp>(block.getParentBlock()->getParentOp()))
480 Operation *operandOp = operand.getDefiningOp();
482 if (operandOp->getParentOp()->hasTrait<ProceduralRegion>()) {
483 cantHoist |= operandOp->getBlock() == op->getBlock();
496 parentOp = op->getParentOp();
499 op->moveBefore(parentOp);
505 if (op->getNumResults() != 1 ||
506 !op->getResult(0).getType().isa<
InOutType, sv::InterfaceType>())
510 return llvm::all_of(op->getOperands(), [](Value operand) ->
bool {
511 auto *defOp = operand.getDefiningOp();
512 return !!defOp && isConstantExpression(defOp);
530 :
public hw::TypeOpVisitor<EmittedExpressionStateManager,
531 EmittedExpressionState>,
532 public comb::CombinationalVisitor<EmittedExpressionStateManager,
533 EmittedExpressionState>,
534 public sv::Visitor<EmittedExpressionStateManager,
535 EmittedExpressionState> {
538 : options(options){};
543 bool dispatchHeuristic(Operation &op);
547 bool shouldSpillWireBasedOnState(Operation &op);
556 LLVM_DEBUG(op->emitWarning() <<
"unhandled by EmittedExpressionState");
557 if (op->getNumOperands() == 0)
559 return mergeOperandsStates(op);
562 return dispatchTypeOpVisitor(op);
565 return visitUnhandledExpr(op);
568 return dispatchSVVisitor(op);
571 return visitUnhandledExpr(op);
574 return visitUnhandledExpr(op);
577 using CombinationalVisitor::visitComb;
578 using Visitor::visitSV;
591 auto it = expressionStates.find(v);
592 if (it != expressionStates.end())
596 if (
auto blockArg = v.dyn_cast<BlockArgument>())
600 dispatchCombinationalVisitor(v.getDefiningOp());
601 expressionStates.insert({v, state});
608 for (
auto operand : op->getOperands())
609 state.
mergeState(getExpressionState(operand));
620 SmallVector<OpOperand *> uses;
623 for (OpOperand &use : op->getUses()) {
625 if (
auto assignUse = dyn_cast<AssignOp>(use.getOwner())) {
632 if (!isa<HWModuleOp>(assignUse->getParentOp()))
641 uses.push_back(&use);
645 if (!assign || uses.empty())
648 if (
auto *cop = assign.getSrc().getDefiningOp())
649 if (isa<ConstantOp>(cop))
653 ImplicitLocOpBuilder
builder(assign.getDest().getLoc(), op->getContext());
654 for (OpOperand *use : uses) {
655 builder.setInsertionPoint(use->getOwner());
659 if (
auto *destOp = assign.getDest().getDefiningOp())
667 if (options.isWireSpillingHeuristicEnabled(
669 if (
auto hint = op.getAttrOfType<StringAttr>(
"sv.namehint")) {
671 if (!hint.getValue().starts_with(
"_"))
675 if (getExpressionState(op.getResult(0)).size >=
676 options.wireSpillingNamehintTermLimit)
688 if (op.getNumResults() == 0 ||
690 isa<ReadInOutOp, ConstantOp>(op))
695 if (op.hasOneUse()) {
696 auto *singleUser = *op.getUsers().begin();
697 if (isa<hw::OutputOp,
sv::AssignOp, sv::BPAssignOp, hw::InstanceOp,
698 hw::InstanceChoiceOp>(singleUser))
703 if (singleUser->hasOneUse() && isa<hw::BitcastOp>(singleUser) &&
704 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp>(
705 *singleUser->getUsers().begin()))
711 if (options.maximumNumberOfTermsPerExpression <
712 getExpressionState(op.getResult(0)).size)
714 return dispatchHeuristic(op);
723 if (block.getParentOp()->hasTrait<ProceduralRegion>())
726 for (
auto &op : llvm::make_early_inc_range(block)) {
735 for (
auto &op : block) {
737 for (
auto ®ion : op.getRegions()) {
756 SmallVectorImpl<WireLowering> &wireLowerings) {
757 for (
auto hwWireOp : block.getOps<hw::WireOp>()) {
761 Block::iterator assignPoint = block.begin();
762 if (
auto *defOp = hwWireOp.getInput().getDefiningOp())
763 if (defOp && defOp->getBlock() == &block)
764 assignPoint = ++Block::iterator(defOp);
769 Block::iterator declarePoint = assignPoint;
770 for (
auto *user : hwWireOp->getUsers()) {
771 while (user->getBlock() != &block)
772 user = user->getParentOp();
773 if (user->isBeforeInBlock(&*declarePoint))
774 declarePoint = Block::iterator(user);
777 wireLowerings.push_back({hwWireOp, declarePoint, assignPoint});
785 ArrayRef<WireLowering> wireLowerings) {
786 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
788 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings) {
790 ImplicitLocOpBuilder
builder(hwWireOp.getLoc(), &block, declarePoint);
792 if (isProceduralRegion) {
793 decl =
builder.create<LogicOp>(hwWireOp.getType(), hwWireOp.getNameAttr(),
794 hwWireOp.getInnerSymAttr());
798 hwWireOp.getInnerSymAttr());
802 auto defaultAttrNames = hwWireOp.getAttributeNames();
803 for (
auto namedAttr : hwWireOp->getAttrs())
804 if (!llvm::is_contained(defaultAttrNames, namedAttr.getName()))
805 decl.getDefiningOp()->setAttr(namedAttr.getName(),
806 namedAttr.getValue());
811 if (assignPoint != declarePoint)
812 builder.setInsertionPoint(&block, assignPoint);
813 if (isProceduralRegion)
814 builder.create<BPAssignOp>(decl, hwWireOp.getInput());
822 if (assignPoint != declarePoint)
823 builder.setInsertionPointAfterValue(decl);
827 hwWireOp.replaceAllUsesWith(readOp.getResult());
830 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings)
841 for (
auto &op : block) {
843 for (
auto ®ion : op.getRegions()) {
854 SmallVector<WireLowering> wireLowerings;
862 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
866 DenseSet<Operation *> visitedAlwaysInlineOperations;
870 SmallVector<Operation *> debugOpsToMoveToEnd;
872 for (Block::iterator opIterator = block.begin(), e = block.end();
874 auto &op = *opIterator++;
876 if (!isa<CombDialect, SVDialect, HWDialect, ltl::LTLDialect,
877 verif::VerifDialect, debug::DebugDialect>(op.getDialect())) {
878 auto d = op.emitError() <<
"dialect \"" << op.getDialect()->getNamespace()
879 <<
"\" not supported for direct Verilog emission";
880 d.attachNote() <<
"ExportVerilog cannot emit this operation; it needs "
881 "to be lowered before running ExportVerilog";
886 if (isa<ltl::LTLDialect>(op.getDialect()))
890 if (isa<debug::DebugDialect>(op.getDialect())) {
891 debugOpsToMoveToEnd.push_back(&op);
900 if (
auto inst = dyn_cast<HWInstanceLike>(op)) {
911 if (isProceduralRegion && isa<LogicOp>(op)) {
916 op.moveBefore(parentOp);
923 isProceduralRegion) {
929 if (!mlir::isMemoryEffectFree(&op)) {
949 if (op.use_empty()) {
955 if (visitedAlwaysInlineOperations.insert(&op).second)
961 if (
auto aggregateConstantOp = dyn_cast<hw::AggregateConstantOp>(op);
963 if (hw::StructType structType =
964 type_dyn_cast<hw::StructType>(aggregateConstantOp.getType())) {
966 SmallVector<Value> operands;
967 ImplicitLocOpBuilder
builder(op.getLoc(), op.getContext());
968 builder.setInsertionPointAfter(&op);
969 for (
auto [value, field] :
970 llvm::zip(aggregateConstantOp.getFieldsAttr(),
971 structType.getElements())) {
972 if (
auto arrayAttr = dyn_cast<mlir::ArrayAttr>(value))
974 builder.create<hw::AggregateConstantOp>(field.type, arrayAttr));
977 field.type, cast<mlir::IntegerAttr>(value)));
982 aggregateConstantOp.getResult().replaceAllUsesWith(structCreate);
984 opIterator = std::next(op.getIterator());
991 if (
auto structCreateOp = dyn_cast<hw::StructCreateOp>(op);
996 ImplicitLocOpBuilder
builder(op.getLoc(), &op);
997 hw::StructType structType =
998 structCreateOp.getResult().getType().cast<hw::StructType>();
999 bool procedural = op.getParentOp()->hasTrait<ProceduralRegion>();
1001 wireOp =
builder.create<LogicOp>(structType);
1005 for (
auto [input, field] :
1006 llvm::zip(structCreateOp.getInput(), structType.getElements())) {
1008 builder.create<sv::StructFieldInOutOp>(wireOp, field.name);
1010 builder.create<BPAssignOp>(target, input);
1016 llvm::make_early_inc_range(structCreateOp.getResult().getUses()))
1019 structCreateOp.erase();
1027 IndexedPartSelectInOutOp, IndexedPartSelectOp>(&op)) {
1032 if (
auto maybeReadOp =
1034 if (isa_and_nonnull<sv::WireOp, LogicOp>(
1035 maybeReadOp.getInput().getDefiningOp())) {
1036 wireOp = maybeReadOp.getInput();
1037 readOp = maybeReadOp;
1042 ImplicitLocOpBuilder
builder(op.getLoc(), &op);
1044 auto type = op.getOperand(1).getType();
1045 const auto *name =
"_GEN_ARRAY_IDX";
1046 if (op.getParentOp()->hasTrait<ProceduralRegion>()) {
1047 wireOp =
builder.create<LogicOp>(type, name);
1048 builder.create<BPAssignOp>(wireOp, op.getOperand(1));
1055 op.setOperand(1, readOp);
1073 if (!isProceduralRegion ||
1077 if (!op.getParentOp()->hasTrait<ProceduralRegion>())
1084 if (isProceduralRegion)
1101 if (op.getNumOperands() > 2 && op.getNumResults() == 1 &&
1102 op.hasTrait<mlir::OpTrait::IsCommutative>() &&
1103 mlir::isMemoryEffectFree(&op) && op.getNumRegions() == 0 &&
1104 op.getNumSuccessors() == 0 &&
1105 llvm::all_of(op.getAttrs(), [](NamedAttribute attr) {
1106 return attr.getNameDialect() != nullptr ||
1107 attr.getName() ==
"twoState";
1110 SmallVector<Operation *> newOps;
1112 op.getResult(0).replaceAllUsesWith(result);
1116 opIterator = Block::iterator(newOps.front());
1121 if (
auto addOp = dyn_cast<comb::AddOp>(op)) {
1122 if (
auto cst = addOp.getOperand(1).getDefiningOp<
hw::ConstantOp>()) {
1123 assert(addOp.getNumOperands() == 2 &&
"commutative lowering is done");
1124 if (cst.getValue().isNegative()) {
1126 opIterator = Block::iterator(firstOp);
1134 if (
auto structExplodeOp = dyn_cast<hw::StructExplodeOp>(op)) {
1136 opIterator = Block::iterator(firstOp);
1148 auto debugBuilder = OpBuilder::atBlockEnd(&block);
1149 if (!block.empty() && block.back().mightHaveTrait<OpTrait::IsTerminator>())
1150 debugBuilder.setInsertionPoint(&block.back());
1151 for (
auto *op : debugOpsToMoveToEnd) {
1153 debugBuilder.insert(op);
1156 if (isProceduralRegion) {
1165 std::pair<Block *, Block::iterator> logicOpInsertionPoint =
1167 for (
auto &op : llvm::make_early_inc_range(block)) {
1168 if (
auto logic = dyn_cast<LogicOp>(&op)) {
1171 if (logicOpInsertionPoint.second == logic->getIterator()) {
1172 ++logicOpInsertionPoint.second;
1176 logic->moveBefore(logicOpInsertionPoint.first,
1177 logicOpInsertionPoint.second);
1187 SmallPtrSet<Operation *, 32> seenOperations;
1189 for (
auto &op : llvm::make_early_inc_range(block)) {
1192 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
1198 bool haveAnyOutOfOrderUses =
false;
1199 for (
auto *userOp : op.getUsers()) {
1202 while (&block != &userOp->getParentRegion()->front())
1203 userOp = userOp->getParentOp();
1205 if (seenOperations.count(userOp)) {
1206 haveAnyOutOfOrderUses =
true;
1212 seenOperations.insert(&op);
1215 if (!haveAnyOutOfOrderUses)
1221 op.moveBefore(&block.front());
1227 op.moveBefore(&block.front());
1233 if (
auto readInOut = dyn_cast<ReadInOutOp>(op)) {
1234 auto *def = readInOut.getInput().getDefiningOp();
1236 op.moveBefore(&block.front());
1237 def->moveBefore(&block.front());
1267 struct PrepareForEmissionPass
1268 :
public PrepareForEmissionBase<PrepareForEmissionPass> {
1269 void runOnOperation()
override {
1273 signalPassFailure();
1280 return std::make_unique<PrepareForEmissionPass>();
assert(baseType &&"element must be base type")
static void lowerUsersToTemporaryWire(Operation &op, bool emitWireAtBlockBegin=false)
Emit an explicit wire or logic to assign operation's result.
static StringAttr getResName(Operation *op, size_t idx)
static std::pair< Block *, Block::iterator > findLogicOpInsertionPoint(Operation *op)
static bool rewriteSideEffectingExpr(Operation *op)
This function is invoked on side effecting Verilog expressions when we're in 'disallowLocalVariables'...
static Operation * lowerStructExplodeOp(hw::StructExplodeOp op)
static void buildWireLowerings(Block &block, SmallVectorImpl< WireLowering > &wireLowerings)
Determine the insertion location of declaration and assignment ops for hw::WireOps in a block.
static StringAttr getArgName(Operation *op, size_t idx)
static void spillWiresForInstanceInputs(HWInstanceLike op)
static void prettifyAfterLegalization(Block &block, EmittedExpressionStateManager &expressionStateManager)
After the legalization, we are able to know accurate verilog AST structures.
static void lowerInstanceResults(HWInstanceLike op)
static bool hoistNonSideEffectExpr(Operation *op)
This function is called for non-side-effecting Verilog expressions when we're in 'disallowLocalVariab...
static Operation * findParentInNonProceduralRegion(Operation *op)
Given an operation in a procedural region, scan up the region tree to find the first operation in a g...
static Operation * rewriteAddWithNegativeConstant(comb::AddOp add, hw::ConstantOp rhsCst)
Transform "a + -cst" ==> "a - cst" for prettier output.
static void lowerAlwaysInlineOperation(Operation *op, const LoweringOptions &options)
static bool reuseExistingInOut(Operation *op, const LoweringOptions &options)
If exactly one use of this op is an assign, replace the other uses with a read from the assigned wire...
static bool isMovableDeclaration(Operation *op)
Check whether an op is a declaration that can be moved.
static LogicalResult legalizeHWModule(Block &block, const LoweringOptions &options)
For each module we emit, do a prepass over the structure, pre-lowering and otherwise rewriting operat...
static Value lowerFullyAssociativeOp(Operation &op, OperandRange operands, SmallVector< Operation * > &newOps)
Lower a variadic fully-associative operation into an expression tree.
static bool shouldSpillWire(Operation &op, const LoweringOptions &options)
static void applyWireLowerings(Block &block, ArrayRef< WireLowering > wireLowerings)
Materialize the SV wire declaration and assignment operations in the locations previously determined ...
This class handles information about AST structures of each expressions.
DenseMap< Value, EmittedExpressionState > expressionStates
bool dispatchHeuristic(Operation &op)
EmittedExpressionState visitUnhandledSV(Operation *op)
EmittedExpressionStateManager(const LoweringOptions &options)
EmittedExpressionState visitInvalidComb(Operation *op)
EmittedExpressionState visitUnhandledTypeOp(Operation *op)
EmittedExpressionState getExpressionState(Value value)
const LoweringOptions & options
EmittedExpressionState visitUnhandledComb(Operation *op)
EmittedExpressionState mergeOperandsStates(Operation *op)
EmittedExpressionState visitUnhandledExpr(Operation *op)
bool shouldSpillWireBasedOnState(Operation &op)
Return true if it is beneficial to spill the operation under the specified spilling heuristic.
EmittedExpressionState visitInvalidTypeOp(Operation *op)
This helps visit TypeOp nodes.
def connect(destination, source)
LogicalResult prepareHWModule(Block &block, const LoweringOptions &options)
For each module we emit, do a prepass over the structure, pre-lowering and otherwise rewriting operat...
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
void pruneZeroValuedLogic(hw::HWModuleOp module)
bool isSimpleReadOrPort(Value v)
Check if the value is from read of a wire or reg or is a port.
static bool isExpressionAlwaysInline(Operation *op)
Return true for operations that must always be inlined into a containing expression for correctness.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
circt::hw::InOutType InOutType
unsigned addSVAttributes(mlir::Operation *op, llvm::ArrayRef< SVAttributeAttr > attrs)
Add a list of SV attributes to an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createPrepareForEmissionPass()
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
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.
@ SpillLargeTermsWithNamehints
bool disallowPackedStructAssignments
If true, eliminate packed struct assignments in favor of a wire + assignments to the individual field...