
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).
84 lines
2.5 KiB
C
84 lines
2.5 KiB
C
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -emit-llvm -o - %s | FileCheck %s --check-prefixes=O1
|
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O0 -emit-llvm -o - %s | FileCheck %s --check-prefix=O0
|
|
//
|
|
// Ensure that we place appropriate lifetime markers around indirectly returned
|
|
// temporaries, and that the lifetime.ends appear in a timely manner.
|
|
//
|
|
// -O1 is used so lifetime markers actually get emitted and optnone is added
|
|
// to avoid elimination of lifetime markers by optimizations.
|
|
|
|
struct S {
|
|
int ns[40];
|
|
};
|
|
|
|
struct S foo(void);
|
|
|
|
// CHECK-LABEL: define dso_local void @bar
|
|
__attribute__((optnone))
|
|
struct S bar(void) {
|
|
// O0-NOT: @llvm.lifetime.start
|
|
// O0-NOT: @llvm.lifetime.end
|
|
|
|
struct S r;
|
|
// O1: %[[TMP1_ALLOCA:[^ ]+]] = alloca %struct.S
|
|
// O1: %[[TMP2_ALLOCA:[^ ]+]] = alloca %struct.S
|
|
// O1: %[[TMP3_ALLOCA:[^ ]+]] = alloca %struct.S
|
|
|
|
// O1: call void @llvm.lifetime.start.p0(ptr %[[TMP1_ALLOCA]])
|
|
// O1: call void @foo
|
|
r = foo();
|
|
// O1: memcpy
|
|
// O1: call void @llvm.lifetime.end.p0(ptr %[[TMP1_ALLOCA]])
|
|
|
|
// O1: call void @llvm.lifetime.start.p0(ptr %[[TMP2_ALLOCA]])
|
|
// O1: call void @foo
|
|
r = foo();
|
|
// O1: memcpy
|
|
// O1: call void @llvm.lifetime.end.p0(ptr %[[TMP2_ALLOCA]])
|
|
|
|
// O1: call void @llvm.lifetime.start.p0(ptr %[[TMP3_ALLOCA]])
|
|
// O1: call void @foo
|
|
r = foo();
|
|
// O1: memcpy
|
|
// O1: call void @llvm.lifetime.end.p0(ptr %[[TMP3_ALLOCA]])
|
|
|
|
return r;
|
|
}
|
|
|
|
struct S foo_int(int);
|
|
|
|
// Be sure that we're placing the lifetime.end so that all paths go through it.
|
|
// CHECK-LABEL: define dso_local void @baz
|
|
__attribute__((optnone))
|
|
struct S baz(int i, volatile int *j) {
|
|
// O0-NOT: @llvm.lifetime.start
|
|
// O0-NOT: @llvm.lifetime.end
|
|
|
|
struct S r;
|
|
// O1: %[[TMP1_ALLOCA:[^ ]+]] = alloca %struct.S
|
|
// O1: %[[TMP2_ALLOCA:[^ ]+]] = alloca %struct.S
|
|
|
|
do {
|
|
// O1: call void @llvm.lifetime.start.p0(ptr %[[TMP1_ALLOCA]])
|
|
//
|
|
// O1: call void @llvm.lifetime.end.p0(ptr %[[TMP1_ALLOCA]])
|
|
//
|
|
// O1: call void @foo_int(ptr dead_on_unwind writable sret(%struct.S) align 4 %[[TMP1_ALLOCA]],
|
|
// O1: call void @llvm.memcpy
|
|
// O1: call void @llvm.lifetime.end.p0(ptr %[[TMP1_ALLOCA]])
|
|
// O1: call void @llvm.lifetime.start.p0(ptr %[[TMP2_ALLOCA]])
|
|
// O1: call void @foo_int(ptr dead_on_unwind writable sret(%struct.S) align 4 %[[TMP2_ALLOCA]],
|
|
// O1: call void @llvm.memcpy
|
|
// O1: call void @llvm.lifetime.end.p0(ptr %[[TMP2_ALLOCA]])
|
|
r = foo_int(({
|
|
if (*j)
|
|
break;
|
|
i++;
|
|
}));
|
|
|
|
r = foo_int(i++);
|
|
} while (1);
|
|
|
|
return r;
|
|
}
|