Add support for __builtin_frame_address(level)

Continuing d6072d37 (Add __builtin_frame_address(0)) implement
__builtin_frame_address for levels greater than zero, in order for
tinycc to be able to compile its own lib/bcheck.c after
cffb7af9 (lib/bcheck: Prevent __bound_local_new / __bound_local_delete
from being miscompiled).

I'm new to the internals, and used the most simple way to do it.
Generated code is not very good for levels >= 2, compare

                gcc                         tcc

    level=0     mov    %ebp,%eax            lea    0x0(%ebp),%eax

    level=1     mov    0x0(%ebp),%eax       mov    0x0(%ebp),%eax

    level=2     mov    0x0(%ebp),%eax       mov    0x0(%ebp),%eax
                mov    (%eax),%eax          mov    %eax,-0x10(%ebp)
                                            mov    -0x10(%ebp),%eax
                                            mov    (%eax),%eax

    level=3     mov    0x0(%ebp),%eax       mov    0x0(%ebp),%eax
                mov    (%eax),%eax          mov    (%eax),%ecx
                mov    (%eax),%eax          mov    (%ecx),%eax

But this is still an improvement and for bcheck we need level=1 for
which the code is good.

For the tests I had to force gcc use -O0 to not inline the functions.
And -fno-omit-frame-pointer just in case.

If someone knows how to improve the generated code - help is
appreciated.

Thanks,
Kirill

Cc: Michael Matz <matz@suse.de>
Cc: Shinichiro Hamaji <shinichiro.hamaji@gmail.com>
master
Kirill Smelkov 2012-11-15 03:31:49 +04:00
parent e79c3533ec
commit b2a02961b4
3 changed files with 37 additions and 7 deletions

View File

@ -3665,20 +3665,23 @@ ST_FUNC void unary(void)
break;
case TOK_builtin_frame_address:
{
int level;
CType type;
next();
skip('(');
if (tok != TOK_CINT) {
tcc_error("__builtin_frame_address only takes integers");
}
if (tokc.i != 0) {
tcc_error("TCC only supports __builtin_frame_address(0)");
if (tok != TOK_CINT || tokc.i < 0) {
tcc_error("__builtin_frame_address only takes positive integers");
}
level = tokc.i;
next();
skip(')');
type.t = VT_VOID;
mk_pointer(&type);
vset(&type, VT_LOCAL, 0);
vset(&type, VT_LOCAL, 0); /* local frame */
while (level--) {
mk_pointer(&vtop->type);
indir(); /* -> parent frame */
}
}
break;
#ifdef TCC_TARGET_X86_64

View File

@ -73,7 +73,7 @@ libtcc_test$(EXESUF): libtcc_test.c ../$(LIBTCC)
# copy only tcclib.h so GCC's stddef and stdarg will be used
test.ref: tcctest.c
cp ../include/tcclib.h .
$(CC) -o tcctest.gcc $< -I. $(CPPFLAGS) -w $(CFLAGS) $(NATIVE_DEFINES) -std=gnu99 $(LDFLAGS)
$(CC) -o tcctest.gcc $< -I. $(CPPFLAGS) -w $(CFLAGS) $(NATIVE_DEFINES) -std=gnu99 -O0 -fno-omit-frame-pointer $(LDFLAGS)
./tcctest.gcc > $@
# auto test

View File

@ -89,6 +89,7 @@ void global_data_test(void);
void cmp_comparison_test(void);
void math_cmp_test(void);
void callsave_test(void);
void builtin_frame_address_test(void);
int fib(int n);
void num(int n);
@ -598,6 +599,7 @@ int main(int argc, char **argv)
cmp_comparison_test();
math_cmp_test();
callsave_test();
builtin_frame_address_test();
return 0;
}
@ -2680,3 +2682,28 @@ void callsave_test(void)
printf ("%d\n", i);
#endif
}
void bfa3(ptrdiff_t str_offset)
{
printf("bfa3: %s\n", (char *)__builtin_frame_address(3) + str_offset);
}
void bfa2(ptrdiff_t str_offset)
{
printf("bfa2: %s\n", (char *)__builtin_frame_address(2) + str_offset);
bfa3(str_offset);
}
void bfa1(ptrdiff_t str_offset)
{
printf("bfa1: %s\n", (char *)__builtin_frame_address(1) + str_offset);
bfa2(str_offset);
}
void builtin_frame_address_test(void)
{
char str[] = "__builtin_frame_address";
char *fp0 = __builtin_frame_address(0);
printf("str: %s\n", str);
bfa1(str-fp0);
}