# HG changeset patch # User Rob Landley # Date 1178943607 14400 # Node ID cd7e1ce83b928cf02089f56750224a8fba478409 # Parent 758a978b6605d70e3a65babdab9ef99c0ed8c65b 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. diff -r 758a978b6605 -r cd7e1ce83b92 Makefile --- 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 diff -r 758a978b6605 -r cd7e1ce83b92 i386/alloca86.S --- /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 + diff -r 758a978b6605 -r cd7e1ce83b92 i386/bound-alloca86.S --- /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 diff -r 758a978b6605 -r cd7e1ce83b92 tcc.c --- 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; diff -r 758a978b6605 -r cd7e1ce83b92 tcctok.h --- 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") diff -r 758a978b6605 -r cd7e1ce83b92 tests/tcctest.c --- 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 + #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;