/src/php-src/ext/opcache/jit/ir/ir_sccp.c
Line | Count | Source |
1 | | /* |
2 | | * IR - Lightweight JIT Compilation Framework |
3 | | * (SCCP - Sparse Conditional Constant Propagation) |
4 | | * This file is part of the IR Project distributed under the MIT-style LICENSE. |
5 | | * Authors: Dmitry Stogov <dmitry@php.net> |
6 | | * |
7 | | * The SCCP algorithm is based on M. N. Wegman and F. K. Zadeck publication |
8 | | * See: M. N. Wegman and F. K. Zadeck. "Constant propagation with conditional branches" |
9 | | * ACM Transactions on Programming Languages and Systems, 13(2):181-210, April 1991 |
10 | | */ |
11 | | |
12 | | #include "ir.h" |
13 | | #include "ir_private.h" |
14 | | |
15 | | #include <math.h> |
16 | | |
17 | | #define IR_COMBO_COPY_PROPAGATION 1 |
18 | | |
19 | 3.06M | #define IR_TOP IR_UNUSED |
20 | 6.67M | #define IR_BOTTOM IR_LAST_OP |
21 | | |
22 | 1.75M | #define IR_MAKE_BOTTOM(ref) do {IR_ASSERT(ref > 0); _values[ref].optx = IR_BOTTOM;} while (0) |
23 | | |
24 | 114k | #define IR_IS_TOP(ref) (ref >= 0 && _values[ref].op == IR_TOP) |
25 | 0 | #define IR_IS_BOTTOM(ref) (ref >= 0 && _values[ref].op == IR_BOTTOM) |
26 | 1.37M | #define IR_IS_REACHABLE(ref) _ir_is_reachable_ctrl(ctx, _values, ref) |
27 | 114k | #define IR_IS_CONST(ref) (IR_IS_CONST_REF(ref) || IR_IS_CONST_OP(_values[ref].op)) |
28 | | |
29 | | typedef struct { |
30 | | union { |
31 | | struct { |
32 | | IR_STRUCT_LOHI( |
33 | | union { |
34 | | IR_STRUCT_LOHI( |
35 | | union { |
36 | | IR_STRUCT_LOHI( |
37 | | uint8_t op, /* [IR_TOP - unreachable, IR_BOTTOM - reachable} for control */ |
38 | | /* {IR_TOP | IR_COPY() | IR_CONST() | IR_BOTTOM} for data */ |
39 | | /* {IR_TOP | IR_MERGE() | IR_BOTTOM} for IR_MERGE */ |
40 | | /* {IR_TOP | IR_IF() | IR_BOTTOM} for IR_IF and IR_SWITCH */ |
41 | | uint8_t type |
42 | | ); |
43 | | uint16_t opt; |
44 | | }, |
45 | | uint16_t _space_1 |
46 | | ); |
47 | | uint32_t optx; |
48 | | }, |
49 | | union { |
50 | | ir_ref copy; /* identity for IR_COPY */ |
51 | | ir_ref unfeasible_inputs; /* number of unfeasible inputs for IR_MERGE */ |
52 | | ir_ref single_output; /* reachable output for IR_IF */ |
53 | | ir_ref visited; /* for IR_TOP */ |
54 | | } |
55 | | ); |
56 | | union { |
57 | | struct { |
58 | | ir_ref next; /* double-linked identities list for IR_COPY */ |
59 | | ir_ref prev; /* double-linked identities list for IR_COPY */ |
60 | | }; |
61 | | ir_val val; /* constant value for IR_CONST */ |
62 | | }; |
63 | | }; |
64 | | ir_insn insn; /* constant insn for IR_CONST */ |
65 | | }; |
66 | | } ir_sccp_val; |
67 | | |
68 | | IR_ALWAYS_INLINE bool _ir_is_reachable_ctrl(const ir_ctx *ctx, const ir_sccp_val *_values, ir_ref ref) |
69 | 1.60M | { |
70 | 1.60M | IR_ASSERT(!IR_IS_CONST_REF(ref)); |
71 | 1.60M | IR_ASSERT(ir_op_flags[ctx->ir_base[ref].op] & IR_OP_FLAG_CONTROL); |
72 | 1.60M | return _values[ref].op != IR_TOP; /* BOTTOM, IF or MERGE */ |
73 | 1.60M | } |
74 | | |
75 | | IR_ALWAYS_INLINE void ir_sccp_add_uses(const ir_ctx *ctx, const ir_sccp_val *_values, ir_bitqueue *worklist, ir_ref ref) |
76 | 1.76M | { |
77 | 1.76M | const ir_use_list *use_list; |
78 | 1.76M | const ir_ref *p; |
79 | 1.76M | ir_ref n, use; |
80 | | |
81 | 1.76M | IR_ASSERT(!IR_IS_CONST_REF(ref)); |
82 | 1.76M | use_list = &ctx->use_lists[ref]; |
83 | 1.76M | n = use_list->count; |
84 | 4.40M | for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) { |
85 | 2.63M | use = *p; |
86 | 2.63M | if (_values[use].op != IR_BOTTOM) { |
87 | 2.61M | ir_bitqueue_add(worklist, use); |
88 | 2.61M | } |
89 | 2.63M | } |
90 | 1.76M | } |
91 | | |
92 | | IR_ALWAYS_INLINE void ir_sccp_add_input(const ir_ctx *ctx, ir_sccp_val *_values, ir_bitqueue *worklist, ir_ref ref) |
93 | 372 | { |
94 | 372 | IR_ASSERT(!IR_IS_CONST_REF(ref)); |
95 | 372 | IR_ASSERT(_values[ref].op == IR_TOP); |
96 | | /* do backward propagaton only once */ |
97 | 372 | if (!_values[ref].visited) { |
98 | 372 | _values[ref].visited = 1; |
99 | 372 | ir_bitqueue_add(worklist, ref); |
100 | 372 | } |
101 | 372 | } |
102 | | |
103 | | #if IR_COMBO_COPY_PROPAGATION |
104 | | IR_ALWAYS_INLINE ir_ref ir_sccp_identity(const ir_ctx *ctx, const ir_sccp_val *_values, ir_ref a) |
105 | 90.1k | { |
106 | 90.1k | if (a > 0 && _values[a].op == IR_COPY) { |
107 | 24.2k | do { |
108 | 24.2k | a = _values[a].copy; |
109 | 24.2k | IR_ASSERT(a > 0); |
110 | 24.2k | } while (_values[a].op == IR_COPY); |
111 | 24.2k | IR_ASSERT(_values[a].op == IR_BOTTOM); |
112 | 65.9k | } else if (a > 0 && _values[a].op == IR_LONG_CONST) { |
113 | 0 | a = _values[a].val.i32; |
114 | 0 | } |
115 | 90.1k | return a; |
116 | 90.1k | } |
117 | | |
118 | | #if 0 |
119 | | static void CHECK_LIST(ir_sccp_val *_values, ir_ref ref) |
120 | | { |
121 | | ir_ref member = _values[ref].op2; |
122 | | while (member != ref) { |
123 | | IR_ASSERT(_values[_values[member].op2].op3 == member); |
124 | | member = _values[member].op2; |
125 | | } |
126 | | IR_ASSERT(_values[_values[ref].op2].op3 == ref); |
127 | | } |
128 | | #else |
129 | | # define CHECK_LIST(_values, ref) |
130 | | #endif |
131 | | |
132 | | static void ir_sccp_add_identity(const ir_ctx *ctx, ir_sccp_val *_values, ir_ref src, ir_ref dst) |
133 | 6.05k | { |
134 | 6.05k | IR_ASSERT(dst > 0 && _values[dst].op != IR_BOTTOM && _values[dst].op != IR_COPY); |
135 | 6.05k | IR_ASSERT((src > 0 && (_values[src].op == IR_BOTTOM || _values[src].op == IR_COPY))); |
136 | 6.05k | IR_ASSERT(ir_sccp_identity(ctx, _values, src) != dst); |
137 | | |
138 | 6.05k | _values[dst].optx = IR_COPY; |
139 | 6.05k | _values[dst].copy = src; |
140 | | |
141 | 6.05k | if (_values[src].op == IR_BOTTOM) { |
142 | | /* initialize empty double-linked list */ |
143 | 6.05k | if (_values[src].copy != src) { |
144 | 6.05k | _values[src].copy = src; |
145 | 6.05k | _values[src].next = src; |
146 | 6.05k | _values[src].prev = src; |
147 | 6.05k | } |
148 | 6.05k | } else { |
149 | 0 | src = ir_sccp_identity(ctx, _values, src); |
150 | 0 | } |
151 | | |
152 | | /* insert into circular double-linked list */ |
153 | 6.05k | ir_ref prev = _values[src].prev; |
154 | 6.05k | _values[dst].next = src; |
155 | 6.05k | _values[dst].prev = prev; |
156 | 6.05k | _values[src].prev = dst; |
157 | 6.05k | _values[prev].next = dst; |
158 | 6.05k | CHECK_LIST(_values, dst); |
159 | 6.05k | } |
160 | | |
161 | | static void ir_sccp_split_partition(const ir_ctx *ctx, ir_sccp_val *_values, ir_bitqueue *worklist, ir_ref ref) |
162 | 6.04k | { |
163 | 6.04k | ir_ref member, head, tail, next, prev; |
164 | | |
165 | 6.04k | CHECK_LIST(_values, ref); |
166 | 6.04k | IR_MAKE_BOTTOM(ref); |
167 | 6.04k | _values[ref].copy = ref; |
168 | | |
169 | 6.04k | member = _values[ref].next; |
170 | 6.04k | head = tail = IR_UNUSED; |
171 | 12.0k | while (member != ref) { |
172 | 6.04k | if (_values[member].op != IR_BOTTOM) { |
173 | 0 | ir_bitqueue_add(worklist, member); |
174 | 0 | } |
175 | 6.04k | ir_sccp_add_uses(ctx, _values, worklist, member); |
176 | | |
177 | 6.04k | next = _values[member].next; |
178 | 6.04k | if (ir_sccp_identity(ctx, _values, member) == ref) { |
179 | | /* remove "member" from the old circular double-linked list */ |
180 | 0 | prev = _values[member].prev; |
181 | 0 | _values[prev].next = next; |
182 | 0 | _values[next].prev = prev; |
183 | | |
184 | | /* insert "member" into the new double-linked list */ |
185 | 0 | if (!head) { |
186 | 0 | head = tail = member; |
187 | 0 | } else { |
188 | 0 | _values[tail].next = member; |
189 | 0 | _values[member].prev = tail; |
190 | 0 | tail = member; |
191 | 0 | } |
192 | 0 | } |
193 | 6.04k | member = next; |
194 | 6.04k | } |
195 | | |
196 | | /* remove "ref" from the old circular double-linked list */ |
197 | 6.04k | next = _values[ref].next; |
198 | 6.04k | prev = _values[ref].prev; |
199 | 6.04k | _values[prev].next = next; |
200 | 6.04k | _values[next].prev = prev; |
201 | 6.04k | CHECK_LIST(_values, next); |
202 | | |
203 | | /* close the new circle */ |
204 | 6.04k | if (head) { |
205 | 0 | _values[ref].next = head; |
206 | 0 | _values[ref].prev = tail; |
207 | 0 | _values[tail].next = ref; |
208 | 0 | _values[head].prev = ref; |
209 | 6.04k | } else { |
210 | 6.04k | _values[ref].next = ref; |
211 | 6.04k | _values[ref].prev = ref; |
212 | 6.04k | } |
213 | 6.04k | CHECK_LIST(_values, ref); |
214 | 6.04k | } |
215 | | |
216 | | IR_ALWAYS_INLINE void ir_sccp_make_bottom_ex(const ir_ctx *ctx, ir_sccp_val *_values, ir_bitqueue *worklist, ir_ref ref) |
217 | 355k | { |
218 | 355k | if (_values[ref].op == IR_COPY) { |
219 | 0 | ir_sccp_split_partition(ctx, _values, worklist, ref); |
220 | 355k | } else { |
221 | 355k | IR_MAKE_BOTTOM(ref); |
222 | 355k | } |
223 | 355k | } |
224 | | |
225 | 355k | # define IR_MAKE_BOTTOM_EX(ref) ir_sccp_make_bottom_ex(ctx, _values, worklist, ref) |
226 | | #else |
227 | | # define ir_sccp_identity(_ctx, _values, ref) (ref) |
228 | | # define IR_MAKE_BOTTOM_EX(ref) IR_MAKE_BOTTOM(ref) |
229 | | #endif |
230 | | |
231 | | IR_ALWAYS_INLINE bool ir_sccp_meet_long_const(const ir_ctx *ctx, ir_sccp_val *_values, ir_bitqueue *worklist, ir_ref ref, ir_ref const_ref, const ir_insn *const_insn) |
232 | 0 | { |
233 | 0 | if (_values[ref].op == IR_TOP) { |
234 | | /* TOP meet NEW_CONST => NEW_CONST */ |
235 | 0 | _values[ref].optx = const_insn->opt; |
236 | 0 | _values[ref].val.i64 = const_ref; |
237 | 0 | return 1; |
238 | 0 | } else if (_values[ref].opt == const_insn->opt) { |
239 | | /* OLD_CONST meet NEW_CONST => (OLD_CONST == NEW_CONST) ? OLD_CONST : BOTTOM */ |
240 | 0 | if (_values[ref].val.i32 == const_ref) { |
241 | 0 | return 0; |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | 0 | IR_MAKE_BOTTOM_EX(ref); |
246 | 0 | return 1; |
247 | 0 | } |
248 | | |
249 | | IR_ALWAYS_INLINE bool ir_sccp_meet_const(const ir_ctx *ctx, ir_sccp_val *_values, ir_bitqueue *worklist, ir_ref ref, const ir_insn *val_insn) |
250 | 2 | { |
251 | 2 | #if IR_COMBO_COPY_PROPAGATION |
252 | 2 | IR_ASSERT(IR_IS_TYPE_SCALAR(val_insn->type)); |
253 | | #else |
254 | | if (!IR_IS_TYPE_SCALAR(val_insn->type)) { |
255 | | IR_MAKE_BOTTOM_EX(ref); |
256 | | return 1; |
257 | | } |
258 | | #endif |
259 | 2 | IR_ASSERT(IR_IS_CONST_OP(val_insn->op) || IR_IS_SYM_CONST(val_insn->op)); |
260 | | |
261 | 2 | if (_values[ref].op == IR_TOP) { |
262 | | /* TOP meet NEW_CONST => NEW_CONST */ |
263 | 2 | _values[ref].optx = val_insn->opt; |
264 | 2 | _values[ref].val.u64 = val_insn->val.u64; |
265 | 2 | return 1; |
266 | 2 | } else if (_values[ref].opt == val_insn->opt) { |
267 | | /* OLD_CONST meet NEW_CONST => (OLD_CONST == NEW_CONST) ? OLD_CONST : BOTTOM */ |
268 | 0 | if (_values[ref].val.u64 == val_insn->val.u64) { |
269 | 0 | return 0; |
270 | 0 | } |
271 | 0 | } |
272 | | |
273 | 0 | IR_MAKE_BOTTOM_EX(ref); |
274 | 0 | return 1; |
275 | 2 | } |
276 | | |
277 | | IR_ALWAYS_INLINE bool ir_sccp_meet_copy(const ir_ctx *ctx, ir_sccp_val *_values, ir_bitqueue *worklist, ir_ref ref, ir_ref val) |
278 | 12.1k | { |
279 | 12.1k | #if IR_COMBO_COPY_PROPAGATION |
280 | 12.1k | if (_values[ref].op == IR_COPY) { |
281 | | /* COPY(OLD_VAL) meet COPY(NEW_VAL) => |
282 | | * (IDENTITY(OLD_VAL) == IDENTITY(NEW_VAL) ? COPY(OLD_VAL) ? BOTTOM */ |
283 | 6.04k | if (ir_sccp_identity(ctx, _values, ref) == ir_sccp_identity(ctx, _values, val)) { |
284 | 0 | return 0; /* not changed */ |
285 | 0 | } |
286 | 6.04k | ir_sccp_split_partition(ctx, _values, worklist, ref); |
287 | 6.04k | return 1; |
288 | 6.05k | } else { |
289 | 6.05k | IR_ASSERT(_values[ref].op != IR_BOTTOM); |
290 | | /* TOP meet COPY(NEW_VAL) -> COPY(NEW_VAL) */ |
291 | | /* OLD_CONST meet COPY(NEW_VAL) -> COPY(NEW_VAL) */ |
292 | 6.05k | ir_sccp_add_identity(ctx, _values, val, ref); |
293 | 6.05k | return 1; |
294 | 6.05k | } |
295 | 0 | #endif |
296 | 0 | IR_MAKE_BOTTOM(ref); |
297 | 0 | return 1; |
298 | 0 | } |
299 | | |
300 | | #if 0 |
301 | | IR_ALWAYS_INLINE bool ir_sccp_meet(const ir_ctx *ctx, ir_sccp_val *_values, ir_bitqueue *worklist, ir_ref ref, ir_ref val) |
302 | | { |
303 | | const ir_insn *val_insn; |
304 | | |
305 | | if (IR_IS_CONST_REF(val)) { |
306 | | val_insn = &ctx->ir_base[val]; |
307 | | } else { |
308 | | val_insn = &_values[val].insn; |
309 | | |
310 | | if (!IR_IS_CONST_OP(val_insn->op) && !IR_IS_SYM_CONST(val_insn->op)) { |
311 | | return ir_sccp_meet_copy(ctx, _values, worklist, ref, val); |
312 | | } |
313 | | } |
314 | | |
315 | | return ir_sccp_meet_const(ctx, _values, worklist, ref, val_insn); |
316 | | } |
317 | | #endif |
318 | | |
319 | | static ir_ref ir_sccp_fold(const ir_ctx *ctx, ir_sccp_val *_values, ir_bitqueue *worklist, ir_ref ref, const ir_insn *insn) |
320 | 9.74k | { |
321 | 9.74k | const ir_insn *op1_insn, *op2_insn, *op3_insn; |
322 | 9.74k | ir_ref op1, op2, op3, copy; |
323 | 9.74k | uint32_t opt = insn->opt; |
324 | | |
325 | 9.74k | op1 = ir_sccp_identity(ctx, _values, insn->op1); |
326 | 9.74k | op2 = ir_sccp_identity(ctx, _values, insn->op2); |
327 | 9.74k | op3 = ir_sccp_identity(ctx, _values, insn->op3); |
328 | | |
329 | 12.7k | restart: |
330 | 12.7k | op1_insn = (op1 > 0 && IR_IS_CONST_OP(_values[op1].op)) ? &_values[op1].insn : ctx->ir_base + op1; |
331 | 12.7k | op2_insn = (op2 > 0 && IR_IS_CONST_OP(_values[op2].op)) ? &_values[op2].insn : ctx->ir_base + op2; |
332 | 12.7k | op3_insn = (op3 > 0 && IR_IS_CONST_OP(_values[op3].op)) ? &_values[op3].insn : ctx->ir_base + op3; |
333 | | |
334 | 12.7k | switch (ir_folding((ir_ctx*)ctx, opt, op1, op2, op3, op1_insn, op2_insn, op3_insn)) { |
335 | 3.02k | case IR_FOLD_DO_RESTART: |
336 | 3.02k | opt = ctx->fold_insn.optx; |
337 | 3.02k | op1 = ctx->fold_insn.op1; |
338 | 3.02k | op2 = ctx->fold_insn.op2; |
339 | 3.02k | op3 = ctx->fold_insn.op3; |
340 | 3.02k | goto restart; |
341 | 6.05k | case IR_FOLD_DO_CSE: |
342 | 9.74k | case IR_FOLD_DO_EMIT: |
343 | 9.74k | IR_MAKE_BOTTOM_EX(ref); |
344 | 9.74k | return 1; |
345 | 0 | case IR_FOLD_DO_COPY: |
346 | 0 | copy = ctx->fold_insn.op1; |
347 | 0 | if (IR_IS_CONST_REF(copy)) { |
348 | 0 | insn = &ctx->ir_base[copy]; |
349 | 0 | if (insn->op == IR_LONG_CONST) { |
350 | 0 | return ir_sccp_meet_long_const(ctx, _values, worklist, ref, copy, insn); |
351 | 0 | } |
352 | 0 | } else { |
353 | 0 | insn = &_values[copy].insn; |
354 | 0 | if (!IR_IS_CONST_OP(insn->op) && !IR_IS_SYM_CONST(insn->op)) { |
355 | 0 | return ir_sccp_meet_copy(ctx, _values, worklist, ref, copy); |
356 | 0 | } |
357 | 0 | } |
358 | 0 | goto meet_const; |
359 | 0 | case IR_FOLD_DO_CONST: |
360 | 0 | insn = &ctx->fold_insn; |
361 | 0 | meet_const: |
362 | 0 | return ir_sccp_meet_const(ctx, _values, worklist, ref, insn); |
363 | 0 | default: |
364 | 0 | IR_ASSERT(0); |
365 | 0 | return 0; |
366 | 12.7k | } |
367 | 12.7k | } |
368 | | |
369 | | static bool ir_sccp_analyze_phi(const ir_ctx *ctx, ir_sccp_val *_values, ir_bitqueue *worklist, ir_ref i, const ir_insn *insn) |
370 | 36.7k | { |
371 | 36.7k | ir_ref j, n, input; |
372 | 36.7k | const ir_ref *merge_input, *p; |
373 | 36.7k | const ir_insn *v, *new_const = NULL; |
374 | 36.7k | #if IR_COMBO_COPY_PROPAGATION |
375 | 36.7k | ir_ref new_copy = IR_UNUSED; |
376 | 36.7k | ir_ref new_copy_identity = IR_UNUSED; |
377 | 36.7k | ir_ref phi_identity = ir_sccp_identity(ctx, _values, i); |
378 | 36.7k | #endif |
379 | | |
380 | 36.7k | if (!IR_IS_REACHABLE(insn->op1)) { |
381 | 0 | return 0; |
382 | 0 | } |
383 | 36.7k | n = insn->inputs_count; |
384 | 36.7k | if (n > 3 && _values[i].op == IR_TOP) { |
385 | 2.97k | for (j = 0; j < (n>>2); j++) { |
386 | 1.48k | _values[i+j+1].optx = IR_BOTTOM; /* keep the tail of a long multislot instruction */ |
387 | 1.48k | } |
388 | 1.48k | } |
389 | | |
390 | 36.7k | p = insn->ops + 2; |
391 | 36.7k | merge_input = ctx->ir_base[insn->op1].ops + 1; |
392 | 54.8k | for (; --n > 0; p++, merge_input++) { |
393 | 48.8k | IR_ASSERT(*merge_input > 0); |
394 | 48.8k | if (!IR_IS_REACHABLE(*merge_input)) { |
395 | 6.05k | continue; |
396 | 6.05k | } |
397 | | |
398 | 42.7k | input = *p; |
399 | 42.7k | if (IR_IS_CONST_REF(input)) { |
400 | 3.30k | v = &ctx->ir_base[input]; |
401 | 3.30k | #if IR_COMBO_COPY_PROPAGATION |
402 | 3.30k | if (v->op == IR_LONG_CONST) { |
403 | 0 | new_copy = input; |
404 | 0 | goto next; |
405 | 0 | } |
406 | 3.30k | #endif |
407 | 39.4k | } else if (input == i) { |
408 | 0 | continue; |
409 | 39.4k | } else { |
410 | 39.4k | v = &_values[input].insn; |
411 | 39.4k | if (v->op == IR_TOP) { |
412 | 0 | ir_sccp_add_input(ctx, _values, worklist, input); |
413 | 0 | continue; |
414 | 0 | #if IR_COMBO_COPY_PROPAGATION |
415 | 39.4k | } else if (v->op == IR_COPY) { |
416 | 0 | input = v->op1; |
417 | 0 | new_copy_identity = ir_sccp_identity(ctx, _values, input); |
418 | 0 | if (new_copy_identity == phi_identity) { |
419 | 0 | new_copy_identity = IR_UNUSED; |
420 | 0 | continue; |
421 | 0 | } |
422 | 0 | new_copy = input; |
423 | 0 | goto next; |
424 | 39.4k | } else if (v->op == IR_LONG_CONST) { |
425 | 0 | new_copy = v->val.i32; |
426 | 0 | goto next; |
427 | 0 | #endif |
428 | 39.4k | } else if (v->op == IR_BOTTOM) { |
429 | 39.4k | #if IR_COMBO_COPY_PROPAGATION |
430 | 39.4k | if (input == phi_identity) { |
431 | 12.0k | continue; |
432 | 12.0k | } |
433 | 27.3k | new_copy = new_copy_identity = input; |
434 | 27.3k | goto next; |
435 | | #else |
436 | | goto make_bottom; |
437 | | #endif |
438 | 39.4k | } |
439 | 39.4k | } |
440 | 3.30k | new_const = v; |
441 | 3.30k | goto next; |
442 | 42.7k | } |
443 | | |
444 | 6.04k | return 0; |
445 | | |
446 | 30.6k | next: |
447 | 30.6k | p++; |
448 | 30.6k | merge_input++; |
449 | | /* for all live merge inputs */ |
450 | 36.7k | for (; --n > 0; p++, merge_input++) { |
451 | 24.6k | IR_ASSERT(*merge_input > 0); |
452 | 24.6k | if (!IR_IS_REACHABLE(*merge_input)) { |
453 | 6.05k | continue; |
454 | 6.05k | } |
455 | | |
456 | 18.5k | input = *p; |
457 | 18.5k | if (IR_IS_CONST_REF(input)) { |
458 | 494 | #if IR_COMBO_COPY_PROPAGATION |
459 | 494 | if (new_copy) { |
460 | 494 | if (new_copy == input) { |
461 | 0 | continue; |
462 | 0 | } |
463 | 494 | goto make_bottom; |
464 | 494 | } |
465 | 0 | #endif |
466 | 0 | v = &ctx->ir_base[input]; |
467 | 18.0k | } else if (input == i) { |
468 | 0 | continue; |
469 | 18.0k | } else { |
470 | 18.0k | v = &_values[input].insn; |
471 | 18.0k | if (v->op == IR_TOP) { |
472 | 0 | ir_sccp_add_input(ctx, _values, worklist, input); |
473 | 0 | continue; |
474 | 0 | #if IR_COMBO_COPY_PROPAGATION |
475 | 18.0k | } else if (v->op == IR_COPY) { |
476 | 0 | ir_ref identity = ir_sccp_identity(ctx, _values, v->op1); |
477 | |
|
478 | 0 | if (identity == phi_identity || identity == new_copy_identity) { |
479 | 0 | continue; |
480 | 0 | } |
481 | 0 | goto make_bottom; |
482 | 18.0k | } else if (v->op == IR_LONG_CONST) { |
483 | 0 | if (v->val.i32 == new_copy) { |
484 | 0 | continue; |
485 | 0 | } |
486 | 0 | goto make_bottom; |
487 | 0 | #endif |
488 | 18.0k | } else if (v->op == IR_BOTTOM) { |
489 | 18.0k | #if IR_COMBO_COPY_PROPAGATION |
490 | 18.0k | if (input == phi_identity || input == new_copy_identity) { |
491 | 0 | continue; |
492 | 0 | } |
493 | 18.0k | #endif |
494 | 18.0k | goto make_bottom; |
495 | 18.0k | } |
496 | 18.0k | } |
497 | 0 | if (!new_const || new_const->opt != v->opt || new_const->val.u64 != v->val.u64) { |
498 | 0 | goto make_bottom; |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | 12.1k | #if IR_COMBO_COPY_PROPAGATION |
503 | 12.1k | if (new_copy) { |
504 | 12.1k | if (IR_IS_CONST_REF(new_copy)) { |
505 | 0 | IR_ASSERT(ctx->ir_base[new_copy].op == IR_LONG_CONST); |
506 | 0 | return ir_sccp_meet_long_const(ctx, _values, worklist, i, new_copy, &ctx->ir_base[new_copy]); |
507 | 0 | } |
508 | 12.1k | IR_ASSERT(!IR_IS_CONST_REF(new_copy)); |
509 | 12.1k | IR_ASSERT(!IR_IS_CONST_OP(_values[new_copy].op) && !IR_IS_SYM_CONST(_values[new_copy].op)); |
510 | 12.1k | return ir_sccp_meet_copy(ctx, _values, worklist, i, new_copy); |
511 | 12.1k | } |
512 | 2 | #endif |
513 | | |
514 | 2 | return ir_sccp_meet_const(ctx, _values, worklist, i, new_const); |
515 | | |
516 | 18.5k | make_bottom: |
517 | 18.5k | IR_MAKE_BOTTOM_EX(i); |
518 | 18.5k | return 1; |
519 | 12.1k | } |
520 | | |
521 | | static bool ir_is_dead_load_ex(const ir_ctx *ctx, ir_ref ref, uint32_t flags, const ir_insn *insn) |
522 | 959k | { |
523 | 959k | return (flags & IR_OP_FLAG_NO_SIDE_EFFECT) && ctx->use_lists[ref].count == 1; |
524 | 959k | } |
525 | | |
526 | | static bool ir_is_dead_load(const ir_ctx *ctx, ir_ref ref) |
527 | 117k | { |
528 | 117k | if (ctx->use_lists[ref].count == 1) { |
529 | 172 | return (ir_op_flags[ctx->ir_base[ref].op] & IR_OP_FLAG_NO_SIDE_EFFECT) != 0; |
530 | 172 | } |
531 | 116k | return 0; |
532 | 117k | } |
533 | | |
534 | | static bool ir_is_dead(const ir_ctx *ctx, ir_ref ref) |
535 | 2.97k | { |
536 | 2.97k | if (ctx->use_lists[ref].count == 0) { |
537 | 183 | return IR_IS_FOLDABLE_OP(ctx->ir_base[ref].op); |
538 | 2.79k | } else { |
539 | 2.79k | return ir_is_dead_load(ctx, ref); |
540 | 2.79k | } |
541 | 0 | return 0; |
542 | 2.97k | } |
543 | | |
544 | | static bool ir_sccp_is_true(const ir_ctx *ctx, const ir_sccp_val *_values, ir_ref a) |
545 | 43 | { |
546 | 43 | const ir_insn *v = IR_IS_CONST_REF(a) ? &ctx->ir_base[a] : &_values[a].insn; |
547 | | |
548 | 43 | return ir_const_is_true(v); |
549 | 43 | } |
550 | | |
551 | | static bool ir_sccp_is_equal(const ir_ctx *ctx, const ir_sccp_val *_values, ir_ref a, ir_ref b) |
552 | 0 | { |
553 | 0 | const ir_insn *v1 = IR_IS_CONST_REF(a) ? &ctx->ir_base[a] : &_values[a].insn; |
554 | 0 | const ir_insn *v2 = IR_IS_CONST_REF(b) ? &ctx->ir_base[b] : &_values[b].insn; |
555 | |
|
556 | 0 | IR_ASSERT(!IR_IS_SYM_CONST(v1->op)); |
557 | 0 | IR_ASSERT(!IR_IS_SYM_CONST(v2->op)); |
558 | 0 | return v1->val.u64 == v2->val.u64; |
559 | 0 | } |
560 | | |
561 | | static bool ir_sccp_in_range(const ir_ctx *ctx, const ir_sccp_val *_values, ir_ref a, ir_ref b, ir_ref c) |
562 | 0 | { |
563 | 0 | const ir_insn *v1 = IR_IS_CONST_REF(a) ? &ctx->ir_base[a] : &_values[a].insn; |
564 | 0 | const ir_insn *v2 = IR_IS_CONST_REF(b) ? &ctx->ir_base[b] : &_values[b].insn; |
565 | 0 | const ir_insn *v3 = IR_IS_CONST_REF(c) ? &ctx->ir_base[c] : &_values[c].insn; |
566 | |
|
567 | 0 | IR_ASSERT(!IR_IS_SYM_CONST(v1->op)); |
568 | 0 | IR_ASSERT(!IR_IS_SYM_CONST(v2->op)); |
569 | 0 | IR_ASSERT(!IR_IS_SYM_CONST(v3->op)); |
570 | 0 | if (IR_IS_TYPE_SIGNED(v1->type)) { |
571 | 0 | return v1->val.i64 >= v2->val.i64 && v1->val.i64 <= v3->val.i64; |
572 | 0 | } else { |
573 | 0 | return v1->val.u64 >= v2->val.u64 && v1->val.u64 <= v3->val.u64; |
574 | 0 | } |
575 | 0 | } |
576 | | |
577 | | #ifdef IR_SCCP_TRACE |
578 | | static void ir_sccp_trace_val(const ir_ctx *ctx, const ir_sccp_val *_values, ir_ref i) |
579 | | { |
580 | | if (IR_IS_BOTTOM(i)) { |
581 | | fprintf(stderr, "BOTTOM"); |
582 | | } else if (IR_IS_CONST_OP(_values[i].op) || IR_IS_SYM_CONST(_values[i].op)) { |
583 | | fprintf(stderr, "CONST("); |
584 | | ir_print_const(ctx, &_values[i].insn, stderr, true); |
585 | | fprintf(stderr, ")"); |
586 | | #if IR_COMBO_COPY_PROPAGATION |
587 | | } else if (_values[i].op == IR_COPY) { |
588 | | fprintf(stderr, "COPY(%d)", _values[i].op1); |
589 | | #endif |
590 | | } else if (IR_IS_TOP(i)) { |
591 | | fprintf(stderr, "TOP"); |
592 | | } else if (_values[i].op == IR_IF) { |
593 | | fprintf(stderr, "IF(%d)", _values[i].op1); |
594 | | } else if (_values[i].op == IR_MERGE) { |
595 | | fprintf(stderr, "MERGE(%d)", _values[i].op1); |
596 | | } else { |
597 | | fprintf(stderr, "%d", _values[i].op); |
598 | | } |
599 | | } |
600 | | |
601 | | static void ir_sccp_trace_start(const ir_ctx *ctx, const ir_sccp_val *_values, ir_ref i) |
602 | | { |
603 | | fprintf(stderr, "%d. ", i); |
604 | | ir_sccp_trace_val(ctx, _values, i); |
605 | | } |
606 | | |
607 | | static void ir_sccp_trace_end(const ir_ctx *ctx, const ir_sccp_val *_values, ir_ref i) |
608 | | { |
609 | | fprintf(stderr, " -> "); |
610 | | ir_sccp_trace_val(ctx, _values, i); |
611 | | fprintf(stderr, "\n"); |
612 | | } |
613 | | #else |
614 | | # define ir_sccp_trace_start(c, v, i) |
615 | | # define ir_sccp_trace_end(c, v, i) |
616 | | #endif |
617 | | |
618 | | static IR_NEVER_INLINE void ir_sccp_analyze(const ir_ctx *ctx, ir_sccp_val *_values, ir_bitqueue *worklist, ir_bitqueue *iter_worklist) |
619 | 9.93k | { |
620 | 9.93k | ir_ref i, j, n, use; |
621 | 9.93k | const ir_ref *p; |
622 | 9.93k | const ir_use_list *use_list; |
623 | 9.93k | const ir_insn *insn, *use_insn; |
624 | 9.93k | uint32_t flags; |
625 | | |
626 | | /* A bit modified SCCP algorithm of M. N. Wegman and F. K. Zadeck */ |
627 | 9.93k | worklist->pos = 0; |
628 | 9.93k | ir_bitset_incl(worklist->set, 1); |
629 | 1.78M | for (; (i = ir_bitqueue_pop(worklist)) >= 0; ir_sccp_trace_end(ctx, _values, i)) { |
630 | 1.77M | IR_ASSERT(_values[i].op != IR_BOTTOM); |
631 | 1.77M | ir_sccp_trace_start(ctx, _values, i); |
632 | 1.77M | insn = &ctx->ir_base[i]; |
633 | 1.77M | flags = ir_op_flags[insn->op]; |
634 | 1.77M | if (flags & IR_OP_FLAG_DATA) { |
635 | 377k | if (ctx->use_lists[i].count == 0) { |
636 | | /* dead code */ |
637 | 2.60k | continue; |
638 | 374k | } else if (insn->op == IR_PHI) { |
639 | 36.7k | if (!ir_sccp_analyze_phi(ctx, _values, worklist, i, insn)) { |
640 | 6.04k | continue; |
641 | 6.04k | } |
642 | 337k | } else if (EXPECTED(IR_IS_FOLDABLE_OP(insn->op))) { |
643 | 337k | bool may_benefit = 0; |
644 | 337k | bool has_top = 0; |
645 | | |
646 | 337k | if (_values[i].op != IR_TOP || insn->op == IR_COPY) { |
647 | 3.68k | may_benefit = 1; |
648 | 3.68k | } |
649 | | |
650 | 337k | IR_ASSERT(!IR_OP_HAS_VAR_INPUTS(flags)); |
651 | 337k | n = IR_INPUT_EDGES_COUNT(flags); |
652 | 337k | if (insn->op == IR_DIV || insn->op == IR_MOD) { |
653 | | /* skip data-control guard edge */ |
654 | 1.18k | n--; |
655 | 1.18k | } |
656 | 998k | for (p = insn->ops + 1; n > 0; p++, n--) { |
657 | 661k | ir_ref input = *p; |
658 | 661k | if (input > 0) { |
659 | 358k | if (_values[input].op == IR_TOP) { |
660 | 324 | has_top = 1; |
661 | 324 | ir_sccp_add_input(ctx, _values, worklist, input); |
662 | 358k | } else if (_values[input].op != IR_BOTTOM) { |
663 | | /* Perform folding only if some of direct inputs |
664 | | * is going to be replaced by a constant or copy. |
665 | | * This approach may miss some folding optimizations |
666 | | * dependent on indirect inputs. e.g. reassociation. |
667 | | */ |
668 | 6.05k | may_benefit = 1; |
669 | 6.05k | } |
670 | 358k | } |
671 | 661k | } |
672 | 337k | if (has_top) { |
673 | 324 | continue; |
674 | 324 | } |
675 | 337k | if (!may_benefit) { |
676 | 327k | IR_MAKE_BOTTOM_EX(i); |
677 | 327k | if (insn->op == IR_FP2FP || insn->op == IR_FP2INT || insn->op == IR_TRUNC) { |
678 | 76 | ir_bitqueue_add(iter_worklist, i); |
679 | 76 | } |
680 | 327k | } else if (!ir_sccp_fold(ctx, _values, worklist, i, insn)) { |
681 | | /* not changed */ |
682 | 0 | continue; |
683 | 9.74k | } else if (_values[i].op == IR_BOTTOM) { |
684 | 9.74k | insn = &ctx->ir_base[i]; |
685 | 9.74k | if (insn->op == IR_FP2FP || insn->op == IR_FP2INT || insn->op == IR_TRUNC) { |
686 | 0 | ir_bitqueue_add(iter_worklist, i); |
687 | 0 | } |
688 | 9.74k | } |
689 | 337k | } else { |
690 | 44 | IR_MAKE_BOTTOM_EX(i); |
691 | 44 | n = IR_INPUT_EDGES_COUNT(flags); |
692 | 88 | for (p = insn->ops + 1; n > 0; p++, n--) { |
693 | 44 | ir_ref input = *p; |
694 | 44 | if (input > 0) { |
695 | 44 | if (_values[input].op == IR_TOP) { |
696 | 0 | ir_sccp_add_input(ctx, _values, worklist, input); |
697 | 0 | } |
698 | 44 | } |
699 | 44 | } |
700 | 44 | } |
701 | 1.39M | } else if (flags & IR_OP_FLAG_BB_START) { |
702 | 321k | if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_BEGIN) { |
703 | 79.9k | ir_bitqueue_add(iter_worklist, i); |
704 | 79.9k | } |
705 | 321k | if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) { |
706 | 78.7k | ir_ref unfeasible_inputs = 0; |
707 | | |
708 | 78.7k | n = insn->inputs_count; |
709 | 78.7k | if (n > 3 && _values[i].op == IR_TOP) { |
710 | 10.7k | for (j = 0; j < (n>>2); j++) { |
711 | 5.38k | _values[i+j+1].optx = IR_BOTTOM; /* keep the tail of a long multislot instruction */ |
712 | 5.38k | } |
713 | 5.35k | } |
714 | 270k | for (p = insn->ops + 1; n > 0; p++, n--) { |
715 | 191k | const ir_ref input = *p; |
716 | 191k | IR_ASSERT(input > 0); |
717 | 191k | if (!IR_IS_REACHABLE(input)) { |
718 | 3.26k | unfeasible_inputs++; |
719 | 3.26k | } |
720 | 191k | } |
721 | 78.7k | if (unfeasible_inputs == 0) { |
722 | 75.5k | IR_MAKE_BOTTOM(i); |
723 | 75.5k | } else if (_values[i].op != IR_MERGE || _values[i].unfeasible_inputs != unfeasible_inputs) { |
724 | 3.26k | _values[i].optx = IR_MERGE; |
725 | 3.26k | _values[i].unfeasible_inputs = unfeasible_inputs; |
726 | 3.26k | } else { |
727 | 0 | continue; |
728 | 0 | } |
729 | 78.7k | if (ctx->flags2 & IR_MEM2SSA_VARS) { |
730 | | /* MEM2SSA puts new PHI at the bottom, but we like to process them now */ |
731 | 0 | use_list = &ctx->use_lists[i]; |
732 | 0 | n = use_list->count; |
733 | 0 | for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) { |
734 | 0 | use = *p; |
735 | 0 | if (_values[use].op != IR_BOTTOM) { |
736 | 0 | if (ctx->ir_base[use].op == IR_PHI) { |
737 | 0 | ir_bitqueue_del(worklist, use); |
738 | 0 | if (ctx->use_lists[use].count != 0) { |
739 | 0 | if (ir_sccp_analyze_phi(ctx, _values, worklist, use, &ctx->ir_base[use])) { |
740 | 0 | ir_sccp_add_uses(ctx, _values, worklist, use); |
741 | 0 | } |
742 | 0 | } |
743 | 0 | } else { |
744 | 0 | ir_bitqueue_add(worklist, use); |
745 | 0 | } |
746 | 0 | } |
747 | 0 | } |
748 | 0 | continue; |
749 | 0 | } |
750 | 242k | } else { |
751 | 242k | IR_ASSERT(insn->op == IR_START || IR_IS_REACHABLE(insn->op1)); |
752 | 242k | IR_MAKE_BOTTOM(i); |
753 | 242k | } |
754 | 1.07M | } else { |
755 | 1.07M | IR_ASSERT(insn->op1 > 0); |
756 | 1.07M | if (!IR_IS_REACHABLE(insn->op1)) { |
757 | | /* control inpt is not feasible */ |
758 | 10 | continue; |
759 | 10 | } |
760 | 1.07M | if (insn->op == IR_IF) { |
761 | 114k | if (IR_IS_TOP(insn->op2)) { |
762 | 0 | ir_sccp_add_input(ctx, _values, worklist, insn->op2); |
763 | 0 | continue; |
764 | 0 | } |
765 | 114k | if (IR_IS_CONST(insn->op2)) { |
766 | 43 | bool b = ir_sccp_is_true(ctx, _values, insn->op2); |
767 | 43 | use_list = &ctx->use_lists[i]; |
768 | 43 | IR_ASSERT(use_list->count == 2); |
769 | 43 | p = &ctx->use_edges[use_list->refs]; |
770 | 43 | use = *p; |
771 | 43 | use_insn = &ctx->ir_base[use]; |
772 | 43 | IR_ASSERT(use_insn->op == IR_IF_TRUE || use_insn->op == IR_IF_FALSE); |
773 | 43 | if ((use_insn->op == IR_IF_TRUE) != b) { |
774 | 22 | use = *(p+1); |
775 | 22 | IR_ASSERT(ctx->ir_base[use].op == IR_IF_TRUE || ctx->ir_base[use].op == IR_IF_FALSE); |
776 | 22 | } |
777 | 43 | if (_values[i].op == IR_TOP) { |
778 | 43 | _values[i].optx = IR_IF; |
779 | 43 | _values[i].single_output = use; |
780 | 43 | ir_bitqueue_add(worklist, use); |
781 | 43 | continue; |
782 | 43 | } else if (_values[i].op == IR_IF && _values[i].single_output == use) { |
783 | 0 | continue; |
784 | 0 | } |
785 | 43 | } |
786 | 114k | IR_MAKE_BOTTOM(i); |
787 | 114k | ir_bitqueue_add(iter_worklist, i); |
788 | 959k | } else if (insn->op == IR_SWITCH) { |
789 | 6 | if (IR_IS_TOP(insn->op2)) { |
790 | 0 | ir_sccp_add_input(ctx, _values, worklist, insn->op2); |
791 | 0 | continue; |
792 | 0 | } |
793 | 6 | if (IR_IS_CONST(insn->op2)) { |
794 | 0 | ir_ref use_case = IR_UNUSED; |
795 | |
|
796 | 0 | use_list = &ctx->use_lists[i]; |
797 | 0 | n = use_list->count; |
798 | 0 | for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) { |
799 | 0 | use = *p; |
800 | 0 | IR_ASSERT(use > 0); |
801 | 0 | use_insn = &ctx->ir_base[use]; |
802 | 0 | if (use_insn->op == IR_CASE_VAL) { |
803 | 0 | if (ir_sccp_is_equal(ctx, _values, insn->op2, use_insn->op2)) { |
804 | 0 | use_case = use; |
805 | 0 | break; |
806 | 0 | } |
807 | 0 | } else if (use_insn->op == IR_CASE_DEFAULT) { |
808 | 0 | use_case = use; |
809 | 0 | } else if (use_insn->op == IR_CASE_RANGE) { |
810 | 0 | if (ir_sccp_in_range(ctx, _values, insn->op2, use_insn->op2, use_insn->op3)) { |
811 | 0 | use_case = use; |
812 | 0 | break; |
813 | 0 | } |
814 | 0 | } |
815 | 0 | } |
816 | 0 | if (use_case) { |
817 | 0 | use_insn = &ctx->ir_base[use_case]; |
818 | 0 | if (_values[i].op == IR_TOP) { |
819 | 0 | _values[i].optx = IR_IF; |
820 | 0 | _values[i].single_output = use_case; |
821 | 0 | ir_bitqueue_add(worklist, use_case); |
822 | 0 | continue; |
823 | 0 | } else if (_values[i].op == IR_IF || _values[i].single_output == use_case) { |
824 | 0 | continue; |
825 | 0 | } |
826 | 0 | } |
827 | 0 | } |
828 | 6 | IR_MAKE_BOTTOM(i); |
829 | 959k | } else if (ir_is_dead_load_ex(ctx, i, flags, insn)) { |
830 | | /* schedule dead load elimination */ |
831 | 101 | ir_bitqueue_add(iter_worklist, i); |
832 | 101 | IR_MAKE_BOTTOM(i); |
833 | 959k | } else { |
834 | | /* control, call, load and store instructions may have unprocessed inputs */ |
835 | 959k | n = IR_INPUT_EDGES_COUNT(flags); |
836 | 959k | if (IR_OP_HAS_VAR_INPUTS(flags) && (n = insn->inputs_count) > 3) { |
837 | 76.1k | for (j = 0; j < (n>>2); j++) { |
838 | 38.0k | _values[i+j+1].optx = IR_BOTTOM; /* keep the tail of a long multislot instruction */ |
839 | 38.0k | } |
840 | 156k | for (j = 2, p = insn->ops + j; j <= n; j++, p++) { |
841 | 118k | IR_ASSERT(IR_OPND_KIND(flags, j) == IR_OPND_DATA); |
842 | 118k | use = *p; |
843 | 118k | if (use > 0 && _values[use].op == IR_TOP) { |
844 | 0 | ir_sccp_add_input(ctx, _values, worklist, use); |
845 | 0 | } |
846 | 118k | } |
847 | 921k | } else if (n >= 2) { |
848 | 518k | IR_ASSERT(IR_OPND_KIND(flags, 2) == IR_OPND_DATA); |
849 | 518k | use = insn->op2; |
850 | 518k | if (use > 0 && _values[use].op == IR_TOP) { |
851 | 0 | ir_sccp_add_input(ctx, _values, worklist, use); |
852 | 0 | } |
853 | 518k | if (n > 2) { |
854 | 232k | IR_ASSERT(n == 3); |
855 | 232k | IR_ASSERT(IR_OPND_KIND(flags, 3) == IR_OPND_DATA); |
856 | 232k | use = insn->op3; |
857 | 232k | if (use > 0 && _values[use].op == IR_TOP) { |
858 | 48 | ir_sccp_add_input(ctx, _values, worklist, use); |
859 | 48 | } |
860 | 232k | } |
861 | 518k | } |
862 | 959k | IR_MAKE_BOTTOM(i); |
863 | 959k | } |
864 | 1.07M | } |
865 | 1.76M | ir_sccp_add_uses(ctx, _values, worklist, i); |
866 | 1.76M | } |
867 | | |
868 | 9.93k | #ifdef IR_DEBUG |
869 | 9.93k | if (ctx->flags & IR_DEBUG_SCCP) { |
870 | 0 | for (i = 1; i < ctx->insns_count; i++) { |
871 | 0 | if (IR_IS_CONST_OP(_values[i].op) || IR_IS_SYM_CONST(_values[i].op)) { |
872 | 0 | fprintf(stderr, "%d. CONST(", i); |
873 | 0 | ir_print_const(ctx, &_values[i].insn, stderr, true); |
874 | 0 | fprintf(stderr, ")\n"); |
875 | 0 | #if IR_COMBO_COPY_PROPAGATION |
876 | 0 | } else if (_values[i].op == IR_COPY) { |
877 | 0 | fprintf(stderr, "%d. COPY(%d)\n", i, _values[i].copy); |
878 | 0 | } else if (_values[i].op == IR_LONG_CONST) { |
879 | 0 | fprintf(stderr, "%d. LONG_CONST(%d)\n", i, _values[i].val.i32); |
880 | 0 | #endif |
881 | 0 | } else if (IR_IS_TOP(i)) { |
882 | 0 | if (ctx->ir_base[i].op != IR_TOP) { |
883 | 0 | fprintf(stderr, "%d. TOP\n", i); |
884 | 0 | } |
885 | 0 | } else if (_values[i].op == IR_IF) { |
886 | 0 | fprintf(stderr, "%d. IF(%d)\n", i, _values[i].single_output); |
887 | 0 | } else if (_values[i].op == IR_MERGE) { |
888 | 0 | fprintf(stderr, "%d. MERGE(%d)\n", i, _values[i].unfeasible_inputs); |
889 | 0 | } else if (!IR_IS_BOTTOM(i)) { |
890 | 0 | fprintf(stderr, "%d. %d\n", i, _values[i].op); |
891 | 0 | } |
892 | 0 | } |
893 | 0 | } |
894 | 9.93k | #endif |
895 | 9.93k | } |
896 | | |
897 | | /**********************/ |
898 | | /* SCCP trasformation */ |
899 | | /**********************/ |
900 | | |
901 | | static void ir_sccp_make_nop(ir_ctx *ctx, ir_ref ref) |
902 | 86 | { |
903 | 86 | ir_ref j, n, *p; |
904 | 86 | ir_insn *insn; |
905 | | |
906 | 86 | CLEAR_USES(ref); |
907 | 86 | insn = &ctx->ir_base[ref]; |
908 | 86 | n = insn->inputs_count; |
909 | 86 | insn->opt = IR_NOP; /* keep "inputs_count" */ |
910 | 215 | for (j = 1, p = insn->ops + j; j <= n; j++, p++) { |
911 | 129 | *p = IR_UNUSED; |
912 | 129 | } |
913 | 86 | } |
914 | | |
915 | | static void ir_sccp_remove_insn(ir_ctx *ctx, const ir_sccp_val *_values, ir_ref ref, ir_bitqueue *worklist) |
916 | 3.77k | { |
917 | 3.77k | ir_ref j, n, *p; |
918 | 3.77k | ir_insn *insn; |
919 | | |
920 | 3.77k | CLEAR_USES(ref); |
921 | 3.77k | insn = &ctx->ir_base[ref]; |
922 | 3.77k | n = insn->inputs_count; |
923 | 3.77k | insn->opt = IR_NOP; /* keep "inputs_count" */ |
924 | 11.4k | for (j = 1, p = insn->ops + j; j <= n; j++, p++) { |
925 | 7.64k | ir_ref input = *p; |
926 | 7.64k | *p = IR_UNUSED; |
927 | | /* we may skip nodes that are going to be removed by SCCP (TOP, CONST and COPY) */ |
928 | 7.64k | if (input > 0 && _values[input].op > IR_COPY) { |
929 | 2.64k | ir_use_list_remove_one(ctx, input, ref); |
930 | 2.64k | if (ir_is_dead(ctx, input)) { |
931 | | /* schedule DCE */ |
932 | 33 | ir_bitqueue_add(worklist, input); |
933 | 33 | } |
934 | 2.64k | } |
935 | 7.64k | } |
936 | 3.77k | } |
937 | | |
938 | | static void ir_sccp_replace_insn(ir_ctx *ctx, const ir_sccp_val *_values, ir_ref ref, ir_ref new_ref, ir_bitqueue *worklist) |
939 | 728 | { |
940 | 728 | ir_ref j, n, *p, use, i; |
941 | 728 | ir_insn *insn; |
942 | 728 | ir_use_list *use_list; |
943 | | |
944 | 728 | IR_ASSERT(ref != new_ref); |
945 | | |
946 | 728 | insn = &ctx->ir_base[ref]; |
947 | | |
948 | 728 | #if IR_COMBO_COPY_PROPAGATION |
949 | 728 | if ((ir_op_flags[insn->op] & IR_OP_FLAG_MEM) && IR_IS_REACHABLE(insn->op1)) { |
950 | | /* remove from control list */ |
951 | 0 | ir_ref prev = insn->op1; |
952 | 0 | ir_ref next = ir_next_control(ctx, ref); |
953 | 0 | ctx->ir_base[next].op1 = prev; |
954 | 0 | ir_use_list_remove_one(ctx, ref, next); |
955 | 0 | ir_use_list_replace_one(ctx, prev, ref, next); |
956 | 0 | insn->op1 = IR_UNUSED; |
957 | 0 | } |
958 | 728 | #endif |
959 | | |
960 | 728 | n = insn->inputs_count; |
961 | 728 | insn->opt = IR_NOP; /* keep "inputs_count" */ |
962 | 1.78k | for (j = 1, p = insn->ops + 1; j <= n; j++, p++) { |
963 | 1.05k | ir_ref input = *p; |
964 | 1.05k | *p = IR_UNUSED; |
965 | | /* we may skip nodes that are going to be removed by SCCP (TOP, CONST and COPY) */ |
966 | 1.05k | if (input > 0 && _values[input].op > IR_COPY) { |
967 | 59 | ir_use_list_remove_one(ctx, input, ref); |
968 | 59 | if (ir_is_dead(ctx, input)) { |
969 | | /* schedule DCE */ |
970 | 49 | ir_bitqueue_add(worklist, input); |
971 | 49 | } |
972 | 59 | } |
973 | 1.05k | } |
974 | | |
975 | 728 | use_list = &ctx->use_lists[ref]; |
976 | 728 | n = use_list->count; |
977 | 728 | p = &ctx->use_edges[use_list->refs]; |
978 | 728 | if (new_ref <= 0) { |
979 | | /* constant or IR_UNUSED */ |
980 | 1.56k | for (; n; p++, n--) { |
981 | 837 | use = *p; |
982 | | /* we may skip nodes that are going to be removed by SCCP (TOP, CONST and COPY) */ |
983 | 837 | if (_values[use].op > IR_COPY) { |
984 | 48 | insn = &ctx->ir_base[use]; |
985 | 48 | i = ir_insn_find_op(insn, ref); |
986 | 48 | if (!i) continue; |
987 | 48 | IR_ASSERT(i > 0); |
988 | 48 | ir_insn_set_op(insn, i, new_ref); |
989 | | /* schedule folding */ |
990 | 48 | ir_bitqueue_add(worklist, use); |
991 | 48 | } |
992 | 837 | } |
993 | 724 | } else { |
994 | 18 | for (j = 0; j < n; j++, p++) { |
995 | 14 | use = *p; |
996 | | /* we may skip nodes that are going to be removed by SCCP (TOP, CONST and COPY) */ |
997 | 14 | if (_values[use].op == IR_BOTTOM) { |
998 | 14 | insn = &ctx->ir_base[use]; |
999 | 14 | i = ir_insn_find_op(insn, ref); |
1000 | 14 | IR_ASSERT(i > 0); |
1001 | 14 | ir_insn_set_op(insn, i, new_ref); |
1002 | 14 | if (ir_use_list_add(ctx, new_ref, use)) { |
1003 | | /* restore after reallocation */ |
1004 | 4 | use_list = &ctx->use_lists[ref]; |
1005 | 4 | n = use_list->count; |
1006 | 4 | p = &ctx->use_edges[use_list->refs + j]; |
1007 | 4 | } |
1008 | | /* schedule folding */ |
1009 | 14 | ir_bitqueue_add(worklist, use); |
1010 | 14 | } |
1011 | 14 | } |
1012 | 4 | } |
1013 | 728 | CLEAR_USES(ref); |
1014 | 728 | } |
1015 | | |
1016 | | static void ir_sccp_remove_if(ir_ctx *ctx, const ir_sccp_val *_values, ir_ref ref, ir_ref dst) |
1017 | 43 | { |
1018 | 43 | ir_ref next; |
1019 | 43 | ir_insn *insn, *next_insn; |
1020 | | |
1021 | 43 | insn = &ctx->ir_base[ref]; |
1022 | 43 | if (ctx->use_lists[dst].count == 1) { |
1023 | 43 | next = ctx->use_edges[ctx->use_lists[dst].refs]; |
1024 | 43 | next_insn = &ctx->ir_base[next]; |
1025 | | /* remove IF and IF_TRUE/FALSE from double linked control list */ |
1026 | 43 | next_insn->op1 = insn->op1; |
1027 | 43 | ir_use_list_replace_one(ctx, insn->op1, ref, next); |
1028 | | /* remove IF and IF_TRUE/FALSE instructions */ |
1029 | 43 | ir_sccp_make_nop(ctx, ref); |
1030 | 43 | ir_sccp_make_nop(ctx, dst); |
1031 | 43 | } else { |
1032 | 0 | insn->op2 = IR_UNUSED; |
1033 | 0 | insn->optx = IR_OPTX(IR_END, IR_VOID, 1); |
1034 | 0 | next_insn = &ctx->ir_base[dst]; |
1035 | 0 | next_insn->op = IR_BEGIN; |
1036 | 0 | next_insn->op2 = IR_UNUSED; |
1037 | 0 | } |
1038 | 43 | } |
1039 | | |
1040 | | static bool ir_sccp_remove_unfeasible_merge_inputs(ir_ctx *ctx, ir_ref ref, ir_insn *insn, ir_bitqueue *worklist) |
1041 | 46 | { |
1042 | 46 | ir_ref old_merge_inputs, new_merge_inputs, i, *p; |
1043 | 46 | ir_use_list *use_list; |
1044 | 46 | ir_bitset life_inputs; |
1045 | 46 | ir_bitset_base_t holder = 0; |
1046 | | |
1047 | 46 | IR_ASSERT(insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN); |
1048 | 46 | old_merge_inputs = insn->inputs_count; |
1049 | 46 | new_merge_inputs = 0; |
1050 | 46 | life_inputs = (old_merge_inputs < IR_BITSET_BITS) ? &holder : ir_bitset_malloc(old_merge_inputs + 1); |
1051 | | |
1052 | 140 | for (i = 1; i <= old_merge_inputs; i++) { |
1053 | 94 | ir_ref input = ir_insn_op(insn, i); |
1054 | | |
1055 | 94 | if (input) { |
1056 | 48 | new_merge_inputs++; |
1057 | 48 | if (new_merge_inputs != i) { |
1058 | 31 | ir_insn_set_op(insn, new_merge_inputs, input); |
1059 | 31 | } |
1060 | 48 | ir_bitset_incl(life_inputs, i); |
1061 | 48 | } |
1062 | 94 | } |
1063 | | |
1064 | 46 | if (new_merge_inputs == old_merge_inputs) { |
1065 | | /* All inputs are feasible */ |
1066 | 0 | if (life_inputs != &holder) { |
1067 | 0 | ir_mem_free(life_inputs); |
1068 | 0 | } |
1069 | 0 | return 0; |
1070 | 0 | } |
1071 | | |
1072 | 92 | for (i = new_merge_inputs + 1; i <= old_merge_inputs; i++) { |
1073 | 46 | ir_insn_set_op(insn, i, IR_UNUSED); |
1074 | 46 | } |
1075 | | |
1076 | 46 | if (new_merge_inputs <= 1) { |
1077 | | #if 0 |
1078 | | if (new_merge_inputs == 1 |
1079 | | && insn->op == IR_LOOP_BEGIN |
1080 | | && insn->op1 > ref) { // TODO: check dominance instead of order |
1081 | | /* dead loop */ |
1082 | | ir_use_list_remove_one(ctx, insn->op1, ref); |
1083 | | insn->op1 = IR_UNUSED; |
1084 | | new_merge_inputs = 0; |
1085 | | } |
1086 | | #endif |
1087 | 44 | insn->optx = IR_OPTX(IR_BEGIN, IR_VOID, 1); |
1088 | 44 | ir_bitqueue_add(worklist, ref); |
1089 | 44 | } else { |
1090 | 2 | insn->inputs_count = new_merge_inputs; |
1091 | 2 | } |
1092 | | |
1093 | | /* Update PHIs */ |
1094 | 46 | use_list = &ctx->use_lists[ref]; |
1095 | 46 | if (use_list->count > 1) { |
1096 | 0 | ir_ref use_count = 0; |
1097 | 0 | ir_ref *q; |
1098 | |
|
1099 | 0 | for (i = 0, p = q = &ctx->use_edges[use_list->refs]; i < use_list->count; p++, i++) { |
1100 | 0 | ir_ref use = *p; |
1101 | 0 | ir_insn *use_insn = &ctx->ir_base[use]; |
1102 | |
|
1103 | 0 | if (use_insn->op == IR_PHI) { |
1104 | 0 | ir_ref j, k; |
1105 | | |
1106 | | /* compress PHI */ |
1107 | 0 | for (j = k = 1; j <= old_merge_inputs; j++) { |
1108 | 0 | ir_ref input = ir_insn_op(use_insn, j + 1); |
1109 | |
|
1110 | 0 | if (ir_bitset_in(life_inputs, j)) { |
1111 | 0 | IR_ASSERT(input); |
1112 | 0 | if (k != j) { |
1113 | 0 | ir_insn_set_op(use_insn, k + 1, input); |
1114 | 0 | } |
1115 | 0 | k++; |
1116 | 0 | } else if (input > 0) { |
1117 | 0 | ir_use_list_remove_one(ctx, input, use); |
1118 | 0 | } |
1119 | 0 | } |
1120 | 0 | while (k <= old_merge_inputs) { |
1121 | 0 | k++; |
1122 | 0 | ir_insn_set_op(use_insn, k, IR_UNUSED); |
1123 | 0 | } |
1124 | |
|
1125 | 0 | if (new_merge_inputs == 0) { |
1126 | | /* remove PHI */ |
1127 | | #if 0 |
1128 | | use_insn->op1 = IR_UNUSED; |
1129 | | ir_iter_remove_insn(ctx, use); |
1130 | | #else |
1131 | 0 | IR_ASSERT(0); |
1132 | 0 | #endif |
1133 | 0 | continue; |
1134 | 0 | } else if (new_merge_inputs == 1) { |
1135 | | /* replace PHI by COPY */ |
1136 | 0 | use_insn->optx = IR_OPTX(IR_COPY, use_insn->type, 1); |
1137 | 0 | use_insn->op1 = use_insn->op2; |
1138 | 0 | use_insn->op2 = IR_UNUSED; |
1139 | 0 | ir_bitqueue_add(worklist, use); |
1140 | 0 | continue; |
1141 | 0 | } else { |
1142 | 0 | use_insn->inputs_count = new_merge_inputs + 1; |
1143 | 0 | } |
1144 | 0 | } |
1145 | 0 | if (p != q) { |
1146 | 0 | *q = use; |
1147 | 0 | } |
1148 | 0 | q++; |
1149 | 0 | use_count++; |
1150 | 0 | } |
1151 | 0 | for (i = use_count; i < use_list->count; q++, i++) { |
1152 | 0 | *q = IR_UNUSED; |
1153 | 0 | } |
1154 | 0 | use_list->count = use_count; |
1155 | 0 | } |
1156 | | |
1157 | 46 | if (life_inputs != &holder) { |
1158 | 0 | ir_mem_free(life_inputs); |
1159 | 0 | } |
1160 | | |
1161 | 46 | return 1; |
1162 | 46 | } |
1163 | | |
1164 | | static IR_NEVER_INLINE void ir_sccp_transform(ir_ctx *ctx, const ir_sccp_val *_values, ir_bitqueue *worklist, ir_bitqueue *iter_worklist) |
1165 | 9.93k | { |
1166 | 9.93k | ir_ref i, j; |
1167 | 9.93k | const ir_sccp_val *value; |
1168 | | |
1169 | 1.81M | for (i = 1, value = _values + i; i < ctx->insns_count; value++, i++) { |
1170 | 1.80M | if (value->op == IR_BOTTOM) { |
1171 | 1.79M | continue; |
1172 | 1.79M | } else if (IR_IS_CONST_OP(value->op)) { |
1173 | | /* replace instruction by constant */ |
1174 | 2 | j = ir_const(ctx, value->val, value->type); |
1175 | 2 | ir_sccp_replace_insn(ctx, _values, i, j, iter_worklist); |
1176 | 4.62k | } else if (IR_IS_SYM_CONST(value->op)) { |
1177 | | /* replace instruction by constant */ |
1178 | 0 | j = ir_const_ex(ctx, value->val, value->type, value->optx); |
1179 | 0 | ir_sccp_replace_insn(ctx, _values, i, j, iter_worklist); |
1180 | 0 | #if IR_COMBO_COPY_PROPAGATION |
1181 | 4.62k | } else if (value->op == IR_COPY) { |
1182 | 4 | ir_sccp_replace_insn(ctx, _values, i, ir_sccp_identity(ctx, _values, value->copy), iter_worklist); |
1183 | 4.62k | } else if (value->op == IR_LONG_CONST) { |
1184 | 0 | ir_sccp_replace_insn(ctx, _values, i, value->val.i32, iter_worklist); |
1185 | 0 | #endif |
1186 | 4.62k | } else if (value->op == IR_TOP) { |
1187 | | /* remove unreachable instruction */ |
1188 | 4.53k | ir_insn *insn = &ctx->ir_base[i]; |
1189 | | |
1190 | 4.53k | if (insn->op == IR_NOP) { |
1191 | | /* already removed */ |
1192 | 4.50k | } else if (ir_op_flags[insn->op] & (IR_OP_FLAG_DATA|IR_OP_FLAG_MEM)) { |
1193 | 3.77k | if (insn->op != IR_PARAM) { |
1194 | 3.77k | ir_sccp_remove_insn(ctx, _values, i, iter_worklist); |
1195 | 3.77k | } |
1196 | 3.77k | } else { |
1197 | 722 | if (ir_op_flags[insn->op] & IR_OP_FLAG_TERMINATOR) { |
1198 | | /* remove from terminators list */ |
1199 | 18 | ir_ref prev = ctx->ir_base[1].op1; |
1200 | 18 | if (prev == i) { |
1201 | 10 | ctx->ir_base[1].op1 = insn->op3; |
1202 | 10 | } else { |
1203 | 8 | while (prev) { |
1204 | 8 | if (ctx->ir_base[prev].op3 == i) { |
1205 | 8 | ctx->ir_base[prev].op3 = insn->op3; |
1206 | 8 | break; |
1207 | 8 | } |
1208 | 0 | prev = ctx->ir_base[prev].op3; |
1209 | 0 | } |
1210 | 8 | } |
1211 | 18 | } |
1212 | 722 | ir_sccp_replace_insn(ctx, _values, i, IR_UNUSED, iter_worklist); |
1213 | 722 | } |
1214 | 4.53k | } else if (value->op == IR_IF) { |
1215 | | /* remove one way IF/SWITCH */ |
1216 | 43 | ir_sccp_remove_if(ctx, _values, i, value->single_output); |
1217 | 46 | } else if (value->op == IR_MERGE) { |
1218 | | /* schedule merge to remove unfeasible MERGE inputs */ |
1219 | 46 | ir_bitqueue_add(worklist, i); |
1220 | 46 | } |
1221 | 1.80M | } |
1222 | | |
1223 | 9.98k | while ((i = ir_bitqueue_pop(worklist)) >= 0) { |
1224 | 46 | IR_ASSERT(_values[i].op == IR_MERGE); |
1225 | 46 | ir_sccp_remove_unfeasible_merge_inputs(ctx, i, &ctx->ir_base[i], iter_worklist); |
1226 | 46 | } |
1227 | 9.93k | } |
1228 | | |
1229 | | /***************************/ |
1230 | | /* Iterative Optimizations */ |
1231 | | /***************************/ |
1232 | | |
1233 | | void ir_iter_add_uses(ir_ctx *ctx, ir_ref ref, ir_bitqueue *worklist) |
1234 | 4 | { |
1235 | 4 | ir_use_list *use_list = &ctx->use_lists[ref]; |
1236 | 4 | ir_ref *p, n = use_list->count; |
1237 | | |
1238 | 14 | for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) { |
1239 | 10 | ir_bitqueue_add(worklist, *p); |
1240 | 10 | } |
1241 | 4 | } |
1242 | | |
1243 | | /* Modification of some instruction may open new optimization oprtunities for other |
1244 | | * instructions that use this one. |
1245 | | * |
1246 | | * For example, let "a = ADD(x, y)" became "a = ADD(x, C1)". In case we also have |
1247 | | * "b = ADD(a, C2)" we may optimize it into "b = ADD(x, C1 + C2)" and then might |
1248 | | * also remove "a". |
1249 | | * |
1250 | | * This implementation supports only few optimization of combinations from ir_fold.h |
1251 | | * |
1252 | | * TODO: Think abput a more general solution ??? |
1253 | | */ |
1254 | | static void ir_iter_add_related_uses(const ir_ctx *ctx, ir_ref ref, ir_bitqueue *worklist) |
1255 | 20 | { |
1256 | 20 | const ir_insn *insn = &ctx->ir_base[ref]; |
1257 | | |
1258 | 20 | if (insn->op == IR_ADD || insn->op == IR_SUB) { |
1259 | 0 | const ir_use_list *use_list = &ctx->use_lists[ref]; |
1260 | |
|
1261 | 0 | if (use_list->count == 1) { |
1262 | 0 | ir_ref use = ctx->use_edges[use_list->refs]; |
1263 | 0 | const ir_insn *use_insn = &ctx->ir_base[ref]; |
1264 | |
|
1265 | 0 | if (use_insn->op == IR_ADD || use_insn->op == IR_SUB) { |
1266 | 0 | ir_bitqueue_add(worklist, use); |
1267 | 0 | } |
1268 | 0 | } |
1269 | 0 | } |
1270 | 20 | } |
1271 | | |
1272 | | static void ir_iter_remove_insn(ir_ctx *ctx, ir_ref ref) |
1273 | 293 | { |
1274 | 293 | ir_ref j, n, *p; |
1275 | 293 | ir_insn *insn; |
1276 | | |
1277 | 293 | CLEAR_USES(ref); |
1278 | 293 | insn = &ctx->ir_base[ref]; |
1279 | 293 | n = insn->inputs_count; |
1280 | 293 | insn->opt = IR_NOP; /* keep "inputs_count" */ |
1281 | 870 | for (j = 1, p = insn->ops + j; j <= n; j++, p++) { |
1282 | 577 | ir_ref input = *p; |
1283 | 577 | *p = IR_UNUSED; |
1284 | 577 | if (input > 0) { |
1285 | 246 | ir_use_list_remove_one(ctx, input, ref); |
1286 | 246 | if (ir_is_dead(ctx, input)) { |
1287 | | /* schedule DCE */ |
1288 | 96 | ir_bitqueue_add(ctx->iter_worklist, input); |
1289 | 150 | } else if (ctx->ir_base[input].op == IR_PHI && ctx->use_lists[input].count == 1) { |
1290 | | /* try to optimize PHI into ABS/MIN/MAX/COND */ |
1291 | 0 | ir_bitqueue_add(ctx->iter_worklist, ctx->ir_base[input].op1); |
1292 | 0 | } |
1293 | 246 | } |
1294 | 577 | } |
1295 | 293 | } |
1296 | | |
1297 | | void ir_iter_replace(ir_ctx *ctx, ir_ref ref, ir_ref new_ref, ir_bitqueue *worklist) |
1298 | 21 | { |
1299 | 21 | ir_ref i, j, n, *p, use; |
1300 | 21 | ir_insn *insn; |
1301 | 21 | ir_use_list *use_list; |
1302 | | |
1303 | 21 | IR_ASSERT(ref != new_ref); |
1304 | | |
1305 | 21 | use_list = &ctx->use_lists[ref]; |
1306 | 21 | n = use_list->count; |
1307 | 21 | p = &ctx->use_edges[use_list->refs]; |
1308 | 21 | if (new_ref <= 0) { |
1309 | | /* constant or IR_UNUSED */ |
1310 | 34 | for (; n; p++, n--) { |
1311 | 20 | use = *p; |
1312 | 20 | IR_ASSERT(use != ref); |
1313 | 20 | insn = &ctx->ir_base[use]; |
1314 | 20 | i = ir_insn_find_op(insn, ref); |
1315 | 20 | IR_ASSERT(i > 0); |
1316 | 20 | ir_insn_set_op(insn, i, new_ref); |
1317 | | /* schedule folding */ |
1318 | 20 | ir_bitqueue_add(worklist, use); |
1319 | 20 | ir_iter_add_related_uses(ctx, use, worklist); |
1320 | 20 | } |
1321 | 14 | } else { |
1322 | 18 | for (j = 0; j < n; j++, p++) { |
1323 | 11 | use = *p; |
1324 | 11 | IR_ASSERT(use != ref); |
1325 | 11 | insn = &ctx->ir_base[use]; |
1326 | 11 | i = ir_insn_find_op(insn, ref); |
1327 | 11 | IR_ASSERT(i > 0); |
1328 | 11 | ir_insn_set_op(insn, i, new_ref); |
1329 | 11 | if (ir_use_list_add(ctx, new_ref, use)) { |
1330 | | /* restore after reallocation */ |
1331 | 4 | use_list = &ctx->use_lists[ref]; |
1332 | 4 | n = use_list->count; |
1333 | 4 | p = &ctx->use_edges[use_list->refs + j]; |
1334 | 4 | } |
1335 | | /* schedule folding */ |
1336 | 11 | ir_bitqueue_add(worklist, use); |
1337 | 11 | } |
1338 | 7 | } |
1339 | 21 | } |
1340 | | |
1341 | | static void ir_iter_replace_insn(ir_ctx *ctx, ir_ref ref, ir_ref new_ref) |
1342 | 21 | { |
1343 | 21 | ir_ref j, n, *p; |
1344 | 21 | ir_insn *insn; |
1345 | | |
1346 | 21 | insn = &ctx->ir_base[ref]; |
1347 | 21 | n = insn->inputs_count; |
1348 | 21 | insn->opt = IR_NOP; /* keep "inputs_count" */ |
1349 | 59 | for (j = 1, p = insn->ops + 1; j <= n; j++, p++) { |
1350 | 38 | ir_ref input = *p; |
1351 | 38 | *p = IR_UNUSED; |
1352 | 38 | if (input > 0) { |
1353 | 7 | ir_use_list_remove_one(ctx, input, ref); |
1354 | 7 | if (ir_is_dead(ctx, input)) { |
1355 | | /* schedule DCE */ |
1356 | 0 | ir_bitqueue_add(ctx->iter_worklist, input); |
1357 | 7 | } else if (ctx->ir_base[input].op == IR_PHI && ctx->use_lists[input].count == 1) { |
1358 | | /* try to optimize PHI into ABS/MIN/MAX/COND */ |
1359 | 0 | ir_bitqueue_add(ctx->iter_worklist, ctx->ir_base[input].op1); |
1360 | 0 | } |
1361 | 7 | } |
1362 | 38 | } |
1363 | | |
1364 | 21 | ir_iter_replace(ctx, ref, new_ref, ctx->iter_worklist); |
1365 | | |
1366 | 21 | CLEAR_USES(ref); |
1367 | 21 | } |
1368 | | |
1369 | | static void ir_iter_update_op(ir_ctx *ctx, ir_ref ref, uint32_t idx, ir_ref new_val) |
1370 | 14 | { |
1371 | 14 | ir_insn *insn = &ctx->ir_base[ref]; |
1372 | 14 | ir_ref old_val = ir_insn_op(insn, idx); |
1373 | | |
1374 | 14 | IR_ASSERT(old_val != new_val); |
1375 | 14 | if (!IR_IS_CONST_REF(new_val)) { |
1376 | 14 | ir_use_list_add(ctx, new_val, ref); |
1377 | 14 | } |
1378 | 14 | ir_insn_set_op(insn, idx, new_val); |
1379 | 14 | if (!IR_IS_CONST_REF(old_val)) { |
1380 | 14 | ir_use_list_remove_one(ctx, old_val, ref); |
1381 | 14 | if (ir_is_dead(ctx, old_val)) { |
1382 | | /* schedule DCE */ |
1383 | 14 | ir_bitqueue_add(ctx->iter_worklist, old_val); |
1384 | 14 | } |
1385 | 14 | } |
1386 | 14 | } |
1387 | | |
1388 | | static ir_ref ir_iter_find_cse1(const ir_ctx *ctx, uint32_t optx, ir_ref op1) |
1389 | 0 | { |
1390 | 0 | IR_ASSERT(!IR_IS_CONST_REF(op1)); |
1391 | |
|
1392 | 0 | const ir_use_list *use_list = &ctx->use_lists[op1]; |
1393 | 0 | const ir_ref *p; |
1394 | 0 | ir_ref n = use_list->count; |
1395 | |
|
1396 | 0 | for (p = ctx->use_edges + use_list->refs; n > 0; p++, n--) { |
1397 | 0 | ir_ref use = *p; |
1398 | 0 | const ir_insn *use_insn = &ctx->ir_base[use]; |
1399 | |
|
1400 | 0 | if (use_insn->optx == optx) { |
1401 | 0 | IR_ASSERT(use_insn->op1 == op1); |
1402 | 0 | return use; |
1403 | 0 | } |
1404 | 0 | } |
1405 | 0 | return IR_UNUSED; |
1406 | 0 | } |
1407 | | |
1408 | | static ir_ref ir_iter_find_cse(const ir_ctx *ctx, ir_ref ref, uint32_t opt, ir_ref op1, ir_ref op2, ir_ref op3) |
1409 | 80 | { |
1410 | 80 | uint32_t n = IR_INPUT_EDGES_COUNT(ir_op_flags[opt & IR_OPT_OP_MASK]); |
1411 | 80 | const ir_use_list *use_list = NULL; |
1412 | 80 | const ir_ref *p; |
1413 | 80 | ir_ref use; |
1414 | 80 | const ir_insn *use_insn; |
1415 | | |
1416 | 80 | if (n == 2) { |
1417 | 4 | if (!IR_IS_CONST_REF(op1)) { |
1418 | 4 | use_list = &ctx->use_lists[op1]; |
1419 | 4 | } |
1420 | 4 | if (!IR_IS_CONST_REF(op2) && (!use_list || use_list->count > ctx->use_lists[op2].count)) { |
1421 | 0 | use_list = &ctx->use_lists[op2]; |
1422 | 0 | } |
1423 | 4 | if (use_list) { |
1424 | 4 | n = use_list->count; |
1425 | 12 | for (p = ctx->use_edges + use_list->refs; n > 0; p++, n--) { |
1426 | 12 | use = *p; |
1427 | 12 | if (use != ref) { |
1428 | 12 | use_insn = &ctx->ir_base[use]; |
1429 | 12 | if (use_insn->opt == opt && use_insn->op1 == op1 && use_insn->op2 == op2) { |
1430 | 4 | IR_ASSERT(use_insn->op3 == op3); |
1431 | 4 | if (use < ref) { |
1432 | 4 | return use; |
1433 | 4 | } else { |
1434 | 0 | ir_bitqueue_add(ctx->iter_worklist, use); |
1435 | 0 | } |
1436 | 4 | } |
1437 | 12 | } |
1438 | 12 | } |
1439 | 4 | } |
1440 | 76 | } else if (n < 2) { |
1441 | 76 | if (op1 > 0) { |
1442 | 76 | IR_ASSERT(n == 1); |
1443 | 76 | use_list = &ctx->use_lists[op1]; |
1444 | 76 | n = use_list->count; |
1445 | 232 | for (p = ctx->use_edges + use_list->refs; n > 0; p++, n--) { |
1446 | 156 | use = *p; |
1447 | 156 | if (use != ref) { |
1448 | 80 | use_insn = &ctx->ir_base[use]; |
1449 | 80 | if (use_insn->opt == opt) { |
1450 | 0 | IR_ASSERT(use_insn->op1 == op1); |
1451 | 0 | IR_ASSERT(use_insn->op2 == op2); |
1452 | 0 | IR_ASSERT(use_insn->op3 == op3); |
1453 | 0 | if (use < ref) { |
1454 | 0 | return use; |
1455 | 0 | } else { |
1456 | 0 | ir_bitqueue_add(ctx->iter_worklist, use); |
1457 | 0 | } |
1458 | 0 | } |
1459 | 80 | } |
1460 | 156 | } |
1461 | 76 | } |
1462 | 76 | } else { |
1463 | 0 | IR_ASSERT(n == 3); |
1464 | 0 | if (!IR_IS_CONST_REF(op1)) { |
1465 | 0 | use_list = &ctx->use_lists[op1]; |
1466 | 0 | } |
1467 | 0 | if (!IR_IS_CONST_REF(op2) && (!use_list || use_list->count > ctx->use_lists[op2].count)) { |
1468 | 0 | use_list = &ctx->use_lists[op2]; |
1469 | 0 | } |
1470 | 0 | if (op3 > 0 && (!use_list || use_list->count > ctx->use_lists[op3].count)) { |
1471 | 0 | use_list = &ctx->use_lists[op3]; |
1472 | 0 | } |
1473 | 0 | if (use_list) { |
1474 | 0 | n = use_list->count; |
1475 | 0 | for (p = ctx->use_edges + use_list->refs; n > 0; p++, n--) { |
1476 | 0 | use = *p; |
1477 | 0 | if (use != ref) { |
1478 | 0 | use_insn = &ctx->ir_base[use]; |
1479 | 0 | if (use_insn->opt == opt && use_insn->op1 == op1 && use_insn->op2 == op2 && use_insn->op3 == op3) { |
1480 | 0 | if (use < ref) { |
1481 | 0 | return use; |
1482 | 0 | } else { |
1483 | 0 | ir_bitqueue_add(ctx->iter_worklist, use); |
1484 | 0 | } |
1485 | 0 | } |
1486 | 0 | } |
1487 | 0 | } |
1488 | 0 | } |
1489 | 0 | } |
1490 | 76 | return IR_UNUSED; |
1491 | 80 | } |
1492 | | |
1493 | | static void ir_iter_fold(ir_ctx *ctx, ir_ref ref) |
1494 | 90 | { |
1495 | 90 | uint32_t opt; |
1496 | 90 | ir_ref op1, op2, op3, copy; |
1497 | 90 | const ir_insn *op1_insn, *op2_insn, *op3_insn; |
1498 | 90 | ir_insn *insn; |
1499 | | |
1500 | 90 | insn = &ctx->ir_base[ref]; |
1501 | 90 | opt = insn->opt; |
1502 | 90 | op1 = insn->op1; |
1503 | 90 | op2 = insn->op2; |
1504 | 90 | op3 = insn->op3; |
1505 | | |
1506 | 94 | restart: |
1507 | 94 | op1_insn = ctx->ir_base + op1; |
1508 | 94 | op2_insn = ctx->ir_base + op2; |
1509 | 94 | op3_insn = ctx->ir_base + op3; |
1510 | | |
1511 | 94 | switch (ir_folding(ctx, opt, op1, op2, op3, op1_insn, op2_insn, op3_insn)) { |
1512 | 4 | case IR_FOLD_DO_RESTART: |
1513 | 4 | opt = ctx->fold_insn.optx; |
1514 | 4 | op1 = ctx->fold_insn.op1; |
1515 | 4 | op2 = ctx->fold_insn.op2; |
1516 | 4 | op3 = ctx->fold_insn.op3; |
1517 | 4 | goto restart; |
1518 | 80 | case IR_FOLD_DO_CSE: |
1519 | 80 | copy = ir_iter_find_cse(ctx, ref, ctx->fold_insn.opt, |
1520 | 80 | ctx->fold_insn.op1, ctx->fold_insn.op2, ctx->fold_insn.op3); |
1521 | 80 | if (copy) { |
1522 | 4 | ir_iter_replace_insn(ctx, ref, copy); |
1523 | 4 | break; |
1524 | 4 | } |
1525 | 76 | IR_FALLTHROUGH; |
1526 | 76 | case IR_FOLD_DO_EMIT: |
1527 | 76 | insn = &ctx->ir_base[ref]; |
1528 | 76 | if (insn->opt != ctx->fold_insn.opt |
1529 | 76 | || insn->op1 != ctx->fold_insn.op1 |
1530 | 76 | || insn->op2 != ctx->fold_insn.op2 |
1531 | 76 | || insn->op3 != ctx->fold_insn.op3) { |
1532 | |
|
1533 | 0 | insn->optx = ctx->fold_insn.opt; |
1534 | 0 | IR_ASSERT(!IR_OP_HAS_VAR_INPUTS(ir_op_flags[opt & IR_OPT_OP_MASK])); |
1535 | 0 | insn->inputs_count = IR_INPUT_EDGES_COUNT(ir_op_flags[opt & IR_OPT_OP_MASK]); |
1536 | 0 | if (insn->op1 != ctx->fold_insn.op1) { |
1537 | 0 | if (insn->op1 > 0) { |
1538 | 0 | ir_use_list_remove_one(ctx, insn->op1, ref); |
1539 | 0 | if (ctx->use_lists[insn->op1].count == 0) ir_bitqueue_add(ctx->iter_worklist, insn->op1); |
1540 | 0 | } |
1541 | 0 | if (ctx->fold_insn.op1 > 0) { |
1542 | 0 | ir_use_list_add(ctx, ctx->fold_insn.op1, ref); |
1543 | 0 | } |
1544 | 0 | } |
1545 | 0 | if (insn->op2 != ctx->fold_insn.op2) { |
1546 | 0 | if (insn->op2 > 0) { |
1547 | 0 | ir_use_list_remove_one(ctx, insn->op2, ref); |
1548 | 0 | if (ctx->use_lists[insn->op2].count == 0) ir_bitqueue_add(ctx->iter_worklist, insn->op2); |
1549 | 0 | } |
1550 | 0 | if (ctx->fold_insn.op2 > 0) { |
1551 | 0 | ir_use_list_add(ctx, ctx->fold_insn.op2, ref); |
1552 | 0 | } |
1553 | 0 | } |
1554 | 0 | if (insn->op3 != ctx->fold_insn.op3) { |
1555 | 0 | if (insn->op3 > 0) { |
1556 | 0 | ir_use_list_remove_one(ctx, insn->op3, ref); |
1557 | 0 | if (ctx->use_lists[insn->op3].count == 0) ir_bitqueue_add(ctx->iter_worklist, insn->op3); |
1558 | 0 | } |
1559 | 0 | if (ctx->fold_insn.op3 > 0) { |
1560 | 0 | ir_use_list_add(ctx, ctx->fold_insn.op3, ref); |
1561 | 0 | } |
1562 | 0 | } |
1563 | 0 | insn->op1 = ctx->fold_insn.op1; |
1564 | 0 | insn->op2 = ctx->fold_insn.op2; |
1565 | 0 | insn->op3 = ctx->fold_insn.op3; |
1566 | |
|
1567 | 0 | ir_iter_add_uses(ctx, ref, ctx->iter_worklist); |
1568 | 0 | } |
1569 | 76 | break; |
1570 | 76 | case IR_FOLD_DO_COPY: |
1571 | 10 | op1 = ctx->fold_insn.op1; |
1572 | 10 | ir_iter_replace_insn(ctx, ref, op1); |
1573 | 10 | break; |
1574 | 0 | case IR_FOLD_DO_CONST: |
1575 | 0 | op1 = ir_const(ctx, ctx->fold_insn.val, ctx->fold_insn.type); |
1576 | 0 | ir_iter_replace_insn(ctx, ref, op1); |
1577 | 0 | break; |
1578 | 0 | default: |
1579 | 0 | IR_ASSERT(0); |
1580 | 0 | break; |
1581 | 94 | } |
1582 | 94 | } |
1583 | | |
1584 | | static bool ir_may_promote_d2f(const ir_ctx *ctx, ir_ref ref) |
1585 | 0 | { |
1586 | 0 | const ir_insn *insn = &ctx->ir_base[ref]; |
1587 | |
|
1588 | 0 | IR_ASSERT(insn->type == IR_DOUBLE); |
1589 | 0 | if (IR_IS_CONST_REF(ref)) { |
1590 | 0 | return !IR_IS_SYM_CONST(insn->op) && insn->val.d == (double)(float)insn->val.d; |
1591 | 0 | } else { |
1592 | 0 | switch (insn->op) { |
1593 | 0 | case IR_FP2FP: |
1594 | 0 | return 1; |
1595 | | // case IR_INT2FP: |
1596 | | // return ctx->use_lists[ref].count == 1; |
1597 | 0 | case IR_NEG: |
1598 | 0 | case IR_ABS: |
1599 | 0 | return ctx->use_lists[ref].count == 1 && |
1600 | 0 | ir_may_promote_d2f(ctx, insn->op1); |
1601 | 0 | case IR_ADD: |
1602 | 0 | case IR_SUB: |
1603 | 0 | case IR_MUL: |
1604 | 0 | case IR_DIV: |
1605 | 0 | case IR_MIN: |
1606 | 0 | case IR_MAX: |
1607 | 0 | return ctx->use_lists[ref].count == 1 && |
1608 | 0 | ir_may_promote_d2f(ctx, insn->op1) && |
1609 | 0 | ir_may_promote_d2f(ctx, insn->op2); |
1610 | 0 | default: |
1611 | 0 | break; |
1612 | 0 | } |
1613 | 0 | } |
1614 | 0 | return 0; |
1615 | 0 | } |
1616 | | |
1617 | | static bool ir_may_promote_f2d(const ir_ctx *ctx, ir_ref ref) |
1618 | 0 | { |
1619 | 0 | const ir_insn *insn = &ctx->ir_base[ref]; |
1620 | |
|
1621 | 0 | IR_ASSERT(insn->type == IR_FLOAT); |
1622 | 0 | if (IR_IS_CONST_REF(ref)) { |
1623 | 0 | return !IR_IS_SYM_CONST(insn->op) && insn->val.f == (float)(double)insn->val.f; |
1624 | 0 | } else { |
1625 | 0 | switch (insn->op) { |
1626 | 0 | case IR_FP2FP: |
1627 | 0 | return 1; |
1628 | | // case IR_INT2FP: |
1629 | | // return ctx->use_lists[ref].count == 1; |
1630 | 0 | case IR_NEG: |
1631 | 0 | case IR_ABS: |
1632 | 0 | return ctx->use_lists[ref].count == 1 && |
1633 | 0 | ir_may_promote_f2d(ctx, insn->op1); |
1634 | 0 | case IR_ADD: |
1635 | 0 | case IR_SUB: |
1636 | 0 | case IR_MUL: |
1637 | | // case IR_DIV: |
1638 | 0 | case IR_MIN: |
1639 | 0 | case IR_MAX: |
1640 | 0 | return ctx->use_lists[ref].count == 1 && |
1641 | 0 | ir_may_promote_f2d(ctx, insn->op1) && |
1642 | 0 | ir_may_promote_f2d(ctx, insn->op2); |
1643 | 0 | default: |
1644 | 0 | break; |
1645 | 0 | } |
1646 | 0 | } |
1647 | 0 | return 0; |
1648 | 0 | } |
1649 | | |
1650 | | static ir_ref ir_promote_d2f(ir_ctx *ctx, ir_ref ref, ir_ref use) |
1651 | 0 | { |
1652 | 0 | ir_insn *insn = &ctx->ir_base[ref]; |
1653 | 0 | uint32_t count; |
1654 | 0 | ir_ref op; |
1655 | |
|
1656 | 0 | IR_ASSERT(insn->type == IR_DOUBLE); |
1657 | 0 | if (IR_IS_CONST_REF(ref)) { |
1658 | 0 | return ir_const_float(ctx, (float)insn->val.d); |
1659 | 0 | } else { |
1660 | 0 | ir_bitqueue_add(ctx->iter_worklist, ref); |
1661 | 0 | switch (insn->op) { |
1662 | 0 | case IR_FP2FP: |
1663 | 0 | count = ctx->use_lists[ref].count; |
1664 | 0 | ir_use_list_remove_all(ctx, ref, use); |
1665 | 0 | if (ctx->use_lists[ref].count == 0) { |
1666 | 0 | ir_use_list_replace_one(ctx, insn->op1, ref, use); |
1667 | 0 | if (count > 1) { |
1668 | 0 | do { |
1669 | 0 | ir_use_list_add(ctx, insn->op1, use); |
1670 | 0 | } while (--count > 1); |
1671 | 0 | } |
1672 | 0 | ref = insn->op1; |
1673 | 0 | MAKE_NOP(insn); |
1674 | 0 | return ref; |
1675 | 0 | } else { |
1676 | 0 | ir_use_list_add(ctx, insn->op1, use); |
1677 | 0 | count -= ctx->use_lists[ref].count; |
1678 | 0 | if (count > 1) { |
1679 | 0 | do { |
1680 | 0 | ir_use_list_add(ctx, insn->op1, use); |
1681 | 0 | } while (--count > 1); |
1682 | 0 | } |
1683 | 0 | } |
1684 | 0 | return insn->op1; |
1685 | | // case IR_INT2FP: |
1686 | | // insn->type = IR_FLOAT; |
1687 | | // return ref; |
1688 | 0 | case IR_NEG: |
1689 | 0 | case IR_ABS: |
1690 | 0 | op = ir_promote_d2f(ctx, insn->op1, ref); |
1691 | 0 | insn = &ctx->ir_base[ref]; |
1692 | 0 | insn->op1 = op; |
1693 | 0 | insn->type = IR_FLOAT; |
1694 | 0 | return ref; |
1695 | 0 | case IR_ADD: |
1696 | 0 | case IR_SUB: |
1697 | 0 | case IR_MUL: |
1698 | 0 | case IR_DIV: |
1699 | 0 | case IR_MIN: |
1700 | 0 | case IR_MAX: |
1701 | 0 | if (insn->op1 == insn->op2) { |
1702 | 0 | op = ir_promote_d2f(ctx, insn->op1, ref); |
1703 | 0 | insn = &ctx->ir_base[ref]; |
1704 | 0 | insn->op2 = insn->op1 = op; |
1705 | 0 | } else { |
1706 | 0 | ir_ref op1 = insn->op1; |
1707 | 0 | ir_ref op2 = insn->op2; |
1708 | 0 | op1 = ir_promote_d2f(ctx, op1, ref); |
1709 | 0 | op2 = ir_promote_d2f(ctx, op2, ref); |
1710 | 0 | insn = &ctx->ir_base[ref]; |
1711 | 0 | insn->op1 = op1; |
1712 | 0 | insn->op2 = op2; |
1713 | 0 | } |
1714 | 0 | insn->type = IR_FLOAT; |
1715 | 0 | return ref; |
1716 | 0 | default: |
1717 | 0 | break; |
1718 | 0 | } |
1719 | 0 | } |
1720 | 0 | IR_ASSERT(0); |
1721 | 0 | return ref; |
1722 | 0 | } |
1723 | | |
1724 | | static ir_ref ir_promote_f2d(ir_ctx *ctx, ir_ref ref, ir_ref use) |
1725 | 0 | { |
1726 | 0 | ir_insn *insn = &ctx->ir_base[ref]; |
1727 | 0 | uint32_t count; |
1728 | 0 | ir_ref old_ref; |
1729 | 0 | ir_ref op; |
1730 | |
|
1731 | 0 | IR_ASSERT(insn->type == IR_FLOAT); |
1732 | 0 | if (IR_IS_CONST_REF(ref)) { |
1733 | 0 | return ir_const_double(ctx, (double)insn->val.f); |
1734 | 0 | } else { |
1735 | 0 | ir_bitqueue_add(ctx->iter_worklist, ref); |
1736 | 0 | switch (insn->op) { |
1737 | 0 | case IR_FP2FP: |
1738 | 0 | count = ctx->use_lists[ref].count; |
1739 | 0 | ir_use_list_remove_all(ctx, ref, use); |
1740 | 0 | if (ctx->use_lists[ref].count == 0) { |
1741 | 0 | ir_use_list_replace_one(ctx, insn->op1, ref, use); |
1742 | 0 | if (count > 1) { |
1743 | 0 | do { |
1744 | 0 | ir_use_list_add(ctx, insn->op1, use); |
1745 | 0 | } while (--count > 1); |
1746 | 0 | } |
1747 | 0 | ref = insn->op1; |
1748 | 0 | MAKE_NOP(insn); |
1749 | 0 | return ref; |
1750 | 0 | } else { |
1751 | 0 | ir_use_list_add(ctx, insn->op1, use); |
1752 | 0 | count -= ctx->use_lists[ref].count; |
1753 | 0 | if (count > 1) { |
1754 | 0 | do { |
1755 | 0 | ir_use_list_add(ctx, insn->op1, use); |
1756 | 0 | } while (--count > 1); |
1757 | 0 | } |
1758 | 0 | } |
1759 | 0 | return insn->op1; |
1760 | 0 | case IR_INT2FP: |
1761 | 0 | old_ref = ir_iter_find_cse1(ctx, IR_OPTX(IR_INT2FP, IR_DOUBLE, 1), insn->op1); |
1762 | 0 | if (old_ref) { |
1763 | 0 | IR_ASSERT(ctx->use_lists[ref].count == 1); |
1764 | 0 | ir_use_list_remove_one(ctx, insn->op1, ref); |
1765 | 0 | CLEAR_USES(ref); |
1766 | 0 | MAKE_NOP(insn); |
1767 | 0 | ir_use_list_add(ctx, old_ref, use); |
1768 | 0 | return old_ref; |
1769 | 0 | } |
1770 | 0 | insn->type = IR_DOUBLE; |
1771 | 0 | return ref; |
1772 | 0 | case IR_NEG: |
1773 | 0 | case IR_ABS: |
1774 | 0 | op = ir_promote_f2d(ctx, insn->op1, ref); |
1775 | 0 | insn = &ctx->ir_base[ref]; |
1776 | 0 | insn->op1 = op; |
1777 | 0 | insn->type = IR_DOUBLE; |
1778 | 0 | return ref; |
1779 | 0 | case IR_ADD: |
1780 | 0 | case IR_SUB: |
1781 | 0 | case IR_MUL: |
1782 | | // case IR_DIV: |
1783 | 0 | case IR_MIN: |
1784 | 0 | case IR_MAX: |
1785 | 0 | if (insn->op1 == insn->op2) { |
1786 | 0 | op = ir_promote_f2d(ctx, insn->op1, ref); |
1787 | 0 | insn = &ctx->ir_base[ref]; |
1788 | 0 | insn->op2 = insn->op1 = op; |
1789 | 0 | } else { |
1790 | 0 | ir_ref op1 = insn->op1; |
1791 | 0 | ir_ref op2 = insn->op2; |
1792 | 0 | op1 = ir_promote_f2d(ctx, op1, ref); |
1793 | 0 | op2 = ir_promote_f2d(ctx, op2, ref); |
1794 | 0 | insn = &ctx->ir_base[ref]; |
1795 | 0 | insn->op1 = op1; |
1796 | 0 | insn->op2 = op2; |
1797 | 0 | } |
1798 | 0 | insn->type = IR_DOUBLE; |
1799 | 0 | return ref; |
1800 | 0 | default: |
1801 | 0 | break; |
1802 | 0 | } |
1803 | 0 | } |
1804 | 0 | IR_ASSERT(0); |
1805 | 0 | return ref; |
1806 | 0 | } |
1807 | | |
1808 | | static bool ir_may_promote_trunc(const ir_ctx *ctx, ir_type type, ir_ref ref) |
1809 | 80 | { |
1810 | 80 | const ir_insn *insn = &ctx->ir_base[ref]; |
1811 | 80 | const ir_ref *p; |
1812 | 80 | ir_ref n, input; |
1813 | | |
1814 | 80 | if (IR_IS_CONST_REF(ref)) { |
1815 | 4 | return !IR_IS_SYM_CONST(insn->op); |
1816 | 76 | } else { |
1817 | 76 | switch (insn->op) { |
1818 | 0 | case IR_ZEXT: |
1819 | 0 | case IR_SEXT: |
1820 | 0 | case IR_TRUNC: |
1821 | 0 | return ctx->ir_base[insn->op1].type == type || ctx->use_lists[ref].count == 1; |
1822 | 0 | case IR_NEG: |
1823 | 0 | case IR_ABS: |
1824 | 0 | case IR_NOT: |
1825 | 0 | return ctx->use_lists[ref].count == 1 && |
1826 | 0 | ir_may_promote_trunc(ctx, type, insn->op1); |
1827 | 62 | case IR_ADD: |
1828 | 65 | case IR_SUB: |
1829 | 65 | case IR_MUL: |
1830 | 65 | case IR_MIN: |
1831 | 65 | case IR_MAX: |
1832 | 65 | case IR_OR: |
1833 | 65 | case IR_AND: |
1834 | 65 | case IR_XOR: |
1835 | 65 | return ctx->use_lists[ref].count == 1 && |
1836 | 0 | ir_may_promote_trunc(ctx, type, insn->op1) && |
1837 | 0 | ir_may_promote_trunc(ctx, type, insn->op2); |
1838 | 0 | case IR_SHL: |
1839 | 0 | return ctx->use_lists[ref].count == 1 && |
1840 | 0 | ir_may_promote_trunc(ctx, type, insn->op1) && |
1841 | 0 | IR_IS_CONST_REF(insn->op2) && |
1842 | 0 | (IR_IS_TYPE_UNSIGNED(ctx->ir_base[insn->op2].type) || ctx->ir_base[insn->op2].val.i64 >= 0) && |
1843 | 0 | ctx->ir_base[insn->op2].val.u64 < ir_type_size[type] * 8; |
1844 | | // case IR_SHR: |
1845 | | // case IR_SAR: |
1846 | | // case IR_DIV: |
1847 | | // case IR_MOD: |
1848 | | // case IR_FP2INT: |
1849 | | // TODO: ??? |
1850 | 0 | case IR_COND: |
1851 | 0 | return ctx->use_lists[ref].count == 1 && |
1852 | 0 | insn->op1 != insn->op2 && |
1853 | 0 | insn->op1 != insn->op3 && |
1854 | 0 | ir_may_promote_trunc(ctx, type, insn->op2) && |
1855 | 0 | ir_may_promote_trunc(ctx, type, insn->op3); |
1856 | 0 | case IR_PHI: |
1857 | 0 | if (ctx->use_lists[ref].count != 1) { |
1858 | 0 | ir_use_list *use_list = &ctx->use_lists[ref]; |
1859 | 0 | ir_ref count = 0; |
1860 | |
|
1861 | 0 | for (p = &ctx->use_edges[use_list->refs], n = use_list->count; n > 0; p++, n--) { |
1862 | 0 | if (*p != ref) { |
1863 | 0 | if (count) { |
1864 | 0 | return 0; |
1865 | 0 | } |
1866 | 0 | count = 1; |
1867 | 0 | } |
1868 | 0 | } |
1869 | 0 | } |
1870 | 0 | for (p = insn->ops + 2, n = insn->inputs_count - 1; n > 0; p++, n--) { |
1871 | 0 | input = *p; |
1872 | 0 | if (input != ref) { |
1873 | 0 | if (!ir_may_promote_trunc(ctx, type, input)) { |
1874 | 0 | return 0; |
1875 | 0 | } |
1876 | 0 | } |
1877 | 0 | } |
1878 | 0 | return 1; |
1879 | 11 | default: |
1880 | 11 | break; |
1881 | 76 | } |
1882 | 76 | } |
1883 | 11 | return 0; |
1884 | 80 | } |
1885 | | |
1886 | | static ir_ref ir_promote_i2i(ir_ctx *ctx, ir_type type, ir_ref ref, ir_ref use) |
1887 | 4 | { |
1888 | 4 | ir_insn *insn = &ctx->ir_base[ref]; |
1889 | 4 | uint32_t count; |
1890 | 4 | ir_ref n, input, op; |
1891 | | |
1892 | 4 | if (IR_IS_CONST_REF(ref)) { |
1893 | 4 | ir_val val; |
1894 | | |
1895 | 4 | switch (type) { |
1896 | 0 | case IR_I8: val.i64 = insn->val.i8; break; |
1897 | 4 | case IR_U8: val.u64 = insn->val.u8; break; |
1898 | 0 | case IR_I16: val.i64 = insn->val.i16; break; |
1899 | 0 | case IR_U16: val.u64 = insn->val.u16; break; |
1900 | 0 | case IR_I32: val.i64 = insn->val.i32; break; |
1901 | 0 | case IR_U32: val.u64 = insn->val.u32; break; |
1902 | 0 | case IR_CHAR:val.i64 = insn->val.i8; break; |
1903 | 0 | case IR_BOOL:val.u64 = insn->val.u8 != 0; break; |
1904 | 0 | default: IR_ASSERT(0); val.u64 = 0; |
1905 | 4 | } |
1906 | 4 | return ir_const(ctx, val, type); |
1907 | 4 | } else { |
1908 | 0 | ir_bitqueue_add(ctx->iter_worklist, ref); |
1909 | 0 | switch (insn->op) { |
1910 | 0 | case IR_ZEXT: |
1911 | 0 | case IR_SEXT: |
1912 | 0 | case IR_TRUNC: |
1913 | 0 | if (ctx->ir_base[insn->op1].type != type) { |
1914 | 0 | ir_type src_type = ctx->ir_base[insn->op1].type; |
1915 | |
|
1916 | 0 | IR_ASSERT(IR_IS_TYPE_INT(src_type) && IR_IS_TYPE_INT(type)); |
1917 | 0 | if (ir_type_size[src_type] == ir_type_size[type]) { |
1918 | 0 | insn->op = IR_BITCAST; |
1919 | 0 | } else if (ir_type_size[src_type] > ir_type_size[type]) { |
1920 | 0 | insn->op = IR_TRUNC; |
1921 | 0 | } else { |
1922 | 0 | if (insn->op != IR_SEXT && insn->op != IR_ZEXT) { |
1923 | 0 | insn->op = IR_IS_TYPE_SIGNED(type) ? IR_SEXT : IR_ZEXT; |
1924 | 0 | } |
1925 | 0 | } |
1926 | 0 | insn->type = type; |
1927 | 0 | ir_iter_add_uses(ctx, ref, ctx->iter_worklist); |
1928 | 0 | return ref; |
1929 | 0 | } |
1930 | | |
1931 | 0 | count = ctx->use_lists[ref].count; |
1932 | 0 | ir_use_list_remove_all(ctx, ref, use); |
1933 | 0 | if (ctx->use_lists[ref].count == 0) { |
1934 | 0 | ir_use_list_replace_one(ctx, insn->op1, ref, use); |
1935 | 0 | if (count > 1) { |
1936 | 0 | do { |
1937 | 0 | ir_use_list_add(ctx, insn->op1, use); |
1938 | 0 | } while (--count > 1); |
1939 | 0 | } |
1940 | 0 | ref = insn->op1; |
1941 | 0 | MAKE_NOP(insn); |
1942 | 0 | return ref; |
1943 | 0 | } else { |
1944 | 0 | ir_use_list_add(ctx, insn->op1, use); |
1945 | 0 | count -= ctx->use_lists[ref].count; |
1946 | 0 | if (count > 1) { |
1947 | 0 | do { |
1948 | 0 | ir_use_list_add(ctx, insn->op1, use); |
1949 | 0 | } while (--count > 1); |
1950 | 0 | } |
1951 | 0 | } |
1952 | 0 | return insn->op1; |
1953 | 0 | case IR_NEG: |
1954 | 0 | case IR_ABS: |
1955 | 0 | case IR_NOT: |
1956 | 0 | op = ir_promote_i2i(ctx, type, insn->op1, ref); |
1957 | 0 | insn = &ctx->ir_base[ref]; |
1958 | 0 | insn->op1 = op; |
1959 | 0 | insn->type = type; |
1960 | 0 | return ref; |
1961 | 0 | case IR_ADD: |
1962 | 0 | case IR_SUB: |
1963 | 0 | case IR_MUL: |
1964 | 0 | case IR_MIN: |
1965 | 0 | case IR_MAX: |
1966 | 0 | case IR_OR: |
1967 | 0 | case IR_AND: |
1968 | 0 | case IR_XOR: |
1969 | 0 | case IR_SHL: |
1970 | 0 | if (insn->op1 == insn->op2) { |
1971 | 0 | op = ir_promote_i2i(ctx, type, insn->op1, ref); |
1972 | 0 | insn = &ctx->ir_base[ref]; |
1973 | 0 | insn->op2 = insn->op1 = op; |
1974 | 0 | } else { |
1975 | 0 | ir_ref op1 = insn->op1; |
1976 | 0 | ir_ref op2 = insn->op2; |
1977 | 0 | op1 = ir_promote_i2i(ctx, type, op1, ref); |
1978 | 0 | op2 = ir_promote_i2i(ctx, type, op2, ref); |
1979 | 0 | insn = &ctx->ir_base[ref]; |
1980 | 0 | insn->op1 = op1; |
1981 | 0 | insn->op2 = op2; |
1982 | 0 | } |
1983 | 0 | insn->type = type; |
1984 | 0 | return ref; |
1985 | | // case IR_DIV: |
1986 | | // case IR_MOD: |
1987 | | // case IR_SHR: |
1988 | | // case IR_SAR: |
1989 | | // case IR_FP2INT: |
1990 | | // TODO: ??? |
1991 | 0 | case IR_COND: |
1992 | 0 | if (insn->op2 == insn->op3) { |
1993 | 0 | op = ir_promote_i2i(ctx, type, insn->op2, ref); |
1994 | 0 | insn = &ctx->ir_base[ref]; |
1995 | 0 | insn->op3 = insn->op2 = op; |
1996 | 0 | } else { |
1997 | 0 | ir_ref op2 = insn->op2; |
1998 | 0 | ir_ref op3 = insn->op3; |
1999 | 0 | op2 = ir_promote_i2i(ctx, type, op2, ref); |
2000 | 0 | op3 = ir_promote_i2i(ctx, type, op3, ref); |
2001 | 0 | insn = &ctx->ir_base[ref]; |
2002 | 0 | insn->op2 = op2; |
2003 | 0 | insn->op3 = op3; |
2004 | 0 | } |
2005 | 0 | insn->type = type; |
2006 | 0 | if (IR_IS_TYPE_SIGNED(type)) { |
2007 | 0 | ir_insn *cond = &ctx->ir_base[insn->op1]; |
2008 | 0 | if (cond->op == IR_LT || cond->op == IR_LE || cond->op == IR_GT || cond->op == IR_GE) { |
2009 | 0 | if (cond->op1 == insn->op2 && cond->op2 == insn->op3) { |
2010 | 0 | insn->op = (cond->op == IR_LT || cond->op == IR_LE) ? IR_MIN : IR_MAX; |
2011 | 0 | ir_use_list_remove_one(ctx, insn->op1, ref); |
2012 | 0 | ir_bitqueue_add(ctx->iter_worklist, insn->op1); |
2013 | 0 | insn->op1 = insn->op2; |
2014 | 0 | insn->op2 = insn->op3; |
2015 | 0 | insn->op3 = IR_UNUSED; |
2016 | 0 | } else if (cond->op1 == insn->op3 && cond->op2 == insn->op1) { |
2017 | 0 | insn->op = (cond->op == IR_LT || cond->op == IR_LE) ? IR_MAX : IR_MIN; |
2018 | 0 | ir_use_list_remove_one(ctx, insn->op1, ref); |
2019 | 0 | ir_bitqueue_add(ctx->iter_worklist, insn->op1); |
2020 | 0 | insn->op1 = insn->op2; |
2021 | 0 | insn->op2 = insn->op3; |
2022 | 0 | insn->op3 = IR_UNUSED; |
2023 | 0 | } |
2024 | 0 | } |
2025 | 0 | } else { |
2026 | 0 | IR_ASSERT(IR_IS_TYPE_UNSIGNED(type)); |
2027 | 0 | ir_insn *cond = &ctx->ir_base[insn->op1]; |
2028 | 0 | if (cond->op == IR_ULT || cond->op == IR_ULE || cond->op == IR_UGT || cond->op == IR_UGE) { |
2029 | 0 | if (cond->op1 == insn->op2 && cond->op2 == insn->op3) { |
2030 | 0 | insn->op = (cond->op == IR_ULT || cond->op == IR_ULE) ? IR_MIN : IR_MAX; |
2031 | 0 | ir_use_list_remove_one(ctx, insn->op1, ref); |
2032 | 0 | ir_bitqueue_add(ctx->iter_worklist, insn->op1); |
2033 | 0 | insn->op1 = insn->op2; |
2034 | 0 | insn->op2 = insn->op3; |
2035 | 0 | insn->op3 = IR_UNUSED; |
2036 | 0 | } else if (cond->op1 == insn->op3 && cond->op2 == insn->op1) { |
2037 | 0 | insn->op = (cond->op == IR_ULT || cond->op == IR_ULE) ? IR_MAX : IR_MIN; |
2038 | 0 | ir_use_list_remove_one(ctx, insn->op1, ref); |
2039 | 0 | ir_bitqueue_add(ctx->iter_worklist, insn->op1); |
2040 | 0 | insn->op1 = insn->op2; |
2041 | 0 | insn->op2 = insn->op3; |
2042 | 0 | insn->op3 = IR_UNUSED; |
2043 | 0 | } |
2044 | 0 | } |
2045 | 0 | } |
2046 | 0 | return ref; |
2047 | 0 | case IR_PHI: |
2048 | 0 | for (count = 2, n = insn->inputs_count - 1; n > 0; count++, n--) { |
2049 | 0 | input = ir_get_op(ctx, ref, count); |
2050 | 0 | if (input != ref) { |
2051 | 0 | op = ir_promote_i2i(ctx, type, input, ref); |
2052 | 0 | ir_set_op(ctx, ref, count, op); |
2053 | 0 | } |
2054 | 0 | } |
2055 | 0 | insn = &ctx->ir_base[ref]; |
2056 | 0 | insn->type = type; |
2057 | 0 | return ref; |
2058 | 0 | default: |
2059 | 0 | break; |
2060 | 0 | } |
2061 | 0 | } |
2062 | 0 | IR_ASSERT(0); |
2063 | 0 | return ref; |
2064 | 0 | } |
2065 | | |
2066 | | static ir_ref ir_ext_const(ir_ctx *ctx, const ir_insn *val_insn, ir_op op, ir_type type) |
2067 | 0 | { |
2068 | 0 | ir_val new_val; |
2069 | |
|
2070 | 0 | switch (val_insn->type) { |
2071 | 0 | default: |
2072 | 0 | IR_ASSERT(0); |
2073 | 0 | case IR_I8: |
2074 | 0 | case IR_U8: |
2075 | 0 | case IR_BOOL: |
2076 | 0 | case IR_CHAR: |
2077 | 0 | if (op == IR_SEXT) { |
2078 | 0 | new_val.i64 = (int64_t)val_insn->val.i8; |
2079 | 0 | } else { |
2080 | 0 | new_val.u64 = (uint64_t)val_insn->val.u8; |
2081 | 0 | } |
2082 | 0 | break; |
2083 | 0 | case IR_I16: |
2084 | 0 | case IR_U16: |
2085 | 0 | if (op == IR_SEXT) { |
2086 | 0 | new_val.i64 = (int64_t)val_insn->val.i16; |
2087 | 0 | } else { |
2088 | 0 | new_val.u64 = (uint64_t)val_insn->val.u16; |
2089 | 0 | } |
2090 | 0 | break; |
2091 | 0 | case IR_I32: |
2092 | 0 | case IR_U32: |
2093 | 0 | if (op == IR_SEXT) { |
2094 | 0 | new_val.i64 = (int64_t)val_insn->val.i32; |
2095 | 0 | } else { |
2096 | 0 | new_val.u64 = (uint64_t)val_insn->val.u32; |
2097 | 0 | } |
2098 | 0 | break; |
2099 | 0 | } |
2100 | 0 | return ir_const(ctx, new_val, type); |
2101 | 0 | } |
2102 | | |
2103 | | static ir_ref ir_ext_ref(ir_ctx *ctx, ir_ref var_ref, ir_ref src_ref, ir_op op, ir_type type) |
2104 | 0 | { |
2105 | 0 | uint32_t optx = IR_OPTX(op, type, 1); |
2106 | 0 | ir_ref ref; |
2107 | |
|
2108 | 0 | if (!IR_IS_CONST_REF(src_ref)) { |
2109 | 0 | ref = ir_iter_find_cse1(ctx, optx, src_ref); |
2110 | 0 | if (ref) { |
2111 | 0 | ir_use_list_add(ctx, ref, var_ref); |
2112 | 0 | if (!IR_IS_CONST_REF(src_ref)) { |
2113 | 0 | ir_use_list_remove_one(ctx, src_ref, var_ref); |
2114 | 0 | } |
2115 | 0 | ir_bitqueue_add(ctx->iter_worklist, ref); |
2116 | 0 | return ref; |
2117 | 0 | } |
2118 | 0 | } |
2119 | | |
2120 | 0 | ref = ir_emit1(ctx, optx, src_ref); |
2121 | 0 | ir_use_list_add(ctx, ref, var_ref); |
2122 | 0 | if (!IR_IS_CONST_REF(src_ref)) { |
2123 | 0 | ir_use_list_replace_one(ctx, src_ref, var_ref, ref); |
2124 | 0 | } |
2125 | 0 | ir_bitqueue_grow(ctx->iter_worklist, ref + 1); |
2126 | 0 | ir_bitqueue_add(ctx->iter_worklist, ref); |
2127 | 0 | return ref; |
2128 | 0 | } |
2129 | | |
2130 | | static uint32_t _ir_estimated_control(const ir_ctx *ctx, ir_ref val, ir_ref loop) |
2131 | 6.04k | { |
2132 | 6.04k | const ir_insn *insn; |
2133 | 6.04k | const ir_ref *p; |
2134 | 6.04k | ir_ref n, input, result, ctrl; |
2135 | | |
2136 | 6.04k | if (val <= 0) { /* constant or IR_UNUSED */ |
2137 | 6.04k | return 1; /* IR_START */ |
2138 | 6.04k | } |
2139 | | |
2140 | 0 | insn = &ctx->ir_base[val]; |
2141 | 0 | if (ir_op_flags[insn->op] & (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM)) { |
2142 | 0 | return val; |
2143 | 0 | } |
2144 | | |
2145 | 0 | IR_ASSERT(ir_op_flags[insn->op] & IR_OP_FLAG_DATA); |
2146 | 0 | if (IR_OPND_KIND(ir_op_flags[insn->op], 1) == IR_OPND_CONTROL_DEP) { |
2147 | 0 | return insn->op1; |
2148 | 0 | } |
2149 | | |
2150 | 0 | n = insn->inputs_count; |
2151 | 0 | p = insn->ops + 1; |
2152 | |
|
2153 | 0 | result = 1; |
2154 | 0 | for (; n > 0; p++, n--) { |
2155 | 0 | input = *p; |
2156 | 0 | ctrl = _ir_estimated_control(ctx, input, loop); |
2157 | 0 | if (ctrl >= loop) return ctrl; |
2158 | 0 | if (ctrl > result) { // TODO: check dominance depth instead of order |
2159 | 0 | result = ctrl; |
2160 | 0 | } |
2161 | 0 | } |
2162 | 0 | return result; |
2163 | 0 | } |
2164 | | |
2165 | | static bool ir_is_loop_invariant(const ir_ctx *ctx, ir_ref ref, ir_ref loop) |
2166 | 6.04k | { |
2167 | 6.04k | ref = _ir_estimated_control(ctx, ref, loop); |
2168 | 6.04k | return ref < loop; // TODO: check dominance instead of order |
2169 | 6.04k | } |
2170 | | |
2171 | | static bool ir_is_cheaper_ext(const ir_ctx *ctx, ir_ref ref, ir_ref loop, ir_ref ext_ref, ir_op op) |
2172 | 0 | { |
2173 | 0 | if (IR_IS_CONST_REF(ref)) { |
2174 | 0 | return 1; |
2175 | 0 | } else { |
2176 | 0 | const ir_insn *insn = &ctx->ir_base[ref]; |
2177 | |
|
2178 | 0 | if (insn->op == IR_LOAD) { |
2179 | 0 | if (ir_is_loop_invariant(ctx, ref, loop)) { |
2180 | 0 | return 1; |
2181 | 0 | } else { |
2182 | | /* ZEXT(LOAD(_, _)) costs the same as LOAD(_, _) */ |
2183 | 0 | if (ctx->use_lists[ref].count == 2) { |
2184 | 0 | return 1; |
2185 | 0 | } else if (ctx->use_lists[ref].count == 3) { |
2186 | 0 | ir_use_list *use_list = &ctx->use_lists[ref]; |
2187 | 0 | ir_ref *p, n, use; |
2188 | |
|
2189 | 0 | for (p = &ctx->use_edges[use_list->refs], n = use_list->count; n > 0; p++, n--) { |
2190 | 0 | use = *p; |
2191 | 0 | if (use != ext_ref) { |
2192 | 0 | const ir_insn *use_insn = &ctx->ir_base[use]; |
2193 | |
|
2194 | 0 | if (use_insn->op != op |
2195 | 0 | && (!(ir_op_flags[use_insn->op] & (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM)) |
2196 | 0 | || use_insn->op1 != ref)) { |
2197 | 0 | return 0; |
2198 | 0 | } |
2199 | 0 | } |
2200 | 0 | } |
2201 | 0 | return 1; |
2202 | 0 | } |
2203 | 0 | } |
2204 | 0 | return 0; |
2205 | 0 | } else { |
2206 | 0 | return ir_is_loop_invariant(ctx, ref, loop); |
2207 | 0 | } |
2208 | 0 | } |
2209 | 0 | } |
2210 | | |
2211 | | static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref phi_ref, ir_ref op_ref) |
2212 | 0 | { |
2213 | 0 | ir_op op = ctx->ir_base[ext_ref].op; |
2214 | 0 | ir_type type = ctx->ir_base[ext_ref].type; |
2215 | 0 | ir_insn *phi_insn; |
2216 | 0 | ir_use_list *use_list; |
2217 | 0 | ir_ref n, *p, use, ext_ref_2 = IR_UNUSED; |
2218 | | |
2219 | | /* Check if we may change the type of the induction variable */ |
2220 | 0 | use_list = &ctx->use_lists[phi_ref]; |
2221 | 0 | n = use_list->count; |
2222 | 0 | if (n > 1) { |
2223 | 0 | for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) { |
2224 | 0 | use = *p; |
2225 | 0 | if (use == op_ref || use == ext_ref) { |
2226 | 0 | continue; |
2227 | 0 | } else { |
2228 | 0 | const ir_insn *use_insn = &ctx->ir_base[use]; |
2229 | |
|
2230 | 0 | if (use_insn->op >= IR_EQ && use_insn->op <= IR_UGT) { |
2231 | 0 | if (use_insn->op1 == phi_ref) { |
2232 | 0 | if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op2].type)) { |
2233 | 0 | return 0; |
2234 | 0 | } |
2235 | 0 | if (ir_is_cheaper_ext(ctx, use_insn->op2, ctx->ir_base[phi_ref].op1, ext_ref, op)) { |
2236 | 0 | continue; |
2237 | 0 | } |
2238 | 0 | } else if (use_insn->op2 == phi_ref) { |
2239 | 0 | if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op1].type)) { |
2240 | 0 | return 0; |
2241 | 0 | } |
2242 | 0 | if (ir_is_cheaper_ext(ctx, use_insn->op1, ctx->ir_base[phi_ref].op1, ext_ref, op)) { |
2243 | 0 | continue; |
2244 | 0 | } |
2245 | 0 | } |
2246 | 0 | return 0; |
2247 | 0 | } else if (use_insn->op == IR_IF) { |
2248 | 0 | continue; |
2249 | 0 | } else if (!ext_ref_2 && use_insn->op == op && use_insn->type == type) { |
2250 | 0 | ext_ref_2 = use; |
2251 | 0 | continue; |
2252 | 0 | } else { |
2253 | 0 | return 0; |
2254 | 0 | } |
2255 | 0 | } |
2256 | 0 | } |
2257 | 0 | } |
2258 | | |
2259 | 0 | use_list = &ctx->use_lists[op_ref]; |
2260 | 0 | n = use_list->count; |
2261 | 0 | if (n > 1) { |
2262 | 0 | for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) { |
2263 | 0 | use = *p; |
2264 | 0 | if (use == phi_ref || use == ext_ref) { |
2265 | 0 | continue; |
2266 | 0 | } else { |
2267 | 0 | const ir_insn *use_insn = &ctx->ir_base[use]; |
2268 | |
|
2269 | 0 | if (use_insn->op >= IR_EQ && use_insn->op <= IR_UGT) { |
2270 | 0 | if (use_insn->op1 == op_ref) { |
2271 | 0 | if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op2].type)) { |
2272 | 0 | return 0; |
2273 | 0 | } |
2274 | 0 | if (ir_is_cheaper_ext(ctx, use_insn->op2, ctx->ir_base[phi_ref].op1, ext_ref, op)) { |
2275 | 0 | continue; |
2276 | 0 | } |
2277 | 0 | } else if (use_insn->op2 == op_ref) { |
2278 | 0 | if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op1].type)) { |
2279 | 0 | return 0; |
2280 | 0 | } |
2281 | 0 | if (ir_is_cheaper_ext(ctx, use_insn->op1, ctx->ir_base[phi_ref].op1, ext_ref, op)) { |
2282 | 0 | continue; |
2283 | 0 | } |
2284 | 0 | } |
2285 | 0 | return 0; |
2286 | 0 | } else if (use_insn->op == IR_IF) { |
2287 | 0 | continue; |
2288 | 0 | } else if (!ext_ref_2 && use_insn->op == op && use_insn->type == type) { |
2289 | 0 | ext_ref_2 = use; |
2290 | 0 | continue; |
2291 | 0 | } else { |
2292 | 0 | return 0; |
2293 | 0 | } |
2294 | 0 | } |
2295 | 0 | } |
2296 | 0 | } |
2297 | | |
2298 | 0 | for (n = 0; n < ctx->use_lists[phi_ref].count; n++) { |
2299 | | /* "use_lists" may be reallocated by ir_ext_ref() */ |
2300 | 0 | use = ctx->use_edges[ctx->use_lists[phi_ref].refs + n]; |
2301 | 0 | if (use == ext_ref) { |
2302 | 0 | continue; |
2303 | 0 | } else { |
2304 | 0 | ir_insn *use_insn = &ctx->ir_base[use]; |
2305 | |
|
2306 | 0 | if (use_insn->op == IR_IF) { |
2307 | 0 | continue; |
2308 | 0 | } else if (use_insn->op == op) { |
2309 | 0 | IR_ASSERT(ext_ref_2 == use); |
2310 | 0 | continue; |
2311 | 0 | } |
2312 | 0 | IR_ASSERT(((use_insn->op >= IR_EQ && use_insn->op <= IR_UGT) |
2313 | 0 | || use_insn->op == IR_ADD || use_insn->op == IR_SUB || use_insn->op == IR_MUL) |
2314 | 0 | && (use_insn->op1 == phi_ref || use_insn->op2 == phi_ref)); |
2315 | 0 | if (use_insn->op1 != phi_ref) { |
2316 | 0 | if (IR_IS_CONST_REF(use_insn->op1) |
2317 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[use_insn->op1].op)) { |
2318 | 0 | ctx->ir_base[use].op1 = ir_ext_const(ctx, &ctx->ir_base[use_insn->op1], op, type); |
2319 | 0 | } else { |
2320 | 0 | ir_ref tmp = ir_ext_ref(ctx, use, use_insn->op1, op, type); |
2321 | 0 | use_insn = &ctx->ir_base[use]; |
2322 | 0 | use_insn->op1 = tmp; |
2323 | 0 | } |
2324 | 0 | ir_bitqueue_add(ctx->iter_worklist, use); |
2325 | 0 | } |
2326 | 0 | if (use_insn->op2 != phi_ref) { |
2327 | 0 | if (IR_IS_CONST_REF(use_insn->op2) |
2328 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[use_insn->op2].op)) { |
2329 | 0 | ctx->ir_base[use].op2 = ir_ext_const(ctx, &ctx->ir_base[use_insn->op2], op, type); |
2330 | 0 | } else { |
2331 | 0 | ir_ref tmp = ir_ext_ref(ctx, use, use_insn->op2, op, type); |
2332 | 0 | use_insn = &ctx->ir_base[use]; |
2333 | 0 | use_insn->op2 = tmp; |
2334 | 0 | } |
2335 | 0 | ir_bitqueue_add(ctx->iter_worklist, use); |
2336 | 0 | } |
2337 | 0 | } |
2338 | 0 | } |
2339 | | |
2340 | 0 | if (ctx->use_lists[op_ref].count > 1) { |
2341 | 0 | for (n = 0; n < ctx->use_lists[op_ref].count; n++) { |
2342 | | /* "use_lists" may be reallocated by ir_ext_ref() */ |
2343 | 0 | use = ctx->use_edges[ctx->use_lists[op_ref].refs + n]; |
2344 | 0 | if (use == ext_ref || use == phi_ref) { |
2345 | 0 | continue; |
2346 | 0 | } else { |
2347 | 0 | ir_insn *use_insn = &ctx->ir_base[use]; |
2348 | |
|
2349 | 0 | if (use_insn->op == IR_IF) { |
2350 | 0 | continue; |
2351 | 0 | } else if (use_insn->op == op) { |
2352 | 0 | IR_ASSERT(ext_ref_2 == use); |
2353 | 0 | continue; |
2354 | 0 | } |
2355 | 0 | IR_ASSERT(use_insn->op >= IR_EQ && use_insn->op <= IR_UGT); |
2356 | 0 | if (use_insn->op1 != op_ref) { |
2357 | 0 | if (IR_IS_CONST_REF(use_insn->op1) |
2358 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[use_insn->op1].op)) { |
2359 | 0 | ctx->ir_base[use].op1 = ir_ext_const(ctx, &ctx->ir_base[use_insn->op1], op, type); |
2360 | 0 | } else { |
2361 | 0 | ir_ref tmp = ir_ext_ref(ctx, use, use_insn->op1, op, type); |
2362 | 0 | use_insn = &ctx->ir_base[use]; |
2363 | 0 | use_insn->op1 = tmp; |
2364 | 0 | } |
2365 | 0 | ir_bitqueue_add(ctx->iter_worklist, use); |
2366 | 0 | } |
2367 | 0 | if (use_insn->op2 != op_ref) { |
2368 | 0 | if (IR_IS_CONST_REF(use_insn->op2) |
2369 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[use_insn->op2].op)) { |
2370 | 0 | ctx->ir_base[use].op2 = ir_ext_const(ctx, &ctx->ir_base[use_insn->op2], op, type); |
2371 | 0 | } else { |
2372 | 0 | ir_ref tmp = ir_ext_ref(ctx, use, use_insn->op2, op, type); |
2373 | 0 | use_insn = &ctx->ir_base[use]; |
2374 | 0 | use_insn->op2 = tmp; |
2375 | 0 | } |
2376 | 0 | ir_bitqueue_add(ctx->iter_worklist, use); |
2377 | 0 | } |
2378 | 0 | } |
2379 | 0 | } |
2380 | 0 | } |
2381 | | |
2382 | 0 | ir_iter_replace_insn(ctx, ext_ref, ctx->ir_base[ext_ref].op1); |
2383 | |
|
2384 | 0 | if (ext_ref_2) { |
2385 | 0 | ir_iter_replace_insn(ctx, ext_ref_2, ctx->ir_base[ext_ref_2].op1); |
2386 | 0 | } |
2387 | |
|
2388 | 0 | ctx->ir_base[op_ref].type = type; |
2389 | |
|
2390 | 0 | phi_insn = &ctx->ir_base[phi_ref]; |
2391 | 0 | phi_insn->type = type; |
2392 | 0 | if (IR_IS_CONST_REF(phi_insn->op2) |
2393 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[phi_insn->op2].op)) { |
2394 | 0 | ctx->ir_base[phi_ref].op2 = ir_ext_const(ctx, &ctx->ir_base[phi_insn->op2], op, type); |
2395 | 0 | } else { |
2396 | 0 | ir_ref tmp = ir_ext_ref(ctx, phi_ref, phi_insn->op2, op, type); |
2397 | 0 | ctx->ir_base[phi_ref].op2 = tmp; |
2398 | 0 | } |
2399 | |
|
2400 | 0 | return 1; |
2401 | 0 | } |
2402 | | |
2403 | | static bool ir_try_promote_ext(ir_ctx *ctx, ir_ref ext_ref, ir_insn *insn) |
2404 | 0 | { |
2405 | 0 | ir_ref ref = insn->op1; |
2406 | | |
2407 | | /* Check for simple induction variable in the form: x2 = PHI(loop, x1, x3); x3 = ADD(x2, _); */ |
2408 | 0 | insn = &ctx->ir_base[ref]; |
2409 | 0 | if (insn->op == IR_PHI |
2410 | 0 | && insn->inputs_count == 3 /* (2 values) */ |
2411 | 0 | && ctx->ir_base[insn->op1].op == IR_LOOP_BEGIN) { |
2412 | 0 | ir_ref op_ref = insn->op3; |
2413 | 0 | ir_insn *op_insn = &ctx->ir_base[op_ref]; |
2414 | |
|
2415 | 0 | if (op_insn->op == IR_ADD || op_insn->op == IR_SUB || op_insn->op == IR_MUL) { |
2416 | 0 | if (op_insn->op1 == ref) { |
2417 | 0 | if (ir_is_loop_invariant(ctx, op_insn->op2, insn->op1)) { |
2418 | 0 | return ir_try_promote_induction_var_ext(ctx, ext_ref, ref, op_ref); |
2419 | 0 | } |
2420 | 0 | } else if (op_insn->op2 == ref) { |
2421 | 0 | if (ir_is_loop_invariant(ctx, op_insn->op1, insn->op1)) { |
2422 | 0 | return ir_try_promote_induction_var_ext(ctx, ext_ref, ref, op_ref); |
2423 | 0 | } |
2424 | 0 | } |
2425 | 0 | } |
2426 | 0 | } else if (insn->op == IR_ADD || insn->op == IR_SUB || insn->op == IR_MUL) { |
2427 | 0 | if (!IR_IS_CONST_REF(insn->op1) |
2428 | 0 | && ctx->ir_base[insn->op1].op == IR_PHI |
2429 | 0 | && ctx->ir_base[insn->op1].inputs_count == 3 /* (2 values) */ |
2430 | 0 | && ctx->ir_base[insn->op1].op3 == ref |
2431 | 0 | && ctx->ir_base[ctx->ir_base[insn->op1].op1].op == IR_LOOP_BEGIN |
2432 | 0 | && ir_is_loop_invariant(ctx, insn->op2, ctx->ir_base[insn->op1].op1)) { |
2433 | 0 | return ir_try_promote_induction_var_ext(ctx, ext_ref, insn->op1, ref); |
2434 | 0 | } else if (!IR_IS_CONST_REF(insn->op2) |
2435 | 0 | && ctx->ir_base[insn->op2].op == IR_PHI |
2436 | 0 | && ctx->ir_base[insn->op2].inputs_count == 3 /* (2 values) */ |
2437 | 0 | && ctx->ir_base[insn->op2].op3 == ref |
2438 | 0 | && ctx->ir_base[ctx->ir_base[insn->op2].op1].op == IR_LOOP_BEGIN |
2439 | 0 | && ir_is_loop_invariant(ctx, insn->op1, ctx->ir_base[insn->op2].op1)) { |
2440 | 0 | return ir_try_promote_induction_var_ext(ctx, ext_ref, insn->op2, ref); |
2441 | 0 | } |
2442 | 0 | } else if (insn->op == IR_COND |
2443 | 0 | && IR_IS_CONST_REF(insn->op2) |
2444 | 0 | && IR_IS_CONST_REF(insn->op3) |
2445 | 0 | && ctx->use_lists[ref].count == 1) { |
2446 | 0 | IR_ASSERT(!IR_IS_SYM_CONST(ctx->ir_base[insn->op2].op)); |
2447 | 0 | IR_ASSERT(!IR_IS_SYM_CONST(ctx->ir_base[insn->op3].op)); |
2448 | |
|
2449 | 0 | ir_op op = ctx->ir_base[ext_ref].op; |
2450 | 0 | ir_type type = ctx->ir_base[ext_ref].type; |
2451 | |
|
2452 | 0 | insn->type = type; |
2453 | 0 | insn->op2 = ir_ext_const(ctx, &ctx->ir_base[insn->op2], op, type);; |
2454 | 0 | insn->op3 = ir_ext_const(ctx, &ctx->ir_base[insn->op3], op, type);; |
2455 | 0 | ir_iter_replace_insn(ctx, ext_ref, ref); |
2456 | 0 | } |
2457 | | |
2458 | 0 | return 0; |
2459 | 0 | } |
2460 | | |
2461 | | static void ir_get_true_false_refs(const ir_ctx *ctx, ir_ref if_ref, ir_ref *if_true_ref, ir_ref *if_false_ref) |
2462 | 10 | { |
2463 | 10 | ir_use_list *use_list = &ctx->use_lists[if_ref]; |
2464 | 10 | ir_ref *p = &ctx->use_edges[use_list->refs]; |
2465 | | |
2466 | 10 | IR_ASSERT(use_list->count == 2); |
2467 | 10 | if (ctx->ir_base[*p].op == IR_IF_TRUE) { |
2468 | 9 | IR_ASSERT(ctx->ir_base[*(p + 1)].op == IR_IF_FALSE); |
2469 | 9 | *if_true_ref = *p; |
2470 | 9 | *if_false_ref = *(p + 1); |
2471 | 9 | } else { |
2472 | 1 | IR_ASSERT(ctx->ir_base[*p].op == IR_IF_FALSE); |
2473 | 1 | IR_ASSERT(ctx->ir_base[*(p + 1)].op == IR_IF_TRUE); |
2474 | 1 | *if_false_ref = *p; |
2475 | 1 | *if_true_ref = *(p + 1); |
2476 | 1 | } |
2477 | 10 | } |
2478 | | |
2479 | | static void ir_merge_blocks(ir_ctx *ctx, ir_ref end, ir_ref begin) |
2480 | 1.17k | { |
2481 | 1.17k | ir_ref prev, next; |
2482 | 1.17k | ir_use_list *use_list; |
2483 | | |
2484 | 1.17k | IR_ASSERT(ctx->ir_base[begin].op == IR_BEGIN); |
2485 | 1.17k | IR_ASSERT(ctx->ir_base[end].op == IR_END); |
2486 | 1.17k | IR_ASSERT(ctx->ir_base[begin].op1 == end); |
2487 | 1.17k | IR_ASSERT(ctx->use_lists[end].count == 1); |
2488 | | |
2489 | 1.17k | prev = ctx->ir_base[end].op1; |
2490 | | |
2491 | 1.17k | use_list = &ctx->use_lists[begin]; |
2492 | 1.17k | IR_ASSERT(use_list->count == 1); |
2493 | 1.17k | next = ctx->use_edges[use_list->refs]; |
2494 | | |
2495 | | /* remove BEGIN and END */ |
2496 | 1.17k | MAKE_NOP(&ctx->ir_base[begin]); CLEAR_USES(begin); |
2497 | 1.17k | MAKE_NOP(&ctx->ir_base[end]); CLEAR_USES(end); |
2498 | | |
2499 | | /* connect their predecessor and successor */ |
2500 | 1.17k | ctx->ir_base[next].op1 = prev; |
2501 | 1.17k | ir_use_list_replace_one(ctx, prev, end, next); |
2502 | | |
2503 | 1.17k | if (ctx->ir_base[prev].op == IR_BEGIN || ctx->ir_base[prev].op == IR_MERGE) { |
2504 | 229 | ir_bitqueue_add(ctx->iter_worklist, prev); |
2505 | 229 | } |
2506 | 1.17k | } |
2507 | | |
2508 | | static void ir_remove_unused_vars(ir_ctx *ctx, ir_ref start, ir_ref end) |
2509 | 0 | { |
2510 | 0 | ir_use_list *use_list = &ctx->use_lists[start]; |
2511 | 0 | ir_ref *p, use, n = use_list->count; |
2512 | |
|
2513 | 0 | for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) { |
2514 | 0 | use = *p; |
2515 | 0 | if (use != end) { |
2516 | 0 | ir_insn *use_insn = &ctx->ir_base[use]; |
2517 | 0 | IR_ASSERT(use_insn->op == IR_VAR); |
2518 | 0 | IR_ASSERT(ctx->use_lists[use].count == 0); |
2519 | 0 | MAKE_NOP(use_insn); |
2520 | 0 | } |
2521 | 0 | } |
2522 | 0 | } |
2523 | | |
2524 | | static bool ir_try_remove_empty_diamond(ir_ctx *ctx, ir_ref ref, ir_insn *insn) |
2525 | 54.9k | { |
2526 | 54.9k | if (insn->inputs_count == 2) { |
2527 | 31.3k | ir_ref end1_ref = insn->op1, end2_ref = insn->op2; |
2528 | 31.3k | ir_insn *end1 = &ctx->ir_base[end1_ref]; |
2529 | 31.3k | ir_insn *end2 = &ctx->ir_base[end2_ref]; |
2530 | | |
2531 | 31.3k | if (end1->op != IR_END || end2->op != IR_END) { |
2532 | 0 | return 0; |
2533 | 0 | } |
2534 | | |
2535 | 31.3k | ir_ref start1_ref = end1->op1, start2_ref = end2->op1; |
2536 | 31.3k | ir_insn *start1 = &ctx->ir_base[start1_ref]; |
2537 | 31.3k | ir_insn *start2 = &ctx->ir_base[start2_ref]; |
2538 | | |
2539 | 31.3k | if (start1->op1 != start2->op1) { |
2540 | 31.3k | return 0; |
2541 | 31.3k | } |
2542 | | |
2543 | 0 | ir_ref root_ref = start1->op1; |
2544 | 0 | ir_insn *root = &ctx->ir_base[root_ref]; |
2545 | |
|
2546 | 0 | if (root->op != IR_IF |
2547 | 0 | && !(root->op == IR_SWITCH && ctx->use_lists[root_ref].count == 2)) { |
2548 | 0 | return 0; |
2549 | 0 | } |
2550 | | |
2551 | | /* Empty Diamond |
2552 | | * |
2553 | | * prev prev |
2554 | | * | condition | condition |
2555 | | * | / | |
2556 | | * IF | |
2557 | | * | \ | |
2558 | | * | +-----+ | |
2559 | | * | IF_FALSE | |
2560 | | * IF_TRUE | => | |
2561 | | * | END | |
2562 | | * END / | |
2563 | | * | +---+ | |
2564 | | * | / | |
2565 | | * MERGE | |
2566 | | * | | |
2567 | | * next next |
2568 | | */ |
2569 | | |
2570 | 0 | ir_ref next_ref = ctx->use_edges[ctx->use_lists[ref].refs]; |
2571 | 0 | ir_insn *next = &ctx->ir_base[next_ref]; |
2572 | |
|
2573 | 0 | if (ctx->use_lists[start1_ref].count != 1) { |
2574 | 0 | ir_remove_unused_vars(ctx, start1_ref, end1_ref); |
2575 | 0 | } |
2576 | 0 | if (ctx->use_lists[start2_ref].count != 1) { |
2577 | 0 | ir_remove_unused_vars(ctx, start2_ref, end2_ref); |
2578 | 0 | } |
2579 | |
|
2580 | 0 | next->op1 = root->op1; |
2581 | 0 | ir_use_list_replace_one(ctx, root->op1, root_ref, next_ref); |
2582 | 0 | if (!IR_IS_CONST_REF(root->op2)) { |
2583 | 0 | ir_use_list_remove_one(ctx, root->op2, root_ref); |
2584 | 0 | if (ir_is_dead(ctx, root->op2)) { |
2585 | 0 | ir_bitqueue_add(ctx->iter_worklist, root->op2); |
2586 | 0 | } |
2587 | 0 | } |
2588 | |
|
2589 | 0 | MAKE_NOP(root); CLEAR_USES(root_ref); |
2590 | 0 | MAKE_NOP(start1); CLEAR_USES(start1_ref); |
2591 | 0 | MAKE_NOP(start2); CLEAR_USES(start2_ref); |
2592 | 0 | MAKE_NOP(end1); CLEAR_USES(end1_ref); |
2593 | 0 | MAKE_NOP(end2); CLEAR_USES(end2_ref); |
2594 | 0 | MAKE_NOP(insn); CLEAR_USES(ref); |
2595 | |
|
2596 | 0 | if (ctx->ir_base[next->op1].op == IR_BEGIN || ctx->ir_base[next->op1].op == IR_MERGE) { |
2597 | 0 | ir_bitqueue_add(ctx->iter_worklist, next->op1); |
2598 | 0 | } |
2599 | |
|
2600 | 0 | return 1; |
2601 | 23.5k | } else { |
2602 | 23.5k | ir_ref i, count = insn->inputs_count, *ops = insn->ops + 1; |
2603 | 23.5k | ir_ref root_ref = IR_UNUSED; |
2604 | | |
2605 | 23.5k | for (i = 0; i < count; i++) { |
2606 | 23.5k | ir_ref end_ref, start_ref; |
2607 | 23.5k | ir_insn *end, *start; |
2608 | | |
2609 | 23.5k | end_ref = ops[i]; |
2610 | 23.5k | end = &ctx->ir_base[end_ref]; |
2611 | 23.5k | if (end->op != IR_END) { |
2612 | 0 | return 0; |
2613 | 0 | } |
2614 | 23.5k | start_ref = end->op1; |
2615 | 23.5k | start = &ctx->ir_base[start_ref]; |
2616 | 23.5k | if (start->op != IR_CASE_VAL && start->op != IR_CASE_RANGE && start->op != IR_CASE_DEFAULT) { |
2617 | 23.5k | return 0; |
2618 | 23.5k | } |
2619 | 4 | if (ctx->use_lists[start_ref].count != 1) { |
2620 | 0 | ir_remove_unused_vars(ctx, start_ref, end_ref); |
2621 | 0 | } |
2622 | 4 | if (!root_ref) { |
2623 | 4 | root_ref = start->op1; |
2624 | 4 | if (ctx->use_lists[root_ref].count != count) { |
2625 | 4 | return 0; |
2626 | 4 | } |
2627 | 4 | } else if (start->op1 != root_ref) { |
2628 | 0 | return 0; |
2629 | 0 | } |
2630 | 4 | } |
2631 | | |
2632 | | /* Empty N-Diamond */ |
2633 | 0 | ir_ref next_ref = ctx->use_edges[ctx->use_lists[ref].refs]; |
2634 | 0 | ir_insn *next = &ctx->ir_base[next_ref]; |
2635 | 0 | ir_insn *root = &ctx->ir_base[root_ref]; |
2636 | |
|
2637 | 0 | next->op1 = root->op1; |
2638 | 0 | ir_use_list_replace_one(ctx, root->op1, root_ref, next_ref); |
2639 | |
|
2640 | 0 | if (!IR_IS_CONST_REF(root->op2)) { |
2641 | 0 | ir_use_list_remove_one(ctx, root->op2, root_ref); |
2642 | 0 | if (ir_is_dead(ctx, root->op2)) { |
2643 | 0 | ir_bitqueue_add(ctx->iter_worklist, root->op2); |
2644 | 0 | } |
2645 | 0 | } |
2646 | |
|
2647 | 0 | MAKE_NOP(root); CLEAR_USES(root_ref); |
2648 | |
|
2649 | 0 | for (i = 0; i < count; i++) { |
2650 | 0 | ir_ref end_ref = ops[i]; |
2651 | 0 | ir_insn *end = &ctx->ir_base[end_ref]; |
2652 | 0 | ir_ref start_ref = end->op1; |
2653 | 0 | ir_insn *start = &ctx->ir_base[start_ref]; |
2654 | |
|
2655 | 0 | MAKE_NOP(start); CLEAR_USES(start_ref); |
2656 | 0 | MAKE_NOP(end); CLEAR_USES(end_ref); |
2657 | 0 | } |
2658 | |
|
2659 | 0 | MAKE_NOP(insn); CLEAR_USES(ref); |
2660 | |
|
2661 | 0 | if (ctx->ir_base[next->op1].op == IR_BEGIN || ctx->ir_base[next->op1].op == IR_MERGE) { |
2662 | 0 | ir_bitqueue_add(ctx->iter_worklist, next->op1); |
2663 | 0 | } |
2664 | |
|
2665 | 0 | return 1; |
2666 | 23.5k | } |
2667 | 54.9k | } |
2668 | | |
2669 | | static bool ir_is_zero(const ir_ctx *ctx, ir_ref ref) |
2670 | 0 | { |
2671 | 0 | return IR_IS_CONST_REF(ref) |
2672 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[ref].op) |
2673 | 0 | && ctx->ir_base[ref].val.u32 == 0; |
2674 | 0 | } |
2675 | | |
2676 | | static bool ir_fix_min_max_const(ir_ctx *ctx, ir_insn *cond, ir_ref ref) |
2677 | 0 | { |
2678 | 0 | if (cond->op == IR_ULE) { |
2679 | | /* (x <= 3 ? 4 : x) => (x < 4 ? 4 : x) => max(x, 4) */ |
2680 | | /* (x <= 3 ? x : 4) => (x < 4 ? x : 4) => min(x, 4) */ |
2681 | 0 | if (!IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op) |
2682 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[ref].op) |
2683 | 0 | && ctx->ir_base[cond->op2].val.u64 == ctx->ir_base[ref].val.u64 - 1 |
2684 | 0 | && ctx->ir_base[cond->op2].type == ctx->ir_base[ref].type) { |
2685 | 0 | cond->op2 = ref; |
2686 | 0 | return 1; |
2687 | 0 | } |
2688 | 0 | } else if (cond->op == IR_UGE) { |
2689 | | /* (x >= 3 ? 2 : x) => (x > 2 ? 2 : x) => min(x, 2) */ |
2690 | | /* (x >= 3 ? x : 2) => (x > 2 ? x : 2) => max(x, 2) */ |
2691 | 0 | if (!IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op) |
2692 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[ref].op) |
2693 | 0 | && ctx->ir_base[cond->op2].val.u64 == ctx->ir_base[ref].val.u64 + 1 |
2694 | 0 | && ctx->ir_base[cond->op2].type == ctx->ir_base[ref].type) { |
2695 | 0 | cond->op2 = ref; |
2696 | 0 | return 1; |
2697 | 0 | } |
2698 | 0 | } else if (cond->op == IR_LE) { |
2699 | | /* (x <= 3 ? 4 : x) => (x < 4 ? 4 : x) => max(x, 4) */ |
2700 | | /* (x <= 3 ? x : 4) => (x < 4 ? x : 4) => min(x, 4) */ |
2701 | 0 | if (!IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op) |
2702 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[ref].op) |
2703 | 0 | && ctx->ir_base[cond->op2].val.u64 == ctx->ir_base[ref].val.u64 - 1 |
2704 | 0 | && ctx->ir_base[cond->op2].type == ctx->ir_base[ref].type) { |
2705 | 0 | cond->op2 = ref; |
2706 | 0 | return 1; |
2707 | 0 | } |
2708 | 0 | } else if (cond->op == IR_GE) { |
2709 | | /* (x >= 3 ? 2 : x) => (x > 2 ? 2 : x) => min(x, 2) */ |
2710 | | /* (x >= 3 ? x : 2) => (x > 2 ? x : 2) => max(x, 2) */ |
2711 | 0 | if (!IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op) |
2712 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[ref].op) |
2713 | 0 | && ctx->ir_base[cond->op2].val.i64 == ctx->ir_base[ref].val.i64 + 1 |
2714 | 0 | && ctx->ir_base[cond->op2].type == ctx->ir_base[ref].type) { |
2715 | 0 | cond->op2 = ref; |
2716 | 0 | return 1; |
2717 | 0 | } |
2718 | 0 | } |
2719 | 0 | return 0; |
2720 | 0 | } |
2721 | | |
2722 | | static bool ir_optimize_phi(ir_ctx *ctx, ir_ref merge_ref, ir_insn *merge, ir_ref ref, ir_insn *insn) |
2723 | 16.1k | { |
2724 | 16.1k | IR_ASSERT(insn->inputs_count == 3); |
2725 | 16.1k | IR_ASSERT(ctx->use_lists[merge_ref].count == 2); |
2726 | | |
2727 | 16.1k | ir_ref end1_ref = merge->op1, end2_ref = merge->op2; |
2728 | 16.1k | ir_insn *end1 = &ctx->ir_base[end1_ref]; |
2729 | 16.1k | ir_insn *end2 = &ctx->ir_base[end2_ref]; |
2730 | | |
2731 | 16.1k | if (end1->op == IR_END && end2->op == IR_END) { |
2732 | 16.1k | ir_ref start1_ref = end1->op1, start2_ref = end2->op1; |
2733 | 16.1k | ir_insn *start1 = &ctx->ir_base[start1_ref]; |
2734 | 16.1k | ir_insn *start2 = &ctx->ir_base[start2_ref]; |
2735 | | |
2736 | 16.1k | if (start1->op1 == start2->op1) { |
2737 | 363 | ir_ref root_ref = start1->op1; |
2738 | 363 | ir_insn *root = &ctx->ir_base[root_ref]; |
2739 | | |
2740 | 363 | if (root->op == IR_IF && !IR_IS_CONST_REF(root->op2)) { |
2741 | 363 | ir_ref cond_ref = root->op2; |
2742 | 363 | ir_insn *cond = &ctx->ir_base[cond_ref]; |
2743 | 363 | ir_type type = insn->type; |
2744 | 363 | bool is_cmp, is_less; |
2745 | | |
2746 | 363 | if (IR_IS_TYPE_FP(type)) { |
2747 | 0 | is_cmp = (cond->op == IR_LT || cond->op == IR_LE || cond->op == IR_GT || cond->op == IR_GE || |
2748 | 0 | cond->op == IR_ULT || cond->op == IR_ULE || cond->op == IR_UGT || cond->op == IR_UGE); |
2749 | 0 | is_less = (cond->op == IR_LT || cond->op == IR_LE || |
2750 | 0 | cond->op == IR_ULT || cond->op == IR_ULE); |
2751 | 363 | } else if (IR_IS_TYPE_SIGNED(type)) { |
2752 | 363 | is_cmp = (cond->op == IR_LT || cond->op == IR_LE || cond->op == IR_GT || cond->op == IR_GE); |
2753 | 363 | is_less = (cond->op == IR_LT || cond->op == IR_LE); |
2754 | 363 | } else { |
2755 | 0 | IR_ASSERT(IR_IS_TYPE_UNSIGNED(type)); |
2756 | 0 | is_cmp = (cond->op == IR_ULT || cond->op == IR_ULE || cond->op == IR_UGT || cond->op == IR_UGE); |
2757 | 0 | is_less = (cond->op == IR_ULT || cond->op == IR_ULE); |
2758 | 0 | } |
2759 | | |
2760 | 363 | if (is_cmp |
2761 | 0 | && ((insn->op2 == cond->op1 |
2762 | 0 | && (insn->op3 == cond->op2 |
2763 | 0 | || (IR_IS_CONST_REF(cond->op2) |
2764 | 0 | && (IR_IS_CONST_REF(insn->op3) |
2765 | 0 | && IR_IS_TYPE_INT(insn->type) |
2766 | 0 | && ir_fix_min_max_const(ctx, cond, insn->op3))))) |
2767 | 0 | || (insn->op3 == cond->op1 |
2768 | 0 | && (insn->op2 == cond->op2 |
2769 | 0 | || (IR_IS_CONST_REF(cond->op2) |
2770 | 0 | && (IR_IS_CONST_REF(insn->op2) |
2771 | 0 | && IR_IS_TYPE_INT(insn->type) |
2772 | 0 | && ir_fix_min_max_const(ctx, cond, insn->op2))))))) { |
2773 | | /* MAX/MIN |
2774 | | * |
2775 | | * prev prev |
2776 | | * | LT(A, B) | |
2777 | | * | / | |
2778 | | * IF | |
2779 | | * | \ | |
2780 | | * | +-----+ | |
2781 | | * | IF_FALSE | |
2782 | | * IF_TRUE | => | |
2783 | | * | END | |
2784 | | * END / | |
2785 | | * | +---+ | |
2786 | | * | / | |
2787 | | * MERGE | |
2788 | | * | \ | |
2789 | | * | PHI(A, B) | MIN(A, B) |
2790 | | * next next |
2791 | | */ |
2792 | 0 | ir_ref next_ref = ctx->use_edges[ctx->use_lists[merge_ref].refs]; |
2793 | 0 | ir_insn *next; |
2794 | |
|
2795 | 0 | if (next_ref == ref) { |
2796 | 0 | next_ref = ctx->use_edges[ctx->use_lists[merge_ref].refs + 1]; |
2797 | 0 | } |
2798 | 0 | next = &ctx->ir_base[next_ref]; |
2799 | |
|
2800 | 0 | if (ctx->use_lists[start1_ref].count != 1) { |
2801 | 0 | ir_remove_unused_vars(ctx, start1_ref, end1_ref); |
2802 | 0 | } |
2803 | 0 | if (ctx->use_lists[start2_ref].count != 1) { |
2804 | 0 | ir_remove_unused_vars(ctx, start2_ref, end2_ref); |
2805 | 0 | } |
2806 | |
|
2807 | 0 | insn->op = ( |
2808 | 0 | (is_less ? cond->op1 : cond->op2) |
2809 | 0 | == |
2810 | 0 | ((start1->op == IR_IF_TRUE) ? insn->op2 : insn->op3) |
2811 | 0 | ) ? IR_MIN : IR_MAX; |
2812 | 0 | insn->inputs_count = 2; |
2813 | 0 | if (insn->op2 > insn->op3) { |
2814 | 0 | insn->op1 = insn->op2; |
2815 | 0 | insn->op2 = insn->op3; |
2816 | 0 | } else { |
2817 | 0 | insn->op1 = insn->op3; |
2818 | 0 | } |
2819 | 0 | insn->op3 = IR_UNUSED; |
2820 | |
|
2821 | 0 | next->op1 = root->op1; |
2822 | 0 | ir_use_list_replace_one(ctx, root->op1, root_ref, next_ref); |
2823 | |
|
2824 | 0 | if (ctx->use_lists[cond_ref].count == 1) { |
2825 | 0 | if (!IR_IS_CONST_REF(insn->op1)) { |
2826 | 0 | ir_use_list_remove_one(ctx, insn->op1, cond_ref); |
2827 | 0 | } |
2828 | 0 | if (!IR_IS_CONST_REF(insn->op2)) { |
2829 | 0 | ir_use_list_remove_one(ctx, insn->op2, cond_ref); |
2830 | 0 | } |
2831 | 0 | MAKE_NOP(cond); CLEAR_USES(cond_ref); |
2832 | 0 | } else { |
2833 | 0 | ir_use_list_remove_one(ctx, cond_ref, root_ref); |
2834 | 0 | } |
2835 | 0 | MAKE_NOP(root); CLEAR_USES(root_ref); |
2836 | 0 | MAKE_NOP(start1); CLEAR_USES(start1_ref); |
2837 | 0 | MAKE_NOP(start2); CLEAR_USES(start2_ref); |
2838 | 0 | MAKE_NOP(end1); CLEAR_USES(end1_ref); |
2839 | 0 | MAKE_NOP(end2); CLEAR_USES(end2_ref); |
2840 | 0 | MAKE_NOP(merge); CLEAR_USES(merge_ref); |
2841 | |
|
2842 | 0 | ir_bitqueue_add(ctx->iter_worklist, ref); |
2843 | 0 | ir_iter_add_uses(ctx, ref, ctx->iter_worklist); |
2844 | 0 | if (ctx->ir_base[next->op1].op == IR_BEGIN || ctx->ir_base[next->op1].op == IR_MERGE) { |
2845 | 0 | ir_bitqueue_add(ctx->iter_worklist, next->op1); |
2846 | 0 | } |
2847 | |
|
2848 | 0 | return 1; |
2849 | 363 | } else if (is_cmp |
2850 | 0 | && ((ctx->ir_base[insn->op2].op == IR_NEG |
2851 | 0 | && ctx->use_lists[insn->op2].count == 1 |
2852 | 0 | && ctx->ir_base[insn->op2].op1 == insn->op3 |
2853 | 0 | && ((cond->op1 == insn->op3 |
2854 | 0 | && ir_is_zero(ctx, cond->op2) |
2855 | 0 | && is_less == (start1->op == IR_IF_TRUE)) |
2856 | 0 | || (cond->op2 == insn->op3 |
2857 | 0 | && ir_is_zero(ctx, cond->op1) |
2858 | 0 | && is_less != (start1->op == IR_IF_TRUE)))) |
2859 | 0 | || (ctx->ir_base[insn->op3].op == IR_NEG |
2860 | 0 | && ctx->use_lists[insn->op3].count == 1 |
2861 | 0 | && ctx->ir_base[insn->op3].op1 == insn->op2 |
2862 | 0 | && ((cond->op1 == insn->op2 |
2863 | 0 | && ir_is_zero(ctx, cond->op2) |
2864 | 0 | && is_less != (start1->op == IR_IF_TRUE)) |
2865 | 0 | || (cond->op2 == insn->op2 |
2866 | 0 | && ir_is_zero(ctx, cond->op1) |
2867 | 0 | && is_less == (start1->op == IR_IF_TRUE)))))) { |
2868 | | /* ABS |
2869 | | * |
2870 | | * prev prev |
2871 | | * | LT(A, 0) | |
2872 | | * | / | |
2873 | | * IF | |
2874 | | * | \ | |
2875 | | * | +-----+ | |
2876 | | * | IF_FALSE | |
2877 | | * IF_TRUE | => | |
2878 | | * | END | |
2879 | | * END / | |
2880 | | * | +---+ | |
2881 | | * | / | |
2882 | | * MERGE | |
2883 | | * | \ | |
2884 | | * | PHI(A, NEG(A)) | ABS(A) |
2885 | | * next next |
2886 | | */ |
2887 | 0 | ir_ref neg_ref; |
2888 | 0 | ir_ref next_ref = ctx->use_edges[ctx->use_lists[merge_ref].refs]; |
2889 | 0 | ir_insn *next; |
2890 | |
|
2891 | 0 | if (next_ref == ref) { |
2892 | 0 | next_ref = ctx->use_edges[ctx->use_lists[merge_ref].refs + 1]; |
2893 | 0 | } |
2894 | 0 | next = &ctx->ir_base[next_ref]; |
2895 | |
|
2896 | 0 | if (ctx->use_lists[start1_ref].count != 1) { |
2897 | 0 | ir_remove_unused_vars(ctx, start1_ref, end1_ref); |
2898 | 0 | } |
2899 | 0 | if (ctx->use_lists[start2_ref].count != 1) { |
2900 | 0 | ir_remove_unused_vars(ctx, start2_ref, end2_ref); |
2901 | 0 | } |
2902 | |
|
2903 | 0 | insn->op = IR_ABS; |
2904 | 0 | insn->inputs_count = 1; |
2905 | 0 | if (ctx->ir_base[insn->op2].op == IR_NEG) { |
2906 | 0 | neg_ref = insn->op2; |
2907 | 0 | insn->op1 = insn->op3; |
2908 | 0 | } else { |
2909 | 0 | neg_ref = insn->op3; |
2910 | 0 | insn->op1 = insn->op2; |
2911 | 0 | } |
2912 | 0 | insn->op2 = IR_UNUSED; |
2913 | 0 | insn->op3 = IR_UNUSED; |
2914 | |
|
2915 | 0 | next->op1 = root->op1; |
2916 | 0 | ir_use_list_replace_one(ctx, root->op1, root_ref, next_ref); |
2917 | 0 | ir_use_list_remove_one(ctx, insn->op1, neg_ref); |
2918 | |
|
2919 | 0 | if (ctx->use_lists[cond_ref].count == 1) { |
2920 | 0 | if (!IR_IS_CONST_REF(insn->op1)) { |
2921 | 0 | ir_use_list_remove_one(ctx, insn->op1, cond_ref); |
2922 | 0 | } |
2923 | 0 | MAKE_NOP(cond); CLEAR_USES(cond_ref); |
2924 | 0 | } else { |
2925 | 0 | ir_use_list_remove_one(ctx, cond_ref, root_ref); |
2926 | 0 | } |
2927 | 0 | MAKE_NOP(root); CLEAR_USES(root_ref); |
2928 | 0 | MAKE_NOP(start1); CLEAR_USES(start1_ref); |
2929 | 0 | MAKE_NOP(start2); CLEAR_USES(start2_ref); |
2930 | 0 | MAKE_NOP(end1); CLEAR_USES(end1_ref); |
2931 | 0 | MAKE_NOP(end2); CLEAR_USES(end2_ref); |
2932 | 0 | MAKE_NOP(merge); CLEAR_USES(merge_ref); |
2933 | 0 | MAKE_NOP(&ctx->ir_base[neg_ref]); CLEAR_USES(neg_ref); |
2934 | |
|
2935 | 0 | ir_bitqueue_add(ctx->iter_worklist, ref); |
2936 | 0 | ir_iter_add_uses(ctx, ref, ctx->iter_worklist); |
2937 | 0 | if (ctx->ir_base[next->op1].op == IR_BEGIN || ctx->ir_base[next->op1].op == IR_MERGE) { |
2938 | 0 | ir_bitqueue_add(ctx->iter_worklist, next->op1); |
2939 | 0 | } |
2940 | |
|
2941 | 0 | return 1; |
2942 | 363 | } else if (insn->op2 <= root_ref && insn->op3 <= root_ref |
2943 | 0 | && cond->op != IR_OVERFLOW |
2944 | | // TODO: temporary disable IF-conversion for RLOAD. |
2945 | | // We don't track anti-dependencies in GCM and Local Scheduling. |
2946 | | // As result COND may be scheduled below the following RSTORE. |
2947 | | // See: https://github.com/dstogov/ir/issues/132 |
2948 | 0 | && cond->op != IR_RLOAD |
2949 | 0 | && !((cond->op >= IR_EQ && cond->op <= IR_UNORDERED) |
2950 | 0 | && ((!IR_IS_CONST_REF(cond->op1) && ctx->ir_base[cond->op1].op == IR_RLOAD) |
2951 | 0 | || (!IR_IS_CONST_REF(cond->op2) && ctx->ir_base[cond->op2].op == IR_RLOAD)))) { |
2952 | | /* COND |
2953 | | * |
2954 | | * prev prev |
2955 | | * | cond | |
2956 | | * | / | |
2957 | | * IF | |
2958 | | * | \ | |
2959 | | * | +-----+ | |
2960 | | * | IF_FALSE | |
2961 | | * IF_TRUE | => | |
2962 | | * | END | |
2963 | | * END / | |
2964 | | * | +---+ | |
2965 | | * | / | |
2966 | | * MERGE | |
2967 | | * | \ | |
2968 | | * | PHI(A, B) | COND(cond, A, B) |
2969 | | * next next |
2970 | | */ |
2971 | 0 | ir_ref next_ref = ctx->use_edges[ctx->use_lists[merge_ref].refs]; |
2972 | 0 | ir_insn *next; |
2973 | |
|
2974 | 0 | if (next_ref == ref) { |
2975 | 0 | next_ref = ctx->use_edges[ctx->use_lists[merge_ref].refs + 1]; |
2976 | 0 | } |
2977 | 0 | next = &ctx->ir_base[next_ref]; |
2978 | |
|
2979 | 0 | if (ctx->use_lists[start1_ref].count != 1) { |
2980 | 0 | ir_remove_unused_vars(ctx, start1_ref, end1_ref); |
2981 | 0 | } |
2982 | 0 | if (ctx->use_lists[start2_ref].count != 1) { |
2983 | 0 | ir_remove_unused_vars(ctx, start2_ref, end2_ref); |
2984 | 0 | } |
2985 | |
|
2986 | 0 | insn->op = IR_COND; |
2987 | 0 | insn->inputs_count = 3; |
2988 | 0 | insn->op1 = cond_ref; |
2989 | 0 | if (start1->op == IR_IF_FALSE) { |
2990 | 0 | SWAP_REFS(insn->op2, insn->op3); |
2991 | 0 | } |
2992 | |
|
2993 | 0 | next->op1 = root->op1; |
2994 | 0 | ir_use_list_replace_one(ctx, cond_ref, root_ref, ref); |
2995 | 0 | ir_use_list_replace_one(ctx, root->op1, root_ref, next_ref); |
2996 | 0 | ir_use_list_remove_one(ctx, root->op2, root_ref); |
2997 | |
|
2998 | 0 | MAKE_NOP(root); CLEAR_USES(root_ref); |
2999 | 0 | MAKE_NOP(start1); CLEAR_USES(start1_ref); |
3000 | 0 | MAKE_NOP(start2); CLEAR_USES(start2_ref); |
3001 | 0 | MAKE_NOP(end1); CLEAR_USES(end1_ref); |
3002 | 0 | MAKE_NOP(end2); CLEAR_USES(end2_ref); |
3003 | 0 | MAKE_NOP(merge); CLEAR_USES(merge_ref); |
3004 | |
|
3005 | 0 | ir_bitqueue_add(ctx->iter_worklist, ref); |
3006 | 0 | ir_iter_add_uses(ctx, ref, ctx->iter_worklist); |
3007 | 0 | if (ctx->ir_base[next->op1].op == IR_BEGIN || ctx->ir_base[next->op1].op == IR_MERGE) { |
3008 | 0 | ir_bitqueue_add(ctx->iter_worklist, next->op1); |
3009 | 0 | } |
3010 | |
|
3011 | 0 | return 1; |
3012 | 0 | } |
3013 | 363 | } |
3014 | 363 | } |
3015 | 16.1k | } |
3016 | | |
3017 | 16.1k | return 0; |
3018 | 16.1k | } |
3019 | | |
3020 | | static bool ir_cmp_is_true(ir_op op, const ir_insn *op1, const ir_insn *op2) |
3021 | 0 | { |
3022 | 0 | IR_ASSERT(op1->type == op2->type); |
3023 | 0 | if (IR_IS_TYPE_INT(op1->type)) { |
3024 | 0 | if (op == IR_EQ) { |
3025 | 0 | return op1->val.u64 == op2->val.u64; |
3026 | 0 | } else if (op == IR_NE) { |
3027 | 0 | return op1->val.u64 != op2->val.u64; |
3028 | 0 | } else if (op == IR_LT) { |
3029 | 0 | if (IR_IS_TYPE_SIGNED(op1->type)) { |
3030 | 0 | return op1->val.i64 < op2->val.i64; |
3031 | 0 | } else if (ir_type_size[op1->type] == 8) { |
3032 | 0 | return op1->val.i64 < op2->val.i64; |
3033 | 0 | } else if (ir_type_size[op1->type] == 4) { |
3034 | 0 | return op1->val.i32 < op2->val.i32; |
3035 | 0 | } else if (ir_type_size[op1->type] == 2) { |
3036 | 0 | return op1->val.i16 < op2->val.i16; |
3037 | 0 | } else if (ir_type_size[op1->type] == 1) { |
3038 | 0 | return op1->val.i8 < op2->val.i8; |
3039 | 0 | } else { |
3040 | 0 | IR_ASSERT(0); |
3041 | 0 | return 0; |
3042 | 0 | } |
3043 | 0 | } else if (op == IR_GE) { |
3044 | 0 | if (IR_IS_TYPE_SIGNED(op1->type)) { |
3045 | 0 | return op1->val.i64 >= op2->val.i64; |
3046 | 0 | } else if (ir_type_size[op1->type] == 8) { |
3047 | 0 | return op1->val.i64 >= op2->val.i64; |
3048 | 0 | } else if (ir_type_size[op1->type] == 4) { |
3049 | 0 | return op1->val.i32 >= op2->val.i32; |
3050 | 0 | } else if (ir_type_size[op1->type] == 2) { |
3051 | 0 | return op1->val.i16 >= op2->val.i16; |
3052 | 0 | } else if (ir_type_size[op1->type] == 1) { |
3053 | 0 | return op1->val.i8 >= op2->val.i8; |
3054 | 0 | } else { |
3055 | 0 | IR_ASSERT(0); |
3056 | 0 | return 0; |
3057 | 0 | } |
3058 | 0 | } else if (op == IR_LE) { |
3059 | 0 | if (IR_IS_TYPE_SIGNED(op1->type)) { |
3060 | 0 | return op1->val.i64 <= op2->val.i64; |
3061 | 0 | } else if (ir_type_size[op1->type] == 8) { |
3062 | 0 | return op1->val.i64 <= op2->val.i64; |
3063 | 0 | } else if (ir_type_size[op1->type] == 4) { |
3064 | 0 | return op1->val.i32 <= op2->val.i32; |
3065 | 0 | } else if (ir_type_size[op1->type] == 2) { |
3066 | 0 | return op1->val.i16 <= op2->val.i16; |
3067 | 0 | } else if (ir_type_size[op1->type] == 1) { |
3068 | 0 | return op1->val.i8 <= op2->val.i8; |
3069 | 0 | } else { |
3070 | 0 | IR_ASSERT(0); |
3071 | 0 | return 0; |
3072 | 0 | } |
3073 | 0 | } else if (op == IR_GT) { |
3074 | 0 | if (IR_IS_TYPE_SIGNED(op1->type)) { |
3075 | 0 | return op1->val.i64 > op2->val.i64; |
3076 | 0 | } else if (ir_type_size[op1->type] == 8) { |
3077 | 0 | return op1->val.i64 > op2->val.i64; |
3078 | 0 | } else if (ir_type_size[op1->type] == 4) { |
3079 | 0 | return op1->val.i32 > op2->val.i32; |
3080 | 0 | } else if (ir_type_size[op1->type] == 2) { |
3081 | 0 | return op1->val.i16 > op2->val.i16; |
3082 | 0 | } else if (ir_type_size[op1->type] == 1) { |
3083 | 0 | return op1->val.i8 > op2->val.i8; |
3084 | 0 | } else { |
3085 | 0 | IR_ASSERT(0); |
3086 | 0 | return 0; |
3087 | 0 | } |
3088 | 0 | } else if (op == IR_ULT) { |
3089 | 0 | return op1->val.u64 < op2->val.u64; |
3090 | 0 | } else if (op == IR_UGE) { |
3091 | 0 | return op1->val.u64 >= op2->val.u64; |
3092 | 0 | } else if (op == IR_ULE) { |
3093 | 0 | return op1->val.u64 <= op2->val.u64; |
3094 | 0 | } else if (op == IR_UGT) { |
3095 | 0 | return op1->val.u64 > op2->val.u64; |
3096 | 0 | } else { |
3097 | 0 | IR_ASSERT(0); |
3098 | 0 | return 0; |
3099 | 0 | } |
3100 | 0 | } else if (op1->type == IR_DOUBLE) { |
3101 | 0 | if (op == IR_EQ) { |
3102 | 0 | return op1->val.d == op2->val.d; |
3103 | 0 | } else if (op == IR_NE) { |
3104 | 0 | return op1->val.d != op2->val.d; |
3105 | 0 | } else if (op == IR_LT) { |
3106 | 0 | return op1->val.d < op2->val.d; |
3107 | 0 | } else if (op == IR_GE) { |
3108 | 0 | return op1->val.d >= op2->val.d; |
3109 | 0 | } else if (op == IR_LE) { |
3110 | 0 | return op1->val.d <= op2->val.d; |
3111 | 0 | } else if (op == IR_GT) { |
3112 | 0 | return op1->val.d > op2->val.d; |
3113 | 0 | } else if (op == IR_ULT) { |
3114 | 0 | return !(op1->val.d >= op2->val.d); |
3115 | 0 | } else if (op == IR_UGE) { |
3116 | 0 | return !(op1->val.d < op2->val.d); |
3117 | 0 | } else if (op == IR_ULE) { |
3118 | 0 | return !(op1->val.d > op2->val.d); |
3119 | 0 | } else if (op == IR_UGT) { |
3120 | 0 | return !(op1->val.d <= op2->val.d); |
3121 | 0 | } else if (op == IR_ORDERED) { |
3122 | 0 | return !isnan(op1->val.d) && !isnan(op2->val.d); |
3123 | 0 | } else if (op == IR_UNORDERED) { |
3124 | 0 | return isnan(op1->val.d) || isnan(op2->val.d); |
3125 | 0 | } else { |
3126 | 0 | IR_ASSERT(0); |
3127 | 0 | return 0; |
3128 | 0 | } |
3129 | 0 | } else { |
3130 | 0 | IR_ASSERT(op1->type == IR_FLOAT); |
3131 | 0 | if (op == IR_EQ) { |
3132 | 0 | return op1->val.f == op2->val.f; |
3133 | 0 | } else if (op == IR_NE) { |
3134 | 0 | return op1->val.f != op2->val.f; |
3135 | 0 | } else if (op == IR_LT) { |
3136 | 0 | return op1->val.f < op2->val.f; |
3137 | 0 | } else if (op == IR_GE) { |
3138 | 0 | return op1->val.f >= op2->val.f; |
3139 | 0 | } else if (op == IR_LE) { |
3140 | 0 | return op1->val.f <= op2->val.f; |
3141 | 0 | } else if (op == IR_GT) { |
3142 | 0 | return op1->val.f > op2->val.f; |
3143 | 0 | } else if (op == IR_ULT) { |
3144 | 0 | return !(op1->val.f >= op2->val.f); |
3145 | 0 | } else if (op == IR_UGE) { |
3146 | 0 | return !(op1->val.f < op2->val.f); |
3147 | 0 | } else if (op == IR_ULE) { |
3148 | 0 | return !(op1->val.f > op2->val.f); |
3149 | 0 | } else if (op == IR_UGT) { |
3150 | 0 | return !(op1->val.f <= op2->val.f); |
3151 | 0 | } else if (op == IR_ORDERED) { |
3152 | 0 | return !isnan(op1->val.f) && !isnan(op2->val.f); |
3153 | 0 | } else if (op == IR_UNORDERED) { |
3154 | 0 | return isnan(op1->val.f) || isnan(op2->val.f); |
3155 | 0 | } else { |
3156 | 0 | IR_ASSERT(0); |
3157 | 0 | return 0; |
3158 | 0 | } |
3159 | 0 | } |
3160 | 0 | } |
3161 | | |
3162 | | static bool ir_try_split_if(ir_ctx *ctx, ir_ref ref, ir_insn *insn) |
3163 | 0 | { |
3164 | 0 | ir_ref cond_ref = insn->op2; |
3165 | 0 | ir_insn *cond = &ctx->ir_base[cond_ref]; |
3166 | |
|
3167 | 0 | if (cond->op == IR_PHI |
3168 | 0 | && cond->inputs_count == 3 |
3169 | 0 | && cond->op1 == insn->op1 |
3170 | 0 | && ((IR_IS_CONST_REF(cond->op2) && !IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op)) |
3171 | 0 | || (IR_IS_CONST_REF(cond->op3) && !IR_IS_SYM_CONST(ctx->ir_base[cond->op3].op)))) { |
3172 | 0 | ir_ref merge_ref = insn->op1; |
3173 | 0 | ir_insn *merge = &ctx->ir_base[merge_ref]; |
3174 | |
|
3175 | 0 | if (ctx->use_lists[merge_ref].count == 2) { |
3176 | 0 | ir_ref end1_ref = merge->op1, end2_ref = merge->op2; |
3177 | 0 | ir_insn *end1 = &ctx->ir_base[end1_ref]; |
3178 | 0 | ir_insn *end2 = &ctx->ir_base[end2_ref]; |
3179 | |
|
3180 | 0 | if (end1->op == IR_END && end2->op == IR_END) { |
3181 | 0 | ir_ref if_true_ref, if_false_ref; |
3182 | 0 | ir_insn *if_true, *if_false; |
3183 | 0 | ir_op op = IR_IF_FALSE; |
3184 | |
|
3185 | 0 | ir_get_true_false_refs(ctx, ref, &if_true_ref, &if_false_ref); |
3186 | |
|
3187 | 0 | if (!IR_IS_CONST_REF(cond->op2) || IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op)) { |
3188 | 0 | IR_ASSERT(IR_IS_CONST_REF(cond->op3)); |
3189 | 0 | SWAP_REFS(cond->op2, cond->op3); |
3190 | 0 | SWAP_REFS(merge->op1, merge->op2); |
3191 | 0 | SWAP_REFS(end1_ref, end2_ref); |
3192 | 0 | SWAP_INSNS(end1, end2); |
3193 | 0 | } |
3194 | 0 | if (ir_const_is_true(&ctx->ir_base[cond->op2])) { |
3195 | 0 | SWAP_REFS(if_true_ref, if_false_ref); |
3196 | 0 | op = IR_IF_TRUE; |
3197 | 0 | } |
3198 | 0 | if_true = &ctx->ir_base[if_true_ref]; |
3199 | 0 | if_false = &ctx->ir_base[if_false_ref]; |
3200 | |
|
3201 | 0 | if (IR_IS_CONST_REF(cond->op3) && !IR_IS_SYM_CONST(ctx->ir_base[cond->op3].op)) { |
3202 | 0 | if (ir_const_is_true(&ctx->ir_base[cond->op3]) ^ (op == IR_IF_TRUE)) { |
3203 | | /* Simple IF Split |
3204 | | * |
3205 | | * | | | | |
3206 | | * | END | END |
3207 | | * END / END \ |
3208 | | * | +---+ | + |
3209 | | * | / | | |
3210 | | * MERGE | | |
3211 | | * | \ | | |
3212 | | * | PHI(false, true) | | |
3213 | | * | / | | |
3214 | | * IF => | | |
3215 | | * | \ | | |
3216 | | * | +------+ | | |
3217 | | * | IF_TRUE | BEGIN |
3218 | | * IF_FALSE | BEGIN |
3219 | | * | | |
3220 | | */ |
3221 | 0 | ir_use_list_replace_one(ctx, end1_ref, merge_ref, if_false_ref); |
3222 | 0 | ir_use_list_replace_one(ctx, end2_ref, merge_ref, if_true_ref); |
3223 | |
|
3224 | 0 | MAKE_NOP(merge); CLEAR_USES(merge_ref); |
3225 | 0 | MAKE_NOP(cond); CLEAR_USES(cond_ref); |
3226 | 0 | MAKE_NOP(insn); CLEAR_USES(ref); |
3227 | |
|
3228 | 0 | if_false->optx = IR_OPTX(IR_BEGIN, IR_VOID, 1); |
3229 | 0 | if_false->op1 = end1_ref; |
3230 | 0 | if_false->op2 = IR_UNUSED; |
3231 | |
|
3232 | 0 | if_true->optx = IR_OPTX(IR_BEGIN, IR_VOID, 1); |
3233 | 0 | if_true->op1 = end2_ref; |
3234 | 0 | if_true->op2 = IR_UNUSED; |
3235 | |
|
3236 | 0 | ir_bitqueue_add(ctx->iter_worklist, if_false_ref); |
3237 | 0 | ir_bitqueue_add(ctx->iter_worklist, if_true_ref); |
3238 | |
|
3239 | 0 | return 1; |
3240 | 0 | } else { |
3241 | | /* Simple IF Split |
3242 | | * |
3243 | | * | | | | |
3244 | | * | END | END |
3245 | | * END / END | |
3246 | | * | +---+ | | |
3247 | | * | / | | |
3248 | | * MERGE | + |
3249 | | * | \ | / |
3250 | | * | PHI(false, false) | / |
3251 | | * | / | / |
3252 | | * IF => | / |
3253 | | * | \ | / |
3254 | | * | +------+ | / |
3255 | | * | IF_TRUE | / BEGIN(unreachable) |
3256 | | * IF_FALSE | MERGE |
3257 | | * | | |
3258 | | */ |
3259 | 0 | ir_use_list_replace_one(ctx, end1_ref, merge_ref, if_false_ref); |
3260 | 0 | ir_use_list_replace_one(ctx, end2_ref, merge_ref, if_false_ref); |
3261 | |
|
3262 | 0 | MAKE_NOP(merge); CLEAR_USES(merge_ref); |
3263 | 0 | MAKE_NOP(cond); CLEAR_USES(cond_ref); |
3264 | 0 | MAKE_NOP(insn); CLEAR_USES(ref); |
3265 | |
|
3266 | 0 | if_false->optx = IR_OPTX(IR_MERGE, IR_VOID, 2); |
3267 | 0 | if_false->op1 = end1_ref; |
3268 | 0 | if_false->op2 = end2_ref; |
3269 | |
|
3270 | 0 | if_true->optx = IR_BEGIN; |
3271 | 0 | if_true->op1 = IR_UNUSED; |
3272 | 0 | if_true->op2 = IR_UNUSED; |
3273 | |
|
3274 | 0 | ctx->flags2 &= ~IR_CFG_REACHABLE; |
3275 | |
|
3276 | 0 | ir_bitqueue_add(ctx->iter_worklist, if_false_ref); |
3277 | |
|
3278 | 0 | return 1; |
3279 | 0 | } |
3280 | 0 | } |
3281 | | |
3282 | | /* Simple IF Split |
3283 | | * |
3284 | | * | | | | |
3285 | | * | END | IF(X) |
3286 | | * END / END / \ |
3287 | | * | +---+ | +--+ + |
3288 | | * | / | / | |
3289 | | * MERGE | IF_FALSE | |
3290 | | * | \ | | | |
3291 | | * | PHI(false, X) | | | |
3292 | | * | / | | | |
3293 | | * IF => | END | |
3294 | | * | \ | | | |
3295 | | * | +------+ | | | |
3296 | | * | IF_TRUE | | IF_TRUE |
3297 | | * IF_FALSE | MERGE |
3298 | | * | | |
3299 | | */ |
3300 | 0 | ir_use_list_remove_one(ctx, merge_ref, cond_ref); |
3301 | 0 | ir_use_list_remove_one(ctx, ref, if_true_ref); |
3302 | 0 | if (!IR_IS_CONST_REF(cond->op3)) { |
3303 | 0 | ir_use_list_replace_one(ctx, cond->op3, cond_ref, end2_ref); |
3304 | 0 | } |
3305 | 0 | ir_use_list_replace_one(ctx, end1_ref, merge_ref, if_false_ref); |
3306 | 0 | ir_use_list_add(ctx, end2_ref, if_true_ref); |
3307 | |
|
3308 | 0 | end2->optx = IR_OPTX(IR_IF, IR_VOID, 2); |
3309 | 0 | end2->op2 = cond->op3; |
3310 | 0 | ir_bitqueue_add(ctx->iter_worklist, end2_ref); |
3311 | |
|
3312 | 0 | merge->optx = IR_OPTX(op, IR_VOID, 1); |
3313 | 0 | merge->op1 = end2_ref; |
3314 | 0 | merge->op2 = IR_UNUSED; |
3315 | |
|
3316 | 0 | MAKE_NOP(cond); |
3317 | 0 | CLEAR_USES(cond_ref); |
3318 | |
|
3319 | 0 | insn->optx = IR_OPTX(IR_END, IR_VOID, 1); |
3320 | 0 | insn->op1 = merge_ref; |
3321 | 0 | insn->op2 = IR_UNUSED; |
3322 | |
|
3323 | 0 | if_true->op1 = end2_ref; |
3324 | |
|
3325 | 0 | if_false->optx = IR_OPTX(IR_MERGE, IR_VOID, 2); |
3326 | 0 | if_false->op1 = end1_ref; |
3327 | 0 | if_false->op2 = ref; |
3328 | |
|
3329 | 0 | ir_bitqueue_add(ctx->iter_worklist, if_false_ref); |
3330 | 0 | if (ctx->ir_base[end2->op1].op == IR_BEGIN || ctx->ir_base[end2->op1].op == IR_MERGE) { |
3331 | 0 | ir_bitqueue_add(ctx->iter_worklist, end2->op1); |
3332 | 0 | } |
3333 | |
|
3334 | 0 | return 1; |
3335 | 0 | } |
3336 | 0 | } |
3337 | 0 | } |
3338 | | |
3339 | 0 | return 0; |
3340 | 0 | } |
3341 | | |
3342 | | static bool ir_try_split_if_cmp(ir_ctx *ctx, ir_ref ref, ir_insn *insn) |
3343 | 0 | { |
3344 | 0 | ir_ref cond_ref = insn->op2; |
3345 | 0 | ir_insn *cond = &ctx->ir_base[cond_ref]; |
3346 | |
|
3347 | 0 | if (cond->op >= IR_EQ && cond->op <= IR_UGT |
3348 | 0 | && IR_IS_CONST_REF(cond->op2) |
3349 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op) |
3350 | 0 | && ctx->use_lists[insn->op2].count == 1) { |
3351 | 0 | ir_ref phi_ref = cond->op1; |
3352 | 0 | ir_insn *phi = &ctx->ir_base[phi_ref]; |
3353 | |
|
3354 | 0 | if (phi->op == IR_PHI |
3355 | 0 | && phi->inputs_count == 3 |
3356 | 0 | && phi->op1 == insn->op1 |
3357 | 0 | && ctx->use_lists[phi_ref].count == 1 |
3358 | 0 | && ((IR_IS_CONST_REF(phi->op2) && !IR_IS_SYM_CONST(ctx->ir_base[phi->op2].op)) |
3359 | 0 | || (IR_IS_CONST_REF(phi->op3) && !IR_IS_SYM_CONST(ctx->ir_base[phi->op3].op)))) { |
3360 | 0 | ir_ref merge_ref = insn->op1; |
3361 | 0 | ir_insn *merge = &ctx->ir_base[merge_ref]; |
3362 | |
|
3363 | 0 | if (ctx->use_lists[merge_ref].count == 2) { |
3364 | 0 | ir_ref end1_ref = merge->op1, end2_ref = merge->op2; |
3365 | 0 | ir_insn *end1 = &ctx->ir_base[end1_ref]; |
3366 | 0 | ir_insn *end2 = &ctx->ir_base[end2_ref]; |
3367 | |
|
3368 | 0 | if (end1->op == IR_END && end2->op == IR_END) { |
3369 | 0 | ir_ref if_true_ref, if_false_ref; |
3370 | 0 | ir_insn *if_true, *if_false; |
3371 | 0 | ir_op op = IR_IF_FALSE; |
3372 | |
|
3373 | 0 | ir_get_true_false_refs(ctx, ref, &if_true_ref, &if_false_ref); |
3374 | |
|
3375 | 0 | if (!IR_IS_CONST_REF(phi->op2) || IR_IS_SYM_CONST(ctx->ir_base[phi->op2].op)) { |
3376 | 0 | IR_ASSERT(IR_IS_CONST_REF(phi->op3)); |
3377 | 0 | SWAP_REFS(phi->op2, phi->op3); |
3378 | 0 | SWAP_REFS(merge->op1, merge->op2); |
3379 | 0 | SWAP_REFS(end1_ref, end2_ref); |
3380 | 0 | SWAP_INSNS(end1, end2); |
3381 | 0 | } |
3382 | 0 | if (ir_cmp_is_true(cond->op, &ctx->ir_base[phi->op2], &ctx->ir_base[cond->op2])) { |
3383 | 0 | SWAP_REFS(if_true_ref, if_false_ref); |
3384 | 0 | op = IR_IF_TRUE; |
3385 | 0 | } |
3386 | 0 | if_true = &ctx->ir_base[if_true_ref]; |
3387 | 0 | if_false = &ctx->ir_base[if_false_ref]; |
3388 | |
|
3389 | 0 | if (IR_IS_CONST_REF(phi->op3) && !IR_IS_SYM_CONST(ctx->ir_base[phi->op3].op)) { |
3390 | 0 | if (ir_cmp_is_true(cond->op, &ctx->ir_base[phi->op3], &ctx->ir_base[cond->op2]) ^ (op == IR_IF_TRUE)) { |
3391 | | /* IF Split |
3392 | | * |
3393 | | * | | | | |
3394 | | * | END | END |
3395 | | * END / END | |
3396 | | * | +---+ | | |
3397 | | * | / | | |
3398 | | * MERGE | | |
3399 | | * | \ | | |
3400 | | * | PHI(C1, X) | | |
3401 | | * | | | | |
3402 | | * | CMP(_, C2) | | |
3403 | | * | / | | |
3404 | | * IF => | | |
3405 | | * | \ | | |
3406 | | * | +------+ | | |
3407 | | * | IF_TRUE | BEGIN |
3408 | | * IF_FALSE | BEGIN | |
3409 | | * | | |
3410 | | */ |
3411 | |
|
3412 | 0 | ir_use_list_replace_one(ctx, end1_ref, merge_ref, if_false_ref); |
3413 | 0 | ir_use_list_replace_one(ctx, end2_ref, merge_ref, if_true_ref); |
3414 | |
|
3415 | 0 | MAKE_NOP(merge); CLEAR_USES(merge_ref); |
3416 | 0 | MAKE_NOP(phi); CLEAR_USES(phi_ref); |
3417 | 0 | MAKE_NOP(cond); CLEAR_USES(cond_ref); |
3418 | 0 | MAKE_NOP(insn); CLEAR_USES(ref); |
3419 | |
|
3420 | 0 | if_false->optx = IR_OPTX(IR_BEGIN, IR_VOID, 1); |
3421 | 0 | if_false->op1 = end1_ref; |
3422 | 0 | if_false->op2 = IR_UNUSED; |
3423 | |
|
3424 | 0 | if_true->optx = IR_OPTX(IR_BEGIN, IR_VOID, 1); |
3425 | 0 | if_true->op1 = end2_ref; |
3426 | 0 | if_true->op2 = IR_UNUSED; |
3427 | |
|
3428 | 0 | ir_bitqueue_add(ctx->iter_worklist, if_false_ref); |
3429 | 0 | ir_bitqueue_add(ctx->iter_worklist, if_true_ref); |
3430 | |
|
3431 | 0 | return 1; |
3432 | 0 | } else { |
3433 | | /* IF Split |
3434 | | * |
3435 | | * | | | | |
3436 | | * | END | END |
3437 | | * END / END | |
3438 | | * | +---+ | | |
3439 | | * | / | | |
3440 | | * MERGE | | |
3441 | | * | \ | | |
3442 | | * | PHI(C1, X) | | |
3443 | | * | | | + |
3444 | | * | CMP(_, C2) | / |
3445 | | * | / | / |
3446 | | * IF => | / |
3447 | | * | \ | / |
3448 | | * | +------+ | / |
3449 | | * | IF_TRUE | / BEGIN(unreachable) |
3450 | | * IF_FALSE | MERGE | |
3451 | | * | | |
3452 | | */ |
3453 | |
|
3454 | 0 | ir_use_list_replace_one(ctx, end1_ref, merge_ref, if_false_ref); |
3455 | 0 | ir_use_list_replace_one(ctx, end2_ref, merge_ref, if_false_ref); |
3456 | |
|
3457 | 0 | MAKE_NOP(merge); CLEAR_USES(merge_ref); |
3458 | 0 | MAKE_NOP(phi); CLEAR_USES(phi_ref); |
3459 | 0 | MAKE_NOP(cond); CLEAR_USES(cond_ref); |
3460 | 0 | MAKE_NOP(insn); CLEAR_USES(ref); |
3461 | |
|
3462 | 0 | if_false->optx = IR_OPTX(IR_MERGE, IR_VOID, 2); |
3463 | 0 | if_false->op1 = end1_ref; |
3464 | 0 | if_false->op2 = end2_ref; |
3465 | |
|
3466 | 0 | if_true->optx = IR_BEGIN; |
3467 | 0 | if_true->op1 = IR_UNUSED; |
3468 | 0 | if_true->op2 = IR_UNUSED; |
3469 | |
|
3470 | 0 | ctx->flags2 &= ~IR_CFG_REACHABLE; |
3471 | |
|
3472 | 0 | ir_bitqueue_add(ctx->iter_worklist, if_false_ref); |
3473 | |
|
3474 | 0 | return 1; |
3475 | 0 | } |
3476 | 0 | } else { |
3477 | | /* IF Split |
3478 | | * |
3479 | | * | | | | |
3480 | | * | END | IF<----+ |
3481 | | * END / END / \ | |
3482 | | * | +---+ | +--+ + | |
3483 | | * | / | / | | |
3484 | | * MERGE | IF_FALSE | | |
3485 | | * | \ | | | | |
3486 | | * | PHI(C1, X) | | | | |
3487 | | * | | | | | | |
3488 | | * | CMP(_, C2) | | | CMP(X, C2) |
3489 | | * | / | | | |
3490 | | * IF => | END | |
3491 | | * | \ | | | |
3492 | | * | +------+ | | | |
3493 | | * | IF_TRUE | | IF_TRUE |
3494 | | * IF_FALSE | MERGE |
3495 | | * | | |
3496 | | */ |
3497 | |
|
3498 | 0 | ir_use_list_remove_one(ctx, merge_ref, phi_ref); |
3499 | 0 | ir_use_list_remove_one(ctx, ref, if_true_ref); |
3500 | 0 | if (!IR_IS_CONST_REF(phi->op3)) { |
3501 | 0 | ir_use_list_replace_one(ctx, phi->op3, phi_ref, insn->op2); |
3502 | 0 | } |
3503 | 0 | ir_use_list_replace_one(ctx, end1_ref, merge_ref, if_false_ref); |
3504 | 0 | ir_use_list_replace_one(ctx, cond_ref, ref, end2_ref); |
3505 | 0 | ir_use_list_add(ctx, end2_ref, if_true_ref); |
3506 | |
|
3507 | 0 | end2->optx = IR_OPTX(IR_IF, IR_VOID, 2); |
3508 | 0 | end2->op2 = insn->op2; |
3509 | 0 | ir_bitqueue_add(ctx->iter_worklist, end2_ref); |
3510 | |
|
3511 | 0 | merge->optx = IR_OPTX(op, IR_VOID, 1); |
3512 | 0 | merge->op1 = end2_ref; |
3513 | 0 | merge->op2 = IR_UNUSED; |
3514 | |
|
3515 | 0 | cond->op1 = phi->op3; |
3516 | 0 | MAKE_NOP(phi); |
3517 | 0 | CLEAR_USES(phi_ref); |
3518 | |
|
3519 | 0 | insn->optx = IR_OPTX(IR_END, IR_VOID, 1); |
3520 | 0 | insn->op1 = merge_ref; |
3521 | 0 | insn->op2 = IR_UNUSED; |
3522 | |
|
3523 | 0 | if_true->op1 = end2_ref; |
3524 | |
|
3525 | 0 | if_false->optx = IR_OPTX(IR_MERGE, IR_VOID, 2); |
3526 | 0 | if_false->op1 = end1_ref; |
3527 | 0 | if_false->op2 = ref; |
3528 | |
|
3529 | 0 | ir_bitqueue_add(ctx->iter_worklist, if_false_ref); |
3530 | 0 | if (ctx->ir_base[end2->op1].op == IR_BEGIN || ctx->ir_base[end2->op1].op == IR_MERGE) { |
3531 | 0 | ir_bitqueue_add(ctx->iter_worklist, end2->op1); |
3532 | 0 | } |
3533 | |
|
3534 | 0 | return 1; |
3535 | 0 | } |
3536 | 0 | } |
3537 | 0 | } |
3538 | 0 | } |
3539 | 0 | } |
3540 | | |
3541 | 0 | return 0; |
3542 | 0 | } |
3543 | | |
3544 | | static void ir_iter_optimize_merge(ir_ctx *ctx, ir_ref merge_ref, ir_insn *merge) |
3545 | 72.5k | { |
3546 | 72.5k | ir_use_list *use_list = &ctx->use_lists[merge_ref]; |
3547 | | |
3548 | 72.5k | if (use_list->count == 1) { |
3549 | 54.9k | ir_try_remove_empty_diamond(ctx, merge_ref, merge); |
3550 | 54.9k | } else if (use_list->count == 2) { |
3551 | 16.6k | if (merge->inputs_count == 2) { |
3552 | 16.1k | ir_ref phi_ref = ctx->use_edges[use_list->refs]; |
3553 | 16.1k | ir_insn *phi = &ctx->ir_base[phi_ref]; |
3554 | | |
3555 | 16.1k | ir_ref next_ref = ctx->use_edges[use_list->refs + 1]; |
3556 | 16.1k | ir_insn *next = &ctx->ir_base[next_ref]; |
3557 | | |
3558 | 16.1k | if (next->op == IR_PHI) { |
3559 | 0 | SWAP_REFS(phi_ref, next_ref); |
3560 | 0 | SWAP_INSNS(phi, next); |
3561 | 0 | } |
3562 | | |
3563 | 16.1k | if (phi->op == IR_PHI && next->op != IR_PHI) { |
3564 | 16.1k | if (next->op == IR_IF && next->op1 == merge_ref && ctx->use_lists[phi_ref].count == 1) { |
3565 | 1 | if (next->op2 == phi_ref) { |
3566 | 0 | if (ir_try_split_if(ctx, next_ref, next)) { |
3567 | 0 | return; |
3568 | 0 | } |
3569 | 1 | } else { |
3570 | 1 | ir_insn *cmp = &ctx->ir_base[next->op2]; |
3571 | | |
3572 | 1 | if (cmp->op >= IR_EQ && cmp->op <= IR_UGT |
3573 | 0 | && cmp->op1 == phi_ref |
3574 | 0 | && IR_IS_CONST_REF(cmp->op2) |
3575 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[cmp->op2].op) |
3576 | 0 | && ctx->use_lists[next->op2].count == 1) { |
3577 | 0 | if (ir_try_split_if_cmp(ctx, next_ref, next)) { |
3578 | 0 | return; |
3579 | 0 | } |
3580 | 0 | } |
3581 | 1 | } |
3582 | 1 | } |
3583 | 16.1k | if (IR_IS_TYPE_SCALAR(phi->type)) { |
3584 | 16.1k | ir_optimize_phi(ctx, merge_ref, merge, phi_ref, phi); |
3585 | 16.1k | } |
3586 | 16.1k | } |
3587 | 16.1k | } |
3588 | 16.6k | } |
3589 | 72.5k | } |
3590 | | |
3591 | | static ir_ref ir_find_ext_use(const ir_ctx *ctx, ir_ref ref) |
3592 | 12.0k | { |
3593 | 12.0k | ir_use_list *use_list = &ctx->use_lists[ref]; |
3594 | 12.0k | ir_ref *p, n, use; |
3595 | 12.0k | ir_insn *use_insn; |
3596 | | |
3597 | 30.2k | for (p = &ctx->use_edges[use_list->refs], n = use_list->count; n > 0; p++, n--) { |
3598 | 18.1k | use = *p; |
3599 | 18.1k | use_insn = &ctx->ir_base[use]; |
3600 | 18.1k | if (use_insn->op == IR_SEXT || use_insn->op == IR_ZEXT) { |
3601 | 0 | return use; |
3602 | 0 | } |
3603 | 18.1k | } |
3604 | 12.0k | return IR_UNUSED; |
3605 | 12.0k | } |
3606 | | |
3607 | | static void ir_iter_optimize_induction_var(ir_ctx *ctx, ir_ref phi_ref, ir_ref op_ref) |
3608 | 6.04k | { |
3609 | 6.04k | ir_ref ext_ref; |
3610 | | |
3611 | 6.04k | ext_ref = ir_find_ext_use(ctx, phi_ref); |
3612 | 6.04k | if (!ext_ref) { |
3613 | 6.04k | ext_ref = ir_find_ext_use(ctx, op_ref); |
3614 | 6.04k | } |
3615 | 6.04k | if (ext_ref) { |
3616 | 0 | ir_try_promote_induction_var_ext(ctx, ext_ref, phi_ref, op_ref); |
3617 | 0 | } |
3618 | 6.04k | } |
3619 | | |
3620 | | static void ir_iter_optimize_loop(ir_ctx *ctx, ir_ref loop_ref, ir_insn *loop) |
3621 | 3.21k | { |
3622 | 3.21k | ir_ref n; |
3623 | | |
3624 | 3.21k | if (loop->inputs_count != 2 || ctx->use_lists[loop_ref].count <= 1) { |
3625 | 191 | return; |
3626 | 191 | } |
3627 | | |
3628 | | /* Check for simple induction variable in the form: x2 = PHI(loop, x1, x3); x3 = ADD(x2, _); */ |
3629 | 12.0k | for (n = 0; n < ctx->use_lists[loop_ref].count; n++) { |
3630 | | /* "use_lists" may be reallocated by ir_ext_ref() */ |
3631 | 9.07k | ir_ref use = ctx->use_edges[ctx->use_lists[loop_ref].refs + n]; |
3632 | 9.07k | ir_insn *use_insn = &ctx->ir_base[use]; |
3633 | | |
3634 | 9.07k | if (use_insn->op == IR_PHI) { |
3635 | 6.04k | ir_ref op_ref = use_insn->op3; |
3636 | 6.04k | ir_insn *op_insn = &ctx->ir_base[op_ref]; |
3637 | | |
3638 | 6.04k | if (op_insn->op == IR_ADD || op_insn->op == IR_SUB || op_insn->op == IR_MUL) { |
3639 | 6.04k | if (op_insn->op1 == use) { |
3640 | 6.04k | if (ir_is_loop_invariant(ctx, op_insn->op2, loop_ref)) { |
3641 | 6.04k | ir_iter_optimize_induction_var(ctx, use, op_ref); |
3642 | 6.04k | } |
3643 | 6.04k | } else if (op_insn->op2 == use) { |
3644 | 0 | if (ir_is_loop_invariant(ctx, op_insn->op1, loop_ref)) { |
3645 | 0 | ir_iter_optimize_induction_var(ctx, use, op_ref); |
3646 | 0 | } |
3647 | 0 | } |
3648 | 6.04k | } |
3649 | 6.04k | } |
3650 | 9.07k | } |
3651 | 3.02k | } |
3652 | | |
3653 | | static ir_ref ir_iter_optimize_condition(ir_ctx *ctx, ir_ref control, ir_ref condition, bool *swap) |
3654 | 114k | { |
3655 | 114k | ir_insn *condition_insn = &ctx->ir_base[condition]; |
3656 | | |
3657 | 114k | while (((condition_insn->op == IR_BITCAST && IR_IS_TYPE_SCALAR(ctx->ir_base[condition_insn->op1].type)) |
3658 | 114k | || condition_insn->op == IR_ZEXT |
3659 | 114k | || condition_insn->op == IR_SEXT) |
3660 | 0 | && ctx->use_lists[condition].count == 1) { |
3661 | 0 | condition = condition_insn->op1; |
3662 | 0 | condition_insn = &ctx->ir_base[condition]; |
3663 | 0 | } |
3664 | | |
3665 | 114k | IR_ASSERT(IR_IS_TYPE_SCALAR(condition_insn->type)); |
3666 | | |
3667 | 114k | if (condition_insn->opt == IR_OPT(IR_NOT, IR_BOOL)) { |
3668 | 0 | *swap = 1; |
3669 | 0 | condition = condition_insn->op1; |
3670 | 0 | condition_insn = &ctx->ir_base[condition]; |
3671 | 0 | } |
3672 | | |
3673 | 114k | if (condition_insn->op == IR_NE && IR_IS_CONST_REF(condition_insn->op2)) { |
3674 | 565 | ir_insn *val_insn = &ctx->ir_base[condition_insn->op2]; |
3675 | | |
3676 | 565 | if (IR_IS_TYPE_INT(val_insn->type) && val_insn->val.u64 == 0) { |
3677 | 0 | condition = condition_insn->op1; |
3678 | 0 | condition_insn = &ctx->ir_base[condition]; |
3679 | 0 | } |
3680 | 113k | } else if (condition_insn->op == IR_EQ && IR_IS_CONST_REF(condition_insn->op2)) { |
3681 | 17.7k | ir_insn *val_insn = &ctx->ir_base[condition_insn->op2]; |
3682 | | |
3683 | 17.7k | if (condition_insn->op2 == IR_TRUE) { |
3684 | 0 | condition = condition_insn->op1; |
3685 | 0 | condition_insn = &ctx->ir_base[condition]; |
3686 | 17.7k | } else if (IR_IS_TYPE_INT(val_insn->type) && val_insn->val.u64 == 0) { |
3687 | 14 | condition = condition_insn->op1; |
3688 | 14 | condition_insn = &ctx->ir_base[condition]; |
3689 | 14 | *swap = !*swap; |
3690 | 14 | } |
3691 | 17.7k | } |
3692 | | |
3693 | 114k | if (condition_insn->op == IR_SHL && IR_IS_CONST_REF(condition_insn->op1)) { |
3694 | 0 | ir_insn *val_insn = &ctx->ir_base[condition_insn->op1]; |
3695 | 0 | if (!IR_IS_SYM_CONST(val_insn->op) && val_insn->val.u64 == 1) { |
3696 | 0 | return IR_TRUE; |
3697 | 0 | } |
3698 | 114k | } else if (condition_insn->op == IR_SHR |
3699 | 0 | && ctx->use_lists[condition].count == 1 |
3700 | 0 | && IR_IS_CONST_REF(condition_insn->op2) |
3701 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[condition_insn->op2].op)) { |
3702 | 0 | uint64_t c1, c2 = ctx->ir_base[condition_insn->op2].val.u64; |
3703 | 0 | ir_insn *op1_insn = &ctx->ir_base[condition_insn->op1]; |
3704 | 0 | ir_val val = {0}; |
3705 | |
|
3706 | 0 | if (op1_insn->op == IR_SHL |
3707 | 0 | && ctx->use_lists[condition_insn->op1].count == 1 |
3708 | 0 | && IR_IS_CONST_REF(op1_insn->op2) |
3709 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[op1_insn->op2].op)) { |
3710 | | /* IF(SHR(SHL(X, C1), C2)) => IF(AND(X, ((-1) << C2) >> C1) */ |
3711 | 0 | c1 = ctx->ir_base[op1_insn->op2].val.u64; |
3712 | 0 | if (op1_insn->op1 > 0) { |
3713 | 0 | ir_use_list_replace_one(ctx, op1_insn->op1, condition_insn->op1, condition); |
3714 | 0 | } |
3715 | 0 | CLEAR_USES(condition_insn->op1); |
3716 | 0 | condition_insn->op1 = op1_insn->op1; |
3717 | 0 | MAKE_NOP(op1_insn); |
3718 | 0 | } else if (op1_insn->op == IR_ADD |
3719 | 0 | && ctx->use_lists[condition_insn->op1].count == 1 |
3720 | 0 | && op1_insn->op1 == op1_insn->op2) { |
3721 | | /* IF(SHR(ADD(X, X), C2)) => IF(AND(X, ((-1) << C2) >> 1) */ |
3722 | 0 | c1 = 1; |
3723 | 0 | if (op1_insn->op1 > 0) { |
3724 | 0 | ir_use_list_replace_one(ctx, op1_insn->op1, condition_insn->op1, condition); |
3725 | 0 | ir_use_list_remove_one(ctx, op1_insn->op1, condition_insn->op1); |
3726 | 0 | } |
3727 | 0 | CLEAR_USES(condition_insn->op1); |
3728 | 0 | condition_insn->op1 = op1_insn->op1; |
3729 | 0 | MAKE_NOP(op1_insn); |
3730 | 0 | } else if (op1_insn->op == IR_MUL |
3731 | 0 | && ctx->use_lists[condition_insn->op1].count == 1 |
3732 | 0 | && IR_IS_CONST_REF(op1_insn->op2) |
3733 | 0 | && !IR_IS_SYM_CONST(ctx->ir_base[op1_insn->op2].op) |
3734 | 0 | && IR_IS_POWER_OF_TWO(ctx->ir_base[op1_insn->op2].val.u64)) { |
3735 | | /* IF(SHR(MUL(X, C1), C2)) => IF(AND(X, ((-1) << C2) >> log2(C1)) */ |
3736 | 0 | c1 = IR_LOG2(ctx->ir_base[op1_insn->op2].val.u64); |
3737 | 0 | if (op1_insn->op1 > 0) { |
3738 | 0 | ir_use_list_replace_one(ctx, op1_insn->op1, condition_insn->op1, condition); |
3739 | 0 | } |
3740 | 0 | CLEAR_USES(condition_insn->op1); |
3741 | 0 | condition_insn->op1 = op1_insn->op1; |
3742 | 0 | MAKE_NOP(op1_insn); |
3743 | 0 | } else { |
3744 | | /* IF(SHR(X, C2)) => IF(AND(X, (-1) << C2) */ |
3745 | 0 | c1 = 0; |
3746 | 0 | } |
3747 | |
|
3748 | 0 | switch (ir_type_size[condition_insn->type]) { |
3749 | 0 | case 1: |
3750 | 0 | val.u64 = (uint8_t)((uint8_t)((uint8_t)(-1) << ((uint8_t)c2 & 7)) >> ((uint8_t)c1 & 7)); |
3751 | 0 | break; |
3752 | 0 | case 2: |
3753 | 0 | val.u64 = (uint16_t)((uint16_t)((uint16_t)(-1) << ((uint16_t)c2 & 0xf)) >> ((uint16_t)c1 & 0xf)); |
3754 | 0 | break; |
3755 | 0 | case 4: |
3756 | 0 | val.u64 = (uint32_t)((uint32_t)((uint32_t)(-1) << ((uint32_t)c2 & 0x1f)) >> ((uint32_t)c1 & 0x1f)); |
3757 | 0 | break; |
3758 | 0 | case 8: |
3759 | 0 | val.u64 = (((uint64_t)(-1) << (c2 & 0x3f)) >> (c1 & 0x3f)); |
3760 | 0 | break; |
3761 | 0 | default: |
3762 | 0 | IR_ASSERT(0); |
3763 | 0 | } |
3764 | 0 | if (IR_IS_TYPE_SIGNED(condition_insn->type)) { |
3765 | 0 | val.u64 = ir_fix_signed(condition_insn->type, val.u64); |
3766 | 0 | } |
3767 | |
|
3768 | 0 | ir_ref c = ir_const(ctx, val, condition_insn->type); |
3769 | 0 | condition_insn = &ctx->ir_base[condition]; |
3770 | 0 | condition_insn->op = IR_AND; |
3771 | 0 | condition_insn->op2 = c; |
3772 | 0 | } |
3773 | | |
3774 | 114k | if (condition_insn->op == IR_AND && IR_IS_CONST_REF(condition_insn->op2)) { |
3775 | 36.2k | ir_insn *val_insn = &ctx->ir_base[condition_insn->op2]; |
3776 | 36.2k | ir_insn *op1_insn = &ctx->ir_base[condition_insn->op1]; |
3777 | | |
3778 | 36.2k | if (!IR_IS_SYM_CONST(val_insn->op) |
3779 | 36.2k | && ctx->use_lists[condition].count == 1 |
3780 | 36.1k | && ctx->use_lists[condition_insn->op1].count == 1 |
3781 | 2 | && (op1_insn->op == IR_ZEXT || op1_insn->op == IR_SEXT) |
3782 | 0 | && val_insn->val.u64 <= (((uint64_t)(0-1ULL)) >> (64 - ir_type_size[ctx->ir_base[op1_insn->op1].type] * 8))) { |
3783 | | /* IF(AND(ZEXT(X), C)) => IF(AND(X, C)) */ |
3784 | 0 | if (op1_insn->op1 > 0) { |
3785 | 0 | ir_use_list_replace_one(ctx, op1_insn->op1, condition_insn->op1, condition); |
3786 | 0 | } |
3787 | 0 | ir_ref op1_ref = condition_insn->op1; |
3788 | 0 | CLEAR_USES(condition_insn->op1); |
3789 | 0 | condition_insn->type = ctx->ir_base[op1_insn->op1].type; |
3790 | 0 | condition_insn->op1 = op1_insn->op1; |
3791 | 0 | ir_val val = val_insn->val; |
3792 | 0 | if (IR_IS_TYPE_SIGNED(condition_insn->type)) { |
3793 | 0 | val.u64 = ir_fix_signed(condition_insn->type, val.u64); |
3794 | 0 | } |
3795 | 0 | ir_ref c = ir_const(ctx, val, condition_insn->type); |
3796 | 0 | condition_insn = &ctx->ir_base[condition]; |
3797 | 0 | condition_insn->op2 = c; |
3798 | 0 | ir_bitqueue_add(ctx->iter_worklist, condition); |
3799 | 0 | MAKE_NOP(&ctx->ir_base[op1_ref]); |
3800 | 0 | return condition; |
3801 | 0 | } |
3802 | 36.2k | } |
3803 | | |
3804 | 114k | while (((condition_insn->op == IR_BITCAST && IR_IS_TYPE_SCALAR(ctx->ir_base[condition_insn->op1].type)) |
3805 | 114k | || condition_insn->op == IR_ZEXT |
3806 | 114k | || condition_insn->op == IR_SEXT) |
3807 | 0 | && ctx->use_lists[condition].count == 1) { |
3808 | 0 | condition = condition_insn->op1; |
3809 | 0 | condition_insn = &ctx->ir_base[condition]; |
3810 | 0 | } |
3811 | | |
3812 | 114k | if (condition_insn->op == IR_ALLOCA || condition_insn->op == IR_VADDR) { |
3813 | 0 | return IR_TRUE; |
3814 | 0 | } |
3815 | | |
3816 | 114k | if (!IR_IS_CONST_REF(condition) && ctx->use_lists[condition].count > 1) { |
3817 | 50.2k | condition = ir_check_dominating_predicates(ctx, control, condition); |
3818 | 50.2k | } |
3819 | | |
3820 | 114k | return condition; |
3821 | 114k | } |
3822 | | |
3823 | | static void ir_iter_optimize_if(ir_ctx *ctx, ir_ref ref, ir_insn *insn) |
3824 | 114k | { |
3825 | 114k | bool swap = 0; |
3826 | 114k | ir_ref condition = ir_iter_optimize_condition(ctx, insn->op1, insn->op2, &swap); |
3827 | | |
3828 | 114k | insn = &ctx->ir_base[ref]; |
3829 | | |
3830 | 114k | if (swap) { |
3831 | 14 | ir_use_list *use_list = &ctx->use_lists[ref]; |
3832 | 14 | ir_ref *p, use; |
3833 | | |
3834 | 14 | IR_ASSERT(use_list->count == 2); |
3835 | 14 | p = ctx->use_edges + use_list->refs; |
3836 | 14 | use = *p; |
3837 | 14 | if (ctx->ir_base[use].op == IR_IF_TRUE) { |
3838 | 10 | ctx->ir_base[use].op = IR_IF_FALSE; |
3839 | 10 | use = *(p+1); |
3840 | 10 | ctx->ir_base[use].op = IR_IF_TRUE; |
3841 | 10 | } else { |
3842 | 4 | ctx->ir_base[use].op = IR_IF_TRUE; |
3843 | 4 | use = *(p+1); |
3844 | 4 | ctx->ir_base[use].op = IR_IF_FALSE; |
3845 | 4 | } |
3846 | 14 | } |
3847 | | |
3848 | 114k | if (IR_IS_CONST_REF(condition)) { |
3849 | | /* |
3850 | | * | | |
3851 | | * IF(TRUE) => END |
3852 | | * | \ | |
3853 | | * | +------+ | |
3854 | | * | IF_TRUE | BEGIN(unreachable) |
3855 | | * IF_FALSE | BEGIN |
3856 | | * | | |
3857 | | */ |
3858 | 10 | ir_ref if_true_ref, if_false_ref; |
3859 | 10 | ir_insn *if_true, *if_false; |
3860 | | |
3861 | 10 | insn->optx = IR_OPTX(IR_END, IR_VOID, 1); |
3862 | 10 | if (!IR_IS_CONST_REF(insn->op2)) { |
3863 | 0 | ir_use_list_remove_one(ctx, insn->op2, ref); |
3864 | 0 | ir_bitqueue_add(ctx->iter_worklist, insn->op2); |
3865 | 0 | } |
3866 | 10 | insn->op2 = IR_UNUSED; |
3867 | | |
3868 | 10 | ir_get_true_false_refs(ctx, ref, &if_true_ref, &if_false_ref); |
3869 | 10 | if_true = &ctx->ir_base[if_true_ref]; |
3870 | 10 | if_false = &ctx->ir_base[if_false_ref]; |
3871 | 10 | if_true->op = IR_BEGIN; |
3872 | 10 | if_true->op2 = IR_UNUSED; |
3873 | 10 | if_false->op = IR_BEGIN; |
3874 | 10 | if_false->op2 = IR_UNUSED; |
3875 | 10 | if (ir_ref_is_true(ctx, condition)) { |
3876 | 3 | if_false->op1 = IR_UNUSED; |
3877 | 3 | ir_use_list_remove_one(ctx, ref, if_false_ref); |
3878 | 3 | ir_bitqueue_add(ctx->iter_worklist, if_true_ref); |
3879 | 7 | } else { |
3880 | 7 | if_true->op1 = IR_UNUSED; |
3881 | 7 | ir_use_list_remove_one(ctx, ref, if_true_ref); |
3882 | 7 | ir_bitqueue_add(ctx->iter_worklist, if_false_ref); |
3883 | 7 | } |
3884 | 10 | ctx->flags2 &= ~IR_CFG_REACHABLE; |
3885 | 114k | } else if (insn->op2 != condition) { |
3886 | 14 | ir_iter_update_op(ctx, ref, 2, condition); |
3887 | 14 | } |
3888 | 114k | } |
3889 | | |
3890 | | static void ir_iter_optimize_guard(ir_ctx *ctx, ir_ref ref, ir_insn *insn) |
3891 | 0 | { |
3892 | 0 | bool swap = 0; |
3893 | 0 | ir_ref condition = ir_iter_optimize_condition(ctx, insn->op1, insn->op2, &swap); |
3894 | |
|
3895 | 0 | insn = &ctx->ir_base[ref]; |
3896 | |
|
3897 | 0 | if (swap) { |
3898 | 0 | if (insn->op == IR_GUARD) { |
3899 | 0 | insn->op = IR_GUARD_NOT; |
3900 | 0 | } else { |
3901 | 0 | insn->op = IR_GUARD; |
3902 | 0 | } |
3903 | 0 | } |
3904 | |
|
3905 | 0 | if (IR_IS_CONST_REF(condition)) { |
3906 | 0 | if (insn->op == IR_GUARD) { |
3907 | 0 | if (ir_ref_is_true(ctx, condition)) { |
3908 | 0 | ir_ref prev, next; |
3909 | |
|
3910 | 0 | remove_guard: |
3911 | 0 | prev = insn->op1; |
3912 | 0 | next = ir_next_control(ctx, ref); |
3913 | 0 | if (ctx->ir_base[prev].op == IR_SNAPSHOT) { |
3914 | 0 | ir_ref snapshot = prev; |
3915 | 0 | prev = ctx->ir_base[prev].op1; |
3916 | 0 | ir_use_list_remove_one(ctx, snapshot, ref); |
3917 | 0 | ir_use_list_remove_one(ctx, ref, next); |
3918 | 0 | ir_use_list_replace_one(ctx, prev, snapshot, next); |
3919 | 0 | ir_iter_remove_insn(ctx, snapshot); |
3920 | 0 | } else { |
3921 | 0 | ir_use_list_remove_one(ctx, ref, next); |
3922 | 0 | ir_use_list_replace_one(ctx, prev, ref, next); |
3923 | 0 | } |
3924 | 0 | ctx->ir_base[next].op1 = prev; |
3925 | 0 | insn->op1 = IR_UNUSED; |
3926 | |
|
3927 | 0 | if (!IR_IS_CONST_REF(insn->op2)) { |
3928 | 0 | ir_use_list_remove_one(ctx, insn->op2, ref); |
3929 | 0 | if (ir_is_dead(ctx, insn->op2)) { |
3930 | | /* schedule DCE */ |
3931 | 0 | ir_bitqueue_add(ctx->iter_worklist, insn->op2); |
3932 | 0 | } |
3933 | 0 | } |
3934 | |
|
3935 | 0 | if (!IR_IS_CONST_REF(insn->op3)) { |
3936 | 0 | ir_use_list_remove_one(ctx, insn->op3, ref); |
3937 | 0 | if (ir_is_dead(ctx, insn->op3)) { |
3938 | | /* schedule DCE */ |
3939 | 0 | ir_bitqueue_add(ctx->iter_worklist, insn->op3); |
3940 | 0 | } |
3941 | 0 | } |
3942 | |
|
3943 | 0 | MAKE_NOP(insn); |
3944 | 0 | return; |
3945 | 0 | } else { |
3946 | 0 | condition = IR_FALSE; |
3947 | 0 | } |
3948 | 0 | } else { |
3949 | 0 | if (ir_ref_is_true(ctx, condition)) { |
3950 | 0 | condition = IR_TRUE; |
3951 | 0 | } else { |
3952 | 0 | goto remove_guard; |
3953 | 0 | } |
3954 | 0 | } |
3955 | 0 | } |
3956 | | |
3957 | 0 | if (insn->op2 != condition) { |
3958 | 0 | ir_iter_update_op(ctx, ref, 2, condition); |
3959 | 0 | } |
3960 | 0 | } |
3961 | | |
3962 | | void ir_iter_opt(ir_ctx *ctx, ir_bitqueue *worklist) |
3963 | 9.93k | { |
3964 | 9.93k | ir_ref i, val; |
3965 | 9.93k | ir_insn *insn; |
3966 | | |
3967 | 9.93k | ctx->iter_worklist = worklist; |
3968 | 201k | while ((i = ir_bitqueue_pop(ctx->iter_worklist)) >= 0) { |
3969 | 191k | insn = &ctx->ir_base[i]; |
3970 | 191k | if (IR_IS_FOLDABLE_OP(insn->op)) { |
3971 | 277 | if (ctx->use_lists[i].count == 0) { |
3972 | 183 | if (insn->op == IR_PHI) { |
3973 | 0 | ir_bitqueue_add(ctx->iter_worklist, insn->op1); |
3974 | 0 | } |
3975 | 183 | ir_iter_remove_insn(ctx, i); |
3976 | 183 | } else { |
3977 | 94 | insn = &ctx->ir_base[i]; |
3978 | 94 | switch (insn->op) { |
3979 | 0 | case IR_FP2FP: |
3980 | 0 | if (insn->type == IR_FLOAT) { |
3981 | 0 | if (ir_may_promote_d2f(ctx, insn->op1)) { |
3982 | 0 | ir_ref ref = ir_promote_d2f(ctx, insn->op1, i); |
3983 | 0 | insn = &ctx->ir_base[i]; |
3984 | 0 | insn->op1 = ref; |
3985 | 0 | ir_iter_replace_insn(ctx, i, ref); |
3986 | 0 | break; |
3987 | 0 | } |
3988 | 0 | } else if (insn->type == IR_DOUBLE) { |
3989 | 0 | if (ir_may_promote_f2d(ctx, insn->op1)) { |
3990 | 0 | ir_ref ref = ir_promote_f2d(ctx, insn->op1, i); |
3991 | 0 | insn = &ctx->ir_base[i]; |
3992 | 0 | insn->op1 = ref; |
3993 | 0 | ir_iter_replace_insn(ctx, i, ref); |
3994 | 0 | break; |
3995 | 0 | } |
3996 | 0 | } |
3997 | 0 | goto folding; |
3998 | 0 | case IR_FP2INT: |
3999 | 0 | if (ctx->ir_base[insn->op1].type == IR_DOUBLE) { |
4000 | 0 | if (ir_may_promote_d2f(ctx, insn->op1)) { |
4001 | 0 | ir_ref ref = ir_promote_d2f(ctx, insn->op1, i); |
4002 | 0 | insn = &ctx->ir_base[i]; |
4003 | 0 | insn->op1 = ref; |
4004 | 0 | } |
4005 | 0 | } else if (ctx->ir_base[insn->op1].type == IR_FLOAT) { |
4006 | 0 | if (ir_may_promote_f2d(ctx, insn->op1)) { |
4007 | 0 | ir_ref ref = ir_promote_f2d(ctx, insn->op1, i); |
4008 | 0 | insn = &ctx->ir_base[i]; |
4009 | 0 | insn->op1 = ref; |
4010 | 0 | } |
4011 | 0 | } |
4012 | 0 | goto folding; |
4013 | 80 | case IR_TRUNC: |
4014 | 80 | if (ir_may_promote_trunc(ctx, insn->type, insn->op1)) { |
4015 | 4 | ir_ref ref = ir_promote_i2i(ctx, insn->type, insn->op1, i); |
4016 | 4 | insn = &ctx->ir_base[i]; |
4017 | 4 | insn->op1 = ref; |
4018 | 4 | ir_iter_replace_insn(ctx, i, ref); |
4019 | 4 | break; |
4020 | 4 | } |
4021 | 76 | goto folding; |
4022 | 76 | case IR_SEXT: |
4023 | 0 | case IR_ZEXT: |
4024 | 0 | if (ir_try_promote_ext(ctx, i, insn)) { |
4025 | 0 | break; |
4026 | 0 | } |
4027 | 0 | goto folding; |
4028 | 0 | case IR_PHI: |
4029 | 0 | break; |
4030 | 14 | default: |
4031 | 90 | folding: |
4032 | 90 | ir_iter_fold(ctx, i); |
4033 | 90 | break; |
4034 | 94 | } |
4035 | 94 | } |
4036 | 191k | } else if (ir_op_flags[insn->op] & IR_OP_FLAG_BB_START) { |
4037 | 76.9k | if (!(ctx->flags & IR_OPT_CFG)) { |
4038 | | /* pass */ |
4039 | 76.9k | } else if (insn->op == IR_BEGIN) { |
4040 | 1.18k | if (insn->op1 |
4041 | 1.17k | && !insn->op2 /* no computed goto label */ |
4042 | 1.17k | && ctx->use_lists[i].count == 1 |
4043 | 1.17k | && ctx->ir_base[insn->op1].op == IR_END) { |
4044 | 1.17k | ir_merge_blocks(ctx, insn->op1, i); |
4045 | 1.17k | } |
4046 | 75.7k | } else if (insn->op == IR_MERGE) { |
4047 | 72.5k | ir_iter_optimize_merge(ctx, i, insn); |
4048 | 72.5k | } else if (insn->op == IR_LOOP_BEGIN) { |
4049 | 3.21k | ir_iter_optimize_loop(ctx, i, insn); |
4050 | 3.21k | } |
4051 | 114k | } else if (ir_is_dead_load(ctx, i)) { |
4052 | 110 | ir_ref next; |
4053 | | |
4054 | | /* remove LOAD from double linked control list */ |
4055 | 110 | remove_mem_insn: |
4056 | 110 | next = ctx->use_edges[ctx->use_lists[i].refs]; |
4057 | 110 | IR_ASSERT(ctx->use_lists[i].count == 1); |
4058 | 110 | ctx->ir_base[next].op1 = insn->op1; |
4059 | 110 | ir_use_list_replace_one(ctx, insn->op1, i, next); |
4060 | 110 | insn->op1 = IR_UNUSED; |
4061 | 110 | ir_iter_remove_insn(ctx, i); |
4062 | 114k | } else if (insn->op == IR_LOAD) { |
4063 | 15 | val = ir_find_aliasing_load(ctx, insn->op1, insn->type, insn->op2); |
4064 | 15 | if (val) { |
4065 | 7 | ir_insn *val_insn; |
4066 | 7 | ir_ref prev, next; |
4067 | | |
4068 | 7 | remove_aliased_load: |
4069 | 7 | prev = insn->op1; |
4070 | 7 | next = ir_next_control(ctx, i); |
4071 | 7 | ctx->ir_base[next].op1 = prev; |
4072 | 7 | ir_use_list_remove_one(ctx, i, next); |
4073 | 7 | ir_use_list_replace_one(ctx, prev, i, next); |
4074 | 7 | insn->op1 = IR_UNUSED; |
4075 | | |
4076 | 7 | val_insn = &ctx->ir_base[val]; |
4077 | 7 | if (val_insn->type == insn->type) { |
4078 | 3 | ir_iter_replace_insn(ctx, i, val); |
4079 | 4 | } else { |
4080 | 4 | if (!IR_IS_CONST_REF(insn->op2)) { |
4081 | 4 | ir_use_list_remove_one(ctx, insn->op2, i); |
4082 | 4 | if (ir_is_dead(ctx, insn->op2)) { |
4083 | | /* schedule DCE */ |
4084 | 0 | ir_bitqueue_add(ctx->iter_worklist, insn->op2); |
4085 | 0 | } |
4086 | 4 | } |
4087 | 4 | if (!IR_IS_CONST_REF(val)) { |
4088 | 0 | ir_use_list_add(ctx, val, i); |
4089 | 0 | } |
4090 | 4 | if (ir_get_type_size(val_insn->type) == ir_get_type_size(insn->type)) { |
4091 | | /* load forwarding with bitcast (L2L) */ |
4092 | 0 | insn->optx = IR_OPTX(IR_BITCAST, insn->type, 1); |
4093 | 4 | } else { |
4094 | | /* partial load forwarding (L2L) */ |
4095 | 4 | insn->optx = IR_OPTX(IR_TRUNC, insn->type, 1); |
4096 | 4 | } |
4097 | 4 | insn->op1 = val; |
4098 | 4 | insn->op2 = IR_UNUSED; |
4099 | 4 | ir_bitqueue_add(ctx->iter_worklist, i); |
4100 | 4 | ir_iter_add_uses(ctx, i, ctx->iter_worklist); |
4101 | 4 | } |
4102 | 7 | } |
4103 | 114k | } else if (insn->op == IR_STORE) { |
4104 | 0 | if (ir_find_aliasing_store(ctx, insn->op1, insn->op2, insn->op3)) { |
4105 | 0 | goto remove_mem_insn; |
4106 | 0 | } else { |
4107 | 0 | ir_insn *val_insn; |
4108 | |
|
4109 | 0 | remove_bitcast: |
4110 | 0 | val = insn->op3; |
4111 | 0 | val_insn = &ctx->ir_base[val]; |
4112 | 0 | if (val_insn->op == IR_BITCAST |
4113 | 0 | && ir_get_type_size(val_insn->type) == ir_get_type_size(ctx->ir_base[val_insn->op1].type)) { |
4114 | 0 | insn->op3 = val_insn->op1; |
4115 | 0 | ir_use_list_remove_one(ctx, val, i); |
4116 | 0 | if (ctx->use_lists[val].count == 0) { |
4117 | 0 | if (!IR_IS_CONST_REF(val_insn->op1)) { |
4118 | 0 | ir_use_list_replace_one(ctx, val_insn->op1, val, i); |
4119 | 0 | } |
4120 | 0 | ir_iter_remove_insn(ctx, val); |
4121 | 0 | } else { |
4122 | 0 | if (!IR_IS_CONST_REF(val_insn->op1)) { |
4123 | 0 | ir_use_list_add(ctx, val_insn->op1, i); |
4124 | 0 | } |
4125 | 0 | } |
4126 | 0 | } |
4127 | 0 | } |
4128 | 114k | } else if (insn->op == IR_VLOAD) { |
4129 | 0 | val = ir_find_aliasing_vload(ctx, insn->op1, insn->type, insn->op2); |
4130 | 0 | if (val) { |
4131 | 0 | goto remove_aliased_load; |
4132 | 0 | } |
4133 | 114k | } else if (insn->op == IR_VSTORE) { |
4134 | 0 | if (ir_find_aliasing_vstore(ctx, insn->op1, insn->op2, insn->op3)) { |
4135 | 0 | goto remove_mem_insn; |
4136 | 0 | } else { |
4137 | 0 | goto remove_bitcast; |
4138 | 0 | } |
4139 | 114k | } else if (insn->op == IR_IF) { |
4140 | 114k | ir_iter_optimize_if(ctx, i, insn); |
4141 | 114k | } else if (insn->op == IR_GUARD || insn->op == IR_GUARD_NOT) { |
4142 | 0 | ir_iter_optimize_guard(ctx, i, insn); |
4143 | 0 | } |
4144 | 191k | } |
4145 | 9.93k | ctx->iter_worklist = NULL; |
4146 | 9.93k | } |
4147 | | |
4148 | | void ir_iter_cleanup(ir_ctx *ctx) |
4149 | 0 | { |
4150 | 0 | ir_bitqueue iter_worklist; |
4151 | 0 | ir_bitqueue cfg_worklist; |
4152 | 0 | ir_ref i, n; |
4153 | 0 | ir_insn *insn; |
4154 | |
|
4155 | 0 | ir_bitqueue_init(&cfg_worklist, ctx->insns_count); |
4156 | 0 | ir_bitqueue_init(&iter_worklist, ctx->insns_count); |
4157 | | |
4158 | | /* Remove unused nodes */ |
4159 | 0 | ctx->iter_worklist = &iter_worklist; |
4160 | 0 | for (i = IR_UNUSED + 1, insn = ctx->ir_base + i; i < ctx->insns_count;) { |
4161 | 0 | if (IR_IS_FOLDABLE_OP(insn->op)) { |
4162 | 0 | if (insn->op != IR_NOP && ctx->use_lists[i].count == 0) { |
4163 | 0 | ir_iter_remove_insn(ctx, i); |
4164 | 0 | } |
4165 | 0 | } else if (insn->op == IR_IF || insn->op == IR_MERGE) { |
4166 | 0 | ir_bitqueue_add(&cfg_worklist, i); |
4167 | 0 | } |
4168 | 0 | n = insn->inputs_count; |
4169 | 0 | n = ir_insn_inputs_to_len(n); |
4170 | 0 | i += n; |
4171 | 0 | insn += n; |
4172 | 0 | } |
4173 | |
|
4174 | 0 | while ((i = ir_bitqueue_pop(&iter_worklist)) >= 0) { |
4175 | 0 | insn = &ctx->ir_base[i]; |
4176 | 0 | if (IR_IS_FOLDABLE_OP(insn->op)) { |
4177 | 0 | if (ctx->use_lists[i].count == 0) { |
4178 | 0 | ir_iter_remove_insn(ctx, i); |
4179 | 0 | } |
4180 | 0 | } |
4181 | 0 | } |
4182 | |
|
4183 | 0 | ctx->iter_worklist = NULL; |
4184 | 0 | ir_bitqueue_free(&iter_worklist); |
4185 | | |
4186 | | /* Cleanup Control Flow */ |
4187 | 0 | ir_iter_opt(ctx, &cfg_worklist); |
4188 | 0 | ir_bitqueue_free(&cfg_worklist); |
4189 | 0 | } |
4190 | | |
4191 | | int ir_sccp(ir_ctx *ctx) |
4192 | 9.93k | { |
4193 | 9.93k | ir_bitqueue sccp_worklist, iter_worklist; |
4194 | 9.93k | ir_sccp_val *_values; |
4195 | | |
4196 | 9.93k | ir_bitqueue_init(&iter_worklist, ctx->insns_count); |
4197 | 9.93k | ir_bitqueue_init(&sccp_worklist, ctx->insns_count); |
4198 | 9.93k | _values = ir_mem_calloc(ctx->insns_count, sizeof(ir_sccp_val)); |
4199 | | |
4200 | 9.93k | ctx->flags2 |= IR_OPT_IN_SCCP; |
4201 | 9.93k | ir_sccp_analyze(ctx, _values, &sccp_worklist, &iter_worklist); |
4202 | 9.93k | ir_sccp_transform(ctx, _values, &sccp_worklist, &iter_worklist); |
4203 | 9.93k | ctx->flags2 &= ~IR_OPT_IN_SCCP; |
4204 | | |
4205 | 9.93k | ir_mem_free(_values); |
4206 | 9.93k | ir_bitqueue_free(&sccp_worklist); |
4207 | | |
4208 | 9.93k | ctx->flags2 |= IR_CFG_REACHABLE; |
4209 | | |
4210 | 9.93k | ir_iter_opt(ctx, &iter_worklist); |
4211 | | |
4212 | 9.93k | ir_bitqueue_free(&iter_worklist); |
4213 | | |
4214 | 9.93k | return 1; |
4215 | 9.93k | } |