On Fri, Jan 10, 2014 at 02:36:47PM -0700, Jeff Law wrote: > As mentioned in the PR. We have a memory load and two extensions. > > The first extension requires a copy because its source and > destination are not the same hard register. > > The second extension does not require a copy and has a wider mode > than the first extension. > > In this case we have to make sure to widen the copy we emit to > eliminate the first extension. Otherwise upper bits aren't going to > have their expected values. Thankfully the copy isn't emitted until > the end of ree and we have the modified memory reference to peek at > to get the proper mode. > > Bootstrapped and regression tested on x86_64-unknown-linux-gnu. OK > for the trunk?
> --- a/gcc/ree.c > +++ b/gcc/ree.c > @@ -1003,11 +1003,20 @@ find_and_remove_re (void) > for (unsigned int i = 0; i < reinsn_copy_list.length (); i += 2) > { > rtx curr_insn = reinsn_copy_list[i]; > + rtx def_insn = reinsn_copy_list[i + 1]; > + > + /* Use the mode of the destination of the defining insn > + for the mode of the copy. This is necessary if the > + defining insn was used to eliminate a second extension > + that was wider than the first. */ > + rtx sub_rtx = *get_sub_rtx (def_insn); > rtx pat = PATTERN (curr_insn); > - rtx new_reg = gen_rtx_REG (GET_MODE (SET_DEST (pat)), > + rtx new_dst = gen_rtx_REG (GET_MODE (SET_DEST (sub_rtx)), > REGNO (XEXP (SET_SRC (pat), 0))); > - rtx set = gen_rtx_SET (VOIDmode, new_reg, SET_DEST (pat)); > - emit_insn_after (set, reinsn_copy_list[i + 1]); > + rtx new_src = gen_rtx_REG (GET_MODE (SET_DEST (sub_rtx)), > + REGNO (SET_DEST (pat))); > + rtx set = gen_rtx_SET (VOIDmode, new_dst, new_src); > + emit_insn_after (set, def_insn); > } There is one thing I still worry about, if some target has an insn to say sign extend or zero extend a short memory load into HARD_REGNO_NREGS () > 1 register, but the other involved register has the only one (or fewer) hard registers available to it. Consider registers SImode hard registers 0, 1, 2, 3: (set (reg:SI 3) (something:SI)) (set (reg:HI 0) (expression:HI)) (set (reg:SI 2) (sign_extend:SI (reg:HI 0))) (set (reg:DI 0) (sign_extend:DI (reg:HI 0))) (use (reg:SI 3)) we transform this into: (set (reg:SI 3) (something:SI)) (set (reg:SI 2) (sign_extend:SI (expression:HI))) (set (reg:SI 0) (reg:HI 2)) (set (reg:DI 0) (sign_extend:DI (reg:HI 0))) (use (reg:SI 3)) first (well, the middle is then pending in copy list), and next: (set (reg:SI 3) (something)) (set (reg:DI 2) (sign_extend:DI (expression:HI))) (set (reg:DI 0) (reg:DI 2)) (use (reg:SI 3)) but that looks wrong, because the second instruction would now clobber (reg:SI 3). Dunno if we have such an target and thus if it is possible to construct a testcase. So, I'd say the handling of the second extend should notice that it is actually extending load into a different register and bail out if it would need more hard registers than it needed previously, or something similar. > --- /dev/null > +++ b/gcc/testsuite/gcc.c-torture/execute/pr59747.c > @@ -0,0 +1,28 @@ > +extern void abort (void) __attribute__ ((__noreturn__)); > +extern void exit (int) __attribute__ ((__noreturn__)); > +extern int printf (const char *, ...); The printf prototype isn't needed, and the noreturn attributes aren't needed either, as they are builtins, the compiler will add those automatically. Jakub