Skip to content

Commit df0214a

Browse files
committed
Teach exclusivity access marker verifier to handle UnsafePointer.
My previous attempt to strengthen this verification ignored the fact that a projection or addressor that returns UnsafePointer can have nested access after being passed as an @inout argument. After inlining, this caused the verifier to assert. Instead, handle access to an UnsafePointer as a valid but unidentified access. I was trying to avoid this because an UnsafePointer could refer to a global or class property, which we can never consider "Unidentified". However, this is reasonably safe because it can only result from a _nested_ access. If a global or class property is being addressed, it must already have its own dynamic access within the addressor, and that access will be exposed to the optimizer. If a non-public KeyPath is being addressed, a keypath instruction must already exist somewhere in the module which is exposed to the optimizer. Fixes <rdar://problem/41660554> Swift CI (macOS release master, OSS): SIL verification failed: Unknown formal access pattern: storage.
1 parent c997080 commit df0214a

File tree

3 files changed

+49
-3
lines changed

3 files changed

+49
-3
lines changed

lib/SIL/MemAccessUtils.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,16 @@ static bool isExternalGlobalAddressor(ApplyInst *AI) {
181181
return funcRef->isGlobalInit() && funcRef->isExternalDeclaration();
182182
}
183183

184+
// Return true if the given StructExtractInst extracts the RawPointer from
185+
// Unsafe[Mutable]Pointer.
186+
static bool isUnsafePointerExtraction(StructExtractInst *SEI) {
187+
assert(isa<BuiltinRawPointerType>(SEI->getType().getASTType()));
188+
auto &C = SEI->getModule().getASTContext();
189+
auto *decl = SEI->getStructDecl();
190+
return decl == C.getUnsafeMutablePointerDecl()
191+
|| decl == C.getUnsafePointerDecl();
192+
}
193+
184194
// Given an address base is a block argument, verify that it is actually a box
185195
// projected from a switch_enum. This is a valid pattern at any SIL stage
186196
// resulting in a block-type phi. In later SIL stages, the optimizer may form
@@ -248,6 +258,14 @@ AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) {
248258
// Don't currently allow any other calls to return an accessed address.
249259
return AccessedStorage();
250260

261+
case ValueKind::StructExtractInst:
262+
// Handle nested access to a KeyPath projection. The projection itself
263+
// uses a Builtin. However, the returned UnsafeMutablePointer may be
264+
// converted to an address and accessed via an inout argument.
265+
if (isUnsafePointerExtraction(cast<StructExtractInst>(address)))
266+
return AccessedStorage(address, AccessedStorage::Unidentified);
267+
return AccessedStorage();
268+
251269
// A block argument may be a box value projected out of
252270
// switch_enum. Address-type block arguments are not allowed.
253271
case ValueKind::SILPHIArgument:
@@ -297,8 +315,8 @@ AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) {
297315
continue;
298316

299317
// Access to a Builtin.RawPointer. Treat this like the inductive cases
300-
// above because some RawPointer's originate from identified locations. See
301-
// the special case for global addressors, which return RawPointer above.
318+
// above because some RawPointers originate from identified locations. See
319+
// the special case for global addressors, which return RawPointer, above.
302320
//
303321
// If the inductive search does not find a valid addressor, it will
304322
// eventually reach the default case that returns in invalid location. This

test/Interpreter/enforce_exclusive_access.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ ExclusiveAccessTestSuite.test("SequentialKeyPathWritesDontOverlap") {
306306

307307
c[keyPath: getF] = 7
308308
c[keyPath: getF] = 8 // no-trap
309-
c[keyPath: getF] += c[keyPath: getF] + 1 // no-trap
309+
c[keyPath: getF] += c[keyPath: getF] // no-trap
310310
}
311311

312312
ExclusiveAccessTestSuite.test("KeyPathInoutKeyPathWriteClassStoredProp")

test/SILOptimizer/exclusivity_static_diagnostics.sil

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,3 +1106,31 @@ bb0(%0 : $UnsafePointer<Int32>, %1 : $*Int32):
11061106
%v = tuple ()
11071107
return %v : $()
11081108
}
1109+
1110+
// <rdar://problem/41660554> Swift CI (macOS release master, OSS): SIL verification failed: Unknown formal access pattern: storage
1111+
// Test access marker verification of a KeyPath projection with nested @inout access after inlining.
1112+
sil @takeInoutInt : $@convention(thin) (@inout Int) -> ()
1113+
1114+
// The compiler intrinsic _projectXXXKeyPath returns a tuple of
1115+
// UnsafePointer and Optional AnyObject. After inlining, the unsafe
1116+
// pointer may appear to originate from anywhere, including a phi.
1117+
// There's currently no way to tell that the UnsafePointer originated
1118+
// from a KeyPath projection. This pattern could occur with any
1119+
// addressor. In either case, the nested access is valid but
1120+
// unidentified. Addressors that require enforcement must start a
1121+
// dynamic access within the addressor itself, before returning an
1122+
// UnsafePointer.
1123+
sil @testNestedKeypathAccess : $@convention(thin) (@guaranteed (UnsafeMutablePointer<Int>, Optional<AnyObject>)) -> () {
1124+
bb0(%0 : $(UnsafeMutablePointer<Int>, Optional<AnyObject>)):
1125+
%up = tuple_extract %0 : $(UnsafeMutablePointer<Int>, Optional<AnyObject>), 0
1126+
%o = tuple_extract %0 : $(UnsafeMutablePointer<Int>, Optional<AnyObject>), 1
1127+
%p = struct_extract %up : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
1128+
%adr = pointer_to_address %p : $Builtin.RawPointer to [strict] $*Int
1129+
%dep = mark_dependence %adr : $*Int on %o : $Optional<AnyObject>
1130+
%access = begin_access [modify] [static] %dep : $*Int
1131+
%f = function_ref @takeInoutInt : $@convention(thin) (@inout Int) -> ()
1132+
%call = apply %f(%access) : $@convention(thin) (@inout Int) -> ()
1133+
end_access %access : $*Int
1134+
%v = tuple ()
1135+
return %v : $()
1136+
}

0 commit comments

Comments
 (0)