Mercurial > hg > tinycc
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;