Index: config/avr/avr.md =================================================================== --- config/avr/avr.md (revision 185105) +++ config/avr/avr.md (working copy) @@ -63,6 +63,7 @@ (define_c_enum "unspec" [UNSPEC_STRLEN UNSPEC_MOVMEM UNSPEC_INDEX_JMP + UNSPEC_LPM UNSPEC_FMUL UNSPEC_FMULS UNSPEC_FMULSU @@ -364,43 +365,24 @@ (define_split ;;======================================================================== ;; Move stuff around -;; "loadqi_libgcc" -;; "loadhi_libgcc" -;; "loadpsi_libgcc" -;; "loadsi_libgcc" -;; "loadsf_libgcc" -(define_expand "load_libgcc" - [(set (match_dup 3) - (match_dup 2)) - (set (reg:MOVMODE 22) - (match_operand:MOVMODE 1 "memory_operand" "")) - (set (match_operand:MOVMODE 0 "register_operand" "") - (reg:MOVMODE 22))] - "avr_load_libgcc_p (operands[1])" - { - operands[3] = gen_rtx_REG (HImode, REG_Z); - operands[2] = force_operand (XEXP (operands[1], 0), NULL_RTX); - operands[1] = replace_equiv_address (operands[1], operands[3]); - set_mem_addr_space (operands[1], ADDR_SPACE_FLASH); - }) +;; Represent a load from __flash that needs libgcc support as UNSPEC. +;; This is legal because we read from non-changing memory. +;; For rationale see the FIXME below. -;; "load_qi_libgcc" -;; "load_hi_libgcc" ;; "load_psi_libgcc" ;; "load_si_libgcc" ;; "load_sf_libgcc" (define_insn "load__libgcc" [(set (reg:MOVMODE 22) - (match_operand:MOVMODE 0 "memory_operand" "m,m"))] - "avr_load_libgcc_p (operands[0]) - && REG_P (XEXP (operands[0], 0)) - && REG_Z == REGNO (XEXP (operands[0], 0))" + (unspec:MOVMODE [(reg:HI REG_Z)] + UNSPEC_LPM))] + "" { - operands[0] = GEN_INT (GET_MODE_SIZE (mode)); - return "%~call __load_%0"; + rtx n_bytes = GEN_INT (GET_MODE_SIZE (mode)); + output_asm_insn ("%~call __load_%0", &n_bytes); + return ""; } - [(set_attr "length" "1,2") - (set_attr "isa" "rjmp,jmp") + [(set_attr "type" "xcall") (set_attr "cc" "clobber")]) @@ -549,10 +531,53 @@ (define_expand "mov" DONE; } - if (avr_load_libgcc_p (src)) + /* ; FIXME: Load from non-generic 16-bit address spaces by means of + ; POST_INC or a call to a support routine from libgcc. Reason is the + ; extreme code bloat caused by subreg lowering which splits multi-byte + ; moves without knowing anything about the additional costs caused by + ; these splits, see PR rtl-optimization/52543. + ; We assign the address to Z already at expand-time because there is + ; no choice for the address register, anyway, and pre-post-increment + ; optimization is virtually non-existent. */ + + if (avr_load_libgcc_p (src) + || (GET_MODE_SIZE (mode) > 1 + && avr_mem_flash_p (src) + && can_create_pseudo_p() + && (GET_CODE (XEXP (src, 0)) != POST_INC + || (GET_CODE (XEXP (src, 0)) == POST_INC + && REGNO (XEXP (XEXP (src, 0), 0)) != REG_Z)))) { - /* For the small devices, do loads per libgcc call. */ - emit_insn (gen_load_libgcc (dest, src)); + rtx addr = XEXP (src, 0); + rtx regz = gen_rtx_REG (Pmode, REG_Z); + + if (GET_CODE (addr) == POST_INC) + emit_move_insn (regz, XEXP (addr, 0)); + else + emit_move_insn (regz, force_reg (Pmode, addr)); + + if (avr_load_libgcc_p (src)) + { + /* For old devices without LPMX, prefer loads per libcall. */ + + emit_insn (gen_load__libgcc ()); + emit_move_insn (operands[0], gen_rtx_REG (mode, 22)); + if (GET_CODE (addr) == POST_INC) + emit_move_insn (XEXP (addr, 0), + plus_constant (XEXP (addr, 0), + GET_MODE_SIZE (mode))); + } + else + { + /* For remaining multi-byte cases hard-code the address as Z++. */ + + emit_move_insn (operands[0], + replace_equiv_address (operands[1], + gen_rtx_POST_INC (Pmode, regz))); + if (GET_CODE (addr) == POST_INC) + emit_move_insn (XEXP (addr, 0), regz); + } + DONE; } }) @@ -694,40 +719,6 @@ (define_peephole2 ; movw_r operands[5] = gen_rtx_REG (HImode, REGNO (operands[3])); }) -;; For LPM loads from AS1 we split -;; R = *Z -;; to -;; R = *Z++ -;; Z = Z - sizeof (R) -;; -;; so that the second instruction can be optimized out. - -(define_split ; "split-lpmx" - [(set (match_operand:HISI 0 "register_operand" "") - (match_operand:HISI 1 "memory_operand" ""))] - "reload_completed - && AVR_HAVE_LPMX" - [(set (match_dup 0) - (match_dup 2)) - (set (match_dup 3) - (plus:HI (match_dup 3) - (match_dup 4)))] - { - rtx addr = XEXP (operands[1], 0); - - if (!avr_mem_flash_p (operands[1]) - || !REG_P (addr) - || reg_overlap_mentioned_p (addr, operands[0])) - { - FAIL; - } - - operands[2] = replace_equiv_address (operands[1], - gen_rtx_POST_INC (Pmode, addr)); - operands[3] = addr; - operands[4] = gen_int_mode (-GET_MODE_SIZE (mode), HImode); - }) - ;;========================================================================== ;; xpointer move (24 bit) Index: config/avr/avr.c =================================================================== --- config/avr/avr.c (revision 185100) +++ config/avr/avr.c (working copy) @@ -1423,6 +1423,22 @@ avr_cannot_modify_jumps_p (void) } +/* Implement `TARGET_MODE_DEPENDENT_ADDRESS_P'. */ + +/* FIXME: PSImode addresses are not mode-dependent in themselves. + This hook just serves to hack around PR rtl-optimization/52543 by + claiming that PSImode addresses (which are used for the 24-bit + address space __memx) were mode-dependent so that lower-subreg.s + will skip these addresses. See also the similar FIXME comment along + with mov expanders in avr.md. */ + +static bool +avr_mode_dependent_address_p (const_rtx addr) +{ + return GET_MODE (addr) != Pmode; +} + + /* Helper function for `avr_legitimate_address_p'. */ static inline bool @@ -11027,6 +11043,9 @@ avr_fold_builtin (tree fndecl, int n_arg #undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS #define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address +#undef TARGET_MODE_DEPENDENT_ADDRESS_P +#define TARGET_MODE_DEPENDENT_ADDRESS_P avr_mode_dependent_address_p + #undef TARGET_PRINT_OPERAND #define TARGET_PRINT_OPERAND avr_print_operand #undef TARGET_PRINT_OPERAND_ADDRESS