Skip to content

Commit 43eb3f4

Browse files
authored
Merge pull request #16169 from atrick/verify-access
Improve -enable-verify-exclusivity.
2 parents 9323d08 + 33ca063 commit 43eb3f4

File tree

4 files changed

+103
-26
lines changed

4 files changed

+103
-26
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,11 @@ AccessedStorage findAccessedStorage(SILValue sourceAddr);
276276
/// storage.
277277
AccessedStorage findAccessedStorageOrigin(SILValue sourceAddr);
278278

279+
/// Return true if the given address operand is used by a memory operation that
280+
/// initializes the memory at that address, implying that the previous value is
281+
/// uninitialized.
282+
bool memInstMustInitialize(Operand *memOper);
283+
279284
/// Return true if the given address producer may be the source of a formal
280285
/// access (a read or write of a potentially aliased, user visible variable).
281286
///

lib/SIL/MemAccessUtils.cpp

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define DEBUG_TYPE "sil-access-utils"
1414

1515
#include "swift/SIL/MemAccessUtils.h"
16+
#include "swift/SIL/SILUndef.h"
1617

1718
using namespace swift;
1819

@@ -115,6 +116,30 @@ static void checkSwitchEnumBlockArg(SILPHIArgument *arg) {
115116
}
116117
}
117118

119+
/// Return true if the given address value is produced by a special address
120+
/// producer that is only used for local initialization, not formal access.
121+
static bool isAddressForLocalInitOnly(SILValue sourceAddr) {
122+
switch (sourceAddr->getKind()) {
123+
default:
124+
return false;
125+
126+
// Value to address conversions: the operand is the non-address source
127+
// value. These allow local mutation of the value but should never be used
128+
// for formal access of an lvalue.
129+
case ValueKind::OpenExistentialBoxInst:
130+
case ValueKind::ProjectExistentialBoxInst:
131+
return true;
132+
133+
// Self-evident local initialization.
134+
case ValueKind::InitEnumDataAddrInst:
135+
case ValueKind::InitExistentialAddrInst:
136+
case ValueKind::AllocExistentialBoxInst:
137+
case ValueKind::AllocValueBufferInst:
138+
case ValueKind::ProjectValueBufferInst:
139+
return true;
140+
}
141+
}
142+
118143
AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) {
119144
SILValue address = sourceAddr;
120145
while (true) {
@@ -127,9 +152,12 @@ AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) {
127152
// Handle other unidentified address sources.
128153
switch (address->getKind()) {
129154
default:
155+
if (isAddressForLocalInitOnly(address))
156+
return AccessedStorage(address, AccessedStorage::Unidentified);
130157
return AccessedStorage();
131158

132159
case ValueKind::PointerToAddressInst:
160+
case ValueKind::SILUndef:
133161
return AccessedStorage(address, AccessedStorage::Unidentified);
134162

135163
// A block argument may be a box value projected out of
@@ -181,22 +209,6 @@ AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) {
181209
case ValueKind::IndexAddrInst:
182210
address = cast<SingleValueInstruction>(address)->getOperand(0);
183211
continue;
184-
185-
// Value to address conversions: the operand is the non-address source
186-
// value. These allow local mutation of the value but should never be used
187-
// for formal access of an lvalue.
188-
case ValueKind::OpenExistentialBoxInst:
189-
case ValueKind::ProjectExistentialBoxInst:
190-
case ValueKind::ProjectValueBufferInst:
191-
return AccessedStorage(address, AccessedStorage::Unidentified);
192-
193-
// Local initialization: these cases are skipped.
194-
case ValueKind::InitEnumDataAddrInst:
195-
case ValueKind::InitExistentialAddrInst:
196-
case ValueKind::AllocExistentialBoxInst:
197-
case ValueKind::AllocValueBufferInst:
198-
case ValueKind::SILUndef:
199-
return AccessedStorage(address, AccessedStorage::Unidentified);
200212
}
201213
}
202214
}
@@ -231,6 +243,35 @@ static bool isScratchBuffer(SILValue value) {
231243
value->getType().getSwiftRValueType());
232244
}
233245

246+
bool swift::memInstMustInitialize(Operand *memOper) {
247+
SILValue address = memOper->get();
248+
SILInstruction *memInst = memOper->getUser();
249+
250+
switch (memInst->getKind()) {
251+
default:
252+
return false;
253+
254+
case SILInstructionKind::CopyAddrInst: {
255+
auto *CAI = cast<CopyAddrInst>(memInst);
256+
return CAI->getDest() == address && CAI->isInitializationOfDest();
257+
}
258+
case SILInstructionKind::InitExistentialAddrInst:
259+
case SILInstructionKind::InitEnumDataAddrInst:
260+
case SILInstructionKind::InjectEnumAddrInst:
261+
return true;
262+
263+
case SILInstructionKind::StoreInst:
264+
return cast<StoreInst>(memInst)->getOwnershipQualifier()
265+
== StoreOwnershipQualifier::Init;
266+
267+
case SILInstructionKind::StoreWeakInst:
268+
return cast<StoreWeakInst>(memInst)->isInitializationOfDest();
269+
270+
case SILInstructionKind::StoreUnownedInst:
271+
return cast<StoreUnownedInst>(memInst)->isInitializationOfDest();
272+
}
273+
}
274+
234275
bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage,
235276
SILFunction *F) {
236277
switch (storage.getKind()) {
@@ -259,6 +300,9 @@ bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage,
259300
return isPossibleFormalAccessBase(nestedStorage, F);
260301
}
261302
case AccessedStorage::Unidentified:
303+
if (isAddressForLocalInitOnly(storage.getValue()))
304+
return false;
305+
262306
if (isa<SILPHIArgument>(storage.getValue())) {
263307
checkSwitchEnumBlockArg(cast<SILPHIArgument>(storage.getValue()));
264308
return false;
@@ -268,12 +312,7 @@ bool swift::isPossibleFormalAccessBase(const AccessedStorage &storage,
268312
if (isa<PointerToAddressInst>(storage.getValue()))
269313
return false;
270314

271-
// Existential initialization is just initialization.
272-
if (isa<InitExistentialAddrInst>(storage.getValue()))
273-
return false;
274-
275-
// Enum initialization is just initialization.
276-
if (isa<InitEnumDataAddrInst>(storage.getValue()))
315+
if (isa<SILUndef>(storage.getValue()))
277316
return false;
278317

279318
if (isScratchBuffer(storage.getValue()))

lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,11 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) {
889889
abort();
890890
};
891891

892+
// If the memory instruction is only used for initialization, it doesn't need
893+
// an access marker.
894+
if (memInstMustInitialize(memOper))
895+
return;
896+
892897
if (auto apply = ApplySite::isa(memInst)) {
893898
SILArgumentConvention conv =
894899
apply.getArgumentConvention(apply.getCalleeArgIndex(*memOper));
@@ -922,8 +927,9 @@ static void checkAccessedAddress(Operand *memOper, StorageMap &Accesses) {
922927
if (!storage || !isPossibleFormalAccessBase(storage, memInst->getFunction()))
923928
return;
924929

925-
// Skip local non-lvalue accesses. There are many local initialization
926-
// patterns that don't require access markers.
930+
// A box or stack variable may represent lvalues, but they can only conflict
931+
// with call sites in the same scope. Some initialization patters (stores to
932+
// the local value) aren't protected by markers, so we need this check.
927933
if (!isa<ApplySite>(memInst)
928934
&& (storage.getKind() == AccessedStorage::Box
929935
|| storage.getKind() == AccessedStorage::Stack)) {

test/SILOptimizer/access_marker_verify.swift

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
// RUN: %target-swift-frontend -module-name access_marker_verify -enable-verify-exclusivity -enforce-exclusivity=checked -enable-sil-ownership -emit-silgen -swift-version 4 -parse-as-library %s | %FileCheck %s
3-
// RUN: %target-swift-frontend -module-name access_marker_verify -enable-verify-exclusivity -enforce-exclusivity=checked -enable-sil-ownership -Onone -emit-sil -swift-version 4 -parse-as-library %s
4-
// RUN: %target-swift-frontend -module-name access_marker_verify -enable-verify-exclusivity -enforce-exclusivity=checked -enable-sil-ownership -O -emit-sil -swift-version 4 -parse-as-library %s
3+
// RUN: %target-swift-frontend -module-name access_marker_verify -enable-verify-exclusivity -enforce-exclusivity=checked -enable-sil-ownership -Onone -emit-sil -swift-version 4 -parse-as-library %s -o /dev/null
4+
// RUN: %target-swift-frontend -module-name access_marker_verify -enable-verify-exclusivity -enforce-exclusivity=checked -enable-sil-ownership -O -emit-sil -swift-version 4 -parse-as-library %s -o /dev/null
55
// REQUIRES: asserts
66

77
// Test the combination of SILGen + DiagnoseStaticExclusivity with verification.
@@ -995,3 +995,30 @@ func testPointerInit(x: Int, y: UnsafeMutablePointer<Int>) {
995995
// CHECK-NOT: begin_access
996996
// CHECK: assign %0 to [[ADR]] : $*Int
997997
// CHECK-LABEL: } // end sil function '$S20access_marker_verify15testPointerInit1x1yySi_SpySiGtF'
998+
999+
// Verification should ignore the address operand of init_existential_addr.
1000+
class testInitExistentialGlobal {
1001+
static var testProperty: P = StructP()
1002+
}
1003+
// CHECK-LABEL: sil private @globalinit{{.*}} : $@convention(c) () -> () {
1004+
// CHECK: alloc_global @$S20access_marker_verify25testInitExistentialGlobalC0D8PropertyAA1P_pvpZ
1005+
// CHECK: [[GADR:%.*]] = global_addr @$S20access_marker_verify25testInitExistentialGlobalC0D8PropertyAA1P_pvpZ : $*P
1006+
// CHECK: [[EADR:%.*]] = init_existential_addr [[GADR]] : $*P, $StructP
1007+
// CHECK: %{{.*}} = apply %{{.*}}({{.*}}) : $@convention(method) (@thin StructP.Type) -> StructP
1008+
// CHECK: store %{{.*}} to [trivial] [[EADR]] : $*StructP
1009+
// CHECK-LABEL: } // end sil function 'globalinit
1010+
1011+
public enum SomeError: Swift.Error {
1012+
case error
1013+
}
1014+
1015+
// Verification should ignore addresses produced by project_existential_box.
1016+
public func testInitBox() throws {
1017+
throw SomeError.error
1018+
}
1019+
// CHECK-LABEL: sil @$S20access_marker_verify11testInitBoxyyKF : $@convention(thin) () -> @error Error {
1020+
// CHECK: [[BOXALLOC:%.*]] = alloc_existential_box $Error, $SomeError
1021+
// CHECK: [[PROJ:%.*]] = project_existential_box $SomeError in [[BOXALLOC]] : $Error
1022+
// CHECK: store %{{.*}} to [trivial] [[PROJ]] : $*SomeError
1023+
// CHECK: throw [[BOXALLOC]] : $Error
1024+
// CHECK-LABEL: } // end sil function '$S20access_marker_verify11testInitBoxyyKF'

0 commit comments

Comments
 (0)