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
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
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);
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));
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
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
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 ();
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
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 ();
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);
emit_insn (fp_plus_insns);
}
- cfun->machine->stack_usage += size;
+ cfun->machine->stack_usage += size_cfa;
} /* !minimize && size != 0 */
} /* !minimize */
}
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 */
int irq_state = -1;
rtx fp, my_fp;
rtx fp_plus_insns;
+ HOST_WIDE_INT size_max;
gcc_assert (frame_pointer_needed
|| !isr_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 **********/
/* 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
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)
}
+/* 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
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. */
}
-/* 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.
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);
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();
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 "";
}
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);
}
-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))
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))
{
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];
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;
/* 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)
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)
{
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);
\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)
{
{
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;
}
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;
}
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;
}
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;
}
#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
[UNSPEC_STRLEN
UNSPEC_MOVMEM
UNSPEC_INDEX_JMP
+ UNSPEC_LPM
UNSPEC_FMUL
UNSPEC_FMULS
UNSPEC_FMULSU
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")
+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.
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 */
#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)
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)
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 */
#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
#endif
ret
#endif
-#endif /* defined (__AVR_HAVE_ELPM__) */
ENDF __tablejump_elpm__
#endif /* defined (L_tablejump_elpm) */
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
#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 */
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
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
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
;; Read from Flash
-#if defined (__AVR_HAVE_RAMPZ__)
+#if defined (__AVR_HAVE_ELPM__)
out __RAMPZ__, HHI8
#endif
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
;; and store that Byte to RAM Destination
st X+, r0
sbiw LOOP, 1
- brne 0b
+ brne 1b
ret
ENDF __movmemx_hi