@@ -287,108 +287,9 @@ llvm::cl::opt<bool> DisableMoveOnlyAddressCheckerLifetimeExtension(
287
287
" move-only values." ));
288
288
289
289
// ===----------------------------------------------------------------------===//
290
- // MARK: Memory Utilities
290
+ // MARK: Utilities
291
291
// ===----------------------------------------------------------------------===//
292
292
293
- static bool memInstMustInitialize (Operand *memOper) {
294
- SILValue address = memOper->get ();
295
-
296
- SILInstruction *memInst = memOper->getUser ();
297
-
298
- switch (memInst->getKind ()) {
299
- default :
300
- return false ;
301
-
302
- case SILInstructionKind::CopyAddrInst: {
303
- auto *CAI = cast<CopyAddrInst>(memInst);
304
- return CAI->getDest () == address && CAI->isInitializationOfDest ();
305
- }
306
- case SILInstructionKind::ExplicitCopyAddrInst: {
307
- auto *CAI = cast<ExplicitCopyAddrInst>(memInst);
308
- return CAI->getDest () == address && CAI->isInitializationOfDest ();
309
- }
310
- case SILInstructionKind::MarkUnresolvedMoveAddrInst: {
311
- return cast<MarkUnresolvedMoveAddrInst>(memInst)->getDest () == address;
312
- }
313
- case SILInstructionKind::InitExistentialAddrInst:
314
- case SILInstructionKind::InitEnumDataAddrInst:
315
- case SILInstructionKind::InjectEnumAddrInst:
316
- return true ;
317
-
318
- case SILInstructionKind::BeginApplyInst:
319
- case SILInstructionKind::TryApplyInst:
320
- case SILInstructionKind::ApplyInst: {
321
- FullApplySite applySite (memInst);
322
- return applySite.isIndirectResultOperand (*memOper);
323
- }
324
- case SILInstructionKind::StoreInst: {
325
- auto qual = cast<StoreInst>(memInst)->getOwnershipQualifier ();
326
- return qual == StoreOwnershipQualifier::Init ||
327
- qual == StoreOwnershipQualifier::Trivial;
328
- }
329
-
330
- #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE (Name, ...) \
331
- case SILInstructionKind::Store##Name##Inst: \
332
- return cast<Store##Name##Inst>(memInst)->isInitializationOfDest ();
333
- #include " swift/AST/ReferenceStorage.def"
334
- }
335
- }
336
-
337
- static bool memInstMustReinitialize (Operand *memOper) {
338
- SILValue address = memOper->get ();
339
-
340
- SILInstruction *memInst = memOper->getUser ();
341
-
342
- switch (memInst->getKind ()) {
343
- default :
344
- return false ;
345
-
346
- case SILInstructionKind::CopyAddrInst: {
347
- auto *CAI = cast<CopyAddrInst>(memInst);
348
- return CAI->getDest () == address && !CAI->isInitializationOfDest ();
349
- }
350
- case SILInstructionKind::ExplicitCopyAddrInst: {
351
- auto *CAI = cast<ExplicitCopyAddrInst>(memInst);
352
- return CAI->getDest () == address && !CAI->isInitializationOfDest ();
353
- }
354
- case SILInstructionKind::YieldInst: {
355
- auto *yield = cast<YieldInst>(memInst);
356
- return yield->getYieldInfoForOperand (*memOper).isIndirectInOut ();
357
- }
358
- case SILInstructionKind::BeginApplyInst:
359
- case SILInstructionKind::TryApplyInst:
360
- case SILInstructionKind::ApplyInst: {
361
- FullApplySite applySite (memInst);
362
- return applySite.getArgumentOperandConvention (*memOper).isInoutConvention ();
363
- }
364
- case SILInstructionKind::StoreInst:
365
- return cast<StoreInst>(memInst)->getOwnershipQualifier () ==
366
- StoreOwnershipQualifier::Assign;
367
-
368
- #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE (Name, ...) \
369
- case SILInstructionKind::Store##Name##Inst: \
370
- return !cast<Store##Name##Inst>(memInst)->isInitializationOfDest ();
371
- #include " swift/AST/ReferenceStorage.def"
372
- }
373
- }
374
-
375
- // / Is this a reinit instruction that we know how to convert into its init form.
376
- static bool isReinitToInitConvertibleInst (SILInstruction *memInst) {
377
- switch (memInst->getKind ()) {
378
- default :
379
- return false ;
380
-
381
- case SILInstructionKind::CopyAddrInst: {
382
- auto *cai = cast<CopyAddrInst>(memInst);
383
- return !cai->isInitializationOfDest ();
384
- }
385
- case SILInstructionKind::StoreInst: {
386
- auto *si = cast<StoreInst>(memInst);
387
- return si->getOwnershipQualifier () == StoreOwnershipQualifier::Assign;
388
- }
389
- }
390
- }
391
-
392
293
static void insertDebugValueBefore (SILInstruction *insertPt,
393
294
DebugVarCarryingInst debugVar,
394
295
llvm::function_ref<SILValue ()> operand) {
@@ -432,47 +333,20 @@ static void convertMemoryReinitToInitForm(SILInstruction *memInst,
432
333
[&]{ return debugVar.getOperandForDebugValueClone (); });
433
334
}
434
335
435
- static bool memInstMustConsume (Operand *memOper) {
436
- SILValue address = memOper->get ();
437
-
438
- SILInstruction *memInst = memOper->getUser ();
439
-
440
- // FIXME: drop_deinit must be handled here!
336
+ // / Is this a reinit instruction that we know how to convert into its init form.
337
+ static bool isReinitToInitConvertibleInst (SILInstruction *memInst) {
441
338
switch (memInst->getKind ()) {
442
339
default :
443
340
return false ;
444
341
445
342
case SILInstructionKind::CopyAddrInst: {
446
- auto *CAI = cast<CopyAddrInst>(memInst);
447
- return (CAI->getSrc () == address && CAI->isTakeOfSrc ()) ||
448
- (CAI->getDest () == address && !CAI->isInitializationOfDest ());
449
- }
450
- case SILInstructionKind::ExplicitCopyAddrInst: {
451
- auto *CAI = cast<ExplicitCopyAddrInst>(memInst);
452
- return (CAI->getSrc () == address && CAI->isTakeOfSrc ()) ||
453
- (CAI->getDest () == address && !CAI->isInitializationOfDest ());
454
- }
455
- case SILInstructionKind::BeginApplyInst:
456
- case SILInstructionKind::TryApplyInst:
457
- case SILInstructionKind::ApplyInst: {
458
- FullApplySite applySite (memInst);
459
- return applySite.getArgumentOperandConvention (*memOper).isOwnedConvention ();
460
- }
461
- case SILInstructionKind::PartialApplyInst: {
462
- // If we are on the stack or have an inout convention, we do not
463
- // consume. Otherwise, we do.
464
- auto *pai = cast<PartialApplyInst>(memInst);
465
- if (pai->isOnStack ())
466
- return false ;
467
- ApplySite applySite (pai);
468
- auto convention = applySite.getArgumentConvention (*memOper);
469
- return !convention.isInoutConvention ();
343
+ auto *cai = cast<CopyAddrInst>(memInst);
344
+ return !cai->isInitializationOfDest ();
345
+ }
346
+ case SILInstructionKind::StoreInst: {
347
+ auto *si = cast<StoreInst>(memInst);
348
+ return si->getOwnershipQualifier () == StoreOwnershipQualifier::Assign;
470
349
}
471
- case SILInstructionKind::DestroyAddrInst:
472
- return true ;
473
- case SILInstructionKind::LoadInst:
474
- return cast<LoadInst>(memInst)->getOwnershipQualifier () ==
475
- LoadOwnershipQualifier::Take;
476
350
}
477
351
}
478
352
@@ -1606,10 +1480,12 @@ struct CopiedLoadBorrowEliminationVisitor final
1606
1480
// MARK: DestructureThroughDeinit Checking
1607
1481
// ===----------------------------------------------------------------------===//
1608
1482
1609
- static void
1610
- checkForDestructureThroughDeinit (MarkMustCheckInst *rootAddress, Operand *use,
1611
- TypeTreeLeafTypeRange usedBits,
1612
- DiagnosticEmitter &diagnosticEmitter) {
1483
+ // / When partial consumption is enabled, we only allow for destructure through
1484
+ // / deinits. When partial consumption is disabled, we error on /all/ partial
1485
+ // / consumption.
1486
+ static void checkForDestructure (MarkMustCheckInst *rootAddress, Operand *use,
1487
+ TypeTreeLeafTypeRange usedBits,
1488
+ DiagnosticEmitter &diagnosticEmitter) {
1613
1489
LLVM_DEBUG (llvm::dbgs () << " DestructureNeedingUse: " << *use->getUser ());
1614
1490
1615
1491
SILFunction *fn = rootAddress->getFunction ();
@@ -1627,6 +1503,29 @@ checkForDestructureThroughDeinit(MarkMustCheckInst *rootAddress, Operand *use,
1627
1503
if (iterType.isMoveOnlyWrapped ())
1628
1504
return ;
1629
1505
1506
+ // If we are not allowing for any partial consumption, just emit an error
1507
+ // immediately.
1508
+ if (!rootAddress->getModule ().getASTContext ().LangOpts .hasFeature (
1509
+ Feature::MoveOnlyPartialConsumption)) {
1510
+ // If the types equal, just bail early.
1511
+ if (iterType == targetType)
1512
+ return ;
1513
+
1514
+ // Otherwise, build up the path string and emit the error.
1515
+ SmallString<128 > pathString;
1516
+ auto rootType = rootAddress->getType ();
1517
+ if (iterType != rootType) {
1518
+ llvm::raw_svector_ostream os (pathString);
1519
+ pair.constructPathString (iterType, {rootType, fn}, rootType, fn, os);
1520
+ }
1521
+
1522
+ diagnosticEmitter.emitCannotDestructureNominalError (
1523
+ rootAddress, pathString, nullptr /* nominal*/ , use->getUser (),
1524
+ false /* is for deinit error*/ );
1525
+ return ;
1526
+ }
1527
+
1528
+ // Otherwise, walk the type looking for the deinit.
1630
1529
while (iterType != targetType) {
1631
1530
// If we have a nominal type as our parent type, see if it has a
1632
1531
// deinit. We know that it must be non-copyable since copyable types
@@ -1645,8 +1544,9 @@ checkForDestructureThroughDeinit(MarkMustCheckInst *rootAddress, Operand *use,
1645
1544
pair.constructPathString (iterType, {rootType, fn}, rootType, fn, os);
1646
1545
}
1647
1546
1648
- diagnosticEmitter.emitCannotDestructureDeinitNominalError (
1649
- rootAddress, pathString, nom, use->getUser ());
1547
+ diagnosticEmitter.emitCannotDestructureNominalError (
1548
+ rootAddress, pathString, nom, use->getUser (),
1549
+ true /* is for deinit error*/ );
1650
1550
break ;
1651
1551
}
1652
1552
}
@@ -1753,7 +1653,7 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
1753
1653
LLVM_DEBUG (llvm::dbgs () << " Visiting user: " << *user;);
1754
1654
1755
1655
// First check if we have init/reinit. These are quick/simple.
1756
- if (::memInstMustInitialize (op)) {
1656
+ if (noncopyable ::memInstMustInitialize (op)) {
1757
1657
LLVM_DEBUG (llvm::dbgs () << " Found init: " << *user);
1758
1658
1759
1659
// TODO: What about copy_addr of itself. We really should just pre-process
@@ -1766,7 +1666,7 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
1766
1666
return true ;
1767
1667
}
1768
1668
1769
- if (::memInstMustReinitialize (op)) {
1669
+ if (noncopyable ::memInstMustReinitialize (op)) {
1770
1670
LLVM_DEBUG (llvm::dbgs () << " Found reinit: " << *user);
1771
1671
auto leafRange = TypeTreeLeafTypeRange::get (op->get (), getRootAddress ());
1772
1672
if (!leafRange)
@@ -1854,8 +1754,10 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
1854
1754
1855
1755
// TODO: Add borrow checking here like below.
1856
1756
1857
- // TODO: Add destructure deinit checking here once address only checking is
1858
- // completely brought up.
1757
+ // If we have a copy_addr, we are either going to have a take or a
1758
+ // copy... in either case, this copy_addr /is/ going to be a consuming
1759
+ // operation. Make sure to check if we semantically destructure.
1760
+ checkForDestructure (markedValue, op, *leafRange, diagnosticEmitter);
1859
1761
1860
1762
if (copyAddr->isTakeOfSrc ()) {
1861
1763
LLVM_DEBUG (llvm::dbgs () << " Found take: " << *user);
@@ -1923,17 +1825,6 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
1923
1825
return false ;
1924
1826
}
1925
1827
1926
- checkForDestructureThroughDeinit (markedValue, op, *leafRange,
1927
- diagnosticEmitter);
1928
-
1929
- // If we emitted an error diagnostic, do not transform further and instead
1930
- // mark that we emitted an early diagnostic and return true.
1931
- if (numDiagnostics != moveChecker.diagnosticEmitter .getDiagnosticCount ()) {
1932
- LLVM_DEBUG (llvm::dbgs ()
1933
- << " Emitting destructure through deinit error!\n " );
1934
- return true ;
1935
- }
1936
-
1937
1828
// Canonicalize the lifetime of the load [take], load [copy].
1938
1829
LLVM_DEBUG (llvm::dbgs () << " Running copy propagation!\n " );
1939
1830
moveChecker.changed |= moveChecker.canonicalizer .canonicalize ();
@@ -2034,6 +1925,19 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
2034
1925
useState.recordLivenessUse (dvi, *leafRange);
2035
1926
}
2036
1927
} else {
1928
+ // Now that we know that we are going to perform a take, perform a
1929
+ // checkForDestructure.
1930
+ checkForDestructure (markedValue, op, *leafRange, diagnosticEmitter);
1931
+
1932
+ // If we emitted an error diagnostic, do not transform further and instead
1933
+ // mark that we emitted an early diagnostic and return true.
1934
+ if (numDiagnostics !=
1935
+ moveChecker.diagnosticEmitter .getDiagnosticCount ()) {
1936
+ LLVM_DEBUG (llvm::dbgs ()
1937
+ << " Emitting destructure through deinit error!\n " );
1938
+ return true ;
1939
+ }
1940
+
2037
1941
// If we had a load [copy], store this into the copy list. These are the
2038
1942
// things that we must merge into destroy_addr or reinits after we are
2039
1943
// done checking. The load [take] are already complete and good to go.
@@ -2050,7 +1954,7 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
2050
1954
2051
1955
// Now that we have handled or loadTakeOrCopy, we need to now track our
2052
1956
// additional pure takes.
2053
- if (::memInstMustConsume (op)) {
1957
+ if (noncopyable ::memInstMustConsume (op)) {
2054
1958
// If we don't have a consumeable and assignable check kind, then we can't
2055
1959
// consume. Emit an error.
2056
1960
//
@@ -2078,8 +1982,7 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
2078
1982
// error.
2079
1983
unsigned numDiagnostics =
2080
1984
moveChecker.diagnosticEmitter .getDiagnosticCount ();
2081
- checkForDestructureThroughDeinit (markedValue, op, *leafRange,
2082
- diagnosticEmitter);
1985
+ checkForDestructure (markedValue, op, *leafRange, diagnosticEmitter);
2083
1986
if (numDiagnostics != moveChecker.diagnosticEmitter .getDiagnosticCount ()) {
2084
1987
LLVM_DEBUG (llvm::dbgs ()
2085
1988
<< " Emitting destructure through deinit error!\n " );
@@ -3212,6 +3115,24 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
3212
3115
state.process ();
3213
3116
}
3214
3117
3118
+ // Then if we have a let allocation, see if we have any copy_addr on our
3119
+ // markedAddress that form temporary allocation chains. This occurs when we
3120
+ // emit SIL for code like:
3121
+ //
3122
+ // let x: AddressOnlyType = ...
3123
+ // let _ = x.y.z
3124
+ //
3125
+ // SILGen will treat y as a separate rvalue from x and will create a temporary
3126
+ // allocation. In contrast if we have a var, we treat x like an lvalue and
3127
+ // just create GEPs appropriately.
3128
+ if (eliminateTemporaryAllocationsFromLet (markedAddress)) {
3129
+ LLVM_DEBUG (
3130
+ llvm::dbgs ()
3131
+ << " Succeeded in eliminating temporary allocations! Fn after:\n " ;
3132
+ markedAddress->getFunction ()->dump ());
3133
+ changed = true ;
3134
+ }
3135
+
3215
3136
// Then gather all uses of our address by walking from def->uses. We use this
3216
3137
// to categorize the uses of this address into their ownership behavior (e.g.:
3217
3138
// init, reinit, take, destroy, etc.).
0 commit comments