232 bool emitWireAtBlockBegin =
false) {
233 Block *block = op.getBlock();
234 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
237 auto createWireForResult = [&](Value result, StringAttr name) {
239 Type wireElementType = result.getType();
240 bool isResultInOut =
false;
243 if (
auto inoutType = hw::type_dyn_cast<hw::InOutType>(result.getType())) {
244 wireElementType = inoutType.getElementType();
245 isResultInOut =
true;
249 if (isProceduralRegion)
250 newWire = LogicOp::create(builder, wireElementType, name);
255 while (!result.use_empty()) {
256 OpOperand &use = *result.getUses().begin();
261 use.set(newWireRead);
262 newWireRead->moveBefore(use.getOwner());
273 if (isProceduralRegion)
274 connect = BPAssignOp::create(
275 builder, newWire, isResultInOut ? resultRead.getResult() : result);
278 builder, newWire, isResultInOut ? resultRead.getResult() : result);
280 connect->moveAfter(&op);
282 resultRead->moveBefore(connect);
285 if (!emitWireAtBlockBegin) {
291 newWire.getDefiningOp()->moveAfter(&op);
297 if (op.getNumResults() == 1) {
299 op.removeAttr(
"sv.namehint");
300 createWireForResult(op.getResult(0), namehint);
305 for (
auto result : op.getResults())
306 createWireForResult(result, StringAttr());
860 ArrayRef<WireLowering> wireLowerings) {
861 bool isProceduralRegion = block.getParentOp()->hasTrait<
ProceduralRegion>();
863 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings) {
865 ImplicitLocOpBuilder builder(hwWireOp.getLoc(), &block, declarePoint);
867 if (isProceduralRegion) {
869 LogicOp::create(builder, hwWireOp.getType(), hwWireOp.getNameAttr(),
870 hwWireOp.getInnerSymAttr());
873 hwWireOp.getNameAttr(),
874 hwWireOp.getInnerSymAttr());
878 auto defaultAttrNames = hwWireOp.getAttributeNames();
879 for (
auto namedAttr : hwWireOp->getAttrs())
880 if (!llvm::is_contained(defaultAttrNames, namedAttr.getName()))
881 decl.getDefiningOp()->setAttr(namedAttr.getName(),
882 namedAttr.getValue());
887 if (assignPoint != declarePoint)
888 builder.setInsertionPoint(&block, assignPoint);
889 if (isProceduralRegion)
890 BPAssignOp::create(builder, decl, hwWireOp.getInput());
898 if (assignPoint != declarePoint)
899 builder.setInsertionPointAfterValue(decl);
903 hwWireOp.replaceAllUsesWith(readOp.getResult());
906 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings)
917 for (
auto &op : block) {
919 for (
auto ®ion : op.getRegions()) {
930 SmallVector<WireLowering> wireLowerings;
938 bool isProceduralRegion = block.getParentOp()->hasTrait<
ProceduralRegion>();
942 DenseSet<Operation *> visitedAlwaysInlineOperations;
946 SmallVector<Operation *> debugOpsToMoveToEnd;
948 for (Block::iterator opIterator = block.begin(), e = block.end();
950 auto &op = *opIterator++;
952 if (!isa<CombDialect, SVDialect, HWDialect, ltl::LTLDialect,
953 verif::VerifDialect, debug::DebugDialect>(op.getDialect())) {
954 auto d = op.emitError() <<
"dialect \"" << op.getDialect()->getNamespace()
955 <<
"\" not supported for direct Verilog emission";
956 d.attachNote() <<
"ExportVerilog cannot emit this operation; it needs "
957 "to be lowered before running ExportVerilog";
962 if (isa<ltl::LTLDialect>(op.getDialect()))
966 if (isa<debug::DebugDialect>(op.getDialect())) {
967 debugOpsToMoveToEnd.push_back(&op);
976 if (
auto inst = dyn_cast<HWInstanceLike>(op)) {
985 if (
auto call = dyn_cast<mlir::CallOpInterface>(op))
990 if (isProceduralRegion && isa<LogicOp, RegOp>(op)) {
995 op.moveBefore(parentOp);
1002 isProceduralRegion) {
1008 if (!mlir::isMemoryEffectFree(&op)) {
1028 if (op.use_empty()) {
1034 if (visitedAlwaysInlineOperations.insert(&op).second)
1040 if (
auto aggregateConstantOp = dyn_cast<hw::AggregateConstantOp>(op);
1042 if (hw::StructType structType =
1043 type_dyn_cast<hw::StructType>(aggregateConstantOp.getType())) {
1045 SmallVector<Value> operands;
1046 ImplicitLocOpBuilder builder(op.getLoc(), op.getContext());
1047 builder.setInsertionPointAfter(&op);
1048 for (
auto [value, field] :
1049 llvm::zip(aggregateConstantOp.getFieldsAttr(),
1050 structType.getElements())) {
1051 if (
auto arrayAttr = dyn_cast<mlir::ArrayAttr>(value))
1052 operands.push_back(hw::AggregateConstantOp::create(
1053 builder, field.type, arrayAttr));
1056 builder, field.type, cast<mlir::IntegerAttr>(value)));
1061 aggregateConstantOp.getResult().replaceAllUsesWith(structCreate);
1063 opIterator = std::next(op.getIterator());
1070 if (
auto structCreateOp = dyn_cast<hw::StructCreateOp>(op);
1075 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1076 hw::StructType structType =
1077 cast<hw::StructType>(structCreateOp.getResult().getType());
1080 wireOp = LogicOp::create(builder, structType);
1084 for (
auto [input, field] :
1085 llvm::zip(structCreateOp.getInput(), structType.getElements())) {
1087 sv::StructFieldInOutOp::create(builder, wireOp, field.name);
1089 BPAssignOp::create(builder, target, input);
1095 llvm::make_early_inc_range(structCreateOp.getResult().getUses()))
1098 structCreateOp.erase();
1104 if (
auto arrayInjectOp = dyn_cast<hw::ArrayInjectOp>(op)) {
1106 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1109 decl = LogicOp::create(builder, arrayInjectOp.getType());
1111 decl = sv::RegOp::create(builder, arrayInjectOp.getType());
1112 for (
auto &use : llvm::make_early_inc_range(arrayInjectOp->getUses()))
1118 auto alwaysOp = sv::AlwaysCombOp::create(builder);
1119 builder.setInsertionPointToStart(alwaysOp.getBodyBlock());
1123 sv::BPAssignOp::create(builder, decl, arrayInjectOp.getInput());
1126 auto target = sv::ArrayIndexInOutOp::create(builder, decl,
1127 arrayInjectOp.getIndex());
1128 sv::BPAssignOp::create(builder, target, arrayInjectOp.getElement());
1130 arrayInjectOp.erase();
1138 IndexedPartSelectInOutOp, IndexedPartSelectOp>(&op)) {
1143 if (
auto maybeReadOp =
1145 if (isa_and_nonnull<sv::WireOp, LogicOp>(
1146 maybeReadOp.getInput().getDefiningOp())) {
1147 wireOp = maybeReadOp.getInput();
1148 readOp = maybeReadOp;
1153 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1155 auto type = op.getOperand(1).getType();
1156 const auto *name =
"_GEN_ARRAY_IDX";
1158 wireOp = LogicOp::create(builder, type, name);
1159 BPAssignOp::create(builder, wireOp, op.getOperand(1));
1166 op.setOperand(1, readOp);
1169 sv::addSVAttributes(wireOp.getDefiningOp(),
1170 SVAttributeAttr::get(wireOp.getContext(),
"keep",
1184 if (!isProceduralRegion ||
1195 if (isProceduralRegion)
1213 if (op.getNumOperands() != 2 && op.getNumResults() == 1 &&
1214 op.hasTrait<mlir::OpTrait::IsCommutative>() &&
1215 mlir::isMemoryEffectFree(&op) && op.getNumRegions() == 0 &&
1216 op.getNumSuccessors() == 0 &&
1217 llvm::all_of(op.getAttrs(), [](NamedAttribute attr) {
1218 return attr.getNameDialect() != nullptr ||
1219 attr.getName() ==
"twoState";
1222 SmallVector<Operation *> newOps;
1224 op.getResult(0).replaceAllUsesWith(result);
1228 if (!newOps.empty())
1229 opIterator = Block::iterator(newOps.front());
1234 if (
auto addOp = dyn_cast<comb::AddOp>(op)) {
1235 if (
auto cst = addOp.getOperand(1).getDefiningOp<
hw::ConstantOp>()) {
1236 assert(addOp.getNumOperands() == 2 &&
"commutative lowering is done");
1237 if (cst.getValue().isNegative()) {
1239 opIterator = Block::iterator(firstOp);
1247 if (
auto structExplodeOp = dyn_cast<hw::StructExplodeOp>(op)) {
1249 opIterator = Block::iterator(firstOp);
1261 auto debugBuilder = OpBuilder::atBlockEnd(&block);
1262 if (!block.empty() && block.back().mightHaveTrait<OpTrait::IsTerminator>())
1263 debugBuilder.setInsertionPoint(&block.back());
1264 for (
auto *op : debugOpsToMoveToEnd) {
1266 debugBuilder.insert(op);
1269 if (isProceduralRegion) {
1278 std::pair<Block *, Block::iterator> logicOpInsertionPoint =
1280 for (
auto &op : llvm::make_early_inc_range(block)) {
1281 if (
auto logic = dyn_cast<LogicOp>(&op)) {
1284 if (logicOpInsertionPoint.second == logic->getIterator()) {
1285 ++logicOpInsertionPoint.second;
1289 logic->moveBefore(logicOpInsertionPoint.first,
1290 logicOpInsertionPoint.second);
1300 SmallPtrSet<Operation *, 32> seenOperations;
1302 for (
auto &op : llvm::make_early_inc_range(block)) {
1305 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
1311 bool haveAnyOutOfOrderUses =
false;
1312 for (
auto *userOp : op.getUsers()) {
1315 while (&block != &userOp->getParentRegion()->front())
1316 userOp = userOp->getParentOp();
1318 if (seenOperations.count(userOp)) {
1319 haveAnyOutOfOrderUses =
true;
1325 seenOperations.insert(&op);
1328 if (!haveAnyOutOfOrderUses)
1334 op.moveBefore(&block.front());
1340 op.moveBefore(&block.front());
1346 if (
auto readInOut = dyn_cast<ReadInOutOp>(op)) {
1347 auto *def = readInOut.getInput().getDefiningOp();
1349 op.moveBefore(&block.front());
1350 def->moveBefore(&block.front());