summaryrefslogtreecommitdiffstats
path: root/test/CodeGenObjC/externally-retained.m
diff options
context:
space:
mode:
Diffstat (limited to 'test/CodeGenObjC/externally-retained.m')
-rw-r--r--test/CodeGenObjC/externally-retained.m115
1 files changed, 115 insertions, 0 deletions
diff --git a/test/CodeGenObjC/externally-retained.m b/test/CodeGenObjC/externally-retained.m
new file mode 100644
index 0000000000..0b4d0d648b
--- /dev/null
+++ b/test/CodeGenObjC/externally-retained.m
@@ -0,0 +1,115 @@
+// RUN: %clang_cc1 -triple x86_64-apple-macosx10.13.0 -fobjc-arc -fblocks -Wno-objc-root-class -O0 %s -S -emit-llvm -o - | FileCheck %s --dump-input-on-failure
+// RUN: %clang_cc1 -triple x86_64-apple-macosx10.13.0 -fobjc-arc -fblocks -Wno-objc-root-class -O0 -xobjective-c++ -std=c++11 %s -S -emit-llvm -o - | FileCheck %s --check-prefix CHECKXX --dump-input-on-failure
+
+#define EXT_RET __attribute__((objc_externally_retained))
+
+@interface ObjTy @end
+
+ObjTy *global;
+
+#if __cplusplus
+// Suppress name mangling in C++ mode for the sake of check lines.
+extern "C" void param(ObjTy *p);
+extern "C" void local();
+extern "C" void in_init();
+extern "C" void anchor();
+extern "C" void block_capture(ObjTy *);
+extern "C" void esc(void (^)());
+extern "C" void escp(void (^)(ObjTy *));
+extern "C" void block_param();
+#endif
+
+void param(ObjTy *p) EXT_RET {
+ // CHECK-LABEL: define void @param
+ // CHECK-NOT: llvm.objc.
+ // CHECK ret
+}
+
+void local() {
+ EXT_RET ObjTy *local = global;
+ // CHECK-LABEL: define void @local
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+
+void in_init() {
+ // Test that we do the right thing when a variable appears in it's own
+ // initializer. Here, we release the value stored in 'wat' after overwriting
+ // it, in case it was somehow set to point to a non-null object while it's
+ // initializer is being evaluated.
+ EXT_RET ObjTy *wat = 0 ? wat : global;
+
+ // CHECK-LABEL: define void @in_init
+ // CHECK: [[WAT:%.*]] = alloca
+ // CHECK-NEXT: store {{.*}} null, {{.*}} [[WAT]]
+ // CHECK-NEXT: [[GLOBAL:%.*]] = load {{.*}} @global
+ // CHECK-NEXT: [[WAT_LOAD:%.*]] = load {{.*}} [[WAT]]
+ // CHECK-NEXT: store {{.*}} [[GLOBAL]], {{.*}} [[WAT]]
+ // CHECK-NEXT: [[CASTED:%.*]] = bitcast {{.*}} [[WAT_LOAD]] to
+ // CHECK-NEXT: call void @llvm.objc.release(i8* [[CASTED]])
+
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+
+void esc(void (^)());
+
+void block_capture(ObjTy *obj) EXT_RET {
+ esc(^{ (void)obj; });
+
+ // CHECK-LABEL: define void @block_capture
+ // CHECK-NOT: llvm.objc.
+ // CHECK: call i8* @llvm.objc.retain
+ // CHECK-NOT: llvm.objc.
+ // CHECK: call void @esc
+ // CHECK-NOT: llvm.objc.
+ // CHECK: call void @llvm.objc.storeStrong({{.*}} null)
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+
+ // CHECK-LABEL: define {{.*}} void @__copy_helper_block_
+ // CHECK-NOT: llvm.objc.
+ // CHECK: llvm.objc.storeStrong
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+
+ // CHECK-LABEL: define {{.*}} void @__destroy_helper_block_
+ // CHECK-NOT: llvm.objc.
+ // CHECK: llvm.objc.storeStrong({{.*}} null)
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+
+void escp(void (^)(ObjTy *));
+
+void block_param() {
+ escp(^(ObjTy *p) EXT_RET {});
+
+ // CHECK-LABEL: define internal void @__block_param_block_invoke
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+
+@interface Inter
+-(void)m1: (ObjTy *)w;
+@end
+
+@implementation Inter
+-(void)m1: (ObjTy *) w EXT_RET {
+ // CHECK-LABEL: define internal void @"\01-[Inter m1:]"
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+-(void)m2: (ObjTy *) w EXT_RET {
+ // CHECK-LABEL: define internal void @"\01-[Inter m2:]"
+ // CHECK-NOT: llvm.objc.
+ // CHECK: ret
+}
+@end
+
+#if __cplusplus
+// Verify that the decltype(p) is resolved before 'p' is made implicitly const.
+__attribute__((objc_externally_retained))
+void foo(ObjTy *p, decltype(p) *) {}
+// CHECKXX: _Z3fooP5ObjTyPU8__strongS0_
+#endif