OSDN Git Service

libgcc/
authorgjl <gjl@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 22 Mar 2012 15:06:57 +0000 (15:06 +0000)
committergjl <gjl@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 22 Mar 2012 15:06:57 +0000 (15:06 +0000)
Backport from 2012-03-07 mainline r185033.

PR target/52507
* config/avr/lib1funcs.S (__movmemx_hi): Fix loop label in RAM-part.

Backport from 2012-03-07 mainline r185031.

PR target/52505
* config/avr/lib1funcs.S (__xload_1): Don't read unintentionally
from RAM.

Backport from 2012-03-07 mainline r185030.

PR target/52461
PR target/52508
* config/avr/lib1funcs.S (__do_copy_data): Clear RAMPZ after usage
if RAMPZ affects reading from RAM.
(__tablejump_elpm__): Ditto.
(.xload): Ditto.
(__movmemx_hi): Ditto.
(__do_global_ctors): Right condition for RAMPZ usage is "have ELPM".
(__do_global_dtors): Ditto.
(__xload_1, __xload_2, __xload_3, __xload_4): Ditto.
(__movmemx_hi): Ditto.

gcc/
Backport from 2012-03-22 mainline r185692.

PR target/52496
* config/avr/avr.md (unspec): Remove UNSPEC_MEMORY_BARRIER.
(unspecv): Add UNSPECV_MEMORY_BARRIER.
(cli_sei): Use unspec_volatile instead of unspec for memory barrier.
(delay_cycles_1, delay_cycles_2): Ditto.
(delay_cycles_3, delay_cycles_4): Ditto.
(nopv, *nopv): Ditto.
(sleep, *sleep): Ditto.
(wdr, *wdr): Ditto.

Backport from 2012-03-21 mainline r185605.

PR rtl-optimization/52543
PR target/52461
* config/avr/avr-protos.h (avr_load_lpm): New prototype.
* config/avr/avr.c (avr_mode_dependent_address_p): New function.
(TARGET_MODE_DEPENDENT_ADDRESS_P): New define.
(avr_load_libgcc_p): Restrict to __flash loads.
(avr_out_lpm): Only handle 1-byte loads from __flash.
(avr_load_lpm): New function.
(avr_find_unused_d_reg): Remove.
(avr_out_lpm_no_lpmx): Remove.
(adjust_insn_length): Handle ADJUST_LEN_LOAD_LPM.
* config/avr/avr.md (unspec): Add UNSPEC_LPM.
(load_<mode>_libgcc): Use UNSPEC_LPM instead of MEM.
(load_<mode>, load_<mode>_clobber): New insns.
(mov<mode>): For multi-byte move from non-generic
16-bit address spaces: Expand to load_<mode> resp.
load_<mode>_clobber.
(load<mode>_libgcc): Remove expander.
(split-lpmx): Remove split.

Backport from 2012-03-13 mainline r185329.

PR target/52488
* config/avr/avr.c (avr_prologue_setup_frame): Cut down stack
offset (size) to a value the insns can deal with.
(expand_epilogue): Ditto.

Backport from 2012-03-12 mainline r185256.

PR target/52499
* config/avr/avr.c (avr_mode_code_base_reg_class): Change return
type from reg_class_t to enum reg_class.
* config/avr/avr-protos.h (avr_mode_code_base_reg_class): Ditto.

Backport from 2012-03-12 mainline r185253.

PR target/52148
* config/avr/avr.c (avr_out_movmem): Fix typo in output template
for the case ADDR_SPACE_FLASH and AVR_HAVE_LPMX introduced in
r184615 from 2012-02-28.

Backport from 2012-03-08 mainline r185105.

* config/avr/avr.md (*addhi3, addhi3_clobber): Add "w" alternative
for constants in [-63,63].

Backport from 2012-03-08 mainline r185100.

PR target/52496
* config/avr/avr.c (avr_mem_clobber): New static function.
(avr_expand_delay_cycles): Add memory clobber operand to
delay_cycles_1, delay_cycles_2, delay_cycles_3, delay_cycles_4.
* config/avr/avr.md (unspec): Add UNSPEC_MEMORY_BARRIER.
(enable_interrupt, disable_interrupt): New expander.
(nopv, sleep, wdr): New expanders.
(delay_cycles_1): Add memory clobber.
(delay_cycles_2): Add memory clobber.
(delay_cycles_3): Add memory clobber.
(delay_cycles_4): Add memory clobber.
(cli_sei): New insn from former "enable_interrupt",
"disable_interrupt" with memory clobber.
(*wdt): New insn from former "wdt" with memory clobber.
(*nopv): Similar, but for "nopv".
(*sleep): Similar, but for "sleep".

Backport from 2012-03-07 mainline r185043.

PR target/52484
* config/avr/avr.md (xload<mode>_A): Add R22... to register footprint.

Backport from 2012-03-07 mainline r185032.

PR target/52506
* gcc/config/avr/avr.c (expand_epilogue): Fix order of restoration
to: RAMPZ, RAMPY, RAMPX, RAMPD.
(expand_prologue): Only clear RAMPZ if it has effect on RAM-read.

Backport from 2012-03-07 mainline r185031.

PR target/52505
* config/avr/avr.c (avr_out_xload): Don't read unintentionally
from RAM.
* config/avr/avr.md (xload_8): Adjust insn length.

Backport from 2012-03-07 mainline r185030.

PR target/52461
* gcc/config/avr/avr.c (avr_out_lpm): Clear RAMPZ after usage
if RAMPZ affects reading from RAM.

Backport from 2012-03-05 mainline r184919.

* config/avr/avr.md (*umaddqihi4.2): New insn-and-split.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-4_7-branch@185697 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/avr/avr-protos.h
gcc/config/avr/avr.c
gcc/config/avr/avr.md
libgcc/ChangeLog
libgcc/config/avr/lib1funcs.S

index 4629a16..842231b 100644 (file)
@@ -1,5 +1,115 @@
 2012-03-22  Georg-Johann Lay  <avr@gjlay.de>
 
+       Backport from 2012-03-22 mainline r185692.
+
+       PR target/52496
+       * config/avr/avr.md (unspec): Remove UNSPEC_MEMORY_BARRIER.
+       (unspecv): Add UNSPECV_MEMORY_BARRIER.
+       (cli_sei): Use unspec_volatile instead of unspec for memory barrier.
+       (delay_cycles_1, delay_cycles_2): Ditto.
+       (delay_cycles_3, delay_cycles_4): Ditto.
+       (nopv, *nopv): Ditto.
+       (sleep, *sleep): Ditto.
+       (wdr, *wdr): Ditto.
+
+       Backport from 2012-03-21 mainline r185605.
+
+       PR rtl-optimization/52543
+       PR target/52461
+       * config/avr/avr-protos.h (avr_load_lpm): New prototype.
+       * config/avr/avr.c (avr_mode_dependent_address_p): New function.
+       (TARGET_MODE_DEPENDENT_ADDRESS_P): New define.
+       (avr_load_libgcc_p): Restrict to __flash loads.
+       (avr_out_lpm): Only handle 1-byte loads from __flash.
+       (avr_load_lpm): New function.
+       (avr_find_unused_d_reg): Remove.
+       (avr_out_lpm_no_lpmx): Remove.
+       (adjust_insn_length): Handle ADJUST_LEN_LOAD_LPM.
+       * config/avr/avr.md (unspec): Add UNSPEC_LPM.
+       (load_<mode>_libgcc): Use UNSPEC_LPM instead of MEM.
+       (load_<mode>, load_<mode>_clobber): New insns.
+       (mov<mode>): For multi-byte move from non-generic
+       16-bit address spaces: Expand to load_<mode> resp.
+       load_<mode>_clobber.
+       (load<mode>_libgcc): Remove expander.
+       (split-lpmx): Remove split.
+
+       Backport from 2012-03-13 mainline r185329.
+
+       PR target/52488
+       * config/avr/avr.c (avr_prologue_setup_frame): Cut down stack
+       offset (size) to a value the insns can deal with.
+       (expand_epilogue): Ditto.
+
+       Backport from 2012-03-12 mainline r185256.
+
+       PR target/52499
+       * config/avr/avr.c (avr_mode_code_base_reg_class): Change return
+       type from reg_class_t to enum reg_class.
+       * config/avr/avr-protos.h (avr_mode_code_base_reg_class): Ditto.
+
+       Backport from 2012-03-12 mainline r185253.
+
+       PR target/52148
+       * config/avr/avr.c (avr_out_movmem): Fix typo in output template
+       for the case ADDR_SPACE_FLASH and AVR_HAVE_LPMX introduced in
+       r184615 from 2012-02-28.
+
+       Backport from 2012-03-08 mainline r185105.
+
+       * config/avr/avr.md (*addhi3, addhi3_clobber): Add "w" alternative
+       for constants in [-63,63].
+
+       Backport from 2012-03-08 mainline r185100.
+
+       PR target/52496
+       * config/avr/avr.c (avr_mem_clobber): New static function.
+       (avr_expand_delay_cycles): Add memory clobber operand to
+       delay_cycles_1, delay_cycles_2, delay_cycles_3, delay_cycles_4.
+       * config/avr/avr.md (unspec): Add UNSPEC_MEMORY_BARRIER.
+       (enable_interrupt, disable_interrupt): New expander.
+       (nopv, sleep, wdr): New expanders.
+       (delay_cycles_1): Add memory clobber.
+       (delay_cycles_2): Add memory clobber.
+       (delay_cycles_3): Add memory clobber.
+       (delay_cycles_4): Add memory clobber.
+       (cli_sei): New insn from former "enable_interrupt",
+       "disable_interrupt" with memory clobber.
+       (*wdt): New insn from former "wdt" with memory clobber.
+       (*nopv): Similar, but for "nopv".
+       (*sleep): Similar, but for "sleep".
+
+       Backport from 2012-03-07 mainline r185043.
+
+       PR target/52484
+       * config/avr/avr.md (xload<mode>_A): Add R22... to register footprint.
+
+       Backport from 2012-03-07 mainline r185032.
+
+       PR target/52506
+       * gcc/config/avr/avr.c (expand_epilogue): Fix order of restoration
+       to: RAMPZ, RAMPY, RAMPX, RAMPD.
+       (expand_prologue): Only clear RAMPZ if it has effect on RAM-read.
+
+       Backport from 2012-03-07 mainline r185031.
+
+       PR target/52505
+       * config/avr/avr.c (avr_out_xload): Don't read unintentionally
+       from RAM.
+       * config/avr/avr.md (xload_8): Adjust insn length.
+
+       Backport from 2012-03-07 mainline r185030.
+
+       PR target/52461
+       * gcc/config/avr/avr.c (avr_out_lpm): Clear RAMPZ after usage
+       if RAMPZ affects reading from RAM.
+
+       Backport from 2012-03-05 mainline r184919.
+
+       * config/avr/avr.md (*umaddqihi4.2): New insn-and-split.
+
+2012-03-22  Georg-Johann Lay  <avr@gjlay.de>
+
        Backport from mainline r185259.
 
        PR other/52545
index f9e893d..158a7be 100644 (file)
@@ -75,6 +75,8 @@ extern const char *avr_out_ashlpsi3 (rtx, rtx*, int*);
 extern const char *avr_out_ashrpsi3 (rtx, rtx*, int*);
 extern const char *avr_out_lshrpsi3 (rtx, rtx*, int*);
 
+extern const char* avr_load_lpm (rtx, rtx*, int*);
+
 extern bool avr_rotate_bytes (rtx operands[]);
 
 extern void expand_prologue (void);
@@ -115,7 +117,7 @@ extern int avr_simplify_comparison_p (enum machine_mode mode,
 extern RTX_CODE avr_normalize_condition (RTX_CODE condition);
 extern void out_shift_with_cnt (const char *templ, rtx insn,
                                rtx operands[], int *len, int t_len);
-extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE);
+extern enum reg_class avr_mode_code_base_reg_class (enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE);
 extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE);
 extern rtx avr_incoming_return_addr_rtx (void);
 extern rtx avr_legitimize_reload_address (rtx*, enum machine_mode, int, int, int, int, rtx (*)(rtx,int));
index 7ead6f4..341fe9b 100644 (file)
@@ -827,7 +827,11 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
   bool isr_p = cfun->machine->is_interrupt || cfun->machine->is_signal;
   int live_seq = sequent_regs_live ();
 
+  HOST_WIDE_INT size_max
+    = (HOST_WIDE_INT) GET_MODE_MASK (AVR_HAVE_8BIT_SP ? QImode : Pmode);
+
   bool minimize = (TARGET_CALL_PROLOGUES
+                   && size < size_max
                    && live_seq
                    && !isr_p
                    && !cfun->machine->is_OS_task
@@ -933,6 +937,7 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               leaf function and thus X has already been saved.  */
               
           int irq_state = -1;
+          HOST_WIDE_INT size_cfa = size;
           rtx fp_plus_insns, fp, my_fp;
 
           gcc_assert (frame_pointer_needed
@@ -951,6 +956,27 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               my_fp = all_regs_rtx[FRAME_POINTER_REGNUM];
             }
 
+          /* Cut down size and avoid size = 0 so that we don't run
+             into ICE like PR52488 in the remainder.  */
+
+          if (size > size_max)
+            {
+              /* Don't error so that insane code from newlib still compiles
+                 and does not break building newlib.  As PR51345 is implemented
+                 now, there are multilib variants with -mtiny-stack.
+                 
+                 If user wants sanity checks he can use -Wstack-usage=
+                 or similar options.
+
+                 For CFA we emit the original, non-saturated size so that
+                 the generic machinery is aware of the real stack usage and
+                 will print the above diagnostic as expected.  */
+              
+              size = size_max;
+            }
+
+          size = trunc_int_for_mode (size, GET_MODE (my_fp));
+          
           /************  Method 1: Adjust frame pointer  ************/
           
           start_sequence ();
@@ -975,7 +1001,7 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               RTX_FRAME_RELATED_P (insn) = 1;
               add_reg_note (insn, REG_CFA_ADJUST_CFA,
                             gen_rtx_SET (VOIDmode, fp,
-                                         plus_constant (fp, -size)));
+                                         plus_constant (fp, -size_cfa)));
             }
           
           /* Copy to stack pointer.  Note that since we've already
@@ -1003,7 +1029,7 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               add_reg_note (insn, REG_CFA_ADJUST_CFA,
                             gen_rtx_SET (VOIDmode, stack_pointer_rtx,
                                          plus_constant (stack_pointer_rtx,
-                                                        -size)));
+                                                        -size_cfa)));
             }
           
           fp_plus_insns = get_insns ();
@@ -1026,7 +1052,7 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               add_reg_note (insn, REG_CFA_ADJUST_CFA,
                             gen_rtx_SET (VOIDmode, stack_pointer_rtx,
                                          plus_constant (stack_pointer_rtx,
-                                                        -size)));
+                                                        -size_cfa)));
               if (frame_pointer_needed)
                 {
                   insn = emit_move_insn (fp, stack_pointer_rtx);
@@ -1048,7 +1074,7 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set)
               emit_insn (fp_plus_insns);
             }
 
-          cfun->machine->stack_usage += size;
+          cfun->machine->stack_usage += size_cfa;
         } /* !minimize && size != 0 */
     } /* !minimize */
 }
@@ -1123,11 +1149,11 @@ expand_prologue (void)
           emit_push_sfr (rampy_rtx, false /* frame-related */, true /* clr */);
         }
 
-      if (AVR_HAVE_RAMPZ 
+      if (AVR_HAVE_RAMPZ
           && TEST_HARD_REG_BIT (set, REG_Z)
           && TEST_HARD_REG_BIT (set, REG_Z + 1))
         {
-          emit_push_sfr (rampz_rtx, false /* frame-related */, true /* clr */);
+          emit_push_sfr (rampz_rtx, false /* frame-related */, AVR_HAVE_RAMPD);
         }
     }  /* is_interrupt is_signal */
 
@@ -1261,6 +1287,7 @@ expand_epilogue (bool sibcall_p)
       int irq_state = -1;
       rtx fp, my_fp;
       rtx fp_plus_insns;
+      HOST_WIDE_INT size_max;
 
       gcc_assert (frame_pointer_needed
                   || !isr_p
@@ -1277,6 +1304,13 @@ expand_epilogue (bool sibcall_p)
                   
           my_fp = all_regs_rtx[FRAME_POINTER_REGNUM];
         }
+
+      /* For rationale see comment in prologue generation.  */
+
+      size_max = (HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (my_fp));
+      if (size > size_max)
+        size = size_max;
+      size = trunc_int_for_mode (size, GET_MODE (my_fp));
               
       /********** Method 1: Adjust fp register  **********/
               
@@ -1347,12 +1381,12 @@ expand_epilogue (bool sibcall_p)
       /* Restore RAMPZ/Y/X/D using tmp_reg as scratch.
          The conditions to restore them must be tha same as in prologue.  */
       
-      if (AVR_HAVE_RAMPX
-          && TEST_HARD_REG_BIT (set, REG_X)
-          && TEST_HARD_REG_BIT (set, REG_X + 1))
+      if (AVR_HAVE_RAMPZ
+          && TEST_HARD_REG_BIT (set, REG_Z)
+          && TEST_HARD_REG_BIT (set, REG_Z + 1))
         {
           emit_pop_byte (TMP_REGNO);
-          emit_move_insn (rampx_rtx, tmp_reg_rtx);
+          emit_move_insn (rampz_rtx, tmp_reg_rtx);
         }
 
       if (AVR_HAVE_RAMPY
@@ -1364,12 +1398,12 @@ expand_epilogue (bool sibcall_p)
           emit_move_insn (rampy_rtx, tmp_reg_rtx);
         }
 
-      if (AVR_HAVE_RAMPZ
-          && TEST_HARD_REG_BIT (set, REG_Z)
-          && TEST_HARD_REG_BIT (set, REG_Z + 1))
+      if (AVR_HAVE_RAMPX
+          && TEST_HARD_REG_BIT (set, REG_X)
+          && TEST_HARD_REG_BIT (set, REG_X + 1))
         {
           emit_pop_byte (TMP_REGNO);
-          emit_move_insn (rampz_rtx, tmp_reg_rtx);
+          emit_move_insn (rampx_rtx, tmp_reg_rtx);
         }
 
       if (AVR_HAVE_RAMPD)
@@ -1423,6 +1457,22 @@ avr_cannot_modify_jumps_p (void)
 }
 
 
+/* Implement `TARGET_MODE_DEPENDENT_ADDRESS_P'.  */
+
+/* FIXME:  PSImode addresses are not mode-dependent in themselves.
+      This hook just serves to hack around PR rtl-optimization/52543 by
+      claiming that PSImode addresses (which are used for the 24-bit
+      address space __memx) were mode-dependent so that lower-subreg.s
+      will skip these addresses.  See also the similar FIXME comment along
+      with mov<mode> expanders in avr.md.  */
+
+static bool
+avr_mode_dependent_address_p (const_rtx addr)
+{
+  return GET_MODE (addr) != Pmode;
+}
+
+
 /* Helper function for `avr_legitimate_address_p'.  */
 
 static inline bool
@@ -2435,7 +2485,8 @@ avr_load_libgcc_p (rtx op)
         
   return (n_bytes > 2
           && !AVR_HAVE_LPMX
-          && avr_mem_flash_p (op));
+          && MEM_P (op)
+          && MEM_ADDR_SPACE (op) == ADDR_SPACE_FLASH);
 }
 
 /* Return true if a value of mode MODE is read by __xload_* function.  */
@@ -2450,155 +2501,6 @@ avr_xload_libgcc_p (enum machine_mode mode)
 }
 
 
-/* Find an unused d-register to be used as scratch in INSN.
-   EXCLUDE is either NULL_RTX or some register. In the case where EXCLUDE
-   is a register, skip all possible return values that overlap EXCLUDE.
-   The policy for the returned register is similar to that of
-   `reg_unused_after', i.e. the returned register may overlap the SET_DEST
-   of INSN.
-
-   Return a QImode d-register or NULL_RTX if nothing found.  */
-
-static rtx
-avr_find_unused_d_reg (rtx insn, rtx exclude)
-{
-  int regno;
-  bool isr_p = (interrupt_function_p (current_function_decl)
-                || signal_function_p (current_function_decl));
-
-  for (regno = 16; regno < 32; regno++)
-    {
-      rtx reg = all_regs_rtx[regno];
-      
-      if ((exclude
-           && reg_overlap_mentioned_p (exclude, reg))
-          || fixed_regs[regno])
-        {
-          continue;
-        }
-
-      /* Try non-live register */
-
-      if (!df_regs_ever_live_p (regno)
-          && (TREE_THIS_VOLATILE (current_function_decl)
-              || cfun->machine->is_OS_task
-              || cfun->machine->is_OS_main
-              || (!isr_p && call_used_regs[regno])))
-        {
-          return reg;
-        }
-
-      /* Any live register can be used if it is unused after.
-         Prologue/epilogue will care for it as needed.  */
-      
-      if (df_regs_ever_live_p (regno)
-          && reg_unused_after (insn, reg))
-        {
-          return reg;
-        }
-    }
-
-  return NULL_RTX;
-}
-
-
-/* Helper function for the next function in the case where only restricted
-   version of LPM instruction is available.  */
-
-static const char*
-avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
-{
-  rtx dest = xop[0];
-  rtx addr = xop[1];
-  int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
-  int regno_dest;
-
-  regno_dest = REGNO (dest);
-
-  /* The implicit target register of LPM.  */
-  xop[3] = lpm_reg_rtx;
-
-  switch (GET_CODE (addr))
-    {
-    default:
-      gcc_unreachable();
-
-    case REG:
-
-      gcc_assert (REG_Z == REGNO (addr));
-
-      switch (n_bytes)
-        {
-        default:
-          gcc_unreachable();
-
-        case 1:
-          avr_asm_len ("%4lpm", xop, plen, 1);
-
-          if (regno_dest != LPM_REGNO)
-            avr_asm_len ("mov %0,%3", xop, plen, 1);
-
-          return "";
-
-        case 2:
-          if (REGNO (dest) == REG_Z)
-            return avr_asm_len ("%4lpm"      CR_TAB
-                                "push %3"    CR_TAB
-                                "adiw %2,1"  CR_TAB
-                                "%4lpm"      CR_TAB
-                                "mov %B0,%3" CR_TAB
-                                "pop %A0", xop, plen, 6);
-          
-          avr_asm_len ("%4lpm"      CR_TAB
-                       "mov %A0,%3" CR_TAB
-                       "adiw %2,1"  CR_TAB
-                       "%4lpm"      CR_TAB
-                       "mov %B0,%3", xop, plen, 5);
-                
-          if (!reg_unused_after (insn, addr))
-            avr_asm_len ("sbiw %2,1", xop, plen, 1);
-          
-          break; /* 2 */
-        }
-      
-      break; /* REG */
-
-    case POST_INC:
-
-      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);
-
-      if (n_bytes >= 2)
-        avr_asm_len ("%4lpm"      CR_TAB
-                     "mov %B0,%3" CR_TAB
-                     "adiw %2,1", xop, plen, 3);
-
-      if (n_bytes >= 3)
-        avr_asm_len ("%4lpm"      CR_TAB
-                     "mov %C0,%3" CR_TAB
-                     "adiw %2,1", xop, plen, 3);
-
-      if (n_bytes >= 4)
-        avr_asm_len ("%4lpm"      CR_TAB
-                     "mov %D0,%3" CR_TAB
-                     "adiw %2,1", xop, plen, 3);
-
-      break; /* POST_INC */
-      
-    } /* switch CODE (addr) */
-      
-  return "";
-}
-
-
 /* If PLEN == NULL: Ouput instructions to load a value from a memory location
    OP[1] in AS1 to register OP[0].
    If PLEN != 0 set *PLEN to the length in words of the instruction sequence.
@@ -2607,13 +2509,11 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen)
 static const char*
 avr_out_lpm (rtx insn, rtx *op, int *plen)
 {
-  rtx xop[6];
+  rtx xop[3];
   rtx dest = op[0];
   rtx src = SET_SRC (single_set (insn));
   rtx addr;
   int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
-  int regno_dest;
-  int segment;
   RTX_CODE code;
   addr_space_t as = MEM_ADDR_SPACE (src);
 
@@ -2634,55 +2534,18 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
   gcc_assert (REG_P (dest));
   gcc_assert (REG == code || POST_INC == code);
 
-  xop[0] = dest;
-  xop[1] = addr;
-  xop[2] = lpm_addr_reg_rtx;
-  xop[4] = xstring_empty;
-  xop[5] = tmp_reg_rtx;
-
-  regno_dest = REGNO (dest);
-
-  segment = avr_addrspace[as].segment;
-
-  /* Set RAMPZ as needed.  */
-
-  if (segment)
-    {
-      xop[4] = GEN_INT (segment);
-      
-      if (xop[3] = avr_find_unused_d_reg (insn, lpm_addr_reg_rtx),
-          xop[3])
-        {
-          avr_asm_len ("ldi %3,%4" CR_TAB
-                       "out __RAMPZ__,%3", xop, plen, 2);
-        }
-      else if (segment == 1)
-        {
-          avr_asm_len ("clr %5" CR_TAB
-                       "inc %5" CR_TAB
-                       "out __RAMPZ__,%5", xop, plen, 3);
-        }
-      else
-        {
-          avr_asm_len ("mov %5,%2"         CR_TAB
-                       "ldi %2,%4"         CR_TAB
-                       "out __RAMPZ__,%2"  CR_TAB
-                       "mov %2,%5", xop, plen, 4);
-        }
-      
-      xop[4] = xstring_e;
-
-      if (!AVR_HAVE_ELPMX)
-        return avr_out_lpm_no_lpmx (insn, xop, plen);
-    }
-  else if (!AVR_HAVE_LPMX)
-    {
-      return avr_out_lpm_no_lpmx (insn, xop, plen);
-    }
+  /* Only 1-byte moves from __flash are representes as open coded
+     mov insns.  All other loads from flash are not handled here but
+     by some UNSPEC instead, see respective FIXME in machine description.  */
+  
+  gcc_assert (as == ADDR_SPACE_FLASH);
+  gcc_assert (n_bytes == 1);
 
-  /* We have [E]LPMX: Output reading from Flash the comfortable way.  */
+  xop[0] = dest;
+  xop[1] = lpm_addr_reg_rtx;
+  xop[2] = lpm_reg_rtx;
 
-  switch (GET_CODE (addr))
+  switch (code)
     {
     default:
       gcc_unreachable();
@@ -2690,79 +2553,107 @@ avr_out_lpm (rtx insn, rtx *op, int *plen)
     case REG:
 
       gcc_assert (REG_Z == REGNO (addr));
+      
+      return AVR_HAVE_LPMX
+        ? avr_asm_len ("lpm %0,%a1", xop, plen, 1)
+        : avr_asm_len ("lpm" CR_TAB
+                       "mov %0,%2", xop, plen, 2);
+      
+    case POST_INC:
+      
+      gcc_assert (REG_Z == REGNO (XEXP (addr, 0)));
 
-      switch (n_bytes)
-        {
-        default:
-          gcc_unreachable();
-
-        case 1:
-          return avr_asm_len ("%4lpm %0,%a2", xop, plen, 1);
+      return AVR_HAVE_LPMX
+        ? avr_asm_len ("lpm %0,%a1+", xop, plen, 1)
+        : avr_asm_len ("lpm"        CR_TAB
+                       "adiw %1, 1" CR_TAB
+                       "mov %0,%2", xop, plen, 3);
+    }
 
-        case 2:
-          if (REGNO (dest) == REG_Z)
-            return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
-                                "%4lpm %B0,%a2" CR_TAB
-                                "mov %A0,%5", xop, plen, 3);
-          else
-            {
-              avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
-                           "%4lpm %B0,%a2", xop, plen, 2);
-                
-              if (!reg_unused_after (insn, addr))
-                avr_asm_len ("sbiw %2,1", xop, plen, 1);
-            }
-          
-          break; /* 2 */
+  return "";
+}
 
-        case 3:
 
-          avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
-                       "%4lpm %B0,%a2+" CR_TAB
-                       "%4lpm %C0,%a2", xop, plen, 3);
-                
-          if (!reg_unused_after (insn, addr))
-            avr_asm_len ("sbiw %2,2", xop, plen, 1);
+/* If PLEN == NULL: Ouput instructions to load $0 with a value from
+   flash address $1:Z.  If $1 = 0 we can use LPM to read, otherwise
+   use ELPM.
+   If PLEN != 0 set *PLEN to the length in words of the instruction sequence.
+   Return "".  */
 
-          break; /* 3 */
+const char*
+avr_load_lpm (rtx insn, rtx *op, int *plen)
+{
+  rtx xop[4];
+  int n, n_bytes = GET_MODE_SIZE (GET_MODE (op[0]));
+  rtx xsegment = op[1];
+  bool clobber_z = PARALLEL == GET_CODE (PATTERN (insn));
+  bool r30_in_tmp = false;
+  
+  if (plen)
+    *plen = 0;
+  
+  xop[1] = lpm_addr_reg_rtx;
+  xop[2] = lpm_reg_rtx;
+  xop[3] = xstring_empty;
+  
+  /* Set RAMPZ as needed.  */
+  
+  if (REG_P (xsegment))
+    {
+      avr_asm_len ("out __RAMPZ__,%0", &xsegment, plen, 1);
+      xop[3] = xstring_e;
+    }
+  
+  /* Load the individual bytes from LSB to MSB.  */
+  
+  for (n = 0; n < n_bytes; n++)
+    {
+      xop[0] = all_regs_rtx[REGNO (op[0]) + n];
       
-        case 4:
-
-          avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
-                       "%4lpm %B0,%a2+", xop, plen, 2);
-          
-          if (REGNO (dest) == REG_Z - 2)
-            return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
-                                "%4lpm %C0,%a2"          CR_TAB
-                                "mov %D0,%5", xop, plen, 3);
-          else
+      if ((CONST_INT_P (xsegment) && AVR_HAVE_LPMX)
+          || (REG_P (xsegment) && AVR_HAVE_ELPMX))
+        {
+          if (n == n_bytes-1)
+            avr_asm_len ("%3lpm %0,%a1", xop, plen, 1);
+          else if (REGNO (xop[0]) == REG_Z)
             {
-              avr_asm_len ("%4lpm %C0,%a2+" CR_TAB
-                           "%4lpm %D0,%a2", xop, plen, 2);
-                
-              if (!reg_unused_after (insn, addr))
-                avr_asm_len ("sbiw %2,3", xop, plen, 1);
+              avr_asm_len ("%3lpm %2,%a1+", xop, plen, 1);
+              r30_in_tmp = true;
             }
+          else
+            avr_asm_len ("%3lpm %0,%a1+", xop, plen, 1);
+        }
+      else
+        {
+          gcc_assert (clobber_z);
+          
+          avr_asm_len ("%3lpm" CR_TAB
+                       "mov %0,%2", xop, plen, 2);
 
-          break; /* 4 */
-        } /* n_bytes */
+          if (n != n_bytes-1)
+            avr_asm_len ("adiw %1,1", xop, plen, 1);
+        }
+    }
+  
+  if (r30_in_tmp)
+    avr_asm_len ("mov %1,%2", xop, plen, 1);
+  
+  if (!clobber_z
+      && n_bytes > 1
+      && !reg_unused_after (insn, lpm_addr_reg_rtx)
+      && !reg_overlap_mentioned_p (op[0], lpm_addr_reg_rtx))
+    {
+      xop[2] = GEN_INT (n_bytes-1);
+      avr_asm_len ("sbiw %1,%2", xop, plen, 1);
+    }
+  
+  if (REG_P (xsegment) && AVR_HAVE_RAMPD)
+    {
+      /* Reset RAMPZ to 0 so that EBI devices don't read garbage from RAM */
       
-      break; /* REG */
-
-    case POST_INC:
-
-      gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
-                  && n_bytes <= 4);
-
-      avr_asm_len                    ("%4lpm %A0,%a2+", xop, plen, 1);
-      if (n_bytes >= 2)  avr_asm_len ("%4lpm %B0,%a2+", xop, plen, 1);
-      if (n_bytes >= 3)  avr_asm_len ("%4lpm %C0,%a2+", xop, plen, 1);
-      if (n_bytes >= 4)  avr_asm_len ("%4lpm %D0,%a2+", xop, plen, 1);
-
-      break; /* POST_INC */
+      avr_asm_len ("out __RAMPZ__,__zero_reg__", xop, plen, 1);
+    }
 
-    } /* switch CODE (addr) */
-      
   return "";
 }
 
@@ -2782,8 +2673,9 @@ avr_out_xload (rtx insn ATTRIBUTE_UNUSED, rtx *op, int *plen)
   if (plen)
     *plen = 0;
 
-  avr_asm_len ("ld %3,%a2" CR_TAB
-               "sbrs %1,7", xop, plen, 2);
+  avr_asm_len ("sbrc %1,7" CR_TAB
+               "ld %3,%a2" CR_TAB
+               "sbrs %1,7", xop, plen, 3);
 
   avr_asm_len (AVR_HAVE_LPMX ? "lpm %3,%a2" : "lpm", xop, plen, 1);
 
@@ -2794,13 +2686,11 @@ avr_out_xload (rtx insn ATTRIBUTE_UNUSED, rtx *op, int *plen)
 }
 
 
-const char *
-output_movqi (rtx insn, rtx operands[], int *l)
+const char*
+output_movqi (rtx insn, rtx operands[], int *real_l)
 {
-  int dummy;
   rtx dest = operands[0];
   rtx src = operands[1];
-  int *real_l = l;
   
   if (avr_mem_flash_p (src)
       || avr_mem_flash_p (dest))
@@ -2808,10 +2698,8 @@ output_movqi (rtx insn, rtx operands[], int *l)
       return avr_out_lpm (insn, operands, real_l);
     }
 
-  if (!l)
-    l = &dummy;
-
-  *l = 1;
+  if (real_l)
+    *real_l = 1;
   
   if (register_operand (dest, QImode))
     {
@@ -2829,10 +2717,10 @@ output_movqi (rtx insn, rtx operands[], int *l)
           output_reload_in_const (operands, NULL_RTX, real_l, false);
           return "";
         }
-      else if (GET_CODE (src) == MEM)
+      else if (MEM_P (src))
        return out_movqi_r_mr (insn, operands, real_l); /* mov r,m */
     }
-  else if (GET_CODE (dest) == MEM)
+  else if (MEM_P (dest))
     {
       rtx xop[2];
 
@@ -6533,6 +6421,7 @@ adjust_insn_length (rtx insn, int len)
     case ADJUST_LEN_MOV32: output_movsisf (insn, op, &len); break;
     case ADJUST_LEN_MOVMEM: avr_out_movmem (insn, op, &len); break;
     case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break;
+    case ADJUST_LEN_LOAD_LPM: avr_load_lpm (insn, op, &len); break;
 
     case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break;
     case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break;
@@ -8975,7 +8864,7 @@ avr_hard_regno_mode_ok (int regno, enum machine_mode mode)
 
 /* Implement `MODE_CODE_BASE_REG_CLASS'.  */
 
-reg_class_t
+enum reg_class
 avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
                               addr_space_t as, RTX_CODE outer_code,
                               RTX_CODE index_code ATTRIBUTE_UNUSED)
@@ -9568,7 +9457,8 @@ avr_addr_space_pointer_mode (addr_space_t as)
 static bool
 avr_reg_ok_for_pgm_addr (rtx reg, bool strict)
 {
-  gcc_assert (REG_P (reg));
+  if (!REG_P (reg))
+    return false;
 
   if (strict)
     {
@@ -9916,7 +9806,7 @@ avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *op, int *plen)
     case ADDR_SPACE_FLASH:
 
       if (AVR_HAVE_LPMX)
-        avr_asm_len ("lpm %2,%Z+", xop, plen, 1);
+        avr_asm_len ("lpm %2,Z+", xop, plen, 1);
       else
         avr_asm_len ("lpm" CR_TAB
                      "adiw r30,1", xop, plen, 2);
@@ -9965,6 +9855,14 @@ avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *op, int *plen)
 \f
 /* Helper for __builtin_avr_delay_cycles */
 
+static rtx
+avr_mem_clobber (void)
+{
+  rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
+  MEM_VOLATILE_P (mem) = 1;
+  return mem;
+}
+
 static void
 avr_expand_delay_cycles (rtx operands0)
 {
@@ -9976,7 +9874,8 @@ avr_expand_delay_cycles (rtx operands0)
     {
       loop_count = ((cycles - 9) / 6) + 1;
       cycles_used = ((loop_count - 1) * 6) + 9;
-      emit_insn (gen_delay_cycles_4 (gen_int_mode (loop_count, SImode)));
+      emit_insn (gen_delay_cycles_4 (gen_int_mode (loop_count, SImode),
+                                     avr_mem_clobber()));
       cycles -= cycles_used;
     }
   
@@ -9986,7 +9885,8 @@ avr_expand_delay_cycles (rtx operands0)
       if (loop_count > 0xFFFFFF)
         loop_count = 0xFFFFFF;
       cycles_used = ((loop_count - 1) * 5) + 7;
-      emit_insn (gen_delay_cycles_3 (gen_int_mode (loop_count, SImode)));
+      emit_insn (gen_delay_cycles_3 (gen_int_mode (loop_count, SImode),
+                                     avr_mem_clobber()));
       cycles -= cycles_used;
     }
   
@@ -9996,7 +9896,8 @@ avr_expand_delay_cycles (rtx operands0)
       if (loop_count > 0xFFFF)
         loop_count = 0xFFFF;
       cycles_used = ((loop_count - 1) * 4) + 5;
-      emit_insn (gen_delay_cycles_2 (gen_int_mode (loop_count, HImode)));
+      emit_insn (gen_delay_cycles_2 (gen_int_mode (loop_count, HImode),
+                                     avr_mem_clobber()));
       cycles -= cycles_used;
     }
   
@@ -10006,7 +9907,8 @@ avr_expand_delay_cycles (rtx operands0)
       if (loop_count > 255) 
         loop_count = 255;
       cycles_used = loop_count * 3;
-      emit_insn (gen_delay_cycles_1 (gen_int_mode (loop_count, QImode)));
+      emit_insn (gen_delay_cycles_1 (gen_int_mode (loop_count, QImode),
+                                     avr_mem_clobber()));
       cycles -= cycles_used;
       }
   
@@ -11007,6 +10909,9 @@ avr_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED, tree *arg,
 #undef  TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS
 #define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address
 
+#undef  TARGET_MODE_DEPENDENT_ADDRESS_P
+#define TARGET_MODE_DEPENDENT_ADDRESS_P avr_mode_dependent_address_p
+
 #undef  TARGET_PRINT_OPERAND
 #define TARGET_PRINT_OPERAND avr_print_operand
 #undef  TARGET_PRINT_OPERAND_ADDRESS
index 5cc3e3e..3fe06da 100644 (file)
@@ -63,6 +63,7 @@
   [UNSPEC_STRLEN
    UNSPEC_MOVMEM
    UNSPEC_INDEX_JMP
+   UNSPEC_LPM
    UNSPEC_FMUL
    UNSPEC_FMULS
    UNSPEC_FMULSU
@@ -77,6 +78,7 @@
    UNSPECV_WRITE_SP
    UNSPECV_GOTO_RECEIVER
    UNSPECV_ENABLE_IRQS
+   UNSPECV_MEMORY_BARRIER
    UNSPECV_NOP
    UNSPECV_SLEEP
    UNSPECV_WDR
   "out_bitop, out_plus, out_plus_noclobber, plus64, addto_sp,
    tsthi, tstpsi, tstsi, compare, compare64, call,
    mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32,
-   xload, movmem,
+   xload, movmem, load_lpm,
    ashlqi, ashrqi, lshrqi,
    ashlhi, ashrhi, lshrhi,
    ashlsi, ashrsi, lshrsi,
 ;;========================================================================
 ;; Move stuff around
 
-(define_expand "load<mode>_libgcc"
-  [(set (match_dup 3)
-        (match_dup 2))
-   (set (reg:MOVMODE 22)
-        (match_operand:MOVMODE 1 "memory_operand" ""))
-   (set (match_operand:MOVMODE 0 "register_operand" "")
-        (reg:MOVMODE 22))]
-  "avr_load_libgcc_p (operands[1])"
-  {
-    operands[3] = gen_rtx_REG (HImode, REG_Z);
-    operands[2] = force_operand (XEXP (operands[1], 0), NULL_RTX);
-    operands[1] = replace_equiv_address (operands[1], operands[3]);
-    set_mem_addr_space (operands[1], ADDR_SPACE_FLASH);
-  })
-    
+;; Represent a load from __flash that needs libgcc support as UNSPEC.
+;; This is legal because we read from non-changing memory.
+;; For rationale see the FIXME below.
+
+;; "load_psi_libgcc"    
+;; "load_si_libgcc"    
+;; "load_sf_libgcc"    
 (define_insn "load_<mode>_libgcc"
   [(set (reg:MOVMODE 22)
-        (match_operand:MOVMODE 0 "memory_operand" "m,m"))]
-  "avr_load_libgcc_p (operands[0])
-   && REG_P (XEXP (operands[0], 0))
-   && REG_Z == REGNO (XEXP (operands[0], 0))"
+        (unspec:MOVMODE [(reg:HI REG_Z)]
+                        UNSPEC_LPM))]
+  ""
   {
-    operands[0] = GEN_INT (GET_MODE_SIZE (<MODE>mode));
-    return "%~call __load_%0";
+    rtx n_bytes = GEN_INT (GET_MODE_SIZE (<MODE>mode));
+    output_asm_insn ("%~call __load_%0", &n_bytes);
+    return "";
   }
-  [(set_attr "length" "1,2")
-   (set_attr "isa" "rjmp,jmp")
+  [(set_attr "type" "xcall")
+   (set_attr "cc" "clobber")])
+
+
+;; Similar for inline reads from flash.  We use UNSPEC instead
+;; of MEM for the same reason as above: PR52543.
+;; $1 contains the memory segment.
+
+(define_insn "load_<mode>"
+  [(set (match_operand:MOVMODE 0 "register_operand" "=r")
+        (unspec:MOVMODE [(reg:HI REG_Z)
+                         (match_operand:QI 1 "reg_or_0_operand" "rL")]
+                        UNSPEC_LPM))]
+  "(CONST_INT_P (operands[1]) && AVR_HAVE_LPMX)
+   || (REG_P (operands[1]) && AVR_HAVE_ELPMX)"
+  {
+    return avr_load_lpm (insn, operands, NULL);
+  }
+  [(set_attr "adjust_len" "load_lpm")
+   (set_attr "cc" "clobber")])
+
+
+;; Similar to above for the complementary situation when there is no [E]LPMx.
+;; Clobber Z in that case.
+
+(define_insn "load_<mode>_clobber"
+  [(set (match_operand:MOVMODE 0 "register_operand" "=r")
+        (unspec:MOVMODE [(reg:HI REG_Z)
+                         (match_operand:QI 1 "reg_or_0_operand" "rL")]
+                        UNSPEC_LPM))
+   (clobber (reg:HI REG_Z))]
+  "!((CONST_INT_P (operands[1]) && AVR_HAVE_LPMX)
+     || (REG_P (operands[1]) && AVR_HAVE_ELPMX))"
+  {
+    return avr_load_lpm (insn, operands, NULL);
+  }
+  [(set_attr "adjust_len" "load_lpm")
    (set_attr "cc" "clobber")])
 
 
     DONE;
   })
 
+;; "xloadqi_A"
+;; "xloadhi_A"
+;; "xloadpsi_A"
+;; "xloadsi_A"
+;; "xloadsf_A"
 (define_insn_and_split "xload<mode>_A"
   [(set (match_operand:MOVMODE 0 "register_operand" "=r")
         (match_operand:MOVMODE 1 "memory_operand"    "m"))
+   (clobber (reg:MOVMODE 22))
    (clobber (reg:QI 21))
    (clobber (reg:HI REG_Z))]
   "can_create_pseudo_p()
   {
     return avr_out_xload (insn, operands, NULL);
   }
-  [(set_attr "length" "3,4")
+  [(set_attr "length" "4,4")
    (set_attr "adjust_len" "*,xload")
    (set_attr "isa" "lpmx,lpm")
    (set_attr "cc" "none")])
       DONE;
     }
 
+    /* For old devices without LPMx, prefer __flash loads per libcall.  */
+
     if (avr_load_libgcc_p (src))
       {
-        /* For the small devices, do loads per libgcc call.  */
-        emit_insn (gen_load<mode>_libgcc (dest, src));
+        emit_move_insn (gen_rtx_REG (Pmode, REG_Z),
+                        force_reg (Pmode, XEXP (src, 0)));
+
+        emit_insn (gen_load_<mode>_libgcc ());
+        emit_move_insn (dest, gen_rtx_REG (<MODE>mode, 22));
         DONE;
       }
+
+    /* ; FIXME:  Hack around PR rtl-optimization/52543.
+       ; lower-subreg.c splits loads from the 16-bit address spaces which
+       ; causes code bloat because each load need his setting of RAMPZ.
+       ; Moreover, the split will happen in such a way that the loads don't
+       ; take advantage of POST_INC addressing.  Thus, we use UNSPEC to
+       ; represent these loads instead.  Notice that this is legitimate
+       ; because the memory content does not change:  Loads from the same
+       ; address will yield the same value.
+       ; POST_INC addressing would make the addresses mode_dependent and could
+       ; work around that PR, too.  However, notice that it is *not* legitimate
+       ; to expand to POST_INC at expand time:  The following passes assert
+       ; that pre-/post-modify addressing is introduced by .auto_inc_dec and
+       ; does not exist before that pass.  */
+
+    if (avr_mem_flash_p (src)
+        && (GET_MODE_SIZE (<MODE>mode) > 1
+            || MEM_ADDR_SPACE (src) != ADDR_SPACE_FLASH))
+      {
+        rtx xsegment = GEN_INT (avr_addrspace[MEM_ADDR_SPACE (src)].segment);
+        if (!AVR_HAVE_ELPM)
+          xsegment = const0_rtx;
+        if (xsegment != const0_rtx)
+          xsegment = force_reg (QImode, xsegment);
+
+        emit_move_insn (gen_rtx_REG (Pmode, REG_Z),
+                        force_reg (Pmode, XEXP (src, 0)));
+
+        if ((CONST_INT_P (xsegment) && AVR_HAVE_LPMX)
+            || (REG_P (xsegment) && AVR_HAVE_ELPMX))
+          emit_insn (gen_load_<mode> (dest, xsegment));
+        else
+          emit_insn (gen_load_<mode>_clobber (dest, xsegment));
+        DONE;
+      }
+
+    /* ; The only address-space for which we use plain MEM and reload
+       ; machinery are 1-byte loads from __flash.  */
   })
 
 ;;========================================================================
     operands[5] = gen_rtx_REG (HImode, REGNO (operands[3]));
   })
 
-;; For LPM loads from AS1 we split 
-;;    R = *Z
-;; to
-;;    R = *Z++
-;;    Z = Z - sizeof (R)
-;;
-;; so that the second instruction can be optimized out.
-
-(define_split ; "split-lpmx"
-  [(set (match_operand:HISI 0 "register_operand" "")
-        (match_operand:HISI 1 "memory_operand" ""))]
-  "reload_completed
-   && AVR_HAVE_LPMX"
-  [(set (match_dup 0)
-        (match_dup 2))
-   (set (match_dup 3)
-        (plus:HI (match_dup 3)
-                 (match_dup 4)))]
-  {
-     rtx addr = XEXP (operands[1], 0);
-
-     if (!avr_mem_flash_p (operands[1])
-         || !REG_P (addr)
-         || reg_overlap_mentioned_p (addr, operands[0]))
-       {
-         FAIL;
-       }
-
-    operands[2] = replace_equiv_address (operands[1],
-                                         gen_rtx_POST_INC (Pmode, addr));
-    operands[3] = addr;
-    operands[4] = gen_int_mode (-GET_MODE_SIZE (<MODE>mode), HImode);
-  })
-
 ;;==========================================================================
 ;; xpointer move (24 bit)
   
    (set_attr "adjust_len" "addto_sp")])
 
 (define_insn "*addhi3"
-  [(set (match_operand:HI 0 "register_operand"          "=r,d,d")
-        (plus:HI (match_operand:HI 1 "register_operand" "%0,0,0")
-                 (match_operand:HI 2 "nonmemory_operand" "r,s,n")))]
+  [(set (match_operand:HI 0 "register_operand"          "=r,d,!w,d")
+        (plus:HI (match_operand:HI 1 "register_operand" "%0,0,0 ,0")
+                 (match_operand:HI 2 "nonmemory_operand" "r,s,IJ,n")))]
   ""
   {
     static const char * const asm_code[] =
       {
         "add %A0,%A2\;adc %B0,%B2",
         "subi %A0,lo8(-(%2))\;sbci %B0,hi8(-(%2))",
+        "",
         ""
       };
 
 
     return avr_out_plus_noclobber (operands, NULL, NULL);
   }
-  [(set_attr "length" "2,2,2")
-   (set_attr "adjust_len" "*,*,out_plus_noclobber")
-   (set_attr "cc" "set_n,set_czn,out_plus_noclobber")])
+  [(set_attr "length" "2,2,2,2")
+   (set_attr "adjust_len" "*,*,out_plus_noclobber,out_plus_noclobber")
+   (set_attr "cc" "set_n,set_czn,out_plus_noclobber,out_plus_noclobber")])
 
 ;; Adding a constant to NO_LD_REGS might have lead to a reload of
 ;; that constant to LD_REGS.  We don't add a scratch to *addhi3
               (clobber (match_dup 2))])])
 
 (define_insn "addhi3_clobber"
-  [(set (match_operand:HI 0 "register_operand"           "=d,l")
-        (plus:HI (match_operand:HI 1 "register_operand"  "%0,0")
-                 (match_operand:HI 2 "const_int_operand"  "n,n")))
-   (clobber (match_scratch:QI 3                          "=X,&d"))]
+  [(set (match_operand:HI 0 "register_operand"           "=!w,d,r")
+        (plus:HI (match_operand:HI 1 "register_operand"   "%0,0,0")
+                 (match_operand:HI 2 "const_int_operand"  "IJ,n,n")))
+   (clobber (match_scratch:QI 3                           "=X,X,&d"))]
   ""
   {
     gcc_assert (REGNO (operands[0]) == REGNO (operands[1]));
 
 ;; Handle small constants
 
+;; Special case of a += 2*b as frequently seen with accesses to int arrays.
+;; This is shorter, faster than MUL and has lower register pressure.
+
+(define_insn_and_split "*umaddqihi4.2"
+  [(set (match_operand:HI 0 "register_operand"                                  "=r")
+        (plus:HI (mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "r"))
+                          (const_int 2))
+                 (match_operand:HI 2 "register_operand"                          "r")))]
+  "!reload_completed
+   && !reg_overlap_mentioned_p (operands[0], operands[1])"
+  { gcc_unreachable(); }
+  "&& 1"
+  [(set (match_dup 0)
+        (match_dup 2))
+   ; *addhi3_zero_extend
+   (set (match_dup 0)
+        (plus:HI (zero_extend:HI (match_dup 1))
+                 (match_dup 0)))
+   ; *addhi3_zero_extend
+   (set (match_dup 0)
+        (plus:HI (zero_extend:HI (match_dup 1))
+                 (match_dup 0)))])
+
 ;; "umaddqihi4.uconst"
 ;; "maddqihi4.sconst"
 (define_insn_and_split "*<extend_u>maddqihi4.<extend_su>const"
    (set_attr "length" "1")])
 
 ;; Enable Interrupts
-(define_insn "enable_interrupt"
-  [(unspec_volatile [(const_int 1)] UNSPECV_ENABLE_IRQS)]
+(define_expand "enable_interrupt"
+  [(clobber (const_int 0))]
   ""
-  "sei"
-  [(set_attr "length" "1")
-   (set_attr "cc" "none")])
+  {
+    rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
+    MEM_VOLATILE_P (mem) = 1;
+    emit_insn (gen_cli_sei (const1_rtx, mem));
+    DONE;
+  })
 
 ;; Disable Interrupts
-(define_insn "disable_interrupt"
-  [(unspec_volatile [(const_int 0)] UNSPECV_ENABLE_IRQS)]
+(define_expand "disable_interrupt"
+  [(clobber (const_int 0))]
+  ""
+  {
+    rtx mem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
+    MEM_VOLATILE_P (mem) = 1;
+    emit_insn (gen_cli_sei (const0_rtx, mem));
+    DONE;
+  })
+
+(define_insn "cli_sei"
+  [(unspec_volatile [(match_operand:QI 0 "const_int_operand" "L,P")]
+                    UNSPECV_ENABLE_IRQS)
+   (set (match_operand:BLK 1 "" "")
+       (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))]
   ""
-  "cli"
+  "@
+       cli
+       sei"
   [(set_attr "length" "1")
    (set_attr "cc" "none")])
 
   [(unspec_volatile [(match_operand:QI 0 "const_int_operand" "n")
                      (const_int 1)]
                     UNSPECV_DELAY_CYCLES)
-   (clobber (match_scratch:QI 1 "=&d"))]
+   (set (match_operand:BLK 1 "" "")
+       (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))
+   (clobber (match_scratch:QI 2 "=&d"))]
   ""
-  "ldi %1,lo8(%0)
-       1: dec %1
+  "ldi %2,lo8(%0)
+       1: dec %2
        brne 1b"
   [(set_attr "length" "3")
    (set_attr "cc" "clobber")])
   [(unspec_volatile [(match_operand:HI 0 "const_int_operand" "n")
                      (const_int 2)]
                     UNSPECV_DELAY_CYCLES)
-   (clobber (match_scratch:HI 1 "=&w"))]
+   (set (match_operand:BLK 1 "" "")
+       (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))
+   (clobber (match_scratch:HI 2 "=&w"))]
   ""
-  "ldi %A1,lo8(%0)
-       ldi %B1,hi8(%0)
-       1: sbiw %A1,1
+  "ldi %A2,lo8(%0)
+       ldi %B2,hi8(%0)
+       1: sbiw %A2,1
        brne 1b"
   [(set_attr "length" "4")
    (set_attr "cc" "clobber")])
   [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "n")
                      (const_int 3)]
                     UNSPECV_DELAY_CYCLES)
-   (clobber (match_scratch:QI 1 "=&d"))
+   (set (match_operand:BLK 1 "" "")
+       (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))
    (clobber (match_scratch:QI 2 "=&d"))
-   (clobber (match_scratch:QI 3 "=&d"))]
+   (clobber (match_scratch:QI 3 "=&d"))
+   (clobber (match_scratch:QI 4 "=&d"))]
   ""
-  "ldi %1,lo8(%0)
-       ldi %2,hi8(%0)
-       ldi %3,hlo8(%0)
-       1: subi %1,1
-       sbci %2,0
+  "ldi %2,lo8(%0)
+       ldi %3,hi8(%0)
+       ldi %4,hlo8(%0)
+       1: subi %2,1
        sbci %3,0
+       sbci %4,0
        brne 1b"
   [(set_attr "length" "7")
    (set_attr "cc" "clobber")])
   [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "n")
                      (const_int 4)]
                     UNSPECV_DELAY_CYCLES)
-   (clobber (match_scratch:QI 1 "=&d"))
+   (set (match_operand:BLK 1 "" "")
+       (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))
    (clobber (match_scratch:QI 2 "=&d"))
    (clobber (match_scratch:QI 3 "=&d"))
-   (clobber (match_scratch:QI 4 "=&d"))]
-  ""
-  "ldi %1,lo8(%0)
-       ldi %2,hi8(%0)
-       ldi %3,hlo8(%0)
-       ldi %4,hhi8(%0)
-       1: subi %1,1
-       sbci %2,0
+   (clobber (match_scratch:QI 4 "=&d"))
+   (clobber (match_scratch:QI 5 "=&d"))]
+  ""
+  "ldi %2,lo8(%0)
+       ldi %3,hi8(%0)
+       ldi %4,hlo8(%0)
+       ldi %5,hhi8(%0)
+       1: subi %2,1
        sbci %3,0
        sbci %4,0
+       sbci %5,0
        brne 1b"
   [(set_attr "length" "9")
    (set_attr "cc" "clobber")])
 ;; CPU instructions
 
 ;; NOP taking 1 or 2 Ticks 
-(define_insn "nopv"
+(define_expand "nopv"
+  [(parallel [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "")] 
+                               UNSPECV_NOP)
+              (set (match_dup 1)
+                   (unspec_volatile:BLK [(match_dup 1)]
+                                        UNSPECV_MEMORY_BARRIER))])]
+  ""
+  {
+    operands[1] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
+    MEM_VOLATILE_P (operands[1]) = 1;
+  })
+
+(define_insn "*nopv"
   [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "P,K")] 
-                    UNSPECV_NOP)]
+                    UNSPECV_NOP)
+   (set (match_operand:BLK 1 "" "")
+       (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER))]
   ""
   "@
        nop
    (set_attr "cc" "none")])
 
 ;; SLEEP
-(define_insn "sleep"
-  [(unspec_volatile [(const_int 0)] UNSPECV_SLEEP)]
+(define_expand "sleep"
+  [(parallel [(unspec_volatile [(const_int 0)] UNSPECV_SLEEP)
+              (set (match_dup 0)
+                   (unspec_volatile:BLK [(match_dup 0)]
+                                        UNSPECV_MEMORY_BARRIER))])]
+  ""
+  {
+    operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
+    MEM_VOLATILE_P (operands[0]) = 1;
+  })
+
+(define_insn "*sleep"
+  [(unspec_volatile [(const_int 0)] UNSPECV_SLEEP)
+   (set (match_operand:BLK 0 "" "")
+       (unspec_volatile:BLK [(match_dup 0)] UNSPECV_MEMORY_BARRIER))]
   ""
   "sleep"
   [(set_attr "length" "1")
    (set_attr "cc" "none")])
  
 ;; WDR
-(define_insn "wdr"
-  [(unspec_volatile [(const_int 0)] UNSPECV_WDR)]
+(define_expand "wdr"
+  [(parallel [(unspec_volatile [(const_int 0)] UNSPECV_WDR)
+              (set (match_dup 0)
+                   (unspec_volatile:BLK [(match_dup 0)]
+                                        UNSPECV_MEMORY_BARRIER))])]
+  ""
+  {
+    operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
+    MEM_VOLATILE_P (operands[0]) = 1;
+  })
+
+(define_insn "*wdr"
+  [(unspec_volatile [(const_int 0)] UNSPECV_WDR)
+   (set (match_operand:BLK 0 "" "")
+       (unspec_volatile:BLK [(match_dup 0)] UNSPECV_MEMORY_BARRIER))]
   ""
   "wdr"
   [(set_attr "length" "1")
index e56ce50..33a229e 100644 (file)
@@ -1,3 +1,30 @@
+2012-03-22  Georg-Johann Lay  <avr@gjlay.de>
+
+       Backport from 2012-03-07 mainline r185033.
+
+       PR target/52507
+       * config/avr/lib1funcs.S (__movmemx_hi): Fix loop label in RAM-part.
+
+       Backport from 2012-03-07 mainline r185031.
+
+       PR target/52505
+       * config/avr/lib1funcs.S (__xload_1): Don't read unintentionally
+       from RAM.
+
+       Backport from 2012-03-07 mainline r185030.
+
+       PR target/52461
+       PR target/52508
+       * config/avr/lib1funcs.S (__do_copy_data): Clear RAMPZ after usage
+       if RAMPZ affects reading from RAM.
+       (__tablejump_elpm__): Ditto.
+       (.xload): Ditto.
+       (__movmemx_hi): Ditto.
+       (__do_global_ctors): Right condition for RAMPZ usage is "have ELPM".
+       (__do_global_dtors): Ditto.
+       (__xload_1, __xload_2, __xload_3, __xload_4): Ditto.
+       (__movmemx_hi): Ditto.
+
 2012-03-22  Release Manager
 
        * GCC 4.7.0 released.
index 2454b51..9bd235b 100644 (file)
@@ -1893,6 +1893,10 @@ DEFUN __do_copy_data
        cpc     r27, r17
        brne    .L__do_copy_data_loop
 #endif /* !defined(__AVR_HAVE_ELPMX__) && !defined(__AVR_HAVE_ELPM__) */
+#if defined (__AVR_HAVE_ELPM__) && defined (__AVR_HAVE_RAMPD__)
+       ;; Reset RAMPZ to 0 so that EBI devices don't read garbage from RAM
+       out     __RAMPZ__, __zero_reg__
+#endif /* ELPM && RAMPD */
 ENDF __do_copy_data
 #endif /* L_copy_data */
 
@@ -1920,7 +1924,7 @@ ENDF __do_clear_bss
 #ifdef L_ctors
        .section .init6,"ax",@progbits
 DEFUN __do_global_ctors
-#if defined(__AVR_HAVE_RAMPZ__)
+#if defined(__AVR_HAVE_ELPM__)
        ldi     r17, hi8(__ctors_start)
        ldi     r28, lo8(__ctors_end)
        ldi     r29, hi8(__ctors_end)
@@ -1953,14 +1957,14 @@ DEFUN __do_global_ctors
        cpi     r28, lo8(__ctors_start)
        cpc     r29, r17
        brne    .L__do_global_ctors_loop
-#endif /* defined(__AVR_HAVE_RAMPZ__) */
+#endif /* defined(__AVR_HAVE_ELPM__) */
 ENDF __do_global_ctors
 #endif /* L_ctors */
 
 #ifdef L_dtors
        .section .fini6,"ax",@progbits
 DEFUN __do_global_dtors
-#if defined(__AVR_HAVE_RAMPZ__)
+#if defined(__AVR_HAVE_ELPM__)
        ldi     r17, hi8(__dtors_end)
        ldi     r28, lo8(__dtors_start)
        ldi     r29, hi8(__dtors_start)
@@ -1993,7 +1997,7 @@ DEFUN __do_global_dtors
        cpi     r28, lo8(__dtors_end)
        cpc     r29, r17
        brne    .L__do_global_dtors_loop
-#endif /* defined(__AVR_HAVE_RAMPZ__) */
+#endif /* defined(__AVR_HAVE_ELPM__) */
 ENDF __do_global_dtors
 #endif /* L_dtors */
 
@@ -2001,18 +2005,21 @@ ENDF __do_global_dtors
     
 #ifdef L_tablejump_elpm
 DEFUN __tablejump_elpm__
-#if defined (__AVR_HAVE_ELPM__)
-#if defined (__AVR_HAVE_LPMX__)
+#if defined (__AVR_HAVE_ELPMX__)
        elpm    __tmp_reg__, Z+
        elpm    r31, Z
        mov     r30, __tmp_reg__
+#if defined (__AVR_HAVE_RAMPD__)
+       ;; Reset RAMPZ to 0 so that EBI devices don't read garbage from RAM
+       out     __RAMPZ__, __zero_reg__
+#endif /* RAMPD */
 #if defined (__AVR_HAVE_EIJMP_EICALL__)
        eijmp
 #else
        ijmp
 #endif
 
-#else
+#elif defined (__AVR_HAVE_ELPM__)
        elpm
        adiw    r30, 1
        push    r0
@@ -2024,7 +2031,6 @@ DEFUN __tablejump_elpm__
 #endif
        ret
 #endif
-#endif /* defined (__AVR_HAVE_ELPM__) */
 ENDF __tablejump_elpm__
 #endif /* defined (L_tablejump_elpm) */
 
@@ -2114,11 +2120,18 @@ ENDF __load_4
     adiw    r30, 1
 .endif
 #endif
+#if defined (__AVR_HAVE_ELPM__) && defined (__AVR_HAVE_RAMPD__)
+.if \dest == D0+\n-1
+    ;; Reset RAMPZ to 0 so that EBI devices don't read garbage from RAM
+    out     __RAMPZ__, __zero_reg__
+.endif
+#endif
 .endm ; .xload
 
 #if defined (L_xload_1)
 DEFUN __xload_1
-#if defined (__AVR_HAVE_LPMX__) && !defined (__AVR_HAVE_RAMPZ__)
+#if defined (__AVR_HAVE_LPMX__) && !defined (__AVR_HAVE_ELPM__)
+    sbrc    HHI8, 7
     ld      D0, Z
     sbrs    HHI8, 7
     lpm     D0, Z
@@ -2126,14 +2139,14 @@ DEFUN __xload_1
 #else
     sbrc    HHI8, 7
     rjmp    1f
-#if defined (__AVR_HAVE_RAMPZ__)
+#if defined (__AVR_HAVE_ELPM__)
     out     __RAMPZ__, HHI8
-#endif /* __AVR_HAVE_RAMPZ__ */
+#endif /* __AVR_HAVE_ELPM__ */
     .xload  D0, 1
     ret
 1:  ld      D0, Z
     ret
-#endif /* LPMx && ! RAMPZ */
+#endif /* LPMx && ! ELPM */
 ENDF __xload_1
 #endif /* L_xload_1 */
 
@@ -2141,9 +2154,9 @@ ENDF __xload_1
 DEFUN __xload_2
     sbrc    HHI8, 7
     rjmp    1f
-#if defined (__AVR_HAVE_RAMPZ__)
+#if defined (__AVR_HAVE_ELPM__)
     out     __RAMPZ__, HHI8
-#endif /* __AVR_HAVE_RAMPZ__ */
+#endif /* __AVR_HAVE_ELPM__ */
     .xload  D0, 2
     .xload  D1, 2
     ret
@@ -2157,9 +2170,9 @@ ENDF __xload_2
 DEFUN __xload_3
     sbrc    HHI8, 7
     rjmp    1f
-#if defined (__AVR_HAVE_RAMPZ__)
+#if defined (__AVR_HAVE_ELPM__)
     out     __RAMPZ__, HHI8
-#endif /* __AVR_HAVE_RAMPZ__ */
+#endif /* __AVR_HAVE_ELPM__ */
     .xload  D0, 3
     .xload  D1, 3
     .xload  D2, 3
@@ -2175,9 +2188,9 @@ ENDF __xload_3
 DEFUN __xload_4
     sbrc    HHI8, 7
     rjmp    1f
-#if defined (__AVR_HAVE_RAMPZ__)
+#if defined (__AVR_HAVE_ELPM__)
     out     __RAMPZ__, HHI8
-#endif /* __AVR_HAVE_RAMPZ__ */
+#endif /* __AVR_HAVE_ELPM__ */
     .xload  D0, 4
     .xload  D1, 4
     .xload  D2, 4
@@ -2219,7 +2232,7 @@ DEFUN __movmemx_hi
 
 ;; Read from Flash
 
-#if defined (__AVR_HAVE_RAMPZ__)
+#if defined (__AVR_HAVE_ELPM__)
     out     __RAMPZ__, HHI8
 #endif
 
@@ -2243,6 +2256,10 @@ DEFUN __movmemx_hi
     st      X+, r0
     sbiw    LOOP, 1
     brne    0b
+#if defined (__AVR_HAVE_ELPM__) && defined (__AVR_HAVE_RAMPD__)
+    ;; Reset RAMPZ to 0 so that EBI devices don't read garbage from RAM
+    out        __RAMPZ__, __zero_reg__
+#endif /* ELPM && RAMPD */
     ret
 
 ;; Read from RAM
@@ -2252,7 +2269,7 @@ DEFUN __movmemx_hi
     ;; and store that Byte to RAM Destination
     st      X+, r0
     sbiw    LOOP, 1
-    brne    0b
+    brne    1b
     ret
 ENDF __movmemx_hi