diff options
author | Georg-Johann Lay <avr@gjlay.de> | 2011-11-18 16:44:00 +0000 |
---|---|---|
committer | Georg-Johann Lay <gjl@gcc.gnu.org> | 2011-11-18 16:44:00 +0000 |
commit | 7bc6df2ca1e069c9cd2b58d5b0560d2bd7ffe703 (patch) | |
tree | ef22897c6f94d5a785ffecb11a0779bdd6138771 /gcc | |
parent | 59659b596734466dd182fa99b0a189ca7c376129 (diff) | |
download | gcc-7bc6df2ca1e069c9cd2b58d5b0560d2bd7ffe703.zip gcc-7bc6df2ca1e069c9cd2b58d5b0560d2bd7ffe703.tar.gz gcc-7bc6df2ca1e069c9cd2b58d5b0560d2bd7ffe703.tar.bz2 |
re PR target/49868 (Implement named address space to place/access data in flash memory)
gcc/
PR target/49868
* config/avr/avr.h (base_arch_s): Add field n_segments.
(ADDR_SPACE_PGM1, ADDR_SPACE_PGM2, ADDR_SPACE_PGM3,
ADDR_SPACE_PGM4, ADDR_SPACE_PGM5, ADDR_SPACE_PGMX): New address spaces.
(AVR_HAVE_ELPM, AVR_HAVE_ELPMX): New defines.
(INIT_EXPANDERS): New define.
* config/avr/avr-protos.h (avr_mem_pgmx_p): New.
(avr_init_expanders): New.
(avr_emit_movmemhi, avr_out_movmem): New.
(avr_xload_libgcc_p): New.
* config/avr/avr-c.c (avr_register_target_pragmas): Register
address spaces __pgm1, __pgm2, __pgm3, __pgm4 __pgm5, __pgmx.
(avr_cpu_cpp_builtins): Add built-in defines __PGM1,
__PGM2, __PGM3, __PGM4, __PGM5, __PGMX.
* config/avr/avr-devices.c (avr_arch_types): Set field n_segments.
* config/avr/avr.c (AVR_SECTION_PROGMEM): Change define to cover
3 bits instead of just 1.
(xstring_empty, xstring_e, rampz_rtx): New static GTYed variables.
(progmem_section): Change from section to array of sections.
(progmem_section_prefix): New static variable.
(avr_file_start): Print set for __RAMPZ__
(avr_option_override): Move initialization of RTXes from here...
(avr_init_expanders): ...to this new function.
(avr_pgm_segment): New static function.
(avr_decl_pgm_p): Handle error_mark_node.
(avr_mem_pgmx_p, avr_decl_pgmx_p): New static functions.
(avr_out_xload, avr_find_unused_d_reg): New static functions.
(expand_prologue, expand_epilogue): Use rampz_rtx.
(print_operand): Hande CONST_STRING.
(avr_xload_libgcc_p): New static function.
(avr_out_lpm_no_lpmx, avr_out_lpm): Handle ELPM.
(avr_progmem_p): Return 2 for 24-bit flash address space.
(avr_out_sbxx_branch): Clean-up code from ASn macros.
(out_movqi_r_mr, out_movqi_mr_r): Ditto. And recognize RAMPZ's
address and print symbolically.
(avr_asm_named_section, avr_section_type_flags,
avr_encode_section_info, avr_asm_select_section,
avr_addr_space_address_mode, avr_addr_space_pointer_mode,
avr_addr_space_legitimate_address_p, avr_addr_space_convert,
avr_addr_space_legitimize_address): Handle new address spaces.
(avr_output_progmem_section_asm_op): New static function.
(avr_asm_init_sections): Initialize progmem_section[].
(adjust_insn_length): Handle ADJUST_LEN_XLOAD, ADJUST_LEN_MOVMEM.
(avr_const_address_lo16): New static function.
(avr_assemble_integer): Use it to handle 3-byte integers.
(avr_emit_movmemhi, avr_out_movmem): New functions.
* config/avr/predicates.md (nox_general_operand): Handle new
address spaces.
* config/avr/avr.md (unspec): Add UNSPEC_MOVMEM.
(adjust_len): Add xload, movmem.
(SP_ADDR): New define_constants.
(isa): Add "lpm", "lpmx", "elpm", "elpmx".
(enabled): Handle them.
(load<mode>_libgcc): New expander.
(*load.<mode>.libgcc): Rename to load_<mode>_libgcc.
(xload8_A, xload<mode>_A): New insn-and-splits.
(xload_8, xload_<mode>_libgcc, xload_<mode>, loadmem_elpm): New insns.
(mov<mode>): Handle new address spaces.
(movmemhi): Rewrite using avr_emit_movmemhi.
(MOVMEM_r_d): New mode attribute.
(movmem_<mode>, movmem_qi_elpm): New insns.
(setmemhi, *clrmemqi, *clrmemhi, strlenhi, *strlenhi): Unquote
C-code. Use label instead of hard-coded instrunction lengths.
libgcc/
PR target/49868
* config/avr/t-avr (LIB1ASMFUNCS): Add _xload_2 _xload_3 _xload_4.
* config/avr/lib1funcs.S (__xload_2, __xload_3, __xload_4):
New functions.
From-SVN: r181482
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 68 | ||||
-rw-r--r-- | gcc/config/avr/avr-c.c | 12 | ||||
-rw-r--r-- | gcc/config/avr/avr-devices.c | 33 | ||||
-rw-r--r-- | gcc/config/avr/avr-protos.h | 6 | ||||
-rw-r--r-- | gcc/config/avr/avr.c | 1195 | ||||
-rw-r--r-- | gcc/config/avr/avr.h | 13 | ||||
-rw-r--r-- | gcc/config/avr/avr.md | 433 | ||||
-rw-r--r-- | gcc/config/avr/predicates.md | 5 |
8 files changed, 1370 insertions, 395 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6fb03fa..57bba24 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,71 @@ +2011-11-18 Georg-Johann Lay <avr@gjlay.de> + + PR target/49868 + * config/avr/avr.h (base_arch_s): Add field n_segments. + (ADDR_SPACE_PGM1, ADDR_SPACE_PGM2, ADDR_SPACE_PGM3, + ADDR_SPACE_PGM4, ADDR_SPACE_PGM5, ADDR_SPACE_PGMX): New address spaces. + (AVR_HAVE_ELPM, AVR_HAVE_ELPMX): New defines. + (INIT_EXPANDERS): New define. + * config/avr/avr-protos.h (avr_mem_pgmx_p): New. + (avr_init_expanders): New. + (avr_emit_movmemhi, avr_out_movmem): New. + (avr_xload_libgcc_p): New. + * config/avr/avr-c.c (avr_register_target_pragmas): Register + address spaces __pgm1, __pgm2, __pgm3, __pgm4 __pgm5, __pgmx. + (avr_cpu_cpp_builtins): Add built-in defines __PGM1, + __PGM2, __PGM3, __PGM4, __PGM5, __PGMX. + * config/avr/avr-devices.c (avr_arch_types): Set field n_segments. + + * config/avr/avr.c (AVR_SECTION_PROGMEM): Change define to cover + 3 bits instead of just 1. + (xstring_empty, xstring_e, rampz_rtx): New static GTYed variables. + (progmem_section): Change from section to array of sections. + (progmem_section_prefix): New static variable. + (avr_file_start): Print set for __RAMPZ__ + (avr_option_override): Move initialization of RTXes from here... + (avr_init_expanders): ...to this new function. + (avr_pgm_segment): New static function. + (avr_decl_pgm_p): Handle error_mark_node. + (avr_mem_pgmx_p, avr_decl_pgmx_p): New static functions. + (avr_out_xload, avr_find_unused_d_reg): New static functions. + (expand_prologue, expand_epilogue): Use rampz_rtx. + (print_operand): Hande CONST_STRING. + (avr_xload_libgcc_p): New static function. + (avr_out_lpm_no_lpmx, avr_out_lpm): Handle ELPM. + (avr_progmem_p): Return 2 for 24-bit flash address space. + (avr_out_sbxx_branch): Clean-up code from ASn macros. + (out_movqi_r_mr, out_movqi_mr_r): Ditto. And recognize RAMPZ's + address and print symbolically. + (avr_asm_named_section, avr_section_type_flags, + avr_encode_section_info, avr_asm_select_section, + avr_addr_space_address_mode, avr_addr_space_pointer_mode, + avr_addr_space_legitimate_address_p, avr_addr_space_convert, + avr_addr_space_legitimize_address): Handle new address spaces. + (avr_output_progmem_section_asm_op): New static function. + (avr_asm_init_sections): Initialize progmem_section[]. + (adjust_insn_length): Handle ADJUST_LEN_XLOAD, ADJUST_LEN_MOVMEM. + (avr_const_address_lo16): New static function. + (avr_assemble_integer): Use it to handle 3-byte integers. + (avr_emit_movmemhi, avr_out_movmem): New functions. + + * config/avr/predicates.md (nox_general_operand): Handle new + address spaces. + * config/avr/avr.md (unspec): Add UNSPEC_MOVMEM. + (adjust_len): Add xload, movmem. + (SP_ADDR): New define_constants. + (isa): Add "lpm", "lpmx", "elpm", "elpmx". + (enabled): Handle them. + (load<mode>_libgcc): New expander. + (*load.<mode>.libgcc): Rename to load_<mode>_libgcc. + (xload8_A, xload<mode>_A): New insn-and-splits. + (xload_8, xload_<mode>_libgcc, xload_<mode>, loadmem_elpm): New insns. + (mov<mode>): Handle new address spaces. + (movmemhi): Rewrite using avr_emit_movmemhi. + (MOVMEM_r_d): New mode attribute. + (movmem_<mode>, movmem_qi_elpm): New insns. + (setmemhi, *clrmemqi, *clrmemhi, strlenhi, *strlenhi): Unquote + C-code. Use label instead of hard-coded instrunction lengths. + 2011-11-18 Martin Jambor <mjambor@suse.cz> PR tree-optimization/50605 diff --git a/gcc/config/avr/avr-c.c b/gcc/config/avr/avr-c.c index 63408c0..5fbc51c 100644 --- a/gcc/config/avr/avr-c.c +++ b/gcc/config/avr/avr-c.c @@ -37,6 +37,12 @@ void avr_register_target_pragmas (void) { c_register_addr_space ("__pgm", ADDR_SPACE_PGM); + c_register_addr_space ("__pgm1", ADDR_SPACE_PGM1); + c_register_addr_space ("__pgm2", ADDR_SPACE_PGM2); + c_register_addr_space ("__pgm3", ADDR_SPACE_PGM3); + c_register_addr_space ("__pgm4", ADDR_SPACE_PGM4); + c_register_addr_space ("__pgm5", ADDR_SPACE_PGM5); + c_register_addr_space ("__pgmx", ADDR_SPACE_PGMX); } @@ -109,6 +115,12 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile) if (!strcmp (lang_hooks.name, "GNU C")) { cpp_define (pfile, "__PGM=__pgm"); + cpp_define (pfile, "__PGM1=__pgm1"); + cpp_define (pfile, "__PGM2=__pgm2"); + cpp_define (pfile, "__PGM3=__pgm3"); + cpp_define (pfile, "__PGM4=__pgm4"); + cpp_define (pfile, "__PGM5=__pgm5"); + cpp_define (pfile, "__PGMX=__pgmx"); } /* Define builtin macros so that the user can diff --git a/gcc/config/avr/avr-devices.c b/gcc/config/avr/avr-devices.c index d0dda85..e0e47383 100644 --- a/gcc/config/avr/avr-devices.c +++ b/gcc/config/avr/avr-devices.c @@ -25,18 +25,27 @@ /* List of all known AVR MCU architectures. */ -const struct base_arch_s avr_arch_types[] = { - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, NULL, "avr2" }, /* unknown device specified */ - { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=1", "avr1" }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=2", "avr2" }, - { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=25", "avr25" }, - { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=3", "avr3" }, - { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=31", "avr31" }, - { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=35", "avr35" }, - { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=4", "avr4" }, - { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0x0060, "__AVR_ARCH__=5", "avr5" }, - { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, "__AVR_ARCH__=51", "avr51" }, - { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0x0060, "__AVR_ARCH__=6", "avr6" } +const struct base_arch_s +avr_arch_types[] = +{ + /* unknown device specified */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 1, NULL, "avr2" }, + /* + A M J L E E E d S # F + S U M P L L I a t 6 l + M L P M P P J - - t a 4 a + X M M M a r s + X P t k h */ + { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=1", "avr1" }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=2", "avr2" }, + { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=25", "avr25" }, + { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=3", "avr3" }, + { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0x0060, 2, "__AVR_ARCH__=31", "avr31" }, + { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=35", "avr35" }, + { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=4", "avr4" }, + { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0x0060, 1, "__AVR_ARCH__=5", "avr5" }, + { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, 2, "__AVR_ARCH__=51", "avr51" }, + { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0x0060, 4, "__AVR_ARCH__=6", "avr6" } }; const struct mcu_type_s avr_mcu_types[] = { diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index 6c017e1..c71bc9d 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -34,6 +34,7 @@ extern int avr_hard_regno_rename_ok (unsigned int, unsigned int); extern rtx avr_return_addr_rtx (int count, rtx tem); extern void avr_register_target_pragmas (void); extern bool avr_accumulate_outgoing_args (void); +extern void avr_init_expanders (void); #ifdef TREE_CODE extern void avr_asm_output_aligned_decl_common (FILE*, const_tree, const char*, unsigned HOST_WIDE_INT, unsigned int, bool); @@ -84,6 +85,7 @@ extern bool avr_rotate_bytes (rtx operands[]); extern void expand_prologue (void); extern void expand_epilogue (bool); +extern bool avr_emit_movmemhi (rtx*); extern int avr_epilogue_uses (int regno); extern int avr_starting_frame_offset (void); @@ -94,6 +96,8 @@ extern const char* avr_out_bitop (rtx, rtx*, int*); extern const char* avr_out_plus (rtx*, int*, int*); extern const char* avr_out_plus_noclobber (rtx*, int*, int*); extern const char* avr_out_addto_sp (rtx*, int*); +extern const char* avr_out_xload (rtx, rtx*, int*); +extern const char* avr_out_movmem (rtx, rtx*, int*); extern bool avr_popcount_each_byte (rtx, int, int); extern int extra_constraint_Q (rtx x); @@ -122,7 +126,9 @@ extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_spac extern rtx avr_incoming_return_addr_rtx (void); extern rtx avr_legitimize_reload_address (rtx*, enum machine_mode, int, int, int, int, rtx (*)(rtx,int)); extern bool avr_mem_pgm_p (rtx); +extern bool avr_mem_pgmx_p (rtx); extern bool avr_load_libgcc_p (rtx); +extern bool avr_xload_libgcc_p (enum machine_mode); #endif /* RTX_CODE */ #ifdef REAL_VALUE_TYPE diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index f33c0c5..e7b902d 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -55,7 +55,10 @@ /* Return true if STR starts with PREFIX and false, otherwise. */ #define STR_PREFIX_P(STR,PREFIX) (0 == strncmp (STR, PREFIX, strlen (PREFIX))) -#define AVR_SECTION_PROGMEM (SECTION_MACH_DEP << 0) +/* The 4 bits starting at SECTION_MACH_DEP are reverved to store + 1 + flash segment where progmem data is to be located. + For example, data with __pgm2 is stored as (1+2) * SECTION_MACH_DEP. */ +#define AVR_SECTION_PROGMEM (0xf * SECTION_MACH_DEP) /* Prototypes for local helper functions. */ @@ -97,6 +100,13 @@ static GTY(()) rtx tmp_reg_rtx; /* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */ static GTY(()) rtx zero_reg_rtx; +/* RAMPZ special function register */ +static GTY(()) rtx rampz_rtx; + +/* RTX containing the strings "" and "e", respectively */ +static GTY(()) rtx xstring_empty; +static GTY(()) rtx xstring_e; + /* RTXs for all general purpose registers as QImode */ static GTY(()) rtx all_regs_rtx[32]; @@ -116,7 +126,17 @@ const struct mcu_type_s *avr_current_device; static GTY(()) section *progmem_swtable_section; /* Unnamed section associated to __attribute__((progmem)) aka. PROGMEM. */ -static GTY(()) section *progmem_section; +static GTY(()) section *progmem_section[6]; + +static const char * const progmem_section_prefix[6] = + { + ".progmem.data", + ".progmem1.data", + ".progmem2.data", + ".progmem3.data", + ".progmem4.data", + ".progmem5.data" + }; /* To track if code will use .bss and/or .data. */ bool avr_need_clear_bss_p = false; @@ -317,8 +337,6 @@ avr_popcount_each_byte (rtx xval, int n_bytes, int pop_mask) static void avr_option_override (void) { - int regno; - flag_delete_null_pointer_checks = 0; /* caller-save.c looks for call-clobbered hard registers that are assigned @@ -347,15 +365,6 @@ avr_option_override (void) avr_current_arch = &avr_arch_types[avr_current_device->arch]; avr_extra_arch_macro = avr_current_device->macro; - for (regno = 0; regno < 32; regno ++) - all_regs_rtx[regno] = gen_rtx_REG (QImode, regno); - - lpm_reg_rtx = all_regs_rtx[LPM_REGNO]; - tmp_reg_rtx = all_regs_rtx[TMP_REGNO]; - zero_reg_rtx = all_regs_rtx[ZERO_REGNO]; - - lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z); - init_machine_status = avr_init_machine_status; avr_log_set_avr_log(); @@ -369,6 +378,38 @@ avr_init_machine_status (void) return ggc_alloc_cleared_machine_function (); } + +/* Implement `INIT_EXPANDERS'. */ +/* The function works like a singleton. */ + +void +avr_init_expanders (void) +{ + int regno; + + static bool done = false; + + if (done) + return; + else + done = true; + + for (regno = 0; regno < 32; regno ++) + all_regs_rtx[regno] = gen_rtx_REG (QImode, regno); + + lpm_reg_rtx = all_regs_rtx[LPM_REGNO]; + tmp_reg_rtx = all_regs_rtx[TMP_REGNO]; + zero_reg_rtx = all_regs_rtx[ZERO_REGNO]; + + lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z); + + rampz_rtx = gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)); + + xstring_empty = gen_rtx_CONST_STRING (VOIDmode, ""); + xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e"); +} + + /* Return register class for register R. */ enum reg_class @@ -414,18 +455,60 @@ avr_scalar_mode_supported_p (enum machine_mode mode) } +/* Return the segment number of pgm address space AS, i.e. + the 64k block it lives in. + Return -1 if unknown, i.e. 24-bit AS in flash. + Return -2 for anything else. */ + +static int +avr_pgm_segment (addr_space_t as) +{ + switch (as) + { + default: return -2; + + case ADDR_SPACE_PGMX: return -1; + case ADDR_SPACE_PGM: return 0; + case ADDR_SPACE_PGM1: return 1; + case ADDR_SPACE_PGM2: return 2; + case ADDR_SPACE_PGM3: return 3; + case ADDR_SPACE_PGM4: return 4; + case ADDR_SPACE_PGM5: return 5; + } +} + + /* Return TRUE if DECL is a VAR_DECL located in Flash and FALSE, otherwise. */ static bool avr_decl_pgm_p (tree decl) { - if (TREE_CODE (decl) != VAR_DECL) - return false; + if (TREE_CODE (decl) != VAR_DECL + || TREE_TYPE (decl) == error_mark_node) + { + return false; + } return !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (TREE_TYPE (decl))); } +/* Return TRUE if DECL is a VAR_DECL located in the 24-bit Flash + address space and FALSE, otherwise. */ + +static bool +avr_decl_pgmx_p (tree decl) +{ + if (TREE_CODE (decl) != VAR_DECL + || TREE_TYPE (decl) == error_mark_node) + { + return false; + } + + return (ADDR_SPACE_PGMX == TYPE_ADDR_SPACE (TREE_TYPE (decl))); +} + + /* Return TRUE if X is a MEM rtx located in Flash and FALSE, otherwise. */ bool @@ -436,6 +519,17 @@ avr_mem_pgm_p (rtx x) } +/* Return TRUE if X is a MEM rtx located in the 24-bit Flash + address space and FALSE, otherwise. */ + +bool +avr_mem_pgmx_p (rtx x) +{ + return (MEM_P (x) + && ADDR_SPACE_PGMX == MEM_ADDR_SPACE (x)); +} + + /* A helper for the subsequent function attribute used to dig for attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */ @@ -1041,8 +1135,7 @@ expand_prologue (void) && TEST_HARD_REG_BIT (set, REG_Z) && TEST_HARD_REG_BIT (set, REG_Z + 1)) { - emit_move_insn (tmp_reg_rtx, - gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR))); + emit_move_insn (tmp_reg_rtx, rampz_rtx); emit_push_byte (TMP_REGNO, false); } @@ -1280,8 +1373,7 @@ expand_epilogue (bool sibcall_p) && TEST_HARD_REG_BIT (set, REG_Z + 1)) { emit_pop_byte (TMP_REGNO); - emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)), - tmp_reg_rtx); + emit_move_insn (rampz_rtx, tmp_reg_rtx); } /* Restore SREG using tmp reg as scratch. */ @@ -1795,6 +1887,8 @@ print_operand (FILE *file, rtx x, int code) REAL_VALUE_TO_TARGET_SINGLE (rv, val); fprintf (file, "0x%lx", val); } + else if (GET_CODE (x) == CONST_STRING) + fputs (XSTR (x, 0), file); else if (code == 'j') fputs (cond_string (GET_CODE (x)), file); else if (code == 'k') @@ -2247,6 +2341,70 @@ avr_load_libgcc_p (rtx op) && avr_mem_pgm_p (op)); } +/* Return true if a value of mode MODE is read by __xload_* function. */ + +bool +avr_xload_libgcc_p (enum machine_mode mode) +{ + int n_bytes = GET_MODE_SIZE (mode); + + return (n_bytes > 1 + && avr_current_arch->n_segments > 1 + && !AVR_HAVE_ELPMX); +} + + +/* Find an unused d-register to be used as scratch in INSN. + EXCLUDE is either NULL_RTX or some register. In the case where EXCLUDE + is a register, skip all possible return values that overlap EXCLUDE. + The policy for the returned register is similar to that of + `reg_unused_after', i.e. the returned register may overlap the SET_DEST + of INSN. + + Return a QImode d-register or NULL_RTX if nothing found. */ + +static rtx +avr_find_unused_d_reg (rtx insn, rtx exclude) +{ + int regno; + bool isr_p = (interrupt_function_p (current_function_decl) + || signal_function_p (current_function_decl)); + + for (regno = 16; regno < 32; regno++) + { + rtx reg = all_regs_rtx[regno]; + + if ((exclude + && reg_overlap_mentioned_p (exclude, reg)) + || fixed_regs[regno]) + { + continue; + } + + /* Try non-live register */ + + if (!df_regs_ever_live_p (regno) + && (TREE_THIS_VOLATILE (current_function_decl) + || cfun->machine->is_OS_task + || cfun->machine->is_OS_main + || (!isr_p && call_used_regs[regno]))) + { + return reg; + } + + /* Any live register can be used if it is unused after. + Prologue/epilogue will care for it as needed. */ + + if (df_regs_ever_live_p (regno) + && reg_unused_after (insn, reg)) + { + return reg; + } + } + + return NULL_RTX; +} + /* Helper function for the next function in the case where only restricted version of LPM instruction is available. */ @@ -2279,28 +2437,30 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen) gcc_unreachable(); case 1: - return avr_asm_len ("lpm" CR_TAB - "mov %0,%3", xop, plen, 2); + avr_asm_len ("%4lpm", xop, plen, 1); + + if (regno_dest != LPM_REGNO) + avr_asm_len ("mov %0,%3", xop, plen, 1); + + return ""; case 2: if (REGNO (dest) == REG_Z) - return avr_asm_len ("lpm" CR_TAB + return avr_asm_len ("%4lpm" CR_TAB "push %3" CR_TAB "adiw %2,1" CR_TAB - "lpm" CR_TAB + "%4lpm" CR_TAB "mov %B0,%3" CR_TAB "pop %A0", xop, plen, 6); - else - { - avr_asm_len ("lpm" CR_TAB - "mov %A0,%3" CR_TAB - "adiw %2,1" CR_TAB - "lpm" CR_TAB - "mov %B0,%3", xop, plen, 5); + + avr_asm_len ("%4lpm" CR_TAB + "mov %A0,%3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %B0,%3", xop, plen, 5); - if (!reg_unused_after (insn, addr)) - avr_asm_len ("sbiw %2,1", xop, plen, 1); - } + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,1", xop, plen, 1); break; /* 2 */ } @@ -2310,17 +2470,31 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen) case POST_INC: gcc_assert (REG_Z == REGNO (XEXP (addr, 0)) - && n_bytes <= 2); + && n_bytes <= 4); - avr_asm_len ("lpm" CR_TAB - "mov %A0,%3" CR_TAB - "adiw %2,1", xop, plen, 3); + if (regno_dest == LPM_REGNO) + avr_asm_len ("%4lpm" CR_TAB + "adiw %2,1", xop, plen, 2); + else + avr_asm_len ("%4lpm" CR_TAB + "mov %A0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); if (n_bytes >= 2) - avr_asm_len ("lpm" CR_TAB + avr_asm_len ("%4lpm" CR_TAB "mov %B0,%3" CR_TAB "adiw %2,1", xop, plen, 3); + if (n_bytes >= 3) + avr_asm_len ("%4lpm" CR_TAB + "mov %C0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + if (n_bytes >= 4) + avr_asm_len ("%4lpm" CR_TAB + "mov %D0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + break; /* POST_INC */ } /* switch CODE (addr) */ @@ -2337,12 +2511,13 @@ avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen) static const char* avr_out_lpm (rtx insn, rtx *op, int *plen) { - rtx xop[5]; + rtx xop[6]; rtx dest = op[0]; rtx src = SET_SRC (single_set (insn)); rtx addr; int n_bytes = GET_MODE_SIZE (GET_MODE (dest)); int regno_dest; + int segment; if (plen) *plen = 0; @@ -2357,17 +2532,66 @@ avr_out_lpm (rtx insn, rtx *op, int *plen) addr = XEXP (src, 0); - gcc_assert (!avr_load_libgcc_p (src) - && REG_P (dest) - && (REG_P (addr) || POST_INC == GET_CODE (addr))); + segment = avr_pgm_segment (MEM_ADDR_SPACE (src)); + + gcc_assert (REG_P (dest) + && ((segment >= 0 + && (REG_P (addr) || POST_INC == GET_CODE (addr))) + || (GET_CODE (addr) == LO_SUM && segment == -1))); + + if (segment == -1) + { + /* We are called from avr_out_xload because someone wrote + __pgmx on a device with just one flash segment. */ + + addr = XEXP (addr, 1); + } xop[0] = dest; xop[1] = addr; xop[2] = lpm_addr_reg_rtx; + xop[4] = xstring_empty; + xop[5] = tmp_reg_rtx; regno_dest = REGNO (dest); - if (!AVR_HAVE_LPMX) + /* Cut down segment number to a number the device actually + supports. We do this late to preserve the address space's + name for diagnostics. */ + + segment %= avr_current_arch->n_segments; + + /* Set RAMPZ as needed. */ + + if (segment) + { + xop[4] = GEN_INT (segment); + + if (xop[3] = avr_find_unused_d_reg (insn, lpm_addr_reg_rtx), + xop[3]) + { + avr_asm_len ("ldi %3,%4" CR_TAB + "out __RAMPZ__,%3", xop, plen, 2); + } + else if (segment == 1) + { + avr_asm_len ("clr %5" CR_TAB + "inc %5" CR_TAB + "out __RAMPZ__,%5", xop, plen, 3); + } + else + { + avr_asm_len ("mov %5,%2" CR_TAB + "ldi %2,%4" CR_TAB + "out __RAMPZ__,%2" CR_TAB + "mov %2,%5", xop, plen, 4); + } + + xop[4] = xstring_e; + } + + if ((segment == 0 && !AVR_HAVE_LPMX) + || (segment != 0 && !AVR_HAVE_ELPMX)) { return avr_out_lpm_no_lpmx (insn, xop, plen); } @@ -2387,17 +2611,17 @@ avr_out_lpm (rtx insn, rtx *op, int *plen) gcc_unreachable(); case 1: - return avr_asm_len ("lpm %0,%a2", xop, plen, -1); + return avr_asm_len ("%4lpm %0,%a2", xop, plen, 1); case 2: if (REGNO (dest) == REG_Z) - return avr_asm_len ("lpm __tmp_reg__,%a2+" CR_TAB - "lpm %B0,%a2" CR_TAB - "mov %A0,__tmp_reg__", xop, plen, -3); + return avr_asm_len ("%4lpm %5,%a2+" CR_TAB + "%4lpm %B0,%a2" CR_TAB + "mov %A0,%5", xop, plen, 3); else { - avr_asm_len ("lpm %A0,%a2+" CR_TAB - "lpm %B0,%a2", xop, plen, -2); + avr_asm_len ("%4lpm %A0,%a2+" CR_TAB + "%4lpm %B0,%a2", xop, plen, 2); if (!reg_unused_after (insn, addr)) avr_asm_len ("sbiw %2,1", xop, plen, 1); @@ -2407,9 +2631,9 @@ avr_out_lpm (rtx insn, rtx *op, int *plen) case 3: - avr_asm_len ("lpm %A0,%a2+" CR_TAB - "lpm %B0,%a2+" CR_TAB - "lpm %C0,%a2", xop, plen, -3); + avr_asm_len ("%4lpm %A0,%a2+" CR_TAB + "%4lpm %B0,%a2+" CR_TAB + "%4lpm %C0,%a2", xop, plen, 3); if (!reg_unused_after (insn, addr)) avr_asm_len ("sbiw %2,2", xop, plen, 1); @@ -2418,17 +2642,17 @@ avr_out_lpm (rtx insn, rtx *op, int *plen) case 4: - avr_asm_len ("lpm %A0,%a2+" CR_TAB - "lpm %B0,%a2+", xop, plen, -2); + avr_asm_len ("%4lpm %A0,%a2+" CR_TAB + "%4lpm %B0,%a2+", xop, plen, 2); if (REGNO (dest) == REG_Z - 2) - return avr_asm_len ("lpm __tmp_reg__,%a2+" CR_TAB - "lpm %C0,%a2" CR_TAB - "mov %D0,__tmp_reg__", xop, plen, 3); + return avr_asm_len ("%4lpm %5,%a2+" CR_TAB + "%4lpm %C0,%a2" CR_TAB + "mov %D0,%5", xop, plen, 3); else { - avr_asm_len ("lpm %C0,%a2+" CR_TAB - "lpm %D0,%a2", xop, plen, 2); + avr_asm_len ("%4lpm %C0,%a2+" CR_TAB + "%4lpm %D0,%a2", xop, plen, 2); if (!reg_unused_after (insn, addr)) avr_asm_len ("sbiw %2,3", xop, plen, 1); @@ -2444,10 +2668,10 @@ avr_out_lpm (rtx insn, rtx *op, int *plen) gcc_assert (REG_Z == REGNO (XEXP (addr, 0)) && n_bytes <= 4); - avr_asm_len ("lpm %A0,%a2+", xop, plen, -1); - if (n_bytes >= 2) avr_asm_len ("lpm %B0,%a2+", xop, plen, 1); - if (n_bytes >= 3) avr_asm_len ("lpm %C0,%a2+", xop, plen, 1); - if (n_bytes >= 4) avr_asm_len ("lpm %D0,%a2+", xop, plen, 1); + avr_asm_len ("%4lpm %A0,%a2+", xop, plen, 1); + if (n_bytes >= 2) avr_asm_len ("%4lpm %B0,%a2+", xop, plen, 1); + if (n_bytes >= 3) avr_asm_len ("%4lpm %C0,%a2+", xop, plen, 1); + if (n_bytes >= 4) avr_asm_len ("%4lpm %D0,%a2+", xop, plen, 1); break; /* POST_INC */ @@ -2457,6 +2681,81 @@ avr_out_lpm (rtx insn, rtx *op, int *plen) } +/* Worker function for xload_<mode> and xload_8 insns. */ + +const char* +avr_out_xload (rtx insn, rtx *op, int *plen) +{ + rtx xop[5]; + rtx reg = op[0]; + int n_bytes = GET_MODE_SIZE (GET_MODE (reg)); + unsigned int regno = REGNO (reg); + + if (avr_current_arch->n_segments == 1) + return avr_out_lpm (insn, op, plen); + + xop[0] = reg; + xop[1] = op[1]; + xop[2] = lpm_addr_reg_rtx; + xop[3] = lpm_reg_rtx; + xop[4] = tmp_reg_rtx; + + avr_asm_len ("out __RAMPZ__,%1", xop, plen, -1); + + if (1 == n_bytes) + { + if (AVR_HAVE_ELPMX) + return avr_asm_len ("elpm %0,%a2", xop, plen, 1); + else + return avr_asm_len ("elpm" CR_TAB + "mov %0,%3", xop, plen, 2); + } + + gcc_assert (AVR_HAVE_ELPMX); + + if (!reg_overlap_mentioned_p (reg, lpm_addr_reg_rtx)) + { + /* Insn clobbers the Z-register so we can use post-increment. */ + + avr_asm_len ("elpm %A0,%a2+", xop, plen, 1); + if (n_bytes >= 2) avr_asm_len ("elpm %B0,%a2+", xop, plen, 1); + if (n_bytes >= 3) avr_asm_len ("elpm %C0,%a2+", xop, plen, 1); + if (n_bytes >= 4) avr_asm_len ("elpm %D0,%a2+", xop, plen, 1); + + return ""; + } + + switch (n_bytes) + { + default: + gcc_unreachable(); + + case 2: + gcc_assert (regno == REGNO (lpm_addr_reg_rtx)); + + return avr_asm_len ("elpm %4,%a2+" CR_TAB + "elpm %B0,%a2" CR_TAB + "mov %A0,%4", xop, plen, 3); + + case 3: + case 4: + gcc_assert (regno + 2 == REGNO (lpm_addr_reg_rtx)); + + avr_asm_len ("elpm %A0,%a2+" CR_TAB + "elpm %B0,%a2+", xop, plen, 2); + + if (n_bytes == 3) + return avr_asm_len ("elpm %C0,%a2", xop, plen, 1); + else + return avr_asm_len ("elpm %4,%a2+" CR_TAB + "elpm %D0,%a2" CR_TAB + "mov %C0,%4", xop, plen, 3); + } + + return ""; +} + + const char * output_movqi (rtx insn, rtx operands[], int *l) { @@ -2596,71 +2895,73 @@ output_movhi (rtx insn, rtx operands[], int *l) } const char * -out_movqi_r_mr (rtx insn, rtx op[], int *l) +out_movqi_r_mr (rtx insn, rtx op[], int *plen) { rtx dest = op[0]; rtx src = op[1]; rtx x = XEXP (src, 0); - int dummy; - - if (!l) - l = &dummy; if (CONSTANT_ADDRESS_P (x)) { - if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR) - { - *l = 1; - return AS2 (in,%0,__SREG__); - } + if (CONST_INT_P (x)) + { + if (SREG_ADDR == INTVAL (x)) + return avr_asm_len ("in %0,__SREG__", op, plen, -1); + + if (RAMPZ_ADDR == INTVAL (x)) + return avr_asm_len ("in %0,__RAMPZ__", op, plen, -1); + } + if (optimize > 0 && io_address_operand (x, QImode)) - { - *l = 1; - return AS2 (in,%0,%m1-0x20); - } - *l = 2; - return AS2 (lds,%0,%m1); + return avr_asm_len ("in %0,%m1-0x20", op, plen, -1); + + return avr_asm_len ("lds %0,%m1", op, plen, -2); } - /* memory access by reg+disp */ else if (GET_CODE (x) == PLUS - && REG_P (XEXP (x,0)) - && GET_CODE (XEXP (x,1)) == CONST_INT) + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1))) { - if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (src))) >= 63) - { - int disp = INTVAL (XEXP (x,1)); - if (REGNO (XEXP (x,0)) != REG_Y) - fatal_insn ("incorrect insn:",insn); + /* memory access by reg+disp */ - if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) - return *l = 3, (AS2 (adiw,r28,%o1-63) CR_TAB - AS2 (ldd,%0,Y+63) CR_TAB - AS2 (sbiw,r28,%o1-63)); + int disp = INTVAL (XEXP (x, 1)); + + if (disp - GET_MODE_SIZE (GET_MODE (src)) >= 63) + { + if (REGNO (XEXP (x, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); - return *l = 5, (AS2 (subi,r28,lo8(-%o1)) CR_TAB - AS2 (sbci,r29,hi8(-%o1)) CR_TAB - AS2 (ld,%0,Y) CR_TAB - AS2 (subi,r28,lo8(%o1)) CR_TAB - AS2 (sbci,r29,hi8(%o1))); - } - else if (REGNO (XEXP (x,0)) == REG_X) - { - /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude - it but I have this situation with extremal optimizing options. */ - if (reg_overlap_mentioned_p (dest, XEXP (x,0)) - || reg_unused_after (insn, XEXP (x,0))) - return *l = 2, (AS2 (adiw,r26,%o1) CR_TAB - AS2 (ld,%0,X)); - - return *l = 3, (AS2 (adiw,r26,%o1) CR_TAB - AS2 (ld,%0,X) CR_TAB - AS2 (sbiw,r26,%o1)); - } - *l = 1; - return AS2 (ldd,%0,%1); + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src))) + return avr_asm_len ("adiw r28,%o1-63" CR_TAB + "ldd %0,Y+63" CR_TAB + "sbiw r28,%o1-63", op, plen, -3); + + return avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB + "sbci r29,hi8(-%o1)" CR_TAB + "ld %0,Y" CR_TAB + "subi r28,lo8(%o1)" CR_TAB + "sbci r29,hi8(%o1)", op, plen, -5); + } + else if (REGNO (XEXP (x, 0)) == REG_X) + { + /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude + it but I have this situation with extremal optimizing options. */ + + avr_asm_len ("adiw r26,%o1" CR_TAB + "ld %0,X", op, plen, -2); + + if (!reg_overlap_mentioned_p (dest, XEXP (x,0)) + && !reg_unused_after (insn, XEXP (x,0))) + { + avr_asm_len ("sbiw r26,%o1", op, plen, 1); + } + + return ""; + } + + return avr_asm_len ("ldd %0,%1", op, plen, -1); } - *l = 1; - return AS2 (ld,%0,%1); + + return avr_asm_len ("ld %0,%1", op, plen, -1); } const char * @@ -3539,83 +3840,76 @@ avr_out_movpsi (rtx insn, rtx *op, int *plen) const char * -out_movqi_mr_r (rtx insn, rtx op[], int *l) +out_movqi_mr_r (rtx insn, rtx op[], int *plen) { rtx dest = op[0]; rtx src = op[1]; rtx x = XEXP (dest, 0); - int dummy; - - if (!l) - l = &dummy; if (CONSTANT_ADDRESS_P (x)) { - if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR) - { - *l = 1; - return AS2 (out,__SREG__,%1); - } + if (CONST_INT_P (x)) + { + if (SREG_ADDR == INTVAL (x)) + return avr_asm_len ("out __SREG__,%1", op, plen, -1); + + if (RAMPZ_ADDR == INTVAL (x)) + return avr_asm_len ("out __RAMPZ__,%1", op, plen, -1); + } + if (optimize > 0 && io_address_operand (x, QImode)) - { - *l = 1; - return AS2 (out,%m0-0x20,%1); - } - *l = 2; - return AS2 (sts,%m0,%1); + avr_asm_len ("out %m0-0x20,%1", op, plen, -1); + + return avr_asm_len ("sts %m0,%1", op, plen, -2); } - /* memory access by reg+disp */ - else if (GET_CODE (x) == PLUS - && REG_P (XEXP (x,0)) - && GET_CODE (XEXP (x,1)) == CONST_INT) + else if (GET_CODE (x) == PLUS + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1))) { - if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (dest))) >= 63) - { - int disp = INTVAL (XEXP (x,1)); - if (REGNO (XEXP (x,0)) != REG_Y) - fatal_insn ("incorrect insn:",insn); + /* memory access by reg+disp */ - if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) - return *l = 3, (AS2 (adiw,r28,%o0-63) CR_TAB - AS2 (std,Y+63,%1) CR_TAB - AS2 (sbiw,r28,%o0-63)); + int disp = INTVAL (XEXP (x, 1)); - return *l = 5, (AS2 (subi,r28,lo8(-%o0)) CR_TAB - AS2 (sbci,r29,hi8(-%o0)) CR_TAB - AS2 (st,Y,%1) CR_TAB - AS2 (subi,r28,lo8(%o0)) CR_TAB - AS2 (sbci,r29,hi8(%o0))); - } + if (disp - GET_MODE_SIZE (GET_MODE (dest)) >= 63) + { + if (REGNO (XEXP (x, 0)) != REG_Y) + fatal_insn ("incorrect insn:",insn); + + if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest))) + return avr_asm_len ("adiw r28,%o0-63" CR_TAB + "std Y+63,%1" CR_TAB + "sbiw r28,%o0-63", op, plen, -3); + + return avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB + "sbci r29,hi8(-%o0)" CR_TAB + "st Y,%1" CR_TAB + "subi r28,lo8(%o0)" CR_TAB + "sbci r29,hi8(%o0)", op, plen, -5); + } else if (REGNO (XEXP (x,0)) == REG_X) - { - if (reg_overlap_mentioned_p (src, XEXP (x, 0))) - { - if (reg_unused_after (insn, XEXP (x,0))) - return *l = 3, (AS2 (mov,__tmp_reg__,%1) CR_TAB - AS2 (adiw,r26,%o0) CR_TAB - AS2 (st,X,__tmp_reg__)); - - return *l = 4, (AS2 (mov,__tmp_reg__,%1) CR_TAB - AS2 (adiw,r26,%o0) CR_TAB - AS2 (st,X,__tmp_reg__) CR_TAB - AS2 (sbiw,r26,%o0)); - } - else - { - if (reg_unused_after (insn, XEXP (x,0))) - return *l = 2, (AS2 (adiw,r26,%o0) CR_TAB - AS2 (st,X,%1)); + { + if (reg_overlap_mentioned_p (src, XEXP (x, 0))) + { + avr_asm_len ("mov __tmp_reg__,%1" CR_TAB + "adiw r26,%o0" CR_TAB + "st X,__tmp_reg__", op, plen, -3); + } + else + { + avr_asm_len ("adiw r26,%o0" CR_TAB + "st X,%1", op, plen, -2); + } + + if (!reg_unused_after (insn, XEXP (x,0))) + avr_asm_len ("sbiw r26,%o0", op, plen, 1); - return *l = 3, (AS2 (adiw,r26,%o0) CR_TAB - AS2 (st,X,%1) CR_TAB - AS2 (sbiw,r26,%o0)); - } - } - *l = 1; - return AS2 (std,%0,%1); + return ""; + } + + return avr_asm_len ("std %0,%1", op, plen, 1); } - *l = 1; - return AS2 (st,%0,%1); + + return avr_asm_len ("st %0,%1", op, plen, 1); } const char * @@ -6201,7 +6495,9 @@ adjust_insn_length (rtx insn, int len) case ADJUST_LEN_MOV16: output_movhi (insn, op, &len); break; case ADJUST_LEN_MOV24: avr_out_movpsi (insn, op, &len); break; case ADJUST_LEN_MOV32: output_movsisf (insn, op, &len); break; - + case ADJUST_LEN_MOVMEM: avr_out_movmem (insn, op, &len); break; + case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break; + case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break; case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break; case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break; @@ -6346,6 +6642,49 @@ _reg_unused_after (rtx insn, rtx reg) return 1; } + +/* Return RTX that represents the lower 16 bits of a constant address. + Unfortunately, simplify_gen_subreg does not handle this case. */ + +static rtx +avr_const_address_lo16 (rtx x) +{ + rtx lo16; + + switch (GET_CODE (x)) + { + default: + break; + + case CONST: + if (PLUS == GET_CODE (XEXP (x, 0)) + && SYMBOL_REF == GET_CODE (XEXP (XEXP (x, 0), 0)) + && CONST_INT_P (XEXP (XEXP (x, 0), 1))) + { + HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (x, 0), 1)); + const char *name = XSTR (XEXP (XEXP (x, 0), 0), 0); + + lo16 = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name)); + lo16 = gen_rtx_CONST (Pmode, plus_constant (lo16, offset)); + + return lo16; + } + + break; + + case SYMBOL_REF: + { + const char *name = XSTR (x, 0); + + return gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name)); + } + } + + avr_edump ("\n%?: %r\n", x); + gcc_unreachable(); +} + + /* Target hook for assembling integer objects. The AVR version needs special handling for references to certain labels. */ @@ -6358,11 +6697,30 @@ avr_assemble_integer (rtx x, unsigned int size, int aligned_p) fputs ("\t.word\tgs(", asm_out_file); output_addr_const (asm_out_file, x); fputs (")\n", asm_out_file); + + return true; + } + else if (GET_MODE (x) == PSImode) + { + default_assemble_integer (avr_const_address_lo16 (x), + GET_MODE_SIZE (HImode), aligned_p); + + fputs ("\t.warning\t\"assembling 24-bit address needs binutils extension for hh8(", + asm_out_file); + output_addr_const (asm_out_file, x); + fputs (")\"\n", asm_out_file); + + fputs ("\t.byte\t0\t" ASM_COMMENT_START " hh8(", asm_out_file); + output_addr_const (asm_out_file, x); + fputs (")\n", asm_out_file); + return true; } + return default_assemble_integer (x, size, aligned_p); } + /* Worker function for ASM_DECLARE_FUNCTION_NAME. */ void @@ -6518,6 +6876,7 @@ avr_attribute_table[] = Return non-zero if DECL is data that must end up in Flash and zero if the data lives in RAM (.bss, .data, .rodata, ...). + Return 2 if DECL is located in 24-bit flash address-space Return 1 if DECL is located in 16-bit flash address-space Return -1 if attribute `progmem' occurs in DECL or ATTRIBUTES Return 0 otherwise */ @@ -6530,6 +6889,9 @@ avr_progmem_p (tree decl, tree attributes) if (TREE_CODE (decl) != VAR_DECL) return 0; + if (avr_decl_pgmx_p (decl)) + return 2; + if (avr_decl_pgm_p (decl)) return 1; @@ -6745,11 +7107,23 @@ avr_output_bss_section_asm_op (const void *data) } +/* Unnamed section callback for progmem*.data sections. */ + +static void +avr_output_progmem_section_asm_op (const void *data) +{ + fprintf (asm_out_file, "\t.section\t%s,\"a\",@progbits\n", + (const char*) data); +} + + /* Implement `TARGET_ASM_INIT_SECTIONS'. */ static void avr_asm_init_sections (void) { + unsigned int n; + /* Set up a section for jump tables. Alignment is handled by ASM_OUTPUT_BEFORE_CASE_LABEL. */ @@ -6768,9 +7142,12 @@ avr_asm_init_sections (void) ",\"ax\",@progbits"); } - progmem_section - = get_unnamed_section (0, output_section_asm_op, - "\t.section\t.progmem.data,\"a\",@progbits"); + for (n = 0; n < sizeof (progmem_section) / sizeof (*progmem_section); n++) + { + progmem_section[n] + = get_unnamed_section (0, avr_output_progmem_section_asm_op, + progmem_section_prefix[n]); + } /* Override section callbacks to keep track of `avr_need_clear_bss_p' resp. `avr_need_copy_data_p'. */ @@ -6849,8 +7226,9 @@ avr_asm_named_section (const char *name, unsigned int flags, tree decl) { if (flags & AVR_SECTION_PROGMEM) { + int segment = (flags & AVR_SECTION_PROGMEM) / SECTION_MACH_DEP - 1; const char *old_prefix = ".rodata"; - const char *new_prefix = ".progmem.data"; + const char *new_prefix = progmem_section_prefix[segment]; const char *sname = new_prefix; if (STR_PREFIX_P (name, old_prefix)) @@ -6877,6 +7255,7 @@ avr_asm_named_section (const char *name, unsigned int flags, tree decl) static unsigned int avr_section_type_flags (tree decl, const char *name, int reloc) { + int prog; unsigned int flags = default_section_type_flags (decl, name, reloc); if (STR_PREFIX_P (name, ".noinit")) @@ -6890,11 +7269,16 @@ avr_section_type_flags (tree decl, const char *name, int reloc) } if (decl && DECL_P (decl) - && avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) + && (prog = avr_progmem_p (decl, DECL_ATTRIBUTES (decl)), prog)) { + int segment = 0; + + if (prog == 1) + segment = avr_pgm_segment (TYPE_ADDR_SPACE (TREE_TYPE (decl))); + flags &= ~SECTION_WRITE; flags &= ~SECTION_BSS; - flags |= AVR_SECTION_PROGMEM; + flags |= (1 + segment % avr_current_arch->n_segments) * SECTION_MACH_DEP; } return flags; @@ -6930,16 +7314,25 @@ avr_encode_section_info (tree decl, rtx rtl, static section * avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align) { + int prog; + section * sect = default_elf_select_section (decl, reloc, align); if (decl && DECL_P (decl) - && avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) + && (prog = avr_progmem_p (decl, DECL_ATTRIBUTES (decl)), prog)) { + int segment = 0; + + if (prog == 1) + segment = avr_pgm_segment (TYPE_ADDR_SPACE (TREE_TYPE (decl))); + + segment %= avr_current_arch->n_segments; + if (sect->common.flags & SECTION_NAMED) { const char * name = sect->named.name; const char * old_prefix = ".rodata"; - const char * new_prefix = ".progmem.data"; + const char * new_prefix = progmem_section_prefix[segment]; if (STR_PREFIX_P (name, old_prefix)) { @@ -6950,31 +7343,36 @@ avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align) } } - return progmem_section; + return progmem_section[segment]; } return sect; } /* Implement `TARGET_ASM_FILE_START'. */ -/* Outputs some appropriate text to go at the start of an assembler - file. */ +/* Outputs some text at the start of each assembler file. */ static void avr_file_start (void) { + int sfr_offset = 0x20; + if (avr_current_arch->asm_only) error ("MCU %qs supported for assembler only", avr_current_device->name); default_file_start (); -/* fprintf (asm_out_file, "\t.arch %s\n", avr_current_device->name);*/ - fputs ("__SREG__ = 0x3f\n" - "__SP_H__ = 0x3e\n" - "__SP_L__ = 0x3d\n", asm_out_file); - - fputs ("__tmp_reg__ = 0\n" - "__zero_reg__ = 1\n", asm_out_file); + fprintf (asm_out_file, + "__SREG__ = 0x%02x\n" + "__SP_H__ = 0x%02x\n" + "__SP_L__ = 0x%02x\n" + "__RAMPZ__ = 0x%02x\n" + "__tmp_reg__ = 0\n" + "__zero_reg__ = 1\n", + -sfr_offset + SREG_ADDR, + -sfr_offset + SP_ADDR + 1, + -sfr_offset + SP_ADDR, + -sfr_offset + RAMPZ_ADDR); } @@ -8954,8 +9352,8 @@ const char * avr_out_sbxx_branch (rtx insn, rtx operands[]) { enum rtx_code comp = GET_CODE (operands[0]); - int long_jump = (get_attr_length (insn) >= 4); - int reverse = long_jump || jump_over_one_insn_p (insn, operands[3]); + bool long_jump = get_attr_length (insn) >= 4; + bool reverse = long_jump || jump_over_one_insn_p (insn, operands[3]); if (comp == GE) comp = EQ; @@ -8965,49 +9363,61 @@ avr_out_sbxx_branch (rtx insn, rtx operands[]) if (reverse) comp = reverse_condition (comp); - if (GET_CODE (operands[1]) == CONST_INT) + switch (GET_CODE (operands[1])) { - if (INTVAL (operands[1]) < 0x40) - { - if (comp == EQ) - output_asm_insn (AS2 (sbis,%m1-0x20,%2), operands); - else - output_asm_insn (AS2 (sbic,%m1-0x20,%2), operands); - } + default: + gcc_unreachable(); + + case CONST_INT: + + if (low_io_address_operand (operands[1], QImode)) + { + if (comp == EQ) + output_asm_insn ("sbis %m1-0x20,%2", operands); + else + output_asm_insn ("sbic %m1-0x20,%2", operands); + } else - { - output_asm_insn (AS2 (in,__tmp_reg__,%m1-0x20), operands); - if (comp == EQ) - output_asm_insn (AS2 (sbrs,__tmp_reg__,%2), operands); - else - output_asm_insn (AS2 (sbrc,__tmp_reg__,%2), operands); - } - } - else /* GET_CODE (operands[1]) == REG */ - { + { + output_asm_insn ("in __tmp_reg__,%m1-0x20", operands); + if (comp == EQ) + output_asm_insn ("sbrs __tmp_reg__,%2", operands); + else + output_asm_insn ("sbrc __tmp_reg__,%2", operands); + } + + break; /* CONST_INT */ + + case REG: + if (GET_MODE (operands[1]) == QImode) - { - if (comp == EQ) - output_asm_insn (AS2 (sbrs,%1,%2), operands); - else - output_asm_insn (AS2 (sbrc,%1,%2), operands); - } - else /* HImode or SImode */ - { - static char buf[] = "sbrc %A1,0"; - int bit_nr = INTVAL (operands[2]); - buf[3] = (comp == EQ) ? 's' : 'c'; - buf[6] = 'A' + (bit_nr >> 3); - buf[9] = '0' + (bit_nr & 7); - output_asm_insn (buf, operands); - } - } + { + if (comp == EQ) + output_asm_insn ("sbrs %1,%2", operands); + else + output_asm_insn ("sbrc %1,%2", operands); + } + else /* HImode, PSImode or SImode */ + { + static char buf[] = "sbrc %A1,0"; + unsigned int bit_nr = UINTVAL (operands[2]); + + buf[3] = (comp == EQ) ? 's' : 'c'; + buf[6] = 'A' + (bit_nr / 8); + buf[9] = '0' + (bit_nr % 8); + output_asm_insn (buf, operands); + } + + break; /* REG */ + } /* switch */ if (long_jump) - return (AS1 (rjmp,.+4) CR_TAB - AS1 (jmp,%x3)); + return ("rjmp .+4" CR_TAB + "jmp %x3"); + if (!reverse) - return AS1 (rjmp,%x3); + return "rjmp %x3"; + return ""; } @@ -9055,18 +9465,18 @@ avr_case_values_threshold (void) /* Implement `TARGET_ADDR_SPACE_ADDRESS_MODE'. */ static enum machine_mode -avr_addr_space_address_mode (addr_space_t as ATTRIBUTE_UNUSED) +avr_addr_space_address_mode (addr_space_t as) { - return HImode; + return as == ADDR_SPACE_PGMX ? PSImode : HImode; } /* Implement `TARGET_ADDR_SPACE_POINTER_MODE'. */ static enum machine_mode -avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED) +avr_addr_space_pointer_mode (addr_space_t as) { - return HImode; + return as == ADDR_SPACE_PGMX ? PSImode : HImode; } @@ -9111,6 +9521,11 @@ avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x, return avr_legitimate_address_p (mode, x, strict); case ADDR_SPACE_PGM: + case ADDR_SPACE_PGM1: + case ADDR_SPACE_PGM2: + case ADDR_SPACE_PGM3: + case ADDR_SPACE_PGM4: + case ADDR_SPACE_PGM5: switch (GET_CODE (x)) { @@ -9119,8 +9534,7 @@ avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x, break; case POST_INC: - ok = (!avr_load_libgcc_p (x) - && avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict)); + ok = avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict); break; default: @@ -9128,6 +9542,24 @@ avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x, } break; /* PGM */ + + case ADDR_SPACE_PGMX: + if (REG_P (x)) + ok = (!strict + && can_create_pseudo_p()); + + if (LO_SUM == GET_CODE (x)) + { + rtx hi = XEXP (x, 0); + rtx lo = XEXP (x, 1); + + ok = (REG_P (hi) + && (!strict || REGNO (hi) < FIRST_PSEUDO_REGISTER) + && REG_P (lo) + && REGNO (lo) == REG_Z); + } + + break; /* PGMX */ } if (avr_log.legitimate_address_p) @@ -9177,10 +9609,60 @@ avr_addr_space_legitimize_address (rtx x, rtx old_x, static rtx avr_addr_space_convert (rtx src, tree type_from, tree type_to) { + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (type_from)); + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type_to)); + if (avr_log.progmem) avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n", src, type_from, type_to); + if (as_from != ADDR_SPACE_PGMX + && as_to == ADDR_SPACE_PGMX) + { + rtx new_src; + int n_segments = avr_current_arch->n_segments; + RTX_CODE code = GET_CODE (src); + + if (CONST == code + && PLUS == GET_CODE (XEXP (src, 0)) + && SYMBOL_REF == GET_CODE (XEXP (XEXP (src, 0), 0)) + && CONST_INT_P (XEXP (XEXP (src, 0), 1))) + { + HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (src, 0), 1)); + const char *name = XSTR (XEXP (XEXP (src, 0), 0), 0); + + new_src = gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name)); + new_src = gen_rtx_CONST (PSImode, + plus_constant (new_src, offset)); + return new_src; + } + + if (SYMBOL_REF == code) + { + const char *name = XSTR (src, 0); + + return gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name)); + } + + src = force_reg (Pmode, src); + + if (ADDR_SPACE_GENERIC_P (as_from) + || as_from == ADDR_SPACE_PGM + || n_segments == 1) + { + return gen_rtx_ZERO_EXTEND (PSImode, src); + } + else + { + int segment = avr_pgm_segment (as_from) % n_segments; + + new_src = gen_reg_rtx (PSImode); + emit_insn (gen_n_extendhipsi2 (new_src, GEN_INT (segment), src)); + + return new_src; + } + } + return src; } @@ -9188,13 +9670,242 @@ avr_addr_space_convert (rtx src, tree type_from, tree type_to) /* Implement `TARGET_ADDR_SPACE_SUBSET_P'. */ static bool -avr_addr_space_subset_p (addr_space_t subset ATTRIBUTE_UNUSED, - addr_space_t superset ATTRIBUTE_UNUSED) +avr_addr_space_subset_p (addr_space_t subset, addr_space_t superset) { + if (subset == ADDR_SPACE_PGMX + && superset != ADDR_SPACE_PGMX) + { + return false; + } + + return true; +} + + +/* Worker function for movmemhi insn. + XOP[0] Destination as MEM:BLK + XOP[1] Source " " + XOP[2] # Bytes to copy + + Return TRUE if the expansion is accomplished. + Return FALSE if the operand compination is not supported. */ + +bool +avr_emit_movmemhi (rtx *xop) +{ + HOST_WIDE_INT count; + enum machine_mode loop_mode; + addr_space_t as = MEM_ADDR_SPACE (xop[1]); + rtx loop_reg, addr0, addr1, a_src, a_dest, insn, xas, reg_x; + rtx a_hi8 = NULL_RTX; + + if (avr_mem_pgm_p (xop[0])) + return false; + + if (!CONST_INT_P (xop[2])) + return false; + + count = INTVAL (xop[2]); + if (count <= 0) + return false; + + a_src = XEXP (xop[1], 0); + a_dest = XEXP (xop[0], 0); + + /* See if constant fits in 8 bits. */ + + loop_mode = (count <= 0x100) ? QImode : HImode; + + if (PSImode == GET_MODE (a_src)) + { + addr1 = simplify_gen_subreg (HImode, a_src, PSImode, 0); + a_hi8 = simplify_gen_subreg (QImode, a_src, PSImode, 2); + } + else + { + int seg = avr_pgm_segment (as); + + addr1 = a_src; + + if (seg > 0 + && seg % avr_current_arch->n_segments > 0) + { + a_hi8 = GEN_INT (seg % avr_current_arch->n_segments); + } + } + + if (a_hi8 + && avr_current_arch->n_segments > 1) + { + emit_move_insn (rampz_rtx, a_hi8 = copy_to_mode_reg (QImode, a_hi8)); + } + else if (!ADDR_SPACE_GENERIC_P (as)) + { + as = ADDR_SPACE_PGM; + } + + xas = GEN_INT (as); + + /* Create loop counter register */ + + loop_reg = copy_to_mode_reg (loop_mode, gen_int_mode (count, loop_mode)); + + /* Copy pointers into new pseudos - they will be changed */ + + addr0 = copy_to_mode_reg (HImode, a_dest); + addr1 = copy_to_mode_reg (HImode, addr1); + + /* FIXME: Register allocator might come up with spill fails if it is left + on its own. Thus, we allocate the pointer registers by hand. */ + + emit_move_insn (lpm_addr_reg_rtx, addr1); + addr1 = lpm_addr_reg_rtx; + + reg_x = gen_rtx_REG (HImode, REG_X); + emit_move_insn (reg_x, addr0); + addr0 = reg_x; + + /* FIXME: Register allocator does a bad job and might spill address + register(s) inside the loop leading to additional move instruction + to/from stack which could clobber tmp_reg. Thus, do *not* emit + load and store as seperate insns. Instead, we perform the copy + by means of one monolithic insn. */ + + if (ADDR_SPACE_GENERIC_P (as)) + { + rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx) + = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi; + + insn = fun (addr0, addr1, xas, loop_reg, + addr0, addr1, tmp_reg_rtx, loop_reg); + } + else if (as == ADDR_SPACE_PGM) + { + rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx) + = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi; + + insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1, + AVR_HAVE_LPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg); + } + else + { + rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx) + = QImode == loop_mode ? gen_movmem_qi_elpm : gen_movmem_hi_elpm; + + insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1, + AVR_HAVE_ELPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg, + a_hi8, a_hi8, GEN_INT (RAMPZ_ADDR)); + } + + set_mem_addr_space (SET_SRC (XVECEXP (insn, 0, 0)), as); + emit_insn (insn); + return true; } +/* Print assembler for movmem_qi, movmem_hi insns... + $0, $4 : & dest + $1, $5 : & src + $2 : Address Space + $3, $7 : Loop register + $6 : Scratch register + + ...and movmem_qi_elpm, movmem_hi_elpm insns. + + $8, $9 : hh8 (& src) + $10 : RAMPZ_ADDR +*/ + +const char* +avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen) +{ + addr_space_t as = (addr_space_t) INTVAL (xop[2]); + enum machine_mode loop_mode = GET_MODE (xop[3]); + + bool sbiw_p = test_hard_reg_class (ADDW_REGS, xop[3]); + + gcc_assert (REG_X == REGNO (xop[0]) + && REG_Z == REGNO (xop[1])); + + if (plen) + *plen = 0; + + /* Loop label */ + + avr_asm_len ("0:", xop, plen, 0); + + /* Load with post-increment */ + + switch (as) + { + default: + gcc_unreachable(); + + case ADDR_SPACE_GENERIC: + + avr_asm_len ("ld %6,%a1+", xop, plen, 1); + break; + + case ADDR_SPACE_PGM: + + if (AVR_HAVE_LPMX) + avr_asm_len ("lpm %6,%a1+", xop, plen, 1); + else + avr_asm_len ("lpm" CR_TAB + "adiw %1,1", xop, plen, 2); + break; + + case ADDR_SPACE_PGM1: + case ADDR_SPACE_PGM2: + case ADDR_SPACE_PGM3: + case ADDR_SPACE_PGM4: + case ADDR_SPACE_PGM5: + case ADDR_SPACE_PGMX: + + if (AVR_HAVE_ELPMX) + avr_asm_len ("elpm %6,%a1+", xop, plen, 1); + else + avr_asm_len ("elpm" CR_TAB + "adiw %1,1", xop, plen, 2); + + if (as == ADDR_SPACE_PGMX + && !AVR_HAVE_ELPMX) + { + avr_asm_len ("adc %8,__zero_reg__" CR_TAB + "out __RAMPZ__,%8", xop, plen, 2); + } + + break; + } + + /* Store with post-increment */ + + avr_asm_len ("st %a0+,%6", xop, plen, 1); + + /* Decrement loop-counter and set Z-flag */ + + if (QImode == loop_mode) + { + avr_asm_len ("dec %3", xop, plen, 1); + } + else if (sbiw_p) + { + avr_asm_len ("sbiw %3,1", xop, plen, 1); + } + else + { + avr_asm_len ("subi %A3,1" CR_TAB + "sbci %B3,0", xop, plen, 2); + } + + /* Loop until zero */ + + return avr_asm_len ("brne 0b", xop, plen, 1); +} + + + /* Helper for __builtin_avr_delay_cycles */ static void diff --git a/gcc/config/avr/avr.h b/gcc/config/avr/avr.h index 24a687b..30bca35 100644 --- a/gcc/config/avr/avr.h +++ b/gcc/config/avr/avr.h @@ -54,6 +54,9 @@ struct base_arch_s { /* Default start of data section address for architecture. */ int default_data_section_start; + /* Number of 64k segments in the flash. */ + int n_segments; + const char *const macro; /* Architecture name. */ @@ -131,6 +134,8 @@ extern const struct base_arch_s avr_arch_types[]; #define AVR_HAVE_MUL (avr_current_arch->have_mul) #define AVR_HAVE_MOVW (avr_current_arch->have_movw_lpmx) #define AVR_HAVE_LPMX (avr_current_arch->have_movw_lpmx) +#define AVR_HAVE_ELPM (avr_current_arch->have_elpm) +#define AVR_HAVE_ELPMX (avr_current_arch->have_elpmx) #define AVR_HAVE_RAMPZ (avr_current_arch->have_elpm) #define AVR_HAVE_EIJMP_EICALL (avr_current_arch->have_eijmp_eicall) #define AVR_HAVE_8BIT_SP (avr_current_device->short_sp || TARGET_TINY_STACK) @@ -393,6 +398,12 @@ typedef struct avr_args { #define ADDR_SPACE_PGM 1 +#define ADDR_SPACE_PGM1 2 +#define ADDR_SPACE_PGM2 3 +#define ADDR_SPACE_PGM3 4 +#define ADDR_SPACE_PGM4 5 +#define ADDR_SPACE_PGM5 6 +#define ADDR_SPACE_PGMX 7 #define REGISTER_TARGET_PRAGMAS() \ do { \ @@ -645,3 +656,5 @@ struct GTY(()) machine_function #define PUSH_ROUNDING(X) (X) #define ACCUMULATE_OUTGOING_ARGS avr_accumulate_outgoing_args() + +#define INIT_EXPANDERS avr_init_expanders() diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 78ffe86..9e7fc9a 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -47,11 +47,15 @@ (ZERO_REGNO 1) ; zero register r1 (SREG_ADDR 0x5F) + (SP_ADDR 0x5D) + + ;; Register holding the address' high part when loading via ELPM (RAMPZ_ADDR 0x5B) ]) (define_c_enum "unspec" [UNSPEC_STRLEN + UNSPEC_MOVMEM UNSPEC_INDEX_JMP UNSPEC_FMUL UNSPEC_FMULS @@ -128,6 +132,7 @@ "out_bitop, out_plus, out_plus_noclobber, addto_sp, tsthi, tstpsi, tstsi, compare, call, mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32, + xload, movmem, ashlqi, ashrqi, lshrqi, ashlhi, ashrhi, lshrhi, ashlsi, ashrsi, lshrsi, @@ -137,15 +142,14 @@ ;; Flavours of instruction set architecture (ISA), used in enabled attribute -;; mov: ISA has no MOVW -;; movw: ISA has MOVW -;; rjmp: ISA has no CALL/JMP -;; jmp: ISA has CALL/JMP -;; ijmp: ISA has no EICALL/EIJMP -;; eijmp: ISA has EICALL/EIJMP +;; mov : ISA has no MOVW movw : ISA has MOVW +;; rjmp : ISA has no CALL/JMP jmp : ISA has CALL/JMP +;; ijmp : ISA has no EICALL/EIJMP eijmp : ISA has EICALL/EIJMP +;; lpm : ISA has no LPMX lpmx : ISA has LPMX +;; elpm : ISA has ELPM but no ELPMX elpmx : ISA has ELPMX (define_attr "isa" - "mov,movw, rjmp,jmp, ijmp,eijmp, + "mov,movw, rjmp,jmp, ijmp,eijmp, lpm,lpmx, elpm,elpmx, standard" (const_string "standard")) @@ -176,6 +180,22 @@ (and (eq_attr "isa" "eijmp") (match_test "AVR_HAVE_EIJMP_EICALL")) (const_int 1) + + (and (eq_attr "isa" "lpm") + (match_test "!AVR_HAVE_LPMX")) + (const_int 1) + + (and (eq_attr "isa" "lpmx") + (match_test "AVR_HAVE_LPMX")) + (const_int 1) + + (and (eq_attr "isa" "elpm") + (match_test "AVR_HAVE_ELPM && !AVR_HAVE_ELPMX")) + (const_int 1) + + (and (eq_attr "isa" "elpmx") + (match_test "AVR_HAVE_ELPMX")) + (const_int 1) ] (const_int 0))) @@ -243,12 +263,10 @@ ;; even though its function is identical to that in builtins.c (define_expand "nonlocal_goto" - [ - (use (match_operand 0 "general_operand")) - (use (match_operand 1 "general_operand")) - (use (match_operand 2 "general_operand")) - (use (match_operand 3 "general_operand")) - ] + [(use (match_operand 0 "general_operand")) + (use (match_operand 1 "general_operand")) + (use (match_operand 2 "general_operand")) + (use (match_operand 3 "general_operand"))] "" { rtx r_label = copy_to_reg (operands[1]); @@ -333,7 +351,7 @@ set_mem_addr_space (operands[1], ADDR_SPACE_PGM); }) -(define_insn "*load.<mode>.libgcc" +(define_insn "load_<mode>_libgcc" [(set (reg:MOVMODE 22) (match_operand:MOVMODE 0 "memory_operand" "m,m"))] "avr_load_libgcc_p (operands[0]) @@ -348,6 +366,135 @@ (set_attr "cc" "clobber")]) +(define_insn_and_split "xload8_A" + [(set (match_operand:QI 0 "register_operand" "=r") + (match_operand:QI 1 "memory_operand" "m")) + (clobber (reg:HI REG_Z))] + "can_create_pseudo_p() + && avr_mem_pgmx_p (operands[1]) + && REG_P (XEXP (operands[1], 0))" + { gcc_unreachable(); } + "&& 1" + [(clobber (const_int 0))] + { + rtx insn, 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)); + + insn = emit_insn (gen_xload_8 (operands[0], hi8)); + set_mem_addr_space (SET_SRC (single_set (insn)), + MEM_ADDR_SPACE (operands[1])); + DONE; + }) + +(define_insn_and_split "xload<mode>_A" + [(set (match_operand:MOVMODE 0 "register_operand" "=r") + (match_operand:MOVMODE 1 "memory_operand" "m")) + (clobber (reg:QI 21)) + (clobber (reg:HI REG_Z))] + "QImode != <MODE>mode + && can_create_pseudo_p() + && avr_mem_pgmx_p (operands[1]) + && REG_P (XEXP (operands[1], 0))" + { gcc_unreachable(); } + "&& 1" + [(clobber (const_int 0))] + { + rtx addr = XEXP (operands[1], 0); + rtx reg_z = gen_rtx_REG (HImode, REG_Z); + rtx addr_hi8 = simplify_gen_subreg (QImode, addr, PSImode, 2); + addr_space_t as = MEM_ADDR_SPACE (operands[1]); + rtx hi8, insn; + + emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0)); + + if (avr_xload_libgcc_p (<MODE>mode)) + { + emit_move_insn (gen_rtx_REG (QImode, 21), addr_hi8); + insn = emit_insn (gen_xload_<mode>_libgcc ()); + emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, 22)); + } + else if (avr_current_arch->n_segments == 1 + && GET_MODE_SIZE (<MODE>mode) > 2 + && !AVR_HAVE_LPMX) + { + rtx src = gen_rtx_MEM (<MODE>mode, reg_z); + + as = ADDR_SPACE_PGM; + insn = emit_insn (gen_load_<mode>_libgcc (src)); + emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, 22)); + } + else + { + hi8 = gen_reg_rtx (QImode); + emit_move_insn (hi8, addr_hi8); + insn = emit_insn (gen_xload_<mode> (operands[0], hi8)); + } + + set_mem_addr_space (SET_SRC (single_set (insn)), as); + + DONE; + }) + +;; Move value from address space pgmx to a register +;; These insns must be prior to respective generic move insn. + +(define_insn "xload_8" + [(set (match_operand:QI 0 "register_operand" "=r") + (mem:QI (lo_sum:PSI (match_operand:QI 1 "register_operand" "r") + (reg:HI REG_Z))))] + "" + { + return avr_out_xload (insn, operands, NULL); + } + [(set_attr "adjust_len" "xload") + (set_attr "cc" "clobber")]) + +;; "xload_hi_libgcc" +;; "xload_psi_libgcc" +;; "xload_si_libgcc" +;; "xload_sf_libgcc" +(define_insn "xload_<mode>_libgcc" + [(set (reg:MOVMODE 22) + (mem:MOVMODE (lo_sum:PSI (reg:QI 21) + (reg:HI REG_Z)))) + (clobber (reg:QI 21)) + (clobber (reg:HI REG_Z))] + "<MODE>mode != QImode + && avr_xload_libgcc_p (<MODE>mode)" + { + rtx x_bytes = GEN_INT (GET_MODE_SIZE (<MODE>mode)); + + /* Devices with ELPM* also have CALL. */ + + output_asm_insn ("call __xload_%0", &x_bytes); + return ""; + } + [(set_attr "length" "2") + (set_attr "cc" "clobber")]) + +;; "xload_hi" +;; "xload_psi" +;; "xload_si" +;; "xload_sf" +(define_insn "xload_<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 (scratch:HI)) + (clobber (reg:HI REG_Z))] + "<MODE>mode != QImode + && !avr_xload_libgcc_p (<MODE>mode)" + { + return avr_out_xload (insn, operands, NULL); + } + [(set_attr "adjust_len" "xload") + (set_attr "cc" "clobber")]) + + ;; General move expanders ;; "movqi" @@ -375,6 +522,21 @@ operands[1] = src = copy_to_mode_reg (<MODE>mode, src); } + if (avr_mem_pgmx_p (src)) + { + rtx addr = XEXP (src, 0); + + if (!REG_P (addr)) + src = replace_equiv_address (src, copy_to_mode_reg (PSImode, addr)); + + if (QImode == <MODE>mode) + emit_insn (gen_xload8_A (dest, src)); + else + emit_insn (gen_xload<mode>_A (dest, src)); + + DONE; + } + if (avr_load_libgcc_p (src)) { /* For the small devices, do loads per libgcc call. */ @@ -673,171 +835,164 @@ ;;========================================================================= ;; move string (like memcpy) -;; implement as RTL loop (define_expand "movmemhi" [(parallel [(set (match_operand:BLK 0 "memory_operand" "") - (match_operand:BLK 1 "memory_operand" "")) - (use (match_operand:HI 2 "const_int_operand" "")) - (use (match_operand:HI 3 "const_int_operand" ""))])] + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand:HI 2 "const_int_operand" "")) + (use (match_operand:HI 3 "const_int_operand" ""))])] "" - "{ - int prob; - HOST_WIDE_INT count; - enum machine_mode mode; - rtx label = gen_label_rtx (); - rtx loop_reg; - rtx jump, src; - - /* Copy pointers into new psuedos - they will be changed. */ - rtx addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); - rtx addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); - - /* Create rtx for tmp register - we use this as scratch. */ - rtx tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO); - - if (avr_mem_pgm_p (operands[0])) - DONE; - - if (GET_CODE (operands[2]) != CONST_INT) + { + if (avr_emit_movmemhi (operands)) + DONE; + FAIL; + }) - count = INTVAL (operands[2]); - if (count <= 0) - FAIL; +(define_mode_attr MOVMEM_r_d [(QI "r") + (HI "d")]) + +;; $0, $4 : & dest +;; $1, $5 : & src +;; $2 : Address Space +;; $3, $7 : Loop register +;; $6 : Scratch register + +;; "movmem_qi" +;; "movmem_hi" +(define_insn "movmem_<mode>" + [(set (mem:BLK (match_operand:HI 0 "register_operand" "x")) + (mem:BLK (match_operand:HI 1 "register_operand" "z"))) + (unspec [(match_operand:QI 2 "const_int_operand" "LP")] + UNSPEC_MOVMEM) + (use (match_operand:QIHI 3 "register_operand" "<MOVMEM_r_d>")) + (clobber (match_operand:HI 4 "register_operand" "=0")) + (clobber (match_operand:HI 5 "register_operand" "=1")) + (clobber (match_operand:QI 6 "register_operand" "=&r")) + (clobber (match_operand:QIHI 7 "register_operand" "=3"))] + "" + { + return avr_out_movmem (insn, operands, NULL); + } + [(set_attr "adjust_len" "movmem") + (set_attr "cc" "clobber")]) + +;; Ditto and +;; $8, $9 : hh8 (& src) +;; $10 : RAMPZ_ADDR + +;; "movmem_qi_elpm" +;; "movmem_hi_elpm" +(define_insn "movmem_<mode>_elpm" + [(set (mem:BLK (match_operand:HI 0 "register_operand" "x")) + (mem:BLK (lo_sum:PSI (match_operand:QI 8 "register_operand" "r") + (match_operand:HI 1 "register_operand" "z")))) + (unspec [(match_operand:QI 2 "const_int_operand" "n")] + UNSPEC_MOVMEM) + (use (match_operand:QIHI 3 "register_operand" "<MOVMEM_r_d>")) + (clobber (match_operand:HI 4 "register_operand" "=0")) + (clobber (match_operand:HI 5 "register_operand" "=1")) + (clobber (match_operand:QI 6 "register_operand" "=&r")) + (clobber (match_operand:QIHI 7 "register_operand" "=3")) + (clobber (match_operand:QI 9 "register_operand" "=8")) + (clobber (mem:QI (match_operand:QI 10 "io_address_operand" "n")))] + "" + { + return avr_out_movmem (insn, operands, NULL); + } + [(set_attr "adjust_len" "movmem") + (set_attr "cc" "clobber")]) - /* Work out branch probability for latter use. */ - prob = REG_BR_PROB_BASE - REG_BR_PROB_BASE / count; - - /* See if constant fit 8 bits. */ - mode = (count < 0x100) ? QImode : HImode; - /* Create loop counter register. */ - loop_reg = copy_to_mode_reg (mode, gen_int_mode (count, mode)); - - /* Now create RTL code for move loop. */ - /* Label at top of loop. */ - emit_label (label); - - /* Move one byte into scratch and inc pointer. */ - src = gen_rtx_MEM (QImode, addr1); - set_mem_addr_space (src, MEM_ADDR_SPACE (operands[1])); - emit_move_insn (tmp_reg_rtx, src); - emit_move_insn (addr1, gen_rtx_PLUS (Pmode, addr1, const1_rtx)); - - /* Move to mem and inc pointer. */ - emit_move_insn (gen_rtx_MEM (QImode, addr0), tmp_reg_rtx); - emit_move_insn (addr0, gen_rtx_PLUS (Pmode, addr0, const1_rtx)); - - /* Decrement count. */ - emit_move_insn (loop_reg, gen_rtx_PLUS (mode, loop_reg, constm1_rtx)); - - /* Compare with zero and jump if not equal. */ - emit_cmp_and_jump_insns (loop_reg, const0_rtx, NE, NULL_RTX, mode, 1, - label); - /* Set jump probability based on loop count. */ - jump = get_last_insn (); - add_reg_note (jump, REG_BR_PROB, GEN_INT (prob)); - DONE; -}") -;; =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 +;; =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 ;; memset (%0, %2, %1) (define_expand "setmemhi" [(parallel [(set (match_operand:BLK 0 "memory_operand" "") - (match_operand 2 "const_int_operand" "")) - (use (match_operand:HI 1 "const_int_operand" "")) - (use (match_operand:HI 3 "const_int_operand" "n")) - (clobber (match_scratch:HI 4 "")) - (clobber (match_dup 5))])] + (match_operand 2 "const_int_operand" "")) + (use (match_operand:HI 1 "const_int_operand" "")) + (use (match_operand:HI 3 "const_int_operand" "")) + (clobber (match_scratch:HI 4 "")) + (clobber (match_dup 5))])] "" - "{ - rtx addr0; - enum machine_mode mode; + { + rtx addr0; + enum machine_mode mode; - /* If value to set is not zero, use the library routine. */ - if (operands[2] != const0_rtx) - FAIL; + /* If value to set is not zero, use the library routine. */ + if (operands[2] != const0_rtx) + FAIL; - if (!CONST_INT_P (operands[1])) - FAIL; + if (!CONST_INT_P (operands[1])) + FAIL; + + mode = u8_operand (operands[1], VOIDmode) ? QImode : HImode; + operands[5] = gen_rtx_SCRATCH (mode); + operands[1] = copy_to_mode_reg (mode, + gen_int_mode (INTVAL (operands[1]), mode)); + addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); + operands[0] = gen_rtx_MEM (BLKmode, addr0); + }) - mode = u8_operand (operands[1], VOIDmode) ? QImode : HImode; - operands[5] = gen_rtx_SCRATCH (mode); - operands[1] = copy_to_mode_reg (mode, - gen_int_mode (INTVAL (operands[1]), mode)); - addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); - operands[0] = gen_rtx_MEM (BLKmode, addr0); -}") (define_insn "*clrmemqi" [(set (mem:BLK (match_operand:HI 0 "register_operand" "e")) - (const_int 0)) + (const_int 0)) (use (match_operand:QI 1 "register_operand" "r")) (use (match_operand:QI 2 "const_int_operand" "n")) (clobber (match_scratch:HI 3 "=0")) (clobber (match_scratch:QI 4 "=&1"))] "" - "st %a0+,__zero_reg__ - dec %1 - brne .-6" + "0:\;st %a0+,__zero_reg__\;dec %1\;brne 0b" [(set_attr "length" "3") (set_attr "cc" "clobber")]) + (define_insn "*clrmemhi" [(set (mem:BLK (match_operand:HI 0 "register_operand" "e,e")) - (const_int 0)) + (const_int 0)) (use (match_operand:HI 1 "register_operand" "!w,d")) (use (match_operand:HI 2 "const_int_operand" "n,n")) (clobber (match_scratch:HI 3 "=0,0")) (clobber (match_scratch:HI 4 "=&1,&1"))] "" - "*{ - if (which_alternative==0) - return (AS2 (st,%a0+,__zero_reg__) CR_TAB - AS2 (sbiw,%A1,1) CR_TAB - AS1 (brne,.-6)); - else - return (AS2 (st,%a0+,__zero_reg__) CR_TAB - AS2 (subi,%A1,1) CR_TAB - AS2 (sbci,%B1,0) CR_TAB - AS1 (brne,.-8)); -}" + "@ + 0:\;st %a0+,__zero_reg__\;sbiw %A1,1\;brne 0b + 0:\;st %a0+,__zero_reg__\;subi %A1,1\;sbci %B1,0\;brne 0b" [(set_attr "length" "3,4") (set_attr "cc" "clobber,clobber")]) (define_expand "strlenhi" - [(set (match_dup 4) - (unspec:HI [(match_operand:BLK 1 "memory_operand" "") - (match_operand:QI 2 "const_int_operand" "") - (match_operand:HI 3 "immediate_operand" "")] - UNSPEC_STRLEN)) - (set (match_dup 4) (plus:HI (match_dup 4) - (const_int -1))) - (set (match_operand:HI 0 "register_operand" "") - (minus:HI (match_dup 4) - (match_dup 5)))] - "" - "{ - rtx addr; - if (operands[2] != const0_rtx) - FAIL; - addr = copy_to_mode_reg (Pmode, XEXP (operands[1],0)); - operands[1] = gen_rtx_MEM (BLKmode, addr); - operands[5] = addr; - operands[4] = gen_reg_rtx (HImode); -}") + [(set (match_dup 4) + (unspec:HI [(match_operand:BLK 1 "memory_operand" "") + (match_operand:QI 2 "const_int_operand" "") + (match_operand:HI 3 "immediate_operand" "")] + UNSPEC_STRLEN)) + (set (match_dup 4) + (plus:HI (match_dup 4) + (const_int -1))) + (set (match_operand:HI 0 "register_operand" "") + (minus:HI (match_dup 4) + (match_dup 5)))] + "" + { + rtx addr; + if (operands[2] != const0_rtx) + FAIL; + addr = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); + operands[1] = gen_rtx_MEM (BLKmode, addr); + operands[5] = addr; + operands[4] = gen_reg_rtx (HImode); + }) (define_insn "*strlenhi" - [(set (match_operand:HI 0 "register_operand" "=e") - (unspec:HI [(mem:BLK (match_operand:HI 1 "register_operand" "%0")) - (const_int 0) - (match_operand:HI 2 "immediate_operand" "i")] - UNSPEC_STRLEN))] - "" - "ld __tmp_reg__,%a0+ - tst __tmp_reg__ - brne .-6" + [(set (match_operand:HI 0 "register_operand" "=e") + (unspec:HI [(mem:BLK (match_operand:HI 1 "register_operand" "0")) + (const_int 0) + (match_operand:HI 2 "immediate_operand" "i")] + UNSPEC_STRLEN))] + "" + "0:\;ld __tmp_reg__,%a0+\;tst __tmp_reg__\;brne 0b" [(set_attr "length" "3") (set_attr "cc" "clobber")]) diff --git a/gcc/config/avr/predicates.md b/gcc/config/avr/predicates.md index 9f595cb..fff34b5 100644 --- a/gcc/config/avr/predicates.md +++ b/gcc/config/avr/predicates.md @@ -63,10 +63,11 @@ (match_test "!avr_mem_pgm_p (op)"))) ;; Return 1 if OP is an "ordinary" general operand, i.e. a general -;; operand whose load is not handled by a libgcc call. +;; operand whose load is not handled by a libgcc call or ELPM. (define_predicate "nox_general_operand" (and (match_operand 0 "general_operand") - (match_test "!avr_load_libgcc_p (op)"))) + (not (match_test "avr_load_libgcc_p (op)")) + (not (match_test "avr_mem_pgmx_p (op)")))) ;; Return 1 if OP is the zero constant for MODE. (define_predicate "const0_operand" |