
Now that #149310 has restricted lifetime intrinsics to only work on allocas, we can also drop the explicit size argument. Instead, the size is implied by the alloca. This removes the ability to only mark a prefix of an alloca alive/dead. We never used that capability, so we should remove the need to handle that possibility everywhere (though many key places, including stack coloring, did not actually respect this).
1405 lines
55 KiB
Objective-C
1405 lines
55 KiB
Objective-C
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-objc-root-class -Wno-incompatible-pointer-types -Wno-arc-unsafe-retained-assign -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s
|
|
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-objc-root-class -Wno-incompatible-pointer-types -Wno-arc-unsafe-retained-assign -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-GLOBALS %s
|
|
|
|
// Check both native/non-native arc platforms. Here we check that they treat
|
|
// nonlazybind differently.
|
|
// RUN: %clang_cc1 -fobjc-runtime=macosx-10.6.0 -triple x86_64-apple-darwin10 -Wno-objc-root-class -Wno-incompatible-pointer-types -Wno-arc-unsafe-retained-assign -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=ARC-ALIEN %s
|
|
// RUN: %clang_cc1 -fobjc-runtime=macosx-10.7.0 -triple x86_64-apple-darwin11 -Wno-objc-root-class -Wno-incompatible-pointer-types -Wno-arc-unsafe-retained-assign -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=ARC-NATIVE %s
|
|
|
|
// ARC-ALIEN: declare extern_weak void @llvm.objc.storeStrong(ptr, ptr)
|
|
// ARC-ALIEN: declare extern_weak ptr @llvm.objc.retain(ptr returned)
|
|
// ARC-ALIEN: declare extern_weak ptr @llvm.objc.autoreleaseReturnValue(ptr returned)
|
|
// ARC-ALIEN: declare ptr @objc_msgSend(ptr, ptr, ...) [[NLB:#[0-9]+]]
|
|
// ARC-ALIEN: declare extern_weak void @llvm.objc.release(ptr)
|
|
// ARC-ALIEN: declare extern_weak ptr @llvm.objc.retainAutoreleasedReturnValue(ptr)
|
|
// ARC-ALIEN: declare extern_weak ptr @llvm.objc.initWeak(ptr, ptr)
|
|
// ARC-ALIEN: declare extern_weak ptr @llvm.objc.storeWeak(ptr, ptr)
|
|
// ARC-ALIEN: declare extern_weak ptr @llvm.objc.loadWeakRetained(ptr)
|
|
// ARC-ALIEN: declare extern_weak void @llvm.objc.destroyWeak(ptr)
|
|
// ARC-ALIEN: declare extern_weak ptr @llvm.objc.autorelease(ptr returned)
|
|
// ARC-ALIEN: declare extern_weak ptr @llvm.objc.retainAutorelease(ptr returned)
|
|
|
|
// ARC-NATIVE: declare void @llvm.objc.storeStrong(ptr, ptr)
|
|
// ARC-NATIVE: declare ptr @llvm.objc.retain(ptr returned)
|
|
// ARC-NATIVE: declare ptr @llvm.objc.autoreleaseReturnValue(ptr returned)
|
|
// ARC-NATIVE: declare ptr @objc_msgSend(ptr, ptr, ...) [[NLB:#[0-9]+]]
|
|
// ARC-NATIVE: declare void @llvm.objc.release(ptr)
|
|
// ARC-NATIVE: declare ptr @llvm.objc.retainAutoreleasedReturnValue(ptr)
|
|
// ARC-NATIVE: declare ptr @llvm.objc.initWeak(ptr, ptr)
|
|
// ARC-NATIVE: declare ptr @llvm.objc.storeWeak(ptr, ptr)
|
|
// ARC-NATIVE: declare ptr @llvm.objc.loadWeakRetained(ptr)
|
|
// ARC-NATIVE: declare void @llvm.objc.destroyWeak(ptr)
|
|
// ARC-NATIVE: declare ptr @llvm.objc.autorelease(ptr returned)
|
|
// ARC-NATIVE: declare ptr @llvm.objc.retainAutorelease(ptr returned)
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test0
|
|
void test0(id x) {
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
// CHECK-NEXT: [[PARM:%.*]] = call ptr @llvm.objc.retain(ptr {{.*}})
|
|
// CHECK-NEXT: store ptr [[PARM]], ptr [[X]]
|
|
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
// CHECK-LABEL: define{{.*}} ptr @test1(ptr
|
|
id test1(id x) {
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
// CHECK-NEXT: [[Y:%.*]] = alloca ptr
|
|
// CHECK-NEXT: [[PARM:%.*]] = call ptr @llvm.objc.retain(ptr {{%.*}})
|
|
// CHECK-NEXT: store ptr [[PARM]], ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[Y]])
|
|
// CHECK-NEXT: store ptr null, ptr [[Y]]
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[Y]]
|
|
// CHECK-NEXT: [[RET:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]])
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[Y]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]])
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[Y]])
|
|
// CHECK-NEXT: [[T1:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]])
|
|
// CHECK-NEXT: [[T1:%.*]] = tail call ptr @llvm.objc.autoreleaseReturnValue(ptr [[RET]])
|
|
// CHECK-NEXT: ret ptr [[T1]]
|
|
id y;
|
|
return y;
|
|
}
|
|
|
|
@interface Test2
|
|
+ (void) class_method;
|
|
- (void) inst_method;
|
|
@end
|
|
@implementation Test2
|
|
|
|
// The self pointer of a class method is not retained.
|
|
// CHECK: define internal void @"\01+[Test2 class_method]"
|
|
// CHECK: alloca
|
|
// CHECK-NEXT: alloca
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: ret void
|
|
+ (void) class_method {}
|
|
|
|
// The self pointer of an instance method is not retained.
|
|
// CHECK: define internal void @"\01-[Test2 inst_method]"
|
|
// CHECK: alloca
|
|
// CHECK-NEXT: alloca
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: ret void
|
|
- (void) inst_method {}
|
|
@end
|
|
|
|
@interface Test3
|
|
+ (id) alloc;
|
|
- (id) initWith: (int) x;
|
|
- (id) copy;
|
|
@end
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test3_unelided()
|
|
void test3_unelided(void) {
|
|
extern void test3_helper(void);
|
|
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: store ptr null, ptr [[X]], align
|
|
Test3 *x;
|
|
|
|
// Call to +alloc.
|
|
// CHECK-NEXT: load {{.*}}, ptr @"OBJC_CLASSLIST_REFERENCES_
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[ALLOC:%.*]] = call ptr @objc_msgSend
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr
|
|
[Test3 alloc];
|
|
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[COPY:%.*]] = call ptr @objc_msgSend(ptr noundef [[T0]],
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[COPY]]) [[NUW:#[0-9]+]]
|
|
[x copy];
|
|
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]]
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test3()
|
|
void test3(void) {
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
|
|
id x = [[Test3 alloc] initWith: 5];
|
|
|
|
// Call to +alloc.
|
|
// CHECK-NEXT: load {{.*}}, ptr @"OBJC_CLASSLIST_REFERENCES_
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[ALLOC:%.*]] = call ptr @objc_msgSend
|
|
|
|
// Call to -initWith: with elided retain of consumed argument.
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[INIT:%.*]] = call ptr @objc_msgSend(ptr
|
|
// Assignment for initialization, retention elided.
|
|
// CHECK-NEXT: store ptr [[INIT]], ptr [[X]]
|
|
|
|
// Call to -copy.
|
|
// CHECK-NEXT: [[V:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[COPY:%.*]] = call ptr @objc_msgSend(ptr noundef [[V]],
|
|
|
|
// Assignment to x.
|
|
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: store ptr [[COPY]], ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP]]) [[NUW]]
|
|
|
|
x = [x copy];
|
|
|
|
// Cleanup for x.
|
|
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP]]) [[NUW]]
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
// CHECK-LABEL: define{{.*}} ptr @test4()
|
|
id test4(void) {
|
|
// Call to +alloc.
|
|
// CHECK: load {{.*}}, ptr @"OBJC_CLASSLIST_REFERENCES_
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[ALLOC:%.*]] = call ptr @objc_msgSend
|
|
|
|
// Call to -initWith: with elided retain of consumed argument.
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[INIT:%.*]] = call ptr @objc_msgSend(ptr noundef [[ALLOC]],
|
|
|
|
// Initialization of return value, occurring within full-expression.
|
|
// Retain/release elided.
|
|
// CHECK-NEXT: [[RET:%.*]] = tail call ptr @llvm.objc.autoreleaseReturnValue(ptr [[INIT]])
|
|
|
|
// CHECK-NEXT: ret ptr [[RET]]
|
|
|
|
return [[Test3 alloc] initWith: 6];
|
|
}
|
|
|
|
@interface Test5 {
|
|
@public
|
|
id var;
|
|
}
|
|
@end
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test5
|
|
void test5(Test5 *x, id y) {
|
|
// Prologue.
|
|
// CHECK: [[X:%.*]] = alloca ptr,
|
|
// CHECK-NEXT: [[Y:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call ptr @llvm.objc.retain
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[X]]
|
|
// CHECK-NEXT: call ptr @llvm.objc.retain
|
|
// CHECK-NEXT: store
|
|
|
|
// CHECK-NEXT: load ptr, ptr [[X]]
|
|
// CHECK-NEXT: load i64, ptr @"OBJC_IVAR_$_Test5.var"
|
|
// CHECK-NEXT: [[VAR:%.*]] = getelementptr
|
|
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr [[VAR]]
|
|
// CHECK-NEXT: store ptr null, ptr [[VAR]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP]]) [[NUW]]
|
|
x->var = 0;
|
|
|
|
// CHECK-NEXT: [[YVAL:%.*]] = load ptr, ptr [[Y]]
|
|
// CHECK-NEXT: load ptr, ptr [[X]]
|
|
// CHECK-NEXT: load i64, ptr @"OBJC_IVAR_$_Test5.var"
|
|
// CHECK-NEXT: [[VAR:%.*]] = getelementptr
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @llvm.objc.retain(ptr [[YVAL]]) [[NUW]]
|
|
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr [[VAR]]
|
|
// CHECK-NEXT: store ptr [[T0]], ptr [[VAR]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP]]) [[NUW]]
|
|
x->var = y;
|
|
|
|
// Epilogue.
|
|
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr [[Y]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[TMP]]) [[NUW]]
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]]
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
id test6_helper(void) __attribute__((ns_returns_retained));
|
|
// CHECK-LABEL: define{{.*}} void @test6()
|
|
void test6(void) {
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: [[CALL:%.*]] = call ptr @test6_helper()
|
|
// CHECK-NEXT: store ptr [[CALL]], ptr [[X]]
|
|
// CHECK-NEXT: [[T1:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]]) [[NUW]], !clang.imprecise_release
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
id x = test6_helper();
|
|
}
|
|
|
|
void test7_helper(id __attribute__((ns_consumed)));
|
|
// CHECK-LABEL: define{{.*}} void @test7()
|
|
void test7(void) {
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: store ptr null, ptr [[X]]
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]]) [[NUW]]
|
|
// CHECK-NEXT: call void @test7_helper(ptr noundef [[T1]])
|
|
// CHECK-NEXT: [[T1:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]]) [[NUW]], !clang.imprecise_release
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
id x;
|
|
test7_helper(x);
|
|
}
|
|
|
|
id test8_helper(void) __attribute__((ns_returns_retained));
|
|
void test8(void) {
|
|
__unsafe_unretained id x = test8_helper();
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @test8_helper()
|
|
// CHECK-NEXT: store ptr [[T0]], ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]], !clang.imprecise_release
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
@interface Test10
|
|
@property (retain) Test10 *me;
|
|
@end
|
|
void test10(void) {
|
|
Test10 *x;
|
|
id y = x.me.me;
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test10()
|
|
// CHECK: [[X:%.*]] = alloca ptr, align
|
|
// CHECK-NEXT: [[Y:%.*]] = alloca ptr, align
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: store ptr null, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[Y]])
|
|
// CHECK-NEXT: load ptr, ptr [[X]], align
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_{{[0-9]*}}
|
|
// CHECK-NEXT: [[V:%.*]] = call ptr @objc_msgSend{{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[V]])
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_{{[0-9]*}}
|
|
// CHECK-NEXT: [[T3:%.*]] = call ptr @objc_msgSend{{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T3]])
|
|
// CHECK-NEXT: store ptr [[T3]], ptr [[Y]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[V]])
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[Y]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]])
|
|
// CHECK-NEXT: void @llvm.lifetime.end.p0(ptr [[Y]])
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]])
|
|
// CHECK-NEXT: void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
void test11(id (*f)(void) __attribute__((ns_returns_retained))) {
|
|
// CHECK-LABEL: define{{.*}} void @test11(
|
|
// CHECK: [[F:%.*]] = alloca ptr, align
|
|
// CHECK-NEXT: [[X:%.*]] = alloca ptr, align
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[F]], align
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[F]], align
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr [[T0]]()
|
|
// CHECK-NEXT: store ptr [[T1]], ptr [[X]], align
|
|
// CHECK-NEXT: [[T3:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T3]]) [[NUW]], !clang.imprecise_release
|
|
// CHECK-NEXT: void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
id x = f();
|
|
}
|
|
|
|
void test12(void) {
|
|
extern id test12_helper(void);
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test12()
|
|
// CHECK: [[X:%.*]] = alloca ptr, align
|
|
// CHECK-NEXT: [[Y:%.*]] = alloca ptr, align
|
|
|
|
__weak id x = test12_helper();
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @test12_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: call ptr @llvm.objc.initWeak(ptr [[X]], ptr [[T1]])
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]])
|
|
|
|
x = test12_helper();
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @test12_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: call ptr @llvm.objc.storeWeak(ptr [[X]], ptr [[T1]])
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]])
|
|
|
|
id y = x;
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[Y]])
|
|
// CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.loadWeakRetained(ptr [[X]])
|
|
// CHECK-NEXT: store ptr [[T2]], ptr [[Y]], align
|
|
|
|
// CHECK-NEXT: [[T4:%.*]] = load ptr, ptr [[Y]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T4]]) [[NUW]], !clang.imprecise_release
|
|
// CHECK-NEXT: void @llvm.lifetime.end.p0(ptr [[Y]])
|
|
// CHECK-NEXT: call void @llvm.objc.destroyWeak(ptr [[X]])
|
|
// CHECK-NEXT: void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK: ret void
|
|
}
|
|
|
|
// Indirect consuming calls.
|
|
void test13(void) {
|
|
// CHECK-LABEL: define{{.*}} void @test13()
|
|
// CHECK: [[X:%.*]] = alloca ptr, align
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: store ptr null, ptr [[X]], align
|
|
id x;
|
|
|
|
typedef void fnty(id __attribute__((ns_consumed)));
|
|
extern fnty *test13_func;
|
|
// CHECK-NEXT: [[FN:%.*]] = load ptr, ptr @test13_func, align
|
|
// CHECK-NEXT: [[X_VAL:%.*]] = load ptr, ptr [[X]], align
|
|
// CHECK-NEXT: [[X_TMP:%.*]] = call ptr @llvm.objc.retain(ptr [[X_VAL]]) [[NUW]]
|
|
// CHECK-NEXT: call void [[FN]](ptr noundef [[X_TMP]])
|
|
test13_func(x);
|
|
|
|
extern fnty ^test13_block;
|
|
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr @test13_block, align
|
|
// CHECK-NEXT: [[BLOCK_FN_PTR:%.*]] = getelementptr inbounds nuw [[BLOCKTY:%.*]], ptr [[TMP]], i32 0, i32 3
|
|
// CHECK-NEXT: [[X_VAL:%.*]] = load ptr, ptr [[X]], align
|
|
// CHECK-NEXT: [[X_TMP:%.*]] = call ptr @llvm.objc.retain(ptr [[X_VAL]]) [[NUW]]
|
|
// CHECK-NEXT: [[BLOCK_FN_TMP:%.*]] = load ptr, ptr [[BLOCK_FN_PTR]]
|
|
// CHECK-NEXT: call void [[BLOCK_FN_TMP]](ptr noundef [[TMP]], ptr noundef [[X_TMP]])
|
|
test13_block(x);
|
|
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]]
|
|
// CHECK-NEXT: void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
@interface Test16_super @end
|
|
@interface Test16 : Test16_super {
|
|
id z;
|
|
}
|
|
@property (assign) int x;
|
|
@property (retain) id y;
|
|
- (void) dealloc;
|
|
@end
|
|
@implementation Test16
|
|
@synthesize x;
|
|
@synthesize y;
|
|
- (void) dealloc {
|
|
// CHECK: define internal void @"\01-[Test16 dealloc]"(
|
|
// CHECK: [[SELF:%.*]] = alloca ptr, align
|
|
// CHECK-NEXT: [[CMD:%.*]] = alloca ptr, align
|
|
// CHECK-NEXT: alloca
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[SELF]], align
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[CMD]]
|
|
// CHECK-NEXT: [[BASE:%.*]] = load ptr, ptr [[SELF]]
|
|
|
|
// Call super.
|
|
// CHECK-NEXT: [[T0:%.*]] = getelementptr
|
|
// CHECK-NEXT: store ptr [[BASE]], ptr [[T0]]
|
|
// CHECK-NEXT: load ptr, ptr @"OBJC_CLASSLIST_SUP_REFS_$_
|
|
// CHECK-NEXT: getelementptr
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: call void @objc_msgSendSuper2(
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
// .cxx_destruct
|
|
// CHECK: define internal void @"\01-[Test16 .cxx_destruct]"(
|
|
// CHECK: [[SELF:%.*]] = alloca ptr, align
|
|
// CHECK-NEXT: [[CMD:%.*]] = alloca ptr, align
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[SELF]], align
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[CMD]]
|
|
// CHECK-NEXT: [[BASE:%.*]] = load ptr, ptr [[SELF]]
|
|
|
|
// Destroy y.
|
|
// CHECK-NEXT: [[Y_OFF:%.*]] = load i64, ptr @"OBJC_IVAR_$_Test16.y"
|
|
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8, ptr [[BASE]], i64 [[Y_OFF]]
|
|
// CHECK-NEXT: call void @llvm.objc.storeStrong(ptr [[T1]], ptr null) [[NUW]]
|
|
|
|
// Destroy z.
|
|
// CHECK-NEXT: [[Z_OFF:%.*]] = load i64, ptr @"OBJC_IVAR_$_Test16.z"
|
|
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8, ptr [[BASE]], i64 [[Z_OFF]]
|
|
// CHECK-NEXT: call void @llvm.objc.storeStrong(ptr [[T1]], ptr null) [[NUW]]
|
|
|
|
// CHECK-NEXT: ret void
|
|
|
|
@end
|
|
|
|
// This shouldn't crash.
|
|
@interface Test17A
|
|
@property (assign) int x;
|
|
@end
|
|
@interface Test17B : Test17A
|
|
@end
|
|
@implementation Test17B
|
|
- (int) x { return super.x + 1; }
|
|
@end
|
|
|
|
void test19(void) {
|
|
// CHECK-LABEL: define{{.*}} void @test19()
|
|
// CHECK: [[X:%.*]] = alloca [5 x ptr], align 16
|
|
// CHECK: call void @llvm.lifetime.start
|
|
// CHECK: call void @llvm.memset.p0.i64(ptr align 16 [[X]], i8 0, i64 40, i1 false)
|
|
id x[5];
|
|
|
|
extern id test19_helper(void);
|
|
x[2] = test19_helper();
|
|
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @test19_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [5 x ptr], ptr [[X]], i64 0, i64 2
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SLOT]]
|
|
// CHECK-NEXT: store ptr [[T1]], ptr [[SLOT]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]]
|
|
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [5 x ptr], ptr [[X]], i32 0, i32 0
|
|
// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds ptr, ptr [[BEGIN]], i64 5
|
|
// CHECK-NEXT: br label
|
|
|
|
// CHECK: [[AFTER:%.*]] = phi ptr [ [[END]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ]
|
|
// CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds ptr, ptr [[AFTER]], i64 -1
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[CUR]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]], !clang.imprecise_release
|
|
// CHECK-NEXT: [[EQ:%.*]] = icmp eq ptr [[CUR]], [[BEGIN]]
|
|
// CHECK-NEXT: br i1 [[EQ]],
|
|
|
|
// CHECK: ret void
|
|
}
|
|
|
|
void test20(unsigned n) {
|
|
// CHECK-LABEL: define{{.*}} void @test20
|
|
// CHECK: [[N:%.*]] = alloca i32, align 4
|
|
// CHECK-NEXT: [[SAVED_STACK:%.*]] = alloca ptr
|
|
// CHECK-NEXT: [[VLA_EXPR:%.*]] = alloca i64, align 8
|
|
// CHECK-NEXT: store i32 {{%.*}}, ptr [[N]], align 4
|
|
|
|
id x[n];
|
|
|
|
// Capture the VLA size.
|
|
// CHECK-NEXT: [[T0:%.*]] = load i32, ptr [[N]], align 4
|
|
// CHECK-NEXT: [[DIM:%.*]] = zext i32 [[T0]] to i64
|
|
|
|
// Save the stack pointer.
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @llvm.stacksave.p0()
|
|
// CHECK-NEXT: store ptr [[T0]], ptr [[SAVED_STACK]]
|
|
|
|
// Allocate the VLA.
|
|
// CHECK-NEXT: [[VLA:%.*]] = alloca ptr, i64 [[DIM]], align 16
|
|
|
|
// Store the VLA #elements expression.
|
|
// CHECK-NEXT: store i64 [[DIM]], ptr [[VLA_EXPR]], align 8
|
|
|
|
// Zero-initialize.
|
|
// CHECK-NEXT: [[T1:%.*]] = mul nuw i64 [[DIM]], 8
|
|
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[VLA]], i8 0, i64 [[T1]], i1 false)
|
|
|
|
// Destroy.
|
|
// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds ptr, ptr [[VLA]], i64 [[DIM]]
|
|
// CHECK-NEXT: [[EMPTY:%.*]] = icmp eq ptr [[VLA]], [[END]]
|
|
// CHECK-NEXT: br i1 [[EMPTY]]
|
|
|
|
// CHECK: [[AFTER:%.*]] = phi ptr [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ]
|
|
// CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds ptr, ptr [[AFTER]], i64 -1
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[CUR]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]], !clang.imprecise_release
|
|
// CHECK-NEXT: [[EQ:%.*]] = icmp eq ptr [[CUR]], [[VLA]]
|
|
// CHECK-NEXT: br i1 [[EQ]],
|
|
|
|
// CHECK: [[T0:%.*]] = load ptr, ptr [[SAVED_STACK]]
|
|
// CHECK-NEXT: call void @llvm.stackrestore.p0(ptr [[T0]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
void test21(unsigned n) {
|
|
// CHECK-LABEL: define{{.*}} void @test21
|
|
// CHECK: [[N:%.*]] = alloca i32, align 4
|
|
// CHECK-NEXT: [[SAVED_STACK:%.*]] = alloca ptr
|
|
// CHECK-NEXT: [[VLA_EXPR:%.*]] = alloca i64, align 8
|
|
// CHECK-NEXT: store i32 {{%.*}}, ptr [[N]], align 4
|
|
|
|
id x[2][n][3];
|
|
|
|
// Capture the VLA size.
|
|
// CHECK-NEXT: [[T0:%.*]] = load i32, ptr [[N]], align 4
|
|
// CHECK-NEXT: [[DIM:%.*]] = zext i32 [[T0]] to i64
|
|
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @llvm.stacksave.p0()
|
|
// CHECK-NEXT: store ptr [[T0]], ptr [[SAVED_STACK]]
|
|
|
|
|
|
// Allocate the VLA.
|
|
// CHECK-NEXT: [[T0:%.*]] = mul nuw i64 2, [[DIM]]
|
|
// CHECK-NEXT: [[VLA:%.*]] = alloca [3 x ptr], i64 [[T0]], align 16
|
|
|
|
// Store the VLA #elements expression.
|
|
// CHECK-NEXT: store i64 [[DIM]], ptr [[VLA_EXPR]], align 8
|
|
|
|
// Zero-initialize.
|
|
// CHECK-NEXT: [[T1:%.*]] = mul nuw i64 2, [[DIM]]
|
|
// CHECK-NEXT: [[T2:%.*]] = mul nuw i64 [[T1]], 24
|
|
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[VLA]], i8 0, i64 [[T2]], i1 false)
|
|
|
|
// Destroy.
|
|
// CHECK-NEXT: [[T0:%.*]] = mul nuw i64 2, [[DIM]]
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [3 x ptr], ptr [[VLA]], i32 0, i32 0
|
|
// CHECK-NEXT: [[T1:%.*]] = mul nuw i64 [[T0]], 3
|
|
// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds ptr, ptr [[BEGIN]], i64 [[T1]]
|
|
// CHECK-NEXT: [[EMPTY:%.*]] = icmp eq ptr [[BEGIN]], [[END]]
|
|
// CHECK-NEXT: br i1 [[EMPTY]]
|
|
|
|
// CHECK: [[AFTER:%.*]] = phi ptr [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ]
|
|
// CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds ptr, ptr [[AFTER]], i64 -1
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[CUR]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]]) [[NUW]], !clang.imprecise_release
|
|
// CHECK-NEXT: [[EQ:%.*]] = icmp eq ptr [[CUR]], [[BEGIN]]
|
|
// CHECK-NEXT: br i1 [[EQ]],
|
|
|
|
// CHECK: [[T0:%.*]] = load ptr, ptr [[SAVED_STACK]]
|
|
// CHECK-NEXT: call void @llvm.stackrestore.p0(ptr [[T0]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
// Note that we no longer emit .release_ivars flags.
|
|
// Note that we set the flag saying that we need destruction *and*
|
|
// the flag saying that we don't also need construction.
|
|
// CHECK-GLOBALS: @"_OBJC_CLASS_RO_$_Test23" = internal global [[RO_T:%.*]] { i32 390,
|
|
@interface Test23 { id x; } @end
|
|
@implementation Test23 @end
|
|
|
|
// CHECK-GLOBALS: @"_OBJC_CLASS_RO_$_Test24" = internal global [[RO_T:%.*]] { i32 130,
|
|
@interface Test24 {} @end
|
|
@implementation Test24 @end
|
|
|
|
@interface Test26 { id x[4]; } @end
|
|
@implementation Test26 @end
|
|
// CHECK: define internal void @"\01-[Test26 .cxx_destruct]"(
|
|
// CHECK: [[SELF:%.*]] = load ptr, ptr
|
|
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8, ptr [[SELF]], i64 0
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [4 x ptr], ptr [[T1]], i32 0, i32 0
|
|
// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds ptr, ptr [[BEGIN]], i64 4
|
|
// CHECK-NEXT: br label
|
|
// CHECK: [[PAST:%.*]] = phi ptr [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ]
|
|
// CHECK-NEXT: [[CUR]] = getelementptr inbounds ptr, ptr [[PAST]], i64 -1
|
|
// CHECK-NEXT: call void @llvm.objc.storeStrong(ptr [[CUR]], ptr null)
|
|
// CHECK-NEXT: [[ISDONE:%.*]] = icmp eq ptr [[CUR]], [[BEGIN]]
|
|
// CHECK-NEXT: br i1 [[ISDONE]],
|
|
// CHECK: ret void
|
|
|
|
// Check that 'init' retains self.
|
|
@interface Test27
|
|
- (id) init;
|
|
@end
|
|
@implementation Test27
|
|
- (id) init { return self; }
|
|
// CHECK: define internal ptr @"\01-[Test27 init]"
|
|
// CHECK: [[SELF:%.*]] = alloca ptr,
|
|
// CHECK-NEXT: [[CMD:%.*]] = alloca ptr,
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[SELF]]
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[CMD]]
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SELF]]
|
|
// CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]])
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SELF]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]])
|
|
// CHECK-NEXT: ret ptr [[T2]]
|
|
|
|
@end
|
|
|
|
@interface Test28
|
|
@property (copy) id prop;
|
|
@end
|
|
@implementation Test28
|
|
@synthesize prop;
|
|
@end
|
|
// CHECK: define internal void @"\01-[Test28 .cxx_destruct]"
|
|
// CHECK: [[SELF:%.*]] = load ptr, ptr
|
|
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8, ptr [[SELF]], i64 0
|
|
// CHECK-NEXT: call void @llvm.objc.storeStrong(ptr [[T1]], ptr null)
|
|
// CHECK-NEXT: ret void
|
|
|
|
@interface Test29_super
|
|
- (id) initWithAllocator: (id) allocator;
|
|
@end
|
|
@interface Test29 : Test29_super
|
|
- (id) init;
|
|
- (id) initWithAllocator: (id) allocator;
|
|
@end
|
|
@implementation Test29
|
|
static id _test29_allocator = 0;
|
|
- (id) init {
|
|
// CHECK: define internal ptr @"\01-[Test29 init]"(ptr noundef {{%.*}},
|
|
// CHECK: [[SELF:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: [[CMD:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[SELF]]
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[CMD]]
|
|
|
|
// Evaluate arguments. Note that the send argument is evaluated
|
|
// before the zeroing of self.
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SELF]], align 8
|
|
// CHECK-NEXT: [[T1:%.*]] = load ptr, ptr @_test29_allocator, align 8
|
|
|
|
// Implicit null of 'self', i.e. direct transfer of ownership.
|
|
// CHECK-NEXT: store ptr null, ptr [[SELF]]
|
|
|
|
// Actual message send.
|
|
// CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[CALL:%.*]] = call ptr @objc_msgSend(ptr noundef [[T0]], ptr noundef [[SEL]], ptr noundef [[T1]])
|
|
|
|
// Implicit write of result back into 'self'. This is not supposed to
|
|
// be detectable because we're supposed to ban accesses to the old
|
|
// self value past the delegate init call.
|
|
// CHECK-NEXT: store ptr [[CALL]], ptr [[SELF]]
|
|
|
|
// Return statement.
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @llvm.objc.retain(ptr [[CALL]]) [[NUW]]
|
|
|
|
// Cleanup.
|
|
// CHECK-NEXT: [[T2:%.*]] = load ptr, ptr [[SELF]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T2]]) [[NUW]], !clang.imprecise_release
|
|
|
|
// Return.
|
|
// CHECK-NEXT: ret ptr [[T1]]
|
|
return [self initWithAllocator: _test29_allocator];
|
|
}
|
|
- (id) initWithAllocator: (id) allocator {
|
|
// CHECK: define internal ptr @"\01-[Test29 initWithAllocator:]"(
|
|
// CHECK: [[SELF:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: [[CMD:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: [[ALLOCATOR:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: alloca
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[SELF]]
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[CMD]]
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @llvm.objc.retain(ptr {{%.*}})
|
|
// CHECK-NEXT: store ptr [[T0]], ptr [[ALLOCATOR]]
|
|
|
|
// Evaluate arguments. Note that the send argument is evaluated
|
|
// before the zeroing of self.
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SELF]]
|
|
// CHECK-NEXT: [[T1:%.*]] = load ptr, ptr [[ALLOCATOR]], align 8
|
|
|
|
// Implicit null of 'self', i.e. direct transfer of ownership.
|
|
// CHECK-NEXT: store ptr null, ptr [[SELF]]
|
|
|
|
// Actual message send.
|
|
// CHECK: [[CALL:%.*]] = call {{.*}} @objc_msgSendSuper2
|
|
|
|
// Implicit write of result back into 'self'. This is not supposed to
|
|
// be detectable because we're supposed to ban accesses to the old
|
|
// self value past the delegate init call.
|
|
// CHECK-NEXT: store ptr [[CALL]], ptr [[SELF]]
|
|
|
|
// Assignment.
|
|
// CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retain(ptr [[CALL]]) [[NUW]]
|
|
// CHECK-NEXT: [[T4:%.*]] = load ptr, ptr [[SELF]], align
|
|
// CHECK-NEXT: store ptr [[T2]], ptr [[SELF]], align
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T4]])
|
|
|
|
// Return statement.
|
|
// CHECK-NEXT: [[T3:%.*]] = load ptr, ptr [[SELF]]
|
|
// CHECK-NEXT: [[T4:%.*]] = call ptr @llvm.objc.retain(ptr [[T3]]) [[NUW]]
|
|
|
|
// Cleanup.
|
|
// CHECK-NEXT: [[T5:%.*]] = load ptr, ptr [[ALLOCATOR]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T5]]) [[NUW]], !clang.imprecise_release
|
|
|
|
// CHECK-NEXT: [[T6:%.*]] = load ptr, ptr [[SELF]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T6]]) [[NUW]], !clang.imprecise_release
|
|
|
|
// Return.
|
|
// CHECK-NEXT: ret ptr [[T4]]
|
|
self = [super initWithAllocator: allocator];
|
|
return self;
|
|
}
|
|
@end
|
|
|
|
typedef struct Test30_helper Test30_helper;
|
|
@interface Test30
|
|
- (id) init;
|
|
- (Test30_helper*) initHelper;
|
|
@end
|
|
@implementation Test30 {
|
|
char *helper;
|
|
}
|
|
- (id) init {
|
|
// CHECK: define internal ptr @"\01-[Test30 init]"(ptr noundef {{%.*}},
|
|
// CHECK: [[RET:%.*]] = alloca ptr
|
|
// CHECK-NEXT: alloca ptr
|
|
// CHECK-NEXT: store ptr {{%.*}}, ptr [[SELF]]
|
|
// CHECK-NEXT: store
|
|
|
|
// Call.
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SELF]]
|
|
// CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[CALL:%.*]] = call ptr @objc_msgSend(ptr noundef [[T0]], ptr noundef [[SEL]])
|
|
|
|
// Assignment.
|
|
// CHECK-NEXT: [[T1:%.*]] = load ptr, ptr [[SELF]]
|
|
// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8, ptr [[T1]], i64 0
|
|
// CHECK-NEXT#: [[T5:%.*]] = load ptr, ptr [[T3]]
|
|
// CHECK-NEXT#: [[T6:%.*]] = call ptr @llvm.objc.retain(ptr [[CALL]])
|
|
// CHECK-NEXT#: call void @llvm.objc.release(ptr [[T5]])
|
|
// CHECK-NEXT: store ptr [[CALL]], ptr [[T3]]
|
|
|
|
// Return.
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[SELF]]
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]])
|
|
|
|
// Cleanup.
|
|
// CHECK-NEXT: [[T2:%.*]] = load ptr, ptr [[SELF]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T2]])
|
|
|
|
// Epilogue.
|
|
// CHECK-NEXT: ret ptr [[T1]]
|
|
self->helper = [self initHelper];
|
|
return self;
|
|
}
|
|
- (Test30_helper*) initHelper {
|
|
// CHECK: define internal ptr @"\01-[Test30 initHelper]"(
|
|
// CHECK: alloca
|
|
// CHECK-NEXT: alloca
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: ret ptr null
|
|
return 0;
|
|
}
|
|
|
|
@end
|
|
|
|
__attribute__((ns_returns_retained)) id test32(void) {
|
|
// CHECK-LABEL: define{{.*}} ptr @test32()
|
|
// CHECK: [[T0:%.*]] = call ptr @test32_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
|
|
// CHECK-NEXT: ret ptr [[T0]]
|
|
extern id test32_helper(void);
|
|
return test32_helper();
|
|
}
|
|
|
|
@class Test33_a;
|
|
@interface Test33
|
|
- (void) give: (Test33_a **) x;
|
|
- (void) take: (Test33_a **) x;
|
|
- (void) giveStrong: (out __strong Test33_a **) x;
|
|
- (void) takeStrong: (inout __strong Test33_a **) x;
|
|
- (void) giveOut: (out Test33_a **) x;
|
|
@end
|
|
void test33(Test33 *ptr) {
|
|
Test33_a *a;
|
|
[ptr give: &a];
|
|
[ptr take: &a];
|
|
[ptr giveStrong: &a];
|
|
[ptr takeStrong: &a];
|
|
[ptr giveOut: &a];
|
|
|
|
// CHECK: define{{.*}} void @test33(ptr
|
|
// CHECK: [[PTR:%.*]] = alloca ptr
|
|
// CHECK-NEXT: [[A:%.*]] = alloca ptr
|
|
// CHECK-NEXT: [[TEMP0:%.*]] = alloca ptr
|
|
// CHECK-NEXT: [[TEMP1:%.*]] = alloca ptr
|
|
// CHECK-NEXT: [[TEMP2:%.*]] = alloca ptr
|
|
// CHECK-NEXT: llvm.objc.retain
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: call void @llvm.lifetime.start
|
|
// CHECK-NEXT: store ptr null, ptr [[A]]
|
|
|
|
// CHECK-NEXT: load ptr, ptr [[PTR]]
|
|
// CHECK-NEXT: [[W0:%.*]] = load ptr, ptr [[A]]
|
|
// CHECK-NEXT: store ptr [[W0]], ptr [[TEMP0]]
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: objc_msgSend{{.*}}, ptr noundef [[TEMP0]])
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[TEMP0]]
|
|
// CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]])
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[W0]]) [[NUW]]
|
|
// CHECK-NEXT: [[T4:%.*]] = load ptr, ptr [[A]]
|
|
// CHECK-NEXT: store ptr [[T2]], ptr [[A]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T4]])
|
|
|
|
// CHECK-NEXT: load ptr, ptr [[PTR]]
|
|
// CHECK-NEXT: [[W0:%.*]] = load ptr, ptr [[A]]
|
|
// CHECK-NEXT: store ptr [[W0]], ptr [[TEMP1]]
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: objc_msgSend{{.*}}, ptr noundef [[TEMP1]])
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[TEMP1]]
|
|
// CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]])
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[W0]]) [[NUW]]
|
|
// CHECK-NEXT: [[T4:%.*]] = load ptr, ptr [[A]]
|
|
// CHECK-NEXT: store ptr [[T2]], ptr [[A]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T4]])
|
|
|
|
// CHECK-NEXT: load ptr, ptr [[PTR]]
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: objc_msgSend{{.*}}, ptr noundef [[A]])
|
|
|
|
// CHECK-NEXT: load ptr, ptr [[PTR]]
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: objc_msgSend{{.*}}, ptr noundef [[A]])
|
|
|
|
// 'out'
|
|
// CHECK-NEXT: load ptr, ptr [[PTR]]
|
|
// CHECK-NEXT: store ptr null, ptr [[TEMP2]]
|
|
// CHECK-NEXT: load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: objc_msgSend{{.*}}, ptr noundef [[TEMP2]])
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[TEMP2]]
|
|
// CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]])
|
|
// CHECK-NEXT: [[T4:%.*]] = load ptr, ptr [[A]]
|
|
// CHECK-NEXT: store ptr [[T2]], ptr [[A]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T4]])
|
|
|
|
// CHECK-NEXT: load
|
|
// CHECK-NEXT: llvm.objc.release
|
|
// CHECK-NEXT: call void @llvm.lifetime.end
|
|
// CHECK-NEXT: load
|
|
// CHECK-NEXT: llvm.objc.release
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test36
|
|
void test36(id x) {
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
|
|
// CHECK: call ptr @llvm.objc.retain
|
|
// CHECK: call ptr @llvm.objc.retain
|
|
// CHECK: call ptr @llvm.objc.retain
|
|
id array[3] = { @"A", x, @"y" };
|
|
|
|
// CHECK: [[T0:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: store ptr null, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]])
|
|
x = 0;
|
|
|
|
// CHECK: br label
|
|
// CHECK: call void @llvm.objc.release
|
|
// CHECK: br i1
|
|
|
|
// CHECK: call void @llvm.objc.release
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
@class Test37;
|
|
void test37(void) {
|
|
extern void test37_helper(id *);
|
|
Test37 *var;
|
|
test37_helper(&var);
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test37()
|
|
// CHECK: [[VAR:%.*]] = alloca ptr,
|
|
// CHECK-NEXT: [[TEMP:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[VAR]])
|
|
// CHECK-NEXT: store ptr null, ptr [[VAR]]
|
|
|
|
// CHECK-NEXT: [[W0:%.*]] = load ptr, ptr [[VAR]]
|
|
// CHECK-NEXT: store ptr [[W0]], ptr [[TEMP]]
|
|
// CHECK-NEXT: call void @test37_helper(ptr noundef [[TEMP]])
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[TEMP]]
|
|
// CHECK-NEXT: [[T3:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]])
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.use(ptr [[W0]]) [[NUW]]
|
|
// CHECK-NEXT: [[T5:%.*]] = load ptr, ptr [[VAR]]
|
|
// CHECK-NEXT: store ptr [[T3]], ptr [[VAR]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T5]])
|
|
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[VAR]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]])
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[VAR]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
@interface Test43 @end
|
|
@implementation Test43
|
|
- (id) test __attribute__((ns_returns_retained)) {
|
|
extern id test43_produce(void);
|
|
return test43_produce();
|
|
// CHECK: call ptr @test43_produce(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(
|
|
// CHECK-NEXT: ret
|
|
}
|
|
@end
|
|
|
|
@interface Test45
|
|
@property (retain) id x;
|
|
@end
|
|
@implementation Test45
|
|
@synthesize x;
|
|
@end
|
|
// CHECK: define internal ptr @"\01-[Test45 x]"(
|
|
// CHECK: [[CALL:%.*]] = tail call ptr @objc_getProperty(
|
|
// CHECK-NEXT: ret ptr [[CALL]]
|
|
|
|
void test46(__weak id *wp, __weak volatile id *wvp) {
|
|
extern id test46_helper(void);
|
|
|
|
// TODO: this is sub-optimal, we should retain at the actual call site.
|
|
|
|
// CHECK: [[T1:%.*]] = call ptr @test46_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: [[T2:%.*]] = load ptr, ptr {{%.*}}, align 8
|
|
// CHECK-NEXT: [[T3:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[T2]], ptr [[T1]])
|
|
// CHECK-NEXT: [[T4:%.*]] = call ptr @llvm.objc.retain(ptr [[T3]])
|
|
// CHECK-NEXT: store ptr [[T4]], ptr
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]])
|
|
id x = *wp = test46_helper();
|
|
|
|
// CHECK: [[T1:%.*]] = call ptr @test46_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: [[T2:%.*]] = load ptr, ptr {{%.*}}, align 8
|
|
// CHECK-NEXT: [[T3:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[T2]], ptr [[T1]])
|
|
// CHECK-NEXT: [[T4:%.*]] = call ptr @llvm.objc.retain(ptr [[T3]])
|
|
// CHECK-NEXT: store ptr [[T4]], ptr
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]])
|
|
id y = *wvp = test46_helper();
|
|
}
|
|
|
|
void test47(void) {
|
|
extern id test47_helper(void);
|
|
id x = x = test47_helper();
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test47()
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: store ptr null, ptr [[X]]
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @test47_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
|
|
// CHECK-NEXT: [[T1:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: store ptr [[T0]], ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]])
|
|
// CHECK-NEXT: [[T2:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]])
|
|
// CHECK-NEXT: [[T3:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: store ptr [[T2]], ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T3]])
|
|
// CHECK-NEXT: [[T4:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T4]])
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
void test48(void) {
|
|
extern id test48_helper(void);
|
|
__weak id x = x = test48_helper();
|
|
// CHECK-LABEL: define{{.*}} void @test48()
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @llvm.objc.initWeak(ptr [[X]], ptr null)
|
|
// CHECK-NEXT: [[T2:%.*]] = call ptr @test48_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T2]])
|
|
// CHECK-NEXT: [[T3:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[X]], ptr [[T2]])
|
|
// CHECK-NEXT: [[T4:%.*]] = call ptr @llvm.objc.storeWeak(ptr [[X]], ptr [[T3]])
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T2]])
|
|
// CHECK-NEXT: call void @llvm.objc.destroyWeak(ptr [[X]])
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
void test49(void) {
|
|
extern id test49_helper(void);
|
|
__autoreleasing id x = x = test49_helper();
|
|
// CHECK-LABEL: define{{.*}} void @test49()
|
|
// CHECK: [[X:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: store ptr null, ptr [[X]]
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @test49_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[T0]])
|
|
// CHECK-NEXT: store ptr [[T1]], ptr [[X]]
|
|
// CHECK-NEXT: [[T3:%.*]] = call ptr @llvm.objc.retainAutorelease(ptr [[T1]])
|
|
// CHECK-NEXT: store ptr [[T3]], ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
id x(void);
|
|
void test50(id y) {
|
|
({x();});
|
|
// CHECK: [[T0:%.*]] = call ptr @llvm.objc.retain
|
|
// CHECK: call void @llvm.objc.release
|
|
}
|
|
|
|
struct CGPoint {
|
|
float x;
|
|
float y;
|
|
};
|
|
typedef struct CGPoint CGPoint;
|
|
|
|
@interface Foo
|
|
@property (assign) CGPoint point;
|
|
@end
|
|
|
|
@implementation Foo
|
|
@synthesize point;
|
|
@end
|
|
|
|
id test52(void) {
|
|
id test52_helper(int) __attribute__((ns_returns_retained));
|
|
return ({ int x = 5; test52_helper(x); });
|
|
|
|
// CHECK-LABEL: define{{.*}} ptr @test52()
|
|
// CHECK: [[X:%.*]] = alloca i32
|
|
// CHECK-NEXT: [[TMPALLOCA:%.*]] = alloca ptr
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: store i32 5, ptr [[X]],
|
|
// CHECK-NEXT: [[T0:%.*]] = load i32, ptr [[X]],
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @test52_helper(i32 noundef [[T0]])
|
|
// CHECK-NEXT: store ptr [[T1]], ptr [[TMPALLOCA]]
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: [[T2:%.*]] = load ptr, ptr [[TMPALLOCA]]
|
|
// CHECK-NEXT: [[T3:%.*]] = tail call ptr @llvm.objc.autoreleaseReturnValue(ptr [[T2]])
|
|
// CHECK-NEXT: ret ptr [[T3]]
|
|
}
|
|
|
|
void test53(void) {
|
|
id test53_helper(void);
|
|
id x = ({ id y = test53_helper(); y; });
|
|
(void) x;
|
|
// CHECK-LABEL: define{{.*}} void @test53()
|
|
// CHECK: [[X:%.*]] = alloca ptr,
|
|
// CHECK-NEXT: [[Y:%.*]] = alloca ptr,
|
|
// CHECK-NEXT: [[TMPALLOCA:%.*]] = alloca ptr,
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[Y]])
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @test53_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: store ptr [[T1]], ptr [[Y]],
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[Y]],
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @llvm.objc.retain(ptr [[T0]])
|
|
// CHECK-NEXT: store ptr [[T1]], ptr [[TMPALLOCA]]
|
|
// CHECK-NEXT: [[T2:%.*]] = load ptr, ptr [[Y]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T2]])
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[Y]])
|
|
// CHECK-NEXT: [[T3:%.*]] = load ptr, ptr [[TMPALLOCA]]
|
|
// CHECK-NEXT: store ptr [[T3]], ptr [[X]],
|
|
// CHECK-NEXT: load ptr, ptr [[X]],
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]])
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test54(i32 noundef %first, ...)
|
|
void test54(int first, ...) {
|
|
__builtin_va_list arglist;
|
|
// CHECK: call void @llvm.va_start
|
|
__builtin_va_start(arglist, first);
|
|
// CHECK: call ptr @llvm.objc.retain
|
|
id obj = __builtin_va_arg(arglist, id);
|
|
// CHECK: call void @llvm.va_end
|
|
__builtin_va_end(arglist);
|
|
// CHECK: call void @llvm.objc.release
|
|
// CHECK: ret void
|
|
}
|
|
|
|
// PR10228
|
|
@interface Test55Base @end
|
|
@interface Test55 : Test55Base @end
|
|
@implementation Test55 (Category)
|
|
- (void) dealloc {}
|
|
@end
|
|
// CHECK: define internal void @"\01-[Test55(Category) dealloc]"(
|
|
// CHECK-NOT: ret
|
|
// CHECK: call void @objc_msgSendSuper2(
|
|
|
|
@protocol Test56Protocol
|
|
+ (id) make __attribute__((ns_returns_retained));
|
|
@end
|
|
@interface Test56<Test56Protocol> @end
|
|
@implementation Test56
|
|
// CHECK: define internal ptr @"\01+[Test56 make]"(
|
|
+ (id) make {
|
|
extern id test56_helper(void);
|
|
// CHECK: [[T1:%.*]] = call ptr @test56_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: ret ptr [[T1]]
|
|
return test56_helper();
|
|
}
|
|
@end
|
|
void test56_test(void) {
|
|
id x = [Test56 make];
|
|
// CHECK-LABEL: define{{.*}} void @test56_test()
|
|
// CHECK: [[X:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[X]])
|
|
// CHECK: [[T0:%.*]] = call ptr @objc_msgSend(
|
|
// CHECK-NEXT: store ptr [[T0]], ptr [[X]]
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[X]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]])
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[X]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
@interface Test57
|
|
@property (nonatomic, strong) id strong;
|
|
@property (nonatomic, weak) id weak;
|
|
@property (nonatomic, unsafe_unretained) id unsafe;
|
|
@end
|
|
@implementation Test57
|
|
@synthesize strong, weak, unsafe;
|
|
@end
|
|
// CHECK: define internal ptr @"\01-[Test57 strong]"(
|
|
// CHECK: [[T0:%.*]] = load ptr, ptr {{%.*}}
|
|
// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8, ptr [[T0]], i64 0
|
|
// CHECK-NEXT: [[T5:%.*]] = load ptr, ptr [[T3]]
|
|
// CHECK-NEXT: ret ptr [[T5]]
|
|
|
|
// CHECK: define internal ptr @"\01-[Test57 weak]"(
|
|
// CHECK: [[T0:%.*]] = load ptr, ptr {{%.*}}
|
|
// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8, ptr [[T0]], i64 8
|
|
// CHECK-NEXT: [[T5:%.*]] = call ptr @llvm.objc.loadWeakRetained(ptr [[T3]])
|
|
// CHECK-NEXT: [[T6:%.*]] = tail call ptr @llvm.objc.autoreleaseReturnValue(ptr [[T5]])
|
|
// CHECK-NEXT: ret ptr [[T6]]
|
|
|
|
// CHECK: define internal ptr @"\01-[Test57 unsafe]"(
|
|
// CHECK: [[T0:%.*]] = load ptr, ptr {{%.*}}
|
|
// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8, ptr [[T0]], i64 16
|
|
// CHECK-NEXT: [[T5:%.*]] = load ptr, ptr [[T3]]
|
|
// CHECK-NEXT: ret ptr [[T5]]
|
|
|
|
void test59(void) {
|
|
extern id test59_getlock(void);
|
|
extern void test59_body(void);
|
|
@synchronized (test59_getlock()) {
|
|
test59_body();
|
|
}
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test59()
|
|
// CHECK: [[T1:%.*]] = call ptr @test59_getlock(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: call i32 @objc_sync_enter(ptr [[T1]])
|
|
// CHECK-NEXT: call void @test59_body()
|
|
// CHECK-NEXT: call i32 @objc_sync_exit(ptr [[T1]])
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
// Verify that we don't try to reclaim the result of performSelector.
|
|
@interface Test61
|
|
- (id) performSelector: (SEL) selector;
|
|
- (void) test61_void;
|
|
- (id) test61_id;
|
|
@end
|
|
void test61(void) {
|
|
// CHECK-LABEL: define{{.*}} void @test61()
|
|
// CHECK: [[Y:%.*]] = alloca ptr, align 8
|
|
|
|
extern id test61_make(void);
|
|
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @test61_make(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T0]])
|
|
// CHECK-NEXT: [[T2:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[T3:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[T4:%.*]] = call ptr @objc_msgSend(ptr noundef [[T1]], ptr noundef [[T3]], ptr noundef [[T2]])
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]])
|
|
[test61_make() performSelector: @selector(test61_void)];
|
|
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[Y]])
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @test61_make(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: [[T2:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[T3:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_
|
|
// CHECK-NEXT: [[T5:%.*]] = call ptr @objc_msgSend(ptr noundef [[T1]], ptr noundef [[T3]], ptr noundef [[T2]]){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T5]])
|
|
// CHECK-NEXT: store ptr [[T5]], ptr [[Y]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T1]])
|
|
id y = [test61_make() performSelector: @selector(test61_id)];
|
|
|
|
// CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[Y]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]])
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[Y]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
void test62(void) {
|
|
// CHECK-LABEL: define{{.*}} void @test62()
|
|
// CHECK: [[I:%.*]] = alloca i32, align 4
|
|
// CHECK-NEXT: [[CLEANUP_VALUE:%.*]] = alloca ptr
|
|
// CHECK-NEXT: [[CLEANUP_REQUIRED:%.*]] = alloca i1
|
|
extern id test62_make(void);
|
|
extern void test62_body(void);
|
|
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[I]])
|
|
// CHECK-NEXT: store i32 0, ptr [[I]], align 4
|
|
// CHECK-NEXT: br label
|
|
|
|
// CHECK: [[T0:%.*]] = load i32, ptr [[I]], align 4
|
|
// CHECK-NEXT: [[T1:%.*]] = icmp ne i32 [[T0]], 20
|
|
// CHECK-NEXT: br i1 [[T1]],
|
|
|
|
for (unsigned i = 0; i != 20; ++i) {
|
|
// CHECK: [[T0:%.*]] = load i32, ptr [[I]], align 4
|
|
// CHECK-NEXT: [[T1:%.*]] = icmp ne i32 [[T0]], 0
|
|
// CHECK-NEXT: store i1 false, ptr [[CLEANUP_REQUIRED]]
|
|
// CHECK-NEXT: br i1 [[T1]],
|
|
// CHECK: [[T1:%.*]] = call ptr @test62_make(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: store ptr [[T1]], ptr [[CLEANUP_VALUE]]
|
|
// CHECK-NEXT: store i1 true, ptr [[CLEANUP_REQUIRED]]
|
|
// CHECK-NEXT: [[T2:%.*]] = icmp ne ptr [[T1]], null
|
|
// CHECK-NEXT: br label
|
|
// CHECK: [[COND:%.*]] = phi i1 [ false, {{%.*}} ], [ [[T2]], {{%.*}} ]
|
|
// CHECK-NEXT: [[T0:%.*]] = load i1, ptr [[CLEANUP_REQUIRED]]
|
|
// CHECK-NEXT: br i1 [[T0]],
|
|
// CHECK: [[T0:%.*]] = load ptr, ptr [[CLEANUP_VALUE]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T0]])
|
|
// CHECK-NEXT: br label
|
|
// CHECK: br i1 [[COND]]
|
|
// CHECK: call void @test62_body()
|
|
// CHECK-NEXT: br label
|
|
// CHECK: br label
|
|
if (i != 0 && test62_make() != 0)
|
|
test62_body();
|
|
}
|
|
|
|
// CHECK: [[T0:%.*]] = load i32, ptr [[I]], align 4
|
|
// CHECK-NEXT: [[T1:%.*]] = add i32 [[T0]], 1
|
|
// CHECK-NEXT: store i32 [[T1]], ptr [[I]]
|
|
// CHECK-NEXT: br label
|
|
|
|
// CHECK: ret void
|
|
}
|
|
|
|
@class NSString;
|
|
|
|
@interface Person {
|
|
NSString *name;
|
|
}
|
|
@property NSString *address;
|
|
@end
|
|
|
|
@implementation Person
|
|
@synthesize address;
|
|
@end
|
|
// CHECK: tail call ptr @objc_getProperty
|
|
// CHECK: call void @objc_setProperty
|
|
|
|
// Verify that we successfully parse and preserve this attribute in
|
|
// this position.
|
|
@interface Test66
|
|
- (void) consume: (id __attribute__((ns_consumed))) ptr;
|
|
@end
|
|
void test66(void) {
|
|
extern Test66 *test66_receiver(void);
|
|
extern id test66_arg(void);
|
|
[test66_receiver() consume: test66_arg()];
|
|
}
|
|
// CHECK-LABEL: define{{.*}} void @test66()
|
|
// CHECK: [[T3:%.*]] = call ptr @test66_receiver(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T3]])
|
|
// CHECK-NEXT: [[T4:%.*]] = call ptr @test66_arg(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T4]])
|
|
// CHECK-NEXT: [[SIX:%.*]] = icmp eq ptr [[T3]], null
|
|
// CHECK-NEXT: br i1 [[SIX]], label [[NULINIT:%.*]], label %[[CALL:.*]]
|
|
// CHECK: [[CALL]]:
|
|
// CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES
|
|
// CHECK-NEXT: call void @objc_msgSend(ptr noundef [[T3]], ptr noundef [[SEL]], ptr noundef [[T4]])
|
|
// CHECK-NEXT: br label [[CONT:%.*]]
|
|
// CHECK: call void @llvm.objc.release(ptr [[T4]]) [[NUW]]
|
|
// CHECK-NEXT: br label [[CONT:%.*]]
|
|
// CHECK: call void @llvm.objc.release(ptr [[T3]])
|
|
// CHECK-NEXT: ret void
|
|
|
|
Class test67_helper(void);
|
|
void test67(void) {
|
|
Class cl = test67_helper();
|
|
}
|
|
// CHECK-LABEL: define{{.*}} void @test67()
|
|
// CHECK: [[CL:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[CL]])
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @test67_helper()
|
|
// CHECK-NEXT: store ptr [[T0]], ptr [[CL]], align 8
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[CL]])
|
|
// CHECK-NEXT: ret void
|
|
|
|
Class test68_helper(void);
|
|
void test68(void) {
|
|
__strong Class cl = test67_helper();
|
|
}
|
|
// CHECK-LABEL: define{{.*}} void @test68()
|
|
// CHECK: [[CL:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[CL]])
|
|
// CHECK-NEXT: [[T1:%.*]] = call ptr @test67_helper(){{.*}} [ "clang.arc.attachedcall"(ptr @llvm.objc.retainAutoreleasedReturnValue) ]
|
|
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(ptr [[T1]])
|
|
// CHECK-NEXT: store ptr [[T1]], ptr [[CL]], align 8
|
|
// CHECK-NEXT: [[T2:%.*]] = load ptr, ptr [[CL]]
|
|
// CHECK-NEXT: call void @llvm.objc.release(ptr [[T2]])
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[CL]])
|
|
// CHECK-NEXT: ret void
|
|
|
|
@interface Test69 @end
|
|
@implementation Test69
|
|
- (id) foo { return self; }
|
|
@end
|
|
// CHECK: define internal ptr @"\01-[Test69 foo]"(
|
|
// CHECK: [[SELF:%.*]] = alloca ptr, align 8
|
|
// CHECK: [[T0:%.*]] = load ptr, ptr [[SELF]], align 8
|
|
// CHECK-NEXT: ret ptr [[T0]]
|
|
|
|
void test70(id i) {
|
|
// CHECK-LABEL: define{{.*}} void @test70
|
|
// CHECK: store ptr null, ptr
|
|
// CHECK: store ptr null, ptr
|
|
// CHECK: [[ID:%.*]] = call ptr @llvm.objc.retain(ptr
|
|
// CHECK: store ptr [[ID]], ptr
|
|
id x[3] = {
|
|
[2] = i
|
|
};
|
|
}
|
|
|
|
// Be sure that we emit lifetime intrinsics only after dtors
|
|
struct AggDtor {
|
|
char cs[40];
|
|
id x;
|
|
};
|
|
|
|
struct AggDtor getAggDtor(void);
|
|
|
|
// CHECK-LABEL: define{{.*}} void @test71
|
|
void test71(void) {
|
|
// CHECK: call void @llvm.lifetime.start.p0(ptr %[[T:.*]])
|
|
// CHECK: call void @getAggDtor(ptr dead_on_unwind writable sret(%struct.AggDtor) align 8 %[[T]])
|
|
// CHECK: call void @__destructor_8_s40(ptr %[[T]])
|
|
// CHECK: call void @llvm.lifetime.end.p0(ptr %[[T]])
|
|
// CHECK: call void @llvm.lifetime.start.p0(ptr %[[T2:.*]])
|
|
// CHECK: call void @getAggDtor(ptr dead_on_unwind writable sret(%struct.AggDtor) align 8 %[[T2]])
|
|
// CHECK: call void @__destructor_8_s40(ptr %[[T2]])
|
|
// CHECK: call void @llvm.lifetime.end.p0(ptr %[[T2]])
|
|
getAggDtor();
|
|
getAggDtor();
|
|
}
|
|
|
|
// Check that no extra release calls are emitted to detruct the compond literal.
|
|
|
|
// CHECK: define{{.*}} void @test72(ptr noundef %[[A:.*]], ptr noundef %[[B:.*]])
|
|
// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8
|
|
// CHECK: %[[B_ADDR:.*]] = alloca ptr, align 8
|
|
// CHECK: %[[T:.*]] = alloca [2 x ptr], align 16
|
|
// CHECK: %[[V0:.*]] = call ptr @llvm.objc.retain(ptr %[[A]])
|
|
// CHECK: %[[V1:.*]] = call ptr @llvm.objc.retain(ptr %[[B]]) #2
|
|
// CHECK: %[[V3:.*]] = load ptr, ptr %[[A_ADDR]], align 8, !tbaa !{{[0-9]+}}
|
|
// CHECK: %[[V4:.*]] = call ptr @llvm.objc.retain(ptr %[[V3]]) #2
|
|
// CHECK: store ptr %[[V4]], ptr %[[T]], align 8, !tbaa !{{[0-9]+}}
|
|
// CHECK: %[[ARRAYINIT_ELEMENT:.*]] = getelementptr inbounds ptr, ptr %[[T]], i64 1
|
|
// CHECK: %[[V5:.*]] = load ptr, ptr %[[B_ADDR]], align 8, !tbaa !{{[0-9]+}}
|
|
// CHECK: %[[V6:.*]] = call ptr @llvm.objc.retain(ptr %[[V5]]) #2
|
|
// CHECK: store ptr %[[V6]], ptr %[[ARRAYINIT_ELEMENT]], align 8, !tbaa !{{[0-9]+}}
|
|
// CHECK: %[[ARRAY_BEGIN:.*]] = getelementptr inbounds [2 x ptr], ptr %[[T]], i32 0, i32 0
|
|
// CHECK: %[[V7:.*]] = getelementptr inbounds ptr, ptr %[[ARRAY_BEGIN]], i64 2
|
|
|
|
// CHECK-NOT: call void @llvm.objc.release
|
|
|
|
// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi ptr [ %[[V7]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], %{{.*}} ]
|
|
// CHECK: %[[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds ptr, ptr %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1
|
|
// CHECK: %[[V8:.*]] = load ptr, ptr %[[ARRAYDESTROY_ELEMENT]], align 8
|
|
// CHECK: call void @llvm.objc.release(ptr %[[V8]]) #2, !clang.imprecise_release
|
|
|
|
// CHECK-NOT: call void @llvm.objc.release
|
|
|
|
// CHECK: %[[V10:.*]] = load ptr, ptr %[[B_ADDR]], align 8
|
|
// CHECK: call void @llvm.objc.release(ptr %[[V10]]) #2, !clang.imprecise_release
|
|
// CHECK: %[[V11:.*]] = load ptr, ptr %[[A_ADDR]], align 8
|
|
// CHECK: call void @llvm.objc.release(ptr %[[V11]]) #2, !clang.imprecise_release
|
|
|
|
void test72(id a, id b) {
|
|
__strong id t[] = (__strong id[]){a, b};
|
|
}
|
|
|
|
// ARC-ALIEN: attributes [[NLB]] = { nonlazybind }
|
|
// ARC-NATIVE: attributes [[NLB]] = { nonlazybind }
|
|
// CHECK: attributes [[NUW]] = { nounwind }
|