blob: 9aac6e947838e6987ec1d516f288bc0aa3027047 [file] [log] [blame]
// 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;
}