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] Fix PR middle-end/32602 and middle-end/32603


In store_one_arg, we attempt to detect if the value to be copied to a
function call's outgoing parameter overlaps it on the stack.  If so, we
disallow sibcalls by setting the "sibcall_failure" flag.

Here's the code that attempts to detect that condition:


  if (arg->locate.offset.constant > i)
    {
      if (arg->locate.offset.constant < i + INTVAL (size_rtx))
        sibcall_failure = 1;
    }
  else if (arg->locate.offset.constant < i)
    {
      if (i < arg->locate.offset.constant + INTVAL (size_rtx))
        sibcall_failure = 1;
    }

Unfortunately, there are a couple of problems with this code.  The first
is that we assume if arg->locate.offset.constant == i, then the value is
already where it needs to be and so a sibcall is acceptable.  However,
if the argument is split between the stack and registers it is possible
that both the incoming value and the outgoing argument will have the
same stack offset but will be misaligned with each other.  An example of
this is given in pr32602.  To address this, we can verify that the total
size of the incoming value is the same as the stack usage of the
outgoing argument.

The second problem is this code:

  if (arg->locate.offset.constant < i + INTVAL (size_rtx))

uses "size_rtx" to determine the size of the argument on the stack.
However, this indicates the entire size of the object -- which can be
incorrect if the variable is split between registers and the stack.  The
size which is actually on the stack can be found as
"arg->locate.size.constant".  Since the size on the stack is always less
than or equal to the total size of the object, this wasn't causing bad
codegen, but could result in missed sibcall opportunities, as shown in
pr32603.

The attached patch has been regtested against arm-none-elf, bootstrapped
and regtested against ppc-apple-darwin9 and i686-linux-pc-gnu.

OK for mainline?

Thanks -

Josh

2007-07-03  Josh Conner  <jconner@apple.com>

	PR middle-end/32602
	PR middle-end/32603
	* calls.c (store_one_arg): Handle arguments which are partially
	on the stack when detecting argument overlap.

2007-07-03  Josh Conner  <jconner@apple.com>

	PR middle-end/32602
	* gcc.dg/sibcall-8.c: New test.

2007-07-03  Josh Conner  <jconner@apple.com>

	PR middle-end/32603
	* gcc.target/arm/sibcall-1.c: New test.

Index: gcc/calls.c
===================================================================
--- gcc/calls.c	(revision 125651)
+++ gcc/calls.c	(working copy)
@@ -4323,7 +4323,21 @@ store_one_arg (struct arg_data *arg, rtx
 		}
 	      else if (arg->locate.offset.constant < i)
 		{
-		  if (i < arg->locate.offset.constant + INTVAL (size_rtx))
+		  /* Use arg->locate.size.constant instead of size_rtx
+		     because we only care about the part of the argument
+		     on the stack.  */
+		  if (i < (arg->locate.offset.constant
+			   + arg->locate.size.constant))
+		    sibcall_failure = 1;
+		}
+	      else
+		{
+		  /* Even though they appear to be at the same location,
+		     if part of the outgoing argument is in registers,
+		     they aren't really at the same location.  Check for
+		     this by making sure that the incoming size is the
+		     same as the outgoing size.  */
+		  if (arg->locate.size.constant != INTVAL (size_rtx))
 		    sibcall_failure = 1;
 		}
 	    }
/* { dg-do compile { target { arm32 } } } */
/* { dg-options "-O2" } */

#define noinline __attribute__((noinline))

typedef struct {
  int data[4];
} arr16_t;

int result = 0;

void noinline func2 (int i, int j, arr16_t arr)
{
  result = (arr.data[0] != 1
	    || arr.data[1] != 2
	    || arr.data[2] != 3
	    || arr.data[3] != 4);
}

void func1 (int i, int j, int k, int l, int m, int n, arr16_t a)
{
  func2(i, j, a);
}

int main(int argc, const char *argv[])
{
  arr16_t arr = {{1, 2, 3, 4}};
    
  func1(0, 0, 0, 0, 0, 0, arr);
  return result;
}

/* { dg-final { scan-assembler "\tb\tfunc2\n" } } */

/* { dg-do run } */
/* { dg-options "-O2 -foptimize-sibling-calls" } */

typedef struct {
  int data[4];
} arr16_t;

int result = 0;

void func2(int i, int j, arr16_t arr)
{
  result = (arr.data[0] != 1
	    || arr.data[1] != 2
	    || arr.data[2] != 3
	    || arr.data[3] != 4);
}

void func1(int i, int j, int k, arr16_t a)
{
  func2(i, j, a);
}

int main(int argc, const char *argv[])
{
  arr16_t arr = {{1, 2, 3, 4}};
    
  func1(0, 0, 0, arr);
  return result;
}


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