Skip to content

Commit ae04531

Browse files
committed
Fix EscapeAnalysis value_to_bridge_object and strong_copy_unowned_value.
Noticed via code inspection. This could potentially miscompile, but we haven't seen that happen to my knowledge. Both value_to_bridge_object and strong_copy_XXX need to escape their resulting value. The implementation seemed to assume that it is conservatively correct simply to avoid building a connection graph node for an value. This is *not* true. Any value that has a pointer type requires a connection graph node. The only way to be conservative is to create the value node *and* point it to an escaping content node. We can always declare that certain special types are not considered pointer types, but then we need to handle all conversions from those types to pointer types by escaping the resulting pointer. BridgeObjects are often on the performance-critical path.
1 parent 7d61f75 commit ae04531

File tree

2 files changed

+50
-6
lines changed

2 files changed

+50
-6
lines changed

lib/SILOptimizer/Analysis/EscapeAnalysis.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,12 +2020,9 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
20202020
ConGraph->getNode(cast<SingleValueInstruction>(I));
20212021
return;
20222022

2023-
#define UNCHECKED_REF_STORAGE(Name, ...) \
2024-
case SILInstructionKind::StrongCopy##Name##ValueInst:
20252023
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
20262024
case SILInstructionKind::Name##RetainInst: \
2027-
case SILInstructionKind::StrongRetain##Name##Inst: \
2028-
case SILInstructionKind::StrongCopy##Name##ValueInst:
2025+
case SILInstructionKind::StrongRetain##Name##Inst:
20292026
#include "swift/AST/ReferenceStorage.def"
20302027
case SILInstructionKind::DeallocStackInst:
20312028
case SILInstructionKind::StrongRetainInst:
@@ -2043,8 +2040,11 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
20432040
case SILInstructionKind::SetDeallocatingInst:
20442041
case SILInstructionKind::FixLifetimeInst:
20452042
case SILInstructionKind::ClassifyBridgeObjectInst:
2046-
case SILInstructionKind::ValueToBridgeObjectInst:
2047-
// These instructions don't have any effect on escaping.
2043+
// Early bailout: These instructions never produce a pointer value and
2044+
// have no escaping effect on their operands.
2045+
assert(!llvm::any_of(I->getResults(), [this](SILValue result) {
2046+
return isPointer(result);
2047+
}));
20482048
return;
20492049

20502050
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \

test/SILOptimizer/escape_analysis.sil

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,3 +1844,47 @@ bb3(%4 : $AnyObject):
18441844
%5 = tuple ()
18451845
return %5 : $()
18461846
}
1847+
1848+
// Test value_to_bridge_object merged with a local reference. A graph node
1849+
// must be created for all values involved, and they must point to an
1850+
// escaping content node.
1851+
// CHECK-LABEL: CG of testValueToBridgeObject
1852+
// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%5.1)
1853+
// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%5.1)
1854+
// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2)
1855+
// CHECK-NEXT: Con %5.2 Esc: G, Succ:
1856+
// CHECK-NEXT: Val [ref] %7 Esc: , Succ: (%5.1), %1, %5
1857+
// CHECK-NEXT: Ret [ref] return Esc: , Succ: %7
1858+
// CHECK-LABEL: End
1859+
sil @testValueToBridgeObject : $@convention(thin) (Builtin.Word) -> C {
1860+
bb0(%0 : $Builtin.Word):
1861+
%1 = alloc_ref $C
1862+
cond_br undef, bb1, bb2
1863+
1864+
bb1:
1865+
%derived = ref_to_bridge_object %1 : $C, %0 : $Builtin.Word
1866+
br bb3(%derived : $Builtin.BridgeObject)
1867+
1868+
bb2:
1869+
%5 = value_to_bridge_object %0 : $Builtin.Word
1870+
br bb3(%5 : $Builtin.BridgeObject)
1871+
1872+
bb3(%7 : $Builtin.BridgeObject):
1873+
%result = bridge_object_to_ref %7 : $Builtin.BridgeObject to $C
1874+
return %result : $C
1875+
}
1876+
1877+
// Test strong_copy_unowned_value returned. It must be marked escaping,
1878+
// not simply "returned", because it's reference is formed from a
1879+
// function argument.
1880+
// CHECK-LABEL: CG of testStrongCopyUnowned
1881+
// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%1.1)
1882+
// CHECK-NEXT: Con [int] %1.1 Esc: G, Succ: (%1.2)
1883+
// CHECK-NEXT: Con %1.2 Esc: G, Succ:
1884+
// CHECK-NEXT: Ret [ref] return Esc: , Succ: %1
1885+
// CHECK-LABEL: End
1886+
sil [ossa] @testStrongCopyUnowned : $@convention(thin) (@guaranteed @sil_unowned Builtin.NativeObject) -> @owned Builtin.NativeObject {
1887+
bb0(%0 : @guaranteed $@sil_unowned Builtin.NativeObject):
1888+
%1 = strong_copy_unowned_value %0 : $@sil_unowned Builtin.NativeObject
1889+
return %1 : $Builtin.NativeObject
1890+
}

0 commit comments

Comments
 (0)