Merge pull request #3416 from ludovic-henry/iolayer-extract-wait
[mono.git] / mono / mini / mini-amd64.c
index e31aada27c3978c4c0ea314a813ecba46e10e223..258b265e063cc4f986c7e5b5a7c9bfa47a1e82af 100644 (file)
@@ -8,6 +8,7 @@
  *   Dietmar Maurer (dietmar@ximian.com)
  *   Patrik Torstensson
  *   Zoltan Varga (vargaz@gmail.com)
+ *   Johan Lorensson (lateralusx.github@gmail.com)
  *
  * (C) 2003 Ximian, Inc.
  * Copyright 2003-2011 Novell, Inc (http://www.novell.com)
@@ -475,7 +476,7 @@ allocate_parameter_register_for_valuetype_win64 (ArgInfo *arg_info, ArgumentClas
 }
 
 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)
+allocate_return_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, return_regs, RETURN_REGS, float_return_regs, FLOAT_RETURN_REGS, current_int_reg, current_float_reg);
 }
@@ -509,13 +510,11 @@ allocate_storage_for_valuetype_win64 (ArgInfo *arg_info, MonoType *type, gboolea
 
                /* Parameter cases. */
                if (arg_class != ARG_CLASS_MEMORY && MONO_WIN64_VALUE_TYPE_FITS_REG (arg_size)) {
-
                        assert (arg_size == 1 || arg_size == 2 || arg_size == 4 || arg_size == 8);
 
                        /* 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);
                                
@@ -527,11 +526,9 @@ allocate_storage_for_valuetype_win64 (ArgInfo *arg_info, MonoType *type, gboolea
                                *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);
                                                                
@@ -544,26 +541,22 @@ allocate_storage_for_valuetype_win64 (ArgInfo *arg_info, MonoType *type, gboolea
                        }
                }
        } else {
-
                /* Return value cases. */
                if (arg_class != ARG_CLASS_MEMORY && MONO_WIN64_VALUE_TYPE_FITS_REG (arg_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);
+                       allocate_return_register_for_valuetype_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. */
+                       /* Only RAX/XMM0 should be used to return valuetype. */
                        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);
+                       allocate_return_register_for_valuetype_win64 (arg_info, ARG_CLASS_INTEGER, arg_size, current_int_reg, current_float_reg);
 
-                       /* Only RAX should be used to return valyetype address. */
+                       /* Only RAX should be used to return valuetype address. */
                        assert (arg_info->pair_regs[0] == AMD64_RAX && arg_info->pair_regs[1] == ArgNone);
 
                        arg_size = ALIGN_TO (arg_size, sizeof (mgreg_t));
@@ -581,7 +574,7 @@ get_valuetype_size_win64 (MonoClass *klass, gboolean pinvoke, ArgInfo *arg_info,
 
        assert (klass != NULL && arg_info != NULL && type != NULL && arg_class != NULL && arg_size != NULL);
        
-       if (pinvoke == TRUE) {
+       if (pinvoke) {
                /* Calculate argument class type and size of marshalled type. */
                MonoMarshalType *info = mono_marshal_load_type_info (klass);
                *arg_size = info->native_size;
@@ -589,15 +582,15 @@ get_valuetype_size_win64 (MonoClass *klass, gboolean pinvoke, ArgInfo *arg_info,
                /* 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)) {
+
+       /* Windows ABI only handle value types on stack or passed in integer register (if it fits register size). */
+       *arg_class = MONO_WIN64_VALUE_TYPE_FITS_REG (*arg_size) ? ARG_CLASS_INTEGER : ARG_CLASS_MEMORY;
+
+       if (*arg_class == ARG_CLASS_MEMORY) {
                /* 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);
        }
 
-       /* 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;
-
        /*
        * 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.
@@ -631,9 +624,7 @@ add_valuetype_win64 (MonoMethodSignature *signature, ArgInfo *arg_info, MonoType
        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
-       {
+       } 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);
        }
@@ -1198,6 +1189,8 @@ mono_arch_init (void)
        mono_aot_register_jit_icall ("mono_amd64_throw_corlib_exception", mono_amd64_throw_corlib_exception);
        mono_aot_register_jit_icall ("mono_amd64_resume_unwind", mono_amd64_resume_unwind);
        mono_aot_register_jit_icall ("mono_amd64_get_original_ip", mono_amd64_get_original_ip);
+       mono_aot_register_jit_icall ("mono_amd64_handler_block_trampoline_helper", mono_amd64_handler_block_trampoline_helper);
+
 #if defined(MONO_ARCH_GSHAREDVT_SUPPORTED)
        mono_aot_register_jit_icall ("mono_amd64_start_gsharedvt_call", mono_amd64_start_gsharedvt_call);
 #endif
@@ -1237,6 +1230,11 @@ mono_arch_cpu_optimizations (guint32 *exclude_mask)
        }
 
 #ifdef TARGET_WIN32
+       /* The current SIMD doesn't support the argument used by a LD_ADDR to be of type OP_VTARG_ADDR. */
+       /* This will now be used for value types > 8 or of size 3,5,6,7 as dictated by windows x64 value type ABI. */
+       /* Since OP_VTARG_ADDR needs to be resolved in mono_spill_global_vars and the SIMD implementation optimize */
+       /* away the LD_ADDR in load_simd_vreg, that will cause an error in mono_spill_global_vars since incorrect opcode */
+       /* will now have a reference to an argument that won't be fully decomposed. */
        *exclude_mask |= MONO_OPT_SIMD;
 #endif
 
@@ -3741,6 +3739,30 @@ emit_setup_lmf (MonoCompile *cfg, guint8 *code, gint32 lmf_offset, int cfa_offse
        return code;
 }
 
+#ifdef TARGET_WIN32
+
+#define TEB_LAST_ERROR_OFFSET 0x068
+
+static guint8*
+emit_get_last_error (guint8* code, int dreg)
+{
+       /* Threads last error value is located in TEB_LAST_ERROR_OFFSET. */
+       x86_prefix (code, X86_GS_PREFIX);
+       amd64_mov_reg_membase (code, dreg, TEB_LAST_ERROR_OFFSET, 0, sizeof (guint32));
+
+       return code;
+}
+
+#else
+
+static guint8*
+emit_get_last_error (guint8* code, int dreg)
+{
+       g_assert_not_reached ();
+}
+
+#endif
+
 /* benchmark and set based on cpu */
 #define LOOP_ALIGNMENT 8
 #define bb_is_loop_start(bb) ((bb)->loop_body_start && (bb)->nesting)
@@ -6577,6 +6599,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ins->backend.pc_offset = code - cfg->native_code;
                        bb->spill_slot_defs = g_slist_prepend_mempool (cfg->mempool, bb->spill_slot_defs, ins);
                        break;
+               case OP_GET_LAST_ERROR:
+                       emit_get_last_error(code, ins->dreg);
+                       break;
                default:
                        g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
                        g_assert_not_reached ();
@@ -7811,10 +7836,7 @@ get_delegate_virtual_invoke_impl (MonoTrampInfo **info, gboolean load_imt_reg, i
        amd64_jump_membase (code, AMD64_RAX, offset);
        mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE, NULL);
 
-       if (load_imt_reg)
-               tramp_name = g_strdup_printf ("delegate_virtual_invoke_imt_%d", - offset / sizeof (gpointer));
-       else
-               tramp_name = g_strdup_printf ("delegate_virtual_invoke_%d", offset / sizeof (gpointer));
+       tramp_name = mono_get_delegate_virtual_invoke_impl_name (load_imt_reg, offset);
        *info = mono_tramp_info_create (tramp_name, start, code - start, NULL, unwind_ops);
        g_free (tramp_name);
 
@@ -7842,12 +7864,16 @@ mono_arch_get_delegate_invoke_impls (void)
                res = g_slist_prepend (res, info);
        }
 
-       for (i = 0; i <= MAX_VIRTUAL_DELEGATE_OFFSET; ++i) {
+       for (i = 1; i <= MONO_IMT_SIZE; ++i) {
                get_delegate_virtual_invoke_impl (&info, TRUE, - i * SIZEOF_VOID_P);
                res = g_slist_prepend (res, info);
+       }
 
+       for (i = 0; i <= MAX_VIRTUAL_DELEGATE_OFFSET; ++i) {
                get_delegate_virtual_invoke_impl (&info, FALSE, i * SIZEOF_VOID_P);
                res = g_slist_prepend (res, info);
+               get_delegate_virtual_invoke_impl (&info, TRUE, i * SIZEOF_VOID_P);
+               res = g_slist_prepend (res, info);
        }
 
        return res;