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