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"
36 #define DEBUG_TYPE "prepare-for-emission"
39 #define GEN_PASS_DEF_PREPAREFOREMISSION
40 #include "circt/Conversion/Passes.h.inc"
43 using namespace circt;
47 using namespace ExportVerilog;
51 if (isa<BlockArgument>(v))
53 auto vOp = v.getDefiningOp();
56 if (isa<sv::InOutType>(v.getType()) && isa<sv::WireOp>(vOp))
58 auto read = dyn_cast<ReadInOutOp>(vOp);
61 auto readSrc = read.getInput().getDefiningOp();
64 return isa<sv::WireOp, RegOp, LogicOp, XMROp, XMRRefOp>(readSrc);
76 static StringAttr
getArgName(Operation *op,
size_t idx) {
77 if (
auto inst = dyn_cast<hw::InstanceOp>(op))
78 return inst.getArgumentName(idx);
79 else if (
auto inst = dyn_cast<InstanceChoiceOp>(op))
80 return inst.getArgumentName(idx);
87 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
89 SmallString<32> nameTmp{
"_", op.getInstanceName(),
"_"};
90 auto namePrefixSize = nameTmp.size();
92 for (
size_t opNum = 0, e = op->getNumOperands(); opNum != e; ++opNum) {
93 auto src = op->getOperand(opNum);
98 nameTmp.resize(namePrefixSize);
100 nameTmp += n.getValue().str();
102 nameTmp += std::to_string(opNum);
104 auto newWire = builder.create<
sv::WireOp>(src.getType(), nameTmp);
107 newWireRead->moveBefore(op);
109 op->setOperand(opNum, newWireRead);
117 Value result, StringRef name) {
119 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
123 newTarget = builder.create<sv::LogicOp>(result.getType(),
124 builder.getStringAttr(name));
126 newTarget = builder.create<
sv::WireOp>(result.getType(), name);
129 while (!result.use_empty()) {
131 OpOperand &use = *result.getUses().begin();
133 newRead->moveBefore(use.getOwner());
138 connect = builder.create<sv::BPAssignOp>(newTarget, result);
146 if (
auto inst = dyn_cast<hw::InstanceOp>(op))
147 return inst.getResultName(idx);
148 else if (
auto inst = dyn_cast<InstanceChoiceOp>(op))
149 return inst.getResultName(idx);
156 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
158 SmallString<32> nameTmp{
"_", op.getInstanceName(),
"_"};
159 auto namePrefixSize = nameTmp.size();
161 for (
size_t resNum = 0, e = op->getNumResults(); resNum != e; ++resNum) {
162 auto result = op->getResult(resNum);
171 if (result.hasOneUse()) {
172 OpOperand &use = *result.getUses().begin();
173 if (dyn_cast_or_null<OutputOp>(use.getOwner()))
175 if (
auto assign = dyn_cast_or_null<AssignOp>(use.getOwner())) {
177 assign->moveAfter(op);
182 nameTmp.resize(namePrefixSize);
184 nameTmp += n.getValue().str();
186 nameTmp += std::to_string(resNum);
187 Value newWire = builder.create<
sv::WireOp>(result.getType(), nameTmp);
189 while (!result.use_empty()) {
191 OpOperand &use = *result.getUses().begin();
192 use.set(newWireRead);
193 newWireRead->moveBefore(use.getOwner());
203 Block *block = op->getParentOfType<HWModuleLike>().
getBodyBlock();
204 auto builder = ImplicitLocOpBuilder::atBlockBegin(op->getLoc(), block);
205 auto callee = op->getAttrOfType<FlatSymbolRefAttr>(
"callee");
207 SmallString<32> nameTmp{
"_", callee.getValue(),
"_"};
209 auto namePrefixSize = nameTmp.size();
211 for (
auto [i, result] : llvm::enumerate(op->getResults())) {
212 if (result.hasOneUse()) {
213 Operation *user = *result.getUsers().begin();
214 if (isa<BPAssignOp, AssignOp>(user)) {
221 nameTmp.resize(namePrefixSize);
223 nameTmp += std::to_string(i);
235 bool emitWireAtBlockBegin =
false) {
236 Block *block = op.getBlock();
237 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
238 bool isProceduralRegion = op.getParentOp()->hasTrait<ProceduralRegion>();
240 auto createWireForResult = [&](Value result, StringAttr name) {
242 Type wireElementType = result.getType();
243 bool isResultInOut =
false;
246 if (
auto inoutType = hw::type_dyn_cast<hw::InOutType>(result.getType())) {
247 wireElementType = inoutType.getElementType();
248 isResultInOut =
true;
252 if (isProceduralRegion)
253 newWire = builder.create<LogicOp>(wireElementType, name);
255 newWire = builder.create<
sv::WireOp>(wireElementType, name);
258 while (!result.use_empty()) {
259 OpOperand &use = *result.getUses().begin();
264 use.set(newWireRead);
265 newWireRead->moveBefore(use.getOwner());
276 if (isProceduralRegion)
277 connect = builder.create<BPAssignOp>(
278 newWire, isResultInOut ? resultRead.getResult() : result);
281 newWire, isResultInOut ? resultRead.getResult() : result);
285 resultRead->moveBefore(
connect);
288 if (!emitWireAtBlockBegin) {
294 newWire.getDefiningOp()->moveAfter(&op);
300 if (op.getNumResults() == 1) {
302 op.removeAttr(
"sv.namehint");
303 createWireForResult(op.getResult(0), namehint);
308 for (
auto result : op.getResults())
309 createWireForResult(result, StringAttr());
316 assert(op->getNumResults() == 1 &&
317 "only support 'always inline' ops with one result");
323 auto recursivelyHandleOperands = [&](Operation *op) {
324 for (
auto operand : op->getOperands()) {
325 if (
auto *operandOp = operand.getDefiningOp()) {
339 auto skipToWireImmediatelyBefore = [](Operation *user) {
340 if (!isa<BPAssignOp, AssignOp>(user))
342 auto *wireOp = user->getOperand(0).getDefiningOp();
343 if (wireOp && wireOp->getNextNode() == user)
350 while (!op->hasOneUse()) {
351 OpOperand &use = *op->getUses().begin();
352 Operation *user = skipToWireImmediatelyBefore(use.getOwner());
355 auto *newOp = op->clone();
356 user->getBlock()->getOperations().insert(Block::iterator(user), newOp);
358 use.set(newOp->getResult(0));
362 recursivelyHandleOperands(newOp);
367 Operation *user = skipToWireImmediatelyBefore(*op->getUsers().begin());
368 op->moveBefore(user);
372 recursivelyHandleOperands(op);
379 static std::pair<Block *, Block::iterator>
382 if (isa<IfDefProceduralOp>(op->getParentOp()))
384 return {op->getBlock(), op->getBlock()->begin()};
391 SmallVector<Operation *> &newOps) {
393 auto name = op.getAttr(
"sv.namehint");
395 op.removeAttr(
"sv.namehint");
397 switch (operands.size()) {
399 assert(0 &&
"cannot be called with empty operand range");
408 auto firstHalf = operands.size() / 2;
414 OperationState state(op.getLoc(), op.getName());
415 state.addOperands(ValueRange{lhs, rhs});
416 state.addTypes(op.getResult(0).getType());
417 auto *newOp = Operation::create(state);
418 op.getBlock()->getOperations().insert(Block::iterator(&op), newOp);
419 newOps.push_back(newOp);
421 newOp->setAttr(
"sv.namehint", name);
422 if (
auto twoState = op.getAttr(
"twoState"))
423 newOp->setAttr(
"twoState", twoState);
424 return newOp->getResult(0);
431 ImplicitLocOpBuilder builder(add.getLoc(), add);
437 add.getResult().replaceAllUsesWith(sub);
439 if (rhsCst.use_empty())
447 Operation *firstOp =
nullptr;
448 ImplicitLocOpBuilder builder(op.getLoc(), op);
449 StructType structType = cast<StructType>(op.getInput().getType());
450 for (
auto [res, field] :
451 llvm::zip(op.getResults(), structType.getElements())) {
456 res.replaceAllUsesWith(extract);
468 Operation *parentOp = op->getParentOp();
469 assert(parentOp->hasTrait<ProceduralRegion>() &&
470 "we should only be hoisting from procedural");
471 while (parentOp->getParentOp()->hasTrait<ProceduralRegion>())
472 parentOp = parentOp->getParentOp();
484 assert(op->getNumResults() == 1 &&
"isn't a verilog expression");
487 if (op->hasOneUse()) {
488 if (
auto assign = dyn_cast<BPAssignOp>(*op->user_begin()))
495 Value opValue = op->getResult(0);
499 OpBuilder builder(parentOp);
500 auto reg = builder.create<
RegOp>(op->getLoc(), opValue.getType());
504 opValue.replaceAllUsesWith(value);
508 builder.setInsertionPointAfter(op);
509 builder.
create<BPAssignOp>(op->getLoc(),
reg, opValue);
521 !(isa<sv::ReadInOutOp>(op) ||
522 isa<hw::InOutType>(op->getResult(0).getType())))
531 bool cantHoist =
false;
532 if (llvm::any_of(op->getOperands(), [&](Value operand) ->
bool {
538 if (BlockArgument block = dyn_cast<BlockArgument>(operand)) {
540 if (isa<hw::HWModuleOp>(block.getParentBlock()->getParentOp()))
546 Operation *operandOp = operand.getDefiningOp();
548 if (operandOp->getParentOp()->hasTrait<ProceduralRegion>()) {
549 cantHoist |= operandOp->getBlock() == op->getBlock();
562 parentOp = op->getParentOp();
565 op->moveBefore(parentOp);
571 if (op->getNumResults() != 1 ||
572 !isa<InOutType, sv::InterfaceType>(op->getResult(0).getType()))
576 return llvm::all_of(op->getOperands(), [](Value operand) ->
bool {
577 auto *defOp = operand.getDefiningOp();
578 return !!defOp && isConstantExpression(defOp);
596 :
public hw::TypeOpVisitor<EmittedExpressionStateManager,
597 EmittedExpressionState>,
598 public comb::CombinationalVisitor<EmittedExpressionStateManager,
599 EmittedExpressionState>,
600 public sv::Visitor<EmittedExpressionStateManager,
601 EmittedExpressionState> {
604 : options(options){};
609 bool dispatchHeuristic(Operation &op);
613 bool shouldSpillWireBasedOnState(Operation &op);
622 LLVM_DEBUG(op->emitWarning() <<
"unhandled by EmittedExpressionState");
623 if (op->getNumOperands() == 0)
625 return mergeOperandsStates(op);
628 return dispatchTypeOpVisitor(op);
631 return visitUnhandledExpr(op);
634 return dispatchSVVisitor(op);
637 return visitUnhandledExpr(op);
640 return visitUnhandledExpr(op);
643 using CombinationalVisitor::visitComb;
644 using Visitor::visitSV;
657 auto it = expressionStates.find(v);
658 if (it != expressionStates.end())
662 if (
auto blockArg = dyn_cast<BlockArgument>(v))
666 dispatchCombinationalVisitor(v.getDefiningOp());
667 expressionStates.insert({v, state});
674 for (
auto operand : op->getOperands())
675 state.
mergeState(getExpressionState(operand));
686 SmallVector<OpOperand *> uses;
689 for (OpOperand &use : op->getUses()) {
691 if (
auto assignUse = dyn_cast<AssignOp>(use.getOwner())) {
698 if (!isa<HWModuleOp>(assignUse->getParentOp()))
707 uses.push_back(&use);
711 if (!assign || uses.empty())
714 if (
auto *cop = assign.getSrc().getDefiningOp())
715 if (isa<ConstantOp>(cop))
719 ImplicitLocOpBuilder builder(assign.getDest().getLoc(), op->getContext());
720 for (OpOperand *use : uses) {
721 builder.setInsertionPoint(use->getOwner());
722 auto read = builder.create<
ReadInOutOp>(assign.getDest());
725 if (
auto *destOp = assign.getDest().getDefiningOp())
733 if (options.isWireSpillingHeuristicEnabled(
735 if (
auto hint = op.getAttrOfType<StringAttr>(
"sv.namehint")) {
737 if (!hint.getValue().starts_with(
"_"))
741 if (getExpressionState(op.getResult(0)).size >=
742 options.wireSpillingNamehintTermLimit)
754 if (op.getNumResults() == 0 ||
755 isa<hw::InOutType>(op.getResult(0).getType()) ||
756 isa<ReadInOutOp, ConstantOp>(op))
761 if (op.hasOneUse()) {
762 auto *singleUser = *op.getUsers().begin();
763 if (isa<hw::OutputOp,
sv::AssignOp, sv::BPAssignOp, hw::InstanceOp,
764 hw::InstanceChoiceOp>(singleUser))
769 if (singleUser->hasOneUse() && isa<hw::BitcastOp>(singleUser) &&
770 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp>(
771 *singleUser->getUsers().begin()))
777 if (options.maximumNumberOfTermsPerExpression <
778 getExpressionState(op.getResult(0)).size)
780 return dispatchHeuristic(op);
789 if (block.getParentOp()->hasTrait<ProceduralRegion>())
792 for (
auto &op : llvm::make_early_inc_range(block)) {
801 for (
auto &op : block) {
803 for (
auto ®ion : op.getRegions()) {
822 SmallVectorImpl<WireLowering> &wireLowerings) {
823 for (
auto hwWireOp : block.getOps<hw::WireOp>()) {
827 Block::iterator assignPoint = block.begin();
828 if (
auto *defOp = hwWireOp.getInput().getDefiningOp())
829 if (defOp && defOp->getBlock() == &block)
830 assignPoint = ++Block::iterator(defOp);
835 Block::iterator declarePoint = assignPoint;
836 for (
auto *user : hwWireOp->getUsers()) {
837 while (user->getBlock() != &block)
838 user = user->getParentOp();
839 if (user->isBeforeInBlock(&*declarePoint))
840 declarePoint = Block::iterator(user);
843 wireLowerings.push_back({hwWireOp, declarePoint, assignPoint});
851 ArrayRef<WireLowering> wireLowerings) {
852 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
854 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings) {
856 ImplicitLocOpBuilder builder(hwWireOp.getLoc(), &block, declarePoint);
858 if (isProceduralRegion) {
859 decl = builder.create<LogicOp>(hwWireOp.getType(), hwWireOp.getNameAttr(),
860 hwWireOp.getInnerSymAttr());
863 builder.create<
sv::WireOp>(hwWireOp.getType(), hwWireOp.getNameAttr(),
864 hwWireOp.getInnerSymAttr());
868 auto defaultAttrNames = hwWireOp.getAttributeNames();
869 for (
auto namedAttr : hwWireOp->getAttrs())
870 if (!llvm::is_contained(defaultAttrNames, namedAttr.getName()))
871 decl.getDefiningOp()->setAttr(namedAttr.getName(),
872 namedAttr.getValue());
877 if (assignPoint != declarePoint)
878 builder.setInsertionPoint(&block, assignPoint);
879 if (isProceduralRegion)
880 builder.
create<BPAssignOp>(decl, hwWireOp.getInput());
882 builder.create<
AssignOp>(decl, hwWireOp.getInput());
888 if (assignPoint != declarePoint)
889 builder.setInsertionPointAfterValue(decl);
893 hwWireOp.replaceAllUsesWith(readOp.getResult());
896 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings)
907 for (
auto &op : block) {
909 for (
auto ®ion : op.getRegions()) {
920 SmallVector<WireLowering> wireLowerings;
928 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
932 DenseSet<Operation *> visitedAlwaysInlineOperations;
936 SmallVector<Operation *> debugOpsToMoveToEnd;
938 for (Block::iterator opIterator = block.begin(), e = block.end();
940 auto &op = *opIterator++;
942 if (!isa<CombDialect, SVDialect, HWDialect, ltl::LTLDialect,
943 verif::VerifDialect, debug::DebugDialect>(op.getDialect())) {
944 auto d = op.emitError() <<
"dialect \"" << op.getDialect()->getNamespace()
945 <<
"\" not supported for direct Verilog emission";
946 d.attachNote() <<
"ExportVerilog cannot emit this operation; it needs "
947 "to be lowered before running ExportVerilog";
952 if (isa<ltl::LTLDialect>(op.getDialect()))
956 if (isa<debug::DebugDialect>(op.getDialect())) {
957 debugOpsToMoveToEnd.push_back(&op);
966 if (
auto inst = dyn_cast<HWInstanceLike>(op)) {
975 if (
auto call = dyn_cast<mlir::CallOpInterface>(op))
980 if (isProceduralRegion && isa<LogicOp>(op)) {
985 op.moveBefore(parentOp);
992 isProceduralRegion) {
998 if (!mlir::isMemoryEffectFree(&op)) {
1018 if (op.use_empty()) {
1024 if (visitedAlwaysInlineOperations.insert(&op).second)
1030 if (
auto aggregateConstantOp = dyn_cast<hw::AggregateConstantOp>(op);
1032 if (hw::StructType structType =
1033 type_dyn_cast<hw::StructType>(aggregateConstantOp.getType())) {
1035 SmallVector<Value> operands;
1036 ImplicitLocOpBuilder builder(op.getLoc(), op.getContext());
1037 builder.setInsertionPointAfter(&op);
1038 for (
auto [value, field] :
1039 llvm::zip(aggregateConstantOp.getFieldsAttr(),
1040 structType.getElements())) {
1041 if (
auto arrayAttr = dyn_cast<mlir::ArrayAttr>(value))
1043 builder.create<hw::AggregateConstantOp>(field.type, arrayAttr));
1046 field.type, cast<mlir::IntegerAttr>(value)));
1051 aggregateConstantOp.getResult().replaceAllUsesWith(structCreate);
1053 opIterator = std::next(op.getIterator());
1060 if (
auto structCreateOp = dyn_cast<hw::StructCreateOp>(op);
1065 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1066 hw::StructType structType =
1067 cast<hw::StructType>(structCreateOp.getResult().getType());
1068 bool procedural = op.getParentOp()->hasTrait<ProceduralRegion>();
1070 wireOp = builder.create<LogicOp>(structType);
1072 wireOp = builder.create<
sv::WireOp>(structType);
1074 for (
auto [input, field] :
1075 llvm::zip(structCreateOp.getInput(), structType.getElements())) {
1077 builder.
create<sv::StructFieldInOutOp>(wireOp, field.name);
1079 builder.create<BPAssignOp>(target, input);
1081 builder.create<
AssignOp>(target, input);
1085 llvm::make_early_inc_range(structCreateOp.getResult().getUses()))
1088 structCreateOp.erase();
1096 IndexedPartSelectInOutOp, IndexedPartSelectOp>(&op)) {
1101 if (
auto maybeReadOp =
1103 if (isa_and_nonnull<sv::WireOp, LogicOp>(
1104 maybeReadOp.getInput().getDefiningOp())) {
1105 wireOp = maybeReadOp.getInput();
1106 readOp = maybeReadOp;
1111 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1113 auto type = op.getOperand(1).getType();
1114 const auto *name =
"_GEN_ARRAY_IDX";
1115 if (op.getParentOp()->hasTrait<ProceduralRegion>()) {
1116 wireOp = builder.create<LogicOp>(type, name);
1117 builder.create<BPAssignOp>(wireOp, op.getOperand(1));
1119 wireOp = builder.create<
sv::WireOp>(type, name);
1124 op.setOperand(1, readOp);
1142 if (!isProceduralRegion ||
1146 if (!op.getParentOp()->hasTrait<ProceduralRegion>())
1153 if (isProceduralRegion)
1170 if (op.getNumOperands() > 2 && op.getNumResults() == 1 &&
1171 op.hasTrait<mlir::OpTrait::IsCommutative>() &&
1172 mlir::isMemoryEffectFree(&op) && op.getNumRegions() == 0 &&
1173 op.getNumSuccessors() == 0 &&
1174 llvm::all_of(op.getAttrs(), [](NamedAttribute attr) {
1175 return attr.getNameDialect() != nullptr ||
1176 attr.getName() ==
"twoState";
1179 SmallVector<Operation *> newOps;
1181 op.getResult(0).replaceAllUsesWith(result);
1185 opIterator = Block::iterator(newOps.front());
1190 if (
auto addOp = dyn_cast<comb::AddOp>(op)) {
1191 if (
auto cst = addOp.getOperand(1).getDefiningOp<
hw::ConstantOp>()) {
1192 assert(addOp.getNumOperands() == 2 &&
"commutative lowering is done");
1193 if (cst.getValue().isNegative()) {
1195 opIterator = Block::iterator(firstOp);
1203 if (
auto structExplodeOp = dyn_cast<hw::StructExplodeOp>(op)) {
1205 opIterator = Block::iterator(firstOp);
1217 auto debugBuilder = OpBuilder::atBlockEnd(&block);
1218 if (!block.empty() && block.back().mightHaveTrait<OpTrait::IsTerminator>())
1219 debugBuilder.setInsertionPoint(&block.back());
1220 for (
auto *op : debugOpsToMoveToEnd) {
1222 debugBuilder.insert(op);
1225 if (isProceduralRegion) {
1234 std::pair<Block *, Block::iterator> logicOpInsertionPoint =
1236 for (
auto &op : llvm::make_early_inc_range(block)) {
1237 if (
auto logic = dyn_cast<LogicOp>(&op)) {
1240 if (logicOpInsertionPoint.second == logic->getIterator()) {
1241 ++logicOpInsertionPoint.second;
1245 logic->moveBefore(logicOpInsertionPoint.first,
1246 logicOpInsertionPoint.second);
1256 SmallPtrSet<Operation *, 32> seenOperations;
1258 for (
auto &op : llvm::make_early_inc_range(block)) {
1261 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
1267 bool haveAnyOutOfOrderUses =
false;
1268 for (
auto *userOp : op.getUsers()) {
1271 while (&block != &userOp->getParentRegion()->front())
1272 userOp = userOp->getParentOp();
1274 if (seenOperations.count(userOp)) {
1275 haveAnyOutOfOrderUses =
true;
1281 seenOperations.insert(&op);
1284 if (!haveAnyOutOfOrderUses)
1290 op.moveBefore(&block.front());
1296 op.moveBefore(&block.front());
1302 if (
auto readInOut = dyn_cast<ReadInOutOp>(op)) {
1303 auto *def = readInOut.getInput().getDefiningOp();
1305 op.moveBefore(&block.front());
1306 def->moveBefore(&block.front());
1322 if (!module.getBodyBlock())
1341 struct PrepareForEmissionPass
1342 :
public circt::impl::PrepareForEmissionBase<PrepareForEmissionPass> {
1344 bool canScheduleOn(mlir::RegisteredOperationName opName)
const final {
1345 return opName.hasInterface<hw::HWEmittableModuleLike>();
1348 void runOnOperation()
override {
1349 auto module = cast<hw::HWEmittableModuleLike>(getOperation());
1352 signalPassFailure();
1359 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 lowerFunctionCallResults(Operation *op)
static void spillWiresForInstanceInputs(HWInstanceLike op)
static void prettifyAfterLegalization(Block &block, EmittedExpressionStateManager &expressionStateManager)
After the legalization, we are able to know accurate verilog AST structures.
static void lowerInstanceResults(HWInstanceLike op)
static bool hoistNonSideEffectExpr(Operation *op)
This function is called for non-side-effecting Verilog expressions when we're in 'disallowLocalVariab...
static Operation * findParentInNonProceduralRegion(Operation *op)
Given an operation in a procedural region, scan up the region tree to find the first operation in a g...
static Operation * rewriteAddWithNegativeConstant(comb::AddOp add, hw::ConstantOp rhsCst)
Transform "a + -cst" ==> "a - cst" for prettier output.
static void lowerAlwaysInlineOperation(Operation *op, const LoweringOptions &options)
static void replacePortWithWire(ImplicitLocOpBuilder &builder, Operation *op, Value result, StringRef name)
static bool reuseExistingInOut(Operation *op, const LoweringOptions &options)
If exactly one use of this op is an assign, replace the other uses with a read from the assigned wire...
static bool isMovableDeclaration(Operation *op)
Check whether an op is a declaration that can be moved.
static LogicalResult legalizeHWModule(Block &block, const LoweringOptions &options)
For each module we emit, do a prepass over the structure, pre-lowering and otherwise rewriting operat...
static Value lowerFullyAssociativeOp(Operation &op, OperandRange operands, SmallVector< Operation * > &newOps)
Lower a variadic fully-associative operation into an expression tree.
static bool shouldSpillWire(Operation &op, const LoweringOptions &options)
static void applyWireLowerings(Block &block, ArrayRef< WireLowering > wireLowerings)
Materialize the SV wire declaration and assignment operations in the locations previously determined ...
static Block * getBodyBlock(FModuleLike mod)
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 create(data_type, value)
def create(data_type, name=None, sym_name=None)
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...
void pruneZeroValuedLogic(hw::HWEmittableModuleLike module)
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
bool isSimpleReadOrPort(Value v)
Check if the value is from read of a wire or reg or is a port.
static bool isExpressionAlwaysInline(Operation *op)
Return true for operations that must always be inlined into a containing expression for correctness.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
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...