/* Current frame size calculated by compute_frame_size. */
unsigned xtensa_current_frame_size;
+/* Callee-save area size in the current frame calculated by compute_frame_size. */
+int xtensa_callee_save_size;
/* Largest block move to handle in-line. */
#define LARGEST_MOVE_RATIO 15
1
};
-/* Map hard register number to register class */
-const enum reg_class xtensa_regno_to_class[FIRST_PSEUDO_REGISTER] =
-{
- RL_REGS, SP_REG, RL_REGS, RL_REGS,
- RL_REGS, RL_REGS, RL_REGS, GR_REGS,
- RL_REGS, RL_REGS, RL_REGS, RL_REGS,
- RL_REGS, RL_REGS, RL_REGS, RL_REGS,
- AR_REGS, AR_REGS, BR_REGS,
- FP_REGS, FP_REGS, FP_REGS, FP_REGS,
- FP_REGS, FP_REGS, FP_REGS, FP_REGS,
- FP_REGS, FP_REGS, FP_REGS, FP_REGS,
- FP_REGS, FP_REGS, FP_REGS, FP_REGS,
- ACC_REG,
-};
-
static void xtensa_option_override (void);
static enum internal_test map_test_to_internal_test (enum rtx_code);
static rtx gen_int_relational (enum rtx_code, rtx, rtx, int *);
static bool xtensa_mode_dependent_address_p (const_rtx, addr_space_t);
static bool xtensa_return_in_msb (const_tree);
static void printx (FILE *, signed int);
-static void xtensa_function_epilogue (FILE *, HOST_WIDE_INT);
static rtx xtensa_builtin_saveregs (void);
static bool xtensa_legitimate_address_p (machine_mode, rtx, bool);
static unsigned int xtensa_multibss_section_type_flags (tree, const char *,
static bool xtensa_member_type_forces_blk (const_tree,
machine_mode mode);
-static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
- REG_ALLOC_ORDER;
-\f
-
-/* This macro generates the assembly code for function exit,
- on machines that need it. If FUNCTION_EPILOGUE is not defined
- then individual return instructions are generated for each
- return statement. Args are same as for FUNCTION_PROLOGUE. */
+static void xtensa_conditional_register_usage (void);
-#undef TARGET_ASM_FUNCTION_EPILOGUE
-#define TARGET_ASM_FUNCTION_EPILOGUE xtensa_function_epilogue
+\f
/* These hooks specify assembly directives for creating certain kinds
of integer object. */
#undef TARGET_INVALID_WITHIN_DOLOOP
#define TARGET_INVALID_WITHIN_DOLOOP xtensa_invalid_within_doloop
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE xtensa_conditional_register_usage
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
/* The stack pointer can only be assigned with a MOVSP opcode. */
if (dst_regnum == STACK_POINTER_REGNUM)
- return (mode == SImode
- && register_operand (operands[1], mode)
- && !ACC_REG_P (xt_true_regnum (operands[1])));
+ return !TARGET_WINDOWED_ABI
+ || (mode == SImode
+ && register_operand (operands[1], mode)
+ && !ACC_REG_P (xt_true_regnum (operands[1])));
if (!ACC_REG_P (dst_regnum))
return true;
/* Set flag to cause TARGET_FRAME_POINTER_REQUIRED to return true. */
cfun->machine->accesses_prev_frame = 1;
- emit_library_call
- (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_libgcc_window_spill"),
- LCT_NORMAL, VOIDmode, 0);
+ if (TARGET_WINDOWED_ABI)
+ emit_library_call
+ (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_libgcc_window_spill"),
+ LCT_NORMAL, VOIDmode, 0);
}
rtx tgt = operands[callop];
if (GET_CODE (tgt) == CONST_INT)
- sprintf (result, "call8\t0x%lx", INTVAL (tgt));
+ sprintf (result, "call%d\t0x%lx", WINDOW_SIZE, INTVAL (tgt));
else if (register_operand (tgt, VOIDmode))
- sprintf (result, "callx8\t%%%d", callop);
+ sprintf (result, "callx%d\t%%%d", WINDOW_SIZE, callop);
else
- sprintf (result, "call8\t%%%d", callop);
+ sprintf (result, "call%d\t%%%d", WINDOW_SIZE, callop);
return result;
}
regno = regbase + *arg_words;
if (cum->incoming && regno <= A7_REG && regno + words > A7_REG)
- cfun->machine->need_a7_copy = true;
+ cfun->machine->need_a7_copy = TARGET_WINDOWED_ABI;
return gen_rtx_REG (mode, regno);
}
}
}
+static bool
+xtensa_call_save_reg(int regno)
+{
+ if (TARGET_WINDOWED_ABI)
+ return false;
+
+ if (regno == A0_REG)
+ return crtl->profile || !crtl->is_leaf || crtl->calls_eh_return ||
+ df_regs_ever_live_p (regno);
+
+ if (crtl->calls_eh_return && regno >= 2 && regno < 4)
+ return true;
+
+ return !fixed_regs[regno] && !call_used_regs[regno] &&
+ df_regs_ever_live_p (regno);
+}
/* Return the bytes needed to compute the frame pointer from the current
stack pointer. */
long
compute_frame_size (int size)
{
+ int regno;
+
/* Add space for the incoming static chain value. */
if (cfun->static_chain_decl != NULL)
size += (1 * UNITS_PER_WORD);
+ xtensa_callee_save_size = 0;
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
+ {
+ if (xtensa_call_save_reg(regno))
+ xtensa_callee_save_size += UNITS_PER_WORD;
+ }
+
xtensa_current_frame_size =
XTENSA_STACK_ALIGN (size
+ + xtensa_callee_save_size
+ crtl->outgoing_args_size
+ (WINDOW_SIZE * UNITS_PER_WORD));
+ xtensa_callee_save_size = XTENSA_STACK_ALIGN (xtensa_callee_save_size);
return xtensa_current_frame_size;
}
xtensa_expand_prologue (void)
{
HOST_WIDE_INT total_size;
- rtx size_rtx;
- rtx_insn *insn;
+ rtx_insn *insn = NULL;
rtx note_rtx;
+
total_size = compute_frame_size (get_frame_size ());
- size_rtx = GEN_INT (total_size);
- if (total_size < (1 << (12+3)))
- insn = emit_insn (gen_entry (size_rtx));
+ if (TARGET_WINDOWED_ABI)
+ {
+ if (total_size < (1 << (12+3)))
+ insn = emit_insn (gen_entry (GEN_INT (total_size)));
+ else
+ {
+ /* Use a8 as a temporary since a0-a7 may be live. */
+ rtx tmp_reg = gen_rtx_REG (Pmode, A8_REG);
+ emit_insn (gen_entry (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));
+ insn = emit_insn (gen_movsi (stack_pointer_rtx, tmp_reg));
+ }
+ }
else
{
- /* Use a8 as a temporary since a0-a7 may be live. */
- rtx tmp_reg = gen_rtx_REG (Pmode, A8_REG);
- emit_insn (gen_entry (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));
- insn = emit_insn (gen_movsi (stack_pointer_rtx, tmp_reg));
+ int regno;
+ HOST_WIDE_INT offset = 0;
+
+ /* -128 is a limit of single addi instruction. */
+ if (total_size > 0 && total_size <= 128)
+ {
+ insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-total_size)));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ note_rtx = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ plus_constant (Pmode, stack_pointer_rtx,
+ -total_size));
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx);
+ offset = total_size - UNITS_PER_WORD;
+ }
+ else if (xtensa_callee_save_size)
+ {
+ /* 1020 is maximal s32i offset, if the frame is bigger than that
+ * we move sp to the end of callee-saved save area, save and then
+ * move it to its final location. */
+ if (total_size > 1024)
+ {
+ insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-xtensa_callee_save_size)));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ note_rtx = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ plus_constant (Pmode, stack_pointer_rtx,
+ -xtensa_callee_save_size));
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx);
+ offset = xtensa_callee_save_size - UNITS_PER_WORD;
+ }
+ else
+ {
+ rtx tmp_reg = gen_rtx_REG (Pmode, A9_REG);
+ emit_move_insn (tmp_reg, GEN_INT (total_size));
+ insn = emit_insn (gen_subsi3 (stack_pointer_rtx,
+ stack_pointer_rtx, tmp_reg));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ note_rtx = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ plus_constant (Pmode, stack_pointer_rtx,
+ -total_size));
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx);
+ offset = total_size - UNITS_PER_WORD;
+ }
+ }
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
+ {
+ if (xtensa_call_save_reg(regno))
+ {
+ rtx x = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset));
+ rtx mem = gen_frame_mem (SImode, x);
+ rtx reg = gen_rtx_REG (SImode, regno);
+
+ offset -= UNITS_PER_WORD;
+ insn = emit_move_insn (mem, reg);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR,
+ gen_rtx_SET (VOIDmode, mem, reg));
+ }
+ }
+ if (total_size > 1024)
+ {
+ rtx tmp_reg = gen_rtx_REG (Pmode, A9_REG);
+ emit_move_insn (tmp_reg, GEN_INT (total_size -
+ xtensa_callee_save_size));
+ insn = emit_insn (gen_subsi3 (stack_pointer_rtx,
+ stack_pointer_rtx, tmp_reg));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ note_rtx = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ plus_constant (Pmode, stack_pointer_rtx,
+ xtensa_callee_save_size -
+ total_size));
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx);
+ }
}
if (frame_pointer_needed)
}
}
else
- insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
- stack_pointer_rtx));
- }
-
- /* Create a note to describe the CFA. Because this is only used to set
- DW_AT_frame_base for debug info, don't bother tracking changes through
- each instruction in the prologue. It just takes up space. */
- note_rtx = gen_rtx_SET (VOIDmode, (frame_pointer_needed
- ? hard_frame_pointer_rtx
- : stack_pointer_rtx),
- plus_constant (Pmode, stack_pointer_rtx,
- -total_size));
- RTX_FRAME_RELATED_P (insn) = 1;
- add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx);
-}
-
+ {
+ insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
+ stack_pointer_rtx));
+ if (!TARGET_WINDOWED_ABI)
+ {
+ note_rtx = gen_rtx_SET (VOIDmode, hard_frame_pointer_rtx,
+ stack_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx);
+ }
+ }
+ }
-/* Clear variables at function end. */
+ if (TARGET_WINDOWED_ABI)
+ {
+ /* Create a note to describe the CFA. Because this is only used to set
+ DW_AT_frame_base for debug info, don't bother tracking changes through
+ each instruction in the prologue. It just takes up space. */
+ note_rtx = gen_rtx_SET (VOIDmode, (frame_pointer_needed
+ ? hard_frame_pointer_rtx
+ : stack_pointer_rtx),
+ plus_constant (Pmode, stack_pointer_rtx,
+ -total_size));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx);
+ }
+}
void
-xtensa_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
- HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+xtensa_expand_epilogue (void)
{
+ if (!TARGET_WINDOWED_ABI)
+ {
+ int regno;
+ HOST_WIDE_INT offset;
+
+ if (xtensa_current_frame_size > (frame_pointer_needed ? 127 : 1024))
+ {
+ rtx tmp_reg = gen_rtx_REG (Pmode, A9_REG);
+ emit_move_insn (tmp_reg, GEN_INT (xtensa_current_frame_size -
+ xtensa_callee_save_size));
+ emit_insn (gen_addsi3 (stack_pointer_rtx, frame_pointer_needed ?
+ hard_frame_pointer_rtx : stack_pointer_rtx,
+ tmp_reg));
+ offset = xtensa_callee_save_size - UNITS_PER_WORD;
+ }
+ else
+ {
+ if (frame_pointer_needed)
+ emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
+ offset = xtensa_current_frame_size - UNITS_PER_WORD;
+ }
+
+ /* Prevent reordering of saved a0 update and loading it back from
+ the save area. */
+ if (crtl->calls_eh_return)
+ emit_insn (gen_blockage ());
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
+ {
+ if (xtensa_call_save_reg(regno))
+ {
+ rtx x = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset));
+
+ offset -= UNITS_PER_WORD;
+ emit_move_insn (gen_rtx_REG (SImode, regno),
+ gen_frame_mem (SImode, x));
+ }
+ }
+
+ if (xtensa_current_frame_size > 0)
+ {
+ if (frame_pointer_needed || /* always reachable with addi */
+ xtensa_current_frame_size > 1024 ||
+ xtensa_current_frame_size <= 127)
+ {
+ if (xtensa_current_frame_size <= 127)
+ offset = xtensa_current_frame_size;
+ else
+ offset = xtensa_callee_save_size;
+
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (offset)));
+ }
+ else
+ {
+ rtx tmp_reg = gen_rtx_REG (Pmode, A9_REG);
+ emit_move_insn (tmp_reg, GEN_INT (xtensa_current_frame_size));
+ emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ tmp_reg));
+ }
+ }
+
+ if (crtl->calls_eh_return)
+ emit_insn (gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ EH_RETURN_STACKADJ_RTX));
+ }
xtensa_current_frame_size = 0;
+ xtensa_callee_save_size = 0;
+ emit_jump_insn (gen_return ());
}
+void
+xtensa_set_return_address (rtx address, rtx scratch)
+{
+ HOST_WIDE_INT total_size = compute_frame_size (get_frame_size ());
+ rtx frame = frame_pointer_needed ?
+ hard_frame_pointer_rtx : stack_pointer_rtx;
+ rtx a0_addr = plus_constant (Pmode, frame,
+ total_size - UNITS_PER_WORD);
+ rtx note = gen_rtx_SET (VOIDmode,
+ gen_frame_mem (SImode, a0_addr),
+ gen_rtx_REG (SImode, A0_REG));
+ rtx insn;
+
+ if (total_size > 1024) {
+ emit_move_insn (scratch, GEN_INT (total_size - UNITS_PER_WORD));
+ emit_insn (gen_addsi3 (scratch, frame, scratch));
+ a0_addr = scratch;
+ }
+
+ insn = emit_move_insn (gen_frame_mem (SImode, a0_addr), address);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, note);
+}
rtx
xtensa_return_addr (int count, rtx frame)
{
rtx result, retaddr, curaddr, label;
+ if (!TARGET_WINDOWED_ABI)
+ {
+ if (count != 0)
+ return const0_rtx;
+
+ return get_hard_reg_initial_val (Pmode, A0_REG);
+ }
+
if (count == -1)
retaddr = gen_rtx_REG (Pmode, A0_REG);
else
set_mem_alias_set (gp_regs, get_varargs_alias_set ());
/* Now store the incoming registers. */
- cfun->machine->need_a7_copy = true;
+ cfun->machine->need_a7_copy = TARGET_WINDOWED_ABI;
cfun->machine->vararg_a7 = true;
move_block_from_reg (GP_ARG_FIRST + arg_words,
adjust_address (gp_regs, BLKmode,
arg_words * UNITS_PER_WORD),
gp_left);
- gcc_assert (cfun->machine->vararg_a7_copy != 0);
- emit_insn_before (cfun->machine->vararg_a7_copy, get_insns ());
+ if (cfun->machine->vararg_a7_copy != 0)
+ emit_insn_before (cfun->machine->vararg_a7_copy, get_insns ());
return XEXP (gp_regs, 0);
}
{
if (!leaf_function_p ())
{
- memcpy (reg_alloc_order, reg_nonleaf_alloc_order,
+ static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
+ REG_ALLOC_ORDER;
+ static const int reg_nonleaf_alloc_order_call0[FIRST_PSEUDO_REGISTER] =
+ {
+ 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 12, 13, 14, 15,
+ 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 0, 1, 16, 17,
+ 35,
+ };
+
+ memcpy (reg_alloc_order, TARGET_WINDOWED_ABI ?
+ reg_nonleaf_alloc_order : reg_nonleaf_alloc_order_call0,
FIRST_PSEUDO_REGISTER * sizeof (int));
}
else
static rtx
xtensa_static_chain (const_tree ARG_UNUSED (fndecl_or_type), bool incoming_p)
{
- rtx base = incoming_p ? arg_pointer_rtx : stack_pointer_rtx;
- return gen_frame_mem (Pmode, plus_constant (Pmode, base,
- -5 * UNITS_PER_WORD));
+ if (TARGET_WINDOWED_ABI)
+ {
+ rtx base = incoming_p ? arg_pointer_rtx : stack_pointer_rtx;
+ return gen_frame_mem (Pmode, plus_constant (Pmode, base,
+ -5 * UNITS_PER_WORD));
+ }
+ else
+ return gen_rtx_REG (Pmode, A8_REG);
}
bool use_call0 = (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS);
fprintf (stream, "\t.begin no-transform\n");
- fprintf (stream, "\tentry\tsp, %d\n", MIN_FRAME_SIZE);
- if (use_call0)
+ if (TARGET_WINDOWED_ABI)
{
- /* Save the return address. */
- fprintf (stream, "\tmov\ta10, a0\n");
+ fprintf (stream, "\tentry\tsp, %d\n", MIN_FRAME_SIZE);
- /* 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. */
- fprintf (stream, "\tcall0\t.Lskipconsts\n");
- }
- else
- fprintf (stream, "\tj\t.Lskipconsts\n");
+ if (use_call0)
+ {
+ /* Save the return address. */
+ fprintf (stream, "\tmov\ta10, a0\n");
- fprintf (stream, "\t.align\t4\n");
- fprintf (stream, ".Lchainval:%s0\n", integer_asm_op (4, TRUE));
- fprintf (stream, ".Lfnaddr:%s0\n", integer_asm_op (4, TRUE));
- fprintf (stream, ".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. */
+ fprintf (stream, "\tcall0\t.Lskipconsts\n");
+ }
+ else
+ fprintf (stream, "\tj\t.Lskipconsts\n");
- /* Load the static chain and function address from the trampoline. */
- if (use_call0)
- {
- fprintf (stream, "\taddi\ta0, a0, 3\n");
- fprintf (stream, "\tl32i\ta9, a0, 0\n");
- fprintf (stream, "\tl32i\ta8, a0, 4\n");
+ fprintf (stream, "\t.align\t4\n");
+ fprintf (stream, ".Lchainval:%s0\n", integer_asm_op (4, TRUE));
+ fprintf (stream, ".Lfnaddr:%s0\n", integer_asm_op (4, TRUE));
+ fprintf (stream, ".Lskipconsts:\n");
+
+ /* Load the static chain and function address from the trampoline. */
+ if (use_call0)
+ {
+ fprintf (stream, "\taddi\ta0, a0, 3\n");
+ fprintf (stream, "\tl32i\ta9, a0, 0\n");
+ fprintf (stream, "\tl32i\ta8, a0, 4\n");
+ }
+ else
+ {
+ fprintf (stream, "\tl32r\ta9, .Lchainval\n");
+ fprintf (stream, "\tl32r\ta8, .Lfnaddr\n");
+ }
+
+ /* Store the static chain. */
+ fprintf (stream, "\ts32i\ta9, sp, %d\n", MIN_FRAME_SIZE - 20);
+
+ /* Set the proper stack pointer value. */
+ fprintf (stream, "\tl32i\ta9, a8, 0\n");
+ fprintf (stream, "\textui\ta9, a9, %d, 12\n",
+ TARGET_BIG_ENDIAN ? 8 : 12);
+ fprintf (stream, "\tslli\ta9, a9, 3\n");
+ fprintf (stream, "\taddi\ta9, a9, %d\n", -MIN_FRAME_SIZE);
+ fprintf (stream, "\tsub\ta9, sp, a9\n");
+ fprintf (stream, "\tmovsp\tsp, a9\n");
+
+ if (use_call0)
+ /* 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");
+
+ /* Pad size to a multiple of TRAMPOLINE_ALIGNMENT. */
+ if (use_call0)
+ fprintf (stream, "\t.byte\t0\n");
+ else
+ fprintf (stream, "\tnop\n");
}
else
{
- fprintf (stream, "\tl32r\ta9, .Lchainval\n");
- fprintf (stream, "\tl32r\ta8, .Lfnaddr\n");
- }
-
- /* Store the static chain. */
- fprintf (stream, "\ts32i\ta9, sp, %d\n", MIN_FRAME_SIZE - 20);
-
- /* Set the proper stack pointer value. */
- fprintf (stream, "\tl32i\ta9, a8, 0\n");
- fprintf (stream, "\textui\ta9, a9, %d, 12\n",
- TARGET_BIG_ENDIAN ? 8 : 12);
- fprintf (stream, "\tslli\ta9, a9, 3\n");
- fprintf (stream, "\taddi\ta9, a9, %d\n", -MIN_FRAME_SIZE);
- fprintf (stream, "\tsub\ta9, sp, a9\n");
- fprintf (stream, "\tmovsp\tsp, a9\n");
+ if (use_call0)
+ {
+ /* Save the return address. */
+ fprintf (stream, "\tmov\ta10, a0\n");
- if (use_call0)
- /* Restore the return address. */
- fprintf (stream, "\tmov\ta0, a10\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. */
+ fprintf (stream, "\tcall0\t.Lskipconsts\n");
+ }
+ else
+ fprintf (stream, "\tj\t.Lskipconsts\n");
- /* Jump to the instruction following the ENTRY. */
- fprintf (stream, "\taddi\ta8, a8, 3\n");
- fprintf (stream, "\tjx\ta8\n");
+ fprintf (stream, "\t.align\t4\n");
+ fprintf (stream, ".Lchainval:%s0\n", integer_asm_op (4, TRUE));
+ fprintf (stream, ".Lfnaddr:%s0\n", integer_asm_op (4, TRUE));
+ fprintf (stream, ".Lskipconsts:\n");
- /* Pad size to a multiple of TRAMPOLINE_ALIGNMENT. */
- if (use_call0)
- fprintf (stream, "\t.byte\t0\n");
- else
- fprintf (stream, "\tnop\n");
+ /* Load the static chain and function address from the trampoline. */
+ if (use_call0)
+ {
+ fprintf (stream, "\taddi\ta0, a0, 3\n");
+ fprintf (stream, "\tl32i\ta8, a0, 0\n");
+ fprintf (stream, "\tl32i\ta9, a0, 4\n");
+ fprintf (stream, "\tmov\ta0, a10\n");
+ }
+ else
+ {
+ fprintf (stream, "\tl32r\ta8, .Lchainval\n");
+ fprintf (stream, "\tl32r\ta9, .Lfnaddr\n");
+ }
+ fprintf (stream, "\tjx\ta9\n");
+ /* Pad size to a multiple of TRAMPOLINE_ALIGNMENT. */
+ if (use_call0)
+ fprintf (stream, "\t.byte\t0\n");
+ else
+ fprintf (stream, "\tnop\n");
+ }
fprintf (stream, "\t.end no-transform\n");
}
{
rtx func = XEXP (DECL_RTL (fndecl), 0);
bool use_call0 = (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS);
- int chain_off = use_call0 ? 12 : 8;
- int func_off = use_call0 ? 16 : 12;
+ int chain_off;
+ int func_off;
+
+ if (TARGET_WINDOWED_ABI)
+ {
+ chain_off = use_call0 ? 12 : 8;
+ func_off = use_call0 ? 16 : 12;
+ }
+ else
+ {
+ chain_off = use_call0 ? 8 : 4;
+ func_off = use_call0 ? 12 : 8;
+ }
emit_block_move (m_tramp, assemble_trampoline_template (),
GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL);
xtensa_reorg_loops ();
}
+/* Update register usage after having seen the compiler flags. */
+
+static void
+xtensa_conditional_register_usage (void)
+{
+ unsigned i, c_mask;
+
+ c_mask = TARGET_WINDOWED_ABI ? (1 << 1) : (1 << 2);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ /* Set/reset conditionally defined registers from
+ CALL_USED_REGISTERS initializer. */
+ if (call_used_regs[i] > 1)
+ call_used_regs[i] = !!(call_used_regs[i] & c_mask);
+ }
+
+ /* Remove hard FP register from the preferred reload registers set. */
+ CLEAR_HARD_REG_BIT (reg_class_contents[(int)RL_REGS],
+ HARD_FRAME_POINTER_REGNUM);
+}
+
+/* Map hard register number to register class */
+
+enum reg_class xtensa_regno_to_class (int regno)
+{
+ static const enum reg_class regno_to_class[FIRST_PSEUDO_REGISTER] =
+ {
+ RL_REGS, SP_REG, RL_REGS, RL_REGS,
+ RL_REGS, RL_REGS, RL_REGS, RL_REGS,
+ RL_REGS, RL_REGS, RL_REGS, RL_REGS,
+ RL_REGS, RL_REGS, RL_REGS, RL_REGS,
+ AR_REGS, AR_REGS, BR_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ ACC_REG,
+ };
+
+ if (regno == HARD_FRAME_POINTER_REGNUM)
+ return GR_REGS;
+ else
+ return regno_to_class[regno];
+}
+
#include "gt-xtensa.h"
#define TARGET_ABSOLUTE_LITERALS XSHAL_USE_ABSOLUTE_LITERALS
#define TARGET_THREADPTR XCHAL_HAVE_THREADPTR
#define TARGET_LOOPS XCHAL_HAVE_LOOPS
+#define TARGET_WINDOWED_ABI (XSHAL_ABI == XTHAL_ABI_WINDOWED)
#define TARGET_DEFAULT \
((XCHAL_HAVE_L32R ? 0 : MASK_CONST16) | \
builtin_assert ("machine=xtensa"); \
builtin_define ("__xtensa__"); \
builtin_define ("__XTENSA__"); \
- builtin_define ("__XTENSA_WINDOWED_ABI__"); \
+ builtin_define (TARGET_WINDOWED_ABI ? \
+ "__XTENSA_WINDOWED_ABI__" : "__XTENSA_CALL0_ABI__");\
builtin_define (TARGET_BIG_ENDIAN ? "__XTENSA_EB__" : "__XTENSA_EL__"); \
if (!TARGET_HARD_FLOAT) \
builtin_define ("__XTENSA_SOFT_FLOAT__"); \
registers that can be used without being saved.
The latter must include the registers where values are returned
and the register where structure-value addresses are passed.
- Aside from that, you can include as many other registers as you like. */
+ Aside from that, you can include as many other registers as you like.
+
+ The value encoding is the following:
+ 1: register is used by all ABIs;
+ bit 1 is set: register is used by windowed ABI;
+ bit 2 is set: register is used by call0 ABI.
+
+ Proper values are computed in TARGET_CONDITIONAL_REGISTER_USAGE. */
+
#define CALL_USED_REGISTERS \
{ \
- 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 2, 2, 2, 2, \
1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, \
#define STACK_POINTER_REGNUM (GP_REG_FIRST + 1)
/* Base register for access to local variables of the function. */
-#define HARD_FRAME_POINTER_REGNUM (GP_REG_FIRST + 7)
+#define HARD_FRAME_POINTER_REGNUM (GP_REG_FIRST + \
+ (TARGET_WINDOWED_ABI ? 7 : 15))
/* The register number of the frame pointer register, which is used to
access automatic variables in the stack frame. For Xtensa, this
we use a fixed window size of 8. */
#define INCOMING_REGNO(OUT) \
- ((GP_REG_P (OUT) && \
- ((unsigned) ((OUT) - GP_REG_FIRST) >= WINDOW_SIZE)) ? \
- (OUT) - WINDOW_SIZE : (OUT))
+ (TARGET_WINDOWED_ABI ? \
+ ((GP_REG_P (OUT) && \
+ ((unsigned) ((OUT) - GP_REG_FIRST) >= WINDOW_SIZE)) ? \
+ (OUT) - WINDOW_SIZE : (OUT)) : (OUT))
#define OUTGOING_REGNO(IN) \
- ((GP_REG_P (IN) && \
- ((unsigned) ((IN) - GP_REG_FIRST) < WINDOW_SIZE)) ? \
- (IN) + WINDOW_SIZE : (IN))
+ (TARGET_WINDOWED_ABI ? \
+ ((GP_REG_P (IN) && \
+ ((unsigned) ((IN) - GP_REG_FIRST) < WINDOW_SIZE)) ? \
+ (IN) + WINDOW_SIZE : (IN)) : (IN))
/* Define the classes of registers for register constraints in the
{ 0xfff80000, 0x00000007 }, /* floating-point registers */ \
{ 0x00000000, 0x00000008 }, /* MAC16 accumulator */ \
{ 0x00000002, 0x00000000 }, /* stack pointer register */ \
- { 0x0000ff7d, 0x00000000 }, /* preferred reload registers */ \
+ { 0x0000fffd, 0x00000000 }, /* preferred reload registers */ \
{ 0x0000fffd, 0x00000000 }, /* general-purpose registers */ \
{ 0x0003ffff, 0x00000000 }, /* integer registers */ \
{ 0xffffffff, 0x0000000f } /* all registers */ \
register REGNO. In general there is more that one such class;
choose a class which is "minimal", meaning that no smaller class
also contains the register. */
-extern const enum reg_class xtensa_regno_to_class[FIRST_PSEUDO_REGISTER];
-
-#define REGNO_REG_CLASS(REGNO) xtensa_regno_to_class[ (REGNO) ]
+#define REGNO_REG_CLASS(REGNO) xtensa_regno_to_class (REGNO)
/* Use the Xtensa AR register file for base registers.
No index registers. */
#define STACK_BOUNDARY 128
/* Use a fixed register window size of 8. */
-#define WINDOW_SIZE 8
+#define WINDOW_SIZE (TARGET_WINDOWED_ABI ? 8 : 0)
/* Symbolic macros for the registers used to return integer, floating
point, and values of coprocessor and user-defined modes. */
fprintf (FILE, "\t%s\ta10, a0\n", TARGET_DENSITY ? "mov.n" : "mov"); \
if (flag_pic) \
{ \
- fprintf (FILE, "\tmovi\ta8, _mcount@PLT\n"); \
- fprintf (FILE, "\tcallx8\ta8\n"); \
+ fprintf (FILE, "\tmovi\ta%d, _mcount@PLT\n", WINDOW_SIZE); \
+ fprintf (FILE, "\tcallx%d\ta%d\n", WINDOW_SIZE, WINDOW_SIZE); \
} \
else \
- fprintf (FILE, "\tcall8\t_mcount\n"); \
+ fprintf (FILE, "\tcall%d\t_mcount\n", WINDOW_SIZE); \
} while (0)
/* Stack pointer value doesn't matter at exit. */
/* Size in bytes of the trampoline, as an integer. Make sure this is
a multiple of TRAMPOLINE_ALIGNMENT to avoid -Wpadded warnings. */
-#define TRAMPOLINE_SIZE (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS ? 60 : 52)
+#define TRAMPOLINE_SIZE (TARGET_WINDOWED_ABI ? \
+ (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS ? \
+ 60 : 52) : \
+ (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS ? \
+ 32 : 24))
/* Alignment required for trampolines, in bits. */
#define TRAMPOLINE_ALIGNMENT 32
/* Define this if the return address of a particular stack frame is
accessed from the frame pointer of the previous stack frame. */
-#define RETURN_ADDR_IN_PREVIOUS_FRAME 1
+#define RETURN_ADDR_IN_PREVIOUS_FRAME TARGET_WINDOWED_ABI
/* A C expression whose value is RTL representing the value of the
return address for the frame COUNT steps up from the current
/* Define output to appear before the constant pool. */
#define ASM_OUTPUT_POOL_PROLOGUE(FILE, FUNNAME, FUNDECL, SIZE) \
do { \
- if ((SIZE) > 0) \
+ if ((SIZE) > 0 || !TARGET_WINDOWED_ABI) \
{ \
resolve_unique_section ((FUNDECL), 0, flag_function_sections); \
switch_to_section (function_section (FUNDECL)); \
| DW_EH_PE_pcrel | DW_EH_PE_sdata4) \
: DW_EH_PE_absptr)
+#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, GP_REG_FIRST + 10)
+
/* Emit a PC-relative relocation. */
#define ASM_OUTPUT_DWARF_PCREL(FILE, SIZE, LABEL) \
do { \
a MOVI and let the assembler relax it -- for the .init and .fini
sections, the assembler knows to put the literal in the right
place. */
+#if defined(__XTENSA_WINDOWED_ABI__)
#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \
asm (SECTION_OP "\n\
movi\ta8, " USER_LABEL_PREFIX #FUNC "\n\
callx8\ta8\n" \
TEXT_SECTION_ASM_OP);
+#elif defined(__XTENSA_CALL0_ABI__)
+#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \
+ asm (SECTION_OP "\n\
+ movi\ta0, " USER_LABEL_PREFIX #FUNC "\n\
+ callx0\ta0\n" \
+ TEXT_SECTION_ASM_OP);
+#endif