231 bool emitWireAtBlockBegin =
false) {
232 Block *block = op.getBlock();
233 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
234 bool isProceduralRegion = op.getParentOp()->hasTrait<ProceduralRegion>();
236 auto createWireForResult = [&](Value result, StringAttr name) {
238 Type wireElementType = result.getType();
239 bool isResultInOut =
false;
242 if (
auto inoutType = hw::type_dyn_cast<hw::InOutType>(result.getType())) {
243 wireElementType = inoutType.getElementType();
244 isResultInOut =
true;
248 if (isProceduralRegion)
249 newWire = LogicOp::create(builder, wireElementType, name);
254 while (!result.use_empty()) {
255 OpOperand &use = *result.getUses().begin();
260 use.set(newWireRead);
261 newWireRead->moveBefore(use.getOwner());
272 if (isProceduralRegion)
273 connect = BPAssignOp::create(
274 builder, newWire, isResultInOut ? resultRead.getResult() : result);
277 builder, newWire, isResultInOut ? resultRead.getResult() : result);
279 connect->moveAfter(&op);
281 resultRead->moveBefore(connect);
284 if (!emitWireAtBlockBegin) {
290 newWire.getDefiningOp()->moveAfter(&op);
296 if (op.getNumResults() == 1) {
298 op.removeAttr(
"sv.namehint");
299 createWireForResult(op.getResult(0), namehint);
304 for (
auto result : op.getResults())
305 createWireForResult(result, StringAttr());
859 ArrayRef<WireLowering> wireLowerings) {
860 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
862 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings) {
864 ImplicitLocOpBuilder builder(hwWireOp.getLoc(), &block, declarePoint);
866 if (isProceduralRegion) {
868 LogicOp::create(builder, hwWireOp.getType(), hwWireOp.getNameAttr(),
869 hwWireOp.getInnerSymAttr());
872 hwWireOp.getNameAttr(),
873 hwWireOp.getInnerSymAttr());
877 auto defaultAttrNames = hwWireOp.getAttributeNames();
878 for (
auto namedAttr : hwWireOp->getAttrs())
879 if (!llvm::is_contained(defaultAttrNames, namedAttr.getName()))
880 decl.getDefiningOp()->setAttr(namedAttr.getName(),
881 namedAttr.getValue());
886 if (assignPoint != declarePoint)
887 builder.setInsertionPoint(&block, assignPoint);
888 if (isProceduralRegion)
889 BPAssignOp::create(builder, decl, hwWireOp.getInput());
897 if (assignPoint != declarePoint)
898 builder.setInsertionPointAfterValue(decl);
902 hwWireOp.replaceAllUsesWith(readOp.getResult());
905 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings)
916 for (
auto &op : block) {
918 for (
auto ®ion : op.getRegions()) {
929 SmallVector<WireLowering> wireLowerings;
937 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
941 DenseSet<Operation *> visitedAlwaysInlineOperations;
945 SmallVector<Operation *> debugOpsToMoveToEnd;
947 for (Block::iterator opIterator = block.begin(), e = block.end();
949 auto &op = *opIterator++;
951 if (!isa<CombDialect, SVDialect, HWDialect, ltl::LTLDialect,
952 verif::VerifDialect, debug::DebugDialect>(op.getDialect())) {
953 auto d = op.emitError() <<
"dialect \"" << op.getDialect()->getNamespace()
954 <<
"\" not supported for direct Verilog emission";
955 d.attachNote() <<
"ExportVerilog cannot emit this operation; it needs "
956 "to be lowered before running ExportVerilog";
961 if (isa<ltl::LTLDialect>(op.getDialect()))
965 if (isa<debug::DebugDialect>(op.getDialect())) {
966 debugOpsToMoveToEnd.push_back(&op);
975 if (
auto inst = dyn_cast<HWInstanceLike>(op)) {
984 if (
auto call = dyn_cast<mlir::CallOpInterface>(op))
989 if (isProceduralRegion && isa<LogicOp, RegOp>(op)) {
994 op.moveBefore(parentOp);
1001 isProceduralRegion) {
1007 if (!mlir::isMemoryEffectFree(&op)) {
1027 if (op.use_empty()) {
1033 if (visitedAlwaysInlineOperations.insert(&op).second)
1039 if (
auto aggregateConstantOp = dyn_cast<hw::AggregateConstantOp>(op);
1041 if (hw::StructType structType =
1042 type_dyn_cast<hw::StructType>(aggregateConstantOp.getType())) {
1044 SmallVector<Value> operands;
1045 ImplicitLocOpBuilder builder(op.getLoc(), op.getContext());
1046 builder.setInsertionPointAfter(&op);
1047 for (
auto [value, field] :
1048 llvm::zip(aggregateConstantOp.getFieldsAttr(),
1049 structType.getElements())) {
1050 if (
auto arrayAttr = dyn_cast<mlir::ArrayAttr>(value))
1051 operands.push_back(hw::AggregateConstantOp::create(
1052 builder, field.type, arrayAttr));
1055 builder, field.type, cast<mlir::IntegerAttr>(value)));
1060 aggregateConstantOp.getResult().replaceAllUsesWith(structCreate);
1062 opIterator = std::next(op.getIterator());
1069 if (
auto structCreateOp = dyn_cast<hw::StructCreateOp>(op);
1074 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1075 hw::StructType structType =
1076 cast<hw::StructType>(structCreateOp.getResult().getType());
1077 bool procedural = op.getParentOp()->hasTrait<ProceduralRegion>();
1079 wireOp = LogicOp::create(builder, structType);
1083 for (
auto [input, field] :
1084 llvm::zip(structCreateOp.getInput(), structType.getElements())) {
1086 sv::StructFieldInOutOp::create(builder, wireOp, field.name);
1088 BPAssignOp::create(builder, target, input);
1094 llvm::make_early_inc_range(structCreateOp.getResult().getUses()))
1097 structCreateOp.erase();
1103 if (
auto arrayInjectOp = dyn_cast<hw::ArrayInjectOp>(op)) {
1104 bool procedural = op.getParentOp()->hasTrait<ProceduralRegion>();
1105 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1108 decl = LogicOp::create(builder, arrayInjectOp.getType());
1110 decl = sv::RegOp::create(builder, arrayInjectOp.getType());
1111 for (
auto &use : llvm::make_early_inc_range(arrayInjectOp->getUses()))
1117 auto alwaysOp = sv::AlwaysCombOp::create(builder);
1118 builder.setInsertionPointToStart(alwaysOp.getBodyBlock());
1122 sv::BPAssignOp::create(builder, decl, arrayInjectOp.getInput());
1125 auto target = sv::ArrayIndexInOutOp::create(builder, decl,
1126 arrayInjectOp.getIndex());
1127 sv::BPAssignOp::create(builder, target, arrayInjectOp.getElement());
1129 arrayInjectOp.erase();
1137 IndexedPartSelectInOutOp, IndexedPartSelectOp>(&op)) {
1142 if (
auto maybeReadOp =
1144 if (isa_and_nonnull<sv::WireOp, LogicOp>(
1145 maybeReadOp.getInput().getDefiningOp())) {
1146 wireOp = maybeReadOp.getInput();
1147 readOp = maybeReadOp;
1152 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1154 auto type = op.getOperand(1).getType();
1155 const auto *name =
"_GEN_ARRAY_IDX";
1156 if (op.getParentOp()->hasTrait<ProceduralRegion>()) {
1157 wireOp = LogicOp::create(builder, type, name);
1158 BPAssignOp::create(builder, wireOp, op.getOperand(1));
1165 op.setOperand(1, readOp);
1168 sv::addSVAttributes(wireOp.getDefiningOp(),
1169 SVAttributeAttr::get(wireOp.getContext(),
"keep",
1183 if (!isProceduralRegion ||
1187 if (!op.getParentOp()->hasTrait<ProceduralRegion>())
1194 if (isProceduralRegion)
1212 if (op.getNumOperands() != 2 && op.getNumResults() == 1 &&
1213 op.hasTrait<mlir::OpTrait::IsCommutative>() &&
1214 mlir::isMemoryEffectFree(&op) && op.getNumRegions() == 0 &&
1215 op.getNumSuccessors() == 0 &&
1216 llvm::all_of(op.getAttrs(), [](NamedAttribute attr) {
1217 return attr.getNameDialect() != nullptr ||
1218 attr.getName() ==
"twoState";
1221 SmallVector<Operation *> newOps;
1223 op.getResult(0).replaceAllUsesWith(result);
1227 if (!newOps.empty())
1228 opIterator = Block::iterator(newOps.front());
1233 if (
auto addOp = dyn_cast<comb::AddOp>(op)) {
1234 if (
auto cst = addOp.getOperand(1).getDefiningOp<
hw::ConstantOp>()) {
1235 assert(addOp.getNumOperands() == 2 &&
"commutative lowering is done");
1236 if (cst.getValue().isNegative()) {
1238 opIterator = Block::iterator(firstOp);
1246 if (
auto structExplodeOp = dyn_cast<hw::StructExplodeOp>(op)) {
1248 opIterator = Block::iterator(firstOp);
1260 auto debugBuilder = OpBuilder::atBlockEnd(&block);
1261 if (!block.empty() && block.back().mightHaveTrait<OpTrait::IsTerminator>())
1262 debugBuilder.setInsertionPoint(&block.back());
1263 for (
auto *op : debugOpsToMoveToEnd) {
1265 debugBuilder.insert(op);
1268 if (isProceduralRegion) {
1277 std::pair<Block *, Block::iterator> logicOpInsertionPoint =
1279 for (
auto &op : llvm::make_early_inc_range(block)) {
1280 if (
auto logic = dyn_cast<LogicOp>(&op)) {
1283 if (logicOpInsertionPoint.second == logic->getIterator()) {
1284 ++logicOpInsertionPoint.second;
1288 logic->moveBefore(logicOpInsertionPoint.first,
1289 logicOpInsertionPoint.second);
1299 SmallPtrSet<Operation *, 32> seenOperations;
1301 for (
auto &op : llvm::make_early_inc_range(block)) {
1304 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
1310 bool haveAnyOutOfOrderUses =
false;
1311 for (
auto *userOp : op.getUsers()) {
1314 while (&block != &userOp->getParentRegion()->front())
1315 userOp = userOp->getParentOp();
1317 if (seenOperations.count(userOp)) {
1318 haveAnyOutOfOrderUses =
true;
1324 seenOperations.insert(&op);
1327 if (!haveAnyOutOfOrderUses)
1333 op.moveBefore(&block.front());
1339 op.moveBefore(&block.front());
1345 if (
auto readInOut = dyn_cast<ReadInOutOp>(op)) {
1346 auto *def = readInOut.getInput().getDefiningOp();
1348 op.moveBefore(&block.front());
1349 def->moveBefore(&block.front());