Hi! The following testcase ICEs on i?86, because combiner sees a zero extension of the likely spilled cx register generated during expansion and as it is not a simple register move, propagates it into a use many insns later in the function, enlarging thus the lifetime of the hard register and causing RA failure on a register in between the two that needs to use that particular hard register.
cant_combine_insn_p only special cases simple register moves out of or into likely spilled hard registers. The following patch fixes it by using roughly the same condition to decide if it is ok to emit a zero or sign extension directly from the hard register or if it is better to first copy the hard register into a pseudo (then combiner (and fwprop etc.) seem to do the right thing). Bootstrapped/regtested on x86_64-linux and i686-linux, on both I've verified it doesn't affect code generation for cc1plus (appart from function.o obviously), libstdc++.so and libgcj.so. Ok for trunk (and perhaps after a while for the 4.7 branch)? 2012-07-17 Jakub Jelinek <ja...@redhat.com> PR rtl-optimization/53942 * function.c (assign_parm_setup_reg): Avoid zero/sign extension directly from likely spilled non-fixed hard registers, move them to pseudo first. * gcc.dg/pr53942.c: New test. --- gcc/function.c.jj 2012-06-25 08:38:26.000000000 +0200 +++ gcc/function.c 2012-07-16 13:41:52.847928315 +0200 @@ -2988,11 +2988,26 @@ assign_parm_setup_reg (struct assign_par && insn_operand_matches (icode, 1, op1)) { enum rtx_code code = unsignedp ? ZERO_EXTEND : SIGN_EXTEND; - rtx insn, insns; + rtx insn, insns, t = op1; HARD_REG_SET hardregs; start_sequence (); - insn = gen_extend_insn (op0, op1, promoted_nominal_mode, + /* If op1 is a hard register that is likely spilled, first + force it into a pseudo, otherwise combiner might extend + its lifetime too much. */ + if (GET_CODE (t) == SUBREG) + t = SUBREG_REG (t); + if (REG_P (t) + && HARD_REGISTER_P (t) + && ! TEST_HARD_REG_BIT (fixed_reg_set, REGNO (t)) + && targetm.class_likely_spilled_p (REGNO_REG_CLASS (REGNO (t)))) + { + t = gen_reg_rtx (GET_MODE (op1)); + emit_move_insn (t, op1); + } + else + t = op1; + insn = gen_extend_insn (op0, t, promoted_nominal_mode, data->passed_mode, unsignedp); emit_insn (insn); insns = get_insns (); --- gcc/testsuite/gcc.dg/pr53942.c.jj 2012-06-15 19:53:34.312404791 +0200 +++ gcc/testsuite/gcc.dg/pr53942.c 2012-07-17 11:26:48.863053287 +0200 @@ -0,0 +1,34 @@ +/* PR rtl-optimization/53942 */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ +/* { dg-additional-options "-mtune=pentium2" { target { { i?86-*-* x86_64-*-* } && ia32 } } } */ + +struct S +{ + unsigned short w[3]; + unsigned int x, y; +}; + +struct S *baz (void); + +__attribute__ ((noinline)) +static unsigned char +foo (struct S *x, unsigned char y) +{ + unsigned char c = 0; + unsigned char v = x->w[0]; + c |= v; + v = ((x->w[1]) & (1 << y)) ? 1 : 0; + c |= v << 1; + v = ((x->w[2]) & 0xff) & (1 << y); + c |= v << 2; + return c; +} + +void +bar (void) +{ + struct S *s = baz (); + s->x = foo (s, 6); + s->y = foo (s, 7); +} Jakub