From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5631 invoked by alias); 6 Nov 2011 18:44:16 -0000 Received: (qmail 5596 invoked by uid 22791); 6 Nov 2011 18:44:07 -0000 X-SWARE-Spam-Status: No, hits=0.1 required=5.0 tests=AWL,BAYES_50,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,RCVD_IN_DNSWL_NONE,TW_GM,TW_HR,TW_LQ,TW_OV,TW_PG,TW_RQ,TW_VH,TW_VQ X-Spam-Check-By: sourceware.org Received: from mo-p00-ob.rzone.de (HELO mo-p00-ob.rzone.de) (81.169.146.162) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sun, 06 Nov 2011 18:43:45 +0000 X-RZG-AUTH: :LXoWVUeid/7A29J/hMvvT2k715jHQaJercGObUOFkj18odoYNahU4Q== X-RZG-CLASS-ID: mo00 Received: from [192.168.0.22] (business-188-111-022-002.static.arcor-ip.net [188.111.22.2]) by smtp.strato.de (fruni mo59) (RZmta 26.10 AUTH) with ESMTPA id g03d75nA6IHjsV ; Sun, 6 Nov 2011 19:39:09 +0100 (MET) Message-ID: <4EB6D44C.2020609@gjlay.de> Date: Sun, 06 Nov 2011 18:53:00 -0000 From: Georg-Johann Lay User-Agent: Thunderbird 2.0.0.24 (X11/20100302) MIME-Version: 1.0 To: Denis Chertykov CC: gcc-patches@gcc.gnu.org, Ulrich Weigand , Eric Weddington , Joerg Wunsch , Anatoly Sokolov Subject: Re: [Patch]: PR49868: Named address space support for AVR References: <4E8DC2C0.8040703@gjlay.de> <4EAA8450.9080207@gjlay.de> In-Reply-To: Content-Type: multipart/mixed; boundary="------------090403020503090108020802" X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org X-SW-Source: 2011-11/txt/msg00809.txt.bz2 This is a multi-part message in MIME format. --------------090403020503090108020802 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit Content-length: 11213 Denis Chertykov wrote: > 2011/10/28 Georg-Johann Lay : >> Georg-Johann Lay schrieb: >> >>> This patch adds named address space support to read data from flash (aka. >>> progmem) to target AVR. >>> >>> The patch has two parts: >>> >>> The first part is a repost of Ulrich's work from >>> http://gcc.gnu.org/ml/gcc/2011-08/msg00131.html >>> with the needed changes to ./gcc and ./gcc/doc >>> >>> This patch is needed because the target hooks MODE_CODE_BASE_REG_CLASS and >>> REGNO_MODE_CODE_OK_FOR_BASE_P don't distinguish between different address >>> spaces. Ulrich's patch adds respective support to these hooks. >>> >>> The second part is the AVR dependent part that adds __pgm as address space >>> qualifier for address space AS1. >>> >>> The AVR part is just the worker code. If there is agreement that AS support >>> for AVR is okay in principle and Ulrich's work will go into GCC, I will supply >>> test programs and updates to the user manual, of course. >>> >>> The major drawbacks of the current AS implementation are: >>> >>> - It works only for C. >>> For C++, a language extension would be needed as indicated in >>> ISO/IEC DTR 18037 >>> Annex F - C++ Compatibility and Migration issues >>> F.2 Multiple Address Spaces Support >>> >>> - Register allocation does not a good job. AS1 can only be addressed >>> byte-wise by one single address register (Z) as per *Z or *Z++. >> This flaw from register allocator are filed as PR50775 now. >> >>> The AVR part does several things: >>> >>> - It locates data in AS1 into appropriate section, i.e. somewhere in >>> .progmem >>> >>> - It does early sanity checks to ensure that __pgm is always accompanied >>> with const so that writing to AS1 in not possible. >>> >>> - It prints LPM instructions to access flash memory. >> The attached patch is an update merge so that it fits without conflicts. >> >> The patch requires Ulrich's works which is still in review >> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50775 >> >> The regression tests run with this patch and the new ChangeLog enttry si >> written as if Ulrich's patch was applied. >> >> Tests pass without regression. >> >> Besides the update to a nor up-to-date SVN version, the patch sets a built-in >> define __PGM so that it is easy for users to test if or if not the feature is >> available. >> >> Documentation and test cases will follow in separate patch. >> >> Ok for trunk after Ulrich's work has been approved? >> >> Johann >> >> PR target/49868 >> * config/avr/avr.h (ADDR_SPACE_PGM): New define for address space AS1. >> (REGISTER_TARGET_PRAGMAS): New define. >> * config/avr/avr-protos.h (avr_mem_pgm_p): New prototype. >> (avr_register_target_pragmas): New prototype. >> (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 AS1 as "__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 AS1. >> (avr_regno_mode_code_ok_for_base_p): Handle AS1. >> (lpm_addr_reg_rtx, lpm_reg_rtx): New static GTYed variables. >> (avr_decl_pgm_p): New static function. >> (avr_mem_pgm_p): New function. >> (avr_asm_len): Return always "" instead of void. >> (avr_out_lpm_no_lpmx): New static function. >> (avr_out_lpm): New static function. >> (output_movqi, output_movhi, output_movsisf): Call avr_out_lpm to >> handle loads from progmem. >> (avr_progmem_p): Test if decl is in AS1. >> (avr_pgm_pointer_const_p): New static function. >> (avr_pgm_check_var_decl): New static function. >> (avr_insert_attributes): Use it. Change error message to report >> cause (progmem or AS1) when code wants to write to AS1. >> (avr_section_type_flags): Unset section flag SECTION_BSS for >> data in progmem. >> * config/avr/avr.md (LPM_REGNO): New define_constants. >> (movqi, movhi, movsi, movsf): Skip if code would write to AS1. >> (movmemhi): Ditto. Propagate address space information to newly >> created MEM. >> (split-lpmx): New split. >> > > Approved. > > Denis. The patch from above is still not upstream because it needs the following work which is still pending review: http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01874.html The patch you find in this message contains the above patch with the following minor changes: * The patch is rebased to a newer revision * avr_out_lpm_no_lpmx and avr_out_lpm can also load the new 3-byte PSImode that got upstream meanwhile. * If there is no LMPX instruction and there are more than 2 bytes to load, a libgcc function is called. Moreover, the patch introduces the following major changes: * There is support for a new 24-bit address space called __pgmx * There is support for new 16-bit address spaces that represent the different 64k flash segments. Their names are __pgm2 .. __pgm5 * There are libgcc functions that load from flash for the expensive cases, i.e. there is no ELPMX. The reason why the __pgm address spaces are there is because there is very little to do to support them and there is no need for 24-bit pointers. The work is not yet complete, but I'd like to post it before stage 1 is over. Sorry for the inconvenience for just another review of the matter... What's missing is selection of sections for the numbered address spaces. The libgcc is independent of the not-yet-reviewed patch from Ulrich. Is the libgcc part ok for trunk? Is the gcc part ok provided Ulrich had been reviewed? I'd like to do the remaining as follow-up patches: * Release Notes * Documentation * Test cases * Section handling for numbered address spaces Johann gcc/ PR target/49868 * config/avr/avr.h (base_arch_s): Add field n_segments. (ADDR_SPACE_PGM, 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. (REGISTER_TARGET_PRAGMAS): New define. * config/avr/avr-protos.h (avr_mem_pgm_p, avr_mem_pgmx_p): New. (avr_out_xload, avr_xload_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 spaces __pgm, __pgm2, __pgm3, __pgm4 __pgm5, __pgmx. (avr_cpu_cpp_builtins): Add built-in defines __PGM, __PGM1, __PGM2, __PGM3, __PGM4, __PGM5, __PGMX. * config/avr/avr-devices.c (avr_arch_types): Set field n_segments. * 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, xstring_empty, xstring_e, all_regs_rtx): New static GTYed variables. (avr_option_override): Initialize them. (avr_pgm_segment): New static function. (avr_decl_pgm_p, avr_decl_pgmx_p): New static functions. (avr_mem_pgm_p, avr_mem_pgmx_p): New functions. (avr_asm_len): Return always "" instead of void. (avr_out_lpm, avr_out_lpm_no_lpmx, avr_out_xload, avr_find_unused_d_reg): New static functions. (output_movqi, output_movhi, output_movsisf, avr_out_movpsi): Call avr_out_lpm to handle loads from progmem. (print_operand): Hande CONST_STRING. (avr_xload_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. (adjust_insn_length): Handle ADJUST_LEN_XLOAD. (avr_const_address_lo16): New static function. (avr_assemble_integer): Use it to handle 3-byte integers. (avr_file_star): Print set for __RAMPZ__. (avr_emit_move): New function. * config/avr/predicates.md (hh8_operand): New predicate. (nop_general_operand): New predicate. (nox_general_operand): New predicate. * config/avr/avr.md (LPM_REGNO): New define_constants. (adjust_len): Add xload. (load_libgcc): New expander. (xload8_A, xload_A, *xload.): New insns. (*load..libgcc, *xload.qi, *xload..libgcc): New insn-and-splits. (mov): Call avr_emit_move to expand. (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. (n_extendhipsi2): New insn-and-split. libgcc/ PR target/49868 * config/avr/t-avr (LIB1ASMFUNCS): Add _load_3, _load_4, _xload_2 _xload_3 _xload_4. * config/avr/lib1funcs.S (__load_3, __load_4, __xload_2, __xload_3, __xload_4): New functions. --------------090403020503090108020802 Content-Type: text/x-patch; name="pgmx-4.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="pgmx-4.diff" Content-length: 71952 diff --git a/config/avr/avr-c.c b/config/avr/avr-c.c index 55de2d7..998c79e 100644 --- a/config/avr/avr-c.c +++ b/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,23 @@ #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); + 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); +} -/* Not included in avr.c since this requires C front end. */ /* Worker function for TARGET_CPU_CPP_BUILTINS. */ @@ -90,6 +106,23 @@ 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 as C+ will need a language + extension as of 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"); + 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 easily query if or if not a specific builtin is available. */ diff --git a/config/avr/avr-devices.c b/config/avr/avr-devices.c index d0dda85..944b6bb 100644 --- a/config/avr/avr-devices.c +++ b/config/avr/avr-devices.c @@ -25,18 +25,21 @@ /* 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" }, + { 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/config/avr/avr-log.c b/config/avr/avr-log.c index cdeb669..b9ec18b 100644 --- a/config/avr/avr-log.c +++ b/config/avr/avr-log.c @@ -59,7 +59,9 @@ 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/config/avr/avr-protos.h b/config/avr/avr-protos.h index d351833..e2fbdca 100644 --- a/config/avr/avr-protos.h +++ b/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,7 @@ 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 bool avr_emit_move (rtx*); 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 +122,10 @@ 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_mem_pgmx_p (rtx); +extern const char * avr_out_xload (rtx, rtx*, int*); +extern bool avr_xload_libgcc_p (enum machine_mode); #endif /* RTX_CODE */ #ifdef REAL_VALUE_TYPE @@ -139,12 +144,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/config/avr/avr.c b/config/avr/avr.c index 01f8376..8a942c1 100644 --- a/config/avr/avr.c +++ b/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" @@ -73,7 +74,7 @@ static int avr_num_arg_regs (enum machine_mode, const_tree); static int avr_operand_rtx_cost (rtx, enum machine_mode, enum rtx_code, int, bool); static struct machine_function * avr_init_machine_status (void); - +static rtx avr_find_unused_d_reg (rtx, rtx); /* Prototypes for hook implementors if needed before their implementation. */ @@ -83,12 +84,25 @@ 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; +/* RTX containing the strings "" and "e" */ +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]; + /* AVR register names {"r0", "r1", ..., "r31"} */ static const char *const avr_regnames[] = REGISTER_NAMES; @@ -171,9 +185,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 @@ -188,9 +199,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 @@ -220,6 +228,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. @@ -294,6 +320,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 @@ -322,9 +350,18 @@ 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); + lpm_addr_reg_rtx = gen_rtx_REG (Pmode, REG_Z); + + xstring_empty = gen_rtx_CONST_STRING (VOIDmode, ""); + xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e"); + + 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]; + init_machine_status = avr_init_machine_status; avr_log_set_avr_log(); @@ -383,6 +420,73 @@ 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 or AS is not in flash. */ + +static int +avr_pgm_segment (addr_space_t as) +{ + switch (as) + { + 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; + default: return -1; + } +} + + +/* 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 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) + 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 +avr_mem_pgm_p (rtx x) +{ + return (MEM_P (x) + && !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (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 */ @@ -1378,6 +1482,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 */ @@ -1739,6 +1846,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') @@ -2176,6 +2285,396 @@ 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 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_current_arch->have_elpmx); +} + + +static rtx +avr_find_unused_d_reg (rtx insn, rtx exclude) +{ + int regno; + + for (regno = 16; regno < 32; regno ++) + { + rtx reg = all_regs_rtx[regno]; + + if (exclude + && reg_overlap_mentioned_p (exclude, reg)) + { + continue; + } + + if (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. */ + +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 ("%4lpm" CR_TAB + "mov %0,%3", xop, plen, -2); + + case 2: + if (REGNO (dest) == REG_Z) + return avr_asm_len ("%4lpm" CR_TAB + "push %3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %B0,%3" CR_TAB + "pop %A0", xop, plen, -6); + else + { + 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); + } + + break; /* 2 */ + + case 3: + avr_asm_len ("%4lpm" CR_TAB + "mov %A0,%3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %B0,%3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %C0,%3", xop, plen, -8); + + if (REGNO (dest) != REG_Z - 2 + || !reg_unused_after (insn, addr)) + { + avr_asm_len ("sbiw %2,2", xop, plen, 1); + } + + break; /* 3 */ + + case 4: + avr_asm_len ("%4lpm" CR_TAB + "mov %A0,%3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %B0,%3" CR_TAB + "adiw %2,1", xop, plen, -6); + + if (REGNO (dest) == REG_Z - 2) + return avr_asm_len ("%4lpm" CR_TAB + "push %3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %D0,%3" CR_TAB + "pop %C0", xop, plen, 6); + else + { + avr_asm_len ("%4lpm" CR_TAB + "mov %C0,%3" CR_TAB + "adiw %2,1" CR_TAB + "%4lpm" CR_TAB + "mov %D0,%3", xop, plen, 5); + + if (!reg_unused_after (insn, addr)) + avr_asm_len ("sbiw %2,3", xop, plen, 1); + } + + break; /* 4 */ + } + + break; /* REG */ + + case POST_INC: + + gcc_assert (REG_Z == REGNO (XEXP (addr, 0)) + && n_bytes <= 4); + + avr_asm_len ("%4lpm" CR_TAB + "mov %A0,%3" CR_TAB + "adiw %2,1", xop, plen, -3); + + if (n_bytes >= 2) + 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) */ + + 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; + int segment; + + 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); + + segment = avr_pgm_segment (MEM_ADDR_SPACE (src)); + + gcc_assert (REG_P (dest) + && ((REG_P (addr) && segment >= 0) + || (GET_CODE (addr) == LO_SUM && segment == -1))); + + if (segment == -1) + { + /* We are called from avr_out_xload because someone wrote + __pgmx on a devise with just one flash segment. */ + + addr = XEXP (addr, 1); + } + + xop[0] = dest; + xop[1] = addr; + xop[2] = lpm_addr_reg_rtx; + + regno_dest = REGNO (dest); + + /* 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; + + if (segment == 0) + { + xop[4] = xstring_empty; + } + else + { + 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 __tmp_reg__" CR_TAB + "inc __tmp_reg__" CR_TAB + "out __RAMPZ__,__tmp_reg__", xop, plen, 3); + } + else + { + avr_asm_len ("mov __tmp_reg__,%2" CR_TAB + "ldi %2,%4" CR_TAB + "out __RAMPZ__,%2" CR_TAB + "mov %2,__tmp_reg__", 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); + } + + 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 ("%4lpm %0,%a2", xop, plen, -1); + + case 2: + if (REGNO (dest) == REG_Z) + return avr_asm_len ("%4lpm __tmp_reg__,%a2+" CR_TAB + "%4lpm %B0,%a2" CR_TAB + "mov %A0,__tmp_reg__", xop, plen, -3); + else + { + 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); + } + + break; /* 2 */ + + case 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); + + break; /* 3 */ + + case 4: + + avr_asm_len ("%4lpm %A0,%a2+" CR_TAB + "%4lpm %B0,%a2+", xop, plen, -2); + + if (REGNO (dest) == REG_Z - 2) + return avr_asm_len ("%4lpm __tmp_reg__,%a2+" CR_TAB + "%4lpm %C0,%a2" CR_TAB + "mov %D0,__tmp_reg__", xop, plen, 3); + else + { + 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); + } + + break; /* 4 */ + } /* n_bytes */ + + break; /* REG */ + + case POST_INC: + + gcc_assert (REG_Z == REGNO (XEXP (addr, 0)) + && n_bytes <= 4); + + 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 */ + + } /* switch CODE (addr) */ + + return ""; +} + + +/* Worker function for "*xload." insns. */ + +const char* +avr_out_xload (rtx insn, rtx *op, int *plen) +{ + rtx xop[4]; + int n_bytes = GET_MODE_SIZE (GET_MODE (op[0])); + + if (avr_current_arch->n_segments == 1) + return avr_out_lpm (insn, op, plen); + + xop[0] = op[0]; + xop[1] = op[1]; + xop[2] = lpm_addr_reg_rtx; + xop[3] = lpm_reg_rtx; + + avr_asm_len ("out __RAMPZ__,%1", xop, plen, -1); + + if (1 == n_bytes) + { + if (avr_current_arch->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, 1); + } + + /* 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 ""; +} + + const char * output_movqi (rtx insn, rtx operands[], int *l) { @@ -2184,6 +2683,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; @@ -2276,6 +2781,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; @@ -2889,6 +3400,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; @@ -3208,6 +3725,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 */ @@ -3807,7 +4330,8 @@ out_shift_with_cnt (const char *templ, rtx insn, rtx operands[], if (GET_CODE (operands[2]) == CONST_INT) { - 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. */ @@ -5938,7 +6462,8 @@ 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_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; @@ -6083,23 +6608,85 @@ _reg_unused_after (rtx insn, rtx reg) return 1; } -/* Target hook for assembling integer objects. The AVR version needs - special handling for references to certain labels. */ -static bool -avr_assemble_integer (rtx x, unsigned int size, int aligned_p) +/* 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) { - if (size == POINTER_SIZE / BITS_PER_UNIT && aligned_p - && text_segment_operand (x, VOIDmode) ) + rtx lo16; + + switch (GET_CODE (x)) { - fputs ("\t.word\tgs(", asm_out_file); - output_addr_const (asm_out_file, x); - fputs (")\n", asm_out_file); - return true; + 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. */ + +static bool +avr_assemble_integer (rtx x, unsigned int size, int aligned_p) +{ + if (size == POINTER_SIZE / BITS_PER_UNIT && aligned_p + && text_segment_operand (x, VOIDmode) ) + { + 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\"24-bit address needs assembler 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 @@ -6249,8 +6836,16 @@ 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 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 */ int avr_progmem_p (tree decl, tree attributes) @@ -6260,11 +6855,18 @@ 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; + 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); @@ -6273,16 +6875,122 @@ 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)) @@ -6299,11 +7007,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); } } } @@ -6503,6 +7220,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; } @@ -6578,9 +7296,11 @@ avr_file_start (void) 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); + fprintf (asm_out_file, + "__SREG__ = 0x3f\n" + "__SP_H__ = 0x3e\n" + "__SP_L__ = 0x3d\n" + "__RAMPZ__ = 0x%02x\n", RAMPZ_ADDR - 0x20); fputs ("__tmp_reg__ = 0\n" "__zero_reg__ = 1\n", asm_out_file); @@ -8090,10 +8810,15 @@ 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, + 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; @@ -8112,6 +8837,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 @@ -8651,6 +9397,268 @@ 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 as == ADDR_SPACE_PGMX ? PSImode : Pmode; +} + + +/* Implement `TARGET_ADDR_SPACE_POINTER_MODE'. */ + +static enum machine_mode +avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED) +{ + return as == ADDR_SPACE_PGMX ? PSImode : Pmode; +} + + +/* 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_pseude_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: + case ADDR_SPACE_PGM1: + case ADDR_SPACE_PGM2: + case ADDR_SPACE_PGM3: + case ADDR_SPACE_PGM4: + case ADDR_SPACE_PGM5: + + switch (GET_CODE (x)) + { + case REG: + ok = avr_reg_ok_for_pgm_addr (x, strict); + break; + + case POST_INC: + ok = avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict); + break; + + default: + break; + } + + 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) + { + 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) +{ + 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) + { + src = force_reg (Pmode, src); + + if (ADDR_SPACE_GENERIC_P (as_from) + || as_from == ADDR_SPACE_PGM) + { + return gen_rtx_ZERO_EXTEND (PSImode, src); + } + else + { + int hh8 = avr_pgm_segment (as_from) << 16; + src = SET_SRC (gen_n_extendhipsi2 (src, src, GEN_INT (hh8))); + } + } + + 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) +{ + if (subset == ADDR_SPACE_PGMX + && superset != ADDR_SPACE_PGMX) + { + return false; + } + + return true; +} + + +/* Helper function for the MOVMODE move expanders in the machine description. + Return true: The expansion of the move is complete. + Return false: The move still needs expansion. */ + +bool +avr_emit_move (rtx *xop) +{ + rtx (*fun)(rtx,rtx); + rtx dest = xop[0]; + rtx src = xop[1]; + enum machine_mode mode = GET_MODE (dest); + + /* One of the operands has to be in a register. */ + + if (!register_operand (dest, mode) + && !(register_operand (src, mode) + || src == CONST0_RTX (mode))) + { + src = xop[1] = copy_to_mode_reg (mode, src); + } + + if (avr_mem_pgm_p (dest)) + return true; + + 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)); + + switch (mode) + { + default: gcc_unreachable(); + case QImode: fun = gen_xload8_A; break; + case HImode: fun = gen_xloadhi_A; break; + case PSImode: fun = gen_xloadpsi_A; break; + case SImode: fun = gen_xloadsi_A; break; + case SFmode: fun = gen_xloadsf_A; break; + } + + emit_insn (fun (dest, src)); + + return true; + } + + if (GET_MODE_SIZE (mode) > 2 + && avr_current_arch->n_segments == 1 + && !AVR_HAVE_LPMX + && avr_mem_pgm_p (src)) + { + /* For the small devices, do loads per libgcc call. */ + + switch (mode) + { + default: gcc_unreachable(); + case PSImode: fun = gen_loadpsi_libgcc; break; + case SImode: fun = gen_loadsi_libgcc; break; + case SFmode: fun = gen_loadsf_libgcc; break; + } + + emit_insn (fun (dest, src)); + + return true; + } + + return false; +} + + /* Helper for __builtin_avr_delay_cycles */ static void diff --git a/config/avr/avr.h b/config/avr/avr.h index 7a3bc73..a80b554 100644 --- a/config/avr/avr.h +++ b/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) @@ -391,6 +396,20 @@ typedef struct avr_args { #define NO_FUNCTION_CSE + +#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 { \ + avr_register_target_pragmas(); \ + }while (0); + #define TEXT_SECTION_ASM_OP "\t.text" #define DATA_SECTION_ASM_OP "\t.data" diff --git a/config/avr/avr.md b/config/avr/avr.md index 12e9485..3656eef 100644 --- a/config/avr/avr.md +++ b/config/avr/avr.md @@ -42,8 +42,10 @@ (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 + ;; register holding the address' high part when loading (SREG_ADDR 0x5F) (RAMPZ_ADDR 0x5B) @@ -127,6 +129,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, ashlqi, ashrqi, lshrqi, ashlhi, ashrhi, lshrhi, ashlsi, ashrsi, lshrsi, @@ -315,6 +318,175 @@ "") ;;======================================================================== +;; Move stuff around + +(define_expand "load_libgcc" + [(set (reg:HI REG_Z) + (match_dup 2)) + (parallel [(set (reg:MOVMODE 22) + (match_operand:MOVMODE 1 "memory_operand" "")) + (use (const_int 0))]) + (set (match_operand:MOVMODE 0 "register_operand" "") + (reg:MOVMODE 22))] + "GET_MODE_SIZE (mode) > 2 + && !AVR_HAVE_LPMX" + { + operands[2] = XEXP (operands[1], 0); + operands[1] = replace_equiv_address (operands[1], gen_rtx_REG (Pmode, REG_Z)); + set_mem_addr_space (operands[1], ADDR_SPACE_PGM); + }) + +(define_insn "*load..libgcc" + [(set (reg:MOVMODE 22) + (mem:MOVMODE (reg:HI REG_Z))) + (use (const_int 0))] + "GET_MODE_SIZE (mode) > 2 + && !AVR_HAVE_LPMX" + { + rtx x_bytes = GEN_INT (GET_MODE_SIZE (mode)); + output_asm_insn ("%~call __load_%0", &x_bytes); + return ""; + } + [(set_attr "length" "2") + (set_attr "adjust_len" "call") + (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(); } + "" + [(set (reg:HI REG_Z) + (match_dup 4)) + (set (match_dup 2) + (match_dup 3)) + ;; xload8 + (set (match_dup 0) + (mem:QI (lo_sum:PSI (match_dup 2) + (reg:HI REG_Z))))] + { + rtx addr = XEXP (operands[1], 0); + operands[2] = gen_reg_rtx (QImode); + operands[3] = simplify_gen_subreg (QImode, addr, PSImode, 2); + operands[4] = simplify_gen_subreg (Pmode, addr, PSImode, 0); + }) + +(define_insn_and_split "xload_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 + && can_create_pseudo_p() + && avr_mem_pgmx_p (operands[1]) + && REG_P (XEXP (operands[1], 0))" + { gcc_unreachable(); } + "" + [(set (reg:HI REG_Z) + (match_dup 4)) + (set (match_dup 2) + (match_dup 3)) + ;; *xload. + ;; *xload..libgcc + (parallel [(set (match_dup 5) + (mem:MOVMODE (lo_sum:PSI (match_dup 2) + (reg:HI REG_Z)))) + (clobber (match_dup 6)) + (clobber (reg:HI REG_Z))]) + ;; no-op move in the case of inlined code + (set (match_dup 0) + (match_dup 5))] + { + rtx addr = XEXP (operands[1], 0); + + operands[3] = simplify_gen_subreg (QImode, addr, PSImode, 2); + operands[4] = simplify_gen_subreg (Pmode, addr, PSImode, 0); + + if (avr_xload_libgcc_p (mode)) + { + operands[2] = gen_rtx_REG (QImode, 21); + operands[5] = gen_rtx_REG (mode, 22); + operands[6] = operands[2]; + } + else + { + if (avr_current_arch->n_segments == 1) + { + operands[2] = gen_rtx_REG (QImode, TMP_REGNO); + operands[3] = operands[2]; + } + else + { + operands[2] = gen_reg_rtx (QImode); + } + + operands[5] = operands[0]; + operands[6] = gen_rtx_SCRATCH (HImode); + } + }) + +;; Move value from address space pgmx to a register +;; These insns must be prior to respective generic move insn. + +(define_insn "*xload.qi" + [(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..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 != QImode + && avr_xload_libgcc_p (mode)" + { + rtx x_bytes = GEN_INT (GET_MODE_SIZE (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." + [(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 != QImode + && !avr_xload_libgcc_p (mode)" + { + return avr_out_xload (insn, operands, NULL); + } + [(set_attr "adjust_len" "xload") + (set_attr "cc" "clobber")]) + + +;; General move expanders ;; "movqi" ;; "movhi" @@ -327,13 +499,8 @@ (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])) - { - operands[1] = copy_to_mode_reg (mode, operands[1]); - } + if (avr_emit_move (operands)) + DONE; }) ;;======================================================================== @@ -346,11 +513,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 +595,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 +635,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 (ADDR_SPACE_PGM != MEM_ADDR_SPACE (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 +699,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 +739,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 +755,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 +807,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 +816,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 +839,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. */ @@ -2866,10 +3079,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" "") @@ -2901,21 +3112,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")]) @@ -3003,11 +3218,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")]) @@ -3061,11 +3278,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")]) @@ -3082,11 +3301,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")]) @@ -3107,21 +3328,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")]) @@ -3139,11 +3364,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")]) @@ -3162,11 +3389,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")]) @@ -3183,11 +3412,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")]) @@ -3196,11 +3427,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" "") @@ -3232,21 +3461,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")]) @@ -3264,11 +3497,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")]) @@ -3322,11 +3557,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")]) @@ -3343,11 +3580,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")]) @@ -3605,6 +3844,21 @@ operands[3] = simplify_gen_subreg (QImode, operands[0], PSImode, 2); }) +(define_insn_and_split "n_extendhipsi2" + [(set (match_operand:PSI 0 "register_operand" "=d") + (ior:PSI (zero_extend:PSI (match_operand:HI 1 "register_operand" "r")) + (match_operand:PSI 2 "hh8_operand" "n")))] + "" + "#" + "reload_completed" + [(set (match_dup 3) (match_dup 1)) + (set (match_dup 4) (match_dup 5))] + { + operands[3] = simplify_gen_subreg (HImode, operands[0], PSImode, 0); + operands[4] = simplify_gen_subreg (QImode, operands[0], PSImode, 2); + operands[5] = simplify_gen_subreg (QImode, operands[2], PSImode, 2); + }) + (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/config/avr/predicates.md b/config/avr/predicates.md index 55a25b8..f2a6a70 100644 --- a/config/avr/predicates.md +++ b/config/avr/predicates.md @@ -57,6 +57,22 @@ (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") + (not (match_test "avr_mem_pgm_p (op)")))) + + +;; Return 1 if OP is a general operand not in extended program memory +;; Moreover; deny loads from flash that are performed via libgcc call. +(define_predicate "nox_general_operand" + (and (match_operand 0 "general_operand") + (not (match_test "avr_mem_pgmx_p (op)")) + (not (match_test "GET_MODE_SIZE (mode) > 2 + && avr_current_arch->n_segments == 1 + && !AVR_HAVE_LPMX + && avr_mem_pgm_p (op)")))) + ;; Return 1 if OP is the zero constant for MODE. (define_predicate "const0_operand" (and (match_code "const_int,const_double") @@ -233,3 +249,8 @@ (define_predicate "o16_operand" (and (match_code "const_int") (match_test "IN_RANGE (INTVAL (op), -(1<<16), -1)"))) + +;; CONST_INT were ony sub-byte #2 may have non-zero value. +(define_predicate "hh8_operand" + (and (match_code "const_int") + (match_test "IN_RANGE (INTVAL (op), 0x00010000, 0x00ff0000)"))) --------------090403020503090108020802 Content-Type: text/x-patch; name="pgmx-4-libgcc.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="pgmx-4-libgcc.diff" Content-length: 2927 Index: ../libgcc/config/avr/lib1funcs.S =================================================================== --- ../libgcc/config/avr/lib1funcs.S (revision 180962) +++ ../libgcc/config/avr/lib1funcs.S (working copy) @@ -1175,6 +1175,120 @@ DEFUN __tablejump_elpm__ ENDF __tablejump_elpm__ #endif /* defined (L_tablejump_elpm) */ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Loading n bytes from Flash; n = 2,3,4 +;; R22... = Flash[R21:Z] +;; Clobbers: __tmp_reg__, R21, R30, R31 + +#if (defined (L_xload_2) \ + || defined (L_xload_3) \ + || defined (L_xload_4)) \ + && defined (__AVR_HAVE_ELPM__) \ + && !defined (__AVR_HAVE_ELPMX__) + +#if !defined (__AVR_HAVE_RAMPZ__) +#error Need RAMPZ +#endif /* have RAMPZ */ + +;; Destination +#define D0 22 +#define D1 D0+1 +#define D2 D0+2 +#define D3 D0+3 + +;; Register containing bits 16+ of the address + +#define HHI8 21 + +.macro .xload dest, n + elpm + mov \dest, r0 +.if \dest != D0+\n-1 + adiw r30, 1 + adc HHI8, __zero_reg__ + out __RAMPZ__, HHI8 +.endif +.endm + +#if defined (L_xload_2) +DEFUN __xload_2 + out __RAMPZ__, HHI8 + .xload D0, 2 + .xload D1, 2 + ret +ENDF __xload_2 +#endif /* L_xload_2 */ + +#if defined (L_xload_3) +DEFUN __xload_3 + out __RAMPZ__, HHI8 + .xload D0, 3 + .xload D1, 3 + .xload D2, 3 + ret +ENDF __xload_3 +#endif /* L_xload_3 */ + +#if defined (L_xload_4) +DEFUN __xload_4 + out __RAMPZ__, HHI8 + .xload D0, 4 + .xload D1, 4 + .xload D2, 4 + .xload D3, 4 + ret +ENDF __xload_4 +#endif /* L_xload_4 */ + +#endif /* L_xload_* && 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 Index: ../libgcc/config/avr/t-avr =================================================================== --- ../libgcc/config/avr/t-avr (revision 180962) +++ ../libgcc/config/avr/t-avr (working copy) @@ -21,6 +21,8 @@ LIB1ASMFUNCS = \ _cleanup \ _tablejump \ _tablejump_elpm \ + _load_3 _load_4 \ + _xload_2 _xload_3 _xload_4 \ _copy_data \ _clear_bss \ _ctors \ --------------090403020503090108020802--