Example #1
0
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;
}
Example #2
0
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;
}