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)) { short op_cmp = int_cmpop [i].op_table [type_index]; g_assert (op_cmp); 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, op_cmp); ins->dreg = alloc_preg (cfg); ins->type = STACK_I4; MONO_ADD_INS (cfg->cbb, ins); } else { MONO_INST_NEW (cfg, ins, op_cmp); guint32 fcmp_dreg = ins->dreg = alloc_ireg (cfg); ins->sreg1 = args [0]->dreg; ins->sreg2 = args [1]->dreg; MONO_ADD_INS (cfg->cbb, ins); if (op_cmp == OP_FCLT_UN || op_cmp == OP_FCGT_UN || op_cmp == OP_RCLT_UN || op_cmp == OP_RCGT_UN) { /* we have to negate the result of this comparison: * - op_GreaterThanOrEqual maps to NOT x OP_FCLT_UN / OP_RCLT_UN * - op_LessThanOrEqual maps to NOT x OP_FCGT_UN / OP_RCGT_UN * * this matches generated bytecode by C# when doing the * same operations on float/double. the `_UN` suffix says * that if an operand is NaN, the result is true. If * OP_FCGE/OP_FCLE is used, it is mapped to instructions * on some architectures that don't detect NaN. For * example, on arm64 the condition `eq` doesn't respect * NaN results of a `fcmp` instruction. */ MONO_INST_NEW (cfg, ins, OP_ICOMPARE_IMM); ins->dreg = -1; ins->sreg1 = fcmp_dreg; ins->inst_imm = 0; MONO_ADD_INS (cfg->cbb, ins); MONO_INST_NEW (cfg, ins, OP_CEQ); ins->dreg = alloc_preg (cfg); ins->type = STACK_I4; MONO_ADD_INS (cfg->cbb, ins); } } return ins; } } return NULL; }
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; }