3 * transform CIL into different opcodes for more
4 * efficient interpretation
6 * Written by Bernie Solomon (bernard@ugsolutions.com)
12 #include <mono/metadata/appdomain.h>
13 #include <mono/metadata/class-internals.h>
14 #include <mono/metadata/debug-helpers.h>
15 #include <mono/metadata/exception.h>
16 #include <mono/metadata/exception-internals.h>
17 #include <mono/metadata/mono-endian.h>
18 #include <mono/metadata/marshal.h>
19 #include <mono/metadata/profiler-private.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/mono-basic-block.h>
22 #include <mono/metadata/abi-details.h>
23 #include <mono/metadata/reflection-internals.h>
24 #include <mono/utils/unlocked.h>
25 #include <mono/utils/mono-memory-model.h>
27 #include <mono/mini/mini.h>
28 #include <mono/mini/mini-runtime.h>
31 #include "interp-internals.h"
33 #include "transform.h"
35 MonoInterpStats mono_interp_stats
;
39 #if SIZEOF_VOID_P == 8
40 #define MINT_NEG_P MINT_NEG_I8
41 #define MINT_NOT_P MINT_NOT_I8
43 #define MINT_NEG_FP MINT_NEG_R8
45 #define MINT_ADD_P MINT_ADD_I8
46 #define MINT_SUB_P MINT_SUB_I8
47 #define MINT_MUL_P MINT_MUL_I8
48 #define MINT_DIV_P MINT_DIV_I8
49 #define MINT_DIV_UN_P MINT_DIV_UN_I8
50 #define MINT_REM_P MINT_REM_I8
51 #define MINT_REM_UN_P MINT_REM_UN_I8
52 #define MINT_AND_P MINT_AND_I8
53 #define MINT_OR_P MINT_OR_I8
54 #define MINT_XOR_P MINT_XOR_I8
55 #define MINT_SHL_P MINT_SHL_I8
56 #define MINT_SHR_P MINT_SHR_I8
57 #define MINT_SHR_UN_P MINT_SHR_UN_I8
59 #define MINT_CEQ_P MINT_CEQ_I8
60 #define MINT_CNE_P MINT_CNE_I8
61 #define MINT_CLT_P MINT_CLT_I8
62 #define MINT_CLT_UN_P MINT_CLT_UN_I8
63 #define MINT_CGT_P MINT_CGT_I8
64 #define MINT_CGT_UN_P MINT_CGT_UN_I8
65 #define MINT_CLE_P MINT_CLE_I8
66 #define MINT_CLE_UN_P MINT_CLE_UN_I8
67 #define MINT_CGE_P MINT_CGE_I8
68 #define MINT_CGE_UN_P MINT_CGE_UN_I8
70 #define MINT_ADD_FP MINT_ADD_R8
71 #define MINT_SUB_FP MINT_SUB_R8
72 #define MINT_MUL_FP MINT_MUL_R8
73 #define MINT_DIV_FP MINT_DIV_R8
74 #define MINT_REM_FP MINT_REM_R8
76 #define MINT_CNE_FP MINT_CNE_R8
77 #define MINT_CEQ_FP MINT_CEQ_R8
78 #define MINT_CGT_FP MINT_CGT_R8
79 #define MINT_CGE_FP MINT_CGE_R8
80 #define MINT_CLT_FP MINT_CLT_R8
81 #define MINT_CLE_FP MINT_CLE_R8
83 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I8
86 #define MINT_NEG_P MINT_NEG_I4
87 #define MINT_NOT_P MINT_NOT_I4
89 #define MINT_NEG_FP MINT_NEG_R4
91 #define MINT_ADD_P MINT_ADD_I4
92 #define MINT_SUB_P MINT_SUB_I4
93 #define MINT_MUL_P MINT_MUL_I4
94 #define MINT_DIV_P MINT_DIV_I4
95 #define MINT_DIV_UN_P MINT_DIV_UN_I4
96 #define MINT_REM_P MINT_REM_I4
97 #define MINT_REM_UN_P MINT_REM_UN_I4
98 #define MINT_AND_P MINT_AND_I4
99 #define MINT_OR_P MINT_OR_I4
100 #define MINT_XOR_P MINT_XOR_I4
101 #define MINT_SHL_P MINT_SHL_I4
102 #define MINT_SHR_P MINT_SHR_I4
103 #define MINT_SHR_UN_P MINT_SHR_UN_I4
105 #define MINT_CEQ_P MINT_CEQ_I4
106 #define MINT_CNE_P MINT_CNE_I4
107 #define MINT_CLT_P MINT_CLT_I4
108 #define MINT_CLT_UN_P MINT_CLT_UN_I4
109 #define MINT_CGT_P MINT_CGT_I4
110 #define MINT_CGT_UN_P MINT_CGT_UN_I4
111 #define MINT_CLE_P MINT_CLE_I4
112 #define MINT_CLE_UN_P MINT_CLE_UN_I4
113 #define MINT_CGE_P MINT_CGE_I4
114 #define MINT_CGE_UN_P MINT_CGE_UN_I4
116 #define MINT_ADD_FP MINT_ADD_R4
117 #define MINT_SUB_FP MINT_SUB_R4
118 #define MINT_MUL_FP MINT_MUL_R4
119 #define MINT_DIV_FP MINT_DIV_R4
120 #define MINT_REM_FP MINT_REM_R4
122 #define MINT_CNE_FP MINT_CNE_R4
123 #define MINT_CEQ_FP MINT_CEQ_R4
124 #define MINT_CGT_FP MINT_CGT_R4
125 #define MINT_CGE_FP MINT_CGE_R4
126 #define MINT_CLT_FP MINT_CLT_R4
127 #define MINT_CLE_FP MINT_CLE_R4
129 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I4
133 const gchar
*op_name
;
137 // static const MagicIntrinsic int_binop[] = {
139 static const MagicIntrinsic int_unnop
[] = {
140 { "op_UnaryPlus", {MINT_NOP
, MINT_NOP
, MINT_NOP
}},
141 { "op_UnaryNegation", {MINT_NEG_P
, MINT_NEG_P
, MINT_NEG_FP
}},
142 { "op_OnesComplement", {MINT_NOT_P
, MINT_NOT_P
, MINT_NIY
}}
145 static const MagicIntrinsic int_binop
[] = {
146 { "op_Addition", {MINT_ADD_P
, MINT_ADD_P
, MINT_ADD_FP
}},
147 { "op_Subtraction", {MINT_SUB_P
, MINT_SUB_P
, MINT_SUB_FP
}},
148 { "op_Multiply", {MINT_MUL_P
, MINT_MUL_P
, MINT_MUL_FP
}},
149 { "op_Division", {MINT_DIV_P
, MINT_DIV_UN_P
, MINT_DIV_FP
}},
150 { "op_Modulus", {MINT_REM_P
, MINT_REM_UN_P
, MINT_REM_FP
}},
151 { "op_BitwiseAnd", {MINT_AND_P
, MINT_AND_P
, MINT_NIY
}},
152 { "op_BitwiseOr", {MINT_OR_P
, MINT_OR_P
, MINT_NIY
}},
153 { "op_ExclusiveOr", {MINT_XOR_P
, MINT_XOR_P
, MINT_NIY
}},
154 { "op_LeftShift", {MINT_SHL_P
, MINT_SHL_P
, MINT_NIY
}},
155 { "op_RightShift", {MINT_SHR_P
, MINT_SHR_UN_P
, MINT_NIY
}},
158 static const MagicIntrinsic int_cmpop
[] = {
159 { "op_Inequality", {MINT_CNE_P
, MINT_CNE_P
, MINT_CNE_FP
}},
160 { "op_Equality", {MINT_CEQ_P
, MINT_CEQ_P
, MINT_CEQ_FP
}},
161 { "op_GreaterThan", {MINT_CGT_P
, MINT_CGT_UN_P
, MINT_CGT_FP
}},
162 { "op_GreaterThanOrEqual", {MINT_CGE_P
, MINT_CGE_UN_P
, MINT_CGE_FP
}},
163 { "op_LessThan", {MINT_CLT_P
, MINT_CLT_UN_P
, MINT_CLT_FP
}},
164 { "op_LessThanOrEqual", {MINT_CLE_P
, MINT_CLE_UN_P
, MINT_CLE_FP
}}
167 static const char *stack_type_string
[] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " };
169 static int stack_type
[] = {
170 STACK_TYPE_I4
, /*I1*/
171 STACK_TYPE_I4
, /*U1*/
172 STACK_TYPE_I4
, /*I2*/
173 STACK_TYPE_I4
, /*U2*/
174 STACK_TYPE_I4
, /*I4*/
175 STACK_TYPE_I8
, /*I8*/
176 STACK_TYPE_R4
, /*R4*/
177 STACK_TYPE_R8
, /*R8*/
183 static gboolean
generate_code (TransformData
*td
, MonoMethod
*method
, MonoMethodHeader
*header
, MonoGenericContext
*generic_context
, MonoError
*error
);
186 interp_new_ins (TransformData
*td
, guint16 opcode
, int len
)
188 InterpInst
*new_inst
;
189 // Size of data region of instruction is length of instruction minus 1 (the opcode slot)
190 new_inst
= mono_mempool_alloc0 (td
->mempool
, sizeof (InterpInst
) + sizeof (guint16
) * ((len
> 0) ? (len
- 1) : 0));
191 new_inst
->opcode
= opcode
;
192 new_inst
->il_offset
= td
->current_il_offset
;
196 // This version need to be used with switch opcode, which doesn't have constant length
198 interp_add_ins_explicit (TransformData
*td
, guint16 opcode
, int len
)
200 InterpInst
*new_inst
= interp_new_ins (td
, opcode
, len
);
201 new_inst
->prev
= td
->last_ins
;
203 td
->last_ins
->next
= new_inst
;
205 td
->first_ins
= new_inst
;
206 td
->last_ins
= new_inst
;
211 interp_add_ins (TransformData
*td
, guint16 opcode
)
213 return interp_add_ins_explicit (td
, opcode
, mono_interp_oplen
[opcode
]);
216 // This instruction will have the il_offset of the previous instruction
218 interp_insert_ins (TransformData
*td
, InterpInst
*prev_ins
, guint16 opcode
)
220 InterpInst
*new_inst
= interp_new_ins (td
, opcode
, mono_interp_oplen
[opcode
]);
222 new_inst
->il_offset
= prev_ins
->il_offset
;
224 new_inst
->prev
= prev_ins
;
225 new_inst
->next
= prev_ins
->next
;
226 prev_ins
->next
= new_inst
;
228 if (new_inst
->next
== NULL
)
229 td
->last_ins
= new_inst
;
231 new_inst
->next
->prev
= new_inst
;
237 interp_clear_ins (TransformData
*td
, InterpInst
*ins
)
239 // Clearing instead of removing from the list makes everything easier.
240 // We don't change structure of the instruction list, we don't need
241 // to worry about updating the il_offset, or whether this instruction
242 // was at the start of a basic block etc.
243 ins
->opcode
= MINT_NOP
;
247 interp_prev_ins (InterpInst
*ins
)
250 while (ins
&& ins
->opcode
== MINT_NOP
)
255 #define CHECK_STACK(td, n) \
257 int stack_size = (td)->sp - (td)->stack; \
258 if (stack_size < (n)) \
259 g_warning ("%s.%s: not enough values (%d < %d) on stack at %04x", \
260 m_class_get_name ((td)->method->klass), (td)->method->name, \
261 stack_size, n, (td)->ip - (td)->il_code); \
264 #define ENSURE_I4(td, sp_off) \
266 if ((td)->sp [-sp_off].type == STACK_TYPE_I8) \
267 interp_add_ins (td, sp_off == 1 ? MINT_CONV_I4_I8 : MINT_CONV_I4_I8_SP); \
270 #define CHECK_TYPELOAD(klass) \
272 if (!(klass) || mono_class_has_failure (klass)) { \
273 mono_error_set_for_class_failure (error, klass); \
278 #if NO_UNALIGNED_ACCESS
279 #define WRITE32(ip, v) \
281 * (ip) = * (guint16 *)(v); \
282 * ((ip) + 1) = * ((guint16 *)(v) + 1); \
286 #define WRITE32_INS(ins, index, v) \
288 (ins)->data [index] = * (guint16 *)(v); \
289 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
292 #define WRITE64(ins, v) \
294 *((ins) + 0) = * ((guint16 *)(v) + 0); \
295 *((ins) + 1) = * ((guint16 *)(v) + 1); \
296 *((ins) + 2) = * ((guint16 *)(v) + 2); \
297 *((ins) + 3) = * ((guint16 *)(v) + 3); \
300 #define WRITE64_INS(ins, index, v) \
302 (ins)->data [index] = * (guint16 *)(v); \
303 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
304 (ins)->data [index + 2] = * ((guint16 *)(v) + 2); \
305 (ins)->data [index + 3] = * ((guint16 *)(v) + 3); \
308 #define WRITE32(ip, v) \
310 * (guint32*)(ip) = * (guint32 *)(v); \
313 #define WRITE32_INS(ins, index, v) \
315 * (guint32 *)(&(ins)->data [index]) = * (guint32 *)(v); \
318 #define WRITE64(ip, v) \
320 * (guint64*)(ip) = * (guint64 *)(v); \
323 #define WRITE64_INS(ins, index, v) \
325 * (guint64 *)(&(ins)->data [index]) = * (guint64 *)(v); \
332 handle_branch (TransformData
*td
, int short_op
, int long_op
, int offset
)
334 int shorten_branch
= 0;
335 int target
= td
->ip
+ offset
- td
->il_code
;
336 if (target
< 0 || target
>= td
->code_size
)
337 g_assert_not_reached ();
338 /* Add exception checkpoint or safepoint for backward branches */
340 if (mono_threads_are_safepoints_enabled ())
341 interp_add_ins (td
, MINT_SAFEPOINT
);
343 interp_add_ins (td
, MINT_CHECKPOINT
);
345 if (offset
> 0 && td
->stack_height
[target
] < 0) {
346 td
->stack_height
[target
] = td
->sp
- td
->stack
;
347 if (td
->stack_height
[target
] > 0)
348 td
->stack_state
[target
] = (StackInfo
*)g_memdup (td
->stack
, td
->stack_height
[target
] * sizeof (td
->stack
[0]));
349 td
->vt_stack_size
[target
] = td
->vt_sp
;
352 if (td
->header
->code_size
<= 25000) /* FIX to be precise somehow? */
355 if (shorten_branch
) {
356 interp_add_ins (td
, short_op
);
357 td
->last_ins
->data
[0] = (guint16
) target
;
359 interp_add_ins (td
, long_op
);
360 WRITE32_INS (td
->last_ins
, 0, &target
);
365 one_arg_branch(TransformData
*td
, int mint_op
, int offset
)
367 int type
= td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
? STACK_TYPE_I
: td
->sp
[-1].type
;
368 int long_op
= mint_op
+ type
- STACK_TYPE_I4
;
369 int short_op
= long_op
+ MINT_BRFALSE_I4_S
- MINT_BRFALSE_I4
;
372 handle_branch (td
, short_op
, long_op
, offset
);
376 two_arg_branch(TransformData
*td
, int mint_op
, int offset
)
378 int type1
= td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
? STACK_TYPE_I
: td
->sp
[-1].type
;
379 int type2
= td
->sp
[-2].type
== STACK_TYPE_O
|| td
->sp
[-2].type
== STACK_TYPE_MP
? STACK_TYPE_I
: td
->sp
[-2].type
;
380 int long_op
= mint_op
+ type1
- STACK_TYPE_I4
;
381 int short_op
= long_op
+ MINT_BEQ_I4_S
- MINT_BEQ_I4
;
383 if (type1
== STACK_TYPE_I4
&& type2
== STACK_TYPE_I8
) {
384 // The il instruction starts with the actual branch, and not with the conversion opcodes
385 interp_insert_ins (td
, td
->last_ins
, MINT_CONV_I8_I4
);
386 } else if (type1
== STACK_TYPE_I8
&& type2
== STACK_TYPE_I4
) {
387 interp_insert_ins (td
, td
->last_ins
, MINT_CONV_I8_I4_SP
);
388 } else if (type1
== STACK_TYPE_R4
&& type2
== STACK_TYPE_R8
) {
389 interp_insert_ins (td
, td
->last_ins
, MINT_CONV_R8_R4
);
390 } else if (type1
== STACK_TYPE_R8
&& type2
== STACK_TYPE_R4
) {
391 interp_insert_ins (td
, td
->last_ins
, MINT_CONV_R8_R4_SP
);
392 } else if (type1
!= type2
) {
393 g_warning("%s.%s: branch type mismatch %d %d",
394 m_class_get_name (td
->method
->klass
), td
->method
->name
,
395 td
->sp
[-1].type
, td
->sp
[-2].type
);
398 handle_branch (td
, short_op
, long_op
, offset
);
402 unary_arith_op(TransformData
*td
, int mint_op
)
404 int op
= mint_op
+ td
->sp
[-1].type
- STACK_TYPE_I4
;
406 interp_add_ins (td
, op
);
410 binary_arith_op(TransformData
*td
, int mint_op
)
412 int type1
= td
->sp
[-2].type
;
413 int type2
= td
->sp
[-1].type
;
415 #if SIZEOF_VOID_P == 8
416 if ((type1
== STACK_TYPE_MP
|| type1
== STACK_TYPE_I8
) && type2
== STACK_TYPE_I4
) {
417 interp_add_ins (td
, MINT_CONV_I8_I4
);
418 type2
= STACK_TYPE_I8
;
420 if (type1
== STACK_TYPE_I4
&& (type2
== STACK_TYPE_MP
|| type2
== STACK_TYPE_I8
)) {
421 interp_add_ins (td
, MINT_CONV_I8_I4_SP
);
422 type1
= STACK_TYPE_I8
;
423 td
->sp
[-2].type
= STACK_TYPE_I8
;
426 if (type1
== STACK_TYPE_R8
&& type2
== STACK_TYPE_R4
) {
427 interp_add_ins (td
, MINT_CONV_R8_R4
);
428 type2
= STACK_TYPE_R8
;
430 if (type1
== STACK_TYPE_R4
&& type2
== STACK_TYPE_R8
) {
431 interp_add_ins (td
, MINT_CONV_R8_R4_SP
);
432 type1
= STACK_TYPE_R8
;
433 td
->sp
[-2].type
= STACK_TYPE_R8
;
435 if (type1
== STACK_TYPE_MP
)
436 type1
= STACK_TYPE_I
;
437 if (type2
== STACK_TYPE_MP
)
438 type2
= STACK_TYPE_I
;
439 if (type1
!= type2
) {
440 g_warning("%s.%s: %04x arith type mismatch %s %d %d",
441 m_class_get_name (td
->method
->klass
), td
->method
->name
,
442 td
->ip
- td
->il_code
, mono_interp_opname (mint_op
), type1
, type2
);
444 op
= mint_op
+ type1
- STACK_TYPE_I4
;
446 interp_add_ins (td
, op
);
451 shift_op(TransformData
*td
, int mint_op
)
453 int op
= mint_op
+ td
->sp
[-2].type
- STACK_TYPE_I4
;
455 if (td
->sp
[-1].type
!= STACK_TYPE_I4
) {
456 g_warning("%s.%s: shift type mismatch %d",
457 m_class_get_name (td
->method
->klass
), td
->method
->name
,
460 interp_add_ins (td
, op
);
465 can_store (int st_value
, int vt_value
)
467 if (st_value
== STACK_TYPE_O
|| st_value
== STACK_TYPE_MP
)
468 st_value
= STACK_TYPE_I
;
469 if (vt_value
== STACK_TYPE_O
|| vt_value
== STACK_TYPE_MP
)
470 vt_value
= STACK_TYPE_I
;
471 return st_value
== vt_value
;
474 #define SET_SIMPLE_TYPE(s, ty) \
481 #define SET_TYPE(s, ty, k) \
488 #define REALLOC_STACK(td, sppos) \
490 (td)->stack_capacity *= 2; \
491 (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
492 (td)->sp = (td)->stack + (sppos); \
495 #define PUSH_SIMPLE_TYPE(td, ty) \
499 sp_height = (td)->sp - (td)->stack; \
500 if (sp_height > (td)->max_stack_height) \
501 (td)->max_stack_height = sp_height; \
502 if (sp_height > (td)->stack_capacity) \
503 REALLOC_STACK(td, sp_height); \
504 SET_SIMPLE_TYPE((td)->sp - 1, ty); \
507 #define PUSH_TYPE(td, ty, k) \
511 sp_height = (td)->sp - (td)->stack; \
512 if (sp_height > (td)->max_stack_height) \
513 (td)->max_stack_height = sp_height; \
514 if (sp_height > (td)->stack_capacity) \
515 REALLOC_STACK(td, sp_height); \
516 SET_TYPE((td)->sp - 1, ty, k); \
520 move_stack (TransformData
*td
, int start
, int amount
)
522 int sp_height
= td
->sp
- td
->stack
;
523 int to_move
= sp_height
- start
;
528 if (sp_height
> td
->max_stack_height
)
529 td
->max_stack_height
= sp_height
;
530 if (sp_height
> td
->stack_capacity
)
531 REALLOC_STACK (td
, sp_height
);
533 g_assert (td
->sp
>= td
->stack
);
537 memmove (td
->stack
+ start
+ amount
, td
->stack
+ start
, to_move
* sizeof (StackInfo
));
540 #define PUSH_VT(td, size) \
542 (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
543 if ((td)->vt_sp > (td)->max_vt_sp) \
544 (td)->max_vt_sp = (td)->vt_sp; \
547 #define POP_VT(td, size) \
549 (td)->vt_sp -= ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
553 get_arg_type_exact (TransformData
*td
, int n
, int *mt
)
556 gboolean hasthis
= mono_method_signature_internal (td
->method
)->hasthis
;
558 if (hasthis
&& n
== 0)
559 type
= m_class_get_byval_arg (td
->method
->klass
);
561 type
= mono_method_signature_internal (td
->method
)->params
[n
- !!hasthis
];
564 *mt
= mint_type (type
);
570 load_arg(TransformData
*td
, int n
)
573 MonoClass
*klass
= NULL
;
575 gboolean hasthis
= mono_method_signature_internal (td
->method
)->hasthis
;
577 type
= get_arg_type_exact (td
, n
, &mt
);
579 if (mt
== MINT_TYPE_VT
) {
581 klass
= mono_class_from_mono_type_internal (type
);
582 if (mono_method_signature_internal (td
->method
)->pinvoke
)
583 size
= mono_class_native_size (klass
, NULL
);
585 size
= mono_class_value_size (klass
, NULL
);
587 if (hasthis
&& n
== 0) {
589 interp_add_ins (td
, MINT_LDARG_P0
);
593 interp_add_ins (td
, MINT_LDARG_VT
);
594 td
->last_ins
->data
[0] = n
;
595 WRITE32_INS (td
->last_ins
, 1, &size
);
598 if ((hasthis
|| mt
== MINT_TYPE_P
) && n
== 0) {
600 interp_add_ins (td
, MINT_LDARG_P0
);
602 interp_add_ins (td
, MINT_LDARG_I1
+ (mt
- MINT_TYPE_I1
));
603 td
->last_ins
->data
[0] = n
;
604 if (mt
== MINT_TYPE_O
)
605 klass
= mono_class_from_mono_type_internal (type
);
608 PUSH_TYPE(td
, stack_type
[mt
], klass
);
612 store_arg(TransformData
*td
, int n
)
618 type
= get_arg_type_exact (td
, n
, &mt
);
620 if (mt
== MINT_TYPE_VT
) {
622 MonoClass
*klass
= mono_class_from_mono_type_internal (type
);
623 if (mono_method_signature_internal (td
->method
)->pinvoke
)
624 size
= mono_class_native_size (klass
, NULL
);
626 size
= mono_class_value_size (klass
, NULL
);
627 interp_add_ins (td
, MINT_STARG_VT
);
628 td
->last_ins
->data
[0] = n
;
629 WRITE32_INS (td
->last_ins
, 1, &size
);
630 if (td
->sp
[-1].type
== STACK_TYPE_VT
)
633 interp_add_ins (td
, MINT_STARG_I1
+ (mt
- MINT_TYPE_I1
));
634 td
->last_ins
->data
[0] = n
;
640 load_local (TransformData
*td
, int local
)
642 MonoType
*type
= td
->locals
[local
].type
;
643 int mt
= td
->locals
[local
].mt
;
644 MonoClass
*klass
= NULL
;
645 if (mt
== MINT_TYPE_VT
) {
646 klass
= mono_class_from_mono_type_internal (type
);
647 gint32 size
= mono_class_value_size (klass
, NULL
);
649 interp_add_ins (td
, MINT_LDLOC_VT
);
650 td
->last_ins
->data
[0] = local
;
651 WRITE32_INS (td
->last_ins
, 1, &size
);
653 g_assert (mt
< MINT_TYPE_VT
);
654 interp_add_ins (td
, MINT_LDLOC_I1
+ (mt
- MINT_TYPE_I1
));
655 td
->last_ins
->data
[0] = local
;
656 if (mt
== MINT_TYPE_O
)
657 klass
= mono_class_from_mono_type_internal (type
);
659 PUSH_TYPE(td
, stack_type
[mt
], klass
);
663 store_local (TransformData
*td
, int local
)
665 MonoType
*type
= td
->locals
[local
].type
;
666 int mt
= td
->locals
[local
].mt
;
668 #if SIZEOF_VOID_P == 8
669 if (td
->sp
[-1].type
== STACK_TYPE_I4
&& stack_type
[mt
] == STACK_TYPE_I8
) {
670 interp_add_ins (td
, MINT_CONV_I8_I4
);
671 td
->sp
[-1].type
= STACK_TYPE_I8
;
674 if (!can_store(td
->sp
[-1].type
, stack_type
[mt
])) {
675 g_warning("%s.%s: Store local stack type mismatch %d %d",
676 m_class_get_name (td
->method
->klass
), td
->method
->name
,
677 stack_type
[mt
], td
->sp
[-1].type
);
679 if (mt
== MINT_TYPE_VT
) {
680 MonoClass
*klass
= mono_class_from_mono_type_internal (type
);
681 gint32 size
= mono_class_value_size (klass
, NULL
);
682 interp_add_ins (td
, MINT_STLOC_VT
);
683 td
->last_ins
->data
[0] = local
;
684 WRITE32_INS (td
->last_ins
, 1, &size
);
685 if (td
->sp
[-1].type
== STACK_TYPE_VT
)
688 g_assert (mt
< MINT_TYPE_VT
);
689 interp_add_ins (td
, MINT_STLOC_I1
+ (mt
- MINT_TYPE_I1
));
690 td
->last_ins
->data
[0] = local
;
695 #define SIMPLE_OP(td, op) \
697 interp_add_ins (td, op); \
702 get_data_item_index (TransformData
*td
, void *ptr
)
704 gpointer p
= g_hash_table_lookup (td
->data_hash
, ptr
);
707 return GPOINTER_TO_UINT (p
) - 1;
708 if (td
->max_data_items
== td
->n_data_items
) {
709 td
->max_data_items
= td
->n_data_items
== 0 ? 16 : 2 * td
->max_data_items
;
710 td
->data_items
= (gpointer
*)g_realloc (td
->data_items
, td
->max_data_items
* sizeof(td
->data_items
[0]));
712 index
= td
->n_data_items
;
713 td
->data_items
[index
] = ptr
;
715 g_hash_table_insert (td
->data_hash
, ptr
, GUINT_TO_POINTER (index
+ 1));
720 mono_interp_jit_call_supported (MonoMethod
*method
, MonoMethodSignature
*sig
)
724 if (sig
->param_count
> 6)
728 if (method
->flags
& METHOD_ATTRIBUTE_PINVOKE_IMPL
)
730 if (method
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
)
732 if (method
->is_inflated
)
734 if (method
->string_ctor
)
736 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
739 if (mono_aot_only
&& m_class_get_image (method
->klass
)->aot_module
&& !(method
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
)) {
741 gpointer addr
= mono_jit_compile_method_jit_only (method
, error
);
742 if (addr
&& is_ok (error
))
746 for (l
= mono_interp_jit_classes
; l
; l
= l
->next
) {
747 const char *class_name
= (const char*)l
->data
;
749 if (!strcmp (m_class_get_name (method
->klass
), class_name
))
757 #ifdef ENABLE_EXPERIMENT_TIERED
759 jit_call2_supported (MonoMethod
*method
, MonoMethodSignature
*sig
)
761 if (sig
->param_count
> 6)
765 if (method
->flags
& METHOD_ATTRIBUTE_PINVOKE_IMPL
)
767 if (method
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
)
769 if (method
->is_inflated
)
771 if (method
->string_ctor
)
778 static int mono_class_get_magic_index (MonoClass
*k
)
780 if (mono_class_is_magic_int (k
))
781 return !strcmp ("nint", m_class_get_name (k
)) ? 0 : 1;
783 if (mono_class_is_magic_float (k
))
790 interp_generate_mae_throw (TransformData
*td
, MonoMethod
*method
, MonoMethod
*target_method
)
792 MonoJitICallInfo
*info
= &mono_get_jit_icall_info ()->mono_throw_method_access
;
794 /* Inject code throwing MethodAccessException */
795 interp_add_ins (td
, MINT_MONO_LDPTR
);
796 td
->last_ins
->data
[0] = get_data_item_index (td
, method
);
797 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
799 interp_add_ins (td
, MINT_MONO_LDPTR
);
800 td
->last_ins
->data
[0] = get_data_item_index (td
, target_method
);
801 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
803 interp_add_ins (td
, MINT_ICALL_PP_V
);
804 td
->last_ins
->data
[0] = get_data_item_index (td
, (gpointer
)info
->func
);
810 interp_generate_bie_throw (TransformData
*td
)
812 MonoJitICallInfo
*info
= &mono_get_jit_icall_info ()->mono_throw_bad_image
;
814 interp_add_ins (td
, MINT_ICALL_V_V
);
815 td
->last_ins
->data
[0] = get_data_item_index (td
, (gpointer
)info
->func
);
819 * These are additional locals that can be allocated as we transform the code.
820 * They are allocated past the method locals so they are accessed in the same
821 * way, with an offset relative to the frame->locals.
824 create_interp_local (TransformData
*td
, MonoType
*type
)
826 if (td
->locals_size
== td
->locals_capacity
) {
827 td
->locals_capacity
*= 2;
828 if (td
->locals_capacity
== 0)
829 td
->locals_capacity
= 2;
830 td
->locals
= (InterpLocal
*) g_realloc (td
->locals
, td
->locals_capacity
* sizeof (InterpLocal
));
832 td
->locals
[td
->locals_size
].type
= type
;
833 td
->locals
[td
->locals_size
].mt
= mint_type (type
);
834 td
->locals
[td
->locals_size
].flags
= 0;
835 td
->locals
[td
->locals_size
].offset
= -1;
837 return td
->locals_size
- 1;
841 get_interp_local_offset (TransformData
*td
, int local
)
843 int align
, size
, offset
;
845 if (td
->locals
[local
].offset
!= -1)
846 return td
->locals
[local
].offset
;
848 offset
= td
->total_locals_size
;
849 size
= mono_type_size (td
->locals
[local
].type
, &align
);
850 offset
= ALIGN_TO (offset
, align
);
852 td
->locals
[local
].offset
= offset
;
854 td
->total_locals_size
= offset
+ size
;
855 g_assert (td
->total_locals_size
< G_MAXUINT16
);
861 dump_mint_code (const guint16
*start
, const guint16
* end
)
863 const guint16
*p
= start
;
865 char *ins
= mono_interp_dis_mintop ((gint32
)(p
- start
), TRUE
, p
+ 1, *p
);
866 g_print ("%s\n", ins
);
868 p
= mono_interp_dis_mintop_len (p
);
873 dump_interp_inst (InterpInst
*ins
)
875 char *descr
= mono_interp_dis_mintop (ins
->il_offset
, FALSE
, &ins
->data
[0], ins
->opcode
);
876 g_print ("%s", descr
);
881 dump_interp_inst_newline (InterpInst
*ins
)
883 dump_interp_inst (ins
);
889 mono_interp_print_code (InterpMethod
*imethod
)
891 MonoJitInfo
*jinfo
= imethod
->jinfo
;
897 char *name
= mono_method_full_name (imethod
->method
, 1);
898 g_print ("Method : %s\n", name
);
901 start
= (guint8
*) jinfo
->code_start
;
902 dump_mint_code ((const guint16
*)start
, (const guint16
*)(start
+ jinfo
->code_size
));
907 mono_interp_print_td_code (TransformData
*td
)
909 InterpInst
*ins
= td
->first_ins
;
911 char *name
= mono_method_full_name (td
->method
, TRUE
);
912 g_print ("IR for \"%s\"\n", name
);
915 dump_interp_inst_newline (ins
);
921 static MonoMethodHeader
*
922 interp_method_get_header (MonoMethod
* method
, MonoError
*error
)
924 /* An explanation: mono_method_get_header_internal returns an error if
925 * called on a method with no body (e.g. an abstract method, or an
926 * icall). We don't want that.
928 if (mono_method_has_no_body (method
))
931 return mono_method_get_header_internal (method
, error
);
934 // Returns whether we can optimize away the instructions starting at start.
935 // If any instructions are part of a new basic block, we can't remove them.
937 interp_is_bb_start (TransformData
*td
, InterpInst
*start
, InterpInst
*end
)
939 InterpInst
*ins
= start
;
941 if (ins
->il_offset
!= -1) {
942 if (td
->is_bb_start
[ins
->il_offset
])
948 // Also check if end is bb start
949 if (ins
!= NULL
&& ins
->il_offset
!= -1 && td
->is_bb_start
[ins
->il_offset
])
955 interp_ins_is_ldc (InterpInst
*ins
)
957 return ins
->opcode
>= MINT_LDC_I4_M1
&& ins
->opcode
<= MINT_LDC_I8
;
961 interp_get_const_from_ldc_i4 (InterpInst
*ins
)
963 switch (ins
->opcode
) {
964 case MINT_LDC_I4_M1
: return -1;
965 case MINT_LDC_I4_0
: return 0;
966 case MINT_LDC_I4_1
: return 1;
967 case MINT_LDC_I4_2
: return 2;
968 case MINT_LDC_I4_3
: return 3;
969 case MINT_LDC_I4_4
: return 4;
970 case MINT_LDC_I4_5
: return 5;
971 case MINT_LDC_I4_6
: return 6;
972 case MINT_LDC_I4_7
: return 7;
973 case MINT_LDC_I4_8
: return 8;
974 case MINT_LDC_I4_S
: return (gint32
)(gint8
)ins
->data
[0];
975 case MINT_LDC_I4
: return READ32 (&ins
->data
[0]);
977 g_assert_not_reached ();
981 /* If ins is not null, it will replace it with the ldc */
983 interp_get_ldc_i4_from_const (TransformData
*td
, InterpInst
*ins
, gint32 ct
)
987 case -1: opcode
= MINT_LDC_I4_M1
; break;
988 case 0: opcode
= MINT_LDC_I4_0
; break;
989 case 1: opcode
= MINT_LDC_I4_1
; break;
990 case 2: opcode
= MINT_LDC_I4_2
; break;
991 case 3: opcode
= MINT_LDC_I4_3
; break;
992 case 4: opcode
= MINT_LDC_I4_4
; break;
993 case 5: opcode
= MINT_LDC_I4_5
; break;
994 case 6: opcode
= MINT_LDC_I4_6
; break;
995 case 7: opcode
= MINT_LDC_I4_7
; break;
996 case 8: opcode
= MINT_LDC_I4_8
; break;
998 if (ct
>= -128 && ct
<= 127)
999 opcode
= MINT_LDC_I4_S
;
1001 opcode
= MINT_LDC_I4
;
1005 int new_size
= mono_interp_oplen
[opcode
];
1008 ins
= interp_add_ins (td
, opcode
);
1010 int ins_size
= mono_interp_oplen
[ins
->opcode
];
1011 if (ins_size
< new_size
) {
1012 // We can't replace the passed instruction, discard it and emit a new one
1013 ins
= interp_insert_ins (td
, ins
, opcode
);
1014 interp_clear_ins (td
, ins
->prev
);
1016 ins
->opcode
= opcode
;
1020 ins
->data
[0] = (gint8
)ct
;
1021 else if (new_size
== 3)
1022 WRITE32_INS (ins
, 0, &ct
);
1028 interp_inst_replace_with_i8_const (TransformData
*td
, InterpInst
*ins
, gint64 ct
)
1030 int size
= mono_interp_oplen
[ins
->opcode
];
1033 ins
= interp_insert_ins (td
, ins
, MINT_LDC_I8
);
1034 interp_clear_ins (td
, ins
->prev
);
1036 ins
->opcode
= MINT_LDC_I8
;
1038 WRITE64_INS (ins
, 0, &ct
);
1044 interp_get_ldind_for_mt (int mt
)
1047 case MINT_TYPE_I1
: return MINT_LDIND_I1_CHECK
;
1048 case MINT_TYPE_U1
: return MINT_LDIND_U1_CHECK
;
1049 case MINT_TYPE_I2
: return MINT_LDIND_I2_CHECK
;
1050 case MINT_TYPE_U2
: return MINT_LDIND_U2_CHECK
;
1051 case MINT_TYPE_I4
: return MINT_LDIND_I4_CHECK
;
1052 case MINT_TYPE_I8
: return MINT_LDIND_I8_CHECK
;
1053 case MINT_TYPE_R4
: return MINT_LDIND_R4_CHECK
;
1054 case MINT_TYPE_R8
: return MINT_LDIND_R8_CHECK
;
1055 case MINT_TYPE_O
: return MINT_LDIND_REF
;
1057 g_assert_not_reached ();
1063 interp_emit_ldobj (TransformData
*td
, MonoClass
*klass
)
1065 int mt
= mint_type (m_class_get_byval_arg (klass
));
1068 if (mt
== MINT_TYPE_VT
) {
1069 interp_add_ins (td
, MINT_LDOBJ_VT
);
1070 size
= mono_class_value_size (klass
, NULL
);
1071 WRITE32_INS (td
->last_ins
, 0, &size
);
1074 int opcode
= interp_get_ldind_for_mt (mt
);
1075 interp_add_ins (td
, opcode
);
1078 SET_TYPE (td
->sp
- 1, stack_type
[mt
], klass
);
1082 interp_emit_stobj (TransformData
*td
, MonoClass
*klass
)
1084 int mt
= mint_type (m_class_get_byval_arg (klass
));
1086 if (mt
== MINT_TYPE_VT
) {
1088 interp_add_ins (td
, MINT_STOBJ_VT
);
1089 td
->last_ins
->data
[0] = get_data_item_index(td
, klass
);
1090 size
= mono_class_value_size (klass
, NULL
);
1097 opcode
= MINT_STIND_I1
;
1101 opcode
= MINT_STIND_I2
;
1104 opcode
= MINT_STIND_I4
;
1107 opcode
= MINT_STIND_I8
;
1110 opcode
= MINT_STIND_R4
;
1113 opcode
= MINT_STIND_R8
;
1116 opcode
= MINT_STIND_REF
;
1118 default: g_assert_not_reached (); break;
1120 interp_add_ins (td
, opcode
);
1126 interp_emit_ldelema (TransformData
*td
, MonoClass
*array_class
, MonoClass
*check_class
)
1128 MonoClass
*element_class
= m_class_get_element_class (array_class
);
1129 int rank
= m_class_get_rank (array_class
);
1130 int size
= mono_class_array_element_size (element_class
);
1132 // We only need type checks when writing to array of references
1133 if (!check_class
|| m_class_is_valuetype (element_class
)) {
1135 interp_add_ins (td
, MINT_LDELEMA1
);
1136 WRITE32_INS (td
->last_ins
, 0, &size
);
1138 interp_add_ins (td
, MINT_LDELEMA
);
1139 td
->last_ins
->data
[0] = rank
;
1140 WRITE32_INS (td
->last_ins
, 1, &size
);
1143 interp_add_ins (td
, MINT_LDELEMA_TC
);
1144 td
->last_ins
->data
[0] = rank
;
1145 td
->last_ins
->data
[1] = get_data_item_index (td
, check_class
);
1149 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_MP
);
1152 /* Return TRUE if call transformation is finished */
1154 interp_handle_intrinsics (TransformData
*td
, MonoMethod
*target_method
, MonoClass
*constrained_class
, MonoMethodSignature
*csignature
, gboolean readonly
, int *op
)
1156 const char *tm
= target_method
->name
;
1158 int type_index
= mono_class_get_magic_index (target_method
->klass
);
1159 gboolean in_corlib
= m_class_get_image (target_method
->klass
) == mono_defaults
.corlib
;
1160 const char *klass_name_space
= m_class_get_name_space (target_method
->klass
);
1161 const char *klass_name
= m_class_get_name (target_method
->klass
);
1163 if (target_method
->klass
== mono_defaults
.string_class
) {
1164 if (tm
[0] == 'g') {
1165 if (strcmp (tm
, "get_Chars") == 0)
1167 else if (strcmp (tm
, "get_Length") == 0)
1170 } else if (type_index
>= 0) {
1171 MonoClass
*magic_class
= target_method
->klass
;
1173 const int mt
= mint_type (m_class_get_byval_arg (magic_class
));
1174 if (!strcmp (".ctor", tm
)) {
1175 MonoType
*arg
= csignature
->params
[0];
1176 /* depending on SIZEOF_VOID_P and the type of the value passed to the .ctor we either have to CONV it, or do nothing */
1177 int arg_size
= mini_magic_type_size (NULL
, arg
);
1179 if (arg_size
> SIZEOF_VOID_P
) { // 8 -> 4
1180 switch (type_index
) {
1182 interp_add_ins (td
, MINT_CONV_I4_I8
);
1185 interp_add_ins (td
, MINT_CONV_R4_R8
);
1190 if (arg_size
< SIZEOF_VOID_P
) { // 4 -> 8
1191 switch (type_index
) {
1193 interp_add_ins (td
, MINT_CONV_I8_I4
);
1196 interp_add_ins (td
, MINT_CONV_I8_U4
);
1199 interp_add_ins (td
, MINT_CONV_R8_R4
);
1204 switch (type_index
) {
1206 #if SIZEOF_VOID_P == 4
1207 interp_add_ins (td
, MINT_STIND_I4
);
1209 interp_add_ins (td
, MINT_STIND_I8
);
1213 #if SIZEOF_VOID_P == 4
1214 interp_add_ins (td
, MINT_STIND_R4
);
1216 interp_add_ins (td
, MINT_STIND_R8
);
1224 } else if (!strcmp ("op_Implicit", tm
) || !strcmp ("op_Explicit", tm
)) {
1225 MonoType
*src
= csignature
->params
[0];
1226 MonoType
*dst
= csignature
->ret
;
1227 MonoClass
*src_klass
= mono_class_from_mono_type_internal (src
);
1228 int src_size
= mini_magic_type_size (NULL
, src
);
1229 int dst_size
= mini_magic_type_size (NULL
, dst
);
1231 gboolean managed_fallback
= FALSE
;
1233 switch (type_index
) {
1235 if (!mini_magic_is_int_type (src
) || !mini_magic_is_int_type (dst
)) {
1236 if (mini_magic_is_int_type (src
))
1237 managed_fallback
= TRUE
;
1238 else if (mono_class_is_magic_float (src_klass
))
1239 managed_fallback
= TRUE
;
1245 if (!mini_magic_is_float_type (src
) || !mini_magic_is_float_type (dst
)) {
1246 if (mini_magic_is_float_type (src
))
1247 managed_fallback
= TRUE
;
1248 else if (mono_class_is_magic_int (src_klass
))
1249 managed_fallback
= TRUE
;
1256 if (managed_fallback
)
1259 if (src_size
> dst_size
) { // 8 -> 4
1260 switch (type_index
) {
1262 interp_add_ins (td
, MINT_CONV_I4_I8
);
1265 interp_add_ins (td
, MINT_CONV_R4_R8
);
1270 if (src_size
< dst_size
) { // 4 -> 8
1271 switch (type_index
) {
1273 interp_add_ins (td
, MINT_CONV_I8_I4
);
1276 interp_add_ins (td
, MINT_CONV_I8_U4
);
1279 interp_add_ins (td
, MINT_CONV_R8_R4
);
1284 SET_TYPE (td
->sp
- 1, stack_type
[mint_type (dst
)], mono_class_from_mono_type_internal (dst
));
1287 } else if (!strcmp ("op_Increment", tm
)) {
1288 g_assert (type_index
!= 2); // no nfloat
1289 #if SIZEOF_VOID_P == 8
1290 interp_add_ins (td
, MINT_ADD1_I8
);
1292 interp_add_ins (td
, MINT_ADD1_I4
);
1294 SET_TYPE (td
->sp
- 1, stack_type
[mt
], magic_class
);
1297 } else if (!strcmp ("op_Decrement", tm
)) {
1298 g_assert (type_index
!= 2); // no nfloat
1299 #if SIZEOF_VOID_P == 8
1300 interp_add_ins (td
, MINT_SUB1_I8
);
1302 interp_add_ins (td
, MINT_SUB1_I4
);
1304 SET_TYPE (td
->sp
- 1, stack_type
[mt
], magic_class
);
1307 } else if (!strcmp ("CompareTo", tm
) || !strcmp ("Equals", tm
)) {
1309 } else if (!strcmp (".cctor", tm
)) {
1312 } else if (!strcmp ("Parse", tm
)) {
1315 } else if (!strcmp ("ToString", tm
)) {
1318 } else if (!strcmp ("GetHashCode", tm
)) {
1321 } else if (!strcmp ("IsNaN", tm
) || !strcmp ("IsInfinity", tm
) || !strcmp ("IsNegativeInfinity", tm
) || !strcmp ("IsPositiveInfinity", tm
)) {
1322 g_assert (type_index
== 2); // nfloat only
1327 for (i
= 0; i
< sizeof (int_unnop
) / sizeof (MagicIntrinsic
); ++i
) {
1328 if (!strcmp (int_unnop
[i
].op_name
, tm
)) {
1329 interp_add_ins (td
, int_unnop
[i
].insn
[type_index
]);
1330 SET_TYPE (td
->sp
- 1, stack_type
[mt
], magic_class
);
1336 for (i
= 0; i
< sizeof (int_binop
) / sizeof (MagicIntrinsic
); ++i
) {
1337 if (!strcmp (int_binop
[i
].op_name
, tm
)) {
1338 interp_add_ins (td
, int_binop
[i
].insn
[type_index
]);
1340 SET_TYPE (td
->sp
- 1, stack_type
[mt
], magic_class
);
1346 for (i
= 0; i
< sizeof (int_cmpop
) / sizeof (MagicIntrinsic
); ++i
) {
1347 if (!strcmp (int_cmpop
[i
].op_name
, tm
)) {
1348 MonoClass
*k
= mono_defaults
.boolean_class
;
1349 interp_add_ins (td
, int_cmpop
[i
].insn
[type_index
]);
1351 SET_TYPE (td
->sp
- 1, stack_type
[mint_type (m_class_get_byval_arg (k
))], k
);
1357 g_error ("TODO: interp_transform_call %s:%s", m_class_get_name (target_method
->klass
), tm
);
1358 } else if (mono_class_is_subclass_of_internal (target_method
->klass
, mono_defaults
.array_class
, FALSE
)) {
1359 if (!strcmp (tm
, "get_Rank")) {
1360 *op
= MINT_ARRAY_RANK
;
1361 } else if (!strcmp (tm
, "get_Length")) {
1363 } else if (!strcmp (tm
, "GetElementSize")) {
1364 *op
= MINT_ARRAY_ELEMENT_SIZE
;
1365 } else if (!strcmp (tm
, "IsPrimitive")) {
1366 *op
= MINT_ARRAY_IS_PRIMITIVE
;
1367 } else if (!strcmp (tm
, "Address")) {
1368 MonoClass
*check_class
= readonly
? NULL
: m_class_get_element_class (target_method
->klass
);
1369 interp_emit_ldelema (td
, target_method
->klass
, check_class
);
1372 #ifndef ENABLE_NETCORE
1373 } else if (!strcmp (tm
, "UnsafeMov") || !strcmp (tm
, "UnsafeLoad")) {
1376 } else if (!strcmp (tm
, "Get")) {
1377 interp_emit_ldelema (td
, target_method
->klass
, NULL
);
1378 interp_emit_ldobj (td
, m_class_get_element_class (target_method
->klass
));
1381 } else if (!strcmp (tm
, "Set")) {
1382 MonoClass
*element_class
= m_class_get_element_class (target_method
->klass
);
1383 MonoType
*local_type
= m_class_get_byval_arg (element_class
);
1384 MonoClass
*value_class
= td
->sp
[-1].klass
;
1385 // If value_class is NULL it means the top of stack is a simple type (valuetype)
1386 // which doesn't require type checks, or that we have no type information because
1387 // the code is unsafe (like in some wrappers). In that case we assume the type
1388 // of the array and don't do any checks.
1390 int local
= create_interp_local (td
, local_type
);
1392 store_local (td
, local
);
1393 interp_emit_ldelema (td
, target_method
->klass
, value_class
);
1394 load_local (td
, local
);
1395 interp_emit_stobj (td
, element_class
);
1398 } else if (!strcmp (tm
, "UnsafeStore")) {
1399 g_error ("TODO ArrayClass::UnsafeStore");
1401 } else if (in_corlib
&&
1402 !strcmp (klass_name_space
, "System.Diagnostics") &&
1403 !strcmp (klass_name
, "Debugger")) {
1404 if (!strcmp (tm
, "Break") && csignature
->param_count
== 0) {
1405 if (mini_should_insert_breakpoint (td
->method
))
1408 } else if (in_corlib
&& !strcmp (klass_name_space
, "System") && !strcmp (klass_name
, "ByReference`1")) {
1409 g_assert (!strcmp (tm
, "get_Value"));
1410 *op
= MINT_INTRINS_BYREFERENCE_GET_VALUE
;
1411 } else if (in_corlib
&& !strcmp (klass_name_space
, "System") && !strcmp (klass_name
, "Math") && csignature
->param_count
== 1 && csignature
->params
[0]->type
== MONO_TYPE_R8
) {
1412 if (tm
[0] == 'A') {
1413 if (strcmp (tm
, "Abs") == 0 && csignature
->params
[0]->type
== MONO_TYPE_R8
) {
1415 } else if (strcmp (tm
, "Asin") == 0){
1417 } else if (strcmp (tm
, "Asinh") == 0){
1419 } else if (strcmp (tm
, "Acos") == 0){
1421 } else if (strcmp (tm
, "Acosh") == 0){
1423 } else if (strcmp (tm
, "Atan") == 0){
1425 } else if (strcmp (tm
, "Atanh") == 0){
1428 } else if (tm
[0] == 'C') {
1429 if (strcmp (tm
, "Cos") == 0) {
1431 } else if (strcmp (tm
, "Cbrt") == 0){
1433 } else if (strcmp (tm
, "Cosh") == 0){
1436 } else if (tm
[0] == 'S') {
1437 if (strcmp (tm
, "Sin") == 0) {
1439 } else if (strcmp (tm
, "Sqrt") == 0) {
1441 } else if (strcmp (tm
, "Sinh") == 0){
1444 } else if (tm
[0] == 'T') {
1445 if (strcmp (tm
, "Tan") == 0) {
1447 } else if (strcmp (tm
, "Tanh") == 0){
1451 } else if (in_corlib
&& !strcmp (klass_name_space
, "System") && (!strcmp (klass_name
, "Span`1") || !strcmp (klass_name
, "ReadOnlySpan`1"))) {
1452 if (!strcmp (tm
, "get_Item")) {
1453 MonoGenericClass
*gclass
= mono_class_get_generic_class (target_method
->klass
);
1454 MonoClass
*param_class
= mono_class_from_mono_type_internal (gclass
->context
.class_inst
->type_argv
[0]);
1456 if (!mini_is_gsharedvt_variable_klass (param_class
)) {
1457 MonoClassField
*length_field
= mono_class_get_field_from_name_full (target_method
->klass
, "_length", NULL
);
1458 g_assert (length_field
);
1459 int offset_length
= length_field
->offset
- sizeof (MonoObject
);
1461 MonoClassField
*ptr_field
= mono_class_get_field_from_name_full (target_method
->klass
, "_pointer", NULL
);
1462 g_assert (ptr_field
);
1463 int offset_pointer
= ptr_field
->offset
- sizeof (MonoObject
);
1465 int size
= mono_class_array_element_size (param_class
);
1466 interp_add_ins (td
, MINT_GETITEM_SPAN
);
1467 td
->last_ins
->data
[0] = size
;
1468 td
->last_ins
->data
[1] = offset_length
;
1469 td
->last_ins
->data
[2] = offset_pointer
;
1471 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_MP
);
1476 } else if (!strcmp (tm
, "get_Length")) {
1477 MonoClassField
*length_field
= mono_class_get_field_from_name_full (target_method
->klass
, "_length", NULL
);
1478 g_assert (length_field
);
1479 int offset_length
= length_field
->offset
- sizeof (MonoObject
);
1480 interp_add_ins (td
, MINT_LDLEN_SPAN
);
1481 td
->last_ins
->data
[0] = offset_length
;
1482 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
1486 } else if (((in_corlib
&& !strcmp (klass_name_space
, "Internal.Runtime.CompilerServices"))
1487 || !strcmp (klass_name_space
, "System.Runtime.CompilerServices"))
1488 && !strcmp (klass_name
, "Unsafe")) {
1489 #ifdef ENABLE_NETCORE
1490 if (!strcmp (tm
, "AddByteOffset"))
1491 *op
= MINT_INTRINS_UNSAFE_ADD_BYTE_OFFSET
;
1492 else if (!strcmp (tm
, "ByteOffset"))
1493 *op
= MINT_INTRINS_UNSAFE_BYTE_OFFSET
;
1494 else if (!strcmp (tm
, "As") || !strcmp (tm
, "AsRef"))
1496 else if (!strcmp (tm
, "AsPointer")) {
1498 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_MP
);
1501 } else if (!strcmp (tm
, "IsAddressLessThan")) {
1502 MonoGenericContext
*ctx
= mono_method_get_context (target_method
);
1504 g_assert (ctx
->method_inst
);
1505 g_assert (ctx
->method_inst
->type_argc
== 1);
1507 MonoClass
*k
= mono_defaults
.boolean_class
;
1508 interp_add_ins (td
, MINT_CLT_UN_P
);
1510 SET_TYPE (td
->sp
- 1, stack_type
[mint_type (m_class_get_byval_arg (k
))], k
);
1513 } else if (!strcmp (tm
, "SizeOf")) {
1514 MonoGenericContext
*ctx
= mono_method_get_context (target_method
);
1516 g_assert (ctx
->method_inst
);
1517 g_assert (ctx
->method_inst
->type_argc
== 1);
1518 MonoType
*t
= ctx
->method_inst
->type_argv
[0];
1520 int esize
= mono_type_size (t
, &align
);
1521 interp_add_ins (td
, MINT_LDC_I4
);
1522 WRITE32_INS (td
->last_ins
, 0, &esize
);
1523 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I4
);
1526 } else if (!strcmp (tm
, "AreSame")) {
1528 } else if (!strcmp (tm
, "SkipInit")) {
1532 } else if (in_corlib
&& !strcmp (klass_name_space
, "System.Runtime.CompilerServices") && !strcmp (klass_name
, "RuntimeHelpers")) {
1533 #ifdef ENABLE_NETCORE
1534 if (!strcmp (tm
, "get_OffsetToStringData")) {
1535 g_assert (csignature
->param_count
== 0);
1536 int offset
= MONO_STRUCT_OFFSET (MonoString
, chars
);
1537 interp_add_ins (td
, MINT_LDC_I4
);
1538 WRITE32_INS (td
->last_ins
, 0, &offset
);
1539 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I4
);
1542 } else if (!strcmp (tm
, "IsBitwiseEquatable")) {
1543 g_assert (csignature
->param_count
== 0);
1544 MonoGenericContext
*ctx
= mono_method_get_context (target_method
);
1546 g_assert (ctx
->method_inst
);
1547 g_assert (ctx
->method_inst
->type_argc
== 1);
1548 MonoType
*t
= mini_get_underlying_type (ctx
->method_inst
->type_argv
[0]);
1550 if (MONO_TYPE_IS_PRIMITIVE (t
) && t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
)
1551 *op
= MINT_LDC_I4_1
;
1553 *op
= MINT_LDC_I4_0
;
1554 } else if (!strcmp (tm
, "ObjectHasComponentSize")) {
1555 *op
= MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE
;
1556 } else if (!strcmp (tm
, "IsReferenceOrContainsReferences")) {
1557 g_assert (csignature
->param_count
== 0);
1558 MonoGenericContext
*ctx
= mono_method_get_context (target_method
);
1560 g_assert (ctx
->method_inst
);
1561 g_assert (ctx
->method_inst
->type_argc
== 1);
1562 MonoType
*t
= mini_get_underlying_type (ctx
->method_inst
->type_argv
[0]);
1566 MonoClass
*klass
= mono_class_from_mono_type_internal (t
);
1567 mono_class_init_internal (klass
);
1568 if (MONO_TYPE_IS_REFERENCE (t
))
1570 else if (MONO_TYPE_IS_PRIMITIVE (t
))
1573 has_refs
= m_class_has_references (klass
);
1575 *op
= has_refs
? MINT_LDC_I4_1
: MINT_LDC_I4_0
;
1578 } else if (in_corlib
&& !strcmp (klass_name_space
, "System") && !strcmp (klass_name
, "RuntimeMethodHandle") && !strcmp (tm
, "GetFunctionPointer") && csignature
->param_count
== 1) {
1579 // We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter
1580 *op
= MINT_LDFTN_DYNAMIC
;
1581 } else if (in_corlib
&& target_method
->klass
== mono_defaults
.systemtype_class
&& !strcmp (target_method
->name
, "op_Equality")) {
1583 } else if (in_corlib
&& target_method
->klass
== mono_defaults
.object_class
) {
1584 if (!strcmp (tm
, "InternalGetHashCode"))
1585 *op
= MINT_INTRINS_GET_HASHCODE
;
1586 else if (!strcmp (tm
, "GetType")
1587 #ifndef DISABLE_REMOTING
1588 // Invoking GetType via reflection on proxies has some special semantics
1589 // See InterfaceProxyGetTypeViaReflectionOkay corlib test
1590 && td
->method
->wrapper_type
!= MONO_WRAPPER_RUNTIME_INVOKE
1593 *op
= MINT_INTRINS_GET_TYPE
;
1594 #ifdef ENABLE_NETCORE
1595 else if (!strcmp (tm
, "GetRawData")) {
1596 #if SIZEOF_VOID_P == 8
1597 interp_add_ins (td
, MINT_LDC_I8_S
);
1599 interp_add_ins (td
, MINT_LDC_I4_S
);
1601 td
->last_ins
->data
[0] = (gint16
) MONO_ABI_SIZEOF (MonoObject
);
1603 interp_add_ins (td
, MINT_ADD_P
);
1604 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_MP
);
1610 } else if (in_corlib
&& target_method
->klass
== mono_defaults
.enum_class
&& !strcmp (tm
, "HasFlag")) {
1611 gboolean intrinsify
= FALSE
;
1612 MonoClass
*base_klass
= NULL
;
1613 if (td
->last_ins
&& td
->last_ins
->opcode
== MINT_BOX
&&
1614 td
->last_ins
->prev
&& interp_ins_is_ldc (td
->last_ins
->prev
) &&
1615 td
->last_ins
->prev
->prev
&& td
->last_ins
->prev
->prev
->opcode
== MINT_BOX
&&
1616 td
->sp
[-2].klass
== td
->sp
[-1].klass
&&
1617 !interp_is_bb_start (td
, td
->last_ins
->prev
->prev
, NULL
) &&
1618 !td
->is_bb_start
[td
->in_start
- td
->il_code
]) {
1619 // csc pattern : box, ldc, box, call HasFlag
1620 g_assert (m_class_is_enumtype (td
->sp
[-2].klass
));
1621 MonoType
*base_type
= mono_type_get_underlying_type (m_class_get_byval_arg (td
->sp
[-2].klass
));
1622 base_klass
= mono_class_from_mono_type_internal (base_type
);
1624 // Remove the boxing of valuetypes
1625 interp_clear_ins (td
, td
->last_ins
->prev
->prev
);
1626 interp_clear_ins (td
, td
->last_ins
);
1629 } else if (td
->last_ins
&& td
->last_ins
->opcode
== MINT_BOX
&&
1630 td
->last_ins
->prev
&& interp_ins_is_ldc (td
->last_ins
->prev
) &&
1631 constrained_class
&& td
->sp
[-1].klass
== constrained_class
&&
1632 !interp_is_bb_start (td
, td
->last_ins
->prev
, NULL
) &&
1633 !td
->is_bb_start
[td
->in_start
- td
->il_code
]) {
1634 // mcs pattern : ldc, box, constrained Enum, call HasFlag
1635 g_assert (m_class_is_enumtype (constrained_class
));
1636 MonoType
*base_type
= mono_type_get_underlying_type (m_class_get_byval_arg (constrained_class
));
1637 base_klass
= mono_class_from_mono_type_internal (base_type
);
1638 int mt
= mint_type (m_class_get_byval_arg (base_klass
));
1640 // Remove boxing and load the value of this
1641 interp_clear_ins (td
, td
->last_ins
);
1642 interp_insert_ins (td
, td
->last_ins
->prev
->prev
, interp_get_ldind_for_mt (mt
));
1647 interp_add_ins (td
, MINT_INTRINS_ENUM_HASFLAG
);
1648 td
->last_ins
->data
[0] = get_data_item_index (td
, base_klass
);
1650 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I4
);
1654 } else if (in_corlib
&& !strcmp (klass_name_space
, "System.Threading") && !strcmp (klass_name
, "Interlocked")) {
1655 if (!strcmp (tm
, "MemoryBarrier") && csignature
->param_count
== 0)
1656 *op
= MINT_MONO_MEMORY_BARRIER
;
1657 } else if (in_corlib
&& !strcmp (klass_name_space
, "System.Threading") && !strcmp (klass_name
, "Thread")) {
1658 if (!strcmp (tm
, "MemoryBarrier") && csignature
->param_count
== 0)
1659 *op
= MINT_MONO_MEMORY_BARRIER
;
1660 } else if (in_corlib
&&
1661 !strcmp (klass_name_space
, "System.Runtime.CompilerServices") &&
1662 !strcmp (klass_name
, "JitHelpers") &&
1663 (!strcmp (tm
, "EnumEquals") || !strcmp (tm
, "EnumCompareTo"))) {
1664 MonoGenericContext
*ctx
= mono_method_get_context (target_method
);
1666 g_assert (ctx
->method_inst
);
1667 g_assert (ctx
->method_inst
->type_argc
== 1);
1668 g_assert (csignature
->param_count
== 2);
1670 MonoType
*t
= ctx
->method_inst
->type_argv
[0];
1671 t
= mini_get_underlying_type (t
);
1673 gboolean is_i8
= (t
->type
== MONO_TYPE_I8
|| t
->type
== MONO_TYPE_U8
);
1674 gboolean is_unsigned
= (t
->type
== MONO_TYPE_U1
|| t
->type
== MONO_TYPE_U2
|| t
->type
== MONO_TYPE_U4
|| t
->type
== MONO_TYPE_U8
|| t
->type
== MONO_TYPE_U
);
1676 gboolean is_compareto
= strcmp (tm
, "EnumCompareTo") == 0;
1679 locala
= create_interp_local (td
, t
);
1680 localb
= create_interp_local (td
, t
);
1683 store_local (td
, localb
);
1684 store_local (td
, locala
);
1686 load_local (td
, locala
);
1687 load_local (td
, localb
);
1689 interp_add_ins (td
, is_i8
? MINT_CGT_UN_I8
: MINT_CGT_UN_I4
);
1691 interp_add_ins (td
, is_i8
? MINT_CGT_I8
: MINT_CGT_I4
);
1693 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
1695 load_local (td
, locala
);
1696 load_local (td
, localb
);
1698 interp_add_ins (td
, is_i8
? MINT_CLT_UN_I8
: MINT_CLT_UN_I4
);
1700 interp_add_ins (td
, is_i8
? MINT_CLT_I8
: MINT_CLT_I4
);
1702 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
1703 // (a > b) - (a < b)
1704 interp_add_ins (td
, MINT_SUB_I4
);
1715 } else if (in_corlib
&&
1716 !strcmp ("System.Runtime.CompilerServices", klass_name_space
) &&
1717 !strcmp ("RuntimeFeature", klass_name
)) {
1718 if (!strcmp (tm
, "get_IsDynamicCodeSupported"))
1719 *op
= MINT_LDC_I4_1
;
1720 else if (!strcmp (tm
, "get_IsDynamicCodeCompiled"))
1721 *op
= MINT_LDC_I4_0
;
1728 interp_transform_internal_calls (MonoMethod
*method
, MonoMethod
*target_method
, MonoMethodSignature
*csignature
, gboolean is_virtual
)
1730 if (method
->wrapper_type
== MONO_WRAPPER_NONE
&& target_method
!= NULL
) {
1731 if (target_method
->flags
& METHOD_ATTRIBUTE_PINVOKE_IMPL
)
1732 target_method
= mono_marshal_get_native_wrapper (target_method
, FALSE
, FALSE
);
1733 if (!is_virtual
&& target_method
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
)
1734 target_method
= mono_marshal_get_synchronized_wrapper (target_method
);
1736 if (target_method
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
&& !is_virtual
&& !mono_class_is_marshalbyref (target_method
->klass
) && m_class_get_rank (target_method
->klass
) == 0)
1737 target_method
= mono_marshal_get_native_wrapper (target_method
, FALSE
, FALSE
);
1739 return target_method
;
1743 interp_type_as_ptr (MonoType
*tp
)
1745 if (MONO_TYPE_IS_POINTER (tp
))
1747 if (MONO_TYPE_IS_REFERENCE (tp
))
1749 if ((tp
)->type
== MONO_TYPE_I4
)
1751 #if SIZEOF_VOID_P == 8
1752 if ((tp
)->type
== MONO_TYPE_I8
)
1755 if ((tp
)->type
== MONO_TYPE_BOOLEAN
)
1757 if ((tp
)->type
== MONO_TYPE_CHAR
)
1759 if ((tp
)->type
== MONO_TYPE_VALUETYPE
&& m_class_is_enumtype (tp
->data
.klass
))
1764 #define INTERP_TYPE_AS_PTR(tp) interp_type_as_ptr (tp)
1767 interp_icall_op_for_sig (MonoMethodSignature
*sig
)
1770 switch (sig
->param_count
) {
1772 if (MONO_TYPE_IS_VOID (sig
->ret
))
1773 op
= MINT_ICALL_V_V
;
1774 else if (INTERP_TYPE_AS_PTR (sig
->ret
))
1775 op
= MINT_ICALL_V_P
;
1778 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1779 if (INTERP_TYPE_AS_PTR (sig
->params
[0]))
1780 op
= MINT_ICALL_P_V
;
1781 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1782 if (INTERP_TYPE_AS_PTR (sig
->params
[0]))
1783 op
= MINT_ICALL_P_P
;
1787 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1788 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1789 INTERP_TYPE_AS_PTR (sig
->params
[1]))
1790 op
= MINT_ICALL_PP_V
;
1791 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1792 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1793 INTERP_TYPE_AS_PTR (sig
->params
[1]))
1794 op
= MINT_ICALL_PP_P
;
1798 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1799 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1800 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1801 INTERP_TYPE_AS_PTR (sig
->params
[2]))
1802 op
= MINT_ICALL_PPP_V
;
1803 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1804 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1805 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1806 INTERP_TYPE_AS_PTR (sig
->params
[2]))
1807 op
= MINT_ICALL_PPP_P
;
1811 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1812 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1813 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1814 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1815 INTERP_TYPE_AS_PTR (sig
->params
[3]))
1816 op
= MINT_ICALL_PPPP_V
;
1817 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1818 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1819 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1820 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1821 INTERP_TYPE_AS_PTR (sig
->params
[3]))
1822 op
= MINT_ICALL_PPPP_P
;
1826 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1827 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1828 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1829 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1830 INTERP_TYPE_AS_PTR (sig
->params
[3]) &&
1831 INTERP_TYPE_AS_PTR (sig
->params
[4]))
1832 op
= MINT_ICALL_PPPPP_V
;
1833 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1834 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1835 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1836 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1837 INTERP_TYPE_AS_PTR (sig
->params
[3]) &&
1838 INTERP_TYPE_AS_PTR (sig
->params
[4]))
1839 op
= MINT_ICALL_PPPPP_P
;
1843 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1844 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1845 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1846 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1847 INTERP_TYPE_AS_PTR (sig
->params
[3]) &&
1848 INTERP_TYPE_AS_PTR (sig
->params
[4]) &&
1849 INTERP_TYPE_AS_PTR (sig
->params
[5]))
1850 op
= MINT_ICALL_PPPPPP_V
;
1851 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1852 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1853 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1854 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1855 INTERP_TYPE_AS_PTR (sig
->params
[3]) &&
1856 INTERP_TYPE_AS_PTR (sig
->params
[4]) &&
1857 INTERP_TYPE_AS_PTR (sig
->params
[5]))
1858 op
= MINT_ICALL_PPPPPP_P
;
1865 #define INLINE_LENGTH_LIMIT 20
1868 interp_method_check_inlining (TransformData
*td
, MonoMethod
*method
)
1870 MonoMethodHeaderSummary header
;
1872 if (td
->method
== method
)
1875 if (method
->flags
& METHOD_ATTRIBUTE_REQSECOBJ
)
1876 /* Used to mark methods containing StackCrawlMark locals */
1879 if (!mono_method_get_header_summary (method
, &header
))
1882 /*runtime, icall and pinvoke are checked by summary call*/
1883 if ((method
->iflags
& METHOD_IMPL_ATTRIBUTE_NOINLINING
) ||
1884 (method
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
) ||
1885 (mono_class_is_marshalbyref (method
->klass
)) ||
1889 if (header
.code_size
>= INLINE_LENGTH_LIMIT
&& !(method
->iflags
& METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING
))
1892 if (mono_class_needs_cctor_run (method
->klass
, NULL
)) {
1895 if (!m_class_get_runtime_info (method
->klass
))
1896 /* No vtable created yet */
1898 vtable
= mono_class_vtable_checked (td
->rtm
->domain
, method
->klass
, error
);
1899 if (!is_ok (error
)) {
1900 mono_error_cleanup (error
);
1903 if (!vtable
->initialized
)
1907 /* We currently access at runtime the wrapper data */
1908 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
1911 // FIXME Re-enable this
1912 if (mono_class_get_magic_index (method
->klass
) >= 0)
1919 interp_inline_method (TransformData
*td
, MonoMethod
*target_method
, MonoMethodHeader
*header
, MonoError
*error
)
1921 const unsigned char *prev_ip
, *prev_il_code
, *prev_in_start
;
1922 int *prev_in_offsets
;
1924 unsigned int prev_max_stack_height
, prev_max_vt_sp
, prev_locals_size
;
1925 int prev_n_data_items
;
1928 MonoGenericContext
*generic_context
= NULL
;
1929 StackInfo
*prev_param_area
;
1930 MonoMethod
*prev_inlined_method
;
1931 MonoMethodSignature
*csignature
= mono_method_signature_internal (target_method
);
1932 int nargs
= csignature
->param_count
+ !!csignature
->hasthis
;
1933 InterpInst
*prev_last_ins
;
1935 if (csignature
->is_inflated
)
1936 generic_context
= mono_method_get_context (target_method
);
1938 MonoGenericContainer
*generic_container
= mono_method_get_generic_container (target_method
);
1939 if (generic_container
)
1940 generic_context
= &generic_container
->context
;
1944 prev_il_code
= td
->il_code
;
1945 prev_in_start
= td
->in_start
;
1946 prev_sp_offset
= td
->sp
- td
->stack
;
1947 prev_vt_sp
= td
->vt_sp
;
1948 prev_inlined_method
= td
->inlined_method
;
1949 prev_last_ins
= td
->last_ins
;
1950 td
->inlined_method
= target_method
;
1952 prev_max_stack_height
= td
->max_stack_height
;
1953 prev_max_vt_sp
= td
->max_vt_sp
;
1954 prev_locals_size
= td
->locals_size
;
1956 prev_n_data_items
= td
->n_data_items
;
1957 prev_in_offsets
= td
->in_offsets
;
1958 td
->in_offsets
= (int*)g_malloc0((header
->code_size
+ 1) * sizeof(int));
1960 /* Inlining pops the arguments, restore the stack */
1961 prev_param_area
= (StackInfo
*)g_malloc (nargs
* sizeof (StackInfo
));
1962 memcpy (prev_param_area
, &td
->sp
[-nargs
], nargs
* sizeof (StackInfo
));
1964 int const prev_code_size
= td
->code_size
;
1965 td
->code_size
= header
->code_size
;
1967 if (td
->verbose_level
)
1968 g_print ("Inline start method %s.%s\n", m_class_get_name (target_method
->klass
), target_method
->name
);
1969 ret
= generate_code (td
, target_method
, header
, generic_context
, error
);
1972 if (td
->verbose_level
)
1973 g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method
->klass
), target_method
->name
);
1974 td
->max_stack_height
= prev_max_stack_height
;
1975 td
->max_vt_sp
= prev_max_vt_sp
;
1976 td
->locals_size
= prev_locals_size
;
1979 /* Remove any newly added items */
1980 for (i
= prev_n_data_items
; i
< td
->n_data_items
; i
++) {
1981 g_hash_table_remove (td
->data_hash
, td
->data_items
[i
]);
1983 td
->n_data_items
= prev_n_data_items
;
1984 td
->sp
= td
->stack
+ prev_sp_offset
;
1985 memcpy (&td
->sp
[-nargs
], prev_param_area
, nargs
* sizeof (StackInfo
));
1986 td
->vt_sp
= prev_vt_sp
;
1987 td
->last_ins
= prev_last_ins
;
1989 td
->last_ins
->next
= NULL
;
1990 UnlockedIncrement (&mono_interp_stats
.inline_failures
);
1992 if (td
->verbose_level
)
1993 g_print ("Inline end method %s.%s\n", m_class_get_name (target_method
->klass
), target_method
->name
);
1994 UnlockedIncrement (&mono_interp_stats
.inlined_methods
);
1995 // Make sure we have an IR instruction associated with the now removed IL CALL
1996 // FIXME This could be prettier. We might be able to make inlining saner now that
1997 // that we can easily tweak the instruction list.
1998 if (!prev_inlined_method
) {
1999 if (prev_last_ins
) {
2000 if (prev_last_ins
->next
)
2001 prev_last_ins
->next
->il_offset
= prev_in_start
- prev_il_code
;
2002 } else if (td
->first_ins
) {
2003 td
->first_ins
->il_offset
= prev_in_start
- prev_il_code
;
2009 td
->in_start
= prev_in_start
;
2010 td
->il_code
= prev_il_code
;
2011 td
->inlined_method
= prev_inlined_method
;
2012 td
->code_size
= prev_code_size
;
2014 g_free (td
->in_offsets
);
2015 td
->in_offsets
= prev_in_offsets
;
2017 g_free (prev_param_area
);
2022 interp_constrained_box (TransformData
*td
, MonoDomain
*domain
, MonoClass
*constrained_class
, MonoMethodSignature
*csignature
, MonoError
*error
)
2024 int mt
= mint_type (m_class_get_byval_arg (constrained_class
));
2025 if (mono_class_is_nullable (constrained_class
)) {
2026 g_assert (mt
== MINT_TYPE_VT
);
2027 interp_add_ins (td
, MINT_BOX_NULLABLE
);
2028 td
->last_ins
->data
[0] = get_data_item_index (td
, constrained_class
);
2029 td
->last_ins
->data
[1] = csignature
->param_count
;
2030 td
->last_ins
->data
[2] = (td
->sp
- 1 - csignature
->param_count
)->type
!= STACK_TYPE_MP
? 0 : 1;
2032 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, constrained_class
, error
);
2033 return_if_nok (error
);
2035 if (mt
== MINT_TYPE_VT
) {
2036 interp_add_ins (td
, MINT_BOX_VT
);
2037 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
2038 td
->last_ins
->data
[1] = csignature
->param_count
;
2039 td
->last_ins
->data
[2] = (td
->sp
- 1 - csignature
->param_count
)->type
!= STACK_TYPE_MP
? 0 : 1;
2041 interp_add_ins (td
, MINT_BOX
);
2042 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
2043 td
->last_ins
->data
[1] = csignature
->param_count
;
2049 interp_get_method (MonoMethod
*method
, guint32 token
, MonoImage
*image
, MonoGenericContext
*generic_context
, MonoError
*error
)
2051 if (method
->wrapper_type
== MONO_WRAPPER_NONE
)
2052 return mono_get_method_checked (image
, token
, NULL
, generic_context
, error
);
2054 return (MonoMethod
*)mono_method_get_wrapper_data (method
, token
);
2057 /* Return FALSE if error, including inline failure */
2059 interp_transform_call (TransformData
*td
, MonoMethod
*method
, MonoMethod
*target_method
, MonoDomain
*domain
, MonoGenericContext
*generic_context
, unsigned char *is_bb_start
, MonoClass
*constrained_class
, gboolean readonly
, MonoError
*error
, gboolean check_visibility
, gboolean save_last_error
)
2061 MonoImage
*image
= m_class_get_image (method
->klass
);
2062 MonoMethodSignature
*csignature
;
2063 int is_virtual
= *td
->ip
== CEE_CALLVIRT
;
2064 int calli
= *td
->ip
== CEE_CALLI
|| *td
->ip
== CEE_MONO_CALLI_EXTRA_ARG
;
2066 guint32 vt_stack_used
= 0;
2067 guint32 vt_res_size
= 0;
2071 int need_null_check
= is_virtual
;
2073 guint32 token
= read32 (td
->ip
+ 1);
2075 if (target_method
== NULL
) {
2078 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
2079 csignature
= (MonoMethodSignature
*)mono_method_get_wrapper_data (method
, token
);
2081 csignature
= mono_metadata_parse_signature_checked (image
, token
, error
);
2082 return_val_if_nok (error
, FALSE
);
2085 if (generic_context
) {
2086 csignature
= mono_inflate_generic_signature (csignature
, generic_context
, error
);
2087 return_val_if_nok (error
, FALSE
);
2091 * The compiled interp entry wrapper is passed to runtime_invoke instead of
2092 * the InterpMethod pointer. FIXME
2094 native
= csignature
->pinvoke
|| method
->wrapper_type
== MONO_WRAPPER_RUNTIME_INVOKE
;
2096 target_method
= NULL
;
2098 target_method
= interp_get_method (method
, token
, image
, generic_context
, error
);
2099 return_val_if_nok (error
, FALSE
);
2100 csignature
= mono_method_signature_internal (target_method
);
2102 if (generic_context
) {
2103 csignature
= mono_inflate_generic_signature (csignature
, generic_context
, error
);
2104 return_val_if_nok (error
, FALSE
);
2105 target_method
= mono_class_inflate_generic_method_checked (target_method
, generic_context
, error
);
2106 return_val_if_nok (error
, FALSE
);
2110 csignature
= mono_method_signature_internal (target_method
);
2113 if (check_visibility
&& target_method
&& !mono_method_can_access_method (method
, target_method
))
2114 interp_generate_mae_throw (td
, method
, target_method
);
2116 if (target_method
&& target_method
->string_ctor
) {
2117 /* Create the real signature */
2118 MonoMethodSignature
*ctor_sig
= mono_metadata_signature_dup_mempool (td
->mempool
, csignature
);
2119 ctor_sig
->ret
= m_class_get_byval_arg (mono_defaults
.string_class
);
2121 csignature
= ctor_sig
;
2125 if (target_method
&& interp_handle_intrinsics (td
, target_method
, constrained_class
, csignature
, readonly
, &op
))
2128 if (constrained_class
) {
2129 if (m_class_is_enumtype (constrained_class
) && !strcmp (target_method
->name
, "GetHashCode")) {
2130 /* Use the corresponding method from the base type to avoid boxing */
2131 MonoType
*base_type
= mono_class_enum_basetype_internal (constrained_class
);
2132 g_assert (base_type
);
2133 constrained_class
= mono_class_from_mono_type_internal (base_type
);
2134 target_method
= mono_class_get_method_from_name_checked (constrained_class
, target_method
->name
, 0, 0, error
);
2135 mono_error_assert_ok (error
);
2136 g_assert (target_method
);
2140 if (constrained_class
) {
2141 mono_class_setup_vtable (constrained_class
);
2142 if (mono_class_has_failure (constrained_class
)) {
2143 mono_error_set_for_class_failure (error
, constrained_class
);
2147 g_print ("CONSTRAINED.CALLVIRT: %s::%s. %s (%p) ->\n", target_method
->klass
->name
, target_method
->name
, mono_signature_full_name (target_method
->signature
), target_method
);
2149 target_method
= mono_get_method_constrained_with_method (image
, target_method
, constrained_class
, generic_context
, error
);
2151 g_print (" : %s::%s. %s (%p)\n", target_method
->klass
->name
, target_method
->name
, mono_signature_full_name (target_method
->signature
), target_method
);
2153 /* Intrinsics: Try again, it could be that `mono_get_method_constrained_with_method` resolves to a method that we can substitute */
2154 if (target_method
&& interp_handle_intrinsics (td
, target_method
, constrained_class
, csignature
, readonly
, &op
))
2157 return_val_if_nok (error
, FALSE
);
2158 mono_class_setup_vtable (target_method
->klass
);
2160 if (!m_class_is_valuetype (constrained_class
)) {
2161 /* managed pointer on the stack, we need to deref that puppy */
2162 interp_add_ins (td
, MINT_LDIND_I
);
2163 td
->last_ins
->data
[0] = csignature
->param_count
;
2164 } else if (target_method
->klass
== mono_defaults
.object_class
|| target_method
->klass
== m_class_get_parent (mono_defaults
.enum_class
) || target_method
->klass
== mono_defaults
.enum_class
) {
2166 * Constrained expects a managed pointer that normally needs dereferencing.
2167 * For value types that have their storage on the vtstack, a managed pointer
2168 * to it is identical to the internal pointer that is passed on the stack
2169 * when using the value type, not needing any dereferencing.
2171 g_assert ((td
->sp
- csignature
->param_count
- 1)->type
== STACK_TYPE_MP
);
2172 if (mint_type (m_class_get_byval_arg (constrained_class
)) != MINT_TYPE_VT
) {
2173 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2174 interp_add_ins (td
, MINT_LDIND_I8
);
2175 td
->last_ins
->data
[0] = csignature
->param_count
;
2178 interp_constrained_box (td
, domain
, constrained_class
, csignature
, error
);
2179 return_val_if_nok (error
, FALSE
);
2181 if (target_method
->klass
!= constrained_class
) {
2183 * The type parameter is instantiated as a valuetype,
2184 * but that type doesn't override the method we're
2185 * calling, so we need to box `this'.
2187 g_assert ((td
->sp
- csignature
->param_count
- 1)->type
== STACK_TYPE_MP
);
2188 if (mint_type (m_class_get_byval_arg (constrained_class
)) != MINT_TYPE_VT
) {
2189 /* managed pointer on the stack, we need to deref that puppy */
2190 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2191 interp_add_ins (td
, MINT_LDIND_I8
);
2192 td
->last_ins
->data
[0] = csignature
->param_count
;
2195 interp_constrained_box (td
, domain
, constrained_class
, csignature
, error
);
2196 return_val_if_nok (error
, FALSE
);
2203 mono_class_init_internal (target_method
->klass
);
2205 if (!is_virtual
&& target_method
&& (target_method
->flags
& METHOD_ATTRIBUTE_ABSTRACT
)) {
2206 if (!mono_class_is_interface (method
->klass
))
2207 interp_generate_bie_throw (td
);
2212 if (is_virtual
&& target_method
&& (!(target_method
->flags
& METHOD_ATTRIBUTE_VIRTUAL
) ||
2213 (MONO_METHOD_IS_FINAL (target_method
) &&
2214 target_method
->wrapper_type
!= MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK
)) &&
2215 !(mono_class_is_marshalbyref (target_method
->klass
))) {
2216 /* Not really virtual, just needs a null check */
2218 need_null_check
= TRUE
;
2221 CHECK_STACK (td
, csignature
->param_count
+ csignature
->hasthis
);
2222 if (!td
->gen_sdb_seq_points
&& !calli
&& op
== -1 && (!is_virtual
|| (target_method
->flags
& METHOD_ATTRIBUTE_VIRTUAL
) == 0) &&
2223 (target_method
->flags
& METHOD_ATTRIBUTE_PINVOKE_IMPL
) == 0 &&
2224 (target_method
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
) == 0 &&
2225 !(target_method
->iflags
& METHOD_IMPL_ATTRIBUTE_NOINLINING
)) {
2226 (void)mono_class_vtable_checked (domain
, target_method
->klass
, error
);
2227 return_val_if_nok (error
, FALSE
);
2229 if (method
== target_method
&& *(td
->ip
+ 5) == CEE_RET
&& !(csignature
->hasthis
&& m_class_is_valuetype (target_method
->klass
))) {
2230 if (td
->inlined_method
)
2233 if (td
->verbose_level
)
2234 g_print ("Optimize tail call of %s.%s\n", m_class_get_name (target_method
->klass
), target_method
->name
);
2236 for (i
= csignature
->param_count
- 1 + !!csignature
->hasthis
; i
>= 0; --i
)
2239 interp_add_ins (td
, MINT_BR_S
);
2240 // We are branching to the beginning of the method
2241 td
->last_ins
->data
[0] = 0;
2242 if (!is_bb_start
[td
->ip
+ 5 - td
->il_code
])
2243 ++td
->ip
; /* gobble the CEE_RET if it isn't branched to */
2249 target_method
= interp_transform_internal_calls (method
, target_method
, csignature
, is_virtual
);
2251 if (csignature
->call_convention
== MONO_CALL_VARARG
) {
2252 csignature
= mono_method_get_signature_checked (target_method
, image
, token
, generic_context
, error
);
2253 int vararg_stack
= 0;
2255 * For vararg calls, ArgIterator expects the signature and the varargs to be
2256 * stored in a linear memory. We allocate the necessary vt_stack space for
2257 * this. All varargs will be pushed to the vt_stack at call site.
2259 vararg_stack
+= sizeof (gpointer
);
2260 for (i
= csignature
->sentinelpos
; i
< csignature
->param_count
; ++i
) {
2261 int align
, arg_size
;
2262 arg_size
= mono_type_stack_size (csignature
->params
[i
], &align
);
2263 vararg_stack
= ALIGN_TO (vararg_stack
, align
);
2264 vararg_stack
+= arg_size
;
2266 /* allocate space for the pointer to varargs space start */
2267 vararg_stack
+= sizeof (gpointer
);
2268 vt_stack_used
+= ALIGN_TO (vararg_stack
, MINT_VT_ALIGNMENT
);
2269 PUSH_VT (td
, vararg_stack
);
2272 if (need_null_check
) {
2273 interp_add_ins (td
, MINT_CKNULL_N
);
2274 td
->last_ins
->data
[0] = csignature
->param_count
+ 1;
2277 g_assert (csignature
->call_convention
!= MONO_CALL_FASTCALL
);
2278 if ((mono_interp_opt
& INTERP_OPT_INLINE
) && op
== -1 && !is_virtual
&& target_method
&& interp_method_check_inlining (td
, target_method
)) {
2279 MonoMethodHeader
*mheader
= interp_method_get_header (target_method
, error
);
2280 return_val_if_nok (error
, FALSE
);
2282 if (interp_inline_method (td
, target_method
, mheader
, error
)) {
2288 /* Don't inline methods that do calls */
2289 if (op
== -1 && td
->inlined_method
)
2292 /* We need to convert delegate invoke to a indirect call on the interp_invoke_impl field */
2293 if (target_method
&& m_class_get_parent (target_method
->klass
) == mono_defaults
.multicastdelegate_class
) {
2294 const char *name
= target_method
->name
;
2295 if (*name
== 'I' && (strcmp (name
, "Invoke") == 0)) {
2297 interp_add_ins (td
, MINT_LD_DELEGATE_INVOKE_IMPL
);
2298 td
->last_ins
->data
[0] = csignature
->param_count
+ 1;
2299 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
2303 /* Pop the function pointer */
2307 td
->sp
-= csignature
->param_count
+ !!csignature
->hasthis
;
2308 for (i
= 0; i
< csignature
->param_count
; ++i
) {
2309 if (td
->sp
[i
+ !!csignature
->hasthis
].type
== STACK_TYPE_VT
) {
2311 MonoClass
*klass
= mono_class_from_mono_type_internal (csignature
->params
[i
]);
2312 if (csignature
->pinvoke
&& method
->wrapper_type
!= MONO_WRAPPER_NONE
)
2313 size
= mono_class_native_size (klass
, NULL
);
2315 size
= mono_class_value_size (klass
, NULL
);
2316 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
2317 vt_stack_used
+= size
;
2321 /* need to handle typedbyref ... */
2322 if (csignature
->ret
->type
!= MONO_TYPE_VOID
) {
2323 int mt
= mint_type(csignature
->ret
);
2324 MonoClass
*klass
= mono_class_from_mono_type_internal (csignature
->ret
);
2325 if (mt
== MINT_TYPE_VT
) {
2326 if (csignature
->pinvoke
&& method
->wrapper_type
!= MONO_WRAPPER_NONE
)
2327 vt_res_size
= mono_class_native_size (klass
, NULL
);
2329 vt_res_size
= mono_class_value_size (klass
, NULL
);
2330 if (mono_class_has_failure (klass
)) {
2331 mono_error_set_for_class_failure (error
, klass
);
2334 PUSH_VT(td
, vt_res_size
);
2336 PUSH_TYPE(td
, stack_type
[mt
], klass
);
2341 interp_add_ins (td
, op
);
2343 if (op
== MINT_LDLEN
) {
2344 #ifdef MONO_BIG_ARRAYS
2345 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I8
);
2347 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
2351 #ifndef ENABLE_NETCORE
2352 if (op
== MINT_CALLRUN
) {
2353 td
->last_ins
->data
[0] = get_data_item_index (td
, target_method
);
2354 td
->last_ins
->data
[1] = get_data_item_index (td
, mono_method_signature_internal (target_method
));
2357 } else if (!calli
&& !is_virtual
&& mono_interp_jit_call_supported (target_method
, csignature
)) {
2358 interp_add_ins (td
, MINT_JIT_CALL
);
2359 td
->last_ins
->data
[0] = get_data_item_index (td
, (void *)mono_interp_get_imethod (domain
, target_method
, error
));
2360 mono_error_assert_ok (error
);
2362 #ifndef MONO_ARCH_HAS_NO_PROPER_MONOCTX
2363 /* Try using fast icall path for simple signatures */
2364 if (native
&& !method
->dynamic
)
2365 op
= interp_icall_op_for_sig (csignature
);
2367 if (csignature
->call_convention
== MONO_CALL_VARARG
)
2368 interp_add_ins (td
, MINT_CALL_VARARG
);
2370 interp_add_ins (td
, native
? ((op
!= -1) ? MINT_CALLI_NAT_FAST
: MINT_CALLI_NAT
) : MINT_CALLI
);
2371 else if (is_virtual
&& !mono_class_is_marshalbyref (target_method
->klass
))
2372 interp_add_ins (td
, is_void
? MINT_VCALLVIRT_FAST
: MINT_CALLVIRT_FAST
);
2373 else if (is_virtual
)
2374 interp_add_ins (td
, is_void
? MINT_VCALLVIRT
: MINT_CALLVIRT
);
2376 interp_add_ins (td
, is_void
? MINT_VCALL
: MINT_CALL
);
2379 td
->last_ins
->data
[0] = get_data_item_index (td
, (void *)csignature
);
2381 td
->last_ins
->data
[1] = op
;
2382 if (td
->last_ins
->opcode
== MINT_CALLI_NAT_FAST
)
2383 td
->last_ins
->data
[2] = save_last_error
;
2384 } else if (op
== -1 && td
->last_ins
->opcode
== MINT_CALLI_NAT
) {
2385 td
->last_ins
->data
[1] = save_last_error
;
2388 InterpMethod
*imethod
= mono_interp_get_imethod (domain
, target_method
, error
);
2389 td
->last_ins
->data
[0] = get_data_item_index (td
, (void *)imethod
);
2390 td
->last_ins
->data
[1] = imethod
->param_count
+ imethod
->hasthis
;
2391 #ifdef ENABLE_EXPERIMENT_TIERED
2392 if (MINT_IS_PATCHABLE_CALL (td
->last_ins
->opcode
)) {
2393 g_assert (!calli
&& !is_virtual
);
2394 td
->last_ins
->flags
|= INTERP_INST_FLAG_RECORD_CALL_PATCH
;
2395 g_hash_table_insert (td
->patchsite_hash
, td
->last_ins
, target_method
);
2398 return_val_if_nok (error
, FALSE
);
2399 if (csignature
->call_convention
== MONO_CALL_VARARG
)
2400 td
->last_ins
->data
[1] = get_data_item_index (td
, (void *)csignature
);
2401 else if (is_virtual
&& !mono_class_is_marshalbyref (target_method
->klass
)) {
2402 /* FIXME Use fastpath also for MBRO. Asserts in mono_method_get_vtable_slot */
2403 if (mono_class_is_interface (target_method
->klass
))
2404 td
->last_ins
->data
[1] = -2 * MONO_IMT_SIZE
+ mono_method_get_imt_slot (target_method
);
2406 td
->last_ins
->data
[1] = mono_method_get_vtable_slot (target_method
);
2411 if (vt_stack_used
!= 0 || vt_res_size
!= 0) {
2412 interp_add_ins (td
, MINT_VTRESULT
);
2413 td
->last_ins
->data
[0] = vt_res_size
;
2414 WRITE32_INS (td
->last_ins
, 1, &vt_stack_used
);
2415 td
->vt_sp
-= vt_stack_used
;
2421 static MonoClassField
*
2422 interp_field_from_token (MonoMethod
*method
, guint32 token
, MonoClass
**klass
, MonoGenericContext
*generic_context
, MonoError
*error
)
2424 MonoClassField
*field
= NULL
;
2425 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
) {
2426 field
= (MonoClassField
*) mono_method_get_wrapper_data (method
, token
);
2427 *klass
= field
->parent
;
2429 mono_class_setup_fields (field
->parent
);
2431 field
= mono_field_from_token_checked (m_class_get_image (method
->klass
), token
, klass
, generic_context
, error
);
2432 return_val_if_nok (error
, NULL
);
2435 if (!method
->skip_visibility
&& !mono_method_can_access_field (method
, field
)) {
2436 char *method_fname
= mono_method_full_name (method
, TRUE
);
2437 char *field_fname
= mono_field_full_name (field
);
2438 mono_error_set_generic_error (error
, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname
, method_fname
);
2439 g_free (method_fname
);
2440 g_free (field_fname
);
2447 static InterpBasicBlock
*
2448 get_bb (TransformData
*td
, InterpBasicBlock
*cbb
, unsigned char *ip
)
2450 int offset
= ip
- td
->il_code
;
2451 InterpBasicBlock
*bb
= td
->offset_to_bb
[offset
];
2454 bb
= (InterpBasicBlock
*)mono_mempool_alloc0 (td
->mempool
, sizeof (InterpBasicBlock
));
2456 td
->offset_to_bb
[offset
] = bb
;
2458 td
->basic_blocks
= g_list_append_mempool (td
->mempool
, td
->basic_blocks
, bb
);
2462 bb
->preds
= g_slist_prepend_mempool (td
->mempool
, bb
->preds
, cbb
);
2469 * Compute the set of IL level basic blocks.
2472 get_basic_blocks (TransformData
*td
)
2474 guint8
*start
= (guint8
*)td
->il_code
;
2475 guint8
*end
= (guint8
*)td
->il_code
+ td
->code_size
;
2477 unsigned char *target
;
2480 const MonoOpcode
*opcode
;
2481 InterpBasicBlock
*cbb
;
2483 td
->offset_to_bb
= (InterpBasicBlock
**)mono_mempool_alloc0 (td
->mempool
, sizeof (InterpBasicBlock
*) * (end
- start
+ 1));
2484 td
->entry_bb
= cbb
= get_bb (td
, NULL
, start
);
2487 cli_addr
= ip
- start
;
2488 td
->offset_to_bb
[cli_addr
] = cbb
;
2489 i
= mono_opcode_value ((const guint8
**)&ip
, end
);
2490 opcode
= &mono_opcodes
[i
];
2491 switch (opcode
->argument
) {
2492 case MonoInlineNone
:
2495 case MonoInlineString
:
2496 case MonoInlineType
:
2497 case MonoInlineField
:
2498 case MonoInlineMethod
:
2501 case MonoShortInlineR
:
2508 case MonoShortInlineVar
:
2509 case MonoShortInlineI
:
2512 case MonoShortInlineBrTarget
:
2513 target
= start
+ cli_addr
+ 2 + (signed char)ip
[1];
2514 get_bb (td
, cbb
, target
);
2516 cbb
= get_bb (td
, cbb
, ip
);
2518 case MonoInlineBrTarget
:
2519 target
= start
+ cli_addr
+ 5 + (gint32
)read32 (ip
+ 1);
2520 get_bb (td
, cbb
, target
);
2522 cbb
= get_bb (td
, cbb
, ip
);
2524 case MonoInlineSwitch
: {
2525 guint32 n
= read32 (ip
+ 1);
2528 cli_addr
+= 5 + 4 * n
;
2529 target
= start
+ cli_addr
;
2530 get_bb (td
, cbb
, target
);
2532 for (j
= 0; j
< n
; ++j
) {
2533 target
= start
+ cli_addr
+ (gint32
)read32 (ip
);
2534 get_bb (td
, cbb
, target
);
2537 cbb
= get_bb (td
, cbb
, ip
);
2545 g_assert_not_reached ();
2549 cbb
= get_bb (td
, NULL
, ip
);
2554 interp_save_debug_info (InterpMethod
*rtm
, MonoMethodHeader
*header
, TransformData
*td
, GArray
*line_numbers
)
2556 MonoDebugMethodJitInfo
*dinfo
;
2559 if (!mono_debug_enabled ())
2563 * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
2566 dinfo
= g_new0 (MonoDebugMethodJitInfo
, 1);
2567 dinfo
->num_params
= rtm
->param_count
;
2568 dinfo
->params
= g_new0 (MonoDebugVarInfo
, dinfo
->num_params
);
2569 dinfo
->num_locals
= header
->num_locals
;
2570 dinfo
->locals
= g_new0 (MonoDebugVarInfo
, header
->num_locals
);
2571 dinfo
->code_start
= (guint8
*)rtm
->code
;
2572 dinfo
->code_size
= td
->new_code_end
- td
->new_code
;
2573 dinfo
->epilogue_begin
= 0;
2574 dinfo
->has_var_info
= TRUE
;
2575 dinfo
->num_line_numbers
= line_numbers
->len
;
2576 dinfo
->line_numbers
= g_new0 (MonoDebugLineNumberEntry
, dinfo
->num_line_numbers
);
2578 for (i
= 0; i
< dinfo
->num_params
; i
++) {
2579 MonoDebugVarInfo
*var
= &dinfo
->params
[i
];
2580 var
->type
= rtm
->param_types
[i
];
2582 for (i
= 0; i
< dinfo
->num_locals
; i
++) {
2583 MonoDebugVarInfo
*var
= &dinfo
->locals
[i
];
2584 var
->type
= mono_metadata_type_dup (NULL
, header
->locals
[i
]);
2587 for (i
= 0; i
< dinfo
->num_line_numbers
; i
++)
2588 dinfo
->line_numbers
[i
] = g_array_index (line_numbers
, MonoDebugLineNumberEntry
, i
);
2589 mono_debug_add_method (rtm
->method
, dinfo
, rtm
->domain
);
2591 mono_debug_free_method_jit_info (dinfo
);
2594 /* Same as the code in seq-points.c */
2596 insert_pred_seq_point (SeqPoint
*last_sp
, SeqPoint
*sp
, GSList
**next
)
2599 int src_index
= last_sp
->next_offset
;
2600 int dst_index
= sp
->next_offset
;
2602 /* bb->in_bb might contain duplicates */
2603 for (l
= next
[src_index
]; l
; l
= l
->next
)
2604 if (GPOINTER_TO_UINT (l
->data
) == dst_index
)
2607 next
[src_index
] = g_slist_append (next
[src_index
], GUINT_TO_POINTER (dst_index
));
2611 recursively_make_pred_seq_points (TransformData
*td
, InterpBasicBlock
*bb
)
2613 SeqPoint
** const MONO_SEQ_SEEN_LOOP
= (SeqPoint
**)GINT_TO_POINTER(-1);
2616 GArray
*predecessors
= g_array_new (FALSE
, TRUE
, sizeof (gpointer
));
2617 GHashTable
*seen
= g_hash_table_new_full (g_direct_hash
, NULL
, NULL
, NULL
);
2619 // Insert/remove sentinel into the memoize table to detect loops containing bb
2620 bb
->pred_seq_points
= MONO_SEQ_SEEN_LOOP
;
2622 for (l
= bb
->preds
; l
; l
= l
->next
) {
2623 InterpBasicBlock
*in_bb
= (InterpBasicBlock
*)l
->data
;
2625 // This bb has the last seq point, append it and continue
2626 if (in_bb
->last_seq_point
!= NULL
) {
2627 predecessors
= g_array_append_val (predecessors
, in_bb
->last_seq_point
);
2631 // We've looped or handled this before, exit early.
2632 // No last sequence points to find.
2633 if (in_bb
->pred_seq_points
== MONO_SEQ_SEEN_LOOP
)
2636 // Take sequence points from incoming basic blocks
2638 if (in_bb
== td
->entry_bb
)
2641 if (in_bb
->pred_seq_points
== NULL
)
2642 recursively_make_pred_seq_points (td
, in_bb
);
2644 // Union sequence points with incoming bb's
2645 for (int i
=0; i
< in_bb
->num_pred_seq_points
; i
++) {
2646 if (!g_hash_table_lookup (seen
, in_bb
->pred_seq_points
[i
])) {
2647 g_array_append_val (predecessors
, in_bb
->pred_seq_points
[i
]);
2648 g_hash_table_insert (seen
, in_bb
->pred_seq_points
[i
], (gpointer
)&MONO_SEQ_SEEN_LOOP
);
2651 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
2654 g_hash_table_destroy (seen
);
2656 if (predecessors
->len
!= 0) {
2657 bb
->pred_seq_points
= (SeqPoint
**)mono_mempool_alloc0 (td
->mempool
, sizeof (SeqPoint
*) * predecessors
->len
);
2658 bb
->num_pred_seq_points
= predecessors
->len
;
2660 for (int newer
= 0; newer
< bb
->num_pred_seq_points
; newer
++) {
2661 bb
->pred_seq_points
[newer
] = (SeqPoint
*)g_array_index (predecessors
, gpointer
, newer
);
2665 g_array_free (predecessors
, TRUE
);
2669 collect_pred_seq_points (TransformData
*td
, InterpBasicBlock
*bb
, SeqPoint
*seqp
, GSList
**next
)
2671 // Doesn't have a last sequence point, must find from incoming basic blocks
2672 if (bb
->pred_seq_points
== NULL
&& bb
!= td
->entry_bb
)
2673 recursively_make_pred_seq_points (td
, bb
);
2675 for (int i
= 0; i
< bb
->num_pred_seq_points
; i
++)
2676 insert_pred_seq_point (bb
->pred_seq_points
[i
], seqp
, next
);
2682 save_seq_points (TransformData
*td
, MonoJitInfo
*jinfo
)
2685 int i
, seq_info_size
;
2686 MonoSeqPointInfo
*info
;
2687 GSList
**next
= NULL
;
2690 if (!td
->gen_sdb_seq_points
)
2694 * For each sequence point, compute the list of sequence points immediately
2695 * following it, this is needed to implement 'step over' in the debugger agent.
2696 * Similar to the code in mono_save_seq_point_info ().
2698 for (i
= 0; i
< td
->seq_points
->len
; ++i
) {
2699 SeqPoint
*sp
= (SeqPoint
*)g_ptr_array_index (td
->seq_points
, i
);
2701 /* Store the seq point index here temporarily */
2702 sp
->next_offset
= i
;
2704 next
= (GSList
**)mono_mempool_alloc0 (td
->mempool
, sizeof (GList
*) * td
->seq_points
->len
);
2705 for (bblist
= td
->basic_blocks
; bblist
; bblist
= bblist
->next
) {
2706 InterpBasicBlock
*bb
= (InterpBasicBlock
*)bblist
->data
;
2708 GSList
*bb_seq_points
= g_slist_reverse (bb
->seq_points
);
2709 SeqPoint
*last
= NULL
;
2710 for (GSList
*l
= bb_seq_points
; l
; l
= l
->next
) {
2711 SeqPoint
*sp
= (SeqPoint
*)l
->data
;
2713 if (sp
->il_offset
== METHOD_ENTRY_IL_OFFSET
|| sp
->il_offset
== METHOD_EXIT_IL_OFFSET
)
2714 /* Used to implement method entry/exit events */
2718 /* Link with the previous seq point in the same bb */
2719 next
[last
->next_offset
] = g_slist_append_mempool (td
->mempool
, next
[last
->next_offset
], GINT_TO_POINTER (sp
->next_offset
));
2721 /* Link with the last bb in the previous bblocks */
2722 collect_pred_seq_points (td
, bb
, sp
, next
);
2728 /* Serialize the seq points into a byte array */
2729 array
= g_byte_array_new ();
2730 SeqPoint zero_seq_point
= {0};
2731 SeqPoint
* last_seq_point
= &zero_seq_point
;
2732 for (i
= 0; i
< td
->seq_points
->len
; ++i
) {
2733 SeqPoint
*sp
= (SeqPoint
*)g_ptr_array_index (td
->seq_points
, i
);
2735 sp
->next_offset
= 0;
2736 if (mono_seq_point_info_add_seq_point (array
, sp
, last_seq_point
, next
[i
], TRUE
))
2737 last_seq_point
= sp
;
2740 if (td
->verbose_level
) {
2741 g_print ("\nSEQ POINT MAP FOR %s: \n", td
->method
->name
);
2743 for (i
= 0; i
< td
->seq_points
->len
; ++i
) {
2744 SeqPoint
*sp
= (SeqPoint
*)g_ptr_array_index (td
->seq_points
, i
);
2750 g_print ("\tIL0x%x[0x%0x] ->", sp
->il_offset
, sp
->native_offset
);
2751 for (l
= next
[i
]; l
; l
= l
->next
) {
2752 int next_index
= GPOINTER_TO_UINT (l
->data
);
2753 g_print (" IL0x%x", ((SeqPoint
*)g_ptr_array_index (td
->seq_points
, next_index
))->il_offset
);
2759 info
= mono_seq_point_info_new (array
->len
, TRUE
, array
->data
, TRUE
, &seq_info_size
);
2760 mono_atomic_fetch_add_i32 (&mono_jit_stats
.allocated_seq_points_size
, seq_info_size
);
2762 g_byte_array_free (array
, TRUE
);
2764 jinfo
->seq_points
= info
;
2768 interp_emit_memory_barrier (TransformData
*td
, int kind
)
2770 #if defined(TARGET_WASM)
2771 // mono_memory_barrier is dummy on wasm
2772 #elif defined(TARGET_X86) || defined(TARGET_AMD64)
2773 if (kind
== MONO_MEMORY_BARRIER_SEQ
)
2774 interp_add_ins (td
, MINT_MONO_MEMORY_BARRIER
);
2776 interp_add_ins (td
, MINT_MONO_MEMORY_BARRIER
);
2780 #define BARRIER_IF_VOLATILE(td, kind) \
2783 interp_emit_memory_barrier (td, kind); \
2784 volatile_ = FALSE; \
2788 #define INLINE_FAILURE \
2795 interp_method_compute_offsets (TransformData
*td
, InterpMethod
*imethod
, MonoMethodSignature
*signature
, MonoMethodHeader
*header
)
2797 int i
, offset
, size
, align
;
2799 imethod
->local_offsets
= (guint32
*)g_malloc (header
->num_locals
* sizeof(guint32
));
2800 td
->locals
= (InterpLocal
*)g_malloc (header
->num_locals
* sizeof (InterpLocal
));
2801 td
->locals_size
= header
->num_locals
;
2802 td
->locals_capacity
= td
->locals_size
;
2804 for (i
= 0; i
< header
->num_locals
; ++i
) {
2805 size
= mono_type_size (header
->locals
[i
], &align
);
2806 offset
+= align
- 1;
2807 offset
&= ~(align
- 1);
2808 imethod
->local_offsets
[i
] = offset
;
2809 td
->locals
[i
].offset
= offset
;
2810 td
->locals
[i
].flags
= 0;
2811 td
->locals
[i
].type
= header
->locals
[i
];
2812 td
->locals
[i
].mt
= mint_type (header
->locals
[i
]);
2815 offset
= (offset
+ 7) & ~7;
2817 imethod
->exvar_offsets
= (guint32
*)g_malloc (header
->num_clauses
* sizeof (guint32
));
2818 for (i
= 0; i
< header
->num_clauses
; i
++) {
2819 imethod
->exvar_offsets
[i
] = offset
;
2820 offset
+= sizeof (MonoObject
*);
2822 offset
= (offset
+ 7) & ~7;
2824 imethod
->locals_size
= offset
;
2825 g_assert (imethod
->locals_size
< 65536);
2826 td
->total_locals_size
= offset
;
2830 mono_test_interp_method_compute_offsets (TransformData
*td
, InterpMethod
*imethod
, MonoMethodSignature
*signature
, MonoMethodHeader
*header
)
2832 interp_method_compute_offsets (td
, imethod
, signature
, header
);
2835 /* Return false is failure to init basic blocks due to being in inline method */
2837 init_bb_start (TransformData
*td
, MonoMethodHeader
*header
, gboolean inlining
)
2839 const unsigned char *ip
, *end
;
2840 const MonoOpcode
*opcode
;
2841 int offset
, i
, in
, backwards
;
2843 /* intern the strings in the method. */
2845 end
= ip
+ header
->code_size
;
2847 /* inlined method continues the basic block of parent method */
2849 td
->is_bb_start
[0] = 1;
2856 else if (in
== 0xf0) {
2858 in
= *ip
+ MONO_CEE_MONO_ICALL
;
2860 opcode
= &mono_opcodes
[in
];
2861 switch (opcode
->argument
) {
2862 case MonoInlineNone
:
2865 case MonoInlineString
:
2868 case MonoInlineType
:
2871 case MonoInlineMethod
:
2874 case MonoInlineField
:
2878 case MonoShortInlineR
:
2881 case MonoInlineBrTarget
:
2882 offset
= read32 (ip
+ 1);
2884 /* this branch is ignored */
2885 if (offset
== 0 && in
== MONO_CEE_BR
)
2887 backwards
= offset
< 0;
2888 offset
+= ip
- header
->code
;
2889 g_assert (offset
>= 0 && offset
< header
->code_size
);
2892 td
->is_bb_start
[offset
] |= backwards
? 2 : 1;
2894 case MonoShortInlineBrTarget
:
2895 offset
= ((gint8
*)ip
) [1];
2897 /* this branch is ignored */
2898 if (offset
== 0 && in
== MONO_CEE_BR_S
)
2900 backwards
= offset
< 0;
2901 offset
+= ip
- header
->code
;
2902 g_assert (offset
>= 0 && offset
< header
->code_size
);
2905 td
->is_bb_start
[offset
] |= backwards
? 2 : 1;
2910 case MonoShortInlineVar
:
2911 case MonoShortInlineI
:
2914 case MonoInlineSwitch
: {
2916 const unsigned char *next_ip
;
2920 next_ip
= ip
+ 4 * n
;
2921 for (i
= 0; i
< n
; i
++) {
2922 offset
= read32 (ip
);
2923 backwards
= offset
< 0;
2924 offset
+= next_ip
- header
->code
;
2925 g_assert (offset
>= 0 && offset
< header
->code_size
);
2928 td
->is_bb_start
[offset
] |= backwards
? 2 : 1;
2938 g_assert_not_reached ();
2944 #ifdef NO_UNALIGNED_ACCESS
2946 get_unaligned_opcode (int opcode
)
2950 return MINT_LDFLD_I8_UNALIGNED
;
2952 return MINT_LDFLD_R8_UNALIGNED
;
2954 return MINT_STFLD_I8_UNALIGNED
;
2956 return MINT_STFLD_R8_UNALIGNED
;
2958 g_assert_not_reached ();
2965 interp_handle_isinst (TransformData
*td
, MonoClass
*klass
, gboolean isinst_instr
)
2967 /* Follow the logic from jit's handle_isinst */
2968 if (!mono_class_has_variant_generic_params (klass
)) {
2969 if (mono_class_is_interface (klass
))
2970 interp_add_ins (td
, isinst_instr
? MINT_ISINST_INTERFACE
: MINT_CASTCLASS_INTERFACE
);
2971 else if (!mono_class_is_marshalbyref (klass
) && m_class_get_rank (klass
) == 0 && !mono_class_is_nullable (klass
))
2972 interp_add_ins (td
, isinst_instr
? MINT_ISINST_COMMON
: MINT_CASTCLASS_COMMON
);
2974 interp_add_ins (td
, isinst_instr
? MINT_ISINST
: MINT_CASTCLASS
);
2976 interp_add_ins (td
, isinst_instr
? MINT_ISINST
: MINT_CASTCLASS
);
2978 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
2983 interp_emit_ldsflda (TransformData
*td
, MonoClassField
*field
, MonoError
*error
)
2985 MonoDomain
*domain
= td
->rtm
->domain
;
2986 // Initialize the offset for the field
2987 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, field
->parent
, error
);
2988 return_if_nok (error
);
2990 if (mono_class_field_is_special_static (field
)) {
2993 mono_domain_lock (domain
);
2994 g_assert (domain
->special_static_fields
);
2995 offset
= GPOINTER_TO_UINT (g_hash_table_lookup (domain
->special_static_fields
, field
));
2996 mono_domain_unlock (domain
);
2999 interp_add_ins (td
, MINT_LDSSFLDA
);
3000 WRITE32_INS(td
->last_ins
, 0, &offset
);
3002 interp_add_ins (td
, MINT_LDSFLDA
);
3003 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
3004 td
->last_ins
->data
[1] = get_data_item_index (td
, (char*)mono_vtable_get_static_field_data (vtable
) + field
->offset
);
3009 interp_emit_load_const (TransformData
*td
, gpointer field_addr
, int mt
)
3011 if (mt
>= MINT_TYPE_I1
&& mt
<= MINT_TYPE_I4
3012 #if SIZEOF_VOID_P == 4
3013 || mt
== MINT_TYPE_P
3019 val
= *(gint8
*)field_addr
;
3022 val
= *(guint8
*)field_addr
;
3025 val
= *(gint16
*)field_addr
;
3028 val
= *(guint16
*)field_addr
;
3031 val
= *(gint32
*)field_addr
;
3033 interp_get_ldc_i4_from_const (td
, NULL
, val
);
3034 } else if (mt
== MINT_TYPE_I8
3035 #if SIZEOF_VOID_P == 8
3036 || mt
== MINT_TYPE_P
3039 gint64 val
= *(gint64
*)field_addr
;
3040 interp_add_ins (td
, MINT_LDC_I8
);
3041 WRITE64_INS (td
->last_ins
, 0, &val
);
3042 } else if (mt
== MINT_TYPE_R4
) {
3043 float val
= *(float*)field_addr
;
3044 interp_add_ins (td
, MINT_LDC_R4
);
3045 WRITE32_INS (td
->last_ins
, 0, &val
);
3046 } else if (mt
== MINT_TYPE_R8
) {
3047 double val
= *(double*)field_addr
;
3048 interp_add_ins (td
, MINT_LDC_R8
);
3049 WRITE64_INS (td
->last_ins
, 0, &val
);
3059 * Emit some implicit conversions which are not part of the .net spec, but are allowed by MS.NET.
3062 emit_convert (TransformData
*td
, int stack_type
, MonoType
*ftype
)
3064 ftype
= mini_get_underlying_type (ftype
);
3067 switch (ftype
->type
) {
3068 case MONO_TYPE_I8
: {
3069 switch (stack_type
) {
3071 interp_add_ins (td
, MINT_CONV_I8_I4
);
3084 interp_emit_sfld_access (TransformData
*td
, MonoClassField
*field
, MonoClass
*field_class
, int mt
, gboolean is_load
, MonoError
*error
)
3086 MonoDomain
*domain
= td
->rtm
->domain
;
3087 // Initialize the offset for the field
3088 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, field
->parent
, error
);
3089 return_if_nok (error
);
3091 if (mono_class_field_is_special_static (field
)) {
3094 mono_domain_lock (domain
);
3095 g_assert (domain
->special_static_fields
);
3096 offset
= GPOINTER_TO_UINT (g_hash_table_lookup (domain
->special_static_fields
, field
));
3097 mono_domain_unlock (domain
);
3100 // Offset is SpecialStaticOffset
3101 if ((offset
& 0x80000000) == 0 && mt
!= MINT_TYPE_VT
) {
3102 // This field is thread static
3103 interp_add_ins (td
, (is_load
? MINT_LDTSFLD_I1
: MINT_STTSFLD_I1
) + mt
);
3104 WRITE32_INS(td
->last_ins
, 0, &offset
);
3106 if (mt
== MINT_TYPE_VT
) {
3107 interp_add_ins (td
, is_load
? MINT_LDSSFLD_VT
: MINT_STSSFLD_VT
);
3108 WRITE32_INS(td
->last_ins
, 0, &offset
);
3110 int size
= mono_class_value_size (field_class
, NULL
);
3111 WRITE32_INS(td
->last_ins
, 2, &size
);
3113 interp_add_ins (td
, is_load
? MINT_LDSSFLD
: MINT_STSSFLD
);
3114 td
->last_ins
->data
[0] = get_data_item_index (td
, field
);
3115 WRITE32_INS(td
->last_ins
, 1, &offset
);
3119 gpointer field_addr
= (char*)mono_vtable_get_static_field_data (vtable
) + field
->offset
;
3121 MonoType
*ftype
= mono_field_get_type_internal (field
);
3122 if (ftype
->attrs
& FIELD_ATTRIBUTE_INIT_ONLY
&& vtable
->initialized
) {
3123 if (interp_emit_load_const (td
, field_addr
, mt
))
3126 interp_add_ins (td
, (mt
== MINT_TYPE_VT
) ? MINT_LDSFLD_VT
: (MINT_LDSFLD_I1
+ mt
- MINT_TYPE_I1
));
3128 interp_add_ins (td
, (mt
== MINT_TYPE_VT
) ? MINT_STSFLD_VT
: (MINT_STSFLD_I1
+ mt
- MINT_TYPE_I1
));
3131 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
3132 td
->last_ins
->data
[1] = get_data_item_index (td
, (char*)field_addr
);
3134 if (mt
== MINT_TYPE_VT
) {
3135 int size
= mono_class_value_size (field_class
, NULL
);
3136 WRITE32_INS(td
->last_ins
, 2, &size
);
3142 generate_code (TransformData
*td
, MonoMethod
*method
, MonoMethodHeader
*header
, MonoGenericContext
*generic_context
, MonoError
*error
)
3145 int offset
, mt
, i
, i32
;
3148 const unsigned char *end
;
3149 MonoSimpleBasicBlock
*bb
= NULL
, *original_bb
= NULL
;
3150 gboolean sym_seq_points
= FALSE
;
3151 MonoBitSet
*seq_point_locs
= NULL
;
3152 gboolean readonly
= FALSE
;
3153 gboolean volatile_
= FALSE
;
3154 MonoClass
*constrained_class
= NULL
;
3156 MonoClassField
*field
;
3157 MonoImage
*image
= m_class_get_image (method
->klass
);
3158 InterpMethod
*rtm
= td
->rtm
;
3159 MonoDomain
*domain
= rtm
->domain
;
3160 MonoMethodSignature
*signature
= mono_method_signature_internal (method
);
3161 gboolean ret
= TRUE
;
3162 gboolean emitted_funccall_seq_point
= FALSE
;
3163 guint32
*arg_locals
= NULL
;
3164 guint32
*local_locals
= NULL
;
3165 InterpInst
*last_seq_point
= NULL
;
3166 gboolean save_last_error
= FALSE
;
3167 gboolean inlining
= td
->method
!= method
;
3169 original_bb
= bb
= mono_basic_block_split (method
, error
, header
);
3170 goto_if_nok (error
, exit
);
3173 td
->il_code
= header
->code
;
3174 td
->in_start
= td
->ip
= header
->code
;
3175 end
= td
->ip
+ header
->code_size
;
3177 if (!init_bb_start (td
, header
, inlining
))
3181 for (i
= 0; i
< header
->code_size
; i
++) {
3182 td
->stack_height
[i
] = -1;
3183 td
->clause_indexes
[i
] = -1;
3187 for (i
= 0; i
< header
->num_clauses
; i
++) {
3188 MonoExceptionClause
*c
= header
->clauses
+ i
;
3189 td
->stack_height
[c
->handler_offset
] = 0;
3190 td
->vt_stack_size
[c
->handler_offset
] = 0;
3191 td
->is_bb_start
[c
->handler_offset
] = 1;
3192 td
->is_bb_start
[c
->try_offset
] = 1;
3194 td
->stack_height
[c
->handler_offset
] = 1;
3195 td
->stack_state
[c
->handler_offset
] = (StackInfo
*)g_malloc0(sizeof(StackInfo
));
3196 td
->stack_state
[c
->handler_offset
][0].type
= STACK_TYPE_O
;
3197 td
->stack_state
[c
->handler_offset
][0].klass
= NULL
; /*FIX*/
3199 if (c
->flags
& MONO_EXCEPTION_CLAUSE_FILTER
) {
3200 td
->stack_height
[c
->data
.filter_offset
] = 0;
3201 td
->vt_stack_size
[c
->data
.filter_offset
] = 0;
3202 td
->is_bb_start
[c
->data
.filter_offset
] = 1;
3204 td
->stack_height
[c
->data
.filter_offset
] = 1;
3205 td
->stack_state
[c
->data
.filter_offset
] = (StackInfo
*)g_malloc0(sizeof(StackInfo
));
3206 td
->stack_state
[c
->data
.filter_offset
][0].type
= STACK_TYPE_O
;
3207 td
->stack_state
[c
->data
.filter_offset
][0].klass
= NULL
; /*FIX*/
3210 for (int j
= c
->handler_offset
; j
< c
->handler_offset
+ c
->handler_len
; ++j
) {
3211 if (td
->clause_indexes
[j
] == -1)
3212 td
->clause_indexes
[j
] = i
;
3216 if (td
->gen_sdb_seq_points
&& !inlining
) {
3217 MonoDebugMethodInfo
*minfo
;
3218 get_basic_blocks (td
);
3220 minfo
= mono_debug_lookup_method (method
);
3223 MonoSymSeqPoint
*sps
;
3224 int i
, n_il_offsets
;
3226 mono_debug_get_seq_points (minfo
, NULL
, NULL
, NULL
, &sps
, &n_il_offsets
);
3228 seq_point_locs
= mono_bitset_mem_new (mono_mempool_alloc0 (td
->mempool
, mono_bitset_alloc_size (header
->code_size
, 0)), header
->code_size
, 0);
3229 sym_seq_points
= TRUE
;
3231 for (i
= 0; i
< n_il_offsets
; ++i
) {
3232 if (sps
[i
].il_offset
< header
->code_size
)
3233 mono_bitset_set_fast (seq_point_locs
, sps
[i
].il_offset
);
3237 MonoDebugMethodAsyncInfo
* asyncMethod
= mono_debug_lookup_method_async_debug_info (method
);
3239 for (i
= 0; asyncMethod
!= NULL
&& i
< asyncMethod
->num_awaits
; i
++) {
3240 mono_bitset_set_fast (seq_point_locs
, asyncMethod
->resume_offsets
[i
]);
3241 mono_bitset_set_fast (seq_point_locs
, asyncMethod
->yield_offsets
[i
]);
3243 mono_debug_free_method_async_debug_info (asyncMethod
);
3245 } else if (!method
->wrapper_type
&& !method
->dynamic
&& mono_debug_image_has_debug_info (m_class_get_image (method
->klass
))) {
3246 /* Methods without line number info like auto-generated property accessors */
3247 seq_point_locs
= mono_bitset_new (header
->code_size
, 0);
3248 sym_seq_points
= TRUE
;
3252 if (sym_seq_points
) {
3253 last_seq_point
= interp_add_ins (td
, MINT_SDB_SEQ_POINT
);
3254 last_seq_point
->flags
|= INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY
;
3257 if (mono_debugger_method_has_breakpoint (method
))
3258 interp_add_ins (td
, MINT_BREAKPOINT
);
3261 if (td
->verbose_level
) {
3262 char *tmp
= mono_disasm_code (NULL
, method
, td
->ip
, end
);
3263 char *name
= mono_method_full_name (method
, TRUE
);
3264 g_print ("Method %s, original code:\n", name
);
3265 g_print ("%s\n", tmp
);
3270 if (header
->num_locals
&& header
->init_locals
)
3271 interp_add_ins (td
, MINT_INITLOCALS
);
3273 guint16 enter_profiling
= 0;
3274 if (mono_jit_trace_calls
!= NULL
&& mono_trace_eval (method
))
3275 enter_profiling
|= TRACING_FLAG
;
3276 if (rtm
->prof_flags
& MONO_PROFILER_CALL_INSTRUMENTATION_ENTER
)
3277 enter_profiling
|= PROFILING_FLAG
;
3278 if (enter_profiling
) {
3279 interp_add_ins (td
, MINT_PROF_ENTER
);
3280 td
->last_ins
->data
[0] = enter_profiling
;
3284 * If safepoints are required by default, always check for polling,
3285 * without emitting new instructions. This optimizes method entry in
3286 * the common scenario, which is coop.
3288 #if !defined(ENABLE_HYBRID_SUSPEND) && !defined(ENABLE_COOP_SUSPEND)
3289 /* safepoint is required on method entry */
3290 if (mono_threads_are_safepoints_enabled ())
3291 interp_add_ins (td
, MINT_SAFEPOINT
);
3295 arg_locals
= (guint32
*) g_malloc ((!!signature
->hasthis
+ signature
->param_count
) * sizeof (guint32
));
3296 /* Allocate locals to store inlined method args from stack */
3297 for (i
= signature
->param_count
- 1; i
>= 0; i
--) {
3298 local
= create_interp_local (td
, signature
->params
[i
]);
3299 arg_locals
[i
+ !!signature
->hasthis
] = local
;
3300 store_local (td
, local
);
3303 if (signature
->hasthis
) {
3305 * If this is value type, it is passed by address and not by value.
3306 * FIXME We should use MINT_TYPE_P instead of MINT_TYPE_O
3308 MonoType
*type
= mono_get_object_type ();
3309 local
= create_interp_local (td
, type
);
3310 arg_locals
[0] = local
;
3311 store_local (td
, local
);
3314 local_locals
= (guint32
*) g_malloc (header
->num_locals
* sizeof (guint32
));
3315 /* Allocate locals to store inlined method args from stack */
3316 for (i
= 0; i
< header
->num_locals
; i
++)
3317 local_locals
[i
] = create_interp_local (td
, header
->locals
[i
]);
3320 while (td
->ip
< end
) {
3321 g_assert (td
->sp
>= td
->stack
);
3322 g_assert (td
->vt_sp
< 0x10000000);
3323 in_offset
= td
->ip
- header
->code
;
3325 td
->current_il_offset
= in_offset
;
3326 td
->in_start
= td
->ip
;
3327 InterpInst
*prev_last_ins
= td
->last_ins
;
3329 // Inlined method doesn't have clauses or branches
3330 if (!inlining
&& td
->stack_height
[in_offset
] >= 0) {
3331 g_assert (td
->is_bb_start
[in_offset
]);
3332 if (td
->stack_height
[in_offset
] > 0)
3333 memcpy (td
->stack
, td
->stack_state
[in_offset
], td
->stack_height
[in_offset
] * sizeof(td
->stack
[0]));
3334 td
->sp
= td
->stack
+ td
->stack_height
[in_offset
];
3335 td
->vt_sp
= td
->vt_stack_size
[in_offset
];
3338 if (in_offset
== bb
->end
)
3342 int op_size
= mono_opcode_size (td
->ip
, end
);
3343 g_assert (op_size
> 0); /* The BB formation pass must catch all bad ops */
3345 if (td
->verbose_level
> 1)
3346 g_print ("SKIPPING DEAD OP at %x\n", in_offset
);
3352 if (td
->verbose_level
> 1) {
3353 g_print ("IL_%04lx %s %-10s, sp %ld, %s %-12s vt_sp %u (max %u)\n",
3354 td
->ip
- td
->il_code
,
3355 td
->is_bb_start
[td
->ip
- td
->il_code
] == 3 ? "<>" :
3356 td
->is_bb_start
[td
->ip
- td
->il_code
] == 2 ? "< " :
3357 td
->is_bb_start
[td
->ip
- td
->il_code
] == 1 ? " >" : " ",
3358 mono_opcode_name (*td
->ip
), td
->sp
- td
->stack
,
3359 td
->sp
> td
->stack
? stack_type_string
[td
->sp
[-1].type
] : " ",
3360 (td
->sp
> td
->stack
&& (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_VT
)) ? (td
->sp
[-1].klass
== NULL
? "?" : m_class_get_name (td
->sp
[-1].klass
)) : "",
3361 td
->vt_sp
, td
->max_vt_sp
);
3364 if (sym_seq_points
&& mono_bitset_test_fast (seq_point_locs
, td
->ip
- header
->code
)) {
3365 InterpBasicBlock
*cbb
= td
->offset_to_bb
[td
->ip
- header
->code
];
3369 * Make methods interruptable at the beginning, and at the targets of
3370 * backward branches.
3372 if (in_offset
== 0 || g_slist_length (cbb
->preds
) > 1)
3373 interp_add_ins (td
, MINT_SDB_INTR_LOC
);
3375 last_seq_point
= interp_add_ins (td
, MINT_SDB_SEQ_POINT
);
3378 if (!inlining
&& td
->is_bb_start
[in_offset
]) {
3379 int index
= td
->clause_indexes
[in_offset
];
3381 MonoExceptionClause
*clause
= &header
->clauses
[index
];
3382 if ((clause
->flags
== MONO_EXCEPTION_CLAUSE_FINALLY
||
3383 clause
->flags
== MONO_EXCEPTION_CLAUSE_FAULT
) &&
3384 in_offset
== clause
->handler_offset
)
3385 interp_add_ins (td
, MINT_START_ABORT_PROT
);
3392 emitted_funccall_seq_point
= FALSE
;
3396 SIMPLE_OP(td
, MINT_BREAK
);
3402 int arg_n
= *td
->ip
- CEE_LDARG_0
;
3404 load_arg (td
, arg_n
);
3406 load_local (td
, arg_locals
[arg_n
]);
3414 int loc_n
= *td
->ip
- CEE_LDLOC_0
;
3416 load_local (td
, loc_n
);
3418 load_local (td
, local_locals
[loc_n
]);
3426 int loc_n
= *td
->ip
- CEE_STLOC_0
;
3428 store_local (td
, loc_n
);
3430 store_local (td
, local_locals
[loc_n
]);
3435 int arg_n
= ((guint8
*)td
->ip
)[1];
3437 load_arg (td
, arg_n
);
3439 load_local (td
, arg_locals
[arg_n
]);
3443 case CEE_LDARGA_S
: {
3444 /* NOTE: n includes this */
3445 int n
= ((guint8
*) td
->ip
) [1];
3448 get_arg_type_exact (td
, n
, &mt
);
3449 interp_add_ins (td
, mt
== MINT_TYPE_VT
? MINT_LDARGA_VT
: MINT_LDARGA
);
3450 td
->last_ins
->data
[0] = n
;
3452 int loc_n
= arg_locals
[n
];
3453 interp_add_ins (td
, MINT_LDLOCA_S
);
3454 td
->last_ins
->data
[0] = loc_n
;
3455 td
->locals
[loc_n
].flags
|= INTERP_LOCAL_FLAG_INDIRECT
;
3457 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_MP
);
3462 int arg_n
= ((guint8
*)td
->ip
)[1];
3464 store_arg (td
, arg_n
);
3466 store_local (td
, arg_locals
[arg_n
]);
3471 int loc_n
= ((guint8
*)td
->ip
)[1];
3473 load_local (td
, loc_n
);
3475 load_local (td
, local_locals
[loc_n
]);
3479 case CEE_LDLOCA_S
: {
3480 int loc_n
= ((guint8
*)td
->ip
)[1];
3481 interp_add_ins (td
, MINT_LDLOCA_S
);
3483 loc_n
= local_locals
[loc_n
];
3484 td
->last_ins
->data
[0] = loc_n
;
3485 td
->locals
[loc_n
].flags
|= INTERP_LOCAL_FLAG_INDIRECT
;
3486 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_MP
);
3491 int loc_n
= ((guint8
*)td
->ip
)[1];
3493 store_local (td
, loc_n
);
3495 store_local (td
, local_locals
[loc_n
]);
3500 SIMPLE_OP(td
, MINT_LDNULL
);
3501 PUSH_TYPE(td
, STACK_TYPE_O
, NULL
);
3504 SIMPLE_OP(td
, MINT_LDC_I4_M1
);
3505 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3508 // Only single basic block functions are inlined.
3509 if (td
->ip
- td
->il_code
+ 2 < td
->code_size
&& (inlining
|| !td
->is_bb_start
[td
->ip
+ 1 - td
->il_code
]) && td
->ip
[1] == 0xfe && td
->ip
[2] == CEE_CEQ
&&
3510 td
->sp
> td
->stack
&& td
->sp
[-1].type
== STACK_TYPE_I4
) {
3511 SIMPLE_OP(td
, MINT_CEQ0_I4
);
3514 SIMPLE_OP(td
, MINT_LDC_I4_0
);
3515 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3519 // Only single basic block functions are inlined.
3520 if (td
->ip
- td
->il_code
+ 1 < td
->code_size
&& (inlining
|| !td
->is_bb_start
[td
->ip
+ 1 - td
->il_code
]) &&
3521 (td
->ip
[1] == CEE_ADD
|| td
->ip
[1] == CEE_SUB
) && td
->sp
[-1].type
== STACK_TYPE_I4
) {
3522 interp_add_ins (td
, td
->ip
[1] == CEE_ADD
? MINT_ADD1_I4
: MINT_SUB1_I4
);
3525 SIMPLE_OP(td
, MINT_LDC_I4_1
);
3526 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3536 SIMPLE_OP(td
, (*td
->ip
- CEE_LDC_I4_0
) + MINT_LDC_I4_0
);
3537 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3540 interp_add_ins (td
, MINT_LDC_I4_S
);
3541 td
->last_ins
->data
[0] = ((gint8
*) td
->ip
) [1];
3543 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3546 i32
= read32 (td
->ip
+ 1);
3547 interp_add_ins (td
, MINT_LDC_I4
);
3548 WRITE32_INS (td
->last_ins
, 0, &i32
);
3550 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3553 gint64 val
= read64 (td
->ip
+ 1);
3554 interp_add_ins (td
, MINT_LDC_I8
);
3555 WRITE64_INS (td
->last_ins
, 0, &val
);
3557 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I8
);
3562 readr4 (td
->ip
+ 1, &val
);
3563 interp_add_ins (td
, MINT_LDC_R4
);
3564 WRITE32_INS (td
->last_ins
, 0, &val
);
3566 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_R4
);
3571 readr8 (td
->ip
+ 1, &val
);
3572 interp_add_ins (td
, MINT_LDC_R8
);
3573 WRITE64_INS (td
->last_ins
, 0, &val
);
3575 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_R8
);
3579 int type
= td
->sp
[-1].type
;
3580 MonoClass
*klass
= td
->sp
[-1].klass
;
3581 if (td
->sp
[-1].type
== STACK_TYPE_VT
) {
3582 gint32 size
= mono_class_value_size (klass
, NULL
);
3584 interp_add_ins (td
, MINT_DUP_VT
);
3585 WRITE32_INS (td
->last_ins
, 0, &size
);
3588 SIMPLE_OP(td
, MINT_DUP
);
3589 PUSH_TYPE(td
, type
, klass
);
3594 SIMPLE_OP(td
, MINT_POP
);
3595 if (td
->sp
[-1].type
== STACK_TYPE_VT
) {
3596 int size
= mono_class_value_size (td
->sp
[-1].klass
, NULL
);
3597 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
3598 interp_add_ins (td
, MINT_VTRESULT
);
3599 td
->last_ins
->data
[0] = 0;
3600 WRITE32_INS (td
->last_ins
, 1, &size
);
3608 if (td
->sp
> td
->stack
)
3609 g_warning ("CEE_JMP: stack must be empty");
3610 token
= read32 (td
->ip
+ 1);
3611 m
= mono_get_method_checked (image
, token
, NULL
, generic_context
, error
);
3612 goto_if_nok (error
, exit
);
3613 interp_add_ins (td
, MINT_JMP
);
3614 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
3615 goto_if_nok (error
, exit
);
3619 case CEE_CALLVIRT
: /* Fall through */
3620 case CEE_CALLI
: /* Fall through */
3622 gboolean need_seq_point
= FALSE
;
3624 if (sym_seq_points
&& !mono_bitset_test_fast (seq_point_locs
, td
->ip
+ 5 - header
->code
))
3625 need_seq_point
= TRUE
;
3627 if (!interp_transform_call (td
, method
, NULL
, domain
, generic_context
, td
->is_bb_start
, constrained_class
, readonly
, error
, TRUE
, save_last_error
))
3630 if (need_seq_point
) {
3631 //check is is a nested call and remove the MONO_INST_NONEMPTY_STACK of the last breakpoint, only for non native methods
3632 if (!(method
->flags
& METHOD_IMPL_ATTRIBUTE_NATIVE
)) {
3633 if (emitted_funccall_seq_point
) {
3635 last_seq_point
->flags
|= INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL
;
3638 emitted_funccall_seq_point
= TRUE
;
3640 last_seq_point
= interp_add_ins (td
, MINT_SDB_SEQ_POINT
);
3641 // This seq point is actually associated with the instruction following the call
3642 last_seq_point
->il_offset
= td
->ip
- header
->code
;
3643 last_seq_point
->flags
= INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK
;
3646 constrained_class
= NULL
;
3648 save_last_error
= FALSE
;
3652 /* Return from inlined method, return value is on top of stack */
3653 if (td
->method
!= method
) {
3659 MonoType
*ult
= mini_type_get_underlying_type (signature
->ret
);
3660 if (ult
->type
!= MONO_TYPE_VOID
) {
3661 CHECK_STACK (td
, 1);
3663 if (mint_type (ult
) == MINT_TYPE_VT
) {
3664 MonoClass
*klass
= mono_class_from_mono_type_internal (ult
);
3665 vt_size
= mono_class_value_size (klass
, NULL
);
3668 if (td
->sp
> td
->stack
) {
3669 mono_error_set_generic_error (error
, "System", "InvalidProgramException", "");
3672 if (td
->vt_sp
!= ALIGN_TO (vt_size
, MINT_VT_ALIGNMENT
))
3673 g_error ("%s: CEE_RET: value type stack: %d vs. %d", mono_method_full_name (td
->method
, TRUE
), td
->vt_sp
, vt_size
);
3675 if (sym_seq_points
) {
3676 last_seq_point
= interp_add_ins (td
, MINT_SDB_SEQ_POINT
);
3677 td
->last_ins
->flags
|= INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT
;
3680 guint16 exit_profiling
= 0;
3681 if (mono_jit_trace_calls
!= NULL
&& mono_trace_eval (method
))
3682 exit_profiling
|= TRACING_FLAG
;
3683 if (rtm
->prof_flags
& MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE
)
3684 exit_profiling
|= PROFILING_FLAG
;
3685 if (exit_profiling
) {
3686 /* This does the return as well */
3687 if (ult
->type
== MONO_TYPE_VOID
) {
3688 interp_add_ins (td
, MINT_PROF_EXIT_VOID
);
3691 interp_add_ins (td
, MINT_PROF_EXIT
);
3693 td
->last_ins
->data
[0] = exit_profiling
;
3694 WRITE32_INS (td
->last_ins
, 1, &vt_size
);
3695 POP_VT (td
, vt_size
);
3699 SIMPLE_OP(td
, ult
->type
== MONO_TYPE_VOID
? MINT_RET_VOID
: MINT_RET
);
3701 interp_add_ins (td
, MINT_RET_VT
);
3702 WRITE32_INS (td
->last_ins
, 0, &vt_size
);
3703 POP_VT (td
, vt_size
);
3710 int offset
= read32 (td
->ip
+ 1);
3713 handle_branch (td
, MINT_BR_S
, MINT_BR
, 5 + offset
);
3719 int offset
= (gint8
)td
->ip
[1];
3722 handle_branch (td
, MINT_BR_S
, MINT_BR
, 2 + (gint8
)td
->ip
[1]);
3729 one_arg_branch (td
, MINT_BRFALSE_I4
, 5 + read32 (td
->ip
+ 1));
3734 one_arg_branch (td
, MINT_BRFALSE_I4
, 2 + (gint8
)td
->ip
[1]);
3739 one_arg_branch (td
, MINT_BRTRUE_I4
, 5 + read32 (td
->ip
+ 1));
3744 one_arg_branch (td
, MINT_BRTRUE_I4
, 2 + (gint8
)td
->ip
[1]);
3749 two_arg_branch (td
, MINT_BEQ_I4
, 5 + read32 (td
->ip
+ 1));
3754 two_arg_branch (td
, MINT_BEQ_I4
, 2 + (gint8
) td
->ip
[1]);
3759 two_arg_branch (td
, MINT_BGE_I4
, 5 + read32 (td
->ip
+ 1));
3764 two_arg_branch (td
, MINT_BGE_I4
, 2 + (gint8
) td
->ip
[1]);
3769 two_arg_branch (td
, MINT_BGT_I4
, 5 + read32 (td
->ip
+ 1));
3774 two_arg_branch (td
, MINT_BGT_I4
, 2 + (gint8
) td
->ip
[1]);
3779 two_arg_branch (td
, MINT_BLT_I4
, 5 + read32 (td
->ip
+ 1));
3784 two_arg_branch (td
, MINT_BLT_I4
, 2 + (gint8
) td
->ip
[1]);
3789 two_arg_branch (td
, MINT_BLE_I4
, 5 + read32 (td
->ip
+ 1));
3794 two_arg_branch (td
, MINT_BLE_I4
, 2 + (gint8
) td
->ip
[1]);
3799 two_arg_branch (td
, MINT_BNE_UN_I4
, 5 + read32 (td
->ip
+ 1));
3804 two_arg_branch (td
, MINT_BNE_UN_I4
, 2 + (gint8
) td
->ip
[1]);
3809 two_arg_branch (td
, MINT_BGE_UN_I4
, 5 + read32 (td
->ip
+ 1));
3814 two_arg_branch (td
, MINT_BGE_UN_I4
, 2 + (gint8
) td
->ip
[1]);
3819 two_arg_branch (td
, MINT_BGT_UN_I4
, 5 + read32 (td
->ip
+ 1));
3824 two_arg_branch (td
, MINT_BGT_UN_I4
, 2 + (gint8
) td
->ip
[1]);
3829 two_arg_branch (td
, MINT_BLE_UN_I4
, 5 + read32 (td
->ip
+ 1));
3834 two_arg_branch (td
, MINT_BLE_UN_I4
, 2 + (gint8
) td
->ip
[1]);
3839 two_arg_branch (td
, MINT_BLT_UN_I4
, 5 + read32 (td
->ip
+ 1));
3844 two_arg_branch (td
, MINT_BLT_UN_I4
, 2 + (gint8
) td
->ip
[1]);
3850 const unsigned char *next_ip
;
3852 n
= read32 (td
->ip
);
3853 interp_add_ins_explicit (td
, MINT_SWITCH
, MINT_SWITCH_LEN (n
));
3854 WRITE32_INS (td
->last_ins
, 0, &n
);
3856 next_ip
= td
->ip
+ n
* 4;
3858 int stack_height
= td
->sp
- td
->stack
;
3859 for (i
= 0; i
< n
; i
++) {
3860 offset
= read32 (td
->ip
);
3861 target
= next_ip
- td
->il_code
+ offset
;
3864 if (stack_height
> 0 && stack_height
!= td
->stack_height
[target
])
3865 g_warning ("SWITCH with back branch and non-empty stack");
3868 td
->stack_height
[target
] = stack_height
;
3869 td
->vt_stack_size
[target
] = td
->vt_sp
;
3870 if (stack_height
> 0)
3871 td
->stack_state
[target
] = (StackInfo
*)g_memdup (td
->stack
, stack_height
* sizeof (td
->stack
[0]));
3873 WRITE32_INS (td
->last_ins
, 2 + i
* 2, &target
);
3879 CHECK_STACK (td
, 1);
3880 SIMPLE_OP (td
, MINT_LDIND_I1_CHECK
);
3881 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3882 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3885 CHECK_STACK (td
, 1);
3886 SIMPLE_OP (td
, MINT_LDIND_U1_CHECK
);
3887 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3888 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3891 CHECK_STACK (td
, 1);
3892 SIMPLE_OP (td
, MINT_LDIND_I2_CHECK
);
3893 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3894 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3897 CHECK_STACK (td
, 1);
3898 SIMPLE_OP (td
, MINT_LDIND_U2_CHECK
);
3899 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3900 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3903 CHECK_STACK (td
, 1);
3904 SIMPLE_OP (td
, MINT_LDIND_I4_CHECK
);
3905 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3906 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3909 CHECK_STACK (td
, 1);
3910 SIMPLE_OP (td
, MINT_LDIND_U4_CHECK
);
3911 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3912 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3915 CHECK_STACK (td
, 1);
3916 SIMPLE_OP (td
, MINT_LDIND_I8_CHECK
);
3917 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
3918 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3921 CHECK_STACK (td
, 1);
3922 SIMPLE_OP (td
, MINT_LDIND_REF_CHECK
);
3923 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I
);
3924 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3927 CHECK_STACK (td
, 1);
3928 SIMPLE_OP (td
, MINT_LDIND_R4_CHECK
);
3929 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R4
);
3930 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3933 CHECK_STACK (td
, 1);
3934 SIMPLE_OP (td
, MINT_LDIND_R8_CHECK
);
3935 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R8
);
3936 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3939 CHECK_STACK (td
, 1);
3940 SIMPLE_OP (td
, MINT_LDIND_REF_CHECK
);
3941 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3942 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_O
);
3945 CHECK_STACK (td
, 2);
3946 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
3947 SIMPLE_OP (td
, MINT_STIND_REF
);
3951 CHECK_STACK (td
, 2);
3952 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
3953 SIMPLE_OP (td
, MINT_STIND_I1
);
3957 CHECK_STACK (td
, 2);
3958 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
3959 SIMPLE_OP (td
, MINT_STIND_I2
);
3963 CHECK_STACK (td
, 2);
3964 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
3965 SIMPLE_OP (td
, MINT_STIND_I4
);
3969 CHECK_STACK (td
, 2);
3970 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
3971 SIMPLE_OP (td
, MINT_STIND_I
);
3975 CHECK_STACK (td
, 2);
3976 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
3977 SIMPLE_OP (td
, MINT_STIND_I8
);
3981 CHECK_STACK (td
, 2);
3982 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
3983 SIMPLE_OP (td
, MINT_STIND_R4
);
3987 CHECK_STACK (td
, 2);
3988 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
3989 SIMPLE_OP (td
, MINT_STIND_R8
);
3993 binary_arith_op(td
, MINT_ADD_I4
);
3997 binary_arith_op(td
, MINT_SUB_I4
);
4001 binary_arith_op(td
, MINT_MUL_I4
);
4005 binary_arith_op(td
, MINT_DIV_I4
);
4009 binary_arith_op(td
, MINT_DIV_UN_I4
);
4013 binary_arith_op (td
, MINT_REM_I4
);
4017 binary_arith_op (td
, MINT_REM_UN_I4
);
4021 binary_arith_op (td
, MINT_AND_I4
);
4025 binary_arith_op (td
, MINT_OR_I4
);
4029 binary_arith_op (td
, MINT_XOR_I4
);
4033 shift_op (td
, MINT_SHL_I4
);
4037 shift_op (td
, MINT_SHR_I4
);
4041 shift_op (td
, MINT_SHR_UN_I4
);
4045 unary_arith_op (td
, MINT_NEG_I4
);
4049 unary_arith_op (td
, MINT_NOT_I4
);
4053 CHECK_STACK (td
, 1);
4054 switch (td
->sp
[-1].type
) {
4056 interp_add_ins (td
, MINT_CONV_U1_R4
);
4059 interp_add_ins (td
, MINT_CONV_U1_R8
);
4062 interp_add_ins (td
, MINT_CONV_U1_I4
);
4065 interp_add_ins (td
, MINT_CONV_U1_I8
);
4068 g_assert_not_reached ();
4071 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4074 CHECK_STACK (td
, 1);
4075 switch (td
->sp
[-1].type
) {
4077 interp_add_ins (td
, MINT_CONV_I1_R4
);
4080 interp_add_ins (td
, MINT_CONV_I1_R8
);
4083 interp_add_ins (td
, MINT_CONV_I1_I4
);
4086 interp_add_ins (td
, MINT_CONV_I1_I8
);
4089 g_assert_not_reached ();
4092 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4095 CHECK_STACK (td
, 1);
4096 switch (td
->sp
[-1].type
) {
4098 interp_add_ins (td
, MINT_CONV_U2_R4
);
4101 interp_add_ins (td
, MINT_CONV_U2_R8
);
4104 interp_add_ins (td
, MINT_CONV_U2_I4
);
4107 interp_add_ins (td
, MINT_CONV_U2_I8
);
4110 g_assert_not_reached ();
4113 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4116 CHECK_STACK (td
, 1);
4117 switch (td
->sp
[-1].type
) {
4119 interp_add_ins (td
, MINT_CONV_I2_R4
);
4122 interp_add_ins (td
, MINT_CONV_I2_R8
);
4125 interp_add_ins (td
, MINT_CONV_I2_I4
);
4128 interp_add_ins (td
, MINT_CONV_I2_I8
);
4131 g_assert_not_reached ();
4134 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4137 CHECK_STACK (td
, 1);
4138 switch (td
->sp
[-1].type
) {
4140 #if SIZEOF_VOID_P == 4
4141 interp_add_ins (td
, MINT_CONV_U4_R8
);
4143 interp_add_ins (td
, MINT_CONV_U8_R8
);
4147 #if SIZEOF_VOID_P == 8
4148 interp_add_ins (td
, MINT_CONV_I8_U4
);
4152 #if SIZEOF_VOID_P == 4
4153 interp_add_ins (td
, MINT_CONV_U4_I8
);
4160 g_assert_not_reached ();
4163 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I
);
4166 CHECK_STACK (td
, 1);
4167 switch (td
->sp
[-1].type
) {
4169 #if SIZEOF_VOID_P == 8
4170 interp_add_ins (td
, MINT_CONV_I8_R8
);
4172 interp_add_ins (td
, MINT_CONV_I4_R8
);
4176 #if SIZEOF_VOID_P == 8
4177 interp_add_ins (td
, MINT_CONV_I8_R4
);
4179 interp_add_ins (td
, MINT_CONV_I4_R4
);
4183 #if SIZEOF_VOID_P == 8
4184 interp_add_ins (td
, MINT_CONV_I8_I4
);
4192 #if SIZEOF_VOID_P == 4
4193 interp_add_ins (td
, MINT_CONV_I4_I8
);
4197 g_assert_not_reached ();
4200 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I
);
4203 CHECK_STACK (td
, 1);
4204 switch (td
->sp
[-1].type
) {
4206 interp_add_ins (td
, MINT_CONV_U4_R4
);
4209 interp_add_ins (td
, MINT_CONV_U4_R8
);
4214 interp_add_ins (td
, MINT_CONV_U4_I8
);
4217 #if SIZEOF_VOID_P == 8
4218 interp_add_ins (td
, MINT_CONV_U4_I8
);
4222 g_assert_not_reached ();
4225 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4228 CHECK_STACK (td
, 1);
4229 switch (td
->sp
[-1].type
) {
4231 interp_add_ins (td
, MINT_CONV_I4_R4
);
4234 interp_add_ins (td
, MINT_CONV_I4_R8
);
4239 interp_add_ins (td
, MINT_CONV_I4_I8
);
4242 #if SIZEOF_VOID_P == 8
4243 interp_add_ins (td
, MINT_CONV_I4_I8
);
4247 g_assert_not_reached ();
4250 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4253 CHECK_STACK (td
, 1);
4254 switch (td
->sp
[-1].type
) {
4256 interp_add_ins (td
, MINT_CONV_I8_R4
);
4259 interp_add_ins (td
, MINT_CONV_I8_R8
);
4261 case STACK_TYPE_I4
: {
4262 if (interp_ins_is_ldc (td
->last_ins
) && (inlining
|| !td
->is_bb_start
[in_offset
])) {
4263 gint64 ct
= interp_get_const_from_ldc_i4 (td
->last_ins
);
4264 interp_clear_ins (td
, td
->last_ins
);
4266 interp_add_ins (td
, MINT_LDC_I8
);
4267 WRITE64_INS (td
->last_ins
, 0, &ct
);
4269 interp_add_ins (td
, MINT_CONV_I8_I4
);
4276 #if SIZEOF_VOID_P == 4
4277 interp_add_ins (td
, MINT_CONV_I8_I4
);
4281 g_assert_not_reached ();
4284 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
4287 CHECK_STACK (td
, 1);
4288 switch (td
->sp
[-1].type
) {
4290 interp_add_ins (td
, MINT_CONV_R4_R8
);
4293 interp_add_ins (td
, MINT_CONV_R4_I8
);
4296 interp_add_ins (td
, MINT_CONV_R4_I4
);
4302 g_assert_not_reached ();
4305 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R4
);
4308 CHECK_STACK (td
, 1);
4309 switch (td
->sp
[-1].type
) {
4311 interp_add_ins (td
, MINT_CONV_R8_I4
);
4314 interp_add_ins (td
, MINT_CONV_R8_I8
);
4317 interp_add_ins (td
, MINT_CONV_R8_R4
);
4322 g_assert_not_reached ();
4325 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R8
);
4328 CHECK_STACK (td
, 1);
4329 switch (td
->sp
[-1].type
) {
4331 if (interp_ins_is_ldc (td
->last_ins
) && (inlining
|| !td
->is_bb_start
[in_offset
])) {
4332 gint64 ct
= (guint32
)interp_get_const_from_ldc_i4 (td
->last_ins
);
4333 interp_clear_ins (td
, td
->last_ins
);
4335 interp_add_ins (td
, MINT_LDC_I8
);
4336 WRITE64_INS (td
->last_ins
, 0, &ct
);
4338 interp_add_ins (td
, MINT_CONV_I8_U4
);
4344 interp_add_ins (td
, MINT_CONV_U8_R4
);
4347 interp_add_ins (td
, MINT_CONV_U8_R8
);
4350 #if SIZEOF_VOID_P == 4
4351 interp_add_ins (td
, MINT_CONV_I8_U4
);
4355 g_assert_not_reached ();
4358 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
4361 CHECK_STACK (td
, 2);
4363 token
= read32 (td
->ip
+ 1);
4364 klass
= mono_class_get_and_inflate_typespec_checked (image
, token
, generic_context
, error
);
4365 goto_if_nok (error
, exit
);
4367 if (m_class_is_valuetype (klass
)) {
4368 int mt
= mint_type (m_class_get_byval_arg (klass
));
4369 interp_add_ins (td
, (mt
== MINT_TYPE_VT
) ? MINT_CPOBJ_VT
: MINT_CPOBJ
);
4370 td
->last_ins
->data
[0] = get_data_item_index(td
, klass
);
4372 interp_add_ins (td
, MINT_LDIND_REF
);
4373 interp_add_ins (td
, MINT_STIND_REF
);
4380 CHECK_STACK (td
, 1);
4382 token
= read32 (td
->ip
+ 1);
4384 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
4385 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
4387 klass
= mono_class_get_and_inflate_typespec_checked (image
, token
, generic_context
, error
);
4388 goto_if_nok (error
, exit
);
4391 interp_emit_ldobj (td
, klass
);
4394 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
4398 token
= mono_metadata_token_index (read32 (td
->ip
+ 1));
4400 if (method
->wrapper_type
== MONO_WRAPPER_NONE
) {
4401 MonoString
*s
= mono_ldstr_checked (domain
, image
, token
, error
);
4402 goto_if_nok (error
, exit
);
4403 /* GC won't scan code stream, but reference is held by metadata
4404 * machinery so we are good here */
4405 interp_add_ins (td
, MINT_LDSTR
);
4406 td
->last_ins
->data
[0] = get_data_item_index (td
, s
);
4408 /* defer allocation to execution-time */
4409 interp_add_ins (td
, MINT_LDSTR_TOKEN
);
4410 td
->last_ins
->data
[0] = get_data_item_index (td
, GUINT_TO_POINTER (token
));
4412 PUSH_TYPE(td
, STACK_TYPE_O
, mono_defaults
.string_class
);
4417 MonoMethodSignature
*csignature
;
4418 guint32 vt_stack_used
= 0;
4419 guint32 vt_res_size
= 0;
4422 token
= read32 (td
->ip
);
4425 m
= interp_get_method (method
, token
, image
, generic_context
, error
);
4426 goto_if_nok (error
, exit
);
4428 csignature
= mono_method_signature_internal (m
);
4431 if (!mono_class_init_internal (klass
)) {
4432 mono_error_set_for_class_failure (error
, klass
);
4433 goto_if_nok (error
, exit
);
4436 if (mono_class_get_flags (klass
) & TYPE_ATTRIBUTE_ABSTRACT
) {
4437 char* full_name
= mono_type_get_full_name (klass
);
4438 mono_error_set_member_access (error
, "Cannot create an abstract class: %s", full_name
);
4440 goto_if_nok (error
, exit
);
4443 if (mono_class_is_magic_int (klass
) || mono_class_is_magic_float (klass
)) {
4444 td
->sp
-= csignature
->param_count
;
4445 #if SIZEOF_VOID_P == 8
4446 if (mono_class_is_magic_int (klass
) && td
->sp
[0].type
== STACK_TYPE_I4
)
4447 interp_add_ins (td
, MINT_CONV_I8_I4
);
4448 else if (mono_class_is_magic_float (klass
) && td
->sp
[0].type
== STACK_TYPE_R4
)
4449 interp_add_ins (td
, MINT_CONV_R8_R4
);
4451 interp_add_ins (td
, MINT_NEWOBJ_MAGIC
);
4452 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
4453 goto_if_nok (error
, exit
);
4455 PUSH_TYPE (td
, stack_type
[mint_type (m_class_get_byval_arg (klass
))], klass
);
4457 if (m_class_get_parent (klass
) == mono_defaults
.array_class
) {
4458 interp_add_ins (td
, MINT_NEWOBJ_ARRAY
);
4459 td
->last_ins
->data
[0] = get_data_item_index (td
, m
->klass
);
4460 td
->last_ins
->data
[1] = csignature
->param_count
;
4461 } else if (m_class_get_image (klass
) == mono_defaults
.corlib
&&
4462 !strcmp (m_class_get_name (m
->klass
), "ByReference`1") &&
4463 !strcmp (m
->name
, ".ctor")) {
4464 /* public ByReference(ref T value) */
4465 g_assert (csignature
->hasthis
&& csignature
->param_count
== 1);
4466 interp_add_ins (td
, MINT_INTRINS_BYREFERENCE_CTOR
);
4467 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
4468 } else if (klass
!= mono_defaults
.string_class
&&
4469 !mono_class_is_marshalbyref (klass
) &&
4470 !mono_class_has_finalizer (klass
) &&
4471 !m_class_has_weak_fields (klass
)) {
4472 if (!m_class_is_valuetype (klass
)) {
4473 InterpInst
*newobj_fast
= interp_add_ins (td
, MINT_NEWOBJ_FAST
);
4475 newobj_fast
->data
[1] = csignature
->param_count
;
4477 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, klass
, error
);
4478 goto_if_nok (error
, exit
);
4479 newobj_fast
->data
[2] = get_data_item_index (td
, vtable
);
4481 move_stack (td
, (td
->sp
- td
->stack
) - csignature
->param_count
, 2);
4483 StackInfo
*tmp_sp
= td
->sp
- csignature
->param_count
- 2;
4484 SET_TYPE (tmp_sp
, STACK_TYPE_O
, klass
);
4485 SET_TYPE (tmp_sp
+ 1, STACK_TYPE_O
, klass
);
4487 if ((mono_interp_opt
& INTERP_OPT_INLINE
) && interp_method_check_inlining (td
, m
)) {
4488 MonoMethodHeader
*mheader
= interp_method_get_header (m
, error
);
4489 goto_if_nok (error
, exit
);
4491 if (interp_inline_method (td
, m
, mheader
, error
)) {
4492 newobj_fast
->data
[0] = INLINED_METHOD_FLAG
;
4496 // If inlining failed we need to restore the stack
4497 move_stack (td
, (td
->sp
- td
->stack
) - csignature
->param_count
, -2);
4498 // Set the method to be executed as part of newobj instruction
4499 newobj_fast
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
4501 if (mint_type (m_class_get_byval_arg (klass
)) == MINT_TYPE_VT
)
4502 interp_add_ins (td
, MINT_NEWOBJ_VTST_FAST
);
4504 interp_add_ins (td
, MINT_NEWOBJ_VT_FAST
);
4506 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
4507 td
->last_ins
->data
[1] = csignature
->param_count
;
4509 if (mint_type (m_class_get_byval_arg (klass
)) == MINT_TYPE_VT
) {
4510 td
->last_ins
->data
[2] = mono_class_value_size (klass
, NULL
);
4514 interp_add_ins (td
, MINT_NEWOBJ
);
4515 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
4517 goto_if_nok (error
, exit
);
4518 /* The constructor was not inlined, abort inlining of current method */
4521 td
->sp
-= csignature
->param_count
;
4522 if (mint_type (m_class_get_byval_arg (klass
)) == MINT_TYPE_VT
) {
4523 vt_res_size
= mono_class_value_size (klass
, NULL
);
4524 PUSH_VT (td
, vt_res_size
);
4526 for (i
= 0; i
< csignature
->param_count
; ++i
) {
4527 int mt
= mint_type(csignature
->params
[i
]);
4528 if (mt
== MINT_TYPE_VT
) {
4529 MonoClass
*k
= mono_class_from_mono_type_internal (csignature
->params
[i
]);
4530 gint32 size
= mono_class_value_size (k
, NULL
);
4531 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
4532 vt_stack_used
+= size
;
4535 if (vt_stack_used
!= 0 || vt_res_size
!= 0) {
4536 interp_add_ins (td
, MINT_VTRESULT
);
4537 td
->last_ins
->data
[0] = vt_res_size
;
4538 WRITE32_INS (td
->last_ins
, 1, &vt_stack_used
);
4539 td
->vt_sp
-= vt_stack_used
;
4541 PUSH_TYPE (td
, stack_type
[mint_type (m_class_get_byval_arg (klass
))], klass
);
4547 gboolean isinst_instr
= *td
->ip
== CEE_ISINST
;
4548 CHECK_STACK (td
, 1);
4549 token
= read32 (td
->ip
+ 1);
4550 klass
= mini_get_class (method
, token
, generic_context
);
4551 CHECK_TYPELOAD (klass
);
4552 interp_handle_isinst (td
, klass
, isinst_instr
);
4554 td
->sp
[-1].klass
= klass
;
4558 switch (td
->sp
[-1].type
) {
4562 interp_add_ins (td
, MINT_CONV_R_UN_I8
);
4565 interp_add_ins (td
, MINT_CONV_R_UN_I4
);
4568 g_assert_not_reached ();
4570 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R8
);
4574 CHECK_STACK (td
, 1);
4575 token
= read32 (td
->ip
+ 1);
4577 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
4578 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
4580 klass
= mono_class_get_and_inflate_typespec_checked (image
, token
, generic_context
, error
);
4581 goto_if_nok (error
, exit
);
4584 if (mono_class_is_nullable (klass
)) {
4585 MonoMethod
*target_method
;
4586 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass
)))
4587 target_method
= mono_class_get_method_from_name_checked (klass
, "UnboxExact", 1, 0, error
);
4589 target_method
= mono_class_get_method_from_name_checked (klass
, "Unbox", 1, 0, error
);
4590 goto_if_nok (error
, exit
);
4591 /* td->ip is incremented by interp_transform_call */
4592 if (!interp_transform_call (td
, method
, target_method
, domain
, generic_context
, td
->is_bb_start
, NULL
, FALSE
, error
, FALSE
, FALSE
))
4595 * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type
4596 * We create a local variable in the frame so that we can fetch its address.
4598 int local
= create_interp_local (td
, m_class_get_byval_arg (klass
));
4599 store_local (td
, local
);
4600 interp_add_ins (td
, MINT_LDLOCA_S
);
4601 td
->last_ins
->data
[0] = local
;
4602 td
->locals
[local
].flags
|= INTERP_LOCAL_FLAG_INDIRECT
;
4603 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_MP
);
4605 interp_add_ins (td
, MINT_UNBOX
);
4606 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
4607 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_MP
);
4612 CHECK_STACK (td
, 1);
4613 token
= read32 (td
->ip
+ 1);
4615 klass
= mini_get_class (method
, token
, generic_context
);
4616 CHECK_TYPELOAD (klass
);
4618 if (mini_type_is_reference (m_class_get_byval_arg (klass
))) {
4619 int mt
= mint_type (m_class_get_byval_arg (klass
));
4620 interp_handle_isinst (td
, klass
, FALSE
);
4621 SET_TYPE (td
->sp
- 1, stack_type
[mt
], klass
);
4622 } else if (mono_class_is_nullable (klass
)) {
4623 MonoMethod
*target_method
;
4624 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass
)))
4625 target_method
= mono_class_get_method_from_name_checked (klass
, "UnboxExact", 1, 0, error
);
4627 target_method
= mono_class_get_method_from_name_checked (klass
, "Unbox", 1, 0, error
);
4628 goto_if_nok (error
, exit
);
4629 /* td->ip is incremented by interp_transform_call */
4630 if (!interp_transform_call (td
, method
, target_method
, domain
, generic_context
, td
->is_bb_start
, NULL
, FALSE
, error
, FALSE
, FALSE
))
4633 interp_add_ins (td
, MINT_UNBOX
);
4634 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
4636 interp_emit_ldobj (td
, klass
);
4644 CHECK_STACK (td
, 1);
4645 SIMPLE_OP (td
, MINT_THROW
);
4649 CHECK_STACK (td
, 1);
4650 token
= read32 (td
->ip
+ 1);
4651 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4652 goto_if_nok (error
, exit
);
4653 MonoType
*ftype
= mono_field_get_type_internal (field
);
4654 gboolean is_static
= !!(ftype
->attrs
& FIELD_ATTRIBUTE_STATIC
);
4655 mono_class_init_internal (klass
);
4656 #ifndef DISABLE_REMOTING
4657 if (m_class_get_marshalbyref (klass
) || mono_class_is_contextbound (klass
) || klass
== mono_defaults
.marshalbyrefobject_class
) {
4658 g_assert (!is_static
);
4659 int offset
= m_class_is_valuetype (klass
) ? field
->offset
- MONO_ABI_SIZEOF (MonoObject
) : field
->offset
;
4661 interp_add_ins (td
, MINT_MONO_LDPTR
);
4662 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
4663 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
4664 interp_add_ins (td
, MINT_MONO_LDPTR
);
4665 td
->last_ins
->data
[0] = get_data_item_index (td
, field
);
4666 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
4667 interp_add_ins (td
, MINT_LDC_I4
);
4668 WRITE32_INS (td
->last_ins
, 0, &offset
);
4669 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I4
);
4670 #if SIZEOF_VOID_P == 8
4671 interp_add_ins (td
, MINT_CONV_I8_I4
);
4674 MonoMethod
*wrapper
= mono_marshal_get_ldflda_wrapper (field
->type
);
4675 /* td->ip is incremented by interp_transform_call */
4676 if (!interp_transform_call (td
, method
, wrapper
, domain
, generic_context
, td
->is_bb_start
, NULL
, FALSE
, error
, FALSE
, FALSE
))
4682 interp_add_ins (td
, MINT_POP
);
4683 interp_emit_ldsflda (td
, field
, error
);
4684 goto_if_nok (error
, exit
);
4686 if ((td
->sp
- 1)->type
== STACK_TYPE_O
) {
4687 interp_add_ins (td
, MINT_LDFLDA
);
4689 g_assert ((td
->sp
-1)->type
== STACK_TYPE_MP
);
4690 interp_add_ins (td
, MINT_LDFLDA_UNSAFE
);
4692 td
->last_ins
->data
[0] = m_class_is_valuetype (klass
) ? field
->offset
- MONO_ABI_SIZEOF (MonoObject
) : field
->offset
;
4696 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
4700 CHECK_STACK (td
, 1);
4701 token
= read32 (td
->ip
+ 1);
4702 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4703 goto_if_nok (error
, exit
);
4704 MonoType
*ftype
= mono_field_get_type_internal (field
);
4705 gboolean is_static
= !!(ftype
->attrs
& FIELD_ATTRIBUTE_STATIC
);
4706 mono_class_init_internal (klass
);
4708 MonoClass
*field_klass
= mono_class_from_mono_type_internal (ftype
);
4709 mt
= mint_type (m_class_get_byval_arg (field_klass
));
4710 #ifndef DISABLE_REMOTING
4711 if ((m_class_get_marshalbyref (klass
) && !(signature
->hasthis
&& td
->last_ins
->opcode
== MINT_LDARG_P0
)) ||
4712 mono_class_is_contextbound (klass
) ||
4713 klass
== mono_defaults
.marshalbyrefobject_class
) {
4714 g_assert (!is_static
);
4715 interp_add_ins (td
, mt
== MINT_TYPE_VT
? MINT_LDRMFLD_VT
: MINT_LDRMFLD
);
4716 td
->last_ins
->data
[0] = get_data_item_index (td
, field
);
4721 interp_add_ins (td
, MINT_POP
);
4722 interp_emit_sfld_access (td
, field
, field_klass
, mt
, TRUE
, error
);
4723 goto_if_nok (error
, exit
);
4724 } else if (td
->sp
[-1].type
!= STACK_TYPE_O
&& td
->sp
[-1].type
!= STACK_TYPE_MP
&& (mono_class_is_magic_int (klass
) || mono_class_is_magic_float (klass
))) {
4725 // No need to load anything, the value is already on the execution stack
4727 int opcode
= MINT_LDFLD_I1
+ mt
- MINT_TYPE_I1
;
4728 #ifdef NO_UNALIGNED_ACCESS
4729 if ((mt
== MINT_TYPE_I8
|| mt
== MINT_TYPE_R8
) && field
->offset
% SIZEOF_VOID_P
!= 0)
4730 opcode
= get_unaligned_opcode (opcode
);
4732 interp_add_ins (td
, opcode
);
4733 td
->last_ins
->data
[0] = m_class_is_valuetype (klass
) ? field
->offset
- MONO_ABI_SIZEOF (MonoObject
) : field
->offset
;
4734 if (mt
== MINT_TYPE_VT
) {
4735 int size
= mono_class_value_size (field_klass
, NULL
);
4736 WRITE32_INS (td
->last_ins
, 1, &size
);
4740 if (mt
== MINT_TYPE_VT
) {
4741 int size
= mono_class_value_size (field_klass
, NULL
);
4744 if (td
->sp
[-1].type
== STACK_TYPE_VT
) {
4745 int size
= mono_class_value_size (klass
, NULL
);
4746 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
4747 int field_vt_size
= 0;
4748 if (mt
== MINT_TYPE_VT
) {
4750 * Pop the loaded field from the vtstack (it will still be present
4751 * at the same vtstack address) and we will load it in place of the
4752 * containing valuetype with the second MINT_VTRESULT.
4754 field_vt_size
= mono_class_value_size (field_klass
, NULL
);
4755 field_vt_size
= ALIGN_TO (field_vt_size
, MINT_VT_ALIGNMENT
);
4756 interp_add_ins (td
, MINT_VTRESULT
);
4757 td
->last_ins
->data
[0] = 0;
4758 WRITE32_INS (td
->last_ins
, 1, &field_vt_size
);
4761 interp_add_ins (td
, MINT_VTRESULT
);
4762 td
->last_ins
->data
[0] = field_vt_size
;
4763 WRITE32_INS (td
->last_ins
, 1, &size
);
4766 SET_TYPE (td
->sp
- 1, stack_type
[mt
], field_klass
);
4767 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
4771 CHECK_STACK (td
, 2);
4772 token
= read32 (td
->ip
+ 1);
4773 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4774 goto_if_nok (error
, exit
);
4775 MonoType
*ftype
= mono_field_get_type_internal (field
);
4776 gboolean is_static
= !!(ftype
->attrs
& FIELD_ATTRIBUTE_STATIC
);
4777 MonoClass
*field_klass
= mono_class_from_mono_type_internal (ftype
);
4778 mono_class_init_internal (klass
);
4779 mt
= mint_type (ftype
);
4781 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4783 #ifndef DISABLE_REMOTING
4784 if (m_class_get_marshalbyref (klass
)) {
4785 g_assert (!is_static
);
4786 interp_add_ins (td
, mt
== MINT_TYPE_VT
? MINT_STRMFLD_VT
: MINT_STRMFLD
);
4787 td
->last_ins
->data
[0] = get_data_item_index (td
, field
);
4792 interp_emit_sfld_access (td
, field
, field_klass
, mt
, FALSE
, error
);
4793 goto_if_nok (error
, exit
);
4795 /* pop the unused object reference */
4796 interp_add_ins (td
, MINT_POP
);
4798 /* the vtable of the field might not be initialized at this point */
4799 mono_class_vtable_checked (domain
, field_klass
, error
);
4800 goto_if_nok (error
, exit
);
4802 int opcode
= MINT_STFLD_I1
+ mt
- MINT_TYPE_I1
;
4803 #ifdef NO_UNALIGNED_ACCESS
4804 if ((mt
== MINT_TYPE_I8
|| mt
== MINT_TYPE_R8
) && field
->offset
% SIZEOF_VOID_P
!= 0)
4805 opcode
= get_unaligned_opcode (opcode
);
4807 interp_add_ins (td
, opcode
);
4808 td
->last_ins
->data
[0] = m_class_is_valuetype (klass
) ? field
->offset
- MONO_ABI_SIZEOF (MonoObject
) : field
->offset
;
4809 if (mt
== MINT_TYPE_VT
) {
4810 /* the vtable of the field might not be initialized at this point */
4811 mono_class_vtable_checked (domain
, field_klass
, error
);
4812 goto_if_nok (error
, exit
);
4814 td
->last_ins
->data
[1] = get_data_item_index (td
, field_klass
);
4818 if (mt
== MINT_TYPE_VT
) {
4819 int size
= mono_class_value_size (field_klass
, NULL
);
4827 token
= read32 (td
->ip
+ 1);
4828 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4829 goto_if_nok (error
, exit
);
4830 interp_emit_ldsflda (td
, field
, error
);
4831 goto_if_nok (error
, exit
);
4833 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_MP
);
4837 token
= read32 (td
->ip
+ 1);
4838 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4839 goto_if_nok (error
, exit
);
4840 MonoType
*ftype
= mono_field_get_type_internal (field
);
4841 mt
= mint_type (ftype
);
4842 klass
= mono_class_from_mono_type_internal (ftype
);
4844 interp_emit_sfld_access (td
, field
, klass
, mt
, TRUE
, error
);
4845 goto_if_nok (error
, exit
);
4847 if (mt
== MINT_TYPE_VT
) {
4848 int size
= mono_class_value_size (klass
, NULL
);
4852 PUSH_TYPE(td
, stack_type
[mt
], klass
);
4856 CHECK_STACK (td
, 1);
4857 token
= read32 (td
->ip
+ 1);
4858 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4859 goto_if_nok (error
, exit
);
4860 MonoType
*ftype
= mono_field_get_type_internal (field
);
4861 mt
= mint_type (ftype
);
4863 emit_convert (td
, td
->sp
[-1].type
, ftype
);
4865 /* the vtable of the field might not be initialized at this point */
4866 MonoClass
*fld_klass
= mono_class_from_mono_type_internal (ftype
);
4867 mono_class_vtable_checked (domain
, fld_klass
, error
);
4868 goto_if_nok (error
, exit
);
4870 interp_emit_sfld_access (td
, field
, fld_klass
, mt
, FALSE
, error
);
4871 goto_if_nok (error
, exit
);
4873 if (mt
== MINT_TYPE_VT
) {
4874 int size
= mono_class_value_size (fld_klass
, NULL
);
4882 token
= read32 (td
->ip
+ 1);
4884 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
4885 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
4887 klass
= mini_get_class (method
, token
, generic_context
);
4888 CHECK_TYPELOAD (klass
);
4890 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4892 interp_emit_stobj (td
, klass
);
4897 case CEE_CONV_OVF_I_UN
:
4898 case CEE_CONV_OVF_U_UN
:
4899 CHECK_STACK (td
, 1);
4900 switch (td
->sp
[-1].type
) {
4902 #if SIZEOF_VOID_P == 8
4903 interp_add_ins (td
, MINT_CONV_OVF_I8_UN_R8
);
4905 interp_add_ins (td
, MINT_CONV_OVF_I4_UN_R8
);
4909 #if SIZEOF_VOID_P == 4
4910 interp_add_ins (td
, MINT_CONV_OVF_I4_UN_I8
);
4914 #if SIZEOF_VOID_P == 8
4915 interp_add_ins (td
, MINT_CONV_I8_U4
);
4916 #elif SIZEOF_VOID_P == 4
4917 if (*td
->ip
== CEE_CONV_OVF_I_UN
)
4918 interp_add_ins (td
, MINT_CONV_OVF_I4_U4
);
4922 g_assert_not_reached ();
4925 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I
);
4928 case CEE_CONV_OVF_I8_UN
:
4929 case CEE_CONV_OVF_U8_UN
:
4930 CHECK_STACK (td
, 1);
4931 switch (td
->sp
[-1].type
) {
4933 interp_add_ins (td
, MINT_CONV_OVF_I8_UN_R8
);
4936 if (*td
->ip
== CEE_CONV_OVF_I8_UN
)
4937 interp_add_ins (td
, MINT_CONV_OVF_I8_U8
);
4940 interp_add_ins (td
, MINT_CONV_I8_U4
);
4943 interp_add_ins (td
, MINT_CONV_OVF_I8_UN_R4
);
4946 g_assert_not_reached ();
4949 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
4954 CHECK_STACK (td
, 1);
4955 token
= read32 (td
->ip
+ 1);
4956 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
4957 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
4959 klass
= mini_get_class (method
, token
, generic_context
);
4960 CHECK_TYPELOAD (klass
);
4962 if (mono_class_is_nullable (klass
)) {
4963 MonoMethod
*target_method
= mono_class_get_method_from_name_checked (klass
, "Box", 1, 0, error
);
4964 goto_if_nok (error
, exit
);
4965 /* td->ip is incremented by interp_transform_call */
4966 if (!interp_transform_call (td
, method
, target_method
, domain
, generic_context
, td
->is_bb_start
, NULL
, FALSE
, error
, FALSE
, FALSE
))
4968 } else if (!m_class_is_valuetype (klass
)) {
4969 /* already boxed, do nothing. */
4972 if (G_UNLIKELY (m_class_is_byreflike (klass
))) {
4973 mono_error_set_bad_image (error
, image
, "Cannot box IsByRefLike type '%s.%s'", m_class_get_name_space (klass
), m_class_get_name (klass
));
4976 if (mint_type (m_class_get_byval_arg (klass
)) == MINT_TYPE_VT
) {
4977 size
= mono_class_value_size (klass
, NULL
);
4978 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
4980 } else if (td
->sp
[-1].type
== STACK_TYPE_R8
&& m_class_get_byval_arg (klass
)->type
== MONO_TYPE_R4
) {
4981 interp_add_ins (td
, MINT_CONV_R4_R8
);
4983 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, klass
, error
);
4984 goto_if_nok (error
, exit
);
4986 if (mint_type (m_class_get_byval_arg (klass
)) == MINT_TYPE_VT
)
4987 interp_add_ins (td
, MINT_BOX_VT
);
4989 interp_add_ins (td
, MINT_BOX
);
4990 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
4991 td
->last_ins
->data
[1] = 0;
4992 SET_TYPE(td
->sp
- 1, STACK_TYPE_O
, klass
);
4999 CHECK_STACK (td
, 1);
5000 token
= read32 (td
->ip
+ 1);
5002 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
5003 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
5005 klass
= mini_get_class (method
, token
, generic_context
);
5006 CHECK_TYPELOAD (klass
);
5008 MonoClass
*array_class
= mono_class_create_array (klass
, 1);
5009 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, array_class
, error
);
5010 goto_if_nok (error
, exit
);
5012 unsigned char lentype
= (td
->sp
- 1)->type
;
5013 if (lentype
== STACK_TYPE_I8
) {
5014 /* mimic mini behaviour */
5015 interp_add_ins (td
, MINT_CONV_OVF_U4_I8
);
5017 g_assert (lentype
== STACK_TYPE_I4
);
5018 interp_add_ins (td
, MINT_CONV_OVF_U4_I4
);
5020 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
5021 interp_add_ins (td
, MINT_NEWARR
);
5022 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
5023 SET_TYPE (td
->sp
- 1, STACK_TYPE_O
, array_class
);
5028 CHECK_STACK (td
, 1);
5029 SIMPLE_OP (td
, MINT_LDLEN
);
5030 #ifdef MONO_BIG_ARRAYS
5031 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I8
);
5033 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
5038 CHECK_STACK (td
, 2);
5040 token
= read32 (td
->ip
+ 1);
5042 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
5043 klass
= (MonoClass
*) mono_method_get_wrapper_data (method
, token
);
5045 klass
= mini_get_class (method
, token
, generic_context
);
5047 CHECK_TYPELOAD (klass
);
5049 if (!m_class_is_valuetype (klass
) && method
->wrapper_type
== MONO_WRAPPER_NONE
&& !readonly
) {
5051 * Check the class for failures before the type check, which can
5052 * throw other exceptions.
5054 mono_class_setup_vtable (klass
);
5055 CHECK_TYPELOAD (klass
);
5056 interp_add_ins (td
, MINT_LDELEMA_TC
);
5057 td
->last_ins
->data
[0] = 1;
5058 td
->last_ins
->data
[1] = get_data_item_index (td
, klass
);
5060 interp_add_ins (td
, MINT_LDELEMA1
);
5061 mono_class_init_internal (klass
);
5062 size
= mono_class_array_element_size (klass
);
5063 WRITE32_INS (td
->last_ins
, 0, &size
);
5070 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
5074 CHECK_STACK (td
, 2);
5076 SIMPLE_OP (td
, MINT_LDELEM_I1
);
5078 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5081 CHECK_STACK (td
, 2);
5083 SIMPLE_OP (td
, MINT_LDELEM_U1
);
5085 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5088 CHECK_STACK (td
, 2);
5090 SIMPLE_OP (td
, MINT_LDELEM_I2
);
5092 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5095 CHECK_STACK (td
, 2);
5097 SIMPLE_OP (td
, MINT_LDELEM_U2
);
5099 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5102 CHECK_STACK (td
, 2);
5104 SIMPLE_OP (td
, MINT_LDELEM_I4
);
5106 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5109 CHECK_STACK (td
, 2);
5111 SIMPLE_OP (td
, MINT_LDELEM_U4
);
5113 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5116 CHECK_STACK (td
, 2);
5118 SIMPLE_OP (td
, MINT_LDELEM_I8
);
5120 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
5123 CHECK_STACK (td
, 2);
5125 SIMPLE_OP (td
, MINT_LDELEM_I
);
5127 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I
);
5130 CHECK_STACK (td
, 2);
5132 SIMPLE_OP (td
, MINT_LDELEM_R4
);
5134 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R4
);
5137 CHECK_STACK (td
, 2);
5139 SIMPLE_OP (td
, MINT_LDELEM_R8
);
5141 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R8
);
5143 case CEE_LDELEM_REF
:
5144 CHECK_STACK (td
, 2);
5146 SIMPLE_OP (td
, MINT_LDELEM_REF
);
5148 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_O
);
5151 CHECK_STACK (td
, 2);
5152 token
= read32 (td
->ip
+ 1);
5153 klass
= mini_get_class (method
, token
, generic_context
);
5154 CHECK_TYPELOAD (klass
);
5155 switch (mint_type (m_class_get_byval_arg (klass
))) {
5158 SIMPLE_OP (td
, MINT_LDELEM_I1
);
5160 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5164 SIMPLE_OP (td
, MINT_LDELEM_U1
);
5166 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5170 SIMPLE_OP (td
, MINT_LDELEM_U2
);
5172 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5176 SIMPLE_OP (td
, MINT_LDELEM_I2
);
5178 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5182 SIMPLE_OP (td
, MINT_LDELEM_I4
);
5184 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5188 SIMPLE_OP (td
, MINT_LDELEM_I8
);
5190 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
5194 SIMPLE_OP (td
, MINT_LDELEM_R4
);
5196 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R4
);
5200 SIMPLE_OP (td
, MINT_LDELEM_R8
);
5202 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R8
);
5206 SIMPLE_OP (td
, MINT_LDELEM_REF
);
5208 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_O
);
5210 case MINT_TYPE_VT
: {
5211 int size
= mono_class_value_size (klass
, NULL
);
5213 SIMPLE_OP (td
, MINT_LDELEM_VT
);
5214 WRITE32_INS (td
->last_ins
, 0, &size
);
5216 SET_TYPE (td
->sp
- 1, STACK_TYPE_VT
, klass
);
5221 GString
*res
= g_string_new ("");
5222 mono_type_get_desc (res
, m_class_get_byval_arg (klass
), TRUE
);
5223 g_print ("LDELEM: %s -> %d (%s)\n", m_class_get_name (klass
), mint_type (m_class_get_byval_arg (klass
)), res
->str
);
5224 g_string_free (res
, TRUE
);
5232 CHECK_STACK (td
, 3);
5234 SIMPLE_OP (td
, MINT_STELEM_I
);
5238 CHECK_STACK (td
, 3);
5240 SIMPLE_OP (td
, MINT_STELEM_I1
);
5244 CHECK_STACK (td
, 3);
5246 SIMPLE_OP (td
, MINT_STELEM_I2
);
5250 CHECK_STACK (td
, 3);
5252 SIMPLE_OP (td
, MINT_STELEM_I4
);
5256 CHECK_STACK (td
, 3);
5258 SIMPLE_OP (td
, MINT_STELEM_I8
);
5262 CHECK_STACK (td
, 3);
5264 SIMPLE_OP (td
, MINT_STELEM_R4
);
5268 CHECK_STACK (td
, 3);
5270 SIMPLE_OP (td
, MINT_STELEM_R8
);
5273 case CEE_STELEM_REF
:
5274 CHECK_STACK (td
, 3);
5276 SIMPLE_OP (td
, MINT_STELEM_REF
);
5280 CHECK_STACK (td
, 3);
5282 token
= read32 (td
->ip
+ 1);
5283 klass
= mini_get_class (method
, token
, generic_context
);
5284 CHECK_TYPELOAD (klass
);
5285 switch (mint_type (m_class_get_byval_arg (klass
))) {
5287 SIMPLE_OP (td
, MINT_STELEM_I1
);
5290 SIMPLE_OP (td
, MINT_STELEM_U1
);
5293 SIMPLE_OP (td
, MINT_STELEM_I2
);
5296 SIMPLE_OP (td
, MINT_STELEM_U2
);
5299 SIMPLE_OP (td
, MINT_STELEM_I4
);
5302 SIMPLE_OP (td
, MINT_STELEM_I8
);
5305 SIMPLE_OP (td
, MINT_STELEM_R4
);
5308 SIMPLE_OP (td
, MINT_STELEM_R8
);
5311 SIMPLE_OP (td
, MINT_STELEM_REF
);
5313 case MINT_TYPE_VT
: {
5314 int size
= mono_class_value_size (klass
, NULL
);
5315 SIMPLE_OP (td
, MINT_STELEM_VT
);
5316 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
5317 WRITE32_INS (td
->last_ins
, 1, &size
);
5322 GString
*res
= g_string_new ("");
5323 mono_type_get_desc (res
, m_class_get_byval_arg (klass
), TRUE
);
5324 g_print ("STELEM: %s -> %d (%s)\n", m_class_get_name (klass
), mint_type (m_class_get_byval_arg (klass
)), res
->str
);
5325 g_string_free (res
, TRUE
);
5334 case CEE_CONV_OVF_U1
:
5336 case CEE_CONV_OVF_I8
:
5338 #if SIZEOF_VOID_P == 8
5339 case CEE_CONV_OVF_U
:
5343 CHECK_STACK (td
, 1);
5344 SIMPLE_OP (td
, MINT_CKFINITE
);
5347 CHECK_STACK (td
, 1);
5349 token
= read32 (td
->ip
+ 1);
5350 klass
= mini_get_class (method
, token
, generic_context
);
5351 CHECK_TYPELOAD (klass
);
5353 interp_add_ins (td
, MINT_MKREFANY
);
5354 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
5357 PUSH_VT (td
, sizeof (MonoTypedRef
));
5358 SET_TYPE(td
->sp
- 1, STACK_TYPE_VT
, mono_defaults
.typed_reference_class
);
5360 case CEE_REFANYVAL
: {
5361 CHECK_STACK (td
, 1);
5363 token
= read32 (td
->ip
+ 1);
5364 klass
= mini_get_class (method
, token
, generic_context
);
5365 CHECK_TYPELOAD (klass
);
5367 interp_add_ins (td
, MINT_REFANYVAL
);
5368 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
5370 POP_VT (td
, sizeof (MonoTypedRef
));
5371 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
5376 case CEE_CONV_OVF_I1
:
5377 case CEE_CONV_OVF_I1_UN
: {
5378 gboolean is_un
= *td
->ip
== CEE_CONV_OVF_I1_UN
;
5379 CHECK_STACK (td
, 1);
5380 switch (td
->sp
[-1].type
) {
5382 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I1_UN_R8
: MINT_CONV_OVF_I1_R8
);
5385 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I1_U4
: MINT_CONV_OVF_I1_I4
);
5388 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I1_U8
: MINT_CONV_OVF_I1_I8
);
5391 g_assert_not_reached ();
5394 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5397 case CEE_CONV_OVF_U1
:
5398 case CEE_CONV_OVF_U1_UN
:
5399 CHECK_STACK (td
, 1);
5400 switch (td
->sp
[-1].type
) {
5402 interp_add_ins (td
, MINT_CONV_OVF_U1_R8
);
5405 interp_add_ins (td
, MINT_CONV_OVF_U1_I4
);
5408 interp_add_ins (td
, MINT_CONV_OVF_U1_I8
);
5411 g_assert_not_reached ();
5414 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5416 case CEE_CONV_OVF_I2
:
5417 case CEE_CONV_OVF_I2_UN
: {
5418 gboolean is_un
= *td
->ip
== CEE_CONV_OVF_I2_UN
;
5419 CHECK_STACK (td
, 1);
5420 switch (td
->sp
[-1].type
) {
5422 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I2_UN_R8
: MINT_CONV_OVF_I2_R8
);
5425 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I2_U4
: MINT_CONV_OVF_I2_I4
);
5428 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I2_U8
: MINT_CONV_OVF_I2_I8
);
5431 g_assert_not_reached ();
5434 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5437 case CEE_CONV_OVF_U2_UN
:
5438 case CEE_CONV_OVF_U2
:
5439 CHECK_STACK (td
, 1);
5440 switch (td
->sp
[-1].type
) {
5442 interp_add_ins (td
, MINT_CONV_OVF_U2_R8
);
5445 interp_add_ins (td
, MINT_CONV_OVF_U2_I4
);
5448 interp_add_ins (td
, MINT_CONV_OVF_U2_I8
);
5451 g_assert_not_reached ();
5454 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5456 #if SIZEOF_VOID_P == 4
5457 case CEE_CONV_OVF_I
:
5459 case CEE_CONV_OVF_I4
:
5460 case CEE_CONV_OVF_I4_UN
:
5461 CHECK_STACK (td
, 1);
5462 switch (td
->sp
[-1].type
) {
5464 interp_add_ins (td
, MINT_CONV_OVF_I4_R4
);
5467 interp_add_ins (td
, MINT_CONV_OVF_I4_R8
);
5470 if (*td
->ip
== CEE_CONV_OVF_I4_UN
)
5471 interp_add_ins (td
, MINT_CONV_OVF_I4_U4
);
5474 if (*td
->ip
== CEE_CONV_OVF_I4_UN
)
5475 interp_add_ins (td
, MINT_CONV_OVF_I4_U8
);
5477 interp_add_ins (td
, MINT_CONV_OVF_I4_I8
);
5480 g_assert_not_reached ();
5483 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5485 #if SIZEOF_VOID_P == 4
5486 case CEE_CONV_OVF_U
:
5488 case CEE_CONV_OVF_U4
:
5489 case CEE_CONV_OVF_U4_UN
:
5490 CHECK_STACK (td
, 1);
5491 switch (td
->sp
[-1].type
) {
5493 interp_add_ins (td
, MINT_CONV_OVF_U4_R4
);
5496 interp_add_ins (td
, MINT_CONV_OVF_U4_R8
);
5499 if (*td
->ip
!= CEE_CONV_OVF_U4_UN
)
5500 interp_add_ins (td
, MINT_CONV_OVF_U4_I4
);
5503 interp_add_ins (td
, MINT_CONV_OVF_U4_I8
);
5506 interp_add_ins (td
, MINT_CONV_OVF_U4_P
);
5509 g_assert_not_reached ();
5512 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5514 #if SIZEOF_VOID_P == 8
5515 case CEE_CONV_OVF_I
:
5517 case CEE_CONV_OVF_I8
:
5518 CHECK_STACK (td
, 1);
5519 switch (td
->sp
[-1].type
) {
5521 interp_add_ins (td
, MINT_CONV_OVF_I8_R4
);
5524 interp_add_ins (td
, MINT_CONV_OVF_I8_R8
);
5527 interp_add_ins (td
, MINT_CONV_I8_I4
);
5532 g_assert_not_reached ();
5535 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
5537 #if SIZEOF_VOID_P == 8
5538 case CEE_CONV_OVF_U
:
5540 case CEE_CONV_OVF_U8
:
5541 CHECK_STACK (td
, 1);
5542 switch (td
->sp
[-1].type
) {
5544 interp_add_ins (td
, MINT_CONV_OVF_U8_R4
);
5547 interp_add_ins (td
, MINT_CONV_OVF_U8_R8
);
5550 interp_add_ins (td
, MINT_CONV_OVF_U8_I4
);
5553 interp_add_ins (td
, MINT_CONV_OVF_U8_I8
);
5556 g_assert_not_reached ();
5559 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
5564 token
= read32 (td
->ip
+ 1);
5565 if (method
->wrapper_type
== MONO_WRAPPER_DYNAMIC_METHOD
|| method
->wrapper_type
== MONO_WRAPPER_SYNCHRONIZED
) {
5566 handle
= mono_method_get_wrapper_data (method
, token
);
5567 klass
= (MonoClass
*) mono_method_get_wrapper_data (method
, token
+ 1);
5568 if (klass
== mono_defaults
.typehandle_class
)
5569 handle
= m_class_get_byval_arg ((MonoClass
*) handle
);
5571 if (generic_context
) {
5572 handle
= mono_class_inflate_generic_type_checked ((MonoType
*)handle
, generic_context
, error
);
5573 goto_if_nok (error
, exit
);
5576 handle
= mono_ldtoken_checked (image
, token
, &klass
, generic_context
, error
);
5577 goto_if_nok (error
, exit
);
5579 mono_class_init_internal (klass
);
5580 mt
= mint_type (m_class_get_byval_arg (klass
));
5581 g_assert (mt
== MINT_TYPE_VT
);
5582 size
= mono_class_value_size (klass
, NULL
);
5583 g_assert (size
== sizeof(gpointer
));
5585 const unsigned char *next_ip
= td
->ip
+ 5;
5586 MonoMethod
*cmethod
;
5587 if (next_ip
< end
&&
5588 (inlining
|| !td
->is_bb_start
[next_ip
- td
->il_code
]) &&
5589 (*next_ip
== CEE_CALL
|| *next_ip
== CEE_CALLVIRT
) &&
5590 (cmethod
= interp_get_method (method
, read32 (next_ip
+ 1), image
, generic_context
, error
)) &&
5591 (cmethod
->klass
== mono_defaults
.systemtype_class
) &&
5592 (strcmp (cmethod
->name
, "GetTypeFromHandle") == 0)) {
5593 interp_add_ins (td
, MINT_MONO_LDPTR
);
5594 gpointer systype
= mono_type_get_object_checked (domain
, (MonoType
*)handle
, error
);
5595 goto_if_nok (error
, exit
);
5596 td
->last_ins
->data
[0] = get_data_item_index (td
, systype
);
5597 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_MP
);
5598 td
->ip
= next_ip
+ 5;
5600 PUSH_VT (td
, sizeof(gpointer
));
5601 interp_add_ins (td
, MINT_LDTOKEN
);
5602 td
->last_ins
->data
[0] = get_data_item_index (td
, handle
);
5603 PUSH_TYPE (td
, stack_type
[mt
], klass
);
5610 binary_arith_op(td
, MINT_ADD_OVF_I4
);
5613 case CEE_ADD_OVF_UN
:
5614 binary_arith_op(td
, MINT_ADD_OVF_UN_I4
);
5618 binary_arith_op(td
, MINT_MUL_OVF_I4
);
5621 case CEE_MUL_OVF_UN
:
5622 binary_arith_op(td
, MINT_MUL_OVF_UN_I4
);
5626 binary_arith_op(td
, MINT_SUB_OVF_I4
);
5629 case CEE_SUB_OVF_UN
:
5630 binary_arith_op(td
, MINT_SUB_OVF_UN_I4
);
5633 case CEE_ENDFINALLY
: {
5634 g_assert (td
->clause_indexes
[in_offset
] != -1);
5636 SIMPLE_OP (td
, MINT_ENDFINALLY
);
5637 td
->last_ins
->data
[0] = td
->clause_indexes
[in_offset
];
5638 // next instructions, if they exist, are always part of new bb
5639 // endfinally can be the last instruction in a function.
5640 // functions with clauses/endfinally are never inlined.
5641 // is_bb_start is not valid while inlining.
5642 g_assert (!inlining
);
5643 if (td
->ip
- td
->il_code
< td
->code_size
)
5644 td
->is_bb_start
[td
->ip
- header
->code
] = 1;
5651 if (*td
->ip
== CEE_LEAVE
)
5652 offset
= 5 + read32 (td
->ip
+ 1);
5654 offset
= 2 + (gint8
)td
->ip
[1];
5657 if (td
->clause_indexes
[in_offset
] != -1) {
5658 /* LEAVE instructions in catch clauses need to check for abort exceptions */
5659 handle_branch (td
, MINT_LEAVE_S_CHECK
, MINT_LEAVE_CHECK
, offset
);
5661 handle_branch (td
, MINT_LEAVE_S
, MINT_LEAVE
, offset
);
5664 if (*td
->ip
== CEE_LEAVE
)
5670 case MONO_CUSTOM_PREFIX
:
5673 case CEE_MONO_RETHROW
:
5674 CHECK_STACK (td
, 1);
5675 SIMPLE_OP (td
, MINT_MONO_RETHROW
);
5679 case CEE_MONO_LD_DELEGATE_METHOD_PTR
:
5682 interp_add_ins (td
, MINT_LD_DELEGATE_METHOD_PTR
);
5683 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
5685 case CEE_MONO_CALLI_EXTRA_ARG
:
5686 /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
5687 interp_add_ins (td
, MINT_POP1
);
5689 if (!interp_transform_call (td
, method
, NULL
, domain
, generic_context
, td
->is_bb_start
, NULL
, FALSE
, error
, FALSE
, FALSE
))
5692 case CEE_MONO_JIT_ICALL_ADDR
: {
5693 const guint32 token
= read32 (td
->ip
+ 1);
5695 const gconstpointer func
= mono_find_jit_icall_info ((MonoJitICallId
)token
)->func
;
5697 interp_add_ins (td
, MINT_LDFTN
);
5698 td
->last_ins
->data
[0] = get_data_item_index (td
, (gpointer
)func
);
5699 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
5702 case CEE_MONO_ICALL
: {
5703 MonoJitICallId
const jit_icall_id
= (MonoJitICallId
)read32 (td
->ip
+ 1);
5704 MonoJitICallInfo
const * const info
= mono_find_jit_icall_info (jit_icall_id
);
5707 CHECK_STACK (td
, info
->sig
->param_count
);
5708 if (jit_icall_id
== MONO_JIT_ICALL_mono_threads_attach_coop
) {
5709 rtm
->needs_thread_attach
= 1;
5711 /* attach needs two arguments, and has one return value: leave one element on the stack */
5712 interp_add_ins (td
, MINT_POP
);
5713 } else if (jit_icall_id
== MONO_JIT_ICALL_mono_threads_detach_coop
) {
5714 g_assert (rtm
->needs_thread_attach
);
5716 /* detach consumes two arguments, and no return value: drop both of them */
5717 interp_add_ins (td
, MINT_POP
);
5718 interp_add_ins (td
, MINT_POP
);
5720 int const icall_op
= interp_icall_op_for_sig (info
->sig
);
5721 g_assert (icall_op
!= -1);
5723 interp_add_ins (td
, icall_op
);
5724 // hash here is overkill
5725 td
->last_ins
->data
[0] = get_data_item_index (td
, (gpointer
)info
->func
);
5727 td
->sp
-= info
->sig
->param_count
;
5729 if (!MONO_TYPE_IS_VOID (info
->sig
->ret
)) {
5730 int mt
= mint_type (info
->sig
->ret
);
5731 PUSH_SIMPLE_TYPE(td
, stack_type
[mt
]);
5735 case CEE_MONO_VTADDR
: {
5737 CHECK_STACK (td
, 1);
5738 if (method
->wrapper_type
== MONO_WRAPPER_MANAGED_TO_NATIVE
)
5739 size
= mono_class_native_size(td
->sp
[-1].klass
, NULL
);
5741 size
= mono_class_value_size(td
->sp
[-1].klass
, NULL
);
5742 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
5743 interp_add_ins (td
, MINT_VTRESULT
);
5744 td
->last_ins
->data
[0] = 0;
5745 WRITE32_INS (td
->last_ins
, 1, &size
);
5748 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
5751 case CEE_MONO_LDPTR
:
5752 case CEE_MONO_CLASSCONST
:
5753 token
= read32 (td
->ip
+ 1);
5755 interp_add_ins (td
, MINT_MONO_LDPTR
);
5756 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_method_get_wrapper_data (method
, token
));
5757 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
5759 case CEE_MONO_OBJADDR
:
5760 CHECK_STACK (td
, 1);
5762 td
->sp
[-1].type
= STACK_TYPE_MP
;
5765 case CEE_MONO_NEWOBJ
:
5766 token
= read32 (td
->ip
+ 1);
5768 interp_add_ins (td
, MINT_MONO_NEWOBJ
);
5769 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_method_get_wrapper_data (method
, token
));
5770 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_O
);
5772 case CEE_MONO_RETOBJ
:
5773 CHECK_STACK (td
, 1);
5774 token
= read32 (td
->ip
+ 1);
5776 interp_add_ins (td
, MINT_MONO_RETOBJ
);
5779 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
5781 /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/
5783 if (td
->sp
> td
->stack
)
5784 g_warning ("CEE_MONO_RETOBJ: more values on stack: %d", td
->sp
-td
->stack
);
5786 case CEE_MONO_LDNATIVEOBJ
:
5787 token
= read32 (td
->ip
+ 1);
5789 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
5790 g_assert(m_class_is_valuetype (klass
));
5791 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
5793 case CEE_MONO_TLS
: {
5794 gint32 key
= read32 (td
->ip
+ 1);
5796 g_assertf (key
== TLS_KEY_SGEN_THREAD_INFO
, "%d", key
);
5797 interp_add_ins (td
, MINT_MONO_SGEN_THREAD_INFO
);
5798 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_MP
);
5801 case CEE_MONO_ATOMIC_STORE_I4
:
5802 CHECK_STACK (td
, 2);
5803 SIMPLE_OP (td
, MINT_MONO_ATOMIC_STORE_I4
);
5807 case CEE_MONO_SAVE_LMF
:
5808 case CEE_MONO_RESTORE_LMF
:
5809 case CEE_MONO_NOT_TAKEN
:
5812 case CEE_MONO_LDPTR_INT_REQ_FLAG
:
5813 interp_add_ins (td
, MINT_MONO_LDPTR
);
5814 td
->last_ins
->data
[0] = get_data_item_index (td
, &mono_thread_interruption_request_flag
);
5815 PUSH_TYPE (td
, STACK_TYPE_MP
, NULL
);
5818 case CEE_MONO_MEMORY_BARRIER
:
5819 interp_add_ins (td
, MINT_MONO_MEMORY_BARRIER
);
5822 case CEE_MONO_LDDOMAIN
:
5823 interp_add_ins (td
, MINT_MONO_LDDOMAIN
);
5824 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
5827 case CEE_MONO_SAVE_LAST_ERROR
:
5828 save_last_error
= TRUE
;
5831 case CEE_MONO_GET_SP
:
5832 interp_add_ins (td
, MINT_MONO_GET_SP
);
5833 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
5837 g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td
->ip
, td
->ip
-header
->code
);
5847 case CEE_PREFIXREF
: ves_abort(); break;
5850 * Note: Exceptions thrown when executing a prefixed opcode need
5851 * to take into account the number of prefix bytes (usually the
5852 * throw point is just (ip - n_prefix_bytes).
5858 interp_add_ins (td
, MINT_ARGLIST
);
5859 PUSH_VT (td
, SIZEOF_VOID_P
);
5860 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_VT
);
5865 if (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
) {
5866 interp_add_ins (td
, MINT_CEQ_I4
+ STACK_TYPE_I
- STACK_TYPE_I4
);
5868 if (td
->sp
[-1].type
== STACK_TYPE_R4
&& td
->sp
[-2].type
== STACK_TYPE_R8
)
5869 interp_add_ins (td
, MINT_CONV_R8_R4
);
5870 if (td
->sp
[-1].type
== STACK_TYPE_R8
&& td
->sp
[-2].type
== STACK_TYPE_R4
)
5871 interp_add_ins (td
, MINT_CONV_R8_R4_SP
);
5872 interp_add_ins (td
, MINT_CEQ_I4
+ td
->sp
[-1].type
- STACK_TYPE_I4
);
5875 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5880 if (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
)
5881 interp_add_ins (td
, MINT_CGT_I4
+ STACK_TYPE_I
- STACK_TYPE_I4
);
5883 interp_add_ins (td
, MINT_CGT_I4
+ td
->sp
[-1].type
- STACK_TYPE_I4
);
5885 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5890 if (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
)
5891 interp_add_ins (td
, MINT_CGT_UN_I4
+ STACK_TYPE_I
- STACK_TYPE_I4
);
5893 interp_add_ins (td
, MINT_CGT_UN_I4
+ td
->sp
[-1].type
- STACK_TYPE_I4
);
5895 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5900 if (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
)
5901 interp_add_ins (td
, MINT_CLT_I4
+ STACK_TYPE_I
- STACK_TYPE_I4
);
5903 interp_add_ins (td
, MINT_CLT_I4
+ td
->sp
[-1].type
- STACK_TYPE_I4
);
5905 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5910 if (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
)
5911 interp_add_ins (td
, MINT_CLT_UN_I4
+ STACK_TYPE_I
- STACK_TYPE_I4
);
5913 interp_add_ins (td
, MINT_CLT_UN_I4
+ td
->sp
[-1].type
- STACK_TYPE_I4
);
5915 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5918 case CEE_LDVIRTFTN
: /* fallthrough */
5921 if (*td
->ip
== CEE_LDVIRTFTN
) {
5922 CHECK_STACK (td
, 1);
5925 token
= read32 (td
->ip
+ 1);
5926 m
= interp_get_method (method
, token
, image
, generic_context
, error
);
5927 goto_if_nok (error
, exit
);
5929 if (!mono_method_can_access_method (method
, m
))
5930 interp_generate_mae_throw (td
, method
, m
);
5932 if (method
->wrapper_type
== MONO_WRAPPER_NONE
&& m
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
)
5933 m
= mono_marshal_get_synchronized_wrapper (m
);
5935 interp_add_ins (td
, *td
->ip
== CEE_LDFTN
? MINT_LDFTN
: MINT_LDVIRTFTN
);
5936 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
5937 goto_if_nok (error
, exit
);
5939 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_F
);
5943 int arg_n
= read16 (td
->ip
+ 1);
5945 load_arg (td
, arg_n
);
5947 load_local (td
, arg_locals
[arg_n
]);
5952 int n
= read16 (td
->ip
+ 1);
5955 get_arg_type_exact (td
, n
, &mt
);
5956 interp_add_ins (td
, mt
== MINT_TYPE_VT
? MINT_LDARGA_VT
: MINT_LDARGA
);
5957 td
->last_ins
->data
[0] = n
;
5959 int loc_n
= arg_locals
[n
];
5960 interp_add_ins (td
, MINT_LDLOCA_S
);
5961 td
->last_ins
->data
[0] = loc_n
;
5962 td
->locals
[loc_n
].flags
|= INTERP_LOCAL_FLAG_INDIRECT
;
5964 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_MP
);
5969 int arg_n
= read16 (td
->ip
+ 1);
5971 store_arg (td
, arg_n
);
5973 store_local (td
, arg_locals
[arg_n
]);
5978 int loc_n
= read16 (td
->ip
+ 1);
5980 load_local (td
, loc_n
);
5982 load_local (td
, local_locals
[loc_n
]);
5987 int loc_n
= read16 (td
->ip
+ 1);
5988 interp_add_ins (td
, MINT_LDLOCA_S
);
5990 loc_n
= local_locals
[loc_n
];
5991 td
->last_ins
->data
[0] = loc_n
;
5992 td
->locals
[loc_n
].flags
|= INTERP_LOCAL_FLAG_INDIRECT
;
5993 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_MP
);
5998 int loc_n
= read16 (td
->ip
+ 1);
6000 store_local (td
, loc_n
);
6002 store_local (td
, local_locals
[loc_n
]);
6008 CHECK_STACK (td
, 1);
6009 #if SIZEOF_VOID_P == 8
6010 if (td
->sp
[-1].type
== STACK_TYPE_I8
)
6011 interp_add_ins (td
, MINT_CONV_I4_I8
);
6013 interp_add_ins (td
, MINT_LOCALLOC
);
6014 if (td
->sp
!= td
->stack
+ 1)
6015 g_warning("CEE_LOCALLOC: stack not empty");
6017 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
6020 case CEE_UNUSED57
: ves_abort(); break;
6023 interp_add_ins (td
, MINT_ENDFILTER
);
6026 case CEE_UNALIGNED_
:
6035 /* FIX: should do something? */;
6036 // TODO: This should raise a method_tail_call profiler event.
6040 token
= read32 (td
->ip
+ 1);
6041 klass
= mini_get_class (method
, token
, generic_context
);
6042 CHECK_TYPELOAD (klass
);
6043 if (m_class_is_valuetype (klass
)) {
6044 interp_add_ins (td
, MINT_INITOBJ
);
6045 i32
= mono_class_value_size (klass
, NULL
);
6046 WRITE32_INS (td
->last_ins
, 0, &i32
);
6049 interp_add_ins (td
, MINT_LDNULL
);
6050 PUSH_TYPE(td
, STACK_TYPE_O
, NULL
);
6051 interp_add_ins (td
, MINT_STIND_REF
);
6058 /* FIX? convert length to I8? */
6060 interp_add_ins (td
, MINT_MONO_MEMORY_BARRIER
);
6061 interp_add_ins (td
, MINT_CPBLK
);
6062 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_SEQ
);
6070 case CEE_CONSTRAINED_
:
6071 token
= read32 (td
->ip
+ 1);
6072 constrained_class
= mini_get_class (method
, token
, generic_context
);
6073 CHECK_TYPELOAD (constrained_class
);
6078 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
6079 interp_add_ins (td
, MINT_INITBLK
);
6084 /* FIXME: implement */
6088 int clause_index
= td
->clause_indexes
[in_offset
];
6089 g_assert (clause_index
!= -1);
6090 SIMPLE_OP (td
, MINT_RETHROW
);
6091 td
->last_ins
->data
[0] = rtm
->exvar_offsets
[clause_index
];
6097 token
= read32 (td
->ip
+ 1);
6099 if (mono_metadata_token_table (token
) == MONO_TABLE_TYPESPEC
&& !image_is_dynamic (m_class_get_image (method
->klass
)) && !generic_context
) {
6101 MonoType
*type
= mono_type_create_from_typespec_checked (image
, token
, error
);
6102 goto_if_nok (error
, exit
);
6103 size
= mono_type_size (type
, &align
);
6106 MonoClass
*szclass
= mini_get_class (method
, token
, generic_context
);
6107 CHECK_TYPELOAD (szclass
);
6109 if (!szclass
->valuetype
)
6110 THROW_EX (mono_exception_from_name (mono_defaults
.corlib
, "System", "InvalidProgramException"), ip
- 5);
6112 size
= mono_type_size (m_class_get_byval_arg (szclass
), &align
);
6114 interp_add_ins (td
, MINT_LDC_I4
);
6115 WRITE32_INS (td
->last_ins
, 0, &size
);
6116 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
6119 case CEE_REFANYTYPE
:
6120 interp_add_ins (td
, MINT_REFANYTYPE
);
6122 POP_VT (td
, sizeof (MonoTypedRef
));
6123 PUSH_VT (td
, sizeof (gpointer
));
6124 SET_TYPE(td
->sp
- 1, STACK_TYPE_VT
, NULL
);
6127 g_error ("transform.c: Unimplemented opcode: 0xFE %02x (%s) at 0x%x\n", *td
->ip
, mono_opcode_name (256 + *td
->ip
), td
->ip
-header
->code
);
6131 g_error ("transform.c: Unimplemented opcode: %02x at 0x%x\n", *td
->ip
, td
->ip
-header
->code
);
6134 // No IR instructions were added as part of a bb_start IL instruction. Add a MINT_NOP
6135 // so we always have an instruction associated with a bb_start. This is simple and avoids
6136 // any complications associated with il_offset tracking.
6137 if (prev_last_ins
== td
->last_ins
&& (!inlining
&& td
->is_bb_start
[in_offset
]) && td
->ip
< end
)
6138 interp_add_ins (td
, MINT_NOP
);
6141 g_assert (td
->ip
== end
);
6144 g_free (arg_locals
);
6145 g_free (local_locals
);
6146 mono_basic_block_free (original_bb
);
6154 // Find the offset of the first interp instruction generated starting il_offset
6155 // This is needed to find the end of clauses.
6157 find_in_offset (TransformData
*td
, int il_offset
)
6160 while (!td
->in_offsets
[i
])
6162 return td
->in_offsets
[i
] - 1;
6165 // We store in the in_offset array the native_offset + 1, so 0 can mean only that the il
6166 // offset is uninitialized. Otherwise 0 is valid value for first interp instruction.
6168 get_in_offset (TransformData
*td
, int il_offset
)
6170 int target_offset
= td
->in_offsets
[il_offset
];
6171 g_assert (target_offset
);
6172 return target_offset
- 1;
6176 handle_relocations (TransformData
*td
)
6178 // Handle relocations
6179 for (int i
= 0; i
< td
->relocs
->len
; ++i
) {
6180 Reloc
*reloc
= (Reloc
*)g_ptr_array_index (td
->relocs
, i
);
6181 int offset
= get_in_offset (td
, reloc
->target
) - reloc
->offset
;
6183 switch (reloc
->type
) {
6184 case RELOC_SHORT_BRANCH
:
6185 g_assert (td
->new_code
[reloc
->offset
+ 1] == 0xdead);
6186 td
->new_code
[reloc
->offset
+ 1] = offset
;
6188 case RELOC_LONG_BRANCH
: {
6189 guint16
*v
= (guint16
*) &offset
;
6190 g_assert (td
->new_code
[reloc
->offset
+ 1] == 0xdead);
6191 g_assert (td
->new_code
[reloc
->offset
+ 2] == 0xbeef);
6192 td
->new_code
[reloc
->offset
+ 1] = *(guint16
*) v
;
6193 td
->new_code
[reloc
->offset
+ 2] = *(guint16
*) (v
+ 1);
6196 case RELOC_SWITCH
: {
6197 guint16
*v
= (guint16
*)&offset
;
6198 g_assert (td
->new_code
[reloc
->offset
] == 0xdead);
6199 g_assert (td
->new_code
[reloc
->offset
+ 1] == 0xbeef);
6200 td
->new_code
[reloc
->offset
] = *(guint16
*)v
;
6201 td
->new_code
[reloc
->offset
+ 1] = *(guint16
*)(v
+ 1);
6205 g_assert_not_reached ();
6213 get_inst_length (InterpInst
*ins
)
6215 if (ins
->opcode
== MINT_SWITCH
)
6216 return MINT_SWITCH_LEN (READ32 (&ins
->data
[0]));
6217 #ifdef ENABLE_EXPERIMENT_TIERED
6218 else if (MINT_IS_PATCHABLE_CALL (ins
->opcode
))
6219 return MAX (mono_interp_oplen
[MINT_JIT_CALL2
], mono_interp_oplen
[ins
->opcode
]);
6222 return mono_interp_oplen
[ins
->opcode
];
6226 get_inst_stack_usage (TransformData
*td
, InterpInst
*ins
, int *pop
, int *push
)
6228 guint16 opcode
= ins
->opcode
;
6229 if (mono_interp_oppop
[opcode
] == MINT_VAR_POP
||
6230 mono_interp_oppush
[opcode
] == MINT_VAR_PUSH
) {
6235 case MINT_CALLVIRT_FAST
:
6237 case MINT_VCALLVIRT
:
6238 case MINT_VCALLVIRT_FAST
: {
6239 InterpMethod
*imethod
= (InterpMethod
*) td
->data_items
[ins
->data
[0]];
6240 *pop
= imethod
->param_count
+ imethod
->hasthis
;
6241 if (opcode
== MINT_JIT_CALL
)
6242 *push
= imethod
->rtype
->type
!= MONO_TYPE_VOID
;
6244 *push
= opcode
== MINT_CALL
|| opcode
== MINT_CALLVIRT
|| opcode
== MINT_CALLVIRT_FAST
;
6247 #ifndef ENABLE_NETCORE
6248 case MINT_CALLRUN
: {
6249 MonoMethodSignature
*csignature
= (MonoMethodSignature
*) td
->data_items
[ins
->data
[1]];
6250 *pop
= csignature
->param_count
+ csignature
->hasthis
;
6251 *push
= csignature
->ret
->type
!= MONO_TYPE_VOID
;
6256 case MINT_CALLI_NAT
:
6257 case MINT_CALLI_NAT_FAST
: {
6258 MonoMethodSignature
*csignature
= (MonoMethodSignature
*) td
->data_items
[ins
->data
[0]];
6259 *pop
= csignature
->param_count
+ csignature
->hasthis
+ 1;
6260 *push
= csignature
->ret
->type
!= MONO_TYPE_VOID
;
6263 case MINT_CALL_VARARG
: {
6264 InterpMethod
*imethod
= (InterpMethod
*) td
->data_items
[ins
->data
[0]];
6265 MonoMethodSignature
*csignature
= (MonoMethodSignature
*) td
->data_items
[ins
->data
[1]];
6266 *pop
= imethod
->param_count
+ imethod
->hasthis
+ csignature
->param_count
- csignature
->sentinelpos
;
6267 *push
= imethod
->rtype
->type
!= MONO_TYPE_VOID
;
6270 case MINT_NEWOBJ_FAST
: {
6271 int param_count
= ins
->data
[1];
6272 gboolean is_inlined
= ins
->data
[0] == INLINED_METHOD_FLAG
;
6274 // This needs to be handled explictly during cprop, in order to properly
6275 // keep track of stack contents
6284 case MINT_NEWOBJ_ARRAY
:
6285 case MINT_NEWOBJ_VT_FAST
:
6286 case MINT_NEWOBJ_VTST_FAST
:
6287 *pop
= ins
->data
[1];
6291 case MINT_LDELEMA_TC
:
6292 *pop
= ins
->data
[0] + 1;
6296 InterpMethod
*imethod
= (InterpMethod
*) td
->data_items
[ins
->data
[0]];
6297 *pop
= imethod
->param_count
;
6301 case MINT_INTRINS_BYREFERENCE_CTOR
: {
6302 InterpMethod
*imethod
= (InterpMethod
*) td
->data_items
[ins
->data
[0]];
6303 *pop
= imethod
->param_count
;
6308 g_assert_not_reached ();
6311 *pop
= mono_interp_oppop
[opcode
];
6312 *push
= mono_interp_oppush
[opcode
];
6317 emit_compacted_instruction (TransformData
*td
, guint16
* start_ip
, InterpInst
*ins
)
6319 guint16 opcode
= ins
->opcode
;
6320 guint16
*ip
= start_ip
;
6322 // We know what IL offset this instruction was created for. We can now map the IL offset
6323 // to the IR offset. We use this array to resolve the relocations, which reference the IL.
6324 if (ins
->il_offset
!= -1 && !td
->in_offsets
[ins
->il_offset
]) {
6325 g_assert (ins
->il_offset
>= 0 && ins
->il_offset
< td
->header
->code_size
);
6326 td
->in_offsets
[ins
->il_offset
] = start_ip
- td
->new_code
+ 1;
6328 MonoDebugLineNumberEntry lne
;
6329 lne
.native_offset
= (guint8
*)start_ip
- (guint8
*)td
->new_code
;
6330 lne
.il_offset
= ins
->il_offset
;
6331 g_array_append_val (td
->line_numbers
, lne
);
6334 if (opcode
== MINT_NOP
)
6338 if (opcode
== MINT_SWITCH
) {
6339 int labels
= READ32 (&ins
->data
[0]);
6340 // Write number of switch labels
6341 *ip
++ = ins
->data
[0];
6342 *ip
++ = ins
->data
[1];
6343 // Add relocation for each label
6344 for (int i
= 0; i
< labels
; i
++) {
6345 Reloc
*reloc
= (Reloc
*)mono_mempool_alloc0 (td
->mempool
, sizeof (Reloc
));
6346 reloc
->type
= RELOC_SWITCH
;
6347 reloc
->offset
= ip
- td
->new_code
;
6348 reloc
->target
= READ32 (&ins
->data
[2 + i
* 2]);
6349 g_ptr_array_add (td
->relocs
, reloc
);
6353 } else if ((opcode
>= MINT_BRFALSE_I4_S
&& opcode
<= MINT_BRTRUE_R8_S
) ||
6354 (opcode
>= MINT_BEQ_I4_S
&& opcode
<= MINT_BLT_UN_R8_S
) ||
6355 opcode
== MINT_BR_S
|| opcode
== MINT_LEAVE_S
|| opcode
== MINT_LEAVE_S_CHECK
) {
6356 const int br_offset
= start_ip
- td
->new_code
;
6357 if (ins
->data
[0] < ins
->il_offset
) {
6358 // Backwards branch. We can already patch it.
6359 *ip
++ = get_in_offset (td
, ins
->data
[0]) - br_offset
;
6361 // We don't know the in_offset of the target, add a reloc
6362 Reloc
*reloc
= (Reloc
*)mono_mempool_alloc0 (td
->mempool
, sizeof (Reloc
));
6363 reloc
->type
= RELOC_SHORT_BRANCH
;
6364 reloc
->offset
= br_offset
;
6365 reloc
->target
= ins
->data
[0];
6366 g_ptr_array_add (td
->relocs
, reloc
);
6369 } else if ((opcode
>= MINT_BRFALSE_I4
&& opcode
<= MINT_BRTRUE_R8
) ||
6370 (opcode
>= MINT_BEQ_I4
&& opcode
<= MINT_BLT_UN_R8
) ||
6371 opcode
== MINT_BR
|| opcode
== MINT_LEAVE
|| opcode
== MINT_LEAVE_CHECK
) {
6372 const int br_offset
= start_ip
- td
->new_code
;
6373 int target_il
= READ32 (&ins
->data
[0]);
6374 if (target_il
< ins
->il_offset
) {
6375 // Backwards branch. We can already patch it
6376 const int br_offset
= start_ip
- td
->new_code
;
6377 int target_offset
= get_in_offset (td
, target_il
) - br_offset
;
6378 WRITE32 (ip
, &target_offset
);
6380 Reloc
*reloc
= (Reloc
*)mono_mempool_alloc0 (td
->mempool
, sizeof (Reloc
));
6381 reloc
->type
= RELOC_LONG_BRANCH
;
6382 reloc
->offset
= br_offset
;
6383 reloc
->target
= target_il
;
6384 g_ptr_array_add (td
->relocs
, reloc
);
6388 } else if (opcode
== MINT_SDB_SEQ_POINT
) {
6389 SeqPoint
*seqp
= (SeqPoint
*)mono_mempool_alloc0 (td
->mempool
, sizeof (SeqPoint
));
6390 InterpBasicBlock
*cbb
;
6392 if (ins
->flags
& INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY
) {
6393 seqp
->il_offset
= METHOD_ENTRY_IL_OFFSET
;
6394 cbb
= td
->offset_to_bb
[0];
6396 if (ins
->flags
& INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT
)
6397 seqp
->il_offset
= METHOD_EXIT_IL_OFFSET
;
6399 seqp
->il_offset
= ins
->il_offset
;
6400 cbb
= td
->offset_to_bb
[ins
->il_offset
];
6402 seqp
->native_offset
= (guint8
*)start_ip
- (guint8
*)td
->new_code
;
6403 if (ins
->flags
& INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK
)
6404 seqp
->flags
|= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK
;
6405 if (ins
->flags
& INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL
)
6406 seqp
->flags
|= MONO_SEQ_POINT_FLAG_NESTED_CALL
;
6407 g_ptr_array_add (td
->seq_points
, seqp
);
6409 cbb
->seq_points
= g_slist_prepend_mempool (td
->mempool
, cbb
->seq_points
, seqp
);
6410 cbb
->last_seq_point
= seqp
;
6411 #ifdef ENABLE_EXPERIMENT_TIERED
6412 } else if (ins
->flags
& INTERP_INST_FLAG_RECORD_CALL_PATCH
) {
6413 g_assert (MINT_IS_PATCHABLE_CALL (opcode
));
6415 /* TODO: could `ins` be removed by any interp optimization? */
6416 MonoMethod
*target_method
= (MonoMethod
*) g_hash_table_lookup (td
->patchsite_hash
, ins
);
6417 g_assert (target_method
);
6418 g_hash_table_remove (td
->patchsite_hash
, ins
);
6420 mini_tiered_record_callsite (start_ip
, target_method
, TIERED_PATCH_KIND_INTERP
);
6422 int size
= mono_interp_oplen
[ins
->opcode
];
6423 int jit_call2_size
= mono_interp_oplen
[MINT_JIT_CALL2
];
6425 g_assert (size
< jit_call2_size
);
6427 // Emit the rest of the data
6428 for (int i
= 0; i
< size
- 1; i
++)
6429 *ip
++ = ins
->data
[i
];
6431 /* intentional padding so we can patch a MINT_JIT_CALL2 here */
6432 for (int i
= size
- 1; i
< (jit_call2_size
- 1); i
++)
6436 if (MINT_IS_LDLOC (opcode
) || MINT_IS_STLOC (opcode
) || MINT_IS_STLOC_NP (opcode
) || opcode
== MINT_LDLOCA_S
||
6437 MINT_IS_LDLOCFLD (opcode
) || MINT_IS_LOCUNOP (opcode
) || MINT_IS_STLOCFLD (opcode
)) {
6438 ins
->data
[0] = get_interp_local_offset (td
, ins
->data
[0]);
6439 } else if (MINT_IS_MOVLOC (opcode
)) {
6440 ins
->data
[0] = get_interp_local_offset (td
, ins
->data
[0]);
6441 ins
->data
[1] = get_interp_local_offset (td
, ins
->data
[1]);
6444 int size
= get_inst_length (ins
) - 1;
6445 // Emit the rest of the data
6446 for (int i
= 0; i
< size
; i
++)
6447 *ip
++ = ins
->data
[i
];
6449 mono_interp_stats
.emitted_instructions
++;
6453 // Generates the final code, after we are done with all the passes
6455 generate_compacted_code (TransformData
*td
)
6459 td
->relocs
= g_ptr_array_new ();
6461 // Iterate once to compute the exact size of the compacted code
6462 InterpInst
*ins
= td
->first_ins
;
6464 size
+= get_inst_length (ins
);
6468 // Generate the compacted stream of instructions
6469 td
->new_code
= ip
= (guint16
*)mono_domain_alloc0 (td
->rtm
->domain
, size
* sizeof (guint16
));
6470 ins
= td
->first_ins
;
6472 ip
= emit_compacted_instruction (td
, ip
, ins
);
6475 td
->new_code_end
= ip
;
6476 td
->in_offsets
[td
->header
->code_size
] = td
->new_code_end
- td
->new_code
;
6478 // Patch all branches
6479 handle_relocations (td
);
6481 g_ptr_array_free (td
->relocs
, TRUE
);
6485 get_movloc_for_type (int mt
)
6490 return MINT_MOVLOC_1
;
6493 return MINT_MOVLOC_2
;
6496 return MINT_MOVLOC_4
;
6499 return MINT_MOVLOC_8
;
6502 #if SIZEOF_VOID_P == 8
6503 return MINT_MOVLOC_8
;
6505 return MINT_MOVLOC_4
;
6508 return MINT_MOVLOC_VT
;
6510 g_assert_not_reached ();
6513 // The value of local has changed. This means the contents of the stack where the
6514 // local was loaded, no longer contain the value of the local. Clear them.
6516 clear_stack_content_info_for_local (StackContentInfo
*start
, StackContentInfo
*end
, int local
)
6518 StackContentInfo
*si
;
6519 for (si
= start
; si
< end
; si
++) {
6520 if (si
->val
.type
== STACK_VALUE_LOCAL
&& si
->val
.local
== local
)
6521 si
->val
.type
= STACK_VALUE_NONE
;
6525 // The value of argument has changed. This means the contents of the stack where the
6526 // argument was loaded, no longer contain the value of the argument. Clear them.
6528 clear_stack_content_info_for_argument (StackContentInfo
*start
, StackContentInfo
*end
, int argument
)
6530 StackContentInfo
*si
;
6531 for (si
= start
; si
< end
; si
++) {
6532 if (si
->val
.type
== STACK_VALUE_ARG
&& si
->val
.arg
== argument
)
6533 si
->val
.type
= STACK_VALUE_NONE
;
6537 // The value of local has changed. This means we can no longer assume that any other local
6538 // is a copy of this local.
6540 clear_local_content_info_for_local (StackValue
*start
, StackValue
*end
, int local
)
6543 for (sval
= start
; sval
< end
; sval
++) {
6544 if (sval
->type
== STACK_VALUE_LOCAL
&& sval
->local
== local
)
6545 sval
->type
= STACK_VALUE_NONE
;
6550 interp_local_deadce (TransformData
*td
, int *local_ref_count
)
6553 gboolean needs_dce
= FALSE
;
6554 gboolean needs_cprop
= FALSE
;
6556 for (int i
= 0; i
< td
->locals_size
; i
++) {
6557 g_assert (local_ref_count
[i
] >= 0);
6558 if (!local_ref_count
[i
] &&
6559 (td
->locals
[i
].flags
& INTERP_LOCAL_FLAG_INDIRECT
) == 0 &&
6560 (td
->locals
[i
].flags
& INTERP_LOCAL_FLAG_DEAD
) == 0) {
6562 // If we do another deadce iteration over the code, make sure we don't try
6563 // to kill instructions accessing locals that have already been handled in
6564 // a previous iteration.
6565 td
->locals
[i
].flags
|= INTERP_LOCAL_FLAG_DEAD
;
6570 // Return early if all locals are alive
6574 // Kill instructions that don't use stack and are storing into dead locals
6575 for (ins
= td
->first_ins
; ins
!= NULL
; ins
= ins
->next
) {
6576 if (MINT_IS_STLOC_NP (ins
->opcode
)) {
6577 if (!local_ref_count
[ins
->data
[0]] && (td
->locals
[ins
->data
[0]].flags
& INTERP_LOCAL_FLAG_INDIRECT
) == 0) {
6578 interp_clear_ins (td
, ins
);
6579 mono_interp_stats
.killed_instructions
++;
6580 // We killed an instruction that makes use of the stack. This might uncover new optimizations
6583 } else if (MINT_IS_MOVLOC (ins
->opcode
)) {
6584 if (!local_ref_count
[ins
->data
[1]] && (td
->locals
[ins
->data
[1]].flags
& INTERP_LOCAL_FLAG_INDIRECT
) == 0) {
6585 interp_clear_ins (td
, ins
);
6586 mono_interp_stats
.killed_instructions
++;
6588 } else if (MINT_IS_STLOC (ins
->opcode
) && ins
->opcode
!= MINT_STLOC_VT
) {
6589 if (!local_ref_count
[ins
->data
[0]] && (td
->locals
[ins
->data
[0]].flags
& INTERP_LOCAL_FLAG_INDIRECT
) == 0) {
6590 // We store to a dead stloc, we can replace it with a POP to save local space
6591 ins
->opcode
= MINT_POP
;
6592 mono_interp_stats
.added_pop_count
++;
6593 // We might to be able to kill both the pop and the instruction pushing the value
6601 #define INTERP_FOLD_UNOP(opcode,stack_type,field,op) \
6603 g_assert (sp->val.type == stack_type); \
6604 result.type = stack_type; \
6605 result.field = op sp->val.field; \
6608 #define INTERP_FOLD_CONV(opcode,stack_type_dst,field_dst,stack_type_src,field_src,cast_type) \
6610 g_assert (sp->val.type == stack_type_src); \
6611 result.type = stack_type_dst; \
6612 result.field_dst = (cast_type)sp->val.field_src; \
6616 interp_fold_unop (TransformData
*td
, StackContentInfo
*sp
, InterpInst
*ins
)
6619 // Decrement sp so it's easier to access top of the stack
6621 if (sp
->val
.type
!= STACK_VALUE_I4
&& sp
->val
.type
!= STACK_VALUE_I8
)
6624 // Top of the stack is a constant
6625 switch (ins
->opcode
) {
6626 INTERP_FOLD_UNOP (MINT_ADD1_I4
, STACK_VALUE_I4
, i
, 1+);
6627 INTERP_FOLD_UNOP (MINT_ADD1_I8
, STACK_VALUE_I8
, l
, 1+);
6628 INTERP_FOLD_UNOP (MINT_SUB1_I4
, STACK_VALUE_I4
, i
, -1+);
6629 INTERP_FOLD_UNOP (MINT_SUB1_I8
, STACK_VALUE_I8
, l
, -1+);
6630 INTERP_FOLD_UNOP (MINT_NEG_I4
, STACK_VALUE_I4
, i
, -);
6631 INTERP_FOLD_UNOP (MINT_NEG_I8
, STACK_VALUE_I8
, l
, -);
6632 INTERP_FOLD_UNOP (MINT_NOT_I4
, STACK_VALUE_I4
, i
, ~);
6633 INTERP_FOLD_UNOP (MINT_NOT_I8
, STACK_VALUE_I8
, l
, ~);
6634 INTERP_FOLD_UNOP (MINT_CEQ0_I4
, STACK_VALUE_I4
, i
, 0 ==);
6636 INTERP_FOLD_CONV (MINT_CONV_I1_I4
, STACK_VALUE_I4
, i
, STACK_VALUE_I4
, i
, gint8
);
6637 INTERP_FOLD_CONV (MINT_CONV_I1_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, gint8
);
6638 INTERP_FOLD_CONV (MINT_CONV_U1_I4
, STACK_VALUE_I4
, i
, STACK_VALUE_I4
, i
, guint8
);
6639 INTERP_FOLD_CONV (MINT_CONV_U1_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, guint8
);
6641 INTERP_FOLD_CONV (MINT_CONV_I2_I4
, STACK_VALUE_I4
, i
, STACK_VALUE_I4
, i
, gint16
);
6642 INTERP_FOLD_CONV (MINT_CONV_I2_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, gint16
);
6643 INTERP_FOLD_CONV (MINT_CONV_U2_I4
, STACK_VALUE_I4
, i
, STACK_VALUE_I4
, i
, guint16
);
6644 INTERP_FOLD_CONV (MINT_CONV_U2_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, guint16
);
6646 INTERP_FOLD_CONV (MINT_CONV_I4_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, gint32
);
6647 INTERP_FOLD_CONV (MINT_CONV_U4_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, gint32
);
6649 INTERP_FOLD_CONV (MINT_CONV_I8_I4
, STACK_VALUE_I8
, l
, STACK_VALUE_I4
, i
, gint32
);
6650 INTERP_FOLD_CONV (MINT_CONV_I8_U4
, STACK_VALUE_I8
, l
, STACK_VALUE_I4
, i
, guint32
);
6656 // We were able to compute the result of the ins instruction. We store the
6657 // current value for the top of the stack and, if possible, try to replace the
6658 // instructions that are part of this unary operation with a single LDC.
6659 mono_interp_stats
.constant_folds
++;
6660 if (sp
->ins
!= NULL
) {
6661 interp_clear_ins (td
, sp
->ins
);
6662 mono_interp_stats
.killed_instructions
++;
6663 if (result
.type
== STACK_VALUE_I4
)
6664 ins
= interp_get_ldc_i4_from_const (td
, ins
, result
.i
);
6665 else if (result
.type
== STACK_VALUE_I8
)
6666 ins
= interp_inst_replace_with_i8_const (td
, ins
, result
.l
);
6668 g_assert_not_reached ();
6669 if (td
->verbose_level
) {
6670 g_print ("Fold unop :\n\t");
6671 dump_interp_inst_newline (ins
);
6680 sp
->val
.type
= STACK_VALUE_NONE
;
6684 #define INTERP_FOLD_BINOP(opcode,stack_type,field,op) \
6686 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6687 result.type = stack_type; \
6688 result.field = sp [0].val.field op sp [1].val.field; \
6691 #define INTERP_FOLD_BINOP_FULL(opcode,stack_type,field,op,cast_type,cond) \
6693 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6694 if (!(cond)) goto cfold_failed; \
6695 result.type = stack_type; \
6696 result.field = (cast_type)sp [0].val.field op (cast_type)sp [1].val.field; \
6699 #define INTERP_FOLD_SHIFTOP(opcode,stack_type,field,shift_op,cast_type) \
6701 g_assert (sp [1].val.type == STACK_VALUE_I4); \
6702 result.type = stack_type; \
6703 result.field = (cast_type)sp [0].val.field shift_op sp [1].val.i; \
6706 #define INTERP_FOLD_RELOP(opcode,stack_type,field,relop,cast_type) \
6708 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6709 result.type = STACK_VALUE_I4; \
6710 result.i = (cast_type) sp [0].val.field relop (cast_type) sp [1].val.field; \
6715 interp_fold_binop (TransformData
*td
, StackContentInfo
*sp
, InterpInst
*ins
)
6718 // Decrement sp so it's easier to access top of the stack
6720 if (sp
[0].val
.type
!= STACK_VALUE_I4
&& sp
[0].val
.type
!= STACK_VALUE_I8
)
6722 if (sp
[1].val
.type
!= STACK_VALUE_I4
&& sp
[1].val
.type
!= STACK_VALUE_I8
)
6725 // Top two values of the stack are constants
6726 switch (ins
->opcode
) {
6727 INTERP_FOLD_BINOP (MINT_ADD_I4
, STACK_VALUE_I4
, i
, +);
6728 INTERP_FOLD_BINOP (MINT_ADD_I8
, STACK_VALUE_I8
, l
, +);
6729 INTERP_FOLD_BINOP (MINT_SUB_I4
, STACK_VALUE_I4
, i
, -);
6730 INTERP_FOLD_BINOP (MINT_SUB_I8
, STACK_VALUE_I8
, l
, -);
6731 INTERP_FOLD_BINOP (MINT_MUL_I4
, STACK_VALUE_I4
, i
, *);
6732 INTERP_FOLD_BINOP (MINT_MUL_I8
, STACK_VALUE_I8
, l
, *);
6734 INTERP_FOLD_BINOP (MINT_AND_I4
, STACK_VALUE_I4
, i
, &);
6735 INTERP_FOLD_BINOP (MINT_AND_I8
, STACK_VALUE_I8
, l
, &);
6736 INTERP_FOLD_BINOP (MINT_OR_I4
, STACK_VALUE_I4
, i
, |);
6737 INTERP_FOLD_BINOP (MINT_OR_I8
, STACK_VALUE_I8
, l
, |);
6738 INTERP_FOLD_BINOP (MINT_XOR_I4
, STACK_VALUE_I4
, i
, ^);
6739 INTERP_FOLD_BINOP (MINT_XOR_I8
, STACK_VALUE_I8
, l
, ^);
6741 INTERP_FOLD_SHIFTOP (MINT_SHL_I4
, STACK_VALUE_I4
, i
, <<, gint32
);
6742 INTERP_FOLD_SHIFTOP (MINT_SHL_I8
, STACK_VALUE_I8
, l
, <<, gint64
);
6743 INTERP_FOLD_SHIFTOP (MINT_SHR_I4
, STACK_VALUE_I4
, i
, >>, gint32
);
6744 INTERP_FOLD_SHIFTOP (MINT_SHR_I8
, STACK_VALUE_I8
, l
, >>, gint64
);
6745 INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I4
, STACK_VALUE_I4
, i
, >>, guint32
);
6746 INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I8
, STACK_VALUE_I8
, l
, >>, guint64
);
6748 INTERP_FOLD_RELOP (MINT_CEQ_I4
, STACK_VALUE_I4
, i
, ==, gint32
);
6749 INTERP_FOLD_RELOP (MINT_CEQ_I8
, STACK_VALUE_I8
, l
, ==, gint64
);
6750 INTERP_FOLD_RELOP (MINT_CNE_I4
, STACK_VALUE_I4
, i
, !=, gint32
);
6751 INTERP_FOLD_RELOP (MINT_CNE_I8
, STACK_VALUE_I8
, l
, !=, gint64
);
6753 INTERP_FOLD_RELOP (MINT_CGT_I4
, STACK_VALUE_I4
, i
, >, gint32
);
6754 INTERP_FOLD_RELOP (MINT_CGT_I8
, STACK_VALUE_I8
, l
, >, gint64
);
6755 INTERP_FOLD_RELOP (MINT_CGT_UN_I4
, STACK_VALUE_I4
, i
, >, guint32
);
6756 INTERP_FOLD_RELOP (MINT_CGT_UN_I8
, STACK_VALUE_I8
, l
, >, guint64
);
6758 INTERP_FOLD_RELOP (MINT_CGE_I4
, STACK_VALUE_I4
, i
, >=, gint32
);
6759 INTERP_FOLD_RELOP (MINT_CGE_I8
, STACK_VALUE_I8
, l
, >=, gint64
);
6760 INTERP_FOLD_RELOP (MINT_CGE_UN_I4
, STACK_VALUE_I4
, i
, >=, guint32
);
6761 INTERP_FOLD_RELOP (MINT_CGE_UN_I8
, STACK_VALUE_I8
, l
, >=, guint64
);
6763 INTERP_FOLD_RELOP (MINT_CLT_I4
, STACK_VALUE_I4
, i
, <, gint32
);
6764 INTERP_FOLD_RELOP (MINT_CLT_I8
, STACK_VALUE_I8
, l
, <, gint64
);
6765 INTERP_FOLD_RELOP (MINT_CLT_UN_I4
, STACK_VALUE_I4
, i
, <, guint32
);
6766 INTERP_FOLD_RELOP (MINT_CLT_UN_I8
, STACK_VALUE_I8
, l
, <, guint64
);
6768 INTERP_FOLD_RELOP (MINT_CLE_I4
, STACK_VALUE_I4
, i
, <=, gint32
);
6769 INTERP_FOLD_RELOP (MINT_CLE_I8
, STACK_VALUE_I8
, l
, <=, gint64
);
6770 INTERP_FOLD_RELOP (MINT_CLE_UN_I4
, STACK_VALUE_I4
, i
, <=, guint32
);
6771 INTERP_FOLD_RELOP (MINT_CLE_UN_I8
, STACK_VALUE_I8
, l
, <=, guint64
);
6773 INTERP_FOLD_BINOP_FULL (MINT_DIV_I4
, STACK_VALUE_I4
, i
, /, gint32
, sp
[1].val
.i
!= 0 && (sp
[0].val
.i
!= G_MININT32
|| sp
[1].val
.i
!= -1));
6774 INTERP_FOLD_BINOP_FULL (MINT_DIV_I8
, STACK_VALUE_I8
, l
, /, gint64
, sp
[1].val
.l
!= 0 && (sp
[0].val
.l
!= G_MININT64
|| sp
[1].val
.l
!= -1));
6775 INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I4
, STACK_VALUE_I4
, i
, /, guint32
, sp
[1].val
.i
!= 0);
6776 INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I8
, STACK_VALUE_I8
, l
, /, guint64
, sp
[1].val
.l
!= 0);
6778 INTERP_FOLD_BINOP_FULL (MINT_REM_I4
, STACK_VALUE_I4
, i
, %, gint32
, sp
[1].val
.i
!= 0 && (sp
[0].val
.i
!= G_MININT32
|| sp
[1].val
.i
!= -1));
6779 INTERP_FOLD_BINOP_FULL (MINT_REM_I8
, STACK_VALUE_I8
, l
, %, gint64
, sp
[1].val
.l
!= 0 && (sp
[0].val
.l
!= G_MININT64
|| sp
[1].val
.l
!= -1));
6780 INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I4
, STACK_VALUE_I4
, i
, %, guint32
, sp
[1].val
.i
!= 0);
6781 INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I8
, STACK_VALUE_I8
, l
, %, guint64
, sp
[1].val
.l
!= 0);
6787 // We were able to compute the result of the ins instruction. We store the
6788 // current value for the top of the stack and, if possible, try to replace the
6789 // instructions that are part of this unary operation with a single LDC.
6790 mono_interp_stats
.constant_folds
++;
6791 if (sp
[0].ins
!= NULL
&& sp
[1].ins
!= NULL
) {
6792 interp_clear_ins (td
, sp
[0].ins
);
6793 interp_clear_ins (td
, sp
[1].ins
);
6794 mono_interp_stats
.killed_instructions
+= 2;
6795 if (result
.type
== STACK_VALUE_I4
)
6796 ins
= interp_get_ldc_i4_from_const (td
, ins
, result
.i
);
6797 else if (result
.type
== STACK_VALUE_I8
)
6798 ins
= interp_inst_replace_with_i8_const (td
, ins
, result
.l
);
6800 g_assert_not_reached ();
6801 if (td
->verbose_level
) {
6802 g_print ("Fold binop :\n\t");
6803 dump_interp_inst_newline (ins
);
6809 sp
[0].val
= result
;
6814 sp
->val
.type
= STACK_VALUE_NONE
;
6819 interp_local_equal (StackValue
*locals
, int local1
, int local2
)
6821 if (local1
== local2
)
6823 if (locals
[local1
].type
== STACK_VALUE_LOCAL
&& locals
[local1
].local
== local2
) {
6824 // local1 is a copy of local2
6827 if (locals
[local2
].type
== STACK_VALUE_LOCAL
&& locals
[local2
].local
== local1
) {
6828 // local2 is a copy of local1
6831 if (locals
[local1
].type
== STACK_VALUE_I4
&& locals
[local2
].type
== STACK_VALUE_I4
)
6832 return locals
[local1
].i
== locals
[local2
].i
;
6833 if (locals
[local1
].type
== STACK_VALUE_I8
&& locals
[local2
].type
== STACK_VALUE_I8
)
6834 return locals
[local1
].l
== locals
[local2
].l
;
6839 interp_cprop (TransformData
*td
)
6841 if (!td
->max_stack_height
)
6843 StackContentInfo
*stack
= (StackContentInfo
*) g_malloc (td
->max_stack_height
* sizeof (StackContentInfo
));
6844 StackContentInfo
*stack_end
= stack
+ td
->max_stack_height
;
6845 StackContentInfo
*sp
;
6846 StackValue
*locals
= (StackValue
*) g_malloc (td
->locals_size
* sizeof (StackValue
));
6847 int *local_ref_count
= (int*) g_malloc (td
->locals_size
* sizeof (int));
6853 last_il_offset
= -1;
6854 memset (local_ref_count
, 0, td
->locals_size
* sizeof (int));
6856 for (ins
= td
->first_ins
; ins
!= NULL
; ins
= ins
->next
) {
6858 int il_offset
= ins
->il_offset
;
6859 // Optimizations take place only inside a single basic block
6860 // If two instructions have the same il_offset, then the second one
6861 // cannot be part the start of a basic block.
6862 gboolean is_bb_start
= il_offset
!= -1 && td
->is_bb_start
[il_offset
] && il_offset
!= last_il_offset
;
6864 if (td
->stack_height
[il_offset
] >= 0) {
6865 sp
= stack
+ td
->stack_height
[il_offset
];
6866 g_assert (sp
<= stack_end
);
6867 memset (stack
, 0, (sp
- stack
) * sizeof (StackContentInfo
));
6869 memset (locals
, 0, td
->locals_size
* sizeof (StackValue
));
6871 // The instruction pops some values then pushes some other
6872 get_inst_stack_usage (td
, ins
, &pop
, &push
);
6873 if (td
->verbose_level
&& ins
->opcode
!= MINT_NOP
) {
6874 dump_interp_inst (ins
);
6875 g_print (", sp %d, (pop %d, push %d)\n", sp
- stack
, pop
, push
);
6877 if (MINT_IS_LDLOC (ins
->opcode
)) {
6879 int loaded_local
= ins
->data
[0];
6880 local_ref_count
[loaded_local
]++;
6881 InterpInst
*prev_ins
= interp_prev_ins (ins
);
6882 if (prev_ins
&& MINT_IS_STLOC (prev_ins
->opcode
) && !interp_is_bb_start (td
, prev_ins
, ins
) && interp_local_equal (locals
, prev_ins
->data
[0], loaded_local
)) {
6883 int mt
= prev_ins
->opcode
- MINT_STLOC_I1
;
6884 if (ins
->opcode
- MINT_LDLOC_I1
== mt
) {
6885 if (mt
== MINT_TYPE_I4
)
6886 replace_op
= MINT_STLOC_NP_I4
;
6887 else if (mt
== MINT_TYPE_I8
)
6888 replace_op
= MINT_STLOC_NP_I8
;
6889 else if (mt
== MINT_TYPE_R4
)
6890 replace_op
= MINT_STLOC_NP_R4
;
6891 else if (mt
== MINT_TYPE_R8
)
6892 replace_op
= MINT_STLOC_NP_R8
;
6893 else if (mt
== MINT_TYPE_O
|| mt
== MINT_TYPE_P
)
6894 replace_op
= MINT_STLOC_NP_O
;
6896 int stored_local
= prev_ins
->data
[0];
6898 if (sp
->val
.type
== STACK_VALUE_NONE
) {
6899 // We know what local is on the stack now. Track it
6900 sp
->val
.type
= STACK_VALUE_LOCAL
;
6901 sp
->val
.local
= stored_local
;
6904 // Clear the previous stloc instruction
6905 interp_clear_ins (td
, prev_ins
);
6906 ins
->opcode
= replace_op
;
6907 ins
->data
[0] = stored_local
;
6908 local_ref_count
[loaded_local
]--;
6909 if (td
->verbose_level
) {
6910 g_print ("Add stloc.np :\n\t");
6911 dump_interp_inst_newline (ins
);
6913 mono_interp_stats
.stloc_nps
++;
6914 mono_interp_stats
.killed_instructions
++;
6917 } else if (locals
[loaded_local
].type
== STACK_VALUE_LOCAL
) {
6918 g_assert (locals
[loaded_local
].type
== STACK_VALUE_LOCAL
);
6919 g_assert (!(td
->locals
[loaded_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
));
6920 // do copy propagation of the original source
6921 mono_interp_stats
.copy_propagations
++;
6922 local_ref_count
[loaded_local
]--;
6923 ins
->data
[0] = locals
[loaded_local
].local
;
6924 local_ref_count
[ins
->data
[0]]++;
6925 if (td
->verbose_level
) {
6926 g_print ("cprop loc %d -> loc %d :\n\t", loaded_local
, locals
[loaded_local
].local
);
6927 dump_interp_inst_newline (ins
);
6929 } else if (locals
[loaded_local
].type
== STACK_VALUE_I4
|| locals
[loaded_local
].type
== STACK_VALUE_I8
) {
6930 gboolean is_i4
= locals
[loaded_local
].type
== STACK_VALUE_I4
;
6931 g_assert (!(td
->locals
[loaded_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
));
6933 ins
= interp_get_ldc_i4_from_const (td
, ins
, locals
[loaded_local
].i
);
6935 ins
= interp_inst_replace_with_i8_const (td
, ins
, locals
[loaded_local
].l
);
6937 sp
->val
= locals
[loaded_local
];
6938 local_ref_count
[loaded_local
]--;
6939 mono_interp_stats
.copy_propagations
++;
6940 if (td
->verbose_level
) {
6941 g_print ("cprop loc %d -> ct :\n\t", loaded_local
);
6942 dump_interp_inst_newline (ins
);
6944 // FIXME this replace_op got ugly
6945 replace_op
= ins
->opcode
;
6948 // Save the ldloc on the stack if it wasn't optimized away
6949 // For simplicity we don't track locals that have their address taken
6950 // since it is hard to detect instructions that change the local value.
6951 if (td
->locals
[loaded_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
) {
6952 sp
->val
.type
= STACK_VALUE_NONE
;
6954 sp
->val
.type
= STACK_VALUE_LOCAL
;
6955 sp
->val
.local
= ins
->data
[0];
6960 } else if (MINT_IS_STLOC (ins
->opcode
)) {
6961 int dest_local
= ins
->data
[0];
6963 if (sp
->val
.type
== STACK_VALUE_LOCAL
) {
6964 int src_local
= sp
->val
.local
;
6965 if (td
->locals
[src_local
].mt
== td
->locals
[dest_local
].mt
) {
6966 // The locals have the same type. We can propagate the value
6967 int vtsize
= (ins
->opcode
== MINT_STLOC_VT
) ? ins
->data
[1] : 0;
6969 if (!(td
->locals
[dest_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
)) {
6970 // Track what exactly is stored into local
6971 locals
[dest_local
].type
= STACK_VALUE_LOCAL
;
6972 locals
[dest_local
].local
= src_local
;
6976 interp_clear_ins (td
, sp
->ins
);
6977 interp_clear_ins (td
, ins
);
6979 ins
= interp_insert_ins (td
, ins
, get_movloc_for_type (td
->locals
[src_local
].mt
));
6980 ins
->data
[0] = src_local
;
6981 ins
->data
[1] = dest_local
;
6983 ins
->data
[2] = vtsize
;
6984 // Clear ldloc / stloc pair and replace it with movloc superinstruction
6985 if (td
->verbose_level
) {
6986 g_print ("Add movloc (ldloc off %d) :\n\t", sp
->ins
->il_offset
);
6987 dump_interp_inst_newline (ins
);
6989 mono_interp_stats
.movlocs
++;
6990 mono_interp_stats
.killed_instructions
++;
6993 locals
[dest_local
].type
= STACK_VALUE_NONE
;
6995 } else if (sp
->val
.type
== STACK_VALUE_NONE
|| sp
->val
.type
== STACK_VALUE_ARG
) {
6996 locals
[dest_local
].type
= STACK_VALUE_NONE
;
6998 g_assert (sp
->val
.type
== STACK_VALUE_I4
|| sp
->val
.type
== STACK_VALUE_I8
);
6999 if (!(td
->locals
[dest_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
))
7000 locals
[dest_local
] = sp
->val
;
7002 clear_stack_content_info_for_local (stack
, sp
, dest_local
);
7003 clear_local_content_info_for_local (locals
, locals
+ td
->locals_size
, dest_local
);
7004 } else if (MINT_IS_LDC_I4 (ins
->opcode
) || ins
->opcode
== MINT_LDC_I8
) {
7006 gboolean is_i8
= ins
->opcode
== MINT_LDC_I8
;
7007 InterpInst
*prev_ins
= interp_prev_ins (ins
);
7010 val
.type
= STACK_VALUE_I8
;
7011 val
.l
= READ64 (&ins
->data
[0]);
7013 val
.type
= STACK_VALUE_I4
;
7014 val
.i
= interp_get_const_from_ldc_i4 (ins
);
7017 if (prev_ins
&& prev_ins
->opcode
== MINT_POP
&&
7018 ((is_i8
&& sp
->val
.type
== STACK_VALUE_I8
&& sp
->val
.l
== val
.l
) ||
7019 (!is_i8
&& sp
->val
.type
== STACK_VALUE_I4
&& sp
->val
.i
== val
.i
)) &&
7020 !interp_is_bb_start (td
, prev_ins
, ins
)) {
7021 // The previous instruction pops the stack of the value we are pushing
7022 // right now. We can kill both instructions
7023 if (td
->verbose_level
)
7024 g_print ("Kill redundant pop/ldc pair: pop (off %p), ldc (off %p)\n", prev_ins
->il_offset
, ins
->il_offset
);
7025 interp_clear_ins (td
, prev_ins
);
7026 interp_clear_ins (td
, ins
);
7027 mono_interp_stats
.killed_instructions
+= 2;
7033 } else if (MINT_IS_MOVLOC (ins
->opcode
)) {
7034 int src_local
= ins
->data
[0];
7035 int dest_local
= ins
->data
[1];
7036 local_ref_count
[src_local
]++;
7037 if (!(td
->locals
[dest_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
)) {
7038 if (locals
[src_local
].type
!= STACK_VALUE_NONE
) {
7039 locals
[dest_local
] = locals
[src_local
];
7041 locals
[dest_local
].type
= STACK_VALUE_LOCAL
;
7042 locals
[dest_local
].local
= src_local
;
7044 clear_stack_content_info_for_local (stack
, sp
, dest_local
);
7045 clear_local_content_info_for_local (locals
, locals
+ td
->locals_size
, dest_local
);
7047 } else if (MINT_IS_STLOC_NP (ins
->opcode
)) {
7048 int dest_local
= ins
->data
[0];
7049 // Prevent optimizing away the instruction that pushed the value on the stack
7051 // The local contains the value of the top of stack
7052 if (!(td
->locals
[dest_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
)) {
7053 locals
[dest_local
] = sp
[-1].val
;
7054 clear_stack_content_info_for_local (stack
, sp
, dest_local
);
7055 clear_local_content_info_for_local (locals
, locals
+ td
->locals_size
, dest_local
);
7057 } else if (ins
->opcode
== MINT_DUP
|| ins
->opcode
== MINT_DUP_VT
) {
7058 sp
[0].val
= sp
[-1].val
;
7060 // If top of stack is known, we could also replace dup with an explicit
7061 // propagated instruction, so we remove the top of stack dependency
7064 } else if (MINT_IS_LDARG (ins
->opcode
)) {
7066 sp
->val
.type
= STACK_VALUE_ARG
;
7067 sp
->val
.arg
= ins
->opcode
== MINT_LDARG_P0
? 0 : ins
->data
[0];
7069 } else if (MINT_IS_STARG (ins
->opcode
)) {
7070 int dest_arg
= ins
->data
[0];
7072 clear_stack_content_info_for_argument (stack
, sp
, dest_arg
);
7073 } else if (ins
->opcode
>= MINT_BOX
&& ins
->opcode
<= MINT_BOX_NULLABLE
) {
7074 int offset
= ins
->data
[1];
7075 // Clear the stack slot that is boxed
7076 memset (&sp
[-1 - offset
], 0, sizeof (StackContentInfo
));
7077 // Make sure that the instructions that pushed this stack slot can't be
7078 // optimized away. If we would optimize them away, we would also need to
7079 // update the offset in the box instruction, which we can't, for now.
7080 for (int i
= 1; i
<= offset
; i
++)
7082 } else if (ins
->opcode
== MINT_CKNULL_N
) {
7083 int offset
= ins
->data
[0];
7084 for (int i
= 1; i
<= offset
; i
++)
7086 } else if (ins
->opcode
== MINT_LD_DELEGATE_INVOKE_IMPL
) {
7087 int offset
= ins
->data
[0];
7088 for (int i
= 1; i
<= offset
; i
++)
7090 memset (sp
, 0, sizeof (StackContentInfo
));
7092 } else if (ins
->opcode
== MINT_POP
) {
7095 // The top of the stack is not used by any instructions. Kill both the
7096 // instruction that pushed it and the pop.
7097 interp_clear_ins (td
, sp
->ins
);
7098 interp_clear_ins (td
, ins
);
7099 mono_interp_stats
.killed_instructions
+= 2;
7100 // The value pop-ed by this instruction can still be accessed. If we also
7101 // kill the instruction pushing the value, then we need to empty the
7102 // value of the stack, so it is not considered for further optimizations.
7103 sp
->val
.type
= STACK_VALUE_NONE
;
7105 } else if (ins
->opcode
== MINT_NEWOBJ_FAST
&& ins
->data
[0] == INLINED_METHOD_FLAG
) {
7106 int param_count
= ins
->data
[1];
7107 // memmove the stack values while clearing ins, to prevent instruction removal
7108 for (int i
= 1; i
<= param_count
; i
++) {
7109 sp
[-i
+ 2] = sp
[-i
];
7110 sp
[-i
+ 2].ins
= NULL
;
7112 // clear stack information for the slots where the allocated object resides
7113 memset (&sp
[-param_count
], 0, 2 * sizeof (StackContentInfo
));
7115 } else if (ins
->opcode
== MINT_CASTCLASS
|| ins
->opcode
== MINT_CASTCLASS_COMMON
|| ins
->opcode
== MINT_CASTCLASS_INTERFACE
) {
7116 // Keep the value on the stack, but prevent optimizing away
7118 } else if (MINT_IS_CONDITIONAL_BRANCH (ins
->opcode
)) {
7120 g_assert (push
== 0);
7121 // We can't clear any instruction that pushes the stack, because the
7122 // branched code will expect a certain stack size.
7123 for (StackContentInfo
*sp_iter
= stack
; sp_iter
< sp
; sp_iter
++)
7124 sp_iter
->ins
= NULL
;
7125 } else if (MINT_IS_UNOP (ins
->opcode
)) {
7126 ins
= interp_fold_unop (td
, sp
, ins
);
7127 } else if (MINT_IS_BINOP (ins
->opcode
)) {
7128 ins
= interp_fold_binop (td
, sp
, ins
);
7130 } else if (ins
->opcode
>= MINT_STFLD_I1
&& ins
->opcode
<= MINT_STFLD_P
&& (mono_interp_opt
& INTERP_OPT_SUPER_INSTRUCTIONS
)) {
7131 StackContentInfo
*src
= &sp
[-2];
7133 if (src
->val
.type
== STACK_VALUE_LOCAL
) {
7134 int loc_index
= src
->val
.local
;
7135 int fld_offset
= ins
->data
[0];
7136 int mt
= ins
->opcode
- MINT_STFLD_I1
;
7137 ins
= interp_insert_ins (td
, ins
, MINT_STLOCFLD_I1
+ mt
);
7138 ins
->data
[0] = loc_index
;
7139 ins
->data
[1] = fld_offset
;
7140 local_ref_count
[loc_index
]++;
7141 interp_clear_ins (td
, ins
->prev
);
7142 interp_clear_ins (td
, src
->ins
);
7143 mono_interp_stats
.super_instructions
++;
7144 mono_interp_stats
.killed_instructions
++;
7145 } else if (src
->val
.type
== STACK_VALUE_ARG
) {
7146 int arg_index
= src
->val
.arg
;
7147 int fld_offset
= ins
->data
[0];
7148 int mt
= ins
->opcode
- MINT_STFLD_I1
;
7149 ins
= interp_insert_ins (td
, ins
, MINT_STARGFLD_I1
+ mt
);
7150 ins
->data
[0] = arg_index
;
7151 ins
->data
[1] = fld_offset
;
7152 interp_clear_ins (td
, ins
->prev
);
7153 interp_clear_ins (td
, src
->ins
);
7154 mono_interp_stats
.super_instructions
++;
7155 mono_interp_stats
.killed_instructions
++;
7159 } else if (MINT_IS_STLOCFLD (ins
->opcode
)) {
7160 local_ref_count
[ins
->data
[0]]++;
7163 if (pop
== MINT_POP_ALL
)
7166 g_assert (sp
>= stack
&& sp
<= stack_end
);
7167 g_assert ((sp
- push
) >= stack
&& (sp
- push
) <= stack_end
);
7168 memset (sp
- push
, 0, push
* sizeof (StackContentInfo
));
7169 // If this instruction only pushes a single value, make it a candidate for
7170 // removal, if its value is not used anywhere.
7171 if (push
== 1 && pop
== 0 && !MINT_IS_CALL (ins
->opcode
) && !MINT_IS_NEWOBJ (ins
->opcode
))
7174 last_il_offset
= ins
->il_offset
;
7177 if (interp_local_deadce (td
, local_ref_count
))
7182 g_free (local_ref_count
);
7186 mono_test_interp_cprop (TransformData
*td
)
7192 interp_super_instructions (TransformData
*td
)
7195 InterpInst
*prev1_ins
= NULL
;
7196 InterpInst
*prev2_ins
= NULL
;
7197 int last_il_offset
= -1;
7198 for (ins
= td
->first_ins
; ins
!= NULL
; ins
= ins
->next
) {
7199 int il_offset
= ins
->il_offset
;
7200 // If two instructions have the same il_offset, then the second one
7201 // cannot be the start of a basic block.
7202 gboolean is_bb_start
= il_offset
!= -1 && td
->is_bb_start
[il_offset
] && il_offset
!= last_il_offset
;
7203 last_il_offset
= il_offset
;
7204 if (ins
->opcode
== MINT_NOP
)
7207 // Prevent optimizations spanning multiple basic blocks
7211 if (ins
->opcode
>= MINT_LDFLD_I1
&& ins
->opcode
<= MINT_LDFLD_P
&& prev1_ins
) {
7212 if (prev1_ins
->opcode
== MINT_LDLOC_O
) {
7213 int loc_index
= prev1_ins
->data
[0];
7214 int fld_offset
= ins
->data
[0];
7215 int mt
= ins
->opcode
- MINT_LDFLD_I1
;
7216 ins
= interp_insert_ins (td
, ins
, MINT_LDLOCFLD_I1
+ mt
);
7217 ins
->data
[0] = loc_index
;
7218 ins
->data
[1] = fld_offset
;
7219 interp_clear_ins (td
, ins
->prev
);
7220 interp_clear_ins (td
, prev1_ins
);
7222 mono_interp_stats
.super_instructions
++;
7223 mono_interp_stats
.killed_instructions
++;
7224 } else if (prev1_ins
->opcode
== MINT_LDARG_O
|| prev1_ins
->opcode
== MINT_LDARG_P0
) {
7226 int fld_offset
= ins
->data
[0];
7227 int mt
= ins
->opcode
- MINT_LDFLD_I1
;
7228 if (prev1_ins
->opcode
== MINT_LDARG_O
)
7229 arg_index
= prev1_ins
->data
[0];
7230 ins
= interp_insert_ins (td
, ins
, MINT_LDARGFLD_I1
+ mt
);
7231 ins
->data
[0] = arg_index
;
7232 ins
->data
[1] = fld_offset
;
7233 interp_clear_ins (td
, ins
->prev
);
7234 interp_clear_ins (td
, prev1_ins
);
7236 mono_interp_stats
.super_instructions
++;
7237 mono_interp_stats
.killed_instructions
++;
7239 } else if (MINT_IS_STLOC (ins
->opcode
) && prev1_ins
&& prev2_ins
) {
7240 if (prev1_ins
->opcode
== MINT_ADD1_I4
|| prev1_ins
->opcode
== MINT_ADD1_I8
||
7241 prev1_ins
->opcode
== MINT_SUB1_I4
|| prev1_ins
->opcode
== MINT_SUB1_I8
) {
7242 if (MINT_IS_LDLOC (prev2_ins
->opcode
) && prev2_ins
->data
[0] == ins
->data
[0]) {
7243 if (prev1_ins
->opcode
== MINT_ADD1_I4
)
7244 ins
->opcode
= MINT_LOCADD1_I4
;
7245 else if (prev1_ins
->opcode
== MINT_ADD1_I8
)
7246 ins
->opcode
= MINT_LOCADD1_I8
;
7247 else if (prev1_ins
->opcode
== MINT_SUB1_I4
)
7248 ins
->opcode
= MINT_LOCSUB1_I4
;
7250 ins
->opcode
= MINT_LOCSUB1_I8
;
7251 // the local index is already set inside the replaced STLOC instruction
7252 interp_clear_ins (td
, prev1_ins
);
7253 interp_clear_ins (td
, prev2_ins
);
7255 mono_interp_stats
.super_instructions
++;
7256 mono_interp_stats
.killed_instructions
+= 2;
7260 prev2_ins
= prev1_ins
;
7266 interp_optimize_code (TransformData
*td
)
7268 if (mono_interp_opt
& INTERP_OPT_CPROP
)
7269 MONO_TIME_TRACK (mono_interp_stats
.cprop_time
, interp_cprop (td
));
7271 if (mono_interp_opt
& INTERP_OPT_SUPER_INSTRUCTIONS
)
7272 MONO_TIME_TRACK (mono_interp_stats
.super_instructions_time
, interp_super_instructions (td
));
7276 generate (MonoMethod
*method
, MonoMethodHeader
*header
, InterpMethod
*rtm
, MonoGenericContext
*generic_context
, MonoError
*error
)
7278 MonoDomain
*domain
= rtm
->domain
;
7280 TransformData transform_data
;
7282 static gboolean verbose_method_inited
;
7283 static char* verbose_method_name
;
7285 if (!verbose_method_inited
) {
7286 verbose_method_name
= g_getenv ("MONO_VERBOSE_METHOD");
7287 verbose_method_inited
= TRUE
;
7290 memset (&transform_data
, 0, sizeof(transform_data
));
7291 td
= &transform_data
;
7293 td
->method
= method
;
7295 td
->code_size
= header
->code_size
;
7296 td
->header
= header
;
7297 td
->max_code_size
= td
->code_size
;
7298 td
->in_offsets
= (int*)g_malloc0((header
->code_size
+ 1) * sizeof(int));
7299 td
->stack_height
= (int*)g_malloc(header
->code_size
* sizeof(int));
7300 td
->stack_state
= (StackInfo
**)g_malloc0(header
->code_size
* sizeof(StackInfo
*));
7301 td
->vt_stack_size
= (int*)g_malloc(header
->code_size
* sizeof(int));
7302 td
->clause_indexes
= (int*)g_malloc (header
->code_size
* sizeof (int));
7303 td
->is_bb_start
= (guint8
*)g_malloc0(header
->code_size
);
7304 td
->mempool
= mono_mempool_new ();
7305 td
->n_data_items
= 0;
7306 td
->max_data_items
= 0;
7307 td
->data_items
= NULL
;
7308 td
->data_hash
= g_hash_table_new (NULL
, NULL
);
7309 #ifdef ENABLE_EXPERIMENT_TIERED
7310 td
->patchsite_hash
= g_hash_table_new (NULL
, NULL
);
7312 td
->gen_sdb_seq_points
= mini_debug_options
.gen_sdb_seq_points
;
7313 td
->seq_points
= g_ptr_array_new ();
7314 td
->verbose_level
= mono_interp_traceopt
;
7315 rtm
->data_items
= td
->data_items
;
7317 interp_method_compute_offsets (td
, rtm
, mono_method_signature_internal (method
), header
);
7319 if (verbose_method_name
) {
7320 const char *name
= verbose_method_name
;
7322 if ((strchr (name
, '.') > name
) || strchr (name
, ':')) {
7323 MonoMethodDesc
*desc
;
7325 desc
= mono_method_desc_new (name
, TRUE
);
7326 if (mono_method_desc_full_match (desc
, method
)) {
7327 td
->verbose_level
= 4;
7329 mono_method_desc_free (desc
);
7331 if (strcmp (method
->name
, name
) == 0)
7332 td
->verbose_level
= 4;
7336 td
->stack
= (StackInfo
*)g_malloc0 ((header
->max_stack
+ 1) * sizeof (td
->stack
[0]));
7337 td
->stack_capacity
= header
->max_stack
+ 1;
7339 td
->max_stack_height
= 0;
7340 td
->line_numbers
= g_array_new (FALSE
, TRUE
, sizeof (MonoDebugLineNumberEntry
));
7341 td
->current_il_offset
= -1;
7343 generate_code (td
, method
, header
, generic_context
, error
);
7344 goto_if_nok (error
, exit
);
7346 interp_optimize_code (td
);
7348 generate_compacted_code (td
);
7350 if (td
->verbose_level
) {
7351 g_print ("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method
, TRUE
), rtm
, td
->max_vt_sp
);
7352 g_print ("Calculated stack size: %d, stated size: %d\n", td
->max_stack_height
, header
->max_stack
);
7353 dump_mint_code (td
->new_code
, td
->new_code_end
);
7356 /* Check if we use excessive stack space */
7357 if (td
->max_stack_height
> header
->max_stack
* 3 && header
->max_stack
> 16)
7358 g_warning ("Excessive stack space usage for method %s, %d/%d", method
->name
, td
->max_stack_height
, header
->max_stack
);
7360 int code_len_u8
, code_len_u16
;
7361 code_len_u8
= (guint8
*) td
->new_code_end
- (guint8
*) td
->new_code
;
7362 code_len_u16
= td
->new_code_end
- td
->new_code
;
7364 rtm
->clauses
= (MonoExceptionClause
*)mono_domain_alloc0 (domain
, header
->num_clauses
* sizeof (MonoExceptionClause
));
7365 memcpy (rtm
->clauses
, header
->clauses
, header
->num_clauses
* sizeof(MonoExceptionClause
));
7366 rtm
->code
= (gushort
*)td
->new_code
;
7367 rtm
->init_locals
= header
->init_locals
;
7368 rtm
->num_clauses
= header
->num_clauses
;
7369 for (i
= 0; i
< header
->num_clauses
; i
++) {
7370 MonoExceptionClause
*c
= rtm
->clauses
+ i
;
7371 int end_off
= c
->try_offset
+ c
->try_len
;
7372 c
->try_offset
= get_in_offset (td
, c
->try_offset
);
7373 c
->try_len
= find_in_offset (td
, end_off
) - c
->try_offset
;
7374 g_assert ((c
->try_offset
+ c
->try_len
) < code_len_u16
);
7375 end_off
= c
->handler_offset
+ c
->handler_len
;
7376 c
->handler_offset
= get_in_offset (td
, c
->handler_offset
);
7377 c
->handler_len
= find_in_offset (td
, end_off
) - c
->handler_offset
;
7378 g_assert (c
->handler_len
>= 0 && (c
->handler_offset
+ c
->handler_len
) <= code_len_u16
);
7379 if (c
->flags
& MONO_EXCEPTION_CLAUSE_FILTER
)
7380 c
->data
.filter_offset
= get_in_offset (td
, c
->data
.filter_offset
);
7382 rtm
->stack_size
= (sizeof (stackval
)) * (td
->max_stack_height
+ 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/
7383 rtm
->stack_size
= ALIGN_TO (rtm
->stack_size
, MINT_VT_ALIGNMENT
);
7384 rtm
->vt_stack_size
= td
->max_vt_sp
;
7385 rtm
->total_locals_size
= td
->total_locals_size
;
7386 rtm
->alloca_size
= ALIGN_TO (rtm
->total_locals_size
+ rtm
->vt_stack_size
+ rtm
->stack_size
, 8);
7387 rtm
->data_items
= (gpointer
*)mono_domain_alloc0 (domain
, td
->n_data_items
* sizeof (td
->data_items
[0]));
7388 memcpy (rtm
->data_items
, td
->data_items
, td
->n_data_items
* sizeof (td
->data_items
[0]));
7390 /* Save debug info */
7391 interp_save_debug_info (rtm
, header
, td
, td
->line_numbers
);
7393 /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
7395 jinfo_len
= mono_jit_info_size ((MonoJitInfoFlags
)0, header
->num_clauses
, 0);
7397 jinfo
= (MonoJitInfo
*)mono_domain_alloc0 (domain
, jinfo_len
);
7398 jinfo
->is_interp
= 1;
7400 mono_jit_info_init (jinfo
, method
, (guint8
*)rtm
->code
, code_len_u8
, (MonoJitInfoFlags
)0, header
->num_clauses
, 0);
7401 for (i
= 0; i
< jinfo
->num_clauses
; ++i
) {
7402 MonoJitExceptionInfo
*ei
= &jinfo
->clauses
[i
];
7403 MonoExceptionClause
*c
= rtm
->clauses
+ i
;
7405 ei
->flags
= c
->flags
;
7406 ei
->try_start
= (guint8
*)(rtm
->code
+ c
->try_offset
);
7407 ei
->try_end
= (guint8
*)(rtm
->code
+ c
->try_offset
+ c
->try_len
);
7408 ei
->handler_start
= (guint8
*)(rtm
->code
+ c
->handler_offset
);
7409 ei
->exvar_offset
= rtm
->exvar_offsets
[i
];
7410 if (ei
->flags
== MONO_EXCEPTION_CLAUSE_FILTER
) {
7411 ei
->data
.filter
= (guint8
*)(rtm
->code
+ c
->data
.filter_offset
);
7412 } else if (ei
->flags
== MONO_EXCEPTION_CLAUSE_FINALLY
) {
7413 ei
->data
.handler_end
= (guint8
*)(rtm
->code
+ c
->handler_offset
+ c
->handler_len
);
7415 ei
->data
.catch_class
= c
->data
.catch_class
;
7419 save_seq_points (td
, jinfo
);
7420 #ifdef ENABLE_EXPERIMENT_TIERED
7421 /* debugging aid, it makes `mono_pmip` work. */
7422 mono_jit_info_table_add (domain
, jinfo
);
7426 g_free (td
->in_offsets
);
7427 g_free (td
->stack_height
);
7428 for (i
= 0; i
< header
->code_size
; ++i
)
7429 g_free (td
->stack_state
[i
]);
7430 g_free (td
->stack_state
);
7431 g_free (td
->vt_stack_size
);
7432 g_free (td
->clause_indexes
);
7433 g_free (td
->data_items
);
7435 g_free (td
->is_bb_start
);
7436 g_free (td
->locals
);
7437 g_hash_table_destroy (td
->data_hash
);
7438 #ifdef ENABLE_EXPERIMENT_TIERED
7439 g_hash_table_destroy (td
->patchsite_hash
);
7441 g_ptr_array_free (td
->seq_points
, TRUE
);
7442 g_array_free (td
->line_numbers
, TRUE
);
7443 mono_mempool_destroy (td
->mempool
);
7447 mono_test_interp_generate_code (TransformData
*td
, MonoMethod
*method
, MonoMethodHeader
*header
, MonoGenericContext
*generic_context
, MonoError
*error
)
7449 return generate_code (td
, method
, header
, generic_context
, error
);
7452 static mono_mutex_t calc_section
;
7454 #ifdef ENABLE_EXPERIMENT_TIERED
7456 tiered_patcher (MiniTieredPatchPointContext
*ctx
, gpointer patchsite
)
7459 MonoMethod
*m
= ctx
->target_method
;
7461 if (!jit_call2_supported (m
, mono_method_signature_internal (m
)))
7464 /* TODO: Force compilation here. Currently the JIT will be invoked upon
7465 * first execution of `MINT_JIT_CALL2`. */
7466 InterpMethod
*rmethod
= mono_interp_get_imethod (ctx
->domain
, m
, error
);
7467 mono_error_assert_ok (error
);
7469 guint16
*ip
= ((guint16
*) patchsite
);
7470 *ip
++ = MINT_JIT_CALL2
;
7471 /* FIXME: this only works on 64bit */
7472 WRITE64 (ip
, &rmethod
);
7473 mono_memory_barrier ();
7481 mono_interp_transform_init (void)
7483 mono_os_mutex_init_recursive(&calc_section
);
7485 #ifdef ENABLE_EXPERIMENT_TIERED
7486 mini_tiered_register_callsite_patcher (tiered_patcher
, TIERED_PATCH_KIND_INTERP
);
7491 mono_interp_transform_method (InterpMethod
*imethod
, ThreadContext
*context
, MonoError
*error
)
7493 MonoMethod
*method
= imethod
->method
;
7494 MonoMethodHeader
*header
= NULL
;
7495 MonoMethodSignature
*signature
= mono_method_signature_internal (method
);
7496 MonoVTable
*method_class_vt
;
7497 MonoGenericContext
*generic_context
= NULL
;
7498 MonoDomain
*domain
= imethod
->domain
;
7499 InterpMethod tmp_imethod
;
7500 InterpMethod
*real_imethod
;
7504 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method
->klass
))) {
7505 mono_error_set_invalid_operation (error
, "%s", "Could not execute the method because the containing type is not fully instantiated.");
7509 // g_printerr ("TRANSFORM(0x%016lx): begin %s::%s\n", mono_thread_current (), method->klass->name, method->name);
7510 method_class_vt
= mono_class_vtable_checked (domain
, imethod
->method
->klass
, error
);
7511 return_if_nok (error
);
7513 if (!method_class_vt
->initialized
) {
7514 mono_runtime_class_init_full (method_class_vt
, error
);
7515 return_if_nok (error
);
7518 MONO_PROFILER_RAISE (jit_begin
, (method
));
7520 if (mono_method_signature_internal (method
)->is_inflated
)
7521 generic_context
= mono_method_get_context (method
);
7523 MonoGenericContainer
*generic_container
= mono_method_get_generic_container (method
);
7524 if (generic_container
)
7525 generic_context
= &generic_container
->context
;
7528 if (method
->iflags
& (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
| METHOD_IMPL_ATTRIBUTE_RUNTIME
)) {
7529 MonoMethod
*nm
= NULL
;
7530 if (imethod
->transformed
) {
7531 MONO_PROFILER_RAISE (jit_done
, (method
, imethod
->jinfo
));
7535 /* assumes all internal calls with an array this are built in... */
7536 if (method
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
&& (! mono_method_signature_internal (method
)->hasthis
|| m_class_get_rank (method
->klass
) == 0)) {
7537 nm
= mono_marshal_get_native_wrapper (method
, FALSE
, FALSE
);
7538 signature
= mono_method_signature_internal (nm
);
7540 const char *name
= method
->name
;
7541 if (m_class_get_parent (method
->klass
) == mono_defaults
.multicastdelegate_class
) {
7542 if (*name
== '.' && (strcmp (name
, ".ctor") == 0)) {
7543 MonoJitICallInfo
*mi
= &mono_get_jit_icall_info ()->ves_icall_mono_delegate_ctor_interp
;
7544 nm
= mono_marshal_get_icall_wrapper (mi
, TRUE
);
7545 } else if (*name
== 'I' && (strcmp (name
, "Invoke") == 0)) {
7547 * Usually handled during transformation of the caller, but
7548 * when the caller is handled by another execution engine
7549 * (for example fullAOT) we need to handle it here. That's
7550 * known to be wrong in cases where the reference to
7551 * `MonoDelegate` would be needed (FIXME).
7553 nm
= mono_marshal_get_delegate_invoke (method
, NULL
);
7554 } else if (*name
== 'B' && (strcmp (name
, "BeginInvoke") == 0)) {
7555 nm
= mono_marshal_get_delegate_begin_invoke (method
);
7556 } else if (*name
== 'E' && (strcmp (name
, "EndInvoke") == 0)) {
7557 nm
= mono_marshal_get_delegate_end_invoke (method
);
7561 g_assert_not_reached ();
7564 mono_os_mutex_lock (&calc_section
);
7565 imethod
->stack_size
= sizeof (stackval
); /* for tracing */
7566 imethod
->alloca_size
= imethod
->stack_size
;
7567 mono_memory_barrier ();
7568 imethod
->transformed
= TRUE
;
7569 mono_interp_stats
.methods_transformed
++;
7570 mono_os_mutex_unlock (&calc_section
);
7571 MONO_PROFILER_RAISE (jit_done
, (method
, NULL
));
7575 header
= interp_method_get_header (nm
, error
);
7576 return_if_nok (error
);
7580 header
= mono_method_get_header_checked (method
, error
);
7581 return_if_nok (error
);
7584 g_assert ((signature
->param_count
+ signature
->hasthis
) < 1000);
7585 // g_printerr ("TRANSFORM(0x%016lx): end %s::%s\n", mono_thread_current (), method->klass->name, method->name);
7587 /* Make modifications to a copy of imethod, copy them back inside the lock */
7588 real_imethod
= imethod
;
7589 memcpy (&tmp_imethod
, imethod
, sizeof (InterpMethod
));
7590 imethod
= &tmp_imethod
;
7592 MONO_TIME_TRACK (mono_interp_stats
.transform_time
, generate (method
, header
, imethod
, generic_context
, error
));
7594 mono_metadata_free_mh (header
);
7596 return_if_nok (error
);
7598 /* Copy changes back */
7599 imethod
= real_imethod
;
7600 mono_os_mutex_lock (&calc_section
);
7601 if (!imethod
->transformed
) {
7602 // Ignore the first two fields which are unchanged. next_jit_code_hash shouldn't
7603 // be modified because it is racy with internal hash table insert.
7604 const int start_offset
= 2 * sizeof (gpointer
);
7605 memcpy ((char*)imethod
+ start_offset
, (char*)&tmp_imethod
+ start_offset
, sizeof (InterpMethod
) - start_offset
);
7606 mono_memory_barrier ();
7607 imethod
->transformed
= TRUE
;
7608 mono_interp_stats
.methods_transformed
++;
7609 mono_atomic_fetch_add_i32 (&mono_jit_stats
.methods_with_interp
, 1);
7612 mono_os_mutex_unlock (&calc_section
);
7614 mono_domain_lock (domain
);
7615 if (mono_stats_method_desc
&& mono_method_desc_full_match (mono_stats_method_desc
, imethod
->method
)) {
7616 g_printf ("Printing runtime stats at method: %s\n", mono_method_get_full_name (imethod
->method
));
7617 mono_runtime_print_stats ();
7619 if (!g_hash_table_lookup (domain_jit_info (domain
)->seq_points
, imethod
->method
))
7620 g_hash_table_insert (domain_jit_info (domain
)->seq_points
, imethod
->method
, imethod
->jinfo
->seq_points
);
7621 mono_domain_unlock (domain
);
7623 // FIXME: Add a different callback ?
7624 MONO_PROFILER_RAISE (jit_done
, (method
, imethod
->jinfo
));