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