Skip to content

Commit 17cfa93

Browse files
committed
Add test cases for EscapeAnalysis.
Tests for commit fbe38ce: Fix EscapeAnalysis losing precision during merge. Array storage was being stack promoted even though it escaped. This happened because multiple locally allocated arrays were merged into the same locally allocated array value box. For this to become a problem, other bizarre merge events need to take place, such as a value node being mapped with a content node. The series of events led to a missing edge in the connection graph. One of the arrays was mapped directly to a project_box instruction which had forgotten it's relationship with the alloc_box. <rdar://58371330> app crashes (miscompile)~
1 parent fbe38ce commit 17cfa93

File tree

1 file changed

+216
-0
lines changed

1 file changed

+216
-0
lines changed
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// RUN: %target-sil-opt %s -escapes-dump -o /dev/null | %FileCheck %s
2+
3+
// REQUIRES: asserts
4+
// REQUIRES: OS=macosx
5+
// REQUIRES: PTRSIZE=64
6+
7+
sil_stage canonical
8+
9+
import Builtin
10+
import Swift
11+
import SwiftShims
12+
13+
// =============================================================================
14+
// Test call to array.uninitialized that has extra release_value uses
15+
16+
class C {
17+
var c: C
18+
}
19+
20+
class DummyArrayStorage<Element> {
21+
@_hasStorage var count: Int { get }
22+
@_hasStorage var capacity: Int { get }
23+
init()
24+
}
25+
26+
// init_any_array_with_buffer
27+
sil [_semantics "array.uninitialized"] @init_any_array_with_buffer : $@convention(thin) (@owned DummyArrayStorage<AnyObject>, Int32, @thin Array<AnyObject>.Type) -> (@owned Array<AnyObject>, UnsafeMutablePointer<AnyObject>)
28+
29+
// CHECK-LABEL: CG of testBadArrayUninit
30+
// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1)
31+
// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2)
32+
// CHECK-NEXT: Con [ref] %2.2 Esc: G, Succ:
33+
// CHECK-NEXT: Val %5 Esc: , Succ: (%5.1)
34+
// CHECK-NEXT: Con %5.1 Esc: G, Succ: %10
35+
// CHECK-NEXT: Val [ref] %10 Esc: G, Succ: (%10.1)
36+
// CHECK-NEXT: Con %10.1 Esc: G, Succ:
37+
// CHECK-LABEL: End
38+
sil hidden @testBadArrayUninit : $@convention(thin) (Builtin.Word, Int32) -> () {
39+
bb0(%0 : $Builtin.Word, %1 : $Int32):
40+
// create an array
41+
%2 = alloc_ref [tail_elems $AnyObject * %0 : $Builtin.Word] $DummyArrayStorage<AnyObject>
42+
%3 = metatype $@thin Array<AnyObject>.Type
43+
%4 = function_ref @init_any_array_with_buffer : $@convention(thin) (@owned DummyArrayStorage<AnyObject>, Int32, @thin Array<AnyObject>.Type) -> (@owned Array<AnyObject>, UnsafeMutablePointer<AnyObject>)
44+
%5 = apply %4(%2, %1, %3) : $@convention(thin) (@owned DummyArrayStorage<AnyObject>, Int32, @thin Array<AnyObject>.Type) -> (@owned Array<AnyObject>, UnsafeMutablePointer<AnyObject>)
45+
%6 = tuple_extract %5 : $(Array<AnyObject>, UnsafeMutablePointer<AnyObject>), 0
46+
%7 = tuple_extract %5 : $(Array<AnyObject>, UnsafeMutablePointer<AnyObject>), 1
47+
%8 = struct_extract %7 : $UnsafeMutablePointer<AnyObject>, #UnsafeMutablePointer._rawValue
48+
%9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*AnyObject
49+
50+
// store an elt
51+
%10 = alloc_ref $C
52+
%11 = init_existential_ref %10 : $C : $C, $AnyObject
53+
store %11 to %9 : $*AnyObject
54+
55+
// extra use of the call
56+
release_value %5 : $(Array<AnyObject>, UnsafeMutablePointer<AnyObject>) // id: %228
57+
%13 = tuple ()
58+
return %13 : $()
59+
}
60+
61+
// =============================================================================
62+
// testArrayEscapeToBox: test that an array is marked escaping when
63+
// assigned to a box. When multiple arrays are merged into the same
64+
// box, ensure that a previous mapping from the project_box address to
65+
// the box's content is not lost during the merge.
66+
67+
class ElementClass {
68+
init()
69+
}
70+
71+
class StagedContext {
72+
init()
73+
}
74+
75+
class VFSStagedContext : StagedContext {
76+
override init()
77+
}
78+
79+
// specialized Array.init()
80+
sil @$sS2ayxGycfCSo12ElementClassC_Tg5 : $@convention(method) (@thin Array<ElementClass>.Type) -> @owned Array<ElementClass>
81+
82+
// specialized Array._getCount()
83+
sil @$sSa9_getCountSiyFSo12ElementClassC_Tg5 : $@convention(method) (@guaranteed Array<ElementClass>) -> Int
84+
85+
// specialized static Array._adoptStorage(_:count:)
86+
sil shared [_semantics "array.uninitialized"] @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo12ElementClassC_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage<ElementClass>, Int, @thin Array<ElementClass>.Type) -> (@owned Array<ElementClass>, UnsafeMutablePointer<ElementClass>) {
87+
// %0 // users: %13, %3
88+
// %1 // users: %9, %4
89+
bb0(%0 : $_ContiguousArrayStorage<ElementClass>, %1 : $Int, %2 : $@thin Array<ElementClass>.Type):
90+
%3 = upcast %0 : $_ContiguousArrayStorage<ElementClass> to $__ContiguousArrayStorageBase // users: %17, %11
91+
%4 = struct_extract %1 : $Int, #Int._value // user: %6
92+
%5 = integer_literal $Builtin.Int64, 1 // users: %7, %6
93+
%6 = builtin "shl_Int64"(%4 : $Builtin.Int64, %5 : $Builtin.Int64) : $Builtin.Int64 // user: %7
94+
%7 = builtin "or_Int64"(%6 : $Builtin.Int64, %5 : $Builtin.Int64) : $Builtin.Int64 // user: %8
95+
%8 = struct $UInt (%7 : $Builtin.Int64) // user: %9
96+
%9 = struct $_SwiftArrayBodyStorage (%1 : $Int, %8 : $UInt) // user: %10
97+
%10 = struct $_ArrayBody (%9 : $_SwiftArrayBodyStorage) // user: %12
98+
%11 = ref_element_addr %3 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity // user: %12
99+
store %10 to %11 : $*_ArrayBody // id: %12
100+
%13 = unchecked_ref_cast %0 : $_ContiguousArrayStorage<ElementClass> to $Builtin.BridgeObject // user: %14
101+
%14 = struct $_BridgeStorage<__ContiguousArrayStorageBase> (%13 : $Builtin.BridgeObject) // user: %15
102+
%15 = struct $_ArrayBuffer<ElementClass> (%14 : $_BridgeStorage<__ContiguousArrayStorageBase>) // user: %16
103+
%16 = struct $Array<ElementClass> (%15 : $_ArrayBuffer<ElementClass>) // user: %20
104+
%17 = ref_tail_addr %3 : $__ContiguousArrayStorageBase, $ElementClass // user: %18
105+
%18 = address_to_pointer %17 : $*ElementClass to $Builtin.RawPointer // user: %19
106+
%19 = struct $UnsafeMutablePointer<ElementClass> (%18 : $Builtin.RawPointer) // user: %20
107+
%20 = tuple (%16 : $Array<ElementClass>, %19 : $UnsafeMutablePointer<ElementClass>) // user: %21
108+
return %20 : $(Array<ElementClass>, UnsafeMutablePointer<ElementClass>) // id: %21
109+
}
110+
111+
// testArrayUsePointsClosure1
112+
sil @testArrayUsePointsClosure1 : $@convention(thin) (@guaranteed { var Optional<VFSStagedContext> }, @guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> ()) -> ()
113+
114+
// testArrayUsePointsClosure2
115+
sil @testArrayUsePointsClosure2 : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> (), @guaranteed Optional<VFSStagedContext>, @guaranteed { var Array<ElementClass> }) -> ()
116+
117+
// Make sure both locally allocated array's are globally escaping.
118+
//
119+
// CHECK-LABEL: CG of testArrayEscapeToBox
120+
// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%21)
121+
// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%21)
122+
// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %0, %1
123+
// CHECK-NEXT: Val [ref] %8 Esc: , Succ: (%21)
124+
// CHECK-NEXT: Val [ref] %12 Esc: G, Succ: (%21)
125+
// CHECK-NEXT: Val [ref] %17 Esc: G, Succ: (%21)
126+
// CHECK-NEXT: Val [ref] %20 Esc: G, Succ: %17
127+
// CHECK-NEXT: Con [int] %21 Esc: G, Succ: (%39)
128+
// CHECK-NEXT: Val [ref] %31 Esc: G, Succ: (%21)
129+
// CHECK-NEXT: Val [ref] %34 Esc: G, Succ: %31
130+
// CHECK-NEXT: Con %39 Esc: G, Succ: (%21), %12, %20, %34
131+
// CHECK-NEXT: Val [ref] %45 Esc: , Succ: %0, %8, %39
132+
// CHECK-NEXT: End
133+
sil private @testArrayEscapeToBox : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> ()) -> () {
134+
// %0 // users: %54, %51, %47, %45, %5, %4
135+
bb0(%0 : $@callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> ()):
136+
%1 = alloc_box ${ var Optional<VFSStagedContext> }, var, name "context" // users: %59, %6, %4, %2
137+
%2 = project_box %1 : ${ var Optional<VFSStagedContext> }, 0 // user: %44
138+
// function_ref testArrayUsePointsClosure1
139+
%3 = function_ref @testArrayUsePointsClosure1 : $@convention(thin) (@guaranteed { var Optional<VFSStagedContext> }, @guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> ()) -> () // user: %4
140+
%4 = partial_apply [callee_guaranteed] %3(%1, %0) : $@convention(thin) (@guaranteed { var Optional<VFSStagedContext> }, @guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> ()) -> ()
141+
strong_retain %0 : $@callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> () // id: %5
142+
strong_retain %1 : ${ var Optional<VFSStagedContext> } // id: %6
143+
br bb1 // id: %7
144+
145+
bb1: // Preds: bb0
146+
%8 = alloc_box ${ var Array<ElementClass> }, var, name "intents" // users: %58, %56, %52, %48, %45, %9
147+
%9 = project_box %8 : ${ var Array<ElementClass> }, 0 // users: %41, %36, %27, %22, %13
148+
%10 = metatype $@thin Array<ElementClass>.Type // users: %33, %19, %12
149+
// function_ref specialized Array.init()
150+
%11 = function_ref @$sS2ayxGycfCSo12ElementClassC_Tg5 : $@convention(method) (@thin Array<ElementClass>.Type) -> @owned Array<ElementClass> // user: %12
151+
%12 = apply %11(%10) : $@convention(method) (@thin Array<ElementClass>.Type) -> @owned Array<ElementClass> // user: %13
152+
store %12 to %9 : $*Array<ElementClass> // id: %13
153+
cond_br undef, bb2, bb3 // id: %14
154+
155+
bb2: // Preds: bb1
156+
%15 = integer_literal $Builtin.Int64, 1 // user: %16
157+
%16 = struct $Int (%15 : $Builtin.Int64) // user: %19
158+
%17 = alloc_ref [tail_elems $ElementClass * undef : $Builtin.Word] $_ContiguousArrayStorage<ElementClass> // user: %19
159+
// function_ref specialized static Array._adoptStorage(_:count:)
160+
%18 = function_ref @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo12ElementClassC_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage<ElementClass>, Int, @thin Array<ElementClass>.Type) -> (@owned Array<ElementClass>, UnsafeMutablePointer<ElementClass>) // user: %19
161+
%19 = apply %18(%17, %16, %10) : $@convention(method) (@owned _ContiguousArrayStorage<ElementClass>, Int, @thin Array<ElementClass>.Type) -> (@owned Array<ElementClass>, UnsafeMutablePointer<ElementClass>) // users: %21, %20
162+
%20 = tuple_extract %19 : $(Array<ElementClass>, UnsafeMutablePointer<ElementClass>), 0 // user: %27
163+
%21 = tuple_extract %19 : $(Array<ElementClass>, UnsafeMutablePointer<ElementClass>), 1
164+
%22 = struct_element_addr %9 : $*Array<ElementClass>, #Array._buffer // user: %23
165+
%23 = struct_element_addr %22 : $*_ArrayBuffer<ElementClass>, #_ArrayBuffer._storage // user: %24
166+
%24 = struct_element_addr %23 : $*_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue // user: %25
167+
%25 = load %24 : $*Builtin.BridgeObject // user: %26
168+
strong_release %25 : $Builtin.BridgeObject // id: %26
169+
store %20 to %9 : $*Array<ElementClass> // id: %27
170+
br bb4 // id: %28
171+
172+
bb3: // Preds: bb1
173+
%29 = integer_literal $Builtin.Int64, 1 // user: %30
174+
%30 = struct $Int (%29 : $Builtin.Int64) // user: %33
175+
%31 = alloc_ref [tail_elems $ElementClass * undef : $Builtin.Word] $_ContiguousArrayStorage<ElementClass> // user: %33
176+
// function_ref specialized static Array._adoptStorage(_:count:)
177+
%32 = function_ref @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo12ElementClassC_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage<ElementClass>, Int, @thin Array<ElementClass>.Type) -> (@owned Array<ElementClass>, UnsafeMutablePointer<ElementClass>) // user: %33
178+
%33 = apply %32(%31, %30, %10) : $@convention(method) (@owned _ContiguousArrayStorage<ElementClass>, Int, @thin Array<ElementClass>.Type) -> (@owned Array<ElementClass>, UnsafeMutablePointer<ElementClass>) // users: %35, %34
179+
%34 = tuple_extract %33 : $(Array<ElementClass>, UnsafeMutablePointer<ElementClass>), 0 // user: %41
180+
%35 = tuple_extract %33 : $(Array<ElementClass>, UnsafeMutablePointer<ElementClass>), 1
181+
%36 = struct_element_addr %9 : $*Array<ElementClass>, #Array._buffer // user: %37
182+
%37 = struct_element_addr %36 : $*_ArrayBuffer<ElementClass>, #_ArrayBuffer._storage // user: %38
183+
%38 = struct_element_addr %37 : $*_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue // user: %39
184+
%39 = load %38 : $*Builtin.BridgeObject // user: %40
185+
strong_release %39 : $Builtin.BridgeObject // id: %40
186+
store %34 to %9 : $*Array<ElementClass> // id: %41
187+
br bb4 // id: %42
188+
189+
bb4: // Preds: bb3 bb2
190+
// function_ref testArrayUsePointsClosure2
191+
%43 = function_ref @testArrayUsePointsClosure2 : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> (), @guaranteed Optional<VFSStagedContext>, @guaranteed { var Array<ElementClass> }) -> () // user: %45
192+
%44 = load %2 : $*Optional<VFSStagedContext> // users: %55, %53, %49, %45
193+
%45 = partial_apply [callee_guaranteed] %43(%0, %44, %8) : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> (), @guaranteed Optional<VFSStagedContext>, @guaranteed { var Array<ElementClass> }) -> () // user: %57
194+
// function_ref specialized Array._getCount()
195+
%46 = function_ref @$sSa9_getCountSiyFSo12ElementClassC_Tg5 : $@convention(method) (@guaranteed Array<ElementClass>) -> Int
196+
strong_retain %0 : $@callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> () // id: %47
197+
strong_retain %8 : ${ var Array<ElementClass> } // id: %48
198+
retain_value %44 : $Optional<VFSStagedContext> // id: %49
199+
br bb5 // id: %50
200+
201+
bb5: // Preds: bb4
202+
strong_retain %0 : $@callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> () // id: %51
203+
strong_retain %8 : ${ var Array<ElementClass> } // id: %52
204+
retain_value %44 : $Optional<VFSStagedContext> // id: %53
205+
strong_release %0 : $@callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> () // id: %54
206+
release_value %44 : $Optional<VFSStagedContext> // id: %55
207+
strong_release %8 : ${ var Array<ElementClass> } // id: %56
208+
strong_release %45 : $@callee_guaranteed () -> () // id: %57
209+
strong_release %8 : ${ var Array<ElementClass> } // id: %58
210+
strong_release %1 : ${ var Optional<VFSStagedContext> } // id: %59
211+
br bb6 // id: %60
212+
213+
bb6: // Preds: bb5
214+
%61 = tuple () // user: %62
215+
return %61 : $() // id: %62
216+
}

0 commit comments

Comments
 (0)