/* Return true if STR starts with PREFIX and false, otherwise. */
#define STR_PREFIX_P(STR,PREFIX) (0 == strncmp (STR, PREFIX, strlen (PREFIX)))
-#define AVR_SECTION_PROGMEM (SECTION_MACH_DEP << 0)
+/* The 4 bits starting at SECTION_MACH_DEP are reverved to store
+ 1 + flash segment where progmem data is to be located.
+ For example, data with __pgm2 is stored as (1+2) * SECTION_MACH_DEP. */
+#define AVR_SECTION_PROGMEM (0xf * SECTION_MACH_DEP)
/* Prototypes for local helper functions. */
/* Zeroed register RTX (gen_rtx_REG (QImode, ZERO_REGNO)) */
static GTY(()) rtx zero_reg_rtx;
+/* RAMPZ special function register */
+static GTY(()) 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];
static GTY(()) section *progmem_swtable_section;
/* Unnamed section associated to __attribute__((progmem)) aka. PROGMEM. */
-static GTY(()) section *progmem_section;
+static GTY(()) section *progmem_section[6];
+
+static const char * const progmem_section_prefix[6] =
+ {
+ ".progmem.data",
+ ".progmem1.data",
+ ".progmem2.data",
+ ".progmem3.data",
+ ".progmem4.data",
+ ".progmem5.data"
+ };
/* To track if code will use .bss and/or .data. */
bool avr_need_clear_bss_p = false;
static void
avr_option_override (void)
{
- int regno;
-
flag_delete_null_pointer_checks = 0;
/* caller-save.c looks for call-clobbered hard registers that are assigned
avr_current_arch = &avr_arch_types[avr_current_device->arch];
avr_extra_arch_macro = avr_current_device->macro;
- for (regno = 0; regno < 32; regno ++)
- all_regs_rtx[regno] = gen_rtx_REG (QImode, regno);
-
- lpm_reg_rtx = all_regs_rtx[LPM_REGNO];
- tmp_reg_rtx = all_regs_rtx[TMP_REGNO];
- zero_reg_rtx = all_regs_rtx[ZERO_REGNO];
-
- lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z);
-
init_machine_status = avr_init_machine_status;
avr_log_set_avr_log();
return ggc_alloc_cleared_machine_function ();
}
+
+/* Implement `INIT_EXPANDERS'. */
+/* The function works like a singleton. */
+
+void
+avr_init_expanders (void)
+{
+ int regno;
+
+ static bool done = false;
+
+ if (done)
+ return;
+ else
+ done = true;
+
+ for (regno = 0; regno < 32; regno ++)
+ all_regs_rtx[regno] = gen_rtx_REG (QImode, regno);
+
+ lpm_reg_rtx = all_regs_rtx[LPM_REGNO];
+ tmp_reg_rtx = all_regs_rtx[TMP_REGNO];
+ zero_reg_rtx = all_regs_rtx[ZERO_REGNO];
+
+ lpm_addr_reg_rtx = gen_rtx_REG (HImode, REG_Z);
+
+ rampz_rtx = gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR));
+
+ xstring_empty = gen_rtx_CONST_STRING (VOIDmode, "");
+ xstring_e = gen_rtx_CONST_STRING (VOIDmode, "e");
+}
+
+
/* Return register class for register R. */
enum reg_class
}
+/* Return the segment number of pgm address space AS, i.e.
+ the 64k block it lives in.
+ Return -1 if unknown, i.e. 24-bit AS in flash.
+ Return -2 for anything else. */
+
+static int
+avr_pgm_segment (addr_space_t as)
+{
+ switch (as)
+ {
+ default: return -2;
+
+ case ADDR_SPACE_PGMX: return -1;
+ case ADDR_SPACE_PGM: return 0;
+ case ADDR_SPACE_PGM1: return 1;
+ case ADDR_SPACE_PGM2: return 2;
+ case ADDR_SPACE_PGM3: return 3;
+ case ADDR_SPACE_PGM4: return 4;
+ case ADDR_SPACE_PGM5: return 5;
+ }
+}
+
+
/* Return TRUE if DECL is a VAR_DECL located in Flash and FALSE, otherwise. */
static bool
avr_decl_pgm_p (tree decl)
{
- if (TREE_CODE (decl) != VAR_DECL)
- return false;
+ if (TREE_CODE (decl) != VAR_DECL
+ || TREE_TYPE (decl) == error_mark_node)
+ {
+ return false;
+ }
return !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (TREE_TYPE (decl)));
}
+/* Return TRUE if DECL is a VAR_DECL located in the 24-bit Flash
+ address space and FALSE, otherwise. */
+
+static bool
+avr_decl_pgmx_p (tree decl)
+{
+ if (TREE_CODE (decl) != VAR_DECL
+ || TREE_TYPE (decl) == error_mark_node)
+ {
+ return false;
+ }
+
+ return (ADDR_SPACE_PGMX == TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+}
+
+
/* Return TRUE if X is a MEM rtx located in Flash and FALSE, otherwise. */
bool
}
+/* Return TRUE if X is a MEM rtx located in the 24-bit Flash
+ address space and FALSE, otherwise. */
+
+bool
+avr_mem_pgmx_p (rtx x)
+{
+ return (MEM_P (x)
+ && ADDR_SPACE_PGMX == MEM_ADDR_SPACE (x));
+}
+
+
/* A helper for the subsequent function attribute used to dig for
attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
&& TEST_HARD_REG_BIT (set, REG_Z)
&& TEST_HARD_REG_BIT (set, REG_Z + 1))
{
- emit_move_insn (tmp_reg_rtx,
- gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)));
+ emit_move_insn (tmp_reg_rtx, rampz_rtx);
emit_push_byte (TMP_REGNO, false);
}
&& TEST_HARD_REG_BIT (set, REG_Z + 1))
{
emit_pop_byte (TMP_REGNO);
- emit_move_insn (gen_rtx_MEM (QImode, GEN_INT (RAMPZ_ADDR)),
- tmp_reg_rtx);
+ emit_move_insn (rampz_rtx, tmp_reg_rtx);
}
/* Restore SREG using tmp reg as scratch. */
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
fprintf (file, "0x%lx", val);
}
+ else if (GET_CODE (x) == CONST_STRING)
+ fputs (XSTR (x, 0), file);
else if (code == 'j')
fputs (cond_string (GET_CODE (x)), file);
else if (code == 'k')
&& avr_mem_pgm_p (op));
}
+/* Return true if a value of mode MODE is read by __xload_* function. */
+
+bool
+avr_xload_libgcc_p (enum machine_mode mode)
+{
+ int n_bytes = GET_MODE_SIZE (mode);
+
+ return (n_bytes > 1
+ && avr_current_arch->n_segments > 1
+ && !AVR_HAVE_ELPMX);
+}
+
+
+/* Find an unused d-register to be used as scratch in INSN.
+ EXCLUDE is either NULL_RTX or some register. In the case where EXCLUDE
+ is a register, skip all possible return values that overlap EXCLUDE.
+ The policy for the returned register is similar to that of
+ `reg_unused_after', i.e. the returned register may overlap the SET_DEST
+ of INSN.
+
+ Return a QImode d-register or NULL_RTX if nothing found. */
+
+static rtx
+avr_find_unused_d_reg (rtx insn, rtx exclude)
+{
+ int regno;
+ bool isr_p = (interrupt_function_p (current_function_decl)
+ || signal_function_p (current_function_decl));
+
+ for (regno = 16; regno < 32; regno++)
+ {
+ rtx reg = all_regs_rtx[regno];
+
+ if ((exclude
+ && reg_overlap_mentioned_p (exclude, reg))
+ || fixed_regs[regno])
+ {
+ continue;
+ }
+
+ /* Try non-live register */
+
+ if (!df_regs_ever_live_p (regno)
+ && (TREE_THIS_VOLATILE (current_function_decl)
+ || cfun->machine->is_OS_task
+ || cfun->machine->is_OS_main
+ || (!isr_p && call_used_regs[regno])))
+ {
+ return reg;
+ }
+
+ /* Any live register can be used if it is unused after.
+ Prologue/epilogue will care for it as needed. */
+
+ if (df_regs_ever_live_p (regno)
+ && reg_unused_after (insn, reg))
+ {
+ return reg;
+ }
+ }
+
+ return NULL_RTX;
+}
+
/* Helper function for the next function in the case where only restricted
version of LPM instruction is available. */
gcc_unreachable();
case 1:
- return avr_asm_len ("lpm" CR_TAB
- "mov %0,%3", xop, plen, 2);
+ avr_asm_len ("%4lpm", xop, plen, 1);
+
+ if (regno_dest != LPM_REGNO)
+ avr_asm_len ("mov %0,%3", xop, plen, 1);
+
+ return "";
case 2:
if (REGNO (dest) == REG_Z)
- return avr_asm_len ("lpm" CR_TAB
+ return avr_asm_len ("%4lpm" CR_TAB
"push %3" CR_TAB
"adiw %2,1" CR_TAB
- "lpm" CR_TAB
+ "%4lpm" CR_TAB
"mov %B0,%3" CR_TAB
"pop %A0", xop, plen, 6);
- else
- {
- avr_asm_len ("lpm" CR_TAB
- "mov %A0,%3" CR_TAB
- "adiw %2,1" CR_TAB
- "lpm" CR_TAB
- "mov %B0,%3", xop, plen, 5);
+
+ avr_asm_len ("%4lpm" CR_TAB
+ "mov %A0,%3" CR_TAB
+ "adiw %2,1" CR_TAB
+ "%4lpm" CR_TAB
+ "mov %B0,%3", xop, plen, 5);
- if (!reg_unused_after (insn, addr))
- avr_asm_len ("sbiw %2,1", xop, plen, 1);
- }
+ if (!reg_unused_after (insn, addr))
+ avr_asm_len ("sbiw %2,1", xop, plen, 1);
break; /* 2 */
}
case POST_INC:
gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
- && n_bytes <= 2);
+ && n_bytes <= 4);
- avr_asm_len ("lpm" CR_TAB
- "mov %A0,%3" CR_TAB
- "adiw %2,1", xop, plen, 3);
+ if (regno_dest == LPM_REGNO)
+ avr_asm_len ("%4lpm" CR_TAB
+ "adiw %2,1", xop, plen, 2);
+ else
+ avr_asm_len ("%4lpm" CR_TAB
+ "mov %A0,%3" CR_TAB
+ "adiw %2,1", xop, plen, 3);
if (n_bytes >= 2)
- avr_asm_len ("lpm" CR_TAB
+ avr_asm_len ("%4lpm" CR_TAB
"mov %B0,%3" CR_TAB
"adiw %2,1", xop, plen, 3);
+ if (n_bytes >= 3)
+ avr_asm_len ("%4lpm" CR_TAB
+ "mov %C0,%3" CR_TAB
+ "adiw %2,1", xop, plen, 3);
+
+ if (n_bytes >= 4)
+ avr_asm_len ("%4lpm" CR_TAB
+ "mov %D0,%3" CR_TAB
+ "adiw %2,1", xop, plen, 3);
+
break; /* POST_INC */
} /* switch CODE (addr) */
static const char*
avr_out_lpm (rtx insn, rtx *op, int *plen)
{
- rtx xop[5];
+ rtx xop[6];
rtx dest = op[0];
rtx src = SET_SRC (single_set (insn));
rtx addr;
int n_bytes = GET_MODE_SIZE (GET_MODE (dest));
int regno_dest;
+ int segment;
if (plen)
*plen = 0;
addr = XEXP (src, 0);
- gcc_assert (!avr_load_libgcc_p (src)
- && REG_P (dest)
- && (REG_P (addr) || POST_INC == GET_CODE (addr)));
+ segment = avr_pgm_segment (MEM_ADDR_SPACE (src));
+
+ gcc_assert (REG_P (dest)
+ && ((segment >= 0
+ && (REG_P (addr) || POST_INC == GET_CODE (addr)))
+ || (GET_CODE (addr) == LO_SUM && segment == -1)));
+
+ if (segment == -1)
+ {
+ /* We are called from avr_out_xload because someone wrote
+ __pgmx on a device with just one flash segment. */
+
+ addr = XEXP (addr, 1);
+ }
xop[0] = dest;
xop[1] = addr;
xop[2] = lpm_addr_reg_rtx;
+ xop[4] = xstring_empty;
+ xop[5] = tmp_reg_rtx;
regno_dest = REGNO (dest);
- if (!AVR_HAVE_LPMX)
+ /* Cut down segment number to a number the device actually
+ supports. We do this late to preserve the address space's
+ name for diagnostics. */
+
+ segment %= avr_current_arch->n_segments;
+
+ /* Set RAMPZ as needed. */
+
+ if (segment)
+ {
+ xop[4] = GEN_INT (segment);
+
+ if (xop[3] = avr_find_unused_d_reg (insn, lpm_addr_reg_rtx),
+ xop[3])
+ {
+ avr_asm_len ("ldi %3,%4" CR_TAB
+ "out __RAMPZ__,%3", xop, plen, 2);
+ }
+ else if (segment == 1)
+ {
+ avr_asm_len ("clr %5" CR_TAB
+ "inc %5" CR_TAB
+ "out __RAMPZ__,%5", xop, plen, 3);
+ }
+ else
+ {
+ avr_asm_len ("mov %5,%2" CR_TAB
+ "ldi %2,%4" CR_TAB
+ "out __RAMPZ__,%2" CR_TAB
+ "mov %2,%5", xop, plen, 4);
+ }
+
+ xop[4] = xstring_e;
+ }
+
+ if ((segment == 0 && !AVR_HAVE_LPMX)
+ || (segment != 0 && !AVR_HAVE_ELPMX))
{
return avr_out_lpm_no_lpmx (insn, xop, plen);
}
gcc_unreachable();
case 1:
- return avr_asm_len ("lpm %0,%a2", xop, plen, -1);
+ return avr_asm_len ("%4lpm %0,%a2", xop, plen, 1);
case 2:
if (REGNO (dest) == REG_Z)
- return avr_asm_len ("lpm __tmp_reg__,%a2+" CR_TAB
- "lpm %B0,%a2" CR_TAB
- "mov %A0,__tmp_reg__", xop, plen, -3);
+ return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
+ "%4lpm %B0,%a2" CR_TAB
+ "mov %A0,%5", xop, plen, 3);
else
{
- avr_asm_len ("lpm %A0,%a2+" CR_TAB
- "lpm %B0,%a2", xop, plen, -2);
+ avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+ "%4lpm %B0,%a2", xop, plen, 2);
if (!reg_unused_after (insn, addr))
avr_asm_len ("sbiw %2,1", xop, plen, 1);
case 3:
- avr_asm_len ("lpm %A0,%a2+" CR_TAB
- "lpm %B0,%a2+" CR_TAB
- "lpm %C0,%a2", xop, plen, -3);
+ avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+ "%4lpm %B0,%a2+" CR_TAB
+ "%4lpm %C0,%a2", xop, plen, 3);
if (!reg_unused_after (insn, addr))
avr_asm_len ("sbiw %2,2", xop, plen, 1);
case 4:
- avr_asm_len ("lpm %A0,%a2+" CR_TAB
- "lpm %B0,%a2+", xop, plen, -2);
+ avr_asm_len ("%4lpm %A0,%a2+" CR_TAB
+ "%4lpm %B0,%a2+", xop, plen, 2);
if (REGNO (dest) == REG_Z - 2)
- return avr_asm_len ("lpm __tmp_reg__,%a2+" CR_TAB
- "lpm %C0,%a2" CR_TAB
- "mov %D0,__tmp_reg__", xop, plen, 3);
+ return avr_asm_len ("%4lpm %5,%a2+" CR_TAB
+ "%4lpm %C0,%a2" CR_TAB
+ "mov %D0,%5", xop, plen, 3);
else
{
- avr_asm_len ("lpm %C0,%a2+" CR_TAB
- "lpm %D0,%a2", xop, plen, 2);
+ avr_asm_len ("%4lpm %C0,%a2+" CR_TAB
+ "%4lpm %D0,%a2", xop, plen, 2);
if (!reg_unused_after (insn, addr))
avr_asm_len ("sbiw %2,3", xop, plen, 1);
gcc_assert (REG_Z == REGNO (XEXP (addr, 0))
&& n_bytes <= 4);
- avr_asm_len ("lpm %A0,%a2+", xop, plen, -1);
- if (n_bytes >= 2) avr_asm_len ("lpm %B0,%a2+", xop, plen, 1);
- if (n_bytes >= 3) avr_asm_len ("lpm %C0,%a2+", xop, plen, 1);
- if (n_bytes >= 4) avr_asm_len ("lpm %D0,%a2+", xop, plen, 1);
+ avr_asm_len ("%4lpm %A0,%a2+", xop, plen, 1);
+ if (n_bytes >= 2) avr_asm_len ("%4lpm %B0,%a2+", xop, plen, 1);
+ if (n_bytes >= 3) avr_asm_len ("%4lpm %C0,%a2+", xop, plen, 1);
+ if (n_bytes >= 4) avr_asm_len ("%4lpm %D0,%a2+", xop, plen, 1);
break; /* POST_INC */
}
+/* Worker function for xload_<mode> and xload_8 insns. */
+
+const char*
+avr_out_xload (rtx insn, 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);
+
+ xop[0] = reg;
+ 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 "";
+ }
+
+ switch (n_bytes)
+ {
+ default:
+ gcc_unreachable();
+
+ case 2:
+ gcc_assert (regno == REGNO (lpm_addr_reg_rtx));
+
+ return avr_asm_len ("elpm %4,%a2+" CR_TAB
+ "elpm %B0,%a2" CR_TAB
+ "mov %A0,%4", xop, plen, 3);
+
+ 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);
+
+ 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);
+ }
+
+ return "";
+}
+
+
const char *
output_movqi (rtx insn, rtx operands[], int *l)
{
}
const char *
-out_movqi_r_mr (rtx insn, rtx op[], int *l)
+out_movqi_r_mr (rtx insn, rtx op[], int *plen)
{
rtx dest = op[0];
rtx src = op[1];
rtx x = XEXP (src, 0);
- int dummy;
-
- if (!l)
- l = &dummy;
if (CONSTANT_ADDRESS_P (x))
{
- if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR)
- {
- *l = 1;
- return AS2 (in,%0,__SREG__);
- }
+ if (CONST_INT_P (x))
+ {
+ if (SREG_ADDR == INTVAL (x))
+ return avr_asm_len ("in %0,__SREG__", op, plen, -1);
+
+ if (RAMPZ_ADDR == INTVAL (x))
+ return avr_asm_len ("in %0,__RAMPZ__", op, plen, -1);
+ }
+
if (optimize > 0 && io_address_operand (x, QImode))
- {
- *l = 1;
- return AS2 (in,%0,%m1-0x20);
- }
- *l = 2;
- return AS2 (lds,%0,%m1);
+ return avr_asm_len ("in %0,%m1-0x20", op, plen, -1);
+
+ return avr_asm_len ("lds %0,%m1", op, plen, -2);
}
- /* memory access by reg+disp */
else if (GET_CODE (x) == PLUS
- && REG_P (XEXP (x,0))
- && GET_CODE (XEXP (x,1)) == CONST_INT)
+ && REG_P (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1)))
{
- if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (src))) >= 63)
- {
- int disp = INTVAL (XEXP (x,1));
- if (REGNO (XEXP (x,0)) != REG_Y)
- fatal_insn ("incorrect insn:",insn);
+ /* memory access by reg+disp */
- if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
- return *l = 3, (AS2 (adiw,r28,%o1-63) CR_TAB
- AS2 (ldd,%0,Y+63) CR_TAB
- AS2 (sbiw,r28,%o1-63));
+ int disp = INTVAL (XEXP (x, 1));
+
+ if (disp - GET_MODE_SIZE (GET_MODE (src)) >= 63)
+ {
+ if (REGNO (XEXP (x, 0)) != REG_Y)
+ fatal_insn ("incorrect insn:",insn);
- return *l = 5, (AS2 (subi,r28,lo8(-%o1)) CR_TAB
- AS2 (sbci,r29,hi8(-%o1)) CR_TAB
- AS2 (ld,%0,Y) CR_TAB
- AS2 (subi,r28,lo8(%o1)) CR_TAB
- AS2 (sbci,r29,hi8(%o1)));
- }
- else if (REGNO (XEXP (x,0)) == REG_X)
- {
- /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude
- it but I have this situation with extremal optimizing options. */
- if (reg_overlap_mentioned_p (dest, XEXP (x,0))
- || reg_unused_after (insn, XEXP (x,0)))
- return *l = 2, (AS2 (adiw,r26,%o1) CR_TAB
- AS2 (ld,%0,X));
-
- return *l = 3, (AS2 (adiw,r26,%o1) CR_TAB
- AS2 (ld,%0,X) CR_TAB
- AS2 (sbiw,r26,%o1));
- }
- *l = 1;
- return AS2 (ldd,%0,%1);
+ if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (src)))
+ return avr_asm_len ("adiw r28,%o1-63" CR_TAB
+ "ldd %0,Y+63" CR_TAB
+ "sbiw r28,%o1-63", op, plen, -3);
+
+ return avr_asm_len ("subi r28,lo8(-%o1)" CR_TAB
+ "sbci r29,hi8(-%o1)" CR_TAB
+ "ld %0,Y" CR_TAB
+ "subi r28,lo8(%o1)" CR_TAB
+ "sbci r29,hi8(%o1)", op, plen, -5);
+ }
+ else if (REGNO (XEXP (x, 0)) == REG_X)
+ {
+ /* This is a paranoid case LEGITIMIZE_RELOAD_ADDRESS must exclude
+ it but I have this situation with extremal optimizing options. */
+
+ avr_asm_len ("adiw r26,%o1" CR_TAB
+ "ld %0,X", op, plen, -2);
+
+ if (!reg_overlap_mentioned_p (dest, XEXP (x,0))
+ && !reg_unused_after (insn, XEXP (x,0)))
+ {
+ avr_asm_len ("sbiw r26,%o1", op, plen, 1);
+ }
+
+ return "";
+ }
+
+ return avr_asm_len ("ldd %0,%1", op, plen, -1);
}
- *l = 1;
- return AS2 (ld,%0,%1);
+
+ return avr_asm_len ("ld %0,%1", op, plen, -1);
}
const char *
const char *
-out_movqi_mr_r (rtx insn, rtx op[], int *l)
+out_movqi_mr_r (rtx insn, rtx op[], int *plen)
{
rtx dest = op[0];
rtx src = op[1];
rtx x = XEXP (dest, 0);
- int dummy;
-
- if (!l)
- l = &dummy;
if (CONSTANT_ADDRESS_P (x))
{
- if (CONST_INT_P (x) && INTVAL (x) == SREG_ADDR)
- {
- *l = 1;
- return AS2 (out,__SREG__,%1);
- }
+ if (CONST_INT_P (x))
+ {
+ if (SREG_ADDR == INTVAL (x))
+ return avr_asm_len ("out __SREG__,%1", op, plen, -1);
+
+ if (RAMPZ_ADDR == INTVAL (x))
+ return avr_asm_len ("out __RAMPZ__,%1", op, plen, -1);
+ }
+
if (optimize > 0 && io_address_operand (x, QImode))
- {
- *l = 1;
- return AS2 (out,%m0-0x20,%1);
- }
- *l = 2;
- return AS2 (sts,%m0,%1);
+ avr_asm_len ("out %m0-0x20,%1", op, plen, -1);
+
+ return avr_asm_len ("sts %m0,%1", op, plen, -2);
}
- /* memory access by reg+disp */
- else if (GET_CODE (x) == PLUS
- && REG_P (XEXP (x,0))
- && GET_CODE (XEXP (x,1)) == CONST_INT)
+ else if (GET_CODE (x) == PLUS
+ && REG_P (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1)))
{
- if ((INTVAL (XEXP (x,1)) - GET_MODE_SIZE (GET_MODE (dest))) >= 63)
- {
- int disp = INTVAL (XEXP (x,1));
- if (REGNO (XEXP (x,0)) != REG_Y)
- fatal_insn ("incorrect insn:",insn);
+ /* memory access by reg+disp */
- if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
- return *l = 3, (AS2 (adiw,r28,%o0-63) CR_TAB
- AS2 (std,Y+63,%1) CR_TAB
- AS2 (sbiw,r28,%o0-63));
+ int disp = INTVAL (XEXP (x, 1));
- return *l = 5, (AS2 (subi,r28,lo8(-%o0)) CR_TAB
- AS2 (sbci,r29,hi8(-%o0)) CR_TAB
- AS2 (st,Y,%1) CR_TAB
- AS2 (subi,r28,lo8(%o0)) CR_TAB
- AS2 (sbci,r29,hi8(%o0)));
- }
+ if (disp - GET_MODE_SIZE (GET_MODE (dest)) >= 63)
+ {
+ if (REGNO (XEXP (x, 0)) != REG_Y)
+ fatal_insn ("incorrect insn:",insn);
+
+ if (disp <= 63 + MAX_LD_OFFSET (GET_MODE (dest)))
+ return avr_asm_len ("adiw r28,%o0-63" CR_TAB
+ "std Y+63,%1" CR_TAB
+ "sbiw r28,%o0-63", op, plen, -3);
+
+ return avr_asm_len ("subi r28,lo8(-%o0)" CR_TAB
+ "sbci r29,hi8(-%o0)" CR_TAB
+ "st Y,%1" CR_TAB
+ "subi r28,lo8(%o0)" CR_TAB
+ "sbci r29,hi8(%o0)", op, plen, -5);
+ }
else if (REGNO (XEXP (x,0)) == REG_X)
- {
- if (reg_overlap_mentioned_p (src, XEXP (x, 0)))
- {
- if (reg_unused_after (insn, XEXP (x,0)))
- return *l = 3, (AS2 (mov,__tmp_reg__,%1) CR_TAB
- AS2 (adiw,r26,%o0) CR_TAB
- AS2 (st,X,__tmp_reg__));
-
- return *l = 4, (AS2 (mov,__tmp_reg__,%1) CR_TAB
- AS2 (adiw,r26,%o0) CR_TAB
- AS2 (st,X,__tmp_reg__) CR_TAB
- AS2 (sbiw,r26,%o0));
- }
- else
- {
- if (reg_unused_after (insn, XEXP (x,0)))
- return *l = 2, (AS2 (adiw,r26,%o0) CR_TAB
- AS2 (st,X,%1));
+ {
+ if (reg_overlap_mentioned_p (src, XEXP (x, 0)))
+ {
+ avr_asm_len ("mov __tmp_reg__,%1" CR_TAB
+ "adiw r26,%o0" CR_TAB
+ "st X,__tmp_reg__", op, plen, -3);
+ }
+ else
+ {
+ avr_asm_len ("adiw r26,%o0" CR_TAB
+ "st X,%1", op, plen, -2);
+ }
+
+ if (!reg_unused_after (insn, XEXP (x,0)))
+ avr_asm_len ("sbiw r26,%o0", op, plen, 1);
- return *l = 3, (AS2 (adiw,r26,%o0) CR_TAB
- AS2 (st,X,%1) CR_TAB
- AS2 (sbiw,r26,%o0));
- }
- }
- *l = 1;
- return AS2 (std,%0,%1);
+ return "";
+ }
+
+ return avr_asm_len ("std %0,%1", op, plen, 1);
}
- *l = 1;
- return AS2 (st,%0,%1);
+
+ return avr_asm_len ("st %0,%1", op, plen, 1);
}
const char *
case ADJUST_LEN_MOV16: output_movhi (insn, op, &len); break;
case ADJUST_LEN_MOV24: avr_out_movpsi (insn, op, &len); break;
case ADJUST_LEN_MOV32: output_movsisf (insn, op, &len); break;
-
+ case ADJUST_LEN_MOVMEM: avr_out_movmem (insn, op, &len); break;
+ case ADJUST_LEN_XLOAD: avr_out_xload (insn, op, &len); break;
+
case ADJUST_LEN_TSTHI: avr_out_tsthi (insn, op, &len); break;
case ADJUST_LEN_TSTPSI: avr_out_tstpsi (insn, op, &len); break;
case ADJUST_LEN_TSTSI: avr_out_tstsi (insn, op, &len); break;
return 1;
}
+
+/* Return RTX that represents the lower 16 bits of a constant address.
+ Unfortunately, simplify_gen_subreg does not handle this case. */
+
+static rtx
+avr_const_address_lo16 (rtx x)
+{
+ rtx lo16;
+
+ switch (GET_CODE (x))
+ {
+ default:
+ break;
+
+ case CONST:
+ if (PLUS == GET_CODE (XEXP (x, 0))
+ && SYMBOL_REF == GET_CODE (XEXP (XEXP (x, 0), 0))
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1)))
+ {
+ HOST_WIDE_INT offset = INTVAL (XEXP (XEXP (x, 0), 1));
+ const char *name = XSTR (XEXP (XEXP (x, 0), 0), 0);
+
+ lo16 = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+ lo16 = gen_rtx_CONST (Pmode, plus_constant (lo16, offset));
+
+ return lo16;
+ }
+
+ break;
+
+ case SYMBOL_REF:
+ {
+ const char *name = XSTR (x, 0);
+
+ return gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+ }
+ }
+
+ avr_edump ("\n%?: %r\n", x);
+ gcc_unreachable();
+}
+
+
/* Target hook for assembling integer objects. The AVR version needs
special handling for references to certain labels. */
fputs ("\t.word\tgs(", asm_out_file);
output_addr_const (asm_out_file, x);
fputs (")\n", asm_out_file);
+
+ return true;
+ }
+ else if (GET_MODE (x) == PSImode)
+ {
+ default_assemble_integer (avr_const_address_lo16 (x),
+ GET_MODE_SIZE (HImode), aligned_p);
+
+ fputs ("\t.warning\t\"assembling 24-bit address needs binutils extension for hh8(",
+ asm_out_file);
+ output_addr_const (asm_out_file, x);
+ fputs (")\"\n", asm_out_file);
+
+ fputs ("\t.byte\t0\t" ASM_COMMENT_START " hh8(", asm_out_file);
+ output_addr_const (asm_out_file, x);
+ fputs (")\n", asm_out_file);
+
return true;
}
+
return default_assemble_integer (x, size, aligned_p);
}
+
/* Worker function for ASM_DECLARE_FUNCTION_NAME. */
void
Return non-zero if DECL is data that must end up in Flash and
zero if the data lives in RAM (.bss, .data, .rodata, ...).
+ Return 2 if DECL is located in 24-bit flash address-space
Return 1 if DECL is located in 16-bit flash address-space
Return -1 if attribute `progmem' occurs in DECL or ATTRIBUTES
Return 0 otherwise */
if (TREE_CODE (decl) != VAR_DECL)
return 0;
+ if (avr_decl_pgmx_p (decl))
+ return 2;
+
if (avr_decl_pgm_p (decl))
return 1;
}
+/* Unnamed section callback for progmem*.data sections. */
+
+static void
+avr_output_progmem_section_asm_op (const void *data)
+{
+ fprintf (asm_out_file, "\t.section\t%s,\"a\",@progbits\n",
+ (const char*) data);
+}
+
+
/* Implement `TARGET_ASM_INIT_SECTIONS'. */
static void
avr_asm_init_sections (void)
{
+ unsigned int n;
+
/* Set up a section for jump tables. Alignment is handled by
ASM_OUTPUT_BEFORE_CASE_LABEL. */
",\"ax\",@progbits");
}
- progmem_section
- = get_unnamed_section (0, output_section_asm_op,
- "\t.section\t.progmem.data,\"a\",@progbits");
+ for (n = 0; n < sizeof (progmem_section) / sizeof (*progmem_section); n++)
+ {
+ progmem_section[n]
+ = get_unnamed_section (0, avr_output_progmem_section_asm_op,
+ progmem_section_prefix[n]);
+ }
/* Override section callbacks to keep track of `avr_need_clear_bss_p'
resp. `avr_need_copy_data_p'. */
{
if (flags & AVR_SECTION_PROGMEM)
{
+ int segment = (flags & AVR_SECTION_PROGMEM) / SECTION_MACH_DEP - 1;
const char *old_prefix = ".rodata";
- const char *new_prefix = ".progmem.data";
+ const char *new_prefix = progmem_section_prefix[segment];
const char *sname = new_prefix;
if (STR_PREFIX_P (name, old_prefix))
static unsigned int
avr_section_type_flags (tree decl, const char *name, int reloc)
{
+ int prog;
unsigned int flags = default_section_type_flags (decl, name, reloc);
if (STR_PREFIX_P (name, ".noinit"))
}
if (decl && DECL_P (decl)
- && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
+ && (prog = avr_progmem_p (decl, DECL_ATTRIBUTES (decl)), prog))
{
+ int segment = 0;
+
+ if (prog == 1)
+ segment = avr_pgm_segment (TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+
flags &= ~SECTION_WRITE;
flags &= ~SECTION_BSS;
- flags |= AVR_SECTION_PROGMEM;
+ flags |= (1 + segment % avr_current_arch->n_segments) * SECTION_MACH_DEP;
}
return flags;
static section *
avr_asm_select_section (tree decl, int reloc, unsigned HOST_WIDE_INT align)
{
+ int prog;
+
section * sect = default_elf_select_section (decl, reloc, align);
if (decl && DECL_P (decl)
- && avr_progmem_p (decl, DECL_ATTRIBUTES (decl)))
+ && (prog = avr_progmem_p (decl, DECL_ATTRIBUTES (decl)), prog))
{
+ int segment = 0;
+
+ if (prog == 1)
+ segment = avr_pgm_segment (TYPE_ADDR_SPACE (TREE_TYPE (decl)));
+
+ segment %= avr_current_arch->n_segments;
+
if (sect->common.flags & SECTION_NAMED)
{
const char * name = sect->named.name;
const char * old_prefix = ".rodata";
- const char * new_prefix = ".progmem.data";
+ const char * new_prefix = progmem_section_prefix[segment];
if (STR_PREFIX_P (name, old_prefix))
{
}
}
- return progmem_section;
+ return progmem_section[segment];
}
return sect;
}
/* Implement `TARGET_ASM_FILE_START'. */
-/* Outputs some appropriate text to go at the start of an assembler
- file. */
+/* Outputs some text at the start of each assembler file. */
static void
avr_file_start (void)
{
+ int sfr_offset = 0x20;
+
if (avr_current_arch->asm_only)
error ("MCU %qs supported for assembler only", avr_current_device->name);
default_file_start ();
-/* fprintf (asm_out_file, "\t.arch %s\n", avr_current_device->name);*/
- fputs ("__SREG__ = 0x3f\n"
- "__SP_H__ = 0x3e\n"
- "__SP_L__ = 0x3d\n", asm_out_file);
-
- fputs ("__tmp_reg__ = 0\n"
- "__zero_reg__ = 1\n", asm_out_file);
+ fprintf (asm_out_file,
+ "__SREG__ = 0x%02x\n"
+ "__SP_H__ = 0x%02x\n"
+ "__SP_L__ = 0x%02x\n"
+ "__RAMPZ__ = 0x%02x\n"
+ "__tmp_reg__ = 0\n"
+ "__zero_reg__ = 1\n",
+ -sfr_offset + SREG_ADDR,
+ -sfr_offset + SP_ADDR + 1,
+ -sfr_offset + SP_ADDR,
+ -sfr_offset + RAMPZ_ADDR);
}
avr_out_sbxx_branch (rtx insn, rtx operands[])
{
enum rtx_code comp = GET_CODE (operands[0]);
- int long_jump = (get_attr_length (insn) >= 4);
- int reverse = long_jump || jump_over_one_insn_p (insn, operands[3]);
+ bool long_jump = get_attr_length (insn) >= 4;
+ bool reverse = long_jump || jump_over_one_insn_p (insn, operands[3]);
if (comp == GE)
comp = EQ;
if (reverse)
comp = reverse_condition (comp);
- if (GET_CODE (operands[1]) == CONST_INT)
+ switch (GET_CODE (operands[1]))
{
- if (INTVAL (operands[1]) < 0x40)
- {
- if (comp == EQ)
- output_asm_insn (AS2 (sbis,%m1-0x20,%2), operands);
- else
- output_asm_insn (AS2 (sbic,%m1-0x20,%2), operands);
- }
+ default:
+ gcc_unreachable();
+
+ case CONST_INT:
+
+ if (low_io_address_operand (operands[1], QImode))
+ {
+ if (comp == EQ)
+ output_asm_insn ("sbis %m1-0x20,%2", operands);
+ else
+ output_asm_insn ("sbic %m1-0x20,%2", operands);
+ }
else
- {
- output_asm_insn (AS2 (in,__tmp_reg__,%m1-0x20), operands);
- if (comp == EQ)
- output_asm_insn (AS2 (sbrs,__tmp_reg__,%2), operands);
- else
- output_asm_insn (AS2 (sbrc,__tmp_reg__,%2), operands);
- }
- }
- else /* GET_CODE (operands[1]) == REG */
- {
+ {
+ output_asm_insn ("in __tmp_reg__,%m1-0x20", operands);
+ if (comp == EQ)
+ output_asm_insn ("sbrs __tmp_reg__,%2", operands);
+ else
+ output_asm_insn ("sbrc __tmp_reg__,%2", operands);
+ }
+
+ break; /* CONST_INT */
+
+ case REG:
+
if (GET_MODE (operands[1]) == QImode)
- {
- if (comp == EQ)
- output_asm_insn (AS2 (sbrs,%1,%2), operands);
- else
- output_asm_insn (AS2 (sbrc,%1,%2), operands);
- }
- else /* HImode or SImode */
- {
- static char buf[] = "sbrc %A1,0";
- int bit_nr = INTVAL (operands[2]);
- buf[3] = (comp == EQ) ? 's' : 'c';
- buf[6] = 'A' + (bit_nr >> 3);
- buf[9] = '0' + (bit_nr & 7);
- output_asm_insn (buf, operands);
- }
- }
+ {
+ if (comp == EQ)
+ output_asm_insn ("sbrs %1,%2", operands);
+ else
+ output_asm_insn ("sbrc %1,%2", operands);
+ }
+ else /* HImode, PSImode or SImode */
+ {
+ static char buf[] = "sbrc %A1,0";
+ unsigned int bit_nr = UINTVAL (operands[2]);
+
+ buf[3] = (comp == EQ) ? 's' : 'c';
+ buf[6] = 'A' + (bit_nr / 8);
+ buf[9] = '0' + (bit_nr % 8);
+ output_asm_insn (buf, operands);
+ }
+
+ break; /* REG */
+ } /* switch */
if (long_jump)
- return (AS1 (rjmp,.+4) CR_TAB
- AS1 (jmp,%x3));
+ return ("rjmp .+4" CR_TAB
+ "jmp %x3");
+
if (!reverse)
- return AS1 (rjmp,%x3);
+ return "rjmp %x3";
+
return "";
}
/* Implement `TARGET_ADDR_SPACE_ADDRESS_MODE'. */
static enum machine_mode
-avr_addr_space_address_mode (addr_space_t as ATTRIBUTE_UNUSED)
+avr_addr_space_address_mode (addr_space_t as)
{
- return HImode;
+ return as == ADDR_SPACE_PGMX ? PSImode : HImode;
}
/* Implement `TARGET_ADDR_SPACE_POINTER_MODE'. */
static enum machine_mode
-avr_addr_space_pointer_mode (addr_space_t as ATTRIBUTE_UNUSED)
+avr_addr_space_pointer_mode (addr_space_t as)
{
- return HImode;
+ return as == ADDR_SPACE_PGMX ? PSImode : HImode;
}
return avr_legitimate_address_p (mode, x, strict);
case ADDR_SPACE_PGM:
+ case ADDR_SPACE_PGM1:
+ case ADDR_SPACE_PGM2:
+ case ADDR_SPACE_PGM3:
+ case ADDR_SPACE_PGM4:
+ case ADDR_SPACE_PGM5:
switch (GET_CODE (x))
{
break;
case POST_INC:
- ok = (!avr_load_libgcc_p (x)
- && avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict));
+ ok = avr_reg_ok_for_pgm_addr (XEXP (x, 0), strict);
break;
default:
}
break; /* PGM */
+
+ case ADDR_SPACE_PGMX:
+ if (REG_P (x))
+ ok = (!strict
+ && can_create_pseudo_p());
+
+ if (LO_SUM == GET_CODE (x))
+ {
+ rtx hi = XEXP (x, 0);
+ rtx lo = XEXP (x, 1);
+
+ ok = (REG_P (hi)
+ && (!strict || REGNO (hi) < FIRST_PSEUDO_REGISTER)
+ && REG_P (lo)
+ && REGNO (lo) == REG_Z);
+ }
+
+ break; /* PGMX */
}
if (avr_log.legitimate_address_p)
static rtx
avr_addr_space_convert (rtx src, tree type_from, tree type_to)
{
+ addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (type_from));
+ addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type_to));
+
if (avr_log.progmem)
avr_edump ("\n%!: op = %r\nfrom = %t\nto = %t\n",
src, type_from, type_to);
+ if (as_from != ADDR_SPACE_PGMX
+ && as_to == ADDR_SPACE_PGMX)
+ {
+ rtx new_src;
+ int n_segments = avr_current_arch->n_segments;
+ RTX_CODE code = GET_CODE (src);
+
+ 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)))
+ {
+ 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;
+ }
+
+ if (SYMBOL_REF == code)
+ {
+ const char *name = XSTR (src, 0);
+
+ return gen_rtx_SYMBOL_REF (PSImode, ggc_strdup (name));
+ }
+
+ 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_pgm_segment (as_from) % n_segments;
+
+ new_src = gen_reg_rtx (PSImode);
+ emit_insn (gen_n_extendhipsi2 (new_src, GEN_INT (segment), src));
+
+ return new_src;
+ }
+ }
+
return src;
}
/* Implement `TARGET_ADDR_SPACE_SUBSET_P'. */
static bool
-avr_addr_space_subset_p (addr_space_t subset ATTRIBUTE_UNUSED,
- addr_space_t superset ATTRIBUTE_UNUSED)
+avr_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
{
+ if (subset == ADDR_SPACE_PGMX
+ && superset != ADDR_SPACE_PGMX)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+/* Worker function for movmemhi insn.
+ XOP[0] Destination as MEM:BLK
+ XOP[1] Source " "
+ XOP[2] # Bytes to copy
+
+ Return TRUE if the expansion is accomplished.
+ Return FALSE if the operand compination is not supported. */
+
+bool
+avr_emit_movmemhi (rtx *xop)
+{
+ HOST_WIDE_INT count;
+ enum machine_mode loop_mode;
+ addr_space_t as = MEM_ADDR_SPACE (xop[1]);
+ 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]))
+ return false;
+
+ if (!CONST_INT_P (xop[2]))
+ return false;
+
+ count = INTVAL (xop[2]);
+ if (count <= 0)
+ return false;
+
+ 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))
+ {
+ addr1 = simplify_gen_subreg (HImode, a_src, PSImode, 0);
+ a_hi8 = simplify_gen_subreg (QImode, a_src, PSImode, 2);
+ }
+ else
+ {
+ int seg = avr_pgm_segment (as);
+
+ addr1 = a_src;
+
+ if (seg > 0
+ && seg % avr_current_arch->n_segments > 0)
+ {
+ a_hi8 = GEN_INT (seg % avr_current_arch->n_segments);
+ }
+ }
+
+ 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;
+ }
+
+ 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. */
+
+ 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);
+ addr0 = reg_x;
+
+ /* FIXME: Register allocator does a bad job and might spill address
+ register(s) inside the loop leading to additional move instruction
+ to/from stack which could clobber tmp_reg. Thus, do *not* emit
+ load and store as seperate insns. Instead, we perform the copy
+ by means of one monolithic insn. */
+
+ if (ADDR_SPACE_GENERIC_P (as))
+ {
+ 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 (*fun) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx)
+ = QImode == loop_mode ? gen_movmem_qi_elpm : gen_movmem_hi_elpm;
+
+ 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));
+ }
+
+ set_mem_addr_space (SET_SRC (XVECEXP (insn, 0, 0)), as);
+ emit_insn (insn);
+
return true;
}
+/* Print assembler for movmem_qi, movmem_hi insns...
+ $0, $4 : & dest
+ $1, $5 : & src
+ $2 : Address Space
+ $3, $7 : Loop register
+ $6 : Scratch register
+
+ ...and movmem_qi_elpm, movmem_hi_elpm insns.
+
+ $8, $9 : hh8 (& src)
+ $10 : RAMPZ_ADDR
+*/
+
+const char*
+avr_out_movmem (rtx insn ATTRIBUTE_UNUSED, rtx *xop, int *plen)
+{
+ addr_space_t as = (addr_space_t) INTVAL (xop[2]);
+ enum machine_mode loop_mode = GET_MODE (xop[3]);
+
+ bool sbiw_p = test_hard_reg_class (ADDW_REGS, xop[3]);
+
+ gcc_assert (REG_X == REGNO (xop[0])
+ && REG_Z == REGNO (xop[1]));
+
+ if (plen)
+ *plen = 0;
+
+ /* Loop label */
+
+ avr_asm_len ("0:", xop, plen, 0);
+
+ /* Load with post-increment */
+
+ switch (as)
+ {
+ default:
+ gcc_unreachable();
+
+ case ADDR_SPACE_GENERIC:
+
+ avr_asm_len ("ld %6,%a1+", xop, plen, 1);
+ break;
+
+ case ADDR_SPACE_PGM:
+
+ if (AVR_HAVE_LPMX)
+ avr_asm_len ("lpm %6,%a1+", xop, plen, 1);
+ else
+ avr_asm_len ("lpm" CR_TAB
+ "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:
+
+ 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;
+ }
+
+ /* Store with post-increment */
+
+ avr_asm_len ("st %a0+,%6", xop, plen, 1);
+
+ /* Decrement loop-counter and set Z-flag */
+
+ if (QImode == loop_mode)
+ {
+ avr_asm_len ("dec %3", xop, plen, 1);
+ }
+ else if (sbiw_p)
+ {
+ avr_asm_len ("sbiw %3,1", xop, plen, 1);
+ }
+ else
+ {
+ avr_asm_len ("subi %A3,1" CR_TAB
+ "sbci %B3,0", xop, plen, 2);
+ }
+
+ /* Loop until zero */
+
+ return avr_asm_len ("brne 0b", xop, plen, 1);
+}
+
+
+\f
/* Helper for __builtin_avr_delay_cycles */
static void
(ZERO_REGNO 1) ; zero register r1
(SREG_ADDR 0x5F)
+ (SP_ADDR 0x5D)
+
+ ;; Register holding the address' high part when loading via ELPM
(RAMPZ_ADDR 0x5B)
])
(define_c_enum "unspec"
[UNSPEC_STRLEN
+ UNSPEC_MOVMEM
UNSPEC_INDEX_JMP
UNSPEC_FMUL
UNSPEC_FMULS
"out_bitop, out_plus, out_plus_noclobber, addto_sp,
tsthi, tstpsi, tstsi, compare, call,
mov8, mov16, mov24, mov32, reload_in16, reload_in24, reload_in32,
+ xload, movmem,
ashlqi, ashrqi, lshrqi,
ashlhi, ashrhi, lshrhi,
ashlsi, ashrsi, lshrsi,
;; Flavours of instruction set architecture (ISA), used in enabled attribute
-;; mov: ISA has no MOVW
-;; movw: ISA has MOVW
-;; rjmp: ISA has no CALL/JMP
-;; jmp: ISA has CALL/JMP
-;; ijmp: ISA has no EICALL/EIJMP
-;; eijmp: ISA has EICALL/EIJMP
+;; mov : ISA has no MOVW movw : ISA has MOVW
+;; rjmp : ISA has no CALL/JMP jmp : ISA has CALL/JMP
+;; ijmp : ISA has no EICALL/EIJMP eijmp : ISA has EICALL/EIJMP
+;; lpm : ISA has no LPMX lpmx : ISA has LPMX
+;; elpm : ISA has ELPM but no ELPMX elpmx : ISA has ELPMX
(define_attr "isa"
- "mov,movw, rjmp,jmp, ijmp,eijmp,
+ "mov,movw, rjmp,jmp, ijmp,eijmp, lpm,lpmx, elpm,elpmx,
standard"
(const_string "standard"))
(and (eq_attr "isa" "eijmp")
(match_test "AVR_HAVE_EIJMP_EICALL"))
(const_int 1)
+
+ (and (eq_attr "isa" "lpm")
+ (match_test "!AVR_HAVE_LPMX"))
+ (const_int 1)
+
+ (and (eq_attr "isa" "lpmx")
+ (match_test "AVR_HAVE_LPMX"))
+ (const_int 1)
+
+ (and (eq_attr "isa" "elpm")
+ (match_test "AVR_HAVE_ELPM && !AVR_HAVE_ELPMX"))
+ (const_int 1)
+
+ (and (eq_attr "isa" "elpmx")
+ (match_test "AVR_HAVE_ELPMX"))
+ (const_int 1)
] (const_int 0)))
;; even though its function is identical to that in builtins.c
(define_expand "nonlocal_goto"
- [
- (use (match_operand 0 "general_operand"))
- (use (match_operand 1 "general_operand"))
- (use (match_operand 2 "general_operand"))
- (use (match_operand 3 "general_operand"))
- ]
+ [(use (match_operand 0 "general_operand"))
+ (use (match_operand 1 "general_operand"))
+ (use (match_operand 2 "general_operand"))
+ (use (match_operand 3 "general_operand"))]
""
{
rtx r_label = copy_to_reg (operands[1]);
set_mem_addr_space (operands[1], ADDR_SPACE_PGM);
})
-(define_insn "*load.<mode>.libgcc"
+(define_insn "load_<mode>_libgcc"
[(set (reg:MOVMODE 22)
(match_operand:MOVMODE 0 "memory_operand" "m,m"))]
"avr_load_libgcc_p (operands[0])
(set_attr "cc" "clobber")])
+(define_insn_and_split "xload8_A"
+ [(set (match_operand:QI 0 "register_operand" "=r")
+ (match_operand:QI 1 "memory_operand" "m"))
+ (clobber (reg:HI REG_Z))]
+ "can_create_pseudo_p()
+ && avr_mem_pgmx_p (operands[1])
+ && REG_P (XEXP (operands[1], 0))"
+ { gcc_unreachable(); }
+ "&& 1"
+ [(clobber (const_int 0))]
+ {
+ rtx insn, addr = XEXP (operands[1], 0);
+ rtx hi8 = gen_reg_rtx (QImode);
+ rtx reg_z = gen_rtx_REG (HImode, REG_Z);
+
+ emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0));
+ emit_move_insn (hi8, simplify_gen_subreg (QImode, addr, PSImode, 2));
+
+ insn = emit_insn (gen_xload_8 (operands[0], hi8));
+ set_mem_addr_space (SET_SRC (single_set (insn)),
+ MEM_ADDR_SPACE (operands[1]));
+ DONE;
+ })
+
+(define_insn_and_split "xload<mode>_A"
+ [(set (match_operand:MOVMODE 0 "register_operand" "=r")
+ (match_operand:MOVMODE 1 "memory_operand" "m"))
+ (clobber (reg:QI 21))
+ (clobber (reg:HI REG_Z))]
+ "QImode != <MODE>mode
+ && can_create_pseudo_p()
+ && avr_mem_pgmx_p (operands[1])
+ && REG_P (XEXP (operands[1], 0))"
+ { gcc_unreachable(); }
+ "&& 1"
+ [(clobber (const_int 0))]
+ {
+ rtx addr = XEXP (operands[1], 0);
+ rtx reg_z = gen_rtx_REG (HImode, REG_Z);
+ rtx addr_hi8 = simplify_gen_subreg (QImode, addr, PSImode, 2);
+ addr_space_t as = MEM_ADDR_SPACE (operands[1]);
+ rtx hi8, insn;
+
+ emit_move_insn (reg_z, simplify_gen_subreg (HImode, addr, PSImode, 0));
+
+ if (avr_xload_libgcc_p (<MODE>mode))
+ {
+ emit_move_insn (gen_rtx_REG (QImode, 21), addr_hi8);
+ insn = emit_insn (gen_xload_<mode>_libgcc ());
+ emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, 22));
+ }
+ else if (avr_current_arch->n_segments == 1
+ && GET_MODE_SIZE (<MODE>mode) > 2
+ && !AVR_HAVE_LPMX)
+ {
+ rtx src = gen_rtx_MEM (<MODE>mode, reg_z);
+
+ as = ADDR_SPACE_PGM;
+ insn = emit_insn (gen_load_<mode>_libgcc (src));
+ emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, 22));
+ }
+ else
+ {
+ hi8 = gen_reg_rtx (QImode);
+ emit_move_insn (hi8, addr_hi8);
+ insn = emit_insn (gen_xload_<mode> (operands[0], hi8));
+ }
+
+ set_mem_addr_space (SET_SRC (single_set (insn)), as);
+
+ DONE;
+ })
+
+;; Move value from address space pgmx to a register
+;; These insns must be prior to respective generic move insn.
+
+(define_insn "xload_8"
+ [(set (match_operand:QI 0 "register_operand" "=r")
+ (mem:QI (lo_sum:PSI (match_operand:QI 1 "register_operand" "r")
+ (reg:HI REG_Z))))]
+ ""
+ {
+ return avr_out_xload (insn, operands, NULL);
+ }
+ [(set_attr "adjust_len" "xload")
+ (set_attr "cc" "clobber")])
+
+;; "xload_hi_libgcc"
+;; "xload_psi_libgcc"
+;; "xload_si_libgcc"
+;; "xload_sf_libgcc"
+(define_insn "xload_<mode>_libgcc"
+ [(set (reg:MOVMODE 22)
+ (mem:MOVMODE (lo_sum:PSI (reg:QI 21)
+ (reg:HI REG_Z))))
+ (clobber (reg:QI 21))
+ (clobber (reg:HI REG_Z))]
+ "<MODE>mode != QImode
+ && avr_xload_libgcc_p (<MODE>mode)"
+ {
+ rtx x_bytes = GEN_INT (GET_MODE_SIZE (<MODE>mode));
+
+ /* Devices with ELPM* also have CALL. */
+
+ output_asm_insn ("call __xload_%0", &x_bytes);
+ return "";
+ }
+ [(set_attr "length" "2")
+ (set_attr "cc" "clobber")])
+
+;; "xload_hi"
+;; "xload_psi"
+;; "xload_si"
+;; "xload_sf"
+(define_insn "xload_<mode>"
+ [(set (match_operand:MOVMODE 0 "register_operand" "=r")
+ (mem:MOVMODE (lo_sum:PSI (match_operand:QI 1 "register_operand" "r")
+ (reg:HI REG_Z))))
+ (clobber (scratch:HI))
+ (clobber (reg:HI REG_Z))]
+ "<MODE>mode != QImode
+ && !avr_xload_libgcc_p (<MODE>mode)"
+ {
+ return avr_out_xload (insn, operands, NULL);
+ }
+ [(set_attr "adjust_len" "xload")
+ (set_attr "cc" "clobber")])
+
+
;; General move expanders
;; "movqi"
operands[1] = src = copy_to_mode_reg (<MODE>mode, src);
}
+ if (avr_mem_pgmx_p (src))
+ {
+ rtx addr = XEXP (src, 0);
+
+ if (!REG_P (addr))
+ src = replace_equiv_address (src, copy_to_mode_reg (PSImode, addr));
+
+ if (QImode == <MODE>mode)
+ emit_insn (gen_xload8_A (dest, src));
+ else
+ emit_insn (gen_xload<mode>_A (dest, src));
+
+ DONE;
+ }
+
if (avr_load_libgcc_p (src))
{
/* For the small devices, do loads per libgcc call. */
;;=========================================================================
;; move string (like memcpy)
-;; implement as RTL loop
(define_expand "movmemhi"
[(parallel [(set (match_operand:BLK 0 "memory_operand" "")
- (match_operand:BLK 1 "memory_operand" ""))
- (use (match_operand:HI 2 "const_int_operand" ""))
- (use (match_operand:HI 3 "const_int_operand" ""))])]
+ (match_operand:BLK 1 "memory_operand" ""))
+ (use (match_operand:HI 2 "const_int_operand" ""))
+ (use (match_operand:HI 3 "const_int_operand" ""))])]
""
- "{
- int prob;
- HOST_WIDE_INT count;
- enum machine_mode mode;
- rtx label = gen_label_rtx ();
- rtx loop_reg;
- rtx jump, src;
-
- /* Copy pointers into new psuedos - they will be changed. */
- rtx addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
- rtx addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
-
- /* Create rtx for tmp register - we use this as scratch. */
- rtx tmp_reg_rtx = gen_rtx_REG (QImode, TMP_REGNO);
-
- if (avr_mem_pgm_p (operands[0]))
- DONE;
-
- if (GET_CODE (operands[2]) != CONST_INT)
+ {
+ if (avr_emit_movmemhi (operands))
+ DONE;
+
FAIL;
+ })
- count = INTVAL (operands[2]);
- if (count <= 0)
- FAIL;
+(define_mode_attr MOVMEM_r_d [(QI "r")
+ (HI "d")])
+
+;; $0, $4 : & dest
+;; $1, $5 : & src
+;; $2 : Address Space
+;; $3, $7 : Loop register
+;; $6 : Scratch register
+
+;; "movmem_qi"
+;; "movmem_hi"
+(define_insn "movmem_<mode>"
+ [(set (mem:BLK (match_operand:HI 0 "register_operand" "x"))
+ (mem:BLK (match_operand:HI 1 "register_operand" "z")))
+ (unspec [(match_operand:QI 2 "const_int_operand" "LP")]
+ UNSPEC_MOVMEM)
+ (use (match_operand:QIHI 3 "register_operand" "<MOVMEM_r_d>"))
+ (clobber (match_operand:HI 4 "register_operand" "=0"))
+ (clobber (match_operand:HI 5 "register_operand" "=1"))
+ (clobber (match_operand:QI 6 "register_operand" "=&r"))
+ (clobber (match_operand:QIHI 7 "register_operand" "=3"))]
+ ""
+ {
+ return avr_out_movmem (insn, operands, NULL);
+ }
+ [(set_attr "adjust_len" "movmem")
+ (set_attr "cc" "clobber")])
+
+;; Ditto and
+;; $8, $9 : hh8 (& src)
+;; $10 : RAMPZ_ADDR
+
+;; "movmem_qi_elpm"
+;; "movmem_hi_elpm"
+(define_insn "movmem_<mode>_elpm"
+ [(set (mem:BLK (match_operand:HI 0 "register_operand" "x"))
+ (mem:BLK (lo_sum:PSI (match_operand:QI 8 "register_operand" "r")
+ (match_operand:HI 1 "register_operand" "z"))))
+ (unspec [(match_operand:QI 2 "const_int_operand" "n")]
+ UNSPEC_MOVMEM)
+ (use (match_operand:QIHI 3 "register_operand" "<MOVMEM_r_d>"))
+ (clobber (match_operand:HI 4 "register_operand" "=0"))
+ (clobber (match_operand:HI 5 "register_operand" "=1"))
+ (clobber (match_operand:QI 6 "register_operand" "=&r"))
+ (clobber (match_operand:QIHI 7 "register_operand" "=3"))
+ (clobber (match_operand:QI 9 "register_operand" "=8"))
+ (clobber (mem:QI (match_operand:QI 10 "io_address_operand" "n")))]
+ ""
+ {
+ return avr_out_movmem (insn, operands, NULL);
+ }
+ [(set_attr "adjust_len" "movmem")
+ (set_attr "cc" "clobber")])
- /* Work out branch probability for latter use. */
- prob = REG_BR_PROB_BASE - REG_BR_PROB_BASE / count;
-
- /* See if constant fit 8 bits. */
- mode = (count < 0x100) ? QImode : HImode;
- /* Create loop counter register. */
- loop_reg = copy_to_mode_reg (mode, gen_int_mode (count, mode));
-
- /* Now create RTL code for move loop. */
- /* Label at top of loop. */
- emit_label (label);
-
- /* Move one byte into scratch and inc pointer. */
- src = gen_rtx_MEM (QImode, addr1);
- set_mem_addr_space (src, MEM_ADDR_SPACE (operands[1]));
- emit_move_insn (tmp_reg_rtx, src);
- emit_move_insn (addr1, gen_rtx_PLUS (Pmode, addr1, const1_rtx));
-
- /* Move to mem and inc pointer. */
- emit_move_insn (gen_rtx_MEM (QImode, addr0), tmp_reg_rtx);
- emit_move_insn (addr0, gen_rtx_PLUS (Pmode, addr0, const1_rtx));
-
- /* Decrement count. */
- emit_move_insn (loop_reg, gen_rtx_PLUS (mode, loop_reg, constm1_rtx));
-
- /* Compare with zero and jump if not equal. */
- emit_cmp_and_jump_insns (loop_reg, const0_rtx, NE, NULL_RTX, mode, 1,
- label);
- /* Set jump probability based on loop count. */
- jump = get_last_insn ();
- add_reg_note (jump, REG_BR_PROB, GEN_INT (prob));
- DONE;
-}")
-;; =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2
+;; =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2 =%2
;; memset (%0, %2, %1)
(define_expand "setmemhi"
[(parallel [(set (match_operand:BLK 0 "memory_operand" "")
- (match_operand 2 "const_int_operand" ""))
- (use (match_operand:HI 1 "const_int_operand" ""))
- (use (match_operand:HI 3 "const_int_operand" "n"))
- (clobber (match_scratch:HI 4 ""))
- (clobber (match_dup 5))])]
+ (match_operand 2 "const_int_operand" ""))
+ (use (match_operand:HI 1 "const_int_operand" ""))
+ (use (match_operand:HI 3 "const_int_operand" ""))
+ (clobber (match_scratch:HI 4 ""))
+ (clobber (match_dup 5))])]
""
- "{
- rtx addr0;
- enum machine_mode mode;
+ {
+ rtx addr0;
+ enum machine_mode mode;
- /* If value to set is not zero, use the library routine. */
- if (operands[2] != const0_rtx)
- FAIL;
+ /* If value to set is not zero, use the library routine. */
+ if (operands[2] != const0_rtx)
+ FAIL;
- if (!CONST_INT_P (operands[1]))
- FAIL;
+ if (!CONST_INT_P (operands[1]))
+ FAIL;
+
+ mode = u8_operand (operands[1], VOIDmode) ? QImode : HImode;
+ operands[5] = gen_rtx_SCRATCH (mode);
+ operands[1] = copy_to_mode_reg (mode,
+ gen_int_mode (INTVAL (operands[1]), mode));
+ addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
+ operands[0] = gen_rtx_MEM (BLKmode, addr0);
+ })
- mode = u8_operand (operands[1], VOIDmode) ? QImode : HImode;
- operands[5] = gen_rtx_SCRATCH (mode);
- operands[1] = copy_to_mode_reg (mode,
- gen_int_mode (INTVAL (operands[1]), mode));
- addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
- operands[0] = gen_rtx_MEM (BLKmode, addr0);
-}")
(define_insn "*clrmemqi"
[(set (mem:BLK (match_operand:HI 0 "register_operand" "e"))
- (const_int 0))
+ (const_int 0))
(use (match_operand:QI 1 "register_operand" "r"))
(use (match_operand:QI 2 "const_int_operand" "n"))
(clobber (match_scratch:HI 3 "=0"))
(clobber (match_scratch:QI 4 "=&1"))]
""
- "st %a0+,__zero_reg__
- dec %1
- brne .-6"
+ "0:\;st %a0+,__zero_reg__\;dec %1\;brne 0b"
[(set_attr "length" "3")
(set_attr "cc" "clobber")])
+
(define_insn "*clrmemhi"
[(set (mem:BLK (match_operand:HI 0 "register_operand" "e,e"))
- (const_int 0))
+ (const_int 0))
(use (match_operand:HI 1 "register_operand" "!w,d"))
(use (match_operand:HI 2 "const_int_operand" "n,n"))
(clobber (match_scratch:HI 3 "=0,0"))
(clobber (match_scratch:HI 4 "=&1,&1"))]
""
- "*{
- if (which_alternative==0)
- return (AS2 (st,%a0+,__zero_reg__) CR_TAB
- AS2 (sbiw,%A1,1) CR_TAB
- AS1 (brne,.-6));
- else
- return (AS2 (st,%a0+,__zero_reg__) CR_TAB
- AS2 (subi,%A1,1) CR_TAB
- AS2 (sbci,%B1,0) CR_TAB
- AS1 (brne,.-8));
-}"
+ "@
+ 0:\;st %a0+,__zero_reg__\;sbiw %A1,1\;brne 0b
+ 0:\;st %a0+,__zero_reg__\;subi %A1,1\;sbci %B1,0\;brne 0b"
[(set_attr "length" "3,4")
(set_attr "cc" "clobber,clobber")])
(define_expand "strlenhi"
- [(set (match_dup 4)
- (unspec:HI [(match_operand:BLK 1 "memory_operand" "")
- (match_operand:QI 2 "const_int_operand" "")
- (match_operand:HI 3 "immediate_operand" "")]
- UNSPEC_STRLEN))
- (set (match_dup 4) (plus:HI (match_dup 4)
- (const_int -1)))
- (set (match_operand:HI 0 "register_operand" "")
- (minus:HI (match_dup 4)
- (match_dup 5)))]
- ""
- "{
- rtx addr;
- if (operands[2] != const0_rtx)
- FAIL;
- addr = copy_to_mode_reg (Pmode, XEXP (operands[1],0));
- operands[1] = gen_rtx_MEM (BLKmode, addr);
- operands[5] = addr;
- operands[4] = gen_reg_rtx (HImode);
-}")
+ [(set (match_dup 4)
+ (unspec:HI [(match_operand:BLK 1 "memory_operand" "")
+ (match_operand:QI 2 "const_int_operand" "")
+ (match_operand:HI 3 "immediate_operand" "")]
+ UNSPEC_STRLEN))
+ (set (match_dup 4)
+ (plus:HI (match_dup 4)
+ (const_int -1)))
+ (set (match_operand:HI 0 "register_operand" "")
+ (minus:HI (match_dup 4)
+ (match_dup 5)))]
+ ""
+ {
+ rtx addr;
+ if (operands[2] != const0_rtx)
+ FAIL;
+ addr = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
+ operands[1] = gen_rtx_MEM (BLKmode, addr);
+ operands[5] = addr;
+ operands[4] = gen_reg_rtx (HImode);
+ })
(define_insn "*strlenhi"
- [(set (match_operand:HI 0 "register_operand" "=e")
- (unspec:HI [(mem:BLK (match_operand:HI 1 "register_operand" "%0"))
- (const_int 0)
- (match_operand:HI 2 "immediate_operand" "i")]
- UNSPEC_STRLEN))]
- ""
- "ld __tmp_reg__,%a0+
- tst __tmp_reg__
- brne .-6"
+ [(set (match_operand:HI 0 "register_operand" "=e")
+ (unspec:HI [(mem:BLK (match_operand:HI 1 "register_operand" "0"))
+ (const_int 0)
+ (match_operand:HI 2 "immediate_operand" "i")]
+ UNSPEC_STRLEN))]
+ ""
+ "0:\;ld __tmp_reg__,%a0+\;tst __tmp_reg__\;brne 0b"
[(set_attr "length" "3")
(set_attr "cc" "clobber")])