Skip to content

Commit b614dd3

Browse files
committed
[move-only] Clean up the local per-block dataflow computation to use a state structure with helpers.
This just allows me to sink busy code into helpers on the state making the implementation easier to understand. It also gave me a good place to add comments that will make it clearer what we are actually doing. NFCI.
1 parent df81263 commit b614dd3

File tree

1 file changed

+179
-85
lines changed

1 file changed

+179
-85
lines changed

lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp

Lines changed: 179 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,58 +1276,185 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
12761276
if (auto *fArg = dyn_cast<SILFunctionArgument>(markedAddress->getOperand()))
12771277
isInOut |= fArg->getArgumentConvention().isInoutConvention();
12781278

1279+
/// A per block state structure that we use as we walk the block. We do not
1280+
/// persist these. We use this data structure so we can simplify the code
1281+
/// below using helper functions.
1282+
struct LocalDataflowState {
1283+
// As we walk the block, this is set to the state associated with the last
1284+
// take we have seen. If this is non-null when we reach the top of the
1285+
// block, this is the take that is propagated upwards out of the block.
1286+
InstOptionalLeafTypePair takeUp = {nullptr, {}};
1287+
1288+
/// This is the last liveness providing instruction that we saw since the
1289+
/// last init or the end of the block. If this is set when we reach the top
1290+
/// of the block, this is the liveness instruction that we use for the
1291+
///
1292+
/// This is reset when we track a new init since we want to make sure that
1293+
/// we do not hit any liveness errors from takes that may be earlier than
1294+
/// the init in the block.
1295+
InstOptionalLeafTypePair livenessUp = {nullptr, {}};
1296+
1297+
// MARK: Local Dataflow Kill State
1298+
1299+
/// If we have not yet seen a take or an init in this block, this is set to
1300+
/// false. Once we have seen one of those, we set this to true. We use this
1301+
/// to generate our kill set for the block and do it only once.
1302+
bool foundFirstNonLivenessUse = false;
1303+
1304+
/// This is the first take that we see as we walk up the block if we haven't
1305+
/// yet seen an init. The reason why we track this is that if we have
1306+
/// liveness in a successor block, we need to error on this take.
1307+
InstOptionalLeafTypePair firstTake = {nullptr, {}};
1308+
1309+
/// This is the first init that we see in the block if we have not seen a
1310+
/// different init. If this is set, then we know that we need to kill the
1311+
/// variable in this block. It also lets us know that any takes earlier in
1312+
/// the block that we see can not have a liveness error due to liveness in
1313+
/// earlier blocks.
1314+
InstOptionalLeafTypePair firstInit = {nullptr, {}};
1315+
1316+
void initializeLivenessUp(SILInstruction *inst, SILValue address) {
1317+
livenessUp = {inst, TypeTreeLeafTypeRange(address)};
1318+
}
1319+
1320+
/// If we are not already tracking liveness, begin tracking liveness for
1321+
/// this type tree range. If we are already tracking liveness, use the later
1322+
/// instruction.
1323+
void trackLivenessUp(SILInstruction *inst, TypeTreeLeafTypeRange range) {
1324+
if (livenessUp.first) {
1325+
LLVM_DEBUG(
1326+
llvm::dbgs()
1327+
<< " Found liveness use! Already tracking liveness. Inst: "
1328+
<< inst);
1329+
return;
1330+
}
1331+
1332+
LLVM_DEBUG(llvm::dbgs()
1333+
<< " Found liveness use! Propagating liveness. Inst: "
1334+
<< inst);
1335+
livenessUp = {inst, range};
1336+
}
1337+
1338+
void trackTake(SILInstruction *inst, TypeTreeLeafTypeRange range) {
1339+
// If we haven't found a first init use, we need to track a first take
1340+
// since we need to make sure that we do not have any liveness issues
1341+
// coming up from a successor block.
1342+
if (!foundFirstNonLivenessUse)
1343+
firstTake = {inst, range};
1344+
takeUp = {inst, range};
1345+
foundFirstNonLivenessUse = true;
1346+
}
1347+
1348+
void trackInit(SILInstruction *inst, TypeTreeLeafTypeRange range) {
1349+
// Since we are tracking a new variable, reset takeUp and livenessUp.
1350+
takeUp = {nullptr, {}};
1351+
livenessUp = {nullptr, {}};
1352+
1353+
// Then see if we need to set firstInit so we setup that this block kills
1354+
// the given range.
1355+
if (!foundFirstNonLivenessUse) {
1356+
LLVM_DEBUG(llvm::dbgs() << " Updated with new init: " << inst);
1357+
firstInit = {inst, range};
1358+
} else {
1359+
LLVM_DEBUG(
1360+
llvm::dbgs()
1361+
<< " Found init! Already have first non liveness use. Inst: "
1362+
<< inst);
1363+
}
1364+
foundFirstNonLivenessUse = true;
1365+
}
1366+
1367+
/// Track a take given that we emitted a liveness error.
1368+
///
1369+
/// In this case, we reset livenessUp and track the take. We do this to
1370+
/// ensure that earlier errors are on the consuming take rather than on the
1371+
/// liveness use. This is especially important when emitting "inout not
1372+
/// reintialized" errors since we only want one of those to be emitted.
1373+
void trackTakeForLivenessError(SILInstruction *take,
1374+
TypeTreeLeafTypeRange range) {
1375+
livenessUp = {nullptr, {}};
1376+
trackTake(take, range);
1377+
}
1378+
1379+
bool isTrackingAnyState() const { return takeUp.first || livenessUp.first; }
1380+
1381+
bool hasLivenessUp() const { return livenessUp.first; }
1382+
1383+
SILInstruction *getLivenessUp() const { return livenessUp.first; }
1384+
1385+
TypeTreeLeafTypeRange getLivenessUpTypeRange() const {
1386+
return *livenessUp.second;
1387+
}
1388+
1389+
SILInstruction *getFirstInit() const { return firstInit.first; }
1390+
1391+
TypeTreeLeafTypeRange getFirstInitTypeRange() const {
1392+
return *firstInit.second;
1393+
}
1394+
1395+
SILInstruction *getFirstTake() const { return firstTake.first; }
1396+
1397+
TypeTreeLeafTypeRange getFirstTakeTypeRange() const {
1398+
return *firstTake.second;
1399+
}
1400+
1401+
bool hasTakeUp() const { return takeUp.first; }
1402+
1403+
SILInstruction *getTakeUp() const { return takeUp.first; }
1404+
1405+
TypeTreeLeafTypeRange getTakeUpTypeRange() const { return *takeUp.second; }
1406+
};
1407+
12791408
LLVM_DEBUG(llvm::dbgs() << "Performing single basic block checks!\n");
12801409
for (auto &block : *fn) {
12811410
LLVM_DEBUG(llvm::dbgs()
12821411
<< "Visiting block: bb" << block.getDebugID() << "\n");
12831412
// Then walk backwards from the bottom of the block to the top, initializing
12841413
// its state, handling any completely in block diagnostics.
1285-
InstOptionalLeafTypePair takeUp = {nullptr, {}};
1286-
InstOptionalLeafTypePair firstTake = {nullptr, {}};
1287-
InstOptionalLeafTypePair firstInit = {nullptr, {}};
1288-
InstOptionalLeafTypePair livenessUp = {nullptr, {}};
1289-
bool foundFirstNonLivenessUse = false;
12901414
auto *term = block.getTerminator();
12911415
bool isExitBlock = term->isFunctionExiting();
1416+
1417+
LocalDataflowState state;
12921418
for (auto &inst : llvm::reverse(block)) {
12931419
if (isExitBlock && isInOut && &inst == term) {
12941420
LLVM_DEBUG(llvm::dbgs()
12951421
<< " Found inout term liveness user: " << inst);
1296-
livenessUp = {&inst, TypeTreeLeafTypeRange(markedAddress)};
1422+
state.initializeLivenessUp(&inst, markedAddress);
12971423
continue;
12981424
}
12991425

13001426
{
13011427
auto iter = addressUseState.takeInsts.find(&inst);
13021428
if (iter != addressUseState.takeInsts.end()) {
1303-
SWIFT_DEFER { foundFirstNonLivenessUse = true; };
1304-
13051429
// If we are not yet tracking a "take up" or a "liveness up", then we
13061430
// can update our state. In those other two cases we emit an error
13071431
// diagnostic below.
1308-
if (!takeUp.first && !livenessUp.first) {
1432+
if (!state.isTrackingAnyState()) {
13091433
LLVM_DEBUG(llvm::dbgs()
13101434
<< " Tracking new take up: " << *iter->first);
1311-
if (!foundFirstNonLivenessUse) {
1312-
firstTake = {iter->first, iter->second};
1313-
}
1314-
takeUp = {iter->first, iter->second};
1435+
state.trackTake(iter->first, iter->second);
13151436
continue;
13161437
}
13171438

13181439
bool emittedSomeDiagnostic = false;
1319-
if (takeUp.first) {
1440+
if (auto *takeUp = state.getTakeUp()) {
13201441
LLVM_DEBUG(llvm::dbgs()
13211442
<< " Found two takes, emitting error!\n");
1322-
LLVM_DEBUG(llvm::dbgs() << " First take: " << *takeUp.first);
1443+
LLVM_DEBUG(llvm::dbgs() << " First take: " << *takeUp);
13231444
LLVM_DEBUG(llvm::dbgs() << " Second take: " << inst);
13241445
diagnosticEmitter.emitAddressDiagnostic(
1325-
markedAddress, takeUp.first, &inst, true /*is consuming*/);
1446+
markedAddress, takeUp, &inst, true /*is consuming*/);
13261447
emittedSomeDiagnostic = true;
13271448
}
13281449

1329-
if (livenessUp.first) {
1330-
if (livenessUp.first == term && isInOut) {
1450+
// If we found a liveness inst, we are going to emit an error since we
1451+
// have a use after free.
1452+
if (auto *livenessUpInst = state.getLivenessUp()) {
1453+
// If we are tracking state for an inout and our liveness inst is a
1454+
// function exiting instruction, we want to emit a special
1455+
// diagnostic error saying that the user has not reinitialized inout
1456+
// along a path to the end of the function.
1457+
if (livenessUpInst == term && isInOut) {
13311458
LLVM_DEBUG(llvm::dbgs()
13321459
<< " Found liveness inout error: " << inst);
13331460
// Even though we emit a diagnostic for inout here, we actually
@@ -1338,13 +1465,14 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
13381465
// if we have multiple consumes along that path.
13391466
diagnosticEmitter.emitInOutEndOfFunctionDiagnostic(markedAddress,
13401467
&inst);
1341-
livenessUp = {nullptr, {}};
1342-
takeUp = {iter->first, iter->second};
1468+
state.trackTakeForLivenessError(iter->first, iter->second);
13431469
} else {
1470+
// Otherwise, we just emit a normal liveness error.
13441471
LLVM_DEBUG(llvm::dbgs() << " Found liveness error: " << inst);
13451472
diagnosticEmitter.emitAddressDiagnostic(
1346-
markedAddress, livenessUp.first, &inst,
1473+
markedAddress, livenessUpInst, &inst,
13471474
false /*is not consuming*/);
1475+
state.trackTakeForLivenessError(iter->first, iter->second);
13481476
}
13491477
emittedSomeDiagnostic = true;
13501478
}
@@ -1359,17 +1487,7 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
13591487
{
13601488
auto iter = addressUseState.livenessUses.find(&inst);
13611489
if (iter != addressUseState.livenessUses.end()) {
1362-
if (!livenessUp.first) {
1363-
LLVM_DEBUG(llvm::dbgs()
1364-
<< " Found liveness use! Propagating liveness. Inst: "
1365-
<< inst);
1366-
livenessUp = {iter->first, iter->second};
1367-
} else {
1368-
LLVM_DEBUG(
1369-
llvm::dbgs()
1370-
<< " Found liveness use! Already tracking liveness. Inst: "
1371-
<< inst);
1372-
}
1490+
state.trackLivenessUp(iter->first, iter->second);
13731491
continue;
13741492
}
13751493
}
@@ -1378,17 +1496,7 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
13781496
{
13791497
auto iter = addressUseState.borrows.find(&inst);
13801498
if (iter != addressUseState.borrows.end()) {
1381-
if (!livenessUp.first) {
1382-
LLVM_DEBUG(llvm::dbgs()
1383-
<< " Found borrowed use! Propagating liveness. Inst: "
1384-
<< inst);
1385-
livenessUp = {iter->first, iter->second};
1386-
} else {
1387-
LLVM_DEBUG(
1388-
llvm::dbgs()
1389-
<< " Found borrowed use! Already tracking liveness. Inst: "
1390-
<< inst);
1391-
}
1499+
state.trackLivenessUp(iter->first, iter->second);
13921500
continue;
13931501
}
13941502
}
@@ -1397,78 +1505,64 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
13971505
{
13981506
auto iter = addressUseState.initInsts.find(&inst);
13991507
if (iter != addressUseState.initInsts.end()) {
1400-
takeUp = {nullptr, {}};
1401-
livenessUp = {nullptr, {}};
1402-
if (!foundFirstNonLivenessUse) {
1403-
LLVM_DEBUG(llvm::dbgs() << " Updated with new init: " << inst);
1404-
firstInit = {iter->first, iter->second};
1405-
} else {
1406-
LLVM_DEBUG(
1407-
llvm::dbgs()
1408-
<< " Found init! Already have first non liveness use. Inst: "
1409-
<< inst);
1410-
}
1411-
foundFirstNonLivenessUse = true;
1508+
state.trackInit(iter->first, iter->second);
14121509
continue;
14131510
}
14141511
}
14151512

1416-
// We treat the reinit a reinit as only a kill and not an additional
1417-
// take. This is because, we are treating their destroy as a last use like
1418-
// destroy_addr. The hope is that as part of doing fixups, we can just
1419-
// change this to a store [init] if there isn't an earlier reachable take
1420-
// use, like we do with destroy_addr.
1513+
// We treat reinits in the following way:
1514+
//
1515+
// 1. For takes that we want to treat like destroy_addr (e.x.: store
1516+
// [assign] and copy_addr [!init], we treat the reinit as only a kill and
1517+
// not an additional take. This is because, we are treating their destroy
1518+
// as a last use like destroy_addr. The hope is that as part of doing
1519+
// fixups, we can just change this to a store [init] if there isn't an
1520+
// earlier reachable take use, like we do with destroy_addr.
1521+
//
1522+
// 2. If we are unable to convert the reinit, it is a reinit like a
1523+
// YieldInst or an ApplySite. In that case, we treat this as an actual
1524+
// kill/take and reinit with a new value.
14211525
{
14221526
auto iter = addressUseState.reinitInsts.find(&inst);
14231527
if (iter != addressUseState.reinitInsts.end()) {
1424-
takeUp = {nullptr, {}};
1425-
livenessUp = {nullptr, {}};
1426-
if (!foundFirstNonLivenessUse) {
1427-
LLVM_DEBUG(llvm::dbgs() << " Updated with new reinit: " << inst);
1428-
firstInit = {iter->first, iter->second};
1429-
} else {
1430-
LLVM_DEBUG(llvm::dbgs() << " Found new reinit, but already "
1431-
"tracking a liveness init. Inst: "
1432-
<< inst);
1433-
}
1434-
foundFirstNonLivenessUse = true;
1528+
state.trackInit(iter->first, iter->second);
14351529
continue;
14361530
}
14371531
}
14381532
}
14391533

14401534
LLVM_DEBUG(llvm::dbgs() << "End of block. Dumping Results!\n");
1441-
if (firstInit.first) {
1442-
LLVM_DEBUG(llvm::dbgs() << " First Init Down: " << *firstInit.first);
1443-
initDownInsts.emplace_back(firstInit.first, *firstInit.second);
1535+
if (auto *firstInit = state.getFirstInit()) {
1536+
LLVM_DEBUG(llvm::dbgs() << " First Init Down: " << *firstInit);
1537+
initDownInsts.emplace_back(firstInit, state.getFirstInitTypeRange());
14441538
} else {
14451539
LLVM_DEBUG(llvm::dbgs() << " No Init Down!\n");
14461540
}
14471541

14481542
// At this point we want to begin mapping blocks to "first" users.
1449-
if (takeUp.first) {
1450-
LLVM_DEBUG(llvm::dbgs() << "Take Up: " << *takeUp.first);
1543+
if (auto *takeUp = state.getTakeUp()) {
1544+
LLVM_DEBUG(llvm::dbgs() << "Take Up: " << takeUp);
14511545
blockToState.try_emplace(&block,
1452-
BlockState(takeUp.first, firstInit.first));
1453-
takeUpInsts.emplace_back(takeUp.first, *takeUp.second);
1546+
BlockState(takeUp, state.getFirstInit()));
1547+
takeUpInsts.emplace_back(takeUp, state.getTakeUpTypeRange());
14541548
} else {
14551549
LLVM_DEBUG(llvm::dbgs() << " No Take Up!\n");
14561550
}
14571551

1458-
if (livenessUp.first) {
1552+
if (auto *liveness = state.getLivenessUp()) {
14591553
// This try_emplace fail if we already above initialized blockToState
14601554
// above in the previous if block.
14611555
blockToState.try_emplace(&block,
1462-
BlockState(livenessUp.first, firstInit.first));
1463-
LLVM_DEBUG(llvm::dbgs() << "Liveness Up: " << *livenessUp.first);
1464-
livenessUpInsts.emplace_back(livenessUp.first, *livenessUp.second);
1556+
BlockState(liveness, state.getFirstInit()));
1557+
LLVM_DEBUG(llvm::dbgs() << "Liveness Up: " << *liveness);
1558+
livenessUpInsts.emplace_back(liveness, state.getLivenessUpTypeRange());
14651559
} else {
14661560
LLVM_DEBUG(llvm::dbgs() << " No Liveness Up!\n");
14671561
}
14681562

1469-
if (firstTake.first) {
1470-
LLVM_DEBUG(llvm::dbgs() << " First Take Down: " << *firstTake.first);
1471-
takeDownInsts.emplace_back(firstTake.first, *firstTake.second);
1563+
if (auto *firstTakeInst = state.getFirstTake()) {
1564+
LLVM_DEBUG(llvm::dbgs() << " First Take Down: " << *firstTakeInst);
1565+
takeDownInsts.emplace_back(firstTakeInst, state.getFirstTakeTypeRange());
14721566
// We only emplace if we didn't already have a takeUp above. In such a
14731567
// case, the try_emplace fails.
14741568
blockToState.try_emplace(&block, BlockState(nullptr, false));

0 commit comments

Comments
 (0)