| 1 | // Purpose: |
| 2 | // Verifies that the debugging experience of loops marked optnone is as expected. |
| 3 | |
| 4 | // REQUIRES: lldb |
| 5 | // UNSUPPORTED: system-windows |
| 6 | // UNSUPPORTED: system-darwin |
| 7 | |
| 8 | // RUN: %clang++ -std=gnu++11 -O2 -g %s -o %t |
| 9 | // RUN: %dexter --fail-lt 1.0 -w \ |
| 10 | // RUN: --binary %t --debugger 'lldb' -- %s |
| 11 | |
| 12 | // A simple loop of assignments. |
| 13 | // With optimization level > 0 the compiler reorders basic blocks |
| 14 | // based on the basic block frequency analysis information. |
| 15 | // This also happens with optnone and it shouldn't. |
| 16 | // This is not affecting debug info so it is a minor limitation. |
| 17 | // Basic block placement based on the block frequency analysis |
| 18 | // is normally done to improve i-Cache performances. |
| 19 | __attribute__((optnone)) void simple_memcpy_loop(int *dest, const int *src, |
| 20 | unsigned nelems) { |
| 21 | for (unsigned i = 0; i != nelems; ++i) |
| 22 | dest[i] = src[i]; // DexLabel('target_simple_memcpy_loop') |
| 23 | } |
| 24 | |
| 25 | // DexLimitSteps('i', 0, 4, 8, on_line=ref('target_simple_memcpy_loop')) |
| 26 | // DexExpectWatchValue('nelems', '16', on_line=ref('target_simple_memcpy_loop')) |
| 27 | // DexExpectWatchValue('src[i]', '3', '7', '1', on_line=ref('target_simple_memcpy_loop')) |
| 28 | |
| 29 | |
| 30 | // A trivial loop that could be optimized into a builtin memcpy |
| 31 | // which is either expanded into a optimal sequence of mov |
| 32 | // instructions or directly into a call to memset@plt |
| 33 | __attribute__((optnone)) void trivial_memcpy_loop(int *dest, const int *src) { |
| 34 | for (unsigned i = 0; i != 16; ++i) |
| 35 | dest[i] = src[i]; // DexLabel('target_trivial_memcpy_loop') |
| 36 | } |
| 37 | |
| 38 | // DexLimitSteps('i', 3, 7, 9, 14, 15, on_line=ref('target_trivial_memcpy_loop')) |
| 39 | // DexExpectWatchValue('i', 3, 7, 9, 14, 15, on_line=ref('target_trivial_memcpy_loop')) |
| 40 | // DexExpectWatchValue('dest[i-1] == src[i-1]', 'true', on_line=ref('target_trivial_memcpy_loop')) |
| 41 | |
| 42 | |
| 43 | __attribute__((always_inline)) int foo(int a) { return a + 5; } |
| 44 | |
| 45 | // A trivial loop of calls to a 'always_inline' function. |
| 46 | __attribute__((optnone)) void nonleaf_function_with_loop(int *dest, |
| 47 | const int *src) { |
| 48 | for (unsigned i = 0; i != 16; ++i) |
| 49 | dest[i] = foo(a: src[i]); // DexLabel('target_nonleaf_function_with_loop') |
| 50 | } |
| 51 | |
| 52 | // DexLimitSteps('i', 1, on_line=ref('target_nonleaf_function_with_loop')) |
| 53 | // DexExpectWatchValue('dest[0]', '8', on_line=ref('target_nonleaf_function_with_loop')) |
| 54 | // DexExpectWatchValue('dest[1]', '4', on_line=ref('target_nonleaf_function_with_loop')) |
| 55 | // DexExpectWatchValue('dest[2]', '5', on_line=ref('target_nonleaf_function_with_loop')) |
| 56 | // DexExpectWatchValue('src[0]', '8', on_line=ref('target_nonleaf_function_with_loop')) |
| 57 | // DexExpectWatchValue('src[1]', '4', on_line=ref('target_nonleaf_function_with_loop')) |
| 58 | // DexExpectWatchValue('src[2]', '5', on_line=ref('target_nonleaf_function_with_loop')) |
| 59 | |
| 60 | // DexExpectWatchValue('src[1] == dest[1]', 'true', on_line=ref('target_nonleaf_function_with_loop')) |
| 61 | // DexExpectWatchValue('src[2] == dest[2]', 'true', on_line=ref('target_nonleaf_function_with_loop')) |
| 62 | |
| 63 | |
| 64 | // This entire function could be optimized into a |
| 65 | // simple movl %esi, %eax. |
| 66 | // That is because we can compute the loop trip count |
| 67 | // knowing that ind-var 'i' can never be negative. |
| 68 | __attribute__((optnone)) int counting_loop(unsigned values) { |
| 69 | unsigned i = 0; |
| 70 | while (values--) // DexLabel('target_counting_loop') |
| 71 | i++; |
| 72 | return i; |
| 73 | } |
| 74 | |
| 75 | // DexLimitSteps('i', 8, 16, on_line=ref('target_counting_loop')) |
| 76 | // DexExpectWatchValue('i', 8, 16, on_line=ref('target_counting_loop')) |
| 77 | |
| 78 | |
| 79 | // This loop could be rotated. |
| 80 | // while(cond){ |
| 81 | // .. |
| 82 | // cond--; |
| 83 | // } |
| 84 | // |
| 85 | // --> |
| 86 | // if(cond) { |
| 87 | // do { |
| 88 | // ... |
| 89 | // cond--; |
| 90 | // } while(cond); |
| 91 | // } |
| 92 | // |
| 93 | // the compiler will not try to optimize this function. |
| 94 | // However the Machine BB Placement Pass will try |
| 95 | // to reorder the basic block that computes the |
| 96 | // expression 'count' in order to simplify the control |
| 97 | // flow. |
| 98 | __attribute__((optnone)) int loop_rotate_test(int *src, unsigned count) { |
| 99 | int result = 0; |
| 100 | |
| 101 | while (count) { |
| 102 | result += src[count - 1]; // DexLabel('target_loop_rotate_test') |
| 103 | count--; |
| 104 | } |
| 105 | return result; // DexLabel('target_loop_rotate_test_ret') |
| 106 | } |
| 107 | |
| 108 | // DexLimitSteps('result', 13, on_line=ref('target_loop_rotate_test')) |
| 109 | // DexExpectWatchValue('src[count]', 13, on_line=ref('target_loop_rotate_test')) |
| 110 | // DexLimitSteps('result', 158, on_line=ref('target_loop_rotate_test_ret')) |
| 111 | // DexExpectWatchValue('result', 158, on_line=ref('target_loop_rotate_test_ret')) |
| 112 | |
| 113 | |
| 114 | typedef int *intptr __attribute__((aligned(16))); |
| 115 | |
| 116 | // This loop can be vectorized if we enable |
| 117 | // the loop vectorizer. |
| 118 | __attribute__((optnone)) void loop_vectorize_test(intptr dest, intptr src) { |
| 119 | unsigned count = 0; |
| 120 | |
| 121 | int tempArray[16]; |
| 122 | |
| 123 | while(count != 16) { // DexLabel('target_loop_vectorize_test') |
| 124 | tempArray[count] = src[count]; |
| 125 | tempArray[count+1] = src[count+1]; // DexLabel('target_loop_vectorize_test_2') |
| 126 | tempArray[count+2] = src[count+2]; // DexLabel('target_loop_vectorize_test_3') |
| 127 | tempArray[count+3] = src[count+3]; // DexLabel('target_loop_vectorize_test_4') |
| 128 | dest[count] = tempArray[count]; // DexLabel('target_loop_vectorize_test_5') |
| 129 | dest[count+1] = tempArray[count+1]; // DexLabel('target_loop_vectorize_test_6') |
| 130 | dest[count+2] = tempArray[count+2]; // DexLabel('target_loop_vectorize_test_7') |
| 131 | dest[count+3] = tempArray[count+3]; // DexLabel('target_loop_vectorize_test_8') |
| 132 | count += 4; // DexLabel('target_loop_vectorize_test_9') |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | // DexLimitSteps('count', 4, 8, 12, 16, from_line=ref('target_loop_vectorize_test'), to_line=ref('target_loop_vectorize_test_9')) |
| 137 | // DexExpectWatchValue('tempArray[count] == src[count]', 'true', on_line=ref('target_loop_vectorize_test_2')) |
| 138 | // DexExpectWatchValue('tempArray[count+1] == src[count+1]', 'true', on_line=ref('target_loop_vectorize_test_3')) |
| 139 | // DexExpectWatchValue('tempArray[count+2] == src[count+2]', 'true', on_line=ref('target_loop_vectorize_test_4')) |
| 140 | // DexExpectWatchValue('tempArray[count+3] == src[count+3]', 'true', on_line=ref('target_loop_vectorize_test_5')) |
| 141 | // DexExpectWatchValue('dest[count] == tempArray[count]', 'true', on_line=ref('target_loop_vectorize_test_6')) |
| 142 | // DexExpectWatchValue('dest[count+1] == tempArray[count+1]', 'true', on_line=ref('target_loop_vectorize_test_7')) |
| 143 | // DexExpectWatchValue('dest[count+2] == tempArray[count+2]', 'true', on_line=ref('target_loop_vectorize_test_8')) |
| 144 | // DexExpectWatchValue('dest[count+3] == tempArray[count+3]', 'true', on_line=ref('target_loop_vectorize_test_9')) |
| 145 | |
| 146 | |
| 147 | int main() { |
| 148 | int A[] = {3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; |
| 149 | int B[] = {13, 14, 15, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; |
| 150 | int C[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| 151 | |
| 152 | simple_memcpy_loop(dest: C, src: A, nelems: 16); |
| 153 | trivial_memcpy_loop(dest: B, src: C); |
| 154 | nonleaf_function_with_loop(dest: B, src: B); |
| 155 | int count = counting_loop(values: 16); |
| 156 | count += loop_rotate_test(src: B, count: 16); |
| 157 | loop_vectorize_test(dest: A, src: B); |
| 158 | |
| 159 | return A[0] + count; |
| 160 | } |
| 161 | |
| 162 | |