AVR: target/118001 - Add __flashx as 24-bit named address space.
This patch adds __flashx as a new named address space that allocates objects in .progmemx.data. The handling is mostly the same or similar to that of 24-bit space __memx, except that the asm routines are simpler and more efficient. Loads are emit inline when ELPMX or LPMX is available. The address space uses a 24-bit addresses even on devices with a program memory size of 64 KiB or less. PR target/118001 gcc/ * doc/extend.texi (AVR Named Address Spaces): Document __flashx. * config/avr/avr.h (ADDR_SPACE_FLASHX): New enum value. * config/avr/avr-protos.h (avr_out_fload, avr_mem_flashx_p) (avr_fload_libgcc_p, avr_load_libgcc_mem_p) (avr_load_libgcc_insn_p): New. * config/avr/avr.cc (avr_addrspace): Add ADDR_SPACE_FLASHX. (avr_decl_flashx_p, avr_mem_flashx_p, avr_fload_libgcc_p) (avr_load_libgcc_mem_p, avr_load_libgcc_insn_p, avr_out_fload): New functions. (avr_adjust_insn_length) [ADJUST_LEN_FLOAD]: Handle case. (avr_progmem_p) [avr_decl_flashx_p]: return 2. (avr_addr_space_legitimate_address_p) [ADDR_SPACE_FLASHX]: Has same behavior like ADDR_SPACE_MEMX. (avr_addr_space_convert): Use pointer sizes rather then ASes. (avr_addr_space_contains): New function. (avr_convert_to_type): Use it. (avr_emit_cpymemhi): Handle ADDR_SPACE_FLASHX. * config/avr/avr.md (adjust_len) <fload>: New attr value. (gen_load<mode>_libgcc): Renamed from load<mode>_libgcc. (xload8<mode>_A): Iterate over MOVMODE rather than over ALL1. (fxmov<mode>_A): New from xloadv<mode>_A. (xmov<mode>_8): New from xload<mode>_A. (fmov<mode>): New insns. (fxload<mode>_A): New from xload<mode>_A. (fxload_<mode>_libgcc): New from xload_<mode>_libgcc. (*fxload_<mode>_libgcc): New from *xload_<mode>_libgcc. (mov<mode>) [avr_mem_flashx_p]: Hande ADDR_SPACE_FLASHX. (cpymemx_<mode>): Make sure the address space is not lost when splitting. (*cpymemx_<mode>) [ADDR_SPACE_FLASHX]: Use __movmemf_<mode> for asm. (*ashlqi.1.zextpsi_split): New combine pattern. * config/avr/predicates.md (nox_general_operand): Don't match when avr_mem_flashx_p is true. * config/avr/avr-passes.cc (AVR_LdSt_Props): ADDR_SPACE_FLASHX has no post_inc. gcc/testsuite/ * gcc.target/avr/torture/addr-space-1.h [AVR_HAVE_ELPM]: Use a function to bump .progmemx.data to a high address. * gcc.target/avr/torture/addr-space-2.h: Same. * gcc.target/avr/torture/addr-space-1-fx.c: New test. * gcc.target/avr/torture/addr-space-2-fx.c: New test. libgcc/ * config/avr/t-avr (LIB1ASMFUNCS): Add _fload_1, _fload_2, _fload_3, _fload_4, _movmemf. * config/avr/lib1funcs.S (.branch_plus): New .macro. (__xload_1, __xload_2, __xload_3, __xload_4): When the address is located in flash, then forward to... (__fload_1, __fload_2, __fload_3, __fload_4): ...these new functions, respectively. (__movmemx_hi): When the address is located in flash, forward to... (__movmemf_hi): ...this new function.
This commit is contained in:
parent
d46c7f313b
commit
f8a602ce53
13 changed files with 575 additions and 204 deletions
|
@ -4359,7 +4359,8 @@ struct AVR_LdSt_Props
|
|||
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);
|
||||
|
|
|
@ -113,6 +113,7 @@ extern const char* avr_out_add_msb (rtx_insn*, rtx*, rtx_code, int*);
|
|||
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);
|
||||
|
@ -144,9 +145,13 @@ extern rtx avr_incoming_return_addr_rtx (void);
|
|||
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);
|
||||
|
|
|
@ -115,6 +115,7 @@ const avr_addrspace_t avr_addrspace[ADDR_SPACE_COUNT] =
|
|||
{ 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" },
|
||||
};
|
||||
|
||||
|
@ -646,7 +647,7 @@ avr_decl_flash_p (tree decl)
|
|||
|
||||
|
||||
/* 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)
|
||||
|
@ -661,6 +662,22 @@ 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
|
||||
|
@ -671,8 +688,8 @@ avr_mem_flash_p (rtx x)
|
|||
}
|
||||
|
||||
|
||||
/* 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)
|
||||
|
@ -682,6 +699,17 @@ 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. */
|
||||
|
||||
|
@ -3204,7 +3232,8 @@ avr_load_libgcc_p (rtx op)
|
|||
}
|
||||
|
||||
|
||||
/* 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)
|
||||
|
@ -3216,6 +3245,66 @@ 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. */
|
||||
|
@ -3677,7 +3766,9 @@ avr_out_lpm (rtx_insn *insn, rtx *op, int *plen)
|
|||
}
|
||||
|
||||
|
||||
/* 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)
|
||||
|
@ -3707,6 +3798,53 @@ 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.
|
||||
|
@ -10726,6 +10864,7 @@ avr_adjust_insn_length (rtx_insn *insn, int len)
|
|||
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;
|
||||
|
@ -11151,7 +11290,8 @@ avr_progmem_p (tree decl, tree attributes)
|
|||
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))
|
||||
|
@ -14147,6 +14287,7 @@ avr_addr_space_legitimate_address_p (machine_mode mode, rtx x, bool strict,
|
|||
break; /* FLASH */
|
||||
|
||||
case ADDR_SPACE_MEMX:
|
||||
case ADDR_SPACE_FLASHX:
|
||||
if (REG_P (x))
|
||||
ok = (!strict
|
||||
&& can_create_pseudo_p ());
|
||||
|
@ -14162,7 +14303,7 @@ avr_addr_space_legitimate_address_p (machine_mode mode, rtx x, bool strict,
|
|||
&& REGNO (lo) == REG_Z);
|
||||
}
|
||||
|
||||
break; /* MEMX */
|
||||
break; /* MEMX, FLASHX */
|
||||
}
|
||||
|
||||
if (avr_log.legitimate_address_p)
|
||||
|
@ -14210,19 +14351,20 @@ avr_addr_space_legitimize_address (rtx x, rtx old_x,
|
|||
/* 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);
|
||||
|
@ -14239,14 +14381,16 @@ avr_addr_space_convert (rtx src, tree type_from, tree type_to)
|
|||
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);
|
||||
|
||||
|
@ -14259,8 +14403,7 @@ avr_addr_space_convert (rtx src, tree type_from, tree type_to)
|
|||
|
||||
/* 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);
|
||||
|
||||
|
@ -14286,6 +14429,18 @@ avr_addr_space_subset_p (addr_space_t /*subset*/, addr_space_t /*superset*/)
|
|||
}
|
||||
|
||||
|
||||
/* 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
|
||||
|
@ -14326,8 +14481,7 @@ avr_convert_to_type (tree type, tree expr)
|
|||
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;
|
||||
|
@ -14511,9 +14665,18 @@ avr_emit_cpymemhi (rtx *xop)
|
|||
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)
|
||||
{
|
||||
gcc_assert (as == ADDR_SPACE_MEMX);
|
||||
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 || as == ADDR_SPACE_FLASHX);
|
||||
|
||||
loop_mode = (count < 0x100) ? QImode : HImode;
|
||||
loop_reg = gen_rtx_REG (loop_mode, 24);
|
||||
|
@ -14561,7 +14724,7 @@ avr_emit_cpymemhi (rtx *xop)
|
|||
|
||||
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. */
|
||||
|
@ -14576,7 +14739,7 @@ avr_emit_cpymemhi (rtx *xop)
|
|||
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));
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ enum
|
|||
ADDR_SPACE_FLASH3,
|
||||
ADDR_SPACE_FLASH4,
|
||||
ADDR_SPACE_FLASH5,
|
||||
ADDR_SPACE_FLASHX,
|
||||
ADDR_SPACE_MEMX,
|
||||
/* Sentinel */
|
||||
ADDR_SPACE_COUNT
|
||||
|
|
|
@ -164,7 +164,7 @@
|
|||
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,
|
||||
|
@ -532,17 +532,13 @@
|
|||
;;========================================================================
|
||||
;; 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])"
|
||||
{
|
||||
|
@ -581,24 +577,25 @@
|
|||
[(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);
|
||||
|
@ -606,29 +603,68 @@
|
|||
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);
|
||||
|
@ -637,65 +673,57 @@
|
|||
|
||||
// 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")])
|
||||
|
||||
|
||||
|
@ -707,8 +735,8 @@
|
|||
;; "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];
|
||||
|
@ -740,7 +768,19 @@
|
|||
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);
|
||||
|
||||
|
@ -751,10 +791,11 @@
|
|||
? 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);
|
||||
|
@ -762,7 +803,7 @@
|
|||
|| 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)
|
||||
|
@ -774,7 +815,7 @@
|
|||
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;
|
||||
}
|
||||
})
|
||||
|
@ -1297,7 +1338,7 @@
|
|||
[(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
|
||||
|
@ -1308,7 +1349,9 @@
|
|||
|
||||
(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)
|
||||
|
@ -1323,8 +1366,7 @@
|
|||
"#"
|
||||
"&& 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))
|
||||
|
@ -1334,11 +1376,15 @@
|
|||
(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)
|
||||
|
@ -1351,7 +1397,12 @@
|
|||
(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")])
|
||||
|
||||
|
||||
|
@ -5576,6 +5627,31 @@
|
|||
[(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
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
(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.
|
||||
|
|
|
@ -1525,6 +1525,14 @@ address space @code{__flash@var{N}}.
|
|||
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:
|
||||
|
@ -1604,9 +1612,9 @@ together with attribute @code{progmem}.
|
|||
@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
|
||||
|
|
6
gcc/testsuite/gcc.target/avr/torture/addr-space-1-fx.c
Normal file
6
gcc/testsuite/gcc.target/avr/torture/addr-space-1-fx.c
Normal file
|
@ -0,0 +1,6 @@
|
|||
/* { dg-options "-std=gnu99" } */
|
||||
/* { dg-do run { target { ! avr_tiny } } } */
|
||||
|
||||
#define __as __flashx
|
||||
|
||||
#include "addr-space-1.h"
|
|
@ -31,8 +31,21 @@ const __as volatile a_t V =
|
|||
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();
|
||||
|
|
9
gcc/testsuite/gcc.target/avr/torture/addr-space-2-fx.c
Normal file
9
gcc/testsuite/gcc.target/avr/torture/addr-space-2-fx.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* { 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"
|
|
@ -90,8 +90,21 @@ test3 (const __as tree *pt)
|
|||
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);
|
||||
|
|
|
@ -101,6 +101,20 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|||
#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
|
||||
|
@ -2596,7 +2610,82 @@ ENDF __load_4
|
|||
|
||||
#define HHI8 21
|
||||
|
||||
.macro .xload dest, n
|
||||
#if defined (L_xload_1)
|
||||
DEFUN __xload_1
|
||||
#if defined (__AVR_HAVE_LPMX__) && !defined (__AVR_HAVE_ELPM__)
|
||||
sbrc HHI8, 7
|
||||
ld D0, Z
|
||||
sbrs HHI8, 7
|
||||
lpm D0, Z
|
||||
ret
|
||||
#else
|
||||
.branch_plus HHI8, __fload_1
|
||||
ld D0, Z
|
||||
ret
|
||||
#endif /* LPMx && ! ELPM */
|
||||
ENDF __xload_1
|
||||
#endif /* L_xload_1 */
|
||||
|
||||
#if defined (L_xload_2)
|
||||
DEFUN __xload_2
|
||||
.branch_plus HHI8, __fload_2
|
||||
ld D0, Z+
|
||||
ld D1, Z+
|
||||
ret
|
||||
ENDF __xload_2
|
||||
#endif /* L_xload_2 */
|
||||
|
||||
#if defined (L_xload_3)
|
||||
DEFUN __xload_3
|
||||
.branch_plus HHI8, __fload_3
|
||||
ld D0, Z+
|
||||
ld D1, Z+
|
||||
ld D2, Z+
|
||||
ret
|
||||
ENDF __xload_3
|
||||
#endif /* L_xload_3 */
|
||||
|
||||
#if defined (L_xload_4)
|
||||
DEFUN __xload_4
|
||||
.branch_plus HHI8, __fload_4
|
||||
ld D0, Z+
|
||||
ld D1, Z+
|
||||
ld D2, Z+
|
||||
ld D3, Z+
|
||||
ret
|
||||
ENDF __xload_4
|
||||
#endif /* L_xload_4 */
|
||||
|
||||
#endif /* L_xload_{1|2|3|4} */
|
||||
#endif /* if !defined (__AVR_TINY__) */
|
||||
|
||||
#if !defined (__AVR_TINY__)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; 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__)
|
||||
|
@ -2622,90 +2711,53 @@ ENDF __load_4
|
|||
out __RAMPZ__, __zero_reg__
|
||||
.endif
|
||||
#endif
|
||||
.endm ; .xload
|
||||
.endm ; .fload
|
||||
|
||||
#if defined (L_xload_1)
|
||||
DEFUN __xload_1
|
||||
#if defined (L_fload_1)
|
||||
DEFUN __fload_1
|
||||
#if defined (__AVR_HAVE_LPMX__) && !defined (__AVR_HAVE_ELPM__)
|
||||
sbrc HHI8, 7
|
||||
ld D0, Z
|
||||
sbrs HHI8, 7
|
||||
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
|
||||
.fload D0, 1
|
||||
ret
|
||||
#endif /* LPMx && ! ELPM */
|
||||
ENDF __xload_1
|
||||
#endif /* L_xload_1 */
|
||||
ENDF __fload_1
|
||||
#endif /* L_fload_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
|
||||
#if defined (L_fload_2)
|
||||
DEFUN __fload_2
|
||||
.fload D0, 2
|
||||
.fload D1, 2
|
||||
ret
|
||||
1: ld D0, Z+
|
||||
ld D1, Z+
|
||||
ret
|
||||
ENDF __xload_2
|
||||
#endif /* L_xload_2 */
|
||||
ENDF __fload_2
|
||||
#endif /* L_fload_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
|
||||
#if defined (L_fload_3)
|
||||
DEFUN __fload_3
|
||||
.fload D0, 3
|
||||
.fload D1, 3
|
||||
.fload D2, 3
|
||||
ret
|
||||
1: ld D0, Z+
|
||||
ld D1, Z+
|
||||
ld D2, Z+
|
||||
ret
|
||||
ENDF __xload_3
|
||||
#endif /* L_xload_3 */
|
||||
ENDF __fload_3
|
||||
#endif /* L_fload_3 */
|
||||
|
||||
#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
|
||||
#if defined (L_fload_4)
|
||||
DEFUN __fload_4
|
||||
.fload D0, 4
|
||||
.fload D1, 4
|
||||
.fload D2, 4
|
||||
.fload D3, 4
|
||||
ret
|
||||
1: ld D0, Z+
|
||||
ld D1, Z+
|
||||
ld D2, Z+
|
||||
ld D3, Z+
|
||||
ret
|
||||
ENDF __xload_4
|
||||
#endif /* L_xload_4 */
|
||||
ENDF __fload_4
|
||||
#endif /* L_fload_4 */
|
||||
|
||||
#endif /* L_xload_{1|2|3|4} */
|
||||
#endif /* L_fload_{1|2|3|4} */
|
||||
#endif /* if !defined (__AVR_TINY__) */
|
||||
|
||||
#if !defined (__AVR_TINY__)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; memcopy from Address Space __pgmx to RAM
|
||||
;; memcopy from Address Space __memx to RAM
|
||||
;; R23:Z = Source Address
|
||||
;; X = Destination Address
|
||||
;; Clobbers: __tmp_reg__, R23, R24, R25, X, Z
|
||||
|
@ -2716,7 +2768,7 @@ ENDF __xload_4
|
|||
#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
|
||||
|
@ -2724,9 +2776,41 @@ ENDF __movmemx_qi
|
|||
|
||||
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
|
||||
|
||||
|
@ -2759,22 +2843,12 @@ DEFUN __movmemx_hi
|
|||
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__) */
|
||||
|
||||
|
||||
|
|
|
@ -36,13 +36,13 @@ LIB1ASMFUNCS = \
|
|||
_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 += \
|
||||
|
@ -61,7 +61,8 @@ 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 \
|
||||
|
|
Loading…
Add table
Reference in a new issue