diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2166ec5c54b..1162471468f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2003-07-17 Jakub Jelinek + + PR target/11087 + * loop.c (basic_induction_var): Check if convert_modes emitted any + instructions. Remove them and return 0 if so. + 2003-07-18 Eric Botcazou PR optimization/11083 diff --git a/gcc/loop.c b/gcc/loop.c index 71c322c254b..683d83aef04 100644 --- a/gcc/loop.c +++ b/gcc/loop.c @@ -6197,7 +6197,7 @@ basic_induction_var (const struct loop *loop, rtx x, enum machine_mode mode, { enum rtx_code code; rtx *argp, arg; - rtx insn, set = 0; + rtx insn, set = 0, last, inc; code = GET_CODE (x); *location = NULL; @@ -6225,7 +6225,26 @@ basic_induction_var (const struct loop *loop, rtx x, enum machine_mode mode, if (loop_invariant_p (loop, arg) != 1) return 0; - *inc_val = convert_modes (GET_MODE (dest_reg), GET_MODE (x), arg, 0); + /* convert_modes can emit new instructions, e.g. when arg is a loop + invariant MEM and dest_reg has a different mode. + These instructions would be emitted after the end of the function + and then *inc_val would be an unitialized pseudo. + Detect this and bail in this case. + Other alternatives to solve this can be introducing a convert_modes + variant which is allowed to fail but not allowed to emit new + instructions, emit these instructions before loop start and let + it be garbage collected if *inc_val is never used or saving the + *inc_val initialization sequence generated here and when *inc_val + is going to be actually used, emit it at some suitable place. */ + last = get_last_insn (); + inc = convert_modes (GET_MODE (dest_reg), GET_MODE (x), arg, 0); + if (get_last_insn () != last) + { + delete_insns_since (last); + return 0; + } + + *inc_val = inc; *mult_val = const1_rtx; *location = argp; return 1; @@ -6306,7 +6325,15 @@ basic_induction_var (const struct loop *loop, rtx x, enum machine_mode mode, && GET_MODE_CLASS (mode) != MODE_CC) { /* Possible bug here? Perhaps we don't know the mode of X. */ - *inc_val = convert_modes (GET_MODE (dest_reg), mode, x, 0); + last = get_last_insn (); + inc = convert_modes (GET_MODE (dest_reg), mode, x, 0); + if (get_last_insn () != last) + { + delete_insns_since (last); + return 0; + } + + *inc_val = inc; *mult_val = const0_rtx; return 1; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c69031a5be2..70c6936fb98 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2003-07-17 Jakub Jelinek + + PR target/11087 + * gcc.c-torture/execute/20030717-1.c: New test. + 2003-07-18 Eric Botcazou * g++.dg/opt/cfg1.C: New test. diff --git a/gcc/testsuite/gcc.c-torture/execute/20030717-1.c b/gcc/testsuite/gcc.c-torture/execute/20030717-1.c new file mode 100644 index 00000000000..7e43e44be04 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/20030717-1.c @@ -0,0 +1,69 @@ +/* PR target/11087 + This testcase was miscompiled on ppc64, because basic_induction_var called + convert_modes, yet did not expect it to emit any new instructions. + Those were emitted at the end of the function and destroyed during life + analysis, while the program used uninitialized pseudos created by + convert_modes. */ + +struct A +{ + unsigned short a1; + unsigned long a2; +}; + +struct B +{ + int b1, b2, b3, b4, b5; +}; + +struct C +{ + struct B c1[1]; + int c2, c3; +}; + +static +int foo (int x) +{ + return x < 0 ? -x : x; +} + +int bar (struct C *x, struct A *y) +{ + int a = x->c3; + const int b = y->a1 >> 9; + const unsigned long c = y->a2; + int d = a; + unsigned long e, f; + + f = foo (c - x->c1[d].b4); + do + { + if (d <= 0) + d = x->c2; + d--; + + e = foo (c-x->c1[d].b4); + if (e < f) + a = d; + } + while (d != x->c3); + x->c1[a].b4 = c + b; + return a; +} + +int +main () +{ + struct A a; + struct C b; + int c; + + a.a1 = 512; + a.a2 = 4242; + __builtin_memset (&b, 0, sizeof (b)); + b.c1[0].b3 = 424242; + b.c2 = 1; + c = bar (&b, &a); + return 0; +}