1 | // Note: This test requires compiler support for DWARF entry values. |
2 | |
3 | int dummy; |
4 | volatile int global = 0; |
5 | |
6 | template <typename... T> __attribute__((optnone)) void use(T...) {} |
7 | |
8 | struct S1 { |
9 | int field1 = 123; |
10 | int *field2 = &field1; |
11 | }; |
12 | |
13 | __attribute__((noinline)) void func1(int &sink) { |
14 | // First use works around a compiler "bug" where an unused variable gets |
15 | // no location descriptions. The second use overwrites the function arguments |
16 | // with other values. |
17 | use<int &>(sink); |
18 | use<int &>(dummy); |
19 | |
20 | ++global; |
21 | //% prefix = "FUNC1-GNU" if "GNU" in self.name else "FUNC1-V5" |
22 | //% self.filecheck("image lookup -v -a $pc", "main.cpp", "-check-prefix="+prefix) |
23 | // FUNC1-GNU: name = "sink", type = "int &", valid ranges = {{.*}}, location = {{.*}} DW_OP_GNU_entry_value |
24 | // FUNC1-V5: name = "sink", type = "int &", valid ranges = {{.*}}, location = {{.*}} DW_OP_entry_value |
25 | } |
26 | |
27 | __attribute__((noinline)) void func2(int &sink, int x) { |
28 | use<int &, int>(sink, x); |
29 | use<int &, int>(dummy, 0); |
30 | |
31 | ++global; |
32 | //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC2-EXPR1") |
33 | //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC2-EXPR2") |
34 | // FUNC2-EXPR1: ${{.*}} = 123 |
35 | // FUNC2-EXPR2: ${{.*}} = 2 |
36 | } |
37 | |
38 | __attribute__((noinline)) void func3(int &sink, int *p) { |
39 | use<int &, int *>(sink, p); |
40 | use<int &, int *>(dummy, nullptr); |
41 | |
42 | //% self.filecheck("expr *p", "main.cpp", "-check-prefix=FUNC3-EXPR") |
43 | // FUNC3-EXPR: (int) ${{.*}} = 123 |
44 | } |
45 | |
46 | __attribute__((noinline)) void func4_amb(int &sink, int x) { |
47 | use<int &, int>(sink, x); |
48 | use<int &, int>(dummy, 0); |
49 | |
50 | ++global; |
51 | //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC4-EXPR-FAIL", |
52 | //% expect_cmd_failure=True) |
53 | //% self.filecheck("expr sink", "main.cpp","-check-prefix=FUNC4-EXPR", |
54 | //% expect_cmd_failure=True) |
55 | // FUNC4-EXPR-FAIL: couldn't get the value of variable x: Could not evaluate |
56 | // DW_OP_entry_value. FUNC4-EXPR: couldn't get the value of variable sink: |
57 | // Could not evaluate DW_OP_entry_value. |
58 | } |
59 | |
60 | __attribute__((noinline)) void func5_amb() {} |
61 | |
62 | __attribute__((noinline)) void func6(int &sink, int x) { |
63 | if (sink > 0) |
64 | func4_amb(sink, x); /* tail (taken) */ |
65 | else |
66 | func5_amb(); /* tail */ |
67 | } |
68 | |
69 | __attribute__((noinline)) void func7(int &sink, int x) { |
70 | //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC7-BT") |
71 | // FUNC7-BT: func7 |
72 | // FUNC7-BT-NEXT: [inlined] func8_inlined |
73 | // FUNC7-BT-NEXT: [inlined] func9_inlined |
74 | // FUNC7-BT-NEXT: func10 |
75 | use<int &, int>(sink, x); |
76 | use<int &, int>(dummy, 0); |
77 | |
78 | ++global; |
79 | //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC7-EXPR1") |
80 | //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC7-EXPR2") |
81 | // FUNC7-EXPR1: ${{.*}} = 123 |
82 | // FUNC7-EXPR2: ${{.*}} = 5 |
83 | } |
84 | |
85 | __attribute__((always_inline)) void func8_inlined(int &sink, int x) { |
86 | func7(sink, x); |
87 | } |
88 | |
89 | __attribute__((always_inline)) void func9_inlined(int &sink, int x) { |
90 | func8_inlined(sink, x); |
91 | } |
92 | |
93 | __attribute__((noinline, disable_tail_calls)) void func10(int &sink, int x) { |
94 | func9_inlined(sink, x); |
95 | } |
96 | |
97 | __attribute__((noinline)) void func11_tailcalled(int &sink, int x) { |
98 | //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC11-BT") |
99 | // FUNC11-BT: func11_tailcalled{{.*}} |
100 | // FUNC11-BT-NEXT: func12{{.*}} [artificial] |
101 | use<int &, int>(sink, x); |
102 | use<int &, int>(dummy, 0); |
103 | |
104 | ++global; |
105 | //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC11-EXPR1") |
106 | //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC11-EXPR2") |
107 | // FUNC11-EXPR1: ${{.*}} = 123 |
108 | // FUNC11-EXPR2: ${{.*}} = 5 |
109 | } |
110 | |
111 | __attribute__((noinline)) void func12(int &sink, int x) { |
112 | func11_tailcalled(sink, x); |
113 | } |
114 | |
115 | __attribute__((noinline)) void func13(int &sink, int x) { |
116 | //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC13-BT") |
117 | // FUNC13-BT: func13{{.*}} |
118 | // FUNC13-BT-NEXT: func14{{.*}} |
119 | use<int &, int>(sink, x); |
120 | use<int &, int>(dummy, 0); |
121 | |
122 | ++global; |
123 | |
124 | //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC13-EXPR1") |
125 | //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC13-EXPR2") |
126 | // FUNC13-EXPR1: ${{.*}} = 123 |
127 | // FUNC13-EXPR2: ${{.*}} = 5 |
128 | } |
129 | |
130 | __attribute__((noinline, disable_tail_calls)) void |
131 | func14(int &sink, void (*target_no_tailcall)(int &, int)) { |
132 | // Move the call target into a register that won't get clobbered. Do this |
133 | // by calling the same indirect target twice, and hoping that regalloc is |
134 | // 'smart' enough to stash the call target in a non-clobbered register. |
135 | // |
136 | // llvm.org/PR43926 tracks work in the compiler to emit call targets which |
137 | // describe non-clobbered values. |
138 | target_no_tailcall(sink, 123); |
139 | target_no_tailcall(sink, 123); |
140 | } |
141 | |
142 | /// A structure that is guaranteed -- when passed to a callee by value -- to be |
143 | /// passed via a pointer to a temporary copy in the caller. On x86_64 & aarch64 |
144 | /// only. |
145 | struct StructPassedViaPointerToTemporaryCopy { |
146 | // Under the 64-bit AAPCS, a struct larger than 16 bytes is not SROA'd, and |
147 | // is instead passed via pointer to a temporary copy. |
148 | long a, b, c; |
149 | StructPassedViaPointerToTemporaryCopy() : a(1), b(2), c(3) {} |
150 | |
151 | // Failing that, a virtual method forces passing via pointer to a temporary |
152 | // copy under the common calling conventions (e.g. 32/64-bit x86, Linux/Win, |
153 | // according to https://www.agner.org/optimize/calling_conventions.pdf). |
154 | virtual void add_vtable() {} |
155 | }; |
156 | |
157 | __attribute__((noinline)) void func15(StructPassedViaPointerToTemporaryCopy S) { |
158 | use<StructPassedViaPointerToTemporaryCopy &>(S); |
159 | use<int &>(dummy); |
160 | |
161 | ++global; |
162 | //% self.filecheck("expr S", "main.cpp", "-check-prefix=FUNC15-EXPR") |
163 | // FUNC15-EXPR: (a = 1, b = 2, c = 3) |
164 | } |
165 | |
166 | __attribute__((disable_tail_calls)) int main() { |
167 | int sink = 0; |
168 | S1 s1; |
169 | |
170 | // Test location dumping for DW_OP_entry_value. |
171 | func1(sink); |
172 | |
173 | sink = 2; |
174 | // Test evaluation of "DW_OP_constu" in the parent frame. |
175 | func2(sink, x: 123); |
176 | |
177 | // Test evaluation of "DW_OP_fbreg -24, DW_OP_deref" in the parent frame. |
178 | // Disabled for now, see: llvm.org/PR43343 |
179 | #if 0 |
180 | func3(sink, s1.field2); |
181 | #endif |
182 | |
183 | // The sequences `main -> func4 -> func{5,6}_amb -> sink` are both plausible. |
184 | // Test that lldb doesn't attempt to guess which one occurred: entry value |
185 | // evaluation should fail. |
186 | func6(sink, x: 123); |
187 | |
188 | sink = 5; |
189 | // Test that evaluation can "see through" inlining. |
190 | func10(sink, x: 123); |
191 | |
192 | // Test that evaluation can "see through" tail calls. |
193 | func12(sink, x: 123); |
194 | |
195 | // Test that evaluation can "see through" an indirect tail call. |
196 | func14(sink, target_no_tailcall: func13); |
197 | |
198 | // Test evaluation of an entry value that dereferences a temporary stack |
199 | // slot set up by the caller for a StructPassedViaPointerToTemporaryCopy. |
200 | func15(S: StructPassedViaPointerToTemporaryCopy()); |
201 | |
202 | return 0; |
203 | } |
204 | |