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 | |