diff --git a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values_x86_64/Makefile b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/Makefile rename from lldb/test/API/functionalities/param_entry_vals/basic_entry_values_x86_64/Makefile rename to lldb/test/API/functionalities/param_entry_vals/basic_entry_values/Makefile diff --git a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/TestBasicEntryValues.py b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/TestBasicEntryValues.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/TestBasicEntryValues.py @@ -0,0 +1,11 @@ +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'])]) diff --git a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp @@ -0,0 +1,173 @@ +// Note: This test requires compiler support for DWARF entry values. + +int dummy; +volatile int global = 0; + +template __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(sink); + use(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(sink, x); + use(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(sink, p); + use(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(sink, x); + use(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(sink, x); + use(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(sink, x); + use(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(sink, x); + use(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; +} diff --git a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py deleted file mode 100644 --- a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py +++ /dev/null @@ -1,14 +0,0 @@ -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'])]) diff --git a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp deleted file mode 100644 --- a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp +++ /dev/null @@ -1,248 +0,0 @@ -// 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 __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; -}