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());
847 ArrayRef<WireLowering> wireLowerings) {
848 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
850 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings) {
852 ImplicitLocOpBuilder builder(hwWireOp.getLoc(), &block, declarePoint);
854 if (isProceduralRegion) {
856 LogicOp::create(builder, hwWireOp.getType(), hwWireOp.getNameAttr(),
857 hwWireOp.getInnerSymAttr());
860 hwWireOp.getNameAttr(),
861 hwWireOp.getInnerSymAttr());
865 auto defaultAttrNames = hwWireOp.getAttributeNames();
866 for (
auto namedAttr : hwWireOp->getAttrs())
867 if (!llvm::is_contained(defaultAttrNames, namedAttr.getName()))
868 decl.getDefiningOp()->setAttr(namedAttr.getName(),
869 namedAttr.getValue());
874 if (assignPoint != declarePoint)
875 builder.setInsertionPoint(&block, assignPoint);
876 if (isProceduralRegion)
877 BPAssignOp::create(builder, decl, hwWireOp.getInput());
885 if (assignPoint != declarePoint)
886 builder.setInsertionPointAfterValue(decl);
890 hwWireOp.replaceAllUsesWith(readOp.getResult());
893 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings)
904 for (
auto &op : block) {
906 for (
auto ®ion : op.getRegions()) {
917 SmallVector<WireLowering> wireLowerings;
925 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
929 DenseSet<Operation *> visitedAlwaysInlineOperations;
933 SmallVector<Operation *> debugOpsToMoveToEnd;
935 for (Block::iterator opIterator = block.begin(), e = block.end();
937 auto &op = *opIterator++;
939 if (!isa<CombDialect, SVDialect, HWDialect, ltl::LTLDialect,
940 verif::VerifDialect, debug::DebugDialect>(op.getDialect())) {
941 auto d = op.emitError() <<
"dialect \"" << op.getDialect()->getNamespace()
942 <<
"\" not supported for direct Verilog emission";
943 d.attachNote() <<
"ExportVerilog cannot emit this operation; it needs "
944 "to be lowered before running ExportVerilog";
949 if (isa<ltl::LTLDialect>(op.getDialect()))
953 if (isa<debug::DebugDialect>(op.getDialect())) {
954 debugOpsToMoveToEnd.push_back(&op);
963 if (
auto inst = dyn_cast<HWInstanceLike>(op)) {
972 if (
auto call = dyn_cast<mlir::CallOpInterface>(op))
977 if (isProceduralRegion && isa<LogicOp, RegOp>(op)) {
982 op.moveBefore(parentOp);
989 isProceduralRegion) {
995 if (!mlir::isMemoryEffectFree(&op)) {
1015 if (op.use_empty()) {
1021 if (visitedAlwaysInlineOperations.insert(&op).second)
1027 if (
auto aggregateConstantOp = dyn_cast<hw::AggregateConstantOp>(op);
1029 if (hw::StructType structType =
1030 type_dyn_cast<hw::StructType>(aggregateConstantOp.getType())) {
1032 SmallVector<Value> operands;
1033 ImplicitLocOpBuilder builder(op.getLoc(), op.getContext());
1034 builder.setInsertionPointAfter(&op);
1035 for (
auto [value, field] :
1036 llvm::zip(aggregateConstantOp.getFieldsAttr(),
1037 structType.getElements())) {
1038 if (
auto arrayAttr = dyn_cast<mlir::ArrayAttr>(value))
1039 operands.push_back(hw::AggregateConstantOp::create(
1040 builder, field.type, arrayAttr));
1043 builder, field.type, cast<mlir::IntegerAttr>(value)));
1048 aggregateConstantOp.getResult().replaceAllUsesWith(structCreate);
1050 opIterator = std::next(op.getIterator());
1057 if (
auto structCreateOp = dyn_cast<hw::StructCreateOp>(op);
1062 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1063 hw::StructType structType =
1064 cast<hw::StructType>(structCreateOp.getResult().getType());
1065 bool procedural = op.getParentOp()->hasTrait<ProceduralRegion>();
1067 wireOp = LogicOp::create(builder, structType);
1071 for (
auto [input, field] :
1072 llvm::zip(structCreateOp.getInput(), structType.getElements())) {
1074 sv::StructFieldInOutOp::create(builder, wireOp, field.name);
1076 BPAssignOp::create(builder, target, input);
1082 llvm::make_early_inc_range(structCreateOp.getResult().getUses()))
1085 structCreateOp.erase();
1091 if (
auto arrayInjectOp = dyn_cast<hw::ArrayInjectOp>(op)) {
1092 bool procedural = op.getParentOp()->hasTrait<ProceduralRegion>();
1093 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1096 decl = LogicOp::create(builder, arrayInjectOp.getType());
1098 decl = sv::RegOp::create(builder, arrayInjectOp.getType());
1099 for (
auto &use : llvm::make_early_inc_range(arrayInjectOp->getUses()))
1105 auto alwaysOp = sv::AlwaysCombOp::create(builder);
1106 builder.setInsertionPointToStart(alwaysOp.getBodyBlock());
1110 sv::BPAssignOp::create(builder, decl, arrayInjectOp.getInput());
1113 auto target = sv::ArrayIndexInOutOp::create(builder, decl,
1114 arrayInjectOp.getIndex());
1115 sv::BPAssignOp::create(builder, target, arrayInjectOp.getElement());
1117 arrayInjectOp.erase();
1125 IndexedPartSelectInOutOp, IndexedPartSelectOp>(&op)) {
1130 if (
auto maybeReadOp =
1132 if (isa_and_nonnull<sv::WireOp, LogicOp>(
1133 maybeReadOp.getInput().getDefiningOp())) {
1134 wireOp = maybeReadOp.getInput();
1135 readOp = maybeReadOp;
1140 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1142 auto type = op.getOperand(1).getType();
1143 const auto *name =
"_GEN_ARRAY_IDX";
1144 if (op.getParentOp()->hasTrait<ProceduralRegion>()) {
1145 wireOp = LogicOp::create(builder, type, name);
1146 BPAssignOp::create(builder, wireOp, op.getOperand(1));
1153 op.setOperand(1, readOp);
1156 sv::addSVAttributes(wireOp.getDefiningOp(),
1157 SVAttributeAttr::get(wireOp.getContext(),
"keep",
1171 if (!isProceduralRegion ||
1175 if (!op.getParentOp()->hasTrait<ProceduralRegion>())
1182 if (isProceduralRegion)
1200 if (op.getNumOperands() != 2 && op.getNumResults() == 1 &&
1201 op.hasTrait<mlir::OpTrait::IsCommutative>() &&
1202 mlir::isMemoryEffectFree(&op) && op.getNumRegions() == 0 &&
1203 op.getNumSuccessors() == 0 &&
1204 llvm::all_of(op.getAttrs(), [](NamedAttribute attr) {
1205 return attr.getNameDialect() != nullptr ||
1206 attr.getName() ==
"twoState";
1209 SmallVector<Operation *> newOps;
1211 op.getResult(0).replaceAllUsesWith(result);
1215 if (!newOps.empty())
1216 opIterator = Block::iterator(newOps.front());
1221 if (
auto addOp = dyn_cast<comb::AddOp>(op)) {
1222 if (
auto cst = addOp.getOperand(1).getDefiningOp<
hw::ConstantOp>()) {
1223 assert(addOp.getNumOperands() == 2 &&
"commutative lowering is done");
1224 if (cst.getValue().isNegative()) {
1226 opIterator = Block::iterator(firstOp);
1234 if (
auto structExplodeOp = dyn_cast<hw::StructExplodeOp>(op)) {
1236 opIterator = Block::iterator(firstOp);
1248 auto debugBuilder = OpBuilder::atBlockEnd(&block);
1249 if (!block.empty() && block.back().mightHaveTrait<OpTrait::IsTerminator>())
1250 debugBuilder.setInsertionPoint(&block.back());
1251 for (
auto *op : debugOpsToMoveToEnd) {
1253 debugBuilder.insert(op);
1256 if (isProceduralRegion) {
1265 std::pair<Block *, Block::iterator> logicOpInsertionPoint =
1267 for (
auto &op : llvm::make_early_inc_range(block)) {
1268 if (
auto logic = dyn_cast<LogicOp>(&op)) {
1271 if (logicOpInsertionPoint.second == logic->getIterator()) {
1272 ++logicOpInsertionPoint.second;
1276 logic->moveBefore(logicOpInsertionPoint.first,
1277 logicOpInsertionPoint.second);
1287 SmallPtrSet<Operation *, 32> seenOperations;
1289 for (
auto &op : llvm::make_early_inc_range(block)) {
1292 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
1298 bool haveAnyOutOfOrderUses =
false;
1299 for (
auto *userOp : op.getUsers()) {
1302 while (&block != &userOp->getParentRegion()->front())
1303 userOp = userOp->getParentOp();
1305 if (seenOperations.count(userOp)) {
1306 haveAnyOutOfOrderUses =
true;
1312 seenOperations.insert(&op);
1315 if (!haveAnyOutOfOrderUses)
1321 op.moveBefore(&block.front());
1327 op.moveBefore(&block.front());
1333 if (
auto readInOut = dyn_cast<ReadInOutOp>(op)) {
1334 auto *def = readInOut.getInput().getDefiningOp();
1336 op.moveBefore(&block.front());
1337 def->moveBefore(&block.front());