invoke.texi (SPARC options): Add -mflat.
authorEric Botcazou <ebotcazou@adacore.com>
Fri, 10 Jun 2011 10:50:20 +0000 (10:50 +0000)
committerEric Botcazou <ebotcazou@gcc.gnu.org>
Fri, 10 Jun 2011 10:50:20 +0000 (10:50 +0000)
gcc/
* 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.
libgcc/
* config/sparc/linux-unwind.h (STACK_BIAS): Define.
(sparc64_fallback_frame_state): Use it.
(sparc64_frob_update_context): Further adjust context.
* config/sparc/sol2-unwind.h (sparc64_frob_update_context): Likewise.
* config/sparc/sol2-ci.S: Add TARGET_FLAT handling.
* config/sparc/sol2-cn.S: Likewise.

Co-Authored-By: Laurent Rougé <laurent.rouge@menta.fr>
From-SVN: r174897

17 files changed:
gcc/ChangeLog
gcc/config/sparc/sparc-protos.h
gcc/config/sparc/sparc.c
gcc/config/sparc/sparc.h
gcc/config/sparc/sparc.md
gcc/config/sparc/sparc.opt
gcc/config/sparc/t-elf
gcc/config/sparc/t-leon
gcc/doc/invoke.texi
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/20020503-1.c
gcc/testsuite/gcc.target/sparc/sparc-ret.c
libgcc/ChangeLog
libgcc/config/sparc/linux-unwind.h
libgcc/config/sparc/sol2-ci.S
libgcc/config/sparc/sol2-cn.S
libgcc/config/sparc/sol2-unwind.h

index 43ec6bfa7bfe79a11ba2d48839faa31a2709f02c..8fb1ad988ac76da77beb9a2c62705ab8881bc066 100644 (file)
@@ -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.
index ccf16573cb496fbcda8b47570199d89b06e8b97e..a5e25879793e7f6b900dfe8a40dabaaa95c7329a 100644 (file)
@@ -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);
index ae35cf826009d4101ec08c5bff10f0775f81cace..0de98c67974143ab516f2fe6519c19c5fc0a85d0 100644 (file)
@@ -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)
     }
 }
 \f
+/* 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
index 4f9ef3a5d4c1de3958404c57c2d5cdb2cd691b14..8f3ace32c1156e3073017515b416497be542e3ee 100644 (file)
@@ -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 {                                                                    \
 \f
 /* 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))
-\f
 /* 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
 \f
 /* 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
index aa9f9fb7b17a6fc6c909078965a5905a3ade2750..acf317d46bd512aea86f08decbf49d8af5b45799 100644 (file)
 (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)"))
   (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).
   [(const_int 0)]
   ""
 {
-  sparc_expand_prologue ();
+  if (TARGET_FLAT)
+    sparc_flat_expand_prologue ();
+  else
+    sparc_expand_prologue ();
   DONE;
 })
 
    (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 ()"
   "* 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)
 
   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
 {
   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"
 
   /* 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));
index b7fac0946e6e8e58366f9de2cbb563fa0c1d00e8..d729214d3c49c0bfd9bf2a2974d9bbfd06801ac2 100644 (file)
@@ -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
index b1d18fda62e45c4182c4c1c858932916c3db2036..962d0009f9bada5c64374acf4ca5747a50a47105 100644 (file)
@@ -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
index 6573f824e8ba79c67ae048430592ae7d46092cbf..e3ab3bc98933d23ba7669b347a8611f05702c1cb 100644 (file)
@@ -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
index 0069f788edb061c14c55606a38026a9ba2062a5a..fee3a2f6356d9e8185b0792b4fadf0d69db653e0 100644 (file)
@@ -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
index a80c3cd4afd7b78a5936c50274071cf9939563f0..31f5d1e65c8975da15c7c868625406d69c22f16a 100644 (file)
@@ -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
index 3edc7cf98589fda327bc2ff72768454a31f31559..e13cf954efb7a6504e9077ee5f93f4dbf3336413 100644 (file)
@@ -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)
 {
index 11afc10821d4a7befec52c85c02edf9af18e84f4..f58b059e5aa4a529c4f3ab603fbea19c56e3672a 100644 (file)
@@ -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" } } */
index 75e7ef0d2118997d606c8b7a8f1126b21da116d4..06c7613dd896c87cf846dc7412adc2e0acf85096 100644 (file)
@@ -1,3 +1,12 @@
+2011-06-10  Eric Botcazou  <ebotcazou@adacore.com>
+
+       * config/sparc/linux-unwind.h (STACK_BIAS): Define.
+       (sparc64_fallback_frame_state): Use it.
+       (sparc64_frob_update_context): Further adjust context.
+       * config/sparc/sol2-unwind.h (sparc64_frob_update_context): Likewise.
+       * config/sparc/sol2-ci.S: Add TARGET_FLAT handling.
+       * config/sparc/sol2-cn.S: Likewise.
+
 2011-06-09  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
 
        * enable-execute-stack-empty.c: New file.
index adfef6ec29d0275390cdc4255dfaede8a1670325..c5e7f8d9c1584f255b0a294ff80b52e87622a75e 100644 (file)
@@ -27,6 +27,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 #if defined(__arch64__)
 
+#undef STACK_BIAS
+#define STACK_BIAS 2047
+
 /* 64-bit SPARC version */
 #define MD_FALLBACK_FRAME_STATE_FOR sparc64_fallback_frame_state
 
@@ -49,7 +52,8 @@ sparc64_fallback_frame_state (struct _Unwind_Context *context,
   fpu_save_off = regs_off + (16 * 8) + (3 * 8) + (2 * 4);
 
   new_cfa = *(long *)(this_cfa + regs_off + (14 * 8));
-  new_cfa += 2047; /* Stack bias */
+  /* The frame address is %sp + STACK_BIAS in 64-bit mode.  */
+  new_cfa += STACK_BIAS;
   fpu_save = *(long *)(this_cfa + fpu_save_off);
   fs->regs.cfa_how = CFA_REG_OFFSET;
   fs->regs.cfa_reg = __builtin_dwarf_sp_column ();
@@ -112,7 +116,16 @@ sparc64_frob_update_context (struct _Unwind_Context *context,
       && fs->regs.cfa_how == CFA_REG_OFFSET
       && fs->regs.cfa_offset != 0
       && !fs->signal_frame)
-    context->cfa -= 2047;
+    {
+      long i;
+
+      context->cfa -= STACK_BIAS;
+
+      for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)
+       if (fs->regs.reg[i].how == REG_SAVED_OFFSET)
+         _Unwind_SetGRPtr (context, i,
+                           _Unwind_GetGRPtr (context, i) - STACK_BIAS);
+    }
 }
 
 #else
index 8825f79583489dba5a2a22edbc5d1f94586af566..a89cc20c86f5e7af34405cf7c79dda013e100313 100644 (file)
@@ -1,6 +1,6 @@
 ! crti.s for solaris 2.0.
 
-!   Copyright (C) 1992, 2008, 2009 Free Software Foundation, Inc.
+!   Copyright (C) 1992, 2008, 2009, 2011 Free Software Foundation, Inc.
 !   Written By David Vinayak Henkel-Wallace, June 1992
 ! 
 ! This file is free software; you can redistribute it and/or modify it
        .type   _init,#function
        .align  4
 _init:
+#ifdef _FLAT
+#ifdef __sparcv9
+       stx     %i7, [%sp+2167]
+       add     %sp, -176, %sp
+#else
+       st      %i7, [%sp+60]
+       add     %sp, -96, %sp
+#endif
+       mov     %o7, %i7
+#else
 #ifdef __sparcv9
        save    %sp, -176, %sp
 #else
        save    %sp, -96, %sp
 #endif
+#endif
 
 
        .section        ".fini"
@@ -48,8 +59,19 @@ _init:
        .type   _fini,#function
        .align  4
 _fini:
+#ifdef _FLAT
+#ifdef __sparcv9
+       stx     %i7, [%sp+2167]
+       add     %sp, -176, %sp
+#else
+       st      %i7, [%sp+60]
+       add     %sp, -96, %sp
+#endif
+       mov     %o7, %i7
+#else
 #ifdef __sparcv9
        save    %sp, -176, %sp
 #else
        save    %sp, -96, %sp
 #endif
+#endif
index b92f3cf08d679d7e6f56130008b7cec45af3348c..08862a78e02e07f4f52bfe4d2dd2aee14e94439e 100644 (file)
@@ -1,6 +1,6 @@
 ! crtn.s for solaris 2.0.
 
-!   Copyright (C) 1992, 2008, 2009 Free Software Foundation, Inc.
+!   Copyright (C) 1992, 2008, 2009, 2011 Free Software Foundation, Inc.
 !   Written By David Vinayak Henkel-Wallace, June 1992
 ! 
 ! This file is free software; you can redistribute it and/or modify it
 
        .section        ".init"
        .align          4
-
-       ret
+#ifdef _FLAT
+       mov     %i7, %o7
+#ifdef __sparcv9
+       ldx     [%sp+2343], %i7
+       sub     %sp, -176, %sp
+#else
+       ld      [%sp+156], %i7
+       sub     %sp, -96, %sp
+#endif
+#else
        restore
+#endif
+       jmp     %o7+8
+        nop
 
        .section        ".fini"
        .align          4
-
-       ret
+#ifdef _FLAT
+       mov     %i7, %o7
+#ifdef __sparcv9
+       ldx     [%sp+2343], %i7
+       sub     %sp, -176, %sp
+#else
+       ld      [%sp+156], %i7
+       sub     %sp, -96, %sp
+#endif
+#else
        restore
+#endif
+       jmp     %o7+8
+        nop
 
 ! Th-th-th-that is all folks!
index f8b99027247b89379c6105504f89908d989a381c..129405e95ba66be731ac430ff6c24f622d14bd3c 100644 (file)
@@ -164,7 +164,16 @@ sparc64_frob_update_context (struct _Unwind_Context *context,
       && fs->regs.cfa_how == CFA_REG_OFFSET
       && fs->regs.cfa_offset != 0
       && !fs->signal_frame)
-    context->cfa -= STACK_BIAS;
+    {
+      long i;
+
+      context->cfa -= STACK_BIAS;
+
+      for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)
+       if (fs->regs.reg[i].how == REG_SAVED_OFFSET)
+         _Unwind_SetGRPtr (context, i,
+                           _Unwind_GetGRPtr (context, i) - STACK_BIAS);
+    }
 }
 
 #else