make sure that the modified instructions are loaded into the instruction
fetch buffer. */
-#define TRAMPOLINE_SIZE 49
+#define TRAMPOLINE_SIZE 59
.text
.align 4
/* Xtensa configuration settings.
- Copyright (C) 2001,2002 Free Software Foundation, Inc.
+ Copyright (C) 2001,2002,2003 Free Software Foundation, Inc.
Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
** NOTE: This file was automatically generated by the Xtensa Processor
#define XCHAL_HAVE_BE 1
#define XCHAL_HAVE_DENSITY 1
+#define XCHAL_HAVE_CONST16 0
+#define XCHAL_HAVE_L32R 1
#define XCHAL_HAVE_MAC16 0
#define XCHAL_HAVE_MUL16 0
#define XCHAL_HAVE_MUL32 0
extern int smalloffset_double_mem_p PARAMS ((rtx));
extern int constantpool_address_p PARAMS ((rtx));
extern int constantpool_mem_p PARAMS ((rtx));
-extern int non_const_move_operand PARAMS ((rtx, enum machine_mode));
extern int const_float_1_operand PARAMS ((rtx, enum machine_mode));
extern int fpmem_offset_operand PARAMS ((rtx, enum machine_mode));
extern void xtensa_extend_reg PARAMS ((rtx, rtx));
-extern void xtensa_load_constant PARAMS ((rtx, rtx));
extern int branch_operator PARAMS ((rtx, enum machine_mode));
extern int ubranch_operator PARAMS ((rtx, enum machine_mode));
extern int boolean_operator PARAMS ((rtx, enum machine_mode));
extern void override_options PARAMS ((void));
extern long compute_frame_size PARAMS ((int));
extern int xtensa_frame_pointer_required PARAMS ((void));
-extern void xtensa_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
-extern void xtensa_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
+extern void xtensa_expand_prologue PARAMS ((void));
extern void order_regs_for_local_alloc PARAMS ((void));
#endif /* !__XTENSA_PROTOS_H__ */
static rtx fixup_subreg_mem PARAMS ((rtx x));
static enum machine_mode xtensa_find_mode_for_size PARAMS ((unsigned));
static struct machine_function * xtensa_init_machine_status PARAMS ((void));
-static void xtensa_reorg PARAMS ((void));
static void printx PARAMS ((FILE *, signed int));
+static void xtensa_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
static unsigned int xtensa_multibss_section_type_flags
PARAMS ((tree, const char *, int));
static void xtensa_select_rtx_section
PARAMS ((enum machine_mode, rtx, unsigned HOST_WIDE_INT));
static bool xtensa_rtx_costs PARAMS ((rtx, int, int, int *));
-static rtx frame_size_const;
static int current_function_arg_words;
static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
REG_ALLOC_ORDER;
\f
-/* This macro generates the assembly code for function entry.
- FILE is a stdio stream to output the code to.
- SIZE is an int: how many units of temporary storage to allocate.
- Refer to the array 'regs_ever_live' to determine which registers
- to save; 'regs_ever_live[I]' is nonzero if register number I
- is ever used in the function. This macro is responsible for
- knowing which registers should not be saved even if used. */
-
-#undef TARGET_ASM_FUNCTION_PROLOGUE
-#define TARGET_ASM_FUNCTION_PROLOGUE xtensa_function_prologue
/* This macro generates the assembly code for function exit,
on machines that need it. If FUNCTION_EPILOGUE is not defined
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST hook_int_rtx_0
-#undef TARGET_MACHINE_DEPENDENT_REORG
-#define TARGET_MACHINE_DEPENDENT_REORG xtensa_reorg
-
struct gcc_target targetm = TARGET_INITIALIZER;
\f
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT)
- return (xtensa_simm8 (INTVAL (op)) ||
- xtensa_simm8x256 (INTVAL (op)));
+ return (xtensa_simm8 (INTVAL (op)) || xtensa_simm8x256 (INTVAL (op)));
return register_operand (op, mode);
}
rtx op;
enum machine_mode mode;
{
- if (register_operand (op, mode))
+ if (register_operand (op, mode)
+ || memory_operand (op, mode))
return TRUE;
+ if (mode == SFmode)
+ return TARGET_CONST16 && CONSTANT_P (op);
+
/* Accept CONSTANT_P_RTX, since it will be gone by CSE1 and
result in 0/1. */
if (GET_CODE (op) == CONSTANT_P_RTX)
return TRUE;
- if (GET_CODE (op) == CONST_INT)
- return xtensa_simm12b (INTVAL (op));
+ if (GET_CODE (op) == CONST_INT && xtensa_simm12b (INTVAL (op)))
+ return TRUE;
- if (GET_CODE (op) == MEM)
- return memory_address_p (mode, XEXP (op, 0));
+ if (mode == SImode)
+ return TARGET_CONST16 && CONSTANT_P (op);
return FALSE;
}
}
-int
-non_const_move_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (register_operand (op, mode))
- return 1;
- if (GET_CODE (op) == SUBREG)
- op = SUBREG_REG (op);
- if (GET_CODE (op) == MEM)
- return memory_address_p (mode, XEXP (op, 0));
- return FALSE;
-}
-
-
/* Accept the floating point constant 1 in the appropriate mode. */
int
}
-void
-xtensa_load_constant (dst, src)
- rtx dst;
- rtx src;
-{
- enum machine_mode mode = GET_MODE (dst);
- src = force_const_mem (SImode, src);
-
- /* PC-relative loads are always SImode so we have to add a SUBREG if that
- is not the desired mode */
-
- if (mode != SImode)
- {
- if (register_operand (dst, mode))
- dst = simplify_gen_subreg (SImode, dst, mode, 0);
- else
- {
- src = force_reg (SImode, src);
- src = gen_lowpart_SUBREG (mode, src);
- }
- }
-
- emit_move_insn (dst, src);
-}
-
-
int
branch_operator (x, mode)
rtx x;
moved in < "move_ratio" pieces. The worst case is when the block is
aligned but has a size of (3 mod 4) (does this happen?) so that the
last piece requires a byte load/store. */
- return (xtensa_uimm8 (v) &&
- xtensa_uimm8 (v + MOVE_MAX * LARGEST_MOVE_RATIO));
+ return (xtensa_uimm8 (v)
+ && xtensa_uimm8 (v + MOVE_MAX * LARGEST_MOVE_RATIO));
case QImode:
return xtensa_uimm8 (v);
/* Emit insns to move operands[1] into operands[0].
-
Return 1 if we have written out everything that needs to be done to
do the move. Otherwise, return 0 and the caller will emit the move
normally. */
&& (GET_CODE (operands[1]) != CONST_INT
|| !xtensa_simm12b (INTVAL (operands[1]))))
{
- xtensa_load_constant (operands[0], operands[1]);
- return 1;
+ if (!TARGET_CONST16)
+ operands[1] = force_const_mem (SImode, operands[1]);
+
+ /* PC-relative loads are always SImode, and CONST16 is only
+ supported in the movsi pattern, so add a SUBREG for any other
+ (smaller) mode. */
+
+ if (mode != SImode)
+ {
+ if (register_operand (operands[0], mode))
+ {
+ operands[0] = simplify_gen_subreg (SImode, operands[0], mode, 0);
+ emit_move_insn (operands[0], operands[1]);
+ return 1;
+ }
+ else
+ {
+ operands[1] = force_reg (SImode, operands[1]);
+ operands[1] = gen_lowpart_SUBREG (mode, operands[1]);
+ }
+ }
}
if (!(reload_in_progress | reload_completed))
return 0;
}
+
static rtx
fixup_subreg_mem (x)
rtx x;
xtensa_char_to_class['C'] = ((TARGET_MUL16) ? GR_REGS: NO_REGS);
xtensa_char_to_class['D'] = ((TARGET_DENSITY) ? GR_REGS: NO_REGS);
xtensa_char_to_class['d'] = ((TARGET_DENSITY) ? AR_REGS: NO_REGS);
+ xtensa_char_to_class['W'] = ((TARGET_CONST16) ? GR_REGS: NO_REGS);
/* Set up array giving whether a given register can hold a given mode. */
for (mode = VOIDmode;
int temp;
if (ACC_REG_P (regno))
- temp = (TARGET_MAC16 &&
- (class == MODE_INT) && (size <= UNITS_PER_WORD));
+ temp = (TARGET_MAC16
+ && (class == MODE_INT) && (size <= UNITS_PER_WORD));
else if (GP_REG_P (regno))
temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
else if (FP_REG_P (regno))
init_machine_status = xtensa_init_machine_status;
- /* Check PIC settings. There's no need for -fPIC on Xtensa and
- some targets need to always use PIC. */
- if (flag_pic > 1 || (XTENSA_ALWAYS_PIC))
+ /* Check PIC settings. PIC is only supported when using L32R
+ instructions, and some targets need to always use PIC. */
+ if (flag_pic && TARGET_CONST16)
+ error ("-f%s is not supported with CONST16 instructions",
+ (flag_pic > 1 ? "PIC" : "pic"));
+ else if (XTENSA_ALWAYS_PIC)
+ {
+ if (TARGET_CONST16)
+ error ("PIC is required but not supported with CONST16 instructions");
+ flag_pic = 1;
+ }
+ /* There's no need for -fPIC (as opposed to -fpic) on Xtensa. */
+ if (flag_pic > 1)
flag_pic = 1;
}
'D' REG, print second register of double-word register operand
'N' MEM, print address of next word following a memory operand
'v' MEM, if memory reference is volatile, output a MEMW before it
+ 't' any constant, add "@h" suffix for top 16 bits
+ 'b' any constant, add "@l" suffix for bottom 16 bits
*/
static void
void
-print_operand (file, op, letter)
+print_operand (file, x, letter)
FILE *file; /* file to write to */
- rtx op; /* operand to print */
+ rtx x; /* operand to print */
int letter; /* %<letter> or 0 */
{
- enum rtx_code code;
-
- if (! op)
+ if (!x)
error ("PRINT_OPERAND null pointer");
- code = GET_CODE (op);
- switch (code)
+ switch (letter)
{
- case REG:
- case SUBREG:
- {
- int regnum = xt_true_regnum (op);
- if (letter == 'D')
- regnum++;
- fprintf (file, "%s", reg_names[regnum]);
- break;
- }
+ case 'D':
+ if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
+ fprintf (file, "%s", reg_names[xt_true_regnum (x) + 1]);
+ else
+ output_operand_lossage ("invalid %%D value");
+ break;
- case MEM:
- /* For a volatile memory reference, emit a MEMW before the
- load or store. */
- if (letter == 'v')
- {
- if (MEM_VOLATILE_P (op) && TARGET_SERIALIZE_VOLATILE)
- fprintf (file, "memw\n\t");
- break;
- }
- else if (letter == 'N')
- {
- enum machine_mode mode;
- switch (GET_MODE (op))
- {
- case DFmode: mode = SFmode; break;
- case DImode: mode = SImode; break;
- default: abort ();
- }
- op = adjust_address (op, mode, 4);
- }
+ case 'v':
+ if (GET_CODE (x) == MEM)
+ {
+ /* For a volatile memory reference, emit a MEMW before the
+ load or store. */
+ if (MEM_VOLATILE_P (x) && TARGET_SERIALIZE_VOLATILE)
+ fprintf (file, "memw\n\t");
+ }
+ else
+ output_operand_lossage ("invalid %%v value");
+ break;
- output_address (XEXP (op, 0));
- break;
+ case 'N':
+ if (GET_CODE (x) == MEM
+ && (GET_MODE (x) == DFmode || GET_MODE (x) == DImode))
+ {
+ x = adjust_address (x, GET_MODE (x) == DFmode ? SFmode : SImode, 4);
+ output_address (XEXP (x, 0));
+ }
+ else
+ output_operand_lossage ("invalid %%N value");
+ break;
- case CONST_INT:
- switch (letter)
+ case 'K':
+ if (GET_CODE (x) == CONST_INT)
{
- case 'K':
- {
- int num_bits = 0;
- unsigned val = INTVAL (op);
- while (val & 1)
- {
- num_bits += 1;
- val = val >> 1;
- }
- if ((val != 0) || (num_bits == 0) || (num_bits > 16))
- fatal_insn ("invalid mask", op);
+ int num_bits = 0;
+ unsigned val = INTVAL (x);
+ while (val & 1)
+ {
+ num_bits += 1;
+ val = val >> 1;
+ }
+ if ((val != 0) || (num_bits == 0) || (num_bits > 16))
+ fatal_insn ("invalid mask", x);
- fprintf (file, "%d", num_bits);
- break;
- }
+ fprintf (file, "%d", num_bits);
+ }
+ else
+ output_operand_lossage ("invalid %%K value");
+ break;
- case 'L':
- fprintf (file, "%ld", (32 - INTVAL (op)) & 0x1f);
- break;
+ case 'L':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "%ld", (32 - INTVAL (x)) & 0x1f);
+ else
+ output_operand_lossage ("invalid %%L value");
+ break;
- case 'R':
- fprintf (file, "%ld", INTVAL (op) & 0x1f);
- break;
+ case 'R':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "%ld", INTVAL (x) & 0x1f);
+ else
+ output_operand_lossage ("invalid %%R value");
+ break;
- case 'x':
- printx (file, INTVAL (op));
- break;
+ case 'x':
+ if (GET_CODE (x) == CONST_INT)
+ printx (file, INTVAL (x));
+ else
+ output_operand_lossage ("invalid %%x value");
+ break;
- case 'd':
- default:
- fprintf (file, "%ld", INTVAL (op));
- break;
+ case 'd':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "%ld", INTVAL (x));
+ else
+ output_operand_lossage ("invalid %%d value");
+ break;
+ case 't':
+ case 'b':
+ if (GET_CODE (x) == CONST_INT)
+ {
+ printx (file, INTVAL (x));
+ fputs (letter == 't' ? "@h" : "@l", file);
+ }
+ else if (GET_CODE (x) == CONST_DOUBLE)
+ {
+ REAL_VALUE_TYPE r;
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ if (GET_MODE (x) == SFmode)
+ {
+ long l;
+ REAL_VALUE_TO_TARGET_SINGLE (r, l);
+ fprintf (file, "0x%08lx@%c", l, letter == 't' ? 'h' : 'l');
+ }
+ else
+ output_operand_lossage ("invalid %%t/%%b value");
+ }
+ else if (GET_CODE (x) == CONST)
+ {
+ /* X must be a symbolic constant on ELF. Write an expression
+ suitable for 'const16' that sets the high or low 16 bits. */
+ if (GET_CODE (XEXP (x, 0)) != PLUS
+ || (GET_CODE (XEXP (XEXP (x, 0), 0)) != SYMBOL_REF
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF)
+ || GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT)
+ output_operand_lossage ("invalid %%t/%%b value");
+ print_operand (file, XEXP (XEXP (x, 0), 0), 0);
+ fputs (letter == 't' ? "@h" : "@l", file);
+ /* There must be a non-alphanumeric character between 'h' or 'l'
+ and the number. The '-' is added by print_operand() already. */
+ if (INTVAL (XEXP (XEXP (x, 0), 1)) >= 0)
+ fputs ("+", file);
+ print_operand (file, XEXP (XEXP (x, 0), 1), 0);
+ }
+ else
+ {
+ output_addr_const (file, x);
+ fputs (letter == 't' ? "@h" : "@l", file);
}
break;
default:
- output_addr_const (file, op);
+ if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
+ fprintf (file, "%s", reg_names[xt_true_regnum (x)]);
+ else if (GET_CODE (x) == MEM)
+ output_address (XEXP (x, 0));
+ else if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "%ld", INTVAL (x));
+ else
+ output_addr_const (file, x);
}
}
}
-/* If the stack frame size is too big to fit in the immediate field of
- the ENTRY instruction, we need to store the frame size in the
- constant pool. However, the code in xtensa_function_prologue runs too
- late to be able to add anything to the constant pool. Since the
- final frame size isn't known until reload is complete, this seems
- like the best place to do it.
+void
+xtensa_expand_prologue ()
+{
+ HOST_WIDE_INT total_size;
+ rtx size_rtx;
- There may also be some fixup required if there is an incoming argument
- in a7 and the function requires a frame pointer. */
+ total_size = compute_frame_size (get_frame_size ());
+ size_rtx = GEN_INT (total_size);
-static void
-xtensa_reorg ()
-{
- rtx first, insn, set_frame_ptr_insn = 0;
-
- unsigned long tsize = compute_frame_size (get_frame_size ());
- first = get_insns ();
- if (tsize < (1 << (12+3)))
- frame_size_const = 0;
+ if (total_size < (1 << (12+3)))
+ emit_insn (gen_entry (size_rtx, size_rtx));
else
{
- frame_size_const = force_const_mem (SImode, GEN_INT (tsize - 16));;
-
- /* make sure the constant is used so it doesn't get eliminated
- from the constant pool */
- emit_insn_before (gen_rtx_USE (SImode, frame_size_const), first);
+ /* Use a8 as a temporary since a0-a7 may be live. */
+ rtx tmp_reg = gen_rtx_REG (Pmode, A8_REG);
+ emit_insn (gen_entry (size_rtx, GEN_INT (MIN_FRAME_SIZE)));
+ emit_move_insn (tmp_reg, GEN_INT (total_size - MIN_FRAME_SIZE));
+ emit_insn (gen_subsi3 (tmp_reg, stack_pointer_rtx, tmp_reg));
+ emit_move_insn (stack_pointer_rtx, tmp_reg);
}
- if (!frame_pointer_needed)
- return;
-
- /* Search all instructions, looking for the insn that sets up the
- frame pointer. This search will fail if the function does not
- have an incoming argument in $a7, but in that case, we can just
- set up the frame pointer at the very beginning of the
- function. */
-
- for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (frame_pointer_needed)
{
- rtx pat;
+ rtx first, insn, set_frame_ptr_insn = 0;
- if (!INSN_P (insn))
- continue;
+ push_topmost_sequence ();
+ first = get_insns ();
+ pop_topmost_sequence ();
- pat = PATTERN (insn);
- if (GET_CODE (pat) == SET
- && GET_CODE (SET_SRC (pat)) == UNSPEC_VOLATILE
- && (XINT (SET_SRC (pat), 1) == UNSPECV_SET_FP))
- {
- set_frame_ptr_insn = insn;
- break;
- }
- }
+ /* Search all instructions, looking for the insn that sets up the
+ frame pointer. This search will fail if the function does not
+ have an incoming argument in $a7, but in that case, we can just
+ set up the frame pointer at the very beginning of the
+ function. */
- if (set_frame_ptr_insn)
- {
- /* for all instructions prior to set_frame_ptr_insn, replace
- hard_frame_pointer references with stack_pointer */
- for (insn = first; insn != set_frame_ptr_insn; insn = NEXT_INSN (insn))
+ for (insn = first; insn; insn = NEXT_INSN (insn))
{
- if (INSN_P (insn))
- PATTERN (insn) = replace_rtx (copy_rtx (PATTERN (insn)),
- hard_frame_pointer_rtx,
- stack_pointer_rtx);
- }
- }
- else
- {
- /* emit the frame pointer move immediately after the NOTE that starts
- the function */
- emit_insn_after (gen_movsi (hard_frame_pointer_rtx,
- stack_pointer_rtx), first);
- }
-}
-
+ rtx pat;
-/* Set up the stack and frame (if desired) for the function. */
+ if (!INSN_P (insn))
+ continue;
-void
-xtensa_function_prologue (file, size)
- FILE *file;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
-{
- unsigned long tsize = compute_frame_size (get_frame_size ());
-
- if (frame_pointer_needed)
- fprintf (file, "\t.frame\ta7, %ld\n", tsize);
- else
- fprintf (file, "\t.frame\tsp, %ld\n", tsize);
-
-
- if (tsize < (1 << (12+3)))
- {
- fprintf (file, "\tentry\tsp, %ld\n", tsize);
- }
- else
- {
- fprintf (file, "\tentry\tsp, 16\n");
+ pat = PATTERN (insn);
+ if (GET_CODE (pat) == SET
+ && GET_CODE (SET_SRC (pat)) == UNSPEC_VOLATILE
+ && (XINT (SET_SRC (pat), 1) == UNSPECV_SET_FP))
+ {
+ set_frame_ptr_insn = insn;
+ break;
+ }
+ }
- /* use a8 as a temporary since a0-a7 may be live */
- fprintf (file, "\tl32r\ta8, ");
- print_operand (file, frame_size_const, 0);
- fprintf (file, "\n\tsub\ta8, sp, a8\n");
- fprintf (file, "\tmovsp\tsp, a8\n");
+ if (set_frame_ptr_insn)
+ {
+ /* For all instructions prior to set_frame_ptr_insn, replace
+ hard_frame_pointer references with stack_pointer. */
+ for (insn = first;
+ insn != set_frame_ptr_insn;
+ insn = NEXT_INSN (insn))
+ {
+ if (INSN_P (insn))
+ PATTERN (insn) = replace_rtx (copy_rtx (PATTERN (insn)),
+ hard_frame_pointer_rtx,
+ stack_pointer_rtx);
+ }
+ }
+ else
+ emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
}
}
-/* Do any necessary cleanup after a function to restore
- stack, frame, and regs. */
+/* Clear variables at function end. */
void
xtensa_function_epilogue (file, size)
- FILE *file;
+ FILE *file ATTRIBUTE_UNUSED;
HOST_WIDE_INT size ATTRIBUTE_UNUSED;
{
- rtx insn = get_last_insn ();
- /* If the last insn was a BARRIER, we don't have to write anything. */
- if (GET_CODE (insn) == NOTE)
- insn = prev_nonnote_insn (insn);
- if (insn == 0 || GET_CODE (insn) != BARRIER)
- fprintf (file, TARGET_DENSITY ? "\tretw.n\n" : "\tretw\n");
-
xtensa_current_frame_size = 0;
}
rtx result, retaddr;
if (count == -1)
- retaddr = gen_rtx_REG (Pmode, 0);
+ retaddr = gen_rtx_REG (Pmode, A0_REG);
else
{
rtx addr = plus_constant (frame, -4 * UNITS_PER_WORD);
}
if (xtensa_simm12b (INTVAL (x)))
*total = 5;
+ else if (TARGET_CONST16)
+ *total = COSTS_N_INSNS (2);
else
*total = 6;
return true;
case CONST:
case LABEL_REF:
case SYMBOL_REF:
- *total = 5;
+ if (TARGET_CONST16)
+ *total = COSTS_N_INSNS (2);
+ else
+ *total = 5;
return true;
case CONST_DOUBLE:
- *total = 7;
+ if (TARGET_CONST16)
+ *total = COSTS_N_INSNS (4);
+ else
+ *total = 7;
return true;
case MEM:
#define MASK_HARD_FLOAT_RSQRT 0x00004000 /* floating-point recip sqrt */
#define MASK_NO_FUSED_MADD 0x00008000 /* avoid f-p mul/add */
#define MASK_SERIALIZE_VOLATILE 0x00010000 /* serialize volatile refs */
+#define MASK_CONST16 0x00020000 /* use CONST16 instruction */
/* Macros used in the machine description to test the flags. */
#define TARGET_HARD_FLOAT_RSQRT (target_flags & MASK_HARD_FLOAT_RSQRT)
#define TARGET_NO_FUSED_MADD (target_flags & MASK_NO_FUSED_MADD)
#define TARGET_SERIALIZE_VOLATILE (target_flags & MASK_SERIALIZE_VOLATILE)
+#define TARGET_CONST16 (target_flags & MASK_CONST16)
/* Default target_flags if no switches are specified */
#define TARGET_DEFAULT ( \
(XCHAL_HAVE_BE ? MASK_BIG_ENDIAN : 0) | \
(XCHAL_HAVE_DENSITY ? MASK_DENSITY : 0) | \
+ (XCHAL_HAVE_L32R ? 0 : MASK_CONST16) | \
(XCHAL_HAVE_MAC16 ? MASK_MAC16 : 0) | \
(XCHAL_HAVE_MUL16 ? MASK_MUL16 : 0) | \
(XCHAL_HAVE_MUL32 ? MASK_MUL32 : 0) | \
N_("Use the Xtensa code density option")}, \
{"no-density", -MASK_DENSITY, \
N_("Do not use the Xtensa code density option")}, \
+ {"const16", MASK_CONST16, \
+ N_("Use CONST16 instruction to load constants")}, \
+ {"no-const16", -MASK_CONST16, \
+ N_("Use PC-relative L32R instruction to load constants")}, \
{"mac16", MASK_MAC16, \
N_("Use the Xtensa MAC16 option")}, \
{"no-mac16", -MASK_MAC16, \
'A' MAC16 accumulator (only if MAC16 option enabled)
'B' general-purpose registers (only if sext instruction enabled)
'C' general-purpose registers (only if mul16 option enabled)
+ 'W' general-purpose registers (only if const16 option enabled)
'b' coprocessor boolean registers
'f' floating-point registers
*/
&& REGNO (OP) >= FIRST_PSEUDO_REGISTER) \
: ((CODE) == 'R') ? smalloffset_mem_p (OP) \
: ((CODE) == 'S') ? smalloffset_double_mem_p (OP) \
- : ((CODE) == 'T') ? constantpool_mem_p (OP) \
+ : ((CODE) == 'T') ? !TARGET_CONST16 && constantpool_mem_p (OP) \
: ((CODE) == 'U') ? !constantpool_mem_p (OP) \
: FALSE)
fprintf (STREAM, "\t.begin no-generics\n"); \
fprintf (STREAM, "\tentry\tsp, %d\n", MIN_FRAME_SIZE); \
\
- /* GCC isn't prepared to deal with data at the beginning of the \
- trampoline, and the Xtensa l32r instruction requires that the \
- constant pool be located before the code. We put the constant \
- pool in the middle of the trampoline and jump around it. */ \
+ /* save the return address */ \
+ fprintf (STREAM, "\tmov\ta10, a0\n"); \
\
- fprintf (STREAM, "\tj\t.Lskipconsts\n"); \
+ /* Use a CALL0 instruction to skip past the constants and in the \
+ process get the PC into A0. This allows PC-relative access to \
+ the constants without relying on L32R, which may not always be \
+ available. */ \
+ \
+ fprintf (STREAM, "\tcall0\t.Lskipconsts\n"); \
fprintf (STREAM, "\t.align\t4\n"); \
- fprintf (STREAM, ".Lfnaddr:%s0\n", integer_asm_op (4, TRUE)); \
fprintf (STREAM, ".Lchainval:%s0\n", integer_asm_op (4, TRUE)); \
+ fprintf (STREAM, ".Lfnaddr:%s0\n", integer_asm_op (4, TRUE)); \
fprintf (STREAM, ".Lskipconsts:\n"); \
\
/* store the static chain */ \
- fprintf (STREAM, "\tl32r\ta8, .Lchainval\n"); \
- fprintf (STREAM, "\ts32i\ta8, sp, %d\n", \
- MIN_FRAME_SIZE - (5 * UNITS_PER_WORD)); \
+ fprintf (STREAM, "\taddi\ta0, a0, 3\n"); \
+ fprintf (STREAM, "\tl32i\ta8, a0, 0\n"); \
+ fprintf (STREAM, "\ts32i\ta8, sp, %d\n", MIN_FRAME_SIZE - 20); \
\
/* set the proper stack pointer value */ \
- fprintf (STREAM, "\tl32r\ta8, .Lfnaddr\n"); \
+ fprintf (STREAM, "\tl32i\ta8, a0, 4\n"); \
fprintf (STREAM, "\tl32i\ta9, a8, 0\n"); \
fprintf (STREAM, "\textui\ta9, a9, %d, 12\n", \
TARGET_BIG_ENDIAN ? 8 : 12); \
fprintf (STREAM, "\tsub\ta9, sp, a9\n"); \
fprintf (STREAM, "\tmovsp\tsp, a9\n"); \
\
+ /* restore the return address */ \
+ fprintf (STREAM, "\tmov\ta0, a10\n"); \
+ \
/* jump to the instruction following the entry */ \
fprintf (STREAM, "\taddi\ta8, a8, 3\n"); \
fprintf (STREAM, "\tjx\ta8\n"); \
} while (0)
/* Size in bytes of the trampoline, as an integer. */
-#define TRAMPOLINE_SIZE 49
+#define TRAMPOLINE_SIZE 59
/* Alignment required for trampolines, in bits. */
#define TRAMPOLINE_ALIGNMENT (32)
#define INITIALIZE_TRAMPOLINE(ADDR, FUNC, CHAIN) \
do { \
rtx addr = ADDR; \
- emit_move_insn (gen_rtx_MEM (SImode, plus_constant (addr, 8)), FUNC); \
emit_move_insn (gen_rtx_MEM (SImode, plus_constant (addr, 12)), CHAIN); \
+ emit_move_insn (gen_rtx_MEM (SImode, plus_constant (addr, 16)), FUNC); \
emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__xtensa_sync_caches"), \
0, VOIDmode, 1, addr, Pmode); \
} while (0)
\
/* allow constant pool addresses */ \
if ((MODE) != BLKmode && GET_MODE_SIZE (MODE) >= UNITS_PER_WORD \
- && constantpool_address_p (xinsn)) \
+ && !TARGET_CONST16 && constantpool_address_p (xinsn)) \
goto LABEL; \
\
while (GET_CODE (xinsn) == SUBREG) \
{"call_insn_operand", { CONST_INT, CONST, SYMBOL_REF, REG }}, \
{"move_operand", { REG, SUBREG, MEM, CONST_INT, CONST_DOUBLE, \
CONST, SYMBOL_REF, LABEL_REF }}, \
- {"non_const_move_operand", { REG, SUBREG, MEM }}, \
{"const_float_1_operand", { CONST_DOUBLE }}, \
{"branch_operator", { EQ, NE, LT, GE }}, \
{"ubranch_operator", { LTU, GEU }}, \
(define_constants [
(A0_REG 0)
+ (A1_REG 1)
(A7_REG 7)
+ (A8_REG 8)
(UNSPEC_NSAU 1)
(UNSPEC_NOP 2)
(UNSPEC_PLT 3)
(UNSPEC_RET_ADDR 4)
(UNSPECV_SET_FP 1)
+ (UNSPECV_ENTRY 2)
])
;;
""
"
{
- if (CONSTANT_P (operands[1]))
+ if (CONSTANT_P (operands[1])
+ && register_operand (operands[0], DImode))
{
rtx src0, src1, dst0, dst1;
- if ((dst0 = operand_subword (operands[0], 0, 1, DImode))
- && (src0 = operand_subword (operands[1], 0, 1, DImode))
- && (dst1 = operand_subword (operands[0], 1, 1, DImode))
- && (src1 = operand_subword (operands[1], 1, 1, DImode)))
- {
- emit_insn (gen_movsi (dst0, src0));
- emit_insn (gen_movsi (dst1, src1));
- DONE;
- }
- else
- /* any other constant will be loaded from memory */
- operands[1] = force_const_mem (DImode, operands[1]);
+ dst0 = operand_subword (operands[0], 0, 1, DImode);
+ src0 = operand_subword (operands[1], 0, 1, DImode);
+ dst1 = operand_subword (operands[0], 1, 1, DImode);
+ src1 = operand_subword (operands[1], 1, 1, DImode);
+ if (!dst0 || !src0 || !dst1 || !src1)
+ abort ();
+ emit_insn (gen_movsi (dst0, src0));
+ emit_insn (gen_movsi (dst1, src1));
+ DONE;
}
if (!(reload_in_progress | reload_completed))
}")
(define_insn "movdi_internal"
- [(set (match_operand:DI 0 "nonimmed_operand" "=D,D,S,a,a,a,U")
- (match_operand:DI 1 "non_const_move_operand" "d,S,d,r,T,U,r"))]
+ [(set (match_operand:DI 0 "nonimmed_operand" "=D,D,S,a,a,U")
+ (match_operand:DI 1 "nonimmed_operand" "d,S,d,r,U,r"))]
"register_operand (operands[0], DImode)
|| register_operand (operands[1], DImode)"
"*
{
+ rtx dstreg;
switch (which_alternative)
{
case 0: return \"mov.n\\t%0, %1\;mov.n\\t%D0, %D1\";
case 2: return \"%v0s32i.n\\t%1, %0\;s32i.n\\t%D1, %N0\";
case 3: return \"mov\\t%0, %1\;mov\\t%D0, %D1\";
- case 6: return \"%v0s32i\\t%1, %0\;s32i\\t%D1, %N0\";
+ case 5: return \"%v0s32i\\t%1, %0\;s32i\\t%D1, %N0\";
case 1:
case 4:
- case 5:
- {
- /* Check if the first half of the destination register is used
- in the source address. If so, reverse the order of the loads
- so that the source address doesn't get clobbered until it is
- no longer needed. */
-
- rtx dstreg = operands[0];
- if (GET_CODE (dstreg) == SUBREG)
- dstreg = SUBREG_REG (dstreg);
- if (GET_CODE (dstreg) != REG)
- abort();
-
- if (reg_mentioned_p (dstreg, operands[1]))
- {
- switch (which_alternative)
- {
- case 1: return \"%v1l32i.n\\t%D0, %N1\;l32i.n\\t%0, %1\";
- case 4: return \"%v1l32r\\t%D0, %N1\;l32r\\t%0, %1\";
- case 5: return \"%v1l32i\\t%D0, %N1\;l32i\\t%0, %1\";
- }
- }
- else
- {
- switch (which_alternative)
- {
- case 1: return \"%v1l32i.n\\t%0, %1\;l32i.n\\t%D0, %N1\";
- case 4: return \"%v1l32r\\t%0, %1\;l32r\\t%D0, %N1\";
- case 5: return \"%v1l32i\\t%0, %1\;l32i\\t%D0, %N1\";
- }
- }
- }
+ /* Check if the first half of the destination register is used
+ in the source address. If so, reverse the order of the loads
+ so that the source address doesn't get clobbered until it is
+ no longer needed. */
+
+ dstreg = operands[0];
+ if (GET_CODE (dstreg) == SUBREG)
+ dstreg = SUBREG_REG (dstreg);
+ if (GET_CODE (dstreg) != REG)
+ abort();
+
+ if (reg_mentioned_p (dstreg, operands[1]))
+ {
+ switch (which_alternative)
+ {
+ case 1: return \"%v1l32i.n\\t%D0, %N1\;l32i.n\\t%0, %1\";
+ case 4: return \"%v1l32i\\t%D0, %N1\;l32i\\t%0, %1\";
+ }
+ }
+ else
+ {
+ switch (which_alternative)
+ {
+ case 1: return \"%v1l32i.n\\t%0, %1\;l32i.n\\t%D0, %N1\";
+ case 4: return \"%v1l32i\\t%0, %1\;l32i\\t%D0, %N1\";
+ }
+ }
}
abort ();
return \"\";
}"
- [(set_attr "type" "move,load,store,move,load,load,store")
+ [(set_attr "type" "move,load,store,move,load,store")
(set_attr "mode" "DI")
- (set_attr "length" "4,4,4,6,6,6,6")])
+ (set_attr "length" "4,4,4,6,6,6")])
;; 32-bit Integer moves
}")
(define_insn "movsi_internal"
- [(set (match_operand:SI 0 "nonimmed_operand" "=D,D,D,D,R,R,a,q,a,a,a,U,*a,*A")
- (match_operand:SI 1 "move_operand" "M,D,d,R,D,d,r,r,I,T,U,r,*A,*r"))]
+ [(set (match_operand:SI 0 "nonimmed_operand" "=D,D,D,D,R,R,a,q,a,W,a,a,U,*a,*A")
+ (match_operand:SI 1 "move_operand" "M,D,d,R,D,d,r,r,I,i,T,U,r,*A,*r"))]
"xtensa_valid_move (SImode, operands)"
"@
movi.n\\t%0, %x1
mov\\t%0, %1
movsp\\t%0, %1
movi\\t%0, %x1
+ const16\\t%0, %t1\;const16\\t%0, %b1
%v1l32r\\t%0, %1
%v1l32i\\t%0, %1
%v0s32i\\t%1, %0
rsr\\t%0, 16 # ACCLO
wsr\\t%1, 16 # ACCLO"
- [(set_attr "type" "move,move,move,load,store,store,move,move,move,load,load,store,rsr,wsr")
+ [(set_attr "type" "move,move,move,load,store,store,move,move,move,move,load,load,store,rsr,wsr")
(set_attr "mode" "SI")
- (set_attr "length" "2,2,2,2,2,2,3,3,3,3,3,3,3,3")])
+ (set_attr "length" "2,2,2,2,2,2,3,3,3,6,3,3,3,3,3")])
;; 16-bit Integer moves
""
"
{
- if (GET_CODE (operands[1]) == CONST_DOUBLE)
+ if (!TARGET_CONST16 && CONSTANT_P (operands[1]))
operands[1] = force_const_mem (SFmode, operands[1]);
if (!(reload_in_progress | reload_completed))
{
- if (((!register_operand (operands[0], SFmode)
+ if ((!register_operand (operands[0], SFmode)
&& !register_operand (operands[1], SFmode))
|| (FP_REG_P (xt_true_regnum (operands[0]))
- && constantpool_mem_p (operands[1]))))
+ && (constantpool_mem_p (operands[1])
+ || CONSTANT_P (operands[1]))))
operands[1] = force_reg (SFmode, operands[1]);
if (xtensa_copy_incoming_a7 (operands, SFmode))
}")
(define_insn "movsf_internal"
- [(set (match_operand:SF 0 "nonimmed_operand"
- "=f,f,U,D,D,R,a,f,a,a,a,U")
- (match_operand:SF 1 "non_const_move_operand"
- "f,U,f,d,R,d,r,r,f,T,U,r"))]
+ [(set (match_operand:SF 0 "nonimmed_operand" "=f,f,U,D,D,R,a,f,a,W,a,a,U")
+ (match_operand:SF 1 "move_operand" "f,U,f,d,R,d,r,r,f,F,T,U,r"))]
"((register_operand (operands[0], SFmode)
|| register_operand (operands[1], SFmode))
- && (!FP_REG_P (xt_true_regnum (operands[0]))
- || !constantpool_mem_p (operands[1])))"
+ && !(FP_REG_P (xt_true_regnum (operands[0]))
+ && (constantpool_mem_p (operands[1]) || CONSTANT_P (operands[1]))))"
"@
mov.s\\t%0, %1
%v1lsi\\t%0, %1
mov\\t%0, %1
wfr\\t%0, %1
rfr\\t%0, %1
+ const16\\t%0, %t1\;const16\\t%0, %b1
%v1l32r\\t%0, %1
%v1l32i\\t%0, %1
%v0s32i\\t%1, %0"
- [(set_attr "type" "farith,fload,fstore,move,load,store,move,farith,farith,load,load,store")
+ [(set_attr "type" "farith,fload,fstore,move,load,store,move,farith,farith,move,load,load,store")
(set_attr "mode" "SF")
- (set_attr "length" "3,3,3,2,2,2,3,3,3,3,3,3")])
+ (set_attr "length" "3,3,3,2,2,2,3,3,3,6,3,3,3")])
(define_insn "*lsiu"
[(set (match_operand:SF 0 "register_operand" "=f")
""
"
{
- if (GET_CODE (operands[1]) == CONST_DOUBLE)
- operands[1] = force_const_mem (DFmode, operands[1]);
+ if (CONSTANT_P (operands[1]))
+ {
+ rtx src0, src1, dst0, dst1;
+ dst0 = operand_subword (operands[0], 0, 1, DFmode);
+ src0 = operand_subword (operands[1], 0, 1, DFmode);
+ dst1 = operand_subword (operands[0], 1, 1, DFmode);
+ src1 = operand_subword (operands[1], 1, 1, DFmode);
+ if (!dst0 || !src0 || !dst1 || !src1)
+ abort ();
+ emit_insn (gen_movsi (dst0, src0));
+ emit_insn (gen_movsi (dst1, src1));
+ DONE;
+ }
if (!(reload_in_progress | reload_completed))
{
}")
(define_insn "movdf_internal"
- [(set (match_operand:DF 0 "nonimmed_operand" "=D,D,S,a,a,a,U")
- (match_operand:DF 1 "non_const_move_operand" "d,S,d,r,T,U,r"))]
+ [(set (match_operand:DF 0 "nonimmed_operand" "=D,D,S,a,a,U")
+ (match_operand:DF 1 "nonimmed_operand" "d,S,d,r,U,r"))]
"register_operand (operands[0], DFmode)
|| register_operand (operands[1], DFmode)"
"*
{
+ rtx dstreg;
switch (which_alternative)
{
case 0: return \"mov.n\\t%0, %1\;mov.n\\t%D0, %D1\";
case 2: return \"%v0s32i.n\\t%1, %0\;s32i.n\\t%D1, %N0\";
case 3: return \"mov\\t%0, %1\;mov\\t%D0, %D1\";
- case 6: return \"%v0s32i\\t%1, %0\;s32i\\t%D1, %N0\";
+ case 5: return \"%v0s32i\\t%1, %0\;s32i\\t%D1, %N0\";
case 1:
case 4:
- case 5:
- {
- /* Check if the first half of the destination register is used
- in the source address. If so, reverse the order of the loads
- so that the source address doesn't get clobbered until it is
- no longer needed. */
-
- rtx dstreg = operands[0];
- if (GET_CODE (dstreg) == SUBREG)
- dstreg = SUBREG_REG (dstreg);
- if (GET_CODE (dstreg) != REG)
- abort ();
-
- if (reg_mentioned_p (dstreg, operands[1]))
- {
- switch (which_alternative)
- {
- case 1: return \"%v1l32i.n\\t%D0, %N1\;l32i.n\\t%0, %1\";
- case 4: return \"%v1l32r\\t%D0, %N1\;l32r\\t%0, %1\";
- case 5: return \"%v1l32i\\t%D0, %N1\;l32i\\t%0, %1\";
- }
- }
- else
- {
- switch (which_alternative)
- {
- case 1: return \"%v1l32i.n\\t%0, %1\;l32i.n\\t%D0, %N1\";
- case 4: return \"%v1l32r\\t%0, %1\;l32r\\t%D0, %N1\";
- case 5: return \"%v1l32i\\t%0, %1\;l32i\\t%D0, %N1\";
- }
- }
- }
+ /* Check if the first half of the destination register is used
+ in the source address. If so, reverse the order of the loads
+ so that the source address doesn't get clobbered until it is
+ no longer needed. */
+
+ dstreg = operands[0];
+ if (GET_CODE (dstreg) == SUBREG)
+ dstreg = SUBREG_REG (dstreg);
+ if (GET_CODE (dstreg) != REG)
+ abort ();
+
+ if (reg_mentioned_p (dstreg, operands[1]))
+ {
+ switch (which_alternative)
+ {
+ case 1: return \"%v1l32i.n\\t%D0, %N1\;l32i.n\\t%0, %1\";
+ case 4: return \"%v1l32i\\t%D0, %N1\;l32i\\t%0, %1\";
+ }
+ }
+ else
+ {
+ switch (which_alternative)
+ {
+ case 1: return \"%v1l32i.n\\t%0, %1\;l32i.n\\t%D0, %N1\";
+ case 4: return \"%v1l32i\\t%0, %1\;l32i\\t%D0, %N1\";
+ }
+ }
}
abort ();
return \"\";
}"
- [(set_attr "type" "move,load,store,move,load,load,store")
+ [(set_attr "type" "move,load,store,move,load,store")
(set_attr "mode" "DF")
- (set_attr "length" "4,4,4,6,6,6,6")])
+ (set_attr "length" "4,4,4,6,6,6")])
;; Block moves
(set_attr "mode" "none")
(set_attr "length" "3")])
+(define_insn "entry"
+ [(set (reg:SI A1_REG)
+ (unspec_volatile:SI [(match_operand:SI 0 "const_int_operand" "i")
+ (match_operand:SI 1 "const_int_operand" "i")]
+ UNSPECV_ENTRY))]
+ ""
+ "*
+{
+ if (frame_pointer_needed)
+ output_asm_insn (\".frame\\ta7, %0\", operands);
+ else
+ output_asm_insn (\".frame\\tsp, %0\", operands);
+ return \"entry\\tsp, %1\";
+}"
+ [(set_attr "type" "move")
+ (set_attr "mode" "SI")
+ (set_attr "length" "3")])
+
(define_insn "return"
[(return)
(use (reg:SI A0_REG))]
;; ....................
;;
+(define_expand "prologue"
+ [(const_int 0)]
+ ""
+ "
+{
+ xtensa_expand_prologue ();
+ DONE;
+}")
+
+(define_expand "epilogue"
+ [(return)]
+ ""
+ "
+{
+ emit_jump_insn (gen_return ());
+ DONE;
+}")
+
(define_insn "nop"
[(const_int 0)]
""
;; to set up the frame pointer.
(define_insn "set_frame_ptr"
- [(set (reg:SI A7_REG) (unspec_volatile [(const_int 0)] UNSPECV_SET_FP))]
+ [(set (reg:SI A7_REG) (unspec_volatile:SI [(const_int 0)] UNSPECV_SET_FP))]
""
"*
{
;; Post-reload splitter to remove fp assignment when it's not needed.
(define_split
- [(set (reg:SI A7_REG) (unspec_volatile [(const_int 0)] UNSPECV_SET_FP))]
+ [(set (reg:SI A7_REG) (unspec_volatile:SI [(const_int 0)] UNSPECV_SET_FP))]
"reload_completed && !frame_pointer_needed"
[(unspec [(const_int 0)] UNSPEC_NOP)]
"")
@emph{Xtensa Options}
@gccoptlist{-mbig-endian -mlittle-endian @gol
-mdensity -mno-density @gol
+-mconst16 -mno-const16 @gol
-mmac16 -mno-mac16 @gol
-mmul16 -mno-mul16 @gol
-mmul32 -mno-mul32 @gol
@opindex mno-density
Enable or disable use of the optional Xtensa code density instructions.
+@item -mconst16
+@itemx -mno-const16
+@opindex mconst16
+@opindex mno-const16
+Enable or disable use of CONST16 instructions for loading constant values.
+The CONST16 instruction is currently not a standard option from Tensilica.
+When enabled, CONST16 instructions are always used in place of the standard
+L32R instructions. The use of CONST16 is enabled by default only if the
+L32R instruction is not available.
+
@item -mmac16
@itemx -mno-mac16
@opindex mmac16