[MemoryLifetimeVerifier] Permit leaks in dead-ends

This commit is contained in:
Nate Chandler
2025-08-27 18:03:53 -07:00
parent eb9f5b2a92
commit 5851dcb971
5 changed files with 33 additions and 48 deletions

View File

@@ -1676,7 +1676,8 @@ public:
}
/// Verifies the lifetime of memory locations in the function.
void verifyMemoryLifetime(CalleeCache *calleeCache);
void verifyMemoryLifetime(CalleeCache *calleeCache,
DeadEndBlocks *deadEndBlocks);
/// Verifies ownership of the function.
/// Since we don't have complete lifetimes everywhere, computes DeadEndBlocks

View File

@@ -12,13 +12,14 @@
#define DEBUG_TYPE "sil-memory-lifetime-verifier"
#include "swift/Basic/Assertions.h"
#include "swift/SIL/MemoryLocations.h"
#include "swift/SIL/BitDataflow.h"
#include "swift/SIL/CalleeCache.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/ApplySite.h"
#include "swift/SIL/BasicBlockDatastructures.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/BitDataflow.h"
#include "swift/SIL/CalleeCache.h"
#include "swift/SIL/MemoryLocations.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILFunction.h"
#include "llvm/Support/CommandLine.h"
using namespace swift;
@@ -43,6 +44,7 @@ class MemoryLifetimeVerifier {
SILFunction *function;
CalleeCache *calleeCache;
DeadEndBlocks *deadEndBlocks;
MemoryLocations locations;
/// alloc_stack memory locations which are used for store_borrow.
@@ -140,11 +142,12 @@ class MemoryLifetimeVerifier {
}
public:
MemoryLifetimeVerifier(SILFunction *function, CalleeCache *calleeCache) :
function(function),
calleeCache(calleeCache),
locations(/*handleNonTrivialProjections*/ true,
/*handleTrivialLocations*/ true) {}
MemoryLifetimeVerifier(SILFunction *function, CalleeCache *calleeCache,
DeadEndBlocks *deadEndBlocks)
: function(function), calleeCache(calleeCache),
deadEndBlocks(deadEndBlocks),
locations(/*handleNonTrivialProjections*/ true,
/*handleTrivialLocations*/ true) {}
/// The main entry point to verify the lifetime of all memory locations in
/// the function.
@@ -883,7 +886,12 @@ void MemoryLifetimeVerifier::checkBlock(SILBasicBlock *block, Bits &bits) {
}
case SILInstructionKind::DeallocStackInst: {
SILValue opVal = cast<DeallocStackInst>(&I)->getOperand();
requireBitsClear(bits & nonTrivialLocations, opVal, &I);
if (!deadEndBlocks->isDeadEnd(I.getParent())) {
// TODO: rdar://159311784: Maybe at some point the invariant will be
// enforced that values stored into addresses
// don't leak in dead-ends.
requireBitsClear(bits & nonTrivialLocations, opVal, &I);
}
// Needed to clear any bits of trivial locations (which are not required
// to be zero).
locations.clearBits(bits, opVal);
@@ -973,7 +981,8 @@ void MemoryLifetimeVerifier::verify() {
} // anonymous namespace
void SILFunction::verifyMemoryLifetime(CalleeCache *calleeCache) {
MemoryLifetimeVerifier verifier(this, calleeCache);
void SILFunction::verifyMemoryLifetime(CalleeCache *calleeCache,
DeadEndBlocks *deadEndBlocks) {
MemoryLifetimeVerifier verifier(this, calleeCache, deadEndBlocks);
verifier.verify();
}

View File

@@ -7380,7 +7380,7 @@ public:
if (F->hasOwnership() && F->shouldVerifyOwnership() &&
!mod.getASTContext().hadError()) {
F->verifyMemoryLifetime(calleeCache);
F->verifyMemoryLifetime(calleeCache, &getDeadEndBlocks());
}
}

View File

@@ -864,3 +864,11 @@ bb0:
%10 = tuple ()
return %10
}
sil [ossa] @storage_leaked_into_dead_ends : $@convention(thin) () -> () {
%t = apply undef() : $@convention(thin) () -> (@owned T)
%t_addr = alloc_stack $T
store %t to [init] %t_addr
dealloc_stack %t_addr
unreachable
}

View File

@@ -1205,39 +1205,6 @@ bb0(%0 : $*T, %1 : $*T, %2 : $*T):
return %15 : $()
}
// CHECK-LABEL: sil [ossa] @alloc_box_in_specialized_callee :
// CHECK-NOT: alloc_box
// CHECK-LABEL: } // end sil function 'alloc_box_in_specialized_callee'
sil [ossa] @alloc_box_in_specialized_callee : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%2 = project_box %1, 0
store %0 to [trivial] %2
%4 = function_ref @callee_with_allocbox : $@convention(thin) (@guaranteed { var Int }) -> ()
%5 = apply %4(%1) : $@convention(thin) (@guaranteed { var Int }) -> ()
destroy_value %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil shared [ossa] @$s20callee_with_allocboxTf0s_n :
// CHECK-NOT: alloc_box
// CHECK-LABEL: } // end sil function '$s20callee_with_allocboxTf0s_n'
// CHECK-LABEL: sil [ossa] @callee_with_allocbox :
// CHECK-NOT: alloc_box
// CHECK-LABEL: } // end sil function 'callee_with_allocbox'
sil [ossa] @callee_with_allocbox : $@convention(thin) (@guaranteed { var Int }) -> () {
bb0(%0 : @guaranteed ${ var Int }):
%1 = alloc_box ${ var Int }
%2 = project_box %1 : ${ var Int }, 0
%3 = project_box %0 : ${ var Int }, 0
copy_addr %3 to %2
destroy_value %1
%r = tuple ()
return %r
}
sil @getC : $@convention(thin) () -> (@owned C)
// CHECK-LABEL: sil [ossa] @leak_to_inf_loop_1 : {{.*}} {