64-bit tests now pass (well, nearly).
authorJames Lyon <jamesly0n@hotmail.com>
Wed, 24 Apr 2013 01:19:15 +0000 (24 02:19 +0100)
committerJames Lyon <jamesly0n@hotmail.com>
Wed, 24 Apr 2013 01:19:15 +0000 (24 02:19 +0100)
tcctest1-3 fail, but this appears to be due to bugs in GCC rather than TCC
(from manual inspection of the output).

CMakeLists.txt
config.h.in
include/stdarg.h
tccgen.c
tcctok.h
tests/CMakeLists.txt
tests/abitest.c
x86_64-gen.c

index c055ab6..12fda70 100644 (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})
dissimilarity index 66%
index 63b8151..588408d 100644 (file)
@@ -1,16 +1,5 @@
-#define CONFIG_TCCDIR "${CMAKE_INSTALL_PREFIX}"
-#define TCC_VERSION "${TCC_VERSION}"
-
-#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
+#define CONFIG_TCCDIR "${CMAKE_INSTALL_PREFIX}"
+#define TCC_VERSION "${TCC_VERSION}"
+
+#cmakedefine CONFIG_WIN32
+#cmakedefine CONFIG_WIN64
index 666adf7..154030f 100644 (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
 
index d897296..0d1e21b 100644 (file)
--- a/tccgen.c
+++ b/tccgen.c
@@ -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;
@@ -3725,6 +3748,7 @@ ST_FUNC void unary(void)
         }
         break;
 #endif
+#endif
     case TOK_INC:
     case TOK_DEC:
         t = tok;
index f754af5..fde13dd 100644 (file)
--- a/tcctok.h
+++ b/tcctok.h
      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__")
 
index 22a3552..684e809 100644 (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)
index 3064fa5..a51eb9b 100644 (file)
@@ -263,40 +263,132 @@ static int two_member_union_test(void) {
 }
 
 /*
+ * 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;
 }
index 4b9d28d..1cf3bbc 100644 (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);
+            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 {
+                    /* 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 {
-                d = arg_regs[j];
-                if (d < NB_REGS) {
-                    gv(reg_classes[d] & ~RC_INT);
+                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 {
-                    r = gv(RC_INT);
-                    if (d != r) {
-                        orex(1,d,r, 0x89);
-                        o(0xc0 + REG_VALUE(d) + REG_VALUE(r) * 8);
-                    }
+                    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++;
     }
 }