1 | // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -S -emit-llvm -o - %s | FileCheck %s --check-prefix=O1 |
2 | // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O0 -S -emit-llvm -o - %s | FileCheck %s --check-prefix=O0 |
3 | // |
4 | // Ensure that we place appropriate lifetime markers around indirectly returned |
5 | // temporaries, and that the lifetime.ends appear in a timely manner. |
6 | // |
7 | // -O1 is used so lifetime markers actually get emitted. |
8 | |
9 | struct S { |
10 | int ns[40]; |
11 | }; |
12 | |
13 | struct S foo(void); |
14 | |
15 | // CHECK-LABEL: define dso_local void @bar |
16 | struct S bar() { |
17 | // O0-NOT: @llvm.lifetime.start |
18 | // O0-NOT: @llvm.lifetime.end |
19 | |
20 | struct S r; |
21 | // O1: call void @llvm.lifetime.start.p0i8({{[^,]*}}, i8* nonnull %[[TMP1:[^)]+]]) |
22 | // O1: call void @foo |
23 | r = foo(); |
24 | // O1: call void @llvm.lifetime.end.p0i8({{[^,]*}}, i8* nonnull %[[TMP1]]) |
25 | |
26 | // O1: call void @llvm.lifetime.start.p0i8({{[^,]*}}, i8* nonnull %[[TMP2:[^)]+]]) |
27 | // O1: call void @foo |
28 | r = foo(); |
29 | // O1: call void @llvm.lifetime.end.p0i8({{[^,]*}}, i8* nonnull %[[TMP2]]) |
30 | |
31 | // O1: call void @llvm.lifetime.start.p0i8({{[^,]*}}, i8* nonnull %[[TMP3:[^)]+]]) |
32 | // O1: call void @foo |
33 | r = foo(); |
34 | // O1: call void @llvm.lifetime.end.p0i8({{[^,]*}}, i8* nonnull %[[TMP3]]) |
35 | |
36 | return r; |
37 | } |
38 | |
39 | struct S foo_int(int); |
40 | |
41 | // Be sure that we're placing the lifetime.end so that all paths go through it. |
42 | // Since this function turns out to be large-ish, optnone to hopefully keep it |
43 | // stable. |
44 | // CHECK-LABEL: define dso_local void @baz |
45 | __attribute__((optnone)) |
46 | struct S baz(int i, volatile int *j) { |
47 | // O0-NOT: @llvm.lifetime.start |
48 | // O0-NOT: @llvm.lifetime.end |
49 | |
50 | struct S r; |
51 | // O1: %[[TMP1_ALLOCA:[^ ]+]] = alloca %struct.S |
52 | // O1: %[[TMP2_ALLOCA:[^ ]+]] = alloca %struct.S |
53 | // O1: br label %[[DO_BODY:.+]] |
54 | |
55 | do { |
56 | // O1: [[DO_BODY]]: |
57 | // O1: %[[P:[^ ]+]] = bitcast %struct.S* %[[TMP1_ALLOCA]] to i8* |
58 | // O1: call void @llvm.lifetime.start.p0i8({{[^,]*}}, i8* %[[P]]) |
59 | // O1: br i1 {{[^,]+}}, label %[[IF_THEN:[^,]+]], label %[[IF_END:[^,]+]] |
60 | // |
61 | // O1: [[IF_THEN]]: |
62 | // O1: %[[P:[^ ]+]] = bitcast %struct.S* %[[TMP1_ALLOCA]] to i8* |
63 | // O1: call void @llvm.lifetime.end.p0i8({{[^,]*}}, i8* %[[P]]) |
64 | // O1: br label %[[DO_END:.*]] |
65 | // |
66 | // O1: [[IF_END]]: |
67 | // O1: call void @foo_int(%struct.S* sret %[[TMP1_ALLOCA]], |
68 | // O1: call void @llvm.memcpy |
69 | // O1: %[[P:[^ ]+]] = bitcast %struct.S* %[[TMP1_ALLOCA]] to i8* |
70 | // O1: call void @llvm.lifetime.end.p0i8({{[^,]*}}, i8* %[[P]]) |
71 | // O1: %[[P:[^ ]+]] = bitcast %struct.S* %[[TMP2_ALLOCA]] to i8* |
72 | // O1: call void @llvm.lifetime.start.p0i8({{[^,]*}}, i8* %[[P]]) |
73 | // O1: call void @foo_int(%struct.S* sret %[[TMP2_ALLOCA]], |
74 | // O1: call void @llvm.memcpy |
75 | // O1: %[[P:[^ ]+]] = bitcast %struct.S* %[[TMP2_ALLOCA]] to i8* |
76 | // O1: call void @llvm.lifetime.end.p0i8({{[^,]*}}, i8* %[[P]]) |
77 | // O1: br label %[[DO_COND:.*]] |
78 | // |
79 | // O1: [[DO_COND]]: |
80 | // O1: br label %[[DO_BODY]] |
81 | r = foo_int(({ |
82 | if (*j) |
83 | break; |
84 | i++; |
85 | })); |
86 | |
87 | r = foo_int(i++); |
88 | } while (1); |
89 | |
90 | // O1: [[DO_END]]: |
91 | // O1-NEXT: ret void |
92 | return r; |
93 | } |
94 | |