64-bit tests now pass (well, nearly).

tcctest1-3 fail, but this appears to be due to bugs in GCC rather than TCC
(from manual inspection of the output).
This commit is contained in:
James Lyon 2013-04-24 02:19:15 +01:00
parent 8a81f9e103
commit 5c35ba66c5
8 changed files with 296 additions and 143 deletions

View File

@ -46,32 +46,36 @@ set(I386_SOURCES i386-gen.c i386-asm.c i386-asm.h i386-tok.h)
set(X86_64_SOURCES x86_64-gen.c i386-asm.c x86_64-asm.h)
if(TCC_TARGET STREQUAL "Win32")
set(TCC_TARGET_I386 ON)
set(TCC_TARGET_PE ON)
set(TCC_TARGET_FLAGS -DTCC_TARGET_I386 -DTCC_TARGET_PE)
set(LIBTCC_TARGET_SOURCES ${I386_SOURCES} tccpe.c)
elseif(TCC_TARGET STREQUAL "Win64")
set(TCC_TARGET_PE ON)
set(TCC_TARGET_X86_64 ON)
set(TCC_TARGET_FLAGS -DTCC_TARGET_X86_64 -DTCC_TARGET_PE)
set(LIBTCC_TARGET_SOURCES ${X86_64_SOURCES} tccpe.c)
elseif(TCC_TARGET STREQUAL "WinCE")
set(TCC_TARGET_ARM ON)
set(TCC_TARGET_FLAGS -DTCC_TARGET_ARM
-DTCC_ARM_VERSION=${TCC_ARM_VERSION})
set(LIBTCC_TARGET_SOURCES arm-gen.c tccpe.c)
elseif(TCC_TARGET STREQUAL "i386")
set(TCC_TARGET_I386 ON)
set(TCC_TARGET_FLAGS -DTCC_TARGET_I386)
set(LIBTCC_TARGET_SOURCES ${I386_SOURCES})
elseif(TCC_TARGET STREQUAL "x86-64")
set(TCC_TARGET_X86_64 ON)
set(TCC_TARGET_FLAGS -DTCC_TARGET_X86_64)
set(LIBTCC_TARGET_SOURCES ${X86_64_SOURCES})
elseif(TCC_TARGET STREQUAL "ARM")
set(TCC_TARGET_ARM ON)
set(TCC_TARGET_FLAGS -DTCC_TARGET_ARM
-DTCC_ARM_VERSION=${TCC_ARM_VERSION}
-DTCC_ARM_EABI=${TCC_ARM_EABI}
-DTCC_ARM_VFP=${TCC_ARM_VFP}
-DTCC_ARM_HARDFLOAT=${TCC_ARM_HARDFLOAT})
set(LIBTCC_TARGET_SOURCES arm-gen.c)
elseif(TCC_TARGET STREQUAL "C67")
set(TCC_TARGET_C67 ON)
set(TCC_TARGET_FLAGS -DTCC_TARGET_C67)
set(LIBTCC_TARGET_SOURCES c67-gen.c tcccoff.c)
else()
message(FATAL_ERROR "Unrecognised target in TCC_TARGET, must be one of ${TCC_TARGET_LIST}")
endif()
add_definitions(${TCC_TARGET_FLAGS})
file(STRINGS "VERSION" TCC_VERSION)
list(GET TCC_VERSION 0 TCC_VERSION)
include_directories(${CMAKE_BINARY_DIR})
@ -86,7 +90,7 @@ set_target_properties(libtcc PROPERTIES PREFIX "")
add_executable(tcc tcc.c)
target_link_libraries(tcc libtcc)
set(TCC_CFLAGS -I${CMAKE_SOURCE_DIR}/include)
set(TCC_CFLAGS -I${CMAKE_SOURCE_DIR}/include ${CMAKE_C_FLAGS})
if(TCC_TARGET MATCHES "^Win")
set(TCC_CFLAGS ${TCC_CFLAGS} -I${CMAKE_SOURCE_DIR}/win32/include)
endif()
@ -108,7 +112,7 @@ elseif(TCC_TARGET STREQUAL "x86-64")
endif()
if (TCC_TARGET MATCHES "^Win")
set(XCC tcc ${TCC_CFLAGS} -B${CMAKE_SOURCE_DIR}/win32)
set(XCC tcc ${TCC_CFLAGS} ${TCC_TARGET_FLAGS} -B${CMAKE_SOURCE_DIR}/win32)
set(XAR tiny_libmaker${CMAKE_EXECUTABLE_SUFFIX})
set(XDEPENDS tiny_libmaker)
endif()
@ -119,7 +123,7 @@ if(LIBTCC1_SOURCES)
string(REGEX MATCH "[^/]+$" LIBTCC1_O ${LIBTCC1_C})
string(REGEX MATCH "^[^.]+" LIBTCC1_O ${LIBTCC1_O})
set(LIBTCC1_O ${LIBTCC1_O}.o)
add_custom_command(OUTPUT ${LIBTCC1_O} COMMAND ${XCC} -c ${CMAKE_SOURCE_DIR}/${LIBTCC1_C} -o ${LIBTCC1_O})
add_custom_command(OUTPUT ${LIBTCC1_O} COMMAND ${XCC} -c ${CMAKE_SOURCE_DIR}/${LIBTCC1_C} -o ${LIBTCC1_O} DEPENDS ${CMAKE_SOURCE_DIR}/${LIBTCC1_C})
list(APPEND LIBTCC1_OBJECTS ${LIBTCC1_O})
endforeach()
add_custom_command(OUTPUT libtcc1.a COMMAND ${XAR} libtcc1.a ${LIBTCC1_OBJECTS} DEPENDS ${LIBTCC1_OBJECTS} ${XDEPENDS})

View File

@ -3,14 +3,3 @@
#cmakedefine CONFIG_WIN32
#cmakedefine CONFIG_WIN64
#cmakedefine TCC_TARGET_I386
#cmakedefine TCC_TARGET_X86_64
#cmakedefine TCC_TARGET_ARM
#cmakedefine TCC_TARGET_C67
#cmakedefine TCC_TARGET_PE
#define TCC_ARM_VERSION ${TCC_ARM_VERSION}
#cmakedefine TCC_ARM_VFP
#cmakedefine TCC_ARM_EABI
#cmakedefine TCC_ARM_HARDFLOAT

View File

@ -19,9 +19,9 @@ void __va_end(va_list ap);
#else /* _WIN64 */
typedef char *va_list;
#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+7)&~7)
#define va_arg(ap,type) (ap += (sizeof(type)+7)&~7, *(type *)(ap - ((sizeof(type)+7)&~7)))
#define va_copy(dest, src) (dest) = (src)
#define va_start(ap,last) __builtin_va_start(ap,last)
#define va_arg(ap,type) (ap += 8, sizeof(type)<=8 ? *(type*)ap : **(type**)ap)
#define va_copy(dest, src) ((dest) = (src))
#define va_end(ap)
#endif

View File

@ -851,7 +851,13 @@ ST_FUNC int gv(int rc)
t = vtop->type.t;
t1 = t;
/* compute memory access type */
if (vtop->r & VT_LVAL_BYTE)
if (vtop->r & VT_REF)
#ifdef TCC_TARGET_X86_64
t = VT_PTR;
#else
t = VT_INT;
#endif
else if (vtop->r & VT_LVAL_BYTE)
t = VT_BYTE;
else if (vtop->r & VT_LVAL_SHORT)
t = VT_SHORT;
@ -3712,7 +3718,24 @@ ST_FUNC void unary(void)
}
}
break;
#if defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_PE)
#ifdef TCC_TARGET_X86_64
#ifdef TCC_TARGET_PE
case TOK_builtin_va_start:
{
next();
skip('(');
expr_eq();
skip(',');
expr_eq();
skip(')');
if ((vtop->r & VT_VALMASK) != VT_LOCAL)
tcc_error("__builtin_va_start expects a local variable");
vtop->r &= ~(VT_LVAL | VT_REF);
vtop->type = char_pointer_type;
vstore();
}
break;
#else
case TOK_builtin_va_arg_types:
{
CType type;
@ -3724,6 +3747,7 @@ ST_FUNC void unary(void)
vpushi(classify_x86_64_va_arg(&type));
}
break;
#endif
#endif
case TOK_INC:
case TOK_DEC:

View File

@ -125,7 +125,11 @@
DEF(TOK_builtin_constant_p, "__builtin_constant_p")
DEF(TOK_builtin_frame_address, "__builtin_frame_address")
#ifdef TCC_TARGET_X86_64
#ifdef TCC_TARGET_PE
DEF(TOK_builtin_va_start, "__builtin_va_start")
#else
DEF(TOK_builtin_va_arg_types, "__builtin_va_arg_types")
#endif
#endif
DEF(TOK_REGPARM1, "regparm")
DEF(TOK_REGPARM2, "__regparm__")

View File

@ -6,7 +6,7 @@ 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} -I${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/abitest.c ${LIBTCC_LIB} -o ${ABITEST_TCC} DEPENDS abitest.c)
add_custom_command(OUTPUT ${ABITEST_TCC} COMMAND tcc ${TCC_CFLAGS} -g -I${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/abitest.c ${LIBTCC_LIB} -o ${ABITEST_TCC} DEPENDS 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)
@ -20,7 +20,7 @@ if(WIN32)
set(TCC_TEST_CFLAGS ${TCC_TEST_CFLAGS} -I${CMAKE_SOURCE_DIR}/win32/include/winapi)
endif()
set(TCC_TEST_SOURCE ${TCC_TEST_CFLAGS} -run ${CMAKE_CURRENT_SOURCE_DIR}/tcctest.c)
set(TCC_TEST_RUN ${TCC_TEST_CFLAGS} -DONE_SOURCE -run ${CMAKE_SOURCE_DIR}/tcc.c)
set(TCC_TEST_RUN ${TCC_TEST_CFLAGS} ${TCC_TARGET_FLAGS} -DONE_SOURCE -run ${CMAKE_SOURCE_DIR}/tcc.c)
add_custom_command(OUTPUT tcctest.ref COMMAND tcctest-cc > tcctest.ref)
add_custom_command(OUTPUT tcctest.1 COMMAND tcc ${TCC_TEST_SOURCE} > tcctest.1)

View File

@ -262,41 +262,133 @@ static int two_member_union_test(void) {
return run_callback(src, two_member_union_test_callback);
}
/*
* Win64 calling convetntion test.
*/
typedef struct many_struct_test_type_s {long long a, b, c;} many_struct_test_type;
typedef many_struct_test_type (*many_struct_test_function_type) (many_struct_test_type,many_struct_test_type,many_struct_test_type,many_struct_test_type,many_struct_test_type,many_struct_test_type);
static int many_struct_test_callback(void *ptr) {
many_struct_test_function_type f = (many_struct_test_function_type)ptr;
many_struct_test_type v = {1, 2, 3};
many_struct_test_type r = f(v,v,v,v,v,v);
return ((r.a == 6) && (r.b == 12) && (r.c == 18))?0:-1;
}
static int many_struct_test(void) {
const char *src =
"typedef struct many_struct_test_type_s {long long a, b, c;} many_struct_test_type;\n"
"many_struct_test_type f(many_struct_test_type x1, many_struct_test_type x2, many_struct_test_type x3, many_struct_test_type x4, many_struct_test_type x5, many_struct_test_type x6) {\n"
" many_struct_test_type y;\n"
" y.a = x1.a + x2.a + x3.a + x4.a + x5.a + x6.a;\n"
" y.b = x1.b + x2.b + x3.b + x4.b + x5.b + x6.b;\n"
" y.c = x1.c + x2.c + x3.c + x4.c + x5.c + x6.c;\n"
" return y;\n"
"}\n";
return run_callback(src, many_struct_test_callback);
}
/*
* Win64 calling convention test.
*/
typedef struct many_struct_test_2_type_s {int a, b;} many_struct_test_2_type;
typedef many_struct_test_2_type (*many_struct_test_2_function_type) (many_struct_test_2_type,many_struct_test_2_type,many_struct_test_2_type,many_struct_test_2_type,many_struct_test_2_type,many_struct_test_2_type);
static int many_struct_test_2_callback(void *ptr) {
many_struct_test_2_function_type f = (many_struct_test_2_function_type)ptr;
many_struct_test_2_type v = {1,2};
many_struct_test_2_type r = f(v,v,v,v,v,v);
return ((r.a == 6) && (r.b == 12))?0:-1;
}
static int many_struct_test_2(void) {
const char *src =
"typedef struct many_struct_test_2_type_s {int a, b;} many_struct_test_2_type;\n"
"many_struct_test_2_type f(many_struct_test_2_type x1, many_struct_test_2_type x2, many_struct_test_2_type x3, many_struct_test_2_type x4, many_struct_test_2_type x5, many_struct_test_2_type x6) {\n"
" many_struct_test_2_type y;\n"
" y.a = x1.a + x2.a + x3.a + x4.a + x5.a + x6.a;\n"
" y.b = x1.b + x2.b + x3.b + x4.b + x5.b + x6.b;\n"
" return y;\n"
"}\n";
return run_callback(src, many_struct_test_2_callback);
}
/*
* stdarg_test: Test variable argument list ABI
*/
typedef void (*stdarg_test_function_type) (int,int,...);
typedef struct {long long a, b, c;} stdarg_test_struct_type;
typedef void (*stdarg_test_function_type) (int,int,int,...);
static int stdarg_test_callback(void *ptr) {
stdarg_test_function_type f = (stdarg_test_function_type)ptr;
int x;
double y;
f(10, 10,
stdarg_test_struct_type z = {1, 2, 3}, w;
f(10, 10, 5,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, &x,
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, &y);
return ((x == 55) && (y == 55)) ? 0 : -1;
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, &y,
z, z, z, z, z, &w);
return ((x == 55) && (y == 55) && (w.a == 5) && (w.b == 10) && (w.c == 15)) ? 0 : -1;
}
static int stdarg_test(void) {
const char *src =
"#include <stdarg.h>\n"
"void f(int n_int, int n_float, ...) {\n"
"typedef struct {long long a, b, c;} stdarg_test_struct_type;\n"
"void f(int n_int, int n_float, int n_struct, ...) {\n"
" int i, ti;\n"
" double td;\n"
" stdarg_test_struct_type ts = {0,0,0}, tmp;\n"
" va_list ap;\n"
" va_start(ap, n_float);\n"
" va_start(ap, n_struct);\n"
" for (i = 0, ti = 0; i < n_int; ++i)\n"
" ti += va_arg(ap, int);\n"
" *va_arg(ap, int*) = ti;\n"
" for (i = 0, td = 0; i < n_float; ++i)\n"
" td += va_arg(ap, double);\n"
" *va_arg(ap, double*) = td;\n"
" for (i = 0; i < n_struct; ++i) {\n"
" tmp = va_arg(ap, stdarg_test_struct_type);\n"
" ts.a += tmp.a; ts.b += tmp.b; ts.c += tmp.c;"
" }\n"
" *va_arg(ap, stdarg_test_struct_type*) = ts;\n"
" va_end(ap);"
"}\n";
return run_callback(src, stdarg_test_callback);
}
/*
* Test Win32 stdarg handling, since the calling convention will pass a pointer
* to the struct and the stdarg pointer must point to that pointer initially.
*/
typedef struct {long long a, b, c;} stdarg_struct_test_struct_type;
typedef int (*stdarg_struct_test_function_type) (stdarg_struct_test_struct_type a, ...);
static int stdarg_struct_test_callback(void *ptr) {
stdarg_struct_test_function_type f = (stdarg_struct_test_function_type)ptr;
stdarg_struct_test_struct_type v = {10, 35, 99};
int x = f(v, 234);
return (x == 378) ? 0 : -1;
}
static int stdarg_struct_test(void) {
const char *src =
"#include <stdarg.h>\n"
"typedef struct {long long a, b, c;} stdarg_struct_test_struct_type;\n"
"int f(stdarg_struct_test_struct_type a, ...) {\n"
" va_list ap;\n"
" va_start(ap, a);\n"
" int z = va_arg(ap, int);\n"
" va_end(ap);\n"
" return z + a.a + a.b + a.c;\n"
"}\n";
return run_callback(src, stdarg_struct_test_callback);
}
#define RUN_TEST(t) \
if (!testname || (strcmp(#t, testname) == 0)) { \
fputs(#t "... ", stdout); \
@ -336,6 +428,9 @@ int main(int argc, char **argv) {
RUN_TEST(sret_test);
RUN_TEST(one_member_union_test);
RUN_TEST(two_member_union_test);
RUN_TEST(many_struct_test);
RUN_TEST(many_struct_test_2);
RUN_TEST(stdarg_test);
RUN_TEST(stdarg_struct_test);
return retval;
}

View File

@ -23,7 +23,7 @@
#ifdef TARGET_DEFS_ONLY
/* number of available registers */
#define NB_REGS 24
#define NB_REGS 22
#define NB_ASM_REGS 8
/* a register can belong to several classes. The classes must be
@ -45,8 +45,6 @@
#define RC_XMM3 0x8000
#define RC_XMM4 0x10000
#define RC_XMM5 0x20000
#define RC_XMM6 0x40000
#define RC_XMM7 0x80000
#define RC_IRET RC_RAX /* function return: integer register */
#define RC_LRET RC_RDX /* function return: second integer register */
#define RC_FRET RC_XMM0 /* function return: float register */
@ -142,9 +140,7 @@ ST_DATA const int reg_classes[NB_REGS] = {
/* xmm2 */ RC_FLOAT | RC_XMM2,
/* xmm3 */ RC_FLOAT | RC_XMM3,
/* xmm4 */ RC_FLOAT | RC_XMM4,
/* xmm5 */ RC_FLOAT | RC_XMM5,
/* xmm6 */ RC_FLOAT | RC_XMM6,
/* xmm7 */ RC_FLOAT | RC_XMM7,
/* xmm5 */ RC_FLOAT | RC_XMM5 /* only up to xmm5: xmm6-15 must be callee saved on Win64 */
};
static unsigned long func_sub_sp_offset;
@ -484,7 +480,7 @@ void load(int r, SValue *sv)
orex(0,r,0,0);
oad(0xb8 + REG_VALUE(r), t ^ 1); /* mov $0, r */
} else if (v != r) {
if ((r == TREG_XMM0) || (r == TREG_XMM1)) {
if ((r >= TREG_XMM0) && (r <= TREG_XMM7)) {
if (v == TREG_ST0) {
/* gen_cvt_ftof(VT_DOUBLE); */
o(0xf0245cdd); /* fstpl -0x10(%rsp) */
@ -493,7 +489,7 @@ void load(int r, SValue *sv)
o(0x44 + REG_VALUE(r)*8); /* %xmmN */
o(0xf024);
} else {
assert((v == TREG_XMM0) || (v == TREG_XMM1));
assert((v >= TREG_XMM0) && (v <= TREG_XMM7));
if ((ft & VT_BTYPE) == VT_FLOAT) {
o(0x100ff3);
} else {
@ -626,10 +622,20 @@ static void gcall_or_jmp(int is_jmp)
#ifdef TCC_TARGET_PE
#define REGN 4
static const uint8_t arg_regs[] = {
static const uint8_t arg_regs[REGN] = {
TREG_RCX, TREG_RDX, TREG_R8, TREG_R9
};
/* Prepare arguments in R10 and R11 rather than RCX and RDX
because gv() will not ever use these */
static int arg_prepare_reg(int idx) {
if (idx == 0 || idx == 1)
/* idx=0: r10, idx=1: r11 */
return idx + 10;
else
return arg_regs[idx];
}
static int func_scratch;
/* Generate function call. The function address is pushed first, then
@ -651,26 +657,56 @@ void gen_offs_sp(int b, int r, int d)
/* Return 1 if this function returns via an sret pointer, 0 otherwise */
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) {
*ret_align = 1; // Never have to re-align return values for x86-64
return 1;
int size, align;
size = type_size(vt, &align);
ret->ref = NULL;
if (size > 8) {
return 1;
} else if (size > 4) {
ret->t = VT_LLONG;
return 0;
} else if (size > 2) {
ret->t = VT_INT;
return 0;
} else if (size > 1) {
ret->t = VT_SHORT;
return 0;
} else {
ret->t = VT_BYTE;
return 0;
}
}
int gfunc_arg_size(CType *type) {
if (type->t & (VT_ARRAY|VT_BITFIELD))
return 8;
int align;
return type_size(type, &align);
}
void gfunc_call(int nb_args)
{
int size, align, r, args_size, i, d, j, bt, struct_size;
int nb_reg_args, gen_reg;
int size, r, args_size, i, d, bt, struct_size;
int arg;
nb_reg_args = nb_args;
args_size = (nb_reg_args < REGN ? REGN : nb_reg_args) * PTR_SIZE;
args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE;
arg = nb_args;
/* for struct arguments, we need to call memcpy and the function
call breaks register passing arguments we are preparing.
So, we process arguments which will be passed by stack first. */
struct_size = args_size;
for(i = 0; i < nb_args; i++) {
--arg;
SValue *sv = &vtop[-i];
bt = (sv->type.t & VT_BTYPE);
size = gfunc_arg_size(&sv->type);
if (size <= 8)
continue; /* arguments smaller than 8 bytes passed in registers or on stack */
if (bt == VT_STRUCT) {
size = type_size(&sv->type, &align);
/* align to stack align size */
size = (size + 15) & ~15;
/* generate structure store */
@ -683,85 +719,81 @@ void gfunc_call(int nb_args)
vpushv(sv);
vstore();
--vtop;
} else if (bt == VT_LDOUBLE) {
gv(RC_ST0);
gen_offs_sp(0xdb, 0x107, struct_size);
struct_size += 16;
}
}
if (func_scratch < struct_size)
func_scratch = struct_size;
#if 1
for (i = 0; i < REGN; ++i)
save_reg(arg_regs[i]);
save_reg(TREG_RAX);
#endif
gen_reg = nb_reg_args;
arg = nb_args;
struct_size = args_size;
for(i = 0; i < nb_args; i++) {
--arg;
bt = (vtop->type.t & VT_BTYPE);
if (bt == VT_STRUCT || bt == VT_LDOUBLE) {
if (bt == VT_LDOUBLE)
size = 16;
else
size = type_size(&vtop->type, &align);
size = gfunc_arg_size(&vtop->type);
if (size > 8) {
/* align to stack align size */
size = (size + 15) & ~15;
j = --gen_reg;
if (j >= REGN) {
d = TREG_RAX;
if (arg >= REGN) {
d = get_reg(RC_INT);
gen_offs_sp(0x8d, d, struct_size);
gen_offs_sp(0x89, d, j*8);
gen_offs_sp(0x89, d, arg*8);
} else {
d = arg_regs[j];
d = arg_prepare_reg(arg);
gen_offs_sp(0x8d, d, struct_size);
}
struct_size += size;
} else if (is_sse_float(vtop->type.t)) {
gv(RC_XMM0); /* only one float register */
j = --gen_reg;
if (j >= REGN) {
/* movq %xmm0, j*8(%rsp) */
gen_offs_sp(0xd60f66, 0x100, j*8);
} else {
/* movaps %xmm0, %xmmN */
o(0x280f);
o(0xc0 + (j << 3));
d = arg_regs[j];
/* mov %xmm0, %rxx */
o(0x66);
orex(1,d,0, 0x7e0f);
o(0xc0 + REG_VALUE(d));
}
} else {
j = --gen_reg;
if (j >= REGN) {
r = gv(RC_INT);
gen_offs_sp(0x89, r, j*8);
} else {
d = arg_regs[j];
if (d < NB_REGS) {
gv(reg_classes[d] & ~RC_INT);
if (is_sse_float(vtop->type.t)) {
gv(RC_XMM0); /* only use one float register */
if (arg >= REGN) {
/* movq %xmm0, j*8(%rsp) */
gen_offs_sp(0xd60f66, 0x100, arg*8);
} else {
r = gv(RC_INT);
if (d != r) {
orex(1,d,r, 0x89);
o(0xc0 + REG_VALUE(d) + REG_VALUE(r) * 8);
}
/* movaps %xmm0, %xmmN */
o(0x280f);
o(0xc0 + (arg << 3));
d = arg_prepare_reg(arg);
/* mov %xmm0, %rxx */
o(0x66);
orex(1,d,0, 0x7e0f);
o(0xc0 + REG_VALUE(d));
}
} else {
if (bt == VT_STRUCT) {
vtop->type.ref = NULL;
vtop->type.t = size > 4 ? VT_LLONG : size > 2 ? VT_INT
: size > 1 ? VT_SHORT : VT_BYTE;
}
r = gv(RC_INT);
if (arg >= REGN) {
gen_offs_sp(0x89, r, arg*8);
} else {
d = arg_prepare_reg(arg);
orex(1,d,r,0x89); /* mov */
o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
}
}
}
vtop--;
}
save_regs(0);
/* Copy R10 and R11 into RCX and RDX, respectively */
if (nb_args > 0) {
o(0xd1894c); /* mov %r10, %rcx */
if (nb_args > 1) {
o(0xda894c); /* mov %r11, %rdx */
}
}
gcall_or_jmp(0);
vtop--;
}
@ -772,7 +804,7 @@ void gfunc_call(int nb_args)
/* generate function prolog of type 't' */
void gfunc_prolog(CType *func_type)
{
int addr, reg_param_index, bt;
int addr, reg_param_index, bt, size;
Sym *sym;
CType *type;
@ -790,34 +822,46 @@ void gfunc_prolog(CType *func_type)
/* if the function returns a structure, then add an
implicit pointer parameter */
func_vt = sym->type;
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
size = gfunc_arg_size(&func_vt);
if (size > 8) {
gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
func_vc = addr;
reg_param_index++;
addr += PTR_SIZE;
addr += 8;
}
/* define parameters */
while ((sym = sym->next) != NULL) {
type = &sym->type;
bt = type->t & VT_BTYPE;
if (reg_param_index < REGN) {
/* save arguments passed by register */
gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
}
if (bt == VT_STRUCT || bt == VT_LDOUBLE) {
size = gfunc_arg_size(type);
if (size > 8) {
if (reg_param_index < REGN) {
gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
}
sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL | VT_REF, addr);
} else {
if (reg_param_index < REGN) {
/* save arguments passed by register */
if ((bt == VT_FLOAT) || (bt == VT_DOUBLE)) {
o(0xd60f66); /* movq */
gen_modrm(reg_param_index, VT_LOCAL, NULL, addr);
} else {
gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
}
}
sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr);
}
addr += 8;
reg_param_index++;
addr += PTR_SIZE;
}
while (reg_param_index < REGN) {
if (func_type->ref->c == FUNC_ELLIPSIS)
if (func_type->ref->c == FUNC_ELLIPSIS) {
gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
addr += 8;
}
reg_param_index++;
addr += PTR_SIZE;
}
}
@ -1072,13 +1116,13 @@ void gfunc_call(int nb_args)
}
for(i = 0; i < nb_args;) {
/* Swap argument to top, it will possibly be changed here,
and might use more temps. At the end of the loop we keep
in on the stack and swap it back to its original position
if it is a register. */
/* Swap argument to top, it will possibly be changed here,
and might use more temps. At the end of the loop we keep
in on the stack and swap it back to its original position
if it is a register. */
SValue tmp = vtop[0];
vtop[0] = vtop[-i];
vtop[-i] = tmp;
vtop[0] = vtop[-i];
vtop[-i] = tmp;
mode = classify_x86_64_arg(&vtop->type, NULL, &size, &reg_count);
@ -1183,23 +1227,23 @@ void gfunc_call(int nb_args)
/* Alter stack entry type so that gv() knows how to treat it */
vtop->type = type;
if (mode == x86_64_mode_sse) {
if (reg_count == 2) {
sse_reg -= 2;
gv(RC_FRET); /* Use pair load into xmm0 & xmm1 */
if (sse_reg) { /* avoid redundant movaps %xmm0, %xmm0 */
/* movaps %xmm0, %xmmN */
o(0x280f);
o(0xc0 + (sse_reg << 3));
/* movaps %xmm1, %xmmN */
o(0x280f);
o(0xc1 + ((sse_reg+1) << 3));
}
} else {
assert(reg_count == 1);
--sse_reg;
/* Load directly to register */
gv(RC_XMM0 << sse_reg);
}
if (reg_count == 2) {
sse_reg -= 2;
gv(RC_FRET); /* Use pair load into xmm0 & xmm1 */
if (sse_reg) { /* avoid redundant movaps %xmm0, %xmm0 */
/* movaps %xmm0, %xmmN */
o(0x280f);
o(0xc0 + (sse_reg << 3));
/* movaps %xmm1, %xmmN */
o(0x280f);
o(0xc1 + ((sse_reg+1) << 3));
}
} else {
assert(reg_count == 1);
--sse_reg;
/* Load directly to register */
gv(RC_XMM0 << sse_reg);
}
} else if (mode == x86_64_mode_integer) {
/* simple type */
/* XXX: implicit cast ? */
@ -1209,8 +1253,6 @@ void gfunc_call(int nb_args)
orex(1,d,r,0x89); /* mov */
o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
if (reg_count == 2) {
/* Second word of two-word value should always be in rdx
this case is handled via RC_IRET */
d = arg_prepare_reg(gen_reg+1);
orex(1,d,vtop->r2,0x89); /* mov */
o(0xc0 + REG_VALUE(vtop->r2) * 8 + REG_VALUE(d));
@ -1255,7 +1297,7 @@ void gfunc_prolog(CType *func_type)
{
X86_64_Mode mode;
int i, addr, align, size, reg_count;
int param_index, param_addr, reg_param_index, sse_param_index;
int param_addr, reg_param_index, sse_param_index;
Sym *sym;
CType *type;
@ -1328,7 +1370,6 @@ void gfunc_prolog(CType *func_type)
}
sym = func_type->ref;
param_index = 0;
reg_param_index = 0;
sse_param_index = 0;
@ -1338,10 +1379,7 @@ void gfunc_prolog(CType *func_type)
mode = classify_x86_64_arg(&func_vt, NULL, &size, &reg_count);
if (mode == x86_64_mode_memory) {
push_arg_reg(reg_param_index);
param_addr = loc;
func_vc = loc;
param_index++;
reg_param_index++;
}
/* define parameters */
@ -1391,7 +1429,6 @@ void gfunc_prolog(CType *func_type)
}
sym_push(sym->v & ~SYM_FIELD, type,
VT_LOCAL | VT_LVAL, param_addr);
param_index++;
}
}