@@ -1276,58 +1276,185 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
1276
1276
if (auto *fArg = dyn_cast<SILFunctionArgument>(markedAddress->getOperand ()))
1277
1277
isInOut |= fArg ->getArgumentConvention ().isInoutConvention ();
1278
1278
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
+
1279
1408
LLVM_DEBUG (llvm::dbgs () << " Performing single basic block checks!\n " );
1280
1409
for (auto &block : *fn) {
1281
1410
LLVM_DEBUG (llvm::dbgs ()
1282
1411
<< " Visiting block: bb" << block.getDebugID () << " \n " );
1283
1412
// Then walk backwards from the bottom of the block to the top, initializing
1284
1413
// 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 ;
1290
1414
auto *term = block.getTerminator ();
1291
1415
bool isExitBlock = term->isFunctionExiting ();
1416
+
1417
+ LocalDataflowState state;
1292
1418
for (auto &inst : llvm::reverse (block)) {
1293
1419
if (isExitBlock && isInOut && &inst == term) {
1294
1420
LLVM_DEBUG (llvm::dbgs ()
1295
1421
<< " Found inout term liveness user: " << inst);
1296
- livenessUp = { &inst, TypeTreeLeafTypeRange ( markedAddress)} ;
1422
+ state. initializeLivenessUp ( &inst, markedAddress);
1297
1423
continue ;
1298
1424
}
1299
1425
1300
1426
{
1301
1427
auto iter = addressUseState.takeInsts .find (&inst);
1302
1428
if (iter != addressUseState.takeInsts .end ()) {
1303
- SWIFT_DEFER { foundFirstNonLivenessUse = true ; };
1304
-
1305
1429
// If we are not yet tracking a "take up" or a "liveness up", then we
1306
1430
// can update our state. In those other two cases we emit an error
1307
1431
// diagnostic below.
1308
- if (!takeUp. first && !livenessUp. first ) {
1432
+ if (!state. isTrackingAnyState () ) {
1309
1433
LLVM_DEBUG (llvm::dbgs ()
1310
1434
<< " 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 );
1315
1436
continue ;
1316
1437
}
1317
1438
1318
1439
bool emittedSomeDiagnostic = false ;
1319
- if (takeUp. first ) {
1440
+ if (auto * takeUp = state. getTakeUp () ) {
1320
1441
LLVM_DEBUG (llvm::dbgs ()
1321
1442
<< " Found two takes, emitting error!\n " );
1322
- LLVM_DEBUG (llvm::dbgs () << " First take: " << *takeUp. first );
1443
+ LLVM_DEBUG (llvm::dbgs () << " First take: " << *takeUp);
1323
1444
LLVM_DEBUG (llvm::dbgs () << " Second take: " << inst);
1324
1445
diagnosticEmitter.emitAddressDiagnostic (
1325
- markedAddress, takeUp. first , &inst, true /* is consuming*/ );
1446
+ markedAddress, takeUp, &inst, true /* is consuming*/ );
1326
1447
emittedSomeDiagnostic = true ;
1327
1448
}
1328
1449
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) {
1331
1458
LLVM_DEBUG (llvm::dbgs ()
1332
1459
<< " Found liveness inout error: " << inst);
1333
1460
// Even though we emit a diagnostic for inout here, we actually
@@ -1338,13 +1465,14 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
1338
1465
// if we have multiple consumes along that path.
1339
1466
diagnosticEmitter.emitInOutEndOfFunctionDiagnostic (markedAddress,
1340
1467
&inst);
1341
- livenessUp = {nullptr , {}};
1342
- takeUp = {iter->first , iter->second };
1468
+ state.trackTakeForLivenessError (iter->first , iter->second );
1343
1469
} else {
1470
+ // Otherwise, we just emit a normal liveness error.
1344
1471
LLVM_DEBUG (llvm::dbgs () << " Found liveness error: " << inst);
1345
1472
diagnosticEmitter.emitAddressDiagnostic (
1346
- markedAddress, livenessUp. first , &inst,
1473
+ markedAddress, livenessUpInst , &inst,
1347
1474
false /* is not consuming*/ );
1475
+ state.trackTakeForLivenessError (iter->first , iter->second );
1348
1476
}
1349
1477
emittedSomeDiagnostic = true ;
1350
1478
}
@@ -1359,17 +1487,7 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
1359
1487
{
1360
1488
auto iter = addressUseState.livenessUses .find (&inst);
1361
1489
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 );
1373
1491
continue ;
1374
1492
}
1375
1493
}
@@ -1378,17 +1496,7 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
1378
1496
{
1379
1497
auto iter = addressUseState.borrows .find (&inst);
1380
1498
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 );
1392
1500
continue ;
1393
1501
}
1394
1502
}
@@ -1397,78 +1505,64 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
1397
1505
{
1398
1506
auto iter = addressUseState.initInsts .find (&inst);
1399
1507
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 );
1412
1509
continue ;
1413
1510
}
1414
1511
}
1415
1512
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.
1421
1525
{
1422
1526
auto iter = addressUseState.reinitInsts .find (&inst);
1423
1527
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 );
1435
1529
continue ;
1436
1530
}
1437
1531
}
1438
1532
}
1439
1533
1440
1534
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 () );
1444
1538
} else {
1445
1539
LLVM_DEBUG (llvm::dbgs () << " No Init Down!\n " );
1446
1540
}
1447
1541
1448
1542
// 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);
1451
1545
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 () );
1454
1548
} else {
1455
1549
LLVM_DEBUG (llvm::dbgs () << " No Take Up!\n " );
1456
1550
}
1457
1551
1458
- if (livenessUp. first ) {
1552
+ if (auto *liveness = state. getLivenessUp () ) {
1459
1553
// This try_emplace fail if we already above initialized blockToState
1460
1554
// above in the previous if block.
1461
1555
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 () );
1465
1559
} else {
1466
1560
LLVM_DEBUG (llvm::dbgs () << " No Liveness Up!\n " );
1467
1561
}
1468
1562
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 () );
1472
1566
// We only emplace if we didn't already have a takeUp above. In such a
1473
1567
// case, the try_emplace fails.
1474
1568
blockToState.try_emplace (&block, BlockState (nullptr , false ));
0 commit comments