aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog90
-rw-r--r--gcc/config/sparc/sparc-protos.h4
-rw-r--r--gcc/config/sparc/sparc.c845
-rw-r--r--gcc/config/sparc/sparc.h76
-rw-r--r--gcc/config/sparc/sparc.md104
-rw-r--r--gcc/config/sparc/sparc.opt4
-rw-r--r--gcc/config/sparc/t-elf4
-rw-r--r--gcc/config/sparc/t-leon4
-rw-r--r--gcc/doc/invoke.texi15
-rw-r--r--gcc/testsuite/ChangeLog6
-rw-r--r--gcc/testsuite/gcc.dg/20020503-1.c2
-rw-r--r--gcc/testsuite/gcc.target/sparc/sparc-ret.c5
12 files changed, 893 insertions, 266 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 43ec6bf..8fb1ad9 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,93 @@
+2011-06-10 Eric Botcazou <ebotcazou@adacore.com>
+ Laurent Rougé <laurent.rouge@menta.fr>
+
+ * doc/invoke.texi (SPARC options): Add -mflat.
+ * config/sparc/sparc.opt: Likewise.
+ * config/sparc/sparc-protos.h (sparc_expand_epilogue): Add parameter.
+ (sparc_flat_expand_prologue): Declare.
+ (sparc_flat_expand_epilogue): Likewise.
+ * config/sparc/sparc.h (CPP_CPU_SPEC): Do not handle -msoft-float.
+ (CPP_ENDIAN_SPEC): Replace with...
+ (CPP_OTHER_SPEC): ...this. Also handle -mflat and -msoft-float.
+ (CPP_SPEC): Adjust to above change.
+ (EXTRA_SPECS): Likewise.
+ (SPARC_INCOMING_INT_ARG_FIRST): Add TARGET_FLAT handling.
+ (INCOMING_REGNO): Likewise.
+ (OUTGOING_REGNO): Likewise.
+ (LOCAL_REGNO): Likewise.
+ (SETUP_FRAME_ADDRESSES): Likewise.
+ (FIXED_REGISTERS): Set 0 for %fp.
+ (CALL_USED_REGISTERS): Likewise.
+ (INITIAL_ELIMINATION_OFFSET): Pass current_function_is_leaf.
+ (EXIT_IGNORE_STACK): Define to 1 unconditionally.
+ (RETURN_ADDR_REGNUM): Define.
+ (RETURN_ADDR_RTX): Use it.
+ (INCOMING_RETURN_ADDR_REGNUM): Define.
+ (INCOMING_RETURN_ADDR_RTX): Use it.
+ (DWARF_FRAME_RETURN_COLUMN): Likewise.
+ (EH_RETURN_REGNUM): Define.
+ (EH_RETURN_STACKADJ_RTX): Use it.
+ (EH_RETURN_HANDLER_RTX): Delete.
+ (EPILOGUE_USES): Use them and add TARGET_FLAT handling.
+ * config/sparc/sparc.c (apparent_fsize, actual_fsize, num_gfregs):
+ Delete.
+ (struct machine_function): Add frame_size, apparent_frame_size,
+ frame_base_reg, frame_base_offset, n_global_fp_regs and
+ save_local_in_regs_p fields.
+ (sparc_frame_size, sparc_apparent_frame_size, sparc_frame_base_reg,
+ sparc_frame_base_offset, sparc_n_global_fp_regs,
+ sparc_save_local_in_regs_p): New macros.
+ (sparc_option_override): Error out if -fcall-saved-REG is specified
+ for Out registers.
+ (eligible_for_restore_insn): Fix formatting.
+ (eligible_for_return_delay): Likewise. Add TARGET_FLAT handling.
+ (eligible_for_sibcall_delay): Likewise.
+ (RTX_OK_FOR_OFFSET_P, RTX_OK_FOR_OLO10_P): Add MODE parameter.
+ (sparc_legitimate_address_p): Adjust to above change.
+ (save_global_or_fp_reg_p): New predicate.
+ (return_addr_reg_needed_p): Likewise.
+ (save_local_or_in_reg_p): Likewise.
+ (sparc_compute_frame_size): Use them. Add TARGET_FLAT handling.
+ (SORR_SAVE, SORR_RESTORE): Delete.
+ (sorr_pred_t): New typedef.
+ (sorr_act_t): New enum.
+ (save_or_restore_regs): Rename to...
+ (emit_save_or_restore_regs): ...this. Change type of LOW and HIGH
+ parameters, remove ACTION parameter, add LEAF_FUNCTION_P, SAVE_P,
+ ACTION_TRUE and ACTION_FALSE parameters. Implement more general
+ mechanism. Add CFI information for double-word saves in 32-bit mode.
+ (emit_adjust_base_to_offset): New function extracted from...
+ (emit_save_or_restore_regs): ...this. Rename the rest to...
+ (emit_save_or_restore_regs_global_fp_regs): ...this.
+ (emit_save_or_restore_regs_local_in_regs): New function.
+ (gen_create_flat_frame_[123]): New functions.
+ (sparc_expand_prologue): Use SIZE local variable. Adjust.
+ (sparc_flat_expand_prologue): New function.
+ (sparc_asm_function_prologue): Add TARGET_FLAT handling.
+ (sparc_expand_epilogue): Use SIZE local variable. Adjust.
+ (sparc_flat_expand_epilogue): New function.
+ (sparc_can_use_return_insn_p): Add TARGET_FLAT handling.
+ (output_return): Likewise.
+ (output_sibcall): Likewise.
+ (sparc_output_mi_thunk): Likewise.
+ (sparc_frame_pointer_required): Likewise.
+ (sparc_conditional_register_usage): If TARGET_FLAT, disable the leaf
+ function optimization.
+ * config/sparc/sparc.md (flat): New attribute.
+ (prologue): Add TARGET_FLAT handling.
+ (save_register_window): Disable if TARGET_FLAT.
+ (create_flat_frame_[123]): New patterns.
+ (epilogue): Add TARGET_FLAT handling.
+ (sibcall_epilogue): Likewise.
+ (eh_return): New expander.
+ (eh_return_internal): New insn and splitter.
+ (return_internal): Add TARGET_FLAT handling.
+ (untyped_return): Remove bogus test and use RETURN_ADDR_REGNUM.
+ (save_stack_nonlocal): Use RETURN_ADDR_REGNUM.
+ (nonlocal_goto): Add TARGET_FLAT handling.
+ * config/sparc/t-elf: Add -mflat multilib.
+ * config/sparc/t-leon: Likewise.
+
2011-06-10 Jan Hubicka <jh@suse.cz>
* ipa-utils.c (searchc): Use cgraph_function_or_thunk_node.
diff --git a/gcc/config/sparc/sparc-protos.h b/gcc/config/sparc/sparc-protos.h
index ccf1657..a5e2587 100644
--- a/gcc/config/sparc/sparc-protos.h
+++ b/gcc/config/sparc/sparc-protos.h
@@ -38,7 +38,9 @@ extern enum direction function_arg_padding (enum machine_mode, const_tree);
extern void order_regs_for_local_alloc (void);
extern HOST_WIDE_INT sparc_compute_frame_size (HOST_WIDE_INT, int);
extern void sparc_expand_prologue (void);
-extern void sparc_expand_epilogue (void);
+extern void sparc_flat_expand_prologue (void);
+extern void sparc_expand_epilogue (bool);
+extern void sparc_flat_expand_epilogue (bool);
extern bool sparc_can_use_return_insn_p (void);
extern int check_pic (int);
extern int short_branch (int, int);
diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c
index ae35cf8..0de98c6 100644
--- a/gcc/config/sparc/sparc.c
+++ b/gcc/config/sparc/sparc.c
@@ -287,21 +287,6 @@ const struct processor_costs *sparc_costs = &cypress_costs;
((TARGET_ARCH64 && !TARGET_CM_MEDLOW) || flag_pic)
#endif
-/* Global variables for machine-dependent things. */
-
-/* Size of frame. Need to know this to emit return insns from leaf procedures.
- ACTUAL_FSIZE is set by sparc_compute_frame_size() which is called during the
- reload pass. This is important as the value is later used for scheduling
- (to see what can go in a delay slot).
- APPARENT_FSIZE is the size of the stack less the register save area and less
- the outgoing argument area. It is used when saving call preserved regs. */
-static HOST_WIDE_INT apparent_fsize;
-static HOST_WIDE_INT actual_fsize;
-
-/* Number of live general or floating point registers needed to be
- saved (as 4-byte quantities). */
-static int num_gfregs;
-
/* Vector to say how input registers are mapped to output registers.
HARD_FRAME_POINTER_REGNUM cannot be remapped by this function to
eliminate it. You must use -fomit-frame-pointer to get that. */
@@ -341,28 +326,46 @@ char sparc_leaf_regs[] =
struct GTY(()) machine_function
{
+ /* Size of the frame of the function. */
+ HOST_WIDE_INT frame_size;
+
+ /* Size of the frame of the function minus the register window save area
+ and the outgoing argument area. */
+ HOST_WIDE_INT apparent_frame_size;
+
+ /* Register we pretend the frame pointer is allocated to. Normally, this
+ is %fp, but if we are in a leaf procedure, this is (%sp + offset). We
+ record "offset" separately as it may be too big for (reg + disp). */
+ rtx frame_base_reg;
+ HOST_WIDE_INT frame_base_offset;
+
/* Some local-dynamic TLS symbol name. */
const char *some_ld_name;
+ /* Number of global or FP registers to be saved (as 4-byte quantities). */
+ int n_global_fp_regs;
+
/* True if the current function is leaf and uses only leaf regs,
so that the SPARC leaf function optimization can be applied.
Private version of current_function_uses_only_leaf_regs, see
sparc_expand_prologue for the rationale. */
int leaf_function_p;
+ /* True if the prologue saves local or in registers. */
+ bool save_local_in_regs_p;
+
/* True if the data calculated by sparc_expand_prologue are valid. */
bool prologue_data_valid_p;
};
-#define sparc_leaf_function_p cfun->machine->leaf_function_p
-#define sparc_prologue_data_valid_p cfun->machine->prologue_data_valid_p
-
-/* Register we pretend to think the frame pointer is allocated to.
- Normally, this is %fp, but if we are in a leaf procedure, this
- is %sp+"something". We record "something" separately as it may
- be too big for reg+constant addressing. */
-static rtx frame_base_reg;
-static HOST_WIDE_INT frame_base_offset;
+#define sparc_frame_size cfun->machine->frame_size
+#define sparc_apparent_frame_size cfun->machine->apparent_frame_size
+#define sparc_frame_base_reg cfun->machine->frame_base_reg
+#define sparc_frame_base_offset cfun->machine->frame_base_offset
+#define sparc_n_global_fp_regs cfun->machine->n_global_fp_regs
+#define sparc_leaf_function_p cfun->machine->leaf_function_p
+#define sparc_save_local_in_regs_p cfun->machine->save_local_in_regs_p
+#define sparc_prologue_data_valid_p cfun->machine->prologue_data_valid_p
/* 1 if the next opcode is to be specially indented. */
int sparc_indent_opcode = 0;
@@ -387,8 +390,6 @@ static rtx sparc_builtin_saveregs (void);
static int epilogue_renumber (rtx *, int);
static bool sparc_assemble_integer (rtx, unsigned int, int);
static int set_extends (rtx);
-static int save_or_restore_regs (int, int, rtx, int, int);
-static void emit_save_or_restore_regs (int);
static void sparc_asm_function_prologue (FILE *, HOST_WIDE_INT);
static void sparc_asm_function_epilogue (FILE *, HOST_WIDE_INT);
#ifdef TARGET_SOLARIS
@@ -764,6 +765,7 @@ sparc_option_override (void)
{ MASK_ISA, MASK_V9},
};
const struct cpu_table *cpu;
+ unsigned int i;
int fpu;
#ifdef SUBTARGET_OVERRIDE_OPTIONS
@@ -808,6 +810,14 @@ sparc_option_override (void)
error ("-mcmodel= is not supported on 32 bit systems");
}
+ /* Check that -fcall-saved-REG wasn't specified for out registers. */
+ for (i = 8; i < 16; i++)
+ if (!call_used_regs [i])
+ {
+ error ("-fcall-saved-REG is not supported for out registers");
+ call_used_regs [i] = 1;
+ }
+
fpu = target_flags & MASK_FPU; /* save current -mfpu status */
/* Set the default CPU. */
@@ -2769,9 +2779,11 @@ eligible_for_restore_insn (rtx trial, bool return_p)
/* If we have the 'return' instruction, anything that does not use
local or output registers and can go into a delay slot wins. */
- else if (return_p && TARGET_V9 && ! epilogue_renumber (&pat, 1)
- && (get_attr_in_uncond_branch_delay (trial)
- == IN_UNCOND_BRANCH_DELAY_TRUE))
+ else if (return_p
+ && TARGET_V9
+ && !epilogue_renumber (&pat, 1)
+ && get_attr_in_uncond_branch_delay (trial)
+ == IN_UNCOND_BRANCH_DELAY_TRUE)
return 1;
/* The 'restore src1,src2,dest' pattern for SImode. */
@@ -2806,8 +2818,7 @@ eligible_for_restore_insn (rtx trial, bool return_p)
return 0;
}
-/* Return nonzero if TRIAL can go into the function return's
- delay slot. */
+/* Return nonzero if TRIAL can go into the function return's delay slot. */
int
eligible_for_return_delay (rtx trial)
@@ -2825,10 +2836,10 @@ eligible_for_return_delay (rtx trial)
if (crtl->calls_eh_return)
return 0;
- /* In the case of a true leaf function, anything can go into the slot. */
- if (sparc_leaf_function_p)
- return get_attr_in_uncond_branch_delay (trial)
- == IN_UNCOND_BRANCH_DELAY_TRUE;
+ /* In the case of a leaf or flat function, anything can go into the slot. */
+ if (sparc_leaf_function_p || TARGET_FLAT)
+ return
+ get_attr_in_uncond_branch_delay (trial) == IN_UNCOND_BRANCH_DELAY_TRUE;
pat = PATTERN (trial);
@@ -2843,15 +2854,14 @@ eligible_for_return_delay (rtx trial)
with FP_REGS. */
if (REGNO (SET_DEST (pat)) >= 32)
return (TARGET_V9
- && ! epilogue_renumber (&pat, 1)
- && (get_attr_in_uncond_branch_delay (trial)
- == IN_UNCOND_BRANCH_DELAY_TRUE));
+ && !epilogue_renumber (&pat, 1)
+ && get_attr_in_uncond_branch_delay (trial)
+ == IN_UNCOND_BRANCH_DELAY_TRUE);
return eligible_for_restore_insn (trial, true);
}
-/* Return nonzero if TRIAL can go into the sibling call's
- delay slot. */
+/* Return nonzero if TRIAL can go into the sibling call's delay slot. */
int
eligible_for_sibcall_delay (rtx trial)
@@ -2866,7 +2876,7 @@ eligible_for_sibcall_delay (rtx trial)
pat = PATTERN (trial);
- if (sparc_leaf_function_p)
+ if (sparc_leaf_function_p || TARGET_FLAT)
{
/* If the tail call is done using the call instruction,
we have to restore %o7 in the delay slot. */
@@ -3117,11 +3127,15 @@ legitimate_pic_operand_p (rtx x)
return true;
}
-#define RTX_OK_FOR_OFFSET_P(X) \
- (CONST_INT_P (X) && INTVAL (X) >= -0x1000 && INTVAL (X) < 0x1000 - 8)
+#define RTX_OK_FOR_OFFSET_P(X, MODE) \
+ (CONST_INT_P (X) \
+ && INTVAL (X) >= -0x1000 \
+ && INTVAL (X) < (0x1000 - GET_MODE_SIZE (MODE)))
-#define RTX_OK_FOR_OLO10_P(X) \
- (CONST_INT_P (X) && INTVAL (X) >= -0x1000 && INTVAL (X) < 0xc00 - 8)
+#define RTX_OK_FOR_OLO10_P(X, MODE) \
+ (CONST_INT_P (X) \
+ && INTVAL (X) >= -0x1000 \
+ && INTVAL (X) < (0xc00 - GET_MODE_SIZE (MODE)))
/* Handle the TARGET_LEGITIMATE_ADDRESS_P target hook.
@@ -3163,7 +3177,7 @@ sparc_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
&& (GET_CODE (rs2) != CONST_INT || SMALL_INT (rs2)))
|| ((REG_P (rs1)
|| GET_CODE (rs1) == SUBREG)
- && RTX_OK_FOR_OFFSET_P (rs2)))
+ && RTX_OK_FOR_OFFSET_P (rs2, mode)))
{
imm1 = rs2;
rs2 = NULL;
@@ -3193,7 +3207,7 @@ sparc_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
&& GET_CODE (rs1) == LO_SUM
&& TARGET_ARCH64
&& ! TARGET_CM_MEDMID
- && RTX_OK_FOR_OLO10_P (rs2))
+ && RTX_OK_FOR_OLO10_P (rs2, mode))
{
rs2 = NULL;
imm1 = XEXP (rs1, 1);
@@ -4104,59 +4118,138 @@ sparc_init_modes (void)
}
}
+/* Return whether REGNO, a global or FP register, must be saved/restored. */
+
+static inline bool
+save_global_or_fp_reg_p (unsigned int regno,
+ int leaf_function ATTRIBUTE_UNUSED)
+{
+ return !call_used_regs[regno] && df_regs_ever_live_p (regno);
+}
+
+/* Return whether the return address register (%i7) is needed. */
+
+static inline bool
+return_addr_reg_needed_p (int leaf_function)
+{
+ /* If it is live, for example because of __builtin_return_address (0). */
+ if (df_regs_ever_live_p (RETURN_ADDR_REGNUM))
+ return true;
+
+ /* Otherwise, it is needed as save register if %o7 is clobbered. */
+ if (!leaf_function
+ /* Loading the GOT register clobbers %o7. */
+ || crtl->uses_pic_offset_table
+ || df_regs_ever_live_p (INCOMING_RETURN_ADDR_REGNUM))
+ return true;
+
+ return false;
+}
+
+/* Return whether REGNO, a local or in register, must be saved/restored. */
+
+static bool
+save_local_or_in_reg_p (unsigned int regno, int leaf_function)
+{
+ /* General case: call-saved registers live at some point. */
+ if (!call_used_regs[regno] && df_regs_ever_live_p (regno))
+ return true;
+
+ /* Frame pointer register (%fp) if needed. */
+ if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
+ return true;
+
+ /* Return address register (%i7) if needed. */
+ if (regno == RETURN_ADDR_REGNUM && return_addr_reg_needed_p (leaf_function))
+ return true;
+
+ /* PIC register (%l7) if needed. */
+ if (regno == PIC_OFFSET_TABLE_REGNUM && crtl->uses_pic_offset_table)
+ return true;
+
+ /* If the function accesses prior frames, the frame pointer and the return
+ address of the previous frame must be saved on the stack. */
+ if (crtl->accesses_prior_frames
+ && (regno == HARD_FRAME_POINTER_REGNUM || regno == RETURN_ADDR_REGNUM))
+ return true;
+
+ return false;
+}
+
/* Compute the frame size required by the function. This function is called
during the reload pass and also by sparc_expand_prologue. */
HOST_WIDE_INT
-sparc_compute_frame_size (HOST_WIDE_INT size, int leaf_function_p)
+sparc_compute_frame_size (HOST_WIDE_INT size, int leaf_function)
{
- int outgoing_args_size = (crtl->outgoing_args_size
- + REG_PARM_STACK_SPACE (current_function_decl));
- int n_regs = 0; /* N_REGS is the number of 4-byte regs saved thus far. */
- int i;
+ HOST_WIDE_INT frame_size, apparent_frame_size;
+ int args_size, n_global_fp_regs = 0;
+ bool save_local_in_regs_p = false;
+ unsigned int i;
- if (TARGET_ARCH64)
- {
- for (i = 0; i < 8; i++)
- if (df_regs_ever_live_p (i) && ! call_used_regs[i])
- n_regs += 2;
- }
+ /* If the function allocates dynamic stack space, the dynamic offset is
+ computed early and contains REG_PARM_STACK_SPACE, so we need to cope. */
+ if (leaf_function && !cfun->calls_alloca)
+ args_size = 0;
else
- {
- for (i = 0; i < 8; i += 2)
- if ((df_regs_ever_live_p (i) && ! call_used_regs[i])
- || (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1]))
- n_regs += 2;
- }
+ args_size = crtl->outgoing_args_size + REG_PARM_STACK_SPACE (cfun->decl);
- for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2)
- if ((df_regs_ever_live_p (i) && ! call_used_regs[i])
- || (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1]))
- n_regs += 2;
+ /* Calculate space needed for global registers. */
+ if (TARGET_ARCH64)
+ for (i = 0; i < 8; i++)
+ if (save_global_or_fp_reg_p (i, 0))
+ n_global_fp_regs += 2;
+ else
+ for (i = 0; i < 8; i += 2)
+ if (save_global_or_fp_reg_p (i, 0) || save_global_or_fp_reg_p (i + 1, 0))
+ n_global_fp_regs += 2;
- /* Set up values for use in prologue and epilogue. */
- num_gfregs = n_regs;
+ /* In the flat window model, find out which local and in registers need to
+ be saved. We don't reserve space in the current frame for them as they
+ will be spilled into the register window save area of the caller's frame.
+ However, as soon as we use this register window save area, we must create
+ that of the current frame to make it the live one. */
+ if (TARGET_FLAT)
+ for (i = 16; i < 32; i++)
+ if (save_local_or_in_reg_p (i, leaf_function))
+ {
+ save_local_in_regs_p = true;
+ break;
+ }
- if (leaf_function_p
- && n_regs == 0
- && size == 0
- && crtl->outgoing_args_size == 0)
- actual_fsize = apparent_fsize = 0;
+ /* Calculate space needed for FP registers. */
+ for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2)
+ if (save_global_or_fp_reg_p (i, 0) || save_global_or_fp_reg_p (i + 1, 0))
+ n_global_fp_regs += 2;
+
+ if (size == 0
+ && n_global_fp_regs == 0
+ && args_size == 0
+ && !save_local_in_regs_p)
+ frame_size = apparent_frame_size = 0;
else
{
/* We subtract STARTING_FRAME_OFFSET, remember it's negative. */
- apparent_fsize = (size - STARTING_FRAME_OFFSET + 7) & -8;
- apparent_fsize += n_regs * 4;
- actual_fsize = apparent_fsize + ((outgoing_args_size + 7) & -8);
+ apparent_frame_size = (size - STARTING_FRAME_OFFSET + 7) & -8;
+ apparent_frame_size += n_global_fp_regs * 4;
+
+ /* We need to add the size of the outgoing argument area. */
+ frame_size = apparent_frame_size + ((args_size + 7) & -8);
+
+ /* And that of the register window save area. */
+ frame_size += FIRST_PARM_OFFSET (cfun->decl);
+
+ /* Finally, bump to the appropriate alignment. */
+ frame_size = SPARC_STACK_ALIGN (frame_size);
}
- /* Make sure nothing can clobber our register windows.
- If a SAVE must be done, or there is a stack-local variable,
- the register window area must be allocated. */
- if (! leaf_function_p || size > 0)
- actual_fsize += FIRST_PARM_OFFSET (current_function_decl);
+ /* Set up values for use in prologue and epilogue. */
+ sparc_frame_size = frame_size;
+ sparc_apparent_frame_size = apparent_frame_size;
+ sparc_n_global_fp_regs = n_global_fp_regs;
+ sparc_save_local_in_regs_p = save_local_in_regs_p;
- return SPARC_STACK_ALIGN (actual_fsize);
+ return frame_size;
}
/* Output any necessary .register pseudo-ops. */
@@ -4342,43 +4435,66 @@ output_probe_stack_range (rtx reg1, rtx reg2)
return "";
}
-/* Save/restore call-saved registers from LOW to HIGH at BASE+OFFSET
- as needed. LOW should be double-word aligned for 32-bit registers.
- Return the new OFFSET. */
+/* Emit code to save/restore registers from LOW to HIGH at BASE+OFFSET as
+ needed. LOW is supposed to be double-word aligned for 32-bit registers.
+ SAVE_P decides whether a register must be saved/restored. ACTION_TRUE
+ is the action to be performed if SAVE_P returns true and ACTION_FALSE
+ the action to be performed if it returns false. Return the new offset. */
-#define SORR_SAVE 0
-#define SORR_RESTORE 1
+typedef bool (*sorr_pred_t) (unsigned int, int);
+typedef enum { SORR_NONE, SORR_ADVANCE, SORR_SAVE, SORR_RESTORE } sorr_act_t;
static int
-save_or_restore_regs (int low, int high, rtx base, int offset, int action)
+emit_save_or_restore_regs (unsigned int low, unsigned int high, rtx base,
+ int offset, int leaf_function, sorr_pred_t save_p,
+ sorr_act_t action_true, sorr_act_t action_false)
{
+ unsigned int i;
rtx mem, insn;
- int i;
if (TARGET_ARCH64 && high <= 32)
{
+ int fp_offset = -1;
+
for (i = low; i < high; i++)
{
- if (df_regs_ever_live_p (i) && ! call_used_regs[i])
+ if (save_p (i, leaf_function))
{
mem = gen_frame_mem (DImode, plus_constant (base, offset));
- if (action == SORR_SAVE)
+ if (action_true == SORR_SAVE)
{
insn = emit_move_insn (mem, gen_rtx_REG (DImode, i));
RTX_FRAME_RELATED_P (insn) = 1;
}
- else /* action == SORR_RESTORE */
- emit_move_insn (gen_rtx_REG (DImode, i), mem);
+ else /* action_true == SORR_RESTORE */
+ {
+ /* The frame pointer must be restored last since its old
+ value may be used as base address for the frame. This
+ is problematic in 64-bit mode only because of the lack
+ of double-word load instruction. */
+ if (i == HARD_FRAME_POINTER_REGNUM)
+ fp_offset = offset;
+ else
+ emit_move_insn (gen_rtx_REG (DImode, i), mem);
+ }
offset += 8;
}
+ else if (action_false == SORR_ADVANCE)
+ offset += 8;
+ }
+
+ if (fp_offset >= 0)
+ {
+ mem = gen_frame_mem (DImode, plus_constant (base, fp_offset));
+ emit_move_insn (hard_frame_pointer_rtx, mem);
}
}
else
{
for (i = low; i < high; i += 2)
{
- bool reg0 = df_regs_ever_live_p (i) && ! call_used_regs[i];
- bool reg1 = df_regs_ever_live_p (i+1) && ! call_used_regs[i+1];
+ bool reg0 = save_p (i, leaf_function);
+ bool reg1 = save_p (i + 1, leaf_function);
enum machine_mode mode;
int regno;
@@ -4399,15 +4515,35 @@ save_or_restore_regs (int low, int high, rtx base, int offset, int action)
offset += 4;
}
else
- continue;
+ {
+ if (action_false == SORR_ADVANCE)
+ offset += 8;
+ continue;
+ }
mem = gen_frame_mem (mode, plus_constant (base, offset));
- if (action == SORR_SAVE)
+ if (action_true == SORR_SAVE)
{
insn = emit_move_insn (mem, gen_rtx_REG (mode, regno));
RTX_FRAME_RELATED_P (insn) = 1;
+ if (mode == DImode)
+ {
+ rtx set1, set2;
+ mem = gen_frame_mem (SImode, plus_constant (base, offset));
+ set1 = gen_rtx_SET (VOIDmode, mem,
+ gen_rtx_REG (SImode, regno));
+ RTX_FRAME_RELATED_P (set1) = 1;
+ mem
+ = gen_frame_mem (SImode, plus_constant (base, offset + 4));
+ set2 = gen_rtx_SET (VOIDmode, mem,
+ gen_rtx_REG (SImode, regno + 1));
+ RTX_FRAME_RELATED_P (set2) = 1;
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR,
+ gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, set1, set2)));
+ }
}
- else /* action == SORR_RESTORE */
+ else /* action_true == SORR_RESTORE */
emit_move_insn (gen_rtx_REG (mode, regno), mem);
/* Always preserve double-word alignment. */
@@ -4418,36 +4554,54 @@ save_or_restore_regs (int low, int high, rtx base, int offset, int action)
return offset;
}
-/* Emit code to save call-saved registers. */
+/* Emit code to adjust BASE to OFFSET. Return the new base. */
+
+static rtx
+emit_adjust_base_to_offset (rtx base, int offset)
+{
+ /* ??? This might be optimized a little as %g1 might already have a
+ value close enough that a single add insn will do. */
+ /* ??? Although, all of this is probably only a temporary fix because
+ if %g1 can hold a function result, then sparc_expand_epilogue will
+ lose (the result will be clobbered). */
+ rtx new_base = gen_rtx_REG (Pmode, 1);
+ emit_move_insn (new_base, GEN_INT (offset));
+ emit_insn (gen_rtx_SET (VOIDmode,
+ new_base, gen_rtx_PLUS (Pmode, base, new_base)));
+ return new_base;
+}
+
+/* Emit code to save/restore call-saved global and FP registers. */
static void
-emit_save_or_restore_regs (int action)
+emit_save_or_restore_global_fp_regs (rtx base, int offset, sorr_act_t action)
{
- HOST_WIDE_INT offset;
- rtx base;
+ if (offset < -4096 || offset + sparc_n_global_fp_regs * 4 > 4095)
+ {
+ base = emit_adjust_base_to_offset (base, offset);
+ offset = 0;
+ }
- offset = frame_base_offset - apparent_fsize;
+ offset
+ = emit_save_or_restore_regs (0, 8, base, offset, 0,
+ save_global_or_fp_reg_p, action, SORR_NONE);
+ emit_save_or_restore_regs (32, TARGET_V9 ? 96 : 64, base, offset, 0,
+ save_global_or_fp_reg_p, action, SORR_NONE);
+}
- if (offset < -4096 || offset + num_gfregs * 4 > 4095)
+/* Emit code to save/restore call-saved local and in registers. */
+
+static void
+emit_save_or_restore_local_in_regs (rtx base, int offset, sorr_act_t action)
+{
+ if (offset < -4096 || offset + 16 * UNITS_PER_WORD > 4095)
{
- /* ??? This might be optimized a little as %g1 might already have a
- value close enough that a single add insn will do. */
- /* ??? Although, all of this is probably only a temporary fix
- because if %g1 can hold a function result, then
- sparc_expand_epilogue will lose (the result will be
- clobbered). */
- base = gen_rtx_REG (Pmode, 1);
- emit_move_insn (base, GEN_INT (offset));
- emit_insn (gen_rtx_SET (VOIDmode,
- base,
- gen_rtx_PLUS (Pmode, frame_base_reg, base)));
+ base = emit_adjust_base_to_offset (base, offset);
offset = 0;
}
- else
- base = frame_base_reg;
- offset = save_or_restore_regs (0, 8, base, offset, action);
- save_or_restore_regs (32, TARGET_V9 ? 96 : 64, base, offset, action);
+ emit_save_or_restore_regs (16, 32, base, offset, sparc_leaf_function_p,
+ save_local_or_in_reg_p, action, SORR_ADVANCE);
}
/* Generate a save_register_window insn. */
@@ -4461,6 +4615,39 @@ gen_save_register_window (rtx increment)
return gen_save_register_windowsi (increment);
}
+/* Generate a create_flat_frame_1 insn. */
+
+static rtx
+gen_create_flat_frame_1 (rtx increment)
+{
+ if (TARGET_ARCH64)
+ return gen_create_flat_frame_1di (increment);
+ else
+ return gen_create_flat_frame_1si (increment);
+}
+
+/* Generate a create_flat_frame_2 insn. */
+
+static rtx
+gen_create_flat_frame_2 (rtx increment)
+{
+ if (TARGET_ARCH64)
+ return gen_create_flat_frame_2di (increment);
+ else
+ return gen_create_flat_frame_2si (increment);
+}
+
+/* Generate a create_flat_frame_3 insn. */
+
+static rtx
+gen_create_flat_frame_3 (rtx increment)
+{
+ if (TARGET_ARCH64)
+ return gen_create_flat_frame_3di (increment);
+ else
+ return gen_create_flat_frame_3si (increment);
+}
+
/* Generate an increment for the stack pointer. */
static rtx
@@ -4492,6 +4679,7 @@ gen_stack_pointer_dec (rtx decrement)
void
sparc_expand_prologue (void)
{
+ HOST_WIDE_INT size;
rtx insn;
int i;
@@ -4520,70 +4708,52 @@ sparc_expand_prologue (void)
sparc_leaf_function_p
= optimize > 0 && current_function_is_leaf && only_leaf_regs_used ();
- /* Need to use actual_fsize, since we are also allocating
- space for our callee (and our own register save area). */
- actual_fsize
- = sparc_compute_frame_size (get_frame_size(), sparc_leaf_function_p);
-
- /* Advertise that the data calculated just above are now valid. */
- sparc_prologue_data_valid_p = true;
+ size = sparc_compute_frame_size (get_frame_size(), sparc_leaf_function_p);
if (flag_stack_usage_info)
- current_function_static_stack_size = actual_fsize;
+ current_function_static_stack_size = size;
- if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK && actual_fsize)
- sparc_emit_probe_stack_range (STACK_CHECK_PROTECT, actual_fsize);
+ if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK && size)
+ sparc_emit_probe_stack_range (STACK_CHECK_PROTECT, size);
- if (sparc_leaf_function_p)
- {
- frame_base_reg = stack_pointer_rtx;
- frame_base_offset = actual_fsize + SPARC_STACK_BIAS;
- }
- else
- {
- frame_base_reg = hard_frame_pointer_rtx;
- frame_base_offset = SPARC_STACK_BIAS;
- }
-
- if (actual_fsize == 0)
- /* do nothing. */ ;
+ if (size == 0)
+ ; /* do nothing. */
else if (sparc_leaf_function_p)
{
- if (actual_fsize <= 4096)
- insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-actual_fsize)));
- else if (actual_fsize <= 8192)
+ if (size <= 4096)
+ insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-size)));
+ else if (size <= 8192)
{
insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-4096)));
/* %sp is still the CFA register. */
RTX_FRAME_RELATED_P (insn) = 1;
- insn
- = emit_insn (gen_stack_pointer_inc (GEN_INT (4096-actual_fsize)));
+ insn = emit_insn (gen_stack_pointer_inc (GEN_INT (4096 - size)));
}
else
{
rtx reg = gen_rtx_REG (Pmode, 1);
- emit_move_insn (reg, GEN_INT (-actual_fsize));
+ emit_move_insn (reg, GEN_INT (-size));
insn = emit_insn (gen_stack_pointer_inc (reg));
add_reg_note (insn, REG_FRAME_RELATED_EXPR,
- gen_stack_pointer_inc (GEN_INT (-actual_fsize)));
+ gen_stack_pointer_inc (GEN_INT (-size)));
}
RTX_FRAME_RELATED_P (insn) = 1;
}
else
{
- if (actual_fsize <= 4096)
- insn = emit_insn (gen_save_register_window (GEN_INT (-actual_fsize)));
- else if (actual_fsize <= 8192)
+ if (size <= 4096)
+ insn = emit_insn (gen_save_register_window (GEN_INT (-size)));
+ else if (size <= 8192)
{
insn = emit_insn (gen_save_register_window (GEN_INT (-4096)));
/* %sp is not the CFA register anymore. */
- emit_insn (gen_stack_pointer_inc (GEN_INT (4096-actual_fsize)));
+ emit_insn (gen_stack_pointer_inc (GEN_INT (4096 - size)));
}
else
{
rtx reg = gen_rtx_REG (Pmode, 1);
- emit_move_insn (reg, GEN_INT (-actual_fsize));
+ emit_move_insn (reg, GEN_INT (-size));
insn = emit_insn (gen_save_register_window (reg));
}
@@ -4592,12 +4762,179 @@ sparc_expand_prologue (void)
RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, i)) = 1;
}
- if (num_gfregs)
- emit_save_or_restore_regs (SORR_SAVE);
+ if (sparc_leaf_function_p)
+ {
+ sparc_frame_base_reg = stack_pointer_rtx;
+ sparc_frame_base_offset = size + SPARC_STACK_BIAS;
+ }
+ else
+ {
+ sparc_frame_base_reg = hard_frame_pointer_rtx;
+ sparc_frame_base_offset = SPARC_STACK_BIAS;
+ }
+
+ if (sparc_n_global_fp_regs > 0)
+ emit_save_or_restore_global_fp_regs (sparc_frame_base_reg,
+ sparc_frame_base_offset
+ - sparc_apparent_frame_size,
+ SORR_SAVE);
/* Load the GOT register if needed. */
if (crtl->uses_pic_offset_table)
load_got_register ();
+
+ /* Advertise that the data calculated just above are now valid. */
+ sparc_prologue_data_valid_p = true;
+}
+
+/* Expand the function prologue. The prologue is responsible for reserving
+ storage for the frame, saving the call-saved registers and loading the
+ GOT register if needed. */
+
+void
+sparc_flat_expand_prologue (void)
+{
+ HOST_WIDE_INT size;
+ rtx insn;
+ int i;
+
+ sparc_leaf_function_p = optimize > 0 && current_function_is_leaf;
+
+ size = sparc_compute_frame_size (get_frame_size(), sparc_leaf_function_p);
+
+ if (flag_stack_usage_info)
+ current_function_static_stack_size = size;
+
+ if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK && size)
+ sparc_emit_probe_stack_range (STACK_CHECK_PROTECT, size);
+
+ if (sparc_save_local_in_regs_p)
+ emit_save_or_restore_local_in_regs (stack_pointer_rtx, SPARC_STACK_BIAS,
+ SORR_SAVE);
+
+ if (size == 0)
+ ; /* do nothing. */
+ else if (frame_pointer_needed)
+ {
+ if (size <= 4096)
+ {
+ if (return_addr_reg_needed_p (sparc_leaf_function_p))
+ insn = emit_insn (gen_create_flat_frame_1 (GEN_INT (-size)));
+ else
+ insn = emit_insn (gen_create_flat_frame_2 (GEN_INT (-size)));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ for (i=0; i < XVECLEN (PATTERN (insn), 0); i++)
+ RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, i)) = 1;
+ }
+ else
+ {
+ rtx reg = gen_rtx_REG (Pmode, 1), note;
+ emit_move_insn (reg, GEN_INT (-size));
+ if (return_addr_reg_needed_p (sparc_leaf_function_p))
+ {
+ insn = emit_insn (gen_create_flat_frame_1 (reg));
+ note
+ = gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec
+ (3, copy_rtx
+ (XVECEXP (PATTERN (insn), 0, 0)),
+ gen_stack_pointer_inc
+ (GEN_INT (-size)),
+ copy_rtx
+ (XVECEXP (PATTERN (insn), 0, 2))));
+ }
+ else
+ {
+ insn = emit_insn (gen_create_flat_frame_2 (reg));
+ note
+ = gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec
+ (2, copy_rtx
+ (XVECEXP (PATTERN (insn), 0, 0)),
+ gen_stack_pointer_inc
+ (GEN_INT (-size))));
+ }
+
+ RTX_FRAME_RELATED_P (insn) = 1;
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, note);
+ for (i=0; i < XVECLEN (note, 0); i++)
+ RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1;
+ }
+ }
+ else if (return_addr_reg_needed_p (sparc_leaf_function_p))
+ {
+ if (size <= 4096)
+ {
+ insn = emit_insn (gen_create_flat_frame_3 (GEN_INT (-size)));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ for (i=0; i < XVECLEN (PATTERN (insn), 0); i++)
+ RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, i)) = 1;
+ }
+ else
+ {
+ rtx reg = gen_rtx_REG (Pmode, 1), note;
+ emit_move_insn (reg, GEN_INT (-size));
+ insn = emit_insn (gen_create_flat_frame_3 (reg));
+ note
+ = gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec
+ (2, gen_stack_pointer_inc (GEN_INT (-size)),
+ copy_rtx
+ (XVECEXP (PATTERN (insn), 0, 1))));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, note);
+ for (i=0; i < XVECLEN (note, 0); i++)
+ RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1;
+ }
+ }
+ else
+ {
+ if (size <= 4096)
+ insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-size)));
+ else if (size <= 8192)
+ {
+ insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-4096)));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ insn = emit_insn (gen_stack_pointer_inc (GEN_INT (4096 - size)));
+ }
+ else
+ {
+ rtx reg = gen_rtx_REG (Pmode, 1);
+ emit_move_insn (reg, GEN_INT (-size));
+ insn = emit_insn (gen_stack_pointer_inc (reg));
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR,
+ gen_stack_pointer_inc (GEN_INT (-size)));
+ }
+
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
+ /* Make sure nothing is scheduled until after the frame is established. */
+ emit_insn (gen_blockage ());
+
+ if (frame_pointer_needed)
+ {
+ sparc_frame_base_reg = hard_frame_pointer_rtx;
+ sparc_frame_base_offset = SPARC_STACK_BIAS;
+ }
+ else
+ {
+ sparc_frame_base_reg = stack_pointer_rtx;
+ sparc_frame_base_offset = size + SPARC_STACK_BIAS;
+ }
+
+ if (sparc_n_global_fp_regs > 0)
+ emit_save_or_restore_global_fp_regs (sparc_frame_base_reg,
+ sparc_frame_base_offset
+ - sparc_apparent_frame_size,
+ SORR_SAVE);
+
+ /* Load the GOT register if needed. */
+ if (crtl->uses_pic_offset_table)
+ load_got_register ();
+
+ /* Advertise that the data calculated just above are now valid. */
+ sparc_prologue_data_valid_p = true;
}
/* This function generates the assembly code for function entry, which boils
@@ -4607,7 +4944,8 @@ static void
sparc_asm_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
/* Check that the assumption we made in sparc_expand_prologue is valid. */
- gcc_assert (sparc_leaf_function_p == current_function_uses_only_leaf_regs);
+ if (!TARGET_FLAT)
+ gcc_assert (sparc_leaf_function_p == current_function_uses_only_leaf_regs);
sparc_output_scratch_registers (file);
}
@@ -4616,26 +4954,91 @@ sparc_asm_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
We emit all the instructions except the return or the call. */
void
-sparc_expand_epilogue (void)
+sparc_expand_epilogue (bool for_eh)
{
- if (num_gfregs)
- emit_save_or_restore_regs (SORR_RESTORE);
+ HOST_WIDE_INT size = sparc_frame_size;
+
+ if (sparc_n_global_fp_regs > 0)
+ emit_save_or_restore_global_fp_regs (sparc_frame_base_reg,
+ sparc_frame_base_offset
+ - sparc_apparent_frame_size,
+ SORR_RESTORE);
- if (actual_fsize == 0)
- /* do nothing. */ ;
+ if (size == 0 || for_eh)
+ ; /* do nothing. */
else if (sparc_leaf_function_p)
{
- if (actual_fsize <= 4096)
- emit_insn (gen_stack_pointer_dec (GEN_INT (- actual_fsize)));
- else if (actual_fsize <= 8192)
+ if (size <= 4096)
+ emit_insn (gen_stack_pointer_dec (GEN_INT (-size)));
+ else if (size <= 8192)
{
emit_insn (gen_stack_pointer_dec (GEN_INT (-4096)));
- emit_insn (gen_stack_pointer_dec (GEN_INT (4096 - actual_fsize)));
+ emit_insn (gen_stack_pointer_dec (GEN_INT (4096 - size)));
}
else
{
rtx reg = gen_rtx_REG (Pmode, 1);
- emit_move_insn (reg, GEN_INT (-actual_fsize));
+ emit_move_insn (reg, GEN_INT (-size));
+ emit_insn (gen_stack_pointer_dec (reg));
+ }
+ }
+}
+
+/* Expand the function epilogue, either normal or part of a sibcall.
+ We emit all the instructions except the return or the call. */
+
+void
+sparc_flat_expand_epilogue (bool for_eh)
+{
+ HOST_WIDE_INT size = sparc_frame_size;
+
+ if (sparc_n_global_fp_regs > 0)
+ emit_save_or_restore_global_fp_regs (sparc_frame_base_reg,
+ sparc_frame_base_offset
+ - sparc_apparent_frame_size,
+ SORR_RESTORE);
+
+ /* If we have a frame pointer, we'll need both to restore it before the
+ frame is destroyed and use its current value in destroying the frame.
+ Since we don't have an atomic way to do that in the flat window model,
+ we save the current value into a temporary register (%g1). */
+ if (frame_pointer_needed && !for_eh)
+ emit_move_insn (gen_rtx_REG (Pmode, 1), hard_frame_pointer_rtx);
+
+ if (return_addr_reg_needed_p (sparc_leaf_function_p))
+ emit_move_insn (gen_rtx_REG (Pmode, INCOMING_RETURN_ADDR_REGNUM),
+ gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM));
+
+ if (sparc_save_local_in_regs_p)
+ emit_save_or_restore_local_in_regs (sparc_frame_base_reg,
+ sparc_frame_base_offset,
+ SORR_RESTORE);
+
+ if (size == 0 || for_eh)
+ ; /* do nothing. */
+ else if (frame_pointer_needed)
+ {
+ /* Make sure the frame is destroyed after everything else is done. */
+ emit_insn (gen_blockage ());
+
+ emit_move_insn (stack_pointer_rtx, gen_rtx_REG (Pmode, 1));
+ }
+ else
+ {
+ /* Likewise. */
+ emit_insn (gen_blockage ());
+
+ if (size <= 4096)
+ emit_insn (gen_stack_pointer_dec (GEN_INT (-size)));
+ else if (size <= 8192)
+ {
+ emit_insn (gen_stack_pointer_dec (GEN_INT (-4096)));
+ emit_insn (gen_stack_pointer_dec (GEN_INT (4096 - size)));
+ }
+ else
+ {
+ rtx reg = gen_rtx_REG (Pmode, 1);
+ emit_move_insn (reg, GEN_INT (-size));
emit_insn (gen_stack_pointer_dec (reg));
}
}
@@ -4648,8 +5051,10 @@ bool
sparc_can_use_return_insn_p (void)
{
return sparc_prologue_data_valid_p
- && num_gfregs == 0
- && (actual_fsize == 0 || !sparc_leaf_function_p);
+ && sparc_n_global_fp_regs == 0
+ && TARGET_FLAT
+ ? (sparc_frame_size == 0 && !sparc_save_local_in_regs_p)
+ : (sparc_frame_size == 0 || !sparc_leaf_function_p);
}
/* This function generates the assembly code for function exit. */
@@ -4728,15 +5133,42 @@ output_restore (rtx pat)
const char *
output_return (rtx insn)
{
- if (sparc_leaf_function_p)
+ if (crtl->calls_eh_return)
+ {
+ /* If the function uses __builtin_eh_return, the eh_return
+ machinery occupies the delay slot. */
+ gcc_assert (!final_sequence);
+
+ if (flag_delayed_branch)
+ {
+ if (!TARGET_FLAT && TARGET_V9)
+ fputs ("\treturn\t%i7+8\n", asm_out_file);
+ else
+ {
+ if (!TARGET_FLAT)
+ fputs ("\trestore\n", asm_out_file);
+
+ fputs ("\tjmp\t%o7+8\n", asm_out_file);
+ }
+
+ fputs ("\t add\t%sp, %g1, %sp\n", asm_out_file);
+ }
+ else
+ {
+ if (!TARGET_FLAT)
+ fputs ("\trestore\n", asm_out_file);
+
+ fputs ("\tadd\t%sp, %g1, %sp\n", asm_out_file);
+ fputs ("\tjmp\t%o7+8\n\t nop\n", asm_out_file);
+ }
+ }
+ else if (sparc_leaf_function_p || TARGET_FLAT)
{
- /* This is a leaf function so we don't have to bother restoring the
- register window, which frees us from dealing with the convoluted
+ /* This is a leaf or flat function so we don't have to bother restoring
+ the register window, which frees us from dealing with the convoluted
semantics of restore/return. We simply output the jump to the
return address and the insn in the delay slot (if any). */
- gcc_assert (! crtl->calls_eh_return);
-
return "jmp\t%%o7+%)%#";
}
else
@@ -4746,28 +5178,7 @@ output_return (rtx insn)
combined with the 'restore' instruction or put in the delay slot of
the 'return' instruction. */
- if (crtl->calls_eh_return)
- {
- /* If the function uses __builtin_eh_return, the eh_return
- machinery occupies the delay slot. */
- gcc_assert (! final_sequence);
-
- if (flag_delayed_branch)
- {
- if (TARGET_V9)
- fputs ("\treturn\t%i7+8\n", asm_out_file);
- else
- fputs ("\trestore\n\tjmp\t%o7+8\n", asm_out_file);
-
- fputs ("\t add\t%sp, %g1, %sp\n", asm_out_file);
- }
- else
- {
- fputs ("\trestore\n\tadd\t%sp, %g1, %sp\n", asm_out_file);
- fputs ("\tjmp\t%o7+8\n\t nop\n", asm_out_file);
- }
- }
- else if (final_sequence)
+ if (final_sequence)
{
rtx delay, pat;
@@ -4815,10 +5226,10 @@ output_sibcall (rtx insn, rtx call_operand)
operands[0] = call_operand;
- if (sparc_leaf_function_p)
+ if (sparc_leaf_function_p || TARGET_FLAT)
{
- /* This is a leaf function so we don't have to bother restoring the
- register window. We simply output the jump to the function and
+ /* This is a leaf or flat function so we don't have to bother restoring
+ the register window. We simply output the jump to the function and
the insn in the delay slot (if any). */
gcc_assert (!(LEAF_SIBCALL_SLOT_RESERVED_P && final_sequence));
@@ -9352,7 +9763,13 @@ sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
emit_note (NOTE_INSN_PROLOGUE_END);
- if (flag_delayed_branch)
+ if (TARGET_FLAT)
+ {
+ sparc_leaf_function_p = 1;
+
+ int_arg_first = SPARC_OUTGOING_INT_ARG_FIRST;
+ }
+ else if (flag_delayed_branch)
{
/* We will emit a regular sibcall below, so we need to instruct
output_sibcall that we are in a leaf function. */
@@ -9473,8 +9890,6 @@ sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
{
spill_reg = gen_rtx_REG (word_mode, 15); /* %o7 */
start_sequence ();
- /* Delay emitting the GOT helper function because it needs to
- change the section and we are emitting assembly code. */
load_got_register (); /* clobbers %o7 */
scratch = sparc_legitimize_pic_address (funexp, scratch);
seq = get_insns ();
@@ -9593,8 +10008,10 @@ get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
}
/* Handle the TARGET_DWARF_HANDLE_FRAME_UNSPEC hook.
+
This is called from dwarf2out.c to emit call frame instructions
- for frame-related insns containing UNSPECs and UNSPEC_VOLATILEs. */
+ for frame-related insns containing UNSPECs and UNSPEC_VOLATILEs. */
+
static void
sparc_dwarf_handle_frame_unspec (const char *label,
rtx pattern ATTRIBUTE_UNUSED,
@@ -9810,6 +10227,16 @@ sparc_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
static bool
sparc_frame_pointer_required (void)
{
+ /* If the stack pointer is dynamically modified in the function, it cannot
+ serve as the frame pointer. */
+ if (cfun->calls_alloca)
+ return true;
+
+ /* In flat mode, that's it. */
+ if (TARGET_FLAT)
+ return false;
+
+ /* Otherwise, the frame pointer is required if the function isn't leaf. */
return !(current_function_is_leaf && only_leaf_regs_used ());
}
@@ -9880,6 +10307,14 @@ sparc_conditional_register_usage (void)
fixed_regs[4] = 1;
else if (fixed_regs[4] == 2)
fixed_regs[4] = 0;
+ if (TARGET_FLAT)
+ {
+ int regno;
+ /* Disable leaf functions. */
+ memset (sparc_leaf_regs, 0, FIRST_PSEUDO_REGISTER);
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ leaf_reg_remap [regno] = regno;
+ }
}
/* Implement TARGET_PREFERRED_RELOAD_CLASS
diff --git a/gcc/config/sparc/sparc.h b/gcc/config/sparc/sparc.h
index 4f9ef3a..8f3ace3 100644
--- a/gcc/config/sparc/sparc.h
+++ b/gcc/config/sparc/sparc.h
@@ -360,7 +360,6 @@ extern enum cmodel sparc_cmodel;
/* Common CPP definitions used by CPP_SPEC amongst the various targets
for handling -mcpu=xxx switches. */
#define CPP_CPU_SPEC "\
-%{msoft-float:-D_SOFT_FLOAT} \
%{mcpu=sparclet:-D__sparclet__} %{mcpu=tsc701:-D__sparclet__} \
%{mcpu=sparclite:-D__sparclite__} \
%{mcpu=f930:-D__sparclite__} %{mcpu=f934:-D__sparclite__} \
@@ -388,14 +387,18 @@ extern enum cmodel sparc_cmodel;
%{!m32:%{!m64:%(cpp_arch_default)}} \
"
-/* Macro to distinguish endianness. */
-#define CPP_ENDIAN_SPEC "\
-%{mlittle-endian:-D__LITTLE_ENDIAN__}"
+/* Macros to distinguish the endianness, window model and FP support. */
+#define CPP_OTHER_SPEC "\
+%{mlittle-endian:-D__LITTLE_ENDIAN__} \
+%{mflat:-D_FLAT} \
+%{msoft-float:-D_SOFT_FLOAT} \
+"
/* Macros to distinguish the particular subtarget. */
#define CPP_SUBTARGET_SPEC ""
-#define CPP_SPEC "%(cpp_cpu) %(cpp_arch) %(cpp_endian) %(cpp_subtarget)"
+#define CPP_SPEC \
+ "%(cpp_cpu) %(cpp_arch) %(cpp_endian) %(cpp_other) %(cpp_subtarget)"
/* This used to translate -dalign to -malign, but that is no good
because it can't turn off the usual meaning of making debugging dumps. */
@@ -464,7 +467,7 @@ extern enum cmodel sparc_cmodel;
{ "cpp_arch64", CPP_ARCH64_SPEC }, \
{ "cpp_arch_default", CPP_ARCH_DEFAULT_SPEC },\
{ "cpp_arch", CPP_ARCH_SPEC }, \
- { "cpp_endian", CPP_ENDIAN_SPEC }, \
+ { "cpp_other", CPP_OTHER_SPEC }, \
{ "cpp_subtarget", CPP_SUBTARGET_SPEC }, \
{ "asm_cpu", ASM_CPU_SPEC }, \
{ "asm_cpu_default", ASM_CPU_DEFAULT_SPEC }, \
@@ -687,7 +690,7 @@ extern enum cmodel sparc_cmodel;
/* Argument passing regs. */
#define SPARC_OUTGOING_INT_ARG_FIRST 8
-#define SPARC_INCOMING_INT_ARG_FIRST 24
+#define SPARC_INCOMING_INT_ARG_FIRST (TARGET_FLAT ? 8 : 24)
#define SPARC_FP_ARG_FIRST 32
/* 1 for registers that have pervasive standard uses
@@ -721,7 +724,7 @@ extern enum cmodel sparc_cmodel;
{1, 0, 2, 2, 2, 2, 1, 1, \
0, 0, 0, 0, 0, 0, 1, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 1, 1, \
+ 0, 0, 0, 0, 0, 0, 0, 1, \
\
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
@@ -746,7 +749,7 @@ extern enum cmodel sparc_cmodel;
{1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, \
0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 1, 1, \
+ 0, 0, 0, 0, 0, 0, 0, 1, \
\
1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, \
@@ -1223,13 +1226,11 @@ extern char leaf_reg_remap[];
{{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM} }
-/* We always pretend that this is a leaf function because if it's not,
- there's no point in trying to eliminate the frame pointer. If it
- is a leaf function, we guessed right! */
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
do { \
if ((TO) == STACK_POINTER_REGNUM) \
- (OFFSET) = sparc_compute_frame_size (get_frame_size (), 1); \
+ (OFFSET) = sparc_compute_frame_size (get_frame_size (), \
+ current_function_is_leaf); \
else \
(OFFSET) = 0; \
(OFFSET) += SPARC_STACK_BIAS; \
@@ -1247,7 +1248,7 @@ extern char leaf_reg_remap[];
Return OUT if register number OUT is not an outbound register. */
#define INCOMING_REGNO(OUT) \
- (((OUT) < 8 || (OUT) > 15) ? (OUT) : (OUT) + 16)
+ ((TARGET_FLAT || (OUT) < 8 || (OUT) > 15) ? (OUT) : (OUT) + 16)
/* Define this macro if the target machine has "register windows". This
C expression returns the register number as seen by the calling function
@@ -1255,14 +1256,14 @@ extern char leaf_reg_remap[];
Return IN if register number IN is not an inbound register. */
#define OUTGOING_REGNO(IN) \
- (((IN) < 24 || (IN) > 31) ? (IN) : (IN) - 16)
+ ((TARGET_FLAT || (IN) < 24 || (IN) > 31) ? (IN) : (IN) - 16)
/* Define this macro if the target machine has register windows. This
C expression returns true if the register is call-saved but is in the
register window. */
#define LOCAL_REGNO(REGNO) \
- ((REGNO) >= 16 && (REGNO) <= 31)
+ (!TARGET_FLAT && (REGNO) >= 16 && (REGNO) <= 31)
/* Define the size of space to allocate for the return value of an
untyped_call. */
@@ -1373,35 +1374,27 @@ do { \
/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
the stack pointer does not matter. The value is tested only in
- functions that have frame pointers.
- No definition is equivalent to always zero. */
+ functions that have frame pointers. */
+#define EXIT_IGNORE_STACK 1
-#define EXIT_IGNORE_STACK \
- (get_frame_size () != 0 \
- || cfun->calls_alloca || crtl->outgoing_args_size)
-
-/* Define registers used by the epilogue and return instruction. */
-#define EPILOGUE_USES(REGNO) ((REGNO) == 31 \
- || (crtl->calls_eh_return && (REGNO) == 1))
-
/* We need 2 words, so we can save the stack pointer and the return register
of the function containing a non-local goto target. */
-
#define STACK_SAVEAREA_MODE(LEVEL) \
((LEVEL) == SAVE_NONLOCAL ? (TARGET_ARCH64 ? TImode : DImode) : Pmode)
/* Length in units of the trampoline for entering a nested function. */
-
#define TRAMPOLINE_SIZE (TARGET_ARCH64 ? 32 : 16)
/* Alignment required for trampolines, in bits. */
-
#define TRAMPOLINE_ALIGNMENT 128
/* Generate RTL to flush the register windows so as to make arbitrary frames
available. */
-#define SETUP_FRAME_ADDRESSES() \
- emit_insn (gen_flush_register_windows ())
+#define SETUP_FRAME_ADDRESSES() \
+ do { \
+ if (!TARGET_FLAT) \
+ emit_insn (gen_flush_register_windows ());\
+ } while (0)
/* Given an rtx for the address of a frame,
return an rtx for the address of the word in the frame
@@ -1428,9 +1421,10 @@ do { \
farther back is in the register window save area at [%fp+60]. */
/* ??? This ignores the fact that the actual return address is +8 for normal
returns, and +12 for structure returns. */
+#define RETURN_ADDR_REGNUM 31
#define RETURN_ADDR_RTX(count, frame) \
((count == -1) \
- ? gen_rtx_REG (Pmode, 31) \
+ ? gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM) \
: gen_rtx_MEM (Pmode, \
memory_address (Pmode, plus_constant (frame, \
15 * UNITS_PER_WORD \
@@ -1440,9 +1434,11 @@ do { \
+12, but always using +8 is close enough for frame unwind purposes.
Actually, just using %o7 is close enough for unwinding, but %o7+8
is something you can return to. */
+#define INCOMING_RETURN_ADDR_REGNUM 15
#define INCOMING_RETURN_ADDR_RTX \
- plus_constant (gen_rtx_REG (word_mode, 15), 8)
-#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (15)
+ plus_constant (gen_rtx_REG (word_mode, INCOMING_RETURN_ADDR_REGNUM), 8)
+#define DWARF_FRAME_RETURN_COLUMN \
+ DWARF_FRAME_REGNUM (INCOMING_RETURN_ADDR_REGNUM)
/* The offset from the incoming value of %sp to the top of the stack frame
for the current function. On sparc64, we have to account for the stack
@@ -1450,9 +1446,17 @@ do { \
#define INCOMING_FRAME_SP_OFFSET SPARC_STACK_BIAS
/* Describe how we implement __builtin_eh_return. */
+#define EH_RETURN_REGNUM 1
#define EH_RETURN_DATA_REGNO(N) ((N) < 4 ? (N) + 24 : INVALID_REGNUM)
-#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, 1) /* %g1 */
-#define EH_RETURN_HANDLER_RTX gen_rtx_REG (Pmode, 31) /* %i7 */
+#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, EH_RETURN_REGNUM)
+
+/* Define registers used by the epilogue and return instruction. */
+#define EPILOGUE_USES(REGNO) \
+ ((REGNO) == RETURN_ADDR_REGNUM \
+ || (TARGET_FLAT \
+ && epilogue_completed \
+ && (REGNO) == INCOMING_RETURN_ADDR_REGNUM) \
+ || (crtl->calls_eh_return && (REGNO) == EH_RETURN_REGNUM))
/* Select a format to encode pointers in exception handling data. CODE
is 0 for data, 1 for code labels, 2 for function pointers. GLOBAL is
diff --git a/gcc/config/sparc/sparc.md b/gcc/config/sparc/sparc.md
index aa9f9fb..acf317d 100644
--- a/gcc/config/sparc/sparc.md
+++ b/gcc/config/sparc/sparc.md
@@ -164,7 +164,7 @@
(define_attr "calls_eh_return" "false,true"
(symbol_ref "(crtl->calls_eh_return != 0
? CALLS_EH_RETURN_TRUE : CALLS_EH_RETURN_FALSE)"))
-
+
(define_attr "leaf_function" "false,true"
(symbol_ref "(current_function_uses_only_leaf_regs != 0
? LEAF_FUNCTION_TRUE : LEAF_FUNCTION_FALSE)"))
@@ -173,6 +173,10 @@
(symbol_ref "(flag_delayed_branch != 0
? DELAYED_BRANCH_TRUE : DELAYED_BRANCH_FALSE)"))
+(define_attr "flat" "false,true"
+ (symbol_ref "(TARGET_FLAT != 0
+ ? FLAT_TRUE : FLAT_FALSE)"))
+
;; Length (in # of insns).
;; Beware that setting a length greater or equal to 3 for conditional branches
;; has a side-effect (see output_cbranch and output_v9branch).
@@ -6265,7 +6269,10 @@
[(const_int 0)]
""
{
- sparc_expand_prologue ();
+ if (TARGET_FLAT)
+ sparc_flat_expand_prologue ();
+ else
+ sparc_expand_prologue ();
DONE;
})
@@ -6282,25 +6289,87 @@
(set (reg:P 14) (unspec_volatile:P [(reg:P 14)
(match_operand:P 0 "arith_operand" "rI")] UNSPECV_SAVEW))
(set (reg:P 31) (reg:P 15))]
- ""
+ "!TARGET_FLAT"
"save\t%%sp, %0, %%sp"
[(set_attr "type" "savew")])
+;; Likewise for the "create flat frame" insns. We need to use special insns
+;; because %fp cannot be clobbered until after the frame is established (so
+;; that it contains the live register window save area) and %i7 changed with
+;; a simple move as it is a fixed register and the move would be eliminated.
+
+(define_insn "create_flat_frame_1<P:mode>"
+ [(set (reg:P 30) (reg:P 14))
+ (set (reg:P 14) (plus:P (reg:P 14)
+ (match_operand:P 0 "arith_operand" "rI")))
+ (set (reg:P 31) (reg:P 15))]
+ "TARGET_FLAT"
+ "add\t%%sp, %0, %%sp\n\tsub\t%%sp, %0, %%fp\n\tmov\t%%o7, %%i7"
+ [(set_attr "type" "multi")
+ (set_attr "length" "3")])
+
+(define_insn "create_flat_frame_2<P:mode>"
+ [(set (reg:P 30) (reg:P 14))
+ (set (reg:P 14) (plus:P (reg:P 14)
+ (match_operand:P 0 "arith_operand" "rI")))]
+ "TARGET_FLAT"
+ "add\t%%sp, %0, %%sp\n\tsub\t%%sp, %0, %%fp"
+ [(set_attr "type" "multi")
+ (set_attr "length" "2")])
+
+(define_insn "create_flat_frame_3<P:mode>"
+ [(set (reg:P 14) (plus:P (reg:P 14)
+ (match_operand:P 0 "arith_operand" "rI")))
+ (set (reg:P 31) (reg:P 15))]
+ "TARGET_FLAT"
+ "add\t%%sp, %0, %%sp\n\tmov\t%%o7, %%i7"
+ [(set_attr "type" "multi")
+ (set_attr "length" "2")])
+
(define_expand "epilogue"
[(return)]
""
{
- sparc_expand_epilogue ();
+ if (TARGET_FLAT)
+ sparc_flat_expand_epilogue (false);
+ else
+ sparc_expand_epilogue (false);
})
(define_expand "sibcall_epilogue"
[(return)]
""
{
- sparc_expand_epilogue ();
+ if (TARGET_FLAT)
+ sparc_flat_expand_epilogue (false);
+ else
+ sparc_expand_epilogue (false);
+ DONE;
+})
+
+(define_expand "eh_return"
+ [(use (match_operand 0 "general_operand" ""))]
+ ""
+{
+ emit_move_insn (gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM), operands[0]);
+ emit_jump_insn (gen_eh_return_internal ());
+ emit_barrier ();
DONE;
})
+(define_insn_and_split "eh_return_internal"
+ [(eh_return)]
+ ""
+ "#"
+ "epilogue_completed"
+ [(return)]
+{
+ if (TARGET_FLAT)
+ sparc_flat_expand_epilogue (true);
+ else
+ sparc_expand_epilogue (true);
+})
+
(define_expand "return"
[(return)]
"sparc_can_use_return_insn_p ()"
@@ -6312,16 +6381,19 @@
"* return output_return (insn);"
[(set_attr "type" "return")
(set (attr "length")
- (cond [(eq_attr "leaf_function" "true")
+ (cond [(eq_attr "calls_eh_return" "true")
+ (if_then_else (eq_attr "delayed_branch" "true")
+ (if_then_else (ior (eq_attr "isa" "v9")
+ (eq_attr "flat" "true"))
+ (const_int 2)
+ (const_int 3))
+ (if_then_else (eq_attr "flat" "true")
+ (const_int 3)
+ (const_int 4)))
+ (ior (eq_attr "leaf_function" "true") (eq_attr "flat" "true"))
(if_then_else (eq_attr "empty_delay_slot" "true")
(const_int 2)
(const_int 1))
- (eq_attr "calls_eh_return" "true")
- (if_then_else (eq_attr "delayed_branch" "true")
- (if_then_else (eq_attr "isa" "v9")
- (const_int 2)
- (const_int 3))
- (const_int 4))
(eq_attr "empty_delay_slot" "true")
(if_then_else (eq_attr "delayed_branch" "true")
(const_int 2)
@@ -6367,8 +6439,7 @@
if (! TARGET_ARCH64)
{
- rtx rtnreg = gen_rtx_REG (SImode, (current_function_uses_only_leaf_regs
- ? 15 : 31));
+ rtx rtnreg = gen_rtx_REG (SImode, RETURN_ADDR_REGNUM);
rtx value = gen_reg_rtx (SImode);
/* Fetch the instruction where we will return to and see if it's an unimp
@@ -6449,7 +6520,7 @@
{
operands[0] = adjust_address_nv (operands[0], Pmode, 0);
operands[2] = adjust_address_nv (operands[0], Pmode, GET_MODE_SIZE (Pmode));
- operands[3] = gen_rtx_REG (Pmode, 31); /* %i7 */
+ operands[3] = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
})
(define_expand "restore_stack_nonlocal"
@@ -6474,7 +6545,8 @@
/* We need to flush all the register windows so that their contents will
be re-synchronized by the restore insn of the target function. */
- emit_insn (gen_flush_register_windows ());
+ if (!TARGET_FLAT)
+ emit_insn (gen_flush_register_windows ());
emit_clobber (gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)));
emit_clobber (gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx));
diff --git a/gcc/config/sparc/sparc.opt b/gcc/config/sparc/sparc.opt
index b7fac09..d729214 100644
--- a/gcc/config/sparc/sparc.opt
+++ b/gcc/config/sparc/sparc.opt
@@ -33,6 +33,10 @@ msoft-float
Target RejectNegative InverseMask(FPU)
Do not use hardware FP
+mflat
+Target Report Mask(FLAT)
+Use flat register window model
+
munaligned-doubles
Target Report Mask(UNALIGNED_DOUBLES)
Assume possible double misalignment
diff --git a/gcc/config/sparc/t-elf b/gcc/config/sparc/t-elf
index b1d18fd..962d000 100644
--- a/gcc/config/sparc/t-elf
+++ b/gcc/config/sparc/t-elf
@@ -32,8 +32,8 @@ fp-bit.c: $(srcdir)/config/fp-bit.c
echo '#define FLOAT' > fp-bit.c
cat $(srcdir)/config/fp-bit.c >> fp-bit.c
-MULTILIB_OPTIONS = msoft-float mcpu=v8
-MULTILIB_DIRNAMES = soft v8
+MULTILIB_OPTIONS = msoft-float mcpu=v8 mflat
+MULTILIB_DIRNAMES = soft v8 flat
MULTILIB_MATCHES = msoft-float=mno-fpu
LIBGCC = stmp-multilib
diff --git a/gcc/config/sparc/t-leon b/gcc/config/sparc/t-leon
index 6573f82..e3ab3bc 100644
--- a/gcc/config/sparc/t-leon
+++ b/gcc/config/sparc/t-leon
@@ -34,8 +34,8 @@ fp-bit.c: $(srcdir)/config/fp-bit.c
# Multilibs for LEON
# LEON is a SPARC-V8, but the AT697 implementation has a bug in the
# V8-specific instructions.
-MULTILIB_OPTIONS = mcpu=v7 msoft-float
-MULTILIB_DIRNAMES = v7 soft
+MULTILIB_OPTIONS = mcpu=v7 msoft-float mflat
+MULTILIB_DIRNAMES = v7 soft flat
MULTILIB_MATCHES = mcpu?v7=mv7 msoft-float=mno-fpu
LIBGCC = stmp-multilib
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 0069f78..fee3a2f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -863,7 +863,7 @@ See RS/6000 and PowerPC Options.
-mtune=@var{cpu-type} @gol
-mcmodel=@var{code-model} @gol
-m32 -m64 -mapp-regs -mno-app-regs @gol
--mfaster-structs -mno-faster-structs @gol
+-mfaster-structs -mno-faster-structs -mflat -mno-flat @gol
-mfpu -mno-fpu -mhard-float -msoft-float @gol
-mhard-quad-float -msoft-quad-float @gol
-mlittle-endian @gol
@@ -17043,6 +17043,19 @@ To be fully SVR4 ABI compliant at the cost of some performance loss,
specify @option{-mno-app-regs}. You should compile libraries and system
software with this option.
+@item -mflat
+@itemx -mno-flat
+@opindex mflat
+@opindex mno-flat
+With @option{-mflat}, the compiler does not generate save/restore instructions
+and uses a ``flat'' or single register window model. This model is compatible
+with the regular register window model. The local registers and the input
+registers (0--5) are still treated as ``call-saved'' registers and will be
+saved on the stack as needed.
+
+With @option{-mno-flat} (the default), the compiler generates save/restore
+instructions (except for leaf functions). This is the normal operating mode.
+
@item -mfpu
@itemx -mhard-float
@opindex mfpu
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index a80c3cd..31f5d1e 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2011-06-10 Eric Botcazou <ebotcazou@adacore.com>
+ Laurent Rougé <laurent.rouge@menta.fr>
+
+ * gcc.dg/20020503-1.c: Add back -mflat option on the SPARC.
+ * gcc.target/sparc/sparc-ret.c: Skip if -mflat is passed.
+
2011-06-10 Daniel Carrera <dcarrera@gmail.com>
* gfortran.dg/coarray/sync_1.f90: New test for
diff --git a/gcc/testsuite/gcc.dg/20020503-1.c b/gcc/testsuite/gcc.dg/20020503-1.c
index 3edc7cf..e13cf95 100644
--- a/gcc/testsuite/gcc.dg/20020503-1.c
+++ b/gcc/testsuite/gcc.dg/20020503-1.c
@@ -4,7 +4,7 @@
for leaf functions, the function was still leaf, but LEAF_REG_REMAP
returned -1 for some registers (like %o0). */
/* { dg-do compile } */
-/* { dg-options "-O2 -g" } */
+/* { dg-options "-O2 -g -mflat" { target sparc*-*-* } } */
void foo (char *a, char *b, char *c, char *d)
{
diff --git a/gcc/testsuite/gcc.target/sparc/sparc-ret.c b/gcc/testsuite/gcc.target/sparc/sparc-ret.c
index 11afc10..f58b059 100644
--- a/gcc/testsuite/gcc.target/sparc/sparc-ret.c
+++ b/gcc/testsuite/gcc.target/sparc/sparc-ret.c
@@ -1,4 +1,5 @@
/* { dg-do compile } */
+/* { dg-skip-if "no register windows" { *-*-* } { "-mflat" } { "" } } */
/* { dg-require-effective-target ilp32 } */
/* { dg-options "-mcpu=ultrasparc -O" } */
@@ -11,7 +12,7 @@ int bar (int a, int b, int c, int d, int e, int f, int g, int h)
toto (&res);
return h;
}
-/* { dg-final { global compiler_flags; if ![string match "*-m64 *" $compiler_flags] { scan-assembler "return\[ \t\]*%i7\\+8\n\[^\n\]*ld\[ \t\]*\\\[%sp\\+96\\\]" } } } */
+/* { dg-final { scan-assembler "return\[ \t\]*%i7\\+8\n\[^\n\]*ld\[ \t\]*\\\[%sp\\+96\\\]" } } */
int bar2 ()
{
@@ -20,4 +21,4 @@ int bar2 ()
toto (&res);
return res;
}
-/* { dg-final { global compiler_flags; if ![string match "*-m64 *" $compiler_flags] { scan-assembler "return\[ \t\]*%i7\\+8\n\[^\n\]*nop" } } } */
+/* { dg-final { scan-assembler "return\[ \t\]*%i7\\+8\n\[^\n\]*nop" } } */