Add support for __builtin_frame_address(level)
authorKirill Smelkov <kirr@navytux.spb.ru>
Wed, 14 Nov 2012 23:31:49 +0000 (15 03:31 +0400)
committerKirill Smelkov <kirr@navytux.spb.ru>
Fri, 16 Nov 2012 06:22:14 +0000 (16 10:22 +0400)
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>
tccgen.c
tests/Makefile
tests/tcctest.c

index f183913..f79da36 100644 (file)
--- a/tccgen.c
+++ b/tccgen.c
@@ -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
index 2e7dd23..af1fdb8 100644 (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
index 82762ea..4f9f2a3 100644 (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);
+}