} while (0)
#define GENERIC_SHARING_FAILURE(opcode) do { \
if (cfg->generic_sharing_context) { \
- if (cfg->verbose_level > -1) \
+ if (cfg->verbose_level > 1) \
printf ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", method->klass->name_space, method->klass->name, method->name, method->signature->param_count, mono_opcode_name ((opcode)), __LINE__); \
cfg->exception_type = MONO_EXCEPTION_GENERIC_SHARING_FAILED; \
goto exception_exit; \
guint inline_offset, gboolean is_virtual_call);
/* helper methods signature */
-MonoMethodSignature *helper_sig_class_init_trampoline;
-MonoMethodSignature *helper_sig_domain_get;
-MonoMethodSignature *helper_sig_generic_class_init_trampoline;
-MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
+extern MonoMethodSignature *helper_sig_class_init_trampoline;
+extern MonoMethodSignature *helper_sig_domain_get;
+extern MonoMethodSignature *helper_sig_generic_class_init_trampoline;
+extern MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
/*
* Instruction metadata
}
#endif
+static MonoJumpInfo *
+mono_patch_info_new (MonoMemPool *mp, int ip, MonoJumpInfoType type, gconstpointer target)
+{
+ MonoJumpInfo *ji = mono_mempool_alloc (mp, sizeof (MonoJumpInfo));
+
+ ji->ip.i = ip;
+ ji->type = type;
+ ji->data.target = target;
+
+ return ji;
+}
+
inline static MonoInst*
mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args);
}
static MonoInst*
-mono_emit_imt_method_call (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig,
- MonoInst **args, MonoInst *this, MonoInst *imt_arg)
+mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig,
+ MonoInst **args, MonoInst *this, MonoInst *imt_arg)
{
gboolean virtual = this != NULL;
gboolean enable_for_aot = TRUE;
if (method->string_ctor) {
/* Create the real signature */
/* FIXME: Cache these */
- MonoMethodSignature *ctor_sig = mono_metadata_signature_dup (sig);
+ MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_full (cfg->mempool, sig);
ctor_sig->ret = &mono_defaults.string_class->byval_arg;
sig = ctor_sig;
}
static inline MonoInst*
-mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig,
- MonoInst **args, MonoInst *this)
+mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this)
{
- return mono_emit_imt_method_call (cfg, method, sig, args, this, NULL);
+ return mono_emit_method_call_full (cfg, method, mono_method_signature (method), args, this, NULL);
}
MonoInst*
return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
}
+/*
+ * mono_emit_abs_call:
+ *
+ * Emit a call to the runtime function described by PATCH_TYPE and DATA.
+ */
+inline static MonoInst*
+mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer data,
+ MonoMethodSignature *sig, MonoInst **args)
+{
+ MonoJumpInfo *ji = mono_patch_info_new (cfg->mempool, 0, patch_type, data);
+ MonoInst *ins;
+
+ /*
+ * We pass ji as the call address, the PATCH_INFO_ABS resolving code will
+ * handle it.
+ */
+ if (cfg->abs_patches == NULL)
+ cfg->abs_patches = g_hash_table_new (NULL, NULL);
+ g_hash_table_insert (cfg->abs_patches, ji, ji);
+ ins = mono_emit_native_call (cfg, ji, sig, args);
+ ((MonoCallInst*)ins)->fptr_is_patch = TRUE;
+ return ins;
+}
+
static MonoMethod*
get_memcpy_method (void)
{
EMIT_NEW_ICONST (cfg, iargs [2], n);
memcpy_method = get_memcpy_method ();
- mono_emit_method_call (cfg, memcpy_method, memcpy_method->signature, iargs, NULL);
+ mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
}
}
iargs [0] = dest;
EMIT_NEW_ICONST (cfg, iargs [1], 0);
EMIT_NEW_ICONST (cfg, iargs [2], n);
- mono_emit_method_call (cfg, memset_method, memset_method->signature, iargs, NULL);
+ mono_emit_method_call (cfg, memset_method, iargs, NULL);
}
}
(rgctx) = emit_get_rgctx (cfg, method, (context_used)); \
} while (0)
-static MonoInst*
-emit_get_rgctx_other_table_ptr (MonoCompile *cfg, MonoInst *rgc_ptr, int slot)
+static MonoJumpInfoRgctxEntry *
+mono_patch_info_rgctx_entry_new (MonoMemPool *mp, MonoMethod *method, gboolean in_mrgctx, MonoJumpInfoType patch_type, gconstpointer patch_data, int info_type)
{
- MonoMethodSignature *sig = helper_sig_rgctx_lazy_fetch_trampoline;
- guint8 *tramp = mini_create_rgctx_lazy_fetch_trampoline (slot);
+ MonoJumpInfoRgctxEntry *res = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoRgctxEntry));
+ res->method = method;
+ res->in_mrgctx = in_mrgctx;
+ res->data = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
+ res->data->type = patch_type;
+ res->data->data.target = patch_data;
+ res->info_type = info_type;
+
+ return res;
+}
- return mono_emit_native_call (cfg, tramp, sig, &rgc_ptr);
+static inline MonoInst*
+emit_rgctx_fetch (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
+{
+ return mono_emit_abs_call (cfg, MONO_PATCH_INFO_RGCTX_FETCH, entry, helper_sig_rgctx_lazy_fetch_trampoline, &rgctx);
}
static MonoInst*
emit_get_rgctx_klass (MonoCompile *cfg, int context_used,
MonoInst *rgctx, MonoClass *klass, int rgctx_type)
{
- guint32 slot = mono_method_lookup_or_register_other_info (cfg->current_method,
- context_used & MONO_GENERIC_CONTEXT_USED_METHOD, &klass->byval_arg, rgctx_type, cfg->generic_context);
+ MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_CLASS, klass, rgctx_type);
- return emit_get_rgctx_other_table_ptr (cfg, rgctx, slot);
+ return emit_rgctx_fetch (cfg, rgctx, entry);
}
static MonoInst*
emit_get_rgctx_method (MonoCompile *cfg, int context_used,
MonoInst *rgctx, MonoMethod *cmethod, int rgctx_type)
{
- guint32 slot = mono_method_lookup_or_register_other_info (cfg->current_method,
- context_used & MONO_GENERIC_CONTEXT_USED_METHOD, cmethod, rgctx_type, cfg->generic_context);
+ MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type);
- return emit_get_rgctx_other_table_ptr (cfg, rgctx, slot);
+ return emit_rgctx_fetch (cfg, rgctx, entry);
}
static MonoInst*
emit_get_rgctx_field (MonoCompile *cfg, int context_used,
MonoInst *rgctx, MonoClassField *field, int rgctx_type)
{
- guint32 slot = mono_method_lookup_or_register_other_info (cfg->current_method,
- context_used & MONO_GENERIC_CONTEXT_USED_METHOD, field, rgctx_type, cfg->generic_context);
+ MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_FIELD, field, rgctx_type);
- return emit_get_rgctx_other_table_ptr (cfg, rgctx, slot);
-}
-
-static MonoInst*
-emit_get_rgctx_method_rgctx (MonoCompile *cfg, int context_used,
- MonoInst *rgctx, MonoMethod *rgctx_method)
-{
- guint32 slot = mono_method_lookup_or_register_other_info (cfg->current_method,
- context_used & MONO_GENERIC_CONTEXT_USED_METHOD, rgctx_method,
- MONO_RGCTX_INFO_METHOD_RGCTX, cfg->generic_context);
-
- return emit_get_rgctx_other_table_ptr (cfg, rgctx, slot);
+ return emit_rgctx_fetch (cfg, rgctx, entry);
}
/**
handle_unbox_nullable (MonoCompile* cfg, MonoInst* val, MonoClass* klass, int context_used, MonoInst *rgctx)
{
MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1);
- // Can't encode method ref
- cfg->disable_aot = TRUE;
if (context_used) {
MonoInst *addr = emit_get_rgctx_method (cfg, context_used, rgctx, method,
return mono_emit_rgctx_calli (cfg, mono_method_signature (method), &val, addr, rgctx);
} else {
- return mono_emit_method_call (cfg, method, mono_method_signature (method), &val, NULL);
+ return mono_emit_method_call (cfg, method, &val, NULL);
}
}
if (managed_alloc) {
EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
- return mono_emit_method_call (cfg, managed_alloc, mono_method_signature (managed_alloc), iargs, NULL);
+ return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
}
alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
if (pass_lw) {
iargs [1] = data_inst;
alloc_ftn = mono_object_new;
} else {
- g_assert (!cfg->compile_aot);
-
if (managed_alloc) {
iargs [0] = data_inst;
- return mono_emit_method_call (cfg, managed_alloc,
- mono_method_signature (managed_alloc), iargs, NULL);
+ return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
}
iargs [0] = data_inst;
if (mono_class_is_nullable (klass)) {
MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
- // Can't encode method ref
- cfg->disable_aot = TRUE;
- return mono_emit_method_call (cfg, method, mono_method_signature (method), &val, NULL);
+ return mono_emit_method_call (cfg, method, &val, NULL);
}
alloc = handle_alloc (cfg, klass, TRUE);
}
static MonoInst *
-handle_box_from_inst (MonoCompile *cfg, MonoInst *val, MonoClass *klass, MonoInst *data_inst)
+handle_box_from_inst (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used, MonoInst *rgctx, MonoInst *data_inst)
{
MonoInst *alloc, *ins;
- g_assert (!mono_class_is_nullable (klass));
+ if (mono_class_is_nullable (klass)) {
+ MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
+ MonoInst *addr = emit_get_rgctx_method (cfg, context_used, rgctx, method,
+ MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
- alloc = handle_alloc_from_inst (cfg, klass, data_inst, TRUE);
+ return mono_emit_rgctx_calli (cfg, mono_method_signature (method), &val, addr, rgctx);
+ } else {
+ alloc = handle_alloc_from_inst (cfg, klass, data_inst, TRUE);
- EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
+ EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
- return alloc;
+ return alloc;
+ }
}
static MonoInst*
MONO_ADD_INS (cfg->bb_exit, dummy_use);
}
-#define CODE_IS_STLOC(ip) (((ip) [0] >= CEE_STLOC_0 && (ip) [0] <= CEE_STLOC_3) || ((ip) [0] == CEE_STLOC_S))
-
static gboolean
mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
{
element_size = mono_class_array_element_size (cmethod->klass->element_class);
addr_method = mono_marshal_get_array_address (rank, element_size);
- addr = mono_emit_method_call (cfg, addr_method, addr_method->signature, sp, NULL);
+ addr = mono_emit_method_call (cfg, addr_method, sp, NULL);
return addr;
}
return NULL;
EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
iargs [1] = args [0];
- return mono_emit_method_call (cfg, managed_alloc, mono_method_signature (managed_alloc), iargs, this);
+ return mono_emit_method_call (cfg, managed_alloc, iargs, this);
}
}
return NULL;
#define MONO_INLINE_CALLER_LIMITED_METHODS 1
#if (MONO_INLINE_CALLED_LIMITED_METHODS)
-static char*
-mono_inline_called_method_name_limit = NULL;
-static gboolean check_inline_called_method_name_limit (MonoMethod *called_method) {
- char *called_method_name = mono_method_full_name (called_method, TRUE);
+static gboolean
+check_inline_called_method_name_limit (MonoMethod *called_method)
+{
int strncmp_result;
+ static char *limit = NULL;
- if (mono_inline_called_method_name_limit == NULL) {
+ if (limit == NULL) {
char *limit_string = getenv ("MONO_INLINE_CALLED_METHOD_NAME_LIMIT");
- if (limit_string != NULL) {
- mono_inline_called_method_name_limit = limit_string;
- } else {
- mono_inline_called_method_name_limit = (char *) "";
- }
+
+ if (limit_string != NULL)
+ limit = limit_string;
+ else
+ limit = (char *) "";
}
+
+ if (limit [0] != '\0') {
+ char *called_method_name = mono_method_full_name (called_method, TRUE);
+
+ strncmp_result = strncmp (called_method_name, limit, strlen (limit));
+ g_free (called_method_name);
- strncmp_result = strncmp (called_method_name, mono_inline_called_method_name_limit, strlen (mono_inline_called_method_name_limit));
- g_free (called_method_name);
-
- //return (strncmp_result <= 0);
- return (strncmp_result == 0);
+ //return (strncmp_result <= 0);
+ return (strncmp_result == 0);
+ } else {
+ return FALSE;
+ }
}
#endif
#if (MONO_INLINE_CALLER_LIMITED_METHODS)
-static char*
-mono_inline_caller_method_name_limit = NULL;
-static gboolean check_inline_caller_method_name_limit (MonoMethod *caller_method) {
- char *caller_method_name = mono_method_full_name (caller_method, TRUE);
+static gboolean
+check_inline_caller_method_name_limit (MonoMethod *caller_method)
+{
int strncmp_result;
+ static char *limit = NULL;
- if (mono_inline_caller_method_name_limit == NULL) {
+ if (limit == NULL) {
char *limit_string = getenv ("MONO_INLINE_CALLER_METHOD_NAME_LIMIT");
if (limit_string != NULL) {
- mono_inline_caller_method_name_limit = limit_string;
+ limit = limit_string;
} else {
- mono_inline_caller_method_name_limit = (char *) "";
+ limit = (char *) "";
}
}
+
+ if (limit [0] != '\0') {
+ char *caller_method_name = mono_method_full_name (caller_method, TRUE);
+
+ strncmp_result = strncmp (caller_method_name, limit, strlen (limit));
+ g_free (caller_method_name);
- strncmp_result = strncmp (caller_method_name, mono_inline_caller_method_name_limit, strlen (mono_inline_caller_method_name_limit));
- g_free (caller_method_name);
-
- //return (strncmp_result <= 0);
- return (strncmp_result == 0);
+ //return (strncmp_result <= 0);
+ return (strncmp_result == 0);
+ } else {
+ return FALSE;
+ }
}
#endif
NEW_ICONST (cfg, args [0], 4);
NEW_METHODCONST (cfg, args [1], caller);
- mono_emit_method_call (cfg, secman->linkdemandsecurityexception, mono_method_signature (secman->linkdemandsecurityexception), args, NULL);
+ mono_emit_method_call (cfg, secman->linkdemandsecurityexception, args, NULL);
} else if (cfg->exception_type == MONO_EXCEPTION_NONE) {
/* don't hide previous results */
cfg->exception_type = MONO_EXCEPTION_SECURITY_LINKDEMAND;
EMIT_NEW_METHODCONST (cfg, args [0], caller);
EMIT_NEW_METHODCONST (cfg, args [1], callee);
- mono_emit_method_call (cfg, thrower, mono_method_signature (thrower), args, NULL);
+ mono_emit_method_call (cfg, thrower, args, NULL);
}
static MonoMethod*
{
MonoMethod *thrower = verification_exception ();
- mono_emit_method_call (cfg, thrower, mono_method_signature (thrower), NULL, NULL);
+ mono_emit_method_call (cfg, thrower, NULL, NULL);
}
static void
EMIT_NEW_DECLSECCONST (cfg, args[0], image, actions.demand);
EMIT_NEW_ICONST (cfg, args [1], actions.demand.size);
/* Calls static void SecurityManager.InternalDemand (byte* permissions, int size); */
- mono_emit_method_call (cfg, secman->demand, mono_method_signature (secman->demand), args, NULL);
+ mono_emit_method_call (cfg, secman->demand, args, NULL);
}
if (actions.noncasdemand.blob) {
/* CLR 1.x uses a .noncasdemand (but 2.x doesn't) */
EMIT_NEW_DECLSECCONST (cfg, args[0], image, actions.noncasdemand);
EMIT_NEW_ICONST (cfg, args [1], actions.noncasdemand.size);
/* Calls static void SecurityManager.InternalDemand (byte* permissions, int size); */
- mono_emit_method_call (cfg, secman->demand, mono_method_signature (secman->demand), args, NULL);
+ mono_emit_method_call (cfg, secman->demand, args, NULL);
}
if (actions.demandchoice.blob) {
/* New in 2.0, Demand must succeed for one of the permissions (i.e. not all) */
EMIT_NEW_DECLSECCONST (cfg, args[0], image, actions.demandchoice);
EMIT_NEW_ICONST (cfg, args [1], actions.demandchoice.size);
/* Calls static void SecurityManager.InternalDemandChoice (byte* permissions, int size); */
- mono_emit_method_call (cfg, secman->demandchoice, mono_method_signature (secman->demandchoice), args, NULL);
+ mono_emit_method_call (cfg, secman->demandchoice, args, NULL);
}
}
/* we must Demand SecurityPermission.Unmanaged before p/invoking */
if (pinvoke) {
- mono_emit_method_call (cfg, secman->demandunmanaged, mono_method_signature (secman->demandunmanaged), NULL, NULL);
+ mono_emit_method_call (cfg, secman->demandunmanaged, NULL, NULL);
}
if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) {
token = read32 (ip + 4);
klass = mini_get_class (method, token, generic_context);
CHECK_TYPELOAD (klass);
- if (cfg->generic_sharing_context && mono_class_check_context_used (klass))
- GENERIC_SHARING_FAILURE (CEE_INITOBJ);
-
- if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
+ if (generic_class_is_reference_type (cfg, klass)) {
+ MONO_EMIT_NEW_PCONST (cfg, cfg->locals [ip [1]]->dreg, NULL);
+ } else if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
MONO_EMIT_NEW_PCONST (cfg, cfg->locals [ip [1]]->dreg, NULL);
} else if (MONO_TYPE_ISSTRUCT (&klass->byval_arg)) {
MONO_EMIT_NEW_VZERO (cfg, cfg->locals [ip [1]]->dreg, klass);
CHECK_OPSIZE (5);
if (stack_start != sp)
UNVERIFIED;
- MONO_INST_NEW_CALL (cfg, call, OP_JMP);
- ins = (MonoInst*)call;
token = read32 (ip + 1);
/* FIXME: check the signature matches */
cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
CHECK_CFG_EXCEPTION;
}
+#ifdef __x86_64__
+ {
+ MonoMethodSignature *fsig = mono_method_signature (cmethod);
+ int i, n;
+
+ /* FIXME: Remove OP_JMP from mini-amd64.c when the old JIT is removed */
+
+ /* Handle tail calls similarly to calls */
+ n = fsig->param_count + fsig->hasthis;
+
+ MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
+ call->method = cmethod;
+ call->tail_call = TRUE;
+ call->signature = mono_method_signature (cmethod);
+ call->args = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
+ call->inst.inst_p0 = cmethod;
+ for (i = 0; i < n; ++i)
+ EMIT_NEW_ARGLOAD (cfg, call->args [i], i);
+
+ mono_arch_emit_call (cfg, call);
+ MONO_ADD_INS (bblock, (MonoInst*)call);
+ }
+#else
+ MONO_INST_NEW_CALL (cfg, call, OP_JMP);
+ ins = (MonoInst*)call;
ins->inst_p0 = cmethod;
MONO_ADD_INS (bblock, ins);
+#endif
+
ip += 5;
start_new_bblock = 1;
-
- /* FIXME: */
- cfg->disable_aot = 1;
break;
}
case CEE_CALLI:
goto load_error;
if (mono_method_signature (cmethod)->pinvoke) {
- MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod, check_for_pending_exc);
+ MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod, check_for_pending_exc, FALSE);
fsig = mono_method_signature (wrapper);
} else if (constrained_call) {
fsig = mono_method_signature (cmethod);
* generic method).
*/
if (sharing_enabled && context_sharable &&
- !mini_method_get_context (cmethod)->method_inst)
+ !(mini_method_get_context (cmethod) && mini_method_get_context (cmethod)->method_inst))
pass_vtable = TRUE;
}
mono_get_vtable_var (cfg);
}
- // FIXME:
- if (!cmethod)
- GENERIC_SHARING_FAILURE (*ip);
-
if (pass_vtable) {
if (context_used) {
MonoInst *rgctx;
MonoInst *rgctx;
EMIT_GET_RGCTX (rgctx, context_used);
- vtable_arg = emit_get_rgctx_method_rgctx (cfg, context_used, rgctx, cmethod);
+ vtable_arg = emit_get_rgctx_method (cfg, context_used, rgctx, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
} else {
- MonoMethodRuntimeGenericContext *mrgctx;
-
- mrgctx = mono_method_lookup_rgctx (mono_class_vtable (cfg->domain, cmethod->klass),
- mini_method_get_context (cmethod)->method_inst);
-
- EMIT_NEW_PCONST (cfg, vtable_arg, mrgctx);
+ EMIT_NEW_METHOD_RGCTX_CONST (cfg, vtable_arg, cmethod);
}
if (!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
/* FIXME: This should be a managed pointer */
this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
- /* Because of the PCONST below */
- cfg->disable_aot = TRUE;
EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
if (context_used) {
MonoInst *rgctx;
iargs [1] = emit_get_rgctx_method (cfg, context_used, rgctx, cmethod, MONO_RGCTX_INFO_METHOD);
EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
addr = mono_emit_jit_icall (cfg,
- mono_helper_compile_generic_method_wo_context, iargs);
+ mono_helper_compile_generic_method, iargs);
} else {
EMIT_NEW_METHODCONST (cfg, iargs [1], cmethod);
- EMIT_NEW_PCONST (cfg, iargs [2], mono_method_get_context (cmethod));
- EMIT_NEW_TEMPLOADA (cfg, iargs [3], this_arg_temp->inst_c0);
+ EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
addr = mono_emit_jit_icall (cfg, mono_helper_compile_generic_method, iargs);
}
}
#endif
- /* FIXME: */
- cfg->disable_aot = 1;
-
ins = (MonoInst*)call;
ins->inst_p0 = cmethod;
ins->inst_p1 = arg_array [0];
(cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
/* Prevent inlining of methods that call wrappers */
INLINE_FAILURE;
- cmethod = mono_marshal_get_native_wrapper (cmethod, check_for_pending_exc);
+ cmethod = mono_marshal_get_native_wrapper (cmethod, check_for_pending_exc, FALSE);
allways = TRUE;
}
int rgctx_reg = mono_alloc_preg (cfg);
MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, vtable_arg->dreg);
- ins = (MonoInst*)mono_emit_method_call (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL);
+ ins = (MonoInst*)mono_emit_method_call_full (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL, NULL);
call = (MonoCallInst*)ins;
mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE);
#else
GENERIC_SHARING_FAILURE (*ip);
#endif
} else if (imt_arg) {
- ins = (MonoInst*)mono_emit_imt_method_call (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL, imt_arg);
+ ins = (MonoInst*)mono_emit_method_call_full (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL, imt_arg);
} else {
- ins = (MonoInst*)mono_emit_method_call (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL);
+ ins = (MonoInst*)mono_emit_method_call_full (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL, NULL);
}
if (!MONO_TYPE_IS_VOID (fsig->ret))
case CEE_RET:
if (cfg->method != method) {
/* return from inlined method */
- if (return_var) {
+ /*
+ * If in_count == 0, that means the ret is unreachable due to
+ * being preceeded by a throw. In that case, inline_method () will
+ * handle setting the return value
+ * (test case: test_0_inline_throw ()).
+ */
+ if (return_var && cfg->cbb->in_count) {
MonoInst *store;
CHECK_STACK (1);
--sp;
MonoBasicBlock **targets;
MonoBasicBlock *default_bblock;
MonoJumpInfoBBTable *table;
+#ifndef __arm__
int offset_reg = alloc_preg (cfg);
int target_reg = alloc_preg (cfg);
int table_reg = alloc_preg (cfg);
int sum_reg = alloc_preg (cfg);
+#endif
CHECK_OPSIZE (5);
CHECK_STACK (1);
if (mini_class_is_system_array (cmethod->klass)) {
g_assert (!context_used);
EMIT_NEW_METHODCONST (cfg, *sp, cmethod);
- if (fsig->param_count == 2)
- /* Avoid varargs in the common case */
+
+ /* Avoid varargs in the common case */
+ if (fsig->param_count == 1)
+ alloc = mono_emit_jit_icall (cfg, mono_array_new_1, sp);
+ else if (fsig->param_count == 2)
alloc = mono_emit_jit_icall (cfg, mono_array_new_2, sp);
else
alloc = handle_array_new (cfg, fsig->param_count, sp, ip);
/* we simply pass a null pointer */
EMIT_NEW_PCONST (cfg, *sp, NULL);
/* now call the string ctor */
- alloc = mono_emit_method_call (cfg, cmethod, fsig, sp, NULL);
+ alloc = mono_emit_method_call_full (cfg, cmethod, fsig, sp, NULL, NULL);
} else {
MonoInst* callvirt_this_arg = NULL;
* As a workaround, we call class cctors before allocating objects.
*/
if (mini_field_access_needs_cctor_run (cfg, method, vtable) && !(g_slist_find (class_inits, vtable))) {
- guint8 *tramp = mono_create_class_init_trampoline (vtable);
- mono_emit_native_call (cfg, tramp,
- helper_sig_class_init_trampoline,
- NULL);
+ mono_emit_abs_call (cfg, MONO_PATCH_INFO_CLASS_INIT, vtable->klass, helper_sig_class_init_trampoline, NULL);
if (cfg->verbose_level > 2)
printf ("class %s.%s needs init call for ctor\n", cmethod->klass->name_space, cmethod->klass->name);
class_inits = g_slist_prepend (class_inits, vtable);
inline_costs += costs - 5;
} else {
INLINE_FAILURE;
- mono_emit_method_call (cfg, cmethod, fsig, sp, callvirt_this_arg);
+ mono_emit_method_call_full (cfg, cmethod, fsig, sp, callvirt_this_arg, NULL);
}
} else if (context_used &&
(cmethod->klass->valuetype ||
mono_emit_calli (cfg, fsig, sp, cmethod_addr);
} else {
INLINE_FAILURE;
- mono_emit_method_call (cfg, cmethod, fsig, sp, callvirt_this_arg);
+ mono_emit_method_call_full (cfg, cmethod, fsig, sp, callvirt_this_arg, NULL);
}
}
ip += 5;
}
break;
- case CEE_ISINST:
+ case CEE_ISINST: {
CHECK_STACK (1);
--sp;
CHECK_OPSIZE (5);
if (sp [0]->type != STACK_OBJ)
UNVERIFIED;
- if (cfg->generic_sharing_context && mono_class_check_context_used (klass))
- GENERIC_SHARING_FAILURE (CEE_ISINST);
+ if (cfg->generic_sharing_context)
+ context_used = mono_class_check_context_used (klass);
- if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
-
+ if (context_used) {
+ MonoInst *rgctx, *args [2];
+
+ /* obj */
+ args [0] = *sp;
+
+ /* klass */
+ EMIT_GET_RGCTX (rgctx, context_used);
+ args [1] = emit_get_rgctx_klass (cfg, context_used, rgctx, klass, MONO_RGCTX_INFO_KLASS);
+
+ *sp = mono_emit_jit_icall (cfg, mono_object_isinst, args);
+ sp++;
+ ip += 5;
+ inline_costs += 2;
+ } else if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
MonoMethod *mono_isinst;
MonoInst *iargs [1];
int costs;
ip += 5;
}
break;
+ }
case CEE_UNBOX_ANY: {
MonoInst *rgctx = NULL;
klass = mini_get_class (method, token, generic_context);
CHECK_TYPELOAD (klass);
+ mono_save_token_info (cfg, image, token, klass);
+
if (cfg->generic_sharing_context)
context_used = mono_class_check_context_used (klass);
}
case CEE_BOX: {
MonoInst *val;
- int context_used = 0;
CHECK_STACK (1);
--sp;
klass = mini_get_class (method, token, generic_context);
CHECK_TYPELOAD (klass);
- if (cfg->generic_sharing_context) {
- context_used = mono_class_check_context_used (klass);
+ mono_save_token_info (cfg, image, token, klass);
- if (context_used & MONO_GENERIC_CONTEXT_USED_METHOD)
- GENERIC_SHARING_FAILURE (*ip);
- }
+ if (cfg->generic_sharing_context)
+ context_used = mono_class_check_context_used (klass);
if (generic_class_is_reference_type (cfg, klass)) {
*sp++ = val;
if (context_used) {
MonoInst *rgctx;
+ MonoInst *data;
+ int rgctx_info;
- if (mono_class_is_nullable (klass)) {
- GENERIC_SHARING_FAILURE (CEE_BOX);
- } else {
- MonoInst *data;
- int rgctx_info;
-
- EMIT_GET_RGCTX (rgctx, context_used);
- if (cfg->opt & MONO_OPT_SHARED)
- rgctx_info = MONO_RGCTX_INFO_KLASS;
- else
- rgctx_info = MONO_RGCTX_INFO_VTABLE;
- data = emit_get_rgctx_klass (cfg, context_used, rgctx, klass, rgctx_info);
- *sp++ = handle_box_from_inst (cfg, val, klass, data);
- }
+ EMIT_GET_RGCTX (rgctx, context_used);
+ if (cfg->opt & MONO_OPT_SHARED)
+ rgctx_info = MONO_RGCTX_INFO_KLASS;
+ else
+ rgctx_info = MONO_RGCTX_INFO_VTABLE;
+ data = emit_get_rgctx_klass (cfg, context_used, rgctx, klass, rgctx_info);
+ *sp++ = handle_box_from_inst (cfg, val, klass, context_used, rgctx, data);
} else {
*sp++ = handle_box (cfg, val, klass);
}
klass = mini_get_class (method, token, generic_context);
CHECK_TYPELOAD (klass);
+ mono_save_token_info (cfg, image, token, klass);
+
if (cfg->generic_sharing_context)
context_used = mono_class_check_context_used (klass);
inline_costs += costs;
break;
} else {
- mono_emit_method_call (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper), iargs, NULL);
+ mono_emit_method_call (cfg, stfld_wrapper, iargs, NULL);
}
} else {
MonoInst *store;
inline_costs += costs;
break;
} else {
- ins = mono_emit_method_call (cfg, wrapper, mono_method_signature (wrapper), iargs, NULL);
+ ins = mono_emit_method_call (cfg, wrapper, iargs, NULL);
*sp++ = ins;
}
} else {
/* Have to compute the address of the variable */
- var = cfg->vreg_to_inst [sp [0]->dreg];
- if (!var && sp [0]->opcode == OP_VMOVE)
- var = cfg->vreg_to_inst [sp [0]->sreg1];
- g_assert (var);
+ var = get_vreg_to_inst (cfg, sp [0]->dreg);
+ if (!var)
+ var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, sp [0]->dreg);
+ else
+ g_assert (var->klass == klass);
EMIT_NEW_VARLOADA (cfg, ins, var, &var->klass->byval_arg);
sp [0] = ins;
* the calling convention, so assign it manually, and make a call
* using a signature without parameters.
*/
- call = (MonoCallInst*)mono_emit_native_call (cfg, mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT), helper_sig_generic_class_init_trampoline, &vtable);
+ call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_GENERIC_CLASS_INIT, NULL, helper_sig_generic_class_init_trampoline, &vtable);
#ifdef MONO_ARCH_VTABLE_REG
mono_call_inst_add_outarg_reg (cfg, call, vtable->dreg, MONO_ARCH_VTABLE_REG, FALSE);
#else
CHECK_TYPELOAD (klass);
if (!addr) {
if (mini_field_access_needs_cctor_run (cfg, method, vtable) && !(g_slist_find (class_inits, vtable))) {
- guint8 *tramp = mono_create_class_init_trampoline (vtable);
- mono_emit_native_call (cfg, tramp,
- helper_sig_class_init_trampoline,
- NULL);
+ mono_emit_abs_call (cfg, MONO_PATCH_INFO_CLASS_INIT, vtable->klass, helper_sig_class_init_trampoline, NULL);
if (cfg->verbose_level > 2)
printf ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, field->name);
class_inits = g_slist_prepend (class_inits, vtable);
load->flags |= ins_flag;
ins_flag = 0;
*sp++ = load;
-
- /* fixme: dont see the problem why this does not work */
- //cfg->disable_aot = TRUE;
}
}
ip += 5;
EMIT_NEW_PCONST (cfg, iargs [1], (char*)data_ptr);
}
EMIT_NEW_ICONST (cfg, iargs [2], data_size);
- mono_emit_method_call (cfg, memcpy_method, memcpy_method->signature, iargs, NULL);
+ mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
ip += 11;
}
iargs [1] = sp [1];
iargs [0] = sp [0];
- mono_emit_method_call (cfg, helper, mono_method_signature (helper), iargs, NULL);
+ mono_emit_method_call (cfg, helper, iargs, NULL);
} else {
if (sp [1]->opcode == OP_ICONST) {
int array_reg = sp [0]->dreg;
MONO_ADD_INS (bblock, ins);
*sp++ = ins;
+ mono_decompose_opcode (cfg, ins);
+
++ip;
break;
}
case CEE_REFANYVAL: {
MonoInst *src_var, *src;
- int context_used = 0;
int klass_reg = alloc_preg (cfg);
int dreg = alloc_preg (cfg);
CHECK_TYPELOAD (klass);
mono_class_init (klass);
- if (cfg->generic_sharing_context) {
+ if (cfg->generic_sharing_context)
context_used = mono_class_check_context_used (klass);
- if (context_used && cfg->compile_aot)
- GENERIC_SHARING_FAILURE (*ip);
- }
+
+ // FIXME:
+ src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
+ if (!src_var)
+ src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
+ EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, src->dreg, G_STRUCT_OFFSET (MonoTypedRef, klass));
if (context_used) {
+ MonoInst *rgctx, *klass_ins;
+
+ EMIT_GET_RGCTX (rgctx, context_used);
+ klass_ins = emit_get_rgctx_klass (cfg, context_used, rgctx, klass, MONO_RGCTX_INFO_KLASS);
+
// FIXME:
- GENERIC_SHARING_FAILURE (*ip);
+ MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_ins->dreg);
+ MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
} else {
- // FIXME:
- src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
- if (!src_var)
- src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
- EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, src->dreg, G_STRUCT_OFFSET (MonoTypedRef, klass));
mini_emit_class_check (cfg, klass_reg, klass);
- EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, src->dreg, G_STRUCT_OFFSET (MonoTypedRef, value));
}
+ EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, src->dreg, G_STRUCT_OFFSET (MonoTypedRef, value));
ins->type = STACK_MP;
*sp++ = ins;
ip += 5;
}
case CEE_MKREFANY: {
MonoInst *loc, *addr;
- int context_used = 0;
CHECK_STACK (1);
MONO_INST_NEW (cfg, ins, *ip);
CHECK_TYPELOAD (klass);
mono_class_init (klass);
- if (cfg->generic_sharing_context) {
+ if (cfg->generic_sharing_context)
context_used = mono_class_check_context_used (klass);
- if (context_used && cfg->compile_aot)
- GENERIC_SHARING_FAILURE (CEE_MKREFANY);
- }
loc = mono_compile_create_var (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL);
EMIT_NEW_TEMPLOADA (cfg, addr, loc->inst_c0);
if (context_used) {
- GENERIC_SHARING_FAILURE (CEE_MKREFANY);
+ MonoInst *rgctx, *const_ins;
+ int type_reg = alloc_preg (cfg);
+
+ EMIT_GET_RGCTX (rgctx, context_used);
+ const_ins = emit_get_rgctx_klass (cfg, context_used, rgctx, klass, MONO_RGCTX_INFO_KLASS);
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, G_STRUCT_OFFSET (MonoTypedRef, klass), const_ins->dreg);
+ MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_ins->dreg, G_STRUCT_OFFSET (MonoClass, byval_arg));
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, G_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
} else if (cfg->compile_aot) {
int const_reg = alloc_preg (cfg);
int type_reg = alloc_preg (cfg);
case CEE_LDTOKEN: {
gpointer handle;
MonoClass *handle_class;
- int context_used = 0;
CHECK_STACK_OVF (1);
if (context_used) {
MonoInst *rgctx;
- g_assert (!cfg->compile_aot);
EMIT_GET_RGCTX (rgctx, context_used);
ins = emit_get_rgctx_klass (cfg, context_used, rgctx, tclass, MONO_RGCTX_INFO_REFLECTION_TYPE);
} else if (cfg->compile_aot) {
if (context_used) {
MonoInst *rgctx;
- g_assert (!cfg->compile_aot);
-
EMIT_GET_RGCTX (rgctx, context_used);
if (handle_class == mono_defaults.typehandle_class) {
ins = emit_get_rgctx_klass (cfg, context_used, rgctx,
token = read32 (ip + 2);
ptr = mono_method_get_wrapper_data (method, token);
- if (cfg->compile_aot && (cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE || cfg->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE)) {
- MonoMethod *wrapped = mono_marshal_method_from_wrapper (cfg->method);
-
- if (wrapped && ptr != NULL && mono_lookup_internal_call (wrapped) == ptr) {
- EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_ICALL_ADDR, wrapped);
+ if (cfg->compile_aot && (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) && (strstr (method->name, "__icall_wrapper_") == method->name)) {
+ MonoJitICallInfo *callinfo;
+ const char *icall_name;
+
+ icall_name = method->name + strlen ("__icall_wrapper_");
+ g_assert (icall_name);
+ callinfo = mono_find_jit_icall_by_name (icall_name);
+ g_assert (callinfo);
+
+ if (ptr == callinfo->func) {
+ /* Will be transformed into an AOTCONST later */
+ EMIT_NEW_PCONST (cfg, ins, ptr);
*sp++ = ins;
ip += 6;
break;
}
-
- if ((method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) && (strstr (method->name, "__icall_wrapper_") == method->name)) {
- MonoJitICallInfo *callinfo;
- const char *icall_name;
-
- icall_name = method->name + strlen ("__icall_wrapper_");
- g_assert (icall_name);
- callinfo = mono_find_jit_icall_by_name (icall_name);
- g_assert (callinfo);
-
- if (ptr == callinfo->func) {
- /* Will be transformed into an AOTCONST later */
- EMIT_NEW_PCONST (cfg, ins, ptr);
- *sp++ = ins;
- ip += 6;
- break;
- }
- }
}
/* FIXME: Generalize this */
if (cfg->compile_aot && ptr == mono_thread_interruption_request_flag ()) {
cfg->disable_aot = 1;
break;
}
+ case CEE_MONO_ICALL_ADDR: {
+ MonoMethod *cmethod;
+ gpointer ptr;
+
+ CHECK_STACK_OVF (1);
+ CHECK_OPSIZE (6);
+ token = read32 (ip + 2);
+
+ cmethod = mono_method_get_wrapper_data (method, token);
+
+ if (cfg->compile_aot) {
+ EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_ICALL_ADDR, cmethod);
+ } else {
+ ptr = mono_lookup_internal_call (cmethod);
+ g_assert (ptr);
+ EMIT_NEW_PCONST (cfg, ins, ptr);
+ }
+ *sp++ = ins;
+ ip += 6;
+ break;
+ }
case CEE_MONO_VTADDR: {
MonoInst *src_var, *src;
goto load_error;
mono_class_init (cmethod->klass);
+ mono_save_token_info (cfg, image, n, cmethod);
+
if (cfg->generic_sharing_context)
context_used = mono_method_check_context_used (cmethod);
goto load_error;
mono_class_init (cmethod->klass);
- if (cfg->generic_sharing_context && mono_method_check_context_used (cmethod))
- GENERIC_SHARING_FAILURE (CEE_LDVIRTFTN);
+ if (cfg->generic_sharing_context)
+ context_used = mono_method_check_context_used (cmethod);
if (mono_security_get_mode () == MONO_SECURITY_MODE_CAS) {
if (check_linkdemand (cfg, method, cmethod))
--sp;
args [0] = *sp;
- EMIT_NEW_METHODCONST (cfg, args [1], cmethod);
- *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args);
+
+ if (context_used) {
+ MonoInst *rgctx;
+
+ EMIT_GET_RGCTX (rgctx, context_used);
+ args [1] = emit_get_rgctx_method (cfg, context_used, rgctx, cmethod, MONO_RGCTX_INFO_METHOD);
+ *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn_gshared, args);
+ } else {
+ EMIT_NEW_METHODCONST (cfg, args [1], cmethod);
+ *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args);
+ }
ip += 6;
inline_costs += 10 * num_calls++;
iargs [2] = sp [2];
if (ip [1] == CEE_CPBLK) {
MonoMethod *memcpy_method = get_memcpy_method ();
- mono_emit_method_call (cfg, memcpy_method, memcpy_method->signature, iargs, NULL);
+ mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
} else {
MonoMethod *memset_method = get_memset_method ();
- mono_emit_method_call (cfg, memset_method, memset_method->signature, iargs, NULL);
+ mono_emit_method_call (cfg, memset_method, iargs, NULL);
}
}
ip += 2;
mono_op_to_op_imm_noemul (int opcode)
{
switch (opcode) {
-#if SIZEOF_VOID_P == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPTS)
+#if SIZEOF_VOID_P == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS)
case OP_LSHR:
case OP_LSHL:
case OP_LSHR_UN:
if (cfg->verbose_level > 1)
printf ("LONG VREG R%d made global.\n", vreg);
-
- /*
- * Make the component vregs volatile since the optimizations can
- * get confused otherwise.
- */
- get_vreg_to_inst (cfg, vreg + 1)->flags |= MONO_INST_VOLATILE;
- get_vreg_to_inst (cfg, vreg + 2)->flags |= MONO_INST_VOLATILE;
}
+
+ /*
+ * Make the component vregs volatile since the optimizations can
+ * get confused otherwise.
+ */
+ get_vreg_to_inst (cfg, vreg + 1)->flags |= MONO_INST_VOLATILE;
+ get_vreg_to_inst (cfg, vreg + 2)->flags |= MONO_INST_VOLATILE;
}
#endif