This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH, rs6000] ELFv2 ABI 6/8: Eliminate register save area in some cases


Hello,

the final area of changes implemented by the ELFv2 ABI is stack space
consumption.  Reducing the miminum stack size requirements is important
for environments where stack space is restricted, e.g. Linux kernel code,
or where we have a large number of stacks (e.g. heavily multi-threaded
applications).

One reason for the large stack space requirement of the current ABI is
that every caller must provide an argument save area of 64 bytes to its
caller.

This is actually useful in one specific case: if the called function
uses variable arguments and constructs a va_list.  Because of the way
the ABI is designed, the callee can in this case simply store the 8
GPRs (potentially) carrying arguments into that save area, and is
then guaranteed to have all function arguments in a linear arrangement
on the stack, which makes a trivial va_arg implementation possible.

If the callee is not vararg, this wouldn't really be necessary.  However,
if we were to skip that area then, vararg and non-vararg routines would
have incompatible ABIs, which would make it impossible to generate code
for a function call if the caller does not have a prototype of the called
function.  Therefore the old ABI requires the save area to be present
always.

With the new ABI, we wanted to retain the possibility of using a
trivial va_arg implementation, and also the possibility of calling
non-prototyped routines safely.  However, even so, there is a set
of cases where it is still not necessary to provide a save area:
when calling a function we have a prototype for and know that it
is neither vararg nor uses on-stack arguments.

This patch implement this change by having REG_PARM_STACK_SPACE
return a different value depending on whether the called routine
requires on-stack arguments (and we have a prototype).


There was one problem exposed by this change: rs6000_function_arg
contained this piece of code:

	  if (mode == BLKmode)
	    mode = Pmode;

which marked the mode of a GPR holding an incoming struct argument
as Pmode.  This is now causing a problem.  When REG_PARM_STACK_SPACE
returns 0, function.c must allocate a stack slot within the callee's
frame to serve as DECL_RTL for the parameter.  This is done in
assign_parm_setup_block.  This routine attempts to use the mode
of the incoming register as the mode of that stack slot, if the
sizes match.

This means that the DECL_RTL for an argument of struct type may
now have mode Pmode, even if that type cannot reliably be operated
on in Pmode, which causes ICEs in several test cases for me.

Note that when REG_PARM_STACK_SPACE is non-zero, this problem does
not occur, because in this case, function.c allocates the stack slot
for the parameter's DECL_RTL in the register save area, using a
different function assign_parm_find_stack_rtl.   *That* function
will keep the mode as BLKmode even if the incoming register is
in another mode.

Removing the above two lines from rs6000_function_arg fixes this
problem, and does not appear to introduce any problem for the
ELFv1 ABI case either (full bootstrap / regtest still passes).

Looking back at the ChangeLog, those two lines where added by
Alan Modra back in 2004:
http://gcc.gnu.org/ml/gcc/2004-11/msg00617.html
to fix ICEs after a change by Richard Henderson:
http://gcc.gnu.org/ml/gcc/2004-11/msg00569.html
but Richard shortly afterwards reverted that change again
since it caused problems elsewhere too:
http://gcc.gnu.org/ml/gcc/2004-11/msg00751.html

So all in all, it seems this change by Alan was simply not
necessary and can be reverted.


Bye,
Ulrich


ChangeLog:

2013-11-11  Ulrich Weigand  <Ulrich.Weigand@de.ibm.com>
	    Alan Modra  <amodra@gmail.com>

	* config/rs6000/rs6000-protos.h (rs6000_reg_parm_stack_space):
	Add prototype.
	* config/rs6000/rs6000.h (RS6000_REG_SAVE): Remove.
	(REG_PARM_STACK_SPACE): Call rs6000_reg_parm_stack_space.
	* config/rs6000/rs6000.c (rs6000_parm_needs_stack): New function.
	(rs6000_function_parms_need_stack): Likewise.
	(rs6000_reg_parm_stack_space): Likewise.
	(rs6000_function_arg): Do not replace BLKmode by Pmode when
	returning a register argument.

Index: gcc/gcc/config/rs6000/rs6000-protos.h
===================================================================
--- gcc.orig/gcc/config/rs6000/rs6000-protos.h
+++ gcc/gcc/config/rs6000/rs6000-protos.h
@@ -158,6 +158,7 @@ extern tree altivec_resolve_overloaded_b
 extern rtx rs6000_libcall_value (enum machine_mode);
 extern rtx rs6000_va_arg (tree, tree);
 extern int function_ok_for_sibcall (tree);
+extern int rs6000_reg_parm_stack_space (tree);
 extern void rs6000_elf_declare_function_name (FILE *, const char *, tree);
 extern bool rs6000_elf_in_small_data_p (const_tree);
 #ifdef ARGS_SIZE_RTX
Index: gcc/gcc/config/rs6000/rs6000.h
===================================================================
--- gcc.orig/gcc/config/rs6000/rs6000.h
+++ gcc/gcc/config/rs6000/rs6000.h
@@ -1527,10 +1527,6 @@ extern enum reg_class rs6000_constraints
 #define FRAME_GROWS_DOWNWARD (flag_stack_protect != 0			\
 			      || (flag_sanitize & SANITIZE_ADDRESS) != 0)
 
-/* Size of the outgoing register save area */
-#define RS6000_REG_SAVE \
-  ((DEFAULT_ABI == ABI_V4 ? 0 : 32) << (TARGET_64BIT ? 1 : 0))
-
 /* Size of the fixed area on the stack */
 #define RS6000_SAVE_AREA \
   ((DEFAULT_ABI == ABI_V4 ? 8 : 24) << (TARGET_64BIT ? 1 : 0))
@@ -1588,7 +1584,7 @@ extern enum reg_class rs6000_constraints
 /* Define this if stack space is still allocated for a parameter passed
    in a register.  The value is the number of bytes allocated to this
    area.  */
-#define REG_PARM_STACK_SPACE(FNDECL)	RS6000_REG_SAVE
+#define REG_PARM_STACK_SPACE(FNDECL) rs6000_reg_parm_stack_space((FNDECL))
 
 /* Define this if the above stack space is to be considered part of the
    space allocated by the caller.  */
Index: gcc/gcc/config/rs6000/rs6000.c
===================================================================
--- gcc.orig/gcc/config/rs6000/rs6000.c
+++ gcc/gcc/config/rs6000/rs6000.c
@@ -10093,9 +10093,6 @@ rs6000_function_arg (cumulative_args_t c
 	  if (TARGET_32BIT && TARGET_POWERPC64)
 	    return rs6000_mixed_function_arg (mode, type, align_words);
 
-	  if (mode == BLKmode)
-	    mode = Pmode;
-
 	  return gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
 	}
       else
@@ -10250,6 +10247,139 @@ rs6000_pass_by_reference (cumulative_arg
   return 0;
 }
 
+/* Process parameter of type TYPE after ARGS_SO_FAR parameters were
+   already processes.  Return true if the parameter must be passed
+   (fully or partially) on the stack.  */
+
+static bool
+rs6000_parm_needs_stack (cumulative_args_t args_so_far, tree type)
+{
+  enum machine_mode mode;
+  int unsignedp;
+  rtx entry_parm;
+
+  /* Catch errors.  */
+  if (type == NULL || type == error_mark_node)
+    return true;
+
+  /* Handle types with no storage requirement.  */
+  if (TYPE_MODE (type) == VOIDmode)
+    return false;
+
+  /* Handle complex types.  */
+  if (TREE_CODE (type) == COMPLEX_TYPE)
+    return (rs6000_parm_needs_stack (args_so_far, TREE_TYPE (type))
+	    || rs6000_parm_needs_stack (args_so_far, TREE_TYPE (type)));
+
+  /* Handle transparent aggregates.  */
+  if ((TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == RECORD_TYPE)
+      && TYPE_TRANSPARENT_AGGR (type))
+    type = TREE_TYPE (first_field (type));
+
+  /* See if this arg was passed by invisible reference.  */
+  if (pass_by_reference (get_cumulative_args (args_so_far),
+			 TYPE_MODE (type), type, true))
+    type = build_pointer_type (type);
+
+  /* Find mode as it is passed by the ABI.  */
+  unsignedp = TYPE_UNSIGNED (type);
+  mode = promote_mode (type, TYPE_MODE (type), &unsignedp);
+
+  /* If we must pass in stack, we need a stack.  */
+  if (rs6000_must_pass_in_stack (mode, type))
+    return true;
+
+  /* If there is no incoming register, we need a stack.  */
+  entry_parm = rs6000_function_arg (args_so_far, mode, type, true);
+  if (entry_parm == NULL)
+    return true;
+
+  /* Likewise if we need to pass both in registers and on the stack.  */
+  if (GET_CODE (entry_parm) == PARALLEL
+      && XEXP (XVECEXP (entry_parm, 0, 0), 0) == NULL_RTX)
+    return true;
+
+  /* Also true if we're partially in registers and partially not.  */
+  if (rs6000_arg_partial_bytes (args_so_far, mode, type, true) != 0)
+    return true;
+
+  /* Update info on where next arg arrives in registers.  */
+  rs6000_function_arg_advance (args_so_far, mode, type, true);
+  return false;
+}
+
+/* Return true if FUN has no prototype, has a variable argument
+   list, or passes any parameter in memory.  */
+
+static bool
+rs6000_function_parms_need_stack (tree fun)
+{
+  function_args_iterator args_iter;
+  tree arg_type;
+  CUMULATIVE_ARGS args_so_far_v;
+  cumulative_args_t args_so_far;
+
+  if (!fun)
+    /* Must be a libcall, all of which only use reg parms.  */
+    return false;
+  if (!TYPE_P (fun))
+    fun = TREE_TYPE (fun);
+
+  /* Varargs functions need the parameter save area.  */
+  if (!prototype_p (fun) || stdarg_p (fun))
+    return true;
+
+  INIT_CUMULATIVE_INCOMING_ARGS (args_so_far_v, fun, NULL_RTX);
+  args_so_far = pack_cumulative_args (&args_so_far_v);
+
+  if (aggregate_value_p (TREE_TYPE (fun), fun))
+    {
+      tree type = build_pointer_type (TREE_TYPE (fun));
+      rs6000_parm_needs_stack (args_so_far, type);
+    }
+
+  FOREACH_FUNCTION_ARGS (fun, arg_type, args_iter)
+    if (rs6000_parm_needs_stack (args_so_far, arg_type))
+      return true;
+
+  return false;
+}
+
+/* Return the size of the REG_PARM_STACK_SPACE are for FUN.  This is
+   usually a constant depending on the ABI.  However, in the ELFv2 ABI
+   the register parameter area is optional when calling a function that
+   has a prototype is scope, has no variable argument list, and passes
+   all parameters in registers.  */
+
+int
+rs6000_reg_parm_stack_space (tree fun)
+{
+  int reg_parm_stack_space;
+
+  switch (DEFAULT_ABI)
+    {
+    default:
+      reg_parm_stack_space = 0;
+      break;
+
+    case ABI_AIX:
+    case ABI_DARWIN:
+      reg_parm_stack_space = TARGET_64BIT ? 64 : 32;
+      break;
+
+    case ABI_ELFv2:
+      /* ??? Recomputing this every time is a bit expensive.  Is there
+	 a place to cache this information?  */
+      if (rs6000_function_parms_need_stack (fun))
+	reg_parm_stack_space = TARGET_64BIT ? 64 : 32;
+      else
+	reg_parm_stack_space = 0;
+      break;
+    }
+
+  return reg_parm_stack_space;
+}
+
 static void
 rs6000_move_block_from_reg (int regno, rtx x, int nregs)
 {
-- 
  Dr. Ulrich Weigand
  GNU/Linux compilers and toolchain
  Ulrich.Weigand@de.ibm.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]