ifcvt: Fix PR104153 and PR104198.
This is a bugfix for r12-6747-gaa8cfe785953a0 which caused an ICE on or1k (PR104153) and broke SPARC bootstrap (PR104198). cond_exec_get_condition () returns the jump condition directly and we now pass it to the backend. The or1k backend modified the condition in-place (other backends do that as well) but this modification is not reverted when the sequence in question is discarded. Therefore we copy the RTX instead of using it directly. The SPARC problem is due to the SPARC backend recreating the initial condition when being passed a CC comparison. This causes the sequence to read from an already overwritten condition operand. Generally, this could also happen on other targets. The workaround is to always first emit to a temporary. In a second run of noce_convert_multiple_sets_1 we know which sequences actually require the comparison and will use no temporaries if all sequences after the current one do not require it. PR rtl-optimization/104198 PR rtl-optimization/104153 gcc/ChangeLog: * ifcvt.cc (noce_convert_multiple_sets_1): Copy rtx instead of using it directly. Rework comparison handling and always perform a second pass. gcc/testsuite/ChangeLog: * gcc.dg/pr104198.c: New test.
This commit is contained in:
parent
71e6353e1b
commit
d0d4601ccd
2 changed files with 81 additions and 1 deletions
46
gcc/ifcvt.cc
46
gcc/ifcvt.cc
|
@ -3391,7 +3391,11 @@ noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
|
|||
rtx cond = noce_get_condition (jump, &cond_earliest, false);
|
||||
|
||||
rtx cc_cmp = cond_exec_get_condition (jump);
|
||||
if (cc_cmp)
|
||||
cc_cmp = copy_rtx (cc_cmp);
|
||||
rtx rev_cc_cmp = cond_exec_get_condition (jump, /* get_reversed */ true);
|
||||
if (rev_cc_cmp)
|
||||
rev_cc_cmp = copy_rtx (rev_cc_cmp);
|
||||
|
||||
rtx_insn *insn;
|
||||
int count = 0;
|
||||
|
@ -3515,6 +3519,7 @@ noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
|
|||
unsigned cost1 = 0, cost2 = 0;
|
||||
rtx_insn *seq, *seq1, *seq2;
|
||||
rtx temp_dest = NULL_RTX, temp_dest1 = NULL_RTX, temp_dest2 = NULL_RTX;
|
||||
bool read_comparison = false;
|
||||
|
||||
seq1 = try_emit_cmove_seq (if_info, temp, cond,
|
||||
new_val, old_val, need_cmov,
|
||||
|
@ -3524,10 +3529,41 @@ noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
|
|||
as well. This allows the backend to emit a cmov directly without
|
||||
creating an additional compare for each. If successful, costing
|
||||
is easier and this sequence is usually preferred. */
|
||||
seq2 = try_emit_cmove_seq (if_info, target, cond,
|
||||
seq2 = try_emit_cmove_seq (if_info, temp, cond,
|
||||
new_val, old_val, need_cmov,
|
||||
&cost2, &temp_dest2, cc_cmp, rev_cc_cmp);
|
||||
|
||||
/* The backend might have created a sequence that uses the
|
||||
condition. Check this. */
|
||||
rtx_insn *walk = seq2;
|
||||
while (walk)
|
||||
{
|
||||
rtx set = single_set (walk);
|
||||
|
||||
if (!set || !SET_SRC (set))
|
||||
{
|
||||
walk = NEXT_INSN (walk);
|
||||
continue;
|
||||
}
|
||||
|
||||
rtx src = SET_SRC (set);
|
||||
|
||||
if (XEXP (set, 1) && GET_CODE (XEXP (set, 1)) == IF_THEN_ELSE)
|
||||
; /* We assume that this is the cmove created by the backend that
|
||||
naturally uses the condition. Therefore we ignore it. */
|
||||
else
|
||||
{
|
||||
if (reg_mentioned_p (XEXP (cond, 0), src)
|
||||
|| reg_mentioned_p (XEXP (cond, 1), src))
|
||||
{
|
||||
read_comparison = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
walk = NEXT_INSN (walk);
|
||||
}
|
||||
|
||||
/* Check which version is less expensive. */
|
||||
if (seq1 != NULL_RTX && (cost1 <= cost2 || seq2 == NULL_RTX))
|
||||
{
|
||||
|
@ -3540,6 +3576,8 @@ noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
|
|||
{
|
||||
seq = seq2;
|
||||
temp_dest = temp_dest2;
|
||||
if (!second_try && read_comparison)
|
||||
*last_needs_comparison = count;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3558,6 +3596,12 @@ noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
|
|||
unmodified_insns->safe_push (insn);
|
||||
}
|
||||
|
||||
/* Even if we did not actually need the comparison, we want to make sure
|
||||
to try a second time in order to get rid of the temporaries. */
|
||||
if (*last_needs_comparison == -1)
|
||||
*last_needs_comparison = 0;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
36
gcc/testsuite/gcc.dg/pr104198.c
Normal file
36
gcc/testsuite/gcc.dg/pr104198.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* Make sure if conversion for two instructions does not break
|
||||
anything (if it runs). */
|
||||
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-O2 -std=c99" } */
|
||||
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
__attribute__ ((noinline))
|
||||
int foo (int *a, int n)
|
||||
{
|
||||
int min = 999999;
|
||||
int bla = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (a[i] < min)
|
||||
{
|
||||
min = a[i];
|
||||
bla = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (bla)
|
||||
min += 1;
|
||||
return min;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int a[] = {2, 1, -13, INT_MAX, INT_MIN, 0};
|
||||
|
||||
int res = foo (a, sizeof (a) / sizeof (a[0]));
|
||||
|
||||
assert (res == (INT_MIN + 1));
|
||||
}
|
Loading…
Add table
Reference in a new issue