1 /**
2  * \file
3  * intrinsics for variable sized int/floats
4  *
5  * Author:
6  *   Rodrigo Kumpera (kumpera@gmail.com)
7  *
8  * (C) 2013 Xamarin
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11 
12 #include <config.h>
13 #include <stdio.h>
14 
15 #include "mini.h"
16 #include "ir-emit.h"
17 #include "glib.h"
18 
19 
20 typedef struct {
21 	const char *op_name;
22 	short op_table[4];
23 } IntIntrisic;
24 
25 typedef struct {
26 	short op_index;
27 	short big_stack_type;
28 	short small_stack_type;
29 	short stack_type;
30 	short conv_4_to_8;
31 	short conv_8_to_4;
32 	short move;
33 	short inc_op;
34 	short dec_op;
35 	short store_op;
36 	short compare_op;
37 } MagicTypeInfo;
38 
39 
40 #if SIZEOF_VOID_P == 8
41 #define OP_PT_ADD OP_LADD
42 #define OP_PT_SUB OP_LSUB
43 #define OP_PT_MUL OP_LMUL
44 #define OP_PT_DIV OP_LDIV
45 #define OP_PT_REM OP_LREM
46 #define OP_PT_NEG OP_LNEG
47 #define OP_PT_AND OP_LAND
48 #define OP_PT_OR OP_LOR
49 #define OP_PT_XOR OP_LXOR
50 #define OP_PT_NOT OP_LNOT
51 #define OP_PT_SHL OP_LSHL
52 #define OP_PT_SHR OP_LSHR
53 
54 #define OP_PT_DIV_UN OP_LDIV_UN
55 #define OP_PT_REM_UN OP_LREM_UN
56 #define OP_PT_SHR_UN OP_LSHR_UN
57 
58 #define OP_PT_ADD_IMM OP_LADD_IMM
59 #define OP_PT_SUB_IMM OP_LSUB_IMM
60 
61 #define OP_PT_STORE_FP_MEMBASE_REG OP_STORER8_MEMBASE_REG
62 
63 #define OP_PCOMPARE OP_LCOMPARE
64 
65 #else
66 #define OP_PT_ADD OP_IADD
67 #define OP_PT_SUB OP_ISUB
68 #define OP_PT_MUL OP_IMUL
69 #define OP_PT_DIV OP_IDIV
70 #define OP_PT_REM OP_IREM
71 #define OP_PT_NEG OP_INEG
72 #define OP_PT_AND OP_IAND
73 #define OP_PT_OR OP_IOR
74 #define OP_PT_XOR OP_IXOR
75 #define OP_PT_NOT OP_INOT
76 #define OP_PT_SHL OP_ISHL
77 #define OP_PT_SHR OP_ISHR
78 
79 #define OP_PT_DIV_UN OP_IDIV_UN
80 #define OP_PT_REM_UN OP_IREM_UN
81 #define OP_PT_SHR_UN OP_ISHR_UN
82 
83 #define OP_PT_ADD_IMM OP_IADD_IMM
84 #define OP_PT_SUB_IMM OP_ISUB_IMM
85 
86 #define OP_PT_STORE_FP_MEMBASE_REG OP_STORER4_MEMBASE_REG
87 
88 #define OP_PCOMPARE OP_ICOMPARE
89 
90 #endif
91 
92 static const IntIntrisic int_binop[] = {
93 	{ "op_Addition", { OP_PT_ADD, OP_PT_ADD, OP_FADD, OP_RADD } },
94 	{ "op_Subtraction", { OP_PT_SUB, OP_PT_SUB, OP_FSUB, OP_RSUB } },
95 	{ "op_Multiply", { OP_PT_MUL, OP_PT_MUL, OP_FMUL, OP_RMUL } },
96 	{ "op_Division", { OP_PT_DIV, OP_PT_DIV_UN, OP_FDIV, OP_RDIV } },
97 	{ "op_Modulus", { OP_PT_REM, OP_PT_REM_UN, OP_FREM, OP_RREM } },
98 	{ "op_BitwiseAnd", { OP_PT_AND, OP_PT_AND } },
99 	{ "op_BitwiseOr", { OP_PT_OR, OP_PT_OR } },
100 	{ "op_ExclusiveOr", { OP_PT_XOR, OP_PT_XOR } },
101 	{ "op_LeftShift", { OP_PT_SHL, OP_PT_SHL } },
102 	{ "op_RightShift", { OP_PT_SHR, OP_PT_SHR_UN } },
103 };
104 
105 static const IntIntrisic int_unnop[] = {
106 	{ "op_UnaryPlus", { OP_MOVE, OP_MOVE, OP_FMOVE, OP_RMOVE } },
107 	{ "op_UnaryNegation", { OP_PT_NEG, OP_PT_NEG, OP_FNEG, OP_RNEG } },
108 	{ "op_OnesComplement", { OP_PT_NOT, OP_PT_NOT, OP_FNOT, OP_RNOT } },
109 };
110 
111 static const IntIntrisic int_cmpop[] = {
112 	{ "op_Inequality", { OP_ICNEQ, OP_ICNEQ, OP_FCNEQ, OP_RCNEQ } },
113 	{ "op_Equality", { OP_ICEQ, OP_ICEQ, OP_FCEQ, OP_RCEQ } },
114 	{ "op_GreaterThan", { OP_ICGT, OP_ICGT_UN, OP_FCGT, OP_RCGT } },
115 	{ "op_GreaterThanOrEqual", { OP_ICGE, OP_ICGE_UN, OP_FCGE, OP_RCGE } },
116 	{ "op_LessThan", { OP_ICLT, OP_ICLT_UN, OP_FCLT, OP_RCLT } },
117 	{ "op_LessThanOrEqual", { OP_ICLE, OP_ICLE_UN, OP_FCLE, OP_RCLE } },
118 };
119 
120 static const MagicTypeInfo type_info[] = {
121 	//nint
122 	{ 0, STACK_I8, STACK_I4, STACK_PTR, OP_ICONV_TO_I8, OP_LCONV_TO_I4, OP_MOVE, OP_PT_ADD_IMM, OP_PT_SUB_IMM, OP_STORE_MEMBASE_REG, OP_PCOMPARE },
123 	//nuint
124 	{ 1, STACK_I8, STACK_I4, STACK_PTR, OP_ICONV_TO_U8, OP_LCONV_TO_U4, OP_MOVE, OP_PT_ADD_IMM, OP_PT_SUB_IMM, OP_STORE_MEMBASE_REG, OP_PCOMPARE },
125 	//nfloat
126 	{ 2, STACK_R8, STACK_R8, STACK_R8, OP_FCONV_TO_R8, OP_FCONV_TO_R4, OP_FMOVE, 0, 0, OP_PT_STORE_FP_MEMBASE_REG, 0 },
127 };
128 
129 
130 static inline gboolean
type_size(MonoCompile * cfg,MonoType * type)131 type_size (MonoCompile *cfg, MonoType *type)
132 {
133 	if (type->type == MONO_TYPE_I4 || type->type == MONO_TYPE_U4)
134 		return 4;
135 	else if (type->type == MONO_TYPE_I8 || type->type == MONO_TYPE_U8)
136 		return 8;
137 	else if (type->type == MONO_TYPE_R4 && !type->byref && cfg->r4fp)
138 		return 4;
139 	else if (type->type == MONO_TYPE_R8 && !type->byref)
140 		return 8;
141 	return SIZEOF_VOID_P;
142 }
143 
144 #ifndef DISABLE_JIT
145 
146 static gboolean is_int_type (MonoType *t);
147 static gboolean is_float_type (MonoType *t);
148 
149 static MonoInst*
emit_narrow(MonoCompile * cfg,const MagicTypeInfo * info,int sreg)150 emit_narrow (MonoCompile *cfg, const MagicTypeInfo *info, int sreg)
151 {
152 	MonoInst *ins;
153 
154 	MONO_INST_NEW (cfg, ins, info->conv_8_to_4);
155 	ins->sreg1 = sreg;
156 	if (info->conv_8_to_4 == OP_FCONV_TO_R4)
157 		ins->type = cfg->r4_stack_type;
158 	else
159 		ins->type = info->small_stack_type;
160 	ins->dreg = alloc_dreg (cfg, ins->type);
161 	MONO_ADD_INS (cfg->cbb, ins);
162 	return mono_decompose_opcode (cfg, ins);
163 }
164 
165 static MonoInst*
emit_widen(MonoCompile * cfg,const MagicTypeInfo * info,int sreg)166 emit_widen (MonoCompile *cfg, const MagicTypeInfo *info, int sreg)
167 {
168 	MonoInst *ins;
169 
170 	if (cfg->r4fp && info->conv_4_to_8 == OP_FCONV_TO_R8)
171 		MONO_INST_NEW (cfg, ins, OP_RCONV_TO_R8);
172 	else
173 		MONO_INST_NEW (cfg, ins, info->conv_4_to_8);
174 	ins->sreg1 = sreg;
175 	ins->type = info->big_stack_type;
176 	ins->dreg = alloc_dreg (cfg, info->big_stack_type);
177 	MONO_ADD_INS (cfg->cbb, ins);
178 	return mono_decompose_opcode (cfg, ins);
179 }
180 
181 static MonoInst*
emit_intrinsics(MonoCompile * cfg,MonoMethod * cmethod,MonoMethodSignature * fsig,MonoInst ** args,const MagicTypeInfo * info)182 emit_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, const MagicTypeInfo *info)
183 {
184 	int i = 0;
185 	const char *name = cmethod->name;
186 	MonoInst *ins;
187 	int type_index, stack_type;
188 
189 	if (info->op_index == 2 && cfg->r4fp && SIZEOF_VOID_P == 4) {
190 		type_index = 3;
191 		stack_type = STACK_R4;
192 	} else {
193 		type_index = info->op_index;
194 		stack_type = info->stack_type;
195 	}
196 
197 	if (!strcmp ("op_Implicit", name) || !strcmp ("op_Explicit", name)) {
198 		int source_size = type_size (cfg, fsig->params [0]);
199 		int dest_size = type_size (cfg, fsig->ret);
200 
201 		switch (info->big_stack_type) {
202 		case STACK_I8:
203 			if (!is_int_type (fsig->params [0]) || !is_int_type (fsig->ret))
204 				return NULL;
205 			break;
206 		case STACK_R8:
207 			if (!is_float_type (fsig->params [0]) || !is_float_type (fsig->ret))
208 				return NULL;
209 			break;
210 		default:
211 			g_assert_not_reached ();
212 		}
213 
214 		//4 -> 4 or 8 -> 8
215 		if (source_size == dest_size)
216 			return args [0];
217 
218 		//4 -> 8
219 		if (source_size < dest_size)
220 			return emit_widen (cfg, info, args [0]->dreg);
221 
222 		//8 -> 4
223 		return emit_narrow (cfg, info, args [0]->dreg);
224 	}
225 
226 	if (!strcmp (".ctor", name)) {
227 		gboolean is_ldaddr = args [0]->opcode == OP_LDADDR;
228 		int arg0 = args [1]->dreg;
229 		int arg_size = type_size (cfg, fsig->params [0]);
230 
231 		if (arg_size > SIZEOF_VOID_P) //8 -> 4
232 			arg0 = emit_narrow (cfg, info, arg0)->dreg;
233 		else if (arg_size < SIZEOF_VOID_P) //4 -> 8
234 			arg0 = emit_widen (cfg, info, arg0)->dreg;
235 
236 		if (is_ldaddr) { /*Eliminate LDADDR if it's initing a local var*/
237 			int dreg = ((MonoInst*)args [0]->inst_p0)->dreg;
238 			NULLIFY_INS (args [0]);
239 			EMIT_NEW_UNALU (cfg, ins, info->move, dreg, arg0);
240 			cfg->has_indirection = TRUE;
241 		} else {
242 			EMIT_NEW_STORE_MEMBASE (cfg, ins, info->store_op, args [0]->dreg, 0, arg0);
243 		}
244 		return ins;
245 	}
246 
247 	if (!strcmp ("op_Increment", name) || !strcmp ("op_Decrement", name)) {
248 		gboolean inc = !strcmp ("op_Increment", name);
249 		/* FIXME float inc is too complex to bother with*/
250 		//this is broken with ints too
251 		// if (!info->inc_op)
252 			return NULL;
253 
254 		/* We have IR for inc/dec */
255 		MONO_INST_NEW (cfg, ins, inc ? info->inc_op : info->dec_op);
256 		ins->dreg = alloc_dreg (cfg, info->stack_type);
257 		ins->sreg1 = args [0]->dreg;
258 		ins->inst_imm = 1;
259 		ins->type = info->stack_type;
260 		MONO_ADD_INS (cfg->cbb, ins);
261 		return ins;
262 	}
263 
264 	for (i = 0; i < sizeof (int_binop) / sizeof  (IntIntrisic); ++i) {
265 		if (!strcmp (int_binop [i].op_name, name)) {
266 			if (!int_binop [i].op_table [info->op_index])
267 				return NULL;
268 			g_assert (int_binop [i].op_table [type_index]);
269 
270 			MONO_INST_NEW (cfg, ins, int_binop [i].op_table [type_index]);
271 			ins->dreg = alloc_dreg (cfg, stack_type);
272 			ins->sreg1 = args [0]->dreg;
273 	        ins->sreg2 = args [1]->dreg;
274 			ins->type = stack_type;
275 			MONO_ADD_INS (cfg->cbb, ins);
276 			return mono_decompose_opcode (cfg, ins);
277 		}
278 	}
279 
280 	for (i = 0; i < sizeof (int_unnop) / sizeof  (IntIntrisic); ++i) {
281 		if (!strcmp (int_unnop [i].op_name, name)) {
282 			g_assert (int_unnop [i].op_table [type_index]);
283 
284 			MONO_INST_NEW (cfg, ins, int_unnop [i].op_table [type_index]);
285 			ins->dreg = alloc_dreg (cfg, stack_type);
286 			ins->sreg1 = args [0]->dreg;
287 			ins->type = stack_type;
288 			MONO_ADD_INS (cfg->cbb, ins);
289 			return ins;
290 		}
291 	}
292 
293 	for (i = 0; i < sizeof (int_cmpop) / sizeof  (IntIntrisic); ++i) {
294 		if (!strcmp (int_cmpop [i].op_name, name)) {
295 			g_assert (int_cmpop [i].op_table [type_index]);
296 
297 			if (info->compare_op) {
298 				MONO_INST_NEW (cfg, ins, info->compare_op);
299 		        ins->dreg = -1;
300 				ins->sreg1 = args [0]->dreg;
301 		        ins->sreg2 = args [1]->dreg;
302 				MONO_ADD_INS (cfg->cbb, ins);
303 
304 				MONO_INST_NEW (cfg, ins, int_cmpop [i].op_table [type_index]);
305 		        ins->dreg = alloc_preg (cfg);
306 				ins->type = STACK_I4;
307 				MONO_ADD_INS (cfg->cbb, ins);
308 			} else {
309 				MONO_INST_NEW (cfg, ins, int_cmpop [i].op_table [type_index]);
310 				ins->dreg = alloc_ireg (cfg);
311 				ins->sreg1 = args [0]->dreg;
312 		        ins->sreg2 = args [1]->dreg;
313 				MONO_ADD_INS (cfg->cbb, ins);
314 			}
315 
316 			return ins;
317 		}
318 	}
319 
320 	return NULL;
321 }
322 
323 
324 MonoInst*
mono_emit_native_types_intrinsics(MonoCompile * cfg,MonoMethod * cmethod,MonoMethodSignature * fsig,MonoInst ** args)325 mono_emit_native_types_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
326 {
327 	if (mono_class_is_magic_int (cmethod->klass)) {
328 		const char *class_name = cmethod->klass->name;
329 		if (!strcmp ("nint", class_name))
330 			return emit_intrinsics (cfg, cmethod, fsig, args, &type_info [0]);
331 		else
332 			return emit_intrinsics (cfg, cmethod, fsig, args, &type_info [1]);
333 	} else if (mono_class_is_magic_float (cmethod->klass))
334 		return emit_intrinsics (cfg, cmethod, fsig, args, &type_info [2]);
335 
336 	return NULL;
337 }
338 
339 #endif /* !DISABLE_JIT */
340 
341 static inline gboolean
mono_class_is_magic_assembly(MonoClass * klass)342 mono_class_is_magic_assembly (MonoClass *klass)
343 {
344 	if (!klass->image->assembly_name)
345 		return FALSE;
346 	if (!strcmp ("Xamarin.iOS", klass->image->assembly_name))
347 		return TRUE;
348 	if (!strcmp ("Xamarin.Mac", klass->image->assembly_name))
349 		return TRUE;
350 	if (!strcmp ("Xamarin.WatchOS", klass->image->assembly_name))
351 		return TRUE;
352 	/* regression test suite */
353 	if (!strcmp ("builtin-types", klass->image->assembly_name))
354 		return TRUE;
355 	if (!strcmp ("mini_tests", klass->image->assembly_name))
356 		return TRUE;
357 	return FALSE;
358 }
359 
360 gboolean
mono_class_is_magic_int(MonoClass * klass)361 mono_class_is_magic_int (MonoClass *klass)
362 {
363 	static MonoClass *magic_nint_class;
364 	static MonoClass *magic_nuint_class;
365 
366 	if (klass == magic_nint_class)
367 		return TRUE;
368 
369 	if (klass == magic_nuint_class)
370 		return TRUE;
371 
372 	if (magic_nint_class && magic_nuint_class)
373 		return FALSE;
374 
375 	if (!mono_class_is_magic_assembly (klass))
376 		return FALSE;
377 
378 	if (strcmp ("System", klass->name_space) != 0)
379 		return FALSE;
380 
381 	if (strcmp ("nint", klass->name) == 0) {
382 		magic_nint_class = klass;
383 		return TRUE;
384 	}
385 
386 	if (strcmp ("nuint", klass->name) == 0){
387 		magic_nuint_class = klass;
388 		return TRUE;
389 	}
390 	return FALSE;
391 }
392 
393 gboolean
mono_class_is_magic_float(MonoClass * klass)394 mono_class_is_magic_float (MonoClass *klass)
395 {
396 	static MonoClass *magic_nfloat_class;
397 
398 	if (klass == magic_nfloat_class)
399 		return TRUE;
400 
401 	if (magic_nfloat_class)
402 		return FALSE;
403 
404 	if (!mono_class_is_magic_assembly (klass))
405 		return FALSE;
406 
407 	if (strcmp ("System", klass->name_space) != 0)
408 		return FALSE;
409 
410 	if (strcmp ("nfloat", klass->name) == 0) {
411 		magic_nfloat_class = klass;
412 
413 		/* Assert that we are using the matching assembly */
414 		MonoClassField *value_field = mono_class_get_field_from_name (klass, "v");
415 		g_assert (value_field);
416 		MonoType *t = mono_field_get_type (value_field);
417 		MonoType *native = mini_native_type_replace_type (&klass->byval_arg);
418 		if (t->type != native->type)
419 			g_error ("Assembly used for native types '%s' doesn't match this runtime, %s is mapped to %s, expecting %s.\n", klass->image->name, klass->name, mono_type_full_name (t), mono_type_full_name (native));
420 		return TRUE;
421 	}
422 	return FALSE;
423 }
424 
425 static gboolean
is_int_type(MonoType * t)426 is_int_type (MonoType *t)
427 {
428 	if (t->type != MONO_TYPE_I4 && t->type != MONO_TYPE_I8 && t->type != MONO_TYPE_U4 && t->type != MONO_TYPE_U8 && !mono_class_is_magic_int (mono_class_from_mono_type (t)))
429 		return FALSE;
430 	return TRUE;
431 }
432 
433 static gboolean
is_float_type(MonoType * t)434 is_float_type (MonoType *t)
435 {
436 	if (t->type != MONO_TYPE_R4 && t->type != MONO_TYPE_R8 && !mono_class_is_magic_float (mono_class_from_mono_type (t)))
437 		return FALSE;
438 	return TRUE;
439 }
440 
441 MonoType*
mini_native_type_replace_type(MonoType * type)442 mini_native_type_replace_type (MonoType *type)
443 {
444 	MonoClass *klass;
445 
446 	if (type->type != MONO_TYPE_VALUETYPE)
447 		return type;
448 	klass = type->data.klass;
449 
450 	if (mono_class_is_magic_int (klass))
451 		return type->byref ? &mono_defaults.int_class->this_arg : &mono_defaults.int_class->byval_arg;
452 	if (mono_class_is_magic_float (klass))
453 #if SIZEOF_VOID_P == 8
454 		return type->byref ? &mono_defaults.double_class->this_arg : &mono_defaults.double_class->byval_arg;
455 #else
456 		return type->byref ? &mono_defaults.single_class->this_arg : &mono_defaults.single_class->byval_arg;
457 #endif
458 	return type;
459 }
460