diff options
Diffstat (limited to 'gcc/config/avr/avr.c')
-rw-r--r-- | gcc/config/avr/avr.c | 304 |
1 files changed, 131 insertions, 173 deletions
diff --git a/gcc/config/avr/avr.c b/gcc/config/avr/avr.c index a62835c..8d0a57f 100644 --- a/gcc/config/avr/avr.c +++ b/gcc/config/avr/avr.c @@ -62,6 +62,22 @@ This must be the rightmost field of machine dependent section flags. */ #define AVR_SECTION_PROGMEM (0xf * SECTION_MACH_DEP) +/* Similar 4-bit region for SYMBOL_REF_FLAGS. */ +#define AVR_SYMBOL_FLAG_PROGMEM (0xf * SYMBOL_FLAG_MACH_DEP) + +/* Similar 4-bit region in SYMBOL_REF_FLAGS: + Set address-space AS in SYMBOL_REF_FLAGS of SYM */ +#define AVR_SYMBOL_SET_ADDR_SPACE(SYM,AS) \ + do { \ + SYMBOL_REF_FLAGS (sym) &= ~AVR_SYMBOL_FLAG_PROGMEM; \ + SYMBOL_REF_FLAGS (sym) |= (AS) * SYMBOL_FLAG_MACH_DEP; \ + } while (0) + +/* Read address-space from SYMBOL_REF_FLAGS of SYM */ +#define AVR_SYMBOL_GET_ADDR_SPACE(SYM) \ + ((SYMBOL_REF_FLAGS (sym) & AVR_SYMBOL_FLAG_PROGMEM) \ + / SYMBOL_FLAG_MACH_DEP) + /* 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[] = @@ -2427,8 +2443,7 @@ avr_xload_libgcc_p (enum machine_mode mode) int n_bytes = GET_MODE_SIZE (mode); return (n_bytes > 1 - && avr_current_arch->n_segments > 1 - && !AVR_HAVE_ELPMX); + || avr_current_arch->n_segments > 1); } @@ -2597,8 +2612,7 @@ avr_out_lpm (rtx insn, rtx *op, int *plen) int regno_dest; int segment; RTX_CODE code; - - addr_space_t as; + addr_space_t as = MEM_ADDR_SPACE (src); if (plen) *plen = 0; @@ -2611,24 +2625,11 @@ avr_out_lpm (rtx insn, rtx *op, int *plen) return ""; } - as = MEM_ADDR_SPACE (src); - addr = XEXP (src, 0); code = GET_CODE (addr); gcc_assert (REG_P (dest)); - - if (as == ADDR_SPACE_PGMX) - { - /* We are called from avr_out_xload because someone wrote - __pgmx on a device with just one flash segment. */ - - gcc_assert (LO_SUM == code); - - addr = XEXP (addr, 1); - } - else - gcc_assert (REG == code || POST_INC == code); + gcc_assert (REG == code || POST_INC == code); xop[0] = dest; xop[1] = addr; @@ -2766,76 +2767,28 @@ avr_out_lpm (rtx insn, rtx *op, int *plen) } -/* Worker function for xload_<mode> and xload_8 insns. */ +/* Worker function for xload_8 insn. */ const char* -avr_out_xload (rtx insn, rtx *op, int *plen) +avr_out_xload (rtx insn ATTRIBUTE_UNUSED, rtx *op, int *plen) { - rtx xop[5]; - rtx reg = op[0]; - int n_bytes = GET_MODE_SIZE (GET_MODE (reg)); - unsigned int regno = REGNO (reg); - - if (avr_current_arch->n_segments == 1) - return avr_out_lpm (insn, op, plen); + rtx xop[4]; - xop[0] = reg; + xop[0] = op[0]; xop[1] = op[1]; xop[2] = lpm_addr_reg_rtx; - xop[3] = lpm_reg_rtx; - xop[4] = tmp_reg_rtx; - - avr_asm_len ("out __RAMPZ__,%1", xop, plen, -1); - - if (1 == n_bytes) - { - if (AVR_HAVE_ELPMX) - return avr_asm_len ("elpm %0,%a2", xop, plen, 1); - else - return avr_asm_len ("elpm" CR_TAB - "mov %0,%3", xop, plen, 2); - } - - gcc_assert (AVR_HAVE_ELPMX); - - if (!reg_overlap_mentioned_p (reg, lpm_addr_reg_rtx)) - { - /* Insn clobbers the Z-register so we can use post-increment. */ - - avr_asm_len ("elpm %A0,%a2+", xop, plen, 1); - if (n_bytes >= 2) avr_asm_len ("elpm %B0,%a2+", xop, plen, 1); - if (n_bytes >= 3) avr_asm_len ("elpm %C0,%a2+", xop, plen, 1); - if (n_bytes >= 4) avr_asm_len ("elpm %D0,%a2+", xop, plen, 1); - - return ""; - } + xop[3] = AVR_HAVE_LPMX ? op[0] : lpm_reg_rtx; - switch (n_bytes) - { - default: - gcc_unreachable(); - - case 2: - gcc_assert (regno == REGNO (lpm_addr_reg_rtx)); + if (plen) + *plen = 0; - return avr_asm_len ("elpm %4,%a2+" CR_TAB - "elpm %B0,%a2" CR_TAB - "mov %A0,%4", xop, plen, 3); + avr_asm_len ("ld %3,%a2" CR_TAB + "sbrs %1,7", xop, plen, 2); - case 3: - case 4: - gcc_assert (regno + 2 == REGNO (lpm_addr_reg_rtx)); - - avr_asm_len ("elpm %A0,%a2+" CR_TAB - "elpm %B0,%a2+", xop, plen, 2); + avr_asm_len (AVR_HAVE_LPMX ? "lpm %3,%a2" : "lpm", xop, plen, 1); - if (n_bytes == 3) - return avr_asm_len ("elpm %C0,%a2", xop, plen, 1); - else - return avr_asm_len ("elpm %4,%a2+" CR_TAB - "elpm %D0,%a2" CR_TAB - "mov %C0,%4", xop, plen, 3); - } + if (REGNO (xop[0]) != REGNO (xop[3])) + avr_asm_len ("mov %0,%3", xop, plen, 1); return ""; } @@ -6673,8 +6626,8 @@ avr_assemble_integer (rtx x, unsigned int size, int aligned_p) default_assemble_integer (avr_const_address_lo16 (x), GET_MODE_SIZE (HImode), aligned_p); - fputs ("\t.warning\t\"assembling 24-bit address needs binutils extension for hh8(", - asm_out_file); + fputs ("\t.warning\t\"assembling 24-bit address needs binutils" + " extension for hh8(", asm_out_file); output_addr_const (asm_out_file, x); fputs (")\"\n", asm_out_file); @@ -7277,6 +7230,23 @@ avr_encode_section_info (tree decl, rtx rtl, int new_decl_p) } default_encode_section_info (decl, rtl, new_decl_p); + + if (decl && DECL_P (decl) + && TREE_CODE (decl) != FUNCTION_DECL + && MEM_P (rtl) + && SYMBOL_REF == GET_CODE (XEXP (rtl, 0))) + { + rtx sym = XEXP (rtl, 0); + addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl)); + + /* PSTR strings are in generic space but located in flash: + patch address space. */ + + if (-1 == avr_progmem_p (decl, DECL_ATTRIBUTES (decl))) + as = ADDR_SPACE_PGM; + + AVR_SYMBOL_SET_ADDR_SPACE (sym, as); + } } @@ -9019,10 +8989,8 @@ output_reload_in_const (rtx *op, rtx clobber_reg, int *len, bool clear_p) xop[1] = src; xop[2] = clobber_reg; - if (n >= 2 + (avr_current_arch->n_segments > 1)) - avr_asm_len ("mov %0,__zero_reg__", xop, len, 1); - else - avr_asm_len (asm_code[n][ldreg_p], xop, len, ldreg_p ? 1 : 2); + avr_asm_len (asm_code[n][ldreg_p], xop, len, ldreg_p ? 1 : 2); + continue; } @@ -9596,51 +9564,57 @@ avr_addr_space_convert (rtx src, tree type_from, tree type_to) avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n", src, type_from, type_to); + /* Up-casting from 16-bit to 24-bit pointer. */ + if (as_from != ADDR_SPACE_PGMX && as_to == ADDR_SPACE_PGMX) { - rtx new_src; - int n_segments = avr_current_arch->n_segments; - RTX_CODE code = GET_CODE (src); + int msb; + rtx sym = src; + rtx reg = gen_reg_rtx (PSImode); + + while (CONST == GET_CODE (sym) || PLUS == GET_CODE (sym)) + sym = XEXP (sym, 0); + + /* Look at symbol flags: avr_encode_section_info set the flags + also if attribute progmem was seen so that we get the right + promotion for, e.g. PSTR-like strings that reside in generic space + but are located in flash. In that case we patch the incoming + address space. */ - if (CONST == code - && PLUS == GET_CODE (XEXP (src, 0)) - && SYMBOL_REF == GET_CODE (XEXP (XEXP (src, 0), 0)) - && CONST_INT_P (XEXP (XEXP (src, 0), 1))) + if (SYMBOL_REF == GET_CODE (sym) + && ADDR_SPACE_PGM == AVR_SYMBOL_GET_ADDR_SPACE (sym)) { - HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (src, 0), 1)); - const char *name = XSTR (XEXP (XEXP (src, 0), 0), 0); - - new_src = gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name)); - new_src = gen_rtx_CONST (PSImode, - plus_constant (new_src, offset)); - return new_src; + as_from = ADDR_SPACE_PGM; } - if (SYMBOL_REF == code) - { - const char *name = XSTR (src, 0); - - return gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name)); - } - + /* Linearize memory: RAM has bit 23 set. */ + + msb = ADDR_SPACE_GENERIC_P (as_from) + ? 0x80 + : avr_addrspace[as_from].segment % avr_current_arch->n_segments; + src = force_reg (Pmode, src); - if (ADDR_SPACE_GENERIC_P (as_from) - || as_from == ADDR_SPACE_PGM - || n_segments == 1) - { - return gen_rtx_ZERO_EXTEND (PSImode, src); - } - else - { - int segment = avr_addrspace[as_from].segment % n_segments; + emit_insn (msb == 0 + ? gen_zero_extendhipsi2 (reg, src) + : gen_n_extendhipsi2 (reg, gen_int_mode (msb, QImode), src)); + + return reg; + } - new_src = gen_reg_rtx (PSImode); - emit_insn (gen_n_extendhipsi2 (new_src, GEN_INT (segment), src)); + /* Down-casting from 24-bit to 16-bit throws away the high byte. */ - return new_src; - } + if (as_from == ADDR_SPACE_PGMX + && as_to != ADDR_SPACE_PGMX) + { + rtx new_src = gen_reg_rtx (Pmode); + + src = force_reg (PSImode, src); + + emit_move_insn (new_src, + simplify_gen_subreg (Pmode, src, PSImode, 0)); + return new_src; } return src; @@ -9650,19 +9624,16 @@ avr_addr_space_convert (rtx src, tree type_from, tree type_to) /* Implement `TARGET_ADDR_SPACE_SUBSET_P'. */ static bool -avr_addr_space_subset_p (addr_space_t subset, addr_space_t superset) +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; - } + /* Allow any kind of pointer mess. */ return true; } -/* Worker function for movmemhi insn. +/* Worker function for movmemhi expander. XOP[0] Destination as MEM:BLK XOP[1] Source " " XOP[2] # Bytes to copy @@ -9692,12 +9663,14 @@ avr_emit_movmemhi (rtx *xop) a_src = XEXP (xop[1], 0); a_dest = XEXP (xop[0], 0); - /* See if constant fits in 8 bits. */ - - loop_mode = (count <= 0x100) ? QImode : HImode; - if (PSImode == GET_MODE (a_src)) { + gcc_assert (as == ADDR_SPACE_PGMX); + + loop_mode = (count < 0x100) ? QImode : HImode; + loop_reg = gen_rtx_REG (loop_mode, 24); + emit_move_insn (loop_reg, gen_int_mode (count, loop_mode)); + addr1 = simplify_gen_subreg (HImode, a_src, PSImode, 0); a_hi8 = simplify_gen_subreg (QImode, a_src, PSImode, 2); } @@ -9705,41 +9678,35 @@ avr_emit_movmemhi (rtx *xop) { int segment = avr_addrspace[as].segment % avr_current_arch->n_segments; + if (segment + && avr_current_arch->n_segments > 1) + { + a_hi8 = GEN_INT (segment); + emit_move_insn (rampz_rtx, a_hi8 = copy_to_mode_reg (QImode, a_hi8)); + } + else if (!ADDR_SPACE_GENERIC_P (as)) + { + as = ADDR_SPACE_PGM; + } + addr1 = a_src; - if (segment) - a_hi8 = GEN_INT (segment); - } - - if (a_hi8 - && avr_current_arch->n_segments > 1) - { - emit_move_insn (rampz_rtx, a_hi8 = copy_to_mode_reg (QImode, a_hi8)); - } - else if (!ADDR_SPACE_GENERIC_P (as)) - { - as = ADDR_SPACE_PGM; + loop_mode = (count <= 0x100) ? QImode : HImode; + loop_reg = copy_to_mode_reg (loop_mode, gen_int_mode (count, loop_mode)); } xas = GEN_INT (as); - /* Create loop counter register */ - - loop_reg = copy_to_mode_reg (loop_mode, gen_int_mode (count, loop_mode)); - - /* Copy pointers into new pseudos - they will be changed */ - - addr0 = copy_to_mode_reg (HImode, a_dest); - addr1 = copy_to_mode_reg (HImode, addr1); - /* FIXME: Register allocator might come up with spill fails if it is left - on its own. Thus, we allocate the pointer registers by hand. */ + on its own. Thus, we allocate the pointer registers by hand: + Z = source address + X = destination address */ emit_move_insn (lpm_addr_reg_rtx, addr1); addr1 = lpm_addr_reg_rtx; reg_x = gen_rtx_REG (HImode, REG_X); - emit_move_insn (reg_x, addr0); + emit_move_insn (reg_x, a_dest); addr0 = reg_x; /* FIXME: Register allocator does a bad job and might spill address @@ -9748,30 +9715,30 @@ avr_emit_movmemhi (rtx *xop) load and store as seperate insns. Instead, we perform the copy by means of one monolithic insn. */ - if (ADDR_SPACE_GENERIC_P (as)) + gcc_assert (TMP_REGNO == LPM_REGNO); + + if (as != ADDR_SPACE_PGMX) { + /* Load instruction ([E]LPM or LD) is known at compile time: + Do the copy-loop inline. */ + rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx) = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi; insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1, tmp_reg_rtx, loop_reg); } - else if (as == ADDR_SPACE_PGM) - { - rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx) - = QImode == loop_mode ? gen_movmem_qi : gen_movmem_hi; - - insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1, - AVR_HAVE_LPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg); - } else { + rtx loop_reg16 = gen_rtx_REG (HImode, 24); + rtx r23 = gen_rtx_REG (QImode, 23); rtx (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx) - = QImode == loop_mode ? gen_movmem_qi_elpm : gen_movmem_hi_elpm; + = QImode == loop_mode ? gen_movmemx_qi : gen_movmemx_hi; + + emit_move_insn (r23, a_hi8); insn = fun (addr0, addr1, xas, loop_reg, addr0, addr1, - AVR_HAVE_ELPMX ? tmp_reg_rtx : lpm_reg_rtx, loop_reg, - a_hi8, a_hi8, GEN_INT (RAMPZ_ADDR)); + lpm_reg_rtx, loop_reg16, r23, r23, GEN_INT (RAMPZ_ADDR)); } set_mem_addr_space (SET_SRC (XVECEXP (insn, 0, 0)), as); @@ -9838,21 +9805,12 @@ avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen) case ADDR_SPACE_PGM3: case ADDR_SPACE_PGM4: case ADDR_SPACE_PGM5: - case ADDR_SPACE_PGMX: if (AVR_HAVE_ELPMX) avr_asm_len ("elpm %6,%a1+", xop, plen, 1); else avr_asm_len ("elpm" CR_TAB "adiw %1,1", xop, plen, 2); - - if (as == ADDR_SPACE_PGMX - && !AVR_HAVE_ELPMX) - { - avr_asm_len ("adc %8,__zero_reg__" CR_TAB - "out __RAMPZ__,%8", xop, plen, 2); - } - break; } |