2 * magic-types.c: intrinsics for variable sized int/floats
5 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
27 short small_stack_type
;
39 #if SIZEOF_VOID_P == 8
40 #define OP_PT_ADD OP_LADD
41 #define OP_PT_SUB OP_LSUB
42 #define OP_PT_MUL OP_LMUL
43 #define OP_PT_DIV OP_LDIV
44 #define OP_PT_REM OP_LREM
45 #define OP_PT_NEG OP_LNEG
46 #define OP_PT_AND OP_LAND
47 #define OP_PT_OR OP_LOR
48 #define OP_PT_XOR OP_LXOR
49 #define OP_PT_NOT OP_LNOT
50 #define OP_PT_SHL OP_LSHL
51 #define OP_PT_SHR OP_LSHR
53 #define OP_PT_DIV_UN OP_LDIV_UN
54 #define OP_PT_REM_UN OP_LREM_UN
55 #define OP_PT_SHR_UN OP_LSHR_UN
57 #define OP_PT_ADD_IMM OP_LADD_IMM
58 #define OP_PT_SUB_IMM OP_LSUB_IMM
60 #define OP_PT_STORE_FP_MEMBASE_REG OP_STORER8_MEMBASE_REG
62 #define OP_PCOMPARE OP_LCOMPARE
65 #define OP_PT_ADD OP_IADD
66 #define OP_PT_SUB OP_ISUB
67 #define OP_PT_MUL OP_IMUL
68 #define OP_PT_DIV OP_IDIV
69 #define OP_PT_REM OP_IREM
70 #define OP_PT_NEG OP_INEG
71 #define OP_PT_AND OP_IAND
72 #define OP_PT_OR OP_IOR
73 #define OP_PT_XOR OP_IXOR
74 #define OP_PT_NOT OP_INOT
75 #define OP_PT_SHL OP_ISHL
76 #define OP_PT_SHR OP_ISHR
78 #define OP_PT_DIV_UN OP_IDIV_UN
79 #define OP_PT_REM_UN OP_IREM_UN
80 #define OP_PT_SHR_UN OP_ISHR_UN
82 #define OP_PT_ADD_IMM OP_IADD_IMM
83 #define OP_PT_SUB_IMM OP_ISUB_IMM
85 #define OP_PT_STORE_FP_MEMBASE_REG OP_STORER4_MEMBASE_REG
87 #define OP_PCOMPARE OP_ICOMPARE
91 static const IntIntrisic int_binop
[] = {
92 { "op_Addition", { OP_PT_ADD
, OP_PT_ADD
, OP_FADD
, OP_RADD
} },
93 { "op_Subtraction", { OP_PT_SUB
, OP_PT_SUB
, OP_FSUB
, OP_RSUB
} },
94 { "op_Multiply", { OP_PT_MUL
, OP_PT_MUL
, OP_FMUL
, OP_RMUL
} },
95 { "op_Division", { OP_PT_DIV
, OP_PT_DIV_UN
, OP_FDIV
, OP_RDIV
} },
96 { "op_Modulus", { OP_PT_REM
, OP_PT_REM_UN
, OP_FREM
, OP_RREM
} },
97 { "op_BitwiseAnd", { OP_PT_AND
, OP_PT_AND
} },
98 { "op_BitwiseOr", { OP_PT_OR
, OP_PT_OR
} },
99 { "op_ExclusiveOr", { OP_PT_XOR
, OP_PT_XOR
} },
100 { "op_LeftShift", { OP_PT_SHL
, OP_PT_SHL
} },
101 { "op_RightShift", { OP_PT_SHR
, OP_PT_SHR_UN
} },
104 static const IntIntrisic int_unnop
[] = {
105 { "op_UnaryPlus", { OP_MOVE
, OP_MOVE
, OP_FMOVE
, OP_RMOVE
} },
106 { "op_UnaryNegation", { OP_PT_NEG
, OP_PT_NEG
, OP_FNEG
, OP_RNEG
} },
107 { "op_OnesComplement", { OP_PT_NOT
, OP_PT_NOT
, OP_FNOT
, OP_RNOT
} },
110 static const IntIntrisic int_cmpop
[] = {
111 { "op_Inequality", { OP_ICNEQ
, OP_ICNEQ
, OP_FCNEQ
, OP_RCNEQ
} },
112 { "op_Equality", { OP_ICEQ
, OP_ICEQ
, OP_FCEQ
, OP_RCEQ
} },
113 { "op_GreaterThan", { OP_ICGT
, OP_ICGT_UN
, OP_FCGT
, OP_RCGT
} },
114 { "op_GreaterThanOrEqual", { OP_ICGE
, OP_ICGE_UN
, OP_FCGE
, OP_RCGE
} },
115 { "op_LessThan", { OP_ICLT
, OP_ICLT_UN
, OP_FCLT
, OP_RCLT
} },
116 { "op_LessThanOrEqual", { OP_ICLE
, OP_ICLE_UN
, OP_FCLE
, OP_RCLE
} },
119 static const MagicTypeInfo type_info
[] = {
121 { 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 { 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 { 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 },
128 static inline gboolean
mono_class_is_magic_int (MonoClass
*klass
);
129 static inline gboolean
mono_class_is_magic_float (MonoClass
*klass
);
132 static inline gboolean
133 type_size (MonoCompile
*cfg
, MonoType
*type
)
135 if (type
->type
== MONO_TYPE_I4
|| type
->type
== MONO_TYPE_U4
)
137 else if (type
->type
== MONO_TYPE_I8
|| type
->type
== MONO_TYPE_U8
)
139 else if (type
->type
== MONO_TYPE_R4
&& !type
->byref
&& cfg
->r4fp
)
141 else if (type
->type
== MONO_TYPE_R8
&& !type
->byref
)
143 return SIZEOF_VOID_P
;
148 static gboolean
is_int_type (MonoType
*t
);
149 static gboolean
is_float_type (MonoType
*t
);
152 emit_narrow (MonoCompile
*cfg
, const MagicTypeInfo
*info
, int sreg
)
156 MONO_INST_NEW (cfg
, ins
, info
->conv_8_to_4
);
158 if (info
->conv_8_to_4
== OP_FCONV_TO_R4
)
159 ins
->type
= cfg
->r4_stack_type
;
161 ins
->type
= info
->small_stack_type
;
162 ins
->dreg
= alloc_dreg (cfg
, ins
->type
);
163 MONO_ADD_INS (cfg
->cbb
, ins
);
164 return mono_decompose_opcode (cfg
, ins
);
168 emit_widen (MonoCompile
*cfg
, const MagicTypeInfo
*info
, int sreg
)
172 if (cfg
->r4fp
&& info
->conv_4_to_8
== OP_FCONV_TO_R8
)
173 MONO_INST_NEW (cfg
, ins
, OP_RCONV_TO_R8
);
175 MONO_INST_NEW (cfg
, ins
, info
->conv_4_to_8
);
177 ins
->type
= info
->big_stack_type
;
178 ins
->dreg
= alloc_dreg (cfg
, info
->big_stack_type
);
179 MONO_ADD_INS (cfg
->cbb
, ins
);
180 return mono_decompose_opcode (cfg
, ins
);
184 emit_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
, const MagicTypeInfo
*info
)
187 const char *name
= cmethod
->name
;
189 int type_index
, stack_type
;
191 if (info
->op_index
== 2 && cfg
->r4fp
&& SIZEOF_VOID_P
== 4) {
193 stack_type
= STACK_R4
;
195 type_index
= info
->op_index
;
196 stack_type
= info
->stack_type
;
199 if (!strcmp ("op_Implicit", name
) || !strcmp ("op_Explicit", name
)) {
200 int source_size
= type_size (cfg
, fsig
->params
[0]);
201 int dest_size
= type_size (cfg
, fsig
->ret
);
203 switch (info
->big_stack_type
) {
205 if (!is_int_type (fsig
->params
[0]) || !is_int_type (fsig
->ret
))
209 if (!is_float_type (fsig
->params
[0]) || !is_float_type (fsig
->ret
))
213 g_assert_not_reached ();
217 if (source_size
== dest_size
)
221 if (source_size
< dest_size
)
222 return emit_widen (cfg
, info
, args
[0]->dreg
);
225 return emit_narrow (cfg
, info
, args
[0]->dreg
);
228 if (!strcmp (".ctor", name
)) {
229 gboolean is_ldaddr
= args
[0]->opcode
== OP_LDADDR
;
230 int arg0
= args
[1]->dreg
;
231 int arg_size
= type_size (cfg
, fsig
->params
[0]);
233 if (arg_size
> SIZEOF_VOID_P
) //8 -> 4
234 arg0
= emit_narrow (cfg
, info
, arg0
)->dreg
;
235 else if (arg_size
< SIZEOF_VOID_P
) //4 -> 8
236 arg0
= emit_widen (cfg
, info
, arg0
)->dreg
;
238 if (is_ldaddr
) { /*Eliminate LDADDR if it's initing a local var*/
239 int dreg
= ((MonoInst
*)args
[0]->inst_p0
)->dreg
;
240 NULLIFY_INS (args
[0]);
241 EMIT_NEW_UNALU (cfg
, ins
, info
->move
, dreg
, arg0
);
242 cfg
->has_indirection
= TRUE
;
244 EMIT_NEW_STORE_MEMBASE (cfg
, ins
, info
->store_op
, args
[0]->dreg
, 0, arg0
);
249 if (!strcmp ("op_Increment", name
) || !strcmp ("op_Decrement", name
)) {
250 gboolean inc
= !strcmp ("op_Increment", name
);
251 /* FIXME float inc is too complex to bother with*/
252 //this is broken with ints too
253 // if (!info->inc_op)
256 /* We have IR for inc/dec */
257 MONO_INST_NEW (cfg
, ins
, inc
? info
->inc_op
: info
->dec_op
);
258 ins
->dreg
= alloc_dreg (cfg
, info
->stack_type
);
259 ins
->sreg1
= args
[0]->dreg
;
261 ins
->type
= info
->stack_type
;
262 MONO_ADD_INS (cfg
->cbb
, ins
);
266 for (i
= 0; i
< sizeof (int_binop
) / sizeof (IntIntrisic
); ++i
) {
267 if (!strcmp (int_binop
[i
].op_name
, name
)) {
268 if (!int_binop
[i
].op_table
[info
->op_index
])
270 g_assert (int_binop
[i
].op_table
[type_index
]);
272 MONO_INST_NEW (cfg
, ins
, int_binop
[i
].op_table
[type_index
]);
273 ins
->dreg
= alloc_dreg (cfg
, stack_type
);
274 ins
->sreg1
= args
[0]->dreg
;
275 ins
->sreg2
= args
[1]->dreg
;
276 ins
->type
= stack_type
;
277 MONO_ADD_INS (cfg
->cbb
, ins
);
278 return mono_decompose_opcode (cfg
, ins
);
282 for (i
= 0; i
< sizeof (int_unnop
) / sizeof (IntIntrisic
); ++i
) {
283 if (!strcmp (int_unnop
[i
].op_name
, name
)) {
284 g_assert (int_unnop
[i
].op_table
[type_index
]);
286 MONO_INST_NEW (cfg
, ins
, int_unnop
[i
].op_table
[type_index
]);
287 ins
->dreg
= alloc_dreg (cfg
, stack_type
);
288 ins
->sreg1
= args
[0]->dreg
;
289 ins
->type
= stack_type
;
290 MONO_ADD_INS (cfg
->cbb
, ins
);
295 for (i
= 0; i
< sizeof (int_cmpop
) / sizeof (IntIntrisic
); ++i
) {
296 if (!strcmp (int_cmpop
[i
].op_name
, name
)) {
297 g_assert (int_cmpop
[i
].op_table
[type_index
]);
299 if (info
->compare_op
) {
300 MONO_INST_NEW (cfg
, ins
, info
->compare_op
);
302 ins
->sreg1
= args
[0]->dreg
;
303 ins
->sreg2
= args
[1]->dreg
;
304 MONO_ADD_INS (cfg
->cbb
, ins
);
306 MONO_INST_NEW (cfg
, ins
, int_cmpop
[i
].op_table
[type_index
]);
307 ins
->dreg
= alloc_preg (cfg
);
308 ins
->type
= STACK_I4
;
309 MONO_ADD_INS (cfg
->cbb
, ins
);
311 MONO_INST_NEW (cfg
, ins
, int_cmpop
[i
].op_table
[type_index
]);
312 ins
->dreg
= alloc_ireg (cfg
);
313 ins
->sreg1
= args
[0]->dreg
;
314 ins
->sreg2
= args
[1]->dreg
;
315 MONO_ADD_INS (cfg
->cbb
, ins
);
327 mono_emit_native_types_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
329 if (mono_class_is_magic_int (cmethod
->klass
)) {
330 const char *class_name
= cmethod
->klass
->name
;
331 if (!strcmp ("nint", class_name
))
332 return emit_intrinsics (cfg
, cmethod
, fsig
, args
, &type_info
[0]);
334 return emit_intrinsics (cfg
, cmethod
, fsig
, args
, &type_info
[1]);
335 } else if (mono_class_is_magic_float (cmethod
->klass
))
336 return emit_intrinsics (cfg
, cmethod
, fsig
, args
, &type_info
[2]);
341 #endif /* !DISABLE_JIT */
343 static inline gboolean
344 mono_class_is_magic_assembly (MonoClass
*klass
)
346 if (!klass
->image
->assembly_name
)
348 if (!strcmp ("Xamarin.iOS", klass
->image
->assembly_name
))
350 if (!strcmp ("Xamarin.Mac", klass
->image
->assembly_name
))
352 if (!strcmp ("Xamarin.WatchOS", klass
->image
->assembly_name
))
357 static inline gboolean
358 mono_class_is_magic_int (MonoClass
*klass
)
360 static MonoClass
*magic_nint_class
;
361 static MonoClass
*magic_nuint_class
;
363 if (klass
== magic_nint_class
)
366 if (klass
== magic_nuint_class
)
369 if (magic_nint_class
&& magic_nuint_class
)
372 if (!mono_class_is_magic_assembly (klass
))
375 if (strcmp ("System", klass
->name_space
) != 0)
378 if (strcmp ("nint", klass
->name
) == 0) {
379 magic_nint_class
= klass
;
383 if (strcmp ("nuint", klass
->name
) == 0){
384 magic_nuint_class
= klass
;
390 static inline gboolean
391 mono_class_is_magic_float (MonoClass
*klass
)
393 static MonoClass
*magic_nfloat_class
;
395 if (klass
== magic_nfloat_class
)
398 if (magic_nfloat_class
)
401 if (!mono_class_is_magic_assembly (klass
))
404 if (strcmp ("System", klass
->name_space
) != 0)
407 if (strcmp ("nfloat", klass
->name
) == 0) {
408 magic_nfloat_class
= klass
;
410 /* Assert that we are using the matching assembly */
411 MonoClassField
*value_field
= mono_class_get_field_from_name (klass
, "v");
412 g_assert (value_field
);
413 MonoType
*t
= mono_field_get_type (value_field
);
414 MonoType
*native
= mini_native_type_replace_type (&klass
->byval_arg
);
415 if (t
->type
!= native
->type
)
416 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
));
423 is_int_type (MonoType
*t
)
425 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
)))
431 is_float_type (MonoType
*t
)
433 if (t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
&& !mono_class_is_magic_float (mono_class_from_mono_type (t
)))
439 mini_native_type_replace_type (MonoType
*type
)
443 if (type
->type
!= MONO_TYPE_VALUETYPE
)
445 klass
= type
->data
.klass
;
447 if (mono_class_is_magic_int (klass
))
448 return type
->byref
? &mono_defaults
.int_class
->this_arg
: &mono_defaults
.int_class
->byval_arg
;
449 if (mono_class_is_magic_float (klass
))
450 #if SIZEOF_VOID_P == 8
451 return type
->byref
? &mono_defaults
.double_class
->this_arg
: &mono_defaults
.double_class
->byval_arg
;
453 return type
->byref
? &mono_defaults
.single_class
->this_arg
: &mono_defaults
.single_class
->byval_arg
;