sibcall.c (skip_copy_to_return_value): Use OUTGOING_REGNO for comparison if regno...
authorJakub Jelinek <jakub@redhat.com>
Fri, 24 Mar 2000 21:48:01 +0000 (22:48 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 24 Mar 2000 21:48:01 +0000 (22:48 +0100)
* sibcall.c (skip_copy_to_return_value): Use OUTGOING_REGNO for
comparison if regno's are equal.
* calls.c (initialize_argument_informat): Add ecf_flags argument.
Use FUNCTION_INCOMING_ARG if available and ECF_SIBCALL.
(expand_call): Update caller.
Avoid making a sibling call if argument size of the callee is larger
than argument size of the caller.
Call hard_function_value with outgoing set if in sibcall pass.
Use FUNCTION_INCOMING_ARG if available and ECF_SIBCALL.

* final.c (permitted_reg_in_leaf_functions, only_leaf_regs_used):
Change LEAF_REGISTERS from an array initializer to actual array
identifier. Move static global variable into the function.
(leaf_function_p): Allow SIBLING_CALL_P calls even outside of
sequences for leaf functions.
* global.c (global_alloc): Likewise.
* tm.texi (LEAF_REGISTERS): Update documentation.

* config/sparc/sparc.h (CONDITIONAL_REGISTER_USAGE): Remove the ugly
TARGET_FLAT leaf disabling hack.
(LEAF_REGISTERS): Changed from an array initializer to actual array
identifier to avoid duplication and remove the above hack.
(FUNCTION_OK_FOR_SIBCALL): Define.
* config/sparc/sparc.md (sibcall): New attr type. Use it almost
always like call attribute.
(eligible_for_sibcall_delay): New attribute.
(sibcall): New delay type.
(sibcall, sibcall_value, sibcall_epilogue): New expands.
(sibcall_symbolic_sp32, sibcall_symbolic_sp64,
sibcall_value_symbolic_sp32, sibcall_value_symbolic_sp64): New insns.
* config/sparc/sparc.c (sparc_leaf_regs): New array.
(eligible_for_sibcall_delay, output_restore_regs, output_sibcall):
New functions.
(output_function_epilogue): Move part of the code into
output_restore_regs.
(ultra_code_from_mask, ultrasparc_sched_reorder): Handle
TYPE_SIBCALL.
* sparc-protos.h (output_sibcall, eligible_for_sibcall_delay): New
prototypes.

From-SVN: r32730

gcc/ChangeLog
gcc/calls.c
gcc/config/sparc/sparc-protos.h
gcc/config/sparc/sparc.c
gcc/config/sparc/sparc.h
gcc/config/sparc/sparc.md
gcc/final.c
gcc/global.c
gcc/sibcall.c
gcc/tm.texi

index 828b9a30caa03237f862d657c7df3f1dd001553e..71bc926322728d9898b8b16d5ab00158ac939b1d 100644 (file)
@@ -1,3 +1,45 @@
+2000-03-24  Jakub Jelinek  <jakub@redhat.com>
+
+       * sibcall.c (skip_copy_to_return_value): Use OUTGOING_REGNO for
+       comparison if regno's are equal.
+       * calls.c (initialize_argument_informat): Add ecf_flags argument.
+       Use FUNCTION_INCOMING_ARG if available and ECF_SIBCALL.
+       (expand_call): Update caller.
+       Avoid making a sibling call if argument size of the callee is larger
+       than argument size of the caller.
+       Call hard_function_value with outgoing set if in sibcall pass.
+       Use FUNCTION_INCOMING_ARG if available and ECF_SIBCALL.
+
+       * final.c (permitted_reg_in_leaf_functions, only_leaf_regs_used):
+       Change LEAF_REGISTERS from an array initializer to actual array
+       identifier. Move static global variable into the function.
+       (leaf_function_p): Allow SIBLING_CALL_P calls even outside of
+       sequences for leaf functions.
+       * global.c (global_alloc): Likewise.
+       * tm.texi (LEAF_REGISTERS): Update documentation.
+
+       * config/sparc/sparc.h (CONDITIONAL_REGISTER_USAGE): Remove the ugly
+       TARGET_FLAT leaf disabling hack.
+       (LEAF_REGISTERS): Changed from an array initializer to actual array
+       identifier to avoid duplication and remove the above hack.
+       (FUNCTION_OK_FOR_SIBCALL): Define.
+       * config/sparc/sparc.md (sibcall): New attr type. Use it almost
+       always like call attribute.
+       (eligible_for_sibcall_delay): New attribute.
+       (sibcall): New delay type.
+       (sibcall, sibcall_value, sibcall_epilogue): New expands.
+       (sibcall_symbolic_sp32, sibcall_symbolic_sp64,
+       sibcall_value_symbolic_sp32, sibcall_value_symbolic_sp64): New insns.
+       * config/sparc/sparc.c (sparc_leaf_regs): New array.
+       (eligible_for_sibcall_delay, output_restore_regs, output_sibcall):
+       New functions.
+       (output_function_epilogue): Move part of the code into
+       output_restore_regs.
+       (ultra_code_from_mask, ultrasparc_sched_reorder): Handle
+       TYPE_SIBCALL.
+       * sparc-protos.h (output_sibcall, eligible_for_sibcall_delay): New
+       prototypes.
+
 Fri Mar 24 13:49:45 2000  Jeffrey A Law  (law@cygnus.com)
 
        * integrate.c (save_for_inline_nocopy): Clear in_nonparm_insns here.
index dfb38ee6d7e6cd7f96d013f039ca20ca266af943..0a8688c6885f8c5bed448787d6e159d3f551911f 100644 (file)
@@ -165,7 +165,7 @@ static void initialize_argument_information PARAMS ((int,
                                                         int, tree, tree,
                                                         CUMULATIVE_ARGS *,
                                                         int, rtx *, int *,
-                                                        int *, int *));
+                                                        int *, int *, int));
 static void compute_argument_addresses         PARAMS ((struct arg_data *,
                                                         rtx, int));
 static rtx rtx_for_function_call               PARAMS ((tree, tree));
@@ -980,7 +980,8 @@ static void
 initialize_argument_information (num_actuals, args, args_size, n_named_args,
                                 actparms, fndecl, args_so_far,
                                 reg_parm_stack_space, old_stack_level,
-                                old_pending_adj, must_preallocate, is_const)
+                                old_pending_adj, must_preallocate, is_const,
+                                ecf_flags)
      int num_actuals ATTRIBUTE_UNUSED;
      struct arg_data *args;
      struct args_size *args_size;
@@ -993,6 +994,7 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
      int *old_pending_adj;
      int *must_preallocate;
      int *is_const;
+     int ecf_flags;
 {
   /* 1 if scanning parms front to back, -1 if scanning back to front.  */
   int inc;
@@ -1150,8 +1152,19 @@ initialize_argument_information (num_actuals, args, args_size, n_named_args,
 
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
-      args[i].reg = FUNCTION_ARG (*args_so_far, mode, type,
-                                 argpos < n_named_args);
+
+#ifdef FUNCTION_INCOMING_ARG
+      /* If this is a sibling call and the machine has register windows, the
+        register window has to be unwinded before calling the routine, so
+        arguments have to go into the incoming registers.  */
+      if (ecf_flags & ECF_SIBCALL)
+       args[i].reg = FUNCTION_INCOMING_ARG (*args_so_far, mode, type,
+                                            argpos < n_named_args);
+      else
+#endif
+       args[i].reg = FUNCTION_ARG (*args_so_far, mode, type,
+                                   argpos < n_named_args);
+
 #ifdef FUNCTION_ARG_PARTIAL_NREGS
       if (args[i].reg)
        args[i].partial
@@ -2131,7 +2144,7 @@ expand_call (exp, target, ignore)
         call expansion.  */
       int save_pending_stack_adjust;
       rtx insns;
-      rtx before_call;
+      rtx before_call, next_arg_reg;
 
       if (pass == 0)
        {
@@ -2284,7 +2297,8 @@ expand_call (exp, target, ignore)
                                       n_named_args, actparms, fndecl,
                                       &args_so_far, reg_parm_stack_space,
                                       &old_stack_level, &old_pending_adj,
-                                      &must_preallocate, &is_const);
+                                      &must_preallocate, &is_const,
+                                      (pass == 0) ? ECF_SIBCALL : 0);
 
 #ifdef FINAL_REG_PARM_STACK_SPACE
       reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
@@ -2305,6 +2319,13 @@ expand_call (exp, target, ignore)
          sibcall_failure = 1;
        }
 
+      if (args_size.constant > current_function_args_size)
+       {
+         /* If this function requires more stack slots than the current
+            function, we cannot change it into a sibling call.  */
+         sibcall_failure = 1;
+       }
+
       /* Compute the actual size of the argument block required.  The variable
         and constant sizes must be combined, the size may have to be rounded,
         and there may be a minimum required size.  When generating a sibcall
@@ -2569,9 +2590,9 @@ expand_call (exp, target, ignore)
        {
          if (pcc_struct_value)
            valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
-                                         fndecl, 0);
+                                         fndecl, (pass == 0));
          else
-           valreg = hard_function_value (TREE_TYPE (exp), fndecl, 0);
+           valreg = hard_function_value (TREE_TYPE (exp), fndecl, (pass == 0));
        }
 
       /* Precompute all register parameters.  It isn't safe to compute anything
@@ -2665,14 +2686,24 @@ expand_call (exp, target, ignore)
         later safely search backwards to find the CALL_INSN.  */
       before_call = get_last_insn ();
 
+      /* Set up next argument register.  For sibling calls on machines
+        with register windows this should be the incoming register.  */
+#ifdef FUNCTION_INCOMING_ARG
+      if (pass == 0)
+       next_arg_reg = FUNCTION_INCOMING_ARG (args_so_far, VOIDmode,
+                                             void_type_node, 1);
+      else
+#endif
+       next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode,
+                                    void_type_node, 1);
+
       /* All arguments and registers used for the call must be set up by
         now!  */
 
       /* Generate the actual call instruction.  */
       emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
                   args_size.constant, struct_value_size,
-                  FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
-                  valreg, old_inhibit_defer_pop, call_fusage,
+                  next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
                   ((is_const ? ECF_IS_CONST : 0)
                    | (nothrow ? ECF_NOTHROW : 0)
                    | (pass == 0 ? ECF_SIBCALL : 0)));
@@ -2956,11 +2987,9 @@ expand_call (exp, target, ignore)
        {
          tail_call_insns = insns;
 
-         /* If the current function's argument block is not large enough
-            to hold the outoing arguments, or we encountered some other
-            situation we couldn't handle, zero out the sequence.  */
-         if (current_function_args_size < args_size.constant
-             || sibcall_failure)
+         /* If something prevents making this a sibling call,
+            zero out the sequence.  */
+         if (sibcall_failure)
            tail_call_insns = NULL_RTX;
 
          /* Restore the pending stack adjustment now that we have
index 9fa6d45ab35cadeca264a3ca1bd837fc3a02d979..52d95f897628f8c9ad2c7d76d8d98d8ee4cd451d 100644 (file)
@@ -96,6 +96,7 @@ extern int sparc_splitdi_legitimate PARAMS ((rtx, rtx));
 extern int sparc_absnegfloat_split_legitimate PARAMS ((rtx, rtx));
 extern char *output_cbranch PARAMS ((rtx, int, int, int, int, rtx));
 extern const char *output_return PARAMS ((rtx *));
+extern const char *output_sibcall PARAMS ((rtx, rtx));
 extern char *output_v9branch PARAMS ((rtx, int, int, int, int, int, rtx));
 extern void emit_v9_brxx_insn PARAMS ((enum rtx_code, rtx, rtx));
 extern void output_double_int PARAMS ((FILE *, rtx));
@@ -121,6 +122,7 @@ extern int cc_arithopn PARAMS ((rtx, enum machine_mode));
 extern int data_segment_operand PARAMS ((rtx, enum machine_mode));
 extern int eligible_for_epilogue_delay PARAMS ((rtx, int));
 extern int eligible_for_return_delay PARAMS ((rtx));
+extern int eligible_for_sibcall_delay PARAMS ((rtx));
 extern int emit_move_sequence PARAMS ((rtx, enum machine_mode));
 extern int extend_op PARAMS ((rtx, enum machine_mode));
 extern int fcc_reg_operand PARAMS ((rtx, enum machine_mode));
index 5a9c87b1f1e89931622610e2b9c3f2642b0d3dfc..8d8487512eb28b225dede5ee555c5818c4c43f1e 100644 (file)
@@ -99,6 +99,24 @@ char leaf_reg_remap[] =
   88, 89, 90, 91, 92, 93, 94, 95,
   96, 97, 98, 99, 100};
 
+/* Vector, indexed by hard register number, which contains 1
+   for a register that is allowable in a candidate for leaf
+   function treatment.  */
+char sparc_leaf_regs[] =
+{ 1, 1, 1, 1, 1, 1, 1, 1,
+  0, 0, 0, 0, 0, 0, 1, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+  1, 1, 1, 1, 1, 1, 0, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1};
+
 #endif
 
 /* Name of where we pretend to think the frame pointer points.
@@ -2458,6 +2476,98 @@ eligible_for_epilogue_delay (trial, slot)
   return 0;
 }
 
+/* Return nonzero if TRIAL can go into the sibling call
+   delay slot.  */
+
+int
+eligible_for_sibcall_delay (trial)
+     rtx trial;
+{
+  rtx pat, src;
+
+  if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
+    return 0;
+
+  if (get_attr_length (trial) != 1 || profile_block_flag == 2)
+    return 0;
+
+  pat = PATTERN (trial);
+
+  if (current_function_uses_only_leaf_regs)
+    {
+      /* If the tail call is done using the call instruction,
+        we have to restore %o7 in the delay slot.  */
+      if (TARGET_ARCH64 && ! TARGET_CM_MEDLOW)
+       return 0;
+
+      /* %g1 is used to build the function address */
+      if (reg_mentioned_p (gen_rtx_REG (Pmode, 1), pat))
+       return 0;
+
+      return 1;
+    }
+
+  /* Otherwise, only operations which can be done in tandem with
+     a `restore' insn can go into the delay slot.  */
+  if (GET_CODE (SET_DEST (pat)) != REG
+      || REGNO (SET_DEST (pat)) < 24
+      || REGNO (SET_DEST (pat)) >= 32)
+    return 0;
+
+  /* If it mentions %o7, it can't go in, because sibcall will clobber it
+     in most cases.  */
+  if (reg_mentioned_p (gen_rtx_REG (Pmode, 15), pat))
+    return 0;
+
+  src = SET_SRC (pat);
+
+  if (arith_operand (src, GET_MODE (src)))
+    {
+      if (TARGET_ARCH64)
+        return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
+      else
+        return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (SImode);
+    }
+
+  else if (arith_double_operand (src, GET_MODE (src)))
+    return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
+
+  else if (! TARGET_FPU && restore_operand (SET_DEST (pat), SFmode)
+          && register_operand (src, SFmode))
+    return 1;
+
+  else if (GET_CODE (src) == PLUS
+          && arith_operand (XEXP (src, 0), SImode)
+          && arith_operand (XEXP (src, 1), SImode)
+          && (register_operand (XEXP (src, 0), SImode)
+              || register_operand (XEXP (src, 1), SImode)))
+    return 1;
+
+  else if (GET_CODE (src) == PLUS
+          && arith_double_operand (XEXP (src, 0), DImode)
+          && arith_double_operand (XEXP (src, 1), DImode)
+          && (register_operand (XEXP (src, 0), DImode)
+              || register_operand (XEXP (src, 1), DImode)))
+    return 1;
+
+  else if (GET_CODE (src) == LO_SUM
+          && ! TARGET_CM_MEDMID
+          && ((register_operand (XEXP (src, 0), SImode)
+               && immediate_operand (XEXP (src, 1), SImode))
+              || (TARGET_ARCH64
+                  && register_operand (XEXP (src, 0), DImode)
+                  && immediate_operand (XEXP (src, 1), DImode))))
+    return 1;
+
+  else if (GET_CODE (src) == ASHIFT
+          && (register_operand (XEXP (src, 0), SImode)
+              || register_operand (XEXP (src, 0), DImode))
+          && XEXP (src, 1) == const1_rtx)
+    return 1;
+
+  return 0;
+}
+
 static int
 check_return_regs (x)
      rtx x;
@@ -3423,6 +3533,40 @@ output_function_prologue (file, size, leaf_function)
     }
 }
 
+/* Output code to restore any call saved registers.  */
+
+static void
+output_restore_regs (file, leaf_function)
+     FILE *file;
+     int leaf_function;
+{
+  int offset, n_regs;
+  const char *base;
+
+  offset = -apparent_fsize + frame_base_offset;
+  if (offset < -4096 || offset + num_gfregs * 4 > 4096 - 8 /*double*/)
+    {
+      build_big_number (file, offset, "%g1");
+      fprintf (file, "\tadd\t%s, %%g1, %%g1\n", frame_base_name);
+      base = "%g1";
+      offset = 0;
+    }
+  else
+    {
+      base = frame_base_name;
+    }
+
+  n_regs = 0;
+  if (TARGET_EPILOGUE && ! leaf_function)
+    /* ??? Originally saved regs 0-15 here.  */
+    n_regs = restore_regs (file, 0, 8, base, offset, 0);
+  else if (leaf_function)
+    /* ??? Originally saved regs 0-31 here.  */
+    n_regs = restore_regs (file, 0, 8, base, offset, 0);
+  if (TARGET_EPILOGUE)
+    restore_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs);
+}
+
 /* Output code for the function epilogue.  */
 
 void
@@ -3457,35 +3601,8 @@ output_function_epilogue (file, size, leaf_function)
       goto output_vectors;                                                    
     }
 
-  /* Restore any call saved registers.  */
   if (num_gfregs)
-    {
-      int offset, n_regs;
-      const char *base;
-
-      offset = -apparent_fsize + frame_base_offset;
-      if (offset < -4096 || offset + num_gfregs * 4 > 4096 - 8 /*double*/)
-       {
-         build_big_number (file, offset, "%g1");
-         fprintf (file, "\tadd\t%s, %%g1, %%g1\n", frame_base_name);
-         base = "%g1";
-         offset = 0;
-       }
-      else
-       {
-         base = frame_base_name;
-       }
-
-      n_regs = 0;
-      if (TARGET_EPILOGUE && ! leaf_function)
-       /* ??? Originally saved regs 0-15 here.  */
-       n_regs = restore_regs (file, 0, 8, base, offset, 0);
-      else if (leaf_function)
-       /* ??? Originally saved regs 0-31 here.  */
-       n_regs = restore_regs (file, 0, 8, base, offset, 0);
-      if (TARGET_EPILOGUE)
-       restore_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs);
-    }
+    output_restore_regs (file, leaf_function);
 
   /* Work out how to skip the caller's unimp instruction if required.  */
   if (leaf_function)
@@ -3575,6 +3692,139 @@ output_function_epilogue (file, size, leaf_function)
  output_vectors:
   sparc_output_deferred_case_vectors ();
 }
+
+/* Output a sibling call.  */
+
+const char *
+output_sibcall (insn, call_operand)
+     rtx insn, call_operand;
+{
+  int leaf_regs = current_function_uses_only_leaf_regs;
+  rtx operands[3];
+  int delay_slot = dbr_sequence_length () > 0;
+
+  if (num_gfregs)
+    {
+      /* Call to restore global regs might clobber
+        the delay slot. Instead of checking for this
+        output the delay slot now.  */
+      if (delay_slot)
+       {
+         rtx delay = NEXT_INSN (insn);
+
+         if (! delay)
+           abort ();
+
+         final_scan_insn (delay, asm_out_file, 1, 0, 1);
+         PATTERN (delay) = gen_blockage ();
+         INSN_CODE (delay) = -1;
+         delay_slot = 0;
+       }
+      output_restore_regs (asm_out_file, leaf_regs);
+    }
+
+  operands[0] = call_operand;
+
+  if (leaf_regs)
+    {
+      int spare_slot = (TARGET_ARCH32 || TARGET_CM_MEDLOW);
+      int size = 0;
+
+      if ((actual_fsize || ! spare_slot) && delay_slot)
+       {
+         rtx delay = NEXT_INSN (insn);
+
+         if (! delay)
+           abort ();
+
+         final_scan_insn (delay, asm_out_file, 1, 0, 1);
+         PATTERN (delay) = gen_blockage ();
+         INSN_CODE (delay) = -1;
+         delay_slot = 0;
+       }
+      if (actual_fsize)
+       {
+         if (actual_fsize <= 4096)
+           size = actual_fsize;
+         else if (actual_fsize <= 8192)
+           {
+             fputs ("\tsub\t%sp, -4096, %sp\n", asm_out_file);
+             size = actual_fsize - 4096;
+           }
+         else if ((actual_fsize & 0x3ff) == 0)
+           fprintf (asm_out_file,
+                    "\tsethi\t%%hi(%d), %%g1\n\tadd\t%%sp, %%g1, %%sp\n",
+                    actual_fsize);
+         else
+           {
+             fprintf (asm_out_file,
+                      "\tsethi\t%%hi(%d), %%g1\n\tor\t%%g1, %%lo(%d), %%g1\n",
+                      actual_fsize, actual_fsize);
+             fputs ("\tadd\t%%sp, %%g1, %%sp\n", asm_out_file);
+           }
+       }
+      if (spare_slot)
+       {
+         output_asm_insn ("sethi\t%%hi(%a0), %%g1", operands);
+         output_asm_insn ("jmpl\t%%g1 + %%lo(%a0), %%g0", operands);
+         if (size)
+           fprintf (asm_out_file, "\t sub\t%%sp, -%d, %%sp\n", size);
+         else if (! delay_slot)
+           fputs ("\t nop\n", asm_out_file);
+       }
+      else
+       {
+         if (size)
+           fprintf (asm_out_file, "\tsub\t%%sp, -%d, %%sp\n", size);
+         output_asm_insn ("mov\t%%o7, %%g1", operands);
+         output_asm_insn ("call\t%a0, 0", operands);
+         output_asm_insn (" mov\t%%g1, %%o7", operands);
+       }
+      return "";
+    }
+
+  output_asm_insn ("call\t%a0, 0", operands);
+  if (delay_slot)
+    {
+      rtx delay = NEXT_INSN (insn), pat;
+
+      if (! delay)
+       abort ();
+
+      pat = PATTERN (delay);
+      if (GET_CODE (pat) != SET)
+       abort ();
+
+      operands[0] = SET_DEST (pat);
+      pat = SET_SRC (pat);
+      switch (GET_CODE (pat))
+       {
+       case PLUS:
+         operands[1] = XEXP (pat, 0);
+         operands[2] = XEXP (pat, 1);
+         output_asm_insn (" restore %r1, %2, %Y0", operands);
+         break;
+       case LO_SUM:
+         operands[1] = XEXP (pat, 0);
+         operands[2] = XEXP (pat, 1);
+         output_asm_insn (" restore %r1, %%lo(%a2), %Y0", operands);
+         break;
+       case ASHIFT:
+         operands[1] = XEXP (pat, 0);
+         output_asm_insn (" restore %r1, %r1, %Y0", operands);
+         break;
+       default:
+         operands[1] = pat;
+         output_asm_insn (" restore %%g0, %1, %Y0", operands);
+         break;
+       }
+      PATTERN (delay) = gen_blockage ();
+      INSN_CODE (delay) = -1;
+    }
+  else
+    fputs ("\t restore\n", asm_out_file);
+  return "";
+}
 \f
 /* Functions for handling argument passing.
 
@@ -7014,6 +7264,7 @@ ultra_code_from_mask (type_mask)
     return IEU0;
   else if (type_mask & (TMASK (TYPE_COMPARE) |
                        TMASK (TYPE_CALL) |
+                       TMASK (TYPE_SIBCALL) |
                        TMASK (TYPE_UNCOND_BRANCH)))
     return IEU1;
   else if (type_mask & (TMASK (TYPE_IALU) | TMASK (TYPE_BINARY) |
@@ -7486,6 +7737,7 @@ ultrasparc_sched_reorder (dump, sched_verbose, ready, n_ready)
        /* If we are not in the process of emptying out the pipe, try to
           obtain an instruction which must be the first in it's group.  */
        ip = ultra_find_type ((TMASK (TYPE_CALL) |
+                              TMASK (TYPE_SIBCALL) |
                               TMASK (TYPE_CALL_NO_DELAY_SLOT) |
                               TMASK (TYPE_UNCOND_BRANCH)),
                              ready, this_insn);
index b42f5085de51492e39abfb34d84f56e0b090974e..d11f387b671a059bd25952c82b990eeb0783b69a 100644 (file)
@@ -1066,8 +1066,8 @@ do                                                                \
           %fp, but output it as %i7.  */                       \
        fixed_regs[31] = 1;                                     \
        reg_names[FRAME_POINTER_REGNUM] = "%i7";                \
-       /* ??? This is a hack to disable leaf functions.  */    \
-       global_regs[7] = 1;                                     \
+       /* Disable leaf functions */                            \
+       bzero (sparc_leaf_regs, FIRST_PSEUDO_REGISTER);         \
       }                                                                \
     if (profile_block_flag)                                    \
       {                                                                \
@@ -1373,26 +1373,8 @@ extern enum reg_class sparc_regno_reg_class[];
   
 #define ORDER_REGS_FOR_LOCAL_ALLOC order_regs_for_local_alloc ()
 
-/* ??? %g7 is not a leaf register to effectively #undef LEAF_REGISTERS when
-   -mflat is used.  Function only_leaf_regs_used will return 0 if a global
-   register is used and is not permitted in a leaf function.  We make %g7
-   a global reg if -mflat and voila.  Since %g7 is a system register and is
-   fixed it won't be used by gcc anyway.  */
-
-#define LEAF_REGISTERS \
-{ 1, 1, 1, 1, 1, 1, 1, 0,      \
-  0, 0, 0, 0, 0, 0, 1, 0,      \
-  0, 0, 0, 0, 0, 0, 0, 0,      \
-  1, 1, 1, 1, 1, 1, 0, 1,      \
-  1, 1, 1, 1, 1, 1, 1, 1,      \
-  1, 1, 1, 1, 1, 1, 1, 1,      \
-  1, 1, 1, 1, 1, 1, 1, 1,      \
-  1, 1, 1, 1, 1, 1, 1, 1,      \
-  1, 1, 1, 1, 1, 1, 1, 1,      \
-  1, 1, 1, 1, 1, 1, 1, 1,      \
-  1, 1, 1, 1, 1, 1, 1, 1,      \
-  1, 1, 1, 1, 1, 1, 1, 1,      \
-  1, 1, 1, 1, 1}
+extern char sparc_leaf_regs[];
+#define LEAF_REGISTERS sparc_leaf_regs
 
 extern char leaf_reg_remap[];
 #define LEAF_REG_REMAP(REGNO) (leaf_reg_remap[REGNO])
@@ -2145,6 +2127,10 @@ LFLGRET"ID":\n\
 
 #define STRICT_ARGUMENT_NAMING TARGET_V9
 
+/* We do not allow sibling calls if -mflat, nor
+   we do not allow indirect calls to be optimized into sibling calls.  */
+#define FUNCTION_OK_FOR_SIBCALL(DECL) (DECL && ! TARGET_FLAT)
+
 /* Generate RTL to flush the register windows so as to make arbitrary frames
    available.  */
 #define SETUP_FRAME_ADDRESSES()                \
index efa572c9b50ca4f8094b019f507e0d21132b2af0..3b62a5eff7de9bba52b38b46b977bdebf2ad86da 100644 (file)
@@ -88,7 +88,7 @@
 ;; type "call_no_delay_slot" is a call followed by an unimp instruction.
 
 (define_attr "type"
-  "move,unary,binary,compare,load,sload,store,ialu,shift,uncond_branch,branch,call,call_no_delay_slot,return,address,imul,fpload,fpstore,fp,fpmove,fpcmove,fpcmp,fpmul,fpdivs,fpdivd,fpsqrts,fpsqrtd,cmove,multi,misc"
+  "move,unary,binary,compare,load,sload,store,ialu,shift,uncond_branch,branch,call,sibcall,call_no_delay_slot,return,address,imul,fpload,fpstore,fp,fpmove,fpcmove,fpcmp,fpmul,fpdivs,fpdivd,fpsqrts,fpsqrtd,cmove,multi,misc"
   (const_string "binary"))
 
 ;; Set true if insn uses call-clobbered intermediate register.
 ;; Attributes for instruction and branch scheduling
 
 (define_attr "in_call_delay" "false,true"
-  (cond [(eq_attr "type" "uncond_branch,branch,call,call_no_delay_slot,return,multi")
+  (cond [(eq_attr "type" "uncond_branch,branch,call,sibcall,call_no_delay_slot,return,multi")
                (const_string "false")
         (eq_attr "type" "load,fpload,store,fpstore")
                (if_then_else (eq_attr "length" "1")
 (define_delay (eq_attr "type" "call")
   [(eq_attr "in_call_delay" "true") (nil) (nil)])
 
+(define_attr "eligible_for_sibcall_delay" "false,true"
+  (symbol_ref "eligible_for_sibcall_delay(insn)"))
+
+(define_delay (eq_attr "type" "sibcall")
+  [(eq_attr "eligible_for_sibcall_delay" "true") (nil) (nil)])
+
 (define_attr "leaf_function" "false,true"
   (const (symbol_ref "current_function_uses_only_leaf_regs")))
 
 ;; because it prevents us from moving back the final store of inner loops.
 
 (define_attr "in_branch_delay" "false,true"
-  (if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,call_no_delay_slot,multi")
+  (if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,sibcall,call_no_delay_slot,multi")
                     (eq_attr "length" "1"))
                (const_string "true")
                (const_string "false")))
 
 (define_attr "in_uncond_branch_delay" "false,true"
-  (if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,call_no_delay_slot,multi")
+  (if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,sibcall,call_no_delay_slot,multi")
                     (eq_attr "length" "1"))
                (const_string "true")
                (const_string "false")))
 
 (define_attr "in_annul_branch_delay" "false,true"
-  (if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,call_no_delay_slot,multi")
+  (if_then_else (and (eq_attr "type" "!uncond_branch,branch,call,sibcall,call_no_delay_slot,multi")
                     (eq_attr "length" "1"))
                (const_string "true")
                (const_string "false")))
 
 (define_function_unit "ieuN" 2 0
   (and (eq_attr "cpu" "ultrasparc")
-    (eq_attr "type" "ialu,binary,move,unary,shift,compare,call,call_no_delay_slot,uncond_branch"))
+    (eq_attr "type" "ialu,binary,move,unary,shift,compare,call,sibcall,call_no_delay_slot,uncond_branch"))
   1 1)
 
 (define_function_unit "ieu0" 1 0
 
 (define_function_unit "ieu1" 1 0
   (and (eq_attr "cpu" "ultrasparc")
-    (eq_attr "type" "compare,call,call_no_delay_slot,uncond_branch"))
+    (eq_attr "type" "compare,call,sibcall,call_no_delay_slot,uncond_branch"))
   1 1)
 
 (define_function_unit "cti" 1 0
   DONE;
 }")
 
+;;- tail calls
+(define_expand "sibcall"
+  [(parallel [(call (match_operand 0 "call_operand" "") (const_int 0))
+             (return)])]
+  ""
+  "")
+
+(define_insn "*sibcall_symbolic_sp32"
+  [(call (mem:SI (match_operand:SI 0 "symbolic_operand" "s"))
+        (match_operand 1 "" ""))
+   (return)]
+  "! TARGET_PTR64"
+  "* return output_sibcall(insn, operands[0]);"
+  [(set_attr "type" "sibcall")])
+
+(define_insn "*sibcall_symbolic_sp64"
+  [(call (mem:SI (match_operand:DI 0 "symbolic_operand" "s"))
+        (match_operand 1 "" ""))
+   (return)]
+  "TARGET_PTR64"
+  "* return output_sibcall(insn, operands[0]);"
+  [(set_attr "type" "sibcall")])
+
+(define_expand "sibcall_value"
+  [(parallel [(set (match_operand 0 "register_operand" "=rf")
+               (call (match_operand:SI 1 "" "") (const_int 0)))
+             (return)])]
+  ""
+  "")
+
+(define_insn "*sibcall_value_symbolic_sp32"
+  [(set (match_operand 0 "" "=rf")
+       (call (mem:SI (match_operand:SI 1 "symbolic_operand" "s"))
+             (match_operand 2 "" "")))
+   (return)]
+  "! TARGET_PTR64"
+  "* return output_sibcall(insn, operands[1]);"
+  [(set_attr "type" "sibcall")])
+
+(define_insn "*sibcall_value_symbolic_sp64"
+  [(set (match_operand 0 "" "")
+       (call (mem:SI (match_operand:DI 1 "symbolic_operand" "s"))
+             (match_operand 2 "" "")))
+   (return)]
+  "TARGET_PTR64"
+  "* return output_sibcall(insn, operands[1]);"
+  [(set_attr "type" "sibcall")])
+
+(define_expand "sibcall_epilogue"
+  [(const_int 0)]
+  ""
+  "DONE;")
+
 ;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
 ;; all of memory.  This blocks insns from being moved across this point.
 
index 90437e0eb020f391a2722e3543efd4e715008068..5693d460ef88974c4846dbfae17e8c8aba391314 100644 (file)
@@ -4015,7 +4015,8 @@ leaf_function_p ()
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
-      if (GET_CODE (insn) == CALL_INSN)
+      if (GET_CODE (insn) == CALL_INSN
+         && ! SIBLING_CALL_P (insn))
        return 0;
       if (GET_CODE (insn) == INSN
          && GET_CODE (PATTERN (insn)) == SEQUENCE
@@ -4025,7 +4026,8 @@ leaf_function_p ()
     }
   for (insn = current_function_epilogue_delay_list; insn; insn = XEXP (insn, 1))
     {
-      if (GET_CODE (XEXP (insn, 0)) == CALL_INSN)
+      if (GET_CODE (XEXP (insn, 0)) == CALL_INSN
+         && ! SIBLING_CALL_P (insn))
        return 0;
       if (GET_CODE (XEXP (insn, 0)) == INSN
          && GET_CODE (PATTERN (XEXP (insn, 0))) == SEQUENCE
@@ -4048,8 +4050,6 @@ leaf_function_p ()
 
 #ifdef LEAF_REGISTERS
 
-static char permitted_reg_in_leaf_functions[] = LEAF_REGISTERS;
-
 /* Return 1 if this function uses only the registers that can be
    safely renumbered.  */
 
@@ -4057,6 +4057,7 @@ int
 only_leaf_regs_used ()
 {
   int i;
+  char *permitted_reg_in_leaf_functions = LEAF_REGISTERS;
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     if ((regs_ever_live[i] || global_regs[i])
index 74d9fd2a6b736fedea3ba61f5b46589e259a6a0f..f672076aeb5bab34b4d770369920f27034fce4ed 100644 (file)
@@ -374,7 +374,7 @@ global_alloc (file)
      a leaf function.  */
   {
     char *cheap_regs;
-    static char leaf_regs[] = LEAF_REGISTERS;
+    char *leaf_regs = LEAF_REGISTERS;
 
     if (only_leaf_regs_used () && leaf_function_p ())
       cheap_regs = leaf_regs;
index bbb70243e72f665f54a7295ef2dcb9d360c2db46..1a3a310388dd069e9421e4582c2be190d1a90be1 100644 (file)
@@ -140,9 +140,13 @@ skip_copy_to_return_value (orig_insn, hardret, softret)
      called function's return value was copied.  Otherwise we're returning
      some other value.  */
 
+#ifndef OUTGOING_REGNO
+#define OUTGOING_REGNO(N) (N)
+#endif
+
   if (SET_DEST (set) == current_function_return_rtx
       && REG_P (SET_DEST (set))
-      && REGNO (SET_DEST (set)) == REGNO (hardret)
+      && OUTGOING_REGNO (REGNO (SET_DEST (set))) == REGNO (hardret)
       && SET_SRC (set) == softret)
     return insn;
 
@@ -353,7 +357,6 @@ replace_call_placeholder (insn, use)
   NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
 }
 
-
 /* Given a (possibly empty) set of potential sibling or tail recursion call
    sites, determine if optimization is possible.
 
index 3a1b9ebebf4d6518d2e533306b82d0f729939f48..1a1396f91a815333620ebd846140954a704e25f2 100644 (file)
@@ -1652,7 +1652,7 @@ accomplish this.
 @table @code
 @findex LEAF_REGISTERS
 @item LEAF_REGISTERS
-A C initializer for a vector, indexed by hard register number, which
+Name of a char vector, indexed by hard register number, which
 contains 1 for a register that is allowable in a candidate for leaf
 function treatment.