[PATCH] Alignment and split function args

Paul Brook paul@codesourcery.com
Fri Feb 27 03:36:00 GMT 2004


I've run into a problem with argument alignment when function arguments are 
split between registers and memory.

Consider the following example:
struct s {int val[4]};
void foo (int a, struct s b, long long c);

On arm a and the first 3 words of b are passed in registers. The last word of 
b, and c are passed on the stack. For some subtargets STACK_BOUNDARY == 32,  
PREFERRED_STACK_BOUNDARY == 64, and long long types require doubleword 
(64-bit) alignment. This results in a pad word before arg c.

On entry to the function we allocate 3 extra words of "pretend" stack args and 
fill them with in-regs part of b. The arg pointer is adjusted to include 
these, but only aligned to 32 bits.

We then perform alignment of the following arg (c) relative to the new arg 
pointer (ie. that start of b). This results in the incorrect location (and 
alignment) for c.

We should really be aligning relative to the original stack pointer, ie. 
before and pretend args are pushed.

AFAICS arm(iwmmxt) is the only target currently effected. All other ports 
either have STACK_BOUNDARY == PREFERRED_STACK_BOUNDARY or do not allow 
splitting of args between registers and memory.

Patch below tested with cross-compiler to arm-none-elf and bootstrapped on 
hppa2.0w-hp-hpux11.11
Ok?

The other alternative is to make pretend_args a multiple of 
PREFERRED_STACK_BOUNDARY. This would waste stack space for no good reason.

Paul

2004-02-16  Paul Brook  <paul@codesourcery.com>

	* function.c (assign_parms): Don't count pretend args for alignment.

Index: function.c
===================================================================
RCS file: /var/cvsroot/gcc-cvs/gcc/gcc/function.c,v
retrieving revision 1.496
diff -u -p -r1.496 function.c
--- a/function.c	19 Feb 2004 08:00:47 -0000	1.496
+++ b/function.c	27 Feb 2004 00:33:17 -0000
@@ -4285,6 +4285,7 @@ assign_parms (tree fndecl)
   /* Total space needed so far for args on the stack,
      given as a constant and a tree-expression.  */
   struct args_size stack_args_size;
+  HOST_WIDE_INT extra_pretend_bytes = 0;
   tree fntype = TREE_TYPE (fndecl);
   tree fnargs = DECL_ARGUMENTS (fndecl), orig_fnargs;
   /* This is used for the arg pointer when referring to stack args.  */
@@ -4576,15 +4577,19 @@ assign_parms (tree fndecl)
 		 bits.  We must preserve this invariant by rounding
 		 CURRENT_FUNCTION_PRETEND_ARGS_SIZE up to a stack
 		 boundary.  */
+
+	      /* We assume at most one partial arg, and it must be the first
+	         argument on the stack.  */
+	      if (extra_pretend_bytes || current_function_pretend_args_size)
+		abort ();
+
 	      pretend_bytes = partial * UNITS_PER_WORD;
 	      current_function_pretend_args_size
 		= CEIL_ROUND (pretend_bytes, STACK_BYTES);
 
-	      /* If PRETEND_BYTES != CURRENT_FUNCTION_PRETEND_ARGS_SIZE,
-		 insert the padding before the start of the first pretend
-		 argument.  */
-	      stack_args_size.constant
-		= (current_function_pretend_args_size - pretend_bytes);
+	      /* We want to align relative to the actual stack pointer, so
+	         don't include this in the stack size until later.  */
+	      extra_pretend_bytes = current_function_pretend_args_size;
 	    }
 	}
 #endif
@@ -4593,6 +4598,13 @@ assign_parms (tree fndecl)
       locate_and_pad_parm (promoted_mode, passed_type, in_regs,
 			   entry_parm ? partial : 0, fndecl,
 			   &stack_args_size, &locate);
+      /* Adjust offsets to include pretend args, unless this is the
+         split arg.  */
+      if (pretend_bytes == 0)
+	{
+	  locate.slot_offset.constant += extra_pretend_bytes;
+	  locate.offset.constant += extra_pretend_bytes;
+	}
 
       {
 	rtx offset_rtx;
@@ -4674,7 +4686,7 @@ assign_parms (tree fndecl)
 #endif
 	  )
 	{
-	  stack_args_size.constant += pretend_bytes + locate.size.constant;
+	  stack_args_size.constant += locate.size.constant;
 	  if (locate.size.var)
 	    ADD_PARM_SIZE (stack_args_size, locate.size.var);
 	}
@@ -5286,6 +5298,8 @@ assign_parms (tree fndecl)
 
   last_parm_insn = get_last_insn ();
 
+  /* We have aligned all the args, so add space for the pretend args.  */
+  stack_args_size.constant += extra_pretend_bytes;
   current_function_args_size = stack_args_size.constant;
 
   /* Adjust function incoming argument size for alignment and



More information about the Gcc-patches mailing list