[debuginfo-tests] Add some optnone tests

Add dexter tests using the optnone attribute in various scenarios. Our users
have found optnone useful when debugging optimised code. We have these tests
downstream (and one upstream already: D89873) and we would like to contribute
them if there is any interest.

The tests are fairly self explanatory. Testing optnone with:
  * optnone-fastmath.cpp: floats and -ffast-math,
  * optnone-simple-functions: simple functions and integer arithmetic,
  * optnone-struct-and-methods: a struct with methods,
  * optnone-vectors-and-functions: templates and integer vector arithmetic.

optnone-vectors-and-functions contains two FIXMEs. The first problem is that
lldb seems to struggle with evaluating expressions with the templates used
here (example below). Perhaps this is PR42920?

  (lldb) p TypeTraits<int __attribute__((ext_vector_type(4)))>::NumElements
  error: <user expression 0>:1:1: no template named 'TypeTraits'
  TypeTraits<int __attribute__((ext_vector_type(4)))>::NumElements
  ^
The second is that while lldb cannot evaluate the following expression, gdb
can, but it reports that the variable has been optimzed away. It does this when
compiling at O0 too. llvm-dwarfdump shows that MysteryNumber does have a
location. I don't know whether the DIE is bad or if both debuggers just don't
support it.

  TypeTraits<int __attribute__((ext_vector_type(4)))>::MysteryNumber

  DW_TAG_variable
      DW_AT_specification   (0x0000006b "MysteryNumber")
      DW_AT_location        (DW_OP_addr 0x601028)
      DW_AT_linkage_name    ("_ZN10TypeTraitsIDv4_iE13MysteryNumberE")

Reviewed By: rnk

Differential Revision: https://reviews.llvm.org/D97668
This commit is contained in:
OCHyams 2021-03-02 08:45:53 +00:00
parent d6ba8ecb60
commit 14be3f0e88
4 changed files with 448 additions and 0 deletions

View File

@ -0,0 +1,104 @@
// RUN: %dexter --fail-lt 1.0 -w \
// RUN: --builder 'clang' --debugger 'lldb' \
// RUN: --cflags "-ffast-math -O2 -g" -- %s
// RUN: %dexter --fail-lt 1.0 -w \
// RUN: --builder 'clang' --debugger 'lldb' \
// RUN: --cflags "-ffast-math -O0 -g" -- %s
// REQUIRES: lldb
// UNSUPPORTED: system-windows
//// Check that the debugging experience with __attribute__((optnone)) at O2
//// matches O0. Test scalar floating point arithmetic with -ffast-math.
//// Example of strength reduction.
//// The division by 10.0f can be rewritten as a multiply by 0.1f.
//// A / 10.f ==> A * 0.1f
//// This is safe with fastmath since we treat the two operations
//// as equally precise. However we don't want this to happen
//// with optnone.
__attribute__((optnone))
float test_fdiv(float A) {
float result;
result = A / 10.f; // DexLabel('fdiv_assign')
return result; // DexLabel('fdiv_ret')
}
// DexExpectWatchValue('A', 4, on_line='fdiv_assign')
// DexExpectWatchValue('result', '0.400000006', on_line='fdiv_ret')
//// (A * B) - (A * C) ==> A * (B - C)
__attribute__((optnone))
float test_distributivity(float A, float B, float C) {
float result;
float op1 = A * B;
float op2 = A * C; // DexLabel('distributivity_op2')
result = op1 - op2; // DexLabel('distributivity_result')
return result; // DexLabel('distributivity_ret')
}
// DexExpectWatchValue('op1', '20', on_line='distributivity_op2')
// DexExpectWatchValue('op2', '24', on_line='distributivity_result')
// DexExpectWatchValue('result', '-4', on_line='distributivity_ret')
//// (A + B) + C == A + (B + C)
//// therefore, ((A + B) + C) + (A + (B + C)))
//// can be rewritten as
//// 2.0f * ((A + B) + C)
//// Clang is currently unable to spot this optimization
//// opportunity with fastmath.
__attribute__((optnone))
float test_associativity(float A, float B, float C) {
float result;
float op1 = A + B;
float op2 = B + C;
op1 += C; // DexLabel('associativity_op1')
op2 += A;
result = op1 + op2; // DexLabel('associativity_result')
return result; // DexLabel('associativity_ret')
}
// DexExpectWatchValue('op1', '9', '15', from_line='associativity_op1', to_line='associativity_result')
// DexExpectWatchValue('op2', '11', '15', from_line='associativity_op1', to_line='associativity_result')
// DexExpectWatchValue('result', '30', on_line='associativity_ret')
//// With fastmath, the ordering of instructions doesn't matter
//// since we work under the assumption that there is no loss
//// in precision. This simplifies things for the optimizer which
//// can then decide to reorder instructions and fold
//// redundant operations like this:
//// A += 5.0f
//// A -= 5.0f
//// -->
//// A
//// This function can be simplified to a return A + B.
__attribute__((optnone))
float test_simplify_fp_operations(float A, float B) {
float result = A + 10.0f; // DexLabel('fp_operations_result')
result += B; // DexLabel('fp_operations_add')
result -= 10.0f;
return result; // DexLabel('fp_operations_ret')
}
// DexExpectWatchValue('A', '8.25', on_line='fp_operations_result')
// DexExpectWatchValue('B', '26.3999996', on_line='fp_operations_result')
// DexExpectWatchValue('result', '18.25', '44.6500015', '34.6500015', from_line='fp_operations_add', to_line='fp_operations_ret')
//// Again, this is a simple return A + B.
//// Clang is unable to spot the opportunity to fold the code sequence.
__attribute__((optnone))
float test_simplify_fp_operations_2(float A, float B, float C) {
float result = A + C; // DexLabel('fp_operations_2_result')
result += B;
result -= C; // DexLabel('fp_operations_2_subtract')
return result; // DexLabel('fp_operations_2_ret')
}
// DexExpectWatchValue('A', '9.11999988', on_line='fp_operations_2_result')
// DexExpectWatchValue('B', '61.050003', on_line='fp_operations_2_result')
// DexExpectWatchValue('C', '1002.11102', on_line='fp_operations_2_result')
// DexExpectWatchValue('result', '1072.28101', '70.1699829', from_line='fp_operations_2_subtract', to_line='fp_operations_2_ret')
int main() {
float result = test_fdiv(4.0f);
result += test_distributivity(4.0f, 5.0f, 6.0f);
result += test_associativity(4.0f, 5.0f, 6.0f);
result += test_simplify_fp_operations(8.25, result);
result += test_simplify_fp_operations_2(9.12, result, 1002.111);
return static_cast<int>(result);
}

View File

@ -0,0 +1,104 @@
// RUN: %dexter --fail-lt 1.0 -w \
// RUN: --builder 'clang' --debugger 'lldb' \
// RUN: --cflags "-O2 -g" -- %s
// RUN: %dexter --fail-lt 1.0 -w \
// RUN: --builder 'clang' --debugger 'lldb' \
// RUN: --cflags "-O0 -g" -- %s
// REQUIRES: lldb
// UNSUPPORTED: system-windows
//// Check that the debugging experience with __attribute__((optnone)) at O2
//// matches O0. Test simple functions performing simple arithmetic
//// operations and small loops.
__attribute__((optnone))
int test1(int test1_a, int test1_b) {
int test1_result = 0;
// DexLabel('test1_start')
test1_result = test1_a + test1_b; // DexExpectStepOrder(1)
return test1_result; // DexExpectStepOrder(2)
// DexLabel('test1_end')
}
// DexExpectWatchValue('test1_a', 3, from_line='test1_start', to_line='test1_end')
// DexExpectWatchValue('test1_b', 4, from_line='test1_start', to_line='test1_end')
// DexExpectWatchValue('test1_result', 0, 7, from_line='test1_start', to_line='test1_end')
__attribute__((optnone))
int test2(int test2_a, int test2_b) {
int test2_result = test2_a + test2_a + test2_a + test2_a; // DexExpectStepOrder(3)
// DexLabel('test2_start')
return test2_a << 2; // DexExpectStepOrder(4)
// DexLabel('test2_end')
}
// DexExpectWatchValue('test2_a', 1, from_line='test2_start', to_line='test2_end')
// DexExpectWatchValue('test2_b', 2, from_line='test2_start', to_line='test2_end')
// DexExpectWatchValue('test2_result', 4, from_line='test2_start', to_line='test2_end')
__attribute__((optnone))
int test3(int test3_a, int test3_b) {
int test3_temp1 = 0, test3_temp2 = 0;
// DexLabel('test3_start')
test3_temp1 = test3_a + 5; // DexExpectStepOrder(5)
test3_temp2 = test3_b + 5; // DexExpectStepOrder(6)
if (test3_temp1 > test3_temp2) { // DexExpectStepOrder(7)
test3_temp1 *= test3_temp2; // DexUnreachable()
}
return test3_temp1; // DexExpectStepOrder(8)
// DexLabel('test3_end')
}
// DexExpectWatchValue('test3_a', 5, from_line='test3_start', to_line='test3_end')
// DexExpectWatchValue('test3_b', 6, from_line='test3_start', to_line='test3_end')
// DexExpectWatchValue('test3_temp1', 0, 10, from_line='test3_start', to_line='test3_end')
// DexExpectWatchValue('test3_temp2', 0, 11, from_line='test3_start', to_line='test3_end')
unsigned num_iterations = 4;
__attribute__((optnone))
int test4(int test4_a, int test4_b) {
int val1 = 0, val2 = 0;
// DexLabel('test4_start')
val1 = (test4_a > test4_b) ? test4_a : test4_b; // DexExpectStepOrder(9)
val2 = val1;
val2 += val1; // DexExpectStepOrder(10)
for (unsigned i=0; i != num_iterations; ++i) { // DexExpectStepOrder(11, 13, 15, 17, 19)
val1--;
val2 += i;
if (val2 % 2 == 0) // DexExpectStepOrder(12, 14, 16, 18)
val2 /= 2;
}
return (val1 > val2) ? val2 : val1; // DexExpectStepOrder(20)
// DexLabel('test4_end')
}
// DexExpectWatchValue('test4_a', 1, from_line='test4_start', to_line='test4_end')
// DexExpectWatchValue('test4_b', 9, from_line='test4_start', to_line='test4_end')
// DexExpectWatchValue('val1', 0, 9, 8, 7, 6, 5, from_line='test4_start', to_line='test4_end')
// DexExpectWatchValue('val2', 0, 9, 18, 9, 10, 5, 7, 10, 5, 9, from_line='test4_start', to_line='test4_end')
__attribute__((optnone))
int test5(int test5_val) {
int c = 1; // DexExpectStepOrder(21)
// DexLabel('test5_start')
if (test5_val) // DexExpectStepOrder(22)
c = 5; // DexExpectStepOrder(23)
return c ? test5_val : test5_val; // DexExpectStepOrder(24)
// DexLabel('test5_end')
}
// DexExpectWatchValue('test5_val', 7, from_line='test5_start', to_line='test5_end')
// DexExpectWatchValue('c', 1, 5, from_line='test5_start', to_line='test5_end')
int main() {
int main_result = 0;
// DexLabel('main_start')
main_result = test1(3,4);
main_result += test2(1,2);
main_result += test3(5,6);
main_result += test4(1,9);
main_result += test5(7);
return main_result;
// DexLabel('main_end')
}
// DexExpectWatchValue('main_result', 0, 7, 11, 21, 26, 33, from_line='main_start', to_line='main_end')

View File

@ -0,0 +1,105 @@
// RUN: %dexter --fail-lt 1.0 -w \
// RUN: --builder 'clang' --debugger 'lldb' \
// RUN: --cflags "-g -O2" -v -- %s
// RUN: %dexter --fail-lt 1.0 -w \
// RUN: --builder 'clang' --debugger 'lldb' \
// RUN: --cflags "-g -O0" -- %s
// REQUIRES: lldb
// UNSUPPORTED: system-windows
//// Check that the debugging experience with __attribute__((optnone)) at O2
//// matches O0. Test simple structs and methods.
long a_global_ptr[] = { 0xCAFEBABEL, 0xFEEDBEEFL };
namespace {
struct A {
int a;
float b;
enum B {
A_VALUE = 0x1,
B_VALUE = 0x2
};
struct some_data {
enum B other_b;
enum B other_other_b;
};
struct other_data {
union {
void *raw_ptr;
long *long_ptr;
float *float_ptr;
} a;
struct some_data b;
struct some_data c;
};
private:
struct other_data _data;
public:
struct other_data *getOtherData() { return &_data; }
__attribute__((always_inline,nodebug))
void setSomeData1(A::B value, A::B other_value) {
struct other_data *data = getOtherData();
data->b.other_b = value;
data->b.other_other_b = other_value;
}
__attribute__((always_inline))
void setSomeData2(A::B value, A::B other_value) {
struct other_data *data = getOtherData();
data->c.other_b = value;
data->c.other_other_b = other_value;
}
void setOtherData() {
setSomeData2(A_VALUE, B_VALUE);
getOtherData()->a.long_ptr = &a_global_ptr[0];
}
__attribute__((optnone))
A() {
__builtin_memset(this, 0xFF, sizeof(*this));
} //DexLabel('break_0')
// DexExpectWatchValue('a', '-1', on_line='break_0')
//// Check b is NaN by comparing it to itself.
// DexExpectWatchValue('this->b == this->b', 'false', on_line='break_0')
// DexExpectWatchValue('_data.a.raw_ptr == -1', 'true', on_line='break_0')
// DexExpectWatchValue('_data.a.float_ptr == -1', 'true', on_line='break_0')
// DexExpectWatchValue('_data.a.float_ptr == -1', 'true', on_line='break_0')
// DexExpectWatchValue('a_global_ptr[0]', 0xcafebabe, on_line='break_0')
// DexExpectWatchValue('a_global_ptr[1]', 0xfeedbeef, on_line='break_0')
__attribute__((optnone))
~A() {
*getOtherData()->a.long_ptr = 0xADDF00DL;
} //DexLabel('break_1')
// DexExpectWatchValue('_data.a.raw_ptr == a_global_ptr', 'true', on_line='break_1')
// DexExpectWatchValue('a_global_ptr[0]', 0xaddf00d, on_line='break_1')
__attribute__((optnone))
long getData() {
setSomeData1(B_VALUE, A_VALUE);
setOtherData();
return getOtherData()->a.long_ptr[1]; //DexLabel('break_2')
}
// DexExpectWatchValue('_data.b.other_b', 'B_VALUE', on_line='break_2')
// DexExpectWatchValue('_data.b.other_other_b', 'A_VALUE', on_line='break_2')
};
} // anonymous namespace
int main() {
int result = 0;
{
A a;
result = a.getData();
}
return result;
}

View File

@ -0,0 +1,135 @@
// RUN: %dexter --fail-lt 1.0 -w \
// RUN: --builder 'clang' --debugger 'lldb' \
// RUN: --cflags "-g -O2" -v -- %s
// RUN: %dexter --fail-lt 1.0 -w \
// RUN: --builder 'clang' --debugger 'lldb' \
// RUN: --cflags "-g -O0" -- %s
// REQUIRES: lldb
// UNSUPPORTED: system-windows
//// Check that the debugging experience with __attribute__((optnone)) at O2
//// matches O0. Test simple template functions performing simple arithmetic
//// vector operations and trivial loops.
typedef int int4 __attribute__((ext_vector_type(4)));
template<typename T> struct TypeTraits {};
template<>
struct TypeTraits<int4> {
static const unsigned NumElements = 4;
static const unsigned UnusedField = 0xDEADBEEFU;
static unsigned MysteryNumber;
};
unsigned TypeTraits<int4>::MysteryNumber = 3U;
template<typename T>
__attribute__((optnone))
T test1(T x, T y) {
T tmp = x + y; // DexLabel('break_0')
T tmp2 = tmp + y;
return tmp; // DexLabel('break_1')
}
// DexLimitSteps('1', '1', from_line='break_0', to_line='break_1')
//// FIXME: gdb can print this but lldb cannot. Perhaps PR42920?
// \DexExpectWatchValue('TypeTraits<int __attribute__((ext_vector_type(4)))>::NumElements', 4, on_line='break_0')
// \DexExpectWatchValue('TypeTraits<int __attribute__((ext_vector_type(4)))>::UnusedField', 0xdeadbeef, on_line='break_0')
// DexExpectWatchValue('x[0]', 1, on_line='break_0')
// DexExpectWatchValue('x[1]', 2, on_line='break_0')
// DexExpectWatchValue('x[2]', 3, on_line='break_0')
// DexExpectWatchValue('x[3]', 4, on_line='break_0')
// DexExpectWatchValue('y[0]', 5, on_line='break_0')
// DexExpectWatchValue('y[1]', 6, on_line='break_0')
// DexExpectWatchValue('y[2]', 7, on_line='break_0')
// DexExpectWatchValue('y[3]', 8, on_line='break_0')
// DexExpectWatchValue('tmp[0]', 6, on_line='break_1')
// DexExpectWatchValue('tmp[1]', 8, on_line='break_1')
// DexExpectWatchValue('tmp[2]', 10, on_line='break_1')
// DexExpectWatchValue('tmp[3]', 12, on_line='break_1')
// DexExpectWatchValue('tmp2[0]', 11, on_line='break_1')
// DexExpectWatchValue('tmp2[1]', 14, on_line='break_1')
// DexExpectWatchValue('tmp2[2]', 17, on_line='break_1')
// DexExpectWatchValue('tmp2[3]', 20, on_line='break_1')
template<typename T>
__attribute__((optnone))
T test2(T x, T y) {
T tmp = x;
int break_2 = 0; // DexLabel('break_2')
for (unsigned i = 0; i != TypeTraits<T>::NumElements; ++i) {
tmp <<= 1; // DexLabel('break_3')
tmp |= y;
}
tmp[0] >>= TypeTraits<T>::MysteryNumber;
return tmp; // DexLabel('break_5')
}
// DexLimitSteps('1', '1', on_line='break_2')
// DexExpectWatchValue('x[0]', 6, on_line='break_2')
// DexExpectWatchValue('x[1]', 8, on_line='break_2')
// DexExpectWatchValue('x[2]', 10, on_line='break_2')
// DexExpectWatchValue('x[3]', 12, on_line='break_2')
// DexExpectWatchValue('y[0]', 5, on_line='break_2')
// DexExpectWatchValue('y[1]', 6, on_line='break_2')
// DexExpectWatchValue('y[2]', 7, on_line='break_2')
// DexExpectWatchValue('y[3]', 8, on_line='break_2')
// DexExpectWatchValue('tmp[0]', 6, on_line='break_2')
// DexExpectWatchValue('tmp[1]', 8, on_line='break_2')
// DexExpectWatchValue('tmp[2]', 10, on_line='break_2')
// DexExpectWatchValue('tmp[3]', 12, on_line='break_2')
// DexLimitSteps('i', 3, on_line='break_3')
// DexExpectWatchValue('tmp[0]', 63, on_line='break_3')
// DexExpectWatchValue('tmp[1]', 94, on_line='break_3')
// DexExpectWatchValue('tmp[2]', 95, on_line='break_3')
// DexExpectWatchValue('tmp[3]', 120, on_line='break_3')
// DexLimitSteps('i', 3, on_line='break_5')
// DexExpectWatchValue('tmp[0]', 15, on_line='break_5')
template<typename T>
__attribute__((optnone))
T test3(T InVec) {
T result;
for (unsigned i=0; i != TypeTraits<T>::NumElements; ++i)
result[i] = InVec[i]; // DexLabel('break_6')
return result; // DexLabel('break_7')
}
// DexLimitSteps('i', '3', from_line='break_6', to_line='break_7')
// DexExpectWatchValue('InVec[0]', 15, from_line='break_6', to_line='break_7')
// DexExpectWatchValue('InVec[1]', 190, from_line='break_6', to_line='break_7')
// DexExpectWatchValue('InVec[2]', 191, from_line='break_6', to_line='break_7')
// DexExpectWatchValue('InVec[3]', 248, from_line='break_6', to_line='break_7')
// DexExpectWatchValue('result[0]', 15, from_line='break_6', to_line='break_7')
// DexExpectWatchValue('result[1]', 190, from_line='break_6', to_line='break_7')
// DexExpectWatchValue('result[2]', 191, from_line='break_6', to_line='break_7')
// DexExpectWatchValue('result[3]', 248, on_line='break_7')
template<typename T>
__attribute__((optnone))
T test4(T x, T y) {
for (unsigned i=0; i != TypeTraits<T>::NumElements; ++i)
x[i] = (x[i] > y[i])? x[i] : y[i] + TypeTraits<T>::MysteryNumber; // DexLabel('break_11')
return x; // DexLabel('break_12')
}
// DexLimitSteps('1', '1', from_line='break_11', to_line='break_12')
//// FIXME: lldb won't print this but gdb unexpectedly says it's optimized out, even at O0.
// \DexExpectWatchValue('TypeTraits<int __attribute__((ext_vector_type(4)))>::MysteryNumber', 3, on_line='break_11')
// DexExpectWatchValue('i', 0, 1, 2, 3, on_line='break_11')
// DexExpectWatchValue('x[0]', 1, 8, from_line='break_11', to_line='break_12')
// DexExpectWatchValue('x[1]', 2, 9, from_line='break_11', to_line='break_12')
// DexExpectWatchValue('x[2]', 3, 10, from_line='break_11', to_line='break_12')
// DexExpectWatchValue('x[3]', 4, 11, from_line='break_11', to_line='break_12')
// DexExpectWatchValue('y[0]', 5, from_line='break_11', to_line='break_12')
// DexExpectWatchValue('y[1]', 6, from_line='break_11', to_line='break_12')
// DexExpectWatchValue('y[2]', 7, from_line='break_11', to_line='break_12')
// DexExpectWatchValue('y[3]', 8, from_line='break_11', to_line='break_12')
int main() {
int4 a = (int4){1,2,3,4};
int4 b = (int4){5,6,7,8};
int4 tmp = test1(a,b);
tmp = test2(tmp,b);
tmp = test3(tmp);
tmp += test4(a,b);
return tmp[0];
}