9aac6e947838e6987ec1d516f288bc0aa3027047
[openbsd] /
1 // Note: This test requires the SysV AMD64 ABI to be in use, and requires
2 // compiler support for DWARF entry values.
3
4 // Inhibit dead-arg-elim by using 'x'.
5 template<typename T> __attribute__((noinline)) void use(T x) {
6   asm volatile (""
7       /* Outputs */  :
8       /* Inputs */   : "g"(x)
9       /* Clobbers */ :
10   );
11 }
12
13 // Destroy %rsi in the current frame.
14 #define DESTROY_RSI \
15   asm volatile ("xorq %%rsi, %%rsi" \
16       /* Outputs */  : \
17       /* Inputs */   : \
18       /* Clobbers */ : "rsi" \
19   );
20
21 // Destroy %rbx in the current frame.
22 #define DESTROY_RBX \
23   asm volatile ("xorq %%rbx, %%rbx" \
24       /* Outputs */  : \
25       /* Inputs */   : \
26       /* Clobbers */ : "rbx" \
27   );
28
29 struct S1 {
30   int field1 = 123;
31   int *field2 = &field1;
32 };
33
34 __attribute__((noinline))
35 void func1(int &sink, int x) {
36   use(x);
37
38   // Destroy 'x' in the current frame.
39   DESTROY_RSI;
40
41   // NOTE: Currently, we do not generate DW_OP_entry_value for the 'x',
42   // since it gets copied into a register that is not callee saved,
43   // and we can not guarantee that its value has not changed.
44
45   ++sink;
46
47   // Destroy 'sink' in the current frame.
48   DESTROY_RBX;
49
50   //% self.filecheck("image lookup -va $pc", "main.cpp", "-check-prefix=FUNC1-DESC")
51   // FUNC1-DESC: name = "sink", type = "int &", location = DW_OP_entry_value(DW_OP_reg5 RDI)
52 }
53
54 __attribute__((noinline))
55 void func2(int &sink, int x) {
56   use(x);
57
58   // Destroy 'x' in the current frame.
59   DESTROY_RSI;
60
61   //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC2-EXPR-FAIL", expect_cmd_failure=True)
62   // FUNC2-EXPR-FAIL: couldn't get the value of variable x: variable not available
63
64   ++sink;
65
66   // Destroy 'sink' in the current frame.
67   DESTROY_RBX;
68
69   //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC2-EXPR")
70   // FUNC2-EXPR: ${{.*}} = 2
71 }
72
73 __attribute__((noinline))
74 void func3(int &sink, int *p) {
75   use(p);
76
77   // Destroy 'p' in the current frame.
78   DESTROY_RSI;
79
80   //% self.filecheck("expr *p", "main.cpp", "-check-prefix=FUNC3-EXPR")
81   // FUNC3-EXPR: (int) ${{.*}} = 123
82
83   ++sink;
84 }
85
86 __attribute__((noinline))
87 void func4_amb(int &sink, int x) {
88   use(x);
89
90   // Destroy 'x' in the current frame.
91   DESTROY_RSI;
92
93   //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC4-EXPR-FAIL", expect_cmd_failure=True)
94   // FUNC4-EXPR-FAIL: couldn't get the value of variable x: variable not available
95
96   ++sink;
97
98   // Destroy 'sink' in the current frame.
99   DESTROY_RBX;
100
101   //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC4-EXPR", expect_cmd_failure=True)
102   // FUNC4-EXPR: couldn't get the value of variable sink: Could not evaluate DW_OP_entry_value.
103 }
104
105 __attribute__((noinline))
106 void func5_amb() {}
107
108 __attribute__((noinline))
109 void func6(int &sink, int x) {
110   if (sink > 0)
111     func4_amb(sink, x); /* tail (taken) */
112   else
113     func5_amb(); /* tail */
114 }
115
116 __attribute__((noinline))
117 void func7(int &sink, int x) {
118   //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC7-BT")
119   // FUNC7-BT: func7
120   // FUNC7-BT-NEXT: [inlined] func8_inlined
121   // FUNC7-BT-NEXT: [inlined] func9_inlined
122   // FUNC7-BT-NEXT: func10
123   use(x);
124
125   // Destroy 'x' in the current frame.
126   DESTROY_RSI;
127
128   //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC7-EXPR-FAIL", expect_cmd_failure=True)
129   // FUNC7-EXPR-FAIL: couldn't get the value of variable x: variable not available
130
131   ++sink;
132
133   // Destroy 'sink' in the current frame.
134   DESTROY_RBX;
135
136   //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC7-EXPR")
137   // FUNC7-EXPR: ${{.*}} = 4
138 }
139
140 __attribute__((always_inline))
141 void func8_inlined(int &sink, int x) {
142   func7(sink, x);
143 }
144
145 __attribute__((always_inline))
146 void func9_inlined(int &sink, int x) {
147   func8_inlined(sink, x);
148 }
149
150 __attribute__((noinline, disable_tail_calls))
151 void func10(int &sink, int x) {
152   func9_inlined(sink, x);
153 }
154
155 __attribute__((noinline))
156 void func11_tailcalled(int &sink, int x) {
157   //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC11-BT")
158   // FUNC11-BT: func11_tailcalled{{.*}}
159   // FUNC11-BT-NEXT: func12{{.*}} [artificial]
160   use(x);
161
162   // Destroy 'x' in the current frame.
163   DESTROY_RSI;
164
165   //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC11-EXPR-FAIL", expect_cmd_failure=True)
166   // FUNC11-EXPR-FAIL: couldn't get the value of variable x: variable not available
167
168   ++sink;
169
170   // Destroy 'sink' in the current frame.
171   DESTROY_RBX;
172
173   //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC11-EXPR")
174   // FUNC11-EXPR: ${{.*}} = 5
175 }
176
177 __attribute__((noinline))
178 void func12(int &sink, int x) {
179   func11_tailcalled(sink, x);
180 }
181
182 __attribute__((noinline))
183 void func13(int &sink, int x) {
184   //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC13-BT")
185   // FUNC13-BT: func13{{.*}}
186   // FUNC13-BT-NEXT: func14{{.*}}
187   use(x);
188
189   // Destroy 'x' in the current frame.
190   DESTROY_RSI;
191
192   //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC13-EXPR-FAIL", expect_cmd_failure=True)
193   // FUNC13-EXPR-FAIL: couldn't get the value of variable x: variable not available
194
195   use(sink);
196
197   // Destroy 'sink' in the current frame.
198   DESTROY_RBX;
199
200   //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC13-EXPR")
201   // FUNC13-EXPR: ${{.*}} = 5
202 }
203
204 __attribute__((noinline, disable_tail_calls))
205 void func14(int &sink, void (*target_no_tailcall)(int &, int)) {
206   // Move the call target into a register that won't get clobbered. Do this
207   // by calling the same indirect target twice, and hoping that regalloc is
208   // 'smart' enough to stash the call target in a non-clobbered register.
209   //
210   // llvm.org/PR43926 tracks work in the compiler to emit call targets which
211   // describe non-clobbered values.
212   target_no_tailcall(sink, 123);
213   target_no_tailcall(sink, 123);
214 }
215
216 __attribute__((disable_tail_calls))
217 int main() {
218   int sink = 0;
219   S1 s1;
220
221   // Test location dumping for DW_OP_entry_value.
222   func1(sink, 123);
223
224   // Test evaluation of "DW_OP_constu" in the parent frame.
225   func2(sink, 123);
226
227   // Test evaluation of "DW_OP_fbreg -24, DW_OP_deref" in the parent frame.
228   // Disabled for now, see: llvm.org/PR43343
229 #if 0
230   func3(sink, s1.field2);
231 #endif
232
233   // The sequences `main -> func4 -> func{5,6}_amb -> sink` are both plausible.
234   // Test that lldb doesn't attempt to guess which one occurred: entry value
235   // evaluation should fail.
236   func6(sink, 123);
237
238   // Test that evaluation can "see through" inlining.
239   func10(sink, 123);
240
241   // Test that evaluation can "see through" tail calls.
242   func12(sink, 123);
243
244   // Test that evaluation can "see through" an indirect tail call.
245   func14(sink, func13);
246
247   return 0;
248 }