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 -ur --unidirectional-new-file tinycc-rl-1.0.0/alloca86.S tinycc-alloca/alloca86.S --- tinycc-rl-1.0.0/alloca86.S 1969-12-31 19:00:00.000000000 -0500 +++ tinycc-alloca/alloca86.S 2007-05-09 14:42:23.000000000 -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 -ur --unidirectional-new-file tinycc-rl-1.0.0/bound-alloca86.S tinycc-alloca/bound-alloca86.S --- tinycc-rl-1.0.0/bound-alloca86.S 1969-12-31 19:00:00.000000000 -0500 +++ tinycc-alloca/bound-alloca86.S 2007-05-09 14:41:58.000000000 -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 -ur --unidirectional-new-file tinycc-rl-1.0.0/config.mak tinycc-alloca/config.mak --- tinycc-rl-1.0.0/config.mak 2007-05-09 11:59:02.000000000 -0400 +++ tinycc-alloca/config.mak 2007-05-09 14:20:15.000000000 -0400 @@ -18,4 +18,4 @@ EXESUF= ARCH=i386 VERSION=0.9.23 -SRC_PATH=/home/dwheeler/temp/tinycc-rl-1.0.0 +SRC_PATH=/home/dwheeler/temp/tinycc-alloca diff -ur --unidirectional-new-file tinycc-rl-1.0.0/include/alloca.h tinycc-alloca/include/alloca.h --- tinycc-rl-1.0.0/include/alloca.h 1969-12-31 19:00:00.000000000 -0500 +++ tinycc-alloca/include/alloca.h 2007-05-09 09:56:24.000000000 -0400 @@ -0,0 +1,16 @@ + +#ifndef _ALLOCA_H +#define _ALLOCA_H + +#ifdef __TINYC__ +#define alloca(x) _alloca_tcc(x) + +extern void * _alloca_tcc(unsigned x); +extern void * __bound__alloca_tcc(unsigned x); + +#else +extern void * alloca(unsigned x); +#endif + +#endif + diff -ur --unidirectional-new-file tinycc-rl-1.0.0/Makefile tinycc-alloca/Makefile --- tinycc-rl-1.0.0/Makefile 2007-04-18 14:52:22.000000000 -0400 +++ tinycc-alloca/Makefile 2007-05-09 10:52:29.000000000 -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+=alloca86.o bound-alloca86.o +endif LIBTCC1_CC=$(CC) endif diff -ur --unidirectional-new-file tinycc-rl-1.0.0/tcc.c tinycc-alloca/tcc.c --- tinycc-rl-1.0.0/tcc.c 2007-04-18 14:52:22.000000000 -0400 +++ tinycc-alloca/tcc.c 2007-05-09 09:56:24.000000000 -0400 @@ -450,6 +450,7 @@ case TOK_memset: case TOK_strlen: case TOK_strcpy: + case TOK_alloca: strcpy(buf, "__bound_"); strcat(buf, name); name = buf; diff -ur --unidirectional-new-file tinycc-rl-1.0.0/tcctok.h tinycc-alloca/tcctok.h --- tinycc-rl-1.0.0/tcctok.h 2007-04-18 14:52:22.000000000 -0400 +++ tinycc-alloca/tcctok.h 2007-05-09 09:56:24.000000000 -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 -ur --unidirectional-new-file tinycc-rl-1.0.0/tests/tcctest.c tinycc-alloca/tests/tcctest.c --- tinycc-rl-1.0.0/tests/tcctest.c 2007-04-18 14:52:22.000000000 -0400 +++ tinycc-alloca/tests/tcctest.c 2007-05-09 12:01:42.000000000 -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); @@ -526,6 +529,7 @@ whitespace_test(); relocation_test(); old_style_function(); + alloca_test(); sizeof_test(); typeof_test(); statement_expr_test(); @@ -1772,6 +1776,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;