This is a tentative patch to fix PR46779 and hopefully also related issues like PR45291.
In the present version of avr_hard_regno_mode_ok, QImode is forbidden in r29/r28. If a 16-bit value or composite is allocated to r28, this can lead to odd subregs like (set (subreg:QI (reg:HI r28) 0) ...) These subregs are produced by IRA and reload treats the subreg with a RELOAD_WRITE. As reload spills r28 to another hard reg that can access QI, there will be no input reload. Therefore, if r29 already contains data that data will get garbaged. See also the discussion around http://gcc.gnu.org/ml/gcc/2011-06/msg00005.html Tested with two regressions less compared to unpatched compiler. Testcases that now pass are: * gcc.target/avr/pr46779-1.c * gcc.target/avr/pr46779-2.c Johann -- PR target/46779 * config/avr/avr.c (avr_hard_regno_mode_ok): Rewrite. In particular, allow 8-bit values in r28 and r29. (avr_hard_regno_scratch_ok): Disallow any register that might be part of the frame pointer. (avr_hard_regno_rename_ok): Same.
Index: config/avr/avr.c =================================================================== --- config/avr/avr.c (Revision 174701) +++ config/avr/avr.c (Arbeitskopie) @@ -6276,26 +6276,35 @@ jump_over_one_insn_p (rtx insn, rtx dest int avr_hard_regno_mode_ok (int regno, enum machine_mode mode) { - /* Disallow QImode in stack pointer regs. */ - if ((regno == REG_SP || regno == (REG_SP + 1)) && mode == QImode) + /* Don't allocate data to non-GENERAL_REGS registers. */ + + if (regno >= 32) return 0; - /* The only thing that can go into registers r28:r29 is a Pmode. */ - if (regno == REG_Y && mode == Pmode) + /* Any GENERAL_REGS register can hold 8-bit values. */ + /* FIXME: + 8-bit values must not be disallowed for R28 or R29. Disallowing + QI et al. in these registers might lead to code like + (set (subreg:QI (reg:HI 28)) ...) + which will result in wrong code because reload does not handle + SUBREGs of hard regsisters like this. This could be fixed in reload. + However, it appears that fixing reload is not wanted by reload people. */ + + if (GET_MODE_SIZE (mode) == 1) return 1; + + /* Disallow big registers that overlap the frame pointer. + This will hopefully reduce the number of spill failures. */ + + if (GET_MODE_SIZE (mode) > 2 + && regno <= REG_Y + && regno + GET_MODE_SIZE (mode) >= REG_Y + 1) + { + return 0; + } - /* Otherwise disallow all regno/mode combinations that span r28:r29. */ - if (regno <= (REG_Y + 1) && (regno + GET_MODE_SIZE (mode)) >= (REG_Y + 1)) - return 0; - - if (mode == QImode) - return 1; - - /* Modes larger than QImode occupy consecutive registers. */ - if (regno + GET_MODE_SIZE (mode) > FIRST_PSEUDO_REGISTER) - return 0; - - /* All modes larger than QImode should start in an even register. */ + /* All modes larger than 8 bits should start in an even register. */ + return !(regno & 1); } @@ -6422,13 +6431,23 @@ avr_hard_regno_scratch_ok (unsigned int && !df_regs_ever_live_p (regno)) return false; + /* Don't allow hard registers that might be part of the frame pointer. + Some places in the compiler just test for [HARD_]FRAME_POINTER_REGNUM + and don't care for a frame pointer that spans more than one register. */ + + if ((!reload_completed || frame_pointer_needed) + && (regno == REG_Y || regno == REG_Y + 1)) + { + return false; + } + return true; } /* Return nonzero if register OLD_REG can be renamed to register NEW_REG. */ int -avr_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, +avr_hard_regno_rename_ok (unsigned int old_reg, unsigned int new_reg) { /* Interrupt functions can only use registers that have already been @@ -6439,6 +6458,17 @@ avr_hard_regno_rename_ok (unsigned int o && !df_regs_ever_live_p (new_reg)) return 0; + /* Don't allow hard registers that might be part of the frame pointer. + Some places in the compiler just test for [HARD_]FRAME_POINTER_REGNUM + and don't care for a frame pointer that spans more than one register. */ + + if ((!reload_completed || frame_pointer_needed) + && (old_reg == REG_Y || old_reg == REG_Y + 1 + || new_reg == REG_Y || new_reg == REG_Y + 1)) + { + return 0; + } + return 1; }