This is implemented by generating two wrappers for each concrete signature found in the assembly:
* an 'in' wrapper translates from the normal to the gsharedvt signature
* an 'out' wrapper translates from the gsharedvt to the normal signature
The wrappers take an extra argument, which points to a function descriptor, which is an <addr, arg> pair. They
make a call to ADDR which ARG as an extra argument. ADDR/ARG is either a gsharedvt method with an rgctx
argument, or another gsharedvt wrapper with a function descriptor argument.
All calls made out of gsharedvt methods with a variable signature use the gsharedvt calling convention. If the
callee is also gsharedvt, no wrapper is needed. If the callee is not gsharedvt, the call goes thought
a gsharedvt out wrapper.
Calls made from normal methods to gsharedvt methods go though a gsharedvt in wrapper.
* mini-generic-sharing.c (mini_get_gsharedvt_in_sig_wrapper): New function to generate gsharedvt in
wrappers as IL code.
(mini_get_gsharedvt_out_sig_wrapper): Ditto for out wrappers.
* method-to-ir.c (emit_llvmonly_calli): New helper function to make a call to a function descriptor.
(mono_method_to_ir): Make some indirect calls to MONO_RGCTX_INFO entries using function descriptors.
* aot-compiler.c (compile_method): Generate gsharedvt wrappers for the signatures used by the method.
* aot-runtime.c (decode_method_ref_with_target): Decode gsharedvt wrappers.
* jit-icalls.c (resolve_vcall): Rewrite this so it can handle gsharedvt as well.
(resolve_iface_call): Ditto.
(mono_init_delegate): Ditto.
(mono_init_delegate_virtual): Ditto.
* mini-trampolines.c (mini_create_llvmonly_ftndesc): New helper function to create a function descriptor.
(mini_add_method_wrappers_llvmonly): New helper function to add gsharedvt in/out wrappers, similar
to mini_add_method_trampolines ().
* mini-generic-sharing.c (mini_get_gsharedvt_wrapper): Return the newly added wrappers in llvmonly mode.
(class_type_info): Insert gsharedvt wrappers if needed. Some MONO_RGCTX_INFO types will now resolve to
function descriptors instead of function addresses.
(instantiate_info): Ditto.
* before calling the runtime invoke wrapper. In this case, the wrapper ignores
* its METHOD argument.
* If PASS_RGCTX is TRUE, the signature of the called method is changed to include a 'gpointer rgctx' as the
- * first argument (after 'this').
+ * last argument (after 'this').
*/
MonoMethod *
mono_marshal_get_runtime_invoke (MonoMethod *method, gboolean virtual_, gboolean pass_rgctx)
WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER,
/* Subtypes of MONO_WRAPPER_DELEGATE_INVOKE */
WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL,
- WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND
+ WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND,
+ /* Subtypes of MONO_WRAPPER_UNKNOWN */
+ WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG,
+ WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG,
} WrapperSubtype;
typedef struct {
MonoMethod *method;
} RemotingWrapperInfo;
+typedef struct {
+ MonoMethodSignature *sig;
+} GsharedvtWrapperInfo;
+
/*
* This structure contains additional information to uniquely identify a given wrapper
* method. It can be retrieved by mono_marshal_get_wrapper_info () for certain types
UnboxWrapperInfo unbox;
/* MONO_WRAPPER_REMOTING_INVOKE/MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK/MONO_WRAPPER_XDOMAIN_INVOKE */
RemotingWrapperInfo remoting;
+ /* GSHAREDVT_IN_SIG/GSHAREDVT_OUT_SIG */
+ GsharedvtWrapperInfo gsharedvt;
} d;
} WrapperInfo;
GHashTable *klass_blob_hash;
/* Maps MonoMethod* -> blob offset */
GHashTable *method_blob_hash;
+ GHashTable *signatures;
guint32 *plt_got_info_offsets;
guint32 got_offset, llvm_got_offset, plt_offset, plt_got_offset_base, nshared_got_entries;
/* Number of GOT entries reserved for trampolines */
encode_method_ref (acfg, info->d.synchronized_inner.method, p, &p);
else if (info->subtype == WRAPPER_SUBTYPE_ARRAY_ACCESSOR)
encode_method_ref (acfg, info->d.array_accessor.method, p, &p);
+ else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG)
+ encode_signature (acfg, info->d.gsharedvt.sig, p, &p);
+ else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG)
+ encode_signature (acfg, info->d.gsharedvt.sig, p, &p);
break;
}
case MONO_WRAPPER_MANAGED_TO_NATIVE: {
if (!cfg->has_got_slots)
InterlockedIncrement (&acfg->stats.methods_without_got_slots);
+ if (!cfg->method->wrapper_type)
+ cfg->signatures = g_slist_prepend_mempool (cfg->mempool, cfg->signatures, mono_method_signature (cfg->method));
+
+ /* Add gsharedvt wrappers for signatures used by the method */
+ if (acfg->aot_opts.llvm_only && (acfg->opts & MONO_OPT_GSHAREDVT)) {
+ GSList *l;
+
+ for (l = cfg->signatures; l; l = l->next) {
+ MonoMethodSignature *sig = mono_metadata_signature_dup ((MonoMethodSignature*)l->data);
+
+ if (!g_hash_table_lookup (acfg->signatures, sig) && !mini_is_gsharedvt_variable_signature (sig)) {
+ g_hash_table_insert (acfg->signatures, sig, sig);
+ if (!sig->has_type_parameters) {
+ MonoMethod *wrapper;
+
+ //printf ("%s\n", mono_signature_full_name (sig));
+
+ wrapper = mini_get_gsharedvt_in_sig_wrapper (sig);
+ add_extra_method (acfg, wrapper);
+ wrapper = mini_get_gsharedvt_out_sig_wrapper (sig);
+ add_extra_method (acfg, wrapper);
+ } else {
+ /* For signatures creared during generic sharing, convert them to a concrete signature if possible */
+ MonoMethodSignature *copy = mono_metadata_signature_dup (sig);
+ int i;
+ gboolean concrete = TRUE;
+
+ //printf ("%s\n", mono_signature_full_name (sig));
+
+ copy->ret = mini_get_underlying_type (sig->ret);
+ // FIXME: Add more cases
+ if (copy->ret->type == MONO_TYPE_VAR || copy->ret->type == MONO_TYPE_MVAR || copy->ret->type == MONO_TYPE_GENERICINST || copy->ret->type == MONO_TYPE_SZARRAY)
+ concrete = FALSE;
+ for (i = 0; i < sig->param_count; ++i) {
+ copy->params [i] = mini_get_underlying_type (sig->params [i]);
+ if (copy->params [i]->type == MONO_TYPE_VAR || copy->params [i]->type == MONO_TYPE_MVAR || copy->params [i]->type == MONO_TYPE_GENERICINST || copy->params [i]->type == MONO_TYPE_SZARRAY)
+ concrete = FALSE;
+ }
+ if (concrete) {
+ copy->has_type_parameters = 0;
+ sig = copy;
+ if (!g_hash_table_lookup (acfg->signatures, sig)) {
+ g_hash_table_insert (acfg->signatures, sig, sig);
+ MonoMethod *wrapper = mini_get_gsharedvt_in_sig_wrapper (sig);
+ add_extra_method (acfg, wrapper);
+
+ //printf ("%s\n", mono_method_full_name (wrapper, 1));
+ }
+ }
+ }
+ }
+ }
+ }
+
/*
* FIXME: Instead of this mess, allocate the patches from the aot mempool.
*/
}
}
+ /* gsharedvt in wrappers */
+ /* Generate a wrapper for each generic signature used in the module */
+ if (acfg->aot_opts.llvm_only && (acfg->opts & MONO_OPT_GSHAREDVT)) {
+ // FIXME: Is this needed ?
+ for (mindex = 0; mindex < image->tables [MONO_TABLE_METHODSPEC].rows; ++mindex) {
+ MonoMethod *method, *wrapper;
+ MonoMethodSignature *sig;
+ guint32 token = MONO_TOKEN_METHOD_SPEC | (mindex + 1);
+
+ method = mono_get_method (acfg->image, token, NULL);
+ // FIXME:
+ g_assert (method);
+
+ sig = mono_method_signature (method);
+ if (!sig->has_type_parameters) {
+ wrapper = mini_get_gsharedvt_in_sig_wrapper (sig);
+ add_extra_method (acfg, wrapper);
+ wrapper = mini_get_gsharedvt_out_sig_wrapper (sig);
+ add_extra_method (acfg, wrapper);
+ }
+ }
+ }
+
add_generic_instances (acfg);
if (mono_aot_mode_is_full (&acfg->aot_opts))
acfg->klass_blob_hash = g_hash_table_new (NULL, NULL);
acfg->method_blob_hash = g_hash_table_new (NULL, NULL);
acfg->plt_entry_debug_sym_cache = g_hash_table_new (g_str_hash, g_str_equal);
+ acfg->signatures = g_hash_table_new ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal);
mono_os_mutex_init_recursive (&acfg->mutex);
init_got_info (&acfg->got_info);
{
MonoMethodSignature *sig;
guint32 flags;
- int i, param_count, call_conv;
+ int i, gen_param_count = 0, param_count, call_conv;
guint8 *p = buf;
gboolean hasthis, explicit_this, has_gen_params;
call_conv = flags & 0x0F;
if (has_gen_params)
- /* gen_param_count = */ decode_value (p, &p);
+ gen_param_count = decode_value (p, &p);
param_count = decode_value (p, &p);
if (target && param_count != target->param_count)
return NULL;
sig->hasthis = hasthis;
sig->explicit_this = explicit_this;
sig->call_convention = call_conv;
- sig->param_count = param_count;
+ sig->generic_param_count = gen_param_count;
sig->ret = decode_type (module, p, &p);
for (i = 0; i < param_count; ++i) {
if (*p == MONO_TYPE_SENTINEL) {
ref->method = mono_marshal_get_gsharedvt_in_wrapper ();
} else if (subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT) {
ref->method = mono_marshal_get_gsharedvt_out_wrapper ();
+ } else if (subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG) {
+ MonoMethodSignature *sig = decode_signature (module, p, &p);
+ if (!sig)
+ return FALSE;
+ ref->method = mini_get_gsharedvt_in_sig_wrapper (sig);
+ } else if (subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG) {
+ MonoMethodSignature *sig = decode_signature (module, p, &p);
+ if (!sig)
+ return FALSE;
+ ref->method = mini_get_gsharedvt_out_sig_wrapper (sig);
} else {
g_assert_not_reached ();
}
}
/*
- * mono_resolve_iface_call:
+ * resolve_iface_call:
*
* Return the executable code for the iface method IMT_METHOD called on THIS.
*/
-gpointer
-mono_resolve_iface_call (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_rgctx_arg)
+static gpointer
+resolve_iface_call (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg, gboolean caller_gsharedvt)
{
MonoVTable *vt;
gpointer *imt, *vtable_slot;
addr = compiled_method = mono_compile_method (impl_method);
g_assert (addr);
- if (imt_method->is_inflated && ((MonoMethodInflated*)imt_method)->context.method_inst) {
+ if (imt_method->is_inflated && ((MonoMethodInflated*)imt_method)->context.method_inst)
generic_virtual = imt_method;
- need_rgctx_tramp = TRUE;
- }
if (generic_virtual || variant_iface) {
if (vt->klass->valuetype) /*FIXME is this required variant iface?*/
need_unbox_tramp = TRUE;
}
- addr = mini_add_method_trampoline (impl_method, addr, need_rgctx_tramp, need_unbox_tramp);
+ addr = mini_add_method_wrappers_llvmonly (impl_method, addr, caller_gsharedvt, need_unbox_tramp, out_arg);
if (generic_virtual || variant_iface) {
MonoMethod *target = generic_virtual ? generic_virtual : variant_iface;
*vtable_slot = addr;
- if (need_rgctx_tramp && out_rgctx_arg) {
- MonoMethod *m = impl_method;
- MonoJitInfo *ji;
+ return addr;
+}
- /*
- * The exact compiled method might not be shared so it doesn't have an rgctx arg.
- */
- ji = mini_jit_info_table_find (mono_domain_get (), (char *)compiled_method, NULL);
- if (!ji || (ji && ji->has_generic_jit_info)) {
- if (m->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED && m->klass->rank && strstr (m->name, "System.Collections.Generic"))
- m = mono_aot_get_array_helper_from_wrapper (m);
- *out_rgctx_arg = mini_method_get_rgctx (m);
- }
- }
+gpointer
+mono_resolve_iface_call (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg)
+{
+ return resolve_iface_call (this_obj, imt_slot, imt_method, out_arg, FALSE);
+}
- return addr;
+gpointer
+mono_resolve_iface_call_gsharedvt (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg)
+{
+ return resolve_iface_call (this_obj, imt_slot, imt_method, out_arg, TRUE);
}
static gboolean
*
* Return the executable code for calling this_obj->vtable [slot].
*/
-gpointer
-mono_resolve_vcall (MonoObject *this_obj, int slot, MonoMethod *imt_method)
+static gpointer
+resolve_vcall (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_arg, gboolean gsharedvt)
{
MonoVTable *vt;
MonoMethod *m, *generic_virtual = NULL;
gpointer *vtable_slot;
- gpointer addr;
- gboolean need_rgctx_tramp = FALSE, need_unbox_tramp = FALSE;
+ gpointer addr, compiled_method;
+ gboolean need_unbox_tramp = FALSE;
// FIXME: Optimize this
m = mono_class_inflate_generic_method_checked (declaring, &context, &error);
g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
- /* FIXME: only do this if the method is sharable */
- // FIXME:
- //g_assert_not_reached ();
- //need_rgctx_tramp = TRUE;
}
if (generic_virtual) {
- if (vt->klass->valuetype) /*FIXME is this required variant iface?*/
+ if (vt->klass->valuetype)
need_unbox_tramp = TRUE;
} else {
if (m->klass->valuetype)
}
// FIXME: This can throw exceptions
- addr = mono_compile_method (m);
+ addr = compiled_method = mono_compile_method (m);
g_assert (addr);
- addr = mini_add_method_trampoline (m, addr, need_rgctx_tramp, need_unbox_tramp);
+ addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, need_unbox_tramp, out_arg);
+
+ // FIXME: Unify this with mono_resolve_iface_call
*vtable_slot = addr;
+ if (gsharedvt) {
+ /*
+ * The callee uses the gsharedvt calling convention, have to add an out wrapper.
+ */
+ g_assert (out_arg);
+ g_assert (*out_arg);
+
+ gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, mono_method_signature (imt_method), NULL, -1, FALSE);
+ gpointer *out_wrapper_arg = mini_create_llvmonly_ftndesc (addr, *out_arg);
+
+ addr = out_wrapper;
+ *out_arg = out_wrapper_arg;
+ }
+
return addr;
}
+gpointer
+mono_resolve_vcall (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_rgctx_arg)
+{
+ return resolve_vcall (this_obj, slot, imt_method, out_rgctx_arg, FALSE);
+}
+
+gpointer
+mono_resolve_vcall_gsharedvt (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_rgctx_arg)
+{
+ return resolve_vcall (this_obj, slot, imt_method, out_rgctx_arg, TRUE);
+}
+
/*
* mono_init_delegate:
*
MONO_OBJECT_SETREF (del, target, target);
del->method = method;
del->method_ptr = mono_compile_method (method);
- if (mono_method_needs_static_rgctx_invoke (method, FALSE))
- del->rgctx = mini_method_get_rgctx (method);
+
+ del->method_ptr = mini_add_method_wrappers_llvmonly (method, del->method_ptr, FALSE, FALSE, &del->rgctx);
+ if (!del->rgctx) {
+ if (mono_method_needs_static_rgctx_invoke (method, FALSE))
+ del->rgctx = mini_method_get_rgctx (method);
+ }
}
void
MONO_OBJECT_SETREF (del, target, target);
del->method = method;
del->method_ptr = mono_compile_method (method);
- if (mono_method_needs_static_rgctx_invoke (method, FALSE))
- del->rgctx = mini_method_get_rgctx (method);
+
+ del->method_ptr = mini_add_method_wrappers_llvmonly (method, del->method_ptr, FALSE, FALSE, &del->rgctx);
+ if (!del->rgctx) {
+ if (mono_method_needs_static_rgctx_invoke (method, FALSE))
+ del->rgctx = mini_method_get_rgctx (method);
+ }
}
MonoObject*
gpointer mono_fill_method_rgctx (MonoMethodRuntimeGenericContext *mrgctx, int index);
-gpointer mono_resolve_iface_call (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_rgctx_arg);
+gpointer mono_resolve_iface_call (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg);
-gpointer mono_resolve_vcall (MonoObject *this_obj, int slot, MonoMethod *imt_method);
+gpointer mono_resolve_vcall (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_arg);
+
+gpointer mono_resolve_iface_call_gsharedvt (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg);
+
+gpointer mono_resolve_vcall_gsharedvt (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg);
void mono_init_delegate (MonoDelegate *del, MonoObject *target, MonoMethod *method);
// FIXME: Vcall optimizations below
MonoInst *icall_args [16];
MonoInst *ins;
+ int rgctx_reg;
if (sig->generic_param_count) {
/*
EMIT_NEW_PCONST (cfg, ins, NULL);
icall_args [2] = ins;
}
+ rgctx_reg = alloc_preg (cfg);
+ MONO_EMIT_NEW_PCONST (cfg, rgctx_reg, NULL);
+ EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], rgctx_reg, &mono_defaults.int_class->byval_arg);
call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall, icall_args);
}
return ins;
}
+static MonoMethodSignature*
+sig_to_rgctx_sig (MonoMethodSignature *sig)
+{
+ // FIXME: memory allocation
+ MonoMethodSignature *res;
+ int i;
+
+ res = (MonoMethodSignature *)g_malloc (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count + 1) * sizeof (MonoType*));
+ memcpy (res, sig, MONO_SIZEOF_METHOD_SIGNATURE);
+ res->param_count = sig->param_count + 1;
+ for (i = 0; i < sig->param_count; ++i)
+ res->params [i] = sig->params [i];
+ res->params [sig->param_count] = &mono_defaults.int_class->this_arg;
+ return res;
+}
+
+/* Make an indirect call to FSIG passing an additional argument */
+static MonoInst*
+emit_extra_arg_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **orig_args, int arg_reg, MonoInst *call_target)
+{
+ MonoMethodSignature *csig;
+ MonoInst *args_buf [16];
+ MonoInst **args;
+ int i, pindex, tmp_reg;
+
+ /* Make a call with an rgctx/extra arg */
+ if (fsig->param_count + 2 < 16)
+ args = args_buf;
+ else
+ args = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (fsig->param_count + 2));
+ pindex = 0;
+ if (fsig->hasthis)
+ args [pindex ++] = orig_args [0];
+ for (i = 0; i < fsig->param_count; ++i)
+ args [pindex ++] = orig_args [fsig->hasthis + i];
+ tmp_reg = alloc_preg (cfg);
+ EMIT_NEW_UNALU (cfg, args [pindex], OP_MOVE, tmp_reg, arg_reg);
+ csig = sig_to_rgctx_sig (fsig);
+ return mono_emit_calli (cfg, csig, args, call_target, NULL, NULL);
+}
+
+/* Emit an indirect call to the function descriptor ADDR */
+static MonoInst*
+emit_llvmonly_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, MonoInst *addr)
+{
+ int addr_reg, arg_reg;
+ MonoInst *call_target;
+
+ g_assert (cfg->llvm_only);
+
+ /*
+ * addr points to a <addr, arg> pair, load both of them, and
+ * make a call to addr, passing arg as an extra arg.
+ */
+ addr_reg = alloc_preg (cfg);
+ EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, addr->dreg, 0);
+ arg_reg = alloc_preg (cfg);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, addr->dreg, sizeof (gpointer));
+
+ return emit_extra_arg_calli (cfg, fsig, args, arg_reg, call_target);
+}
+
static gboolean
direct_icalls_enabled (MonoCompile *cfg)
{
RGCTX. */
addr = emit_get_rgctx_method (cfg, context_used, method,
MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
+ if (cfg->llvm_only && cfg->gsharedvt) {
+ return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr);
+ } else {
+ rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
- rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
-
- return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
+ return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
+ }
} else {
gboolean pass_vtable, pass_mrgctx;
MonoInst *rgctx_arg = NULL;
unbox_sig->ret = &klass->byval_arg;
unbox_sig->param_count = 1;
unbox_sig->params [0] = &mono_defaults.object_class->byval_arg;
- unbox_call = mono_emit_calli (cfg, unbox_sig, &obj, addr, NULL, NULL);
+
+ if (cfg->llvm_only)
+ unbox_call = emit_llvmonly_calli (cfg, unbox_sig, &obj, addr);
+ else
+ unbox_call = mono_emit_calli (cfg, unbox_sig, &obj, addr, NULL, NULL);
EMIT_NEW_VARLOADA_VREG (cfg, addr, unbox_call->dreg, &klass->byval_arg);
addr->dreg = addr_reg;
MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
if (context_used) {
- /* FIXME: What if the class is shared? We might not
- have to get the method address from the RGCTX. */
- MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
- MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
- MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
+ if (cfg->llvm_only && cfg->gsharedvt) {
+ MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
+ MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
+ return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr);
+ } else {
+ /* FIXME: What if the class is shared? We might not
+ have to get the method address from the RGCTX. */
+ MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
+ MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
+ MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
- return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
+ return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
+ }
} else {
gboolean pass_vtable, pass_mrgctx;
MonoInst *rgctx_arg = NULL;
box_sig->ret = &mono_defaults.object_class->byval_arg;
box_sig->param_count = 1;
box_sig->params [0] = &klass->byval_arg;
- box_call = mono_emit_calli (cfg, box_sig, &val, addr, NULL, NULL);
+
+ if (cfg->llvm_only)
+ box_call = emit_llvmonly_calli (cfg, box_sig, &val, addr);
+ else
+ box_call = mono_emit_calli (cfg, box_sig, &val, addr, NULL, NULL);
EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, box_call->dreg);
res->type = STACK_OBJ;
res->klass = klass;
MonoInst *addr;
addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
- mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
+
+ if (cfg->llvm_only) {
+ // FIXME: Avoid initializing vtable_arg
+ emit_llvmonly_calli (cfg, fsig, sp, addr);
+ } else {
+ mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
+ }
} else if (context_used &&
((!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
!mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
/* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
- cmethod_addr = emit_get_rgctx_method (cfg, context_used,
- cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
+ if (cfg->llvm_only) {
+ MonoInst *addr = emit_get_rgctx_method (cfg, context_used, cmethod,
+ MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
+ emit_llvmonly_calli (cfg, fsig, sp, addr);
+ } else {
+ cmethod_addr = emit_get_rgctx_method (cfg, context_used,
+ cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
- mono_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
+ mono_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
+ }
} else {
INLINE_FAILURE ("ctor call");
ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp,
}
}
-static MonoMethodSignature*
-sig_to_rgctx_sig (MonoMethodSignature *sig)
-{
- // FIXME: memory allocation
- MonoMethodSignature *res;
- int i;
-
- res = (MonoMethodSignature *)g_malloc (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count + 1) * sizeof (MonoType*));
- memcpy (res, sig, MONO_SIZEOF_METHOD_SIGNATURE);
- res->param_count = sig->param_count + 1;
- for (i = 0; i < sig->param_count; ++i)
- res->params [i] = sig->params [i];
- res->params [sig->param_count] = &mono_defaults.int_class->byval_arg;
- return res;
-}
-
/*
* mono_method_to_ir:
*
/* Not tested */
GSHAREDVT_FAILURE (*ip);
+ if (cfg->llvm_only)
+ // FIXME:
+ GSHAREDVT_FAILURE (*ip);
+
addr = emit_get_rgctx_sig (cfg, context_used,
fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee);
CHECK_CFG_ERROR;
}
+ if (cfg->llvm_only && !cfg->method->wrapper_type)
+ cfg->signatures = g_slist_prepend_mempool (cfg->mempool, cfg->signatures, fsig);
+
/* See code below */
if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
MonoBasicBlock *tbb;
* patching gshared method addresses into a gsharedvt method.
*/
if (cfg->gsharedvt && (mini_is_gsharedvt_signature (fsig) || cmethod->is_inflated || cmethod->klass->generic_class) &&
- !(cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY)) {
+ !(cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY) &&
+ (!(cfg->llvm_only && virtual_))) {
MonoRgctxInfoType info_type;
if (virtual_) {
info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE;
addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, info_type);
- ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
+ if (cfg->llvm_only) {
+ // FIXME: Avoid initializing vtable_arg
+ ins = emit_llvmonly_calli (cfg, fsig, sp, addr);
+ } else {
+ ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
+ }
goto call_end;
}
MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
- ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
+ if (cfg->llvm_only) {
+ // FIXME: Avoid initializing imt_arg/vtable_arg
+ ins = emit_llvmonly_calli (cfg, fsig, sp, addr);
+ } else {
+ ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
+ }
goto call_end;
}
}
/*
- * Interface calls in llvm-only mode are complicated becase the callee might need an rgctx arg,
- * (i.e. its a vtype method), and there is no way to for the caller to know this at compile time.
- * So we make resolve_iface_call return the rgctx, and do two calls with different signatures
- * based on whenever there is an rgctx or not.
+ * Virtual calls in llvm-only mode.
*/
- if (cfg->llvm_only && virtual_ && cmethod && (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
- MonoInst *args_buf [16], *icall_args [16];
- MonoInst **args;
- MonoBasicBlock *rgctx_bb, *end_bb;
- MonoInst *call1, *call2, *call_target;
- MonoMethodSignature *rgctx_sig;
- int rgctx_reg, tmp_reg;
+ if (cfg->llvm_only && virtual_ && cmethod && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
+ MonoInst *icall_args [16];
+ MonoInst *call_target;
+ int arg_reg;
+ gboolean is_iface = cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE;
MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
- NEW_BBLOCK (cfg, rgctx_bb);
- NEW_BBLOCK (cfg, end_bb);
-
// FIXME: Optimize this
- guint32 imt_slot = mono_method_get_imt_slot (cmethod);
+ guint32 slot;
+
+ if (is_iface)
+ slot = mono_method_get_imt_slot (cmethod);
+ else
+ slot = mono_method_get_vtable_index (cmethod);
icall_args [0] = sp [0];
- EMIT_NEW_ICONST (cfg, icall_args [1], imt_slot);
- if (imt_arg) {
+ EMIT_NEW_ICONST (cfg, icall_args [1], slot);
+
+ if (fsig->generic_param_count) {
+ /* virtual generic call */
+ g_assert (!imt_arg);
+ /* Same as the virtual generic case above */
+ imt_arg = emit_get_rgctx_method (cfg, context_used,
+ cmethod, MONO_RGCTX_INFO_METHOD);
+ icall_args [2] = imt_arg;
+ } else if (imt_arg) {
icall_args [2] = imt_arg;
} else {
EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_METHODCONST, cmethod);
icall_args [2] = ins;
}
- rgctx_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_PCONST (cfg, rgctx_reg, NULL);
- EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], rgctx_reg, &mono_defaults.int_class->byval_arg);
- //EMIT_NEW_PCONST (cfg, icall_args [3], NULL);
-
- call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call, icall_args);
-
- // FIXME: Only do this if needed (generic calls)
-
- // Check whenever to pass an rgctx
- MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0);
- MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, rgctx_bb);
- /* Non rgctx case */
- call1 = mono_emit_calli (cfg, fsig, sp, call_target, NULL, vtable_arg);
- MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
- /* Rgctx case */
- MONO_START_BB (cfg, rgctx_bb);
- /* Make a call with an rgctx */
- if (fsig->param_count + 2 < 16)
- args = args_buf;
- else
- args = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (fsig->param_count + 2));
- args [0] = sp [0];
- for (i = 0; i < fsig->param_count; ++i)
- args [i + 1] = sp [i + 1];
- tmp_reg = alloc_preg (cfg);
- EMIT_NEW_UNALU (cfg, args [fsig->param_count + 1], OP_MOVE, tmp_reg, rgctx_reg);
- rgctx_sig = sig_to_rgctx_sig (fsig);
- call2 = mono_emit_calli (cfg, rgctx_sig, args, call_target, NULL, NULL);
- call2->dreg = call1->dreg;
- MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
- /* End */
- MONO_START_BB (cfg, end_bb);
- ins = call1;
+ // FIXME: For generic virtual calls, avoid computing the rgctx twice
+
+ arg_reg = alloc_preg (cfg);
+ MONO_EMIT_NEW_PCONST (cfg, arg_reg, NULL);
+ EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], arg_reg, &mono_defaults.int_class->byval_arg);
+
+ if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) {
+ /*
+ * We handle virtual calls made from gsharedvt methods here instead
+ * of the gsharedvt block above.
+ */
+ if (is_iface)
+ call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call_gsharedvt, icall_args);
+ else
+ call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall_gsharedvt, icall_args);
+ } else {
+ if (is_iface)
+ call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call, icall_args);
+ else
+ call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall, icall_args);
+ }
+
+ /*
+ * Pass the extra argument even if the callee doesn't receive it, most calling
+ * calling conventions allow this.
+ */
+ ins = emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
goto call_end;
}
}
/* FIXME: SGEN support */
- if (invoke_context_used == 0) {
+ if (invoke_context_used == 0 || cfg->llvm_only) {
ip += 6;
if (cfg->verbose_level > 3)
g_print ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
if (mono_arch_is_soft_float ())
break;
+ /*
+ if (var->type == STACK_VTYPE && cfg->gsharedvt && mini_is_gsharedvt_variable_type (var->inst_vtype))
+ break;
+ */
+
/* Arguments are implicitly global */
/* Putting R4 vars into registers doesn't work currently */
/* The gsharedvt vars are implicitly referenced by ldaddr opcodes, but those opcodes are only generated later */
#include <config.h>
#include <mono/metadata/class.h>
+#include <mono/metadata/method-builder.h>
#include <mono/utils/mono-counters.h>
#include "mini.h"
case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
MonoMethod *method;
- gpointer addr;
+ gpointer addr, arg;
MonoJitInfo *ji;
+ MonoMethodSignature *sig, *gsig;
+ MonoMethod *gmethod;
if (!mono_class_is_nullable (klass))
/* This can happen since all the entries in MonoGSharedVtMethodInfo are inflated, even those which are not used */
method = mono_class_get_method_from_name (klass, "Unbox", 1);
addr = mono_compile_method (method);
+
// The caller uses the gsharedvt call signature
+
+ if (mono_llvm_only) {
+ /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
+ gmethod = mini_get_shared_method_full (method, FALSE, TRUE);
+ sig = mono_method_signature (method);
+ gsig = mono_method_signature (gmethod);
+
+ addr = mini_add_method_wrappers_llvmonly (method, addr, TRUE, FALSE, &arg);
+ return mini_create_llvmonly_ftndesc (addr, arg);
+ }
+
ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (addr), NULL);
g_assert (ji);
if (mini_jit_info_is_gsharedvt (ji))
return mono_create_static_rgctx_trampoline (method, addr);
else {
- MonoMethodSignature *sig, *gsig;
- MonoMethod *gmethod;
-
/* Need to add an out wrapper */
/* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
tramp1->addr == tramp2->addr && tramp1->sig == tramp2->sig && tramp1->gsig == tramp2->gsig;
}
+static MonoType*
+get_wrapper_shared_type (MonoType *t)
+{
+ if (t->byref)
+ return &mono_defaults.int_class->this_arg;
+ t = mini_get_underlying_type (t);
+
+ // FIXME: Merge more types (objref/int etc).
+
+ switch (t->type) {
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_SZARRAY:
+ return &mono_defaults.object_class->byval_arg;
+ case MONO_TYPE_GENERICINST:
+ if (!MONO_TYPE_ISSTRUCT (t))
+ return &mono_defaults.object_class->byval_arg;
+ default:
+ break;
+ }
+
+ //printf ("%s\n", mono_type_full_name (t));
+
+ return t;
+}
+
+static MonoMethodSignature*
+mini_get_underlying_signature (MonoMethodSignature *sig)
+{
+ MonoMethodSignature *res = mono_metadata_signature_dup (sig);
+ int i;
+
+ res->ret = get_wrapper_shared_type (sig->ret);
+ for (i = 0; i < sig->param_count; ++i)
+ res->params [i] = get_wrapper_shared_type (sig->params [i]);
+
+ return res;
+}
+
+/*
+ * mini_get_gsharedvt_in_sig_wrapper:
+ *
+ * Return a wrapper to translate between the normal and gsharedvt calling conventions of SIG.
+ * The returned wrapper has a signature of SIG, plus one extra argument, which is an <addr, rgctx> pair.
+ * The extra argument is passed the same way as an rgctx to shared methods.
+ * It calls <addr> using the gsharedvt version of SIG, passing in <rgctx> as an extra argument.
+ */
+MonoMethod*
+mini_get_gsharedvt_in_sig_wrapper (MonoMethodSignature *sig)
+{
+ MonoMethodBuilder *mb;
+ MonoMethod *res;
+ WrapperInfo *info;
+ MonoMethodSignature *csig, *gsharedvt_sig;
+ int i, pindex, retval_var;
+ static GHashTable *cache;
+
+ // FIXME: Memory management
+ sig = mini_get_underlying_signature (sig);
+
+ // FIXME: Normal cache
+ if (!cache)
+ cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
+ // FIXME: Locking
+ res = g_hash_table_lookup (cache, sig);
+ if (res) {
+ g_free (sig);
+ return res;
+ }
+
+ /* Create the signature for the wrapper */
+ // FIXME:
+ csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 1) * sizeof (MonoType*)));
+ memcpy (csig, sig, mono_metadata_signature_size (sig));
+ csig->param_count ++;
+ csig->params [sig->param_count] = &mono_defaults.int_class->byval_arg;
+
+ /* Create the signature for the gsharedvt callconv */
+ gsharedvt_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
+ memcpy (gsharedvt_sig, sig, mono_metadata_signature_size (sig));
+ pindex = 0;
+ /* The return value is returned using an explicit vret argument */
+ if (sig->ret->type != MONO_TYPE_VOID) {
+ gsharedvt_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+ gsharedvt_sig->ret = &mono_defaults.void_class->byval_arg;
+ }
+ for (i = 0; i < sig->param_count; i++) {
+ gsharedvt_sig->params [pindex] = sig->params [i];
+ if (!sig->params [i]->byref) {
+ gsharedvt_sig->params [pindex] = mono_metadata_type_dup (NULL, gsharedvt_sig->params [pindex]);
+ gsharedvt_sig->params [pindex]->byref = 1;
+ }
+ pindex ++;
+ }
+ /* Rgctx arg */
+ gsharedvt_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+ gsharedvt_sig->param_count = pindex;
+
+ // FIXME: Use shared signatures
+ mb = mono_mb_new (mono_defaults.object_class, sig->hasthis ? "gsharedvt_in_sig" : "gsharedvt_in_sig_static", MONO_WRAPPER_UNKNOWN);
+
+ if (sig->ret->type != MONO_TYPE_VOID)
+ retval_var = mono_mb_add_local (mb, sig->ret);
+
+ /* Make the call */
+ if (sig->hasthis)
+ mono_mb_emit_ldarg (mb, 0);
+ if (sig->ret->type != MONO_TYPE_VOID)
+ mono_mb_emit_ldloc_addr (mb, retval_var);
+ for (i = 0; i < sig->param_count; i++) {
+ if (sig->params [i]->byref)
+ mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
+ else
+ mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE));
+ }
+ /* Rgctx arg */
+ mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
+ mono_mb_emit_icon (mb, sizeof (gpointer));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ /* Method to call */
+ mono_mb_emit_ldarg (mb, sig->param_count + (sig->hasthis ? 1 : 0));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_calli (mb, gsharedvt_sig);
+ if (sig->ret->type != MONO_TYPE_VOID)
+ mono_mb_emit_ldloc (mb, retval_var);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG);
+ info->d.gsharedvt.sig = sig;
+
+ res = mono_mb_create (mb, csig, sig->param_count + 16, info);
+
+ // FIXME: Locking
+ g_hash_table_insert (cache, sig, res);
+
+ return res;
+}
+
+/*
+ * mini_get_gsharedvt_out_sig_wrapper:
+ *
+ * Same as in_sig_wrapper, but translate between the gsharedvt and normal signatures.
+ */
+MonoMethod*
+mini_get_gsharedvt_out_sig_wrapper (MonoMethodSignature *sig)
+{
+ MonoMethodBuilder *mb;
+ MonoMethod *res;
+ WrapperInfo *info;
+ MonoMethodSignature *normal_sig, *csig;
+ int i, pindex, args_start, ldind_op, stind_op;
+ static GHashTable *cache;
+
+ // FIXME: Memory management
+ sig = mini_get_underlying_signature (sig);
+
+ // FIXME: Normal cache
+ if (!cache)
+ cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
+ // FIXME: Locking
+ res = g_hash_table_lookup (cache, sig);
+ if (res) {
+ g_free (sig);
+ return res;
+ }
+
+ /* Create the signature for the wrapper */
+ // FIXME:
+ csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
+ memcpy (csig, sig, mono_metadata_signature_size (sig));
+ pindex = 0;
+ /* The return value is returned using an explicit vret argument */
+ if (sig->ret->type != MONO_TYPE_VOID) {
+ csig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+ csig->ret = &mono_defaults.void_class->byval_arg;
+ }
+ args_start = pindex;
+ if (sig->hasthis)
+ args_start ++;
+ for (i = 0; i < sig->param_count; i++) {
+ csig->params [pindex] = sig->params [i];
+ if (!sig->params [i]->byref) {
+ csig->params [pindex] = mono_metadata_type_dup (NULL, csig->params [pindex]);
+ csig->params [pindex]->byref = 1;
+ }
+ pindex ++;
+ }
+ /* Rgctx arg */
+ csig->params [pindex ++] = &mono_defaults.int_class->byval_arg;
+ csig->param_count = pindex;
+
+ /* Create the signature for the normal callconv */
+ normal_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*)));
+ memcpy (normal_sig, sig, mono_metadata_signature_size (sig));
+ normal_sig->param_count ++;
+ normal_sig->params [sig->param_count] = &mono_defaults.int_class->byval_arg;
+
+ // FIXME: Use shared signatures
+ mb = mono_mb_new (mono_defaults.object_class, "gsharedvt_out_sig", MONO_WRAPPER_UNKNOWN);
+
+ if (sig->ret->type != MONO_TYPE_VOID)
+ /* Load return address */
+ mono_mb_emit_ldarg (mb, sig->hasthis ? 1 : 0);
+
+ /* Make the call */
+ if (sig->hasthis)
+ mono_mb_emit_ldarg (mb, 0);
+ for (i = 0; i < sig->param_count; i++) {
+ if (sig->params [i]->byref) {
+ mono_mb_emit_ldarg (mb, args_start + i);
+ } else {
+ ldind_op = mono_type_to_ldind (sig->params [i]);
+ mono_mb_emit_ldarg (mb, args_start + i);
+ // FIXME:
+ if (ldind_op == CEE_LDOBJ)
+ mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (sig->params [i]));
+ else
+ mono_mb_emit_byte (mb, ldind_op);
+ }
+ }
+ /* Rgctx arg */
+ mono_mb_emit_ldarg (mb, args_start + sig->param_count);
+ mono_mb_emit_icon (mb, sizeof (gpointer));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ /* Method to call */
+ mono_mb_emit_ldarg (mb, args_start + sig->param_count);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_calli (mb, normal_sig);
+ if (sig->ret->type != MONO_TYPE_VOID) {
+ /* Store return value */
+ stind_op = mono_type_to_stind (sig->ret);
+ // FIXME:
+ if (stind_op == CEE_STOBJ)
+ mono_mb_emit_op (mb, CEE_STOBJ, mono_class_from_mono_type (sig->ret));
+ else
+ mono_mb_emit_byte (mb, stind_op);
+ }
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG);
+ info->d.gsharedvt.sig = sig;
+
+ res = mono_mb_create (mb, csig, sig->param_count + 16, info);
+
+ // FIXME: Locking
+ g_hash_table_insert (cache, sig, res);
+
+ return res;
+}
+
/*
* mini_get_gsharedvt_wrapper:
*
inited = TRUE;
}
+ if (mono_llvm_only) {
+ MonoMethod *wrapper;
+
+ if (gsharedvt_in)
+ wrapper = mini_get_gsharedvt_in_sig_wrapper (normal_sig);
+ else
+ wrapper = mini_get_gsharedvt_out_sig_wrapper (normal_sig);
+ res = mono_compile_method (wrapper);
+ return res;
+ }
+
memset (&tinfo, 0, sizeof (tinfo));
tinfo.is_in = gsharedvt_in;
tinfo.calli = calli;
case MONO_RGCTX_INFO_METHOD:
return data;
case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: {
+ MonoMethod *m = (MonoMethod*)data;
gpointer addr;
+ gpointer arg = NULL;
+
+ if (mono_llvm_only) {
+ addr = mono_compile_method (m);
+ addr = mini_add_method_wrappers_llvmonly (m, addr, FALSE, FALSE, &arg);
- addr = mono_compile_method ((MonoMethod *)data);
- return mini_add_method_trampoline ((MonoMethod *)data, addr, mono_method_needs_static_rgctx_invoke ((MonoMethod *)data, FALSE), FALSE);
+ /* Returns an ftndesc */
+ return mini_create_llvmonly_ftndesc (addr, arg);
+ } else {
+ addr = mono_compile_method ((MonoMethod *)data);
+ return mini_add_method_trampoline ((MonoMethod *)data, addr, mono_method_needs_static_rgctx_invoke ((MonoMethod *)data, FALSE), FALSE);
+ }
}
case MONO_RGCTX_INFO_VIRT_METHOD_CODE: {
MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
sig = mono_method_signature (method);
gsig = call_sig;
- addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, vcall_offset, FALSE);
+ if (mono_llvm_only) {
+ if (mini_is_gsharedvt_variable_signature (call_sig)) {
+ /* The virtual case doesn't go through this code */
+ g_assert (!virtual_);
+
+ gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, FALSE);
+ gpointer *out_wrapper_arg = mini_create_llvmonly_ftndesc (callee_ji->code_start, mini_method_get_rgctx (method));
+
+ /* Returns an ftndesc */
+ addr = mini_create_llvmonly_ftndesc (out_wrapper, out_wrapper_arg);
+ } else {
+ addr = mini_create_llvmonly_ftndesc (addr, mini_method_get_rgctx (method));
+ }
+ } else {
+ addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, vcall_offset, FALSE);
+ }
#if 0
if (virtual)
printf ("OUT-VCALL: %s\n", mono_method_full_name (method, TRUE));
else
printf ("OUT: %s\n", mono_method_full_name (method, TRUE));
#endif
- // } else if (!mini_is_gsharedvt_variable_signature (mono_method_signature (caller_method)) && callee_gsharedvt) {
} else if (callee_gsharedvt) {
MonoMethodSignature *sig, *gsig;
* FIXME: Optimize this.
*/
- if (call_sig == mono_method_signature (method)) {
+ if (mono_llvm_only) {
+ /* Both wrappers receive an extra <addr, rgctx> argument */
+ sig = mono_method_signature (method);
+ gsig = mono_method_signature (jinfo_get_method (callee_ji));
+
+ /* Return a function descriptor */
+
+ if (mini_is_gsharedvt_variable_signature (call_sig)) {
+ /*
+ * This is not an optimization, but its needed, since the concrete signature 'sig'
+ * might not exist at all in IL, so the AOT compiler cannot generate the wrappers
+ * for it.
+ */
+ addr = mini_create_llvmonly_ftndesc (callee_ji->code_start, mini_method_get_rgctx (method));
+ } else if (mini_is_gsharedvt_variable_signature (gsig)) {
+ gpointer in_wrapper = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, -1, FALSE);
+
+ gpointer in_wrapper_arg = mini_create_llvmonly_ftndesc (callee_ji->code_start, mini_method_get_rgctx (method));
+
+ gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, -1, FALSE);
+ gpointer *out_wrapper_arg = mini_create_llvmonly_ftndesc (in_wrapper, in_wrapper_arg);
+
+ addr = mini_create_llvmonly_ftndesc (out_wrapper, out_wrapper_arg);
+ } else {
+ addr = mini_create_llvmonly_ftndesc (addr, mini_method_get_rgctx (method));
+ }
+ } else if (call_sig == mono_method_signature (method)) {
} else {
sig = mono_method_signature (method);
gsig = mono_method_signature (jinfo_get_method (callee_ji));
return &mono_defaults.byte_class->byval_arg;
case MONO_TYPE_CHAR:
return &mono_defaults.uint16_class->byval_arg;
+ case MONO_TYPE_STRING:
+ return &mono_defaults.object_class->byval_arg;
default:
return type;
}
mini_type_var_is_vt (MonoType *type)
{
if (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) {
- return type->data.generic_param->gshared_constraint && type->data.generic_param->gshared_constraint->type == MONO_TYPE_VALUETYPE;
+ return type->data.generic_param->gshared_constraint && (type->data.generic_param->gshared_constraint->type == MONO_TYPE_VALUETYPE || type->data.generic_param->gshared_constraint->type == MONO_TYPE_GENERICINST);
} else {
g_assert_not_reached ();
return FALSE;
register_icall (mono_aot_init_gshared_method_rgctx, "mono_aot_init_gshared_method_rgctx", "void ptr int ptr", TRUE);
register_icall_no_wrapper (mono_resolve_iface_call, "mono_resolve_iface_call", "ptr object int ptr ptr");
- register_icall_no_wrapper (mono_resolve_vcall, "mono_resolve_vcall", "ptr object int ptr");
+ register_icall_no_wrapper (mono_resolve_vcall, "mono_resolve_vcall", "ptr object int ptr ptr");
+ register_icall_no_wrapper (mono_resolve_iface_call_gsharedvt, "mono_resolve_iface_call_gsharedvt", "ptr object int ptr ptr");
+ register_icall_no_wrapper (mono_resolve_vcall_gsharedvt, "mono_resolve_vcall_gsharedvt", "ptr object int ptr ptr");
register_icall (mono_init_delegate, "mono_init_delegate", "void object object ptr", TRUE);
register_icall (mono_init_delegate_virtual, "mono_init_delegate_virtual", "void object object ptr", TRUE);
register_icall (mono_get_assembly_object, "mono_get_assembly_object", "object ptr", TRUE);
addr = compiled_method;
if (add_unbox_tramp) {
- /*
+ /*
* The unbox trampolines call the method directly, so need to add
* an rgctx tramp before them.
*/
addr = mini_get_gsharedvt_wrapper (TRUE, addr, sig, gsig, -1, FALSE);
+ if (mono_llvm_only)
+ g_assert_not_reached ();
//printf ("IN: %s\n", mono_method_full_name (m, TRUE));
}
return addr;
}
+/*
+ * mini_create_llvmonly_ftndesc:
+ *
+ * Create a function descriptor of the form <addr, arg>, which
+ * represents a callee ADDR with ARG as the last argument.
+ * This is used for:
+ * - generic sharing (ARG is the rgctx)
+ * - gsharedvt signature wrappers (ARG is a function descriptor)
+ */
+gpointer
+mini_create_llvmonly_ftndesc (gpointer addr, gpointer arg)
+{
+ gpointer *res;
+
+ // FIXME: Memory management
+ res = g_malloc0 (2 * sizeof (gpointer));
+ res [0] = addr;
+ res [1] = arg;
+
+ return res;
+}
+
+/**
+ * mini_add_method_wrappers_llvmonly:
+ *
+ * Add unbox/gsharedvt wrappers around COMPILED_METHOD if needed. Return the wrapper address or COMPILED_METHOD
+ * if no wrapper is needed. Set OUT_ARG to the rgctx/extra argument needed to be passed to the returned method.
+ */
+gpointer
+mini_add_method_wrappers_llvmonly (MonoMethod *m, gpointer compiled_method, gboolean caller_gsharedvt, gboolean add_unbox_tramp, gpointer *out_arg)
+{
+ gpointer addr = compiled_method;
+ gboolean callee_gsharedvt, callee_array_helper;
+ MonoMethod *jmethod = NULL;
+ MonoJitInfo *ji;
+
+ // FIXME: This loads information from AOT (perf problem)
+ ji = mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (compiled_method), NULL);
+ callee_gsharedvt = mini_jit_info_is_gsharedvt (ji);
+
+ callee_array_helper = FALSE;
+ if (m->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED) {
+ WrapperInfo *info = mono_marshal_get_wrapper_info (m);
+
+ /*
+ * generic array helpers.
+ * Have to replace the wrappers with the original generic instances.
+ */
+ if (info && info->subtype == WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER) {
+ callee_array_helper = TRUE;
+ m = info->d.generic_array_helper.method;
+ }
+ } else if (m->wrapper_type == MONO_WRAPPER_UNKNOWN) {
+ WrapperInfo *info = mono_marshal_get_wrapper_info (m);
+
+ /* Same for synchronized inner wrappers */
+ if (info && info->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER) {
+ m = info->d.synchronized_inner.method;
+ }
+ }
+
+ if (callee_gsharedvt)
+ g_assert (m->is_inflated);
+
+ addr = compiled_method;
+
+ if (add_unbox_tramp) {
+ /*
+ * The unbox trampolines call the method directly, so need to add
+ * an rgctx tramp before them.
+ */
+ if (mono_aot_only) {
+ addr = mono_aot_get_unbox_trampoline (m);
+ } else {
+ unbox_trampolines ++;
+ addr = mono_arch_get_unbox_trampoline (m, addr);
+ }
+ }
+
+ g_assert (mono_llvm_only);
+ g_assert (out_arg);
+
+ if (ji && !ji->is_trampoline)
+ jmethod = jinfo_get_method (ji);
+ if (callee_gsharedvt && mini_is_gsharedvt_variable_signature (mono_method_signature (jmethod))) {
+ MonoMethodSignature *sig, *gsig;
+
+ /* Here m is a generic instance, while ji->method is the gsharedvt method implementing it */
+
+ /* Call from normal/gshared code to gsharedvt code with variable signature */
+ sig = mono_method_signature (m);
+ gsig = mono_method_signature (jmethod);
+
+ addr = mini_get_gsharedvt_wrapper (TRUE, addr, sig, gsig, -1, FALSE);
+
+ /*
+ * This is a gsharedvt in wrapper, it gets passed a ftndesc for the gsharedvt method as an argument.
+ */
+ *out_arg = mini_create_llvmonly_ftndesc (compiled_method, mini_method_get_rgctx (m));
+ //printf ("IN: %s\n", mono_method_full_name (m, TRUE));
+ }
+
+ if (!(*out_arg) && mono_method_needs_static_rgctx_invoke (m, FALSE))
+ *out_arg = mini_method_get_rgctx (m);
+
+ if (caller_gsharedvt) {
+ /*
+ * The callee uses the gsharedvt calling convention, have to add an out wrapper.
+ */
+ g_assert (*out_arg);
+
+ gpointer out_wrapper = mini_get_gsharedvt_wrapper (FALSE, NULL, mono_method_signature (m), NULL, -1, FALSE);
+ gpointer *out_wrapper_arg = g_malloc0 (2 * sizeof (gpointer));
+ out_wrapper_arg [0] = addr;
+ out_wrapper_arg [1] = *out_arg;
+
+ addr = out_wrapper;
+ *out_arg = out_wrapper_arg;
+ }
+
+ return addr;
+}
+
/**
* common_call_trampoline:
*
MONO_RGCTX_INFO_TYPE,
MONO_RGCTX_INFO_REFLECTION_TYPE,
MONO_RGCTX_INFO_METHOD,
+ /* In llvmonly mode, this is a function descriptor */
MONO_RGCTX_INFO_GENERIC_METHOD_CODE,
MONO_RGCTX_INFO_CLASS_FIELD,
MONO_RGCTX_INFO_METHOD_RGCTX,
/* +1 to avoid zero values in rgctx slots */
MONO_RGCTX_INFO_FIELD_OFFSET,
/* Either the code for a gsharedvt method, or the address for a gsharedvt-out trampoline for the method */
+ /* In llvmonly mode, this is a function descriptor */
MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE,
/* Same for virtual calls */
+ /* In llvmonly mode, this is a function descriptor */
MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT,
/* Same for calli, associated with a signature */
MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI,
MONO_RGCTX_INFO_MEMCPY,
MONO_RGCTX_INFO_BZERO,
/* The address of Nullable<T>.Box () */
+ /* In llvmonly mode, this is a function descriptor */
MONO_RGCTX_INFO_NULLABLE_CLASS_BOX,
MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX,
/* MONO_PATCH_INFO_VCALL_METHOD */