Fix 32-bit Sparc sibling call predicate

David S. Miller davem@redhat.com
Wed Apr 24 09:42:00 GMT 2002


[ Mark, skip the verbiage, I explained this to you in private
  email.  I'm just looking for 3.1 branch approval for the
  fix so I can close this PR :-) ]

This is a fix for PR target/6420.  Actually, it probably helps
close a lot of 32-bit Sparc c++ GNATS entries.

Sparc background:

	When we call a function that returns a structure and we are
	generating code for 32-bit Sparc, we emit a sequence that
	looks like this:

		call
		 delay slot
		UNIMP

	And the callee knows to return to one instruction past the
	UNIMP instruction.  The UNIMP instruction is put there for
	two reasons:

        1) When prototypes aren't available, it can be used by
	   the callee to verify the amount of space the caller
	   thinks the structure returned should take up.  The
	   size is encoded into the UNIMP instruction and the
	   caller can read it and extract that value.

	2) It traps the cpu if the caller does not expect to
	   return an aggregate.  In this case the caller returns
	   to the UNIMP instruction and an illegal instruction
	   is signalled.

What this amounts to is that in order to return successfully the
callee has to adjust the return address by 4 bytes.

Sibling calls work in such a way that this cannot be done correctly.
When a sibling call occurs, it does not adjust the return address
and if we were to emit one for a call to a function returning a
structure we'd end up at the UNIMP instruction.

Normally, sibling call transformation will fail because the function
being called returns a structure.  This test is performed in
expand_call, with a check of structure_return_addr != NULL_RTX.

However, it is possible for a sibling call to still slip through.
Consider a snippet of c++ code that looks like this:

object foo(void)
{
  return object();
}

The way this is implemented is that the address of the structure
return area is passed to the object constructor as the "this" pointer.
The constructor doesn't return anything.  So it looks OK for a sibling
call.  This is wrong.

The fix is to indicate, via FUNCTION_OK_FOR_SIBCALL, that no sibling
calls are to be emitted if this is 32-bit Sparc and the current
function returns a structure.

[ Sparc experts paying attention will note that, it is actually
  possible to still emit the sibling call in this case.  The way
  to go about this would be to notice this specific case (emitting
  a sibling call, current function returns struct, but function being
  called does not) and in that case diddle the return address before
  the instructions that implement the sibling call.  I am not going
  to even consider trying this out in the 3.1 branch...  I also note
  in passing that last time I checked SunPRO didn't attempt to emit
  tail calls for crazy situations like this, so it would be a checkbox
  for GCC ;-) ]

All of the libstdc++ failures in PR target/6420 were due to this bug.
I am hoping a bunch of the Solaris c++ testsuite failures are cured
by this fix too! :-)  This bug has existed since the sibling call code
went into gcc and Sparc support was added.

Mark, ok for the 3.1 branch?

2002-04-24  David S. Miller  <davem@redhat.com>

	PR target/6420
	* config/sparc/sparc.h (FUNCTION_OK_FOR_SIBCALL): Return false if
	32-bit Sparc and current_function_returns_struct is true.

Index: config/sparc/sparc.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/sparc/sparc.h,v
retrieving revision 1.161.2.17
diff -u -r1.161.2.17 sparc.h
--- config/sparc/sparc.h	18 Apr 2002 23:34:11 -0000	1.161.2.17
+++ config/sparc/sparc.h	24 Apr 2002 15:03:04 -0000
@@ -1914,8 +1914,23 @@
 #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)
+   we do not allow indirect calls to be optimized into sibling calls.
+
+   Also, on sparc 32-bit we cannot emit a sibling call when the
+   current function returns a structure.  This is because the "unimp
+   after call" convention would cause the callee to return to the
+   wrong place.  The generic code already disallows cases where the
+   function being called returns a structure.
+
+   It may seem strange how this last case could occur.  Usually there
+   is code after the call which jumps to epilogue code which dumps the
+   return value into the struct return area.  That ought to invalidate
+   the sibling call right?  Well, in the c++ case we can end up passing
+   the pointer to the struct return area to a constructor (which returns
+   void) and then nothing else happens.  Such a sibling call would look
+   valid without the added check here.  */
+#define FUNCTION_OK_FOR_SIBCALL(DECL) \
+	(! TARGET_FLAT && (TARGET_ARCH64 || ! current_function_returns_struct))
 
 /* Generate RTL to flush the register windows so as to make arbitrary frames
    available.  */



More information about the Gcc-patches mailing list