trampoline management in compound statements

Neal H. Walfield neal@walfield.org
Wed Aug 25 15:22:00 GMT 2010


Hi,

Please CC me in any reply.

I'm trying to understand how trampolines are managed in gcc,
particularly in the context of gcc's compound statements.

I understand from section 6.2.4, Storage durations of objects, of the
C99 specification, that the lifetime of an object allocated in a block
lasts until the end of that block:

  4. An object whose identifier is declared with no linkage and
     without the storage-class specifier static has automatic storage
     duration.

  5. For such an object that does not have a variable length array
     type, its lifetime extends from entry into the block with which
     it is associated until execution of that block ends in any way.

I would expect compound statements to act in the same way, but they
are a gcc extension [1].  I haven't found a formal specification for
them.  Is there one?

  [1] http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html

In any case, a simple test appears to comfirm this:

  $ cat c.c
  #include <stdio.h>

  int
  main (int argc, char *argv[])
  {
    int *f = ({ int foo = 134; &foo; });
    int *b = ({ int bar; &bar; });
    printf ("%p => %i\n", f, *f);
    printf ("%p => %i\n", b, *b);
    return 0;
  }                     
  $ gcc -Wall c.c -o c && ./c
  0xbfd02a04 => 134
  0xbfd02a04 => 134

F and B get the same address.

I observe different behavior, however, with trampolines: trampolines
appear to remain valid until at least the end of the function call.
Consider:

  $ cat d.c
  #include <stdio.h>
  
  int
  main (int argc, char *argv[])
  {
    /* We reference a local variable to ensure that the functions
       create a trampoline.  */
    void (*f)(void)
      = ({ void foo (void) { printf ("%s: %d\n", __func__, argc); } &foo; });
    void (*b)(void)
      = ({ void bar (void) { printf ("%s: %d\n", __func__, argc); } &bar; });
    printf ("%p\n", f); f();
    printf ("%p\n", b); b();
    printf ("&argc: %p\n", &argc);

    if (argc == 0)
      return 0;
    return main (argc - 1, argv);
  }
  $ gcc -Wall c.c -o c && ./c 
  0xbf885532
  foo: 1
  0xbf885528
  bar: 1
  &argc: 0xbf885524
  0xbf8854c2
  foo: 0
  0xbf8854b8
  bar: 0
  &argc: 0xbf8854b4

In this case, the storage does not appear to be reused, even though
they are certainly allocated on the stack frame.

Can I rely on this behavior?  In particular, can I rely on a
trampoline staying valid until the enclosing function returns?  Is the
behavior of trampoline in a compound block specified somewhere?  If
not, I would like to see the behavior that I have observed be codified
as it is useful for creating lambdas in C.  Consider:

  #include <stdlib.h>
  #include <stdio.h>
  
  /* Inspired by <http://groups.google.com/group/comp.lang.c/browse_thread/thread/fd82dc3d97ea87ff/8b01e62f2feae4f0?lnk=gst>
  
     Define a nested function in a compound statement and return that
     function's address.  Is this legitimate?  The lifetime of an object
     defined in a compound statement ends at the end of the compound
     statement.  The lambda does not reference any variables in the
     enclosing function: at most, it references objects in the context
     in which the lambda was expanded.  However, the trampoline is
     likely allocated on the stack in the compound statement.  One would
     expect that that would have the same lifetime.  Emperically (gcc
     4.4.3), it doesn't.  */
  #define lambda(l_ret_type, l_arguments, l_body)                \
    ({                                                           \
      l_ret_type l_anonymous_functions_name l_arguments          \
        l_body                                                   \
      &l_anonymous_functions_name;                               \
    })
  
  int
  main (int argc, char *argv[])
  {
    int array[] = { 4, 3, 1, 2, 5 };
  
    void dump (void)
    {
      int i;
      for (i = 0; i < sizeof (array) / sizeof (array[0]); i ++)
        printf ("%d ", array[i]);
      printf ("\n");
    }
  
    printf ("Initial: ");
    dump ();
  
    /* Ensure that the lambda is a nested function and thus requires a
       trampoline.  */
    int comparison = 0;
  
    qsort (array, sizeof (array) / sizeof (array[0]), sizeof (array[0]),
           lambda (int, (const void *a, const void *b),
                   {
                     dump ();
                     printf ("Comparison %d: %d and %d\n",
                             ++ comparison,
                             *(const int *) a, *(const int *) b);
                     return *(const int *) a - *(const int *) b;
                   }));
  
    printf ("Sorted: ");
    dump ();
  
    return 0;
  }

  Initial: 4 3 1 2 5 
  4 3 1 2 5 
  Comparison 1: 4 and 3
  3 4 1 2 5 
  Comparison 2: 2 and 5
  3 4 1 2 5 
  Comparison 3: 1 and 2
  3 4 1 2 5 
  Comparison 4: 3 and 1
  3 4 1 2 5 
  Comparison 5: 3 and 2
  3 4 1 2 5 
  Comparison 6: 3 and 5
  3 4 1 2 5 
  Comparison 7: 4 and 5
  Sorted: 1 2 3 4 5 


Thanks,

Neal

P.S. Please CC me in any reply.



More information about the Gcc-help mailing list