AVR_LdSt_Props (int regno, bool store_p, bool volatile_p, addr_space_t as)
{
bool generic_p = ADDR_SPACE_GENERIC_P (as);
- bool flashx_p = ! generic_p && as != ADDR_SPACE_MEMX;
+ bool flashx_p = (! generic_p
+ && as != ADDR_SPACE_MEMX && as != ADDR_SPACE_FLASHX);
has_postinc = generic_p || (flashx_p && regno == REG_Z);
has_predec = generic_p;
has_ldd = ! AVR_TINY && generic_p && (regno == REG_Y || regno == REG_Z);
extern const char* avr_out_round (rtx_insn *, rtx*, int* =NULL);
extern const char* avr_out_addto_sp (rtx*, int*);
extern const char* avr_out_xload (rtx_insn *, rtx*, int*);
+extern const char* avr_out_fload (rtx_insn *, rtx*, int*);
extern const char* avr_out_cpymem (rtx_insn *, rtx*, int*);
extern const char* avr_out_insert_bits (rtx*, int*);
extern bool avr_popcount_each_byte (rtx, int, int);
extern rtx avr_legitimize_reload_address (rtx*, machine_mode, int, int, int, int, rtx (*)(rtx,int));
extern bool avr_adiw_reg_p (rtx);
extern bool avr_mem_flash_p (rtx);
+extern bool avr_mem_flashx_p (rtx);
extern bool avr_mem_memx_p (rtx);
extern bool avr_load_libgcc_p (rtx);
extern bool avr_xload_libgcc_p (machine_mode);
+extern bool avr_fload_libgcc_p (machine_mode);
+extern bool avr_load_libgcc_mem_p (rtx, addr_space_t, bool use_libgcc);
+extern bool avr_load_libgcc_insn_p (rtx_insn *, addr_space_t, bool use_libgcc);
extern rtx avr_eval_addr_attrib (rtx x);
extern bool avr_float_lib_compare_returns_bool (machine_mode, rtx_code);
{ ADDR_SPACE_FLASH3, 1, 2, "__flash3", 3, ".progmem3.data" },
{ ADDR_SPACE_FLASH4, 1, 2, "__flash4", 4, ".progmem4.data" },
{ ADDR_SPACE_FLASH5, 1, 2, "__flash5", 5, ".progmem5.data" },
+ { ADDR_SPACE_FLASHX, 1, 3, "__flashx", 0, ".progmemx.data" },
{ ADDR_SPACE_MEMX, 1, 3, "__memx", 0, ".progmemx.data" },
};
/* Return TRUE if DECL is a VAR_DECL located in the 24-bit flash
- address space and FALSE, otherwise. */
+ address space __memx and FALSE, otherwise. */
static bool
avr_decl_memx_p (tree decl)
}
+/* Return TRUE if DECL is a VAR_DECL located in the 24-bit flash
+ address space __flashx and FALSE, otherwise. */
+
+static bool
+avr_decl_flashx_p (tree decl)
+{
+ if (TREE_CODE (decl) != VAR_DECL
+ || TREE_TYPE (decl) == error_mark_node)
+ {
+ return false;
+ }
+
+ return ADDR_SPACE_FLASHX == TYPE_ADDR_SPACE (TREE_TYPE (decl));
+}
+
+
/* Return TRUE if X is a MEM rtx located in flash and FALSE, otherwise. */
bool
}
-/* Return TRUE if X is a MEM rtx located in the 24-bit flash
- address space and FALSE, otherwise. */
+/* Return TRUE if X is a MEM rtx located in the 24-bit
+ address space __memx and FALSE, otherwise. */
bool
avr_mem_memx_p (rtx x)
}
+/* Return TRUE if X is a MEM rtx located in the 24-bit flash
+ address space __flashx and FALSE, otherwise. */
+
+bool
+avr_mem_flashx_p (rtx x)
+{
+ return (MEM_P (x)
+ && ADDR_SPACE_FLASHX == MEM_ADDR_SPACE (x));
+}
+
+
/* A helper for the subsequent function attribute used to dig for
attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE. */
}
-/* Return true if a value of mode MODE is read by __xload_* function. */
+/* Return true if a value of mode MODE is read by __xload_* function
+ provided it is located in __memx. */
bool
avr_xload_libgcc_p (machine_mode mode)
}
+/* Return true if a value of mode MODE is read by __fload_* function
+ provided it is located in __flashx. */
+
+bool
+avr_fload_libgcc_p (machine_mode)
+{
+ return (! AVR_HAVE_ELPMX
+ && ! AVR_HAVE_LPMX);
+}
+
+
+/* USE_LIBGCC = true: Return true when MEM is a mem rtx for address space
+ AS that will be loaded using a libgcc support function.
+ USE_LIBGCC = false: Return true when MEM is a mem rtx for address space
+ AS that will be loaded inline (without using a libgcc support function). */
+
+bool
+avr_load_libgcc_mem_p (rtx mem, addr_space_t as, bool use_libgcc)
+{
+ if (MEM_P (mem))
+ {
+ machine_mode mode = GET_MODE (mem);
+ rtx addr = XEXP (mem, 0);
+
+ if (MEM_ADDR_SPACE (mem) != as
+ || GET_MODE (addr) != targetm.addr_space.pointer_mode (as))
+ return false;
+
+ switch (as)
+ {
+ default:
+ gcc_unreachable ();
+
+ case ADDR_SPACE_FLASH:
+ return avr_load_libgcc_p (mem) == use_libgcc;
+
+ case ADDR_SPACE_MEMX:
+ return avr_xload_libgcc_p (mode) == use_libgcc;
+
+ case ADDR_SPACE_FLASHX:
+ return avr_fload_libgcc_p (mode) == use_libgcc;
+ }
+ }
+
+ return false;
+}
+
+
+/* Like `avr_load_libgcc_mem_p()', but for a single_set insn with
+ a SET_SRC according to avr_load_libgcc_mem_p. */
+
+bool
+avr_load_libgcc_insn_p (rtx_insn *insn, addr_space_t as, bool use_libgcc)
+{
+ rtx set = single_set (insn);
+ return (set
+ && avr_load_libgcc_mem_p (SET_SRC (set), as, use_libgcc));
+}
+
+
/* Return true when INSN has a REG_UNUSED note for hard reg REG.
rtlanal.cc::find_reg_note() uses == to compare XEXP (link, 0)
therefore use a custom function. */
}
-/* Worker function for xload_8 insn. */
+/* Load a value from 24-bit address space __memx and return "".
+ PLEN == 0: Output instructions.
+ PLEN != 0: Set *PLEN to the length of the sequence in words. */
const char *
avr_out_xload (rtx_insn * /*insn*/, rtx *op, int *plen)
}
+/* Load a value from 24-bit address space __flashx and return "".
+ PLEN == 0: Output instructions.
+ PLEN != 0: Set *PLEN to the length of the sequence in words. */
+
+const char *
+avr_out_fload (rtx_insn * /*insn*/, rtx *xop, int *plen)
+{
+ gcc_assert (AVR_HAVE_ELPMX
+ || (! AVR_HAVE_ELPM && AVR_HAVE_LPMX));
+ if (plen)
+ *plen = 0;
+
+ if (AVR_HAVE_ELPMX)
+ avr_asm_len ("out __RAMPZ__,%1", xop, plen, 1);
+
+ const int n_bytes = GET_MODE_SIZE (GET_MODE (xop[0]));
+ const char *s_load = AVR_HAVE_ELPMX ? "elpm %0,Z" : "lpm %0,Z";
+ const char *s_load_inc = AVR_HAVE_ELPMX ? "elpm %0,Z+" : "lpm %0,Z+";
+ const char *s_load_tmp_inc = AVR_HAVE_ELPMX ? "elpm r0,Z+" : "lpm r0,Z+";
+ bool use_tmp_for_r30 = false;
+
+ // There are nasty cases where reload assigns a register to dest that
+ // overlaps Z, even though fmov<mode> clobbers REG_Z.
+ for (int i = 0; i < n_bytes; ++i)
+ {
+ rtx b = avr_byte (xop[0], i);
+ if (i == n_bytes - 1)
+ avr_asm_len (s_load, &b, plen, 1);
+ else if (REGNO (b) == REG_30)
+ {
+ avr_asm_len (s_load_tmp_inc, &b, plen, 1);
+ use_tmp_for_r30 = true;
+ }
+ else
+ avr_asm_len (s_load_inc, &b, plen, 1);
+ }
+
+ if (use_tmp_for_r30)
+ avr_asm_len ("mov r30,r0", xop, plen, 1);
+
+ if (AVR_HAVE_ELPMX && AVR_HAVE_RAMPD)
+ avr_asm_len ("out __RAMPZ__,__zero_reg__", xop, plen, 1);
+
+ return "";
+}
+
+
/* A helper for `output_reload_insisf' and `output_reload_inhi'. */
/* Set register OP[0] to compile-time constant OP[1].
CLOBBER_REG is a QI clobber register or NULL_RTX.
case ADJUST_LEN_MOV32: output_movsisf (insn, op, &len); break;
case ADJUST_LEN_CPYMEM: avr_out_cpymem (insn, op, &len); break;
case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break;
+ case ADJUST_LEN_FLOAD: avr_out_fload (insn, op, &len); break;
case ADJUST_LEN_SEXT: avr_out_sign_extend (insn, op, &len); break;
case ADJUST_LEN_SFRACT: avr_out_fract (insn, op, true, &len); break;
if (TREE_CODE (decl) != VAR_DECL)
return 0;
- if (avr_decl_memx_p (decl))
+ if (avr_decl_memx_p (decl)
+ || avr_decl_flashx_p (decl))
return 2;
if (avr_decl_flash_p (decl))
break; /* FLASH */
case ADDR_SPACE_MEMX:
+ case ADDR_SPACE_FLASHX:
if (REG_P (x))
ok = (!strict
&& can_create_pseudo_p ());
&& REGNO (lo) == REG_Z);
}
- break; /* MEMX */
+ break; /* MEMX, FLASHX */
}
if (avr_log.legitimate_address_p)
/* Implement `TARGET_ADDR_SPACE_CONVERT'. */
static rtx
-avr_addr_space_convert (rtx src, tree type_from, tree type_to)
+avr_addr_space_convert (rtx src, tree type_old, tree type_new)
{
- addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (type_from));
- addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type_to));
+ addr_space_t as_old = TYPE_ADDR_SPACE (TREE_TYPE (type_old));
+ addr_space_t as_new = TYPE_ADDR_SPACE (TREE_TYPE (type_new));
+ int size_old = GET_MODE_SIZE (targetm.addr_space.pointer_mode (as_old));
+ int size_new = GET_MODE_SIZE (targetm.addr_space.pointer_mode (as_new));
if (avr_log.progmem)
avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n",
- src, type_from, type_to);
+ src, type_old, type_new);
/* Up-casting from 16-bit to 24-bit pointer. */
- if (as_from != ADDR_SPACE_MEMX
- && as_to == ADDR_SPACE_MEMX)
+ if (size_old == 2 && size_new == 3)
{
rtx sym = src;
rtx reg = gen_reg_rtx (PSImode);
if (SYMBOL_REF_P (sym)
&& ADDR_SPACE_FLASH == AVR_SYMBOL_GET_ADDR_SPACE (sym))
{
- as_from = ADDR_SPACE_FLASH;
+ as_old = ADDR_SPACE_FLASH;
}
- /* Linearize memory: RAM has bit 23 set. */
+ /* Linearize memory: RAM has bit 23 set. When as_new = __flashx then
+ this is basically UB since __flashx mistreats RAM addresses, but there
+ is no way to bail out. (Though -Waddr-space-convert will tell.) */
- int msb = ADDR_SPACE_GENERIC_P (as_from)
+ int msb = ADDR_SPACE_GENERIC_P (as_old)
? 0x80
- : avr_addrspace[as_from].segment;
+ : avr_addrspace[as_old].segment;
src = force_reg (Pmode, src);
/* Down-casting from 24-bit to 16-bit throws away the high byte. */
- if (as_from == ADDR_SPACE_MEMX
- && as_to != ADDR_SPACE_MEMX)
+ if (size_old == 3 && size_new == 2)
{
rtx new_src = gen_reg_rtx (Pmode);
}
+/* Helps the next function. */
+
+static bool
+avr_addr_space_contains (addr_space_t super, addr_space_t sub)
+{
+ return (super == sub
+ || super == ADDR_SPACE_MEMX
+ || (super == ADDR_SPACE_FLASHX
+ && sub != ADDR_SPACE_MEMX && ! ADDR_SPACE_GENERIC_P (sub)));
+}
+
+
/* Implement `TARGET_CONVERT_TO_TYPE'. */
static tree
if (avr_log.progmem)
avr_edump ("%?: type = %t\nexpr = %t\n\n", type, expr);
- if (as_new != ADDR_SPACE_MEMX
- && as_new != as_old)
+ if (! avr_addr_space_contains (as_new, as_old))
{
location_t loc = EXPR_LOCATION (expr);
const char *name_old = avr_addrspace[as_old].name;
rtx a_src = XEXP (xop[1], 0);
rtx a_dest = XEXP (xop[0], 0);
- if (PSImode == GET_MODE (a_src))
+ if (as == ADDR_SPACE_FLASHX
+ && ! AVR_HAVE_ELPM)
+ {
+ a_src = copy_to_mode_reg (Pmode, avr_word (a_src, 0));
+ as = ADDR_SPACE_FLASH;
+ }
+
+ machine_mode addr_mode = GET_MODE (a_src);
+
+ if (addr_mode == PSImode)
{
- gcc_assert (as == ADDR_SPACE_MEMX);
+ gcc_assert (as == ADDR_SPACE_MEMX || as == ADDR_SPACE_FLASHX);
loop_mode = (count < 0x100) ? QImode : HImode;
loop_reg = gen_rtx_REG (loop_mode, 24);
gcc_assert (TMP_REGNO == LPM_REGNO);
- if (as != ADDR_SPACE_MEMX)
+ if (addr_mode == HImode)
{
/* Load instruction ([E]LPM or LD) is known at compile time:
Do the copy-loop inline. */
rtx (*fun) (rtx, rtx)
= QImode == loop_mode ? gen_cpymemx_qi : gen_cpymemx_hi;
- emit_move_insn (gen_rtx_REG (QImode, 23), a_hi8);
+ emit_move_insn (gen_rtx_REG (QImode, REG_23), a_hi8);
insn = fun (xas, GEN_INT (avr_addr.rampz));
}
ADDR_SPACE_FLASH3,
ADDR_SPACE_FLASH4,
ADDR_SPACE_FLASH5,
+ ADDR_SPACE_FLASHX,
ADDR_SPACE_MEMX,
/* Sentinel */
ADDR_SPACE_COUNT
tsthi, tstpsi, tstsi, compare, compare64, call,
mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32,
ufract, sfract, round,
- xload, cpymem,
+ xload, fload, cpymem,
ashlqi, ashrqi, lshrqi,
ashlhi, ashrhi, lshrhi,
ashlsi, ashrsi, lshrsi,
;;========================================================================
;; Move stuff around
-;; "loadqi_libgcc"
-;; "loadhi_libgcc"
-;; "loadpsi_libgcc"
-;; "loadsi_libgcc"
-;; "loadsf_libgcc"
-(define_expand "load<mode>_libgcc"
+;; Expand helper for mov<mode>.
+(define_expand "gen_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" "")
+ (match_operand:MOVMODE 1 "memory_operand"))
+ (set (match_operand:MOVMODE 0 "register_operand")
(reg:MOVMODE 22))]
"avr_load_libgcc_p (operands[1])"
{
[(set_attr "type" "xcall")])
-;; "xload8qi_A"
-;; "xload8qq_A" "xload8uqq_A"
-(define_insn_and_split "xload8<mode>_A"
- [(set (match_operand:ALL1 0 "register_operand" "=r")
- (match_operand:ALL1 1 "memory_operand" "m"))
+;; Inline load a __memx value when flash <= 64 KiB, or
+;; inline load a __flashx value.
+(define_insn_and_split "fxmov<mode>_A"
+ [(set (match_operand:MOVMODE 0 "register_operand" "=r")
+ (match_operand:MOVMODE 1 "memory_operand" "m"))
(clobber (reg:HI REG_Z))]
"can_create_pseudo_p()
- && !avr_xload_libgcc_p (<MODE>mode)
- && avr_mem_memx_p (operands[1])
- && REG_P (XEXP (operands[1], 0))"
+ && REG_P (XEXP (operands[1], 0))
+ && (avr_load_libgcc_insn_p (insn, ADDR_SPACE_MEMX, false)
+ || avr_load_libgcc_insn_p (insn, ADDR_SPACE_FLASHX, false))"
{ gcc_unreachable(); }
"&& 1"
- [(clobber (const_int 0))]
+ [(scratch)]
{
// Split away the high part of the address. GCC's register allocator
// is not able to allocate segment registers and reload the resulting
// expressions. Notice that no address register can hold a PSImode.
+ addr_space_t as = MEM_ADDR_SPACE (operands[1]);
rtx addr = XEXP (operands[1], 0);
rtx hi8 = gen_reg_rtx (QImode);
rtx reg_z = gen_rtx_REG (HImode, REG_Z);
emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0));
emit_move_insn (hi8, simplify_gen_subreg (QImode, addr, PSImode, 2));
- rtx_insn *insn = emit_insn (gen_xload<mode>_8 (operands[0], hi8));
- set_mem_addr_space (SET_SRC (single_set (insn)),
- MEM_ADDR_SPACE (operands[1]));
+ rtx_insn *insn;
+ if (as == ADDR_SPACE_MEMX)
+ insn = emit_insn (gen_xmov<mode>_8 (operands[0], hi8));
+ else if (as == ADDR_SPACE_FLASHX)
+ insn = emit_insn (gen_fmov<mode> (operands[0], hi8));
+ else
+ gcc_unreachable ();
+
+ set_mem_addr_space (SET_SRC (single_set (insn)), as);
DONE;
})
-;; "xloadqi_A" "xloadqq_A" "xloaduqq_A"
-;; "xloadhi_A" "xloadhq_A" "xloaduhq_A" "xloadha_A" "xloaduha_A"
-;; "xloadsi_A" "xloadsq_A" "xloadusq_A" "xloadsa_A" "xloadusa_A"
-;; "xloadpsi_A"
-;; "xloadsf_A"
-(define_insn_and_split "xload<mode>_A"
+;; Move value from address space memx or flashx to a register
+;; These insns must be prior to respective generic move insn.
+
+;; "xmovqi_8"
+;; "xmovqq_8" "xmovuqq_8"
+(define_insn "xmov<mode>_8"
+ [(set (match_operand:MOVMODE 0 "register_operand" "=&r,r")
+ (mem:MOVMODE (lo_sum:PSI (match_operand:QI 1 "register_operand" "r,r")
+ (reg:HI REG_Z))))]
+ "<SIZE> == 1
+ && avr_load_libgcc_insn_p (insn, ADDR_SPACE_MEMX, false)"
+ {
+ return avr_out_xload (insn, operands, NULL);
+ }
+ [(set_attr "length" "4,4")
+ (set_attr "adjust_len" "*,xload")
+ (set_attr "isa" "lpmx,lpm")])
+
+;; Load a value from __flashx inline.
+(define_insn "fmov<mode>"
+ [(set (match_operand:MOVMODE 0 "register_operand" "=r")
+ (mem:MOVMODE (lo_sum:PSI (match_operand:QI 1 "register_operand" "r")
+ (reg:HI REG_Z))))
+ (clobber (reg:HI REG_Z))]
+ "avr_load_libgcc_insn_p (insn, ADDR_SPACE_FLASHX, false)"
+ {
+ return avr_out_fload (insn, operands, NULL);
+ }
+ [(set_attr "adjust_len" "fload")])
+
+
+;; Load a __memx or __flashx value per libgcc call.
+;; "fxloadqi_A" "fxloadqq_A" "fxloaduqq_A"
+;; "fxloadhi_A" "fxloadhq_A" "fxloaduhq_A" "fxloadha_A" "fxloaduha_A"
+;; "fxloadsi_A" "fxloadsq_A" "fxloadusq_A" "fxloadsa_A" "fxloadusa_A"
+;; "fxloadpsi_A"
+;; "fxloadsf_A"
+(define_insn_and_split "fxload<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:MOVMODE REG_22))
+ (clobber (reg:QI REG_21))
(clobber (reg:HI REG_Z))]
"can_create_pseudo_p()
- && avr_mem_memx_p (operands[1])
- && REG_P (XEXP (operands[1], 0))"
+ && REG_P (XEXP (operands[1], 0))
+ && (avr_load_libgcc_insn_p (insn, ADDR_SPACE_MEMX, true)
+ || avr_load_libgcc_insn_p (insn, ADDR_SPACE_FLASHX, true))"
{ gcc_unreachable(); }
"&& 1"
- [(clobber (const_int 0))]
+ [(scratch)]
{
rtx addr = XEXP (operands[1], 0);
rtx reg_z = gen_rtx_REG (HImode, REG_Z);
// Split the address to R21:Z
emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0));
- emit_move_insn (gen_rtx_REG (QImode, 21), addr_hi8);
+ emit_move_insn (gen_rtx_REG (QImode, REG_21), addr_hi8);
// Load with code from libgcc.
- rtx_insn *insn = emit_insn (gen_xload_<mode>_libgcc ());
+ rtx_insn *insn = emit_insn (gen_fxload_<mode>_libgcc ());
set_mem_addr_space (SET_SRC (single_set (insn)), as);
// Move to destination.
- emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, 22));
+ emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, REG_22));
DONE;
})
-;; Move value from address space memx to a register
-;; These insns must be prior to respective generic move insn.
-
-;; "xloadqi_8"
-;; "xloadqq_8" "xloaduqq_8"
-(define_insn "xload<mode>_8"
- [(set (match_operand:ALL1 0 "register_operand" "=&r,r")
- (mem:ALL1 (lo_sum:PSI (match_operand:QI 1 "register_operand" "r,r")
- (reg:HI REG_Z))))]
- "!avr_xload_libgcc_p (<MODE>mode)"
- {
- return avr_out_xload (insn, operands, NULL);
- }
- [(set_attr "length" "4,4")
- (set_attr "adjust_len" "*,xload")
- (set_attr "isa" "lpmx,lpm")])
-
;; R21:Z : 24-bit source address
;; R22 : 1-4 byte output
-;; "xload_qi_libgcc" "xload_qq_libgcc" "xload_uqq_libgcc"
-;; "xload_hi_libgcc" "xload_hq_libgcc" "xload_uhq_libgcc" "xload_ha_libgcc" "xload_uha_libgcc"
-;; "xload_si_libgcc" "xload_sq_libgcc" "xload_usq_libgcc" "xload_sa_libgcc" "xload_usa_libgcc"
-;; "xload_sf_libgcc"
-;; "xload_psi_libgcc"
-(define_insn_and_split "xload_<mode>_libgcc"
- [(set (reg:MOVMODE 22)
- (mem:MOVMODE (lo_sum:PSI (reg:QI 21)
+;; "fxload_qi_libgcc" "fxload_qq_libgcc" "fxload_uqq_libgcc"
+;; "fxload_hi_libgcc" "fxload_hq_libgcc" "fxload_uhq_libgcc" "fxload_ha_libgcc" "xload_uha_libgcc"
+;; "fxload_si_libgcc" "fxload_sq_libgcc" "fxload_usq_libgcc" "fxload_sa_libgcc" "xload_usa_libgcc"
+;; "fxload_sf_libgcc"
+;; "fxload_psi_libgcc"
+(define_insn_and_split "fxload_<mode>_libgcc"
+ [(set (reg:MOVMODE REG_22)
+ (mem:MOVMODE (lo_sum:PSI (reg:QI REG_21)
(reg:HI REG_Z))))
- (clobber (reg:QI 21))
+ (clobber (reg:QI REG_21))
(clobber (reg:HI REG_Z))]
- "avr_xload_libgcc_p (<MODE>mode)"
+ "avr_load_libgcc_insn_p (insn, ADDR_SPACE_MEMX, true)
+ || avr_load_libgcc_insn_p (insn, ADDR_SPACE_FLASHX, true)"
"#"
"&& reload_completed"
- [(parallel [(set (reg:MOVMODE 22)
- (mem:MOVMODE (lo_sum:PSI (reg:QI 21)
- (reg:HI REG_Z))))
- (clobber (reg:CC REG_CC))])])
+ [(parallel [(set (reg:MOVMODE REG_22)
+ (match_dup 0))
+ (clobber (reg:CC REG_CC))])]
+ {
+ operands[0] = SET_SRC (single_set (curr_insn));
+ })
-(define_insn "*xload_<mode>_libgcc"
- [(set (reg:MOVMODE 22)
- (mem:MOVMODE (lo_sum:PSI (reg:QI 21)
+(define_insn "*fxload_<mode>_libgcc"
+ [(set (reg:MOVMODE REG_22)
+ (mem:MOVMODE (lo_sum:PSI (reg:QI REG_21)
(reg:HI REG_Z))))
(clobber (reg:CC REG_CC))]
- "avr_xload_libgcc_p (<MODE>mode)
- && reload_completed"
- "%~call __xload_<SIZE>"
+ "reload_completed
+ && (avr_load_libgcc_insn_p (insn, ADDR_SPACE_MEMX, true)
+ || avr_load_libgcc_insn_p (insn, ADDR_SPACE_FLASHX, true))"
+ {
+ rtx src = SET_SRC (single_set (insn));
+ return avr_mem_memx_p (src)
+ ? "%~call __xload_<SIZE>"
+ : "%~call __fload_<SIZE>";
+ }
[(set_attr "type" "xcall")])
;; "movsf"
;; "movpsi"
(define_expand "mov<mode>"
- [(set (match_operand:MOVMODE 0 "nonimmediate_operand" "")
- (match_operand:MOVMODE 1 "general_operand" ""))]
+ [(set (match_operand:MOVMODE 0 "nonimmediate_operand")
+ (match_operand:MOVMODE 1 "general_operand"))]
""
{
rtx dest = operands[0];
operands[1] = src = copy_to_mode_reg (<MODE>mode, src);
}
- if (avr_mem_memx_p (src))
+ // Let __flashx decay to __flash on devices <= 64 KiB.
+ if (avr_mem_flashx_p (src)
+ && ! AVR_HAVE_ELPM)
+ {
+ rtx addr = XEXP (src, 0);
+ addr = copy_to_mode_reg (Pmode, avr_word (addr, 0));
+ // replace_equiv_address() hickupps, so do it by hand.
+ operands[1] = src = gen_rtx_MEM (<MODE>mode, addr);
+ set_mem_addr_space (src, ADDR_SPACE_FLASH);
+ }
+
+ if (avr_mem_memx_p (src)
+ || avr_mem_flashx_p (src))
{
rtx addr = XEXP (src, 0);
? gen_reg_rtx (<MODE>mode)
: dest;
- if (!avr_xload_libgcc_p (<MODE>mode))
- // No <mode> here because gen_xload8<mode>_A only iterates over ALL1.
- // insn-emit does not depend on the mode, it's all about operands.
- emit_insn (gen_xload8qi_A (dest2, src));
+ if (avr_load_libgcc_mem_p (src, ADDR_SPACE_MEMX, false)
+ || avr_load_libgcc_mem_p (src, ADDR_SPACE_FLASHX, false))
+ {
+ emit_insn (gen_fxmov<mode>_A (dest2, src));
+ }
else
{
rtx reg_22 = gen_rtx_REG (<MODE>mode, REG_22);
|| reg_overlap_mentioned_p (dest2, all_regs_rtx[REG_21]))
dest2 = gen_reg_rtx (<MODE>mode);
- emit_insn (gen_xload<mode>_A (dest2, src));
+ emit_insn (gen_fxload<mode>_A (dest2, src));
}
if (dest2 != dest)
if (avr_load_libgcc_p (src))
{
// For the small devices, do loads per libgcc call.
- emit_insn (gen_load<mode>_libgcc (dest, src));
+ emit_insn (gen_gen_load<mode>_libgcc (dest, src));
DONE;
}
})
[(set_attr "adjust_len" "cpymem")])
-;; $0 : Address Space
+;; $0 : 24-bit address space
;; $1 : RAMPZ RAM address
;; R24 : #bytes and loop register
;; R23:Z : 24-bit source address
(define_insn_and_split "cpymemx_<mode>"
[(set (mem:BLK (reg:HI REG_X))
- (mem:BLK (lo_sum:PSI (reg:QI 23)
+ ;; Spell out the address. IRA may try to spill
+ ;; a hard reg when operands were used.
+ (mem:BLK (lo_sum:PSI (reg:QI REG_23)
(reg:HI REG_Z))))
(unspec [(match_operand:QI 0 "const_int_operand" "n")]
UNSPEC_CPYMEM)
"#"
"&& reload_completed"
[(parallel [(set (mem:BLK (reg:HI REG_X))
- (mem:BLK (lo_sum:PSI (reg:QI 23)
- (reg:HI REG_Z))))
+ (match_dup 2))
(unspec [(match_dup 0)]
UNSPEC_CPYMEM)
(use (reg:QIHI 24))
(clobber (reg:HI 24))
(clobber (reg:QI 23))
(clobber (mem:QI (match_dup 1)))
- (clobber (reg:CC REG_CC))])])
+ (clobber (reg:CC REG_CC))])]
+ {
+ rtx xset = XVECEXP (PATTERN (curr_insn), 0, 0);
+ operands[2] = SET_SRC (xset);
+ })
(define_insn "*cpymemx_<mode>"
[(set (mem:BLK (reg:HI REG_X))
- (mem:BLK (lo_sum:PSI (reg:QI 23)
+ (mem:BLK (lo_sum:PSI (reg:QI REG_23)
(reg:HI REG_Z))))
(unspec [(match_operand:QI 0 "const_int_operand" "n")]
UNSPEC_CPYMEM)
(clobber (mem:QI (match_operand:QI 1 "io_address_operand" "n")))
(clobber (reg:CC REG_CC))]
"reload_completed"
- "%~call __movmemx_<mode>"
+ {
+ addr_space_t as = (addr_space_t) INTVAL (operands[0]);
+ return as == ADDR_SPACE_MEMX
+ ? "%~call __movmemx_<mode>"
+ : "%~call __movmemf_<mode>";
+ }
[(set_attr "type" "xcall")])
[(set_attr "isa" "*,*,*,3op,*")
(set_attr "adjust_len" "ashlpsi")])
+;; Seen in PSI loads from __flashx tables.
+(define_insn_and_split "*ashlqi.1.zextpsi_split"
+ [(set (match_operand:PSI 0 "register_operand" "=r")
+ (zero_extend:PSI
+ (ashift:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "0"))
+ (const_int 1))))]
+ ""
+ "#"
+ "&& reload_completed"
+ [(parallel [(set (match_dup 2)
+ (const_int 0))
+ (clobber (reg:CC REG_CC))])
+ (parallel [(set (match_dup 3)
+ (const_int 0))
+ (clobber (reg:CC REG_CC))])
+ (parallel [(set (match_dup 4)
+ (ashift:HI (match_dup 4)
+ (const_int 1)))
+ (clobber (reg:CC REG_CC))])]
+ {
+ operands[2] = avr_byte (operands[0], 2);
+ operands[3] = avr_byte (operands[0], 1);
+ operands[4] = avr_word (operands[0], 0);
+ })
+
;; >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >>
;; arithmetic shift right
(define_predicate "nox_general_operand"
(and (match_operand 0 "general_operand")
(not (match_test "avr_load_libgcc_p (op)"))
+ (not (match_test "avr_mem_flashx_p (op)"))
(not (match_test "avr_mem_memx_p (op)"))))
;; Return 1 if OP is the zero constant for MODE.
The compiler sets the @code{RAMPZ} segment register appropriately
before reading data by means of the @code{ELPM} instruction.
+@cindex @code{__flashx} AVR Named Address Spaces
+@item __flashx
+
+This is a 24-bit flash address space locating data in section
+@code{.progmemx.data}.
+The compiler sets the @code{RAMPZ} segment register appropriately
+before reading data by means of the @code{ELPM} instruction.
+
@cindex @code{__memx} AVR Named Address Spaces
@item __memx
This is a 24-bit address space that linearizes flash and RAM:
@item
Reading across the 64@tie{}KiB section boundary of
the @code{__flash} or @code{__flash@var{N}} address spaces
-shows undefined behavior. The only address space that
-supports reading across the 64@tie{}KiB flash segment boundaries is
-@code{__memx}.
+is not supported. The only address spaces that
+support reading across the 64@tie{}KiB flash segment boundaries are
+@code{__memx} and @code{__flashx}.
@item
If you use one of the @code{__flash@var{N}} address spaces
--- /dev/null
+/* { dg-options "-std=gnu99" } */
+/* { dg-do run { target { ! avr_tiny } } } */
+
+#define __as __flashx
+
+#include "addr-space-1.h"
a_t A2;
volatile a_t V2;
+#ifdef __AVR_HAVE_ELPM__
+void eat_flash (void)
+{
+ __asm (".space 0x10000");
+}
+__attribute__((__used__))
+void (*pfun) (void);
+#endif
+
int main (void)
{
+#ifdef __AVR_HAVE_ELPM__
+ pfun = eat_flash;
+#endif
+
if (A.i1 != 12
|| A.i1 != V.i1 -1)
abort();
--- /dev/null
+/* { dg-options "-std=gnu99 -Wa,--no-warn" } */
+/* { dg-do run { target { ! avr_tiny } } } */
+
+/* --no-warn because: "assembling 24-bit address needs binutils extension"
+ see binutils PR13503. */
+
+#define __as __flashx
+
+#include "addr-space-2.h"
abort();
}
+#ifdef __AVR_HAVE_ELPM__
+void eat_flash (void)
+{
+ __asm (".space 0x10000");
+}
+__attribute__((__used__))
+void (*pfun) (void);
+#endif
+
int main (void)
{
+#ifdef __AVR_HAVE_ELPM__
+ pfun = eat_flash;
+#endif
+
const __as tree *t = &abcd;
test1();
test2 (&abcd);
#define XIJMP ijmp
#endif
+;;; [R]JMP to label \labl when \reg is positive (\reg.7 = 0).
+;;; Otherwise, fallthrough.
+.macro .branch_plus reg, labl
+#ifdef __AVR_ERRATA_SKIP_JMP_CALL__
+ ;; Some cores have a problem skipping 2-word instructions
+ tst \reg
+ brmi .L..\@
+#else
+ sbrs \reg, 7
+#endif /* skip erratum */
+ XJMP \labl
+.L..\@:
+.endm ; .branch_plus
+
;; Prologue stuff
.macro do_prologue_saves n_pushed n_frame=0
#define HHI8 21
-.macro .xload dest, n
-#if defined (__AVR_HAVE_ELPMX__)
- elpm \dest, Z+
-#elif defined (__AVR_HAVE_ELPM__)
- elpm
- mov \dest, r0
-.if \dest != D0+\n-1
- adiw r30, 1
- adc HHI8, __zero_reg__
- out __RAMPZ__, HHI8
-.endif
-#elif defined (__AVR_HAVE_LPMX__)
- lpm \dest, Z+
-#else
- lpm
- mov \dest, r0
-.if \dest != D0+\n-1
- 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_ELPM__)
lpm D0, Z
ret
#else
- sbrc HHI8, 7
- rjmp 1f
-#if defined (__AVR_HAVE_ELPM__)
- out __RAMPZ__, HHI8
-#endif /* __AVR_HAVE_ELPM__ */
- .xload D0, 1
- ret
-1: ld D0, Z
+ .branch_plus HHI8, __fload_1
+ ld D0, Z
ret
#endif /* LPMx && ! ELPM */
ENDF __xload_1
#if defined (L_xload_2)
DEFUN __xload_2
- sbrc HHI8, 7
- rjmp 1f
-#if defined (__AVR_HAVE_ELPM__)
- out __RAMPZ__, HHI8
-#endif /* __AVR_HAVE_ELPM__ */
- .xload D0, 2
- .xload D1, 2
- ret
-1: ld D0, Z+
+ .branch_plus HHI8, __fload_2
+ ld D0, Z+
ld D1, Z+
ret
ENDF __xload_2
#if defined (L_xload_3)
DEFUN __xload_3
- sbrc HHI8, 7
- rjmp 1f
-#if defined (__AVR_HAVE_ELPM__)
- out __RAMPZ__, HHI8
-#endif /* __AVR_HAVE_ELPM__ */
- .xload D0, 3
- .xload D1, 3
- .xload D2, 3
- ret
-1: ld D0, Z+
+ .branch_plus HHI8, __fload_3
+ ld D0, Z+
ld D1, Z+
ld D2, Z+
ret
#if defined (L_xload_4)
DEFUN __xload_4
- sbrc HHI8, 7
- rjmp 1f
-#if defined (__AVR_HAVE_ELPM__)
- out __RAMPZ__, HHI8
-#endif /* __AVR_HAVE_ELPM__ */
- .xload D0, 4
- .xload D1, 4
- .xload D2, 4
- .xload D3, 4
- ret
-1: ld D0, Z+
+ .branch_plus HHI8, __fload_4
+ ld D0, Z+
ld D1, Z+
ld D2, Z+
ld D3, Z+
#if !defined (__AVR_TINY__)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; memcopy from Address Space __pgmx to RAM
+;; Loading n bytes from Flash; n = 1,2,3,4
+;; R22... = Flash[R21:Z]
+;; Clobbers: __tmp_reg__, R21, R30, R31
+
+#if (defined (L_fload_1) \
+ || defined (L_fload_2) \
+ || defined (L_fload_3) \
+ || defined (L_fload_4))
+
+;; Destination
+#define D0 22
+#define D1 D0+1
+#define D2 D0+2
+#define D3 D0+3
+
+;; Register containing bits 16+ of the address
+
+#define HHI8 21
+
+.macro .fload dest, n
+#if defined (__AVR_HAVE_ELPM__)
+.if \dest == D0
+ out __RAMPZ__, HHI8
+.endif
+#endif /* __AVR_HAVE_ELPM__ */
+#if defined (__AVR_HAVE_ELPMX__)
+ elpm \dest, Z+
+#elif defined (__AVR_HAVE_ELPM__)
+ elpm
+ mov \dest, r0
+.if \dest != D0+\n-1
+ adiw r30, 1
+ adc HHI8, __zero_reg__
+ out __RAMPZ__, HHI8
+.endif
+#elif defined (__AVR_HAVE_LPMX__)
+ lpm \dest, Z+
+#else
+ lpm
+ mov \dest, r0
+.if \dest != D0+\n-1
+ 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 ; .fload
+
+#if defined (L_fload_1)
+DEFUN __fload_1
+#if defined (__AVR_HAVE_LPMX__) && !defined (__AVR_HAVE_ELPM__)
+ lpm D0, Z
+ ret
+#else
+ .fload D0, 1
+ ret
+#endif /* LPMx && ! ELPM */
+ENDF __fload_1
+#endif /* L_fload_1 */
+
+#if defined (L_fload_2)
+DEFUN __fload_2
+ .fload D0, 2
+ .fload D1, 2
+ ret
+ENDF __fload_2
+#endif /* L_fload_2 */
+
+#if defined (L_fload_3)
+DEFUN __fload_3
+ .fload D0, 3
+ .fload D1, 3
+ .fload D2, 3
+ ret
+ENDF __fload_3
+#endif /* L_fload_3 */
+
+#if defined (L_fload_4)
+DEFUN __fload_4
+ .fload D0, 4
+ .fload D1, 4
+ .fload D2, 4
+ .fload D3, 4
+ ret
+ENDF __fload_4
+#endif /* L_fload_4 */
+
+#endif /* L_fload_{1|2|3|4} */
+#endif /* if !defined (__AVR_TINY__) */
+
+#if !defined (__AVR_TINY__)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; memcopy from Address Space __memx to RAM
;; R23:Z = Source Address
;; X = Destination Address
;; Clobbers: __tmp_reg__, R23, R24, R25, X, Z
#define LOOP 24
DEFUN __movmemx_qi
- ;; #Bytes to copy fity in 8 Bits (1..255)
+ ;; #Bytes to copy fits in 8 Bits (1..255)
;; Zero-extend Loop Counter
clr LOOP+1
;; FALLTHRU
DEFUN __movmemx_hi
-;; Read from where?
- sbrc HHI8, 7
- rjmp 1f
+ .branch_plus HHI8, __movmemf_hi
+
+ ;; Read 1 Byte from RAM...
+1: ld r0, Z+
+ ;; and store that Byte to RAM Destination
+ st X+, r0
+ sbiw LOOP, 1
+ brne 1b
+ ret
+ENDF __movmemx_hi
+
+#undef HHI8
+#undef LOOP
+
+#endif /* L_movmemx */
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; memcopy from Address Space __flashx to RAM
+;; R23:Z = Source Address
+;; X = Destination Address
+;; Clobbers: __tmp_reg__, R23, R24, R25, X, Z
+
+#if defined (L_movmemf)
+
+#define HHI8 23
+#define LOOP 24
+
+DEFUN __movmemf_qi
+ ;; #Bytes to copy fits in 8 Bits (1..255)
+ ;; Zero-extend Loop Counter
+ clr LOOP+1
+ ;; FALLTHRU
+ENDF __movmemf_qi
+
+DEFUN __movmemf_hi
;; Read from Flash
out __RAMPZ__, __zero_reg__
#endif /* ELPM && RAMPD */
ret
-
-;; Read from RAM
-
-1: ;; Read 1 Byte from RAM...
- ld r0, Z+
- ;; and store that Byte to RAM Destination
- st X+, r0
- sbiw LOOP, 1
- brne 1b
- ret
-ENDF __movmemx_hi
+ENDF __movmemf_hi
#undef HHI8
#undef LOOP
-#endif /* L_movmemx */
+#endif /* L_movmemf */
#endif /* !defined (__AVR_TINY__) */
\f
_fmul _fmuls _fmulsu
# The below functions either use registers that are not present
-# in tiny core, or use a different register conventions (don't save
+# in tiny core, or use a different register convention (don't save
# callee saved regs, for example)
# _mulhisi3 and variations - clobber R18, R19
# All *di funcs - use regs < R16 or expect args in regs < R20
# _prologue and _epilogue save registers < R16
-# _load ad _xload variations - expect lpm and elpm support
-# _movmemx - expects elpm/lpm
+# _load, __fload and _xload variations - expect lpm and elpm support
+# _movmemx and _movmemf - expect elpm/lpm
ifneq ($(MULTIFLAGS),-mmcu=avrtiny)
LIB1ASMFUNCS += \
_epilogue \
_load_3 _load_4 \
_xload_1 _xload_2 _xload_3 _xload_4 \
- _movmemx \
+ _fload_1 _fload_2 _fload_3 _fload_4 \
+ _movmemx _movmemf \
_clzdi2 \
_paritydi2 \
_popcountdi2 \