+#ifndef DISABLE_JIT
+
+static gboolean is_int_type (MonoType *t);
+static gboolean is_float_type (MonoType *t);
+
+static MonoInst*
+emit_narrow (MonoCompile *cfg, const MagicTypeInfo *info, int sreg)
+{
+ MonoInst *ins;
+
+ MONO_INST_NEW (cfg, ins, info->conv_8_to_4);
+ ins->sreg1 = sreg;
+ if (info->conv_8_to_4 == OP_FCONV_TO_R4)
+ ins->type = cfg->r4_stack_type;
+ else
+ ins->type = info->small_stack_type;
+ ins->dreg = alloc_dreg (cfg, ins->type);
+ MONO_ADD_INS (cfg->cbb, ins);
+ return mono_decompose_opcode (cfg, ins);
+}
+
+static MonoInst*
+emit_widen (MonoCompile *cfg, const MagicTypeInfo *info, int sreg)
+{
+ MonoInst *ins;
+
+ if (cfg->r4fp && info->conv_4_to_8 == OP_FCONV_TO_R8)
+ MONO_INST_NEW (cfg, ins, OP_RCONV_TO_R8);
+ else
+ MONO_INST_NEW (cfg, ins, info->conv_4_to_8);
+ ins->sreg1 = sreg;
+ ins->type = info->big_stack_type;
+ ins->dreg = alloc_dreg (cfg, info->big_stack_type);
+ MONO_ADD_INS (cfg->cbb, ins);
+ return mono_decompose_opcode (cfg, ins);
+}
+
+static MonoInst*
+emit_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, const MagicTypeInfo *info)
+{
+ int i = 0;
+ const char *name = cmethod->name;
+ MonoInst *ins;
+ int type_index, stack_type;
+
+ if (info->op_index == 2 && cfg->r4fp && SIZEOF_VOID_P == 4) {
+ type_index = 3;
+ stack_type = STACK_R4;
+ } else {
+ type_index = info->op_index;
+ stack_type = info->stack_type;
+ }
+
+ if (!strcmp ("op_Implicit", name) || !strcmp ("op_Explicit", name)) {
+ int source_size = type_size (cfg, fsig->params [0]);
+ int dest_size = type_size (cfg, fsig->ret);
+
+ switch (info->big_stack_type) {
+ case STACK_I8:
+ if (!is_int_type (fsig->params [0]) || !is_int_type (fsig->ret))
+ return NULL;
+ break;
+ case STACK_R8:
+ if (!is_float_type (fsig->params [0]) || !is_float_type (fsig->ret))
+ return NULL;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ //4 -> 4 or 8 -> 8
+ if (source_size == dest_size)
+ return args [0];
+
+ //4 -> 8
+ if (source_size < dest_size)
+ return emit_widen (cfg, info, args [0]->dreg);
+
+ //8 -> 4
+ return emit_narrow (cfg, info, args [0]->dreg);
+ }
+
+ if (!strcmp (".ctor", name)) {
+ gboolean is_ldaddr = args [0]->opcode == OP_LDADDR;
+ int arg0 = args [1]->dreg;
+ int arg_size = type_size (cfg, fsig->params [0]);
+
+ if (arg_size > SIZEOF_VOID_P) //8 -> 4
+ arg0 = emit_narrow (cfg, info, arg0)->dreg;
+ else if (arg_size < SIZEOF_VOID_P) //4 -> 8
+ arg0 = emit_widen (cfg, info, arg0)->dreg;
+
+ if (is_ldaddr) { /*Eliminate LDADDR if it's initing a local var*/
+ int dreg = ((MonoInst*)args [0]->inst_p0)->dreg;
+ NULLIFY_INS (args [0]);
+ EMIT_NEW_UNALU (cfg, ins, info->move, dreg, arg0);
+ cfg->has_indirection = TRUE;
+ } else {
+ EMIT_NEW_STORE_MEMBASE (cfg, ins, info->store_op, args [0]->dreg, 0, arg0);
+ }
+ return ins;
+ }
+
+ if (!strcmp ("op_Increment", name) || !strcmp ("op_Decrement", name)) {
+ gboolean inc = !strcmp ("op_Increment", name);
+ /* FIXME float inc is too complex to bother with*/
+ //this is broken with ints too
+ // if (!info->inc_op)
+ return NULL;
+
+ /* We have IR for inc/dec */
+ MONO_INST_NEW (cfg, ins, inc ? info->inc_op : info->dec_op);
+ ins->dreg = alloc_dreg (cfg, info->stack_type);
+ ins->sreg1 = args [0]->dreg;
+ ins->inst_imm = 1;
+ ins->type = info->stack_type;
+ MONO_ADD_INS (cfg->cbb, ins);
+ return ins;
+ }
+
+ for (i = 0; i < sizeof (int_binop) / sizeof (IntIntrisic); ++i) {
+ if (!strcmp (int_binop [i].op_name, name)) {
+ if (!int_binop [i].op_table [info->op_index])
+ return NULL;
+ g_assert (int_binop [i].op_table [type_index]);
+
+ MONO_INST_NEW (cfg, ins, int_binop [i].op_table [type_index]);
+ ins->dreg = alloc_dreg (cfg, stack_type);
+ ins->sreg1 = args [0]->dreg;
+ ins->sreg2 = args [1]->dreg;
+ ins->type = stack_type;
+ MONO_ADD_INS (cfg->cbb, ins);
+ return mono_decompose_opcode (cfg, ins);
+ }
+ }
+
+ for (i = 0; i < sizeof (int_unnop) / sizeof (IntIntrisic); ++i) {
+ if (!strcmp (int_unnop [i].op_name, name)) {
+ g_assert (int_unnop [i].op_table [type_index]);
+
+ MONO_INST_NEW (cfg, ins, int_unnop [i].op_table [type_index]);
+ ins->dreg = alloc_dreg (cfg, stack_type);
+ ins->sreg1 = args [0]->dreg;
+ ins->type = stack_type;
+ MONO_ADD_INS (cfg->cbb, ins);
+ return ins;
+ }
+ }
+
+ for (i = 0; i < sizeof (int_cmpop) / sizeof (IntIntrisic); ++i) {
+ if (!strcmp (int_cmpop [i].op_name, name)) {
+ g_assert (int_cmpop [i].op_table [type_index]);
+
+ if (info->compare_op) {
+ MONO_INST_NEW (cfg, ins, info->compare_op);
+ ins->dreg = -1;
+ ins->sreg1 = args [0]->dreg;
+ ins->sreg2 = args [1]->dreg;
+ MONO_ADD_INS (cfg->cbb, ins);
+
+ MONO_INST_NEW (cfg, ins, int_cmpop [i].op_table [type_index]);
+ ins->dreg = alloc_preg (cfg);
+ ins->type = STACK_I4;
+ MONO_ADD_INS (cfg->cbb, ins);
+ } else {
+ MONO_INST_NEW (cfg, ins, int_cmpop [i].op_table [type_index]);
+ ins->dreg = alloc_ireg (cfg);
+ ins->sreg1 = args [0]->dreg;
+ ins->sreg2 = args [1]->dreg;
+ MONO_ADD_INS (cfg->cbb, ins);
+ }
+
+ return ins;
+ }
+ }
+
+ return NULL;
+}
+
+