Rewrite of Windows x64 value type ABI to support DirectX wrapper libraries.
authorlateralusX <lateralusx.github@gmail.com>
Thu, 14 Jul 2016 11:27:44 +0000 (13:27 +0200)
committerlateralusX <lateralusx.github@gmail.com>
Thu, 14 Jul 2016 17:34:52 +0000 (19:34 +0200)
Current implementation used different value type ABI’s depending on if call
was a managed->native, native->managed or managed->managed call. The implementation
was also overcomplicated since it was a tweaked copy from System V x64 value type
ABI with scenarios and conditions that wouldn’t apply to the Windows x64 value
type ABI. Having a uniform value type ABI when possible regardless of managed->native,
native->managed or managed->managed calls simplifies scenarios and reduce complexity.

While all the above are legitimate reasons the primary driving factor behind
the rewrite was a different issue. There are .NET wrapper libraries around DirectX API
used by game engines and other libraries that uses a pattern using a call into a stub
managed method that gets patched directly in the MSIL to a calli that will end up in a
native DirectX vtable method. In current ABI we can’t detect scenario like this, so arguments
are passed as if the call was a managed->managed call causing issues for value types. Since DirectX
uses value types a lot (vectors, matrixes etc.), this was a blocker getting these technologies
running on Windows using DirectX at all. Another approach could be to change the behavior of the
library, but this would require a major redesign of the library and since several of these libraries
are mature, that wasn’t a realistic option, especially since MS .NET runtime handles the libraries as is
without modifications.

The rewrite makes sure Windows x64 value type ABI follow the platform value type ABI in all scenarios.

mono/mini/mini-amd64.c
mono/mini/mini-amd64.h

index 2f855729e9adcdb172cbd8dea181edd7294068a9..e31aada27c3978c4c0ea314a813ecba46e10e223 100644 (file)
@@ -428,206 +428,217 @@ collect_field_info_nested (MonoClass *klass, StructFieldInfo *fields, int index,
 }
 
 #ifdef TARGET_WIN32
-static void
-add_valuetype_win64 (MonoMethodSignature *sig, ArgInfo *ainfo, MonoType *type,
-                                        gboolean is_return,
-                                        guint32 *gr, guint32 *fr, guint32 *stack_size)
-{
-       guint32 size, i, nfields;
-       guint32 argsize = 8;
-       ArgumentClass arg_class;
-       MonoMarshalType *info = NULL;
-       StructFieldInfo *fields = NULL;
-       MonoClass *klass;
-       gboolean pass_on_stack = FALSE;
 
-       klass = mono_class_from_mono_type (type);
-       size = mini_type_stack_size_full (&klass->byval_arg, NULL, sig->pinvoke);
+/* Windows x64 ABI can pass/return value types in register of size 1,2,4,8 bytes. */
+#define MONO_WIN64_VALUE_TYPE_FITS_REG(arg_size) (arg_size <= SIZEOF_REGISTER && (arg_size == 1 || arg_size == 2 || arg_size == 4 || arg_size == 8))
 
-       /*
-       * Standard C and C++ doesn't allow empty structs, empty structs will always have a size of 1 byte.
-       * GCC have an extension to allow empty structs, https://gcc.gnu.org/onlinedocs/gcc/Empty-Structures.html.
-       * This cause a little dilemma since runtime build using none GCC compiler will not be compatible with
-       * GCC build C libraries and the other way around. On platforms where empty structs has size of 1 byte
-       * it must be represented in call and cannot be dropped.
-       */
-       if (0 == size && MONO_TYPE_ISSTRUCT (type) && sig->pinvoke)
-               ainfo->pass_empty_struct = TRUE;
-       
-       if (!sig->pinvoke)
-               pass_on_stack = TRUE;
+static gboolean
+allocate_register_for_valuetype_win64 (ArgInfo *arg_info, ArgumentClass arg_class, guint32 arg_size, AMD64_Reg_No int_regs [], int int_reg_count, AMD64_Reg_No float_regs [], int float_reg_count, guint32 *current_int_reg, guint32 *current_float_reg)
+{
+       gboolean result = FALSE;
 
-       /* If this struct can't be split up naturally into 8-byte */
-       /* chunks (registers), pass it on the stack.              */
-       if (sig->pinvoke && !pass_on_stack) {
-               guint32 align;
-               guint32 field_size;
+       assert (arg_info != NULL && int_regs != NULL && float_regs != NULL && current_int_reg != NULL && current_float_reg != NULL);
+       assert (arg_info->storage == ArgValuetypeInReg || arg_info->storage == ArgValuetypeAddrInIReg);
 
-               info = mono_marshal_load_type_info (klass);
-               g_assert (info);
+       arg_info->pair_storage [0] = arg_info->pair_storage [1] = ArgNone;
+       arg_info->pair_regs [0] = arg_info->pair_regs [1] = ArgNone;
+       arg_info->pair_size [0] = 0;
+       arg_info->pair_size [1] = 0;
+       arg_info->nregs = 0;
 
-               /*
-                * Collect field information recursively to be able to
-                * handle nested structures.
-                */
-               nfields = count_fields_nested (klass, sig->pinvoke);
-               fields = g_new0 (StructFieldInfo, nfields);
-               collect_field_info_nested (klass, fields, 0, 0, sig->pinvoke, klass->unicode);
+       if (arg_class == ARG_CLASS_INTEGER && *current_int_reg < int_reg_count) {
+               /* Pass parameter in integer register. */
+               arg_info->pair_storage [0] = ArgInIReg;
+               arg_info->pair_regs [0] = int_regs [*current_int_reg];
+               (*current_int_reg) ++;
+               result = TRUE;
+       } else if (arg_class == ARG_CLASS_SSE && *current_float_reg < float_reg_count) {
+               /* Pass parameter in float register. */
+               arg_info->pair_storage [0] = (arg_size <= sizeof (gfloat)) ? ArgInFloatSSEReg : ArgInDoubleSSEReg;
+               arg_info->pair_regs [0] = float_regs [*current_float_reg];
+               (*current_float_reg) ++;
+               result = TRUE;
+       }
 
-               for (i = 0; i < nfields; ++i) {
-                       if ((fields [i].offset < 8) && (fields [i].offset + fields [i].size) > 8) {
-                               pass_on_stack = TRUE;
-                               break;
-                       }
-               }
+       if (result == TRUE) {
+               arg_info->pair_size [0] = arg_size;
+               arg_info->nregs = 1;
        }
 
-       if (pass_on_stack) {
-               /* Allways pass in memory */
-               ainfo->offset = *stack_size;
-               *stack_size += ALIGN_TO (size, 8);
-               ainfo->storage = is_return ? ArgValuetypeAddrInIReg : ArgOnStack;
-               if (!is_return)
-                       ainfo->arg_size = ALIGN_TO (size, 8);
+       return result;
+}
 
-               g_free (fields);
-               return;
-       }
+inline gboolean
+allocate_parameter_register_for_valuetype_win64 (ArgInfo *arg_info, ArgumentClass arg_class, guint32 arg_size, guint32 *current_int_reg, guint32 *current_float_reg)
+{
+       return allocate_register_for_valuetype_win64 (arg_info, arg_class, arg_size, param_regs, PARAM_REGS, float_param_regs, FLOAT_PARAM_REGS, current_int_reg, current_float_reg);
+}
 
-       if (!sig->pinvoke) {
-               int n = mono_class_value_size (klass, NULL);
+inline gboolean
+allocate_return_register_for_valyetype_win64 (ArgInfo *arg_info, ArgumentClass arg_class, guint32 arg_size, guint32 *current_int_reg, guint32 *current_float_reg)
+{
+       return allocate_register_for_valuetype_win64 (arg_info, arg_class, arg_size, return_regs, RETURN_REGS, float_return_regs, FLOAT_RETURN_REGS, current_int_reg, current_float_reg);
+}
 
-               argsize = n;
+static void
+allocate_storage_for_valuetype_win64 (ArgInfo *arg_info, MonoType *type, gboolean is_return, ArgumentClass arg_class,
+                                                                         guint32 arg_size, guint32 *current_int_reg, guint32 *current_float_reg, guint32 *stack_size)
+{
+       /* Windows x64 value type ABI.
+       *
+       * Parameters: https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
+       *
+       * Integer/Float types smaller than or equals to 8 bytes or porperly sized struct/union (1,2,4,8)
+       *    Try pass in register using ArgValuetypeInReg/(ArgInIReg|ArgInFloatSSEReg|ArgInDoubleSSEReg) as storage and size of parameter(1,2,4,8), if no more registers, pass on stack using ArgOnStack as storage and size of parameter(1,2,4,8).
+       * Integer/Float types bigger than 8 bytes or struct/unions larger than 8 bytes or (3,5,6,7).
+       *    Try to pass pointer in register using ArgValuetypeAddrInIReg, if no more registers, pass pointer on stack using ArgValuetypeAddrOnStack as storage and parameter size of register (8 bytes).
+       *
+       * Return values:  https://msdn.microsoft.com/en-us/library/7572ztz4.aspx.
+       *
+       * Integers/Float types smaller than or equal to 8 bytes
+       *    Return in corresponding register RAX/XMM0 using ArgValuetypeInReg/(ArgInIReg|ArgInFloatSSEReg|ArgInDoubleSSEReg) as storage and size of parameter(1,2,4,8).
+       * Properly sized struct/unions (1,2,4,8)
+       *    Return in register RAX using ArgValuetypeInReg as storage and size of parameter(1,2,4,8).
+       * Types bigger than 8 bytes or struct/unions larger than 8 bytes or (3,5,6,7).
+       *    Return pointer to allocated stack space (allocated by caller) using ArgValuetypeAddrInIReg as storage and parameter size.
+       */
 
-               if (n > 8)
-                       arg_class = ARG_CLASS_MEMORY;
-               else
-                       /* Always pass in 1 integer register */
-                       arg_class = ARG_CLASS_INTEGER;
-       } else {
-               g_assert (info);
+       assert (arg_info != NULL && type != NULL && current_int_reg != NULL && current_float_reg != NULL && stack_size != NULL);
 
-               /*Only drop value type if its not an empty struct as input that must be represented in call*/
-               if ((!fields && !ainfo->pass_empty_struct) || (!fields && ainfo->pass_empty_struct && is_return)) {
-                       ainfo->storage = ArgValuetypeInReg;
-                       ainfo->pair_storage [0] = ainfo->pair_storage [1] = ArgNone;
-                       return;
-               }
+       if (!is_return) {
 
-               switch (info->native_size) {
-               case 0:
-                       g_assert (!fields && MONO_TYPE_ISSTRUCT (type) && !is_return);
-                       break;
-               case 1: case 2: case 4: case 8:
-                       break;
-               default:
-                       if (is_return) {
-                               ainfo->storage = ArgValuetypeAddrInIReg;
-                               ainfo->offset = *stack_size;
-                               *stack_size += ALIGN_TO (info->native_size, 8);
-                       }
-                       else {
-                               ainfo->storage = ArgValuetypeAddrInIReg;
+               /* Parameter cases. */
+               if (arg_class != ARG_CLASS_MEMORY && MONO_WIN64_VALUE_TYPE_FITS_REG (arg_size)) {
 
-                               if (*gr < PARAM_REGS) {
-                                       ainfo->pair_storage [0] = ArgInIReg;
-                                       ainfo->pair_regs [0] = param_regs [*gr];
-                                       (*gr) ++;
-                               }
-                               else {
-                                       ainfo->pair_storage [0] = ArgOnStack;
-                                       ainfo->offset = *stack_size;
-                                       ainfo->arg_size = sizeof (mgreg_t);
-                                       *stack_size += 8;
-                               }
-                       }
+                       assert (arg_size == 1 || arg_size == 2 || arg_size == 4 || arg_size == 8);
 
-                       g_free (fields);
-                       return;
+                       /* First, try to use registers for parameter. If type is struct it can only be passed by value in integer register. */
+                       arg_info->storage = ArgValuetypeInReg;
+                       if (!allocate_parameter_register_for_valuetype_win64 (arg_info, !MONO_TYPE_ISSTRUCT (type) ? arg_class : ARG_CLASS_INTEGER, arg_size, current_int_reg, current_float_reg)) {
+
+                               /* No more registers, fallback passing parameter on stack as value. */
+                               assert (arg_info->pair_storage [0] == ArgNone && arg_info->pair_storage [1] == ArgNone && arg_info->pair_size [0] == 0 && arg_info->pair_size [1] == 0 && arg_info->nregs == 0);
+                               
+                               /* Passing value directly on stack, so use size of value. */
+                               arg_info->storage = ArgOnStack;
+                               arg_size = ALIGN_TO (arg_size, sizeof (mgreg_t));
+                               arg_info->offset = *stack_size;
+                               arg_info->arg_size = arg_size;
+                               *stack_size += arg_size;
+                       }
+               } else {
+                       
+                       /* Fallback to stack, try to pass address to parameter in register. Always use integer register to represent stack address. */
+                       arg_info->storage = ArgValuetypeAddrInIReg;
+                       if (!allocate_parameter_register_for_valuetype_win64 (arg_info, ARG_CLASS_INTEGER, arg_size, current_int_reg, current_float_reg)) {
+
+                               /* No more registers, fallback passing address to parameter on stack. */
+                               assert (arg_info->pair_storage [0] == ArgNone && arg_info->pair_storage [1] == ArgNone && arg_info->pair_size [0] == 0 && arg_info->pair_size [1] == 0 && arg_info->nregs == 0);
+                                                               
+                               /* Passing an address to value on stack, so use size of register as argument size. */
+                               arg_info->storage = ArgValuetypeAddrOnStack;
+                               arg_size = sizeof (mgreg_t);
+                               arg_info->offset = *stack_size;
+                               arg_info->arg_size = arg_size;
+                               *stack_size += arg_size;
+                       }
                }
+       } else {
 
-               int size;
-               guint32 align;
-               ArgumentClass class1;
+               /* Return value cases. */
+               if (arg_class != ARG_CLASS_MEMORY && MONO_WIN64_VALUE_TYPE_FITS_REG (arg_size)) {
 
-               if (nfields == 0 && ainfo->pass_empty_struct) {
-                       g_assert (!fields && !is_return);
-                       class1 = ARG_CLASS_INTEGER;
-               }
-               else if (nfields == 0)
-                       class1 = ARG_CLASS_MEMORY;
-               else
-                       class1 = ARG_CLASS_NO_CLASS;
-               for (i = 0; i < nfields; ++i) {
-                       /* How far into this quad this data extends.*/
-                       /* (8 is size of quad) */
-                       argsize = fields [i].offset + fields [i].size;
+                       assert (arg_size == 1 || arg_size == 2 || arg_size == 4 || arg_size == 8);
+
+                       /* Return value fits into return registers. If type is struct it can only be returned by value in integer register. */
+                       arg_info->storage = ArgValuetypeInReg;
+                       allocate_return_register_for_valyetype_win64 (arg_info, !MONO_TYPE_ISSTRUCT (type) ? arg_class : ARG_CLASS_INTEGER, arg_size, current_int_reg, current_float_reg);
+
+                       /* Only RAX/XMM0 should be used to return valyetype. */
+                       assert ((arg_info->pair_regs[0] == AMD64_RAX && arg_info->pair_regs[1] == ArgNone) || (arg_info->pair_regs[0] == AMD64_XMM0 && arg_info->pair_regs[1] == ArgNone));
+
+               } else {
+
+                       /* Return value doesn't fit into return register, return address to allocated stack space (allocated by caller and passed as input). */
+                       arg_info->storage = ArgValuetypeAddrInIReg;
+                       allocate_return_register_for_valyetype_win64 (arg_info, ARG_CLASS_INTEGER, arg_size, current_int_reg, current_float_reg);
+
+                       /* Only RAX should be used to return valyetype address. */
+                       assert (arg_info->pair_regs[0] == AMD64_RAX && arg_info->pair_regs[1] == ArgNone);
 
-                       class1 = merge_argument_class_from_type (fields [i].type, class1);
+                       arg_size = ALIGN_TO (arg_size, sizeof (mgreg_t));
+                       arg_info->offset = *stack_size;
+                       *stack_size += arg_size;
                }
-               g_assert (class1 != ARG_CLASS_NO_CLASS);
-               arg_class = class1;
        }
+}
 
-       g_free (fields);
+static void
+get_valuetype_size_win64 (MonoClass *klass, gboolean pinvoke, ArgInfo *arg_info, MonoType *type, ArgumentClass *arg_class, guint32 *arg_size)
+{
+       *arg_size = 0;
+       *arg_class = ARG_CLASS_NO_CLASS;
 
-       /* Allocate registers */
-       {
-               int orig_gr = *gr;
-               int orig_fr = *fr;
+       assert (klass != NULL && arg_info != NULL && type != NULL && arg_class != NULL && arg_size != NULL);
+       
+       if (pinvoke == TRUE) {
+               /* Calculate argument class type and size of marshalled type. */
+               MonoMarshalType *info = mono_marshal_load_type_info (klass);
+               *arg_size = info->native_size;
+       } else {
+               /* Calculate argument class type and size of managed type. */
+               *arg_size = mono_class_value_size (klass, NULL);
+       }
+               
+       if (!MONO_WIN64_VALUE_TYPE_FITS_REG (*arg_size)) {
+               /* Value type has a size that doesn't seem to fit register according to ABI. Try to used full stack size of type. */
+               *arg_size = mini_type_stack_size_full (&klass->byval_arg, NULL, pinvoke);
+       }
 
-               while (argsize != 1 && argsize != 2 && argsize != 4 && argsize != 8)
-                       argsize ++;
+       /* Windows ABI only handle value types on stack or passed in integer register (if it fits register size). */
+       *arg_class = (*arg_size > SIZEOF_REGISTER) ? ARG_CLASS_MEMORY : ARG_CLASS_INTEGER;
 
-               ainfo->storage = ArgValuetypeInReg;
-               ainfo->pair_storage [0] = ainfo->pair_storage [1] = ArgNone;
-               ainfo->pair_size [0] = argsize;
-               ainfo->pair_size [1] = 0;
-               ainfo->nregs = 1;
-               switch (arg_class) {
-               case ARG_CLASS_INTEGER:
-                       if (*gr >= PARAM_REGS)
-                               arg_class = ARG_CLASS_MEMORY;
-                       else {
-                               ainfo->pair_storage [0] = ArgInIReg;
-                               if (is_return)
-                                       ainfo->pair_regs [0] = return_regs [*gr];
-                               else
-                                       ainfo->pair_regs [0] = param_regs [*gr];
-                               (*gr) ++;
-                       }
-                       break;
-               case ARG_CLASS_SSE:
-                       if (*fr >= FLOAT_PARAM_REGS)
-                               arg_class = ARG_CLASS_MEMORY;
-                       else {
-                               if (argsize <= 4)
-                                       ainfo->pair_storage [0] = ArgInFloatSSEReg;
-                               else
-                                       ainfo->pair_storage [0] = ArgInDoubleSSEReg;
-                               ainfo->pair_regs [0] = *fr;
-                               (*fr) ++;
-                       }
-                       break;
-               case ARG_CLASS_MEMORY:
-                       break;
-               default:
-                       g_assert_not_reached ();
-               }
+       /*
+       * Standard C and C++ doesn't allow empty structs, empty structs will always have a size of 1 byte.
+       * GCC have an extension to allow empty structs, https://gcc.gnu.org/onlinedocs/gcc/Empty-Structures.html.
+       * This cause a little dilemma since runtime build using none GCC compiler will not be compatible with
+       * GCC build C libraries and the other way around. On platforms where empty structs has size of 1 byte
+       * it must be represented in call and cannot be dropped.
+       */
+       if (*arg_size == 0 && MONO_TYPE_ISSTRUCT (type)) {
+               arg_info->pass_empty_struct = TRUE;
+               *arg_size = SIZEOF_REGISTER;
+               *arg_class = ARG_CLASS_INTEGER;
+       }
 
-               if (arg_class == ARG_CLASS_MEMORY) {
-                       /* Revert possible register assignments */
-                       *gr = orig_gr;
-                       *fr = orig_fr;
+       assert (*arg_class != ARG_CLASS_NO_CLASS);
+}
 
-                       ainfo->offset = *stack_size;
-                       *stack_size += sizeof (mgreg_t);
-                       ainfo->storage = is_return ? ArgValuetypeAddrInIReg : ArgOnStack;
-                       if (!is_return)
-                               ainfo->arg_size = sizeof (mgreg_t);
-               }
+static void
+add_valuetype_win64 (MonoMethodSignature *signature, ArgInfo *arg_info, MonoType *type,
+                                               gboolean is_return, guint32 *current_int_reg, guint32 *current_float_reg, guint32 *stack_size)
+{
+       guint32 arg_size = SIZEOF_REGISTER;
+       MonoClass *klass = NULL;
+       ArgumentClass arg_class;
+       
+       assert (signature != NULL && arg_info != NULL && type != NULL && current_int_reg != NULL && current_float_reg != NULL && stack_size != NULL);
+
+       klass = mono_class_from_mono_type (type);
+       get_valuetype_size_win64 (klass, signature->pinvoke, arg_info, type, &arg_class, &arg_size);
+
+       /* Only drop value type if its not an empty struct as input that must be represented in call */
+       if ((arg_size == 0 && !arg_info->pass_empty_struct) || (arg_size == 0 && arg_info->pass_empty_struct && is_return)) {
+               arg_info->storage = ArgValuetypeInReg;
+               arg_info->pair_storage [0] = arg_info->pair_storage [1] = ArgNone;
+       }
+       else
+       {
+               /* Alocate storage for value type. */
+               allocate_storage_for_valuetype_win64 (arg_info, type, is_return, arg_class, arg_size, current_int_reg, current_float_reg, stack_size);
        }
 }
+
 #endif /* TARGET_WIN32 */
 
 static void
@@ -1225,6 +1236,10 @@ mono_arch_cpu_optimizations (guint32 *exclude_mask)
                *exclude_mask |= MONO_OPT_CMOV;
        }
 
+#ifdef TARGET_WIN32
+       *exclude_mask |= MONO_OPT_SIMD;
+#endif
+
        return opts;
 }
 
@@ -1348,7 +1363,7 @@ mono_arch_compute_omit_fp (MonoCompile *cfg)
        for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
                ArgInfo *ainfo = &cinfo->args [i];
 
-               if (ainfo->storage == ArgOnStack) {
+               if (ainfo->storage == ArgOnStack || ainfo->storage == ArgValuetypeAddrInIReg || ainfo->storage == ArgValuetypeAddrOnStack) {
                        /* 
                         * The stack offset can only be determined when the frame
                         * size is known.
@@ -1727,11 +1742,13 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                                break;
                        case ArgValuetypeInReg:
                                break;
-                       case ArgValuetypeAddrInIReg: {
+                       case ArgValuetypeAddrInIReg:
+                       case ArgValuetypeAddrOnStack: {
                                MonoInst *indir;
                                g_assert (!cfg->arch.omit_fp);
-                               
+                               g_assert (ainfo->storage == ArgValuetypeAddrInIReg || (ainfo->storage == ArgValuetypeAddrOnStack && ainfo->pair_storage [0] == ArgNone));
                                MONO_INST_NEW (cfg, indir, 0);
+
                                indir->opcode = OP_REGOFFSET;
                                if (ainfo->pair_storage [0] == ArgInIReg) {
                                        indir->inst_basereg = cfg->frame_reg;
@@ -1753,7 +1770,7 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                                NOT_IMPLEMENTED;
                        }
 
-                       if (!inreg && (ainfo->storage != ArgOnStack) && (ainfo->storage != ArgValuetypeAddrInIReg) && (ainfo->storage != ArgGSharedVtOnStack)) {
+                       if (!inreg && (ainfo->storage != ArgOnStack) && (ainfo->storage != ArgValuetypeAddrInIReg) && (ainfo->storage != ArgValuetypeAddrOnStack) && (ainfo->storage != ArgGSharedVtOnStack)) {
                                ins->opcode = OP_REGOFFSET;
                                ins->inst_basereg = cfg->frame_reg;
                                /* These arguments are saved to the stack in the prolog */
@@ -2139,6 +2156,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
                case ArgOnStack:
                case ArgValuetypeInReg:
                case ArgValuetypeAddrInIReg:
+               case ArgValuetypeAddrOnStack:
                case ArgGSharedVtInReg:
                case ArgGSharedVtOnStack: {
                        if (ainfo->storage == ArgOnStack && !MONO_TYPE_ISSTRUCT (t) && !call->tail_call)
@@ -2299,8 +2317,12 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
                }
                break;
        }
-       case ArgValuetypeAddrInIReg: {
+       case ArgValuetypeAddrInIReg:
+       case ArgValuetypeAddrOnStack: {
                MonoInst *vtaddr, *load;
+
+               g_assert (ainfo->storage == ArgValuetypeAddrInIReg || (ainfo->storage == ArgValuetypeAddrOnStack && ainfo->pair_storage [0] == ArgNone));
+               
                vtaddr = mono_compile_create_var (cfg, &ins->klass->byval_arg, OP_LOCAL);
                
                MONO_INST_NEW (cfg, load, OP_LDADDR);
@@ -6983,6 +7005,8 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                if (ainfo->pair_storage [0] == ArgInIReg)
                                        amd64_mov_membase_reg (code, ins->inst_left->inst_basereg, ins->inst_left->inst_offset, ainfo->pair_regs [0],  sizeof (gpointer));
                                break;
+                       case ArgValuetypeAddrOnStack:
+                               break;
                        case ArgGSharedVtInReg:
                                amd64_mov_membase_reg (code, ins->inst_basereg, ins->inst_offset, ainfo->reg, 8);
                                break;
index fb65862bbc19b15285cb7a10c20392c86a287b9a..51b8225e51ee87572d64a47c42429719ca649538 100644 (file)
@@ -188,12 +188,20 @@ typedef struct MonoCompileArch {
 } MonoCompileArch;
 
 #ifdef TARGET_WIN32
-#define PARAM_REGS 4
-#define FLOAT_PARAM_REGS 4
 
 static AMD64_Reg_No param_regs [] = { AMD64_RCX, AMD64_RDX, AMD64_R8, AMD64_R9 };
 
-static AMD64_Reg_No return_regs [] = { AMD64_RAX, AMD64_RDX };
+static AMD64_Reg_No float_param_regs [] = { AMD64_XMM0, AMD64_XMM1, AMD64_XMM2, AMD64_XMM3 };
+
+static AMD64_Reg_No return_regs [] = { AMD64_RAX };
+
+static AMD64_Reg_No float_return_regs [] = { AMD64_XMM0 };
+
+#define PARAM_REGS G_N_ELEMENTS(param_regs)
+#define FLOAT_PARAM_REGS G_N_ELEMENTS(float_param_regs)
+#define RETURN_REGS G_N_ELEMENTS(return_regs)
+#define FLOAT_RETURN_REGS G_N_ELEMENTS(float_return_regs)
+
 #else
 #define PARAM_REGS 6
 #define FLOAT_PARAM_REGS 8
@@ -248,6 +256,7 @@ typedef enum {
        ArgOnStack,
        ArgValuetypeInReg,
        ArgValuetypeAddrInIReg,
+       ArgValuetypeAddrOnStack,
        /* gsharedvt argument passed by addr */
        ArgGSharedVtInReg,
        ArgGSharedVtOnStack,