xref: /llvm-project/clang/test/CodeGenObjC/arc-foreach.m (revision 94473f4db6a6f5f12d7c4081455b5b596094eac5)
1// RUN: %clang_cc1 -no-enable-noundef-analysis -triple x86_64-apple-darwin -fblocks -fobjc-arc -fobjc-runtime-has-weak -emit-llvm %s -o - | FileCheck -check-prefix CHECK-LP64 %s
2// RUN: %clang_cc1 -no-enable-noundef-analysis -triple x86_64-apple-darwin -O1 -fblocks -fobjc-arc -fobjc-runtime-has-weak -emit-llvm %s -o - | FileCheck -check-prefix CHECK-LP64-OPT %s
3
4extern void use(id);
5extern void use_block(void (^)(void));
6
7struct NSFastEnumerationState;
8@interface NSArray
9- (unsigned long) countByEnumeratingWithState: (struct NSFastEnumerationState*) state
10                  objects: (id*) buffer
11                  count: (unsigned long) bufferSize;
12@end;
13
14void test0(NSArray *array) {
15  // 'x' should be initialized without a retain.
16  // We should actually do a non-constant capture, and that
17  // capture should require a retain.
18  for (id x in array) {
19    use_block(^{ use(x); });
20  }
21}
22
23// CHECK-LP64-LABEL:    define{{.*}} void @test0(
24// CHECK-LP64:      [[ARRAY:%.*]] = alloca ptr,
25// CHECK-LP64-NEXT: [[X:%.*]] = alloca ptr,
26// CHECK-LP64-NEXT: [[STATE:%.*]] = alloca [[STATE_T:%.*]],
27// CHECK-LP64-NEXT: [[BUFFER:%.*]] = alloca [16 x ptr], align 8
28// CHECK-LP64-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
29
30// CHECK-LP64-OPT-LABEL: define{{.*}} void @test0
31// CHECK-LP64-OPT: [[STATE:%.*]] = alloca [[STATE_T:%.*]], align 8
32// CHECK-LP64-OPT-NEXT: [[BUFFER:%.*]] = alloca [16 x ptr], align 8
33// CHECK-LP64-OPT-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], align 8
34
35// Initialize 'array'.
36// CHECK-LP64-NEXT: store ptr null, ptr [[ARRAY]]
37// CHECK-LP64-NEXT: call void @llvm.objc.storeStrong(ptr [[ARRAY]], ptr {{%.*}}) [[NUW:#[0-9]+]]
38
39// Initialize the fast enumaration state.
40// CHECK-LP64-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[STATE]], i8 0, i64 64, i1 false)
41
42// Evaluate the collection expression and retain.
43// CHECK-LP64-NEXT: [[T0:%.*]] = load ptr, ptr [[ARRAY]], align 8
44// CHECK-LP64-NEXT: [[SAVED_ARRAY:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]])
45
46// Call the enumeration method.
47// CHECK-LP64-NEXT: [[T1:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
48// CHECK-LP64-NEXT: [[SIZE:%.*]] = call i64 @objc_msgSend(ptr [[SAVED_ARRAY]], ptr [[T1]], ptr [[STATE]], ptr [[BUFFER]], i64 16)
49
50// Check for a nonzero result.
51// CHECK-LP64-NEXT: [[T0:%.*]] = icmp eq i64 [[SIZE]], 0
52// CHECK-LP64-NEXT: br i1 [[T0]]
53
54// CHECK-LP64:      [[T0:%.*]] = getelementptr inbounds nuw [[STATE_T]], ptr [[STATE]], i32 0, i32 1
55// CHECK-LP64-NEXT: [[T1:%.*]] = load ptr, ptr [[T0]]
56// CHECK-LP64-NEXT: [[T2:%.*]] = getelementptr inbounds ptr, ptr [[T1]], i64
57// CHECK-LP64-NEXT: [[T3:%.*]] = load ptr, ptr [[T2]]
58// CHECK-LP64-NEXT: store ptr [[T3]], ptr [[X]]
59
60// CHECK-LP64:      [[CAPTURED:%.*]] = getelementptr inbounds nuw [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 5
61// CHECK-LP64-NEXT: [[T1:%.*]] = load ptr, ptr [[X]]
62// CHECK-LP64-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retain(ptr [[T1]])
63// CHECK-LP64-NEXT: store ptr [[T2]], ptr [[CAPTURED]]
64// CHECK-LP64-NEXT: call void @use_block(ptr [[BLOCK]])
65// CHECK-LP64-NEXT: call void @llvm.objc.storeStrong(ptr [[CAPTURED]], ptr null)
66// CHECK-LP64-NOT:  call void (...) @llvm.objc.clang.arc.use(
67
68// CHECK-LP64-OPT: [[D0:%.*]] = getelementptr inbounds nuw i8, ptr [[BLOCK]], i64 32
69// CHECK-LP64-OPT: [[CAPTURE:%.*]] = load ptr, ptr [[D0]]
70// CHECK-LP64-OPT: call void (...) @llvm.objc.clang.arc.use(ptr [[CAPTURE]])
71
72// CHECK-LP64: [[T1:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
73// CHECK-LP64-NEXT: [[SIZE:%.*]] = call i64 @objc_msgSend(ptr [[SAVED_ARRAY]], ptr [[T1]], ptr [[STATE]], ptr [[BUFFER]], i64 16)
74
75// Release the array.
76// CHECK-LP64: call void @llvm.objc.release(ptr [[SAVED_ARRAY]])
77
78// Destroy 'array'.
79// CHECK-LP64: call void @llvm.objc.storeStrong(ptr [[ARRAY]], ptr null)
80// CHECK-LP64-NEXT: ret void
81
82// CHECK-LP64-LABEL:    define internal void @__test0_block_invoke
83// CHECK-LP64-NOT:  ret
84// CHECK-LP64:      [[T0:%.*]] = getelementptr inbounds nuw [[BLOCK_T]], ptr {{%.*}}, i32 0, i32 5
85// CHECK-LP64-NEXT: [[T2:%.*]] = load ptr, ptr [[T0]], align 8
86// CHECK-LP64-NEXT: call void @use(ptr [[T2]])
87
88void test1(NSArray *array) {
89  for (__weak id x in array) {
90    use_block(^{ use(x); });
91  }
92}
93
94// CHECK-LP64-LABEL:    define{{.*}} void @test1(
95// CHECK-LP64:      alloca ptr,
96// CHECK-LP64-NEXT: [[X:%.*]] = alloca ptr,
97// CHECK-LP64-NEXT: [[STATE:%.*]] = alloca [[STATE_T:%.*]],
98// CHECK-LP64-NEXT: alloca [16 x ptr], align 8
99// CHECK-LP64-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
100
101// CHECK-LP64:      [[T0:%.*]] = getelementptr inbounds nuw [[STATE_T]], ptr [[STATE]], i32 0, i32 1
102// CHECK-LP64-NEXT: [[T1:%.*]] = load ptr, ptr [[T0]]
103// CHECK-LP64-NEXT: [[T2:%.*]] = getelementptr inbounds ptr, ptr [[T1]], i64
104// CHECK-LP64-NEXT: [[T3:%.*]] = load ptr, ptr [[T2]]
105// CHECK-LP64-NEXT: call ptr @llvm.objc.initWeak(ptr [[X]], ptr [[T3]])
106
107// CHECK-LP64:      [[T0:%.*]] = getelementptr inbounds nuw [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 5
108// CHECK-LP64: call void @llvm.objc.copyWeak(ptr [[T0]], ptr [[X]])
109// CHECK-LP64: call void @use_block
110// CHECK-LP64-NEXT: call void @llvm.objc.destroyWeak(ptr [[T0]])
111// CHECK-LP64-NEXT: call void @llvm.objc.destroyWeak(ptr [[X]])
112
113@interface Test2
114- (NSArray *) array;
115@end
116void test2(Test2 *a) {
117  for (id x in a.array) {
118    use(x);
119  }
120}
121
122// CHECK-LP64-LABEL:    define{{.*}} void @test2(
123// CHECK-LP64:      [[T0:%.*]] = call ptr @objc_msgSend(
124// CHECK-LP64-NEXT: [[T2:%.*]] = notail call ptr @llvm.objc.retainAutoreleasedReturnValue(ptr [[T0]])
125
126// Make sure it's not immediately released before starting the iteration.
127// CHECK-LP64-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
128// CHECK-LP64-NEXT: @objc_msgSend
129
130// CHECK-LP64: @objc_enumerationMutation
131
132// CHECK-LP64: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
133// CHECK-LP64-NEXT: @objc_msgSend
134
135// CHECK-LP64: call void @llvm.objc.release(ptr [[T2]])
136
137
138// Check that the 'continue' label is positioned appropriately
139// relative to the collection clenaup.
140void test3(NSArray *array) {
141  for (id x in array) {
142    if (!x) continue;
143    use(x);
144  }
145
146  // CHECK-LP64-LABEL:    define{{.*}} void @test3(
147  // CHECK-LP64:      [[ARRAY:%.*]] = alloca ptr, align 8
148  // CHECK-LP64-NEXT: [[X:%.*]] = alloca ptr, align 8
149  // CHECK-LP64:      [[T0:%.*]] = load ptr, ptr [[X]], align 8
150  // CHECK-LP64-NEXT: [[T1:%.*]] = icmp ne ptr [[T0]], null
151  // CHECK-LP64-NEXT: br i1 [[T1]],
152  // CHECK-LP64:      br label [[L:%[^ ]+]]
153  // CHECK-LP64:      [[T0:%.*]] = load ptr, ptr [[X]], align 8
154  // CHECK-LP64-NEXT: call void @use(ptr [[T0]])
155  // CHECK-LP64-NEXT: br label [[L]]
156}
157
158@interface NSObject @end
159
160@interface I1 : NSObject
161- (NSArray *) foo1:(void (^)(void))block;
162- (void) foo2;
163@end
164
165NSArray *array4;
166
167@implementation I1 : NSObject
168- (NSArray *) foo1:(void (^)(void))block {
169  block();
170  return array4;
171}
172
173- (void) foo2 {
174  for (id x in [self foo1:^{ use(self); }]) {
175    use(x);
176    break;
177  }
178}
179@end
180
181// CHECK-LP64-LABEL: define internal void @"\01-[I1 foo2]"(
182// CHECK-LP64:         [[SELF_ADDR:%.*]] = alloca ptr,
183// CHECK-LP64:         [[BLOCK:%.*]] = alloca <{ ptr, i32, i32, ptr, ptr, ptr }>,
184// CHECK-LP64:         store ptr %self, ptr [[SELF_ADDR]]
185// CHECK-LP64:         [[BC:%.*]] = getelementptr inbounds nuw <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr [[BLOCK]], i32 0, i32 5
186// CHECK-LP64:         [[T1:%.*]] = load ptr, ptr [[SELF_ADDR]]
187// CHECK-LP64:         call ptr @llvm.objc.retain(ptr [[T1]])
188
189// CHECK-LP64-OPT-LABEL: define internal void @"\01-[I1 foo2]"(
190// CHECK-LP64-OPT: ptr %self
191// CHECK-LP64-OPT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
192// CHECK-LP64-OPT: [[T0:%.*]] = getelementptr inbounds nuw i8, ptr [[BLOCK]], i64 32
193
194// CHECK-LP64:         call void @llvm.objc.storeStrong(ptr [[BC]], ptr null)
195// CHECK-LP64-NOT:     call void (...) @llvm.objc.clang.arc.use(ptr [[BC]])
196// CHECK-LP64:         switch i32 {{%.*}}, label %[[UNREACHABLE:.*]] [
197// CHECK-LP64-NEXT:      i32 0, label %[[CLEANUP_CONT:.*]]
198// CHECK-LP64-NEXT:      i32 2, label %[[FORCOLL_END:.*]]
199// CHECK-LP64-NEXT:    ]
200
201// CHECK-LP64-OPT: [[T5:%.*]] = load ptr, ptr [[T0]]
202// CHECK-LP64-OPT: call void (...) @llvm.objc.clang.arc.use(ptr [[T5]])
203
204// CHECK-LP64:       {{^|:}}[[CLEANUP_CONT]]
205// CHECK-LP64-NEXT:    br label %[[FORCOLL_END]]
206
207// CHECK-LP64:       {{^|:}}[[FORCOLL_END]]
208// CHECK-LP64-NEXT:    ret void
209
210// CHECK-LP64:       {{^|:}}[[UNREACHABLE]]
211// CHECK-LP64-NEXT:    unreachable
212
213// CHECK-LP64: attributes [[NUW]] = { nounwind }
214