1 /* Post-reload compare elimination.
2 Copyright (C) 2010-2022 Free Software Foundation, Inc.
3
4 This file is part of GCC.
5
6 GCC is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
9 version.
10
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3. If not see
18 <http://www.gnu.org/licenses/>. */
19
20 /* There is a set of targets whose general-purpose move or addition
21 instructions clobber the flags. These targets cannot split their
22 CBRANCH/CSTORE etc patterns before reload is complete, lest reload
23 itself insert these instructions in between the flags setter and user.
24 Because these targets cannot split the compare from the use, they
25 cannot make use of the comparison elimination offered by the combine pass.
26
27 This is a small pass intended to provide comparison elimination similar to
28 what was available via NOTICE_UPDATE_CC for cc0 targets.
29
30 This pass assumes:
31
32 (0) CBRANCH/CSTORE etc have been split in pass_split_after_reload.
33
34 (1) All comparison patterns are represented as
35
36 [(set (reg:CC) (compare:CC (reg) (reg_or_immediate)))]
37
38 (2) All insn patterns that modify the flags are represented as
39
40 [(set (reg) (operation)
41 (clobber (reg:CC))]
42
43 (3) If an insn of form (2) can usefully set the flags, there is
44 another pattern of the form
45
46 [(set (reg:CCM) (compare:CCM (operation) (immediate)))
47 (set (reg) (operation)]
48
49 The mode CCM will be chosen as if by SELECT_CC_MODE.
50
51 Note that unlike NOTICE_UPDATE_CC, we do not handle memory operands.
52 This could be handled as a future enhancement.
53 */
54
55 #include "config.h"
56 #include "system.h"
57 #include "coretypes.h"
58 #include "backend.h"
59 #include "target.h"
60 #include "rtl.h"
61 #include "df.h"
62 #include "memmodel.h"
63 #include "tm_p.h"
64 #include "insn-config.h"
65 #include "recog.h"
66 #include "emit-rtl.h"
67 #include "cfgrtl.h"
68 #include "tree-pass.h"
69 #include "domwalk.h"
70
71
72 /* These structures describe a comparison and how it is used. */
73
74 /* The choice of maximum 3 uses comes from wanting to eliminate the two
75 duplicate compares from a three-way branch on the sign of a value.
76 This is also sufficient to eliminate the duplicate compare against the
77 high-part of a double-word comparison. */
78 #define MAX_CMP_USE 3
79
80 struct comparison_use
81 {
82 /* The instruction in which the result of the compare is used. */
83 rtx_insn *insn;
84 /* The location of the flags register within the use. */
85 rtx *loc;
86 /* The comparison code applied against the flags register. */
87 enum rtx_code code;
88 };
89
90 struct comparison
91 {
92 /* The comparison instruction. */
93 rtx_insn *insn;
94
95 /* The insn prior to the comparison insn that clobbers the flags. */
96 rtx_insn *prev_clobber;
97
98 /* The insn prior to the comparison insn that sets in_a REG. */
99 rtx_insn *in_a_setter;
100
101 /* The two values being compared. These will be either REGs or
102 constants. */
103 rtx in_a, in_b;
104
105 /* The REG_EH_REGION of the comparison. */
106 rtx eh_note;
107
108 /* Information about how this comparison is used. */
109 struct comparison_use uses[MAX_CMP_USE];
110
111 /* The original CC_MODE for this comparison. */
112 machine_mode orig_mode;
113
114 /* The number of uses identified for this comparison. */
115 unsigned short n_uses;
116
117 /* True if not all uses of this comparison have been identified.
118 This can happen either for overflowing the array above, or if
119 the flags register is used in some unusual context. */
120 bool missing_uses;
121
122 /* True if its inputs are still valid at the end of the block. */
123 bool inputs_valid;
124
125 /* Whether IN_A is wrapped in a NOT before being compared. */
126 bool not_in_a;
127 };
128
129 static vec<comparison *> all_compares;
130
131 /* Return whether X is a NOT unary expression. */
132
133 static bool
is_not(rtx x)134 is_not (rtx x)
135 {
136 return GET_CODE (x) == NOT;
137 }
138
139 /* Strip a NOT unary expression around X, if any. */
140
141 static rtx
strip_not(rtx x)142 strip_not (rtx x)
143 {
144 if (is_not (x))
145 return XEXP (x, 0);
146
147 return x;
148 }
149
150 /* Look for a "conforming" comparison, as defined above. If valid, return
151 the rtx for the COMPARE itself. */
152
153 static rtx
conforming_compare(rtx_insn * insn)154 conforming_compare (rtx_insn *insn)
155 {
156 rtx set, src, dest;
157
158 set = single_set (insn);
159 if (set == NULL)
160 return NULL;
161
162 src = SET_SRC (set);
163 if (GET_CODE (src) != COMPARE)
164 return NULL;
165
166 dest = SET_DEST (set);
167 if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum)
168 return NULL;
169
170 if (!REG_P (strip_not (XEXP (src, 0))))
171 return NULL;
172
173 if (CONSTANT_P (XEXP (src, 1)) || REG_P (XEXP (src, 1)))
174 return src;
175
176 if (GET_CODE (XEXP (src, 1)) == UNSPEC)
177 {
178 for (int i = 0; i < XVECLEN (XEXP (src, 1), 0); i++)
179 if (!REG_P (XVECEXP (XEXP (src, 1), 0, i)))
180 return NULL;
181 return src;
182 }
183
184 return NULL;
185 }
186
187 /* Look for a pattern of the "correct" form for an insn with a flags clobber
188 for which we may be able to eliminate a compare later. We're not looking
189 to validate any inputs at this time, merely see that the basic shape is
190 correct. The term "arithmetic" may be somewhat misleading... */
191
192 static bool
arithmetic_flags_clobber_p(rtx_insn * insn)193 arithmetic_flags_clobber_p (rtx_insn *insn)
194 {
195 rtx pat, x;
196
197 if (!NONJUMP_INSN_P (insn))
198 return false;
199 pat = PATTERN (insn);
200 if (asm_noperands (pat) >= 0)
201 return false;
202
203 if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) == 2)
204 {
205 x = XVECEXP (pat, 0, 0);
206 if (GET_CODE (x) != SET)
207 return false;
208 x = SET_DEST (x);
209 if (!REG_P (x))
210 return false;
211
212 x = XVECEXP (pat, 0, 1);
213 if (GET_CODE (x) == CLOBBER)
214 {
215 x = XEXP (x, 0);
216 if (REG_P (x) && REGNO (x) == targetm.flags_regnum)
217 return true;
218 }
219 }
220
221 return false;
222 }
223
224 /* Look for uses of FLAGS in INSN. If we find one we can analyze, record
225 it in CMP; otherwise indicate that we've missed a use. */
226
227 static void
find_flags_uses_in_insn(struct comparison * cmp,rtx_insn * insn)228 find_flags_uses_in_insn (struct comparison *cmp, rtx_insn *insn)
229 {
230 df_ref use;
231
232 /* If we've already lost track of uses, don't bother collecting more. */
233 if (cmp->missing_uses)
234 return;
235
236 /* Find a USE of the flags register. */
237 FOR_EACH_INSN_USE (use, insn)
238 if (DF_REF_REGNO (use) == targetm.flags_regnum)
239 {
240 rtx x, *loc;
241
242 /* If this is an unusual use, quit. */
243 if (DF_REF_TYPE (use) != DF_REF_REG_USE)
244 goto fail;
245
246 /* If we've run out of slots to record uses, quit. */
247 if (cmp->n_uses == MAX_CMP_USE)
248 goto fail;
249
250 /* Unfortunately the location of the flags register, while present
251 in the reference structure, doesn't help. We need to find the
252 comparison code that is outer to the actual flags use. */
253 loc = DF_REF_LOC (use);
254 x = PATTERN (insn);
255 if (GET_CODE (x) == PARALLEL)
256 x = XVECEXP (x, 0, 0);
257 x = SET_SRC (x);
258 if (GET_CODE (x) == IF_THEN_ELSE)
259 x = XEXP (x, 0);
260 if (COMPARISON_P (x)
261 && loc == &XEXP (x, 0)
262 && XEXP (x, 1) == const0_rtx)
263 {
264 /* We've found a use of the flags that we understand. */
265 struct comparison_use *cuse = &cmp->uses[cmp->n_uses++];
266 cuse->insn = insn;
267 cuse->loc = loc;
268 cuse->code = GET_CODE (x);
269 }
270 else
271 goto fail;
272 }
273 return;
274
275 fail:
276 /* We failed to recognize this use of the flags register. */
277 cmp->missing_uses = true;
278 }
279
280 class find_comparison_dom_walker : public dom_walker
281 {
282 public:
find_comparison_dom_walker(cdi_direction direction)283 find_comparison_dom_walker (cdi_direction direction)
284 : dom_walker (direction) {}
285
286 virtual edge before_dom_children (basic_block);
287 };
288
289 /* Return true if conforming COMPARE with EH_NOTE is redundant with comparison
290 CMP and can thus be eliminated. */
291
292 static bool
can_eliminate_compare(rtx compare,rtx eh_note,struct comparison * cmp)293 can_eliminate_compare (rtx compare, rtx eh_note, struct comparison *cmp)
294 {
295 /* Take care that it's in the same EH region. */
296 if (cfun->can_throw_non_call_exceptions
297 && !rtx_equal_p (eh_note, cmp->eh_note))
298 return false;
299
300 /* Make sure the compare is redundant with the previous. */
301 if (!rtx_equal_p (strip_not (XEXP (compare, 0)), cmp->in_a)
302 || !rtx_equal_p (XEXP (compare, 1), cmp->in_b))
303 return false;
304
305 if (is_not (XEXP (compare, 0)) != cmp->not_in_a)
306 return false;
307
308 /* New mode must be compatible with the previous compare mode. */
309 machine_mode new_mode
310 = targetm.cc_modes_compatible (GET_MODE (compare), cmp->orig_mode);
311
312 if (new_mode == VOIDmode)
313 return false;
314
315 if (cmp->orig_mode != new_mode)
316 {
317 /* Generate new comparison for substitution. */
318 rtx flags = gen_rtx_REG (new_mode, targetm.flags_regnum);
319 rtx x = gen_rtx_COMPARE (new_mode, cmp->in_a, cmp->in_b);
320 x = gen_rtx_SET (flags, x);
321
322 if (!validate_change (cmp->insn, &PATTERN (cmp->insn), x, false))
323 return false;
324
325 cmp->orig_mode = new_mode;
326 }
327
328 return true;
329 }
330
331 /* Identify comparison instructions within BB. If the flags from the last
332 compare in the BB is live at the end of the block, install the compare
333 in BB->AUX. Called via dom_walker.walk (). */
334
335 edge
before_dom_children(basic_block bb)336 find_comparison_dom_walker::before_dom_children (basic_block bb)
337 {
338 rtx_insn *insn, *next;
339 bool need_purge = false;
340 rtx_insn *last_setter[FIRST_PSEUDO_REGISTER];
341
342 /* The last comparison that was made. Will be reset to NULL
343 once the flags are clobbered. */
344 struct comparison *last_cmp = NULL;
345
346 /* True iff the last comparison has not been clobbered, nor
347 have its inputs. Used to eliminate duplicate compares. */
348 bool last_cmp_valid = false;
349
350 /* The last insn that clobbered the flags, if that insn is of
351 a form that may be valid for eliminating a following compare.
352 To be reset to NULL once the flags are set otherwise. */
353 rtx_insn *last_clobber = NULL;
354
355 /* Propagate the last live comparison throughout the extended basic block. */
356 if (single_pred_p (bb))
357 {
358 last_cmp = (struct comparison *) single_pred (bb)->aux;
359 if (last_cmp)
360 last_cmp_valid = last_cmp->inputs_valid;
361 }
362
363 memset (last_setter, 0, sizeof (last_setter));
364 for (insn = BB_HEAD (bb); insn; insn = next)
365 {
366 rtx src;
367
368 next = (insn == BB_END (bb) ? NULL : NEXT_INSN (insn));
369 if (!NONDEBUG_INSN_P (insn))
370 continue;
371
372 src = conforming_compare (insn);
373 if (src)
374 {
375 rtx eh_note = NULL;
376
377 if (cfun->can_throw_non_call_exceptions)
378 eh_note = find_reg_note (insn, REG_EH_REGION, NULL);
379
380 if (last_cmp_valid && can_eliminate_compare (src, eh_note, last_cmp))
381 {
382 if (eh_note)
383 need_purge = true;
384 delete_insn (insn);
385 continue;
386 }
387
388 last_cmp = XCNEW (struct comparison);
389 last_cmp->insn = insn;
390 last_cmp->prev_clobber = last_clobber;
391 last_cmp->in_a = strip_not (XEXP (src, 0));
392 last_cmp->in_b = XEXP (src, 1);
393 last_cmp->not_in_a = is_not (XEXP (src, 0));
394 last_cmp->eh_note = eh_note;
395 last_cmp->orig_mode = GET_MODE (src);
396 if (last_cmp->in_b == const0_rtx
397 && last_setter[REGNO (last_cmp->in_a)])
398 {
399 rtx set = single_set (last_setter[REGNO (last_cmp->in_a)]);
400 if (set && rtx_equal_p (SET_DEST (set), last_cmp->in_a))
401 last_cmp->in_a_setter = last_setter[REGNO (last_cmp->in_a)];
402 }
403 all_compares.safe_push (last_cmp);
404
405 /* It's unusual, but be prepared for comparison patterns that
406 also clobber an input, or perhaps a scratch. */
407 last_clobber = NULL;
408 last_cmp_valid = true;
409 }
410
411 else
412 {
413 /* Notice if this instruction uses the flags register. */
414 if (last_cmp)
415 find_flags_uses_in_insn (last_cmp, insn);
416
417 /* Notice if this instruction kills the flags register. */
418 df_ref def;
419 FOR_EACH_INSN_DEF (def, insn)
420 if (DF_REF_REGNO (def) == targetm.flags_regnum)
421 {
422 /* See if this insn could be the "clobber" that eliminates
423 a future comparison. */
424 last_clobber = (arithmetic_flags_clobber_p (insn)
425 ? insn : NULL);
426
427 /* In either case, the previous compare is no longer valid. */
428 last_cmp = NULL;
429 last_cmp_valid = false;
430 break;
431 }
432 }
433
434 /* Notice if any of the inputs to the comparison have changed
435 and remember last insn that sets each register. */
436 df_ref def;
437 FOR_EACH_INSN_DEF (def, insn)
438 {
439 if (last_cmp_valid
440 && (DF_REF_REGNO (def) == REGNO (last_cmp->in_a)
441 || (REG_P (last_cmp->in_b)
442 && DF_REF_REGNO (def) == REGNO (last_cmp->in_b))))
443 last_cmp_valid = false;
444 last_setter[DF_REF_REGNO (def)] = insn;
445 }
446 }
447
448 /* Remember the live comparison for subsequent members of
449 the extended basic block. */
450 if (last_cmp)
451 {
452 bb->aux = last_cmp;
453 last_cmp->inputs_valid = last_cmp_valid;
454
455 /* Look to see if the flags register is live outgoing here, and
456 incoming to any successor not part of the extended basic block. */
457 if (bitmap_bit_p (df_get_live_out (bb), targetm.flags_regnum))
458 {
459 edge e;
460 edge_iterator ei;
461
462 FOR_EACH_EDGE (e, ei, bb->succs)
463 {
464 basic_block dest = e->dest;
465 if (bitmap_bit_p (df_get_live_in (bb), targetm.flags_regnum)
466 && !single_pred_p (dest))
467 {
468 last_cmp->missing_uses = true;
469 break;
470 }
471 }
472 }
473 }
474
475 /* If we deleted a compare with a REG_EH_REGION note, we may need to
476 remove EH edges. */
477 if (need_purge)
478 purge_dead_edges (bb);
479
480 return NULL;
481 }
482
483 /* Find all comparisons in the function. */
484
485 static void
find_comparisons(void)486 find_comparisons (void)
487 {
488 calculate_dominance_info (CDI_DOMINATORS);
489
490 find_comparison_dom_walker (CDI_DOMINATORS)
491 .walk (cfun->cfg->x_entry_block_ptr);
492
493 clear_aux_for_blocks ();
494 free_dominance_info (CDI_DOMINATORS);
495 }
496
497 /* Select an alternate CC_MODE for a comparison insn comparing A and B.
498 Note that inputs are almost certainly different than the IN_A and IN_B
499 stored in CMP -- we're called while attempting to eliminate the compare
500 after all. Return the new FLAGS rtx if successful, else return NULL.
501 Note that this function may start a change group. */
502
503 static rtx
maybe_select_cc_mode(struct comparison * cmp,rtx a ATTRIBUTE_UNUSED,rtx b ATTRIBUTE_UNUSED)504 maybe_select_cc_mode (struct comparison *cmp, rtx a ATTRIBUTE_UNUSED,
505 rtx b ATTRIBUTE_UNUSED)
506 {
507 machine_mode sel_mode;
508 const int n = cmp->n_uses;
509 rtx flags = NULL;
510
511 #ifndef SELECT_CC_MODE
512 /* Minimize code differences when this target macro is undefined. */
513 return NULL;
514 #define SELECT_CC_MODE(A,B,C) (gcc_unreachable (), VOIDmode)
515 #endif
516
517 /* If we don't have access to all of the uses, we can't validate. */
518 if (cmp->missing_uses || n == 0)
519 return NULL;
520
521 /* Find a new mode that works for all of the uses. Special case the
522 common case of exactly one use. */
523 if (n == 1)
524 {
525 sel_mode = SELECT_CC_MODE (cmp->uses[0].code, a, b);
526 if (sel_mode != cmp->orig_mode)
527 {
528 flags = gen_rtx_REG (sel_mode, targetm.flags_regnum);
529 validate_change (cmp->uses[0].insn, cmp->uses[0].loc, flags, true);
530 }
531 }
532 else
533 {
534 int i;
535
536 sel_mode = SELECT_CC_MODE (cmp->uses[0].code, a, b);
537 for (i = 1; i < n; ++i)
538 {
539 machine_mode new_mode = SELECT_CC_MODE (cmp->uses[i].code, a, b);
540 if (new_mode != sel_mode)
541 {
542 sel_mode = targetm.cc_modes_compatible (sel_mode, new_mode);
543 if (sel_mode == VOIDmode)
544 return NULL;
545 }
546 }
547
548 if (sel_mode != cmp->orig_mode)
549 {
550 flags = gen_rtx_REG (sel_mode, targetm.flags_regnum);
551 for (i = 0; i < n; ++i)
552 validate_change (cmp->uses[i].insn, cmp->uses[i].loc, flags, true);
553 }
554 }
555
556 return flags;
557 }
558
559 /* Return a register RTX holding the same value at START as REG at END, or
560 NULL_RTX if there is none. */
561
562 static rtx
equivalent_reg_at_start(rtx reg,rtx_insn * end,rtx_insn * start)563 equivalent_reg_at_start (rtx reg, rtx_insn *end, rtx_insn *start)
564 {
565 machine_mode orig_mode = GET_MODE (reg);
566 rtx_insn *bb_head = BB_HEAD (BLOCK_FOR_INSN (end));
567
568 for (rtx_insn *insn = PREV_INSN (end);
569 insn != start;
570 insn = PREV_INSN (insn))
571 {
572 const int abnormal_flags
573 = (DF_REF_CONDITIONAL | DF_REF_PARTIAL | DF_REF_MAY_CLOBBER
574 | DF_REF_MUST_CLOBBER | DF_REF_SIGN_EXTRACT
575 | DF_REF_ZERO_EXTRACT | DF_REF_STRICT_LOW_PART
576 | DF_REF_PRE_POST_MODIFY);
577 df_ref def;
578
579 /* Note that the BB_HEAD is always either a note or a label, but in
580 any case it means that REG is defined outside the block. */
581 if (insn == bb_head)
582 return NULL_RTX;
583 if (NOTE_P (insn) || DEBUG_INSN_P (insn))
584 continue;
585
586 /* Find a possible def of REG in INSN. */
587 FOR_EACH_INSN_DEF (def, insn)
588 if (DF_REF_REGNO (def) == REGNO (reg))
589 break;
590
591 /* No definitions of REG; continue searching. */
592 if (def == NULL)
593 continue;
594
595 /* Bail if this is not a totally normal set of REG. */
596 if (DF_REF_IS_ARTIFICIAL (def))
597 return NULL_RTX;
598 if (DF_REF_FLAGS (def) & abnormal_flags)
599 return NULL_RTX;
600
601 /* We've found an insn between the compare and the clobber that sets
602 REG. Given that pass_cprop_hardreg has not yet run, we still find
603 situations in which we can usefully look through a copy insn. */
604 rtx x = single_set (insn);
605 if (x == NULL_RTX)
606 return NULL_RTX;
607 reg = SET_SRC (x);
608 if (!REG_P (reg))
609 return NULL_RTX;
610 }
611
612 if (GET_MODE (reg) != orig_mode)
613 return NULL_RTX;
614
615 return reg;
616 }
617
618 /* Return true if it is okay to merge the comparison CMP_INSN with
619 the instruction ARITH_INSN. Both instructions are assumed to be in the
620 same basic block with ARITH_INSN appearing before CMP_INSN. This checks
621 that there are no uses or defs of the condition flags or control flow
622 changes between the two instructions. */
623
624 static bool
can_merge_compare_into_arith(rtx_insn * cmp_insn,rtx_insn * arith_insn)625 can_merge_compare_into_arith (rtx_insn *cmp_insn, rtx_insn *arith_insn)
626 {
627 for (rtx_insn *insn = PREV_INSN (cmp_insn);
628 insn && insn != arith_insn;
629 insn = PREV_INSN (insn))
630 {
631 if (!NONDEBUG_INSN_P (insn))
632 continue;
633 /* Bail if there are jumps or calls in between. */
634 if (!NONJUMP_INSN_P (insn))
635 return false;
636
637 /* Bail on old-style asm statements because they lack
638 data flow information. */
639 if (GET_CODE (PATTERN (insn)) == ASM_INPUT)
640 return false;
641
642 df_ref ref;
643 /* Find a USE of the flags register. */
644 FOR_EACH_INSN_USE (ref, insn)
645 if (DF_REF_REGNO (ref) == targetm.flags_regnum)
646 return false;
647
648 /* Find a DEF of the flags register. */
649 FOR_EACH_INSN_DEF (ref, insn)
650 if (DF_REF_REGNO (ref) == targetm.flags_regnum)
651 return false;
652 }
653 return true;
654 }
655
656 /* Given two SET expressions, SET_A and SET_B determine whether they form
657 a recognizable pattern when emitted in parallel. Return that parallel
658 if so. Otherwise return NULL. */
659
660 static rtx
try_validate_parallel(rtx set_a,rtx set_b)661 try_validate_parallel (rtx set_a, rtx set_b)
662 {
663 rtx par = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set_a, set_b));
664 rtx_insn *insn = make_insn_raw (par);
665
666 if (insn_invalid_p (insn, false))
667 {
668 crtl->emit.x_cur_insn_uid--;
669 return NULL_RTX;
670 }
671
672 SET_PREV_INSN (insn) = NULL_RTX;
673 SET_NEXT_INSN (insn) = NULL_RTX;
674 INSN_LOCATION (insn) = 0;
675 return insn;
676 }
677
678 /* For a comparison instruction described by CMP check if it compares a
679 register with zero i.e. it is of the form CC := CMP R1, 0.
680 If it is, find the instruction defining R1 (say I1) and try to create a
681 PARALLEL consisting of I1 and the comparison, representing a flag-setting
682 arithmetic instruction. Example:
683 I1: R1 := R2 + R3
684 <instructions that don't read the condition register>
685 I2: CC := CMP R1 0
686 I2 can be merged with I1 into:
687 I1: { CC := CMP (R2 + R3) 0 ; R1 := R2 + R3 }
688 This catches cases where R1 is used between I1 and I2 and therefore
689 combine and other RTL optimisations will not try to propagate it into
690 I2. Return true if we succeeded in merging CMP. */
691
692 static bool
try_merge_compare(struct comparison * cmp)693 try_merge_compare (struct comparison *cmp)
694 {
695 rtx_insn *cmp_insn = cmp->insn;
696
697 if (cmp->in_b != const0_rtx || cmp->in_a_setter == NULL)
698 return false;
699 rtx in_a = cmp->in_a;
700 df_ref use;
701
702 FOR_EACH_INSN_USE (use, cmp_insn)
703 if (DF_REF_REGNO (use) == REGNO (in_a))
704 break;
705 if (!use)
706 return false;
707
708 rtx_insn *def_insn = cmp->in_a_setter;
709 rtx set = single_set (def_insn);
710 if (!set)
711 return false;
712
713 if (!can_merge_compare_into_arith (cmp_insn, def_insn))
714 return false;
715
716 rtx src = SET_SRC (set);
717
718 /* If the source uses addressing modes with side effects, we can't
719 do the merge because we'd end up with a PARALLEL that has two
720 instances of that side effect in it. */
721 if (side_effects_p (src))
722 return false;
723
724 rtx flags = maybe_select_cc_mode (cmp, src, CONST0_RTX (GET_MODE (src)));
725 if (!flags)
726 {
727 /* We may already have a change group going through maybe_select_cc_mode.
728 Discard it properly. */
729 cancel_changes (0);
730 return false;
731 }
732
733 rtx flag_set
734 = gen_rtx_SET (flags, gen_rtx_COMPARE (GET_MODE (flags),
735 copy_rtx (src),
736 CONST0_RTX (GET_MODE (src))));
737 rtx arith_set = copy_rtx (PATTERN (def_insn));
738 rtx par = try_validate_parallel (flag_set, arith_set);
739 if (!par)
740 {
741 /* We may already have a change group going through maybe_select_cc_mode.
742 Discard it properly. */
743 cancel_changes (0);
744 return false;
745 }
746 if (!apply_change_group ())
747 return false;
748 emit_insn_after (par, def_insn);
749 delete_insn (def_insn);
750 delete_insn (cmp->insn);
751 return true;
752 }
753
754 /* Attempt to replace a comparison with a prior arithmetic insn that can
755 compute the same flags value as the comparison itself. Return true if
756 successful, having made all rtl modifications necessary. */
757
758 static bool
try_eliminate_compare(struct comparison * cmp)759 try_eliminate_compare (struct comparison *cmp)
760 {
761 rtx flags, in_a, in_b, cmp_a, cmp_b;
762
763 if (try_merge_compare (cmp))
764 return true;
765
766 /* We must have found an interesting "clobber" preceding the compare. */
767 if (cmp->prev_clobber == NULL)
768 return false;
769
770 /* Verify that IN_A is not clobbered in between CMP and PREV_CLOBBER.
771 Given that this target requires this pass, we can assume that most
772 insns do clobber the flags, and so the distance between the compare
773 and the clobber is likely to be small. */
774 /* ??? This is one point at which one could argue that DF_REF_CHAIN would
775 be useful, but it is thought to be too heavy-weight a solution here. */
776 in_a = equivalent_reg_at_start (cmp->in_a, cmp->insn, cmp->prev_clobber);
777 if (!in_a)
778 return false;
779
780 /* Likewise for IN_B if need be. */
781 if (CONSTANT_P (cmp->in_b))
782 in_b = cmp->in_b;
783 else if (REG_P (cmp->in_b))
784 {
785 in_b = equivalent_reg_at_start (cmp->in_b, cmp->insn, cmp->prev_clobber);
786 if (!in_b)
787 return false;
788 }
789 else if (GET_CODE (cmp->in_b) == UNSPEC)
790 {
791 const int len = XVECLEN (cmp->in_b, 0);
792 rtvec v = rtvec_alloc (len);
793 for (int i = 0; i < len; i++)
794 {
795 rtx r = equivalent_reg_at_start (XVECEXP (cmp->in_b, 0, i),
796 cmp->insn, cmp->prev_clobber);
797 if (!r)
798 return false;
799 RTVEC_ELT (v, i) = r;
800 }
801 in_b = gen_rtx_UNSPEC (GET_MODE (cmp->in_b), v, XINT (cmp->in_b, 1));
802 }
803 else
804 gcc_unreachable ();
805
806 /* We've reached PREV_CLOBBER without finding a modification of IN_A.
807 Validate that PREV_CLOBBER itself does in fact refer to IN_A. Do
808 recall that we've already validated the shape of PREV_CLOBBER. */
809 rtx_insn *insn = cmp->prev_clobber;
810
811 rtx x = XVECEXP (PATTERN (insn), 0, 0);
812 if (rtx_equal_p (SET_DEST (x), in_a))
813 cmp_a = SET_SRC (x);
814
815 /* Also check operations with implicit extensions, e.g.:
816 [(set (reg:DI)
817 (zero_extend:DI (plus:SI (reg:SI) (reg:SI))))
818 (set (reg:CCZ flags)
819 (compare:CCZ (plus:SI (reg:SI) (reg:SI))
820 (const_int 0)))] */
821 else if (REG_P (SET_DEST (x))
822 && REG_P (in_a)
823 && REGNO (SET_DEST (x)) == REGNO (in_a)
824 && (GET_CODE (SET_SRC (x)) == ZERO_EXTEND
825 || GET_CODE (SET_SRC (x)) == SIGN_EXTEND)
826 && GET_MODE (XEXP (SET_SRC (x), 0)) == GET_MODE (in_a))
827 cmp_a = XEXP (SET_SRC (x), 0);
828
829 /* Also check fully redundant comparisons, e.g.:
830 [(set (reg:SI)
831 (minus:SI (reg:SI) (reg:SI))))
832 (set (reg:CC flags)
833 (compare:CC (reg:SI) (reg:SI)))] */
834 else if (REG_P (in_b)
835 && GET_CODE (SET_SRC (x)) == MINUS
836 && rtx_equal_p (XEXP (SET_SRC (x), 0), in_a)
837 && rtx_equal_p (XEXP (SET_SRC (x), 1), in_b))
838 cmp_a = in_a;
839
840 else
841 return false;
842
843 /* If the source uses addressing modes with side effects, we can't
844 do the merge because we'd end up with a PARALLEL that has two
845 instances of that side effect in it. */
846 if (side_effects_p (cmp_a))
847 return false;
848
849 if (in_a == in_b)
850 cmp_b = cmp_a;
851 else if (rtx_equal_p (SET_DEST (x), in_b))
852 cmp_b = SET_SRC (x);
853 else
854 cmp_b = in_b;
855 if (side_effects_p (cmp_b))
856 return false;
857
858 /* Determine if we ought to use a different CC_MODE here. */
859 flags = maybe_select_cc_mode (cmp, cmp_a, cmp_b);
860 if (flags == NULL)
861 flags = gen_rtx_REG (cmp->orig_mode, targetm.flags_regnum);
862
863 /* Generate a new comparison for installation in the setter. */
864 rtx y = cmp->not_in_a
865 ? gen_rtx_NOT (GET_MODE (cmp_a), copy_rtx (cmp_a))
866 : copy_rtx (cmp_a);
867 y = gen_rtx_COMPARE (GET_MODE (flags), y, copy_rtx (cmp_b));
868 y = gen_rtx_SET (flags, y);
869
870 /* Canonicalize instruction to:
871 [(set (reg:CCM) (compare:CCM (operation) (immediate)))
872 (set (reg) (operation)] */
873
874 rtvec v = rtvec_alloc (2);
875 RTVEC_ELT (v, 0) = y;
876 RTVEC_ELT (v, 1) = x;
877
878 rtx pat = gen_rtx_PARALLEL (VOIDmode, v);
879
880 /* Succeed if the new instruction is valid. Note that we may have started
881 a change group within maybe_select_cc_mode, therefore we must continue. */
882 validate_change (insn, &PATTERN (insn), pat, true);
883
884 if (!apply_change_group ())
885 return false;
886
887 /* Success. Delete the compare insn... */
888 delete_insn (cmp->insn);
889
890 /* ... and any notes that are now invalid due to multiple sets. */
891 x = find_regno_note (insn, REG_UNUSED, targetm.flags_regnum);
892 if (x)
893 remove_note (insn, x);
894 x = find_reg_note (insn, REG_EQUAL, NULL);
895 if (x)
896 remove_note (insn, x);
897 x = find_reg_note (insn, REG_EQUIV, NULL);
898 if (x)
899 remove_note (insn, x);
900
901 return true;
902 }
903
904 /* Main entry point to the pass. */
905
906 static unsigned int
execute_compare_elim_after_reload(void)907 execute_compare_elim_after_reload (void)
908 {
909 df_set_flags (DF_LR_RUN_DCE);
910 df_analyze ();
911
912 gcc_checking_assert (!all_compares.exists ());
913
914 /* Locate all comparisons and their uses, and eliminate duplicates. */
915 find_comparisons ();
916 if (all_compares.exists ())
917 {
918 struct comparison *cmp;
919 size_t i;
920
921 /* Eliminate comparisons that are redundant with flags computation. */
922 FOR_EACH_VEC_ELT (all_compares, i, cmp)
923 {
924 try_eliminate_compare (cmp);
925 XDELETE (cmp);
926 }
927
928 all_compares.release ();
929 }
930
931 return 0;
932 }
933
934 namespace {
935
936 const pass_data pass_data_compare_elim_after_reload =
937 {
938 RTL_PASS, /* type */
939 "cmpelim", /* name */
940 OPTGROUP_NONE, /* optinfo_flags */
941 TV_NONE, /* tv_id */
942 0, /* properties_required */
943 0, /* properties_provided */
944 0, /* properties_destroyed */
945 0, /* todo_flags_start */
946 ( TODO_df_finish | TODO_df_verify ), /* todo_flags_finish */
947 };
948
949 class pass_compare_elim_after_reload : public rtl_opt_pass
950 {
951 public:
pass_compare_elim_after_reload(gcc::context * ctxt)952 pass_compare_elim_after_reload (gcc::context *ctxt)
953 : rtl_opt_pass (pass_data_compare_elim_after_reload, ctxt)
954 {}
955
956 /* opt_pass methods: */
gate(function *)957 virtual bool gate (function *)
958 {
959 /* Setting this target hook value is how a backend indicates the need. */
960 if (targetm.flags_regnum == INVALID_REGNUM)
961 return false;
962 return flag_compare_elim_after_reload;
963 }
964
execute(function *)965 virtual unsigned int execute (function *)
966 {
967 return execute_compare_elim_after_reload ();
968 }
969
970 }; // class pass_compare_elim_after_reload
971
972 } // anon namespace
973
974 rtl_opt_pass *
make_pass_compare_elim_after_reload(gcc::context * ctxt)975 make_pass_compare_elim_after_reload (gcc::context *ctxt)
976 {
977 return new pass_compare_elim_after_reload (ctxt);
978 }
979