Improved variable length array support.

VLA storage is now freed when it goes out of scope. This makes it
possible to use a VLA inside a loop without consuming an unlimited
amount of memory.

Combining VLAs with alloca() should work as in GCC - when a VLA is
freed, memory allocated by alloca() after the VLA was created is also
freed. There are some exceptions to this rule when using goto: if a VLA
is in scope at the goto, jumping to a label will reset the stack pointer
to where it was immediately after the last VLA was created prior to the
label, or to what it was before the first VLA was created if the label
is outside the scope of any VLA. This means that in some cases combining
alloca() and VLAs will free alloca() memory where GCC would not.
master
James Lyon 2013-04-27 20:39:34 +01:00
parent 6ee366e765
commit 41b3c7a507
12 changed files with 311 additions and 23 deletions

View File

@ -57,6 +57,8 @@ endif()
# Use two variables to keep CMake configuration variable names consistent
set(TCC_BCHECK OFF CACHE BOOL "Enable bounds checking")
set(CONFIG_TCC_BCHECK ${TCC_BCHECK})
set(TCC_ASSERT OFF CACHE BOOL "Enable assertions")
set(CONFIG_TCC_ASSERT ${TCC_ASSERT})
set(TCC_BUILD_NATIVE ON CACHE BOOL "Build native compiler")
set(TCC_BUILD_I386 OFF CACHE BOOL "Build i386 cross compiler")

View File

@ -2017,6 +2017,21 @@ void ggoto(void)
vtop--;
}
/* Save the stack pointer onto the stack and return the location of its address */
ST_FUNC void gen_vla_sp_save(int addr) {
tcc_error("variable length arrays unsupported for this target");
}
/* Restore the SP from a location on the stack */
ST_FUNC void gen_vla_sp_restore(int addr) {
tcc_error("variable length arrays unsupported for this target");
}
/* Subtract from the stack pointer, and push the resulting value onto the stack */
ST_FUNC void gen_vla_alloc(CType *type, int align) {
tcc_error("variable length arrays unsupported for this target");
}
/* end of ARM code generator */
/*************************************************************/
#endif

View File

@ -2560,6 +2560,21 @@ void ggoto(void)
vtop--;
}
/* Save the stack pointer onto the stack and return the location of its address */
ST_FUNC void gen_vla_sp_save(int addr) {
tcc_error("variable length arrays unsupported for this target");
}
/* Restore the SP from a location on the stack */
ST_FUNC void gen_vla_sp_restore(int addr) {
tcc_error("variable length arrays unsupported for this target");
}
/* Subtract from the stack pointer, and push the resulting value onto the stack */
ST_FUNC void gen_vla_alloc(CType *type, int align) {
tcc_error("variable length arrays unsupported for this target");
}
/* end of C67 code generator */
/*************************************************************/
#endif

View File

@ -3,4 +3,5 @@
#cmakedefine CONFIG_WIN32
#cmakedefine CONFIG_WIN64
#cmakedefine CONFIG_TCC_BCHECK
#cmakedefine CONFIG_TCC_BCHECK
#cmakedefine CONFIG_TCC_ASSERT

7
configure vendored
View File

@ -19,6 +19,7 @@ TMPH=$TMPN.h
# default parameters
build_cross="no"
use_libgcc="no"
enable_assert="no"
prefix=""
execprefix=""
bindir=""
@ -165,6 +166,8 @@ for opt do
;;
--enable-cross) build_cross="yes"
;;
--enable-assert) enable_assert="yes"
;;
--disable-static) disable_static="yes"
;;
--disable-rpath) disable_rpath="yes"
@ -274,6 +277,7 @@ Advanced options (experts only):
--enable-mingw32 build windows version on linux with mingw32
--enable-cygwin build windows version on windows with cygwin
--enable-cross build cross compilers
--enable-assert enable debug assertions
--with-selinux use mmap for exec mem [needs writable /tmp]
--sysincludepaths=... specify system include paths, colon separated
--libpaths=... specify system library paths, colon separated
@ -484,6 +488,9 @@ if test "$have_selinux" = "yes" ; then
echo "#define HAVE_SELINUX" >> $TMPH
echo "HAVE_SELINUX=yes" >> config.mak
fi
if test "$enable_assert" = "yes" ; then
echo "#define CONFIG_TCC_ASSERT" >> $TMPH
fi
version=`head $source_path/VERSION`
echo "VERSION=$version" >>config.mak

View File

@ -43,6 +43,7 @@ enum {
TREG_ECX,
TREG_EDX,
TREG_ST0,
TREG_ESP = 4
};
/* return registers for function */
@ -1117,6 +1118,44 @@ ST_FUNC void gen_bounded_ptr_deref(void)
}
#endif
/* Save the stack pointer onto the stack */
ST_FUNC void gen_vla_sp_save(int addr) {
/* mov %esp,addr(%ebp)*/
o(0x89);
gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
}
/* Restore the SP from a location on the stack */
ST_FUNC void gen_vla_sp_restore(int addr) {
o(0x8b);
gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
}
/* Subtract from the stack pointer, and push the resulting value onto the stack */
ST_FUNC void gen_vla_alloc(CType *type, int align) {
#ifdef TCC_TARGET_PE
/* alloca does more than just adjust %rsp on Windows */
vpush_global_sym(&func_old_type, TOK_alloca);
vswap(); /* Move alloca ref past allocation size */
gfunc_call(1);
vset(type, REG_IRET, 0);
#else
int r;
r = gv(RC_INT); /* allocation size */
/* sub r,%rsp */
o(0x2b);
o(0xe0 | r);
/* We align to 16 bytes rather than align */
/* and ~15, %esp */
o(0xf0e483);
/* mov %esp, r */
o(0x89);
o(0xe0 | r);
vpop();
vset(type, r, 0);
#endif
}
/* end of X86 code generator */
/*************************************************************/
#endif

18
tcc.h
View File

@ -40,6 +40,13 @@
#include <setjmp.h>
#include <time.h>
#ifdef CONFIG_TCCASSERT
#include <assert.h>
#define TCC_ASSERT(ex) assert(ex)
#else
#define TCC_ASSERT(ex)
#endif
#ifndef _WIN32
# include <unistd.h>
# include <sys/time.h>
@ -404,6 +411,12 @@ typedef struct AttributeDef {
#define SYM_FIELD 0x20000000 /* struct/union field symbol space */
#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */
#define VLA_SP_LOC_SET 0x01 /* Location of SP on stack has been allocated */
#define VLA_SP_SAVED 0x02 /* SP has been saved to slot already */
#define VLA_NEED_NEW_FRAME 0x04 /* Needs new frame for next VLA */
#define VLA_IN_SCOPE 0x08 /* One or more VLAs are in scope */
#define VLA_SCOPE_FLAGS (VLA_SP_SAVED|VLA_NEED_NEW_FRAME|VLA_IN_SCOPE) /* Flags which are saved and restored upon entering and exiting a block */
/* stored in 'Sym.c' field */
#define FUNC_NEW 1 /* ansi function prototype */
#define FUNC_OLD 2 /* old function prototype */
@ -1201,7 +1214,7 @@ ST_FUNC void decl(int l);
#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_C67
ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size);
#endif
#ifdef TCC_TARGET_X86_64
#if defined TCC_TARGET_X86_64 && !defined TCC_TARGET_PE
ST_FUNC int classify_x86_64_va_arg(CType *ty);
#endif
@ -1286,6 +1299,9 @@ ST_FUNC void o(unsigned int c);
#ifndef TCC_TARGET_ARM
ST_FUNC void gen_cvt_itof(int t);
#endif
ST_FUNC void gen_vla_sp_save(int addr);
ST_FUNC void gen_vla_sp_restore(int addr);
ST_FUNC void gen_vla_alloc(CType *type, int align);
/* ------------ i386-gen.c ------------ */
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64

View File

@ -55,6 +55,11 @@ ST_DATA Sym *define_stack;
ST_DATA Sym *global_label_stack;
ST_DATA Sym *local_label_stack;
ST_DATA int vla_sp_loc_tmp; /* vla_sp_loc is set to this when the value won't be needed later */
ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */
ST_DATA int *vla_sp_loc; /* Pointer to variable holding location to store stack pointer on the stack when modifying stack pointer */
ST_DATA int vla_flags; /* VLA_* flags */
ST_DATA SValue __vstack[1+VSTACK_SIZE], *vtop;
ST_DATA int const_wanted; /* true if constant wanted */
@ -81,6 +86,7 @@ static int decl0(int l, int is_for_loop_init);
static void expr_eq(void);
static void unary_type(CType *type);
static void vla_runtime_type_size(CType *type, int *a);
static void vla_sp_save(void);
static int is_compatible_parameter_types(CType *type1, CType *type2);
static void expr_type(CType *type);
@ -2109,6 +2115,17 @@ ST_FUNC void vla_runtime_type_size(CType *type, int *a)
}
}
static void vla_sp_save(void) {
if (!(vla_flags & VLA_SP_LOC_SET)) {
*vla_sp_loc = (loc -= PTR_SIZE);
vla_flags |= VLA_SP_LOC_SET;
}
if (!(vla_flags & VLA_SP_SAVED)) {
gen_vla_sp_save(*vla_sp_loc);
vla_flags |= VLA_SP_SAVED;
}
}
/* return the pointed type of t */
static inline CType *pointed_type(CType *type)
{
@ -4484,6 +4501,16 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
frame_bottom->next = scope_stack_bottom;
scope_stack_bottom = frame_bottom;
llabel = local_label_stack;
/* save VLA state */
int block_vla_sp_loc = *vla_sp_loc;
int *saved_vla_sp_loc = vla_sp_loc;
if (saved_vla_sp_loc != &vla_sp_root_loc)
vla_sp_loc = &block_vla_sp_loc;
int saved_vla_flags = vla_flags;
vla_flags |= VLA_NEED_NEW_FRAME;
/* handle local labels declarations */
if (tok == TOK_LABEL) {
next();
@ -4527,6 +4554,16 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
/* pop locally defined symbols */
scope_stack_bottom = scope_stack_bottom->next;
sym_pop(&local_stack, s);
/* Pop VLA frames and restore stack pointer if required */
if (saved_vla_sp_loc != &vla_sp_root_loc)
*saved_vla_sp_loc = block_vla_sp_loc;
if (vla_sp_loc != (saved_vla_sp_loc == &vla_sp_root_loc ? &vla_sp_root_loc : &block_vla_sp_loc)) {
vla_sp_loc = saved_vla_sp_loc;
gen_vla_sp_restore(*vla_sp_loc);
}
vla_flags = (vla_flags & ~VLA_SCOPE_FLAGS) | (saved_vla_flags & VLA_SCOPE_FLAGS);
next();
} else if (tok == TOK_RETURN) {
next();
@ -4731,6 +4768,13 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
s->r = LABEL_FORWARD;
}
/* label already defined */
if (vla_flags & VLA_IN_SCOPE) {
/* If VLAs are in use, save the current stack pointer and
reset the stack pointer to what it was at function entry
(label will restore stack pointer in inner scopes) */
vla_sp_save();
gen_vla_sp_restore(vla_sp_root_loc);
}
if (s->r & LABEL_FORWARD)
s->jnext = gjmp(s->jnext);
else
@ -4746,6 +4790,12 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
b = is_label();
if (b) {
/* label case */
if (vla_flags & VLA_IN_SCOPE) {
/* save/restore stack pointer across label
this is a no-op when combined with the load immediately
after the label unless we arrive via goto */
vla_sp_save();
}
s = label_find(b);
if (s) {
if (s->r == LABEL_DEFINED)
@ -4756,6 +4806,10 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
s = label_push(&global_label_stack, b, LABEL_DEFINED);
}
s->jnext = ind;
if (vla_flags & VLA_IN_SCOPE) {
gen_vla_sp_restore(*vla_sp_loc);
vla_flags |= VLA_NEED_NEW_FRAME;
}
/* we accept this, but it is a mistake */
block_after_label:
if (tok == '}') {
@ -5026,24 +5080,21 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
CType *t1;
if (type->t & VT_VLA) {
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
int a;
CValue retcval;
vpush_global_sym(&func_old_type, TOK_alloca);
/* save current stack pointer */
if (vla_flags & VLA_NEED_NEW_FRAME) {
vla_sp_save();
vla_flags = VLA_IN_SCOPE;
vla_sp_loc = &vla_sp_loc_tmp;
}
vla_runtime_type_size(type, &a);
gfunc_call(1);
/* return value */
retcval.i = 0;
vsetc(type, REG_IRET, &retcval);
gen_vla_alloc(type, a);
vset(type, VT_LOCAL|VT_LVAL, c);
vswap();
vstore();
vpop();
#else
tcc_error("variable length arrays unsupported for this target");
#endif
} else if (type->t & VT_ARRAY) {
s = type->ref;
n = s->c;
@ -5585,6 +5636,9 @@ static void gen_function(Sym *sym)
put_extern_sym(sym, cur_text_section, ind, 0);
funcname = get_tok_str(sym->v, NULL);
func_ind = ind;
/* Initialize VLA state */
vla_sp_loc = &vla_sp_root_loc;
vla_flags = VLA_NEED_NEW_FRAME;
/* put debug symbol */
if (tcc_state->do_debug)
put_func_debug(sym);

View File

@ -15,11 +15,16 @@ add_test(NAME abitest-cc WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND abitest-c
set(ABITEST_TCC abitest-tcc${CMAKE_EXECUTABLE_SUFFIX})
get_property(LIBTCC_LIB TARGET libtcc PROPERTY LOCATION)
add_custom_command(OUTPUT ${ABITEST_TCC} COMMAND tcc ${TCC_CFLAGS} -g ${CMAKE_CURRENT_SOURCE_DIR}/abitest.c ${LIBTCC_LDFLAGS} ${LIBTCC_LIB} -o ${ABITEST_TCC} DEPENDS tcc abitest.c)
add_custom_command(OUTPUT ${ABITEST_TCC} COMMAND tcc ${TCC_CFLAGS} -g ${CMAKE_CURRENT_SOURCE_DIR}/abitest.c ${LIBTCC_LDFLAGS} ${LIBTCC_LIB} -o ${ABITEST_TCC} DEPENDS tcc ${CMAKE_CURRENT_SOURCE_DIR}/abitest.c)
add_custom_target(abitest-tcc-exe ALL DEPENDS ${ABITEST_TCC})
add_test(NAME abitest-tcc WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${ABITEST_TCC} lib_path=${CMAKE_BINARY_DIR} include=${CMAKE_SOURCE_DIR}/include)
set(VLA_TEST vla_test${CMAKE_EXECUTABLE_SUFFIX})
add_custom_command(OUTPUT ${VLA_TEST} COMMAND tcc ${TCC_CFLAGS} -g ${CMAKE_CURRENT_SOURCE_DIR}/vla_test.c -o ${VLA_TEST} DEPENDS tcc ${CMAKE_CURRENT_SOURCE_DIR}/vla_test.c)
add_custom_target(vla_test-exe ALL DEPENDS ${VLA_TEST})
add_test(vla_test vla_test)
add_executable(tcctest-cc tcctest.c)
target_link_libraries(tcctest-cc libtcc)
set_target_properties(tcctest-cc PROPERTIES COMPILE_FLAGS -std=gnu99)
@ -41,21 +46,21 @@ if(PYTHONINTERP_FOUND)
# Object + link output
set(TEST4 test4${CMAKE_EXECUTABLE_SUFFIX})
add_custom_command(OUTPUT test4.o COMMAND tcc ${TCC_TEST_CFLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c -c -o test4.o DEPENDS tcc tcctest.c)
add_custom_command(OUTPUT test4.o COMMAND tcc ${TCC_TEST_CFLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c -c -o test4.o DEPENDS tcc ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c)
add_custom_command(OUTPUT ${TEST4} COMMAND tcc ${TCC_TEST_CFLAGS} test4.o -o ${TEST4} DEPENDS tcc test4.o)
add_custom_target(test4-exe ALL DEPENDS ${TEST4})
add_test(test4 ${TCCTEST_PY} ${CMAKE_CURRENT_BINARY_DIR}/${TEST4})
# Dynamic output
set(TEST5 test5${CMAKE_EXECUTABLE_SUFFIX})
add_custom_command(OUTPUT ${TEST5} COMMAND tcc ${TCC_TEST_CFLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c -o ${TEST5} DEPENDS tcc tcctest.c)
add_custom_command(OUTPUT ${TEST5} COMMAND tcc ${TCC_TEST_CFLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c -o ${TEST5} DEPENDS tcc ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c)
add_custom_target(test5-exe ALL DEPENDS ${TEST5})
add_test(test5 ${TCCTEST_PY} ${CMAKE_CURRENT_BINARY_DIR}/${TEST5})
if(TCC_BCHECK)
# Dynamic output + bound check
set(TEST6 test6${CMAKE_EXECUTABLE_SUFFIX})
add_custom_command(OUTPUT ${TEST5} COMMAND tcc ${TCC_TEST_CFLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c -b -o ${TEST6} DEPENDS tcc tcctest.c)
add_custom_command(OUTPUT ${TEST6} COMMAND tcc ${TCC_TEST_CFLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c -b -o ${TEST6} DEPENDS tcc ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c)
add_custom_target(test6-exe ALL DEPENDS ${TEST6})
add_test(test6 ${TCCTEST_PY} ${CMAKE_CURRENT_BINARY_DIR}/${TEST6})
endif()
@ -63,7 +68,7 @@ if(PYTHONINTERP_FOUND)
if(0)
# Static output
set(TEST7 test7${CMAKE_EXECUTABLE_SUFFIX})
add_custom_command(OUTPUT ${TEST7} COMMAND tcc ${TCC_TEST_CFLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c -static -o ${TEST7} DEPENDS tcc tcctest.c)
add_custom_command(OUTPUT ${TEST7} COMMAND tcc ${TCC_TEST_CFLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c -static -o ${TEST7} DEPENDS tcc ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c)
add_custom_target(test7-exe ALL DEPENDS ${TEST7})
add_test(test7 ${TCCTEST_PY} ${CMAKE_CURRENT_BINARY_DIR}/${TEST7})
endif()

View File

@ -14,6 +14,7 @@ TESTS = \
libtest \
test3 \
abitest \
vla_test-run \
moretests
# test4 -- problem with -static
@ -31,6 +32,10 @@ endif
ifeq ($(TARGETOS),Darwin)
TESTS := $(filter-out hello-exe test3 btest,$(TESTS))
endif
ifeq ($(ARCH),i386)
else ifneq ($(ARCH),x86-64)
TESTS := $(filter-out vla_test-run,$(TESTS))
endif
ifdef DISABLE_STATIC
export LD_LIBRARY_PATH:=$(CURDIR)/..
@ -190,6 +195,12 @@ abitest: abitest-cc$(EXESUF) abitest-tcc$(EXESUF)
./abitest-cc$(EXESUF) lib_path=.. include="$(top_srcdir)/include"
./abitest-tcc$(EXESUF) lib_path=.. include="$(top_srcdir)/include"
vla_test$(EXESUF): vla_test.c
$(TCC) -o $@ $^ $(CPPFLAGS) $(CFLAGS)
vla_test-run: vla_test$(EXESUF)
@echo ------------ $@ ------------
./vla_test$(EXESUF)
# targets for development
%.bin: %.c tcc
$(TCC) -g -o $@ $<

84
tests/vla_test.c 100644
View File

@ -0,0 +1,84 @@
/*
* Test that allocating a variable length array in a loop
* does not use up a linear amount of memory
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LOOP_COUNT 1000
#define ARRAY_SIZE 100
/* Overwrite a VLA. This will overwrite the return address if SP is incorrect */
void smash(char *p, int n) {
memset(p, 0, n);
}
int test1(int n) {
int i;
char *array_ptrs[LOOP_COUNT];
for (i = 0; i < LOOP_COUNT; ++i) {
char test[n];
smash(test, n);
array_ptrs[i] = test;
}
return (array_ptrs[0]-array_ptrs[LOOP_COUNT-1] < n) ? 0 : 1;
}
/* ensure goto does not circumvent array free */
int test2(int n) {
char *array_ptrs[LOOP_COUNT];
int i = 0;
loop:;
char test[n];
smash(test, n);
if (i >= LOOP_COUNT)
goto end;
array_ptrs[i] = test;
++i;
goto loop;
end:
smash(test, n);
char test2[n];
smash(test2, n);
return (array_ptrs[0] - array_ptrs[LOOP_COUNT-1] < n) ? 0 : 1;
}
int test3(int n) {
char test[n];
smash(test, n);
goto label;
label:
smash(test, n);
char test2[n];
smash(test2, n);
return (test-test2 >= n) ? 0 : 1;
}
#define RUN_TEST(t) \
if (!testname || (strcmp(#t, testname) == 0)) { \
fputs(#t "... ", stdout); \
fflush(stdout); \
if (t(ARRAY_SIZE) == 0) { \
fputs("success\n", stdout); \
} else { \
fputs("failure\n", stdout); \
retval = EXIT_FAILURE; \
} \
}
int main(int argc, char **argv) {
const char *testname = NULL;
int retval = EXIT_SUCCESS;
if (argc > 1)
testname = argv[1];
RUN_TEST(test1)
RUN_TEST(test2)
RUN_TEST(test3)
return retval;
}

View File

@ -23,7 +23,7 @@
#ifdef TARGET_DEFS_ONLY
/* number of available registers */
#define NB_REGS 24
#define NB_REGS 25
#define NB_ASM_REGS 8
/* a register can belong to several classes. The classes must be
@ -57,6 +57,7 @@ enum {
TREG_RAX = 0,
TREG_RCX = 1,
TREG_RDX = 2,
TREG_RSP = 4,
TREG_RSI = 6,
TREG_RDI = 7,
@ -74,7 +75,7 @@ enum {
TREG_XMM6 = 22,
TREG_XMM7 = 23,
TREG_ST0 = 4, // SP slot won't be used
TREG_ST0 = 24,
TREG_MEM = 0x20,
};
@ -125,7 +126,7 @@ ST_DATA const int reg_classes[NB_REGS] = {
/* ecx */ RC_INT | RC_RCX,
/* edx */ RC_INT | RC_RDX,
0,
/* st0 */ RC_ST0,
0,
0,
0,
0,
@ -147,7 +148,8 @@ ST_DATA const int reg_classes[NB_REGS] = {
but they are not tagged with RC_FLOAT because they are
callee saved on Windows */
RC_XMM6,
RC_XMM7
RC_XMM7,
/* st0 */ RC_ST0
};
static unsigned long func_sub_sp_offset;
@ -2080,6 +2082,43 @@ void ggoto(void)
vtop--;
}
/* Save the stack pointer onto the stack and return the location of its address */
ST_FUNC void gen_vla_sp_save(int addr) {
/* mov %rsp,addr(%rbp)*/
gen_modrm64(0x89, TREG_RSP, VT_LOCAL, NULL, addr);
}
/* Restore the SP from a location on the stack */
ST_FUNC void gen_vla_sp_restore(int addr) {
gen_modrm64(0x8b, TREG_RSP, VT_LOCAL, NULL, addr);
}
/* Subtract from the stack pointer, and push the resulting value onto the stack */
ST_FUNC void gen_vla_alloc(CType *type, int align) {
#ifdef TCC_TARGET_PE
/* alloca does more than just adjust %rsp on Windows */
vpush_global_sym(&func_old_type, TOK_alloca);
vswap(); /* Move alloca ref past allocation size */
gfunc_call(1);
vset(type, REG_IRET, 0);
#else
int r;
r = gv(RC_INT); /* allocation size */
/* sub r,%rsp */
o(0x2b48);
o(0xe0 | REG_VALUE(r));
/* We align to 16 bytes rather than align */
/* and ~15, %rsp */
o(0xf0e48348);
/* mov %rsp, r */
o(0x8948);
o(0xe0 | REG_VALUE(r));
vpop();
vset(type, r, 0);
#endif
}
/* end of x86-64 code generator */
/*************************************************************/
#endif /* ! TARGET_DEFS_ONLY */