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/seq-points-data.h>
22 #include <mono/metadata/mono-basic-block.h>
23 #include <mono/metadata/abi-details.h>
24 #include <mono/metadata/reflection-internals.h>
25 #include <mono/utils/unlocked.h>
26 #include <mono/utils/mono-memory-model.h>
28 #include <mono/mini/mini.h>
29 #include <mono/mini/mini-runtime.h>
32 #include "interp-internals.h"
35 #define INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK 1
36 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY 2
37 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT 4
38 #define INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL 8
39 #define INTERP_INST_FLAG_RECORD_CALL_PATCH 16
41 #define INTERP_LOCAL_FLAG_INDIRECT 1
42 #define INTERP_LOCAL_FLAG_DEAD 2
44 MonoInterpStats mono_interp_stats
;
48 typedef struct InterpInst InterpInst
;
57 #define STACK_VALUE_NONE 0
58 #define STACK_VALUE_LOCAL 1
59 #define STACK_VALUE_I4 2
60 #define STACK_VALUE_I8 3
62 // StackValue contains data to construct an InterpInst that is equivalent with the contents
63 // of the stack slot / local.
65 // Indicates the type of the stored information. It can be a local or a constant
67 // Holds the local index or the actual constant value
77 // This indicates what is currently stored in this stack slot. This can be a constant
78 // or the copy of a local.
80 // The instruction that pushed this stack slot. If ins is null, we can't remove the usage
81 // of the stack slot, because we can't clear the instruction that set it.
87 InterpInst
*next
, *prev
;
88 // If this is -1, this instruction is not logically associated with an IL offset, it is
89 // part of the IL instruction associated with the previous interp instruction.
92 guint16 data
[MONO_ZERO_LEN_ARRAY
];
99 SeqPoint
*last_seq_point
;
101 // This will hold a list of last sequence points of incoming basic blocks
102 SeqPoint
**pred_seq_points
;
103 guint num_pred_seq_points
;
114 /* In the interpreter IR */
130 MonoMethod
*inlined_method
;
131 MonoMethodHeader
*header
;
133 const unsigned char *il_code
;
134 const unsigned char *ip
;
135 const unsigned char *in_start
;
136 InterpInst
*last_ins
, *first_ins
;
139 int current_il_offset
;
140 StackInfo
**stack_state
;
143 unsigned char *is_bb_start
;
144 unsigned short *new_code
;
145 unsigned short *new_code_end
;
146 unsigned int max_code_size
;
149 unsigned int max_stack_height
;
150 unsigned int stack_capacity
;
152 unsigned int max_vt_sp
;
153 unsigned int total_locals_size
;
155 unsigned int locals_size
;
156 unsigned int locals_capacity
;
160 GHashTable
*data_hash
;
161 #ifdef ENABLE_EXPERIMENT_TIERED
162 GHashTable
*patchsite_hash
;
165 gboolean gen_sdb_seq_points
;
166 GPtrArray
*seq_points
;
167 InterpBasicBlock
**offset_to_bb
;
168 InterpBasicBlock
*entry_bb
;
169 MonoMemPool
*mempool
;
172 gboolean verbose_level
;
173 GArray
*line_numbers
;
176 #define STACK_TYPE_I4 0
177 #define STACK_TYPE_I8 1
178 #define STACK_TYPE_R4 2
179 #define STACK_TYPE_R8 3
180 #define STACK_TYPE_O 4
181 #define STACK_TYPE_VT 5
182 #define STACK_TYPE_MP 6
183 #define STACK_TYPE_F 7
185 static const char *stack_type_string
[] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " };
187 #if SIZEOF_VOID_P == 8
188 #define STACK_TYPE_I STACK_TYPE_I8
190 #define STACK_TYPE_I STACK_TYPE_I4
193 static int stack_type
[] = {
194 STACK_TYPE_I4
, /*I1*/
195 STACK_TYPE_I4
, /*U1*/
196 STACK_TYPE_I4
, /*I2*/
197 STACK_TYPE_I4
, /*U2*/
198 STACK_TYPE_I4
, /*I4*/
199 STACK_TYPE_I8
, /*I8*/
200 STACK_TYPE_R4
, /*R4*/
201 STACK_TYPE_R8
, /*R8*/
207 #if SIZEOF_VOID_P == 8
208 #define MINT_NEG_P MINT_NEG_I8
209 #define MINT_NOT_P MINT_NOT_I8
211 #define MINT_NEG_FP MINT_NEG_R8
213 #define MINT_ADD_P MINT_ADD_I8
214 #define MINT_SUB_P MINT_SUB_I8
215 #define MINT_MUL_P MINT_MUL_I8
216 #define MINT_DIV_P MINT_DIV_I8
217 #define MINT_DIV_UN_P MINT_DIV_UN_I8
218 #define MINT_REM_P MINT_REM_I8
219 #define MINT_REM_UN_P MINT_REM_UN_I8
220 #define MINT_AND_P MINT_AND_I8
221 #define MINT_OR_P MINT_OR_I8
222 #define MINT_XOR_P MINT_XOR_I8
223 #define MINT_SHL_P MINT_SHL_I8
224 #define MINT_SHR_P MINT_SHR_I8
225 #define MINT_SHR_UN_P MINT_SHR_UN_I8
227 #define MINT_CEQ_P MINT_CEQ_I8
228 #define MINT_CNE_P MINT_CNE_I8
229 #define MINT_CLT_P MINT_CLT_I8
230 #define MINT_CLT_UN_P MINT_CLT_UN_I8
231 #define MINT_CGT_P MINT_CGT_I8
232 #define MINT_CGT_UN_P MINT_CGT_UN_I8
233 #define MINT_CLE_P MINT_CLE_I8
234 #define MINT_CLE_UN_P MINT_CLE_UN_I8
235 #define MINT_CGE_P MINT_CGE_I8
236 #define MINT_CGE_UN_P MINT_CGE_UN_I8
238 #define MINT_ADD_FP MINT_ADD_R8
239 #define MINT_SUB_FP MINT_SUB_R8
240 #define MINT_MUL_FP MINT_MUL_R8
241 #define MINT_DIV_FP MINT_DIV_R8
242 #define MINT_REM_FP MINT_REM_R8
244 #define MINT_CNE_FP MINT_CNE_R8
245 #define MINT_CEQ_FP MINT_CEQ_R8
246 #define MINT_CGT_FP MINT_CGT_R8
247 #define MINT_CGE_FP MINT_CGE_R8
248 #define MINT_CLT_FP MINT_CLT_R8
249 #define MINT_CLE_FP MINT_CLE_R8
251 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I8
254 #define MINT_NEG_P MINT_NEG_I4
255 #define MINT_NOT_P MINT_NOT_I4
257 #define MINT_NEG_FP MINT_NEG_R4
259 #define MINT_ADD_P MINT_ADD_I4
260 #define MINT_SUB_P MINT_SUB_I4
261 #define MINT_MUL_P MINT_MUL_I4
262 #define MINT_DIV_P MINT_DIV_I4
263 #define MINT_DIV_UN_P MINT_DIV_UN_I4
264 #define MINT_REM_P MINT_REM_I4
265 #define MINT_REM_UN_P MINT_REM_UN_I4
266 #define MINT_AND_P MINT_AND_I4
267 #define MINT_OR_P MINT_OR_I4
268 #define MINT_XOR_P MINT_XOR_I4
269 #define MINT_SHL_P MINT_SHL_I4
270 #define MINT_SHR_P MINT_SHR_I4
271 #define MINT_SHR_UN_P MINT_SHR_UN_I4
273 #define MINT_CEQ_P MINT_CEQ_I4
274 #define MINT_CNE_P MINT_CNE_I4
275 #define MINT_CLT_P MINT_CLT_I4
276 #define MINT_CLT_UN_P MINT_CLT_UN_I4
277 #define MINT_CGT_P MINT_CGT_I4
278 #define MINT_CGT_UN_P MINT_CGT_UN_I4
279 #define MINT_CLE_P MINT_CLE_I4
280 #define MINT_CLE_UN_P MINT_CLE_UN_I4
281 #define MINT_CGE_P MINT_CGE_I4
282 #define MINT_CGE_UN_P MINT_CGE_UN_I4
284 #define MINT_ADD_FP MINT_ADD_R4
285 #define MINT_SUB_FP MINT_SUB_R4
286 #define MINT_MUL_FP MINT_MUL_R4
287 #define MINT_DIV_FP MINT_DIV_R4
288 #define MINT_REM_FP MINT_REM_R4
290 #define MINT_CNE_FP MINT_CNE_R4
291 #define MINT_CEQ_FP MINT_CEQ_R4
292 #define MINT_CGT_FP MINT_CGT_R4
293 #define MINT_CGE_FP MINT_CGE_R4
294 #define MINT_CLT_FP MINT_CLT_R4
295 #define MINT_CLE_FP MINT_CLE_R4
297 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I4
301 const gchar
*op_name
;
305 // static const MagicIntrinsic int_binop[] = {
307 static const MagicIntrinsic int_unnop
[] = {
308 { "op_UnaryPlus", {MINT_NOP
, MINT_NOP
, MINT_NOP
}},
309 { "op_UnaryNegation", {MINT_NEG_P
, MINT_NEG_P
, MINT_NEG_FP
}},
310 { "op_OnesComplement", {MINT_NOT_P
, MINT_NOT_P
, MINT_NIY
}}
313 static const MagicIntrinsic int_binop
[] = {
314 { "op_Addition", {MINT_ADD_P
, MINT_ADD_P
, MINT_ADD_FP
}},
315 { "op_Subtraction", {MINT_SUB_P
, MINT_SUB_P
, MINT_SUB_FP
}},
316 { "op_Multiply", {MINT_MUL_P
, MINT_MUL_P
, MINT_MUL_FP
}},
317 { "op_Division", {MINT_DIV_P
, MINT_DIV_UN_P
, MINT_DIV_FP
}},
318 { "op_Modulus", {MINT_REM_P
, MINT_REM_UN_P
, MINT_REM_FP
}},
319 { "op_BitwiseAnd", {MINT_AND_P
, MINT_AND_P
, MINT_NIY
}},
320 { "op_BitwiseOr", {MINT_OR_P
, MINT_OR_P
, MINT_NIY
}},
321 { "op_ExclusiveOr", {MINT_XOR_P
, MINT_XOR_P
, MINT_NIY
}},
322 { "op_LeftShift", {MINT_SHL_P
, MINT_SHL_P
, MINT_NIY
}},
323 { "op_RightShift", {MINT_SHR_P
, MINT_SHR_UN_P
, MINT_NIY
}},
326 static const MagicIntrinsic int_cmpop
[] = {
327 { "op_Inequality", {MINT_CNE_P
, MINT_CNE_P
, MINT_CNE_FP
}},
328 { "op_Equality", {MINT_CEQ_P
, MINT_CEQ_P
, MINT_CEQ_FP
}},
329 { "op_GreaterThan", {MINT_CGT_P
, MINT_CGT_UN_P
, MINT_CGT_FP
}},
330 { "op_GreaterThanOrEqual", {MINT_CGE_P
, MINT_CGE_UN_P
, MINT_CGE_FP
}},
331 { "op_LessThan", {MINT_CLT_P
, MINT_CLT_UN_P
, MINT_CLT_FP
}},
332 { "op_LessThanOrEqual", {MINT_CLE_P
, MINT_CLE_UN_P
, MINT_CLE_FP
}}
335 static gboolean
generate_code (TransformData
*td
, MonoMethod
*method
, MonoMethodHeader
*header
, MonoGenericContext
*generic_context
, MonoError
*error
);
338 interp_new_ins (TransformData
*td
, guint16 opcode
, int len
)
340 InterpInst
*new_inst
;
341 // Size of data region of instruction is length of instruction minus 1 (the opcode slot)
342 new_inst
= mono_mempool_alloc0 (td
->mempool
, sizeof (InterpInst
) + sizeof (guint16
) * ((len
> 0) ? (len
- 1) : 0));
343 new_inst
->opcode
= opcode
;
344 new_inst
->il_offset
= td
->current_il_offset
;
348 // This version need to be used with switch opcode, which doesn't have constant length
350 interp_add_ins_explicit (TransformData
*td
, guint16 opcode
, int len
)
352 InterpInst
*new_inst
= interp_new_ins (td
, opcode
, len
);
353 new_inst
->prev
= td
->last_ins
;
355 td
->last_ins
->next
= new_inst
;
357 td
->first_ins
= new_inst
;
358 td
->last_ins
= new_inst
;
363 interp_add_ins (TransformData
*td
, guint16 opcode
)
365 return interp_add_ins_explicit (td
, opcode
, mono_interp_oplen
[opcode
]);
368 // This instruction will have the il_offset of the previous instruction
370 interp_insert_ins (TransformData
*td
, InterpInst
*prev_ins
, guint16 opcode
)
372 InterpInst
*new_inst
= interp_new_ins (td
, opcode
, mono_interp_oplen
[opcode
]);
374 new_inst
->il_offset
= prev_ins
->il_offset
;
376 new_inst
->prev
= prev_ins
;
377 new_inst
->next
= prev_ins
->next
;
378 prev_ins
->next
= new_inst
;
380 if (new_inst
->next
== NULL
)
381 td
->last_ins
= new_inst
;
383 new_inst
->next
->prev
= new_inst
;
389 interp_clear_ins (TransformData
*td
, InterpInst
*ins
)
391 // Clearing instead of removing from the list makes everything easier.
392 // We don't change structure of the instruction list, we don't need
393 // to worry about updating the il_offset, or whether this instruction
394 // was at the start of a basic block etc.
395 ins
->opcode
= MINT_NOP
;
399 interp_prev_ins (InterpInst
*ins
)
402 while (ins
&& ins
->opcode
== MINT_NOP
)
407 #define CHECK_STACK(td, n) \
409 int stack_size = (td)->sp - (td)->stack; \
410 if (stack_size < (n)) \
411 g_warning ("%s.%s: not enough values (%d < %d) on stack at %04x", \
412 m_class_get_name ((td)->method->klass), (td)->method->name, \
413 stack_size, n, (td)->ip - (td)->il_code); \
416 #define ENSURE_I4(td, sp_off) \
418 if ((td)->sp [-sp_off].type == STACK_TYPE_I8) \
419 interp_add_ins (td, sp_off == 1 ? MINT_CONV_I4_I8 : MINT_CONV_I4_I8_SP); \
422 #define CHECK_TYPELOAD(klass) \
424 if (!(klass) || mono_class_has_failure (klass)) { \
425 mono_error_set_for_class_failure (error, klass); \
430 #if NO_UNALIGNED_ACCESS
431 #define WRITE32(ip, v) \
433 * (ip) = * (guint16 *)(v); \
434 * ((ip) + 1) = * ((guint16 *)(v) + 1); \
438 #define WRITE32_INS(ins, index, v) \
440 (ins)->data [index] = * (guint16 *)(v); \
441 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
444 #define WRITE64(ins, v) \
446 *((ins) + 0) = * ((guint16 *)(v) + 0); \
447 *((ins) + 1) = * ((guint16 *)(v) + 1); \
448 *((ins) + 2) = * ((guint16 *)(v) + 2); \
449 *((ins) + 3) = * ((guint16 *)(v) + 3); \
452 #define WRITE64_INS(ins, index, v) \
454 (ins)->data [index] = * (guint16 *)(v); \
455 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
456 (ins)->data [index + 2] = * ((guint16 *)(v) + 2); \
457 (ins)->data [index + 3] = * ((guint16 *)(v) + 3); \
460 #define WRITE32(ip, v) \
462 * (guint32*)(ip) = * (guint32 *)(v); \
465 #define WRITE32_INS(ins, index, v) \
467 * (guint32 *)(&(ins)->data [index]) = * (guint32 *)(v); \
470 #define WRITE64(ip, v) \
472 * (guint64*)(ip) = * (guint64 *)(v); \
475 #define WRITE64_INS(ins, index, v) \
477 * (guint64 *)(&(ins)->data [index]) = * (guint64 *)(v); \
484 handle_branch (TransformData
*td
, int short_op
, int long_op
, int offset
)
486 int shorten_branch
= 0;
487 int target
= td
->ip
+ offset
- td
->il_code
;
488 if (target
< 0 || target
>= td
->code_size
)
489 g_assert_not_reached ();
490 /* Add exception checkpoint or safepoint for backward branches */
492 if (mono_threads_are_safepoints_enabled ())
493 interp_add_ins (td
, MINT_SAFEPOINT
);
495 interp_add_ins (td
, MINT_CHECKPOINT
);
497 if (offset
> 0 && td
->stack_height
[target
] < 0) {
498 td
->stack_height
[target
] = td
->sp
- td
->stack
;
499 if (td
->stack_height
[target
] > 0)
500 td
->stack_state
[target
] = (StackInfo
*)g_memdup (td
->stack
, td
->stack_height
[target
] * sizeof (td
->stack
[0]));
501 td
->vt_stack_size
[target
] = td
->vt_sp
;
504 if (td
->header
->code_size
<= 25000) /* FIX to be precise somehow? */
507 if (shorten_branch
) {
508 interp_add_ins (td
, short_op
);
509 td
->last_ins
->data
[0] = (guint16
) target
;
511 interp_add_ins (td
, long_op
);
512 WRITE32_INS (td
->last_ins
, 0, &target
);
517 one_arg_branch(TransformData
*td
, int mint_op
, int offset
)
519 int type
= td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
? STACK_TYPE_I
: td
->sp
[-1].type
;
520 int long_op
= mint_op
+ type
- STACK_TYPE_I4
;
521 int short_op
= long_op
+ MINT_BRFALSE_I4_S
- MINT_BRFALSE_I4
;
524 handle_branch (td
, short_op
, long_op
, offset
);
528 two_arg_branch(TransformData
*td
, int mint_op
, int offset
)
530 int type1
= td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
? STACK_TYPE_I
: td
->sp
[-1].type
;
531 int type2
= td
->sp
[-2].type
== STACK_TYPE_O
|| td
->sp
[-2].type
== STACK_TYPE_MP
? STACK_TYPE_I
: td
->sp
[-2].type
;
532 int long_op
= mint_op
+ type1
- STACK_TYPE_I4
;
533 int short_op
= long_op
+ MINT_BEQ_I4_S
- MINT_BEQ_I4
;
535 if (type1
== STACK_TYPE_I4
&& type2
== STACK_TYPE_I8
) {
536 // The il instruction starts with the actual branch, and not with the conversion opcodes
537 interp_insert_ins (td
, td
->last_ins
, MINT_CONV_I8_I4
);
538 } else if (type1
== STACK_TYPE_I8
&& type2
== STACK_TYPE_I4
) {
539 interp_insert_ins (td
, td
->last_ins
, MINT_CONV_I8_I4_SP
);
540 } else if (type1
== STACK_TYPE_R4
&& type2
== STACK_TYPE_R8
) {
541 interp_insert_ins (td
, td
->last_ins
, MINT_CONV_R8_R4
);
542 } else if (type1
== STACK_TYPE_R8
&& type2
== STACK_TYPE_R4
) {
543 interp_insert_ins (td
, td
->last_ins
, MINT_CONV_R8_R4_SP
);
544 } else if (type1
!= type2
) {
545 g_warning("%s.%s: branch type mismatch %d %d",
546 m_class_get_name (td
->method
->klass
), td
->method
->name
,
547 td
->sp
[-1].type
, td
->sp
[-2].type
);
550 handle_branch (td
, short_op
, long_op
, offset
);
554 unary_arith_op(TransformData
*td
, int mint_op
)
556 int op
= mint_op
+ td
->sp
[-1].type
- STACK_TYPE_I4
;
558 interp_add_ins (td
, op
);
562 binary_arith_op(TransformData
*td
, int mint_op
)
564 int type1
= td
->sp
[-2].type
;
565 int type2
= td
->sp
[-1].type
;
567 #if SIZEOF_VOID_P == 8
568 if ((type1
== STACK_TYPE_MP
|| type1
== STACK_TYPE_I8
) && type2
== STACK_TYPE_I4
) {
569 interp_add_ins (td
, MINT_CONV_I8_I4
);
570 type2
= STACK_TYPE_I8
;
572 if (type1
== STACK_TYPE_I4
&& (type2
== STACK_TYPE_MP
|| type2
== STACK_TYPE_I8
)) {
573 interp_add_ins (td
, MINT_CONV_I8_I4_SP
);
574 type1
= STACK_TYPE_I8
;
575 td
->sp
[-2].type
= STACK_TYPE_I8
;
578 if (type1
== STACK_TYPE_R8
&& type2
== STACK_TYPE_R4
) {
579 interp_add_ins (td
, MINT_CONV_R8_R4
);
580 type2
= STACK_TYPE_R8
;
582 if (type1
== STACK_TYPE_R4
&& type2
== STACK_TYPE_R8
) {
583 interp_add_ins (td
, MINT_CONV_R8_R4_SP
);
584 type1
= STACK_TYPE_R8
;
585 td
->sp
[-2].type
= STACK_TYPE_R8
;
587 if (type1
== STACK_TYPE_MP
)
588 type1
= STACK_TYPE_I
;
589 if (type2
== STACK_TYPE_MP
)
590 type2
= STACK_TYPE_I
;
591 if (type1
!= type2
) {
592 g_warning("%s.%s: %04x arith type mismatch %s %d %d",
593 m_class_get_name (td
->method
->klass
), td
->method
->name
,
594 td
->ip
- td
->il_code
, mono_interp_opname (mint_op
), type1
, type2
);
596 op
= mint_op
+ type1
- STACK_TYPE_I4
;
598 interp_add_ins (td
, op
);
603 shift_op(TransformData
*td
, int mint_op
)
605 int op
= mint_op
+ td
->sp
[-2].type
- STACK_TYPE_I4
;
607 if (td
->sp
[-1].type
!= STACK_TYPE_I4
) {
608 g_warning("%s.%s: shift type mismatch %d",
609 m_class_get_name (td
->method
->klass
), td
->method
->name
,
612 interp_add_ins (td
, op
);
617 can_store (int st_value
, int vt_value
)
619 if (st_value
== STACK_TYPE_O
|| st_value
== STACK_TYPE_MP
)
620 st_value
= STACK_TYPE_I
;
621 if (vt_value
== STACK_TYPE_O
|| vt_value
== STACK_TYPE_MP
)
622 vt_value
= STACK_TYPE_I
;
623 return st_value
== vt_value
;
626 #define SET_SIMPLE_TYPE(s, ty) \
633 #define SET_TYPE(s, ty, k) \
640 #define REALLOC_STACK(td, sppos) \
642 (td)->stack_capacity *= 2; \
643 (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
644 (td)->sp = (td)->stack + (sppos); \
647 #define PUSH_SIMPLE_TYPE(td, ty) \
651 sp_height = (td)->sp - (td)->stack; \
652 if (sp_height > (td)->max_stack_height) \
653 (td)->max_stack_height = sp_height; \
654 if (sp_height > (td)->stack_capacity) \
655 REALLOC_STACK(td, sp_height); \
656 SET_SIMPLE_TYPE((td)->sp - 1, ty); \
659 #define PUSH_TYPE(td, ty, k) \
663 sp_height = (td)->sp - (td)->stack; \
664 if (sp_height > (td)->max_stack_height) \
665 (td)->max_stack_height = sp_height; \
666 if (sp_height > (td)->stack_capacity) \
667 REALLOC_STACK(td, sp_height); \
668 SET_TYPE((td)->sp - 1, ty, k); \
672 move_stack (TransformData
*td
, int start
, int amount
)
674 int sp_height
= td
->sp
- td
->stack
;
675 int to_move
= sp_height
- start
;
680 if (sp_height
> td
->max_stack_height
)
681 td
->max_stack_height
= sp_height
;
682 if (sp_height
> td
->stack_capacity
)
683 REALLOC_STACK (td
, sp_height
);
685 g_assert (td
->sp
>= td
->stack
);
689 memmove (td
->stack
+ start
+ amount
, td
->stack
+ start
, to_move
* sizeof (StackInfo
));
692 #define PUSH_VT(td, size) \
694 (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
695 if ((td)->vt_sp > (td)->max_vt_sp) \
696 (td)->max_vt_sp = (td)->vt_sp; \
699 #define POP_VT(td, size) \
701 (td)->vt_sp -= ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
705 get_arg_type_exact (TransformData
*td
, int n
, int *mt
)
708 gboolean hasthis
= mono_method_signature_internal (td
->method
)->hasthis
;
710 if (hasthis
&& n
== 0)
711 type
= m_class_get_byval_arg (td
->method
->klass
);
713 type
= mono_method_signature_internal (td
->method
)->params
[n
- !!hasthis
];
716 *mt
= mint_type (type
);
722 load_arg(TransformData
*td
, int n
)
725 MonoClass
*klass
= NULL
;
727 gboolean hasthis
= mono_method_signature_internal (td
->method
)->hasthis
;
729 type
= get_arg_type_exact (td
, n
, &mt
);
731 if (mt
== MINT_TYPE_VT
) {
733 klass
= mono_class_from_mono_type_internal (type
);
734 if (mono_method_signature_internal (td
->method
)->pinvoke
)
735 size
= mono_class_native_size (klass
, NULL
);
737 size
= mono_class_value_size (klass
, NULL
);
739 if (hasthis
&& n
== 0) {
741 interp_add_ins (td
, MINT_LDARG_P0
);
745 interp_add_ins (td
, MINT_LDARG_VT
);
746 td
->last_ins
->data
[0] = n
;
747 WRITE32_INS (td
->last_ins
, 1, &size
);
750 if ((hasthis
|| mt
== MINT_TYPE_P
) && n
== 0) {
752 interp_add_ins (td
, MINT_LDARG_P0
);
754 interp_add_ins (td
, MINT_LDARG_I1
+ (mt
- MINT_TYPE_I1
));
755 td
->last_ins
->data
[0] = n
;
756 if (mt
== MINT_TYPE_O
)
757 klass
= mono_class_from_mono_type_internal (type
);
760 PUSH_TYPE(td
, stack_type
[mt
], klass
);
764 store_arg(TransformData
*td
, int n
)
770 type
= get_arg_type_exact (td
, n
, &mt
);
772 if (mt
== MINT_TYPE_VT
) {
774 MonoClass
*klass
= mono_class_from_mono_type_internal (type
);
775 if (mono_method_signature_internal (td
->method
)->pinvoke
)
776 size
= mono_class_native_size (klass
, NULL
);
778 size
= mono_class_value_size (klass
, NULL
);
779 interp_add_ins (td
, MINT_STARG_VT
);
780 td
->last_ins
->data
[0] = n
;
781 WRITE32_INS (td
->last_ins
, 1, &size
);
782 if (td
->sp
[-1].type
== STACK_TYPE_VT
)
785 interp_add_ins (td
, MINT_STARG_I1
+ (mt
- MINT_TYPE_I1
));
786 td
->last_ins
->data
[0] = n
;
792 load_local (TransformData
*td
, int local
)
794 MonoType
*type
= td
->locals
[local
].type
;
795 int mt
= td
->locals
[local
].mt
;
796 MonoClass
*klass
= NULL
;
797 if (mt
== MINT_TYPE_VT
) {
798 klass
= mono_class_from_mono_type_internal (type
);
799 gint32 size
= mono_class_value_size (klass
, NULL
);
801 interp_add_ins (td
, MINT_LDLOC_VT
);
802 td
->last_ins
->data
[0] = local
;
803 WRITE32_INS (td
->last_ins
, 1, &size
);
805 g_assert (mt
< MINT_TYPE_VT
);
806 interp_add_ins (td
, MINT_LDLOC_I1
+ (mt
- MINT_TYPE_I1
));
807 td
->last_ins
->data
[0] = local
;
808 if (mt
== MINT_TYPE_O
)
809 klass
= mono_class_from_mono_type_internal (type
);
811 PUSH_TYPE(td
, stack_type
[mt
], klass
);
815 store_local (TransformData
*td
, int local
)
817 MonoType
*type
= td
->locals
[local
].type
;
818 int mt
= td
->locals
[local
].mt
;
820 #if SIZEOF_VOID_P == 8
821 if (td
->sp
[-1].type
== STACK_TYPE_I4
&& stack_type
[mt
] == STACK_TYPE_I8
) {
822 interp_add_ins (td
, MINT_CONV_I8_I4
);
823 td
->sp
[-1].type
= STACK_TYPE_I8
;
826 if (!can_store(td
->sp
[-1].type
, stack_type
[mt
])) {
827 g_warning("%s.%s: Store local stack type mismatch %d %d",
828 m_class_get_name (td
->method
->klass
), td
->method
->name
,
829 stack_type
[mt
], td
->sp
[-1].type
);
831 if (mt
== MINT_TYPE_VT
) {
832 MonoClass
*klass
= mono_class_from_mono_type_internal (type
);
833 gint32 size
= mono_class_value_size (klass
, NULL
);
834 interp_add_ins (td
, MINT_STLOC_VT
);
835 td
->last_ins
->data
[0] = local
;
836 WRITE32_INS (td
->last_ins
, 1, &size
);
837 if (td
->sp
[-1].type
== STACK_TYPE_VT
)
840 g_assert (mt
< MINT_TYPE_VT
);
841 interp_add_ins (td
, MINT_STLOC_I1
+ (mt
- MINT_TYPE_I1
));
842 td
->last_ins
->data
[0] = local
;
847 #define SIMPLE_OP(td, op) \
849 interp_add_ins (td, op); \
854 get_data_item_index (TransformData
*td
, void *ptr
)
856 gpointer p
= g_hash_table_lookup (td
->data_hash
, ptr
);
859 return GPOINTER_TO_UINT (p
) - 1;
860 if (td
->max_data_items
== td
->n_data_items
) {
861 td
->max_data_items
= td
->n_data_items
== 0 ? 16 : 2 * td
->max_data_items
;
862 td
->data_items
= (gpointer
*)g_realloc (td
->data_items
, td
->max_data_items
* sizeof(td
->data_items
[0]));
864 index
= td
->n_data_items
;
865 td
->data_items
[index
] = ptr
;
867 g_hash_table_insert (td
->data_hash
, ptr
, GUINT_TO_POINTER (index
+ 1));
872 jit_call_supported (MonoMethod
*method
, MonoMethodSignature
*sig
)
876 if (sig
->param_count
> 6)
880 if (method
->flags
& METHOD_ATTRIBUTE_PINVOKE_IMPL
)
882 if (method
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
)
884 if (method
->is_inflated
)
886 if (method
->string_ctor
)
889 if (mono_aot_only
&& m_class_get_image (method
->klass
)->aot_module
&& !(method
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
)) {
891 gpointer addr
= mono_jit_compile_method_jit_only (method
, error
);
892 if (addr
&& is_ok (error
))
896 for (l
= mono_interp_jit_classes
; l
; l
= l
->next
) {
897 const char *class_name
= (const char*)l
->data
;
899 if (!strcmp (m_class_get_name (method
->klass
), class_name
))
907 #ifdef ENABLE_EXPERIMENT_TIERED
909 jit_call2_supported (MonoMethod
*method
, MonoMethodSignature
*sig
)
911 if (sig
->param_count
> 6)
915 if (method
->flags
& METHOD_ATTRIBUTE_PINVOKE_IMPL
)
917 if (method
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
)
919 if (method
->is_inflated
)
921 if (method
->string_ctor
)
928 static int mono_class_get_magic_index (MonoClass
*k
)
930 if (mono_class_is_magic_int (k
))
931 return !strcmp ("nint", m_class_get_name (k
)) ? 0 : 1;
933 if (mono_class_is_magic_float (k
))
940 interp_generate_mae_throw (TransformData
*td
, MonoMethod
*method
, MonoMethod
*target_method
)
942 MonoJitICallInfo
*info
= &mono_get_jit_icall_info ()->mono_throw_method_access
;
944 /* Inject code throwing MethodAccessException */
945 interp_add_ins (td
, MINT_MONO_LDPTR
);
946 td
->last_ins
->data
[0] = get_data_item_index (td
, method
);
947 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
949 interp_add_ins (td
, MINT_MONO_LDPTR
);
950 td
->last_ins
->data
[0] = get_data_item_index (td
, target_method
);
951 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
953 interp_add_ins (td
, MINT_ICALL_PP_V
);
954 td
->last_ins
->data
[0] = get_data_item_index (td
, (gpointer
)info
->func
);
960 interp_generate_bie_throw (TransformData
*td
)
962 MonoJitICallInfo
*info
= &mono_get_jit_icall_info ()->mono_throw_bad_image
;
964 interp_add_ins (td
, MINT_ICALL_V_V
);
965 td
->last_ins
->data
[0] = get_data_item_index (td
, (gpointer
)info
->func
);
969 * These are additional locals that can be allocated as we transform the code.
970 * They are allocated past the method locals so they are accessed in the same
971 * way, with an offset relative to the frame->locals.
974 create_interp_local (TransformData
*td
, MonoType
*type
)
976 if (td
->locals_size
== td
->locals_capacity
) {
977 td
->locals_capacity
*= 2;
978 if (td
->locals_capacity
== 0)
979 td
->locals_capacity
= 2;
980 td
->locals
= (InterpLocal
*) g_realloc (td
->locals
, td
->locals_capacity
* sizeof (InterpLocal
));
982 td
->locals
[td
->locals_size
].type
= type
;
983 td
->locals
[td
->locals_size
].mt
= mint_type (type
);
984 td
->locals
[td
->locals_size
].flags
= 0;
985 td
->locals
[td
->locals_size
].offset
= -1;
987 return td
->locals_size
- 1;
991 get_interp_local_offset (TransformData
*td
, int local
)
993 int align
, size
, offset
;
995 if (td
->locals
[local
].offset
!= -1)
996 return td
->locals
[local
].offset
;
998 offset
= td
->total_locals_size
;
999 size
= mono_type_size (td
->locals
[local
].type
, &align
);
1000 offset
= ALIGN_TO (offset
, align
);
1002 td
->locals
[local
].offset
= offset
;
1004 td
->total_locals_size
= offset
+ size
;
1005 g_assert (td
->total_locals_size
< G_MAXUINT16
);
1011 dump_mint_code (const guint16
*start
, const guint16
* end
)
1013 const guint16
*p
= start
;
1015 char *ins
= mono_interp_dis_mintop ((gint32
)(p
- start
), TRUE
, p
+ 1, *p
);
1016 g_print ("%s\n", ins
);
1018 p
= mono_interp_dis_mintop_len (p
);
1023 dump_interp_inst (InterpInst
*ins
)
1025 char *descr
= mono_interp_dis_mintop (ins
->il_offset
, FALSE
, &ins
->data
[0], ins
->opcode
);
1026 g_print ("%s", descr
);
1031 dump_interp_inst_newline (InterpInst
*ins
)
1033 dump_interp_inst (ins
);
1039 mono_interp_print_code (InterpMethod
*imethod
)
1041 MonoJitInfo
*jinfo
= imethod
->jinfo
;
1042 const guint8
*start
;
1047 char *name
= mono_method_full_name (imethod
->method
, 1);
1048 g_print ("Method : %s\n", name
);
1051 start
= (guint8
*) jinfo
->code_start
;
1052 dump_mint_code ((const guint16
*)start
, (const guint16
*)(start
+ jinfo
->code_size
));
1056 static MonoMethodHeader
*
1057 interp_method_get_header (MonoMethod
* method
, MonoError
*error
)
1059 /* An explanation: mono_method_get_header_internal returns an error if
1060 * called on a method with no body (e.g. an abstract method, or an
1061 * icall). We don't want that.
1063 if (mono_method_has_no_body (method
))
1066 return mono_method_get_header_internal (method
, error
);
1069 /* stores top of stack as local and pushes address of it on stack */
1071 emit_store_value_as_local (TransformData
*td
, MonoType
*src
)
1073 int size
= mini_magic_type_size (NULL
, src
);
1074 int local
= create_interp_local (td
, mini_native_type_replace_type (src
));
1076 store_local (td
, local
);
1078 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
1079 interp_add_ins (td
, MINT_LDLOC_VT
);
1080 td
->last_ins
->data
[0] = local
;
1081 WRITE32_INS (td
->last_ins
, 1, &size
);
1084 PUSH_TYPE (td
, STACK_TYPE_VT
, NULL
);
1087 // Returns whether we can optimize away the instructions starting at start.
1088 // If any instructions are part of a new basic block, we can't remove them.
1090 interp_is_bb_start (TransformData
*td
, InterpInst
*start
, InterpInst
*end
)
1092 InterpInst
*ins
= start
;
1093 while (ins
!= end
) {
1094 if (ins
->il_offset
!= -1) {
1095 if (td
->is_bb_start
[ins
->il_offset
])
1101 // Also check if end is bb start
1102 if (ins
!= NULL
&& ins
->il_offset
!= -1 && td
->is_bb_start
[ins
->il_offset
])
1108 interp_ins_is_ldc (InterpInst
*ins
)
1110 return ins
->opcode
>= MINT_LDC_I4_M1
&& ins
->opcode
<= MINT_LDC_I8
;
1114 interp_get_const_from_ldc_i4 (InterpInst
*ins
)
1116 switch (ins
->opcode
) {
1117 case MINT_LDC_I4_M1
: return -1;
1118 case MINT_LDC_I4_0
: return 0;
1119 case MINT_LDC_I4_1
: return 1;
1120 case MINT_LDC_I4_2
: return 2;
1121 case MINT_LDC_I4_3
: return 3;
1122 case MINT_LDC_I4_4
: return 4;
1123 case MINT_LDC_I4_5
: return 5;
1124 case MINT_LDC_I4_6
: return 6;
1125 case MINT_LDC_I4_7
: return 7;
1126 case MINT_LDC_I4_8
: return 8;
1127 case MINT_LDC_I4_S
: return (gint32
)(gint8
)ins
->data
[0];
1128 case MINT_LDC_I4
: return READ32 (&ins
->data
[0]);
1130 g_assert_not_reached ();
1134 /* If ins is not null, it will replace it with the ldc */
1136 interp_get_ldc_i4_from_const (TransformData
*td
, InterpInst
*ins
, gint32 ct
)
1140 case -1: opcode
= MINT_LDC_I4_M1
; break;
1141 case 0: opcode
= MINT_LDC_I4_0
; break;
1142 case 1: opcode
= MINT_LDC_I4_1
; break;
1143 case 2: opcode
= MINT_LDC_I4_2
; break;
1144 case 3: opcode
= MINT_LDC_I4_3
; break;
1145 case 4: opcode
= MINT_LDC_I4_4
; break;
1146 case 5: opcode
= MINT_LDC_I4_5
; break;
1147 case 6: opcode
= MINT_LDC_I4_6
; break;
1148 case 7: opcode
= MINT_LDC_I4_7
; break;
1149 case 8: opcode
= MINT_LDC_I4_8
; break;
1151 if (ct
>= -128 && ct
<= 127)
1152 opcode
= MINT_LDC_I4_S
;
1154 opcode
= MINT_LDC_I4
;
1158 int new_size
= mono_interp_oplen
[opcode
];
1161 ins
= interp_add_ins (td
, opcode
);
1163 int ins_size
= mono_interp_oplen
[ins
->opcode
];
1164 if (ins_size
< new_size
) {
1165 // We can't replace the passed instruction, discard it and emit a new one
1166 ins
= interp_insert_ins (td
, ins
, opcode
);
1167 interp_clear_ins (td
, ins
->prev
);
1169 ins
->opcode
= opcode
;
1173 ins
->data
[0] = (gint8
)ct
;
1174 else if (new_size
== 3)
1175 WRITE32_INS (ins
, 0, &ct
);
1181 interp_inst_replace_with_i8_const (TransformData
*td
, InterpInst
*ins
, gint64 ct
)
1183 int size
= mono_interp_oplen
[ins
->opcode
];
1186 ins
= interp_insert_ins (td
, ins
, MINT_LDC_I8
);
1187 interp_clear_ins (td
, ins
->prev
);
1189 ins
->opcode
= MINT_LDC_I8
;
1191 WRITE64_INS (ins
, 0, &ct
);
1197 interp_get_ldind_for_mt (int mt
)
1200 case MINT_TYPE_I1
: return MINT_LDIND_I1_CHECK
;
1201 case MINT_TYPE_U1
: return MINT_LDIND_U1_CHECK
;
1202 case MINT_TYPE_I2
: return MINT_LDIND_I2_CHECK
;
1203 case MINT_TYPE_U2
: return MINT_LDIND_U2_CHECK
;
1204 case MINT_TYPE_I4
: return MINT_LDIND_I4_CHECK
;
1205 case MINT_TYPE_I8
: return MINT_LDIND_I8_CHECK
;
1206 case MINT_TYPE_R4
: return MINT_LDIND_R4_CHECK
;
1207 case MINT_TYPE_R8
: return MINT_LDIND_R8_CHECK
;
1208 case MINT_TYPE_O
: return MINT_LDIND_REF
;
1210 g_assert_not_reached ();
1216 interp_emit_ldobj (TransformData
*td
, MonoClass
*klass
)
1218 int mt
= mint_type (m_class_get_byval_arg (klass
));
1221 if (mt
== MINT_TYPE_VT
) {
1222 interp_add_ins (td
, MINT_LDOBJ_VT
);
1223 size
= mono_class_value_size (klass
, NULL
);
1224 WRITE32_INS (td
->last_ins
, 0, &size
);
1227 int opcode
= interp_get_ldind_for_mt (mt
);
1228 interp_add_ins (td
, opcode
);
1231 SET_TYPE (td
->sp
- 1, stack_type
[mt
], klass
);
1235 interp_emit_stobj (TransformData
*td
, MonoClass
*klass
)
1237 int mt
= mint_type (m_class_get_byval_arg (klass
));
1239 if (mt
== MINT_TYPE_VT
) {
1241 interp_add_ins (td
, MINT_STOBJ_VT
);
1242 td
->last_ins
->data
[0] = get_data_item_index(td
, klass
);
1243 size
= mono_class_value_size (klass
, NULL
);
1250 opcode
= MINT_STIND_I1
;
1254 opcode
= MINT_STIND_I2
;
1257 opcode
= MINT_STIND_I4
;
1260 opcode
= MINT_STIND_I8
;
1263 opcode
= MINT_STIND_R4
;
1266 opcode
= MINT_STIND_R8
;
1269 opcode
= MINT_STIND_REF
;
1271 default: g_assert_not_reached (); break;
1273 interp_add_ins (td
, opcode
);
1279 interp_emit_ldelema (TransformData
*td
, MonoClass
*array_class
, MonoClass
*check_class
)
1281 MonoClass
*element_class
= m_class_get_element_class (array_class
);
1282 int rank
= m_class_get_rank (array_class
);
1283 int size
= mono_class_array_element_size (element_class
);
1285 // We only need type checks when writing to array of references
1286 if (!check_class
|| m_class_is_valuetype (element_class
)) {
1288 interp_add_ins (td
, MINT_LDELEMA1
);
1289 WRITE32_INS (td
->last_ins
, 0, &size
);
1291 interp_add_ins (td
, MINT_LDELEMA
);
1292 td
->last_ins
->data
[0] = rank
;
1293 WRITE32_INS (td
->last_ins
, 1, &size
);
1296 interp_add_ins (td
, MINT_LDELEMA_TC
);
1297 td
->last_ins
->data
[0] = rank
;
1298 td
->last_ins
->data
[1] = get_data_item_index (td
, check_class
);
1302 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_MP
);
1305 /* Return TRUE if call transformation is finished */
1307 interp_handle_intrinsics (TransformData
*td
, MonoMethod
*target_method
, MonoClass
*constrained_class
, MonoMethodSignature
*csignature
, gboolean readonly
, int *op
)
1309 const char *tm
= target_method
->name
;
1311 int type_index
= mono_class_get_magic_index (target_method
->klass
);
1312 gboolean in_corlib
= m_class_get_image (target_method
->klass
) == mono_defaults
.corlib
;
1313 const char *klass_name_space
= m_class_get_name_space (target_method
->klass
);
1314 const char *klass_name
= m_class_get_name (target_method
->klass
);
1316 if (target_method
->klass
== mono_defaults
.string_class
) {
1317 if (tm
[0] == 'g') {
1318 if (strcmp (tm
, "get_Chars") == 0)
1320 else if (strcmp (tm
, "get_Length") == 0)
1323 } else if (type_index
>= 0) {
1324 MonoClass
*magic_class
= target_method
->klass
;
1326 const int mt
= mint_type (m_class_get_byval_arg (magic_class
));
1327 if (!strcmp (".ctor", tm
)) {
1328 MonoType
*arg
= csignature
->params
[0];
1329 /* depending on SIZEOF_VOID_P and the type of the value passed to the .ctor we either have to CONV it, or do nothing */
1330 int arg_size
= mini_magic_type_size (NULL
, arg
);
1332 if (arg_size
> SIZEOF_VOID_P
) { // 8 -> 4
1333 switch (type_index
) {
1335 interp_add_ins (td
, MINT_CONV_I4_I8
);
1338 interp_add_ins (td
, MINT_CONV_R4_R8
);
1343 if (arg_size
< SIZEOF_VOID_P
) { // 4 -> 8
1344 switch (type_index
) {
1346 interp_add_ins (td
, MINT_CONV_I8_I4
);
1349 interp_add_ins (td
, MINT_CONV_I8_U4
);
1352 interp_add_ins (td
, MINT_CONV_R8_R4
);
1357 switch (type_index
) {
1359 #if SIZEOF_VOID_P == 4
1360 interp_add_ins (td
, MINT_STIND_I4
);
1362 interp_add_ins (td
, MINT_STIND_I8
);
1366 #if SIZEOF_VOID_P == 4
1367 interp_add_ins (td
, MINT_STIND_R4
);
1369 interp_add_ins (td
, MINT_STIND_R8
);
1377 } else if (!strcmp ("op_Implicit", tm
) || !strcmp ("op_Explicit", tm
)) {
1378 MonoType
*src
= csignature
->params
[0];
1379 MonoType
*dst
= csignature
->ret
;
1380 MonoClass
*src_klass
= mono_class_from_mono_type_internal (src
);
1381 int src_size
= mini_magic_type_size (NULL
, src
);
1382 int dst_size
= mini_magic_type_size (NULL
, dst
);
1384 gboolean store_value_as_local
= FALSE
;
1386 switch (type_index
) {
1388 if (!mini_magic_is_int_type (src
) || !mini_magic_is_int_type (dst
)) {
1389 if (mini_magic_is_int_type (src
))
1390 store_value_as_local
= TRUE
;
1391 else if (mono_class_is_magic_float (src_klass
))
1392 store_value_as_local
= TRUE
;
1398 if (!mini_magic_is_float_type (src
) || !mini_magic_is_float_type (dst
)) {
1399 if (mini_magic_is_float_type (src
))
1400 store_value_as_local
= TRUE
;
1401 else if (mono_class_is_magic_int (src_klass
))
1402 store_value_as_local
= TRUE
;
1409 if (store_value_as_local
) {
1410 emit_store_value_as_local (td
, src
);
1412 /* emit call to managed conversion method */
1416 if (src_size
> dst_size
) { // 8 -> 4
1417 switch (type_index
) {
1419 interp_add_ins (td
, MINT_CONV_I4_I8
);
1422 interp_add_ins (td
, MINT_CONV_R4_R8
);
1427 if (src_size
< dst_size
) { // 4 -> 8
1428 switch (type_index
) {
1430 interp_add_ins (td
, MINT_CONV_I8_I4
);
1433 interp_add_ins (td
, MINT_CONV_I8_U4
);
1436 interp_add_ins (td
, MINT_CONV_R8_R4
);
1441 SET_TYPE (td
->sp
- 1, stack_type
[mint_type (dst
)], mono_class_from_mono_type_internal (dst
));
1444 } else if (!strcmp ("op_Increment", tm
)) {
1445 g_assert (type_index
!= 2); // no nfloat
1446 #if SIZEOF_VOID_P == 8
1447 interp_add_ins (td
, MINT_ADD1_I8
);
1449 interp_add_ins (td
, MINT_ADD1_I4
);
1451 SET_TYPE (td
->sp
- 1, stack_type
[mt
], magic_class
);
1454 } else if (!strcmp ("op_Decrement", tm
)) {
1455 g_assert (type_index
!= 2); // no nfloat
1456 #if SIZEOF_VOID_P == 8
1457 interp_add_ins (td
, MINT_SUB1_I8
);
1459 interp_add_ins (td
, MINT_SUB1_I4
);
1461 SET_TYPE (td
->sp
- 1, stack_type
[mt
], magic_class
);
1464 } else if (!strcmp ("CompareTo", tm
) || !strcmp ("Equals", tm
)) {
1465 MonoType
*arg
= csignature
->params
[0];
1467 /* on 'System.n*::{CompareTo,Equals} (System.n*)' variant we need to push managed
1468 * pointer instead of value */
1469 if (arg
->type
== MONO_TYPE_VALUETYPE
)
1470 emit_store_value_as_local (td
, arg
);
1472 /* emit call to managed conversion method */
1474 } else if (!strcmp (".cctor", tm
)) {
1477 } else if (!strcmp ("Parse", tm
)) {
1480 } else if (!strcmp ("ToString", tm
)) {
1483 } else if (!strcmp ("GetHashCode", tm
)) {
1486 } else if (!strcmp ("IsNaN", tm
) || !strcmp ("IsInfinity", tm
) || !strcmp ("IsNegativeInfinity", tm
) || !strcmp ("IsPositiveInfinity", tm
)) {
1487 g_assert (type_index
== 2); // nfloat only
1492 for (i
= 0; i
< sizeof (int_unnop
) / sizeof (MagicIntrinsic
); ++i
) {
1493 if (!strcmp (int_unnop
[i
].op_name
, tm
)) {
1494 interp_add_ins (td
, int_unnop
[i
].insn
[type_index
]);
1495 SET_TYPE (td
->sp
- 1, stack_type
[mt
], magic_class
);
1501 for (i
= 0; i
< sizeof (int_binop
) / sizeof (MagicIntrinsic
); ++i
) {
1502 if (!strcmp (int_binop
[i
].op_name
, tm
)) {
1503 interp_add_ins (td
, int_binop
[i
].insn
[type_index
]);
1505 SET_TYPE (td
->sp
- 1, stack_type
[mt
], magic_class
);
1511 for (i
= 0; i
< sizeof (int_cmpop
) / sizeof (MagicIntrinsic
); ++i
) {
1512 if (!strcmp (int_cmpop
[i
].op_name
, tm
)) {
1513 MonoClass
*k
= mono_defaults
.boolean_class
;
1514 interp_add_ins (td
, int_cmpop
[i
].insn
[type_index
]);
1516 SET_TYPE (td
->sp
- 1, stack_type
[mint_type (m_class_get_byval_arg (k
))], k
);
1522 g_error ("TODO: interp_transform_call %s:%s", m_class_get_name (target_method
->klass
), tm
);
1523 } else if (mono_class_is_subclass_of_internal (target_method
->klass
, mono_defaults
.array_class
, FALSE
)) {
1524 if (!strcmp (tm
, "get_Rank")) {
1525 *op
= MINT_ARRAY_RANK
;
1526 } else if (!strcmp (tm
, "get_Length")) {
1528 } else if (!strcmp (tm
, "GetElementSize")) {
1529 *op
= MINT_ARRAY_ELEMENT_SIZE
;
1530 } else if (!strcmp (tm
, "Address")) {
1531 MonoClass
*check_class
= readonly
? NULL
: m_class_get_element_class (target_method
->klass
);
1532 interp_emit_ldelema (td
, target_method
->klass
, check_class
);
1535 #ifndef ENABLE_NETCORE
1536 } else if (!strcmp (tm
, "UnsafeMov") || !strcmp (tm
, "UnsafeLoad")) {
1539 } else if (!strcmp (tm
, "Get")) {
1540 interp_emit_ldelema (td
, target_method
->klass
, NULL
);
1541 interp_emit_ldobj (td
, m_class_get_element_class (target_method
->klass
));
1544 } else if (!strcmp (tm
, "Set")) {
1545 MonoClass
*element_class
= m_class_get_element_class (target_method
->klass
);
1546 MonoType
*local_type
= m_class_get_byval_arg (element_class
);
1547 MonoClass
*value_class
= td
->sp
[-1].klass
;
1548 // If value_class is NULL it means the top of stack is a simple type (valuetype)
1549 // which doesn't require type checks, or that we have no type information because
1550 // the code is unsafe (like in some wrappers). In that case we assume the type
1551 // of the array and don't do any checks.
1553 int local
= create_interp_local (td
, local_type
);
1555 store_local (td
, local
);
1556 interp_emit_ldelema (td
, target_method
->klass
, value_class
);
1557 load_local (td
, local
);
1558 interp_emit_stobj (td
, element_class
);
1561 } else if (!strcmp (tm
, "UnsafeStore")) {
1562 g_error ("TODO ArrayClass::UnsafeStore");
1564 } else if (in_corlib
&&
1565 !strcmp (klass_name_space
, "System.Diagnostics") &&
1566 !strcmp (klass_name
, "Debugger")) {
1567 if (!strcmp (tm
, "Break") && csignature
->param_count
== 0) {
1568 if (mini_should_insert_breakpoint (td
->method
))
1571 } else if (in_corlib
&& !strcmp (klass_name_space
, "System") && !strcmp (klass_name
, "ByReference`1")) {
1572 g_assert (!strcmp (tm
, "get_Value"));
1573 *op
= MINT_INTRINS_BYREFERENCE_GET_VALUE
;
1574 } else if (in_corlib
&& !strcmp (klass_name_space
, "System") && !strcmp (klass_name
, "Math") && csignature
->param_count
== 1 && csignature
->params
[0]->type
== MONO_TYPE_R8
) {
1575 if (tm
[0] == 'A') {
1576 if (strcmp (tm
, "Abs") == 0 && csignature
->params
[0]->type
== MONO_TYPE_R8
) {
1578 } else if (strcmp (tm
, "Asin") == 0){
1580 } else if (strcmp (tm
, "Asinh") == 0){
1582 } else if (strcmp (tm
, "Acos") == 0){
1584 } else if (strcmp (tm
, "Acosh") == 0){
1586 } else if (strcmp (tm
, "Atan") == 0){
1588 } else if (strcmp (tm
, "Atanh") == 0){
1591 } else if (tm
[0] == 'C') {
1592 if (strcmp (tm
, "Cos") == 0) {
1594 } else if (strcmp (tm
, "Cbrt") == 0){
1596 } else if (strcmp (tm
, "Cosh") == 0){
1599 } else if (tm
[0] == 'S') {
1600 if (strcmp (tm
, "Sin") == 0) {
1602 } else if (strcmp (tm
, "Sqrt") == 0) {
1604 } else if (strcmp (tm
, "Sinh") == 0){
1607 } else if (tm
[0] == 'T') {
1608 if (strcmp (tm
, "Tan") == 0) {
1610 } else if (strcmp (tm
, "Tanh") == 0){
1614 } else if (in_corlib
&& !strcmp (klass_name_space
, "System") && (!strcmp (klass_name
, "Span`1") || !strcmp (klass_name
, "ReadOnlySpan`1"))) {
1615 if (!strcmp (tm
, "get_Item")) {
1616 MonoGenericClass
*gclass
= mono_class_get_generic_class (target_method
->klass
);
1617 MonoClass
*param_class
= mono_class_from_mono_type_internal (gclass
->context
.class_inst
->type_argv
[0]);
1619 if (!mini_is_gsharedvt_variable_klass (param_class
)) {
1620 MonoClassField
*length_field
= mono_class_get_field_from_name_full (target_method
->klass
, "_length", NULL
);
1621 g_assert (length_field
);
1622 int offset_length
= length_field
->offset
- sizeof (MonoObject
);
1624 MonoClassField
*ptr_field
= mono_class_get_field_from_name_full (target_method
->klass
, "_pointer", NULL
);
1625 g_assert (ptr_field
);
1626 int offset_pointer
= ptr_field
->offset
- sizeof (MonoObject
);
1628 int size
= mono_class_array_element_size (param_class
);
1629 interp_add_ins (td
, MINT_GETITEM_SPAN
);
1630 td
->last_ins
->data
[0] = size
;
1631 td
->last_ins
->data
[1] = offset_length
;
1632 td
->last_ins
->data
[2] = offset_pointer
;
1634 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_MP
);
1639 } else if (!strcmp (tm
, "get_Length")) {
1640 MonoClassField
*length_field
= mono_class_get_field_from_name_full (target_method
->klass
, "_length", NULL
);
1641 g_assert (length_field
);
1642 int offset_length
= length_field
->offset
- sizeof (MonoObject
);
1643 interp_add_ins (td
, MINT_LDLEN_SPAN
);
1644 td
->last_ins
->data
[0] = offset_length
;
1645 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
1649 } else if (in_corlib
&& !strcmp (klass_name_space
, "Internal.Runtime.CompilerServices") && !strcmp (klass_name
, "Unsafe")) {
1650 #ifdef ENABLE_NETCORE
1651 if (!strcmp (tm
, "AddByteOffset"))
1652 *op
= MINT_INTRINS_UNSAFE_ADD_BYTE_OFFSET
;
1653 else if (!strcmp (tm
, "ByteOffset"))
1654 *op
= MINT_INTRINS_UNSAFE_BYTE_OFFSET
;
1655 else if (!strcmp (tm
, "As") || !strcmp (tm
, "AsRef"))
1657 else if (!strcmp (tm
, "AsPointer")) {
1659 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_MP
);
1662 } else if (!strcmp (tm
, "IsAddressLessThan")) {
1663 MonoGenericContext
*ctx
= mono_method_get_context (target_method
);
1665 g_assert (ctx
->method_inst
);
1666 g_assert (ctx
->method_inst
->type_argc
== 1);
1668 MonoClass
*k
= mono_defaults
.boolean_class
;
1669 interp_add_ins (td
, MINT_CLT_UN_P
);
1671 SET_TYPE (td
->sp
- 1, stack_type
[mint_type (m_class_get_byval_arg (k
))], k
);
1674 } else if (!strcmp (tm
, "SizeOf")) {
1675 MonoGenericContext
*ctx
= mono_method_get_context (target_method
);
1677 g_assert (ctx
->method_inst
);
1678 g_assert (ctx
->method_inst
->type_argc
== 1);
1679 MonoType
*t
= ctx
->method_inst
->type_argv
[0];
1681 int esize
= mono_type_size (t
, &align
);
1682 interp_add_ins (td
, MINT_LDC_I4
);
1683 WRITE32_INS (td
->last_ins
, 0, &esize
);
1684 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I4
);
1687 } else if (!strcmp (tm
, "AreSame")) {
1689 } else if (!strcmp (tm
, "SkipInit")) {
1693 } else if (in_corlib
&& !strcmp (klass_name_space
, "System.Runtime.CompilerServices") && !strcmp (klass_name
, "RuntimeHelpers")) {
1694 #ifdef ENABLE_NETCORE
1695 if (!strcmp (tm
, "IsBitwiseEquatable")) {
1696 g_assert (csignature
->param_count
== 0);
1697 MonoGenericContext
*ctx
= mono_method_get_context (target_method
);
1699 g_assert (ctx
->method_inst
);
1700 g_assert (ctx
->method_inst
->type_argc
== 1);
1701 MonoType
*t
= mini_get_underlying_type (ctx
->method_inst
->type_argv
[0]);
1703 if (MONO_TYPE_IS_PRIMITIVE (t
) && t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
)
1704 *op
= MINT_LDC_I4_1
;
1706 *op
= MINT_LDC_I4_0
;
1707 } else if (!strcmp (tm
, "ObjectHasComponentSize")) {
1708 *op
= MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE
;
1711 } else if (in_corlib
&& !strcmp (klass_name_space
, "System") && !strcmp (klass_name
, "RuntimeMethodHandle") && !strcmp (tm
, "GetFunctionPointer") && csignature
->param_count
== 1) {
1712 // We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter
1713 *op
= MINT_LDFTN_DYNAMIC
;
1714 } else if (in_corlib
&& target_method
->klass
== mono_defaults
.object_class
) {
1715 if (!strcmp (tm
, "InternalGetHashCode"))
1716 *op
= MINT_INTRINS_GET_HASHCODE
;
1717 #ifdef DISABLE_REMOTING
1718 else if (!strcmp (tm
, "GetType"))
1719 *op
= MINT_INTRINS_GET_TYPE
;
1721 #ifdef ENABLE_NETCORE
1722 else if (!strcmp (tm
, "GetRawData")) {
1723 #if SIZEOF_VOID_P == 8
1724 interp_add_ins (td
, MINT_LDC_I8_S
);
1726 interp_add_ins (td
, MINT_LDC_I4_S
);
1728 td
->last_ins
->data
[0] = (gint16
) MONO_ABI_SIZEOF (MonoObject
);
1730 interp_add_ins (td
, MINT_ADD_P
);
1731 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_MP
);
1737 } else if (in_corlib
&& target_method
->klass
== mono_defaults
.enum_class
&& !strcmp (tm
, "HasFlag")) {
1738 gboolean intrinsify
= FALSE
;
1739 MonoClass
*base_klass
= NULL
;
1740 if (td
->last_ins
&& td
->last_ins
->opcode
== MINT_BOX
&&
1741 td
->last_ins
->prev
&& interp_ins_is_ldc (td
->last_ins
->prev
) &&
1742 td
->last_ins
->prev
->prev
&& td
->last_ins
->prev
->prev
->opcode
== MINT_BOX
&&
1743 td
->sp
[-2].klass
== td
->sp
[-1].klass
&&
1744 !interp_is_bb_start (td
, td
->last_ins
->prev
->prev
, NULL
) &&
1745 !td
->is_bb_start
[td
->in_start
- td
->il_code
]) {
1746 // csc pattern : box, ldc, box, call HasFlag
1747 g_assert (m_class_is_enumtype (td
->sp
[-2].klass
));
1748 MonoType
*base_type
= mono_type_get_underlying_type (m_class_get_byval_arg (td
->sp
[-2].klass
));
1749 base_klass
= mono_class_from_mono_type_internal (base_type
);
1751 // Remove the boxing of valuetypes
1752 interp_clear_ins (td
, td
->last_ins
->prev
->prev
);
1753 interp_clear_ins (td
, td
->last_ins
);
1756 } else if (td
->last_ins
&& td
->last_ins
->opcode
== MINT_BOX
&&
1757 td
->last_ins
->prev
&& interp_ins_is_ldc (td
->last_ins
->prev
) &&
1758 constrained_class
&& td
->sp
[-1].klass
== constrained_class
&&
1759 !interp_is_bb_start (td
, td
->last_ins
->prev
, NULL
) &&
1760 !td
->is_bb_start
[td
->in_start
- td
->il_code
]) {
1761 // mcs pattern : ldc, box, constrained Enum, call HasFlag
1762 g_assert (m_class_is_enumtype (constrained_class
));
1763 MonoType
*base_type
= mono_type_get_underlying_type (m_class_get_byval_arg (constrained_class
));
1764 base_klass
= mono_class_from_mono_type_internal (base_type
);
1765 int mt
= mint_type (m_class_get_byval_arg (base_klass
));
1767 // Remove boxing and load the value of this
1768 interp_clear_ins (td
, td
->last_ins
);
1769 interp_insert_ins (td
, td
->last_ins
->prev
->prev
, interp_get_ldind_for_mt (mt
));
1774 interp_add_ins (td
, MINT_INTRINS_ENUM_HASFLAG
);
1775 td
->last_ins
->data
[0] = get_data_item_index (td
, base_klass
);
1777 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I4
);
1781 } else if (in_corlib
&& !strcmp (klass_name_space
, "System.Threading") && !strcmp (klass_name
, "Interlocked")) {
1782 if (!strcmp (tm
, "MemoryBarrier") && csignature
->param_count
== 0)
1783 *op
= MINT_MONO_MEMORY_BARRIER
;
1784 } else if (in_corlib
&& !strcmp (klass_name_space
, "System.Threading") && !strcmp (klass_name
, "Thread")) {
1785 if (!strcmp (tm
, "MemoryBarrier") && csignature
->param_count
== 0)
1786 *op
= MINT_MONO_MEMORY_BARRIER
;
1787 } else if (in_corlib
&&
1788 !strcmp (klass_name_space
, "System.Runtime.CompilerServices") &&
1789 !strcmp (klass_name
, "JitHelpers") &&
1790 (!strcmp (tm
, "EnumEquals") || !strcmp (tm
, "EnumCompareTo"))) {
1791 MonoGenericContext
*ctx
= mono_method_get_context (target_method
);
1793 g_assert (ctx
->method_inst
);
1794 g_assert (ctx
->method_inst
->type_argc
== 1);
1795 g_assert (csignature
->param_count
== 2);
1797 MonoType
*t
= ctx
->method_inst
->type_argv
[0];
1798 t
= mini_get_underlying_type (t
);
1800 gboolean is_i8
= (t
->type
== MONO_TYPE_I8
|| t
->type
== MONO_TYPE_U8
);
1801 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
);
1803 gboolean is_compareto
= strcmp (tm
, "EnumCompareTo") == 0;
1806 locala
= create_interp_local (td
, t
);
1807 localb
= create_interp_local (td
, t
);
1810 store_local (td
, localb
);
1811 store_local (td
, locala
);
1813 load_local (td
, locala
);
1814 load_local (td
, localb
);
1816 interp_add_ins (td
, is_i8
? MINT_CGT_UN_I8
: MINT_CGT_UN_I4
);
1818 interp_add_ins (td
, is_i8
? MINT_CGT_I8
: MINT_CGT_I4
);
1820 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
1822 load_local (td
, locala
);
1823 load_local (td
, localb
);
1825 interp_add_ins (td
, is_i8
? MINT_CLT_UN_I8
: MINT_CLT_UN_I4
);
1827 interp_add_ins (td
, is_i8
? MINT_CLT_I8
: MINT_CLT_I4
);
1829 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
1830 // (a > b) - (a < b)
1831 interp_add_ins (td
, MINT_SUB_I4
);
1848 interp_transform_internal_calls (MonoMethod
*method
, MonoMethod
*target_method
, MonoMethodSignature
*csignature
, gboolean is_virtual
)
1850 if (method
->wrapper_type
== MONO_WRAPPER_NONE
&& target_method
!= NULL
) {
1851 if (target_method
->flags
& METHOD_ATTRIBUTE_PINVOKE_IMPL
)
1852 target_method
= mono_marshal_get_native_wrapper (target_method
, FALSE
, FALSE
);
1853 if (!is_virtual
&& target_method
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
)
1854 target_method
= mono_marshal_get_synchronized_wrapper (target_method
);
1856 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)
1857 target_method
= mono_marshal_get_native_wrapper (target_method
, FALSE
, FALSE
);
1859 return target_method
;
1863 interp_type_as_ptr (MonoType
*tp
)
1865 if (MONO_TYPE_IS_POINTER (tp
))
1867 if (MONO_TYPE_IS_REFERENCE (tp
))
1869 if ((tp
)->type
== MONO_TYPE_I4
)
1871 #if SIZEOF_VOID_P == 8
1872 if ((tp
)->type
== MONO_TYPE_I8
)
1875 if ((tp
)->type
== MONO_TYPE_BOOLEAN
)
1877 if ((tp
)->type
== MONO_TYPE_CHAR
)
1879 if ((tp
)->type
== MONO_TYPE_VALUETYPE
&& m_class_is_enumtype (tp
->data
.klass
))
1884 #define INTERP_TYPE_AS_PTR(tp) interp_type_as_ptr (tp)
1887 interp_icall_op_for_sig (MonoMethodSignature
*sig
)
1890 switch (sig
->param_count
) {
1892 if (MONO_TYPE_IS_VOID (sig
->ret
))
1893 op
= MINT_ICALL_V_V
;
1894 else if (INTERP_TYPE_AS_PTR (sig
->ret
))
1895 op
= MINT_ICALL_V_P
;
1898 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1899 if (INTERP_TYPE_AS_PTR (sig
->params
[0]))
1900 op
= MINT_ICALL_P_V
;
1901 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1902 if (INTERP_TYPE_AS_PTR (sig
->params
[0]))
1903 op
= MINT_ICALL_P_P
;
1907 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1908 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1909 INTERP_TYPE_AS_PTR (sig
->params
[1]))
1910 op
= MINT_ICALL_PP_V
;
1911 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1912 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1913 INTERP_TYPE_AS_PTR (sig
->params
[1]))
1914 op
= MINT_ICALL_PP_P
;
1918 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1919 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1920 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1921 INTERP_TYPE_AS_PTR (sig
->params
[2]))
1922 op
= MINT_ICALL_PPP_V
;
1923 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1924 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1925 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1926 INTERP_TYPE_AS_PTR (sig
->params
[2]))
1927 op
= MINT_ICALL_PPP_P
;
1931 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1932 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1933 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1934 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1935 INTERP_TYPE_AS_PTR (sig
->params
[3]))
1936 op
= MINT_ICALL_PPPP_V
;
1937 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1938 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1939 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1940 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1941 INTERP_TYPE_AS_PTR (sig
->params
[3]))
1942 op
= MINT_ICALL_PPPP_P
;
1946 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1947 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1948 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1949 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1950 INTERP_TYPE_AS_PTR (sig
->params
[3]) &&
1951 INTERP_TYPE_AS_PTR (sig
->params
[4]))
1952 op
= MINT_ICALL_PPPPP_V
;
1953 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1954 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1955 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1956 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1957 INTERP_TYPE_AS_PTR (sig
->params
[3]) &&
1958 INTERP_TYPE_AS_PTR (sig
->params
[4]))
1959 op
= MINT_ICALL_PPPPP_P
;
1963 if (MONO_TYPE_IS_VOID (sig
->ret
)) {
1964 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1965 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1966 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1967 INTERP_TYPE_AS_PTR (sig
->params
[3]) &&
1968 INTERP_TYPE_AS_PTR (sig
->params
[4]) &&
1969 INTERP_TYPE_AS_PTR (sig
->params
[5]))
1970 op
= MINT_ICALL_PPPPPP_V
;
1971 } else if (INTERP_TYPE_AS_PTR (sig
->ret
)) {
1972 if (INTERP_TYPE_AS_PTR (sig
->params
[0]) &&
1973 INTERP_TYPE_AS_PTR (sig
->params
[1]) &&
1974 INTERP_TYPE_AS_PTR (sig
->params
[2]) &&
1975 INTERP_TYPE_AS_PTR (sig
->params
[3]) &&
1976 INTERP_TYPE_AS_PTR (sig
->params
[4]) &&
1977 INTERP_TYPE_AS_PTR (sig
->params
[5]))
1978 op
= MINT_ICALL_PPPPPP_P
;
1985 #define INLINE_LENGTH_LIMIT 20
1988 interp_method_check_inlining (TransformData
*td
, MonoMethod
*method
)
1990 MonoMethodHeaderSummary header
;
1992 if (td
->method
== method
)
1995 if (!mono_method_get_header_summary (method
, &header
))
1998 /*runtime, icall and pinvoke are checked by summary call*/
1999 if ((method
->iflags
& METHOD_IMPL_ATTRIBUTE_NOINLINING
) ||
2000 (method
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
) ||
2001 (mono_class_is_marshalbyref (method
->klass
)) ||
2005 if (header
.code_size
>= INLINE_LENGTH_LIMIT
)
2008 if (mono_class_needs_cctor_run (method
->klass
, NULL
)) {
2011 if (!m_class_get_runtime_info (method
->klass
))
2012 /* No vtable created yet */
2014 vtable
= mono_class_vtable_checked (td
->rtm
->domain
, method
->klass
, error
);
2015 if (!is_ok (error
)) {
2016 mono_error_cleanup (error
);
2019 if (!vtable
->initialized
)
2023 /* We currently access at runtime the wrapper data */
2024 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
2027 /* Our usage of `emit_store_value_as_local ()` for nint, nuint and nfloat
2028 * is kinda hacky, and doesn't work with the inliner */
2029 if (mono_class_get_magic_index (method
->klass
) >= 0)
2036 interp_inline_method (TransformData
*td
, MonoMethod
*target_method
, MonoMethodHeader
*header
, MonoError
*error
)
2038 const unsigned char *prev_ip
, *prev_il_code
, *prev_in_start
;
2039 int *prev_in_offsets
;
2041 unsigned int prev_max_stack_height
, prev_max_vt_sp
, prev_locals_size
;
2042 int prev_n_data_items
;
2045 MonoGenericContext
*generic_context
= NULL
;
2046 StackInfo
*prev_param_area
;
2047 MonoMethod
*prev_inlined_method
;
2048 MonoMethodSignature
*csignature
= mono_method_signature_internal (target_method
);
2049 int nargs
= csignature
->param_count
+ !!csignature
->hasthis
;
2050 InterpInst
*prev_last_ins
;
2052 if (csignature
->is_inflated
)
2053 generic_context
= mono_method_get_context (target_method
);
2055 MonoGenericContainer
*generic_container
= mono_method_get_generic_container (target_method
);
2056 if (generic_container
)
2057 generic_context
= &generic_container
->context
;
2061 prev_il_code
= td
->il_code
;
2062 prev_in_start
= td
->in_start
;
2063 prev_sp_offset
= td
->sp
- td
->stack
;
2064 prev_vt_sp
= td
->vt_sp
;
2065 prev_inlined_method
= td
->inlined_method
;
2066 prev_last_ins
= td
->last_ins
;
2067 td
->inlined_method
= target_method
;
2069 prev_max_stack_height
= td
->max_stack_height
;
2070 prev_max_vt_sp
= td
->max_vt_sp
;
2071 prev_locals_size
= td
->locals_size
;
2073 prev_n_data_items
= td
->n_data_items
;
2074 prev_in_offsets
= td
->in_offsets
;
2075 td
->in_offsets
= (int*)g_malloc0((header
->code_size
+ 1) * sizeof(int));
2077 /* Inlining pops the arguments, restore the stack */
2078 prev_param_area
= (StackInfo
*)g_malloc (nargs
* sizeof (StackInfo
));
2079 memcpy (prev_param_area
, &td
->sp
[-nargs
], nargs
* sizeof (StackInfo
));
2081 int const prev_code_size
= td
->code_size
;
2082 td
->code_size
= header
->code_size
;
2084 if (td
->verbose_level
)
2085 g_print ("Inline start method %s.%s\n", m_class_get_name (target_method
->klass
), target_method
->name
);
2086 ret
= generate_code (td
, target_method
, header
, generic_context
, error
);
2089 if (td
->verbose_level
)
2090 g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method
->klass
), target_method
->name
);
2091 td
->max_stack_height
= prev_max_stack_height
;
2092 td
->max_vt_sp
= prev_max_vt_sp
;
2093 td
->locals_size
= prev_locals_size
;
2096 /* Remove any newly added items */
2097 for (i
= prev_n_data_items
; i
< td
->n_data_items
; i
++) {
2098 g_hash_table_remove (td
->data_hash
, td
->data_items
[i
]);
2100 td
->n_data_items
= prev_n_data_items
;
2101 td
->sp
= td
->stack
+ prev_sp_offset
;
2102 memcpy (&td
->sp
[-nargs
], prev_param_area
, nargs
* sizeof (StackInfo
));
2103 td
->vt_sp
= prev_vt_sp
;
2104 td
->last_ins
= prev_last_ins
;
2106 td
->last_ins
->next
= NULL
;
2107 UnlockedIncrement (&mono_interp_stats
.inline_failures
);
2109 if (td
->verbose_level
)
2110 g_print ("Inline end method %s.%s\n", m_class_get_name (target_method
->klass
), target_method
->name
);
2111 UnlockedIncrement (&mono_interp_stats
.inlined_methods
);
2112 // Make sure we have an IR instruction associated with the now removed IL CALL
2113 // FIXME This could be prettier. We might be able to make inlining saner now that
2114 // that we can easily tweak the instruction list.
2115 if (!prev_inlined_method
) {
2116 if (prev_last_ins
) {
2117 if (prev_last_ins
->next
)
2118 prev_last_ins
->next
->il_offset
= prev_in_start
- prev_il_code
;
2119 } else if (td
->first_ins
) {
2120 td
->first_ins
->il_offset
= prev_in_start
- prev_il_code
;
2126 td
->in_start
= prev_in_start
;
2127 td
->il_code
= prev_il_code
;
2128 td
->inlined_method
= prev_inlined_method
;
2129 td
->code_size
= prev_code_size
;
2131 g_free (td
->in_offsets
);
2132 td
->in_offsets
= prev_in_offsets
;
2134 g_free (prev_param_area
);
2139 interp_constrained_box (TransformData
*td
, MonoDomain
*domain
, MonoClass
*constrained_class
, MonoMethodSignature
*csignature
, MonoError
*error
)
2141 int mt
= mint_type (m_class_get_byval_arg (constrained_class
));
2142 if (mono_class_is_nullable (constrained_class
)) {
2143 g_assert (mt
== MINT_TYPE_VT
);
2144 interp_add_ins (td
, MINT_BOX_NULLABLE
);
2145 td
->last_ins
->data
[0] = get_data_item_index (td
, constrained_class
);
2146 td
->last_ins
->data
[1] = csignature
->param_count
;
2147 td
->last_ins
->data
[2] = (td
->sp
- 1 - csignature
->param_count
)->type
!= STACK_TYPE_MP
? 0 : 1;
2149 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, constrained_class
, error
);
2150 return_if_nok (error
);
2152 if (mt
== MINT_TYPE_VT
) {
2153 interp_add_ins (td
, MINT_BOX_VT
);
2154 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
2155 td
->last_ins
->data
[1] = csignature
->param_count
;
2156 td
->last_ins
->data
[2] = (td
->sp
- 1 - csignature
->param_count
)->type
!= STACK_TYPE_MP
? 0 : 1;
2158 interp_add_ins (td
, MINT_BOX
);
2159 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
2160 td
->last_ins
->data
[1] = csignature
->param_count
;
2165 /* Return FALSE if error, including inline failure */
2167 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
)
2169 MonoImage
*image
= m_class_get_image (method
->klass
);
2170 MonoMethodSignature
*csignature
;
2171 int is_virtual
= *td
->ip
== CEE_CALLVIRT
;
2172 int calli
= *td
->ip
== CEE_CALLI
|| *td
->ip
== CEE_MONO_CALLI_EXTRA_ARG
;
2174 guint32 vt_stack_used
= 0;
2175 guint32 vt_res_size
= 0;
2179 int need_null_check
= is_virtual
;
2181 guint32 token
= read32 (td
->ip
+ 1);
2183 if (target_method
== NULL
) {
2186 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
2187 csignature
= (MonoMethodSignature
*)mono_method_get_wrapper_data (method
, token
);
2189 csignature
= mono_metadata_parse_signature_checked (image
, token
, error
);
2190 return_val_if_nok (error
, FALSE
);
2193 if (generic_context
) {
2194 csignature
= mono_inflate_generic_signature (csignature
, generic_context
, error
);
2195 return_val_if_nok (error
, FALSE
);
2199 * The compiled interp entry wrapper is passed to runtime_invoke instead of
2200 * the InterpMethod pointer. FIXME
2202 native
= csignature
->pinvoke
|| method
->wrapper_type
== MONO_WRAPPER_RUNTIME_INVOKE
;
2204 target_method
= NULL
;
2206 if (method
->wrapper_type
== MONO_WRAPPER_NONE
) {
2207 target_method
= mono_get_method_checked (image
, token
, NULL
, generic_context
, error
);
2208 return_val_if_nok (error
, FALSE
);
2210 target_method
= (MonoMethod
*)mono_method_get_wrapper_data (method
, token
);
2211 csignature
= mono_method_signature_internal (target_method
);
2213 if (generic_context
) {
2214 csignature
= mono_inflate_generic_signature (csignature
, generic_context
, error
);
2215 return_val_if_nok (error
, FALSE
);
2216 target_method
= mono_class_inflate_generic_method_checked (target_method
, generic_context
, error
);
2217 return_val_if_nok (error
, FALSE
);
2221 csignature
= mono_method_signature_internal (target_method
);
2224 if (check_visibility
&& target_method
&& !mono_method_can_access_method (method
, target_method
))
2225 interp_generate_mae_throw (td
, method
, target_method
);
2227 if (target_method
&& target_method
->string_ctor
) {
2228 /* Create the real signature */
2229 MonoMethodSignature
*ctor_sig
= mono_metadata_signature_dup_mempool (td
->mempool
, csignature
);
2230 ctor_sig
->ret
= m_class_get_byval_arg (mono_defaults
.string_class
);
2232 csignature
= ctor_sig
;
2236 if (target_method
&& interp_handle_intrinsics (td
, target_method
, constrained_class
, csignature
, readonly
, &op
))
2239 if (constrained_class
) {
2240 if (m_class_is_enumtype (constrained_class
) && !strcmp (target_method
->name
, "GetHashCode")) {
2241 /* Use the corresponding method from the base type to avoid boxing */
2242 MonoType
*base_type
= mono_class_enum_basetype_internal (constrained_class
);
2243 g_assert (base_type
);
2244 constrained_class
= mono_class_from_mono_type_internal (base_type
);
2245 target_method
= mono_class_get_method_from_name_checked (constrained_class
, target_method
->name
, 0, 0, error
);
2246 mono_error_assert_ok (error
);
2247 g_assert (target_method
);
2251 if (constrained_class
) {
2252 mono_class_setup_vtable (constrained_class
);
2253 if (mono_class_has_failure (constrained_class
)) {
2254 mono_error_set_for_class_failure (error
, constrained_class
);
2258 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
);
2260 target_method
= mono_get_method_constrained_with_method (image
, target_method
, constrained_class
, generic_context
, error
);
2262 g_print (" : %s::%s. %s (%p)\n", target_method
->klass
->name
, target_method
->name
, mono_signature_full_name (target_method
->signature
), target_method
);
2264 /* Intrinsics: Try again, it could be that `mono_get_method_constrained_with_method` resolves to a method that we can substitute */
2265 if (target_method
&& interp_handle_intrinsics (td
, target_method
, constrained_class
, csignature
, readonly
, &op
))
2268 return_val_if_nok (error
, FALSE
);
2269 mono_class_setup_vtable (target_method
->klass
);
2271 if (!m_class_is_valuetype (constrained_class
)) {
2272 /* managed pointer on the stack, we need to deref that puppy */
2273 interp_add_ins (td
, MINT_LDIND_I
);
2274 td
->last_ins
->data
[0] = csignature
->param_count
;
2275 } 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
) {
2276 if (target_method
->klass
== mono_defaults
.enum_class
&& (td
->sp
- csignature
->param_count
- 1)->type
== STACK_TYPE_MP
) {
2277 /* managed pointer on the stack, we need to deref that puppy */
2278 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2279 interp_add_ins (td
, MINT_LDIND_I8
);
2280 td
->last_ins
->data
[0] = csignature
->param_count
;
2283 interp_constrained_box (td
, domain
, constrained_class
, csignature
, error
);
2284 return_val_if_nok (error
, FALSE
);
2286 if (target_method
->klass
!= constrained_class
) {
2288 * The type parameter is instantiated as a valuetype,
2289 * but that type doesn't override the method we're
2290 * calling, so we need to box `this'.
2292 if (target_method
->klass
== mono_defaults
.enum_class
&& (td
->sp
- csignature
->param_count
- 1)->type
== STACK_TYPE_MP
) {
2293 /* managed pointer on the stack, we need to deref that puppy */
2294 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2295 interp_add_ins (td
, MINT_LDIND_I8
);
2296 td
->last_ins
->data
[0] = csignature
->param_count
;
2299 interp_constrained_box (td
, domain
, constrained_class
, csignature
, error
);
2300 return_val_if_nok (error
, FALSE
);
2307 mono_class_init_internal (target_method
->klass
);
2309 if (!is_virtual
&& target_method
&& (target_method
->flags
& METHOD_ATTRIBUTE_ABSTRACT
)) {
2310 if (!mono_class_is_interface (method
->klass
))
2311 interp_generate_bie_throw (td
);
2316 if (is_virtual
&& target_method
&& (!(target_method
->flags
& METHOD_ATTRIBUTE_VIRTUAL
) ||
2317 (MONO_METHOD_IS_FINAL (target_method
) &&
2318 target_method
->wrapper_type
!= MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK
)) &&
2319 !(mono_class_is_marshalbyref (target_method
->klass
))) {
2320 /* Not really virtual, just needs a null check */
2322 need_null_check
= TRUE
;
2325 CHECK_STACK (td
, csignature
->param_count
+ csignature
->hasthis
);
2326 if (!td
->gen_sdb_seq_points
&& !calli
&& op
== -1 && (!is_virtual
|| (target_method
->flags
& METHOD_ATTRIBUTE_VIRTUAL
) == 0) &&
2327 (target_method
->flags
& METHOD_ATTRIBUTE_PINVOKE_IMPL
) == 0 &&
2328 (target_method
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
) == 0 &&
2329 !(target_method
->iflags
& METHOD_IMPL_ATTRIBUTE_NOINLINING
)) {
2330 (void)mono_class_vtable_checked (domain
, target_method
->klass
, error
);
2331 return_val_if_nok (error
, FALSE
);
2333 if (method
== target_method
&& *(td
->ip
+ 5) == CEE_RET
&& !(csignature
->hasthis
&& m_class_is_valuetype (target_method
->klass
))) {
2334 if (td
->inlined_method
)
2337 if (td
->verbose_level
)
2338 g_print ("Optimize tail call of %s.%s\n", m_class_get_name (target_method
->klass
), target_method
->name
);
2340 for (i
= csignature
->param_count
- 1 + !!csignature
->hasthis
; i
>= 0; --i
)
2343 interp_add_ins (td
, MINT_BR_S
);
2344 // We are branching to the beginning of the method
2345 td
->last_ins
->data
[0] = 0;
2346 if (!is_bb_start
[td
->ip
+ 5 - td
->il_code
])
2347 ++td
->ip
; /* gobble the CEE_RET if it isn't branched to */
2353 target_method
= interp_transform_internal_calls (method
, target_method
, csignature
, is_virtual
);
2355 if (csignature
->call_convention
== MONO_CALL_VARARG
) {
2356 csignature
= mono_method_get_signature_checked (target_method
, image
, token
, generic_context
, error
);
2357 int vararg_stack
= 0;
2359 * For vararg calls, ArgIterator expects the signature and the varargs to be
2360 * stored in a linear memory. We allocate the necessary vt_stack space for
2361 * this. All varargs will be pushed to the vt_stack at call site.
2363 vararg_stack
+= sizeof (gpointer
);
2364 for (i
= csignature
->sentinelpos
; i
< csignature
->param_count
; ++i
) {
2365 int align
, arg_size
;
2366 arg_size
= mono_type_stack_size (csignature
->params
[i
], &align
);
2367 vararg_stack
+= ALIGN_TO (arg_size
, align
);
2369 /* allocate space for the pointer to varargs space start */
2370 vararg_stack
+= sizeof (gpointer
);
2371 vt_stack_used
+= ALIGN_TO (vararg_stack
, MINT_VT_ALIGNMENT
);
2372 PUSH_VT (td
, vararg_stack
);
2375 if (need_null_check
) {
2376 interp_add_ins (td
, MINT_CKNULL_N
);
2377 td
->last_ins
->data
[0] = csignature
->param_count
+ 1;
2380 g_assert (csignature
->call_convention
!= MONO_CALL_FASTCALL
);
2381 if ((mono_interp_opt
& INTERP_OPT_INLINE
) && op
== -1 && !is_virtual
&& target_method
&& interp_method_check_inlining (td
, target_method
)) {
2382 MonoMethodHeader
*mheader
= interp_method_get_header (target_method
, error
);
2383 return_val_if_nok (error
, FALSE
);
2385 if (interp_inline_method (td
, target_method
, mheader
, error
)) {
2391 /* Don't inline methods that do calls */
2392 if (op
== -1 && td
->inlined_method
)
2395 /* We need to convert delegate invoke to a indirect call on the interp_invoke_impl field */
2396 if (target_method
&& m_class_get_parent (target_method
->klass
) == mono_defaults
.multicastdelegate_class
) {
2397 const char *name
= target_method
->name
;
2398 if (*name
== 'I' && (strcmp (name
, "Invoke") == 0)) {
2400 interp_add_ins (td
, MINT_LD_DELEGATE_INVOKE_IMPL
);
2401 td
->last_ins
->data
[0] = csignature
->param_count
+ 1;
2402 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
2406 /* Pop the function pointer */
2410 td
->sp
-= csignature
->param_count
+ !!csignature
->hasthis
;
2411 for (i
= 0; i
< csignature
->param_count
; ++i
) {
2412 if (td
->sp
[i
+ !!csignature
->hasthis
].type
== STACK_TYPE_VT
) {
2414 MonoClass
*klass
= mono_class_from_mono_type_internal (csignature
->params
[i
]);
2415 if (csignature
->pinvoke
&& method
->wrapper_type
!= MONO_WRAPPER_NONE
)
2416 size
= mono_class_native_size (klass
, NULL
);
2418 size
= mono_class_value_size (klass
, NULL
);
2419 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
2420 vt_stack_used
+= size
;
2424 /* need to handle typedbyref ... */
2425 if (csignature
->ret
->type
!= MONO_TYPE_VOID
) {
2426 int mt
= mint_type(csignature
->ret
);
2427 MonoClass
*klass
= mono_class_from_mono_type_internal (csignature
->ret
);
2428 if (mt
== MINT_TYPE_VT
) {
2429 if (csignature
->pinvoke
&& method
->wrapper_type
!= MONO_WRAPPER_NONE
)
2430 vt_res_size
= mono_class_native_size (klass
, NULL
);
2432 vt_res_size
= mono_class_value_size (klass
, NULL
);
2433 if (mono_class_has_failure (klass
)) {
2434 mono_error_set_for_class_failure (error
, klass
);
2437 PUSH_VT(td
, vt_res_size
);
2439 PUSH_TYPE(td
, stack_type
[mt
], klass
);
2444 interp_add_ins (td
, op
);
2446 if (op
== MINT_LDLEN
) {
2447 #ifdef MONO_BIG_ARRAYS
2448 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I8
);
2450 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
2454 #ifndef ENABLE_NETCORE
2455 if (op
== MINT_CALLRUN
) {
2456 td
->last_ins
->data
[0] = get_data_item_index (td
, target_method
);
2457 td
->last_ins
->data
[1] = get_data_item_index (td
, mono_method_signature_internal (target_method
));
2460 } else if (!calli
&& !is_virtual
&& jit_call_supported (target_method
, csignature
)) {
2461 interp_add_ins (td
, MINT_JIT_CALL
);
2462 td
->last_ins
->data
[0] = get_data_item_index (td
, (void *)mono_interp_get_imethod (domain
, target_method
, error
));
2463 mono_error_assert_ok (error
);
2465 #ifndef MONO_ARCH_HAS_NO_PROPER_MONOCTX
2466 /* Try using fast icall path for simple signatures */
2467 if (native
&& !method
->dynamic
)
2468 op
= interp_icall_op_for_sig (csignature
);
2470 if (csignature
->call_convention
== MONO_CALL_VARARG
)
2471 interp_add_ins (td
, MINT_CALL_VARARG
);
2473 interp_add_ins (td
, native
? ((op
!= -1) ? MINT_CALLI_NAT_FAST
: MINT_CALLI_NAT
) : MINT_CALLI
);
2474 else if (is_virtual
&& !mono_class_is_marshalbyref (target_method
->klass
))
2475 interp_add_ins (td
, is_void
? MINT_VCALLVIRT_FAST
: MINT_CALLVIRT_FAST
);
2476 else if (is_virtual
)
2477 interp_add_ins (td
, is_void
? MINT_VCALLVIRT
: MINT_CALLVIRT
);
2479 interp_add_ins (td
, is_void
? MINT_VCALL
: MINT_CALL
);
2482 td
->last_ins
->data
[0] = get_data_item_index (td
, (void *)csignature
);
2484 td
->last_ins
->data
[1] = op
;
2485 if (td
->last_ins
->opcode
== MINT_CALLI_NAT_FAST
)
2486 td
->last_ins
->data
[2] = save_last_error
;
2487 } else if (op
== -1 && td
->last_ins
->opcode
== MINT_CALLI_NAT
) {
2488 td
->last_ins
->data
[1] = save_last_error
;
2491 td
->last_ins
->data
[0] = get_data_item_index (td
, (void *)mono_interp_get_imethod (domain
, target_method
, error
));
2492 #ifdef ENABLE_EXPERIMENT_TIERED
2493 if (MINT_IS_PATCHABLE_CALL (td
->last_ins
->opcode
)) {
2494 g_assert (!calli
&& !is_virtual
);
2495 td
->last_ins
->flags
|= INTERP_INST_FLAG_RECORD_CALL_PATCH
;
2496 g_hash_table_insert (td
->patchsite_hash
, td
->last_ins
, target_method
);
2499 return_val_if_nok (error
, FALSE
);
2500 if (csignature
->call_convention
== MONO_CALL_VARARG
)
2501 td
->last_ins
->data
[1] = get_data_item_index (td
, (void *)csignature
);
2502 else if (is_virtual
&& !mono_class_is_marshalbyref (target_method
->klass
)) {
2503 /* FIXME Use fastpath also for MBRO. Asserts in mono_method_get_vtable_slot */
2504 if (mono_class_is_interface (target_method
->klass
))
2505 td
->last_ins
->data
[1] = -2 * MONO_IMT_SIZE
+ mono_method_get_imt_slot (target_method
);
2507 td
->last_ins
->data
[1] = mono_method_get_vtable_slot (target_method
);
2512 if (vt_stack_used
!= 0 || vt_res_size
!= 0) {
2513 interp_add_ins (td
, MINT_VTRESULT
);
2514 td
->last_ins
->data
[0] = vt_res_size
;
2515 WRITE32_INS (td
->last_ins
, 1, &vt_stack_used
);
2516 td
->vt_sp
-= vt_stack_used
;
2522 static MonoClassField
*
2523 interp_field_from_token (MonoMethod
*method
, guint32 token
, MonoClass
**klass
, MonoGenericContext
*generic_context
, MonoError
*error
)
2525 MonoClassField
*field
= NULL
;
2526 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
) {
2527 field
= (MonoClassField
*) mono_method_get_wrapper_data (method
, token
);
2528 *klass
= field
->parent
;
2530 mono_class_setup_fields (field
->parent
);
2532 field
= mono_field_from_token_checked (m_class_get_image (method
->klass
), token
, klass
, generic_context
, error
);
2533 return_val_if_nok (error
, NULL
);
2536 if (!method
->skip_visibility
&& !mono_method_can_access_field (method
, field
)) {
2537 char *method_fname
= mono_method_full_name (method
, TRUE
);
2538 char *field_fname
= mono_field_full_name (field
);
2539 mono_error_set_generic_error (error
, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname
, method_fname
);
2540 g_free (method_fname
);
2541 g_free (field_fname
);
2548 static InterpBasicBlock
*
2549 get_bb (TransformData
*td
, InterpBasicBlock
*cbb
, unsigned char *ip
)
2551 int offset
= ip
- td
->il_code
;
2552 InterpBasicBlock
*bb
= td
->offset_to_bb
[offset
];
2555 bb
= (InterpBasicBlock
*)mono_mempool_alloc0 (td
->mempool
, sizeof (InterpBasicBlock
));
2557 td
->offset_to_bb
[offset
] = bb
;
2559 td
->basic_blocks
= g_list_append_mempool (td
->mempool
, td
->basic_blocks
, bb
);
2563 bb
->preds
= g_slist_prepend_mempool (td
->mempool
, bb
->preds
, cbb
);
2570 * Compute the set of IL level basic blocks.
2573 get_basic_blocks (TransformData
*td
)
2575 guint8
*start
= (guint8
*)td
->il_code
;
2576 guint8
*end
= (guint8
*)td
->il_code
+ td
->code_size
;
2578 unsigned char *target
;
2581 const MonoOpcode
*opcode
;
2582 InterpBasicBlock
*cbb
;
2584 td
->offset_to_bb
= (InterpBasicBlock
**)mono_mempool_alloc0 (td
->mempool
, sizeof (InterpBasicBlock
*) * (end
- start
+ 1));
2585 td
->entry_bb
= cbb
= get_bb (td
, NULL
, start
);
2588 cli_addr
= ip
- start
;
2589 td
->offset_to_bb
[cli_addr
] = cbb
;
2590 i
= mono_opcode_value ((const guint8
**)&ip
, end
);
2591 opcode
= &mono_opcodes
[i
];
2592 switch (opcode
->argument
) {
2593 case MonoInlineNone
:
2596 case MonoInlineString
:
2597 case MonoInlineType
:
2598 case MonoInlineField
:
2599 case MonoInlineMethod
:
2602 case MonoShortInlineR
:
2609 case MonoShortInlineVar
:
2610 case MonoShortInlineI
:
2613 case MonoShortInlineBrTarget
:
2614 target
= start
+ cli_addr
+ 2 + (signed char)ip
[1];
2615 get_bb (td
, cbb
, target
);
2617 cbb
= get_bb (td
, cbb
, ip
);
2619 case MonoInlineBrTarget
:
2620 target
= start
+ cli_addr
+ 5 + (gint32
)read32 (ip
+ 1);
2621 get_bb (td
, cbb
, target
);
2623 cbb
= get_bb (td
, cbb
, ip
);
2625 case MonoInlineSwitch
: {
2626 guint32 n
= read32 (ip
+ 1);
2629 cli_addr
+= 5 + 4 * n
;
2630 target
= start
+ cli_addr
;
2631 get_bb (td
, cbb
, target
);
2633 for (j
= 0; j
< n
; ++j
) {
2634 target
= start
+ cli_addr
+ (gint32
)read32 (ip
);
2635 get_bb (td
, cbb
, target
);
2638 cbb
= get_bb (td
, cbb
, ip
);
2646 g_assert_not_reached ();
2650 cbb
= get_bb (td
, NULL
, ip
);
2655 interp_save_debug_info (InterpMethod
*rtm
, MonoMethodHeader
*header
, TransformData
*td
, GArray
*line_numbers
)
2657 MonoDebugMethodJitInfo
*dinfo
;
2660 if (!mono_debug_enabled ())
2664 * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
2667 dinfo
= g_new0 (MonoDebugMethodJitInfo
, 1);
2668 dinfo
->num_params
= rtm
->param_count
;
2669 dinfo
->params
= g_new0 (MonoDebugVarInfo
, dinfo
->num_params
);
2670 dinfo
->num_locals
= header
->num_locals
;
2671 dinfo
->locals
= g_new0 (MonoDebugVarInfo
, header
->num_locals
);
2672 dinfo
->code_start
= (guint8
*)rtm
->code
;
2673 dinfo
->code_size
= td
->new_code_end
- td
->new_code
;
2674 dinfo
->epilogue_begin
= 0;
2675 dinfo
->has_var_info
= TRUE
;
2676 dinfo
->num_line_numbers
= line_numbers
->len
;
2677 dinfo
->line_numbers
= g_new0 (MonoDebugLineNumberEntry
, dinfo
->num_line_numbers
);
2679 for (i
= 0; i
< dinfo
->num_params
; i
++) {
2680 MonoDebugVarInfo
*var
= &dinfo
->params
[i
];
2681 var
->type
= rtm
->param_types
[i
];
2683 for (i
= 0; i
< dinfo
->num_locals
; i
++) {
2684 MonoDebugVarInfo
*var
= &dinfo
->locals
[i
];
2685 var
->type
= mono_metadata_type_dup (NULL
, header
->locals
[i
]);
2688 for (i
= 0; i
< dinfo
->num_line_numbers
; i
++)
2689 dinfo
->line_numbers
[i
] = g_array_index (line_numbers
, MonoDebugLineNumberEntry
, i
);
2690 mono_debug_add_method (rtm
->method
, dinfo
, rtm
->domain
);
2692 mono_debug_free_method_jit_info (dinfo
);
2695 /* Same as the code in seq-points.c */
2697 insert_pred_seq_point (SeqPoint
*last_sp
, SeqPoint
*sp
, GSList
**next
)
2700 int src_index
= last_sp
->next_offset
;
2701 int dst_index
= sp
->next_offset
;
2703 /* bb->in_bb might contain duplicates */
2704 for (l
= next
[src_index
]; l
; l
= l
->next
)
2705 if (GPOINTER_TO_UINT (l
->data
) == dst_index
)
2708 next
[src_index
] = g_slist_append (next
[src_index
], GUINT_TO_POINTER (dst_index
));
2712 recursively_make_pred_seq_points (TransformData
*td
, InterpBasicBlock
*bb
)
2714 SeqPoint
** const MONO_SEQ_SEEN_LOOP
= (SeqPoint
**)GINT_TO_POINTER(-1);
2717 GArray
*predecessors
= g_array_new (FALSE
, TRUE
, sizeof (gpointer
));
2718 GHashTable
*seen
= g_hash_table_new_full (g_direct_hash
, NULL
, NULL
, NULL
);
2720 // Insert/remove sentinel into the memoize table to detect loops containing bb
2721 bb
->pred_seq_points
= MONO_SEQ_SEEN_LOOP
;
2723 for (l
= bb
->preds
; l
; l
= l
->next
) {
2724 InterpBasicBlock
*in_bb
= (InterpBasicBlock
*)l
->data
;
2726 // This bb has the last seq point, append it and continue
2727 if (in_bb
->last_seq_point
!= NULL
) {
2728 predecessors
= g_array_append_val (predecessors
, in_bb
->last_seq_point
);
2732 // We've looped or handled this before, exit early.
2733 // No last sequence points to find.
2734 if (in_bb
->pred_seq_points
== MONO_SEQ_SEEN_LOOP
)
2737 // Take sequence points from incoming basic blocks
2739 if (in_bb
== td
->entry_bb
)
2742 if (in_bb
->pred_seq_points
== NULL
)
2743 recursively_make_pred_seq_points (td
, in_bb
);
2745 // Union sequence points with incoming bb's
2746 for (int i
=0; i
< in_bb
->num_pred_seq_points
; i
++) {
2747 if (!g_hash_table_lookup (seen
, in_bb
->pred_seq_points
[i
])) {
2748 g_array_append_val (predecessors
, in_bb
->pred_seq_points
[i
]);
2749 g_hash_table_insert (seen
, in_bb
->pred_seq_points
[i
], (gpointer
)&MONO_SEQ_SEEN_LOOP
);
2752 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
2755 g_hash_table_destroy (seen
);
2757 if (predecessors
->len
!= 0) {
2758 bb
->pred_seq_points
= (SeqPoint
**)mono_mempool_alloc0 (td
->mempool
, sizeof (SeqPoint
*) * predecessors
->len
);
2759 bb
->num_pred_seq_points
= predecessors
->len
;
2761 for (int newer
= 0; newer
< bb
->num_pred_seq_points
; newer
++) {
2762 bb
->pred_seq_points
[newer
] = (SeqPoint
*)g_array_index (predecessors
, gpointer
, newer
);
2766 g_array_free (predecessors
, TRUE
);
2770 collect_pred_seq_points (TransformData
*td
, InterpBasicBlock
*bb
, SeqPoint
*seqp
, GSList
**next
)
2772 // Doesn't have a last sequence point, must find from incoming basic blocks
2773 if (bb
->pred_seq_points
== NULL
&& bb
!= td
->entry_bb
)
2774 recursively_make_pred_seq_points (td
, bb
);
2776 for (int i
= 0; i
< bb
->num_pred_seq_points
; i
++)
2777 insert_pred_seq_point (bb
->pred_seq_points
[i
], seqp
, next
);
2783 save_seq_points (TransformData
*td
, MonoJitInfo
*jinfo
)
2786 int i
, seq_info_size
;
2787 MonoSeqPointInfo
*info
;
2788 GSList
**next
= NULL
;
2791 if (!td
->gen_sdb_seq_points
)
2795 * For each sequence point, compute the list of sequence points immediately
2796 * following it, this is needed to implement 'step over' in the debugger agent.
2797 * Similar to the code in mono_save_seq_point_info ().
2799 for (i
= 0; i
< td
->seq_points
->len
; ++i
) {
2800 SeqPoint
*sp
= (SeqPoint
*)g_ptr_array_index (td
->seq_points
, i
);
2802 /* Store the seq point index here temporarily */
2803 sp
->next_offset
= i
;
2805 next
= (GSList
**)mono_mempool_alloc0 (td
->mempool
, sizeof (GList
*) * td
->seq_points
->len
);
2806 for (bblist
= td
->basic_blocks
; bblist
; bblist
= bblist
->next
) {
2807 InterpBasicBlock
*bb
= (InterpBasicBlock
*)bblist
->data
;
2809 GSList
*bb_seq_points
= g_slist_reverse (bb
->seq_points
);
2810 SeqPoint
*last
= NULL
;
2811 for (GSList
*l
= bb_seq_points
; l
; l
= l
->next
) {
2812 SeqPoint
*sp
= (SeqPoint
*)l
->data
;
2814 if (sp
->il_offset
== METHOD_ENTRY_IL_OFFSET
|| sp
->il_offset
== METHOD_EXIT_IL_OFFSET
)
2815 /* Used to implement method entry/exit events */
2819 /* Link with the previous seq point in the same bb */
2820 next
[last
->next_offset
] = g_slist_append_mempool (td
->mempool
, next
[last
->next_offset
], GINT_TO_POINTER (sp
->next_offset
));
2822 /* Link with the last bb in the previous bblocks */
2823 collect_pred_seq_points (td
, bb
, sp
, next
);
2829 /* Serialize the seq points into a byte array */
2830 array
= g_byte_array_new ();
2831 SeqPoint zero_seq_point
= {0};
2832 SeqPoint
* last_seq_point
= &zero_seq_point
;
2833 for (i
= 0; i
< td
->seq_points
->len
; ++i
) {
2834 SeqPoint
*sp
= (SeqPoint
*)g_ptr_array_index (td
->seq_points
, i
);
2836 sp
->next_offset
= 0;
2837 if (mono_seq_point_info_add_seq_point (array
, sp
, last_seq_point
, next
[i
], TRUE
))
2838 last_seq_point
= sp
;
2841 if (td
->verbose_level
) {
2842 g_print ("\nSEQ POINT MAP FOR %s: \n", td
->method
->name
);
2844 for (i
= 0; i
< td
->seq_points
->len
; ++i
) {
2845 SeqPoint
*sp
= (SeqPoint
*)g_ptr_array_index (td
->seq_points
, i
);
2851 g_print ("\tIL0x%x[0x%0x] ->", sp
->il_offset
, sp
->native_offset
);
2852 for (l
= next
[i
]; l
; l
= l
->next
) {
2853 int next_index
= GPOINTER_TO_UINT (l
->data
);
2854 g_print (" IL0x%x", ((SeqPoint
*)g_ptr_array_index (td
->seq_points
, next_index
))->il_offset
);
2860 info
= mono_seq_point_info_new (array
->len
, TRUE
, array
->data
, TRUE
, &seq_info_size
);
2861 mono_atomic_fetch_add_i32 (&mono_jit_stats
.allocated_seq_points_size
, seq_info_size
);
2863 g_byte_array_free (array
, TRUE
);
2865 jinfo
->seq_points
= info
;
2869 interp_emit_memory_barrier (TransformData
*td
, int kind
)
2871 #if defined(TARGET_WASM)
2872 // mono_memory_barrier is dummy on wasm
2873 #elif defined(TARGET_X86) || defined(TARGET_AMD64)
2874 if (kind
== MONO_MEMORY_BARRIER_SEQ
)
2875 interp_add_ins (td
, MINT_MONO_MEMORY_BARRIER
);
2877 interp_add_ins (td
, MINT_MONO_MEMORY_BARRIER
);
2881 #define BARRIER_IF_VOLATILE(td, kind) \
2884 interp_emit_memory_barrier (td, kind); \
2885 volatile_ = FALSE; \
2889 #define INLINE_FAILURE \
2896 interp_method_compute_offsets (TransformData
*td
, InterpMethod
*imethod
, MonoMethodSignature
*signature
, MonoMethodHeader
*header
)
2898 int i
, offset
, size
, align
;
2900 imethod
->local_offsets
= (guint32
*)g_malloc (header
->num_locals
* sizeof(guint32
));
2901 td
->locals
= (InterpLocal
*)g_malloc (header
->num_locals
* sizeof (InterpLocal
));
2902 td
->locals_size
= header
->num_locals
;
2903 td
->locals_capacity
= td
->locals_size
;
2905 for (i
= 0; i
< header
->num_locals
; ++i
) {
2906 size
= mono_type_size (header
->locals
[i
], &align
);
2907 offset
+= align
- 1;
2908 offset
&= ~(align
- 1);
2909 imethod
->local_offsets
[i
] = offset
;
2910 td
->locals
[i
].offset
= offset
;
2911 td
->locals
[i
].flags
= 0;
2912 td
->locals
[i
].type
= header
->locals
[i
];
2913 td
->locals
[i
].mt
= mint_type (header
->locals
[i
]);
2916 offset
= (offset
+ 7) & ~7;
2918 imethod
->exvar_offsets
= (guint32
*)g_malloc (header
->num_clauses
* sizeof (guint32
));
2919 for (i
= 0; i
< header
->num_clauses
; i
++) {
2920 imethod
->exvar_offsets
[i
] = offset
;
2921 offset
+= sizeof (MonoObject
*);
2923 offset
= (offset
+ 7) & ~7;
2925 imethod
->locals_size
= offset
;
2926 g_assert (imethod
->locals_size
< 65536);
2927 td
->total_locals_size
= offset
;
2930 /* Return false is failure to init basic blocks due to being in inline method */
2932 init_bb_start (TransformData
*td
, MonoMethodHeader
*header
, gboolean inlining
)
2934 const unsigned char *ip
, *end
;
2935 const MonoOpcode
*opcode
;
2936 int offset
, i
, in
, backwards
;
2938 /* intern the strings in the method. */
2940 end
= ip
+ header
->code_size
;
2942 /* inlined method continues the basic block of parent method */
2944 td
->is_bb_start
[0] = 1;
2951 else if (in
== 0xf0) {
2953 in
= *ip
+ MONO_CEE_MONO_ICALL
;
2955 opcode
= &mono_opcodes
[in
];
2956 switch (opcode
->argument
) {
2957 case MonoInlineNone
:
2960 case MonoInlineString
:
2963 case MonoInlineType
:
2966 case MonoInlineMethod
:
2969 case MonoInlineField
:
2973 case MonoShortInlineR
:
2976 case MonoInlineBrTarget
:
2977 offset
= read32 (ip
+ 1);
2979 /* this branch is ignored */
2980 if (offset
== 0 && in
== MONO_CEE_BR
)
2982 backwards
= offset
< 0;
2983 offset
+= ip
- header
->code
;
2984 g_assert (offset
>= 0 && offset
< header
->code_size
);
2987 td
->is_bb_start
[offset
] |= backwards
? 2 : 1;
2989 case MonoShortInlineBrTarget
:
2990 offset
= ((gint8
*)ip
) [1];
2992 /* this branch is ignored */
2993 if (offset
== 0 && in
== MONO_CEE_BR_S
)
2995 backwards
= offset
< 0;
2996 offset
+= ip
- header
->code
;
2997 g_assert (offset
>= 0 && offset
< header
->code_size
);
3000 td
->is_bb_start
[offset
] |= backwards
? 2 : 1;
3005 case MonoShortInlineVar
:
3006 case MonoShortInlineI
:
3009 case MonoInlineSwitch
: {
3011 const unsigned char *next_ip
;
3015 next_ip
= ip
+ 4 * n
;
3016 for (i
= 0; i
< n
; i
++) {
3017 offset
= read32 (ip
);
3018 backwards
= offset
< 0;
3019 offset
+= next_ip
- header
->code
;
3020 g_assert (offset
>= 0 && offset
< header
->code_size
);
3023 td
->is_bb_start
[offset
] |= backwards
? 2 : 1;
3033 g_assert_not_reached ();
3039 #ifdef NO_UNALIGNED_ACCESS
3041 get_unaligned_opcode (int opcode
)
3045 return MINT_LDFLD_I8_UNALIGNED
;
3047 return MINT_LDFLD_R8_UNALIGNED
;
3049 return MINT_STFLD_I8_UNALIGNED
;
3051 return MINT_STFLD_R8_UNALIGNED
;
3053 g_assert_not_reached ();
3060 interp_handle_isinst (TransformData
*td
, MonoClass
*klass
, gboolean isinst_instr
)
3062 /* Follow the logic from jit's handle_isinst */
3063 if (!mono_class_has_variant_generic_params (klass
)) {
3064 if (mono_class_is_interface (klass
))
3065 interp_add_ins (td
, isinst_instr
? MINT_ISINST_INTERFACE
: MINT_CASTCLASS_INTERFACE
);
3066 else if (!mono_class_is_marshalbyref (klass
) && m_class_get_rank (klass
) == 0 && !mono_class_is_nullable (klass
))
3067 interp_add_ins (td
, isinst_instr
? MINT_ISINST_COMMON
: MINT_CASTCLASS_COMMON
);
3069 interp_add_ins (td
, isinst_instr
? MINT_ISINST
: MINT_CASTCLASS
);
3071 interp_add_ins (td
, isinst_instr
? MINT_ISINST
: MINT_CASTCLASS
);
3073 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
3078 interp_emit_ldsflda (TransformData
*td
, MonoClassField
*field
, MonoError
*error
)
3080 MonoDomain
*domain
= td
->rtm
->domain
;
3081 // Initialize the offset for the field
3082 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, field
->parent
, error
);
3083 return_if_nok (error
);
3085 if (mono_class_field_is_special_static (field
)) {
3088 mono_domain_lock (domain
);
3089 g_assert (domain
->special_static_fields
);
3090 offset
= GPOINTER_TO_UINT (g_hash_table_lookup (domain
->special_static_fields
, field
));
3091 mono_domain_unlock (domain
);
3094 interp_add_ins (td
, MINT_LDSSFLDA
);
3095 WRITE32_INS(td
->last_ins
, 0, &offset
);
3097 interp_add_ins (td
, MINT_LDSFLDA
);
3098 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
3099 td
->last_ins
->data
[1] = get_data_item_index (td
, (char*)mono_vtable_get_static_field_data (vtable
) + field
->offset
);
3104 interp_emit_load_const (TransformData
*td
, gpointer field_addr
, int mt
)
3106 if (mt
>= MINT_TYPE_I1
&& mt
<= MINT_TYPE_I4
3107 #if SIZEOF_VOID_P == 4
3108 || mt
== MINT_TYPE_P
3114 val
= *(gint8
*)field_addr
;
3117 val
= *(guint8
*)field_addr
;
3120 val
= *(gint16
*)field_addr
;
3123 val
= *(guint16
*)field_addr
;
3126 val
= *(gint32
*)field_addr
;
3128 interp_get_ldc_i4_from_const (td
, NULL
, val
);
3129 } else if (mt
== MINT_TYPE_I8
3130 #if SIZEOF_VOID_P == 8
3131 || mt
== MINT_TYPE_P
3134 gint64 val
= *(gint64
*)field_addr
;
3135 interp_add_ins (td
, MINT_LDC_I8
);
3136 WRITE64_INS (td
->last_ins
, 0, &val
);
3137 } else if (mt
== MINT_TYPE_R4
) {
3138 float val
= *(float*)field_addr
;
3139 interp_add_ins (td
, MINT_LDC_R4
);
3140 WRITE32_INS (td
->last_ins
, 0, &val
);
3141 } else if (mt
== MINT_TYPE_R8
) {
3142 double val
= *(double*)field_addr
;
3143 interp_add_ins (td
, MINT_LDC_R8
);
3144 WRITE64_INS (td
->last_ins
, 0, &val
);
3152 interp_emit_sfld_access (TransformData
*td
, MonoClassField
*field
, MonoClass
*field_class
, int mt
, gboolean is_load
, MonoError
*error
)
3154 MonoDomain
*domain
= td
->rtm
->domain
;
3155 // Initialize the offset for the field
3156 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, field
->parent
, error
);
3157 return_if_nok (error
);
3159 if (mono_class_field_is_special_static (field
)) {
3162 mono_domain_lock (domain
);
3163 g_assert (domain
->special_static_fields
);
3164 offset
= GPOINTER_TO_UINT (g_hash_table_lookup (domain
->special_static_fields
, field
));
3165 mono_domain_unlock (domain
);
3168 // Offset is SpecialStaticOffset
3169 if ((offset
& 0x80000000) == 0 && mt
!= MINT_TYPE_VT
) {
3170 // This field is thread static
3171 interp_add_ins (td
, (is_load
? MINT_LDTSFLD_I1
: MINT_STTSFLD_I1
) + mt
);
3172 WRITE32_INS(td
->last_ins
, 0, &offset
);
3174 if (mt
== MINT_TYPE_VT
) {
3175 interp_add_ins (td
, is_load
? MINT_LDSSFLD_VT
: MINT_STSSFLD_VT
);
3176 WRITE32_INS(td
->last_ins
, 0, &offset
);
3178 int size
= mono_class_value_size (field_class
, NULL
);
3179 WRITE32_INS(td
->last_ins
, 2, &size
);
3181 interp_add_ins (td
, is_load
? MINT_LDSSFLD
: MINT_STSSFLD
);
3182 td
->last_ins
->data
[0] = get_data_item_index (td
, field
);
3183 WRITE32_INS(td
->last_ins
, 1, &offset
);
3187 gpointer field_addr
= (char*)mono_vtable_get_static_field_data (vtable
) + field
->offset
;
3189 MonoType
*ftype
= mono_field_get_type_internal (field
);
3190 if (ftype
->attrs
& FIELD_ATTRIBUTE_INIT_ONLY
&& vtable
->initialized
) {
3191 if (interp_emit_load_const (td
, field_addr
, mt
))
3194 interp_add_ins (td
, (mt
== MINT_TYPE_VT
) ? MINT_LDSFLD_VT
: (MINT_LDSFLD_I1
+ mt
- MINT_TYPE_I1
));
3196 interp_add_ins (td
, (mt
== MINT_TYPE_VT
) ? MINT_STSFLD_VT
: (MINT_STSFLD_I1
+ mt
- MINT_TYPE_I1
));
3199 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
3200 td
->last_ins
->data
[1] = get_data_item_index (td
, (char*)field_addr
);
3202 if (mt
== MINT_TYPE_VT
) {
3203 int size
= mono_class_value_size (field_class
, NULL
);
3204 WRITE32_INS(td
->last_ins
, 2, &size
);
3210 generate_code (TransformData
*td
, MonoMethod
*method
, MonoMethodHeader
*header
, MonoGenericContext
*generic_context
, MonoError
*error
)
3213 int offset
, mt
, i
, i32
;
3216 const unsigned char *end
;
3217 MonoSimpleBasicBlock
*bb
= NULL
, *original_bb
= NULL
;
3218 gboolean sym_seq_points
= FALSE
;
3219 MonoBitSet
*seq_point_locs
= NULL
;
3220 gboolean readonly
= FALSE
;
3221 gboolean volatile_
= FALSE
;
3222 MonoClass
*constrained_class
= NULL
;
3224 MonoClassField
*field
;
3225 MonoImage
*image
= m_class_get_image (method
->klass
);
3226 InterpMethod
*rtm
= td
->rtm
;
3227 MonoDomain
*domain
= rtm
->domain
;
3228 MonoMethodSignature
*signature
= mono_method_signature_internal (method
);
3229 gboolean ret
= TRUE
;
3230 gboolean emitted_funccall_seq_point
= FALSE
;
3231 guint32
*arg_locals
= NULL
;
3232 guint32
*local_locals
= NULL
;
3233 InterpInst
*last_seq_point
= NULL
;
3234 gboolean save_last_error
= FALSE
;
3235 gboolean inlining
= td
->method
!= method
;
3237 original_bb
= bb
= mono_basic_block_split (method
, error
, header
);
3238 goto_if_nok (error
, exit
);
3241 td
->il_code
= header
->code
;
3242 td
->in_start
= td
->ip
= header
->code
;
3243 end
= td
->ip
+ header
->code_size
;
3245 if (!init_bb_start (td
, header
, inlining
))
3249 for (i
= 0; i
< header
->code_size
; i
++) {
3250 td
->stack_height
[i
] = -1;
3251 td
->clause_indexes
[i
] = -1;
3255 for (i
= 0; i
< header
->num_clauses
; i
++) {
3256 MonoExceptionClause
*c
= header
->clauses
+ i
;
3257 td
->stack_height
[c
->handler_offset
] = 0;
3258 td
->vt_stack_size
[c
->handler_offset
] = 0;
3259 td
->is_bb_start
[c
->handler_offset
] = 1;
3260 td
->is_bb_start
[c
->try_offset
] = 1;
3262 td
->stack_height
[c
->handler_offset
] = 1;
3263 td
->stack_state
[c
->handler_offset
] = (StackInfo
*)g_malloc0(sizeof(StackInfo
));
3264 td
->stack_state
[c
->handler_offset
][0].type
= STACK_TYPE_O
;
3265 td
->stack_state
[c
->handler_offset
][0].klass
= NULL
; /*FIX*/
3267 if (c
->flags
& MONO_EXCEPTION_CLAUSE_FILTER
) {
3268 td
->stack_height
[c
->data
.filter_offset
] = 0;
3269 td
->vt_stack_size
[c
->data
.filter_offset
] = 0;
3270 td
->is_bb_start
[c
->data
.filter_offset
] = 1;
3272 td
->stack_height
[c
->data
.filter_offset
] = 1;
3273 td
->stack_state
[c
->data
.filter_offset
] = (StackInfo
*)g_malloc0(sizeof(StackInfo
));
3274 td
->stack_state
[c
->data
.filter_offset
][0].type
= STACK_TYPE_O
;
3275 td
->stack_state
[c
->data
.filter_offset
][0].klass
= NULL
; /*FIX*/
3278 for (int j
= c
->handler_offset
; j
< c
->handler_offset
+ c
->handler_len
; ++j
) {
3279 if (td
->clause_indexes
[j
] == -1)
3280 td
->clause_indexes
[j
] = i
;
3284 if (td
->gen_sdb_seq_points
&& !inlining
) {
3285 MonoDebugMethodInfo
*minfo
;
3286 get_basic_blocks (td
);
3288 minfo
= mono_debug_lookup_method (method
);
3291 MonoSymSeqPoint
*sps
;
3292 int i
, n_il_offsets
;
3294 mono_debug_get_seq_points (minfo
, NULL
, NULL
, NULL
, &sps
, &n_il_offsets
);
3296 seq_point_locs
= mono_bitset_mem_new (mono_mempool_alloc0 (td
->mempool
, mono_bitset_alloc_size (header
->code_size
, 0)), header
->code_size
, 0);
3297 sym_seq_points
= TRUE
;
3299 for (i
= 0; i
< n_il_offsets
; ++i
) {
3300 if (sps
[i
].il_offset
< header
->code_size
)
3301 mono_bitset_set_fast (seq_point_locs
, sps
[i
].il_offset
);
3305 MonoDebugMethodAsyncInfo
* asyncMethod
= mono_debug_lookup_method_async_debug_info (method
);
3307 for (i
= 0; asyncMethod
!= NULL
&& i
< asyncMethod
->num_awaits
; i
++) {
3308 mono_bitset_set_fast (seq_point_locs
, asyncMethod
->resume_offsets
[i
]);
3309 mono_bitset_set_fast (seq_point_locs
, asyncMethod
->yield_offsets
[i
]);
3311 mono_debug_free_method_async_debug_info (asyncMethod
);
3313 } else if (!method
->wrapper_type
&& !method
->dynamic
&& mono_debug_image_has_debug_info (m_class_get_image (method
->klass
))) {
3314 /* Methods without line number info like auto-generated property accessors */
3315 seq_point_locs
= mono_bitset_new (header
->code_size
, 0);
3316 sym_seq_points
= TRUE
;
3320 if (sym_seq_points
) {
3321 last_seq_point
= interp_add_ins (td
, MINT_SDB_SEQ_POINT
);
3322 last_seq_point
->flags
|= INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY
;
3325 if (mono_debugger_method_has_breakpoint (method
))
3326 interp_add_ins (td
, MINT_BREAKPOINT
);
3329 if (td
->verbose_level
) {
3330 char *tmp
= mono_disasm_code (NULL
, method
, td
->ip
, end
);
3331 char *name
= mono_method_full_name (method
, TRUE
);
3332 g_print ("Method %s, original code:\n", name
);
3333 g_print ("%s\n", tmp
);
3338 if (header
->num_locals
&& header
->init_locals
)
3339 interp_add_ins (td
, MINT_INITLOCALS
);
3341 if (mono_jit_trace_calls
!= NULL
&& mono_trace_eval (method
))
3342 interp_add_ins (td
, MINT_TRACE_ENTER
);
3343 else if (rtm
->prof_flags
& MONO_PROFILER_CALL_INSTRUMENTATION_ENTER
)
3344 interp_add_ins (td
, MINT_PROF_ENTER
);
3346 /* safepoint is required on method entry */
3347 if (mono_threads_are_safepoints_enabled ())
3348 interp_add_ins (td
, MINT_SAFEPOINT
);
3351 arg_locals
= (guint32
*) g_malloc ((!!signature
->hasthis
+ signature
->param_count
) * sizeof (guint32
));
3352 /* Allocate locals to store inlined method args from stack */
3353 for (i
= signature
->param_count
- 1; i
>= 0; i
--) {
3354 local
= create_interp_local (td
, signature
->params
[i
]);
3355 arg_locals
[i
+ !!signature
->hasthis
] = local
;
3356 store_local (td
, local
);
3359 if (signature
->hasthis
) {
3361 * If this is value type, it is passed by address and not by value.
3362 * FIXME We should use MINT_TYPE_P instead of MINT_TYPE_O
3364 MonoType
*type
= mono_get_object_type ();
3365 local
= create_interp_local (td
, type
);
3366 arg_locals
[0] = local
;
3367 store_local (td
, local
);
3370 local_locals
= (guint32
*) g_malloc (header
->num_locals
* sizeof (guint32
));
3371 /* Allocate locals to store inlined method args from stack */
3372 for (i
= 0; i
< header
->num_locals
; i
++)
3373 local_locals
[i
] = create_interp_local (td
, header
->locals
[i
]);
3376 while (td
->ip
< end
) {
3377 g_assert (td
->sp
>= td
->stack
);
3378 g_assert (td
->vt_sp
< 0x10000000);
3379 in_offset
= td
->ip
- header
->code
;
3381 td
->current_il_offset
= in_offset
;
3382 td
->in_start
= td
->ip
;
3383 InterpInst
*prev_last_ins
= td
->last_ins
;
3385 // Inlined method doesn't have clauses or branches
3386 if (!inlining
&& td
->stack_height
[in_offset
] >= 0) {
3387 g_assert (td
->is_bb_start
[in_offset
]);
3388 if (td
->stack_height
[in_offset
] > 0)
3389 memcpy (td
->stack
, td
->stack_state
[in_offset
], td
->stack_height
[in_offset
] * sizeof(td
->stack
[0]));
3390 td
->sp
= td
->stack
+ td
->stack_height
[in_offset
];
3391 td
->vt_sp
= td
->vt_stack_size
[in_offset
];
3394 if (in_offset
== bb
->end
)
3398 int op_size
= mono_opcode_size (td
->ip
, end
);
3399 g_assert (op_size
> 0); /* The BB formation pass must catch all bad ops */
3401 if (td
->verbose_level
> 1)
3402 g_print ("SKIPPING DEAD OP at %x\n", in_offset
);
3408 if (td
->verbose_level
> 1) {
3409 g_print ("IL_%04lx %s %-10s, sp %ld, %s %-12s vt_sp %u (max %u)\n",
3410 td
->ip
- td
->il_code
,
3411 td
->is_bb_start
[td
->ip
- td
->il_code
] == 3 ? "<>" :
3412 td
->is_bb_start
[td
->ip
- td
->il_code
] == 2 ? "< " :
3413 td
->is_bb_start
[td
->ip
- td
->il_code
] == 1 ? " >" : " ",
3414 mono_opcode_name (*td
->ip
), td
->sp
- td
->stack
,
3415 td
->sp
> td
->stack
? stack_type_string
[td
->sp
[-1].type
] : " ",
3416 (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
)) : "",
3417 td
->vt_sp
, td
->max_vt_sp
);
3420 if (sym_seq_points
&& mono_bitset_test_fast (seq_point_locs
, td
->ip
- header
->code
)) {
3421 InterpBasicBlock
*cbb
= td
->offset_to_bb
[td
->ip
- header
->code
];
3425 * Make methods interruptable at the beginning, and at the targets of
3426 * backward branches.
3428 if (in_offset
== 0 || g_slist_length (cbb
->preds
) > 1)
3429 interp_add_ins (td
, MINT_SDB_INTR_LOC
);
3431 last_seq_point
= interp_add_ins (td
, MINT_SDB_SEQ_POINT
);
3434 if (!inlining
&& td
->is_bb_start
[in_offset
]) {
3435 int index
= td
->clause_indexes
[in_offset
];
3437 MonoExceptionClause
*clause
= &header
->clauses
[index
];
3438 if ((clause
->flags
== MONO_EXCEPTION_CLAUSE_FINALLY
||
3439 clause
->flags
== MONO_EXCEPTION_CLAUSE_FAULT
) &&
3440 in_offset
== clause
->handler_offset
)
3441 interp_add_ins (td
, MINT_START_ABORT_PROT
);
3448 emitted_funccall_seq_point
= FALSE
;
3452 SIMPLE_OP(td
, MINT_BREAK
);
3458 int arg_n
= *td
->ip
- CEE_LDARG_0
;
3460 load_arg (td
, arg_n
);
3462 load_local (td
, arg_locals
[arg_n
]);
3470 int loc_n
= *td
->ip
- CEE_LDLOC_0
;
3472 load_local (td
, loc_n
);
3474 load_local (td
, local_locals
[loc_n
]);
3482 int loc_n
= *td
->ip
- CEE_STLOC_0
;
3484 store_local (td
, loc_n
);
3486 store_local (td
, local_locals
[loc_n
]);
3491 int arg_n
= ((guint8
*)td
->ip
)[1];
3493 load_arg (td
, arg_n
);
3495 load_local (td
, arg_locals
[arg_n
]);
3499 case CEE_LDARGA_S
: {
3500 /* NOTE: n includes this */
3501 int n
= ((guint8
*) td
->ip
) [1];
3504 get_arg_type_exact (td
, n
, &mt
);
3505 interp_add_ins (td
, mt
== MINT_TYPE_VT
? MINT_LDARGA_VT
: MINT_LDARGA
);
3506 td
->last_ins
->data
[0] = n
;
3508 int loc_n
= arg_locals
[n
];
3509 interp_add_ins (td
, MINT_LDLOCA_S
);
3510 td
->last_ins
->data
[0] = loc_n
;
3511 td
->locals
[loc_n
].flags
|= INTERP_LOCAL_FLAG_INDIRECT
;
3513 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_MP
);
3518 int arg_n
= ((guint8
*)td
->ip
)[1];
3520 store_arg (td
, arg_n
);
3522 store_local (td
, arg_locals
[arg_n
]);
3527 int loc_n
= ((guint8
*)td
->ip
)[1];
3529 load_local (td
, loc_n
);
3531 load_local (td
, local_locals
[loc_n
]);
3535 case CEE_LDLOCA_S
: {
3536 int loc_n
= ((guint8
*)td
->ip
)[1];
3537 interp_add_ins (td
, MINT_LDLOCA_S
);
3539 loc_n
= local_locals
[loc_n
];
3540 td
->last_ins
->data
[0] = loc_n
;
3541 td
->locals
[loc_n
].flags
|= INTERP_LOCAL_FLAG_INDIRECT
;
3542 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_MP
);
3547 int loc_n
= ((guint8
*)td
->ip
)[1];
3549 store_local (td
, loc_n
);
3551 store_local (td
, local_locals
[loc_n
]);
3556 SIMPLE_OP(td
, MINT_LDNULL
);
3557 PUSH_TYPE(td
, STACK_TYPE_O
, NULL
);
3560 SIMPLE_OP(td
, MINT_LDC_I4_M1
);
3561 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3564 // Only single basic block functions are inlined.
3565 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
&&
3566 td
->sp
> td
->stack
&& td
->sp
[-1].type
== STACK_TYPE_I4
) {
3567 SIMPLE_OP(td
, MINT_CEQ0_I4
);
3570 SIMPLE_OP(td
, MINT_LDC_I4_0
);
3571 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3575 // Only single basic block functions are inlined.
3576 if (td
->ip
- td
->il_code
+ 1 < td
->code_size
&& (inlining
|| !td
->is_bb_start
[td
->ip
+ 1 - td
->il_code
]) &&
3577 (td
->ip
[1] == CEE_ADD
|| td
->ip
[1] == CEE_SUB
) && td
->sp
[-1].type
== STACK_TYPE_I4
) {
3578 interp_add_ins (td
, td
->ip
[1] == CEE_ADD
? MINT_ADD1_I4
: MINT_SUB1_I4
);
3581 SIMPLE_OP(td
, MINT_LDC_I4_1
);
3582 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3592 SIMPLE_OP(td
, (*td
->ip
- CEE_LDC_I4_0
) + MINT_LDC_I4_0
);
3593 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3596 interp_add_ins (td
, MINT_LDC_I4_S
);
3597 td
->last_ins
->data
[0] = ((gint8
*) td
->ip
) [1];
3599 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3602 i32
= read32 (td
->ip
+ 1);
3603 interp_add_ins (td
, MINT_LDC_I4
);
3604 WRITE32_INS (td
->last_ins
, 0, &i32
);
3606 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
3609 gint64 val
= read64 (td
->ip
+ 1);
3610 interp_add_ins (td
, MINT_LDC_I8
);
3611 WRITE64_INS (td
->last_ins
, 0, &val
);
3613 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I8
);
3618 readr4 (td
->ip
+ 1, &val
);
3619 interp_add_ins (td
, MINT_LDC_R4
);
3620 WRITE32_INS (td
->last_ins
, 0, &val
);
3622 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_R4
);
3627 readr8 (td
->ip
+ 1, &val
);
3628 interp_add_ins (td
, MINT_LDC_R8
);
3629 WRITE64_INS (td
->last_ins
, 0, &val
);
3631 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_R8
);
3635 int type
= td
->sp
[-1].type
;
3636 MonoClass
*klass
= td
->sp
[-1].klass
;
3637 if (td
->sp
[-1].type
== STACK_TYPE_VT
) {
3638 gint32 size
= mono_class_value_size (klass
, NULL
);
3640 interp_add_ins (td
, MINT_DUP_VT
);
3641 WRITE32_INS (td
->last_ins
, 0, &size
);
3644 SIMPLE_OP(td
, MINT_DUP
);
3645 PUSH_TYPE(td
, type
, klass
);
3650 SIMPLE_OP(td
, MINT_POP
);
3651 if (td
->sp
[-1].type
== STACK_TYPE_VT
) {
3652 int size
= mono_class_value_size (td
->sp
[-1].klass
, NULL
);
3653 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
3654 interp_add_ins (td
, MINT_VTRESULT
);
3655 td
->last_ins
->data
[0] = 0;
3656 WRITE32_INS (td
->last_ins
, 1, &size
);
3664 if (td
->sp
> td
->stack
)
3665 g_warning ("CEE_JMP: stack must be empty");
3666 token
= read32 (td
->ip
+ 1);
3667 m
= mono_get_method_checked (image
, token
, NULL
, generic_context
, error
);
3668 goto_if_nok (error
, exit
);
3669 interp_add_ins (td
, MINT_JMP
);
3670 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
3671 goto_if_nok (error
, exit
);
3675 case CEE_CALLVIRT
: /* Fall through */
3676 case CEE_CALLI
: /* Fall through */
3678 gboolean need_seq_point
= FALSE
;
3680 if (sym_seq_points
&& !mono_bitset_test_fast (seq_point_locs
, td
->ip
+ 5 - header
->code
))
3681 need_seq_point
= TRUE
;
3683 if (!interp_transform_call (td
, method
, NULL
, domain
, generic_context
, td
->is_bb_start
, constrained_class
, readonly
, error
, TRUE
, save_last_error
))
3686 if (need_seq_point
) {
3687 //check is is a nested call and remove the MONO_INST_NONEMPTY_STACK of the last breakpoint, only for non native methods
3688 if (!(method
->flags
& METHOD_IMPL_ATTRIBUTE_NATIVE
)) {
3689 if (emitted_funccall_seq_point
) {
3691 last_seq_point
->flags
|= INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL
;
3694 emitted_funccall_seq_point
= TRUE
;
3696 last_seq_point
= interp_add_ins (td
, MINT_SDB_SEQ_POINT
);
3697 // This seq point is actually associated with the instruction following the call
3698 last_seq_point
->il_offset
= td
->ip
- header
->code
;
3699 last_seq_point
->flags
= INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK
;
3702 constrained_class
= NULL
;
3704 save_last_error
= FALSE
;
3708 /* Return from inlined method, return value is on top of stack */
3709 if (td
->method
!= method
) {
3715 MonoType
*ult
= mini_type_get_underlying_type (signature
->ret
);
3716 if (ult
->type
!= MONO_TYPE_VOID
) {
3717 CHECK_STACK (td
, 1);
3719 if (mint_type (ult
) == MINT_TYPE_VT
) {
3720 MonoClass
*klass
= mono_class_from_mono_type_internal (ult
);
3721 vt_size
= mono_class_value_size (klass
, NULL
);
3724 if (td
->sp
> td
->stack
) {
3725 mono_error_set_generic_error (error
, "System", "InvalidProgramException", "");
3728 if (td
->vt_sp
!= ALIGN_TO (vt_size
, MINT_VT_ALIGNMENT
))
3729 g_error ("%s: CEE_RET: value type stack: %d vs. %d", mono_method_full_name (td
->method
, TRUE
), td
->vt_sp
, vt_size
);
3731 if (sym_seq_points
) {
3732 last_seq_point
= interp_add_ins (td
, MINT_SDB_SEQ_POINT
);
3733 td
->last_ins
->flags
|= INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT
;
3736 if (mono_jit_trace_calls
!= NULL
&& mono_trace_eval (method
)) {
3737 /* This does the return as well */
3738 if (ult
->type
== MONO_TYPE_VOID
) {
3739 interp_add_ins (td
, MINT_TRACE_EXIT_VOID
);
3742 interp_add_ins (td
, MINT_TRACE_EXIT
);
3744 WRITE32_INS (td
->last_ins
, 0, &vt_size
);
3748 SIMPLE_OP(td
, ult
->type
== MONO_TYPE_VOID
? MINT_RET_VOID
: MINT_RET
);
3750 interp_add_ins (td
, MINT_RET_VT
);
3751 WRITE32_INS (td
->last_ins
, 0, &vt_size
);
3758 int offset
= read32 (td
->ip
+ 1);
3761 handle_branch (td
, MINT_BR_S
, MINT_BR
, 5 + offset
);
3767 int offset
= (gint8
)td
->ip
[1];
3770 handle_branch (td
, MINT_BR_S
, MINT_BR
, 2 + (gint8
)td
->ip
[1]);
3777 one_arg_branch (td
, MINT_BRFALSE_I4
, 5 + read32 (td
->ip
+ 1));
3782 one_arg_branch (td
, MINT_BRFALSE_I4
, 2 + (gint8
)td
->ip
[1]);
3787 one_arg_branch (td
, MINT_BRTRUE_I4
, 5 + read32 (td
->ip
+ 1));
3792 one_arg_branch (td
, MINT_BRTRUE_I4
, 2 + (gint8
)td
->ip
[1]);
3797 two_arg_branch (td
, MINT_BEQ_I4
, 5 + read32 (td
->ip
+ 1));
3802 two_arg_branch (td
, MINT_BEQ_I4
, 2 + (gint8
) td
->ip
[1]);
3807 two_arg_branch (td
, MINT_BGE_I4
, 5 + read32 (td
->ip
+ 1));
3812 two_arg_branch (td
, MINT_BGE_I4
, 2 + (gint8
) td
->ip
[1]);
3817 two_arg_branch (td
, MINT_BGT_I4
, 5 + read32 (td
->ip
+ 1));
3822 two_arg_branch (td
, MINT_BGT_I4
, 2 + (gint8
) td
->ip
[1]);
3827 two_arg_branch (td
, MINT_BLT_I4
, 5 + read32 (td
->ip
+ 1));
3832 two_arg_branch (td
, MINT_BLT_I4
, 2 + (gint8
) td
->ip
[1]);
3837 two_arg_branch (td
, MINT_BLE_I4
, 5 + read32 (td
->ip
+ 1));
3842 two_arg_branch (td
, MINT_BLE_I4
, 2 + (gint8
) td
->ip
[1]);
3847 two_arg_branch (td
, MINT_BNE_UN_I4
, 5 + read32 (td
->ip
+ 1));
3852 two_arg_branch (td
, MINT_BNE_UN_I4
, 2 + (gint8
) td
->ip
[1]);
3857 two_arg_branch (td
, MINT_BGE_UN_I4
, 5 + read32 (td
->ip
+ 1));
3862 two_arg_branch (td
, MINT_BGE_UN_I4
, 2 + (gint8
) td
->ip
[1]);
3867 two_arg_branch (td
, MINT_BGT_UN_I4
, 5 + read32 (td
->ip
+ 1));
3872 two_arg_branch (td
, MINT_BGT_UN_I4
, 2 + (gint8
) td
->ip
[1]);
3877 two_arg_branch (td
, MINT_BLE_UN_I4
, 5 + read32 (td
->ip
+ 1));
3882 two_arg_branch (td
, MINT_BLE_UN_I4
, 2 + (gint8
) td
->ip
[1]);
3887 two_arg_branch (td
, MINT_BLT_UN_I4
, 5 + read32 (td
->ip
+ 1));
3892 two_arg_branch (td
, MINT_BLT_UN_I4
, 2 + (gint8
) td
->ip
[1]);
3898 const unsigned char *next_ip
;
3900 n
= read32 (td
->ip
);
3901 interp_add_ins_explicit (td
, MINT_SWITCH
, MINT_SWITCH_LEN (n
));
3902 WRITE32_INS (td
->last_ins
, 0, &n
);
3904 next_ip
= td
->ip
+ n
* 4;
3906 int stack_height
= td
->sp
- td
->stack
;
3907 for (i
= 0; i
< n
; i
++) {
3908 offset
= read32 (td
->ip
);
3909 target
= next_ip
- td
->il_code
+ offset
;
3912 if (stack_height
> 0 && stack_height
!= td
->stack_height
[target
])
3913 g_warning ("SWITCH with back branch and non-empty stack");
3916 td
->stack_height
[target
] = stack_height
;
3917 td
->vt_stack_size
[target
] = td
->vt_sp
;
3918 if (stack_height
> 0)
3919 td
->stack_state
[target
] = (StackInfo
*)g_memdup (td
->stack
, stack_height
* sizeof (td
->stack
[0]));
3921 WRITE32_INS (td
->last_ins
, 2 + i
* 2, &target
);
3927 CHECK_STACK (td
, 1);
3928 SIMPLE_OP (td
, MINT_LDIND_I1_CHECK
);
3929 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3930 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3933 CHECK_STACK (td
, 1);
3934 SIMPLE_OP (td
, MINT_LDIND_U1_CHECK
);
3935 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3936 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3939 CHECK_STACK (td
, 1);
3940 SIMPLE_OP (td
, MINT_LDIND_I2_CHECK
);
3941 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3942 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3945 CHECK_STACK (td
, 1);
3946 SIMPLE_OP (td
, MINT_LDIND_U2_CHECK
);
3947 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3948 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3951 CHECK_STACK (td
, 1);
3952 SIMPLE_OP (td
, MINT_LDIND_I4_CHECK
);
3953 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3954 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3957 CHECK_STACK (td
, 1);
3958 SIMPLE_OP (td
, MINT_LDIND_U4_CHECK
);
3959 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
3960 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3963 CHECK_STACK (td
, 1);
3964 SIMPLE_OP (td
, MINT_LDIND_I8_CHECK
);
3965 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
3966 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3969 CHECK_STACK (td
, 1);
3970 SIMPLE_OP (td
, MINT_LDIND_REF_CHECK
);
3971 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I
);
3972 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3975 CHECK_STACK (td
, 1);
3976 SIMPLE_OP (td
, MINT_LDIND_R4_CHECK
);
3977 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R4
);
3978 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3981 CHECK_STACK (td
, 1);
3982 SIMPLE_OP (td
, MINT_LDIND_R8_CHECK
);
3983 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R8
);
3984 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3987 CHECK_STACK (td
, 1);
3988 SIMPLE_OP (td
, MINT_LDIND_REF_CHECK
);
3989 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
3990 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_O
);
3993 CHECK_STACK (td
, 2);
3994 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
3995 SIMPLE_OP (td
, MINT_STIND_REF
);
3999 CHECK_STACK (td
, 2);
4000 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4001 SIMPLE_OP (td
, MINT_STIND_I1
);
4005 CHECK_STACK (td
, 2);
4006 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4007 SIMPLE_OP (td
, MINT_STIND_I2
);
4011 CHECK_STACK (td
, 2);
4012 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4013 SIMPLE_OP (td
, MINT_STIND_I4
);
4017 CHECK_STACK (td
, 2);
4018 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4019 SIMPLE_OP (td
, MINT_STIND_I
);
4023 CHECK_STACK (td
, 2);
4024 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4025 SIMPLE_OP (td
, MINT_STIND_I8
);
4029 CHECK_STACK (td
, 2);
4030 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4031 SIMPLE_OP (td
, MINT_STIND_R4
);
4035 CHECK_STACK (td
, 2);
4036 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4037 SIMPLE_OP (td
, MINT_STIND_R8
);
4041 binary_arith_op(td
, MINT_ADD_I4
);
4045 binary_arith_op(td
, MINT_SUB_I4
);
4049 binary_arith_op(td
, MINT_MUL_I4
);
4053 binary_arith_op(td
, MINT_DIV_I4
);
4057 binary_arith_op(td
, MINT_DIV_UN_I4
);
4061 binary_arith_op (td
, MINT_REM_I4
);
4065 binary_arith_op (td
, MINT_REM_UN_I4
);
4069 binary_arith_op (td
, MINT_AND_I4
);
4073 binary_arith_op (td
, MINT_OR_I4
);
4077 binary_arith_op (td
, MINT_XOR_I4
);
4081 shift_op (td
, MINT_SHL_I4
);
4085 shift_op (td
, MINT_SHR_I4
);
4089 shift_op (td
, MINT_SHR_UN_I4
);
4093 unary_arith_op (td
, MINT_NEG_I4
);
4097 unary_arith_op (td
, MINT_NOT_I4
);
4101 CHECK_STACK (td
, 1);
4102 switch (td
->sp
[-1].type
) {
4104 interp_add_ins (td
, MINT_CONV_U1_R4
);
4107 interp_add_ins (td
, MINT_CONV_U1_R8
);
4110 interp_add_ins (td
, MINT_CONV_U1_I4
);
4113 interp_add_ins (td
, MINT_CONV_U1_I8
);
4116 g_assert_not_reached ();
4119 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4122 CHECK_STACK (td
, 1);
4123 switch (td
->sp
[-1].type
) {
4125 interp_add_ins (td
, MINT_CONV_I1_R4
);
4128 interp_add_ins (td
, MINT_CONV_I1_R8
);
4131 interp_add_ins (td
, MINT_CONV_I1_I4
);
4134 interp_add_ins (td
, MINT_CONV_I1_I8
);
4137 g_assert_not_reached ();
4140 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4143 CHECK_STACK (td
, 1);
4144 switch (td
->sp
[-1].type
) {
4146 interp_add_ins (td
, MINT_CONV_U2_R4
);
4149 interp_add_ins (td
, MINT_CONV_U2_R8
);
4152 interp_add_ins (td
, MINT_CONV_U2_I4
);
4155 interp_add_ins (td
, MINT_CONV_U2_I8
);
4158 g_assert_not_reached ();
4161 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4164 CHECK_STACK (td
, 1);
4165 switch (td
->sp
[-1].type
) {
4167 interp_add_ins (td
, MINT_CONV_I2_R4
);
4170 interp_add_ins (td
, MINT_CONV_I2_R8
);
4173 interp_add_ins (td
, MINT_CONV_I2_I4
);
4176 interp_add_ins (td
, MINT_CONV_I2_I8
);
4179 g_assert_not_reached ();
4182 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4185 CHECK_STACK (td
, 1);
4186 switch (td
->sp
[-1].type
) {
4188 #if SIZEOF_VOID_P == 4
4189 interp_add_ins (td
, MINT_CONV_U4_R8
);
4191 interp_add_ins (td
, MINT_CONV_U8_R8
);
4195 #if SIZEOF_VOID_P == 8
4196 interp_add_ins (td
, MINT_CONV_I8_U4
);
4200 #if SIZEOF_VOID_P == 4
4201 interp_add_ins (td
, MINT_CONV_U4_I8
);
4208 g_assert_not_reached ();
4211 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I
);
4214 CHECK_STACK (td
, 1);
4215 switch (td
->sp
[-1].type
) {
4217 #if SIZEOF_VOID_P == 8
4218 interp_add_ins (td
, MINT_CONV_I8_R8
);
4220 interp_add_ins (td
, MINT_CONV_I4_R8
);
4224 #if SIZEOF_VOID_P == 8
4225 interp_add_ins (td
, MINT_CONV_I8_I4
);
4233 #if SIZEOF_VOID_P == 4
4234 interp_add_ins (td
, MINT_CONV_I4_I8
);
4238 g_assert_not_reached ();
4241 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I
);
4244 CHECK_STACK (td
, 1);
4245 switch (td
->sp
[-1].type
) {
4247 interp_add_ins (td
, MINT_CONV_U4_R4
);
4250 interp_add_ins (td
, MINT_CONV_U4_R8
);
4255 interp_add_ins (td
, MINT_CONV_U4_I8
);
4258 #if SIZEOF_VOID_P == 8
4259 interp_add_ins (td
, MINT_CONV_U4_I8
);
4263 g_assert_not_reached ();
4266 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4269 CHECK_STACK (td
, 1);
4270 switch (td
->sp
[-1].type
) {
4272 interp_add_ins (td
, MINT_CONV_I4_R4
);
4275 interp_add_ins (td
, MINT_CONV_I4_R8
);
4280 interp_add_ins (td
, MINT_CONV_I4_I8
);
4283 #if SIZEOF_VOID_P == 8
4284 interp_add_ins (td
, MINT_CONV_I4_I8
);
4288 g_assert_not_reached ();
4291 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
4294 CHECK_STACK (td
, 1);
4295 switch (td
->sp
[-1].type
) {
4297 interp_add_ins (td
, MINT_CONV_I8_R4
);
4300 interp_add_ins (td
, MINT_CONV_I8_R8
);
4302 case STACK_TYPE_I4
: {
4303 if (interp_ins_is_ldc (td
->last_ins
) && (inlining
|| !td
->is_bb_start
[in_offset
])) {
4304 gint64 ct
= interp_get_const_from_ldc_i4 (td
->last_ins
);
4305 interp_clear_ins (td
, td
->last_ins
);
4307 interp_add_ins (td
, MINT_LDC_I8
);
4308 WRITE64_INS (td
->last_ins
, 0, &ct
);
4310 interp_add_ins (td
, MINT_CONV_I8_I4
);
4317 #if SIZEOF_VOID_P == 4
4318 interp_add_ins (td
, MINT_CONV_I8_I4
);
4322 g_assert_not_reached ();
4325 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
4328 CHECK_STACK (td
, 1);
4329 switch (td
->sp
[-1].type
) {
4331 interp_add_ins (td
, MINT_CONV_R4_R8
);
4334 interp_add_ins (td
, MINT_CONV_R4_I8
);
4337 interp_add_ins (td
, MINT_CONV_R4_I4
);
4343 g_assert_not_reached ();
4346 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R4
);
4349 CHECK_STACK (td
, 1);
4350 switch (td
->sp
[-1].type
) {
4352 interp_add_ins (td
, MINT_CONV_R8_I4
);
4355 interp_add_ins (td
, MINT_CONV_R8_I8
);
4358 interp_add_ins (td
, MINT_CONV_R8_R4
);
4363 g_assert_not_reached ();
4366 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R8
);
4369 CHECK_STACK (td
, 1);
4370 switch (td
->sp
[-1].type
) {
4372 if (interp_ins_is_ldc (td
->last_ins
) && (inlining
|| !td
->is_bb_start
[in_offset
])) {
4373 gint64 ct
= (guint32
)interp_get_const_from_ldc_i4 (td
->last_ins
);
4374 interp_clear_ins (td
, td
->last_ins
);
4376 interp_add_ins (td
, MINT_LDC_I8
);
4377 WRITE64_INS (td
->last_ins
, 0, &ct
);
4379 interp_add_ins (td
, MINT_CONV_I8_U4
);
4385 interp_add_ins (td
, MINT_CONV_U8_R4
);
4388 interp_add_ins (td
, MINT_CONV_U8_R8
);
4391 #if SIZEOF_VOID_P == 4
4392 interp_add_ins (td
, MINT_CONV_I8_U4
);
4396 g_assert_not_reached ();
4399 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
4402 CHECK_STACK (td
, 2);
4404 token
= read32 (td
->ip
+ 1);
4405 klass
= mono_class_get_and_inflate_typespec_checked (image
, token
, generic_context
, error
);
4406 goto_if_nok (error
, exit
);
4408 if (m_class_is_valuetype (klass
)) {
4409 int mt
= mint_type (m_class_get_byval_arg (klass
));
4410 interp_add_ins (td
, (mt
== MINT_TYPE_VT
) ? MINT_CPOBJ_VT
: MINT_CPOBJ
);
4411 td
->last_ins
->data
[0] = get_data_item_index(td
, klass
);
4413 interp_add_ins (td
, MINT_LDIND_REF
);
4414 interp_add_ins (td
, MINT_STIND_REF
);
4421 CHECK_STACK (td
, 1);
4423 token
= read32 (td
->ip
+ 1);
4425 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
4426 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
4428 klass
= mono_class_get_and_inflate_typespec_checked (image
, token
, generic_context
, error
);
4429 goto_if_nok (error
, exit
);
4432 interp_emit_ldobj (td
, klass
);
4435 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
4439 token
= mono_metadata_token_index (read32 (td
->ip
+ 1));
4441 if (method
->wrapper_type
== MONO_WRAPPER_NONE
) {
4442 MonoString
*s
= mono_ldstr_checked (domain
, image
, token
, error
);
4443 goto_if_nok (error
, exit
);
4444 /* GC won't scan code stream, but reference is held by metadata
4445 * machinery so we are good here */
4446 interp_add_ins (td
, MINT_LDSTR
);
4447 td
->last_ins
->data
[0] = get_data_item_index (td
, s
);
4449 /* defer allocation to execution-time */
4450 interp_add_ins (td
, MINT_LDSTR_TOKEN
);
4451 td
->last_ins
->data
[0] = get_data_item_index (td
, GUINT_TO_POINTER (token
));
4453 PUSH_TYPE(td
, STACK_TYPE_O
, mono_defaults
.string_class
);
4458 MonoMethodSignature
*csignature
;
4459 guint32 vt_stack_used
= 0;
4460 guint32 vt_res_size
= 0;
4463 token
= read32 (td
->ip
);
4466 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
4467 m
= (MonoMethod
*)mono_method_get_wrapper_data (method
, token
);
4469 m
= mono_get_method_checked (image
, token
, NULL
, generic_context
, error
);
4470 goto_if_nok (error
, exit
);
4473 csignature
= mono_method_signature_internal (m
);
4476 if (!mono_class_init_internal (klass
)) {
4477 mono_error_set_for_class_failure (error
, klass
);
4478 goto_if_nok (error
, exit
);
4481 if (mono_class_get_flags (klass
) & TYPE_ATTRIBUTE_ABSTRACT
) {
4482 char* full_name
= mono_type_get_full_name (klass
);
4483 mono_error_set_member_access (error
, "Cannot create an abstract class: %s", full_name
);
4485 goto_if_nok (error
, exit
);
4488 if (mono_class_is_magic_int (klass
) || mono_class_is_magic_float (klass
)) {
4489 td
->sp
-= csignature
->param_count
;
4490 #if SIZEOF_VOID_P == 8
4491 if (mono_class_is_magic_int (klass
) && td
->sp
[0].type
== STACK_TYPE_I4
)
4492 interp_add_ins (td
, MINT_CONV_I8_I4
);
4493 else if (mono_class_is_magic_float (klass
) && td
->sp
[0].type
== STACK_TYPE_R4
)
4494 interp_add_ins (td
, MINT_CONV_R8_R4
);
4496 interp_add_ins (td
, MINT_NEWOBJ_MAGIC
);
4497 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
4498 goto_if_nok (error
, exit
);
4500 PUSH_TYPE (td
, stack_type
[mint_type (m_class_get_byval_arg (klass
))], klass
);
4502 if (m_class_get_parent (klass
) == mono_defaults
.array_class
) {
4503 interp_add_ins (td
, MINT_NEWOBJ_ARRAY
);
4504 td
->last_ins
->data
[0] = get_data_item_index (td
, m
->klass
);
4505 td
->last_ins
->data
[1] = csignature
->param_count
;
4506 } else if (m_class_get_image (klass
) == mono_defaults
.corlib
&&
4507 !strcmp (m_class_get_name (m
->klass
), "ByReference`1") &&
4508 !strcmp (m
->name
, ".ctor")) {
4509 /* public ByReference(ref T value) */
4510 g_assert (csignature
->hasthis
&& csignature
->param_count
== 1);
4511 interp_add_ins (td
, MINT_INTRINS_BYREFERENCE_CTOR
);
4512 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
4513 } else if (klass
!= mono_defaults
.string_class
&&
4514 !mono_class_is_marshalbyref (klass
) &&
4515 !mono_class_has_finalizer (klass
) &&
4516 !m_class_has_weak_fields (klass
)) {
4517 if (!m_class_is_valuetype (klass
)) {
4518 InterpInst
*newobj_fast
= interp_add_ins (td
, MINT_NEWOBJ_FAST
);
4520 newobj_fast
->data
[1] = csignature
->param_count
;
4522 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, klass
, error
);
4523 goto_if_nok (error
, exit
);
4524 newobj_fast
->data
[2] = get_data_item_index (td
, vtable
);
4526 move_stack (td
, (td
->sp
- td
->stack
) - csignature
->param_count
, 2);
4528 StackInfo
*tmp_sp
= td
->sp
- csignature
->param_count
- 2;
4529 SET_TYPE (tmp_sp
, STACK_TYPE_O
, klass
);
4530 SET_TYPE (tmp_sp
+ 1, STACK_TYPE_O
, klass
);
4532 if ((mono_interp_opt
& INTERP_OPT_INLINE
) && interp_method_check_inlining (td
, m
)) {
4533 MonoMethodHeader
*mheader
= interp_method_get_header (m
, error
);
4534 goto_if_nok (error
, exit
);
4536 if (interp_inline_method (td
, m
, mheader
, error
)) {
4537 newobj_fast
->data
[0] = INLINED_METHOD_FLAG
;
4541 // If inlining failed we need to restore the stack
4542 move_stack (td
, (td
->sp
- td
->stack
) - csignature
->param_count
, -2);
4543 // Set the method to be executed as part of newobj instruction
4544 newobj_fast
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
4546 if (mint_type (m_class_get_byval_arg (klass
)) == MINT_TYPE_VT
)
4547 interp_add_ins (td
, MINT_NEWOBJ_VTST_FAST
);
4549 interp_add_ins (td
, MINT_NEWOBJ_VT_FAST
);
4551 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
4552 td
->last_ins
->data
[1] = csignature
->param_count
;
4554 if (mint_type (m_class_get_byval_arg (klass
)) == MINT_TYPE_VT
) {
4555 td
->last_ins
->data
[2] = mono_class_value_size (klass
, NULL
);
4559 interp_add_ins (td
, MINT_NEWOBJ
);
4560 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
4562 goto_if_nok (error
, exit
);
4563 /* The constructor was not inlined, abort inlining of current method */
4566 td
->sp
-= csignature
->param_count
;
4567 if (mint_type (m_class_get_byval_arg (klass
)) == MINT_TYPE_VT
) {
4568 vt_res_size
= mono_class_value_size (klass
, NULL
);
4569 PUSH_VT (td
, vt_res_size
);
4571 for (i
= 0; i
< csignature
->param_count
; ++i
) {
4572 int mt
= mint_type(csignature
->params
[i
]);
4573 if (mt
== MINT_TYPE_VT
) {
4574 MonoClass
*k
= mono_class_from_mono_type_internal (csignature
->params
[i
]);
4575 gint32 size
= mono_class_value_size (k
, NULL
);
4576 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
4577 vt_stack_used
+= size
;
4580 if (vt_stack_used
!= 0 || vt_res_size
!= 0) {
4581 interp_add_ins (td
, MINT_VTRESULT
);
4582 td
->last_ins
->data
[0] = vt_res_size
;
4583 WRITE32_INS (td
->last_ins
, 1, &vt_stack_used
);
4584 td
->vt_sp
-= vt_stack_used
;
4586 PUSH_TYPE (td
, stack_type
[mint_type (m_class_get_byval_arg (klass
))], klass
);
4592 gboolean isinst_instr
= *td
->ip
== CEE_ISINST
;
4593 CHECK_STACK (td
, 1);
4594 token
= read32 (td
->ip
+ 1);
4595 klass
= mini_get_class (method
, token
, generic_context
);
4596 CHECK_TYPELOAD (klass
);
4597 interp_handle_isinst (td
, klass
, isinst_instr
);
4599 td
->sp
[-1].klass
= klass
;
4603 switch (td
->sp
[-1].type
) {
4607 interp_add_ins (td
, MINT_CONV_R_UN_I8
);
4610 interp_add_ins (td
, MINT_CONV_R_UN_I4
);
4613 g_assert_not_reached ();
4615 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R8
);
4619 CHECK_STACK (td
, 1);
4620 token
= read32 (td
->ip
+ 1);
4622 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
4623 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
4625 klass
= mono_class_get_and_inflate_typespec_checked (image
, token
, generic_context
, error
);
4626 goto_if_nok (error
, exit
);
4629 if (mono_class_is_nullable (klass
)) {
4630 MonoMethod
*target_method
;
4631 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass
)))
4632 target_method
= mono_class_get_method_from_name_checked (klass
, "UnboxExact", 1, 0, error
);
4634 target_method
= mono_class_get_method_from_name_checked (klass
, "Unbox", 1, 0, error
);
4635 goto_if_nok (error
, exit
);
4636 /* td->ip is incremented by interp_transform_call */
4637 if (!interp_transform_call (td
, method
, target_method
, domain
, generic_context
, td
->is_bb_start
, NULL
, FALSE
, error
, FALSE
, FALSE
))
4640 * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type
4641 * We create a local variable in the frame so that we can fetch its address.
4643 int local
= create_interp_local (td
, m_class_get_byval_arg (klass
));
4644 store_local (td
, local
);
4645 interp_add_ins (td
, MINT_LDLOCA_S
);
4646 td
->last_ins
->data
[0] = local
;
4647 td
->locals
[local
].flags
|= INTERP_LOCAL_FLAG_INDIRECT
;
4648 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_MP
);
4650 interp_add_ins (td
, MINT_UNBOX
);
4651 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
4652 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_MP
);
4657 CHECK_STACK (td
, 1);
4658 token
= read32 (td
->ip
+ 1);
4660 klass
= mini_get_class (method
, token
, generic_context
);
4661 CHECK_TYPELOAD (klass
);
4663 if (mini_type_is_reference (m_class_get_byval_arg (klass
))) {
4664 int mt
= mint_type (m_class_get_byval_arg (klass
));
4665 interp_handle_isinst (td
, klass
, FALSE
);
4666 SET_TYPE (td
->sp
- 1, stack_type
[mt
], klass
);
4667 } else if (mono_class_is_nullable (klass
)) {
4668 MonoMethod
*target_method
;
4669 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass
)))
4670 target_method
= mono_class_get_method_from_name_checked (klass
, "UnboxExact", 1, 0, error
);
4672 target_method
= mono_class_get_method_from_name_checked (klass
, "Unbox", 1, 0, error
);
4673 goto_if_nok (error
, exit
);
4674 /* td->ip is incremented by interp_transform_call */
4675 if (!interp_transform_call (td
, method
, target_method
, domain
, generic_context
, td
->is_bb_start
, NULL
, FALSE
, error
, FALSE
, FALSE
))
4678 interp_add_ins (td
, MINT_UNBOX
);
4679 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
4681 interp_emit_ldobj (td
, klass
);
4689 CHECK_STACK (td
, 1);
4690 SIMPLE_OP (td
, MINT_THROW
);
4694 CHECK_STACK (td
, 1);
4695 token
= read32 (td
->ip
+ 1);
4696 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4697 goto_if_nok (error
, exit
);
4698 MonoType
*ftype
= mono_field_get_type_internal (field
);
4699 gboolean is_static
= !!(ftype
->attrs
& FIELD_ATTRIBUTE_STATIC
);
4700 mono_class_init_internal (klass
);
4701 #ifndef DISABLE_REMOTING
4702 if (m_class_get_marshalbyref (klass
) || mono_class_is_contextbound (klass
) || klass
== mono_defaults
.marshalbyrefobject_class
) {
4703 g_assert (!is_static
);
4704 int offset
= m_class_is_valuetype (klass
) ? field
->offset
- MONO_ABI_SIZEOF (MonoObject
) : field
->offset
;
4706 interp_add_ins (td
, MINT_MONO_LDPTR
);
4707 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
4708 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
4709 interp_add_ins (td
, MINT_MONO_LDPTR
);
4710 td
->last_ins
->data
[0] = get_data_item_index (td
, field
);
4711 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
4712 interp_add_ins (td
, MINT_LDC_I4
);
4713 WRITE32_INS (td
->last_ins
, 0, &offset
);
4714 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I4
);
4715 #if SIZEOF_VOID_P == 8
4716 interp_add_ins (td
, MINT_CONV_I8_I4
);
4719 MonoMethod
*wrapper
= mono_marshal_get_ldflda_wrapper (field
->type
);
4720 /* td->ip is incremented by interp_transform_call */
4721 if (!interp_transform_call (td
, method
, wrapper
, domain
, generic_context
, td
->is_bb_start
, NULL
, FALSE
, error
, FALSE
, FALSE
))
4727 interp_add_ins (td
, MINT_POP
);
4728 interp_emit_ldsflda (td
, field
, error
);
4729 goto_if_nok (error
, exit
);
4731 if ((td
->sp
- 1)->type
== STACK_TYPE_O
) {
4732 interp_add_ins (td
, MINT_LDFLDA
);
4734 g_assert ((td
->sp
-1)->type
== STACK_TYPE_MP
);
4735 interp_add_ins (td
, MINT_LDFLDA_UNSAFE
);
4737 td
->last_ins
->data
[0] = m_class_is_valuetype (klass
) ? field
->offset
- MONO_ABI_SIZEOF (MonoObject
) : field
->offset
;
4741 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
4745 CHECK_STACK (td
, 1);
4746 token
= read32 (td
->ip
+ 1);
4747 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4748 goto_if_nok (error
, exit
);
4749 MonoType
*ftype
= mono_field_get_type_internal (field
);
4750 gboolean is_static
= !!(ftype
->attrs
& FIELD_ATTRIBUTE_STATIC
);
4751 mono_class_init_internal (klass
);
4753 MonoClass
*field_klass
= mono_class_from_mono_type_internal (ftype
);
4754 mt
= mint_type (m_class_get_byval_arg (field_klass
));
4755 #ifndef DISABLE_REMOTING
4756 if (m_class_get_marshalbyref (klass
)) {
4757 g_assert (!is_static
);
4758 interp_add_ins (td
, mt
== MINT_TYPE_VT
? MINT_LDRMFLD_VT
: MINT_LDRMFLD
);
4759 td
->last_ins
->data
[0] = get_data_item_index (td
, field
);
4764 interp_add_ins (td
, MINT_POP
);
4765 interp_emit_sfld_access (td
, field
, field_klass
, mt
, TRUE
, error
);
4766 goto_if_nok (error
, exit
);
4768 int opcode
= MINT_LDFLD_I1
+ mt
- MINT_TYPE_I1
;
4769 #ifdef NO_UNALIGNED_ACCESS
4770 if ((mt
== MINT_TYPE_I8
|| mt
== MINT_TYPE_R8
) && field
->offset
% SIZEOF_VOID_P
!= 0)
4771 opcode
= get_unaligned_opcode (opcode
);
4773 interp_add_ins (td
, opcode
);
4774 td
->last_ins
->data
[0] = m_class_is_valuetype (klass
) ? field
->offset
- MONO_ABI_SIZEOF (MonoObject
) : field
->offset
;
4775 if (mt
== MINT_TYPE_VT
) {
4776 int size
= mono_class_value_size (field_klass
, NULL
);
4777 WRITE32_INS (td
->last_ins
, 1, &size
);
4781 if (mt
== MINT_TYPE_VT
) {
4782 int size
= mono_class_value_size (field_klass
, NULL
);
4785 if (td
->sp
[-1].type
== STACK_TYPE_VT
) {
4786 int size
= mono_class_value_size (klass
, NULL
);
4787 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
4788 int field_vt_size
= 0;
4789 if (mt
== MINT_TYPE_VT
) {
4791 * Pop the loaded field from the vtstack (it will still be present
4792 * at the same vtstack address) and we will load it in place of the
4793 * containing valuetype with the second MINT_VTRESULT.
4795 field_vt_size
= mono_class_value_size (field_klass
, NULL
);
4796 field_vt_size
= ALIGN_TO (field_vt_size
, MINT_VT_ALIGNMENT
);
4797 interp_add_ins (td
, MINT_VTRESULT
);
4798 td
->last_ins
->data
[0] = 0;
4799 WRITE32_INS (td
->last_ins
, 1, &field_vt_size
);
4802 interp_add_ins (td
, MINT_VTRESULT
);
4803 td
->last_ins
->data
[0] = field_vt_size
;
4804 WRITE32_INS (td
->last_ins
, 1, &size
);
4807 SET_TYPE (td
->sp
- 1, stack_type
[mt
], field_klass
);
4808 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_ACQ
);
4812 CHECK_STACK (td
, 2);
4813 token
= read32 (td
->ip
+ 1);
4814 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4815 goto_if_nok (error
, exit
);
4816 MonoType
*ftype
= mono_field_get_type_internal (field
);
4817 gboolean is_static
= !!(ftype
->attrs
& FIELD_ATTRIBUTE_STATIC
);
4818 MonoClass
*field_klass
= mono_class_from_mono_type_internal (ftype
);
4819 mono_class_init_internal (klass
);
4820 mt
= mint_type (ftype
);
4822 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4824 #ifndef DISABLE_REMOTING
4825 if (m_class_get_marshalbyref (klass
)) {
4826 g_assert (!is_static
);
4827 interp_add_ins (td
, mt
== MINT_TYPE_VT
? MINT_STRMFLD_VT
: MINT_STRMFLD
);
4828 td
->last_ins
->data
[0] = get_data_item_index (td
, field
);
4833 interp_emit_sfld_access (td
, field
, field_klass
, mt
, FALSE
, error
);
4834 goto_if_nok (error
, exit
);
4836 /* pop the unused object reference */
4837 interp_add_ins (td
, MINT_POP
);
4839 /* the vtable of the field might not be initialized at this point */
4840 mono_class_vtable_checked (domain
, field_klass
, error
);
4841 goto_if_nok (error
, exit
);
4843 int opcode
= MINT_STFLD_I1
+ mt
- MINT_TYPE_I1
;
4844 #ifdef NO_UNALIGNED_ACCESS
4845 if ((mt
== MINT_TYPE_I8
|| mt
== MINT_TYPE_R8
) && field
->offset
% SIZEOF_VOID_P
!= 0)
4846 opcode
= get_unaligned_opcode (opcode
);
4848 interp_add_ins (td
, opcode
);
4849 td
->last_ins
->data
[0] = m_class_is_valuetype (klass
) ? field
->offset
- MONO_ABI_SIZEOF (MonoObject
) : field
->offset
;
4850 if (mt
== MINT_TYPE_VT
) {
4851 /* the vtable of the field might not be initialized at this point */
4852 mono_class_vtable_checked (domain
, field_klass
, error
);
4853 goto_if_nok (error
, exit
);
4855 td
->last_ins
->data
[1] = get_data_item_index (td
, field_klass
);
4859 if (mt
== MINT_TYPE_VT
) {
4860 int size
= mono_class_value_size (field_klass
, NULL
);
4868 token
= read32 (td
->ip
+ 1);
4869 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4870 goto_if_nok (error
, exit
);
4871 interp_emit_ldsflda (td
, field
, error
);
4872 goto_if_nok (error
, exit
);
4874 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_MP
);
4878 token
= read32 (td
->ip
+ 1);
4879 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4880 goto_if_nok (error
, exit
);
4881 MonoType
*ftype
= mono_field_get_type_internal (field
);
4882 mt
= mint_type (ftype
);
4883 klass
= mono_class_from_mono_type_internal (ftype
);
4885 interp_emit_sfld_access (td
, field
, klass
, mt
, TRUE
, error
);
4886 goto_if_nok (error
, exit
);
4888 if (mt
== MINT_TYPE_VT
) {
4889 int size
= mono_class_value_size (klass
, NULL
);
4893 PUSH_TYPE(td
, stack_type
[mt
], klass
);
4897 CHECK_STACK (td
, 1);
4898 token
= read32 (td
->ip
+ 1);
4899 field
= interp_field_from_token (method
, token
, &klass
, generic_context
, error
);
4900 goto_if_nok (error
, exit
);
4901 MonoType
*ftype
= mono_field_get_type_internal (field
);
4902 mt
= mint_type (ftype
);
4904 /* the vtable of the field might not be initialized at this point */
4905 MonoClass
*fld_klass
= mono_class_from_mono_type_internal (ftype
);
4906 mono_class_vtable_checked (domain
, fld_klass
, error
);
4907 goto_if_nok (error
, exit
);
4909 interp_emit_sfld_access (td
, field
, fld_klass
, mt
, FALSE
, error
);
4910 goto_if_nok (error
, exit
);
4912 if (mt
== MINT_TYPE_VT
) {
4913 int size
= mono_class_value_size (fld_klass
, NULL
);
4921 token
= read32 (td
->ip
+ 1);
4923 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
4924 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
4926 klass
= mini_get_class (method
, token
, generic_context
);
4927 CHECK_TYPELOAD (klass
);
4929 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
4931 interp_emit_stobj (td
, klass
);
4936 case CEE_CONV_OVF_I_UN
:
4937 case CEE_CONV_OVF_U_UN
:
4938 CHECK_STACK (td
, 1);
4939 switch (td
->sp
[-1].type
) {
4941 #if SIZEOF_VOID_P == 8
4942 interp_add_ins (td
, MINT_CONV_OVF_I8_UN_R8
);
4944 interp_add_ins (td
, MINT_CONV_OVF_I4_UN_R8
);
4948 #if SIZEOF_VOID_P == 4
4949 interp_add_ins (td
, MINT_CONV_OVF_I4_UN_I8
);
4953 #if SIZEOF_VOID_P == 8
4954 interp_add_ins (td
, MINT_CONV_I8_U4
);
4955 #elif SIZEOF_VOID_P == 4
4956 if (*td
->ip
== CEE_CONV_OVF_I_UN
)
4957 interp_add_ins (td
, MINT_CONV_OVF_I4_U4
);
4961 g_assert_not_reached ();
4964 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I
);
4967 case CEE_CONV_OVF_I8_UN
:
4968 case CEE_CONV_OVF_U8_UN
:
4969 CHECK_STACK (td
, 1);
4970 switch (td
->sp
[-1].type
) {
4972 interp_add_ins (td
, MINT_CONV_OVF_I8_UN_R8
);
4975 if (*td
->ip
== CEE_CONV_OVF_I8_UN
)
4976 interp_add_ins (td
, MINT_CONV_OVF_I8_U8
);
4979 interp_add_ins (td
, MINT_CONV_I8_U4
);
4982 interp_add_ins (td
, MINT_CONV_OVF_I8_UN_R4
);
4985 g_assert_not_reached ();
4988 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
4993 CHECK_STACK (td
, 1);
4994 token
= read32 (td
->ip
+ 1);
4995 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
4996 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
4998 klass
= mini_get_class (method
, token
, generic_context
);
4999 CHECK_TYPELOAD (klass
);
5001 if (mono_class_is_nullable (klass
)) {
5002 MonoMethod
*target_method
= mono_class_get_method_from_name_checked (klass
, "Box", 1, 0, error
);
5003 goto_if_nok (error
, exit
);
5004 /* td->ip is incremented by interp_transform_call */
5005 if (!interp_transform_call (td
, method
, target_method
, domain
, generic_context
, td
->is_bb_start
, NULL
, FALSE
, error
, FALSE
, FALSE
))
5007 } else if (!m_class_is_valuetype (klass
)) {
5008 /* already boxed, do nothing. */
5011 if (G_UNLIKELY (m_class_is_byreflike (klass
))) {
5012 mono_error_set_bad_image (error
, image
, "Cannot box IsByRefLike type '%s.%s'", m_class_get_name_space (klass
), m_class_get_name (klass
));
5015 if (mint_type (m_class_get_byval_arg (klass
)) == MINT_TYPE_VT
) {
5016 size
= mono_class_value_size (klass
, NULL
);
5017 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
5019 } else if (td
->sp
[-1].type
== STACK_TYPE_R8
&& m_class_get_byval_arg (klass
)->type
== MONO_TYPE_R4
) {
5020 interp_add_ins (td
, MINT_CONV_R4_R8
);
5022 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, klass
, error
);
5023 goto_if_nok (error
, exit
);
5025 if (mint_type (m_class_get_byval_arg (klass
)) == MINT_TYPE_VT
)
5026 interp_add_ins (td
, MINT_BOX_VT
);
5028 interp_add_ins (td
, MINT_BOX
);
5029 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
5030 td
->last_ins
->data
[1] = 0;
5031 SET_TYPE(td
->sp
- 1, STACK_TYPE_O
, klass
);
5038 CHECK_STACK (td
, 1);
5039 token
= read32 (td
->ip
+ 1);
5041 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
5042 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
5044 klass
= mini_get_class (method
, token
, generic_context
);
5045 CHECK_TYPELOAD (klass
);
5047 MonoClass
*array_class
= mono_class_create_array (klass
, 1);
5048 MonoVTable
*vtable
= mono_class_vtable_checked (domain
, array_class
, error
);
5049 goto_if_nok (error
, exit
);
5051 unsigned char lentype
= (td
->sp
- 1)->type
;
5052 if (lentype
== STACK_TYPE_I8
) {
5053 /* mimic mini behaviour */
5054 interp_add_ins (td
, MINT_CONV_OVF_U4_I8
);
5056 g_assert (lentype
== STACK_TYPE_I4
);
5057 interp_add_ins (td
, MINT_CONV_OVF_U4_I4
);
5059 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
5060 interp_add_ins (td
, MINT_NEWARR
);
5061 td
->last_ins
->data
[0] = get_data_item_index (td
, vtable
);
5062 SET_TYPE (td
->sp
- 1, STACK_TYPE_O
, array_class
);
5067 CHECK_STACK (td
, 1);
5068 SIMPLE_OP (td
, MINT_LDLEN
);
5069 #ifdef MONO_BIG_ARRAYS
5070 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I8
);
5072 SET_SIMPLE_TYPE (td
->sp
- 1, STACK_TYPE_I4
);
5077 CHECK_STACK (td
, 2);
5079 token
= read32 (td
->ip
+ 1);
5081 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
5082 klass
= (MonoClass
*) mono_method_get_wrapper_data (method
, token
);
5084 klass
= mini_get_class (method
, token
, generic_context
);
5086 CHECK_TYPELOAD (klass
);
5088 if (!m_class_is_valuetype (klass
) && method
->wrapper_type
== MONO_WRAPPER_NONE
&& !readonly
) {
5090 * Check the class for failures before the type check, which can
5091 * throw other exceptions.
5093 mono_class_setup_vtable (klass
);
5094 CHECK_TYPELOAD (klass
);
5095 interp_add_ins (td
, MINT_LDELEMA_TC
);
5096 td
->last_ins
->data
[0] = 1;
5097 td
->last_ins
->data
[1] = get_data_item_index (td
, klass
);
5099 interp_add_ins (td
, MINT_LDELEMA1
);
5100 mono_class_init_internal (klass
);
5101 size
= mono_class_array_element_size (klass
);
5102 WRITE32_INS (td
->last_ins
, 0, &size
);
5109 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
5113 CHECK_STACK (td
, 2);
5115 SIMPLE_OP (td
, MINT_LDELEM_I1
);
5117 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5120 CHECK_STACK (td
, 2);
5122 SIMPLE_OP (td
, MINT_LDELEM_U1
);
5124 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5127 CHECK_STACK (td
, 2);
5129 SIMPLE_OP (td
, MINT_LDELEM_I2
);
5131 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5134 CHECK_STACK (td
, 2);
5136 SIMPLE_OP (td
, MINT_LDELEM_U2
);
5138 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5141 CHECK_STACK (td
, 2);
5143 SIMPLE_OP (td
, MINT_LDELEM_I4
);
5145 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5148 CHECK_STACK (td
, 2);
5150 SIMPLE_OP (td
, MINT_LDELEM_U4
);
5152 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5155 CHECK_STACK (td
, 2);
5157 SIMPLE_OP (td
, MINT_LDELEM_I8
);
5159 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
5162 CHECK_STACK (td
, 2);
5164 SIMPLE_OP (td
, MINT_LDELEM_I
);
5166 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I
);
5169 CHECK_STACK (td
, 2);
5171 SIMPLE_OP (td
, MINT_LDELEM_R4
);
5173 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R4
);
5176 CHECK_STACK (td
, 2);
5178 SIMPLE_OP (td
, MINT_LDELEM_R8
);
5180 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R8
);
5182 case CEE_LDELEM_REF
:
5183 CHECK_STACK (td
, 2);
5185 SIMPLE_OP (td
, MINT_LDELEM_REF
);
5187 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_O
);
5190 CHECK_STACK (td
, 2);
5191 token
= read32 (td
->ip
+ 1);
5192 klass
= mini_get_class (method
, token
, generic_context
);
5193 CHECK_TYPELOAD (klass
);
5194 switch (mint_type (m_class_get_byval_arg (klass
))) {
5197 SIMPLE_OP (td
, MINT_LDELEM_I1
);
5199 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5203 SIMPLE_OP (td
, MINT_LDELEM_U1
);
5205 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5209 SIMPLE_OP (td
, MINT_LDELEM_U2
);
5211 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5215 SIMPLE_OP (td
, MINT_LDELEM_I2
);
5217 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5221 SIMPLE_OP (td
, MINT_LDELEM_I4
);
5223 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5227 SIMPLE_OP (td
, MINT_LDELEM_I8
);
5229 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
5233 SIMPLE_OP (td
, MINT_LDELEM_R4
);
5235 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R4
);
5239 SIMPLE_OP (td
, MINT_LDELEM_R8
);
5241 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_R8
);
5245 SIMPLE_OP (td
, MINT_LDELEM_REF
);
5247 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_O
);
5249 case MINT_TYPE_VT
: {
5250 int size
= mono_class_value_size (klass
, NULL
);
5252 SIMPLE_OP (td
, MINT_LDELEM_VT
);
5253 WRITE32_INS (td
->last_ins
, 0, &size
);
5255 SET_TYPE (td
->sp
- 1, STACK_TYPE_VT
, klass
);
5260 GString
*res
= g_string_new ("");
5261 mono_type_get_desc (res
, m_class_get_byval_arg (klass
), TRUE
);
5262 g_print ("LDELEM: %s -> %d (%s)\n", m_class_get_name (klass
), mint_type (m_class_get_byval_arg (klass
)), res
->str
);
5263 g_string_free (res
, TRUE
);
5271 CHECK_STACK (td
, 3);
5273 SIMPLE_OP (td
, MINT_STELEM_I
);
5277 CHECK_STACK (td
, 3);
5279 SIMPLE_OP (td
, MINT_STELEM_I1
);
5283 CHECK_STACK (td
, 3);
5285 SIMPLE_OP (td
, MINT_STELEM_I2
);
5289 CHECK_STACK (td
, 3);
5291 SIMPLE_OP (td
, MINT_STELEM_I4
);
5295 CHECK_STACK (td
, 3);
5297 SIMPLE_OP (td
, MINT_STELEM_I8
);
5301 CHECK_STACK (td
, 3);
5303 SIMPLE_OP (td
, MINT_STELEM_R4
);
5307 CHECK_STACK (td
, 3);
5309 SIMPLE_OP (td
, MINT_STELEM_R8
);
5312 case CEE_STELEM_REF
:
5313 CHECK_STACK (td
, 3);
5315 SIMPLE_OP (td
, MINT_STELEM_REF
);
5319 CHECK_STACK (td
, 3);
5321 token
= read32 (td
->ip
+ 1);
5322 klass
= mini_get_class (method
, token
, generic_context
);
5323 CHECK_TYPELOAD (klass
);
5324 switch (mint_type (m_class_get_byval_arg (klass
))) {
5326 SIMPLE_OP (td
, MINT_STELEM_I1
);
5329 SIMPLE_OP (td
, MINT_STELEM_U1
);
5332 SIMPLE_OP (td
, MINT_STELEM_I2
);
5335 SIMPLE_OP (td
, MINT_STELEM_U2
);
5338 SIMPLE_OP (td
, MINT_STELEM_I4
);
5341 SIMPLE_OP (td
, MINT_STELEM_I8
);
5344 SIMPLE_OP (td
, MINT_STELEM_R4
);
5347 SIMPLE_OP (td
, MINT_STELEM_R8
);
5350 SIMPLE_OP (td
, MINT_STELEM_REF
);
5352 case MINT_TYPE_VT
: {
5353 int size
= mono_class_value_size (klass
, NULL
);
5354 SIMPLE_OP (td
, MINT_STELEM_VT
);
5355 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
5356 WRITE32_INS (td
->last_ins
, 1, &size
);
5361 GString
*res
= g_string_new ("");
5362 mono_type_get_desc (res
, m_class_get_byval_arg (klass
), TRUE
);
5363 g_print ("STELEM: %s -> %d (%s)\n", m_class_get_name (klass
), mint_type (m_class_get_byval_arg (klass
)), res
->str
);
5364 g_string_free (res
, TRUE
);
5373 case CEE_CONV_OVF_U1
:
5375 case CEE_CONV_OVF_I8
:
5377 #if SIZEOF_VOID_P == 8
5378 case CEE_CONV_OVF_U
:
5382 CHECK_STACK (td
, 1);
5383 SIMPLE_OP (td
, MINT_CKFINITE
);
5386 CHECK_STACK (td
, 1);
5388 token
= read32 (td
->ip
+ 1);
5389 klass
= mini_get_class (method
, token
, generic_context
);
5390 CHECK_TYPELOAD (klass
);
5392 interp_add_ins (td
, MINT_MKREFANY
);
5393 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
5396 PUSH_VT (td
, sizeof (MonoTypedRef
));
5397 SET_TYPE(td
->sp
- 1, STACK_TYPE_VT
, mono_defaults
.typed_reference_class
);
5399 case CEE_REFANYVAL
: {
5400 CHECK_STACK (td
, 1);
5402 token
= read32 (td
->ip
+ 1);
5403 klass
= mini_get_class (method
, token
, generic_context
);
5404 CHECK_TYPELOAD (klass
);
5406 interp_add_ins (td
, MINT_REFANYVAL
);
5407 td
->last_ins
->data
[0] = get_data_item_index (td
, klass
);
5409 POP_VT (td
, sizeof (MonoTypedRef
));
5410 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
5415 case CEE_CONV_OVF_I1
:
5416 case CEE_CONV_OVF_I1_UN
: {
5417 gboolean is_un
= *td
->ip
== CEE_CONV_OVF_I1_UN
;
5418 CHECK_STACK (td
, 1);
5419 switch (td
->sp
[-1].type
) {
5421 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I1_UN_R8
: MINT_CONV_OVF_I1_R8
);
5424 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I1_U4
: MINT_CONV_OVF_I1_I4
);
5427 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I1_U8
: MINT_CONV_OVF_I1_I8
);
5430 g_assert_not_reached ();
5433 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5436 case CEE_CONV_OVF_U1
:
5437 case CEE_CONV_OVF_U1_UN
:
5438 CHECK_STACK (td
, 1);
5439 switch (td
->sp
[-1].type
) {
5441 interp_add_ins (td
, MINT_CONV_OVF_U1_R8
);
5444 interp_add_ins (td
, MINT_CONV_OVF_U1_I4
);
5447 interp_add_ins (td
, MINT_CONV_OVF_U1_I8
);
5450 g_assert_not_reached ();
5453 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5455 case CEE_CONV_OVF_I2
:
5456 case CEE_CONV_OVF_I2_UN
: {
5457 gboolean is_un
= *td
->ip
== CEE_CONV_OVF_I2_UN
;
5458 CHECK_STACK (td
, 1);
5459 switch (td
->sp
[-1].type
) {
5461 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I2_UN_R8
: MINT_CONV_OVF_I2_R8
);
5464 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I2_U4
: MINT_CONV_OVF_I2_I4
);
5467 interp_add_ins (td
, is_un
? MINT_CONV_OVF_I2_U8
: MINT_CONV_OVF_I2_I8
);
5470 g_assert_not_reached ();
5473 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5476 case CEE_CONV_OVF_U2_UN
:
5477 case CEE_CONV_OVF_U2
:
5478 CHECK_STACK (td
, 1);
5479 switch (td
->sp
[-1].type
) {
5481 interp_add_ins (td
, MINT_CONV_OVF_U2_R8
);
5484 interp_add_ins (td
, MINT_CONV_OVF_U2_I4
);
5487 interp_add_ins (td
, MINT_CONV_OVF_U2_I8
);
5490 g_assert_not_reached ();
5493 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5495 #if SIZEOF_VOID_P == 4
5496 case CEE_CONV_OVF_I
:
5498 case CEE_CONV_OVF_I4
:
5499 case CEE_CONV_OVF_I4_UN
:
5500 CHECK_STACK (td
, 1);
5501 switch (td
->sp
[-1].type
) {
5503 interp_add_ins (td
, MINT_CONV_OVF_I4_R4
);
5506 interp_add_ins (td
, MINT_CONV_OVF_I4_R8
);
5509 if (*td
->ip
== CEE_CONV_OVF_I4_UN
)
5510 interp_add_ins (td
, MINT_CONV_OVF_I4_U4
);
5513 if (*td
->ip
== CEE_CONV_OVF_I4_UN
)
5514 interp_add_ins (td
, MINT_CONV_OVF_I4_U8
);
5516 interp_add_ins (td
, MINT_CONV_OVF_I4_I8
);
5519 g_assert_not_reached ();
5522 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5524 #if SIZEOF_VOID_P == 4
5525 case CEE_CONV_OVF_U
:
5527 case CEE_CONV_OVF_U4
:
5528 case CEE_CONV_OVF_U4_UN
:
5529 CHECK_STACK (td
, 1);
5530 switch (td
->sp
[-1].type
) {
5532 interp_add_ins (td
, MINT_CONV_OVF_U4_R4
);
5535 interp_add_ins (td
, MINT_CONV_OVF_U4_R8
);
5538 if (*td
->ip
!= CEE_CONV_OVF_U4_UN
)
5539 interp_add_ins (td
, MINT_CONV_OVF_U4_I4
);
5542 interp_add_ins (td
, MINT_CONV_OVF_U4_I8
);
5545 interp_add_ins (td
, MINT_CONV_OVF_U4_P
);
5548 g_assert_not_reached ();
5551 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5553 #if SIZEOF_VOID_P == 8
5554 case CEE_CONV_OVF_I
:
5556 case CEE_CONV_OVF_I8
:
5557 CHECK_STACK (td
, 1);
5558 switch (td
->sp
[-1].type
) {
5560 interp_add_ins (td
, MINT_CONV_OVF_I8_R4
);
5563 interp_add_ins (td
, MINT_CONV_OVF_I8_R8
);
5566 interp_add_ins (td
, MINT_CONV_I8_I4
);
5571 g_assert_not_reached ();
5574 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
5576 #if SIZEOF_VOID_P == 8
5577 case CEE_CONV_OVF_U
:
5579 case CEE_CONV_OVF_U8
:
5580 CHECK_STACK (td
, 1);
5581 switch (td
->sp
[-1].type
) {
5583 interp_add_ins (td
, MINT_CONV_OVF_U8_R4
);
5586 interp_add_ins (td
, MINT_CONV_OVF_U8_R8
);
5589 interp_add_ins (td
, MINT_CONV_OVF_U8_I4
);
5592 interp_add_ins (td
, MINT_CONV_OVF_U8_I8
);
5595 g_assert_not_reached ();
5598 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I8
);
5603 token
= read32 (td
->ip
+ 1);
5604 if (method
->wrapper_type
== MONO_WRAPPER_DYNAMIC_METHOD
|| method
->wrapper_type
== MONO_WRAPPER_SYNCHRONIZED
) {
5605 handle
= mono_method_get_wrapper_data (method
, token
);
5606 klass
= (MonoClass
*) mono_method_get_wrapper_data (method
, token
+ 1);
5607 if (klass
== mono_defaults
.typehandle_class
)
5608 handle
= m_class_get_byval_arg ((MonoClass
*) handle
);
5610 if (generic_context
) {
5611 handle
= mono_class_inflate_generic_type_checked ((MonoType
*)handle
, generic_context
, error
);
5612 goto_if_nok (error
, exit
);
5615 handle
= mono_ldtoken_checked (image
, token
, &klass
, generic_context
, error
);
5616 goto_if_nok (error
, exit
);
5618 mono_class_init_internal (klass
);
5619 mt
= mint_type (m_class_get_byval_arg (klass
));
5620 g_assert (mt
== MINT_TYPE_VT
);
5621 size
= mono_class_value_size (klass
, NULL
);
5622 g_assert (size
== sizeof(gpointer
));
5624 const unsigned char *next_ip
= td
->ip
+ 5;
5625 MonoMethod
*cmethod
;
5626 if (next_ip
< end
&&
5627 (inlining
|| !td
->is_bb_start
[next_ip
- td
->il_code
]) &&
5628 (*next_ip
== CEE_CALL
|| *next_ip
== CEE_CALLVIRT
) &&
5629 (cmethod
= mono_get_method_checked (image
, read32 (next_ip
+ 1), NULL
, generic_context
, error
)) &&
5630 (cmethod
->klass
== mono_defaults
.systemtype_class
) &&
5631 (strcmp (cmethod
->name
, "GetTypeFromHandle") == 0)) {
5632 interp_add_ins (td
, MINT_MONO_LDPTR
);
5633 gpointer systype
= mono_type_get_object_checked (domain
, (MonoType
*)handle
, error
);
5634 goto_if_nok (error
, exit
);
5635 td
->last_ins
->data
[0] = get_data_item_index (td
, systype
);
5636 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_MP
);
5637 td
->ip
= next_ip
+ 5;
5639 PUSH_VT (td
, sizeof(gpointer
));
5640 interp_add_ins (td
, MINT_LDTOKEN
);
5641 td
->last_ins
->data
[0] = get_data_item_index (td
, handle
);
5642 PUSH_TYPE (td
, stack_type
[mt
], klass
);
5649 binary_arith_op(td
, MINT_ADD_OVF_I4
);
5652 case CEE_ADD_OVF_UN
:
5653 binary_arith_op(td
, MINT_ADD_OVF_UN_I4
);
5657 binary_arith_op(td
, MINT_MUL_OVF_I4
);
5660 case CEE_MUL_OVF_UN
:
5661 binary_arith_op(td
, MINT_MUL_OVF_UN_I4
);
5665 binary_arith_op(td
, MINT_SUB_OVF_I4
);
5668 case CEE_SUB_OVF_UN
:
5669 binary_arith_op(td
, MINT_SUB_OVF_UN_I4
);
5672 case CEE_ENDFINALLY
: {
5673 g_assert (td
->clause_indexes
[in_offset
] != -1);
5675 SIMPLE_OP (td
, MINT_ENDFINALLY
);
5676 td
->last_ins
->data
[0] = td
->clause_indexes
[in_offset
];
5677 // next instructions, if they exist, are always part of new bb
5678 // endfinally can be the last instruction in a function.
5679 // functions with clauses/endfinally are never inlined.
5680 // is_bb_start is not valid while inlining.
5681 g_assert (!inlining
);
5682 if (td
->ip
- td
->il_code
< td
->code_size
)
5683 td
->is_bb_start
[td
->ip
- header
->code
] = 1;
5690 if (*td
->ip
== CEE_LEAVE
)
5691 offset
= 5 + read32 (td
->ip
+ 1);
5693 offset
= 2 + (gint8
)td
->ip
[1];
5696 if (td
->clause_indexes
[in_offset
] != -1) {
5697 /* LEAVE instructions in catch clauses need to check for abort exceptions */
5698 handle_branch (td
, MINT_LEAVE_S_CHECK
, MINT_LEAVE_CHECK
, offset
);
5700 handle_branch (td
, MINT_LEAVE_S
, MINT_LEAVE
, offset
);
5703 if (*td
->ip
== CEE_LEAVE
)
5709 case MONO_CUSTOM_PREFIX
:
5712 case CEE_MONO_RETHROW
:
5713 CHECK_STACK (td
, 1);
5714 SIMPLE_OP (td
, MINT_MONO_RETHROW
);
5718 case CEE_MONO_LD_DELEGATE_METHOD_PTR
:
5721 interp_add_ins (td
, MINT_LD_DELEGATE_METHOD_PTR
);
5722 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
5724 case CEE_MONO_CALLI_EXTRA_ARG
:
5725 /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
5726 interp_add_ins (td
, MINT_POP1
);
5728 if (!interp_transform_call (td
, method
, NULL
, domain
, generic_context
, td
->is_bb_start
, NULL
, FALSE
, error
, FALSE
, FALSE
))
5731 case CEE_MONO_JIT_ICALL_ADDR
: {
5732 const guint32 token
= read32 (td
->ip
+ 1);
5734 const gconstpointer func
= mono_find_jit_icall_info ((MonoJitICallId
)token
)->func
;
5736 interp_add_ins (td
, MINT_LDFTN
);
5737 td
->last_ins
->data
[0] = get_data_item_index (td
, (gpointer
)func
);
5738 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
5741 case CEE_MONO_ICALL
: {
5742 MonoJitICallId
const jit_icall_id
= (MonoJitICallId
)read32 (td
->ip
+ 1);
5743 MonoJitICallInfo
const * const info
= mono_find_jit_icall_info (jit_icall_id
);
5746 CHECK_STACK (td
, info
->sig
->param_count
);
5747 if (jit_icall_id
== MONO_JIT_ICALL_mono_threads_attach_coop
) {
5748 rtm
->needs_thread_attach
= 1;
5750 /* attach needs two arguments, and has one return value: leave one element on the stack */
5751 interp_add_ins (td
, MINT_POP
);
5752 } else if (jit_icall_id
== MONO_JIT_ICALL_mono_threads_detach_coop
) {
5753 g_assert (rtm
->needs_thread_attach
);
5755 /* detach consumes two arguments, and no return value: drop both of them */
5756 interp_add_ins (td
, MINT_POP
);
5757 interp_add_ins (td
, MINT_POP
);
5759 int const icall_op
= interp_icall_op_for_sig (info
->sig
);
5760 g_assert (icall_op
!= -1);
5762 interp_add_ins (td
, icall_op
);
5763 // hash here is overkill
5764 td
->last_ins
->data
[0] = get_data_item_index (td
, (gpointer
)info
->func
);
5766 td
->sp
-= info
->sig
->param_count
;
5768 if (!MONO_TYPE_IS_VOID (info
->sig
->ret
)) {
5769 int mt
= mint_type (info
->sig
->ret
);
5770 PUSH_SIMPLE_TYPE(td
, stack_type
[mt
]);
5774 case CEE_MONO_VTADDR
: {
5776 CHECK_STACK (td
, 1);
5777 if (method
->wrapper_type
== MONO_WRAPPER_MANAGED_TO_NATIVE
)
5778 size
= mono_class_native_size(td
->sp
[-1].klass
, NULL
);
5780 size
= mono_class_value_size(td
->sp
[-1].klass
, NULL
);
5781 size
= ALIGN_TO (size
, MINT_VT_ALIGNMENT
);
5782 interp_add_ins (td
, MINT_VTRESULT
);
5783 td
->last_ins
->data
[0] = 0;
5784 WRITE32_INS (td
->last_ins
, 1, &size
);
5787 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
5790 case CEE_MONO_LDPTR
:
5791 case CEE_MONO_CLASSCONST
:
5792 token
= read32 (td
->ip
+ 1);
5794 interp_add_ins (td
, MINT_MONO_LDPTR
);
5795 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_method_get_wrapper_data (method
, token
));
5796 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
5798 case CEE_MONO_OBJADDR
:
5799 CHECK_STACK (td
, 1);
5801 td
->sp
[-1].type
= STACK_TYPE_MP
;
5804 case CEE_MONO_NEWOBJ
:
5805 token
= read32 (td
->ip
+ 1);
5807 interp_add_ins (td
, MINT_MONO_NEWOBJ
);
5808 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_method_get_wrapper_data (method
, token
));
5809 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_O
);
5811 case CEE_MONO_RETOBJ
:
5812 CHECK_STACK (td
, 1);
5813 token
= read32 (td
->ip
+ 1);
5815 interp_add_ins (td
, MINT_MONO_RETOBJ
);
5818 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
5820 /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/
5822 if (td
->sp
> td
->stack
)
5823 g_warning ("CEE_MONO_RETOBJ: more values on stack: %d", td
->sp
-td
->stack
);
5825 case CEE_MONO_LDNATIVEOBJ
:
5826 token
= read32 (td
->ip
+ 1);
5828 klass
= (MonoClass
*)mono_method_get_wrapper_data (method
, token
);
5829 g_assert(m_class_is_valuetype (klass
));
5830 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
5832 case CEE_MONO_TLS
: {
5833 gint32 key
= read32 (td
->ip
+ 1);
5835 g_assertf (key
== TLS_KEY_SGEN_THREAD_INFO
, "%d", key
);
5836 interp_add_ins (td
, MINT_MONO_SGEN_THREAD_INFO
);
5837 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_MP
);
5840 case CEE_MONO_ATOMIC_STORE_I4
:
5841 CHECK_STACK (td
, 2);
5842 SIMPLE_OP (td
, MINT_MONO_ATOMIC_STORE_I4
);
5846 case CEE_MONO_SAVE_LMF
:
5847 case CEE_MONO_RESTORE_LMF
:
5848 case CEE_MONO_NOT_TAKEN
:
5851 case CEE_MONO_LDPTR_INT_REQ_FLAG
:
5852 interp_add_ins (td
, MINT_MONO_LDPTR
);
5853 td
->last_ins
->data
[0] = get_data_item_index (td
, &mono_thread_interruption_request_flag
);
5854 PUSH_TYPE (td
, STACK_TYPE_MP
, NULL
);
5857 case CEE_MONO_MEMORY_BARRIER
:
5858 interp_add_ins (td
, MINT_MONO_MEMORY_BARRIER
);
5861 case CEE_MONO_LDDOMAIN
:
5862 interp_add_ins (td
, MINT_MONO_LDDOMAIN
);
5863 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_I
);
5866 case CEE_MONO_SAVE_LAST_ERROR
:
5867 save_last_error
= TRUE
;
5871 g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td
->ip
, td
->ip
-header
->code
);
5881 case CEE_PREFIXREF
: ves_abort(); break;
5884 * Note: Exceptions thrown when executing a prefixed opcode need
5885 * to take into account the number of prefix bytes (usually the
5886 * throw point is just (ip - n_prefix_bytes).
5892 interp_add_ins (td
, MINT_ARGLIST
);
5893 PUSH_VT (td
, SIZEOF_VOID_P
);
5894 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_VT
);
5899 if (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
) {
5900 interp_add_ins (td
, MINT_CEQ_I4
+ STACK_TYPE_I
- STACK_TYPE_I4
);
5902 if (td
->sp
[-1].type
== STACK_TYPE_R4
&& td
->sp
[-2].type
== STACK_TYPE_R8
)
5903 interp_add_ins (td
, MINT_CONV_R8_R4
);
5904 if (td
->sp
[-1].type
== STACK_TYPE_R8
&& td
->sp
[-2].type
== STACK_TYPE_R4
)
5905 interp_add_ins (td
, MINT_CONV_R8_R4_SP
);
5906 interp_add_ins (td
, MINT_CEQ_I4
+ td
->sp
[-1].type
- STACK_TYPE_I4
);
5909 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5914 if (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
)
5915 interp_add_ins (td
, MINT_CGT_I4
+ STACK_TYPE_I
- STACK_TYPE_I4
);
5917 interp_add_ins (td
, MINT_CGT_I4
+ td
->sp
[-1].type
- STACK_TYPE_I4
);
5919 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5924 if (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
)
5925 interp_add_ins (td
, MINT_CGT_UN_I4
+ STACK_TYPE_I
- STACK_TYPE_I4
);
5927 interp_add_ins (td
, MINT_CGT_UN_I4
+ td
->sp
[-1].type
- STACK_TYPE_I4
);
5929 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5934 if (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
)
5935 interp_add_ins (td
, MINT_CLT_I4
+ STACK_TYPE_I
- STACK_TYPE_I4
);
5937 interp_add_ins (td
, MINT_CLT_I4
+ td
->sp
[-1].type
- STACK_TYPE_I4
);
5939 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5944 if (td
->sp
[-1].type
== STACK_TYPE_O
|| td
->sp
[-1].type
== STACK_TYPE_MP
)
5945 interp_add_ins (td
, MINT_CLT_UN_I4
+ STACK_TYPE_I
- STACK_TYPE_I4
);
5947 interp_add_ins (td
, MINT_CLT_UN_I4
+ td
->sp
[-1].type
- STACK_TYPE_I4
);
5949 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_I4
);
5952 case CEE_LDVIRTFTN
: /* fallthrough */
5955 if (*td
->ip
== CEE_LDVIRTFTN
) {
5956 CHECK_STACK (td
, 1);
5959 token
= read32 (td
->ip
+ 1);
5960 if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
5961 m
= (MonoMethod
*)mono_method_get_wrapper_data (method
, token
);
5963 m
= mono_get_method_checked (image
, token
, NULL
, generic_context
, error
);
5964 goto_if_nok (error
, exit
);
5967 if (!mono_method_can_access_method (method
, m
))
5968 interp_generate_mae_throw (td
, method
, m
);
5970 if (method
->wrapper_type
== MONO_WRAPPER_NONE
&& m
->iflags
& METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED
)
5971 m
= mono_marshal_get_synchronized_wrapper (m
);
5973 interp_add_ins (td
, *td
->ip
== CEE_LDFTN
? MINT_LDFTN
: MINT_LDVIRTFTN
);
5974 td
->last_ins
->data
[0] = get_data_item_index (td
, mono_interp_get_imethod (domain
, m
, error
));
5975 goto_if_nok (error
, exit
);
5977 PUSH_SIMPLE_TYPE (td
, STACK_TYPE_F
);
5981 int arg_n
= read16 (td
->ip
+ 1);
5983 load_arg (td
, arg_n
);
5985 load_local (td
, arg_locals
[arg_n
]);
5990 int n
= read16 (td
->ip
+ 1);
5993 get_arg_type_exact (td
, n
, &mt
);
5994 interp_add_ins (td
, mt
== MINT_TYPE_VT
? MINT_LDARGA_VT
: MINT_LDARGA
);
5995 td
->last_ins
->data
[0] = n
;
5997 int loc_n
= arg_locals
[n
];
5998 interp_add_ins (td
, MINT_LDLOCA_S
);
5999 td
->last_ins
->data
[0] = loc_n
;
6000 td
->locals
[loc_n
].flags
|= INTERP_LOCAL_FLAG_INDIRECT
;
6002 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_MP
);
6007 int arg_n
= read16 (td
->ip
+ 1);
6009 store_arg (td
, arg_n
);
6011 store_local (td
, arg_locals
[arg_n
]);
6016 int loc_n
= read16 (td
->ip
+ 1);
6018 load_local (td
, loc_n
);
6020 load_local (td
, local_locals
[loc_n
]);
6025 int loc_n
= read16 (td
->ip
+ 1);
6026 interp_add_ins (td
, MINT_LDLOCA_S
);
6028 loc_n
= local_locals
[loc_n
];
6029 td
->last_ins
->data
[0] = loc_n
;
6030 td
->locals
[loc_n
].flags
|= INTERP_LOCAL_FLAG_INDIRECT
;
6031 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_MP
);
6036 int loc_n
= read16 (td
->ip
+ 1);
6038 store_local (td
, loc_n
);
6040 store_local (td
, local_locals
[loc_n
]);
6046 CHECK_STACK (td
, 1);
6047 #if SIZEOF_VOID_P == 8
6048 if (td
->sp
[-1].type
== STACK_TYPE_I8
)
6049 interp_add_ins (td
, MINT_CONV_I4_I8
);
6051 interp_add_ins (td
, MINT_LOCALLOC
);
6052 if (td
->sp
!= td
->stack
+ 1)
6053 g_warning("CEE_LOCALLOC: stack not empty");
6055 SET_SIMPLE_TYPE(td
->sp
- 1, STACK_TYPE_MP
);
6058 case CEE_UNUSED57
: ves_abort(); break;
6061 interp_add_ins (td
, MINT_ENDFILTER
);
6064 case CEE_UNALIGNED_
:
6073 /* FIX: should do something? */;
6074 // TODO: This should raise a method_tail_call profiler event.
6078 token
= read32 (td
->ip
+ 1);
6079 klass
= mini_get_class (method
, token
, generic_context
);
6080 CHECK_TYPELOAD (klass
);
6081 if (m_class_is_valuetype (klass
)) {
6082 interp_add_ins (td
, MINT_INITOBJ
);
6083 i32
= mono_class_value_size (klass
, NULL
);
6084 WRITE32_INS (td
->last_ins
, 0, &i32
);
6087 interp_add_ins (td
, MINT_LDNULL
);
6088 PUSH_TYPE(td
, STACK_TYPE_O
, NULL
);
6089 interp_add_ins (td
, MINT_STIND_REF
);
6096 /* FIX? convert length to I8? */
6098 interp_add_ins (td
, MINT_MONO_MEMORY_BARRIER
);
6099 interp_add_ins (td
, MINT_CPBLK
);
6100 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_SEQ
);
6108 case CEE_CONSTRAINED_
:
6109 token
= read32 (td
->ip
+ 1);
6110 constrained_class
= mini_get_class (method
, token
, generic_context
);
6111 CHECK_TYPELOAD (constrained_class
);
6116 BARRIER_IF_VOLATILE (td
, MONO_MEMORY_BARRIER_REL
);
6117 interp_add_ins (td
, MINT_INITBLK
);
6122 /* FIXME: implement */
6126 int clause_index
= td
->clause_indexes
[in_offset
];
6127 g_assert (clause_index
!= -1);
6128 SIMPLE_OP (td
, MINT_RETHROW
);
6129 td
->last_ins
->data
[0] = rtm
->exvar_offsets
[clause_index
];
6135 token
= read32 (td
->ip
+ 1);
6137 if (mono_metadata_token_table (token
) == MONO_TABLE_TYPESPEC
&& !image_is_dynamic (m_class_get_image (method
->klass
)) && !generic_context
) {
6139 MonoType
*type
= mono_type_create_from_typespec_checked (image
, token
, error
);
6140 goto_if_nok (error
, exit
);
6141 size
= mono_type_size (type
, &align
);
6144 MonoClass
*szclass
= mini_get_class (method
, token
, generic_context
);
6145 CHECK_TYPELOAD (szclass
);
6147 if (!szclass
->valuetype
)
6148 THROW_EX (mono_exception_from_name (mono_defaults
.corlib
, "System", "InvalidProgramException"), ip
- 5);
6150 size
= mono_type_size (m_class_get_byval_arg (szclass
), &align
);
6152 interp_add_ins (td
, MINT_LDC_I4
);
6153 WRITE32_INS (td
->last_ins
, 0, &size
);
6154 PUSH_SIMPLE_TYPE(td
, STACK_TYPE_I4
);
6157 case CEE_REFANYTYPE
:
6158 interp_add_ins (td
, MINT_REFANYTYPE
);
6160 POP_VT (td
, sizeof (MonoTypedRef
));
6161 PUSH_VT (td
, sizeof (gpointer
));
6162 SET_TYPE(td
->sp
- 1, STACK_TYPE_VT
, NULL
);
6165 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
);
6169 g_error ("transform.c: Unimplemented opcode: %02x at 0x%x\n", *td
->ip
, td
->ip
-header
->code
);
6172 // No IR instructions were added as part of a bb_start IL instruction. Add a MINT_NOP
6173 // so we always have an instruction associated with a bb_start. This is simple and avoids
6174 // any complications associated with il_offset tracking.
6175 if (prev_last_ins
== td
->last_ins
&& (!inlining
&& td
->is_bb_start
[in_offset
]) && td
->ip
< end
)
6176 interp_add_ins (td
, MINT_NOP
);
6179 g_assert (td
->ip
== end
);
6182 g_free (arg_locals
);
6183 g_free (local_locals
);
6184 mono_basic_block_free (original_bb
);
6192 // Find the offset of the first interp instruction generated starting il_offset
6193 // This is needed to find the end of clauses.
6195 find_in_offset (TransformData
*td
, int il_offset
)
6198 while (!td
->in_offsets
[i
])
6200 return td
->in_offsets
[i
] - 1;
6203 // We store in the in_offset array the native_offset + 1, so 0 can mean only that the il
6204 // offset is uninitialized. Otherwise 0 is valid value for first interp instruction.
6206 get_in_offset (TransformData
*td
, int il_offset
)
6208 int target_offset
= td
->in_offsets
[il_offset
];
6209 g_assert (target_offset
);
6210 return target_offset
- 1;
6214 handle_relocations (TransformData
*td
)
6216 // Handle relocations
6217 for (int i
= 0; i
< td
->relocs
->len
; ++i
) {
6218 Reloc
*reloc
= (Reloc
*)g_ptr_array_index (td
->relocs
, i
);
6219 int offset
= get_in_offset (td
, reloc
->target
) - reloc
->offset
;
6221 switch (reloc
->type
) {
6222 case RELOC_SHORT_BRANCH
:
6223 g_assert (td
->new_code
[reloc
->offset
+ 1] == 0xdead);
6224 td
->new_code
[reloc
->offset
+ 1] = offset
;
6226 case RELOC_LONG_BRANCH
: {
6227 guint16
*v
= (guint16
*) &offset
;
6228 g_assert (td
->new_code
[reloc
->offset
+ 1] == 0xdead);
6229 g_assert (td
->new_code
[reloc
->offset
+ 2] == 0xbeef);
6230 td
->new_code
[reloc
->offset
+ 1] = *(guint16
*) v
;
6231 td
->new_code
[reloc
->offset
+ 2] = *(guint16
*) (v
+ 1);
6234 case RELOC_SWITCH
: {
6235 guint16
*v
= (guint16
*)&offset
;
6236 g_assert (td
->new_code
[reloc
->offset
] == 0xdead);
6237 g_assert (td
->new_code
[reloc
->offset
+ 1] == 0xbeef);
6238 td
->new_code
[reloc
->offset
] = *(guint16
*)v
;
6239 td
->new_code
[reloc
->offset
+ 1] = *(guint16
*)(v
+ 1);
6243 g_assert_not_reached ();
6251 get_inst_length (InterpInst
*ins
)
6253 if (ins
->opcode
== MINT_SWITCH
)
6254 return MINT_SWITCH_LEN (READ32 (&ins
->data
[0]));
6255 #ifdef ENABLE_EXPERIMENT_TIERED
6256 else if (MINT_IS_PATCHABLE_CALL (ins
->opcode
))
6257 return MAX (mono_interp_oplen
[MINT_JIT_CALL2
], mono_interp_oplen
[ins
->opcode
]);
6260 return mono_interp_oplen
[ins
->opcode
];
6264 get_inst_stack_usage (TransformData
*td
, InterpInst
*ins
, int *pop
, int *push
)
6266 guint16 opcode
= ins
->opcode
;
6267 if (mono_interp_oppop
[opcode
] == MINT_VAR_POP
||
6268 mono_interp_oppush
[opcode
] == MINT_VAR_PUSH
) {
6273 case MINT_CALLVIRT_FAST
:
6275 case MINT_VCALLVIRT
:
6276 case MINT_VCALLVIRT_FAST
: {
6277 InterpMethod
*imethod
= (InterpMethod
*) td
->data_items
[ins
->data
[0]];
6278 *pop
= imethod
->param_count
+ imethod
->hasthis
;
6279 if (opcode
== MINT_JIT_CALL
)
6280 *push
= imethod
->rtype
->type
!= MONO_TYPE_VOID
;
6282 *push
= opcode
== MINT_CALL
|| opcode
== MINT_CALLVIRT
|| opcode
== MINT_CALLVIRT_FAST
;
6285 #ifndef ENABLE_NETCORE
6286 case MINT_CALLRUN
: {
6287 MonoMethodSignature
*csignature
= (MonoMethodSignature
*) td
->data_items
[ins
->data
[1]];
6288 *pop
= csignature
->param_count
+ csignature
->hasthis
;
6289 *push
= csignature
->ret
->type
!= MONO_TYPE_VOID
;
6294 case MINT_CALLI_NAT
:
6295 case MINT_CALLI_NAT_FAST
: {
6296 MonoMethodSignature
*csignature
= (MonoMethodSignature
*) td
->data_items
[ins
->data
[0]];
6297 *pop
= csignature
->param_count
+ csignature
->hasthis
+ 1;
6298 *push
= csignature
->ret
->type
!= MONO_TYPE_VOID
;
6301 case MINT_CALL_VARARG
: {
6302 InterpMethod
*imethod
= (InterpMethod
*) td
->data_items
[ins
->data
[0]];
6303 MonoMethodSignature
*csignature
= (MonoMethodSignature
*) td
->data_items
[ins
->data
[1]];
6304 *pop
= imethod
->param_count
+ imethod
->hasthis
+ csignature
->param_count
- csignature
->sentinelpos
;
6305 *push
= imethod
->rtype
->type
!= MONO_TYPE_VOID
;
6308 case MINT_NEWOBJ_FAST
: {
6309 int param_count
= ins
->data
[1];
6310 gboolean is_inlined
= ins
->data
[0] == INLINED_METHOD_FLAG
;
6312 // This needs to be handled explictly during cprop, in order to properly
6313 // keep track of stack contents
6322 case MINT_NEWOBJ_ARRAY
:
6323 case MINT_NEWOBJ_VT_FAST
:
6324 case MINT_NEWOBJ_VTST_FAST
:
6325 *pop
= ins
->data
[1];
6329 case MINT_LDELEMA_TC
:
6330 *pop
= ins
->data
[0] + 1;
6334 InterpMethod
*imethod
= (InterpMethod
*) td
->data_items
[ins
->data
[0]];
6335 *pop
= imethod
->param_count
;
6339 case MINT_INTRINS_BYREFERENCE_CTOR
: {
6340 InterpMethod
*imethod
= (InterpMethod
*) td
->data_items
[ins
->data
[0]];
6341 *pop
= imethod
->param_count
;
6346 g_assert_not_reached ();
6349 *pop
= mono_interp_oppop
[opcode
];
6350 *push
= mono_interp_oppush
[opcode
];
6355 emit_compacted_instruction (TransformData
*td
, guint16
* start_ip
, InterpInst
*ins
)
6357 guint16 opcode
= ins
->opcode
;
6358 guint16
*ip
= start_ip
;
6360 // We know what IL offset this instruction was created for. We can now map the IL offset
6361 // to the IR offset. We use this array to resolve the relocations, which reference the IL.
6362 if (ins
->il_offset
!= -1 && !td
->in_offsets
[ins
->il_offset
]) {
6363 g_assert (ins
->il_offset
>= 0 && ins
->il_offset
< td
->header
->code_size
);
6364 td
->in_offsets
[ins
->il_offset
] = start_ip
- td
->new_code
+ 1;
6366 MonoDebugLineNumberEntry lne
;
6367 lne
.native_offset
= (guint8
*)start_ip
- (guint8
*)td
->new_code
;
6368 lne
.il_offset
= ins
->il_offset
;
6369 g_array_append_val (td
->line_numbers
, lne
);
6372 if (opcode
== MINT_NOP
)
6376 if (opcode
== MINT_SWITCH
) {
6377 int labels
= READ32 (&ins
->data
[0]);
6378 // Write number of switch labels
6379 *ip
++ = ins
->data
[0];
6380 *ip
++ = ins
->data
[1];
6381 // Add relocation for each label
6382 for (int i
= 0; i
< labels
; i
++) {
6383 Reloc
*reloc
= (Reloc
*)mono_mempool_alloc0 (td
->mempool
, sizeof (Reloc
));
6384 reloc
->type
= RELOC_SWITCH
;
6385 reloc
->offset
= ip
- td
->new_code
;
6386 reloc
->target
= READ32 (&ins
->data
[2 + i
* 2]);
6387 g_ptr_array_add (td
->relocs
, reloc
);
6391 } else if ((opcode
>= MINT_BRFALSE_I4_S
&& opcode
<= MINT_BRTRUE_R8_S
) ||
6392 (opcode
>= MINT_BEQ_I4_S
&& opcode
<= MINT_BLT_UN_R8_S
) ||
6393 opcode
== MINT_BR_S
|| opcode
== MINT_LEAVE_S
|| opcode
== MINT_LEAVE_S_CHECK
) {
6394 const int br_offset
= start_ip
- td
->new_code
;
6395 if (ins
->data
[0] < ins
->il_offset
) {
6396 // Backwards branch. We can already patch it.
6397 *ip
++ = get_in_offset (td
, ins
->data
[0]) - br_offset
;
6399 // We don't know the in_offset of the target, add a reloc
6400 Reloc
*reloc
= (Reloc
*)mono_mempool_alloc0 (td
->mempool
, sizeof (Reloc
));
6401 reloc
->type
= RELOC_SHORT_BRANCH
;
6402 reloc
->offset
= br_offset
;
6403 reloc
->target
= ins
->data
[0];
6404 g_ptr_array_add (td
->relocs
, reloc
);
6407 } else if ((opcode
>= MINT_BRFALSE_I4
&& opcode
<= MINT_BRTRUE_R8
) ||
6408 (opcode
>= MINT_BEQ_I4
&& opcode
<= MINT_BLT_UN_R8
) ||
6409 opcode
== MINT_BR
|| opcode
== MINT_LEAVE
|| opcode
== MINT_LEAVE_CHECK
) {
6410 const int br_offset
= start_ip
- td
->new_code
;
6411 int target_il
= READ32 (&ins
->data
[0]);
6412 if (target_il
< ins
->il_offset
) {
6413 // Backwards branch. We can already patch it
6414 const int br_offset
= start_ip
- td
->new_code
;
6415 int target_offset
= get_in_offset (td
, target_il
) - br_offset
;
6416 WRITE32 (ip
, &target_offset
);
6418 Reloc
*reloc
= (Reloc
*)mono_mempool_alloc0 (td
->mempool
, sizeof (Reloc
));
6419 reloc
->type
= RELOC_LONG_BRANCH
;
6420 reloc
->offset
= br_offset
;
6421 reloc
->target
= target_il
;
6422 g_ptr_array_add (td
->relocs
, reloc
);
6426 } else if (opcode
== MINT_SDB_SEQ_POINT
) {
6427 SeqPoint
*seqp
= (SeqPoint
*)mono_mempool_alloc0 (td
->mempool
, sizeof (SeqPoint
));
6428 InterpBasicBlock
*cbb
;
6430 if (ins
->flags
& INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY
) {
6431 seqp
->il_offset
= METHOD_ENTRY_IL_OFFSET
;
6432 cbb
= td
->offset_to_bb
[0];
6434 if (ins
->flags
& INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT
)
6435 seqp
->il_offset
= METHOD_EXIT_IL_OFFSET
;
6437 seqp
->il_offset
= ins
->il_offset
;
6438 cbb
= td
->offset_to_bb
[ins
->il_offset
];
6440 seqp
->native_offset
= (guint8
*)start_ip
- (guint8
*)td
->new_code
;
6441 if (ins
->flags
& INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK
)
6442 seqp
->flags
|= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK
;
6443 if (ins
->flags
& INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL
)
6444 seqp
->flags
|= MONO_SEQ_POINT_FLAG_NESTED_CALL
;
6445 g_ptr_array_add (td
->seq_points
, seqp
);
6447 cbb
->seq_points
= g_slist_prepend_mempool (td
->mempool
, cbb
->seq_points
, seqp
);
6448 cbb
->last_seq_point
= seqp
;
6449 #ifdef ENABLE_EXPERIMENT_TIERED
6450 } else if (ins
->flags
& INTERP_INST_FLAG_RECORD_CALL_PATCH
) {
6451 g_assert (MINT_IS_PATCHABLE_CALL (opcode
));
6453 /* TODO: could `ins` be removed by any interp optimization? */
6454 MonoMethod
*target_method
= (MonoMethod
*) g_hash_table_lookup (td
->patchsite_hash
, ins
);
6455 g_assert (target_method
);
6456 g_hash_table_remove (td
->patchsite_hash
, ins
);
6458 mini_tiered_record_callsite (start_ip
, target_method
, TIERED_PATCH_KIND_INTERP
);
6460 int size
= mono_interp_oplen
[ins
->opcode
];
6461 int jit_call2_size
= mono_interp_oplen
[MINT_JIT_CALL2
];
6463 g_assert (size
< jit_call2_size
);
6465 // Emit the rest of the data
6466 for (int i
= 0; i
< size
- 1; i
++)
6467 *ip
++ = ins
->data
[i
];
6469 /* intentional padding so we can patch a MINT_JIT_CALL2 here */
6470 for (int i
= size
- 1; i
< (jit_call2_size
- 1); i
++)
6474 if (MINT_IS_LDLOC (opcode
) || MINT_IS_STLOC (opcode
) || MINT_IS_STLOC_NP (opcode
) || opcode
== MINT_LDLOCA_S
||
6475 MINT_IS_LDLOCFLD (opcode
) || MINT_IS_LOCUNOP (opcode
) || MINT_IS_STLOCFLD (opcode
)) {
6476 ins
->data
[0] = get_interp_local_offset (td
, ins
->data
[0]);
6477 } else if (MINT_IS_MOVLOC (opcode
)) {
6478 ins
->data
[0] = get_interp_local_offset (td
, ins
->data
[0]);
6479 ins
->data
[1] = get_interp_local_offset (td
, ins
->data
[1]);
6482 int size
= get_inst_length (ins
) - 1;
6483 // Emit the rest of the data
6484 for (int i
= 0; i
< size
; i
++)
6485 *ip
++ = ins
->data
[i
];
6487 mono_interp_stats
.emitted_instructions
++;
6491 // Generates the final code, after we are done with all the passes
6493 generate_compacted_code (TransformData
*td
)
6497 td
->relocs
= g_ptr_array_new ();
6499 // Iterate once to compute the exact size of the compacted code
6500 InterpInst
*ins
= td
->first_ins
;
6502 size
+= get_inst_length (ins
);
6506 // Generate the compacted stream of instructions
6507 td
->new_code
= ip
= (guint16
*)mono_domain_alloc0 (td
->rtm
->domain
, size
* sizeof (guint16
));
6508 ins
= td
->first_ins
;
6510 ip
= emit_compacted_instruction (td
, ip
, ins
);
6513 td
->new_code_end
= ip
;
6514 td
->in_offsets
[td
->header
->code_size
] = td
->new_code_end
- td
->new_code
;
6516 // Patch all branches
6517 handle_relocations (td
);
6519 g_ptr_array_free (td
->relocs
, TRUE
);
6523 get_movloc_for_type (int mt
)
6528 return MINT_MOVLOC_1
;
6531 return MINT_MOVLOC_2
;
6534 return MINT_MOVLOC_4
;
6537 return MINT_MOVLOC_8
;
6540 #if SIZEOF_VOID_P == 8
6541 return MINT_MOVLOC_8
;
6543 return MINT_MOVLOC_4
;
6546 return MINT_MOVLOC_VT
;
6548 g_assert_not_reached ();
6551 // The value of local has changed. This means the contents of the stack where the
6552 // local was loaded, no longer contain the value of the local. Clear them.
6554 clear_stack_content_info_for_local (StackContentInfo
*start
, StackContentInfo
*end
, int local
)
6556 StackContentInfo
*si
;
6557 for (si
= start
; si
< end
; si
++) {
6558 if (si
->val
.type
== STACK_VALUE_LOCAL
&& si
->val
.local
== local
)
6559 si
->val
.type
= STACK_VALUE_NONE
;
6563 // The value of local has changed. This means we can no longer assume that any other local
6564 // is a copy of this local.
6566 clear_local_content_info_for_local (StackValue
*start
, StackValue
*end
, int local
)
6569 for (sval
= start
; sval
< end
; sval
++) {
6570 if (sval
->type
== STACK_VALUE_LOCAL
&& sval
->local
== local
)
6571 sval
->type
= STACK_VALUE_NONE
;
6576 interp_local_deadce (TransformData
*td
, int *local_ref_count
)
6579 gboolean needs_dce
= FALSE
;
6580 gboolean needs_cprop
= FALSE
;
6582 for (int i
= 0; i
< td
->locals_size
; i
++) {
6583 g_assert (local_ref_count
[i
] >= 0);
6584 if (!local_ref_count
[i
] &&
6585 (td
->locals
[i
].flags
& INTERP_LOCAL_FLAG_INDIRECT
) == 0 &&
6586 (td
->locals
[i
].flags
& INTERP_LOCAL_FLAG_DEAD
) == 0) {
6588 // If we do another deadce iteration over the code, make sure we don't try
6589 // to kill instructions accessing locals that have already been handled in
6590 // a previous iteration.
6591 td
->locals
[i
].flags
|= INTERP_LOCAL_FLAG_DEAD
;
6596 // Return early if all locals are alive
6600 // Kill instructions that don't use stack and are storing into dead locals
6601 for (ins
= td
->first_ins
; ins
!= NULL
; ins
= ins
->next
) {
6602 if (MINT_IS_STLOC_NP (ins
->opcode
)) {
6603 if (!local_ref_count
[ins
->data
[0]] && (td
->locals
[ins
->data
[0]].flags
& INTERP_LOCAL_FLAG_INDIRECT
) == 0) {
6604 interp_clear_ins (td
, ins
);
6605 mono_interp_stats
.killed_instructions
++;
6606 // We killed an instruction that makes use of the stack. This might uncover new optimizations
6609 } else if (MINT_IS_MOVLOC (ins
->opcode
)) {
6610 if (!local_ref_count
[ins
->data
[1]] && (td
->locals
[ins
->data
[1]].flags
& INTERP_LOCAL_FLAG_INDIRECT
) == 0) {
6611 interp_clear_ins (td
, ins
);
6612 mono_interp_stats
.killed_instructions
++;
6614 } else if (MINT_IS_STLOC (ins
->opcode
) && ins
->opcode
!= MINT_STLOC_VT
) {
6615 if (!local_ref_count
[ins
->data
[0]] && (td
->locals
[ins
->data
[0]].flags
& INTERP_LOCAL_FLAG_INDIRECT
) == 0) {
6616 // We store to a dead stloc, we can replace it with a POP to save local space
6617 ins
->opcode
= MINT_POP
;
6618 mono_interp_stats
.added_pop_count
++;
6619 // We might to be able to kill both the pop and the instruction pushing the value
6627 #define INTERP_FOLD_UNOP(opcode,stack_type,field,op) \
6629 g_assert (sp->val.type == stack_type); \
6630 result.type = stack_type; \
6631 result.field = op sp->val.field; \
6634 #define INTERP_FOLD_CONV(opcode,stack_type_dst,field_dst,stack_type_src,field_src,cast_type) \
6636 g_assert (sp->val.type == stack_type_src); \
6637 result.type = stack_type_dst; \
6638 result.field_dst = (cast_type)sp->val.field_src; \
6642 interp_fold_unop (TransformData
*td
, StackContentInfo
*sp
, InterpInst
*ins
)
6645 // Decrement sp so it's easier to access top of the stack
6647 if (sp
->val
.type
!= STACK_VALUE_I4
&& sp
->val
.type
!= STACK_VALUE_I8
)
6650 // Top of the stack is a constant
6651 switch (ins
->opcode
) {
6652 INTERP_FOLD_UNOP (MINT_ADD1_I4
, STACK_VALUE_I4
, i
, 1+);
6653 INTERP_FOLD_UNOP (MINT_ADD1_I8
, STACK_VALUE_I8
, l
, 1+);
6654 INTERP_FOLD_UNOP (MINT_SUB1_I4
, STACK_VALUE_I4
, i
, -1+);
6655 INTERP_FOLD_UNOP (MINT_SUB1_I8
, STACK_VALUE_I8
, l
, -1+);
6656 INTERP_FOLD_UNOP (MINT_NEG_I4
, STACK_VALUE_I4
, i
, -);
6657 INTERP_FOLD_UNOP (MINT_NEG_I8
, STACK_VALUE_I8
, l
, -);
6658 INTERP_FOLD_UNOP (MINT_NOT_I4
, STACK_VALUE_I4
, i
, ~);
6659 INTERP_FOLD_UNOP (MINT_NOT_I8
, STACK_VALUE_I8
, l
, ~);
6660 INTERP_FOLD_UNOP (MINT_CEQ0_I4
, STACK_VALUE_I4
, i
, 0 ==);
6662 INTERP_FOLD_CONV (MINT_CONV_I1_I4
, STACK_VALUE_I4
, i
, STACK_VALUE_I4
, i
, gint8
);
6663 INTERP_FOLD_CONV (MINT_CONV_I1_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, gint8
);
6664 INTERP_FOLD_CONV (MINT_CONV_U1_I4
, STACK_VALUE_I4
, i
, STACK_VALUE_I4
, i
, guint8
);
6665 INTERP_FOLD_CONV (MINT_CONV_U1_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, guint8
);
6667 INTERP_FOLD_CONV (MINT_CONV_I2_I4
, STACK_VALUE_I4
, i
, STACK_VALUE_I4
, i
, gint16
);
6668 INTERP_FOLD_CONV (MINT_CONV_I2_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, gint16
);
6669 INTERP_FOLD_CONV (MINT_CONV_U2_I4
, STACK_VALUE_I4
, i
, STACK_VALUE_I4
, i
, guint16
);
6670 INTERP_FOLD_CONV (MINT_CONV_U2_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, guint16
);
6672 INTERP_FOLD_CONV (MINT_CONV_I4_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, gint32
);
6673 INTERP_FOLD_CONV (MINT_CONV_U4_I8
, STACK_VALUE_I4
, i
, STACK_VALUE_I8
, l
, gint32
);
6675 INTERP_FOLD_CONV (MINT_CONV_I8_I4
, STACK_VALUE_I8
, l
, STACK_VALUE_I4
, i
, gint32
);
6676 INTERP_FOLD_CONV (MINT_CONV_I8_U4
, STACK_VALUE_I8
, l
, STACK_VALUE_I4
, i
, guint32
);
6682 // We were able to compute the result of the ins instruction. We store the
6683 // current value for the top of the stack and, if possible, try to replace the
6684 // instructions that are part of this unary operation with a single LDC.
6685 mono_interp_stats
.constant_folds
++;
6686 if (sp
->ins
!= NULL
) {
6687 interp_clear_ins (td
, sp
->ins
);
6688 mono_interp_stats
.killed_instructions
++;
6689 if (result
.type
== STACK_VALUE_I4
)
6690 ins
= interp_get_ldc_i4_from_const (td
, ins
, result
.i
);
6691 else if (result
.type
== STACK_VALUE_I8
)
6692 ins
= interp_inst_replace_with_i8_const (td
, ins
, result
.l
);
6694 g_assert_not_reached ();
6695 if (td
->verbose_level
) {
6696 g_print ("Fold unop :\n\t");
6697 dump_interp_inst_newline (ins
);
6706 sp
->val
.type
= STACK_VALUE_NONE
;
6710 #define INTERP_FOLD_BINOP(opcode,stack_type,field,op) \
6712 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6713 result.type = stack_type; \
6714 result.field = sp [0].val.field op sp [1].val.field; \
6717 #define INTERP_FOLD_BINOP_FULL(opcode,stack_type,field,op,cast_type,cond) \
6719 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6720 if (!(cond)) goto cfold_failed; \
6721 result.type = stack_type; \
6722 result.field = (cast_type)sp [0].val.field op (cast_type)sp [1].val.field; \
6725 #define INTERP_FOLD_SHIFTOP(opcode,stack_type,field,shift_op,cast_type) \
6727 g_assert (sp [1].val.type == STACK_VALUE_I4); \
6728 result.type = stack_type; \
6729 result.field = (cast_type)sp [0].val.field shift_op sp [1].val.i; \
6732 #define INTERP_FOLD_RELOP(opcode,stack_type,field,relop,cast_type) \
6734 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6735 result.type = STACK_VALUE_I4; \
6736 result.field = (cast_type) sp [0].val.field relop (cast_type) sp [1].val.field; \
6741 interp_fold_binop (TransformData
*td
, StackContentInfo
*sp
, InterpInst
*ins
)
6744 // Decrement sp so it's easier to access top of the stack
6746 if (sp
[0].val
.type
!= STACK_VALUE_I4
&& sp
[0].val
.type
!= STACK_VALUE_I8
)
6748 if (sp
[1].val
.type
!= STACK_VALUE_I4
&& sp
[1].val
.type
!= STACK_VALUE_I8
)
6751 // Top two values of the stack are constants
6752 switch (ins
->opcode
) {
6753 INTERP_FOLD_BINOP (MINT_ADD_I4
, STACK_VALUE_I4
, i
, +);
6754 INTERP_FOLD_BINOP (MINT_ADD_I8
, STACK_VALUE_I8
, l
, +);
6755 INTERP_FOLD_BINOP (MINT_SUB_I4
, STACK_VALUE_I4
, i
, -);
6756 INTERP_FOLD_BINOP (MINT_SUB_I8
, STACK_VALUE_I8
, l
, -);
6757 INTERP_FOLD_BINOP (MINT_MUL_I4
, STACK_VALUE_I4
, i
, *);
6758 INTERP_FOLD_BINOP (MINT_MUL_I8
, STACK_VALUE_I8
, l
, *);
6760 INTERP_FOLD_BINOP (MINT_AND_I4
, STACK_VALUE_I4
, i
, &);
6761 INTERP_FOLD_BINOP (MINT_AND_I8
, STACK_VALUE_I8
, l
, &);
6762 INTERP_FOLD_BINOP (MINT_OR_I4
, STACK_VALUE_I4
, i
, |);
6763 INTERP_FOLD_BINOP (MINT_OR_I8
, STACK_VALUE_I8
, l
, |);
6764 INTERP_FOLD_BINOP (MINT_XOR_I4
, STACK_VALUE_I4
, i
, ^);
6765 INTERP_FOLD_BINOP (MINT_XOR_I8
, STACK_VALUE_I8
, l
, ^);
6767 INTERP_FOLD_SHIFTOP (MINT_SHL_I4
, STACK_VALUE_I4
, i
, <<, gint32
);
6768 INTERP_FOLD_SHIFTOP (MINT_SHL_I8
, STACK_VALUE_I8
, l
, <<, gint64
);
6769 INTERP_FOLD_SHIFTOP (MINT_SHR_I4
, STACK_VALUE_I4
, i
, >>, gint32
);
6770 INTERP_FOLD_SHIFTOP (MINT_SHR_I8
, STACK_VALUE_I8
, l
, >>, gint64
);
6771 INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I4
, STACK_VALUE_I4
, i
, >>, guint32
);
6772 INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I8
, STACK_VALUE_I8
, l
, >>, guint64
);
6774 INTERP_FOLD_RELOP (MINT_CEQ_I4
, STACK_VALUE_I4
, i
, ==, gint32
);
6775 INTERP_FOLD_RELOP (MINT_CEQ_I8
, STACK_VALUE_I8
, l
, ==, gint64
);
6776 INTERP_FOLD_RELOP (MINT_CNE_I4
, STACK_VALUE_I4
, i
, !=, gint32
);
6777 INTERP_FOLD_RELOP (MINT_CNE_I8
, STACK_VALUE_I8
, l
, !=, gint64
);
6779 INTERP_FOLD_RELOP (MINT_CGT_I4
, STACK_VALUE_I4
, i
, >, gint32
);
6780 INTERP_FOLD_RELOP (MINT_CGT_I8
, STACK_VALUE_I8
, l
, >, gint64
);
6781 INTERP_FOLD_RELOP (MINT_CGT_UN_I4
, STACK_VALUE_I4
, i
, >, guint32
);
6782 INTERP_FOLD_RELOP (MINT_CGT_UN_I8
, STACK_VALUE_I8
, l
, >, guint64
);
6784 INTERP_FOLD_RELOP (MINT_CGE_I4
, STACK_VALUE_I4
, i
, >=, gint32
);
6785 INTERP_FOLD_RELOP (MINT_CGE_I8
, STACK_VALUE_I8
, l
, >=, gint64
);
6786 INTERP_FOLD_RELOP (MINT_CGE_UN_I4
, STACK_VALUE_I4
, i
, >=, guint32
);
6787 INTERP_FOLD_RELOP (MINT_CGE_UN_I8
, STACK_VALUE_I8
, l
, >=, guint64
);
6789 INTERP_FOLD_RELOP (MINT_CLT_I4
, STACK_VALUE_I4
, i
, <, gint32
);
6790 INTERP_FOLD_RELOP (MINT_CLT_I8
, STACK_VALUE_I8
, l
, <, gint64
);
6791 INTERP_FOLD_RELOP (MINT_CLT_UN_I4
, STACK_VALUE_I4
, i
, <, guint32
);
6792 INTERP_FOLD_RELOP (MINT_CLT_UN_I8
, STACK_VALUE_I8
, l
, <, guint64
);
6794 INTERP_FOLD_RELOP (MINT_CLE_I4
, STACK_VALUE_I4
, i
, <=, gint32
);
6795 INTERP_FOLD_RELOP (MINT_CLE_I8
, STACK_VALUE_I8
, l
, <=, gint64
);
6796 INTERP_FOLD_RELOP (MINT_CLE_UN_I4
, STACK_VALUE_I4
, i
, <=, guint32
);
6797 INTERP_FOLD_RELOP (MINT_CLE_UN_I8
, STACK_VALUE_I8
, l
, <=, guint64
);
6799 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));
6800 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));
6801 INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I4
, STACK_VALUE_I4
, i
, /, guint32
, sp
[1].val
.i
!= 0);
6802 INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I8
, STACK_VALUE_I8
, l
, /, guint64
, sp
[1].val
.l
!= 0);
6804 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));
6805 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));
6806 INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I4
, STACK_VALUE_I4
, i
, %, guint32
, sp
[1].val
.i
!= 0);
6807 INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I8
, STACK_VALUE_I8
, l
, %, guint64
, sp
[1].val
.l
!= 0);
6813 // We were able to compute the result of the ins instruction. We store the
6814 // current value for the top of the stack and, if possible, try to replace the
6815 // instructions that are part of this unary operation with a single LDC.
6816 mono_interp_stats
.constant_folds
++;
6817 if (sp
[0].ins
!= NULL
&& sp
[1].ins
!= NULL
) {
6818 interp_clear_ins (td
, sp
[0].ins
);
6819 interp_clear_ins (td
, sp
[1].ins
);
6820 mono_interp_stats
.killed_instructions
+= 2;
6821 if (result
.type
== STACK_VALUE_I4
)
6822 ins
= interp_get_ldc_i4_from_const (td
, ins
, result
.i
);
6823 else if (result
.type
== STACK_VALUE_I8
)
6824 ins
= interp_inst_replace_with_i8_const (td
, ins
, result
.l
);
6826 g_assert_not_reached ();
6827 if (td
->verbose_level
) {
6828 g_print ("Fold binop :\n\t");
6829 dump_interp_inst_newline (ins
);
6835 sp
[0].val
= result
;
6840 sp
->val
.type
= STACK_VALUE_NONE
;
6845 interp_local_equal (StackValue
*locals
, int local1
, int local2
)
6847 if (local1
== local2
)
6849 if (locals
[local1
].type
== STACK_VALUE_LOCAL
&& locals
[local1
].local
== local2
) {
6850 // local1 is a copy of local2
6853 if (locals
[local2
].type
== STACK_VALUE_LOCAL
&& locals
[local2
].local
== local1
) {
6854 // local2 is a copy of local1
6857 if (locals
[local1
].type
== STACK_VALUE_I4
&& locals
[local2
].type
== STACK_VALUE_I4
)
6858 return locals
[local1
].i
== locals
[local2
].i
;
6859 if (locals
[local1
].type
== STACK_VALUE_I8
&& locals
[local2
].type
== STACK_VALUE_I8
)
6860 return locals
[local1
].l
== locals
[local2
].l
;
6865 interp_cprop (TransformData
*td
)
6867 if (!td
->max_stack_height
)
6869 StackContentInfo
*stack
= (StackContentInfo
*) g_malloc (td
->max_stack_height
* sizeof (StackContentInfo
));
6870 StackContentInfo
*stack_end
= stack
+ td
->max_stack_height
;
6871 StackContentInfo
*sp
;
6872 StackValue
*locals
= (StackValue
*) g_malloc (td
->locals_size
* sizeof (StackValue
));
6873 int *local_ref_count
= (int*) g_malloc (td
->locals_size
* sizeof (int));
6879 last_il_offset
= -1;
6880 memset (local_ref_count
, 0, td
->locals_size
* sizeof (int));
6882 for (ins
= td
->first_ins
; ins
!= NULL
; ins
= ins
->next
) {
6884 int il_offset
= ins
->il_offset
;
6885 // Optimizations take place only inside a single basic block
6886 // If two instructions have the same il_offset, then the second one
6887 // cannot be part the start of a basic block.
6888 gboolean is_bb_start
= il_offset
!= -1 && td
->is_bb_start
[il_offset
] && il_offset
!= last_il_offset
;
6890 if (td
->stack_height
[il_offset
] >= 0) {
6891 sp
= stack
+ td
->stack_height
[il_offset
];
6892 g_assert (sp
<= stack_end
);
6893 memset (stack
, 0, (sp
- stack
) * sizeof (StackContentInfo
));
6895 memset (locals
, 0, td
->locals_size
* sizeof (StackValue
));
6897 // The instruction pops some values then pushes some other
6898 get_inst_stack_usage (td
, ins
, &pop
, &push
);
6899 if (td
->verbose_level
&& ins
->opcode
!= MINT_NOP
) {
6900 dump_interp_inst (ins
);
6901 g_print (", sp %d, (pop %d, push %d)\n", sp
- stack
, pop
, push
);
6903 if (MINT_IS_LDLOC (ins
->opcode
)) {
6905 int loaded_local
= ins
->data
[0];
6906 local_ref_count
[loaded_local
]++;
6907 InterpInst
*prev_ins
= interp_prev_ins (ins
);
6908 if (MINT_IS_STLOC (prev_ins
->opcode
) && !interp_is_bb_start (td
, prev_ins
, ins
) && interp_local_equal (locals
, prev_ins
->data
[0], loaded_local
)) {
6909 int mt
= prev_ins
->opcode
- MINT_STLOC_I1
;
6910 if (ins
->opcode
- MINT_LDLOC_I1
== mt
) {
6911 if (mt
== MINT_TYPE_I4
)
6912 replace_op
= MINT_STLOC_NP_I4
;
6913 else if (mt
== MINT_TYPE_I8
)
6914 replace_op
= MINT_STLOC_NP_I8
;
6915 else if (mt
== MINT_TYPE_R4
)
6916 replace_op
= MINT_STLOC_NP_R4
;
6917 else if (mt
== MINT_TYPE_R8
)
6918 replace_op
= MINT_STLOC_NP_R8
;
6919 else if (mt
== MINT_TYPE_O
|| mt
== MINT_TYPE_P
)
6920 replace_op
= MINT_STLOC_NP_O
;
6922 int stored_local
= prev_ins
->data
[0];
6924 if (sp
->val
.type
== STACK_VALUE_NONE
) {
6925 // We know what local is on the stack now. Track it
6926 sp
->val
.type
= STACK_VALUE_LOCAL
;
6927 sp
->val
.local
= stored_local
;
6930 // Clear the previous stloc instruction
6931 interp_clear_ins (td
, prev_ins
);
6932 ins
->opcode
= replace_op
;
6933 ins
->data
[0] = stored_local
;
6934 local_ref_count
[loaded_local
]--;
6935 if (td
->verbose_level
) {
6936 g_print ("Add stloc.np :\n\t");
6937 dump_interp_inst_newline (ins
);
6939 mono_interp_stats
.stloc_nps
++;
6940 mono_interp_stats
.killed_instructions
++;
6943 } else if (locals
[loaded_local
].type
== STACK_VALUE_LOCAL
) {
6944 g_assert (locals
[loaded_local
].type
== STACK_VALUE_LOCAL
);
6945 g_assert (!(td
->locals
[loaded_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
));
6946 // do copy propagation of the original source
6947 mono_interp_stats
.copy_propagations
++;
6948 local_ref_count
[loaded_local
]--;
6949 ins
->data
[0] = locals
[loaded_local
].local
;
6950 local_ref_count
[ins
->data
[0]]++;
6951 if (td
->verbose_level
) {
6952 g_print ("cprop loc %d -> loc %d :\n\t", loaded_local
, locals
[loaded_local
].local
);
6953 dump_interp_inst_newline (ins
);
6955 } else if (locals
[loaded_local
].type
== STACK_VALUE_I4
|| locals
[loaded_local
].type
== STACK_VALUE_I8
) {
6956 gboolean is_i4
= locals
[loaded_local
].type
== STACK_VALUE_I4
;
6957 g_assert (!(td
->locals
[loaded_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
));
6959 ins
= interp_get_ldc_i4_from_const (td
, ins
, locals
[loaded_local
].i
);
6961 ins
= interp_inst_replace_with_i8_const (td
, ins
, locals
[loaded_local
].l
);
6963 sp
->val
= locals
[loaded_local
];
6964 local_ref_count
[loaded_local
]--;
6965 mono_interp_stats
.copy_propagations
++;
6966 if (td
->verbose_level
) {
6967 g_print ("cprop loc %d -> ct :\n\t", loaded_local
);
6968 dump_interp_inst_newline (ins
);
6970 // FIXME this replace_op got ugly
6971 replace_op
= ins
->opcode
;
6974 // Save the ldloc on the stack if it wasn't optimized away
6975 // For simplicity we don't track locals that have their address taken
6976 // since it is hard to detect instructions that change the local value.
6977 if (td
->locals
[loaded_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
) {
6978 sp
->val
.type
= STACK_VALUE_NONE
;
6980 sp
->val
.type
= STACK_VALUE_LOCAL
;
6981 sp
->val
.local
= ins
->data
[0];
6986 } else if (MINT_IS_STLOC (ins
->opcode
)) {
6987 int dest_local
= ins
->data
[0];
6989 if (sp
->val
.type
== STACK_VALUE_LOCAL
) {
6990 int src_local
= sp
->val
.local
;
6991 if (td
->locals
[src_local
].mt
== td
->locals
[dest_local
].mt
) {
6992 // The locals have the same type. We can propagate the value
6993 int vtsize
= (ins
->opcode
== MINT_STLOC_VT
) ? ins
->data
[1] : 0;
6995 if (!(td
->locals
[dest_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
)) {
6996 // Track what exactly is stored into local
6997 locals
[dest_local
].type
= STACK_VALUE_LOCAL
;
6998 locals
[dest_local
].local
= src_local
;
7002 interp_clear_ins (td
, sp
->ins
);
7003 interp_clear_ins (td
, ins
);
7005 ins
= interp_insert_ins (td
, ins
, get_movloc_for_type (td
->locals
[src_local
].mt
));
7006 ins
->data
[0] = src_local
;
7007 ins
->data
[1] = dest_local
;
7009 ins
->data
[2] = vtsize
;
7010 // Clear ldloc / stloc pair and replace it with movloc superinstruction
7011 if (td
->verbose_level
) {
7012 g_print ("Add movloc (ldloc off %d) :\n\t", sp
->ins
->il_offset
);
7013 dump_interp_inst_newline (ins
);
7015 mono_interp_stats
.movlocs
++;
7016 mono_interp_stats
.killed_instructions
++;
7019 locals
[dest_local
].type
= STACK_VALUE_NONE
;
7021 } else if (sp
->val
.type
== STACK_VALUE_NONE
) {
7022 locals
[dest_local
].type
= STACK_VALUE_NONE
;
7024 g_assert (sp
->val
.type
== STACK_VALUE_I4
|| sp
->val
.type
== STACK_VALUE_I8
);
7025 if (!(td
->locals
[dest_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
))
7026 locals
[dest_local
] = sp
->val
;
7028 clear_stack_content_info_for_local (stack
, sp
, dest_local
);
7029 clear_local_content_info_for_local (locals
, locals
+ td
->locals_size
, dest_local
);
7030 } else if (MINT_IS_LDC_I4 (ins
->opcode
) || ins
->opcode
== MINT_LDC_I8
) {
7032 gboolean is_i8
= ins
->opcode
== MINT_LDC_I8
;
7033 InterpInst
*prev_ins
= interp_prev_ins (ins
);
7036 val
.type
= STACK_VALUE_I8
;
7037 val
.l
= READ64 (&ins
->data
[0]);
7039 val
.type
= STACK_VALUE_I4
;
7040 val
.i
= interp_get_const_from_ldc_i4 (ins
);
7043 if (prev_ins
&& prev_ins
->opcode
== MINT_POP
&&
7044 ((is_i8
&& sp
->val
.type
== STACK_VALUE_I8
&& sp
->val
.l
== val
.l
) ||
7045 (!is_i8
&& sp
->val
.type
== STACK_VALUE_I4
&& sp
->val
.i
== val
.i
)) &&
7046 !interp_is_bb_start (td
, prev_ins
, ins
)) {
7047 // The previous instruction pops the stack of the value we are pushing
7048 // right now. We can kill both instructions
7049 if (td
->verbose_level
)
7050 g_print ("Kill redundant pop/ldc pair: pop (off %p), ldc (off %p)\n", prev_ins
->il_offset
, ins
->il_offset
);
7051 interp_clear_ins (td
, prev_ins
);
7052 interp_clear_ins (td
, ins
);
7053 mono_interp_stats
.killed_instructions
+= 2;
7059 } else if (MINT_IS_MOVLOC (ins
->opcode
)) {
7060 int src_local
= ins
->data
[0];
7061 int dest_local
= ins
->data
[1];
7062 local_ref_count
[src_local
]++;
7063 if (!(td
->locals
[dest_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
)) {
7064 if (locals
[src_local
].type
!= STACK_VALUE_NONE
) {
7065 locals
[dest_local
] = locals
[src_local
];
7067 locals
[dest_local
].type
= STACK_VALUE_LOCAL
;
7068 locals
[dest_local
].local
= src_local
;
7070 clear_stack_content_info_for_local (stack
, sp
, dest_local
);
7071 clear_local_content_info_for_local (locals
, locals
+ td
->locals_size
, dest_local
);
7073 } else if (MINT_IS_STLOC_NP (ins
->opcode
)) {
7074 int dest_local
= ins
->data
[0];
7075 // Prevent optimizing away the instruction that pushed the value on the stack
7077 // The local contains the value of the top of stack
7078 if (!(td
->locals
[dest_local
].flags
& INTERP_LOCAL_FLAG_INDIRECT
)) {
7079 locals
[dest_local
] = sp
[-1].val
;
7080 clear_stack_content_info_for_local (stack
, sp
, dest_local
);
7081 clear_local_content_info_for_local (locals
, locals
+ td
->locals_size
, dest_local
);
7083 } else if (ins
->opcode
== MINT_DUP
|| ins
->opcode
== MINT_DUP_VT
) {
7084 sp
[0].val
= sp
[-1].val
;
7086 // If top of stack is known, we could also replace dup with an explicit
7087 // propagated instruction, so we remove the top of stack dependency
7090 } else if (ins
->opcode
>= MINT_BOX
&& ins
->opcode
<= MINT_BOX_NULLABLE
) {
7091 int offset
= ins
->data
[1];
7092 // Clear the stack slot that is boxed
7093 memset (&sp
[-1 - offset
], 0, sizeof (StackContentInfo
));
7094 // Make sure that the instructions that pushed this stack slot can't be
7095 // optimized away. If we would optimize them away, we would also need to
7096 // update the offset in the box instruction, which we can't, for now.
7097 for (int i
= 1; i
<= offset
; i
++)
7099 } else if (ins
->opcode
== MINT_CKNULL_N
) {
7100 int offset
= ins
->data
[0];
7101 for (int i
= 1; i
<= offset
; i
++)
7103 } else if (ins
->opcode
== MINT_LD_DELEGATE_INVOKE_IMPL
) {
7104 int offset
= ins
->data
[0];
7105 for (int i
= 1; i
<= offset
; i
++)
7107 memset (sp
, 0, sizeof (StackContentInfo
));
7109 } else if (ins
->opcode
== MINT_POP
) {
7112 // The top of the stack is not used by any instructions. Kill both the
7113 // instruction that pushed it and the pop.
7114 interp_clear_ins (td
, sp
->ins
);
7115 interp_clear_ins (td
, ins
);
7116 mono_interp_stats
.killed_instructions
+= 2;
7117 // The value pop-ed by this instruction can still be accessed. If we also
7118 // kill the instruction pushing the value, then we need to empty the
7119 // value of the stack, so it is not considered for further optimizations.
7120 sp
->val
.type
= STACK_VALUE_NONE
;
7122 } else if (ins
->opcode
== MINT_NEWOBJ_FAST
&& ins
->data
[0] == INLINED_METHOD_FLAG
) {
7123 int param_count
= ins
->data
[1];
7124 // memmove the stack values while clearing ins, to prevent instruction removal
7125 for (int i
= 1; i
<= param_count
; i
++) {
7126 sp
[-i
+ 2] = sp
[-i
];
7127 sp
[-i
+ 2].ins
= NULL
;
7129 // clear stack information for the slots where the allocated object resides
7130 memset (&sp
[-param_count
], 0, 2 * sizeof (StackContentInfo
));
7132 } else if (ins
->opcode
== MINT_CASTCLASS
|| ins
->opcode
== MINT_CASTCLASS_COMMON
|| ins
->opcode
== MINT_CASTCLASS_INTERFACE
) {
7133 // Keep the value on the stack, but prevent optimizing away
7135 } else if (MINT_IS_CONDITIONAL_BRANCH (ins
->opcode
)) {
7137 g_assert (push
== 0);
7138 // We can't clear any instruction that pushes the stack, because the
7139 // branched code will expect a certain stack size.
7140 for (StackContentInfo
*sp_iter
= stack
; sp_iter
< sp
; sp_iter
++)
7141 sp_iter
->ins
= NULL
;
7142 } else if (MINT_IS_UNOP (ins
->opcode
)) {
7143 ins
= interp_fold_unop (td
, sp
, ins
);
7144 } else if (MINT_IS_BINOP (ins
->opcode
)) {
7145 ins
= interp_fold_binop (td
, sp
, ins
);
7147 } else if (ins
->opcode
>= MINT_STFLD_I1
&& ins
->opcode
<= MINT_STFLD_P
&& (mono_interp_opt
& INTERP_OPT_SUPER_INSTRUCTIONS
)) {
7149 InterpInst
*obj_ins
= sp
[-2].ins
;
7150 if (obj_ins
->opcode
== MINT_LDLOC_O
) {
7151 int loc_index
= obj_ins
->data
[0];
7152 int fld_offset
= ins
->data
[0];
7153 int mt
= ins
->opcode
- MINT_STFLD_I1
;
7154 ins
= interp_insert_ins (td
, ins
, MINT_STLOCFLD_I1
+ mt
);
7155 ins
->data
[0] = loc_index
;
7156 ins
->data
[1] = fld_offset
;
7157 interp_clear_ins (td
, ins
->prev
);
7158 interp_clear_ins (td
, obj_ins
);
7159 mono_interp_stats
.super_instructions
++;
7160 mono_interp_stats
.killed_instructions
++;
7161 } else if (obj_ins
->opcode
== MINT_LDARG_O
|| obj_ins
->opcode
== MINT_LDARG_P0
) {
7163 int fld_offset
= ins
->data
[0];
7164 int mt
= ins
->opcode
- MINT_STFLD_I1
;
7165 if (obj_ins
->opcode
== MINT_LDARG_O
)
7166 arg_index
= obj_ins
->data
[0];
7167 ins
= interp_insert_ins (td
, ins
, MINT_STARGFLD_I1
+ mt
);
7168 ins
->data
[0] = arg_index
;
7169 ins
->data
[1] = fld_offset
;
7170 interp_clear_ins (td
, ins
->prev
);
7171 interp_clear_ins (td
, obj_ins
);
7172 mono_interp_stats
.super_instructions
++;
7173 mono_interp_stats
.killed_instructions
++;
7177 } else if (MINT_IS_STLOCFLD (ins
->opcode
)) {
7178 local_ref_count
[ins
->data
[0]]++;
7181 if (pop
== MINT_POP_ALL
)
7184 g_assert (sp
>= stack
&& sp
<= stack_end
);
7185 g_assert ((sp
- push
) >= stack
&& (sp
- push
) <= stack_end
);
7186 memset (sp
- push
, 0, push
* sizeof (StackContentInfo
));
7187 // If this instruction only pushes a single value, make it a candidate for
7188 // removal, if its value is not used anywhere.
7189 if (push
== 1 && pop
== 0 && !MINT_IS_CALL (ins
->opcode
) && !MINT_IS_NEWOBJ (ins
->opcode
))
7192 last_il_offset
= ins
->il_offset
;
7195 if (interp_local_deadce (td
, local_ref_count
))
7200 g_free (local_ref_count
);
7204 interp_super_instructions (TransformData
*td
)
7207 InterpInst
*prev1_ins
= NULL
;
7208 InterpInst
*prev2_ins
= NULL
;
7209 int last_il_offset
= -1;
7210 for (ins
= td
->first_ins
; ins
!= NULL
; ins
= ins
->next
) {
7211 int il_offset
= ins
->il_offset
;
7212 // If two instructions have the same il_offset, then the second one
7213 // cannot be the start of a basic block.
7214 gboolean is_bb_start
= il_offset
!= -1 && td
->is_bb_start
[il_offset
] && il_offset
!= last_il_offset
;
7215 last_il_offset
= il_offset
;
7216 if (ins
->opcode
== MINT_NOP
)
7219 // Prevent optimizations spanning multiple basic blocks
7223 if (ins
->opcode
>= MINT_LDFLD_I1
&& ins
->opcode
<= MINT_LDFLD_P
&& prev1_ins
) {
7224 if (prev1_ins
->opcode
== MINT_LDLOC_O
) {
7225 int loc_index
= prev1_ins
->data
[0];
7226 int fld_offset
= ins
->data
[0];
7227 int mt
= ins
->opcode
- MINT_LDFLD_I1
;
7228 ins
= interp_insert_ins (td
, ins
, MINT_LDLOCFLD_I1
+ mt
);
7229 ins
->data
[0] = loc_index
;
7230 ins
->data
[1] = fld_offset
;
7231 interp_clear_ins (td
, ins
->prev
);
7232 interp_clear_ins (td
, prev1_ins
);
7234 mono_interp_stats
.super_instructions
++;
7235 mono_interp_stats
.killed_instructions
++;
7236 } else if (prev1_ins
->opcode
== MINT_LDARG_O
|| prev1_ins
->opcode
== MINT_LDARG_P0
) {
7238 int fld_offset
= ins
->data
[0];
7239 int mt
= ins
->opcode
- MINT_LDFLD_I1
;
7240 if (prev1_ins
->opcode
== MINT_LDARG_O
)
7241 arg_index
= prev1_ins
->data
[0];
7242 ins
= interp_insert_ins (td
, ins
, MINT_LDARGFLD_I1
+ mt
);
7243 ins
->data
[0] = arg_index
;
7244 ins
->data
[1] = fld_offset
;
7245 interp_clear_ins (td
, ins
->prev
);
7246 interp_clear_ins (td
, prev1_ins
);
7248 mono_interp_stats
.super_instructions
++;
7249 mono_interp_stats
.killed_instructions
++;
7251 } else if (MINT_IS_STLOC (ins
->opcode
) && prev1_ins
&& prev2_ins
) {
7252 if (prev1_ins
->opcode
== MINT_ADD1_I4
|| prev1_ins
->opcode
== MINT_ADD1_I8
||
7253 prev1_ins
->opcode
== MINT_SUB1_I4
|| prev1_ins
->opcode
== MINT_SUB1_I8
) {
7254 if (MINT_IS_LDLOC (prev2_ins
->opcode
) && prev2_ins
->data
[0] == ins
->data
[0]) {
7255 if (prev1_ins
->opcode
== MINT_ADD1_I4
)
7256 ins
->opcode
= MINT_LOCADD1_I4
;
7257 else if (prev1_ins
->opcode
== MINT_ADD1_I8
)
7258 ins
->opcode
= MINT_LOCADD1_I8
;
7259 else if (prev1_ins
->opcode
== MINT_SUB1_I4
)
7260 ins
->opcode
= MINT_LOCSUB1_I4
;
7262 ins
->opcode
= MINT_LOCSUB1_I8
;
7263 // the local index is already set inside the replaced STLOC instruction
7264 interp_clear_ins (td
, prev1_ins
);
7265 interp_clear_ins (td
, prev2_ins
);
7267 mono_interp_stats
.super_instructions
++;
7268 mono_interp_stats
.killed_instructions
+= 2;
7272 prev2_ins
= prev1_ins
;
7278 interp_optimize_code (TransformData
*td
)
7280 if (mono_interp_opt
& INTERP_OPT_CPROP
)
7281 MONO_TIME_TRACK (mono_interp_stats
.cprop_time
, interp_cprop (td
));
7283 if (mono_interp_opt
& INTERP_OPT_SUPER_INSTRUCTIONS
)
7284 MONO_TIME_TRACK (mono_interp_stats
.super_instructions_time
, interp_super_instructions (td
));
7288 generate (MonoMethod
*method
, MonoMethodHeader
*header
, InterpMethod
*rtm
, MonoGenericContext
*generic_context
, MonoError
*error
)
7290 MonoDomain
*domain
= rtm
->domain
;
7292 TransformData transform_data
;
7294 static gboolean verbose_method_inited
;
7295 static char* verbose_method_name
;
7297 if (!verbose_method_inited
) {
7298 verbose_method_name
= g_getenv ("MONO_VERBOSE_METHOD");
7299 verbose_method_inited
= TRUE
;
7302 memset (&transform_data
, 0, sizeof(transform_data
));
7303 td
= &transform_data
;
7305 td
->method
= method
;
7307 td
->code_size
= header
->code_size
;
7308 td
->header
= header
;
7309 td
->max_code_size
= td
->code_size
;
7310 td
->in_offsets
= (int*)g_malloc0((header
->code_size
+ 1) * sizeof(int));
7311 td
->stack_height
= (int*)g_malloc(header
->code_size
* sizeof(int));
7312 td
->stack_state
= (StackInfo
**)g_malloc0(header
->code_size
* sizeof(StackInfo
*));
7313 td
->vt_stack_size
= (int*)g_malloc(header
->code_size
* sizeof(int));
7314 td
->clause_indexes
= (int*)g_malloc (header
->code_size
* sizeof (int));
7315 td
->is_bb_start
= (guint8
*)g_malloc0(header
->code_size
);
7316 td
->mempool
= mono_mempool_new ();
7317 td
->n_data_items
= 0;
7318 td
->max_data_items
= 0;
7319 td
->data_items
= NULL
;
7320 td
->data_hash
= g_hash_table_new (NULL
, NULL
);
7321 #ifdef ENABLE_EXPERIMENT_TIERED
7322 td
->patchsite_hash
= g_hash_table_new (NULL
, NULL
);
7324 td
->gen_sdb_seq_points
= mini_debug_options
.gen_sdb_seq_points
;
7325 td
->seq_points
= g_ptr_array_new ();
7326 td
->verbose_level
= mono_interp_traceopt
;
7327 rtm
->data_items
= td
->data_items
;
7329 interp_method_compute_offsets (td
, rtm
, mono_method_signature_internal (method
), header
);
7331 if (verbose_method_name
) {
7332 const char *name
= verbose_method_name
;
7334 if ((strchr (name
, '.') > name
) || strchr (name
, ':')) {
7335 MonoMethodDesc
*desc
;
7337 desc
= mono_method_desc_new (name
, TRUE
);
7338 if (mono_method_desc_full_match (desc
, method
)) {
7339 td
->verbose_level
= 4;
7341 mono_method_desc_free (desc
);
7343 if (strcmp (method
->name
, name
) == 0)
7344 td
->verbose_level
= 4;
7348 td
->stack
= (StackInfo
*)g_malloc0 ((header
->max_stack
+ 1) * sizeof (td
->stack
[0]));
7349 td
->stack_capacity
= header
->max_stack
+ 1;
7351 td
->max_stack_height
= 0;
7352 td
->line_numbers
= g_array_new (FALSE
, TRUE
, sizeof (MonoDebugLineNumberEntry
));
7353 td
->current_il_offset
= -1;
7355 generate_code (td
, method
, header
, generic_context
, error
);
7356 goto_if_nok (error
, exit
);
7358 interp_optimize_code (td
);
7360 generate_compacted_code (td
);
7362 if (td
->verbose_level
) {
7363 g_print ("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method
, TRUE
), rtm
, td
->max_vt_sp
);
7364 g_print ("Calculated stack size: %d, stated size: %d\n", td
->max_stack_height
, header
->max_stack
);
7365 dump_mint_code (td
->new_code
, td
->new_code_end
);
7368 /* Check if we use excessive stack space */
7369 if (td
->max_stack_height
> header
->max_stack
* 3 && header
->max_stack
> 16)
7370 g_warning ("Excessive stack space usage for method %s, %d/%d", method
->name
, td
->max_stack_height
, header
->max_stack
);
7372 int code_len_u8
, code_len_u16
;
7373 code_len_u8
= (guint8
*) td
->new_code_end
- (guint8
*) td
->new_code
;
7374 code_len_u16
= td
->new_code_end
- td
->new_code
;
7376 rtm
->clauses
= (MonoExceptionClause
*)mono_domain_alloc0 (domain
, header
->num_clauses
* sizeof (MonoExceptionClause
));
7377 memcpy (rtm
->clauses
, header
->clauses
, header
->num_clauses
* sizeof(MonoExceptionClause
));
7378 rtm
->code
= (gushort
*)td
->new_code
;
7379 rtm
->init_locals
= header
->init_locals
;
7380 rtm
->num_clauses
= header
->num_clauses
;
7381 for (i
= 0; i
< header
->num_clauses
; i
++) {
7382 MonoExceptionClause
*c
= rtm
->clauses
+ i
;
7383 int end_off
= c
->try_offset
+ c
->try_len
;
7384 c
->try_offset
= get_in_offset (td
, c
->try_offset
);
7385 c
->try_len
= find_in_offset (td
, end_off
) - c
->try_offset
;
7386 g_assert ((c
->try_offset
+ c
->try_len
) < code_len_u16
);
7387 end_off
= c
->handler_offset
+ c
->handler_len
;
7388 c
->handler_offset
= get_in_offset (td
, c
->handler_offset
);
7389 c
->handler_len
= find_in_offset (td
, end_off
) - c
->handler_offset
;
7390 g_assert (c
->handler_len
>= 0 && (c
->handler_offset
+ c
->handler_len
) <= code_len_u16
);
7391 if (c
->flags
& MONO_EXCEPTION_CLAUSE_FILTER
)
7392 c
->data
.filter_offset
= get_in_offset (td
, c
->data
.filter_offset
);
7394 rtm
->stack_size
= (sizeof (stackval
)) * (td
->max_stack_height
+ 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/
7395 rtm
->stack_size
= ALIGN_TO (rtm
->stack_size
, MINT_VT_ALIGNMENT
);
7396 rtm
->vt_stack_size
= td
->max_vt_sp
;
7397 rtm
->total_locals_size
= td
->total_locals_size
;
7398 rtm
->alloca_size
= rtm
->total_locals_size
+ rtm
->vt_stack_size
+ rtm
->stack_size
;
7399 rtm
->data_items
= (gpointer
*)mono_domain_alloc0 (domain
, td
->n_data_items
* sizeof (td
->data_items
[0]));
7400 memcpy (rtm
->data_items
, td
->data_items
, td
->n_data_items
* sizeof (td
->data_items
[0]));
7402 /* Save debug info */
7403 interp_save_debug_info (rtm
, header
, td
, td
->line_numbers
);
7405 /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
7407 jinfo_len
= mono_jit_info_size ((MonoJitInfoFlags
)0, header
->num_clauses
, 0);
7409 jinfo
= (MonoJitInfo
*)mono_domain_alloc0 (domain
, jinfo_len
);
7410 jinfo
->is_interp
= 1;
7412 mono_jit_info_init (jinfo
, method
, (guint8
*)rtm
->code
, code_len_u8
, (MonoJitInfoFlags
)0, header
->num_clauses
, 0);
7413 for (i
= 0; i
< jinfo
->num_clauses
; ++i
) {
7414 MonoJitExceptionInfo
*ei
= &jinfo
->clauses
[i
];
7415 MonoExceptionClause
*c
= rtm
->clauses
+ i
;
7417 ei
->flags
= c
->flags
;
7418 ei
->try_start
= (guint8
*)(rtm
->code
+ c
->try_offset
);
7419 ei
->try_end
= (guint8
*)(rtm
->code
+ c
->try_offset
+ c
->try_len
);
7420 ei
->handler_start
= (guint8
*)(rtm
->code
+ c
->handler_offset
);
7421 ei
->exvar_offset
= rtm
->exvar_offsets
[i
];
7422 if (ei
->flags
== MONO_EXCEPTION_CLAUSE_FILTER
) {
7423 ei
->data
.filter
= (guint8
*)(rtm
->code
+ c
->data
.filter_offset
);
7424 } else if (ei
->flags
== MONO_EXCEPTION_CLAUSE_FINALLY
) {
7425 ei
->data
.handler_end
= (guint8
*)(rtm
->code
+ c
->handler_offset
+ c
->handler_len
);
7427 ei
->data
.catch_class
= c
->data
.catch_class
;
7431 save_seq_points (td
, jinfo
);
7432 #ifdef ENABLE_EXPERIMENT_TIERED
7433 /* debugging aid, it makes `mono_pmip` work. */
7434 mono_jit_info_table_add (domain
, jinfo
);
7438 g_free (td
->in_offsets
);
7439 g_free (td
->stack_height
);
7440 for (i
= 0; i
< header
->code_size
; ++i
)
7441 g_free (td
->stack_state
[i
]);
7442 g_free (td
->stack_state
);
7443 g_free (td
->vt_stack_size
);
7444 g_free (td
->clause_indexes
);
7445 g_free (td
->data_items
);
7447 g_free (td
->is_bb_start
);
7448 g_free (td
->locals
);
7449 g_hash_table_destroy (td
->data_hash
);
7450 #ifdef ENABLE_EXPERIMENT_TIERED
7451 g_hash_table_destroy (td
->patchsite_hash
);
7453 g_ptr_array_free (td
->seq_points
, TRUE
);
7454 g_array_free (td
->line_numbers
, TRUE
);
7455 mono_mempool_destroy (td
->mempool
);
7458 static mono_mutex_t calc_section
;
7460 #ifdef ENABLE_EXPERIMENT_TIERED
7462 tiered_patcher (MiniTieredPatchPointContext
*ctx
, gpointer patchsite
)
7465 MonoMethod
*m
= ctx
->target_method
;
7467 if (!jit_call2_supported (m
, mono_method_signature_internal (m
)))
7470 /* TODO: Force compilation here. Currently the JIT will be invoked upon
7471 * first execution of `MINT_JIT_CALL2`. */
7472 InterpMethod
*rmethod
= mono_interp_get_imethod (ctx
->domain
, m
, error
);
7473 mono_error_assert_ok (error
);
7475 guint16
*ip
= ((guint16
*) patchsite
);
7476 *ip
++ = MINT_JIT_CALL2
;
7477 /* FIXME: this only works on 64bit */
7478 WRITE64 (ip
, &rmethod
);
7479 mono_memory_barrier ();
7487 mono_interp_transform_init (void)
7489 mono_os_mutex_init_recursive(&calc_section
);
7491 #ifdef ENABLE_EXPERIMENT_TIERED
7492 mini_tiered_register_callsite_patcher (tiered_patcher
, TIERED_PATCH_KIND_INTERP
);
7497 mono_interp_transform_method (InterpMethod
*imethod
, ThreadContext
*context
, MonoError
*error
)
7499 MonoMethod
*method
= imethod
->method
;
7500 MonoMethodHeader
*header
= NULL
;
7501 MonoMethodSignature
*signature
= mono_method_signature_internal (method
);
7502 MonoVTable
*method_class_vt
;
7503 MonoGenericContext
*generic_context
= NULL
;
7504 MonoDomain
*domain
= imethod
->domain
;
7505 InterpMethod tmp_imethod
;
7506 InterpMethod
*real_imethod
;
7510 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method
->klass
))) {
7511 mono_error_set_invalid_operation (error
, "%s", "Could not execute the method because the containing type is not fully instantiated.");
7515 // g_printerr ("TRANSFORM(0x%016lx): begin %s::%s\n", mono_thread_current (), method->klass->name, method->name);
7516 method_class_vt
= mono_class_vtable_checked (domain
, imethod
->method
->klass
, error
);
7517 return_if_nok (error
);
7519 if (!method_class_vt
->initialized
) {
7520 mono_runtime_class_init_full (method_class_vt
, error
);
7521 return_if_nok (error
);
7524 MONO_PROFILER_RAISE (jit_begin
, (method
));
7526 if (mono_method_signature_internal (method
)->is_inflated
)
7527 generic_context
= mono_method_get_context (method
);
7529 MonoGenericContainer
*generic_container
= mono_method_get_generic_container (method
);
7530 if (generic_container
)
7531 generic_context
= &generic_container
->context
;
7534 if (method
->iflags
& (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
| METHOD_IMPL_ATTRIBUTE_RUNTIME
)) {
7535 MonoMethod
*nm
= NULL
;
7536 if (imethod
->transformed
) {
7537 MONO_PROFILER_RAISE (jit_done
, (method
, imethod
->jinfo
));
7541 /* assumes all internal calls with an array this are built in... */
7542 if (method
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
&& (! mono_method_signature_internal (method
)->hasthis
|| m_class_get_rank (method
->klass
) == 0)) {
7543 nm
= mono_marshal_get_native_wrapper (method
, FALSE
, FALSE
);
7544 signature
= mono_method_signature_internal (nm
);
7546 const char *name
= method
->name
;
7547 if (m_class_get_parent (method
->klass
) == mono_defaults
.multicastdelegate_class
) {
7548 if (*name
== '.' && (strcmp (name
, ".ctor") == 0)) {
7549 MonoJitICallInfo
*mi
= &mono_get_jit_icall_info ()->ves_icall_mono_delegate_ctor_interp
;
7550 nm
= mono_marshal_get_icall_wrapper (mi
, TRUE
);
7551 } else if (*name
== 'I' && (strcmp (name
, "Invoke") == 0)) {
7553 * Usually handled during transformation of the caller, but
7554 * when the caller is handled by another execution engine
7555 * (for example fullAOT) we need to handle it here. That's
7556 * known to be wrong in cases where the reference to
7557 * `MonoDelegate` would be needed (FIXME).
7559 nm
= mono_marshal_get_delegate_invoke (method
, NULL
);
7560 } else if (*name
== 'B' && (strcmp (name
, "BeginInvoke") == 0)) {
7561 nm
= mono_marshal_get_delegate_begin_invoke (method
);
7562 } else if (*name
== 'E' && (strcmp (name
, "EndInvoke") == 0)) {
7563 nm
= mono_marshal_get_delegate_end_invoke (method
);
7567 g_assert_not_reached ();
7570 mono_os_mutex_lock (&calc_section
);
7571 imethod
->stack_size
= sizeof (stackval
); /* for tracing */
7572 imethod
->alloca_size
= imethod
->stack_size
;
7573 mono_memory_barrier ();
7574 imethod
->transformed
= TRUE
;
7575 mono_interp_stats
.methods_transformed
++;
7576 mono_os_mutex_unlock (&calc_section
);
7577 MONO_PROFILER_RAISE (jit_done
, (method
, NULL
));
7581 header
= interp_method_get_header (nm
, error
);
7582 return_if_nok (error
);
7586 header
= mono_method_get_header_checked (method
, error
);
7587 return_if_nok (error
);
7590 g_assert ((signature
->param_count
+ signature
->hasthis
) < 1000);
7591 // g_printerr ("TRANSFORM(0x%016lx): end %s::%s\n", mono_thread_current (), method->klass->name, method->name);
7593 /* Make modifications to a copy of imethod, copy them back inside the lock */
7594 real_imethod
= imethod
;
7595 memcpy (&tmp_imethod
, imethod
, sizeof (InterpMethod
));
7596 imethod
= &tmp_imethod
;
7598 MONO_TIME_TRACK (mono_interp_stats
.transform_time
, generate (method
, header
, imethod
, generic_context
, error
));
7600 mono_metadata_free_mh (header
);
7602 return_if_nok (error
);
7604 /* Copy changes back */
7605 imethod
= real_imethod
;
7606 mono_os_mutex_lock (&calc_section
);
7607 if (!imethod
->transformed
) {
7608 // Ignore the first two fields which are unchanged. next_jit_code_hash shouldn't
7609 // be modified because it is racy with internal hash table insert.
7610 const int start_offset
= 2 * sizeof (gpointer
);
7611 memcpy ((char*)imethod
+ start_offset
, (char*)&tmp_imethod
+ start_offset
, sizeof (InterpMethod
) - start_offset
);
7612 mono_memory_barrier ();
7613 imethod
->transformed
= TRUE
;
7614 mono_interp_stats
.methods_transformed
++;
7615 mono_atomic_fetch_add_i32 (&mono_jit_stats
.methods_with_interp
, 1);
7618 mono_os_mutex_unlock (&calc_section
);
7620 mono_domain_lock (domain
);
7621 if (!g_hash_table_lookup (domain_jit_info (domain
)->seq_points
, imethod
->method
))
7622 g_hash_table_insert (domain_jit_info (domain
)->seq_points
, imethod
->method
, imethod
->jinfo
->seq_points
);
7623 mono_domain_unlock (domain
);
7625 // FIXME: Add a different callback ?
7626 MONO_PROFILER_RAISE (jit_done
, (method
, imethod
->jinfo
));