From 7c209481147555ddd22296b1eff0fd1833588d35 Mon Sep 17 00:00:00 2001 From: Georg-Johann Lay Date: Tue, 15 Nov 2011 09:01:46 +0000 Subject: re PR target/49868 (Implement named address space to place/access data in flash memory) gcc/ PR target/49868 * config/avr/avr.h (ADDR_SPACE_PGM): New address spaces. (REGISTER_TARGET_PRAGMAS): New define. * config/avr/avr-protos.h (avr_mem_pgm_p): New. (avr_load_libgcc_p): New. (asm_output_external_libcall): Remove. (avr_register_target_pragmas): New. (avr_log_t): Add field "progmem". Order alphabetically. * config/avr/avr-log.c (avr_log_set_avr_log): Set avr_log.progmem. * config/avr/avr-c.c (langhooks.h): New include. (avr_register_target_pragmas): New function. Register address space __pgm. (avr_cpu_cpp_builtins): Add built-in define __PGM. * config/avr/avr.c: Include "c-family/c-common.h". (TARGET_LEGITIMATE_ADDRESS_P): Remove define. (TARGET_LEGITIMIZE_ADDRESS): Remove define. (TARGET_ADDR_SPACE_SUBSET_P): Define to... (avr_addr_space_subset_p): ...this new static function. (TARGET_ADDR_SPACE_CONVERT): Define to... (avr_addr_space_convert): ...this new static function. (TARGET_ADDR_SPACE_ADDRESS_MODE): Define to... (avr_addr_space_address_mode): ...this new static function. (TARGET_ADDR_SPACE_POINTER_MODE): Define to... (avr_addr_space_pointer_mode): ...this new static function. (TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P): Define to... (avr_addr_space_legitimate_address_p): ...this new static function. (TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS): Define to... (avr_addr_space_legitimize_address): ...this new static function. (avr_mode_code_base_reg_class): Handle address spaces. (avr_regno_mode_code_ok_for_base_p): Ditto. (lpm_addr_reg_rtx, lpm_reg_rtx, all_regs_rtx): New static variables. (avr_option_override): Initialize them. (output_reload_in_const): Use all_regs_rtx. Fix signedness of loop variables. (avr_pgm_segment): New static function. (avr_decl_pgm_p, avr_mem_pgm_p): New static functions. (avr_out_lpm, avr_out_lpm_no_lpmx): New static functions. (output_movqi, output_movhi, output_movsisf, avr_out_movpsi): Call avr_out_lpm to handle loads from progmem. (avr_load_libgcc_p): New static function. (avr_progmem_p): Test if decl is in flash. (avr_pgm_pointer_const_p): New static function. (avr_nonconst_pointer_addrspace): New static function. (avr_pgm_check_var_decl): New static function. (avr_insert_attributes): Use it. Change error message to report cause (progmem or address space) when code wants to write to flash. (avr_section_type_flags): Unset section flag SECTION_BSS for data in progmem. * config/avr/predicates.md (nop_general_operand): New predicate. (nox_general_operand): New predicate. * config/avr/avr.md (LPM_REGNO): New define_constant. (load_libgcc): New expander. (*load..libgcc): New insn. (mov): Handle loads from non-generic AS. (movmemhi): Ditto. Propagate address space information to newly created MEM. (movqi_insn, *movhi, *movpsi, *movsi, *movsf): Change predicate #1 to nox_general_operand. (ashrqi3, ashrhi3, ashrsi3): Change predicate #1 to nop_general_operand. (ashlqi3, *ashlqi3, ashlhi3, ashlsi3): Ditto. (lshrqi3, *lshrqi3, lshrhi3, lshrsi3): Ditto. (split-lpmx): New split. (*ashlhi3_const, *ashlsi3_const, *ashrhi3_const, *ashrsi3_const, *lshrhi3_const, *lshrsi3_const): Indent, unquote C. libgcc/ PR target/49868 * config/avr/t-avr (LIB1ASMFUNCS): Add _load_3, _load_4. * config/avr/lib1funcs.S (__load_3, __load_4, __xload_2): New functions. From-SVN: r181378 --- gcc/ChangeLog | 69 +++++ gcc/config/avr/avr-c.c | 23 +- gcc/config/avr/avr-log.c | 9 +- gcc/config/avr/avr-protos.h | 11 +- gcc/config/avr/avr.c | 654 ++++++++++++++++++++++++++++++++++++++++-- gcc/config/avr/avr.h | 8 + gcc/config/avr/avr.md | 326 +++++++++++++++------ gcc/config/avr/predicates.md | 11 + libgcc/ChangeLog | 6 + libgcc/config/avr/lib1funcs.S | 46 +++ libgcc/config/avr/t-avr | 1 + 11 files changed, 1036 insertions(+), 128 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 01476a9..8e81d69 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,72 @@ +2011-11-15 Georg-Johann Lay + + PR target/49868 + * config/avr/avr.h (ADDR_SPACE_PGM): New address spaces. + (REGISTER_TARGET_PRAGMAS): New define. + * config/avr/avr-protos.h (avr_mem_pgm_p): New. + (avr_load_libgcc_p): New. + (avr_register_target_pragmas): New. + (asm_output_external_libcall): Remove. + (avr_log_t): Add field "progmem". Order alphabetically. + * config/avr/avr-log.c (avr_log_set_avr_log): Set avr_log.progmem. + * config/avr/avr-c.c (langhooks.h): New include. + (avr_register_target_pragmas): New function. Register address + space __pgm. + (avr_cpu_cpp_builtins): Add built-in define __PGM. + + * config/avr/avr.c: Include "c-family/c-common.h". + (TARGET_LEGITIMATE_ADDRESS_P): Remove define. + (TARGET_LEGITIMIZE_ADDRESS): Remove define. + (TARGET_ADDR_SPACE_SUBSET_P): Define to... + (avr_addr_space_subset_p): ...this new static function. + (TARGET_ADDR_SPACE_CONVERT): Define to... + (avr_addr_space_convert): ...this new static function. + (TARGET_ADDR_SPACE_ADDRESS_MODE): Define to... + (avr_addr_space_address_mode): ...this new static function. + (TARGET_ADDR_SPACE_POINTER_MODE): Define to... + (avr_addr_space_pointer_mode): ...this new static function. + (TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P): Define to... + (avr_addr_space_legitimate_address_p): ...this new static function. + (TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS): Define to... + (avr_addr_space_legitimize_address): ...this new static function. + (avr_mode_code_base_reg_class): Handle address spaces. + (avr_regno_mode_code_ok_for_base_p): Ditto. + (lpm_addr_reg_rtx, lpm_reg_rtx, all_regs_rtx): New static variables. + (avr_option_override): Initialize them. + (output_reload_in_const): Use all_regs_rtx. Fix signedness of loop + variables. + (avr_pgm_segment): New static function. + (avr_decl_pgm_p, avr_mem_pgm_p): New static functions. + (avr_out_lpm, avr_out_lpm_no_lpmx): New static functions. + (output_movqi, output_movhi, output_movsisf, avr_out_movpsi): Call + avr_out_lpm to handle loads from progmem. + (avr_load_libgcc_p): New static function. + (avr_progmem_p): Test if decl is in flash. + (avr_pgm_pointer_const_p): New static function. + (avr_nonconst_pointer_addrspace): New static function. + (avr_pgm_check_var_decl): New static function. + (avr_insert_attributes): Use it. Change error message to report + cause (progmem or address space) when code wants to write to flash. + (avr_section_type_flags): Unset section flag SECTION_BSS for + data in progmem. + + * config/avr/predicates.md (nop_general_operand): New predicate. + (nox_general_operand): New predicate. + * config/avr/avr.md (LPM_REGNO): New define_constant. + (load_libgcc): New expander. + (*load..libgcc): New insn. + (mov): Handle loads from non-generic AS. + (movmemhi): Ditto. Propagate address space information to newly + created MEM. + (movqi_insn, *movhi, *movpsi, *movsi, *movsf): Change predicate #1 + to nox_general_operand. + (ashrqi3, ashrhi3, ashrsi3): Change predicate #1 to nop_general_operand. + (ashlqi3, *ashlqi3, ashlhi3, ashlsi3): Ditto. + (lshrqi3, *lshrqi3, lshrhi3, lshrsi3): Ditto. + (split-lpmx): New split. + (*ashlhi3_const, *ashlsi3_const, *ashrhi3_const, *ashrsi3_const, + *lshrhi3_const, *lshrsi3_const): Indent, unquote C. + 2011-11-15 Maxim Kuvyrkov * ipa-cp.c (ipa_value_from_jfunc): Make global. diff --git a/gcc/config/avr/avr-c.c b/gcc/config/avr/avr-c.c index 55de2d7..63408c0 100644 --- a/gcc/config/avr/avr-c.c +++ b/gcc/config/avr/avr-c.c @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see . */ +/* Not included in avr.c since this requires C front end. */ #include "config.h" #include "system.h" @@ -27,8 +28,17 @@ #include "cpplib.h" #include "tree.h" #include "c-family/c-common.h" +#include "langhooks.h" + + +/* Implement `REGISTER_TARGET_PRAGMAS'. */ + +void +avr_register_target_pragmas (void) +{ + c_register_addr_space ("__pgm", ADDR_SPACE_PGM); +} -/* Not included in avr.c since this requires C front end. */ /* Worker function for TARGET_CPU_CPP_BUILTINS. */ @@ -90,6 +100,17 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile) cpp_define (pfile, "__AVR_ERRATA_SKIP_JMP_CALL__"); } + /* Define builtin macros so that the user can easily query if or if not + non-generic address spaces (and which) are supported. + This is only supported for C. For C++, a language extension is needed + (as mentioned in ISO/IEC DTR 18037; Annex F.2) which is not + implemented in GCC up to now. */ + + if (!strcmp (lang_hooks.name, "GNU C")) + { + cpp_define (pfile, "__PGM=__pgm"); + } + /* Define builtin macros so that the user can easily query if or if not a specific builtin is available. */ diff --git a/gcc/config/avr/avr-log.c b/gcc/config/avr/avr-log.c index cdeb669..2f6b0aa 100644 --- a/gcc/config/avr/avr-log.c +++ b/gcc/config/avr/avr-log.c @@ -59,6 +59,8 @@ F: caller (via __FUNCTION__) P: Pass name and number ?: Print caller, current function and pass info + !: Ditto, but only print if in a pass with static pass number, + else return. == same as printf == @@ -318,12 +320,13 @@ avr_log_set_avr_log (void) || NULL != strstr (str, "," #S ",") \ || NULL != strstr (str, ",all,")) - SET_DUMP_DETAIL (rtx_costs); + SET_DUMP_DETAIL (address_cost); + SET_DUMP_DETAIL (constraints); SET_DUMP_DETAIL (legitimate_address_p); SET_DUMP_DETAIL (legitimize_address); SET_DUMP_DETAIL (legitimize_reload_address); - SET_DUMP_DETAIL (constraints); - SET_DUMP_DETAIL (address_cost); + SET_DUMP_DETAIL (progmem); + SET_DUMP_DETAIL (rtx_costs); #undef SET_DUMP_DETAIL } diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index d351833..6c017e1 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -32,6 +32,7 @@ extern int avr_initial_elimination_offset (int from, int to); extern int avr_simple_epilogue (void); 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); #ifdef TREE_CODE @@ -47,7 +48,6 @@ extern void init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, #endif /* TREE_CODE */ #ifdef RTX_CODE -extern void asm_output_external_libcall (FILE *file, rtx symref); extern const char *output_movqi (rtx insn, rtx operands[], int *l); extern const char *output_movhi (rtx insn, rtx operands[], int *l); extern const char *out_movqi_r_mr (rtx insn, rtx op[], int *l); @@ -121,6 +121,8 @@ extern reg_class_t avr_mode_code_base_reg_class (enum machine_mode, addr_space_t extern bool avr_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_space_t, RTX_CODE, RTX_CODE); extern rtx avr_incoming_return_addr_rtx (void); extern rtx avr_legitimize_reload_address (rtx*, enum machine_mode, int, int, int, int, rtx (*)(rtx,int)); +extern bool avr_mem_pgm_p (rtx); +extern bool avr_load_libgcc_p (rtx); #endif /* RTX_CODE */ #ifdef REAL_VALUE_TYPE @@ -139,12 +141,13 @@ extern void avr_log_set_avr_log (void); typedef struct { - unsigned rtx_costs :1; + unsigned address_cost :1; + unsigned constraints :1; unsigned legitimate_address_p :1; unsigned legitimize_address :1; unsigned legitimize_reload_address :1; - unsigned constraints :1; - unsigned address_cost :1; + unsigned progmem :1; + unsigned rtx_costs :1; } avr_log_t; extern avr_log_t avr_log; diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index 65d43b8..f33c0c5 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -35,6 +35,7 @@ #include "tree.h" #include "output.h" #include "expr.h" +#include "c-family/c-common.h" #include "diagnostic-core.h" #include "obstack.h" #include "function.h" @@ -84,12 +85,21 @@ static bool avr_rtx_costs (rtx, int, int, int, int *, bool); /* Allocate registers from r25 to r8 for parameters for function calls. */ #define FIRST_CUM_REG 26 +/* Implicit target register of LPM instruction (R0) */ +static GTY(()) rtx lpm_reg_rtx; + +/* (Implicit) address register of LPM instruction (R31:R30 = Z) */ +static GTY(()) rtx lpm_addr_reg_rtx; + /* Temporary register RTX (gen_rtx_REG (QImode, TMP_REGNO)) */ static GTY(()) rtx tmp_reg_rtx; /* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */ static GTY(()) rtx zero_reg_rtx; +/* RTXs for all general purpose registers as QImode */ +static GTY(()) rtx all_regs_rtx[32]; + /* AVR register names {"r0", "r1", ..., "r31"} */ static const char *const avr_regnames[] = REGISTER_NAMES; @@ -172,9 +182,6 @@ bool avr_need_copy_data_p = false; #undef TARGET_FUNCTION_ARG_ADVANCE #define TARGET_FUNCTION_ARG_ADVANCE avr_function_arg_advance -#undef TARGET_LEGITIMIZE_ADDRESS -#define TARGET_LEGITIMIZE_ADDRESS avr_legitimize_address - #undef TARGET_RETURN_IN_MEMORY #define TARGET_RETURN_IN_MEMORY avr_return_in_memory @@ -189,9 +196,6 @@ bool avr_need_copy_data_p = false; #undef TARGET_CASE_VALUES_THRESHOLD #define TARGET_CASE_VALUES_THRESHOLD avr_case_values_threshold -#undef TARGET_LEGITIMATE_ADDRESS_P -#define TARGET_LEGITIMATE_ADDRESS_P avr_legitimate_address_p - #undef TARGET_FRAME_POINTER_REQUIRED #define TARGET_FRAME_POINTER_REQUIRED avr_frame_pointer_required_p #undef TARGET_CAN_ELIMINATE @@ -221,6 +225,24 @@ bool avr_need_copy_data_p = false; #undef TARGET_SCALAR_MODE_SUPPORTED_P #define TARGET_SCALAR_MODE_SUPPORTED_P avr_scalar_mode_supported_p +#undef TARGET_ADDR_SPACE_SUBSET_P +#define TARGET_ADDR_SPACE_SUBSET_P avr_addr_space_subset_p + +#undef TARGET_ADDR_SPACE_CONVERT +#define TARGET_ADDR_SPACE_CONVERT avr_addr_space_convert + +#undef TARGET_ADDR_SPACE_ADDRESS_MODE +#define TARGET_ADDR_SPACE_ADDRESS_MODE avr_addr_space_address_mode + +#undef TARGET_ADDR_SPACE_POINTER_MODE +#define TARGET_ADDR_SPACE_POINTER_MODE avr_addr_space_pointer_mode + +#undef TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P +#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P avr_addr_space_legitimate_address_p + +#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS +#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address + /* Custom function to replace string prefix. @@ -295,6 +317,8 @@ 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 @@ -323,8 +347,14 @@ avr_option_override (void) avr_current_arch = &avr_arch_types[avr_current_device->arch]; avr_extra_arch_macro = avr_current_device->macro; - tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO); - zero_reg_rtx = gen_rtx_REG (QImode, ZERO_REGNO); + 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; @@ -384,6 +414,28 @@ avr_scalar_mode_supported_p (enum machine_mode mode) } +/* 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; + + return !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (TREE_TYPE (decl))); +} + + +/* Return TRUE if X is a MEM rtx located in Flash and FALSE, otherwise. */ + +bool +avr_mem_pgm_p (rtx x) +{ + return (MEM_P (x) + && !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (x))); +} + + /* A helper for the subsequent function attribute used to dig for attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */ @@ -1379,6 +1431,9 @@ avr_legitimate_address_p (enum machine_mode mode, rtx x, bool strict) return ok; } + +/* Former implementation of TARGET_LEGITIMIZE_ADDRESS, + now only a helper for avr_addr_space_legitimize_address. */ /* Attempts to replace X with a valid memory address for an operand of mode MODE */ @@ -2177,6 +2232,231 @@ avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee) /*********************************************************************** Functions for outputting various mov's for a various modes ************************************************************************/ + +/* Return true if a value of mode MODE is read from flash by + __load_* function from libgcc. */ + +bool +avr_load_libgcc_p (rtx op) +{ + enum machine_mode mode = GET_MODE (op); + int n_bytes = GET_MODE_SIZE (mode); + + return (n_bytes > 2 + && !AVR_HAVE_LPMX + && avr_mem_pgm_p (op)); +} + + +/* Helper function for the next function in the case where only restricted + version of LPM instruction is available. */ + +static const char* +avr_out_lpm_no_lpmx (rtx insn, rtx *xop, int *plen) +{ + rtx dest = xop[0]; + rtx addr = xop[1]; + int n_bytes = GET_MODE_SIZE (GET_MODE (dest)); + int regno_dest; + + regno_dest = REGNO (dest); + + /* The implicit target register of LPM. */ + xop[3] = lpm_reg_rtx; + + switch (GET_CODE (addr)) + { + default: + gcc_unreachable(); + + case REG: + + gcc_assert (REG_Z == REGNO (addr)); + + switch (n_bytes) + { + default: + gcc_unreachable(); + + case 1: + return avr_asm_len ("lpm" CR_TAB + "mov %0,%3", xop, plen, 2); + + case 2: + if (REGNO (dest) == REG_Z) + return avr_asm_len ("lpm" CR_TAB + "push %3" CR_TAB + "adiw %2,1" CR_TAB + "lpm" 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); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,1", xop, plen, 1); + } + + break; /* 2 */ + } + + break; /* REG */ + + case POST_INC: + + gcc_assert (REG_Z == REGNO (XEXP (addr, 0)) + && n_bytes <= 2); + + avr_asm_len ("lpm" CR_TAB + "mov %A0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + if (n_bytes >= 2) + avr_asm_len ("lpm" CR_TAB + "mov %B0,%3" CR_TAB + "adiw %2,1", xop, plen, 3); + + break; /* POST_INC */ + + } /* switch CODE (addr) */ + + return ""; +} + + +/* If PLEN == NULL: Ouput instructions to load a value from a memory location + OP[1] in AS1 to register OP[0]. + If PLEN != 0 set *PLEN to the length in words of the instruction sequence. + Return "". */ + +static const char* +avr_out_lpm (rtx insn, rtx *op, int *plen) +{ + rtx xop[5]; + 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; + + if (plen) + *plen = 0; + + if (MEM_P (dest)) + { + warning (0, "writing to address space %qs not supported", + c_addr_space_name (MEM_ADDR_SPACE (dest))); + + return ""; + } + + addr = XEXP (src, 0); + + gcc_assert (!avr_load_libgcc_p (src) + && REG_P (dest) + && (REG_P (addr) || POST_INC == GET_CODE (addr))); + + xop[0] = dest; + xop[1] = addr; + xop[2] = lpm_addr_reg_rtx; + + regno_dest = REGNO (dest); + + if (!AVR_HAVE_LPMX) + { + return avr_out_lpm_no_lpmx (insn, xop, plen); + } + + switch (GET_CODE (addr)) + { + default: + gcc_unreachable(); + + case REG: + + gcc_assert (REG_Z == REGNO (addr)); + + switch (n_bytes) + { + default: + gcc_unreachable(); + + case 1: + return avr_asm_len ("lpm %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); + else + { + avr_asm_len ("lpm %A0,%a2+" CR_TAB + "lpm %B0,%a2", xop, plen, -2); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,1", xop, plen, 1); + } + + break; /* 2 */ + + case 3: + + avr_asm_len ("lpm %A0,%a2+" CR_TAB + "lpm %B0,%a2+" CR_TAB + "lpm %C0,%a2", xop, plen, -3); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,2", xop, plen, 1); + + break; /* 3 */ + + case 4: + + avr_asm_len ("lpm %A0,%a2+" CR_TAB + "lpm %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); + else + { + avr_asm_len ("lpm %C0,%a2+" CR_TAB + "lpm %D0,%a2", xop, plen, 2); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,3", xop, plen, 1); + } + + break; /* 4 */ + } /* n_bytes */ + + break; /* REG */ + + case POST_INC: + + 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); + + break; /* POST_INC */ + + } /* switch CODE (addr) */ + + return ""; +} + + const char * output_movqi (rtx insn, rtx operands[], int *l) { @@ -2185,6 +2465,12 @@ output_movqi (rtx insn, rtx operands[], int *l) rtx src = operands[1]; int *real_l = l; + if (avr_mem_pgm_p (src) + || avr_mem_pgm_p (dest)) + { + return avr_out_lpm (insn, operands, real_l); + } + if (!l) l = &dummy; @@ -2235,6 +2521,12 @@ output_movhi (rtx insn, rtx operands[], int *l) rtx src = operands[1]; int *real_l = l; + if (avr_mem_pgm_p (src) + || avr_mem_pgm_p (dest)) + { + return avr_out_lpm (insn, operands, real_l); + } + if (!l) l = &dummy; @@ -2848,6 +3140,12 @@ output_movsisf (rtx insn, rtx operands[], int *l) rtx src = operands[1]; int *real_l = l; + if (avr_mem_pgm_p (src) + || avr_mem_pgm_p (dest)) + { + return avr_out_lpm (insn, operands, real_l); + } + if (!l) l = &dummy; @@ -3167,6 +3465,12 @@ avr_out_movpsi (rtx insn, rtx *op, int *plen) rtx dest = op[0]; rtx src = op[1]; + if (avr_mem_pgm_p (src) + || avr_mem_pgm_p (dest)) + { + return avr_out_lpm (insn, op, plen); + } + if (register_operand (dest, VOIDmode)) { if (register_operand (src, VOIDmode)) /* mov r,r */ @@ -3764,9 +4068,10 @@ out_shift_with_cnt (const char *templ, rtx insn, rtx operands[], if (len) *len = 1; - if (GET_CODE (operands[2]) == CONST_INT) + if (CONST_INT_P (operands[2])) { - int scratch = (GET_CODE (PATTERN (insn)) == PARALLEL); + bool scratch = (GET_CODE (PATTERN (insn)) == PARALLEL + && REG_P (operands[3])); int count = INTVAL (operands[2]); int max_len = 10; /* If larger than this, always use a loop. */ @@ -3819,8 +4124,7 @@ out_shift_with_cnt (const char *templ, rtx insn, rtx operands[], /* No scratch register available, use one from LD_REGS (saved in __tmp_reg__) that doesn't overlap with registers to shift. */ - op[3] = gen_rtx_REG (QImode, - ((true_regnum (operands[0]) - 1) & 15) + 16); + op[3] = all_regs_rtx[((REGNO (operands[0]) - 1) & 15) + 16]; op[4] = tmp_reg_rtx; saved_in_tmp = 1; @@ -6208,8 +6512,15 @@ avr_attribute_table[] = { NULL, 0, 0, false, false, false, NULL, false } }; -/* Look for attribute `progmem' in DECL - if found return 1, otherwise 0. */ + +/* Look if DECL shall be placed in program memory space by + means of attribute `progmem' or some address-space qualifier. + 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 1 if DECL is located in 16-bit flash address-space + Return -1 if attribute `progmem' occurs in DECL or ATTRIBUTES + Return 0 otherwise */ int avr_progmem_p (tree decl, tree attributes) @@ -6219,11 +6530,15 @@ avr_progmem_p (tree decl, tree attributes) if (TREE_CODE (decl) != VAR_DECL) return 0; + if (avr_decl_pgm_p (decl)) + return 1; + if (NULL_TREE != lookup_attribute ("progmem", attributes)) - return 1; + return -1; - a=decl; + a = decl; + do a = TREE_TYPE(a); while (TREE_CODE (a) == ARRAY_TYPE); @@ -6232,16 +6547,123 @@ avr_progmem_p (tree decl, tree attributes) return 0; if (NULL_TREE != lookup_attribute ("progmem", TYPE_ATTRIBUTES (a))) - return 1; + return -1; return 0; } + +/* Scan type TYP for pointer references to address space ASn. + Return ADDR_SPACE_GENERIC (i.e. 0) if all pointers targeting + the AS are also declared to be CONST. + Otherwise, return the respective addres space, i.e. a value != 0. */ + +static addr_space_t +avr_nonconst_pointer_addrspace (tree typ) +{ + while (ARRAY_TYPE == TREE_CODE (typ)) + typ = TREE_TYPE (typ); + + if (POINTER_TYPE_P (typ)) + { + tree target = TREE_TYPE (typ); + + /* Pointer to function: Test the function's return type. */ + + if (FUNCTION_TYPE == TREE_CODE (target)) + return avr_nonconst_pointer_addrspace (TREE_TYPE (target)); + + /* "Ordinary" pointers... */ + + while (TREE_CODE (target) == ARRAY_TYPE) + target = TREE_TYPE (target); + + if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (target)) + && !TYPE_READONLY (target)) + { + /* Pointers to non-generic address space must be const. */ + + return TYPE_ADDR_SPACE (target); + } + + /* Scan pointer's target type. */ + + return avr_nonconst_pointer_addrspace (target); + } + + return ADDR_SPACE_GENERIC; +} + + +/* Sanity check NODE so that all pointers targeting address space AS1 + go along with CONST qualifier. Writing to this address space should + be detected and complained about as early as possible. */ + +static bool +avr_pgm_check_var_decl (tree node) +{ + const char *reason = NULL; + + addr_space_t as = ADDR_SPACE_GENERIC; + + gcc_assert (as == 0); + + if (avr_log.progmem) + avr_edump ("%?: %t\n", node); + + switch (TREE_CODE (node)) + { + default: + break; + + case VAR_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as) + reason = "variable"; + break; + + case PARM_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as) + reason = "function parameter"; + break; + + case FIELD_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (node)), as) + reason = "structure field"; + break; + + case FUNCTION_DECL: + if (as = avr_nonconst_pointer_addrspace (TREE_TYPE (TREE_TYPE (node))), + as) + reason = "return type of function"; + break; + + case POINTER_TYPE: + if (as = avr_nonconst_pointer_addrspace (node), as) + reason = "pointer"; + break; + } + + if (reason) + { + if (TYPE_P (node)) + error ("pointer targeting address space %qs must be const in %qT", + c_addr_space_name (as), node); + else + error ("pointer targeting address space %qs must be const in %s %q+D", + c_addr_space_name (as), reason, node); + } + + return reason == NULL; +} + + /* Add the section attribute if the variable is in progmem. */ static void avr_insert_attributes (tree node, tree *attributes) { + avr_pgm_check_var_decl (node); + if (TREE_CODE (node) == VAR_DECL && (TREE_STATIC (node) || DECL_EXTERNAL (node)) && avr_progmem_p (node, *attributes)) @@ -6258,11 +6680,20 @@ avr_insert_attributes (tree node, tree *attributes) if (error_mark_node == node0) return; - if (!TYPE_READONLY (node0)) + if (!TYPE_READONLY (node0) + && !TREE_READONLY (node)) { + addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (node)); + const char *reason = "__attribute__((progmem))"; + + if (!ADDR_SPACE_GENERIC_P (as)) + reason = c_addr_space_name (as); + + if (avr_log.progmem) + avr_edump ("\n%?: %t\n%t\n", node, node0); + error ("variable %q+D must be const in order to be put into" - " read-only section by means of %<__attribute__((progmem))%>", - node); + " read-only section by means of %qs", node, reason); } } } @@ -6462,6 +6893,7 @@ avr_section_type_flags (tree decl, const char *name, int reloc) && avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) { flags &= ~SECTION_WRITE; + flags &= ~SECTION_BSS; flags |= AVR_SECTION_PROGMEM; } @@ -8049,10 +8481,14 @@ avr_hard_regno_mode_ok (int regno, enum machine_mode mode) reg_class_t avr_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED, - addr_space_t as ATTRIBUTE_UNUSED, - RTX_CODE outer_code, + addr_space_t as, RTX_CODE outer_code, RTX_CODE index_code ATTRIBUTE_UNUSED) { + if (!ADDR_SPACE_GENERIC_P (as)) + { + return POINTER_Z_REGS; + } + if (!avr_strict_X) return reload_completed ? BASE_POINTER_REGS : POINTER_REGS; @@ -8071,6 +8507,27 @@ avr_regno_mode_code_ok_for_base_p (int regno, { bool ok = false; + if (!ADDR_SPACE_GENERIC_P (as)) + { + if (regno < FIRST_PSEUDO_REGISTER + && regno == REG_Z) + { + return true; + } + + if (reg_renumber) + { + regno = reg_renumber[regno]; + + if (regno == REG_Z) + { + return true; + } + } + + return false; + } + if (regno < FIRST_PSEUDO_REGISTER && (regno == REG_X || regno == REG_Y @@ -8122,9 +8579,8 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) int clobber_val = 1234; bool cooked_clobber_p = false; bool set_p = false; - unsigned int n; enum machine_mode mode = GET_MODE (dest); - int n_bytes = GET_MODE_SIZE (mode); + int n, n_bytes = GET_MODE_SIZE (mode); gcc_assert (REG_P (dest) && CONSTANT_P (src)); @@ -8138,7 +8594,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) if (REGNO (dest) < 16 && REGNO (dest) + GET_MODE_SIZE (mode) > 16) { - clobber_reg = gen_rtx_REG (QImode, REGNO (dest) + n_bytes - 1); + clobber_reg = all_regs_rtx[REGNO (dest) + n_bytes - 1]; } /* We might need a clobber reg but don't have one. Look at the value to @@ -8155,7 +8611,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) That's cheaper than loading from constant pool. */ cooked_clobber_p = true; - clobber_reg = gen_rtx_REG (QImode, REG_Z + 1); + clobber_reg = all_regs_rtx[REG_Z + 1]; avr_asm_len ("mov __tmp_reg__,%0", &clobber_reg, len, 1); } @@ -8165,7 +8621,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) { int ldreg_p; bool done_byte = false; - unsigned int j; + int j; rtx xop[3]; /* Crop the n-th destination byte. */ @@ -8595,6 +9051,150 @@ avr_case_values_threshold (void) return (!AVR_HAVE_JMP_CALL || TARGET_CALL_PROLOGUES) ? 8 : 17; } + +/* Implement `TARGET_ADDR_SPACE_ADDRESS_MODE'. */ + +static enum machine_mode +avr_addr_space_address_mode (addr_space_t as ATTRIBUTE_UNUSED) +{ + return HImode; +} + + +/* Implement `TARGET_ADDR_SPACE_POINTER_MODE'. */ + +static enum machine_mode +avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED) +{ + return HImode; +} + + +/* Helper for following function. */ + +static bool +avr_reg_ok_for_pgm_addr (rtx reg, bool strict) +{ + gcc_assert (REG_P (reg)); + + if (strict) + { + return REGNO (reg) == REG_Z; + } + + /* Avoid combine to propagate hard regs. */ + + if (can_create_pseudo_p() + && REGNO (reg) < REG_Z) + { + return false; + } + + return true; +} + + +/* Implement `TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P'. */ + +static bool +avr_addr_space_legitimate_address_p (enum machine_mode mode, rtx x, + bool strict, addr_space_t as) +{ + bool ok = false; + + switch (as) + { + default: + gcc_unreachable(); + + case ADDR_SPACE_GENERIC: + return avr_legitimate_address_p (mode, x, strict); + + case ADDR_SPACE_PGM: + + switch (GET_CODE (x)) + { + case REG: + ok = avr_reg_ok_for_pgm_addr (x, strict); + break; + + case POST_INC: + ok = (!avr_load_libgcc_p (x) + && avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict)); + break; + + default: + break; + } + + break; /* PGM */ + } + + if (avr_log.legitimate_address_p) + { + avr_edump ("\n%?: ret=%b, mode=%m strict=%d " + "reload_completed=%d reload_in_progress=%d %s:", + ok, mode, strict, reload_completed, reload_in_progress, + reg_renumber ? "(reg_renumber)" : ""); + + if (GET_CODE (x) == PLUS + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1)) + && IN_RANGE (INTVAL (XEXP (x, 1)), 0, MAX_LD_OFFSET (mode)) + && reg_renumber) + { + avr_edump ("(r%d ---> r%d)", REGNO (XEXP (x, 0)), + true_regnum (XEXP (x, 0))); + } + + avr_edump ("\n%r\n", x); + } + + return ok; +} + + +/* Implement `TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS'. */ + +static rtx +avr_addr_space_legitimize_address (rtx x, rtx old_x, + enum machine_mode mode, addr_space_t as) +{ + if (ADDR_SPACE_GENERIC_P (as)) + return avr_legitimize_address (x, old_x, mode); + + if (avr_log.legitimize_address) + { + avr_edump ("\n%?: mode=%m\n %r\n", mode, old_x); + } + + return old_x; +} + + +/* Implement `TARGET_ADDR_SPACE_CONVERT'. */ + +static rtx +avr_addr_space_convert (rtx src, tree type_from, tree type_to) +{ + if (avr_log.progmem) + avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n", + src, type_from, type_to); + + return src; +} + + +/* 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) +{ + return true; +} + + /* Helper for __builtin_avr_delay_cycles */ static void diff --git a/gcc/config/avr/avr.h b/gcc/config/avr/avr.h index 7a3bc73..24a687b 100644 --- a/gcc/config/avr/avr.h +++ b/gcc/config/avr/avr.h @@ -391,6 +391,14 @@ typedef struct avr_args { #define NO_FUNCTION_CSE + +#define ADDR_SPACE_PGM 1 + +#define REGISTER_TARGET_PRAGMAS() \ + do { \ + avr_register_target_pragmas(); \ + } while (0) + #define TEXT_SECTION_ASM_OP "\t.text" #define DATA_SECTION_ASM_OP "\t.data" diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 5933010..78ffe86 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -42,6 +42,7 @@ (REG_Z 30) (REG_W 24) (REG_SP 32) + (LPM_REGNO 0) ; implicit target register of LPM (TMP_REGNO 0) ; temporary register r0 (ZERO_REGNO 1) ; zero register r1 @@ -315,6 +316,39 @@ "") ;;======================================================================== +;; Move stuff around + +(define_expand "load_libgcc" + [(set (match_dup 3) + (match_dup 2)) + (set (reg:MOVMODE 22) + (match_operand:MOVMODE 1 "memory_operand" "")) + (set (match_operand:MOVMODE 0 "register_operand" "") + (reg:MOVMODE 22))] + "avr_load_libgcc_p (operands[1])" + { + operands[3] = gen_rtx_REG (HImode, REG_Z); + operands[2] = force_operand (XEXP (operands[1], 0), NULL_RTX); + operands[1] = replace_equiv_address (operands[1], operands[3]); + set_mem_addr_space (operands[1], ADDR_SPACE_PGM); + }) + +(define_insn "*load..libgcc" + [(set (reg:MOVMODE 22) + (match_operand:MOVMODE 0 "memory_operand" "m,m"))] + "avr_load_libgcc_p (operands[0]) + && REG_P (XEXP (operands[0], 0)) + && REG_Z == REGNO (XEXP (operands[0], 0))" + { + operands[0] = GEN_INT (GET_MODE_SIZE (mode)); + return "%~call __load_%0"; + } + [(set_attr "length" "1,2") + (set_attr "isa" "rjmp,jmp") + (set_attr "cc" "clobber")]) + + +;; General move expanders ;; "movqi" ;; "movhi" @@ -327,12 +361,25 @@ (match_operand:MOVMODE 1 "general_operand" ""))] "" { - /* One of the ops has to be in a register. */ - if (!register_operand (operands[0], mode) - && !(register_operand (operands[1], mode) - || CONST0_RTX (mode) == operands[1])) + rtx dest = operands[0]; + rtx src = operands[1]; + + if (avr_mem_pgm_p (dest)) + DONE; + + /* One of the operands has to be in a register. */ + if (!register_operand (dest, mode) + && !(register_operand (src, mode) + || src == CONST0_RTX (mode))) + { + operands[1] = src = copy_to_mode_reg (mode, src); + } + + if (avr_load_libgcc_p (src)) { - operands[1] = copy_to_mode_reg (mode, operands[1]); + /* For the small devices, do loads per libgcc call. */ + emit_insn (gen_load_libgcc (dest, src)); + DONE; } }) @@ -346,11 +393,14 @@ ;; so this may still be a win for registers live across function calls. (define_insn "movqi_insn" - [(set (match_operand:QI 0 "nonimmediate_operand" "=r,d,Qm,r,q,r,*r") - (match_operand:QI 1 "general_operand" "rL,i,rL,Qm,r,q,i"))] - "(register_operand (operands[0],QImode) - || register_operand (operands[1], QImode) || const0_rtx == operands[1])" - "* return output_movqi (insn, operands, NULL);" + [(set (match_operand:QI 0 "nonimmediate_operand" "=r ,d,Qm,r ,q,r,*r") + (match_operand:QI 1 "nox_general_operand" "rL,i,rL,Qm,r,q,i"))] + "register_operand (operands[0], QImode) + || register_operand (operands[1], QImode) + || const0_rtx == operands[1]" + { + return output_movqi (insn, operands, NULL); + } [(set_attr "length" "1,1,5,5,1,1,4") (set_attr "adjust_len" "mov8") (set_attr "cc" "none,none,clobber,clobber,none,none,clobber")]) @@ -425,11 +475,14 @@ (set_attr "cc" "none")]) (define_insn "*movhi" - [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m,d,*r,q,r") - (match_operand:HI 1 "general_operand" "r,L,m,rL,i,i,r,q"))] - "(register_operand (operands[0],HImode) - || register_operand (operands[1],HImode) || const0_rtx == operands[1])" - "* return output_movhi (insn, operands, NULL);" + [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,m ,d,*r,q,r") + (match_operand:HI 1 "nox_general_operand" "r,L,m,rL,i,i ,r,q"))] + "register_operand (operands[0], HImode) + || register_operand (operands[1], HImode) + || const0_rtx == operands[1]" + { + return output_movhi (insn, operands, NULL); + } [(set_attr "length" "2,2,6,7,2,6,5,2") (set_attr "adjust_len" "mov16") (set_attr "cc" "none,clobber,clobber,clobber,none,clobber,none,none")]) @@ -462,6 +515,40 @@ operands[5] = gen_rtx_REG (HImode, REGNO (operands[3])); }) +;; For LPM loads from AS1 we split +;; R = *Z +;; to +;; R = *Z++ +;; Z = Z - sizeof (R) +;; +;; so that the second instruction can be optimized out. + +(define_split ; "split-lpmx" + [(set (match_operand:HISI 0 "register_operand" "") + (match_operand:HISI 1 "memory_operand" ""))] + "reload_completed + && AVR_HAVE_LPMX" + [(set (match_dup 0) + (match_dup 2)) + (set (match_dup 3) + (plus:HI (match_dup 3) + (match_dup 4)))] + { + rtx addr = XEXP (operands[1], 0); + + if (!avr_mem_pgm_p (operands[1]) + || !REG_P (addr) + || reg_overlap_mentioned_p (addr, operands[0])) + { + FAIL; + } + + operands[2] = replace_equiv_address (operands[1], + gen_rtx_POST_INC (Pmode, addr)); + operands[3] = addr; + operands[4] = gen_int_mode (-GET_MODE_SIZE (mode), HImode); + }) + ;;========================================================================== ;; xpointer move (24 bit) @@ -492,7 +579,7 @@ (define_insn "*movpsi" [(set (match_operand:PSI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r") - (match_operand:PSI 1 "general_operand" "r,L,Qm,rL,i ,i"))] + (match_operand:PSI 1 "nox_general_operand" "r,L,Qm,rL,i ,i"))] "register_operand (operands[0], PSImode) || register_operand (operands[1], PSImode) || const0_rtx == operands[1]" @@ -532,10 +619,11 @@ (define_insn "*movsi" - [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r") - (match_operand:SI 1 "general_operand" "r,L,Qm,rL,i,i"))] - "(register_operand (operands[0],SImode) - || register_operand (operands[1],SImode) || const0_rtx == operands[1])" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r") + (match_operand:SI 1 "nox_general_operand" "r,L,Qm,rL,i ,i"))] + "register_operand (operands[0], SImode) + || register_operand (operands[1], SImode) + || const0_rtx == operands[1]" { return output_movsisf (insn, operands, NULL); } @@ -547,8 +635,8 @@ ;; move floating point numbers (32 bit) (define_insn "*movsf" - [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r,Qm,!d,r") - (match_operand:SF 1 "general_operand" "r,G,Qm,rG,F,F"))] + [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r ,Qm,!d,r") + (match_operand:SF 1 "nox_general_operand" "r,G,Qm,rG,F ,F"))] "register_operand (operands[0], SFmode) || register_operand (operands[1], SFmode) || operands[1] == CONST0_RTX (SFmode)" @@ -599,7 +687,7 @@ enum machine_mode mode; rtx label = gen_label_rtx (); rtx loop_reg; - rtx jump; + rtx jump, src; /* Copy pointers into new psuedos - they will be changed. */ rtx addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); @@ -608,6 +696,9 @@ /* 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) FAIL; @@ -628,7 +719,9 @@ emit_label (label); /* Move one byte into scratch and inc pointer. */ - emit_move_insn (tmp_reg_rtx, gen_rtx_MEM (QImode, addr1)); + 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. */ @@ -2868,10 +2961,8 @@ (define_expand "ashlqi3" [(set (match_operand:QI 0 "register_operand" "") - (ashift:QI (match_operand:QI 1 "register_operand" "") - (match_operand:QI 2 "general_operand" "")))] - "" - "") + (ashift:QI (match_operand:QI 1 "register_operand" "") + (match_operand:QI 2 "nop_general_operand" "")))]) (define_split ; ashlqi3_const4 [(set (match_operand:QI 0 "d_register_operand" "") @@ -2903,21 +2994,25 @@ "") (define_insn "*ashlqi3" - [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r") - (ashift:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,K,n,n,Qm")))] + [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r") + (ashift:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))] "" - "* return ashlqi3_out (insn, operands, NULL);" + { + return ashlqi3_out (insn, operands, NULL); + } [(set_attr "length" "5,0,1,2,4,6,9") (set_attr "adjust_len" "ashlqi") (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")]) (define_insn "ashlhi3" - [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") - (ashift:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") + (ashift:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return ashlhi3_out (insn, operands, NULL);" + { + return ashlhi3_out (insn, operands, NULL); + } [(set_attr "length" "6,0,2,2,4,10,10") (set_attr "adjust_len" "ashlhi") (set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")]) @@ -3005,11 +3100,13 @@ (define_insn "ashlsi3" - [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") - (ashift:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") + (ashift:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return ashlsi3_out (insn, operands, NULL);" + { + return ashlsi3_out (insn, operands, NULL); + } [(set_attr "length" "8,0,4,4,8,10,12") (set_attr "adjust_len" "ashlsi") (set_attr "cc" "clobber,none,set_n,clobber,set_n,clobber,clobber")]) @@ -3063,11 +3160,13 @@ (define_insn "*ashlhi3_const" [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r") - (ashift:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + (ashift:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] "reload_completed" - "* return ashlhi3_out (insn, operands, NULL);" + { + return ashlhi3_out (insn, operands, NULL); + } [(set_attr "length" "0,2,2,4,10") (set_attr "adjust_len" "ashlhi") (set_attr "cc" "none,set_n,clobber,set_n,clobber")]) @@ -3084,11 +3183,13 @@ (define_insn "*ashlsi3_const" [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") - (ashift:SI (match_operand:SI 1 "register_operand" "0,0,r,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + (ashift:SI (match_operand:SI 1 "register_operand" "0,0,r,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] "reload_completed" - "* return ashlsi3_out (insn, operands, NULL);" + { + return ashlsi3_out (insn, operands, NULL); + } [(set_attr "length" "0,4,4,10") (set_attr "adjust_len" "ashlsi") (set_attr "cc" "none,set_n,clobber,clobber")]) @@ -3109,21 +3210,25 @@ ;; arithmetic shift right (define_insn "ashrqi3" - [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r ,r ,r") - (ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0 ,0") - (match_operand:QI 2 "general_operand" "r,L,P,K,C03 C04 C05,C06 C07,Qm")))] + [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r ,r ,r") + (ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0 ,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,C03 C04 C05,C06 C07,Qm")))] "" - "* return ashrqi3_out (insn, operands, NULL);" + { + return ashrqi3_out (insn, operands, NULL); + } [(set_attr "length" "5,0,1,2,5,4,9") (set_attr "adjust_len" "ashrqi") (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,clobber,clobber")]) (define_insn "ashrhi3" - [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") - (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") + (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return ashrhi3_out (insn, operands, NULL);" + { + return ashrhi3_out (insn, operands, NULL); + } [(set_attr "length" "6,0,2,4,4,10,10") (set_attr "adjust_len" "ashrhi") (set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")]) @@ -3141,11 +3246,13 @@ (set_attr "cc" "clobber")]) (define_insn "ashrsi3" - [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") - (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return ashrsi3_out (insn, operands, NULL);" + { + return ashrsi3_out (insn, operands, NULL); + } [(set_attr "length" "8,0,4,6,8,10,12") (set_attr "adjust_len" "ashrsi") (set_attr "cc" "clobber,none,clobber,set_n,clobber,clobber,clobber")]) @@ -3164,11 +3271,13 @@ (define_insn "*ashrhi3_const" [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r") - (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + (ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] "reload_completed" - "* return ashrhi3_out (insn, operands, NULL);" + { + return ashrhi3_out (insn, operands, NULL); + } [(set_attr "length" "0,2,4,4,10") (set_attr "adjust_len" "ashrhi") (set_attr "cc" "none,clobber,set_n,clobber,clobber")]) @@ -3185,11 +3294,13 @@ (define_insn "*ashrsi3_const" [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") - (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] "reload_completed" - "* return ashrsi3_out (insn, operands, NULL);" + { + return ashrsi3_out (insn, operands, NULL); + } [(set_attr "length" "0,4,4,10") (set_attr "adjust_len" "ashrsi") (set_attr "cc" "none,clobber,set_n,clobber")]) @@ -3198,11 +3309,9 @@ ;; logical shift right (define_expand "lshrqi3" - [(set (match_operand:QI 0 "register_operand" "") - (lshiftrt:QI (match_operand:QI 1 "register_operand" "") - (match_operand:QI 2 "general_operand" "")))] - "" - "") + [(set (match_operand:QI 0 "register_operand" "") + (lshiftrt:QI (match_operand:QI 1 "register_operand" "") + (match_operand:QI 2 "nop_general_operand" "")))]) (define_split ; lshrqi3_const4 [(set (match_operand:QI 0 "d_register_operand" "") @@ -3234,21 +3343,25 @@ "") (define_insn "*lshrqi3" - [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r") - (lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,K,n,n,Qm")))] + [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,!d,r,r") + (lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0 ,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,K,n ,n,Qm")))] "" - "* return lshrqi3_out (insn, operands, NULL);" + { + return lshrqi3_out (insn, operands, NULL); + } [(set_attr "length" "5,0,1,2,4,6,9") (set_attr "adjust_len" "lshrqi") (set_attr "cc" "clobber,none,set_czn,set_czn,set_czn,set_czn,clobber")]) (define_insn "lshrhi3" - [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") - (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") + (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return lshrhi3_out (insn, operands, NULL);" + { + return lshrhi3_out (insn, operands, NULL); + } [(set_attr "length" "6,0,2,2,4,10,10") (set_attr "adjust_len" "lshrhi") (set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")]) @@ -3266,11 +3379,13 @@ (set_attr "cc" "clobber")]) (define_insn "lshrsi3" - [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") - (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") - (match_operand:QI 2 "general_operand" "r,L,P,O,K,n,Qm")))] + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,0,r,0,0,0") + (match_operand:QI 2 "nop_general_operand" "r,L,P,O,K,n,Qm")))] "" - "* return lshrsi3_out (insn, operands, NULL);" + { + return lshrsi3_out (insn, operands, NULL); + } [(set_attr "length" "8,0,4,4,8,10,12") (set_attr "adjust_len" "lshrsi") (set_attr "cc" "clobber,none,clobber,clobber,clobber,clobber,clobber")]) @@ -3324,11 +3439,13 @@ (define_insn "*lshrhi3_const" [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r") - (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] + (lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0,r,0,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,K,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,X,&d"))] "reload_completed" - "* return lshrhi3_out (insn, operands, NULL);" + { + return lshrhi3_out (insn, operands, NULL); + } [(set_attr "length" "0,2,2,4,10") (set_attr "adjust_len" "lshrhi") (set_attr "cc" "none,clobber,clobber,clobber,clobber")]) @@ -3345,11 +3462,13 @@ (define_insn "*lshrsi3_const" [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") - (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0") - (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) - (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r,0") + (match_operand:QI 2 "const_int_operand" "L,P,O,n"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] "reload_completed" - "* return lshrsi3_out (insn, operands, NULL);" + { + return lshrsi3_out (insn, operands, NULL); + } [(set_attr "length" "0,4,4,10") (set_attr "adjust_len" "lshrsi") (set_attr "cc" "none,clobber,clobber,clobber")]) @@ -3615,6 +3734,27 @@ operands[3] = simplify_gen_subreg (QImode, operands[0], PSImode, 2); }) +(define_insn_and_split "n_extendhipsi2" + [(set (match_operand:PSI 0 "register_operand" "=r,r,d,r") + (lo_sum:PSI (match_operand:QI 1 "const_int_operand" "L,P,n,n") + (match_operand:HI 2 "register_operand" "r,r,r,r"))) + (clobber (match_scratch:QI 3 "=X,X,X,&d"))] + "" + "#" + "reload_completed" + [(set (match_dup 4) (match_dup 2)) + (set (match_dup 3) (match_dup 6)) + ; no-op move in the case where no scratch is needed + (set (match_dup 5) (match_dup 3))] + { + operands[4] = simplify_gen_subreg (HImode, operands[0], PSImode, 0); + operands[5] = simplify_gen_subreg (QImode, operands[0], PSImode, 2); + operands[6] = operands[1]; + + if (GET_CODE (operands[3]) == SCRATCH) + operands[3] = operands[5]; + }) + (define_insn_and_split "zero_extendhisi2" [(set (match_operand:SI 0 "register_operand" "=r") (zero_extend:SI (match_operand:HI 1 "combine_pseudo_register_operand" "r")))] diff --git a/gcc/config/avr/predicates.md b/gcc/config/avr/predicates.md index 55a25b8..9f595cb 100644 --- a/gcc/config/avr/predicates.md +++ b/gcc/config/avr/predicates.md @@ -57,6 +57,17 @@ (and (match_code "const_int") (match_test "IN_RANGE((INTVAL (op)), 0x20, (0x60 - GET_MODE_SIZE(mode)))"))) +;; Return 1 if OP is a general operand not in program memory +(define_predicate "nop_general_operand" + (and (match_operand 0 "general_operand") + (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. +(define_predicate "nox_general_operand" + (and (match_operand 0 "general_operand") + (match_test "!avr_load_libgcc_p (op)"))) + ;; Return 1 if OP is the zero constant for MODE. (define_predicate "const0_operand" (and (match_code "const_int,const_double") diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog index c10667b..c4bb2f2 100644 --- a/libgcc/ChangeLog +++ b/libgcc/ChangeLog @@ -1,3 +1,9 @@ +2011-11-15 Georg-Johann Lay + + PR target/49868 + * config/avr/t-avr (LIB1ASMFUNCS): Add _load_3, _load_4. + * config/avr/lib1funcs.S (__load_3, __load_4, __xload_2): New functions. + 2011-11-13 John David Anglin * config.host (hppa*64*-*-hpux11*): Remove pa/t-stublib64 from diff --git a/libgcc/config/avr/lib1funcs.S b/libgcc/config/avr/lib1funcs.S index f7a8f63..573761b 100644 --- a/libgcc/config/avr/lib1funcs.S +++ b/libgcc/config/avr/lib1funcs.S @@ -1181,6 +1181,52 @@ DEFUN __tablejump_elpm__ ENDF __tablejump_elpm__ #endif /* defined (L_tablejump_elpm) */ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Loading n bytes from Flash; n = 3,4 +;; R22... = Flash[Z] +;; Clobbers: __tmp_reg__ + +#if (defined (L_load_3) \ + || defined (L_load_4)) \ + && !defined (__AVR_HAVE_LPMX__) + +;; Destination +#define D0 22 +#define D1 D0+1 +#define D2 D0+2 +#define D3 D0+3 + +.macro .load dest, n + lpm + mov \dest, r0 +.if \dest != D0+\n-1 + adiw r30, 1 +.else + sbiw r30, \n-1 +.endif +.endm + +#if defined (L_load_3) +DEFUN __load_3 + push D3 + XCALL __load_4 + pop D3 + ret +ENDF __load_3 +#endif /* L_load_3 */ + +#if defined (L_load_4) +DEFUN __load_4 + .load D0, 4 + .load D1, 4 + .load D2, 4 + .load D3, 4 + ret +ENDF __load_4 +#endif /* L_load_4 */ + +#endif /* L_load_3 || L_load_3 */ + .section .text.libgcc.builtins, "ax", @progbits diff --git a/libgcc/config/avr/t-avr b/libgcc/config/avr/t-avr index cd529ae..486214c 100644 --- a/libgcc/config/avr/t-avr +++ b/libgcc/config/avr/t-avr @@ -21,6 +21,7 @@ LIB1ASMFUNCS = \ _cleanup \ _tablejump \ _tablejump_elpm \ + _load_3 _load_4 \ _copy_data \ _clear_bss \ _ctors \ -- cgit v1.1