Skip to content

Commit 27e324d

Browse files
committed
Fix findAccessedStorage to handle cyclic phis.
Fixes infinite recursion in findAccessedStorge. This routine was not designed to be recursive, but recursion was recently added as a hack for address-type phis. Naturally, phis can be cyclic, so this was not a correct workaround. This is only a problem with enforce-exclusivity=checked, with which findAccessedStorge is invoked after arbitrary optimization passes that introduce address-type phis. Ultimately, address phis will be disallowed in SIL, but some optimizer passes must be fixed first. Fixes <rdar://problem/47059671> swiftlang-1001.0.31.11 root: swiftc crashes
1 parent bb3ae47 commit 27e324d

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)