| 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 | // 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 | |
| 61 | __attribute__((noinline)) void func5_amb() {} |
| 62 | |
| 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 | |
| 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: func8_inlined |
| 74 | // FUNC7-BT-NEXT: 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 | |
| 86 | __attribute__((always_inline)) void func8_inlined(int &sink, int x) { |
| 87 | func7(sink, x); |
| 88 | } |
| 89 | |
| 90 | __attribute__((always_inline)) void func9_inlined(int &sink, int x) { |
| 91 | func8_inlined(sink, x); |
| 92 | } |
| 93 | |
| 94 | __attribute__((noinline, disable_tail_calls)) void func10(int &sink, int x) { |
| 95 | func9_inlined(sink, x); |
| 96 | } |
| 97 | |
| 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 | |
| 112 | __attribute__((noinline)) void func12(int &sink, int x) { |
| 113 | func11_tailcalled(sink, x); |
| 114 | } |
| 115 | |
| 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 |
| 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; |
| 150 | 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). |
| 155 | virtual void add_vtable() {} |
| 156 | }; |
| 157 | |
| 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 | |
| 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, x: 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, x: 123); |
| 188 | |
| 189 | sink = 5; |
| 190 | // Test that evaluation can "see through" inlining. |
| 191 | func10(sink, x: 123); |
| 192 | |
| 193 | // Test that evaluation can "see through" tail calls. |
| 194 | func12(sink, x: 123); |
| 195 | |
| 196 | // Test that evaluation can "see through" an indirect tail call. |
| 197 | func14(sink, target_no_tailcall: 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(S: StructPassedViaPointerToTemporaryCopy()); |
| 202 | |
| 203 | return 0; |
| 204 | } |
| 205 | |