--- /dev/null
+from lldbsuite.test import lldbinline
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbplatformutil
+
+supported_archs = ["x86_64", "arm", "aarch64"]
+
+lldbinline.MakeInlineTest(__file__, globals(),
+ [skipIf(archs=no_match(supported_archs)),
+ skipIf(compiler="clang", compiler_version=['<', '10.0']),
+ skipUnlessHasCallSiteInfo,
+ skipIf(dwarf_version=['<', '4'])])
--- /dev/null
+// Note: This test requires compiler support for DWARF entry values.
+
+int dummy;
+volatile int global = 0;
+
+template <typename... T> __attribute__((optnone)) void use(T...) {}
+
+struct S1 {
+ int field1 = 123;
+ int *field2 = &field1;
+};
+
+__attribute__((noinline)) void func1(int &sink) {
+ // First use works around a compiler "bug" where an unused variable gets
+ // no location descriptions. The second use overwrites the function arguments
+ // with other values.
+ use<int &>(sink);
+ use<int &>(dummy);
+
+ ++global;
+ //% self.filecheck("image lookup -va $pc", "main.cpp", "-check-prefix=FUNC1-DESC")
+ // FUNC1-DESC: name = "sink", type = "int &", location = DW_OP_entry_value
+}
+
+__attribute__((noinline)) void func2(int &sink, int x) {
+ use<int &, int>(sink, x);
+ use<int &, int>(dummy, 0);
+
+ ++global;
+ //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC2-EXPR1")
+ //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC2-EXPR2")
+ // FUNC2-EXPR1: ${{.*}} = 123
+ // FUNC2-EXPR2: ${{.*}} = 2
+}
+
+__attribute__((noinline)) void func3(int &sink, int *p) {
+ use<int &, int *>(sink, p);
+ use<int &, int *>(dummy, nullptr);
+
+ //% self.filecheck("expr *p", "main.cpp", "-check-prefix=FUNC3-EXPR")
+ // FUNC3-EXPR: (int) ${{.*}} = 123
+}
+
+__attribute__((noinline)) void func4_amb(int &sink, int x) {
+ use<int &, int>(sink, x);
+ use<int &, int>(dummy, 0);
+
+ ++global;
+ //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC4-EXPR-FAIL",
+ //% expect_cmd_failure=True)
+ //% self.filecheck("expr sink", "main.cpp","-check-prefix=FUNC4-EXPR",
+ //% expect_cmd_failure=True)
+ // FUNC4-EXPR-FAIL: couldn't get the value of variable x: Could not evaluate
+ // DW_OP_entry_value. FUNC4-EXPR: couldn't get the value of variable sink:
+ // Could not evaluate DW_OP_entry_value.
+}
+
+__attribute__((noinline)) void func5_amb() {}
+
+__attribute__((noinline)) void func6(int &sink, int x) {
+ if (sink > 0)
+ func4_amb(sink, x); /* tail (taken) */
+ else
+ func5_amb(); /* tail */
+}
+
+__attribute__((noinline)) void func7(int &sink, int x) {
+ //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC7-BT")
+ // FUNC7-BT: func7
+ // FUNC7-BT-NEXT: [inlined] func8_inlined
+ // FUNC7-BT-NEXT: [inlined] func9_inlined
+ // FUNC7-BT-NEXT: func10
+ use<int &, int>(sink, x);
+ use<int &, int>(dummy, 0);
+
+ ++global;
+ //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC7-EXPR1")
+ //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC7-EXPR2")
+ // FUNC7-EXPR1: ${{.*}} = 123
+ // FUNC7-EXPR2: ${{.*}} = 5
+}
+
+__attribute__((always_inline)) void func8_inlined(int &sink, int x) {
+ func7(sink, x);
+}
+
+__attribute__((always_inline)) void func9_inlined(int &sink, int x) {
+ func8_inlined(sink, x);
+}
+
+__attribute__((noinline, disable_tail_calls)) void func10(int &sink, int x) {
+ func9_inlined(sink, x);
+}
+
+__attribute__((noinline)) void func11_tailcalled(int &sink, int x) {
+ //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC11-BT")
+ // FUNC11-BT: func11_tailcalled{{.*}}
+ // FUNC11-BT-NEXT: func12{{.*}} [artificial]
+ use<int &, int>(sink, x);
+ use<int &, int>(dummy, 0);
+
+ ++global;
+ //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC11-EXPR1")
+ //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC11-EXPR2")
+ // FUNC11-EXPR1: ${{.*}} = 123
+ // FUNC11-EXPR2: ${{.*}} = 5
+}
+
+__attribute__((noinline)) void func12(int &sink, int x) {
+ func11_tailcalled(sink, x);
+}
+
+__attribute__((noinline)) void func13(int &sink, int x) {
+ //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC13-BT")
+ // FUNC13-BT: func13{{.*}}
+ // FUNC13-BT-NEXT: func14{{.*}}
+ use<int &, int>(sink, x);
+ use<int &, int>(dummy, 0);
+
+ ++global;
+
+ //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC13-EXPR1")
+ //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC13-EXPR2")
+ // FUNC13-EXPR1: ${{.*}} = 123
+ // FUNC13-EXPR2: ${{.*}} = 5
+}
+
+__attribute__((noinline, disable_tail_calls)) void
+func14(int &sink, void (*target_no_tailcall)(int &, int)) {
+ // Move the call target into a register that won't get clobbered. Do this
+ // by calling the same indirect target twice, and hoping that regalloc is
+ // 'smart' enough to stash the call target in a non-clobbered register.
+ //
+ // llvm.org/PR43926 tracks work in the compiler to emit call targets which
+ // describe non-clobbered values.
+ target_no_tailcall(sink, 123);
+ target_no_tailcall(sink, 123);
+}
+
+__attribute__((disable_tail_calls)) int main() {
+ int sink = 0;
+ S1 s1;
+
+ // Test location dumping for DW_OP_entry_value.
+ func1(sink);
+
+ sink = 2;
+ // Test evaluation of "DW_OP_constu" in the parent frame.
+ func2(sink, 123);
+
+ // Test evaluation of "DW_OP_fbreg -24, DW_OP_deref" in the parent frame.
+ // Disabled for now, see: llvm.org/PR43343
+#if 0
+ func3(sink, s1.field2);
+#endif
+
+ // The sequences `main -> func4 -> func{5,6}_amb -> sink` are both plausible.
+ // Test that lldb doesn't attempt to guess which one occurred: entry value
+ // evaluation should fail.
+ func6(sink, 123);
+
+ sink = 5;
+ // Test that evaluation can "see through" inlining.
+ func10(sink, 123);
+
+ // Test that evaluation can "see through" tail calls.
+ func12(sink, 123);
+
+ // Test that evaluation can "see through" an indirect tail call.
+ func14(sink, func13);
+
+ return 0;
+}
+++ /dev/null
-from lldbsuite.test import lldbinline
-from lldbsuite.test import decorators
-from lldbsuite.test import lldbplatformutil
-
-supported_platforms = ["linux"]
-supported_platforms.extend(lldbplatformutil.getDarwinOSTriples())
-
-lldbinline.MakeInlineTest(__file__, globals(),
- [decorators.skipIf(bugnumber="llvm.org/PR44774"),
- decorators.skipUnlessPlatform(supported_platforms),
- decorators.skipIf(compiler="clang", compiler_version=['<', '10.0']),
- decorators.skipUnlessArch('x86_64'),
- decorators.skipUnlessHasCallSiteInfo,
- decorators.skipIf(dwarf_version=['<', '4'])])
+++ /dev/null
-// Note: This test requires the SysV AMD64 ABI to be in use, and requires
-// compiler support for DWARF entry values.
-
-// Inhibit dead-arg-elim by using 'x'.
-template<typename T> __attribute__((noinline)) void use(T x) {
- asm volatile (""
- /* Outputs */ :
- /* Inputs */ : "g"(x)
- /* Clobbers */ :
- );
-}
-
-// Destroy %rsi in the current frame.
-#define DESTROY_RSI \
- asm volatile ("xorq %%rsi, %%rsi" \
- /* Outputs */ : \
- /* Inputs */ : \
- /* Clobbers */ : "rsi" \
- );
-
-// Destroy %rbx in the current frame.
-#define DESTROY_RBX \
- asm volatile ("xorq %%rbx, %%rbx" \
- /* Outputs */ : \
- /* Inputs */ : \
- /* Clobbers */ : "rbx" \
- );
-
-struct S1 {
- int field1 = 123;
- int *field2 = &field1;
-};
-
-__attribute__((noinline))
-void func1(int &sink, int x) {
- use(x);
-
- // Destroy 'x' in the current frame.
- DESTROY_RSI;
-
- // NOTE: Currently, we do not generate DW_OP_entry_value for the 'x',
- // since it gets copied into a register that is not callee saved,
- // and we can not guarantee that its value has not changed.
-
- ++sink;
-
- // Destroy 'sink' in the current frame.
- DESTROY_RBX;
-
- //% self.filecheck("image lookup -va $pc", "main.cpp", "-check-prefix=FUNC1-DESC")
- // FUNC1-DESC: name = "sink", type = "int &", location = DW_OP_entry_value(DW_OP_reg5 RDI)
-}
-
-__attribute__((noinline))
-void func2(int &sink, int x) {
- use(x);
-
- // Destroy 'x' in the current frame.
- DESTROY_RSI;
-
- //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC2-EXPR-FAIL", expect_cmd_failure=True)
- // FUNC2-EXPR-FAIL: couldn't get the value of variable x: variable not available
-
- ++sink;
-
- // Destroy 'sink' in the current frame.
- DESTROY_RBX;
-
- //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC2-EXPR")
- // FUNC2-EXPR: ${{.*}} = 2
-}
-
-__attribute__((noinline))
-void func3(int &sink, int *p) {
- use(p);
-
- // Destroy 'p' in the current frame.
- DESTROY_RSI;
-
- //% self.filecheck("expr *p", "main.cpp", "-check-prefix=FUNC3-EXPR")
- // FUNC3-EXPR: (int) ${{.*}} = 123
-
- ++sink;
-}
-
-__attribute__((noinline))
-void func4_amb(int &sink, int x) {
- use(x);
-
- // Destroy 'x' in the current frame.
- DESTROY_RSI;
-
- //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC4-EXPR-FAIL", expect_cmd_failure=True)
- // FUNC4-EXPR-FAIL: couldn't get the value of variable x: variable not available
-
- ++sink;
-
- // Destroy 'sink' in the current frame.
- DESTROY_RBX;
-
- //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC4-EXPR", expect_cmd_failure=True)
- // FUNC4-EXPR: couldn't get the value of variable sink: Could not evaluate DW_OP_entry_value.
-}
-
-__attribute__((noinline))
-void func5_amb() {}
-
-__attribute__((noinline))
-void func6(int &sink, int x) {
- if (sink > 0)
- func4_amb(sink, x); /* tail (taken) */
- else
- func5_amb(); /* tail */
-}
-
-__attribute__((noinline))
-void func7(int &sink, int x) {
- //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC7-BT")
- // FUNC7-BT: func7
- // FUNC7-BT-NEXT: [inlined] func8_inlined
- // FUNC7-BT-NEXT: [inlined] func9_inlined
- // FUNC7-BT-NEXT: func10
- use(x);
-
- // Destroy 'x' in the current frame.
- DESTROY_RSI;
-
- //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC7-EXPR-FAIL", expect_cmd_failure=True)
- // FUNC7-EXPR-FAIL: couldn't get the value of variable x: variable not available
-
- ++sink;
-
- // Destroy 'sink' in the current frame.
- DESTROY_RBX;
-
- //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC7-EXPR")
- // FUNC7-EXPR: ${{.*}} = 4
-}
-
-__attribute__((always_inline))
-void func8_inlined(int &sink, int x) {
- func7(sink, x);
-}
-
-__attribute__((always_inline))
-void func9_inlined(int &sink, int x) {
- func8_inlined(sink, x);
-}
-
-__attribute__((noinline, disable_tail_calls))
-void func10(int &sink, int x) {
- func9_inlined(sink, x);
-}
-
-__attribute__((noinline))
-void func11_tailcalled(int &sink, int x) {
- //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC11-BT")
- // FUNC11-BT: func11_tailcalled{{.*}}
- // FUNC11-BT-NEXT: func12{{.*}} [artificial]
- use(x);
-
- // Destroy 'x' in the current frame.
- DESTROY_RSI;
-
- //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC11-EXPR-FAIL", expect_cmd_failure=True)
- // FUNC11-EXPR-FAIL: couldn't get the value of variable x: variable not available
-
- ++sink;
-
- // Destroy 'sink' in the current frame.
- DESTROY_RBX;
-
- //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC11-EXPR")
- // FUNC11-EXPR: ${{.*}} = 5
-}
-
-__attribute__((noinline))
-void func12(int &sink, int x) {
- func11_tailcalled(sink, x);
-}
-
-__attribute__((noinline))
-void func13(int &sink, int x) {
- //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC13-BT")
- // FUNC13-BT: func13{{.*}}
- // FUNC13-BT-NEXT: func14{{.*}}
- use(x);
-
- // Destroy 'x' in the current frame.
- DESTROY_RSI;
-
- //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC13-EXPR-FAIL", expect_cmd_failure=True)
- // FUNC13-EXPR-FAIL: couldn't get the value of variable x: variable not available
-
- use(sink);
-
- // Destroy 'sink' in the current frame.
- DESTROY_RBX;
-
- //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC13-EXPR")
- // FUNC13-EXPR: ${{.*}} = 5
-}
-
-__attribute__((noinline, disable_tail_calls))
-void func14(int &sink, void (*target_no_tailcall)(int &, int)) {
- // Move the call target into a register that won't get clobbered. Do this
- // by calling the same indirect target twice, and hoping that regalloc is
- // 'smart' enough to stash the call target in a non-clobbered register.
- //
- // llvm.org/PR43926 tracks work in the compiler to emit call targets which
- // describe non-clobbered values.
- target_no_tailcall(sink, 123);
- target_no_tailcall(sink, 123);
-}
-
-__attribute__((disable_tail_calls))
-int main() {
- int sink = 0;
- S1 s1;
-
- // Test location dumping for DW_OP_entry_value.
- func1(sink, 123);
-
- // Test evaluation of "DW_OP_constu" in the parent frame.
- func2(sink, 123);
-
- // Test evaluation of "DW_OP_fbreg -24, DW_OP_deref" in the parent frame.
- // Disabled for now, see: llvm.org/PR43343
-#if 0
- func3(sink, s1.field2);
-#endif
-
- // The sequences `main -> func4 -> func{5,6}_amb -> sink` are both plausible.
- // Test that lldb doesn't attempt to guess which one occurred: entry value
- // evaluation should fail.
- func6(sink, 123);
-
- // Test that evaluation can "see through" inlining.
- func10(sink, 123);
-
- // Test that evaluation can "see through" tail calls.
- func12(sink, 123);
-
- // Test that evaluation can "see through" an indirect tail call.
- func14(sink, func13);
-
- return 0;
-}