@@ -287,108 +287,9 @@ llvm::cl::opt<bool> DisableMoveOnlyAddressCheckerLifetimeExtension(
287287 " move-only values." ));
288288
289289// ===----------------------------------------------------------------------===//
290- // MARK: Memory Utilities
290+ // MARK: Utilities
291291// ===----------------------------------------------------------------------===//
292292
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-
392293static void insertDebugValueBefore (SILInstruction *insertPt,
393294 DebugVarCarryingInst debugVar,
394295 llvm::function_ref<SILValue ()> operand) {
@@ -432,47 +333,20 @@ static void convertMemoryReinitToInitForm(SILInstruction *memInst,
432333 [&]{ return debugVar.getOperandForDebugValueClone (); });
433334}
434335
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) {
441338 switch (memInst->getKind ()) {
442339 default :
443340 return false ;
444341
445342 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;
470349 }
471- case SILInstructionKind::DestroyAddrInst:
472- return true ;
473- case SILInstructionKind::LoadInst:
474- return cast<LoadInst>(memInst)->getOwnershipQualifier () ==
475- LoadOwnershipQualifier::Take;
476350 }
477351}
478352
@@ -1606,10 +1480,12 @@ struct CopiedLoadBorrowEliminationVisitor final
16061480// MARK: DestructureThroughDeinit Checking
16071481// ===----------------------------------------------------------------------===//
16081482
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) {
16131489 LLVM_DEBUG (llvm::dbgs () << " DestructureNeedingUse: " << *use->getUser ());
16141490
16151491 SILFunction *fn = rootAddress->getFunction ();
@@ -1627,6 +1503,29 @@ checkForDestructureThroughDeinit(MarkMustCheckInst *rootAddress, Operand *use,
16271503 if (iterType.isMoveOnlyWrapped ())
16281504 return ;
16291505
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.
16301529 while (iterType != targetType) {
16311530 // If we have a nominal type as our parent type, see if it has a
16321531 // deinit. We know that it must be non-copyable since copyable types
@@ -1645,8 +1544,9 @@ checkForDestructureThroughDeinit(MarkMustCheckInst *rootAddress, Operand *use,
16451544 pair.constructPathString (iterType, {rootType, fn}, rootType, fn, os);
16461545 }
16471546
1648- diagnosticEmitter.emitCannotDestructureDeinitNominalError (
1649- rootAddress, pathString, nom, use->getUser ());
1547+ diagnosticEmitter.emitCannotDestructureNominalError (
1548+ rootAddress, pathString, nom, use->getUser (),
1549+ true /* is for deinit error*/ );
16501550 break ;
16511551 }
16521552 }
@@ -1753,7 +1653,7 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
17531653 LLVM_DEBUG (llvm::dbgs () << " Visiting user: " << *user;);
17541654
17551655 // First check if we have init/reinit. These are quick/simple.
1756- if (::memInstMustInitialize (op)) {
1656+ if (noncopyable ::memInstMustInitialize (op)) {
17571657 LLVM_DEBUG (llvm::dbgs () << " Found init: " << *user);
17581658
17591659 // TODO: What about copy_addr of itself. We really should just pre-process
@@ -1766,7 +1666,7 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
17661666 return true ;
17671667 }
17681668
1769- if (::memInstMustReinitialize (op)) {
1669+ if (noncopyable ::memInstMustReinitialize (op)) {
17701670 LLVM_DEBUG (llvm::dbgs () << " Found reinit: " << *user);
17711671 auto leafRange = TypeTreeLeafTypeRange::get (op->get (), getRootAddress ());
17721672 if (!leafRange)
@@ -1854,8 +1754,10 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
18541754
18551755 // TODO: Add borrow checking here like below.
18561756
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);
18591761
18601762 if (copyAddr->isTakeOfSrc ()) {
18611763 LLVM_DEBUG (llvm::dbgs () << " Found take: " << *user);
@@ -1923,17 +1825,6 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
19231825 return false ;
19241826 }
19251827
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-
19371828 // Canonicalize the lifetime of the load [take], load [copy].
19381829 LLVM_DEBUG (llvm::dbgs () << " Running copy propagation!\n " );
19391830 moveChecker.changed |= moveChecker.canonicalizer .canonicalize ();
@@ -2034,6 +1925,19 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
20341925 useState.recordLivenessUse (dvi, *leafRange);
20351926 }
20361927 } 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+
20371941 // If we had a load [copy], store this into the copy list. These are the
20381942 // things that we must merge into destroy_addr or reinits after we are
20391943 // done checking. The load [take] are already complete and good to go.
@@ -2050,7 +1954,7 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
20501954
20511955 // Now that we have handled or loadTakeOrCopy, we need to now track our
20521956 // additional pure takes.
2053- if (::memInstMustConsume (op)) {
1957+ if (noncopyable ::memInstMustConsume (op)) {
20541958 // If we don't have a consumeable and assignable check kind, then we can't
20551959 // consume. Emit an error.
20561960 //
@@ -2078,8 +1982,7 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
20781982 // error.
20791983 unsigned numDiagnostics =
20801984 moveChecker.diagnosticEmitter .getDiagnosticCount ();
2081- checkForDestructureThroughDeinit (markedValue, op, *leafRange,
2082- diagnosticEmitter);
1985+ checkForDestructure (markedValue, op, *leafRange, diagnosticEmitter);
20831986 if (numDiagnostics != moveChecker.diagnosticEmitter .getDiagnosticCount ()) {
20841987 LLVM_DEBUG (llvm::dbgs ()
20851988 << " Emitting destructure through deinit error!\n " );
@@ -3212,6 +3115,24 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
32123115 state.process ();
32133116 }
32143117
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+
32153136 // Then gather all uses of our address by walking from def->uses. We use this
32163137 // to categorize the uses of this address into their ownership behavior (e.g.:
32173138 // init, reinit, take, destroy, etc.).
0 commit comments