diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index f048bf5fd41c5a0dfafc004a3108bccbf4afa522..d299fceb7824d68b6e53d1068233b885c24f8745 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -4471,28 +4471,21 @@ avr_out_lpm_no_lpmx (rtx_insn *insn, rtx *xop, int *plen) gcc_assert (REG_Z == REGNO (XEXP (addr, 0)) && n_bytes <= 4); - if (regno_dest == LPM_REGNO) - avr_asm_len ("%4lpm" CR_TAB - "adiw %2,1", xop, plen, 2); - else - avr_asm_len ("%4lpm" CR_TAB - "mov %A0,%3" CR_TAB - "adiw %2,1", xop, plen, 3); + for (int i = 0; i < n_bytes; ++i) + { + rtx reg = simplify_gen_subreg (QImode, dest, GET_MODE (dest), i); - if (n_bytes >= 2) - avr_asm_len ("%4lpm" CR_TAB - "mov %B0,%3" CR_TAB - "adiw %2,1", xop, plen, 3); + if (i > 0) + avr_asm_len ("adiw %2,1", xop, plen, 1); - if (n_bytes >= 3) - avr_asm_len ("%4lpm" CR_TAB - "mov %C0,%3" CR_TAB - "adiw %2,1", xop, plen, 3); + avr_asm_len ("%4lpm", xop, plen, 1); - if (n_bytes >= 4) - avr_asm_len ("%4lpm" CR_TAB - "mov %D0,%3" CR_TAB - "adiw %2,1", xop, plen, 3); + if (REGNO (reg) != LPM_REGNO) + avr_asm_len ("mov %0,r0", ®, plen, 1); + } + + if (! _reg_unused_after (insn, xop[2], false)) + avr_asm_len ("adiw %2,1", xop, plen, 1); break; /* POST_INC */ @@ -6685,6 +6678,14 @@ avr_split_tiny_move (rtx_insn * /*insn*/, rtx *xop) if (REGNO (base) > REG_Z) return false; + if (! AVR_TINY + // Only keep base registers that can't do PLUS addressing. + && ((REGNO (base) != REG_X + && ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (mem))) + || avr_load_libgcc_p (mem) + || avr_mem_memx_p (mem))) + return false; + bool volatile_p = MEM_VOLATILE_P (mem); bool mem_volatile_p = false; if (frame_pointer_needed diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index dabf4c0fc5a891fd6774d6e626fe89133f3768ef..2783b8c986f19eb431b6dfe7170705acf9c014e0 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -1035,8 +1035,7 @@ [(parallel [(set (match_operand:MOVMODE 0 "nonimmediate_operand") (match_operand:MOVMODE 1 "general_operand")) (clobber (reg:CC REG_CC))])] - "AVR_TINY - && reload_completed + "reload_completed && avr_fuse_add > 0 // Only split this for .split2 when we are before // pass .avr-fuse-add (which runs after proep). diff --git a/gcc/testsuite/gcc.target/avr/torture/fuse-add.c b/gcc/testsuite/gcc.target/avr/torture/fuse-add.c new file mode 100644 index 0000000000000000000000000000000000000000..b78b1aa9fc928e4d3f8e86018014abc49d978b4d --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/fuse-add.c @@ -0,0 +1,59 @@ +/* { dg-do run } */ +/* { dg-additional-options "-std=gnu99" } */ + +typedef __UINT64_TYPE__ uint64_t; + +extern const uint64_t aa __asm ("real_aa"); +extern const uint64_t bb __asm ("real_bb"); + +__attribute__((used)) const uint64_t real_aa = 0x1122334455667788; +__attribute__((used)) const uint64_t real_bb = 0x0908070605040302; + +__attribute__((noinline,noclone)) +uint64_t add1 (const uint64_t *aa, const uint64_t *bb) +{ + return *aa + *bb; +} + +#ifdef __FLASH +extern const __flash uint64_t fa __asm ("real_fa"); +extern const __flash uint64_t fb __asm ("real_fb"); + +__attribute__((used)) const __flash uint64_t real_fa = 0x1122334455667788; +__attribute__((used)) const __flash uint64_t real_fb = 0x0908070605040302; + +__attribute__((noinline,noclone)) +uint64_t add2 (const __flash uint64_t *aa, const uint64_t *bb) +{ + return *aa + *bb; +} + +uint64_t add3 (const uint64_t *aa, const __flash uint64_t *bb) +{ + return *aa + *bb; +} + +uint64_t add4 (const __flash uint64_t *aa, const __flash uint64_t *bb) +{ + return *aa + *bb; +} +#endif /* have __flash */ + +int main (void) +{ + if (add1 (&aa, &bb) != real_aa + real_bb) + __builtin_exit (__LINE__); + +#ifdef __FLASH + if (add2 (&fa, &bb) != real_fa + real_bb) + __builtin_exit (__LINE__); + + if (add3 (&aa, &fb) != real_aa + real_fb) + __builtin_exit (__LINE__); + + if (add4 (&fa, &fb) != real_fa + real_fb) + __builtin_exit (__LINE__); +#endif + + return 0; +}