Skip to content

Commit fd38e72

Browse files
authored
Merge pull request #21688 from atrick/5.0-fix-access-phi
Fix findAccessedStorage to handle cyclic phis.
2 parents 8012d0f + 27e324d commit fd38e72

File tree

2 files changed

+207
-134
lines changed

2 files changed

+207
-134
lines changed

lib/SIL/MemAccessUtils.cpp

Lines changed: 180 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -243,153 +243,199 @@ static bool isAddressForLocalInitOnly(SILValue sourceAddr) {
243243
}
244244
}
245245

246+
namespace {
247+
// The result of an accessed storage query. A complete result produces an
248+
// AccessedStorage object, which may or may not be valid. An incomplete result
249+
// produces a SILValue representing the source address for use with deeper
250+
// queries. The source address is not necessarily a SIL address type because
251+
// the query can extend past pointer-to-address casts.
252+
class AccessedStorageResult {
253+
AccessedStorage storage;
254+
SILValue address;
255+
bool complete;
256+
257+
explicit AccessedStorageResult(SILValue address)
258+
: address(address), complete(false) {}
259+
260+
public:
261+
AccessedStorageResult(const AccessedStorage &storage)
262+
: storage(storage), complete(true) {}
263+
264+
static AccessedStorageResult incomplete(SILValue address) {
265+
return AccessedStorageResult(address);
266+
}
267+
268+
bool isComplete() const { return complete; }
269+
270+
AccessedStorage getStorage() const { assert(complete); return storage; }
271+
272+
SILValue getAddress() const { assert(!complete); return address; }
273+
};
274+
} // namespace
275+
246276
// AccessEnforcementWMO makes a strong assumption that all accesses are either
247277
// identified or are *not* accessing a global variable or class property defined
248278
// in this module. Consequently, we cannot simply bail out on
249279
// PointerToAddressInst as an Unidentified access.
250-
AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) {
251-
SILValue address = sourceAddr;
252-
while (true) {
253-
AccessedStorage::Kind kind = AccessedStorage::classify(address);
254-
// First handle identified cases: these are always valid as the base of
255-
// a formal access.
256-
if (kind != AccessedStorage::Unidentified)
257-
return AccessedStorage(address, kind);
258-
259-
// If the address producer cannot immediately be classified, follow the
260-
// use-def chain of address, box, or RawPointer producers.
261-
assert(address->getType().isAddress()
262-
|| isa<SILBoxType>(address->getType().getASTType())
263-
|| isa<BuiltinRawPointerType>(address->getType().getASTType()));
264-
265-
// Handle other unidentified address sources.
266-
switch (address->getKind()) {
267-
default:
268-
if (isAddressForLocalInitOnly(address))
269-
return AccessedStorage(address, AccessedStorage::Unidentified);
270-
return AccessedStorage();
271-
272-
case ValueKind::SILUndef:
273-
return AccessedStorage(address, AccessedStorage::Unidentified);
274-
275-
case ValueKind::ApplyInst:
276-
if (isExternalGlobalAddressor(cast<ApplyInst>(address)))
277-
return AccessedStorage(address, AccessedStorage::Unidentified);
278-
279-
// Don't currently allow any other calls to return an accessed address.
280+
static AccessedStorageResult getAccessedStorageFromAddress(SILValue sourceAddr) {
281+
AccessedStorage::Kind kind = AccessedStorage::classify(sourceAddr);
282+
// First handle identified cases: these are always valid as the base of
283+
// a formal access.
284+
if (kind != AccessedStorage::Unidentified)
285+
return AccessedStorage(sourceAddr, kind);
286+
287+
// If the sourceAddr producer cannot immediately be classified, follow the
288+
// use-def chain of sourceAddr, box, or RawPointer producers.
289+
assert(sourceAddr->getType().isAddress()
290+
|| isa<SILBoxType>(sourceAddr->getType().getASTType())
291+
|| isa<BuiltinRawPointerType>(sourceAddr->getType().getASTType()));
292+
293+
// Handle other unidentified address sources.
294+
switch (sourceAddr->getKind()) {
295+
default:
296+
if (isAddressForLocalInitOnly(sourceAddr))
297+
return AccessedStorage(sourceAddr, AccessedStorage::Unidentified);
298+
return AccessedStorage();
299+
300+
case ValueKind::SILUndef:
301+
return AccessedStorage(sourceAddr, AccessedStorage::Unidentified);
302+
303+
case ValueKind::ApplyInst:
304+
if (isExternalGlobalAddressor(cast<ApplyInst>(sourceAddr)))
305+
return AccessedStorage(sourceAddr, AccessedStorage::Unidentified);
306+
307+
// Don't currently allow any other calls to return an accessed address.
308+
return AccessedStorage();
309+
310+
case ValueKind::StructExtractInst:
311+
// Handle nested access to a KeyPath projection. The projection itself
312+
// uses a Builtin. However, the returned UnsafeMutablePointer may be
313+
// converted to an address and accessed via an inout argument.
314+
if (isUnsafePointerExtraction(cast<StructExtractInst>(sourceAddr)))
315+
return AccessedStorage(sourceAddr, AccessedStorage::Unidentified);
316+
return AccessedStorage();
317+
318+
case ValueKind::SILPhiArgument: {
319+
auto *phiArg = cast<SILPhiArgument>(sourceAddr);
320+
if (phiArg->isPhiArgument())
321+
return AccessedStorageResult::incomplete(phiArg);
322+
323+
// A non-phi block argument may be a box value projected out of
324+
// switch_enum. Address-type block arguments are not allowed.
325+
if (sourceAddr->getType().isAddress())
280326
return AccessedStorage();
281327

282-
case ValueKind::StructExtractInst:
283-
// Handle nested access to a KeyPath projection. The projection itself
284-
// uses a Builtin. However, the returned UnsafeMutablePointer may be
285-
// converted to an address and accessed via an inout argument.
286-
if (isUnsafePointerExtraction(cast<StructExtractInst>(address)))
287-
return AccessedStorage(address, AccessedStorage::Unidentified);
288-
return AccessedStorage();
328+
checkSwitchEnumBlockArg(cast<SILPhiArgument>(sourceAddr));
329+
return AccessedStorage(sourceAddr, AccessedStorage::Unidentified);
330+
}
331+
// Load a box from an indirect payload of an opaque enum.
332+
// We must have peeked past the project_box earlier in this loop.
333+
// (the indirectness makes it a box, the load is for address-only).
334+
//
335+
// %payload_adr = unchecked_take_enum_data_addr %enum : $*Enum, #Enum.case
336+
// %box = load [take] %payload_adr : $*{ var Enum }
337+
//
338+
// FIXME: this case should go away with opaque values.
339+
//
340+
// Otherwise return invalid AccessedStorage.
341+
case ValueKind::LoadInst:
342+
if (sourceAddr->getType().is<SILBoxType>()) {
343+
SILValue operAddr = cast<LoadInst>(sourceAddr)->getOperand();
344+
assert(isa<UncheckedTakeEnumDataAddrInst>(operAddr));
345+
return AccessedStorageResult::incomplete(operAddr);
346+
}
347+
return AccessedStorage();
348+
349+
// ref_tail_addr project an address from a reference.
350+
// This is a valid address producer for nested @inout argument
351+
// access, but it is never used for formal access of identified objects.
352+
case ValueKind::RefTailAddrInst:
353+
return AccessedStorage(sourceAddr, AccessedStorage::Unidentified);
354+
355+
// Inductive single-operand cases:
356+
// Look through address casts to find the source address.
357+
case ValueKind::MarkUninitializedInst:
358+
case ValueKind::OpenExistentialAddrInst:
359+
case ValueKind::UncheckedAddrCastInst:
360+
// Inductive cases that apply to any type.
361+
case ValueKind::CopyValueInst:
362+
case ValueKind::MarkDependenceInst:
363+
// Look through a project_box to identify the underlying alloc_box as the
364+
// accesed object. It must be possible to reach either the alloc_box or the
365+
// containing enum in this loop, only looking through simple value
366+
// propagation such as copy_value.
367+
case ValueKind::ProjectBoxInst:
368+
// Handle project_block_storage just like project_box.
369+
case ValueKind::ProjectBlockStorageInst:
370+
// Look through begin_borrow in case a local box is borrowed.
371+
case ValueKind::BeginBorrowInst:
372+
return AccessedStorageResult::incomplete(
373+
cast<SingleValueInstruction>(sourceAddr)->getOperand(0));
374+
375+
// Access to a Builtin.RawPointer. Treat this like the inductive cases
376+
// above because some RawPointers originate from identified locations. See
377+
// the special case for global addressors, which return RawPointer, above.
378+
//
379+
// If the inductive search does not find a valid addressor, it will
380+
// eventually reach the default case that returns in invalid location. This
381+
// is correct for RawPointer because, although accessing a RawPointer is
382+
// legal SIL, there is no way to guarantee that it doesn't access class or
383+
// global storage, so returning a valid unidentified storage object would be
384+
// incorrect. It is the caller's responsibility to know that formal access
385+
// to such a location can be safely ignored.
386+
//
387+
// For example:
388+
//
389+
// - KeyPath Builtins access RawPointer. However, the caller can check
390+
// that the access `isFromBuilin` and ignore the storage.
391+
//
392+
// - lldb generates RawPointer access for debugger variables, but SILGen
393+
// marks debug VarDecl access as 'Unsafe' and SIL passes don't need the
394+
// AccessedStorage for 'Unsafe' access.
395+
case ValueKind::PointerToAddressInst:
396+
return AccessedStorageResult::incomplete(
397+
cast<SingleValueInstruction>(sourceAddr)->getOperand(0));
398+
399+
// Address-to-address subobject projections.
400+
case ValueKind::StructElementAddrInst:
401+
case ValueKind::TupleElementAddrInst:
402+
case ValueKind::UncheckedTakeEnumDataAddrInst:
403+
case ValueKind::TailAddrInst:
404+
case ValueKind::IndexAddrInst:
405+
return AccessedStorageResult::incomplete(
406+
cast<SingleValueInstruction>(sourceAddr)->getOperand(0));
407+
}
408+
}
289409

290-
case ValueKind::SILPhiArgument: {
291-
auto *phiArg = cast<SILPhiArgument>(address);
292-
if (phiArg->isPhiArgument()) {
293-
SmallVector<SILValue, 8> incomingValues;
294-
phiArg->getIncomingPhiValues(incomingValues);
295-
if (incomingValues.empty())
296-
return AccessedStorage();
297-
298-
auto storage = findAccessedStorage(incomingValues.pop_back_val());
299-
for (auto val : incomingValues) {
300-
auto otherStorage = findAccessedStorage(val);
301-
if (!accessingIdenticalLocations(storage, otherStorage))
302-
return AccessedStorage();
410+
AccessedStorage swift::findAccessedStorage(SILValue sourceAddr) {
411+
SmallVector<SILValue, 8> addressWorklist({sourceAddr});
412+
SmallPtrSet<SILPhiArgument *, 4> visitedPhis;
413+
Optional<AccessedStorage> storage;
414+
415+
while (!addressWorklist.empty()) {
416+
AccessedStorageResult result =
417+
getAccessedStorageFromAddress(addressWorklist.pop_back_val());
418+
419+
if (!result.isComplete()) {
420+
SILValue operAddr = result.getAddress();
421+
if (auto *phiArg = dyn_cast<SILPhiArgument>(operAddr)) {
422+
if (phiArg->isPhiArgument()) {
423+
if (visitedPhis.insert(phiArg).second)
424+
phiArg->getIncomingPhiValues(addressWorklist);
425+
continue;
303426
}
304-
return storage;
305427
}
306-
// A non-phi block argument may be a box value projected out of
307-
// switch_enum. Address-type block arguments are not allowed.
308-
if (address->getType().isAddress())
309-
return AccessedStorage();
310-
311-
checkSwitchEnumBlockArg(cast<SILPhiArgument>(address));
312-
return AccessedStorage(address, AccessedStorage::Unidentified);
313-
}
314-
// Load a box from an indirect payload of an opaque enum.
315-
// We must have peeked past the project_box earlier in this loop.
316-
// (the indirectness makes it a box, the load is for address-only).
317-
//
318-
// %payload_adr = unchecked_take_enum_data_addr %enum : $*Enum, #Enum.case
319-
// %box = load [take] %payload_adr : $*{ var Enum }
320-
//
321-
// FIXME: this case should go away with opaque values.
322-
//
323-
// Otherwise return invalid AccessedStorage.
324-
case ValueKind::LoadInst: {
325-
if (address->getType().is<SILBoxType>()) {
326-
address = cast<LoadInst>(address)->getOperand();
327-
assert(isa<UncheckedTakeEnumDataAddrInst>(address));
328-
continue;
329-
}
330-
return AccessedStorage();
331-
}
332-
333-
// ref_tail_addr project an address from a reference.
334-
// This is a valid address producer for nested @inout argument
335-
// access, but it is never used for formal access of identified objects.
336-
case ValueKind::RefTailAddrInst:
337-
return AccessedStorage(address, AccessedStorage::Unidentified);
338-
339-
// Inductive cases:
340-
// Look through address casts to find the source address.
341-
case ValueKind::MarkUninitializedInst:
342-
case ValueKind::OpenExistentialAddrInst:
343-
case ValueKind::UncheckedAddrCastInst:
344-
// Inductive cases that apply to any type.
345-
case ValueKind::CopyValueInst:
346-
case ValueKind::MarkDependenceInst:
347-
// Look through a project_box to identify the underlying alloc_box as the
348-
// accesed object. It must be possible to reach either the alloc_box or the
349-
// containing enum in this loop, only looking through simple value
350-
// propagation such as copy_value.
351-
case ValueKind::ProjectBoxInst:
352-
// Handle project_block_storage just like project_box.
353-
case ValueKind::ProjectBlockStorageInst:
354-
// Look through begin_borrow in case a local box is borrowed.
355-
case ValueKind::BeginBorrowInst:
356-
address = cast<SingleValueInstruction>(address)->getOperand(0);
357-
continue;
358-
359-
// Access to a Builtin.RawPointer. Treat this like the inductive cases
360-
// above because some RawPointers originate from identified locations. See
361-
// the special case for global addressors, which return RawPointer, above.
362-
//
363-
// If the inductive search does not find a valid addressor, it will
364-
// eventually reach the default case that returns in invalid location. This
365-
// is correct for RawPointer because, although accessing a RawPointer is
366-
// legal SIL, there is no way to guarantee that it doesn't access class or
367-
// global storage, so returning a valid unidentified storage object would be
368-
// incorrect. It is the caller's responsibility to know that formal access
369-
// to such a location can be safely ignored.
370-
//
371-
// For example:
372-
//
373-
// - KeyPath Builtins access RawPointer. However, the caller can check
374-
// that the access `isFromBuilin` and ignore the storage.
375-
//
376-
// - lldb generates RawPointer access for debugger variables, but SILGen
377-
// marks debug VarDecl access as 'Unsafe' and SIL passes don't need the
378-
// AccessedStorage for 'Unsafe' access.
379-
case ValueKind::PointerToAddressInst:
380-
address = cast<SingleValueInstruction>(address)->getOperand(0);
428+
addressWorklist.push_back(operAddr);
381429
continue;
382-
383-
// Address-to-address subobject projections.
384-
case ValueKind::StructElementAddrInst:
385-
case ValueKind::TupleElementAddrInst:
386-
case ValueKind::UncheckedTakeEnumDataAddrInst:
387-
case ValueKind::TailAddrInst:
388-
case ValueKind::IndexAddrInst:
389-
address = cast<SingleValueInstruction>(address)->getOperand(0);
430+
}
431+
if (!storage.hasValue()) {
432+
storage = result.getStorage();
390433
continue;
391434
}
435+
if (!accessingIdenticalLocations(storage.getValue(), result.getStorage()))
436+
return AccessedStorage();
392437
}
438+
return storage.getValueOr(AccessedStorage());
393439
}
394440

395441
AccessedStorage swift::findAccessedStorageNonNested(SILValue sourceAddr) {

test/SILOptimizer/verifier.sil

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
import Builtin
88

9+
sil_stage canonical
10+
911
// Don't fail in the verifier on a shared unreachable exit block of two loops.
1012
sil @dont_fail: $@convention(thin) (Builtin.Int1) -> () {
1113
bb0(%0 : $Builtin.Int1):
@@ -80,3 +82,28 @@ bb4:
8082
%10 = tuple ()
8183
return %10 : $()
8284
}
85+
86+
// Verify that findAccessedStorage handles cyclic phis.
87+
// <rdar://47059671> swiftc crashes
88+
class AClass {
89+
var aStoredProp: Builtin.Int32
90+
}
91+
92+
sil @testPhiCycle : $@convention(thin) (@guaranteed AClass) -> () {
93+
bb0(%0 : $AClass):
94+
%accessAddr = ref_element_addr %0 : $AClass, #AClass.aStoredProp
95+
br bbloop(%accessAddr : $*Builtin.Int32)
96+
97+
bbloop(%phiAddr : $*Builtin.Int32):
98+
%access = begin_access [read] [dynamic] [no_nested_conflict] %phiAddr : $*Builtin.Int32
99+
%val = load %access : $*Builtin.Int32
100+
end_access %access : $*Builtin.Int32
101+
cond_br undef, bbtail, bbreturn
102+
103+
bbtail:
104+
br bbloop(%phiAddr : $*Builtin.Int32)
105+
106+
bbreturn:
107+
%v = tuple ()
108+
return %v : $()
109+
}

0 commit comments

Comments
 (0)