From c1dd9790241595c417b028842bf0a7d4b2cf253a Mon Sep 17 00:00:00 2001 From: Joern Rennecke Date: Tue, 21 Oct 2014 20:12:01 +0000 Subject: [PATCH] avr-c.c (avr_cpu_cpp_builtins): Don't define __MEMX for avrtiny. gcc: 2014-10-21 Joern Rennecke Vidya Praveen Praveen Kumar Kaushik Senthil Kumar Selvaraj Pitchumani Sivanupandi * config/avr/avr-c.c (avr_cpu_cpp_builtins): Don't define __MEMX for avrtiny. * config/avr/avr.c (avr_insert_attributes): Reject __memx for avrtiny. (avr_nonconst_pointer_addrspace): Likewise. * config/avr/avr.h (AVR_HAVE_LPM): Define. Added AVRTINY architecture to avr target. * config/avr/avr-arch.h (avr_arch): Added AVRTINY architecture. (base_arch_s): member added for AVRTINY architecture. * config/avr/avr.c: Added TINY_ADIW, TINY_SBIW macros as AVRTINY alternate for adiw/sbiw instructions. Added AVR_TMP_REGNO and AVR_ZERO_REGNO macros for tmp and zero registers. Replaced TMP_REGNO and ZERO_REGNO occurrences by AVR_TMP_REGNO and AVR_ZERO_REGNO respectively. LAST_CALLEE_SAVED_REG macro added for the last register in callee saved register list. (avr_option_override): CCP address updated for AVRTINY. (avr_init_expanders): tmp and zero rtx initialized as per arch. Reset avr_have_dimode if AVRTINY. (sequent_regs_live): Use LAST_CALLEE_SAVED_REG instead magic number. (emit_push_sfr): Use AVR_TMP_REGNO for tmp register number. (avr_prologue_setup_frame): Don't minimize prologue if AVRTINY. Use LAST_CALLEE_SAVED_REG to refer last callee saved register. (expand_epilogue): Likewise. (avr_print_operand): Print CCP address in case of AVRTINY also. bad address (function_arg_regno_p): Check different register list for arguments if AVRTINY. (init_cumulative_args): Check for AVRTINY to update number of argument registers. (tiny_valid_direct_memory_access_range): New function. Return false if direct memory access range is not in accepted range for AVRTINY. (avr_out_movqi_r_mr_reg_disp_tiny): New function to handle register indirect load (with displacement) for AVRTINY. (out_movqi_r_mr): Updated instruction length for AVRTINY. Call avr_out_movqi_r_mr_reg_disp_tiny for load from reg+displacement. (avr_out_movhi_r_mr_reg_no_disp_tiny): New function to handle register indirect load (no displacement) for AVRTINY. (avr_out_movhi_r_mr_reg_disp_tiny): New function to handle register indirect load (with displacement) for AVRTINY. (avr_out_movhi_r_mr_pre_dec_tiny): New function to handle register indirect load for pre-decrement address. (out_movhi_r_mr): In case of AVRTINY, call tiny register indirect load functions. Update instruction length for AVRTINY. (avr_out_movsi_r_mr_reg_no_disp_tiny): New function. Likewise, for SImode. (avr_out_movsi_r_mr_reg_disp_tiny): New function. Likewise, for SImode. (out_movsi_r_mr): Likewise, for SImode. (avr_out_movsi_mr_r_reg_no_disp_tiny): New function to handle register indirect store (no displacement) for AVRTINY. (avr_out_movsi_mr_r_reg_disp_tiny): New function to handle register indirect store (with displacement) for AVRTINY. (out_movsi_mr_r): Emit out insn for IO address store. Update store instruction's size for AVRTINY. For AVRTINY, call tiny SImode indirect store functions. (avr_out_load_psi_reg_no_disp_tiny): New function to handle register indirect load (no displacement) for PSImode in AVRTINY. (avr_out_load_psi_reg_disp_tiny): New function to handle register indirect load (with displacement) for PSImode in AVRTINY. (avr_out_load_psi): Call PSImode register indirect load functions for AVRTINY. Update instruction length for AVRTINY. (avr_out_store_psi_reg_no_disp_tiny): New function to handle register indirect store (no displacement) for PSImode in AVRTINY. (avr_out_store_psi_reg_disp_tiny): New function to handle register indirect store (with displacement) for PSImode in AVRTINY. (avr_out_store_psi): Update instruction length for AVRTINY. Call tiny register indirect store functions for AVRTINY. (avr_out_movqi_mr_r_reg_disp_tiny): New function to handle QImode register indirect store (with displacement) for AVRTINY. (out_movqi_mr_r): Update instruction length for AVRTINY. Call tiny register indirect store function for QImode in AVRTINY. (avr_out_movhi_mr_r_xmega): Update instruction length for AVRTINY. (avr_out_movhi_mr_r_reg_no_disp_tiny): New function to handle register indirect store (no displacement) for HImode in AVRTINY. (avr_out_movhi_mr_r_reg_disp_tiny): New function to handle register indirect store (with displacement) for HImode in AVRTINY. (avr_out_movhi_mr_r_post_inc_tiny): New function to handle register indirect store for post-increment address in HImode. (out_movhi_mr_r): Update instruction length for AVRTINY. Call tiny register indirect store function for HImode in AVRTINY. (avr_out_compare): Use TINY_SBIW/ TINY_ADIW in place of sbiw/adiw in case of AVRTINY. (order_regs_for_local_alloc): Updated register allocation order for AVRTINY. (avr_conditional_register_usage): New function. It is a target hook (TARGET_CONDITIONAL_REGISTER_USAGE) function which updates fixed, call used registers list and register allocation order for AVRTINY. (avr_return_in_memory): Update return value size for AVRTINY. * config/avr/avr-c.c (avr_cpu_cpp_builtins): Added builtin macros for AVRTINY arch and tiny program memory base address. * config/avr/avr-devices.c (avr_arch_types): Added AVRTINY arch. (avr_texinfo): Added description for AVRTINY arch. * config/avr/avr.h: Added macro to identify AVRTINY arch. Updated STATIC_CHAIN_REGNUM for AVRTINY. * config/avr/avr-mcus.def: Added AVRTINY arch devices. * config/avr/avr.md: Added constants for tmp/ zero registers in AVRTINY. Attributes for AVRTINY added. (mov): Move src/ dest address to register if it is not in AVRTINY memory access range. (mov_insn): Avoid QImode direct load for AVRTINY if address not in AVRTINY memory access range. (*mov): Likewise for HImode and SImode. (*movsf): Likewise for SFmode. (delay_cycles_2): Updated instructions to be emitted as AVRTINY does not have sbiw. * config/avr/avr-protos.h: Added function prototype for tiny_valid_direct_memory_access_range. * config/avr/avr-tables.opt: Regenerate. * gcc/config/avr/t-multilib: Regenerate. * doc/avr-mmcu.texi: Regenerate. gcc/testsuite: 2014-10-21 Joern Rennecke * gcc.target/avr/tiny-memx.c: New test. * gcc.target/avr/tiny-caller-save.c: New test. libgcc: 2014-10-21 Joern Rennecke Vidya Praveen Praveen Kumar Kaushik Senthil Kumar Selvaraj Pitchumani Sivanupandi * config/avr/lib1funcs.S (__do_global_dtors): Go back to descending order. Updated library functions for AVRTINY arch. * config/avr/lib1funcs.S: Updated zero/tmp regs for AVRTINY. Replaced occurrences of r0/r1 with tmp/zero reg macros. Added wsubi/ wadi macros that expands conditionally as sbiw/ adiw or AVRTINY equivalent. Replaced occurrences of sbiw/adiw with wsubi/wadi macors. (__mulsi3_helper): Update stack, preserve callee saved regs and argument from stack. Restore callee save registers. (__mulpsi3): Likewise. (__muldi3, __udivmodsi4, __divmodsi4, __negsi2, __umoddi3, __udivmod64, __moddi3, __adddi3, __adddi3_s8, __subdi3, __cmpdi2, __cmpdi2_s8, __negdi2, __prologue_saves__, __epilogue_restores__): Excluded for AVRTINY. (__tablejump2__): Added lpm equivalent instructions for AVRTINY. (__do_copy_data): Added new definition for AVRTINY. (__do_clear_bss): Replace r17 by r18 to preserve zero reg for AVRTINY. (__load_3, __load_4, __xload_1, __xload_2, __xload_3, __xload_4, __movmemx_qi, __movmemx_hi): Excluded for AVRTINY. * config/avr/lib1funcs-fixed.S: Replaced occurrences of r0/r1 with tmp/zero reg macros. Replaced occurrences of sbiw/adiw with wsubi/wadi macors. * config/avr/t-avr (LIB1ASMFUNCS): Remove unsupported functions for AVRTINY. Fix broken long multiplication on tiny arch. Co-Authored-By: Pitchumani Sivanupandi Co-Authored-By: Praveen Kumar Kaushik Co-Authored-By: Senthil Kumar Selvaraj Co-Authored-By: Vidya Praveen From-SVN: r216525 --- gcc/ChangeLog | 116 +++ gcc/config/avr/avr-arch.h | 4 + gcc/config/avr/avr-c.c | 22 +- gcc/config/avr/avr-devices.c | 46 +- gcc/config/avr/avr-mcus.def | 8 + gcc/config/avr/avr-protos.h | 1 + gcc/config/avr/avr-tables.opt | 3 + gcc/config/avr/avr.c | 905 ++++++++++++++++-- gcc/config/avr/avr.h | 4 +- gcc/config/avr/avr.md | 98 +- gcc/config/avr/t-multilib | 7 +- gcc/doc/avr-mmcu.texi | 4 + gcc/testsuite/ChangeLog | 6 + .../gcc.target/avr/tiny-caller-save.c | 78 ++ gcc/testsuite/gcc.target/avr/tiny-memx.c | 4 + libgcc/ChangeLog | 35 + libgcc/config/avr/lib1funcs-fixed.S | 12 + libgcc/config/avr/lib1funcs.S | 168 +++- libgcc/config/avr/t-avr | 62 +- 19 files changed, 1429 insertions(+), 154 deletions(-) create mode 100644 gcc/testsuite/gcc.target/avr/tiny-caller-save.c create mode 100644 gcc/testsuite/gcc.target/avr/tiny-memx.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 4e9f64d694a..6d5e2cc908d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,119 @@ +2014-10-21 Joern Rennecke + Vidya Praveen + Praveen Kumar Kaushik + Senthil Kumar Selvaraj + Pitchumani Sivanupandi + + * config/avr/avr-c.c (avr_cpu_cpp_builtins): Don't define + __MEMX for avrtiny. + * config/avr/avr.c (avr_insert_attributes): Reject __memx for avrtiny. + (avr_nonconst_pointer_addrspace): Likewise. + * config/avr/avr.h (AVR_HAVE_LPM): Define. + + Added AVRTINY architecture to avr target. + * config/avr/avr-arch.h (avr_arch): Added AVRTINY architecture. + (base_arch_s): member added for AVRTINY architecture. + * config/avr/avr.c: Added TINY_ADIW, TINY_SBIW macros as AVRTINY + alternate for adiw/sbiw instructions. Added AVR_TMP_REGNO and + AVR_ZERO_REGNO macros for tmp and zero registers. Replaced TMP_REGNO + and ZERO_REGNO occurrences by AVR_TMP_REGNO and AVR_ZERO_REGNO + respectively. LAST_CALLEE_SAVED_REG macro added for the last register + in callee saved register list. + (avr_option_override): CCP address updated for AVRTINY. + (avr_init_expanders): tmp and zero rtx initialized as per arch. + Reset avr_have_dimode if AVRTINY. + (sequent_regs_live): Use LAST_CALLEE_SAVED_REG instead magic number. + (emit_push_sfr): Use AVR_TMP_REGNO for tmp register number. + (avr_prologue_setup_frame): Don't minimize prologue if AVRTINY. + Use LAST_CALLEE_SAVED_REG to refer last callee saved register. + (expand_epilogue): Likewise. + (avr_print_operand): Print CCP address in case of AVRTINY also. + bad address + (function_arg_regno_p): Check different register list for arguments + if AVRTINY. + (init_cumulative_args): Check for AVRTINY to update number of argument + registers. + (tiny_valid_direct_memory_access_range): New function. Return false if + direct memory access range is not in accepted range for AVRTINY. + (avr_out_movqi_r_mr_reg_disp_tiny): New function to handle register + indirect load (with displacement) for AVRTINY. + (out_movqi_r_mr): Updated instruction length for AVRTINY. Call + avr_out_movqi_r_mr_reg_disp_tiny for load from reg+displacement. + (avr_out_movhi_r_mr_reg_no_disp_tiny): New function to handle register + indirect load (no displacement) for AVRTINY. + (avr_out_movhi_r_mr_reg_disp_tiny): New function to handle register + indirect load (with displacement) for AVRTINY. + (avr_out_movhi_r_mr_pre_dec_tiny): New function to handle register + indirect load for pre-decrement address. + (out_movhi_r_mr): In case of AVRTINY, call tiny register indirect load + functions. Update instruction length for AVRTINY. + (avr_out_movsi_r_mr_reg_no_disp_tiny): New function. Likewise, for + SImode. + (avr_out_movsi_r_mr_reg_disp_tiny): New function. Likewise, for SImode. + (out_movsi_r_mr): Likewise, for SImode. + (avr_out_movsi_mr_r_reg_no_disp_tiny): New function to handle register + indirect store (no displacement) for AVRTINY. + (avr_out_movsi_mr_r_reg_disp_tiny): New function to handle register + indirect store (with displacement) for AVRTINY. + (out_movsi_mr_r): Emit out insn for IO address store. Update store + instruction's size for AVRTINY. For AVRTINY, call tiny SImode indirect + store functions. + (avr_out_load_psi_reg_no_disp_tiny): New function to handle register + indirect load (no displacement) for PSImode in AVRTINY. + (avr_out_load_psi_reg_disp_tiny): New function to handle register + indirect load (with displacement) for PSImode in AVRTINY. + (avr_out_load_psi): Call PSImode register indirect load functions for + AVRTINY. Update instruction length for AVRTINY. + (avr_out_store_psi_reg_no_disp_tiny): New function to handle register + indirect store (no displacement) for PSImode in AVRTINY. + (avr_out_store_psi_reg_disp_tiny): New function to handle register + indirect store (with displacement) for PSImode in AVRTINY. + (avr_out_store_psi): Update instruction length for AVRTINY. Call tiny + register indirect store functions for AVRTINY. + (avr_out_movqi_mr_r_reg_disp_tiny): New function to handle QImode + register indirect store (with displacement) for AVRTINY. + (out_movqi_mr_r): Update instruction length for AVRTINY. Call tiny + register indirect store function for QImode in AVRTINY. + (avr_out_movhi_mr_r_xmega): Update instruction length for AVRTINY. + (avr_out_movhi_mr_r_reg_no_disp_tiny): New function to handle register + indirect store (no displacement) for HImode in AVRTINY. + (avr_out_movhi_mr_r_reg_disp_tiny): New function to handle register + indirect store (with displacement) for HImode in AVRTINY. + (avr_out_movhi_mr_r_post_inc_tiny): New function to handle register + indirect store for post-increment address in HImode. + (out_movhi_mr_r): Update instruction length for AVRTINY. Call tiny + register indirect store function for HImode in AVRTINY. + (avr_out_compare): Use TINY_SBIW/ TINY_ADIW in place of sbiw/adiw + in case of AVRTINY. + (order_regs_for_local_alloc): Updated register allocation order for + AVRTINY. + (avr_conditional_register_usage): New function. It is a target hook + (TARGET_CONDITIONAL_REGISTER_USAGE) function which updates fixed, call + used registers list and register allocation order for AVRTINY. + (avr_return_in_memory): Update return value size for AVRTINY. + * config/avr/avr-c.c (avr_cpu_cpp_builtins): Added builtin macros + for AVRTINY arch and tiny program memory base address. + * config/avr/avr-devices.c (avr_arch_types): Added AVRTINY arch. + (avr_texinfo): Added description for AVRTINY arch. + * config/avr/avr.h: Added macro to identify AVRTINY arch. Updated + STATIC_CHAIN_REGNUM for AVRTINY. + * config/avr/avr-mcus.def: Added AVRTINY arch devices. + * config/avr/avr.md: Added constants for tmp/ zero registers in + AVRTINY. Attributes for AVRTINY added. + (mov): Move src/ dest address to register if it is not in AVRTINY + memory access range. + (mov_insn): Avoid QImode direct load for AVRTINY if address not + in AVRTINY memory access range. + (*mov): Likewise for HImode and SImode. + (*movsf): Likewise for SFmode. + (delay_cycles_2): Updated instructions to be emitted as AVRTINY does + not have sbiw. + * config/avr/avr-protos.h: Added function prototype for + tiny_valid_direct_memory_access_range. + * config/avr/avr-tables.opt: Regenerate. + * gcc/config/avr/t-multilib: Regenerate. + * doc/avr-mmcu.texi: Regenerate. + 2014-10-21 Andrew Pinski * doc/invoke.texi (AARCH64/mtune): Document thunderx as an diff --git a/gcc/config/avr/avr-arch.h b/gcc/config/avr/avr-arch.h index 4e368affc16..168793efccb 100644 --- a/gcc/config/avr/avr-arch.h +++ b/gcc/config/avr/avr-arch.h @@ -37,6 +37,7 @@ enum avr_arch ARCH_AVR5, ARCH_AVR51, ARCH_AVR6, + ARCH_AVRTINY, ARCH_AVRXMEGA2, ARCH_AVRXMEGA4, ARCH_AVRXMEGA5, @@ -77,6 +78,9 @@ typedef struct and thus also the RAMPX, RAMPY and RAMPZ registers. */ int have_rampd; + /* This is a TINY core. */ + int tiny_p; + /* Default start of data section address for architecture. */ int default_data_section_start; diff --git a/gcc/config/avr/avr-c.c b/gcc/config/avr/avr-c.c index d5c40e6b34a..f66f36186f7 100644 --- a/gcc/config/avr/avr-c.c +++ b/gcc/config/avr/avr-c.c @@ -321,6 +321,23 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile) } if (AVR_XMEGA) cpp_define (pfile, "__AVR_XMEGA__"); + + if (AVR_TINY) + { + cpp_define (pfile, "__AVR_TINY__"); + + /* Define macro "__AVR_TINY_PM_BASE_ADDRESS__" with mapped program memory + start address. This macro shall be referred where mapped program memory + is accessed. (Eg. copying data section (do_copy_data) contents to data + memory region. + NOTE: + Program memory of AVR_TINY devices can not be accessed directly, it has + been mapped to the data memory. For AVR_TINY devices (ATtiny4/ 5/ 9/ 10/ + 20 and 40) mapped program memory starts at 0x4000. + */ + cpp_define (pfile, "__AVR_TINY_PM_BASE_ADDRESS__=0x4000"); + } + if (avr_current_arch->have_eijmp_eicall) { cpp_define (pfile, "__AVR_HAVE_EIJMP_EICALL__"); @@ -376,7 +393,10 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile) /* Only supply __FLASH macro if the address space is reasonable for this target. The address space qualifier itself is still supported, but using it will throw an error. */ - && avr_addrspace[i].segment < avr_n_flash) + && avr_addrspace[i].segment < avr_n_flash + /* Only support __MEMX macro if we have LPM. */ + && (AVR_HAVE_LPM || avr_addrspace[i].pointer_size <= 2)) + { const char *name = avr_addrspace[i].name; char *Name = (char*) alloca (1 + strlen (name)); diff --git a/gcc/config/avr/avr-devices.c b/gcc/config/avr/avr-devices.c index 9044d713d89..a926d2055cb 100644 --- a/gcc/config/avr/avr-devices.c +++ b/gcc/config/avr/avr-devices.c @@ -31,29 +31,30 @@ const avr_arch_t avr_arch_types[] = { /* unknown device specified */ - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, NULL, "avr2" }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, NULL, "avr2" }, /* - A M J LM E E E X R d S S O A - S U M PO L L I M A a t F ff r - M L P MV P P J E M t a R s c - XW M M M G P a r e h - X P A D t t ID */ - { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "1", "avr1" }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "2", "avr2" }, - { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0x0060, 32, "25", "avr25" }, - { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "3", "avr3" }, - { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0x0060, 32, "31", "avr31" }, - { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0x0060, 32, "35", "avr35" }, - { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, 32, "4", "avr4" }, - { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0x0060, 32, "5", "avr5" }, - { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, 32, "51", "avr51" }, - { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0x0060, 32, "6", "avr6" }, + A M J LM E E E X R T d S S O A + S U M PO L L I M A I a t F ff r + M L P MV P P J E M N t a R s c + XW M M M G P Y a r e h + X P A D t t ID */ + { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "1", "avr1" }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "2", "avr2" }, + { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "25", "avr25" }, + { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0x0060, 32, "3", "avr3" }, + { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0x0060, 32, "31", "avr31" }, + { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "35", "avr35" }, + { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "4", "avr4" }, + { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0x0060, 32, "5", "avr5" }, + { 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0x0060, 32, "51", "avr51" }, + { 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0x0060, 32, "6", "avr6" }, - { 0, 1, 1, 1, 0, 0, 0, 1, 0, 0x2000, 0, "102", "avrxmega2" }, - { 0, 1, 1, 1, 1, 1, 0, 1, 0, 0x2000, 0, "104", "avrxmega4" }, - { 0, 1, 1, 1, 1, 1, 0, 1, 1, 0x2000, 0, "105", "avrxmega5" }, - { 0, 1, 1, 1, 1, 1, 1, 1, 0, 0x2000, 0, "106", "avrxmega6" }, - { 0, 1, 1, 1, 1, 1, 1, 1, 1, 0x2000, 0, "107", "avrxmega7" } + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0x0040, 0, "100", "avrtiny" }, + { 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0x2000, 0, "102", "avrxmega2" }, + { 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0x2000, 0, "104", "avrxmega4" }, + { 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0x2000, 0, "105", "avrxmega5" }, + { 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0x2000, 0, "106", "avrxmega6" }, + { 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0x2000, 0, "107", "avrxmega7" } }; const avr_arch_info_t @@ -85,6 +86,9 @@ avr_texinfo[] = { ARCH_AVR6, "``Enhanced'' devices with 3-byte PC, i.e.@: with more than 128@tie{}KiB " "of program memory." }, + { ARCH_AVRTINY, + "``TINY'' Tiny core devices with 512@tie{}B up to 4@tie{}KiB of " + "program memory." }, { ARCH_AVRXMEGA2, "``XMEGA'' devices with more than 8@tie{}KiB and up to 64@tie{}KiB " "of program memory." }, diff --git a/gcc/config/avr/avr-mcus.def b/gcc/config/avr/avr-mcus.def index acec6a5c7a1..e890149f158 100644 --- a/gcc/config/avr/avr-mcus.def +++ b/gcc/config/avr/avr-mcus.def @@ -326,6 +326,14 @@ AVR_MCU ("avrxmega7", ARCH_AVRXMEGA7, AVR_ISA_NONE, NULL, AVR_MCU ("atxmega128a1", ARCH_AVRXMEGA7, AVR_ISA_NONE, "__AVR_ATxmega128A1__", 0x2000, 0x0, 3, "x128a1") AVR_MCU ("atxmega128a1u", ARCH_AVRXMEGA7, AVR_ISA_RMW, "__AVR_ATxmega128A1U__", 0x2000, 0x0, 3, "x128a1u") AVR_MCU ("atxmega128a4u", ARCH_AVRXMEGA7, AVR_ISA_RMW, "__AVR_ATxmega128A4U__", 0x2000, 0x0, 3, "x128a4u") +/* Tiny family */ +AVR_MCU ("avrtiny", ARCH_AVRTINY, AVR_ISA_NONE, NULL, 0x0040, 0x0, 1, "tn10") +AVR_MCU ("attiny4", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny4__", 0x0040, 0x0, 1, "tn4") +AVR_MCU ("attiny5", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny5__", 0x0040, 0x0, 1, "tn5") +AVR_MCU ("attiny9", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny9__", 0x0040, 0x0, 1, "tn9") +AVR_MCU ("attiny10", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny10__", 0x0040, 0x0, 1, "tn10") +AVR_MCU ("attiny20", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny20__", 0x0040, 0x0, 1, "tn20") +AVR_MCU ("attiny40", ARCH_AVRTINY, AVR_ISA_NONE, "__AVR_ATtiny40__", 0x0040, 0x0, 1, "tn40") /* Assembler only. */ AVR_MCU ("avr1", ARCH_AVR1, AVR_ISA_NONE, NULL, 0x0060, 0x0, 1, "s1200") AVR_MCU ("at90s1200", ARCH_AVR1, AVR_ISA_NONE, "__AVR_AT90S1200__", 0x0060, 0x0, 1, "s1200") diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index d0784042ef4..6342c087d4a 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -46,6 +46,7 @@ extern void avr_init_cumulative_args (CUMULATIVE_ARGS*, tree, rtx, tree); #ifdef RTX_CODE extern int avr_hard_regno_call_part_clobbered (unsigned, enum machine_mode); +extern bool tiny_valid_direct_memory_access_range(rtx, enum machine_mode); extern const char *output_movqi (rtx_insn *insn, rtx operands[], int *l); extern const char *output_movhi (rtx_insn *insn, rtx operands[], int *l); extern const char *output_movsisf (rtx_insn *insn, rtx operands[], int *l); diff --git a/gcc/config/avr/avr-tables.opt b/gcc/config/avr/avr-tables.opt index 4de0dbd750e..c7e72c2ad77 100644 --- a/gcc/config/avr/avr-tables.opt +++ b/gcc/config/avr/avr-tables.opt @@ -65,6 +65,9 @@ Enum(avr_arch) String(avrxmega6) Value(ARCH_AVRXMEGA6) EnumValue Enum(avr_arch) String(avrxmega7) Value(ARCH_AVRXMEGA7) +EnumValue +Enum(avr_arch) String(avrtiny) Value(ARCH_AVRTINY) + EnumValue Enum(avr_arch) String(avr1) Value(ARCH_AVR1) diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index 704c6f7b35e..6c781c9466d 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -88,6 +88,17 @@ ((SYMBOL_REF_FLAGS (sym) & AVR_SYMBOL_FLAG_PROGMEM) \ / SYMBOL_FLAG_MACH_DEP) +#define TINY_ADIW(REG1, REG2, I) \ + "subi " #REG1 ",lo8(-(" #I "))" CR_TAB \ + "sbci " #REG2 ",hi8(-(" #I "))" + +#define TINY_SBIW(REG1, REG2, I) \ + "subi " #REG1 ",lo8((" #I "))" CR_TAB \ + "sbci " #REG2 ",hi8((" #I "))" + +#define AVR_TMP_REGNO (AVR_TINY ? TMP_REGNO_TINY : TMP_REGNO) +#define AVR_ZERO_REGNO (AVR_TINY ? ZERO_REGNO_TINY : ZERO_REGNO) + /* Known address spaces. The order must be the same as in the respective enum from avr.h (or designated initialized must be used). */ const avr_addrspace_t avr_addrspace[ADDR_SPACE_COUNT] = @@ -156,6 +167,9 @@ 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 +/* Last call saved register */ +#define LAST_CALLEE_SAVED_REG (AVR_TINY ? 19 : 17) + /* Implicit target register of LPM instruction (R0) */ extern GTY(()) rtx lpm_reg_rtx; rtx lpm_reg_rtx; @@ -365,7 +379,7 @@ avr_option_override (void) avr_addr.rampy = 0x3A + avr_current_arch->sfr_offset; avr_addr.rampx = 0x39 + avr_current_arch->sfr_offset; avr_addr.rampd = 0x38 + avr_current_arch->sfr_offset; - avr_addr.ccp = 0x34 + avr_current_arch->sfr_offset; + avr_addr.ccp = (AVR_TINY ? 0x3C : 0x34) + avr_current_arch->sfr_offset; /* SP: Stack Pointer (SP_H:SP_L) */ avr_addr.sp_l = 0x3D + avr_current_arch->sfr_offset; @@ -397,8 +411,8 @@ avr_init_expanders (void) 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]; + tmp_reg_rtx = all_regs_rtx[AVR_TMP_REGNO]; + zero_reg_rtx = all_regs_rtx[AVR_ZERO_REGNO]; lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z); @@ -410,6 +424,11 @@ avr_init_expanders (void) xstring_empty = gen_rtx_CONST_STRING (VOIDmode, ""); xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e"); + + /* TINY core does not have regs r10-r16, but avr-dimode.md expects them + to be present */ + if (AVR_TINY) + avr_have_dimode = false; } @@ -918,7 +937,7 @@ sequent_regs_live (void) int live_seq = 0; int cur_seq = 0; - for (reg = 0; reg < 18; ++reg) + for (reg = 0; reg <= LAST_CALLEE_SAVED_REG; ++reg) { if (fixed_regs[reg]) { @@ -1031,7 +1050,7 @@ emit_push_sfr (rtx sfr, bool frame_related_p, bool clr_p) RTX_FRAME_RELATED_P (insn) = 1; /* PUSH __tmp_reg__ */ - emit_push_byte (TMP_REGNO, frame_related_p); + emit_push_byte (AVR_TMP_REGNO, frame_related_p); if (clr_p) { @@ -1057,7 +1076,8 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set) && live_seq && !isr_p && !cfun->machine->is_OS_task - && !cfun->machine->is_OS_main); + && !cfun->machine->is_OS_main + && !AVR_TINY); if (minimize && (frame_pointer_needed @@ -1094,11 +1114,11 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set) /* Note that live_seq always contains r28+r29, but the other registers to be saved are all below 18. */ - first_reg = 18 - (live_seq - 2); + first_reg = (LAST_CALLEE_SAVED_REG + 1) - (live_seq - 2); for (reg = 29, offset = -live_seq + 1; reg >= first_reg; - reg = (reg == 28 ? 17 : reg - 1), ++offset) + reg = (reg == 28 ? LAST_CALLEE_SAVED_REG : reg - 1), ++offset) { rtx m, r; @@ -1338,10 +1358,10 @@ avr_expand_prologue (void) emit_insn (gen_enable_interrupt ()); /* Push zero reg. */ - emit_push_byte (ZERO_REGNO, true); + emit_push_byte (AVR_ZERO_REGNO, true); /* Push tmp reg. */ - emit_push_byte (TMP_REGNO, true); + emit_push_byte (AVR_TMP_REGNO, true); /* Push SREG. */ /* ??? There's no dwarf2 column reserved for SREG. */ @@ -1483,7 +1503,8 @@ avr_expand_epilogue (bool sibcall_p) && live_seq && !isr_p && !cfun->machine->is_OS_task - && !cfun->machine->is_OS_main); + && !cfun->machine->is_OS_main + && !AVR_TINY); if (minimize && (live_seq > 4 @@ -1641,14 +1662,14 @@ avr_expand_epilogue (bool sibcall_p) /* Restore SREG using tmp_reg as scratch. */ - emit_pop_byte (TMP_REGNO); + emit_pop_byte (AVR_TMP_REGNO); emit_move_insn (sreg_rtx, tmp_reg_rtx); /* Restore tmp REG. */ - emit_pop_byte (TMP_REGNO); + emit_pop_byte (AVR_TMP_REGNO); /* Restore zero REG. */ - emit_pop_byte (ZERO_REGNO); + emit_pop_byte (AVR_ZERO_REGNO); } if (!sibcall_p) @@ -2130,10 +2151,14 @@ avr_print_operand_punct_valid_p (unsigned char code) static void avr_print_operand (FILE *file, rtx x, int code) { - int abcd = 0; + int abcd = 0, ef = 0, ij = 0; if (code >= 'A' && code <= 'D') abcd = code - 'A'; + else if (code == 'E' || code == 'F') + ef = code - 'E'; + else if (code == 'I' || code == 'J') + ij = code - 'I'; if (code == '~') { @@ -2170,6 +2195,16 @@ avr_print_operand (FILE *file, rtx x, int code) else fatal_insn ("operands to %T/%t must be reg + const_int:", x); } + else if (code == 'E' || code == 'F') + { + rtx op = XEXP(x, 0); + fprintf (file, reg_names[REGNO (op) + ef]); + } + else if (code == 'I' || code == 'J') + { + rtx op = XEXP(XEXP(x, 0), 0); + fprintf (file, reg_names[REGNO (op) + ij]); + } else if (REG_P (x)) { if (x == zero_reg_rtx) @@ -2196,7 +2231,7 @@ avr_print_operand (FILE *file, rtx x, int code) fprintf (file, "__RAMPX__"); else if (AVR_HAVE_RAMPD && ival == avr_addr.rampd) fprintf (file, "__RAMPD__"); - else if (AVR_XMEGA && ival == avr_addr.ccp) + else if ((AVR_XMEGA || AVR_TINY) && ival == avr_addr.ccp) fprintf (file, "__CCP__"); else if (ival == avr_addr.sreg) fprintf (file, "__SREG__"); else if (ival == avr_addr.sp_l) fprintf (file, "__SP_L__"); @@ -2239,6 +2274,13 @@ avr_print_operand (FILE *file, rtx x, int code) avr_print_operand (file, XEXP (addr, 1), 0); } + else if (code == 'b') + { + if (GET_CODE (addr) != PLUS) + fatal_insn ("bad address, not (reg+disp):", addr); + + avr_print_operand_address (file, XEXP (addr, 0)); + } else if (code == 'p' || code == 'r') { if (GET_CODE (addr) != POST_INC && GET_CODE (addr) != PRE_DEC) @@ -2596,7 +2638,7 @@ avr_simplify_comparison_p (enum machine_mode mode, RTX_CODE op, rtx x) int avr_function_arg_regno_p(int r) { - return (r >= 8 && r <= 25); + return (AVR_TINY ? r >= 20 && r <= 25 : r >= 8 && r <= 25); } @@ -2608,7 +2650,7 @@ void avr_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname, tree fndecl ATTRIBUTE_UNUSED) { - cum->nregs = 18; + cum->nregs = AVR_TINY ? 6 : 18; cum->regno = FIRST_CUM_REG; if (!libname && stdarg_p (fntype)) cum->nregs = 0; @@ -3144,6 +3186,35 @@ avr_out_xload (rtx_insn *insn ATTRIBUTE_UNUSED, rtx *op, int *plen) return ""; } +/* +AVRTC-579 +if operand is symbol or constant expression with value > 0xbf + return false, otherwise true +This check is used to avoid lds/sts instruction with invalid memory +access range (valid range 0x40..0xbf). For io operand range 0x0..0x3f, +in/out instruction will be generated. +*/ +bool tiny_valid_direct_memory_access_range(rtx op, enum machine_mode mode) +{ + rtx x; + + if (!AVR_TINY) + return true; + + x = XEXP(op,0); + + if (MEM_P(op) && x && (GET_CODE(x) == SYMBOL_REF)) + { + return false; + } + if (MEM_P(op) && x && (CONSTANT_ADDRESS_P (x)) && + !(IN_RANGE (INTVAL (x), 0, 0xC0 - GET_MODE_SIZE (mode)))) + { + return false; + } + + return true; +} const char* output_movqi (rtx_insn *insn, rtx operands[], int *plen) @@ -3272,6 +3343,24 @@ output_movhi (rtx_insn *insn, rtx xop[], int *plen) return ""; } +/* Same as out_movqi_r_mr, but TINY does not have ADIW or SBIW */ +static const char* +avr_out_movqi_r_mr_reg_disp_tiny (rtx_insn *insn, rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx x = XEXP (src, 0); + + avr_asm_len (TINY_ADIW (%I1, %J1, %o1) CR_TAB + "ld %0,%b1" , op, plen, -3); + + if (!reg_overlap_mentioned_p (dest, XEXP (x,0)) + && !reg_unused_after (insn, XEXP (x,0))) + avr_asm_len (TINY_SBIW (%I1, %J1, %o1), op, plen, 2); + + return ""; +} + static const char* out_movqi_r_mr (rtx_insn *insn, rtx op[], int *plen) { @@ -3281,11 +3370,13 @@ out_movqi_r_mr (rtx_insn *insn, rtx op[], int *plen) if (CONSTANT_ADDRESS_P (x)) { + int n_words = AVR_TINY ? 1 : 2; return optimize > 0 && io_address_operand (x, QImode) ? avr_asm_len ("in %0,%i1", op, plen, -1) - : avr_asm_len ("lds %0,%m1", op, plen, -2); + : avr_asm_len ("lds %0,%m1", op, plen, -n_words); } - else if (GET_CODE (x) == PLUS + + if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0)) && CONST_INT_P (XEXP (x, 1))) { @@ -3293,6 +3384,9 @@ out_movqi_r_mr (rtx_insn *insn, rtx op[], int *plen) int disp = INTVAL (XEXP (x, 1)); + if (AVR_TINY) + return avr_out_movqi_r_mr_reg_disp_tiny (insn, op, plen); + if (disp - GET_MODE_SIZE (GET_MODE (src)) >= 63) { if (REGNO (XEXP (x, 0)) != REG_Y) @@ -3332,6 +3426,82 @@ out_movqi_r_mr (rtx_insn *insn, rtx op[], int *plen) return avr_asm_len ("ld %0,%1", op, plen, -1); } +/* Same as movhi_r_mr, but TINY does not have ADIW, SBIW and LDD */ +static const char* +avr_out_movhi_r_mr_reg_no_disp_tiny (rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (src, 0); + + int reg_dest = true_regnum (dest); + int reg_base = true_regnum (base); + + if (reg_dest == reg_base) /* R = (R) */ + return avr_asm_len ("ld __tmp_reg__,%1+" CR_TAB + "ld %B0,%1" CR_TAB + "mov %A0,__tmp_reg__", op, plen, -3); + + return avr_asm_len ("ld %A0,%1" CR_TAB + TINY_ADIW (%E1, %F1, 1) CR_TAB + "ld %B0,%1" CR_TAB + TINY_SBIW (%E1, %F1, 1), op, plen, -6); + +} + +/* Same as movhi_r_mr, but TINY does not have ADIW, SBIW and LDD */ +static const char* +avr_out_movhi_r_mr_reg_disp_tiny (rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (src, 0); + + int reg_dest = true_regnum (dest); + int reg_base = true_regnum (XEXP (base, 0)); + + if (reg_base == reg_dest) + { + return avr_asm_len (TINY_ADIW (%I1, %J1, %o1) CR_TAB + "ld __tmp_reg__,%b1+" CR_TAB + "ld %B0,%b1" CR_TAB + "mov %A0,__tmp_reg__", op, plen, -5); + } + else + { + return avr_asm_len (TINY_ADIW (%I1, %J1, %o1) CR_TAB + "ld %A0,%b1+" CR_TAB + "ld %B0,%b1" CR_TAB + TINY_SBIW (%I1, %J1, %o1+1), op, plen, -6); + } +} + +/* Same as movhi_r_mr, but TINY does not have ADIW, SBIW and LDD */ +static const char* +avr_out_movhi_r_mr_pre_dec_tiny (rtx_insn *insn, rtx op[], int *plen) +{ + int mem_volatile_p = 0; + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (src, 0); + + /* "volatile" forces reading low byte first, even if less efficient, + for correct operation with 16-bit I/O registers. */ + mem_volatile_p = MEM_VOLATILE_P (src); + + if (reg_overlap_mentioned_p (dest, XEXP (base, 0))) + fatal_insn ("incorrect insn:", insn); + + if (!mem_volatile_p) + return avr_asm_len ("ld %B0,%1" CR_TAB + "ld %A0,%1", op, plen, -2); + + return avr_asm_len (TINY_SBIW (%I1, %J1, 2) CR_TAB + "ld %A0,%p1+" CR_TAB + "ld %B0,%p1" CR_TAB + TINY_SBIW (%I1, %J1, 1), op, plen, -6); +} + static const char* out_movhi_r_mr (rtx_insn *insn, rtx op[], int *plen) { @@ -3346,6 +3516,9 @@ out_movhi_r_mr (rtx_insn *insn, rtx op[], int *plen) if (reg_base > 0) { + if (AVR_TINY) + return avr_out_movhi_r_mr_reg_no_disp_tiny (op, plen); + if (reg_dest == reg_base) /* R = (R) */ return avr_asm_len ("ld __tmp_reg__,%1+" CR_TAB "ld %B0,%1" CR_TAB @@ -3368,6 +3541,9 @@ out_movhi_r_mr (rtx_insn *insn, rtx op[], int *plen) int disp = INTVAL (XEXP (base, 1)); int reg_base = true_regnum (XEXP (base, 0)); + if (AVR_TINY) + return avr_out_movhi_r_mr_reg_disp_tiny (op, plen); + if (disp > MAX_LD_OFFSET (GET_MODE (src))) { if (REGNO (XEXP (base, 0)) != REG_Y) @@ -3379,7 +3555,7 @@ out_movhi_r_mr (rtx_insn *insn, rtx op[], int *plen) "ldd %B0,Y+63" CR_TAB "sbiw r28,%o1-62", op, plen, -4) - : avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB + : avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB "sbci r29,hi8(-%o1)" CR_TAB "ld %A0,Y" CR_TAB "ldd %B0,Y+1" CR_TAB @@ -3413,6 +3589,9 @@ out_movhi_r_mr (rtx_insn *insn, rtx op[], int *plen) } else if (GET_CODE (base) == PRE_DEC) /* (--R) */ { + if (AVR_TINY) + return avr_out_movhi_r_mr_pre_dec_tiny (insn, op, plen); + if (reg_overlap_mentioned_p (dest, XEXP (base, 0))) fatal_insn ("incorrect insn:", insn); @@ -3440,18 +3619,112 @@ out_movhi_r_mr (rtx_insn *insn, rtx op[], int *plen) } else if (CONSTANT_ADDRESS_P (base)) { + int n_words = AVR_TINY ? 2 : 4; return optimize > 0 && io_address_operand (base, HImode) ? avr_asm_len ("in %A0,%i1" CR_TAB "in %B0,%i1+1", op, plen, -2) : avr_asm_len ("lds %A0,%m1" CR_TAB - "lds %B0,%m1+1", op, plen, -4); + "lds %B0,%m1+1", op, plen, -n_words); } fatal_insn ("unknown move insn:",insn); return ""; } +static const char* +avr_out_movsi_r_mr_reg_no_disp_tiny (rtx_insn *insn, rtx op[], int *l) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (src, 0); + int reg_dest = true_regnum (dest); + int reg_base = true_regnum (base); + + if (reg_dest == reg_base) + { + /* "ld r26,-X" is undefined */ + return *l=9, (TINY_ADIW (%E1, %F1, 3) CR_TAB + "ld %D0,%1" CR_TAB + "ld %C0,-%1" CR_TAB + "ld __tmp_reg__,-%1" CR_TAB + TINY_SBIW (%E1, %F1, 1) CR_TAB + "ld %A0,%1" CR_TAB + "mov %B0,__tmp_reg__"); + } + else if (reg_dest == reg_base - 2) + { + return *l=5, ("ld %A0,%1+" CR_TAB + "ld %B0,%1+" CR_TAB + "ld __tmp_reg__,%1+" CR_TAB + "ld %D0,%1" CR_TAB + "mov %C0,__tmp_reg__"); + } + else if (reg_unused_after (insn, base)) + { + return *l=4, ("ld %A0,%1+" CR_TAB + "ld %B0,%1+" CR_TAB + "ld %C0,%1+" CR_TAB + "ld %D0,%1"); + } + else + { + return *l=6, ("ld %A0,%1+" CR_TAB + "ld %B0,%1+" CR_TAB + "ld %C0,%1+" CR_TAB + "ld %D0,%1" CR_TAB + TINY_SBIW (%E1, %F1, 3)); + } +} + +static const char* +avr_out_movsi_r_mr_reg_disp_tiny (rtx_insn *insn, rtx op[], int *l) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (src, 0); + int reg_dest = true_regnum (dest); + int reg_base = true_regnum (XEXP (base, 0)); + + if (reg_dest == reg_base) + { + /* "ld r26,-X" is undefined */ + return *l=9, (TINY_ADIW (%I1, %J1, %o1+3) CR_TAB + "ld %D0,%b1" CR_TAB + "ld %C0,-%b1" CR_TAB + "ld __tmp_reg__,-%b1" CR_TAB + TINY_SBIW (%I1, %J1, 1) CR_TAB + "ld %A0,%b1" CR_TAB + "mov %B0,__tmp_reg__"); + } + else if (reg_dest == reg_base - 2) + { + return *l=7, (TINY_ADIW (%I1, %J1, %o1) CR_TAB + "ld %A0,%b1+" CR_TAB + "ld %B0,%b1+" CR_TAB + "ld __tmp_reg__,%b1+" CR_TAB + "ld %D0,%b1" CR_TAB + "mov %C0,__tmp_reg__"); + } + else if (reg_unused_after (insn, XEXP (base, 0))) + { + return *l=6, (TINY_ADIW (%I1, %J1, %o1) CR_TAB + "ld %A0,%b1+" CR_TAB + "ld %B0,%b1+" CR_TAB + "ld %C0,%b1+" CR_TAB + "ld %D0,%b1"); + } + else + { + return *l=8, (TINY_ADIW (%I1, %J1, %o1) CR_TAB + "ld %A0,%b1+" CR_TAB + "ld %B0,%b1+" CR_TAB + "ld %C0,%b1+" CR_TAB + "ld %D0,%b1" CR_TAB + TINY_SBIW (%I1, %J1, %o1+3)); + } +} + static const char* out_movsi_r_mr (rtx_insn *insn, rtx op[], int *l) { @@ -3467,6 +3740,9 @@ out_movsi_r_mr (rtx_insn *insn, rtx op[], int *l) if (reg_base > 0) { + if (AVR_TINY) + return avr_out_movsi_r_mr_reg_no_disp_tiny (insn, op, l); + if (reg_base == REG_X) /* (R26) */ { if (reg_dest == REG_X) @@ -3521,6 +3797,9 @@ out_movsi_r_mr (rtx_insn *insn, rtx op[], int *l) { int disp = INTVAL (XEXP (base, 1)); + if (AVR_TINY) + return avr_out_movsi_r_mr_reg_disp_tiny (insn, op, l); + if (disp > MAX_LD_OFFSET (GET_MODE (src))) { if (REGNO (XEXP (base, 0)) != REG_Y) @@ -3604,15 +3883,133 @@ out_movsi_r_mr (rtx_insn *insn, rtx op[], int *l) "ld %C0,%1" CR_TAB "ld %D0,%1"); else if (CONSTANT_ADDRESS_P (base)) - return *l=8, ("lds %A0,%m1" CR_TAB + { + if (io_address_operand (base, SImode)) + { + *l = 4; + return ("in %A0,%i1" CR_TAB + "in %B0,%i1+1" CR_TAB + "in %C0,%i1+2" CR_TAB + "in %D0,%i1+3"); + } + else + { + *l = AVR_TINY ? 4 : 8; + return ("lds %A0,%m1" CR_TAB "lds %B0,%m1+1" CR_TAB "lds %C0,%m1+2" CR_TAB "lds %D0,%m1+3"); + } + } fatal_insn ("unknown move insn:",insn); return ""; } +static const char* +avr_out_movsi_mr_r_reg_no_disp_tiny (rtx_insn *insn, rtx op[], int *l) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (dest, 0); + int reg_base = true_regnum (base); + int reg_src = true_regnum (src); + + if (reg_base == reg_src) + { + /* "ld r26,-X" is undefined */ + if (reg_unused_after (insn, base)) + { + return *l=7, ("mov __tmp_reg__, %B1" CR_TAB + "st %0,%A1" CR_TAB + TINY_ADIW (%E0, %F0, 1) CR_TAB + "st %0+,__tmp_reg__" CR_TAB + "st %0+,%C1" CR_TAB + "st %0+,%D1"); + } + else + { + return *l=9, ("mov __tmp_reg__, %B1" CR_TAB + "st %0,%A1" CR_TAB + TINY_ADIW (%E0, %F0, 1) CR_TAB + "st %0+,__tmp_reg__" CR_TAB + "st %0+,%C1" CR_TAB + "st %0+,%D1" CR_TAB + TINY_SBIW (%E0, %F0, 3)); + } + } + else if (reg_base == reg_src + 2) + { + if (reg_unused_after (insn, base)) + return *l=7, ("mov __zero_reg__,%C1" CR_TAB + "mov __tmp_reg__,%D1" CR_TAB + "st %0+,%A1" CR_TAB + "st %0+,%B1" CR_TAB + "st %0+,__zero_reg__" CR_TAB + "st %0,__tmp_reg__" CR_TAB + "clr __zero_reg__"); + else + return *l=9, ("mov __zero_reg__,%C1" CR_TAB + "mov __tmp_reg__,%D1" CR_TAB + "st %0+,%A1" CR_TAB + "st %0+,%B1" CR_TAB + "st %0+,__zero_reg__" CR_TAB + "st %0,__tmp_reg__" CR_TAB + "clr __zero_reg__" CR_TAB + TINY_SBIW (%E0, %F0, 3)); + } + + return *l=6, ("st %0+,%A1" CR_TAB + "st %0+,%B1" CR_TAB + "st %0+,%C1" CR_TAB + "st %0,%D1" CR_TAB + TINY_SBIW (%E0, %F0, 3)); +} + +static const char* +avr_out_movsi_mr_r_reg_disp_tiny (rtx op[], int *l) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (dest, 0); + int reg_base = REGNO (XEXP (base, 0)); + int reg_src =true_regnum (src); + + if (reg_base == reg_src) + { + *l = 11; + return ("mov __tmp_reg__,%A2" CR_TAB + "mov __zero_reg__,%B2" CR_TAB + TINY_ADIW (%I0, %J0, %o0) CR_TAB + "st %b0+,__tmp_reg__" CR_TAB + "st %b0+,__zero_reg__" CR_TAB + "st %b0+,%C2" CR_TAB + "st %b0,%D2" CR_TAB + "clr __zero_reg__" CR_TAB + TINY_SBIW (%I0, %J0, %o0+3)); + } + else if (reg_src == reg_base - 2) + { + *l = 11; + return ("mov __tmp_reg__,%C2" CR_TAB + "mov __zero_reg__,%D2" CR_TAB + TINY_ADIW (%I0, %J0, %o0) CR_TAB + "st %b0+,%A0" CR_TAB + "st %b0+,%B0" CR_TAB + "st %b0+,__tmp_reg__" CR_TAB + "st %b0,__zero_reg__" CR_TAB + "clr __zero_reg__" CR_TAB + TINY_SBIW (%I0, %J0, %o0+3)); + } + *l = 8; + return (TINY_ADIW (%I0, %J0, %o0) CR_TAB + "st %b0+,%A1" CR_TAB + "st %b0+,%B1" CR_TAB + "st %b0+,%C1" CR_TAB + "st %b0,%D1" CR_TAB + TINY_SBIW (%I0, %J0, %o0+3)); +} + static const char* out_movsi_mr_r (rtx_insn *insn, rtx op[], int *l) { @@ -3627,12 +4024,29 @@ out_movsi_mr_r (rtx_insn *insn, rtx op[], int *l) l = &tmp; if (CONSTANT_ADDRESS_P (base)) - return *l=8,("sts %m0,%A1" CR_TAB - "sts %m0+1,%B1" CR_TAB - "sts %m0+2,%C1" CR_TAB - "sts %m0+3,%D1"); + { + if (io_address_operand (base, SImode)) + { + return *l=4,("out %i0, %A1" CR_TAB + "out %i0+1,%B1" CR_TAB + "out %i0+2,%C1" CR_TAB + "out %i0+3,%D1"); + } + else + { + *l = AVR_TINY ? 4 : 8; + return ("sts %m0,%A1" CR_TAB + "sts %m0+1,%B1" CR_TAB + "sts %m0+2,%C1" CR_TAB + "sts %m0+3,%D1"); + } + } + if (reg_base > 0) /* (r) */ { + if (AVR_TINY) + return avr_out_movsi_mr_r_reg_no_disp_tiny (insn, op, l); + if (reg_base == REG_X) /* (R26) */ { if (reg_src == REG_X) @@ -3689,6 +4103,10 @@ out_movsi_mr_r (rtx_insn *insn, rtx op[], int *l) else if (GET_CODE (base) == PLUS) /* (R + i) */ { int disp = INTVAL (XEXP (base, 1)); + + if (AVR_TINY) + return avr_out_movsi_mr_r_reg_disp_tiny (op, l); + reg_base = REGNO (XEXP (base, 0)); if (disp > MAX_LD_OFFSET (GET_MODE (dest))) { @@ -3848,6 +4266,73 @@ output_movsisf (rtx_insn *insn, rtx operands[], int *l) /* Handle loads of 24-bit types from memory to register. */ +static const char* +avr_out_load_psi_reg_no_disp_tiny (rtx_insn *insn, rtx *op, int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (src, 0); + int reg_dest = true_regnum (dest); + int reg_base = true_regnum (base); + + if (reg_base == reg_dest) + { + return avr_asm_len (TINY_ADIW (%E1, %F1, 2) CR_TAB + "ld %C0,%1" CR_TAB + "ld __tmp_reg__,-%1" CR_TAB + TINY_SBIW (%E1, %F1, 1) CR_TAB + "ld %A0,%1" CR_TAB + "mov %B0,__tmp_reg__", op, plen, -8); + } + else + { + return avr_asm_len ("ld %A0,%1+" CR_TAB + "ld %B0,%1+" CR_TAB + "ld %C0,%1", op, plen, -3); + + if (reg_dest != reg_base - 2 && + !reg_unused_after (insn, base)) + { + avr_asm_len (TINY_SBIW (%E1, %F1, 2), op, plen, 2); + } + return ""; + } +} + +static const char* +avr_out_load_psi_reg_disp_tiny (rtx_insn *insn, rtx *op, int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (src, 0); + int reg_dest = true_regnum (dest); + int reg_base = true_regnum (base); + + reg_base = true_regnum (XEXP (base, 0)); + if (reg_base == reg_dest) + { + return avr_asm_len (TINY_ADIW (%I1, %J1, %o1+2) CR_TAB + "ld %C0,%b1" CR_TAB + "ld __tmp_reg__,-%b1" CR_TAB + TINY_SBIW (%I1, %J1, 1) CR_TAB + "ld %A0,%b1" CR_TAB + "mov %B0,__tmp_reg__", op, plen, -8); + } + else + { + avr_asm_len (TINY_ADIW (%I1, %J1, %o1) CR_TAB + "ld %A0,%b1+" CR_TAB + "ld %B0,%b1+" CR_TAB + "ld %C0,%b1", op, plen, -5); + + if (reg_dest != (reg_base - 2) + && !reg_unused_after (insn, XEXP (base, 0))) + avr_asm_len (TINY_SBIW (%I1, %J1, %o1+2), op, plen, 2); + + return ""; + } +} + static const char* avr_out_load_psi (rtx_insn *insn, rtx *op, int *plen) { @@ -3859,6 +4344,9 @@ avr_out_load_psi (rtx_insn *insn, rtx *op, int *plen) if (reg_base > 0) { + if (AVR_TINY) + return avr_out_load_psi_reg_no_disp_tiny (insn, op, plen); + if (reg_base == REG_X) /* (R26) */ { if (reg_dest == REG_X) @@ -3901,6 +4389,9 @@ avr_out_load_psi (rtx_insn *insn, rtx *op, int *plen) { int disp = INTVAL (XEXP (base, 1)); + if (AVR_TINY) + return avr_out_load_psi_reg_disp_tiny (insn, op, plen); + if (disp > MAX_LD_OFFSET (GET_MODE (src))) { if (REGNO (XEXP (base, 0)) != REG_Y) @@ -3969,14 +4460,94 @@ avr_out_load_psi (rtx_insn *insn, rtx *op, int *plen) "ld %C0,%1", op, plen, -3); else if (CONSTANT_ADDRESS_P (base)) - return avr_asm_len ("lds %A0,%m1" CR_TAB - "lds %B0,%m1+1" CR_TAB - "lds %C0,%m1+2", op, plen , -6); + { + int n_words = AVR_TINY ? 3 : 6; + return avr_asm_len ("lds %A0,%m1" CR_TAB + "lds %B0,%m1+1" CR_TAB + "lds %C0,%m1+2", op, plen , -n_words); + } fatal_insn ("unknown move insn:",insn); return ""; } + +static const char* +avr_out_store_psi_reg_no_disp_tiny (rtx_insn *insn, rtx *op, int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (dest, 0); + int reg_base = true_regnum (base); + int reg_src = true_regnum (src); + + if (reg_base == reg_src) + { + avr_asm_len ("st %0,%A1" CR_TAB + "mov __tmp_reg__,%B1" CR_TAB + TINY_ADIW (%E0, %F0, 1) CR_TAB /* st X+, r27 is undefined */ + "st %0+,__tmp_reg__" CR_TAB + "st %0,%C1", op, plen, -6); + + } + else if (reg_src == reg_base - 2) + { + avr_asm_len ("st %0,%A1" CR_TAB + "mov __tmp_reg__,%C1" CR_TAB + TINY_ADIW (%E0, %F0, 1) CR_TAB + "st %0+,%B1" CR_TAB + "st %0,__tmp_reg__", op, plen, 6); + } + else + { + avr_asm_len ("st %0+,%A1" CR_TAB + "st %0+,%B1" CR_TAB + "st %0,%C1", op, plen, -3); + } + + if (!reg_unused_after (insn, base)) + avr_asm_len (TINY_SBIW (%E0, %F0, 2), op, plen, 2); + + return ""; +} + +static const char* +avr_out_store_psi_reg_disp_tiny (rtx *op, int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (dest, 0); + int reg_base = REGNO (XEXP (base, 0)); + int reg_src = true_regnum (src); + + if (reg_src == reg_base) + { + return avr_asm_len ("mov __tmp_reg__,%A1" CR_TAB + "mov __zero_reg__,%B1" CR_TAB + TINY_ADIW (%I0, %J0, %o0) CR_TAB + "st %b0+,__tmp_reg__" CR_TAB + "st %b0+,__zero_reg__" CR_TAB + "st %b0,%C1" CR_TAB + "clr __zero_reg__" CR_TAB + TINY_SBIW (%I0, %J0, %o0+2), op, plen, -10); + } + else if (reg_src == reg_base - 2) + { + return avr_asm_len ("mov __tmp_reg__,%C1" CR_TAB + TINY_ADIW (%I0, %J0, %o0) CR_TAB + "st %b0+,%A1" CR_TAB + "st %b0+,%B1" CR_TAB + "st %b0,__tmp_reg__" CR_TAB + TINY_SBIW (%I0, %J0, %o0+2), op, plen, -8); + } + + return avr_asm_len (TINY_ADIW (%I0, %J0, %o0) CR_TAB + "st %b0+,%A1" CR_TAB + "st %b0+,%B1" CR_TAB + "st %b0,%C1" CR_TAB + TINY_SBIW (%I0, %J0, %o0+2), op, plen, -7); +} + /* Handle store of 24-bit type from register or zero to memory. */ static const char* @@ -3988,12 +4559,18 @@ avr_out_store_psi (rtx_insn *insn, rtx *op, int *plen) int reg_base = true_regnum (base); if (CONSTANT_ADDRESS_P (base)) - return avr_asm_len ("sts %m0,%A1" CR_TAB - "sts %m0+1,%B1" CR_TAB - "sts %m0+2,%C1", op, plen, -6); + { + int n_words = AVR_TINY ? 3 : 6; + return avr_asm_len ("sts %m0,%A1" CR_TAB + "sts %m0+1,%B1" CR_TAB + "sts %m0+2,%C1", op, plen, -n_words); + } if (reg_base > 0) /* (r) */ { + if (AVR_TINY) + return avr_out_store_psi_reg_no_disp_tiny (insn, op, plen); + if (reg_base == REG_X) /* (R26) */ { gcc_assert (!reg_overlap_mentioned_p (base, src)); @@ -4015,6 +4592,10 @@ avr_out_store_psi (rtx_insn *insn, rtx *op, int *plen) else if (GET_CODE (base) == PLUS) /* (R + i) */ { int disp = INTVAL (XEXP (base, 1)); + + if (AVR_TINY) + return avr_out_store_psi_reg_disp_tiny (op, plen); + reg_base = REGNO (XEXP (base, 0)); if (disp > MAX_LD_OFFSET (GET_MODE (dest))) @@ -4131,6 +4712,30 @@ avr_out_movpsi (rtx_insn *insn, rtx *op, int *plen) return ""; } +static const char* +avr_out_movqi_mr_r_reg_disp_tiny (rtx_insn *insn, rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx x = XEXP (dest, 0); + + if (reg_overlap_mentioned_p (src, XEXP (x, 0))) + { + avr_asm_len ("mov __tmp_reg__,%1" CR_TAB + TINY_ADIW (%I0, %J0, %o0) CR_TAB + "st %b0,__tmp_reg__", op, plen, -4); + } + else + { + avr_asm_len (TINY_ADIW (%I0, %J0, %o0) CR_TAB + "st %b0,%1" , op, plen, -3); + } + + if (!reg_unused_after (insn, XEXP (x,0))) + avr_asm_len (TINY_SBIW (%I0, %J0, %o0), op, plen, 2); + + return ""; +} static const char* out_movqi_mr_r (rtx_insn *insn, rtx op[], int *plen) @@ -4141,9 +4746,10 @@ out_movqi_mr_r (rtx_insn *insn, rtx op[], int *plen) if (CONSTANT_ADDRESS_P (x)) { + int n_words = AVR_TINY ? 1 : 2; return optimize > 0 && io_address_operand (x, QImode) ? avr_asm_len ("out %i0,%1", op, plen, -1) - : avr_asm_len ("sts %m0,%1", op, plen, -2); + : avr_asm_len ("sts %m0,%1", op, plen, -n_words); } else if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0)) @@ -4153,6 +4759,9 @@ out_movqi_mr_r (rtx_insn *insn, rtx op[], int *plen) int disp = INTVAL (XEXP (x, 1)); + if (AVR_TINY) + return avr_out_movqi_mr_r_reg_disp_tiny (insn, op, plen); + if (disp - GET_MODE_SIZE (GET_MODE (dest)) >= 63) { if (REGNO (XEXP (x, 0)) != REG_Y) @@ -4213,12 +4822,15 @@ avr_out_movhi_mr_r_xmega (rtx_insn *insn, rtx op[], int *plen) int mem_volatile_p = MEM_VOLATILE_P (dest); if (CONSTANT_ADDRESS_P (base)) - return optimize > 0 && io_address_operand (base, HImode) - ? avr_asm_len ("out %i0,%A1" CR_TAB - "out %i0+1,%B1", op, plen, -2) + { + int n_words = AVR_TINY ? 2 : 4; + return optimize > 0 && io_address_operand (base, HImode) + ? avr_asm_len ("out %i0,%A1" CR_TAB + "out %i0+1,%B1", op, plen, -2) - : avr_asm_len ("sts %m0,%A1" CR_TAB - "sts %m0+1,%B1", op, plen, -4); + : avr_asm_len ("sts %m0,%A1" CR_TAB + "sts %m0+1,%B1", op, plen, -n_words); + } if (reg_base > 0) { @@ -4307,6 +4919,70 @@ avr_out_movhi_mr_r_xmega (rtx_insn *insn, rtx op[], int *plen) return ""; } +static const char* +avr_out_movhi_mr_r_reg_no_disp_tiny (rtx_insn *insn, rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (dest, 0); + int reg_base = true_regnum (base); + int reg_src = true_regnum (src); + int mem_volatile_p = MEM_VOLATILE_P (dest); + + if (reg_base == reg_src) + { + return !mem_volatile_p && reg_unused_after (insn, src) + ? avr_asm_len ("mov __tmp_reg__,%B1" CR_TAB + "st %0,%A1" CR_TAB + TINY_ADIW (%E0, %F0, 1) CR_TAB + "st %0,__tmp_reg__", op, plen, -5) + : avr_asm_len ("mov __tmp_reg__,%B1" CR_TAB + TINY_ADIW (%E0, %F0, 1) CR_TAB + "st %0,__tmp_reg__" CR_TAB + TINY_SBIW (%E0, %F0, 1) CR_TAB + "st %0, %A1", op, plen, -7); + } + + return !mem_volatile_p && reg_unused_after (insn, base) + ? avr_asm_len ("st %0+,%A1" CR_TAB + "st %0,%B1", op, plen, -2) + : avr_asm_len (TINY_ADIW (%E0, %F0, 1) CR_TAB + "st %0,%B1" CR_TAB + "st -%0,%A1", op, plen, -4); +} + +static const char* +avr_out_movhi_mr_r_reg_disp_tiny (rtx op[], int *plen) +{ + rtx dest = op[0]; + rtx src = op[1]; + rtx base = XEXP (dest, 0); + int reg_base = REGNO (XEXP (base, 0)); + int reg_src = true_regnum (src); + + return reg_src == reg_base + ? avr_asm_len ("mov __tmp_reg__,%A1" CR_TAB + "mov __zero_reg__,%B1" CR_TAB + TINY_ADIW (%I0, %J0, %o0+1) CR_TAB + "st %b0,__zero_reg__" CR_TAB + "st -%b0,__tmp_reg__" CR_TAB + "clr __zero_reg__" CR_TAB + TINY_SBIW (%I0, %J0, %o0), op, plen, -9) + + : avr_asm_len (TINY_ADIW (%I0, %J0, %o0+1) CR_TAB + "st %b0,%B1" CR_TAB + "st -%b0,%A1" CR_TAB + TINY_SBIW (%I0, %J0, %o0), op, plen, -6); +} + +static const char* +avr_out_movhi_mr_r_post_inc_tiny (rtx op[], int *plen) +{ + return avr_asm_len (TINY_ADIW (%I0, %J0, 1) CR_TAB + "st %p0,%B1" CR_TAB + "st -%p0,%A1" CR_TAB + TINY_ADIW (%I0, %J0, 2), op, plen, -6); +} static const char* out_movhi_mr_r (rtx_insn *insn, rtx op[], int *plen) @@ -4328,15 +5004,21 @@ out_movhi_mr_r (rtx_insn *insn, rtx op[], int *plen) mem_volatile_p = MEM_VOLATILE_P (dest); if (CONSTANT_ADDRESS_P (base)) - return optimize > 0 && io_address_operand (base, HImode) - ? avr_asm_len ("out %i0+1,%B1" CR_TAB - "out %i0,%A1", op, plen, -2) + { + int n_words = AVR_TINY ? 2 : 4; + return optimize > 0 && io_address_operand (base, HImode) + ? avr_asm_len ("out %i0+1,%B1" CR_TAB + "out %i0,%A1", op, plen, -2) - : avr_asm_len ("sts %m0+1,%B1" CR_TAB - "sts %m0,%A1", op, plen, -4); + : avr_asm_len ("sts %m0+1,%B1" CR_TAB + "sts %m0,%A1", op, plen, -n_words); + } if (reg_base > 0) { + if (AVR_TINY) + return avr_out_movhi_mr_r_reg_no_disp_tiny (insn, op, plen); + if (reg_base != REG_X) return avr_asm_len ("std %0+1,%B1" CR_TAB "st %0,%A1", op, plen, -2); @@ -4365,6 +5047,10 @@ out_movhi_mr_r (rtx_insn *insn, rtx op[], int *plen) else if (GET_CODE (base) == PLUS) { int disp = INTVAL (XEXP (base, 1)); + + if (AVR_TINY) + return avr_out_movhi_mr_r_reg_disp_tiny (op, plen); + reg_base = REGNO (XEXP (base, 0)); if (disp > MAX_LD_OFFSET (GET_MODE (dest))) { @@ -4414,6 +5100,9 @@ out_movhi_mr_r (rtx_insn *insn, rtx op[], int *plen) return avr_asm_len ("st %0,%A1" CR_TAB "st %0,%B1", op, plen, -2); + if (AVR_TINY) + return avr_out_movhi_mr_r_post_inc_tiny (op, plen); + return REGNO (XEXP (base, 0)) == REG_X ? avr_asm_len ("adiw r26,1" CR_TAB "st X,%B1" CR_TAB @@ -4596,7 +5285,11 @@ avr_out_compare (rtx_insn *insn, rtx *xop, int *plen) && (val8 == 0 || reg_unused_after (insn, xreg))) { - avr_asm_len ("sbiw %0,%1", xop, plen, 1); + if (AVR_TINY) + avr_asm_len (TINY_SBIW (%A0, %B0, %1), xop, plen, 2); + else + avr_asm_len ("sbiw %0,%1", xop, plen, 1); + i++; continue; } @@ -4606,7 +5299,9 @@ avr_out_compare (rtx_insn *insn, rtx *xop, int *plen) && compare_eq_p (insn) && reg_unused_after (insn, xreg)) { - return avr_asm_len ("adiw %0,%n1", xop, plen, 1); + return AVR_TINY + ? avr_asm_len (TINY_ADIW (%A0, %B0, %n1), xop, plen, 2) + : avr_asm_len ("adiw %0,%n1", xop, plen, 1); } } @@ -8070,7 +8765,8 @@ avr_assemble_integer (rtx x, unsigned int size, int aligned_p) static bool avr_class_likely_spilled_p (reg_class_t c) { - return (c != ALL_REGS && c != ADDW_REGS); + return (c != ALL_REGS && + (AVR_TINY ? 1 : c != ADDW_REGS)); } @@ -8343,7 +9039,9 @@ avr_nonconst_pointer_addrspace (tree typ) if (!ADDR_SPACE_GENERIC_P (as) && (!TYPE_READONLY (target) - || avr_addrspace[as].segment >= avr_n_flash)) + || avr_addrspace[as].segment >= avr_n_flash + /* Also refuse __memx address space if we can't support it. */ + || (!AVR_HAVE_LPM && avr_addrspace[as].pointer_size > 2))) { return as; } @@ -8465,6 +9163,12 @@ avr_insert_attributes (tree node, tree *attributes) " beyond flash of %qs", node, avr_addrspace[as].name, avr_current_device->name); } + else if (!AVR_HAVE_LPM && avr_addrspace[as].pointer_size > 2) + { + error ("variable %q+D located in address space %qs" + " which is not supported by %qs", + node, avr_addrspace[as].name, avr_current_arch->arch_name); + } if (!TYPE_READONLY (node0) && !TREE_READONLY (node)) @@ -8901,10 +9605,10 @@ avr_file_start (void) fprintf (asm_out_file, "__RAMPX__ = 0x%02x\n", avr_addr.rampx - sfr_offset); if (AVR_HAVE_RAMPD) fprintf (asm_out_file, "__RAMPD__ = 0x%02x\n", avr_addr.rampd - sfr_offset); - if (AVR_XMEGA) + if (AVR_XMEGA || AVR_TINY) fprintf (asm_out_file, "__CCP__ = 0x%02x\n", avr_addr.ccp - sfr_offset); - fprintf (asm_out_file, "__tmp_reg__ = %d\n", TMP_REGNO); - fprintf (asm_out_file, "__zero_reg__ = %d\n", ZERO_REGNO); + fprintf (asm_out_file, "__tmp_reg__ = %d\n", AVR_TMP_REGNO); + fprintf (asm_out_file, "__zero_reg__ = %d\n", AVR_ZERO_REGNO); } @@ -8951,6 +9655,18 @@ avr_adjust_reg_alloc_order (void) 0, 1, 32, 33, 34, 35 }; + static const int tiny_order_0[] = { + 20, 21, + 22, 23, + 24, 25, + 30, 31, + 26, 27, + 28, 29, + 19, 18, + 16, 17, + 32, 33, 34, 35, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + }; static const int order_1[] = { 18, 19, 20, 21, 22, 23, 24, 25, @@ -8960,6 +9676,17 @@ avr_adjust_reg_alloc_order (void) 0, 1, 32, 33, 34, 35 }; + static const int tiny_order_1[] = { + 22, 23, + 24, 25, + 30, 31, + 26, 27, + 28, 29, + 21, 20, 19, 18, + 16, 17, + 32, 33, 34, 35, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + }; static const int order_2[] = { 25, 24, 23, 22, 21, 20, 19, 18, @@ -8970,9 +9697,14 @@ avr_adjust_reg_alloc_order (void) 32, 33, 34, 35 }; - const int *order = (TARGET_ORDER_1 ? order_1 : - TARGET_ORDER_2 ? order_2 : - order_0); + /* + Select specific register allocation order. Tiny Core (attiny4/5/9/10/20/40) + devices has only 16 registers, so different allocation order should be used + */ + const int *order = (TARGET_ORDER_1 ? (AVR_TINY ? tiny_order_1 : order_1) : + TARGET_ORDER_2 ? (AVR_TINY ? tiny_order_0 : order_2) : + (AVR_TINY ? tiny_order_0 : order_0)); + for (i = 0; i < ARRAY_SIZE (order_0); ++i) reg_alloc_order[i] = order[i]; } @@ -10657,7 +11389,7 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) { if (!clear_p) avr_asm_len (ldreg_p ? "ldi %0,0" - : ZERO_REGNO == REGNO (xdest[n]) ? "clr %0" + : AVR_ZERO_REGNO == REGNO (xdest[n]) ? "clr %0" : "mov %0,__zero_reg__", &xdest[n], len, 1); continue; @@ -10859,6 +11591,49 @@ avr_output_addr_vec_elt (FILE *stream, int value) fprintf (stream, "\trjmp .L%d\n", value); } +static void +avr_conditional_register_usage(void) { + + if (AVR_TINY) { + unsigned int i; + + const int tiny_reg_alloc_order[] = { + 24, 25, + 22, 23, + 30, 31, + 26, 27, + 28, 29, + 21, 20, 19, 18, + 16, 17, + 32, 33, 34, 35, + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + }; + + /* Set R0-R17 as fixed registers. Reset R0-R17 in call used register list + - R0-R15 are not available in Tiny Core devices + - R16 and R17 are fixed registers + */ + for (i = 0; i <= 17; i++) { + fixed_regs[i] = 1; + call_used_regs[i] = 1; + } + + /* Set R18 to R21 as callee saved registers + - R18, R19, R20 and R21 are the callee saved registers in Tiny Core devices + */ + for (i = 18; i <= LAST_CALLEE_SAVED_REG; i++) { + call_used_regs[i] = 0; + } + + /*update register allocation order for Tiny Core devices */ + for (i=0; i < ARRAY_SIZE (tiny_reg_alloc_order); i++) { + reg_alloc_order[i] = tiny_reg_alloc_order[i]; + } + + CLEAR_HARD_REG_SET(reg_class_contents[(int)ADDW_REGS]); + CLEAR_HARD_REG_SET(reg_class_contents[(int)NO_LD_REGS]); + } +} /* Implement `TARGET_HARD_REGNO_SCRATCH_OK'. */ /* Returns true if SCRATCH are safe to be allocated as a scratch @@ -11013,13 +11788,20 @@ avr_asm_out_dtor (rtx symbol, int priority) static bool avr_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) { - if (TYPE_MODE (type) == BLKmode) - { - HOST_WIDE_INT size = int_size_in_bytes (type); - return (size == -1 || size > 8); - } + HOST_WIDE_INT size = int_size_in_bytes (type); + HOST_WIDE_INT ret_size_limit = AVR_TINY ? 4 : 8; + + /* In avr, there are 8 return registers. But, for Tiny Core + (attiny4/5/9/10/20/40) devices, only 4 registers available. + Return true if size is unknown or greater than the limit */ + if ((size == -1) || (size > ret_size_limit)) + { + return true; + } else + { return false; + } } @@ -12639,6 +13421,9 @@ avr_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED, tree *arg, #undef TARGET_BUILTIN_SETJMP_FRAME_VALUE #define TARGET_BUILTIN_SETJMP_FRAME_VALUE avr_builtin_setjmp_frame_value +#undef TARGET_CONDITIONAL_REGISTER_USAGE +#define TARGET_CONDITIONAL_REGISTER_USAGE avr_conditional_register_usage + #undef TARGET_HARD_REGNO_SCRATCH_OK #define TARGET_HARD_REGNO_SCRATCH_OK avr_hard_regno_scratch_ok #undef TARGET_CASE_VALUES_THRESHOLD diff --git a/gcc/config/avr/avr.h b/gcc/config/avr/avr.h index de2bcd171c8..ba31cd08fc5 100644 --- a/gcc/config/avr/avr.h +++ b/gcc/config/avr/avr.h @@ -63,6 +63,7 @@ enum #define AVR_HAVE_JMP_CALL (avr_current_arch->have_jmp_call) #define AVR_HAVE_MUL (avr_current_arch->have_mul) #define AVR_HAVE_MOVW (avr_current_arch->have_movw_lpmx) +#define AVR_HAVE_LPM (!AVR_TINY) #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) @@ -99,6 +100,7 @@ FIXME: DRIVER_SELF_SPECS has changed. #define AVR_3_BYTE_PC (AVR_HAVE_EIJMP_EICALL) #define AVR_XMEGA (avr_current_arch->xmega_p) +#define AVR_TINY (avr_current_arch->tiny_p) #define BITS_BIG_ENDIAN 0 #define BYTES_BIG_ENDIAN 0 @@ -305,7 +307,7 @@ enum reg_class { #define ARG_POINTER_REGNUM 34 -#define STATIC_CHAIN_REGNUM 2 +#define STATIC_CHAIN_REGNUM ((AVR_TINY) ? 18 :2) #define ELIMINABLE_REGS { \ {ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md index 3f3d6eb195c..06e1cb0abe6 100644 --- a/gcc/config/avr/avr.md +++ b/gcc/config/avr/avr.md @@ -24,6 +24,10 @@ ;; B Add 1 to REG number, MEM address or CONST_INT. ;; C Add 2. ;; D Add 3. +;; E reg number in XEXP(x, 0). +;; F Add 1 to reg number. +;; I reg number in XEXP(XEXP(x, 0), 0). +;; J Add 1 to reg number. ;; j Branch condition. ;; k Reverse branch condition. ;;..m..Constant Direct Data memory address. @@ -59,6 +63,11 @@ (ZERO_REGNO 1) ; zero register r1 ]) +(define_constants + [ (TMP_REGNO_TINY 16) ; r16 is temp register for AVR_TINY + (ZERO_REGNO_TINY 17) ; r17 is zero register for AVR_TINY + ]) + (define_c_enum "unspec" [UNSPEC_STRLEN UNSPEC_MOVMEM @@ -159,9 +168,10 @@ ;; lpm : ISA has no LPMX lpmx : ISA has LPMX ;; elpm : ISA has ELPM but no ELPMX elpmx : ISA has ELPMX ;; no_xmega: non-XMEGA core xmega : XMEGA core +;; no_tiny: non-TINY core tiny : TINY core (define_attr "isa" - "mov,movw, rjmp,jmp, ijmp,eijmp, lpm,lpmx, elpm,elpmx, no_xmega,xmega, + "mov,movw, rjmp,jmp, ijmp,eijmp, lpm,lpmx, elpm,elpmx, no_xmega,xmega, no_tiny,tiny, standard" (const_string "standard")) @@ -213,9 +223,18 @@ (match_test "AVR_XMEGA")) (const_int 1) + (and (eq_attr "isa" "tiny") + (match_test "AVR_TINY")) + (const_int 1) + (and (eq_attr "isa" "no_xmega") (match_test "!AVR_XMEGA")) (const_int 1) + + (and (eq_attr "isa" "no_tiny") + (match_test "!AVR_TINY")) + (const_int 1) + ] (const_int 0))) @@ -620,6 +639,33 @@ emit_insn (gen_load_libgcc (dest, src)); DONE; } + + /* AVRTC-579 + if the source operand expression is out of range for 'lds' instruction + copy source operand expression to register + For tiny core, LDS instruction's memory access range limited to 0x40..0xbf + */ + if (!tiny_valid_direct_memory_access_range(src,mode)) + { + rtx srcx = XEXP(src,0); + operands[1] = src = replace_equiv_address (src,copy_to_mode_reg (GET_MODE(srcx),srcx)); + emit_move_insn(dest,src); + DONE; + } + + /* AVRTC-579 + if the destination operand expression is out of range for 'sts' instruction + copy destination operand expression to register + For tiny core, STS instruction's memory access range limited to 0x40..0xbf + */ + if (!tiny_valid_direct_memory_access_range(dest,mode)) + { + rtx destx = XEXP(dest,0); + operands[0] = dest = replace_equiv_address (dest,copy_to_mode_reg (GET_MODE(destx),destx)); + emit_move_insn(dest,src); + DONE; + } + }) ;;======================================================================== @@ -636,8 +682,13 @@ (define_insn "mov_insn" [(set (match_operand:ALL1 0 "nonimmediate_operand" "=r ,d ,Qm ,r ,q,r,*r") (match_operand:ALL1 1 "nox_general_operand" "r Y00,n Ynn,r Y00,Qm,r,q,i"))] - "register_operand (operands[0], mode) - || reg_or_0_operand (operands[1], mode)" + "(register_operand (operands[0], mode) + || reg_or_0_operand (operands[1], mode)) && + /* skip if operands are out of lds/sts memory access range(0x40..0xbf) + though access range is checked during define_expand, it is required + here to avoid merging rtls during combine pass */ + tiny_valid_direct_memory_access_range(operands[0],QImode) && + tiny_valid_direct_memory_access_range(operands[1],QImode)" { return output_movqi (insn, operands, NULL); } @@ -730,8 +781,13 @@ (define_insn "*mov" [(set (match_operand:ALL2 0 "nonimmediate_operand" "=r,r ,r,m ,d,*r,q,r") (match_operand:ALL2 1 "nox_general_operand" "r,Y00,m,r Y00,i,i ,r,q"))] - "register_operand (operands[0], mode) - || reg_or_0_operand (operands[1], mode)" + "(register_operand (operands[0], mode) + || reg_or_0_operand (operands[1], mode)) && + /* skip if operands are out of lds/sts memory access range(0x40..0xbf) + though access range is checked during define_expand, it is required + here to avoid merging rtls during combine pass */ + tiny_valid_direct_memory_access_range(operands[0],HImode) && + tiny_valid_direct_memory_access_range(operands[1],HImode)" { return output_movhi (insn, operands, NULL); } @@ -879,8 +935,13 @@ (define_insn "*mov" [(set (match_operand:ALL4 0 "nonimmediate_operand" "=r,r ,r ,Qm ,!d,r") (match_operand:ALL4 1 "nox_general_operand" "r,Y00,Qm,r Y00,i ,i"))] - "register_operand (operands[0], mode) - || reg_or_0_operand (operands[1], mode)" + "(register_operand (operands[0], mode) + || reg_or_0_operand (operands[1], mode)) && + /* skip if operands are out of lds/sts memory access range(0x40..0xbf) + though access range is checked during define_expand, it is required + here to avoid merging rtls during combine pass */ + tiny_valid_direct_memory_access_range(operands[0],SImode) && + tiny_valid_direct_memory_access_range(operands[1],SImode)" { return output_movsisf (insn, operands, NULL); } @@ -894,8 +955,13 @@ (define_insn "*movsf" [(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) - || reg_or_0_operand (operands[1], SFmode)" + "(register_operand (operands[0], SFmode) + || reg_or_0_operand (operands[1], SFmode)) && + /* skip if operands are out of lds/sts memory access range(0x40..0xbf) + though access range is checked during define_expand, it is required + here to avoid merging rtls during combine pass */ + tiny_valid_direct_memory_access_range(operands[0],SFmode) && + tiny_valid_direct_memory_access_range(operands[1],SFmode)" { return output_movsisf (insn, operands, NULL); } @@ -5551,18 +5617,18 @@ (set_attr "cc" "clobber")]) (define_insn "delay_cycles_2" - [(unspec_volatile [(match_operand:HI 0 "const_int_operand" "n") + [(unspec_volatile [(match_operand:HI 0 "const_int_operand" "n,n") (const_int 2)] UNSPECV_DELAY_CYCLES) (set (match_operand:BLK 1 "" "") (unspec_volatile:BLK [(match_dup 1)] UNSPECV_MEMORY_BARRIER)) - (clobber (match_scratch:HI 2 "=&w"))] + (clobber (match_scratch:HI 2 "=&w,&d"))] "" - "ldi %A2,lo8(%0) - ldi %B2,hi8(%0) - 1: sbiw %A2,1 - brne 1b" - [(set_attr "length" "4") + "@ + ldi %A2,lo8(%0)\;ldi %B2,hi8(%0)\;1: sbiw %A2,1\;brne 1b + ldi %A2,lo8(%0)\;ldi %B2,hi8(%0)\;1: subi %A2,1\;sbci %B2,0\;brne 1b" + [(set_attr "length" "4,5") + (set_attr "isa" "no_tiny,tiny") (set_attr "cc" "clobber")]) (define_insn "delay_cycles_3" diff --git a/gcc/config/avr/t-multilib b/gcc/config/avr/t-multilib index 9aee17bfda8..89d0036fbdd 100644 --- a/gcc/config/avr/t-multilib +++ b/gcc/config/avr/t-multilib @@ -21,9 +21,9 @@ # along with GCC; see the file COPYING3. If not see # . -MULTILIB_OPTIONS = march=avr2/march=avr25/march=avr3/march=avr31/march=avr35/march=avr4/march=avr5/march=avr51/march=avr6/march=avrxmega2/march=avrxmega4/march=avrxmega5/march=avrxmega6/march=avrxmega7 msp8 +MULTILIB_OPTIONS = march=avr2/march=avr25/march=avr3/march=avr31/march=avr35/march=avr4/march=avr5/march=avr51/march=avr6/march=avrxmega2/march=avrxmega4/march=avrxmega5/march=avrxmega6/march=avrxmega7/march=avrtiny msp8 -MULTILIB_DIRNAMES = avr2 avr25 avr3 avr31 avr35 avr4 avr5 avr51 avr6 avrxmega2 avrxmega4 avrxmega5 avrxmega6 avrxmega7 tiny-stack avr25/tiny-stack +MULTILIB_DIRNAMES = avr2 avr25 avr3 avr31 avr35 avr4 avr5 avr51 avr6 avrxmega2 avrxmega4 avrxmega5 avrxmega6 avrxmega7 avrtiny tiny-stack avr25/tiny-stack MULTILIB_EXCEPTIONS = \ march=avr3/msp8 \ @@ -37,4 +37,5 @@ MULTILIB_EXCEPTIONS = \ march=avrxmega4/msp8 \ march=avrxmega5/msp8 \ march=avrxmega6/msp8 \ - march=avrxmega7/msp8 + march=avrxmega7/msp8 \ + march=avrtiny/msp8 diff --git a/gcc/doc/avr-mmcu.texi b/gcc/doc/avr-mmcu.texi index e3701503e98..468dda0133c 100644 --- a/gcc/doc/avr-mmcu.texi +++ b/gcc/doc/avr-mmcu.texi @@ -68,6 +68,10 @@ ``XMEGA'' devices with more than 128@tie{}KiB of program memory and more than 64@tie{}KiB of RAM. @*@var{mcu}@tie{}= @code{atxmega128a1}, @code{atxmega128a1u}, @code{atxmega128a4u}. +@item avrtiny +``TINY'' Tiny core devices with 512@tie{}B up to 4@tie{}KiB of program memory. +@*@var{mcu}@tie{}= @code{attiny10}, @code{attiny20}, @code{attiny4}, @code{attiny40}, @code{attiny5}, @code{attiny9}. + @item avr1 This ISA is implemented by the minimal AVR core and supported for assembler only. @*@var{mcu}@tie{}= @code{attiny11}, @code{attiny12}, @code{attiny15}, @code{attiny28}, @code{at90s1200}. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 04affb7c2a5..7db3ce4e5b0 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2014-10-21 Joern Rennecke + + * gcc.target/avr/tiny-memx.c: New test. + + * gcc.target/avr/tiny-caller-save.c: New test. + 2014-10-21 Jiong Wang * gcc.target/arm/20031108-1.c (Proc_7): Add explicit declaration. diff --git a/gcc/testsuite/gcc.target/avr/tiny-caller-save.c b/gcc/testsuite/gcc.target/avr/tiny-caller-save.c new file mode 100644 index 00000000000..0e46ee06594 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/tiny-caller-save.c @@ -0,0 +1,78 @@ +/* { dg-do compile } */ +/* { dg-options "-march=avrtiny -gdwarf -Os" } */ + +/* This is a stripped down piece of libgcc2.c that triggerd an ICE for avr with + "-march=avrtiny -g -Os"; replace_reg_with_saved_mem would generate: + (concatn:SI [ + (reg:SI 18 r18) + (reg:SI 19 r19) + (mem/c:QI (plus:HI (reg/f:HI 28 r28) + (const_int 43 [0x2b])) [6 S1 A8]) + (mem/c:QI (plus:HI (reg/f:HI 28 r28) + (const_int 44 [0x2c])) [6 S1 A8]) + ]) */ + +typedef int SItype __attribute__ ((mode (SI))); +typedef unsigned int USItype __attribute__ ((mode (SI))); +typedef int DItype __attribute__ ((mode (DI))); +typedef unsigned int UDItype __attribute__ ((mode (DI))); +struct DWstruct +{ + SItype low, high; +}; +typedef union +{ + struct DWstruct s; + DItype ll; +} DWunion; + +UDItype +__udivmoddi4 (UDItype n, UDItype d) +{ + const DWunion nn = {.ll = n }; + const DWunion dd = {.ll = d }; + USItype d0, d1, n2; + USItype q0; + + d0 = dd.s.low; + d1 = dd.s.high; + n2 = nn.s.high; + + USItype m0; + + do + { + USItype __d1, __d0, __q1, __q0; + USItype __r1, __m; + __d1 = ((USItype) (d1) >> 16); + __d0 = ((USItype) (d1) & (((USItype) 1 << 16) - 1)); + __r1 = (n2) % __d1; + __q1 = (n2) / __d1; + __m = (USItype) __q1 *__d0; + __r1 -= __m; + __q0 = __r1 / __d1; + (q0) = (USItype) __q1 *((USItype) 1 << 16) | __q0; + } + while (0); + do + { + USItype __x0, __x1, __x2; + USItype __ul, __vl, __uh, __vh; + __ul = ((USItype) (q0) & (((USItype) 1 << 16) - 1)); + __uh = ((USItype) (q0) >> 16); + __vl = ((USItype) (d0) & (((USItype) 1 << 16) - 1)); + __vh = ((USItype) (d0) >> 16); + __x0 = (USItype) __ul *__vl; + __x1 = (USItype) __ul *__vh; + __x2 = (USItype) __uh *__vl; + __x1 += ((USItype) (__x0) >> 16); + __x1 += __x2; + (m0) = + ((USItype) (__x1) & (((USItype) 1 << 16) - 1)) * + ((USItype) 1 << 16) + + ((USItype) (__x0) & (((USItype) 1 << 16) - 1)); + } + while (0); + +return m0; +} diff --git a/gcc/testsuite/gcc.target/avr/tiny-memx.c b/gcc/testsuite/gcc.target/avr/tiny-memx.c new file mode 100644 index 00000000000..3ad164dfed4 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/tiny-memx.c @@ -0,0 +1,4 @@ +/* { dg-do compile } */ +/* { dg-options "-march=avrtiny" } */ + +const __memx char ascmonth[] = "Jan"; /* { dg-error "not supported" } */ diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog index 7364f93780f..a87aa3354cf 100644 --- a/libgcc/ChangeLog +++ b/libgcc/ChangeLog @@ -1,3 +1,38 @@ +2014-10-21 Joern Rennecke + Vidya Praveen + Praveen Kumar Kaushik + Senthil Kumar Selvaraj + Pitchumani Sivanupandi + + * config/avr/lib1funcs.S (__do_global_dtors): Go back to descending + order. + + Updated library functions for AVRTINY arch. + * config/avr/lib1funcs.S: Updated zero/tmp regs for AVRTINY. + Replaced occurrences of r0/r1 with tmp/zero reg macros. + Added wsubi/ wadi macros that expands conditionally as sbiw/ adiw + or AVRTINY equivalent. Replaced occurrences of sbiw/adiw with + wsubi/wadi macors. + (__mulsi3_helper): Update stack, preserve callee saved regs and + argument from stack. Restore callee save registers. + (__mulpsi3): Likewise. + (__muldi3, __udivmodsi4, __divmodsi4, __negsi2, __umoddi3, __udivmod64, + __moddi3, __adddi3, __adddi3_s8, __subdi3, __cmpdi2, __cmpdi2_s8, + __negdi2, __prologue_saves__, __epilogue_restores__): Excluded for + AVRTINY. + (__tablejump2__): Added lpm equivalent instructions for AVRTINY. + (__do_copy_data): Added new definition for AVRTINY. + (__do_clear_bss): Replace r17 by r18 to preserve zero reg for AVRTINY. + (__load_3, __load_4, __xload_1, __xload_2, __xload_3, + __xload_4, __movmemx_qi, __movmemx_hi): Excluded for AVRTINY. + * config/avr/lib1funcs-fixed.S: Replaced occurrences of r0/r1 with + tmp/zero reg macros. Replaced occurrences of sbiw/adiw with wsubi/wadi + macors. + * config/avr/t-avr (LIB1ASMFUNCS): Remove unsupported functions for + AVRTINY. + + Fix broken long multiplication on tiny arch. + 2014-10-09 Joseph Myers * soft-fp/double.h: Update from glibc. diff --git a/libgcc/config/avr/lib1funcs-fixed.S b/libgcc/config/avr/lib1funcs-fixed.S index 8f3ed92010d..ae4dcf0fe8a 100644 --- a/libgcc/config/avr/lib1funcs-fixed.S +++ b/libgcc/config/avr/lib1funcs-fixed.S @@ -31,8 +31,18 @@ ;; Fixed point library routines for AVR ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +#if defined __AVR_TINY__ +#define __zero_reg__ r17 +#define __tmp_reg__ r16 +#else +#define __zero_reg__ r1 +#define __tmp_reg__ r0 +#endif + .section .text.libgcc.fixed, "ax", @progbits +#ifndef __AVR_TINY__ + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Conversions to float ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1913,3 +1923,5 @@ DEFUN __ret ret ENDF __ret #endif /* L_ret */ + +#endif /* if not __AVR_TINY__ */ diff --git a/libgcc/config/avr/lib1funcs.S b/libgcc/config/avr/lib1funcs.S index 080e250cd61..0205d833ecb 100644 --- a/libgcc/config/avr/lib1funcs.S +++ b/libgcc/config/avr/lib1funcs.S @@ -21,8 +21,13 @@ a copy of the GCC Runtime Library Exception along with this program; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . */ +#if defined (__AVR_TINY__) +#define __zero_reg__ r17 +#define __tmp_reg__ r16 +#else #define __zero_reg__ r1 #define __tmp_reg__ r0 +#endif #define __SREG__ 0x3f #if defined (__AVR_HAVE_SPH__) #define __SP_H__ 0x3e @@ -126,6 +131,24 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see ;; Support function entry and exit for convenience +.macro wsubi r_arg1, i_arg2 +#if defined (__AVR_TINY__) + subi \r_arg1, lo8(\i_arg2) + sbci \r_arg1+1, hi8(\i_arg2) +#else + sbiw \r_arg1, \i_arg2 +#endif +.endm + +.macro waddi r_arg1, i_arg2 +#if defined (__AVR_TINY__) + subi \r_arg1, lo8(-\i_arg2) + sbci \r_arg1+1, hi8(-\i_arg2) +#else + adiw \r_arg1, \i_arg2 +#endif +.endm + .macro DEFUN name .global \name .func \name @@ -146,7 +169,11 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see .endm ;; Skip next instruction, typically a jump target +#if defined(__AVR_TINY__) #define skip cpse 0,0 +#else +#define skip cpse 16,16 +#endif ;; Negate a 2-byte value held in consecutive registers .macro NEG2 reg @@ -219,16 +246,16 @@ ENDF __mulqi3 Multiplication 16 x 16 without MUL *******************************************************/ -#define A0 r22 -#define A1 r23 -#define B0 r24 -#define BB0 r20 -#define B1 r25 +#define A0 22 +#define A1 23 +#define B0 24 +#define BB0 20 +#define B1 25 ;; Output overlaps input, thus expand result in CC0/1 -#define C0 r24 -#define C1 r25 +#define C0 24 +#define C1 25 #define CC0 __tmp_reg__ -#define CC1 R21 +#define CC1 21 #if defined (L_umulqihi3) ;;; R25:R24 = (unsigned int) R22 * (unsigned int) R24 @@ -294,7 +321,7 @@ DEFUN __mulhi3 rol B1 3: ;; If B == 0 we are ready - sbiw B0, 0 + wsubi B0, 0 breq 9f ;; Carry = n-th bit of A @@ -402,6 +429,18 @@ ENDF __mulhisi3 #if defined (L_mulsi3) DEFUN __mulsi3 +#if defined (__AVR_TINY__) + in r26, __SP_L__ ; safe to use X, as it is CC0/CC1 + in r27, __SP_H__ + subi r26, lo8(-3) ; Add 3 to point past return address + sbci r27, hi8(-3) + push B0 ; save callee saved regs + push B1 + ld B0, X+ ; load from caller stack + ld B1, X+ + ld B2, X+ + ld B3, X +#endif ;; Clear result clr CC2 clr CC3 @@ -427,12 +466,17 @@ DEFUN __mulsi3_helper ;; Only continue if A != 0 sbci A1, 0 brne 2b - sbiw A2, 0 + wsubi A2, 0 brne 2b ;; All bits of A are consumed: Copy result to return register C wmov C0, CC0 wmov C2, CC2 +#if defined (__AVR_TINY__) + pop B1 ; restore callee saved regs + pop B0 +#endif /* defined (__AVR_TINY__) */ + ret ENDF __mulsi3_helper #endif /* L_mulsi3 */ @@ -682,9 +726,12 @@ ENDF __mulpsi3 #undef C0 #else /* !HAVE_MUL */ - ;; C[0..2]: Expand Result +#if defined (__AVR_TINY__) +#define C0 16 +#else #define C0 0 +#endif /* defined (__AVR_TINY__) */ #define C1 C0+1 #define C2 21 @@ -692,6 +739,17 @@ ENDF __mulpsi3 ;; Clobbers: __tmp_reg__, R18, R19, R20, R21 DEFUN __mulpsi3 +#if defined (__AVR_TINY__) + in r26,__SP_L__ + in r27,__SP_H__ + subi r26, lo8(-3) ; Add 3 to point past return address + sbci r27, hi8(-3) + push B0 ; save callee saved regs + push B1 + ld B0,X+ ; load from caller stack + ld B1,X+ + ld B2,X+ +#endif /* defined (__AVR_TINY__) */ ;; C[] = 0 clr __tmp_reg__ @@ -718,6 +776,10 @@ DEFUN __mulpsi3 mov A2, C2 clr __zero_reg__ +#if defined (__AVR_TINY__) + pop B1 + pop B0 +#endif /* (__AVR_TINY__) */ ret ENDF __mulpsi3 @@ -809,8 +871,8 @@ ENDF __mulsqipsi3 #define B6 B0+6 #define B7 B0+7 +#ifndef __AVR_TINY__ #if defined (__AVR_HAVE_MUL__) - ;; Define C[] for convenience ;; Notice that parts of C[] overlap A[] respective B[] #define C0 16 @@ -1012,6 +1074,7 @@ ENDF __muldi3 #endif /* L_muldi3 */ #endif /* HAVE_MUL */ +#endif /* if not __AVR_TINY__ */ #undef B7 #undef B6 @@ -1169,7 +1232,7 @@ ENDF __mulsidi3 /********************************************************** Widening Multiplication 64 = 32 x 32 without MUL **********************************************************/ - +#ifndef __AVR_TINY__ /* if not __AVR_TINY__ */ #if defined (L_mulsidi3) && !defined (__AVR_HAVE_MUL__) #define A0 18 #define A1 A0+1 @@ -1265,7 +1328,7 @@ ENDF __umulsidi3 #undef BB3 #undef Mask #endif /* L_mulsidi3 && !HAVE_MUL */ - +#endif /* if not __AVR_TINY__ */ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1437,7 +1500,7 @@ ENDF __divmodhi4 #define r_cnt 21 #if defined (L_udivmodpsi4) -;; R24:R22 = R24:R22 udiv R20:R18 +;; R24:R22 = R24:R24 udiv R20:R18 ;; R20:R18 = R24:R22 umod R20:R18 ;; Clobbers: R21, R25, R26 @@ -1672,6 +1735,10 @@ ENDF __negsi2 #undef r_arg2L #undef r_cnt +/* *di routines use registers below R19 and won't work with tiny arch + right now. */ + +#if !defined (__AVR_TINY__) /******************************************************* Division 64 / 64 Modulo 64 % 64 @@ -2087,12 +2154,15 @@ ENDF __negdi2 #undef A1 #undef A0 +#endif /* !defined (__AVR_TINY__) */ + .section .text.libgcc.prologue, "ax", @progbits /********************************** * This is a prologue subroutine **********************************/ +#if !defined (__AVR_TINY__) #if defined (L_prologue) ;; This function does not clobber T-flag; 64-bit division relies on it @@ -2194,6 +2264,7 @@ DEFUN __epilogue_restores__ ret ENDF __epilogue_restores__ #endif /* defined (L_epilogue) */ +#endif /* !defined (__AVR_TINY__) */ #ifdef L_exit .section .fini9,"ax",@progbits @@ -2259,6 +2330,12 @@ DEFUN __tablejump2__ lpm r31, Z mov r30, __tmp_reg__ ijmp +#elif defined (__AVR_TINY__) + wsubi 30, -(__AVR_TINY_PM_BASE_ADDRESS__) ; Add PM offset to Z + ld __tmp_reg__, Z+ + ld r31, Z ; Use ld instead of lpm to load Z + mov r30, __tmp_reg__ + ijmp #else lpm push r0 @@ -2270,6 +2347,26 @@ DEFUN __tablejump2__ ENDF __tablejump2__ #endif /* L_tablejump2 */ +#if defined(__AVR_TINY__) +#ifdef L_copy_data + .section .init4,"ax",@progbits + .global __do_copy_data +__do_copy_data: + ldi r18, hi8(__data_end) + ldi r26, lo8(__data_start) + ldi r27, hi8(__data_start) + ldi r30, lo8(__data_load_start + __AVR_TINY_PM_BASE_ADDRESS__) + ldi r31, hi8(__data_load_start + __AVR_TINY_PM_BASE_ADDRESS__) + rjmp .L__do_copy_data_start +.L__do_copy_data_loop: + ld r19, z+ + st X+, r19 +.L__do_copy_data_start: + cpi r26, lo8(__data_end) + cpc r27, r18 + brne .L__do_copy_data_loop +#endif +#else #ifdef L_copy_data .section .init4,"ax",@progbits DEFUN __do_copy_data @@ -2335,13 +2432,14 @@ DEFUN __do_copy_data #endif /* ELPM && RAMPD */ ENDF __do_copy_data #endif /* L_copy_data */ +#endif /* !defined (__AVR_TINY__) */ /* __do_clear_bss is only necessary if there is anything in .bss section. */ #ifdef L_clear_bss .section .init4,"ax",@progbits DEFUN __do_clear_bss - ldi r17, hi8(__bss_end) + ldi r18, hi8(__bss_end) ldi r26, lo8(__bss_start) ldi r27, hi8(__bss_start) rjmp .do_clear_bss_start @@ -2349,7 +2447,7 @@ DEFUN __do_clear_bss st X+, __zero_reg__ .do_clear_bss_start: cpi r26, lo8(__bss_end) - cpc r27, r17 + cpc r27, r18 brne .do_clear_bss_loop ENDF __do_clear_bss #endif /* L_clear_bss */ @@ -2357,10 +2455,16 @@ ENDF __do_clear_bss /* __do_global_ctors and __do_global_dtors are only necessary if there are any constructors/destructors. */ +#if defined(__AVR_TINY__) +#define cdtors_tst_reg r18 +#else +#define cdtors_tst_reg r17 +#endif + #ifdef L_ctors .section .init6,"ax",@progbits DEFUN __do_global_ctors - ldi r17, pm_hi8(__ctors_start) + ldi cdtors_tst_reg, pm_hi8(__ctors_start) ldi r28, pm_lo8(__ctors_end) ldi r29, pm_hi8(__ctors_end) #ifdef __AVR_HAVE_EIJMP_EICALL__ @@ -2368,7 +2472,7 @@ DEFUN __do_global_ctors #endif /* HAVE_EIJMP */ rjmp .L__do_global_ctors_start .L__do_global_ctors_loop: - sbiw r28, 1 + wsubi 28, 1 #ifdef __AVR_HAVE_EIJMP_EICALL__ sbc r16, __zero_reg__ mov r24, r16 @@ -2378,7 +2482,7 @@ DEFUN __do_global_ctors XCALL __tablejump2__ .L__do_global_ctors_start: cpi r28, pm_lo8(__ctors_start) - cpc r29, r17 + cpc r29, cdtors_tst_reg #ifdef __AVR_HAVE_EIJMP_EICALL__ ldi r24, pm_hh8(__ctors_start) cpc r16, r24 @@ -2390,27 +2494,27 @@ ENDF __do_global_ctors #ifdef L_dtors .section .fini6,"ax",@progbits DEFUN __do_global_dtors - ldi r17, pm_hi8(__dtors_start) - ldi r28, pm_lo8(__dtors_end) - ldi r29, pm_hi8(__dtors_end) + ldi cdtors_tst_reg, pm_hi8(__dtors_end) + ldi r28, pm_lo8(__dtors_start) + ldi r29, pm_hi8(__dtors_start) #ifdef __AVR_HAVE_EIJMP_EICALL__ - ldi r16, pm_hh8(__dtors_end) + ldi r16, pm_hh8(__dtors_start) #endif /* HAVE_EIJMP */ rjmp .L__do_global_dtors_start .L__do_global_dtors_loop: - sbiw r28, 1 + waddi 28, 1 #ifdef __AVR_HAVE_EIJMP_EICALL__ - sbc r16, __zero_reg__ + adc r16, __zero_reg__ mov r24, r16 #endif /* HAVE_EIJMP */ mov_h r31, r29 mov_l r30, r28 XCALL __tablejump2__ .L__do_global_dtors_start: - cpi r28, pm_lo8(__dtors_start) - cpc r29, r17 + cpi r28, pm_lo8(__dtors_end) + cpc r29, cdtors_tst_reg #ifdef __AVR_HAVE_EIJMP_EICALL__ - ldi r24, pm_hh8(__dtors_start) + ldi r24, pm_hh8(__dtors_end) cpc r16, r24 #endif /* HAVE_EIJMP */ brne .L__do_global_dtors_loop @@ -2419,6 +2523,7 @@ ENDF __do_global_dtors .section .text.libgcc, "ax", @progbits +#if !defined (__AVR_TINY__) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Loading n bytes from Flash; n = 3,4 ;; R22... = Flash[Z] @@ -2464,7 +2569,9 @@ ENDF __load_4 #endif /* L_load_4 */ #endif /* L_load_3 || L_load_3 */ +#endif /* !defined (__AVR_TINY__) */ +#if !defined (__AVR_TINY__) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Loading n bytes from Flash or RAM; n = 1,2,3,4 ;; R22... = Flash[R21:Z] or RAM[Z] depending on R21.7 @@ -2590,7 +2697,9 @@ ENDF __xload_4 #endif /* L_xload_4 */ #endif /* L_xload_{1|2|3|4} */ +#endif /* if !defined (__AVR_TINY__) */ +#if !defined (__AVR_TINY__) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; memcopy from Address Space __pgmx to RAM ;; R23:Z = Source Address @@ -2662,6 +2771,7 @@ ENDF __movmemx_hi #undef LOOP #endif /* L_movmemx */ +#endif /* !defined (__AVR_TINY__) */ .section .text.libgcc.builtins, "ax", @progbits diff --git a/libgcc/config/avr/t-avr b/libgcc/config/avr/t-avr index 1f7356968aa..c420c5d8fb9 100644 --- a/libgcc/config/avr/t-avr +++ b/libgcc/config/avr/t-avr @@ -3,12 +3,7 @@ LIB1ASMFUNCS = \ _mulqi3 \ _mulhi3 \ _mulqihi3 _umulqihi3 \ - _mulpsi3 _mulsqipsi3 \ - _mulhisi3 \ - _umulhisi3 \ - _usmulhisi3 \ - _muluhisi3 \ - _mulshisi3 \ + _mulpsi3 \ _mulsi3 \ _udivmodqi4 \ _divmodqi4 \ @@ -17,19 +12,10 @@ LIB1ASMFUNCS = \ _divmodpsi4 _udivmodpsi4 \ _udivmodsi4 \ _divmodsi4 \ - _divdi3 _udivdi3 \ - _muldi3 _muldi3_6 \ - _mulsidi3 _umulsidi3 \ - _udivmod64 \ - _negsi2 _negdi2 \ - _prologue \ - _epilogue \ + _negsi2 \ _exit \ _cleanup \ _tablejump2 \ - _load_3 _load_4 \ - _xload_1 _xload_2 _xload_3 _xload_4 \ - _movmemx \ _copy_data \ _clear_bss \ _ctors \ @@ -39,24 +25,54 @@ LIB1ASMFUNCS = \ _loop_ffsqi2 \ _ctzsi2 \ _ctzhi2 \ - _clzdi2 \ _clzsi2 \ _clzhi2 \ - _paritydi2 \ _paritysi2 \ _parityhi2 \ _popcounthi2 \ _popcountsi2 \ - _popcountdi2 \ _popcountqi2 \ _bswapsi2 \ + _fmul _fmuls _fmulsu + +# The below functions either use registers that are not present +# in tiny core, or use a different register conventions (don't save +# callee saved regs, for example) +# _mulhisi3 and variations - clobber R18, R19 +# All *di funcs - use regs < R16 or expect args in regs < R20 +# _prologue and _epilogue save registers < R16 +# _load ad _xload variations - expect lpm and elpm support +# _movmemx - expects elpm/lpm + +ifneq ($(MULTIFLAGS),-mmcu=avrtiny) +LIB1ASMFUNCS += \ + _mulsqipsi3 \ + _mulhisi3 \ + _umulhisi3 \ + _usmulhisi3 \ + _muluhisi3 \ + _mulshisi3 \ + _muldi3 _muldi3_6 \ + _mulsidi3 _umulsidi3 \ + _divdi3 _udivdi3 \ + _udivmod64 \ + _negdi2 \ + _prologue \ + _epilogue \ + _load_3 _load_4 \ + _xload_1 _xload_2 _xload_3 _xload_4 \ + _movmemx \ + _clzdi2 \ + _paritydi2 \ + _popcountdi2 \ _bswapdi2 \ _ashldi3 _ashrdi3 _lshrdi3 _rotldi3 \ _adddi3 _adddi3_s8 _subdi3 \ - _cmpdi2 _cmpdi2_s8 \ - _fmul _fmuls _fmulsu + _cmpdi2 _cmpdi2_s8 +endif # Fixed point routines in avr/lib1funcs-fixed.S +ifneq ($(MULTIFLAGS),-mmcu=avrtiny) LIB1ASMFUNCS += \ _fractqqsf _fractuqqsf \ _fracthqsf _fractuhqsf _fracthasf _fractuhasf \ @@ -87,8 +103,8 @@ LIB1ASMFUNCS += \ _round_x8 \ _rounddq3 _roundudq3 \ _roundda3 _rounduda3 \ - _roundta3 _rounduta3 \ - + _roundta3 _rounduta3 +endif LIB2FUNCS_EXCLUDE = \ _moddi3 _umoddi3 \ -- 2.30.2