Patch RFC: 32-bit x86: Fix regparm for -fsplit-stack with nested function

Message ID
State New
Headers show

Commit Message

Ian Lance Taylor March 17, 2017, 12:11 a.m.
While working on a modification to libgo I ran into a bug with
-fsplit-stack support for 32-bit x86.  When a function has a stack
frame more than 256 bytes, it needs a scratch register to add add the
stack frame size to the stack pointer register.  This happens at the
very beginning of the function, so the scratch register can be any
caller saved register that does not hold a function argument.  On
32-bit x86 such a register is normally available, but that may not be
the case if the registers are used for parameters.  The
ix86_function_regparm function works out how many registers to use for
parameters for a local function.  It checks to make sure that it
leaves a scratch register available when using -fsplit-stack, and it
checks to make sure that it leaves the static chain register available
if it is required.  Unfortunately, it failed to check for the
combination of the two, so using -fsplit-stack with a nested local
function that uses a static chain and has a large enough stack frame
could in some cases cause the compiler to ICE saying "-fsplit-stack
does not support 2 register parameters for a nested function".

This patch fixes this case.  It seems fairly obvious to me, so I plan
to check it in sometime tomorrow after giving people a chance to
comment.  I don't have a test case in C, but future versions of libgo
will implicitly include a test case.


2017-03-16  Ian Lance Taylor  <>

* config/i386/i386.c (ix86_function_regparm): Save an extra
register for -fsplit-stack with DECL_STATIC_CHAIN.


Index: config/i386/i386.c
--- config/i386/i386.c	(revision 246212)
+++ config/i386/i386.c	(working copy)
@@ -7975,8 +7975,14 @@ 
 		local_regparm = 2;
 	      /* Save a register for the split stack.  */
-	      if (local_regparm == 3 && flag_split_stack)
-		local_regparm = 2;
+	      if (flag_split_stack)
+		{
+		  if (local_regparm == 3)
+		    local_regparm = 2;
+		  else if (local_regparm == 2
+			   && DECL_STATIC_CHAIN (target->decl))
+		    local_regparm = 1;
+		}
 	      /* Each fixed register usage increases register pressure,
 		 so less registers should be used for argument passing.