diff mbox

Drop excess size used for run time allocated stack variables.

Message ID 20160503141753.GA17351@linux.vnet.ibm.com
State New
Headers show

Commit Message

Dominik Vogt May 3, 2016, 2:17 p.m. UTC
Version two of the patch including a test case.

On Mon, May 02, 2016 at 09:10:25AM -0600, Jeff Law wrote:
> On 04/29/2016 04:12 PM, Dominik Vogt wrote:
> >The attached patch removes excess stack space allocation with
> >alloca in some situations.  Plese check the commit message in the
> >patch for details.

> However, I would strongly recommend some tests, even if they are
> target specific.  You can always copy pr36728-1 into the s390x
> directory and look at size of the generated stack.  Simliarly for
> pr50938 for x86.

However, x86 uses the "else" branch in round_push, i.e. it uses
"virtual_preferred_stack_boundary_rtx" to calculate the number of
bytes to add for stack alignment.  That value is unknown at the
time round_push is called, so the test case fails on such targets,
and I've no idea how to fix this properly.

Ciao

Dominik ^_^  ^_^

Comments

Jeff Law May 19, 2016, 11:11 p.m. UTC | #1
On 05/03/2016 08:17 AM, Dominik Vogt wrote:
> Version two of the patch including a test case.
>
> On Mon, May 02, 2016 at 09:10:25AM -0600, Jeff Law wrote:
>> On 04/29/2016 04:12 PM, Dominik Vogt wrote:
>>> The attached patch removes excess stack space allocation with
>>> alloca in some situations.  Plese check the commit message in the
>>> patch for details.
>
>> However, I would strongly recommend some tests, even if they are
>> target specific.  You can always copy pr36728-1 into the s390x
>> directory and look at size of the generated stack.  Simliarly for
>> pr50938 for x86.
>
> However, x86 uses the "else" branch in round_push, i.e. it uses
> "virtual_preferred_stack_boundary_rtx" to calculate the number of
> bytes to add for stack alignment.  That value is unknown at the
> time round_push is called, so the test case fails on such targets,
> and I've no idea how to fix this properly.
I'm not going to be able to complete a review of this today.  As I dig 
into the history of this code I came across pr34548, pr47353, & pr46894 
(and their associated discussions on the lists) that we'll need to 
verify we don't break.  This is a bit of a mess and I think the code 
needs some TLC before we start hacking it up further.

Let's start with clean up of dead code:

  /* We will need to ensure that the address we return is aligned to
      REQUIRED_ALIGN.  If STACK_DYNAMIC_OFFSET is defined, we don't
      always know its final value at this point in the compilation (it
      might depend on the size of the outgoing parameter lists, for
      example), so we must align the value to be returned in that case.
      (Note that STACK_DYNAMIC_OFFSET will have a default nonzero value if
      STACK_POINTER_OFFSET or ACCUMULATE_OUTGOING_ARGS are defined).
      We must also do an alignment operation on the returned value if
      the stack pointer alignment is less strict than REQUIRED_ALIGN.

      If we have to align, we must leave space in SIZE for the hole
      that might result from the alignment operation.  */

   must_align = (crtl->preferred_stack_boundary < required_align);
   if (must_align)
     {
       if (required_align > PREFERRED_STACK_BOUNDARY)
         extra_align = PREFERRED_STACK_BOUNDARY;
       else if (required_align > STACK_BOUNDARY)
         extra_align = STACK_BOUNDARY;
       else
         extra_align = BITS_PER_UNIT;
     }

   /* ??? STACK_POINTER_OFFSET is always defined now.  */
#if defined (STACK_DYNAMIC_OFFSET) || defined (STACK_POINTER_OFFSET)
   must_align = true;
   extra_align = BITS_PER_UNIT;
#endif

If we look at defaults.h, it always defines STACK_POINTER_OFFSET.  So 
all the code above I think collapses to:

   must_align = true;
   extra_align = BITS_PER_UNIT

And the only other assignment to must_align assigns it the value "true". 
  There are two conditionals on must_align that looks like

if (must_align)
   {
     CODE;
   }

We should remove the conditional and pull CODE out an indentation level. 
  And remove all remnants of must_align.

I don't think that changes your patch in any way.  Hopefully it makes 
the whole function somewhat easier to grok.

Thoughts?

jeff
Bernd Schmidt June 9, 2016, noon UTC | #2
On 05/20/2016 01:11 AM, Jeff Law wrote:
> Let's start with clean up of dead code:
>
>  /* We will need to ensure that the address we return is aligned to
>      REQUIRED_ALIGN.  If STACK_DYNAMIC_OFFSET is defined, we don't
>      always know its final value at this point in the compilation (it
>      might depend on the size of the outgoing parameter lists, for
>      example), so we must align the value to be returned in that case.
>      (Note that STACK_DYNAMIC_OFFSET will have a default nonzero value if
>      STACK_POINTER_OFFSET or ACCUMULATE_OUTGOING_ARGS are defined).
>      We must also do an alignment operation on the returned value if
>      the stack pointer alignment is less strict than REQUIRED_ALIGN.
>
>      If we have to align, we must leave space in SIZE for the hole
>      that might result from the alignment operation.  */
>
>   must_align = (crtl->preferred_stack_boundary < required_align);
>   if (must_align)
>     {
>       if (required_align > PREFERRED_STACK_BOUNDARY)
>         extra_align = PREFERRED_STACK_BOUNDARY;
>       else if (required_align > STACK_BOUNDARY)
>         extra_align = STACK_BOUNDARY;
>       else
>         extra_align = BITS_PER_UNIT;
>     }
>
>   /* ??? STACK_POINTER_OFFSET is always defined now.  */
> #if defined (STACK_DYNAMIC_OFFSET) || defined (STACK_POINTER_OFFSET)
>   must_align = true;
>   extra_align = BITS_PER_UNIT;
> #endif
>
> If we look at defaults.h, it always defines STACK_POINTER_OFFSET.  So
> all the code above I think collapses to:
>
>   must_align = true;
>   extra_align = BITS_PER_UNIT

(Cc'ing rth because portions of this seem to be his, r165240).

I kind of want to approach this from a different angle; let's look at 
extra_align. The way this is used subsequently makes it appear to be 
misnamed. It looks like it should hold the stack alignment we can assume:

   unsigned extra = (required_align - extra_align) / BITS_PER_UNIT;

   size = plus_constant (Pmode, size, extra);
[...]
   if (extra && size_align > extra_align)
     size_align = extra_align;

(where size_align is the known alignment of the size of the block to be 
allocated). If I'm reading this right, then the first part of the 
cleanup ought to be to get the naming right.

So why BITS_PER_UNIT? Shouldn't it at least be STACK_BOUNDARY? Let's 
look at the previous block a little more closely.

 >   must_align = (crtl->preferred_stack_boundary < required_align);

[ crtl->preferred_stack_boundary is initialized to STACK_BOUNDARY in 
cfgexpand and only ever increased ]

 >   if (must_align)
 >     {

[ if must_align, then required_align > crtl->p_s_b >= STACK_BOUNDARY ]

 >       if (required_align > PREFERRED_STACK_BOUNDARY)
 >         extra_align = PREFERRED_STACK_BOUNDARY;

[ so far so good ]

 >       else if (required_align > STACK_BOUNDARY)
 >         extra_align = STACK_BOUNDARY;

[ always true, right? ]

 >       else
 >         extra_align = BITS_PER_UNIT;
 >     }

[ dead code, right? ]

So we're left with the question of why extra_align is set to 
BITS_PER_UNIT for STACK_DYNAMIC_OFFSET, and I can't really see a reason 
to do that either. AFAIK the minimum alignment of the stack is always 
STACK_BOUNDARY, and it's possible we could do better.

As far as I can tell, no definition of STACK_DYNAMIC_OFFSET differs 
substantially from the default definition in function.c. Why couldn't we 
round up the outgoing_args_size to the preferred stack boundary (or a 
new value to keep track of the required alignment for dynamic 
allocations) before instantiating dynamic_offset? We then wouldn't have 
to add extra alignment for it here.

This rounding seems to happen anyway in port's frame calculations, e.g. 
here in i386:

  if (ACCUMULATE_OUTGOING_ARGS
       && (!crtl->is_leaf || cfun->calls_alloca
           || ix86_current_function_calls_tls_descriptor))
     {
       offset += crtl->outgoing_args_size;
       frame->outgoing_arguments_size = crtl->outgoing_args_size;
     }
   else
     frame->outgoing_arguments_size = 0;

   /* Align stack boundary.  Only needed if we're calling another function
      or using alloca.  */
   if (!crtl->is_leaf || cfun->calls_alloca
       || ix86_current_function_calls_tls_descriptor)
     offset = ROUND_UP (offset, preferred_alignment);

or here in rs6000:
   info->parm_size    = RS6000_ALIGN (crtl->outgoing_args_size,
                                          TARGET_ALTIVEC ? 16 : 8);

which could probably use the default definition of STACK_DYNAMIC_OFFSET 
instead of this, if the outgoing_args_size was rounded appropriately:
#define STACK_DYNAMIC_OFFSET(FUNDECL)                                   \
   (RS6000_ALIGN (crtl->outgoing_args_size,                              \
                  (TARGET_ALTIVEC || TARGET_VSX) ? 16 : 8)               \
    + (STACK_POINTER_OFFSET))


Bernd
Dominik Vogt June 21, 2016, 9:35 a.m. UTC | #3
What do we do now with the two patches?  At the moment, the
functional patch depends on the changes in the cleanup patch, so
it cannot be applied on its own.  Options:

(with the requested cleanup in the functional patch)

 1) Apply both patches as they are now and do further cleanup on
    top of it.
 2) Rewrite the functional patch so that it applies without the
    cleanup patch and commit it now.
 3) Look into the suggested cleanup now and adapt the functional
    patch to it when its ready.

Actually I'd prefer (1) or (2) to just get the functional patch
off my desk.  I agree that the cleanup is very useful, but there's
not relation between the cleanup and the functional stuff except
that they touch the same code.  Having the functional patch
applied would simplify further work for me.

On Thu, Jun 09, 2016 at 02:00:21PM +0200, Bernd Schmidt wrote:
> On 05/20/2016 01:11 AM, Jeff Law wrote:
> >Let's start with clean up of dead code:
> >
> > /* We will need to ensure that the address we return is aligned to
> >     REQUIRED_ALIGN.  If STACK_DYNAMIC_OFFSET is defined, we don't
> >     always know its final value at this point in the compilation (it
> >     might depend on the size of the outgoing parameter lists, for
> >     example), so we must align the value to be returned in that case.
> >     (Note that STACK_DYNAMIC_OFFSET will have a default nonzero value if
> >     STACK_POINTER_OFFSET or ACCUMULATE_OUTGOING_ARGS are defined).
> >     We must also do an alignment operation on the returned value if
> >     the stack pointer alignment is less strict than REQUIRED_ALIGN.
> >
> >     If we have to align, we must leave space in SIZE for the hole
> >     that might result from the alignment operation.  */
> >
> >  must_align = (crtl->preferred_stack_boundary < required_align);
> >  if (must_align)
> >    {
> >      if (required_align > PREFERRED_STACK_BOUNDARY)
> >        extra_align = PREFERRED_STACK_BOUNDARY;
> >      else if (required_align > STACK_BOUNDARY)
> >        extra_align = STACK_BOUNDARY;
> >      else
> >        extra_align = BITS_PER_UNIT;
> >    }
> >
> >  /* ??? STACK_POINTER_OFFSET is always defined now.  */
> >#if defined (STACK_DYNAMIC_OFFSET) || defined (STACK_POINTER_OFFSET)
> >  must_align = true;
> >  extra_align = BITS_PER_UNIT;
> >#endif
> >
> >If we look at defaults.h, it always defines STACK_POINTER_OFFSET.  So
> >all the code above I think collapses to:
> >
> >  must_align = true;
> >  extra_align = BITS_PER_UNIT
> 
> (Cc'ing rth because portions of this seem to be his, r165240).
> 
> I kind of want to approach this from a different angle; let's look
> at extra_align. The way this is used subsequently makes it appear to
> be misnamed. It looks like it should hold the stack alignment we can
> assume:
> 
>   unsigned extra = (required_align - extra_align) / BITS_PER_UNIT;
> 
>   size = plus_constant (Pmode, size, extra);
> [...]
>   if (extra && size_align > extra_align)
>     size_align = extra_align;
> 
> (where size_align is the known alignment of the size of the block to
> be allocated). If I'm reading this right, then the first part of the
> cleanup ought to be to get the naming right.
> 
> So why BITS_PER_UNIT? Shouldn't it at least be STACK_BOUNDARY? Let's
> look at the previous block a little more closely.
> 
> >   must_align = (crtl->preferred_stack_boundary < required_align);
> 
> [ crtl->preferred_stack_boundary is initialized to STACK_BOUNDARY in
> cfgexpand and only ever increased ]
> 
> >   if (must_align)
> >     {
> 
> [ if must_align, then required_align > crtl->p_s_b >= STACK_BOUNDARY ]
> 
> >       if (required_align > PREFERRED_STACK_BOUNDARY)
> >         extra_align = PREFERRED_STACK_BOUNDARY;
> 
> [ so far so good ]
> 
> >       else if (required_align > STACK_BOUNDARY)
> >         extra_align = STACK_BOUNDARY;
> 
> [ always true, right? ]
> 
> >       else
> >         extra_align = BITS_PER_UNIT;
> >     }
> 
> [ dead code, right? ]
> 
> So we're left with the question of why extra_align is set to
> BITS_PER_UNIT for STACK_DYNAMIC_OFFSET, and I can't really see a
> reason to do that either. AFAIK the minimum alignment of the stack
> is always STACK_BOUNDARY, and it's possible we could do better.
> 
> As far as I can tell, no definition of STACK_DYNAMIC_OFFSET differs
> substantially from the default definition in function.c. Why
> couldn't we round up the outgoing_args_size to the preferred stack
> boundary (or a new value to keep track of the required alignment for
> dynamic allocations) before instantiating dynamic_offset? We then
> wouldn't have to add extra alignment for it here.
> 
> This rounding seems to happen anyway in port's frame calculations,
> e.g. here in i386:
> 
>  if (ACCUMULATE_OUTGOING_ARGS
>       && (!crtl->is_leaf || cfun->calls_alloca
>           || ix86_current_function_calls_tls_descriptor))
>     {
>       offset += crtl->outgoing_args_size;
>       frame->outgoing_arguments_size = crtl->outgoing_args_size;
>     }
>   else
>     frame->outgoing_arguments_size = 0;
> 
>   /* Align stack boundary.  Only needed if we're calling another function
>      or using alloca.  */
>   if (!crtl->is_leaf || cfun->calls_alloca
>       || ix86_current_function_calls_tls_descriptor)
>     offset = ROUND_UP (offset, preferred_alignment);
> 
> or here in rs6000:
>   info->parm_size    = RS6000_ALIGN (crtl->outgoing_args_size,
>                                          TARGET_ALTIVEC ? 16 : 8);
> 
> which could probably use the default definition of
> STACK_DYNAMIC_OFFSET instead of this, if the outgoing_args_size was
> rounded appropriately:
> #define STACK_DYNAMIC_OFFSET(FUNDECL)                                   \
>   (RS6000_ALIGN (crtl->outgoing_args_size,                              \
>                  (TARGET_ALTIVEC || TARGET_VSX) ? 16 : 8)               \
>    + (STACK_POINTER_OFFSET))

Ciao

Dominik ^_^  ^_^
Jeff Law June 21, 2016, 10:26 p.m. UTC | #4
On 06/21/2016 03:35 AM, Dominik Vogt wrote:
> What do we do now with the two patches?  At the moment, the
> functional patch depends on the changes in the cleanup patch, so
> it cannot be applied on its own.  Options:
>
> (with the requested cleanup in the functional patch)
>
>  1) Apply both patches as they are now and do further cleanup on
>     top of it.
>  2) Rewrite the functional patch so that it applies without the
>     cleanup patch and commit it now.
>  3) Look into the suggested cleanup now and adapt the functional
>     patch to it when its ready.
>
> Actually I'd prefer (1) or (2) to just get the functional patch
> off my desk.  I agree that the cleanup is very useful, but there's
> not relation between the cleanup and the functional stuff except
> that they touch the same code.  Having the functional patch
> applied would simplify further work for me.
I thought Eric had ack'd the cleanup patch with a comment fix, so that 
can move forward and presumably unblock your functional patch.  Right?

So I think the TODO here is for me to fix the comment per Eric's review 
so that you can move forward.  The trick is getting it done before I go 
on PTO at the end of this week :-)

Jeff
Dominik Vogt June 22, 2016, 8:57 a.m. UTC | #5
On Tue, Jun 21, 2016 at 04:26:03PM -0600, Jeff Law wrote:
> On 06/21/2016 03:35 AM, Dominik Vogt wrote:
> >What do we do now with the two patches?  At the moment, the
> >functional patch depends on the changes in the cleanup patch, so
> >it cannot be applied on its own.  Options:
> >
> >(with the requested cleanup in the functional patch)
> >
> > 1) Apply both patches as they are now and do further cleanup on
> >    top of it.
> > 2) Rewrite the functional patch so that it applies without the
> >    cleanup patch and commit it now.
> > 3) Look into the suggested cleanup now and adapt the functional
> >    patch to it when its ready.
> >
> >Actually I'd prefer (1) or (2) to just get the functional patch
> >off my desk.  I agree that the cleanup is very useful, but there's
> >not relation between the cleanup and the functional stuff except
> >that they touch the same code.  Having the functional patch
> >applied would simplify further work for me.
> I thought Eric had ack'd the cleanup patch with a comment fix, so
> that can move forward and presumably unblock your functional patch.
> Right?
> 
> So I think the TODO here is for me to fix the comment per Eric's
> review so that you can move forward.  The trick is getting it done
> before I go on PTO at the end of this week :-)

The comment fix is part of the version of the cleanup patch I
posted, but I've removed some more dead code.  I can handle all of
this if I know what to do exactly.

Ciao

Dominik ^_^  ^_^
diff mbox

Patch

From f4d252eb2c860450b3738591fca36f568c958232 Mon Sep 17 00:00:00 2001
From: Dominik Vogt <vogt@linux.vnet.ibm.com>
Date: Fri, 29 Apr 2016 08:36:59 +0100
Subject: [PATCH] Drop excess size used for run time allocated stack
 variables.

The present calculation sometimes led to more stack memory being used than
necessary with alloca.  First, (STACK_BOUNDARY -1) would be added to the
allocated size:

  size = plus_constant (Pmode, size, extra);
  size = force_operand (size, NULL_RTX);

Then round_push was called and added another (STACK_BOUNDARY - 1) before
rounding down to a multiple of STACK_BOUNDARY.  On s390x this resulted in
adding 14 before rounding down for "x" in the test case pr36728-1.c.

round_push() now takes an argument to inform it about what has already been
added to size.
---
 gcc/explow.c                   | 33 ++++++++++++++++-----------
 gcc/testsuite/gcc.dg/pr50938.c | 52 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr50938.c

diff --git a/gcc/explow.c b/gcc/explow.c
index e0ce201..1858597 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -949,24 +949,30 @@  anti_adjust_stack (rtx adjust)
 }
 
 /* Round the size of a block to be pushed up to the boundary required
-   by this machine.  SIZE is the desired size, which need not be constant.  */
+   by this machine.  SIZE is the desired size, which need not be constant.
+   ALREADY_ADDED is the number of units that have already been added to SIZE
+   for other alignment reasons.
+*/
 
 static rtx
-round_push (rtx size)
+round_push (rtx size, int already_added)
 {
-  rtx align_rtx, alignm1_rtx;
+  rtx align_rtx, add_rtx;
 
   if (!SUPPORTS_STACK_ALIGNMENT
       || crtl->preferred_stack_boundary == MAX_SUPPORTED_STACK_ALIGNMENT)
     {
       int align = crtl->preferred_stack_boundary / BITS_PER_UNIT;
+      int add;
 
       if (align == 1)
 	return size;
 
+      add = (align > already_added) ? align - already_added - 1 : 0;
+
       if (CONST_INT_P (size))
 	{
-	  HOST_WIDE_INT new_size = (INTVAL (size) + align - 1) / align * align;
+	  HOST_WIDE_INT new_size = (INTVAL (size) + add) / align * align;
 
 	  if (INTVAL (size) != new_size)
 	    size = GEN_INT (new_size);
@@ -974,7 +980,7 @@  round_push (rtx size)
 	}
 
       align_rtx = GEN_INT (align);
-      alignm1_rtx = GEN_INT (align - 1);
+      add_rtx = (add > 0) ? GEN_INT (add) : const0_rtx;
     }
   else
     {
@@ -983,15 +989,15 @@  round_push (rtx size)
 	 substituted by the right value in vregs pass and optimized
 	 during combine.  */
       align_rtx = virtual_preferred_stack_boundary_rtx;
-      alignm1_rtx = force_operand (plus_constant (Pmode, align_rtx, -1),
-				   NULL_RTX);
+      add_rtx = force_operand (plus_constant (Pmode, align_rtx, -1), NULL_RTX);
     }
 
   /* CEIL_DIV_EXPR needs to worry about the addition overflowing,
      but we know it can't.  So add ourselves and then do
      TRUNC_DIV_EXPR.  */
-  size = expand_binop (Pmode, add_optab, size, alignm1_rtx,
-		       NULL_RTX, 1, OPTAB_LIB_WIDEN);
+  if (add_rtx != const0_rtx)
+    size = expand_binop (Pmode, add_optab, size, add_rtx,
+			 NULL_RTX, 1, OPTAB_LIB_WIDEN);
   size = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, size, align_rtx,
 			NULL_RTX, 1);
   size = expand_mult (Pmode, size, align_rtx, NULL_RTX, 1);
@@ -1175,6 +1181,7 @@  allocate_dynamic_stack_space (rtx size, unsigned size_align,
   rtx_code_label *final_label;
   rtx final_target, target;
   unsigned extra_align = 0;
+  unsigned extra = 0;
   bool must_align;
 
   /* If we're asking for zero bytes, it doesn't matter what we point
@@ -1275,9 +1282,9 @@  allocate_dynamic_stack_space (rtx size, unsigned size_align,
   extra_align = BITS_PER_UNIT;
 #endif
 
-  if (must_align)
+  if (must_align && required_align > extra_align)
     {
-      unsigned extra = (required_align - extra_align) / BITS_PER_UNIT;
+      extra = (required_align - extra_align) / BITS_PER_UNIT;
 
       size = plus_constant (Pmode, size, extra);
       size = force_operand (size, NULL_RTX);
@@ -1285,7 +1292,7 @@  allocate_dynamic_stack_space (rtx size, unsigned size_align,
       if (flag_stack_usage_info)
 	stack_usage_size += extra;
 
-      if (extra && size_align > extra_align)
+      if (size_align > extra_align)
 	size_align = extra_align;
     }
 
@@ -1304,7 +1311,7 @@  allocate_dynamic_stack_space (rtx size, unsigned size_align,
      momentarily mis-aligning the stack.  */
   if (size_align % MAX_SUPPORTED_STACK_ALIGNMENT != 0)
     {
-      size = round_push (size);
+      size = round_push (size, extra);
 
       if (flag_stack_usage_info)
 	{
diff --git a/gcc/testsuite/gcc.dg/pr50938.c b/gcc/testsuite/gcc.dg/pr50938.c
new file mode 100644
index 0000000..14b7540
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr50938.c
@@ -0,0 +1,52 @@ 
+/* PR/50938: Check that alloca () reserves the correct amount of stack space.
+ */
+
+/* { dg-do run } */
+/* { dg-options "-O0" } */
+/* { dg-require-effective-target alloca } */
+
+__attribute__ ((noinline))
+static void
+dummy (void)
+{
+}
+
+__attribute__ ((noinline))
+static char *
+get_frame_addr (void *p)
+{
+  void *faddr = __builtin_frame_address (0);
+  /* Call a function to make sure that a stack frame exists.  */
+  dummy ();
+  return faddr;
+}
+
+__attribute__ ((noinline))
+static void *
+stack_var (void)
+{
+  /* 1024 bytes on the stack.  */
+  char s[1024];
+  /* One stack slot.  */
+  void *p = (void *)s;
+  /* Keep stack memory alive by passing it to the function.  */
+  return get_frame_addr (p);
+}
+
+__attribute__ ((noinline))
+static void *
+alloca_var (void)
+{
+  /* 1024 bytes on the stack plus one stack slot for a.  */
+  void *a = __builtin_alloca (1024);
+  return get_frame_addr (a);
+}
+
+int main (void)
+{
+  if (stack_var () != alloca_var ())
+    /* The functions used a different amount of stack space.  */
+    __builtin_abort ();
+
+  return 0;
+}
-- 
2.3.0