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[] =
{
{ ADDR_SPACE_RAM, 0, 2, "" , 0 },
- { ADDR_SPACE_PGM, 1, 2, "__pgm", 0 },
- { ADDR_SPACE_PGM1, 1, 2, "__pgm1", 1 },
- { ADDR_SPACE_PGM2, 1, 2, "__pgm2", 2 },
- { ADDR_SPACE_PGM3, 1, 2, "__pgm3", 3 },
- { ADDR_SPACE_PGM4, 1, 2, "__pgm4", 4 },
- { ADDR_SPACE_PGM5, 1, 2, "__pgm5", 5 },
- { ADDR_SPACE_PGMX, 1, 3, "__pgmx", 0 },
+ { ADDR_SPACE_FLASH, 1, 2, "__flash", 0 },
+ { ADDR_SPACE_FLASH1, 1, 2, "__flash1", 1 },
+ { ADDR_SPACE_FLASH2, 1, 2, "__flash2", 2 },
+ { ADDR_SPACE_FLASH3, 1, 2, "__flash3", 3 },
+ { ADDR_SPACE_FLASH4, 1, 2, "__flash4", 4 },
+ { ADDR_SPACE_FLASH5, 1, 2, "__flash5", 5 },
+ { ADDR_SPACE_MEMX, 1, 3, "__memx", 0 },
{ 0 , 0, 0, NULL, 0 }
};
#define FIRST_CUM_REG 26
/* Implicit target register of LPM instruction (R0) */
-static GTY(()) rtx lpm_reg_rtx;
+extern GTY(()) rtx lpm_reg_rtx;
+rtx lpm_reg_rtx;
/* (Implicit) address register of LPM instruction (R31:R30 = Z) */
-static GTY(()) rtx lpm_addr_reg_rtx;
+extern GTY(()) rtx lpm_addr_reg_rtx;
+rtx lpm_addr_reg_rtx;
+
+/* Temporary register RTX (reg:QI TMP_REGNO) */
+extern GTY(()) rtx tmp_reg_rtx;
+rtx tmp_reg_rtx;
-/* Temporary register RTX (gen_rtx_REG (QImode, TMP_REGNO)) */
-static GTY(()) rtx tmp_reg_rtx;
+/* Zeroed register RTX (reg:QI ZERO_REGNO) */
+extern GTY(()) rtx zero_reg_rtx;
+rtx zero_reg_rtx;
-/* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */
-static GTY(()) rtx zero_reg_rtx;
+/* RTXs for all general purpose registers as QImode */
+extern GTY(()) rtx all_regs_rtx[32];
+rtx all_regs_rtx[32];
/* RAMPZ special function register */
-static GTY(()) rtx rampz_rtx;
+extern GTY(()) rtx rampz_rtx;
+rtx rampz_rtx;
/* RTX containing the strings "" and "e", respectively */
static GTY(()) rtx xstring_empty;
static GTY(()) rtx xstring_e;
-/* RTXs for all general purpose registers as QImode */
-static GTY(()) rtx all_regs_rtx[32];
-
-/* AVR register names {"r0", "r1", ..., "r31"} */
-static const char *const avr_regnames[] = REGISTER_NAMES;
-
/* Preprocessor macros to define depending on MCU type. */
const char *avr_extra_arch_macro;
static GTY(()) section *progmem_swtable_section;
/* Unnamed sections associated to __attribute__((progmem)) aka. PROGMEM
- or to address space __pgm*. */
+ or to address space __flash*. */
static GTY(()) section *progmem_section[6];
+/* Condition for insns/expanders from avr-dimode.md. */
+bool avr_have_dimode = true;
+
/* To track if code will use .bss and/or .data. */
bool avr_need_clear_bss_p = false;
bool avr_need_copy_data_p = false;
#undef TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS
#define TARGET_ADDR_SPACE_LEGITIMIZE_ADDRESS avr_addr_space_legitimize_address
-\f
-
-/* Custom function to replace string prefix.
-
- Return a ggc-allocated string with strlen (OLD_PREFIX) characters removed
- from the start of OLD_STR and then prepended with NEW_PREFIX. */
-
-static inline const char*
-avr_replace_prefix (const char *old_str,
- const char *old_prefix, const char *new_prefix)
-{
- char *new_str;
- size_t len = strlen (old_str) + strlen (new_prefix) - strlen (old_prefix);
-
- gcc_assert (strlen (old_prefix) <= strlen (old_str));
-
- /* Unfortunately, ggc_alloc_string returns a const char* and thus cannot be
- used here. */
-
- new_str = (char*) ggc_alloc_atomic (1 + len);
-
- strcat (stpcpy (new_str, new_prefix), old_str + strlen (old_prefix));
-
- return (const char*) new_str;
-}
+#undef TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND avr_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS avr_print_operand_address
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P avr_print_operand_punct_valid_p
+\f
/* Custom function to count number of set bits. */
/* Return TRUE if DECL is a VAR_DECL located in Flash and FALSE, otherwise. */
static bool
-avr_decl_pgm_p (tree decl)
+avr_decl_flash_p (tree decl)
{
if (TREE_CODE (decl) != VAR_DECL
|| TREE_TYPE (decl) == error_mark_node)
address space and FALSE, otherwise. */
static bool
-avr_decl_pgmx_p (tree decl)
+avr_decl_memx_p (tree decl)
{
if (TREE_CODE (decl) != VAR_DECL
|| TREE_TYPE (decl) == error_mark_node)
return false;
}
- return (ADDR_SPACE_PGMX == TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+ return (ADDR_SPACE_MEMX == TYPE_ADDR_SPACE (TREE_TYPE (decl)));
}
/* Return TRUE if X is a MEM rtx located in Flash and FALSE, otherwise. */
bool
-avr_mem_pgm_p (rtx x)
+avr_mem_flash_p (rtx x)
{
return (MEM_P (x)
&& !ADDR_SPACE_GENERIC_P (MEM_ADDR_SPACE (x)));
address space and FALSE, otherwise. */
bool
-avr_mem_pgmx_p (rtx x)
+avr_mem_memx_p (rtx x)
{
return (MEM_P (x)
- && ADDR_SPACE_PGMX == MEM_ADDR_SPACE (x));
+ && ADDR_SPACE_MEMX == MEM_ADDR_SPACE (x));
}
/* The high byte (r29) does not change:
Prefer SUBI (1 cycle) over ABIW (2 cycles, same size). */
- my_fp = simplify_gen_subreg (QImode, fp, Pmode, 0);
+ my_fp = all_regs_rtx[FRAME_POINTER_REGNUM];
}
/************ Method 1: Adjust frame pointer ************/
/* The high byte (r29) does not change:
Prefer SUBI (1 cycle) over SBIW (2 cycles). */
- my_fp = simplify_gen_subreg (QImode, fp, Pmode, 0);
+ my_fp = all_regs_rtx[FRAME_POINTER_REGNUM];
}
/********** Method 1: Adjust fp register **********/
return "";
}
+
+/* Implement `TARGET_PRINT_OPERAND_ADDRESS'. */
/* Output ADDR to FILE as address. */
-void
-print_operand_address (FILE *file, rtx addr)
+static void
+avr_print_operand_address (FILE *file, rtx addr)
{
switch (GET_CODE (addr))
{
}
+/* Implement `TARGET_PRINT_OPERAND_PUNCT_VALID_P'. */
+
+static bool
+avr_print_operand_punct_valid_p (unsigned char code)
+{
+ return code == '~' || code == '!';
+}
+
+
+/* Implement `TARGET_PRINT_OPERAND'. */
/* Output X as assembler operand to file FILE.
For a description of supported %-codes, see top of avr.md. */
-void
-print_operand (FILE *file, rtx x, int code)
+static void
+avr_print_operand (FILE *file, rtx x, int code)
{
int abcd = 0;
}
else if (code == 'i')
{
- print_operand (file, addr, 'i');
+ avr_print_operand (file, addr, 'i');
}
else if (code == 'o')
{
if (GET_CODE (addr) != PLUS)
fatal_insn ("bad address, not (reg+disp):", addr);
- print_operand (file, XEXP (addr, 1), 0);
+ avr_print_operand (file, XEXP (addr, 1), 0);
}
else if (code == 'p' || code == 'r')
{
fatal_insn ("bad address, not post_inc or pre_dec:", addr);
if (code == 'p')
- print_operand_address (file, XEXP (addr, 0)); /* X, Y, Z */
+ avr_print_operand_address (file, XEXP (addr, 0)); /* X, Y, Z */
else
- print_operand (file, XEXP (addr, 0), 0); /* r26, r28, r30 */
+ avr_print_operand (file, XEXP (addr, 0), 0); /* r26, r28, r30 */
}
else if (GET_CODE (addr) == PLUS)
{
- print_operand_address (file, XEXP (addr,0));
+ avr_print_operand_address (file, XEXP (addr,0));
if (REGNO (XEXP (addr, 0)) == REG_X)
fatal_insn ("internal compiler error. Bad address:"
,addr);
fputc ('+', file);
- print_operand (file, XEXP (addr,1), code);
+ avr_print_operand (file, XEXP (addr,1), code);
}
else
- print_operand_address (file, addr);
+ avr_print_operand_address (file, addr);
}
else if (code == 'i')
{
else if (code == 'k')
fputs (cond_string (reverse_condition (GET_CODE (x))), file);
else
- print_operand_address (file, x);
+ avr_print_operand_address (file, x);
}
/* Update the condition code in the INSN. */
case CC_OUT_PLUS:
case CC_OUT_PLUS_NOCLOBBER:
+ case CC_LDI:
{
rtx *op = recog_data.operand;
int len_dummy, icc;
/* Extract insn's operands. */
extract_constrain_insn_cached (insn);
- if (CC_OUT_PLUS == cc)
- avr_out_plus (op, &len_dummy, &icc);
- else
- avr_out_plus_noclobber (op, &len_dummy, &icc);
-
- cc = (enum attr_cc) icc;
-
+ switch (cc)
+ {
+ default:
+ gcc_unreachable();
+
+ case CC_OUT_PLUS:
+ avr_out_plus (op, &len_dummy, &icc);
+ cc = (enum attr_cc) icc;
+ break;
+
+ case CC_OUT_PLUS_NOCLOBBER:
+ avr_out_plus_noclobber (op, &len_dummy, &icc);
+ cc = (enum attr_cc) icc;
+ break;
+
+ case CC_LDI:
+
+ cc = (op[1] == CONST0_RTX (GET_MODE (op[0]))
+ && reg_overlap_mentioned_p (op[0], zero_reg_rtx))
+ /* Loading zero-reg with 0 uses CLI and thus clobbers cc0. */
+ ? CC_CLOBBER
+ /* Any other "r,rL" combination does not alter cc0. */
+ : CC_NONE;
+
+ break;
+ } /* inner switch */
+
break;
}
- }
+ } /* outer swicth */
switch (cc)
{
return (n_bytes > 2
&& !AVR_HAVE_LPMX
- && avr_mem_pgm_p (op));
+ && avr_mem_flash_p (op));
}
/* Return true if a value of mode MODE is read by __xload_* function. */
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);
}
int regno_dest;
int segment;
RTX_CODE code;
-
- addr_space_t as;
+ addr_space_t as = MEM_ADDR_SPACE (src);
if (plen)
*plen = 0;
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;
}
-/* 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);
- }
+ xop[3] = AVR_HAVE_LPMX ? op[0] : lpm_reg_rtx;
- 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 "";
- }
-
- 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 "";
}
rtx src = operands[1];
int *real_l = l;
- if (avr_mem_pgm_p (src)
- || avr_mem_pgm_p (dest))
+ if (avr_mem_flash_p (src)
+ || avr_mem_flash_p (dest))
{
return avr_out_lpm (insn, operands, real_l);
}
gcc_assert (GET_MODE_SIZE (GET_MODE (dest)) == 2);
- if (avr_mem_pgm_p (src)
- || avr_mem_pgm_p (dest))
+ if (avr_mem_flash_p (src)
+ || avr_mem_flash_p (dest))
{
return avr_out_lpm (insn, xop, plen);
}
rtx src = operands[1];
int *real_l = l;
- if (avr_mem_pgm_p (src)
- || avr_mem_pgm_p (dest))
+ if (avr_mem_flash_p (src)
+ || avr_mem_flash_p (dest))
{
return avr_out_lpm (insn, operands, real_l);
}
rtx dest = op[0];
rtx src = op[1];
- if (avr_mem_pgm_p (src)
- || avr_mem_pgm_p (dest))
+ if (avr_mem_flash_p (src)
+ || avr_mem_flash_p (dest))
{
return avr_out_lpm (insn, op, plen);
}
/* Value (0..0xff) held in clobber register xop[2] or -1 if unknown. */
int clobber_val = -1;
- gcc_assert (REG_P (xreg)
- && CONST_INT_P (xval));
+ gcc_assert (REG_P (xreg));
+ gcc_assert ((CONST_INT_P (xval) && n_bytes <= 4)
+ || (const_double_operand (xval, VOIDmode) && n_bytes == 8));
if (plen)
*plen = 0;
/* Comparisons == +/-1 and != +/-1 can be done similar to camparing
- against 0 by ORing the bytes. This is one instruction shorter. */
+ against 0 by ORing the bytes. This is one instruction shorter.
+ Notice that DImode comparisons are always against reg:DI 18
+ and therefore don't use this. */
if (!test_hard_reg_class (LD_REGS, xreg)
&& compare_eq_p (insn)
}
+/* Prepare operands of compare_const_di2 to be used with avr_out_compare. */
+
+const char*
+avr_out_compare64 (rtx insn, rtx *op, int *plen)
+{
+ rtx xop[3];
+
+ xop[0] = gen_rtx_REG (DImode, 18);
+ xop[1] = op[0];
+ xop[2] = op[1];
+
+ return avr_out_compare (insn, xop, plen);
+}
+
/* Output test instruction for HImode. */
const char*
/* fall through */
- case 31:
+ case 23:
return avr_asm_len ("lsl %C0" CR_TAB
"sbc %A0,%A0" CR_TAB
"mov %B0,%A0" CR_TAB
*pcc = (MINUS == code) ? CC_SET_CZN : CC_CLOBBER;
if (MINUS == code)
- xval = gen_int_mode (-UINTVAL (xval), mode);
+ xval = simplify_unary_operation (NEG, mode, xval, mode);
op[2] = xop[3];
return avr_out_plus (op, plen, pcc);
}
+
+/* Prepare operands of adddi3_const_insn to be used with avr_out_plus_1. */
+
+const char*
+avr_out_plus64 (rtx addend, int *plen)
+{
+ int cc_dummy;
+ rtx op[4];
+
+ op[0] = gen_rtx_REG (DImode, 18);
+ op[1] = op[0];
+ op[2] = addend;
+ op[3] = NULL_RTX;
+
+ avr_out_plus_1 (op, plen, MINUS, &cc_dummy);
+
+ return "";
+}
+
/* Output bit operation (IOR, AND, XOR) with register XOP[0] and compile
time constant XOP[2]:
case ADJUST_LEN_OUT_BITOP: avr_out_bitop (insn, op, &len); break;
case ADJUST_LEN_OUT_PLUS: avr_out_plus (op, &len, NULL); break;
+ case ADJUST_LEN_PLUS64: avr_out_plus64 (op[0], &len); break;
case ADJUST_LEN_OUT_PLUS_NOCLOBBER:
avr_out_plus_noclobber (op, &len, NULL); break;
case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break;
case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break;
case ADJUST_LEN_COMPARE: avr_out_compare (insn, op, &len); break;
+ case ADJUST_LEN_COMPARE64: avr_out_compare64 (insn, op, &len); break;
case ADJUST_LEN_LSHRQI: lshrqi3_out (insn, op, &len); break;
case ADJUST_LEN_LSHRHI: lshrhi3_out (insn, op, &len); break;
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);
if (TREE_CODE (decl) != VAR_DECL)
return 0;
- if (avr_decl_pgmx_p (decl))
+ if (avr_decl_memx_p (decl))
return 2;
- if (avr_decl_pgm_p (decl))
+ if (avr_decl_flash_p (decl))
return 1;
if (NULL_TREE
}
-/* Sanity check NODE so that all pointers targeting address space AS1
- go along with CONST qualifier. Writing to this address space should
+/* Sanity check NODE so that all pointers targeting non-generic addres spaces
+ go along with CONST qualifier. Writing to these address spaces should
be detected and complained about as early as possible. */
static bool
if (STR_PREFIX_P (name, old_prefix))
{
- const char *rname = avr_replace_prefix (name,
- old_prefix, new_prefix);
-
+ const char *rname = ACONCAT ((new_prefix,
+ name + strlen (old_prefix), NULL));
flags &= ~SECTION_CODE;
flags |= AVR_HAVE_JMP_CALL ? 0 : SECTION_CODE;
int segment = avr_addrspace[as].segment % avr_current_arch->n_segments;
const char *old_prefix = ".rodata";
const char *new_prefix = progmem_section_prefix[segment];
- const char *sname = new_prefix;
if (STR_PREFIX_P (name, old_prefix))
{
- sname = avr_replace_prefix (name, old_prefix, new_prefix);
+ const char *sname = ACONCAT ((new_prefix,
+ name + strlen (old_prefix), NULL));
+ default_elf_asm_named_section (sname, flags, decl);
+ return;
}
- default_elf_asm_named_section (sname, flags, decl);
-
+ default_elf_asm_named_section (new_prefix, flags, decl);
return;
}
addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (decl));
/* Attribute progmem puts data in generic address space.
- Set section flags as if it was in __pgm to get the right
+ Set section flags as if it was in __flash to get the right
section prefix in the remainder. */
if (ADDR_SPACE_GENERIC_P (as))
- as = ADDR_SPACE_PGM;
+ as = ADDR_SPACE_FLASH;
flags |= as * SECTION_MACH_DEP;
flags &= ~SECTION_WRITE;
if (new_decl_p
&& decl && DECL_P (decl)
&& NULL_TREE == DECL_INITIAL (decl)
+ && !DECL_EXTERNAL (decl)
&& avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
{
warning (OPT_Wuninitialized,
}
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_FLASH;
+
+ AVR_SYMBOL_SET_ADDR_SPACE (sym, as);
+ }
}
if (STR_PREFIX_P (name, old_prefix))
{
- const char *sname = avr_replace_prefix (name,
- old_prefix, new_prefix);
-
+ const char *sname = ACONCAT ((new_prefix,
+ name + strlen (old_prefix), NULL));
return get_section (sname, sect->common.flags, sect->named.decl);
}
}
if (pattern
&& NONJUMP_INSN_P (insn)
&& SET_DEST (pattern) == cc0_rtx
- && GET_CODE (SET_SRC (pattern)) == COMPARE)
+ && GET_CODE (SET_SRC (pattern)) == COMPARE
+ && DImode != GET_MODE (XEXP (SET_SRC (pattern), 0))
+ && DImode != GET_MODE (XEXP (SET_SRC (pattern), 1)))
{
return pattern;
}
The effect on cc0 is as follows:
- Load 0 to any register : NONE
- Load ld register with any value : NONE
- Anything else: : CLOBBER */
+ Load 0 to any register except ZERO_REG : NONE
+ Load ld register with any value : NONE
+ Anything else: : CLOBBER */
static void
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;
}
if (ival[n] == 0)
{
if (!clear_p)
- avr_asm_len (ldreg_p ? "ldi %0,0" : "mov %0,__zero_reg__",
+ avr_asm_len (ldreg_p ? "ldi %0,0"
+ : ZERO_REGNO == REGNO (xdest[n]) ? "clr %0"
+ : "mov %0,__zero_reg__",
&xdest[n], len, 1);
continue;
}
{
/* Default needs 4 CLR instructions: clear register beforehand. */
- avr_asm_len ("clr %A0" CR_TAB
- "clr %B0" CR_TAB
+ avr_asm_len ("mov %A0,__zero_reg__" CR_TAB
+ "mov %B0,__zero_reg__" CR_TAB
"movw %C0,%A0", &op[0], len, 3);
output_reload_in_const (op, clobber_reg, len, true);
case ADDR_SPACE_GENERIC:
return avr_legitimate_address_p (mode, x, strict);
- case ADDR_SPACE_PGM:
- case ADDR_SPACE_PGM1:
- case ADDR_SPACE_PGM2:
- case ADDR_SPACE_PGM3:
- case ADDR_SPACE_PGM4:
- case ADDR_SPACE_PGM5:
+ case ADDR_SPACE_FLASH:
+ case ADDR_SPACE_FLASH1:
+ case ADDR_SPACE_FLASH2:
+ case ADDR_SPACE_FLASH3:
+ case ADDR_SPACE_FLASH4:
+ case ADDR_SPACE_FLASH5:
switch (GET_CODE (x))
{
break;
}
- break; /* PGM */
+ break; /* FLASH */
- case ADDR_SPACE_PGMX:
+ case ADDR_SPACE_MEMX:
if (REG_P (x))
ok = (!strict
&& can_create_pseudo_p());
&& REGNO (lo) == REG_Z);
}
- break; /* PGMX */
+ break; /* MEMX */
}
if (avr_log.legitimate_address_p)
avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n",
src, type_from, type_to);
- if (as_from != ADDR_SPACE_PGMX
- && as_to == ADDR_SPACE_PGMX)
+ /* Up-casting from 16-bit to 24-bit pointer. */
+
+ if (as_from != ADDR_SPACE_MEMX
+ && as_to == ADDR_SPACE_MEMX)
{
- 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);
- 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)))
+ /* 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 (SYMBOL_REF == GET_CODE (sym)
+ && ADDR_SPACE_FLASH == 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_FLASH;
}
- 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_MEMX
+ && as_to != ADDR_SPACE_MEMX)
+ {
+ 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;
/* 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
rtx loop_reg, addr0, addr1, a_src, a_dest, insn, xas, reg_x;
rtx a_hi8 = NULL_RTX;
- if (avr_mem_pgm_p (xop[0]))
+ if (avr_mem_flash_p (xop[0]))
return false;
if (!CONST_INT_P (xop[2]))
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_MEMX);
+
+ 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);
}
{
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_FLASH;
+ }
+
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
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_MEMX)
{
+ /* 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);
avr_asm_len ("ld %6,%a1+", xop, plen, 1);
break;
- case ADDR_SPACE_PGM:
+ case ADDR_SPACE_FLASH:
if (AVR_HAVE_LPMX)
avr_asm_len ("lpm %6,%a1+", xop, plen, 1);
"adiw %1,1", xop, plen, 2);
break;
- case ADDR_SPACE_PGM1:
- case ADDR_SPACE_PGM2:
- case ADDR_SPACE_PGM3:
- case ADDR_SPACE_PGM4:
- case ADDR_SPACE_PGM5:
- case ADDR_SPACE_PGMX:
+ case ADDR_SPACE_FLASH1:
+ case ADDR_SPACE_FLASH2:
+ case ADDR_SPACE_FLASH3:
+ case ADDR_SPACE_FLASH4:
+ case ADDR_SPACE_FLASH5:
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;
}