changeset 450:cd7e1ce83b92

Implement alloca for x86 (grischka case_8). This implements alloca() on x86, at least for non-Windows. Unlike the grischka version, this patch handles both the bounded and non-bounded cases (when bounded, the alloca'd memory is covered), and when asked to allocate with 0 size, it returns 0 without any allocation. Modify the assembly files to adjust the amount of padding (the unused space after an allocation); this must be at least 1 for bounds-checking. It's recommended that the padding be identical for unchecked and bounded cases, because otherwise turning on bound-checking might change errors to non-errors.
author Rob Landley <rob@landley.net>
date Sat, 12 May 2007 00:20:07 -0400
parents 758a978b6605
children 43ba14a107b0
files Makefile i386/alloca86.S i386/bound-alloca86.S tcc.c tcctok.h tests/tcctest.c
diffstat 6 files changed, 132 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sat May 12 00:15:39 2007 -0400
+++ b/Makefile	Sat May 12 00:20:07 2007 -0400
@@ -46,7 +46,7 @@
 endif
 
 # run local version of tcc with local libraries and includes
-TCC=./tcc -B. -I.
+TCC=./tcc -B. -I./include -I.
 
 all: $(PROGS) libtcc1.a $(BCHECK_O) libtcc.a libtcc_test$(EXESUF) \
      tcc-doc.html tcc.1
@@ -173,6 +173,9 @@
 LIBTCC1_CC=./tcc.exe -Bwin32
 else
 LIBTCC1_OBJS=libtcc1.o
+ifeq ($(ARCH),i386)
+LIBTCC1_OBJS+=i386/alloca86.o i386/bound-alloca86.o
+endif
 LIBTCC1_CC=$(CC)
 endif
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/i386/alloca86.S	Sat May 12 00:20:07 2007 -0400
@@ -0,0 +1,41 @@
+/* Implementation of alloca() for tinycc (tcc) on x86.
+ * Based on grischka case_8, modified by David A. Wheeler 2007-05-09.
+ * Plays games with stack, so it omits the usual prologue and epilogue.
+ * We use the normal cdecl calling convention to reduce the risk of error. */
+
+
+/* alloca_padding is the minimum number of unused bytes AFTER the allocation.
+ * It must be at LEAST 1 for bound-checking to work, 4 if you want
+ * off-by-one word-writes to not overwrite something important, and 0
+ * if stack space is an absolute premium */
+alloca_padding=4
+
+/* Alignment: usually 4, 8, or 16. Power of 2. Result % alignment == 0. */
+alloca_alignment=4
+
+.globl _alloca_tcc
+_alloca_tcc:
+    pop    %edx        /* yank return address from stack */
+    pop    %ecx        /* Get parameter (which is size). */
+
+    /* See if we got 0, and if so, handle specially. */
+    or     $0,%ecx
+    jz     alloc_zero
+
+    /* Allocate memory on the stack */
+    mov    %ecx,%eax
+    add    $(alloca_padding+alloca_alignment-1),%eax
+    and    $(-alloca_alignment),%eax
+    sub    %eax,%esp   /* Allocate!  MODIFIES STACK POINTER HERE */
+
+    mov    %esp,%eax   /* Return beginning of allocated area to caller */
+    push   %edx        /* Re-allocate param space for the caller to remove */
+    push   %edx        /* Restore return address to return to. */
+    ret
+
+alloc_zero:
+    mov    %ecx,%eax   /* Return NULL */
+    push   %eax        /* Re-allocate param space for the caller to remove */
+    push   %edx        /* Restore return address to return to. */
+    ret
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/i386/bound-alloca86.S	Sat May 12 00:20:07 2007 -0400
@@ -0,0 +1,50 @@
+/* Implementation of alloca() for tinycc (tcc) on x86, bound-checking.
+ * See alloca86.S */
+
+/* alloca_padding is the minimum number of bytes AFTER the allocation that will
+ * be unused.  Must be at LEAST 1 for bound-checking to work, 4 if you want
+ * off-by-one word-writes to not overwrite something important, and 0
+ * if stack space is an absolute premium. Often wise to keep bounded and
+ * unbounded values the same. */
+bounded_alloca_padding=4
+
+/* Alignment: usually 4, 8, or 16. Power of 2. Result % alignment == 0. */
+bounded_alloca_alignment=4
+
+.globl __bound__alloca_tcc
+__bound__alloca_tcc:
+    pop    %edx        /* yank return address from stack */
+    pop    %ecx        /* Get parameter (which is size). */
+
+    /* See if we got 0, and if so, handle specially. */
+    or     $0,%ecx
+    jz     bound_alloc_zero
+
+    /* Allocate memory on the stack */
+    mov    %ecx,%eax
+    add    $(bounded_alloca_padding+bounded_alloca_alignment-1),%eax
+    and    $(-bounded_alloca_alignment),%eax
+    sub    %eax,%esp   /* Allocate!  MODIFIES STACK POINTER HERE */
+
+/* Call __bound_new_region(void *p, unsigned long size)
+ * if doing bound checks, where *p is %esp, and size is size (NOT size+1).
+ * For maximum efficiency could merge this with the code afterwards, but
+ * it's easier to see what it does this way. */
+    mov    %esp,%eax
+    push   %edx
+    push   %ecx
+    push   %eax
+    call   __bound_new_region
+    add    $8, %esp
+    pop    %edx
+
+    mov    %esp,%eax   /* Return beginning of allocated area to caller */
+    push   %edx        /* Re-allocate param space for the caller to remove */
+    push   %edx        /* Restore return address to return to. */
+    ret
+
+bound_alloc_zero:
+    mov    %ecx,%eax   /* Return NULL */
+    push   %eax        /* Re-allocate param space for the caller to remove */
+    push   %edx        /* Restore return address to return to. */
+    ret
--- a/tcc.c	Sat May 12 00:15:39 2007 -0400
+++ b/tcc.c	Sat May 12 00:20:07 2007 -0400
@@ -445,6 +445,7 @@
             case TOK_memset:
             case TOK_strlen:
             case TOK_strcpy:
+            case TOK_alloca:
                 strcpy(buf, "__bound_");
                 strcat(buf, name);
                 name = buf;
--- a/tcctok.h	Sat May 12 00:15:39 2007 -0400
+++ b/tcctok.h	Sat May 12 00:20:07 2007 -0400
@@ -130,7 +130,7 @@
      DEF(TOK_memcpy, "memcpy")
      DEF(TOK_memset, "memset")
 #endif
-     DEF(TOK_alloca, "alloca")
+     DEF(TOK_alloca, "_alloca_tcc")
      DEF(TOK___divdi3, "__divdi3")
      DEF(TOK___moddi3, "__moddi3")
      DEF(TOK___udivdi3, "__udivdi3")
--- a/tests/tcctest.c	Sat May 12 00:15:39 2007 -0400
+++ b/tests/tcctest.c	Sat May 12 00:20:07 2007 -0400
@@ -2,6 +2,8 @@
  * TCC auto test program
  */
 #include "config.h"
+#include <alloca.h>
+
 
 #if GCC_MAJOR >= 3
 
@@ -74,6 +76,7 @@
 void whitespace_test(void);
 void relocation_test(void);
 void old_style_function(void);
+void alloca_test(void);
 void sizeof_test(void);
 void typeof_test(void);
 void local_label_test(void);
@@ -537,6 +540,7 @@
     whitespace_test();
     relocation_test();
     old_style_function();
+    alloca_test();
     sizeof_test();
     typeof_test();
     statement_expr_test();
@@ -1889,6 +1893,37 @@
     decl_func2(NULL);
 }
 
+
+void alloca_test1()
+{
+ char *p = alloca(1);
+ *p = 0;
+}
+
+void alloca_test2()
+{
+ char *p = alloca(2000);
+ p += 2000;
+ p--;
+ *p = 0;
+}
+
+void alloca_test()
+{
+  char *p = alloca(16);
+  strcpy(p,"123456789012345");
+  printf("p is %s\n", p);
+
+  char *demo = "This is a test.  This is only a test.\n";
+
+  /* Test alloca embedded in a larger expression */
+  printf("Test: %s\n", strcpy(alloca(strlen(demo)+1),demo) );
+
+  alloca_test2();
+  alloca_test1();
+}
+
+
 void sizeof_test(void)
 {
     int a;