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 &", location = DW_OP_GNU_entry_value
24 // FUNC1-V5: name = "sink", type = "int &", 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 // 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
func5_amb()60 __attribute__((noinline)) void func5_amb() {}
61
func6(int & sink,int x)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
func7(int & sink,int x)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
func8_inlined(int & sink,int x)85 __attribute__((always_inline)) void func8_inlined(int &sink, int x) {
86 func7(sink, x);
87 }
88
func9_inlined(int & sink,int x)89 __attribute__((always_inline)) void func9_inlined(int &sink, int x) {
90 func8_inlined(sink, x);
91 }
92
func10(int & sink,int x)93 __attribute__((noinline, disable_tail_calls)) void func10(int &sink, int x) {
94 func9_inlined(sink, x);
95 }
96
func11_tailcalled(int & sink,int x)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
func12(int & sink,int x)111 __attribute__((noinline)) void func12(int &sink, int x) {
112 func11_tailcalled(sink, x);
113 }
114
func13(int & sink,int x)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
func14(int & sink,void (* target_no_tailcall)(int &,int))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;
StructPassedViaPointerToTemporaryCopyStructPassedViaPointerToTemporaryCopy149 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).
add_vtableStructPassedViaPointerToTemporaryCopy154 virtual void add_vtable() {}
155 };
156
func15(StructPassedViaPointerToTemporaryCopy S)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
main()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, 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, 123);
187
188 sink = 5;
189 // Test that evaluation can "see through" inlining.
190 func10(sink, 123);
191
192 // Test that evaluation can "see through" tail calls.
193 func12(sink, 123);
194
195 // Test that evaluation can "see through" an indirect tail call.
196 func14(sink, 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(StructPassedViaPointerToTemporaryCopy());
201
202 return 0;
203 }
204