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 output 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.
Passes without new regressions. Also passes without new regressions when used as AS in the proposed new target hook TARGET_ADDR_SPACE_FOR_ARTIFICIAL_RODATA, cf. https://gcc.gnu.org/pipermail/gcc-patches/2024-December/671216.html Ok for trunk? Johann -- AVR: 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 output 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. 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.
AVR: 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 output 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. 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. diff --git a/gcc/config/avr/avr-passes.cc b/gcc/config/avr/avr-passes.cc index fad64b1b345..8923bdbf124 100644 --- a/gcc/config/avr/avr-passes.cc +++ b/gcc/config/avr/avr-passes.cc @@ -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); diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index 0a441124387..b93f2b37a72 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -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); diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index 7dc3eb2016a..f03e7ffae8f 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -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" }, }; @@ -690,7 +691,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) @@ -705,6 +706,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 @@ -715,8 +732,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) @@ -726,6 +743,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. */ @@ -3248,7 +3276,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) @@ -3260,6 +3289,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. */ @@ -3721,7 +3810,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) @@ -3751,6 +3842,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. @@ -10770,6 +10908,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; @@ -11207,7 +11346,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)) @@ -14160,6 +14300,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 ()); @@ -14175,7 +14316,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) @@ -14223,19 +14364,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); @@ -14252,14 +14394,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); @@ -14272,8 +14416,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); @@ -14299,6 +14442,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 @@ -14339,8 +14494,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; @@ -14524,9 +14678,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) + { + 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); @@ -14574,7 +14737,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. */ @@ -14589,7 +14752,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)); } diff --git a/gcc/config/avr/avr.h b/gcc/config/avr/avr.h index b9c94bf6fc1..b0ae94cb301 100644 --- a/gcc/config/avr/avr.h +++ b/gcc/config/avr/avr.h @@ -53,6 +53,7 @@ enum ADDR_SPACE_FLASH3, ADDR_SPACE_FLASH4, ADDR_SPACE_FLASH5, + ADDR_SPACE_FLASHX, ADDR_SPACE_MEMX, /* Sentinel */ ADDR_SPACE_COUNT diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 9c348be7c48..02e9ec71ac8 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -164,7 +164,7 @@ (define_attr "adjust_len" 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,12 +532,8 @@ (define_split ;;======================================================================== ;; 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) @@ -581,24 +577,25 @@ (define_insn "*load_<mode>_libgcc" [(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 @@ (define_insn_and_split "xload8<mode>_A" 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 @@ (define_insn_and_split "xload<mode>_A" // 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")]) @@ -740,7 +768,19 @@ (define_expand "mov<mode>" 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 @@ (define_expand "mov<mode>" ? 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 @@ (define_expand "mov<mode>" || 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 @@ (define_expand "mov<mode>" 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 @@ (define_insn "*cpymem_<mode>" [(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 "*cpymem_<mode>" (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 @@ (define_insn_and_split "cpymemx_<mode>" "#" "&& 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 @@ (define_insn_and_split "cpymemx_<mode>" (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 @@ (define_insn "*cpymemx_<mode>" (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 @@ (define_insn "*ashlpsi3" [(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 diff --git a/gcc/config/avr/predicates.md b/gcc/config/avr/predicates.md index a28c63a6478..8253fb9f62c 100644 --- a/gcc/config/avr/predicates.md +++ b/gcc/config/avr/predicates.md @@ -89,6 +89,7 @@ (define_predicate "nop_general_operand" (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. diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 336361e0cfd..5297239a8ff 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -1525,6 +1525,12 @@ 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 address space locating data in section +@code{.progmemx.data}. + @cindex @code{__memx} AVR Named Address Spaces @item __memx This is a 24-bit address space that linearizes flash and RAM: @@ -1604,9 +1610,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}. +shows undefined behavior. The only address spaces that +supports 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 diff --git a/gcc/testsuite/gcc.target/avr/torture/addr-space-1-fx.c b/gcc/testsuite/gcc.target/avr/torture/addr-space-1-fx.c new file mode 100644 index 00000000000..f15fac0e894 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/addr-space-1-fx.c @@ -0,0 +1,6 @@ +/* { dg-options "-std=gnu99" } */ +/* { dg-do run { target { ! avr_tiny } } } */ + +#define __as __flashx + +#include "addr-space-1.h" diff --git a/gcc/testsuite/gcc.target/avr/torture/addr-space-1.h b/gcc/testsuite/gcc.target/avr/torture/addr-space-1.h index 322a5b8b3b6..8b1c12c2ddc 100644 --- a/gcc/testsuite/gcc.target/avr/torture/addr-space-1.h +++ b/gcc/testsuite/gcc.target/avr/torture/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(); diff --git a/gcc/testsuite/gcc.target/avr/torture/addr-space-2-fx.c b/gcc/testsuite/gcc.target/avr/torture/addr-space-2-fx.c new file mode 100644 index 00000000000..65220e9552f --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/addr-space-2-fx.c @@ -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" diff --git a/gcc/testsuite/gcc.target/avr/torture/addr-space-2.h b/gcc/testsuite/gcc.target/avr/torture/addr-space-2.h index c95a1631ab0..7146f631650 100644 --- a/gcc/testsuite/gcc.target/avr/torture/addr-space-2.h +++ b/gcc/testsuite/gcc.target/avr/torture/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); diff --git a/libgcc/config/avr/lib1funcs.S b/libgcc/config/avr/lib1funcs.S index d48b04747da..d5f4c721779 100644 --- a/libgcc/config/avr/lib1funcs.S +++ b/libgcc/config/avr/lib1funcs.S @@ -101,6 +101,19 @@ #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__ + 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,34 +2609,6 @@ DEFUN __load_4 #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__) @@ -2633,14 +2618,8 @@ DEFUN __xload_1 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 @@ -2648,15 +2627,8 @@ DEFUN __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 @@ -2664,16 +2636,8 @@ DEFUN __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 @@ -2682,17 +2646,8 @@ DEFUN __xload_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 - ret -1: ld D0, Z+ + .branch_plus HHI8 __fload_4 + ld D0, Z+ ld D1, Z+ ld D2, Z+ ld D3, Z+ @@ -2705,7 +2660,103 @@ DEFUN __xload_4 #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 @@ -2716,7 +2767,7 @@ DEFUN __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 +2775,41 @@ DEFUN __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 +2842,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__) */ diff --git a/libgcc/config/avr/t-avr b/libgcc/config/avr/t-avr index 971a092aceb..e9fdb98d776 100644 --- a/libgcc/config/avr/t-avr +++ b/libgcc/config/avr/t-avr @@ -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 \