[interp] Remove varargs from InterpFrame and recompute it instead (#16598)
[mono-project.git] / mono / mini / interp / transform.c
blobb73570559d62c3c9d015a6320d7a92ba908e4e45
1 /**
2 * \file
3 * transform CIL into different opcodes for more
4 * efficient interpretation
6 * Written by Bernie Solomon (bernard@ugsolutions.com)
7 * Copyright (c) 2004.
8 */
10 #include "config.h"
11 #include <string.h>
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>
27 #include <mono/mini/mini.h>
28 #include <mono/mini/mini-runtime.h>
30 #include "mintops.h"
31 #include "interp-internals.h"
32 #include "interp.h"
34 #define INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK 1
35 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY 2
36 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT 4
37 #define INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL 8
39 MonoInterpStats mono_interp_stats;
41 #define DEBUG 0
43 typedef struct InterpInst InterpInst;
45 typedef struct
47 MonoClass *klass;
48 unsigned char type;
49 unsigned char flags;
50 } StackInfo;
52 struct InterpInst {
53 guint16 opcode;
54 InterpInst *next, *prev;
55 // If this is -1, this instruction is not logically associated with an IL offset, it is
56 // part of the IL instruction associated with the previous interp instruction.
57 int il_offset;
58 guint32 flags;
59 guint16 data [MONO_ZERO_LEN_ARRAY];
62 typedef struct {
63 guint8 *ip;
64 GSList *preds;
65 GSList *seq_points;
66 SeqPoint *last_seq_point;
68 // This will hold a list of last sequence points of incoming basic blocks
69 SeqPoint **pred_seq_points;
70 guint num_pred_seq_points;
71 } InterpBasicBlock;
73 typedef enum {
74 RELOC_SHORT_BRANCH,
75 RELOC_LONG_BRANCH,
76 RELOC_SWITCH
77 } RelocType;
79 typedef struct {
80 RelocType type;
81 /* In the interpreter IR */
82 int offset;
83 /* In the IL code */
84 int target;
85 } Reloc;
87 typedef struct
89 MonoMethod *method;
90 MonoMethod *inlined_method;
91 MonoMethodHeader *header;
92 InterpMethod *rtm;
93 const unsigned char *il_code;
94 const unsigned char *ip;
95 const unsigned char *in_start;
96 InterpInst *last_ins, *first_ins;
97 int code_size;
98 int *in_offsets;
99 int current_il_offset;
100 StackInfo **stack_state;
101 int *stack_height;
102 int *vt_stack_size;
103 unsigned char *is_bb_start;
104 unsigned short *new_code;
105 unsigned short *new_code_end;
106 unsigned int max_code_size;
107 StackInfo *stack;
108 StackInfo *sp;
109 unsigned int max_stack_height;
110 unsigned int stack_capacity;
111 unsigned int vt_sp;
112 unsigned int max_vt_sp;
113 unsigned int total_locals_size;
114 int n_data_items;
115 int max_data_items;
116 void **data_items;
117 GHashTable *data_hash;
118 int *clause_indexes;
119 gboolean gen_sdb_seq_points;
120 GPtrArray *seq_points;
121 InterpBasicBlock **offset_to_bb;
122 InterpBasicBlock *entry_bb;
123 MonoMemPool *mempool;
124 GList *basic_blocks;
125 GPtrArray *relocs;
126 gboolean verbose_level;
127 GArray *line_numbers;
128 } TransformData;
130 #define STACK_TYPE_I4 0
131 #define STACK_TYPE_I8 1
132 #define STACK_TYPE_R4 2
133 #define STACK_TYPE_R8 3
134 #define STACK_TYPE_O 4
135 #define STACK_TYPE_VT 5
136 #define STACK_TYPE_MP 6
137 #define STACK_TYPE_F 7
139 static const char *stack_type_string [] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " };
141 #if SIZEOF_VOID_P == 8
142 #define STACK_TYPE_I STACK_TYPE_I8
143 #else
144 #define STACK_TYPE_I STACK_TYPE_I4
145 #endif
147 static int stack_type [] = {
148 STACK_TYPE_I4, /*I1*/
149 STACK_TYPE_I4, /*U1*/
150 STACK_TYPE_I4, /*I2*/
151 STACK_TYPE_I4, /*U2*/
152 STACK_TYPE_I4, /*I4*/
153 STACK_TYPE_I8, /*I8*/
154 STACK_TYPE_R4, /*R4*/
155 STACK_TYPE_R8, /*R8*/
156 STACK_TYPE_O, /*O*/
157 STACK_TYPE_MP, /*P*/
158 STACK_TYPE_VT
161 #if SIZEOF_VOID_P == 8
162 #define MINT_NEG_P MINT_NEG_I8
163 #define MINT_NOT_P MINT_NOT_I8
165 #define MINT_NEG_FP MINT_NEG_R8
167 #define MINT_ADD_P MINT_ADD_I8
168 #define MINT_SUB_P MINT_SUB_I8
169 #define MINT_MUL_P MINT_MUL_I8
170 #define MINT_DIV_P MINT_DIV_I8
171 #define MINT_DIV_UN_P MINT_DIV_UN_I8
172 #define MINT_REM_P MINT_REM_I8
173 #define MINT_REM_UN_P MINT_REM_UN_I8
174 #define MINT_AND_P MINT_AND_I8
175 #define MINT_OR_P MINT_OR_I8
176 #define MINT_XOR_P MINT_XOR_I8
177 #define MINT_SHL_P MINT_SHL_I8
178 #define MINT_SHR_P MINT_SHR_I8
179 #define MINT_SHR_UN_P MINT_SHR_UN_I8
181 #define MINT_CEQ_P MINT_CEQ_I8
182 #define MINT_CNE_P MINT_CNE_I8
183 #define MINT_CLT_P MINT_CLT_I8
184 #define MINT_CLT_UN_P MINT_CLT_UN_I8
185 #define MINT_CGT_P MINT_CGT_I8
186 #define MINT_CGT_UN_P MINT_CGT_UN_I8
187 #define MINT_CLE_P MINT_CLE_I8
188 #define MINT_CLE_UN_P MINT_CLE_UN_I8
189 #define MINT_CGE_P MINT_CGE_I8
190 #define MINT_CGE_UN_P MINT_CGE_UN_I8
192 #define MINT_ADD_FP MINT_ADD_R8
193 #define MINT_SUB_FP MINT_SUB_R8
194 #define MINT_MUL_FP MINT_MUL_R8
195 #define MINT_DIV_FP MINT_DIV_R8
196 #define MINT_REM_FP MINT_REM_R8
198 #define MINT_CNE_FP MINT_CNE_R8
199 #define MINT_CEQ_FP MINT_CEQ_R8
200 #define MINT_CGT_FP MINT_CGT_R8
201 #define MINT_CGE_FP MINT_CGE_R8
202 #define MINT_CLT_FP MINT_CLT_R8
203 #define MINT_CLE_FP MINT_CLE_R8
205 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I8
206 #else
208 #define MINT_NEG_P MINT_NEG_I4
209 #define MINT_NOT_P MINT_NOT_I4
211 #define MINT_NEG_FP MINT_NEG_R4
213 #define MINT_ADD_P MINT_ADD_I4
214 #define MINT_SUB_P MINT_SUB_I4
215 #define MINT_MUL_P MINT_MUL_I4
216 #define MINT_DIV_P MINT_DIV_I4
217 #define MINT_DIV_UN_P MINT_DIV_UN_I4
218 #define MINT_REM_P MINT_REM_I4
219 #define MINT_REM_UN_P MINT_REM_UN_I4
220 #define MINT_AND_P MINT_AND_I4
221 #define MINT_OR_P MINT_OR_I4
222 #define MINT_XOR_P MINT_XOR_I4
223 #define MINT_SHL_P MINT_SHL_I4
224 #define MINT_SHR_P MINT_SHR_I4
225 #define MINT_SHR_UN_P MINT_SHR_UN_I4
227 #define MINT_CEQ_P MINT_CEQ_I4
228 #define MINT_CNE_P MINT_CNE_I4
229 #define MINT_CLT_P MINT_CLT_I4
230 #define MINT_CLT_UN_P MINT_CLT_UN_I4
231 #define MINT_CGT_P MINT_CGT_I4
232 #define MINT_CGT_UN_P MINT_CGT_UN_I4
233 #define MINT_CLE_P MINT_CLE_I4
234 #define MINT_CLE_UN_P MINT_CLE_UN_I4
235 #define MINT_CGE_P MINT_CGE_I4
236 #define MINT_CGE_UN_P MINT_CGE_UN_I4
238 #define MINT_ADD_FP MINT_ADD_R4
239 #define MINT_SUB_FP MINT_SUB_R4
240 #define MINT_MUL_FP MINT_MUL_R4
241 #define MINT_DIV_FP MINT_DIV_R4
242 #define MINT_REM_FP MINT_REM_R4
244 #define MINT_CNE_FP MINT_CNE_R4
245 #define MINT_CEQ_FP MINT_CEQ_R4
246 #define MINT_CGT_FP MINT_CGT_R4
247 #define MINT_CGE_FP MINT_CGE_R4
248 #define MINT_CLT_FP MINT_CLT_R4
249 #define MINT_CLE_FP MINT_CLE_R4
251 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I4
252 #endif
254 typedef struct {
255 const gchar *op_name;
256 guint16 insn [3];
257 } MagicIntrinsic;
259 // static const MagicIntrinsic int_binop[] = {
261 static const MagicIntrinsic int_unnop[] = {
262 { "op_UnaryPlus", {MINT_NOP, MINT_NOP, MINT_NOP}},
263 { "op_UnaryNegation", {MINT_NEG_P, MINT_NEG_P, MINT_NEG_FP}},
264 { "op_OnesComplement", {MINT_NOT_P, MINT_NOT_P, MINT_NIY}}
267 static const MagicIntrinsic int_binop[] = {
268 { "op_Addition", {MINT_ADD_P, MINT_ADD_P, MINT_ADD_FP}},
269 { "op_Subtraction", {MINT_SUB_P, MINT_SUB_P, MINT_SUB_FP}},
270 { "op_Multiply", {MINT_MUL_P, MINT_MUL_P, MINT_MUL_FP}},
271 { "op_Division", {MINT_DIV_P, MINT_DIV_UN_P, MINT_DIV_FP}},
272 { "op_Modulus", {MINT_REM_P, MINT_REM_UN_P, MINT_REM_FP}},
273 { "op_BitwiseAnd", {MINT_AND_P, MINT_AND_P, MINT_NIY}},
274 { "op_BitwiseOr", {MINT_OR_P, MINT_OR_P, MINT_NIY}},
275 { "op_ExclusiveOr", {MINT_XOR_P, MINT_XOR_P, MINT_NIY}},
276 { "op_LeftShift", {MINT_SHL_P, MINT_SHL_P, MINT_NIY}},
277 { "op_RightShift", {MINT_SHR_P, MINT_SHR_UN_P, MINT_NIY}},
280 static const MagicIntrinsic int_cmpop[] = {
281 { "op_Inequality", {MINT_CNE_P, MINT_CNE_P, MINT_CNE_FP}},
282 { "op_Equality", {MINT_CEQ_P, MINT_CEQ_P, MINT_CEQ_FP}},
283 { "op_GreaterThan", {MINT_CGT_P, MINT_CGT_UN_P, MINT_CGT_FP}},
284 { "op_GreaterThanOrEqual", {MINT_CGE_P, MINT_CGE_UN_P, MINT_CGE_FP}},
285 { "op_LessThan", {MINT_CLT_P, MINT_CLT_UN_P, MINT_CLT_FP}},
286 { "op_LessThanOrEqual", {MINT_CLE_P, MINT_CLE_UN_P, MINT_CLE_FP}}
289 static gboolean generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error);
291 static InterpInst*
292 interp_new_ins (TransformData *td, guint16 opcode, int len)
294 InterpInst *new_inst;
295 // Size of data region of instruction is length of instruction minus 1 (the opcode slot)
296 new_inst = mono_mempool_alloc0 (td->mempool, sizeof (InterpInst) + sizeof (guint16) * ((len > 0) ? (len - 1) : 0));
297 new_inst->opcode = opcode;
298 new_inst->il_offset = td->current_il_offset;
299 return new_inst;
302 // This version need to be used with switch opcode, which doesn't have constant length
303 static InterpInst*
304 interp_add_ins_explicit (TransformData *td, guint16 opcode, int len)
306 InterpInst *new_inst = interp_new_ins (td, opcode, len);
307 new_inst->prev = td->last_ins;
308 if (td->last_ins)
309 td->last_ins->next = new_inst;
310 else
311 td->first_ins = new_inst;
312 td->last_ins = new_inst;
313 return new_inst;
316 static InterpInst*
317 interp_add_ins (TransformData *td, guint16 opcode)
319 return interp_add_ins_explicit (td, opcode, mono_interp_oplen [opcode]);
322 // This instruction will have the il_offset of the previous instruction
323 static InterpInst*
324 interp_insert_ins (TransformData *td, InterpInst *prev_ins, guint16 opcode)
326 InterpInst *new_inst = interp_new_ins (td, opcode, mono_interp_oplen [opcode]);
327 g_assert (prev_ins);
328 new_inst->il_offset = prev_ins->il_offset;
330 new_inst->prev = prev_ins;
331 new_inst->next = prev_ins->next;
332 prev_ins->next = new_inst;
334 if (new_inst->next == NULL)
335 td->last_ins = new_inst;
336 else
337 new_inst->next->prev = new_inst;
339 return new_inst;
342 static void
343 interp_clear_ins (TransformData *td, InterpInst *ins)
345 // Clearing instead of removing from the list makes everything easier.
346 // We don't change structure of the instruction list, we don't need
347 // to worry about updating the il_offset, or whether this instruction
348 // was at the start of a basic block etc.
349 ins->opcode = MINT_NOP;
352 #define CHECK_STACK(td, n) \
353 do { \
354 int stack_size = (td)->sp - (td)->stack; \
355 if (stack_size < (n)) \
356 g_warning ("%s.%s: not enough values (%d < %d) on stack at %04x", \
357 m_class_get_name ((td)->method->klass), (td)->method->name, \
358 stack_size, n, (td)->ip - (td)->il_code); \
359 } while (0)
361 #define ENSURE_I4(td, sp_off) \
362 do { \
363 if ((td)->sp [-sp_off].type == STACK_TYPE_I8) \
364 interp_add_ins (td, sp_off == 1 ? MINT_CONV_I4_I8 : MINT_CONV_I4_I8_SP); \
365 } while (0)
367 #define CHECK_TYPELOAD(klass) \
368 do { \
369 if (!(klass) || mono_class_has_failure (klass)) { \
370 mono_error_set_for_class_failure (error, klass); \
371 goto exit; \
373 } while (0)
375 #if NO_UNALIGNED_ACCESS
376 #define WRITE32(ip, v) \
377 do { \
378 * (ip) = * (guint16 *)(v); \
379 * ((ip) + 1) = * ((guint16 *)(v) + 1); \
380 (ip) += 2; \
381 } while (0)
383 #define WRITE32_INS(ins, index, v) \
384 do { \
385 (ins)->data [index] = * (guint16 *)(v); \
386 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
387 } while (0)
389 #define WRITE64_INS(ins, index, v) \
390 do { \
391 (ins)->data [index] = * (guint16 *)(v); \
392 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
393 (ins)->data [index + 2] = * ((guint16 *)(v) + 2); \
394 (ins)->data [index + 3] = * ((guint16 *)(v) + 3); \
395 } while (0)
396 #else
397 #define WRITE32(ip, v) \
398 do { \
399 * (guint32*)(ip) = * (guint32 *)(v); \
400 (ip) += 2; \
401 } while (0)
402 #define WRITE32_INS(ins, index, v) \
403 do { \
404 * (guint32 *)(&(ins)->data [index]) = * (guint32 *)(v); \
405 } while (0)
407 #define WRITE64_INS(ins, index, v) \
408 do { \
409 * (guint64 *)(&(ins)->data [index]) = * (guint64 *)(v); \
410 } while (0)
412 #endif
415 static void
416 handle_branch (TransformData *td, int short_op, int long_op, int offset)
418 int shorten_branch = 0;
419 int target = td->ip + offset - td->il_code;
420 if (target < 0 || target >= td->code_size)
421 g_assert_not_reached ();
422 /* Add exception checkpoint or safepoint for backward branches */
423 if (offset < 0) {
424 if (mono_threads_are_safepoints_enabled ())
425 interp_add_ins (td, MINT_SAFEPOINT);
426 else
427 interp_add_ins (td, MINT_CHECKPOINT);
429 if (offset > 0 && td->stack_height [target] < 0) {
430 td->stack_height [target] = td->sp - td->stack;
431 if (td->stack_height [target] > 0)
432 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, td->stack_height [target] * sizeof (td->stack [0]));
433 td->vt_stack_size [target] = td->vt_sp;
436 if (td->header->code_size <= 25000) /* FIX to be precise somehow? */
437 shorten_branch = 1;
439 if (shorten_branch) {
440 interp_add_ins (td, short_op);
441 td->last_ins->data [0] = (guint16) target;
442 } else {
443 interp_add_ins (td, long_op);
444 WRITE32_INS (td->last_ins, 0, &target);
448 static void
449 one_arg_branch(TransformData *td, int mint_op, int offset)
451 int type = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
452 int long_op = mint_op + type - STACK_TYPE_I4;
453 int short_op = long_op + MINT_BRFALSE_I4_S - MINT_BRFALSE_I4;
454 CHECK_STACK(td, 1);
455 --td->sp;
456 handle_branch (td, short_op, long_op, offset);
459 static void
460 two_arg_branch(TransformData *td, int mint_op, int offset)
462 int type1 = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
463 int type2 = td->sp [-2].type == STACK_TYPE_O || td->sp [-2].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-2].type;
464 int long_op = mint_op + type1 - STACK_TYPE_I4;
465 int short_op = long_op + MINT_BEQ_I4_S - MINT_BEQ_I4;
466 CHECK_STACK(td, 2);
467 if (type1 == STACK_TYPE_I4 && type2 == STACK_TYPE_I8) {
468 // The il instruction starts with the actual branch, and not with the conversion opcodes
469 interp_insert_ins (td, td->last_ins, MINT_CONV_I8_I4);
470 } else if (type1 == STACK_TYPE_I8 && type2 == STACK_TYPE_I4) {
471 interp_insert_ins (td, td->last_ins, MINT_CONV_I8_I4_SP);
472 } else if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
473 interp_insert_ins (td, td->last_ins, MINT_CONV_R8_R4);
474 } else if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
475 interp_insert_ins (td, td->last_ins, MINT_CONV_R8_R4_SP);
476 } else if (type1 != type2) {
477 g_warning("%s.%s: branch type mismatch %d %d",
478 m_class_get_name (td->method->klass), td->method->name,
479 td->sp [-1].type, td->sp [-2].type);
481 td->sp -= 2;
482 handle_branch (td, short_op, long_op, offset);
485 static void
486 unary_arith_op(TransformData *td, int mint_op)
488 int op = mint_op + td->sp [-1].type - STACK_TYPE_I4;
489 CHECK_STACK(td, 1);
490 interp_add_ins (td, op);
493 static void
494 binary_arith_op(TransformData *td, int mint_op)
496 int type1 = td->sp [-2].type;
497 int type2 = td->sp [-1].type;
498 int op;
499 #if SIZEOF_VOID_P == 8
500 if ((type1 == STACK_TYPE_MP || type1 == STACK_TYPE_I8) && type2 == STACK_TYPE_I4) {
501 interp_add_ins (td, MINT_CONV_I8_I4);
502 type2 = STACK_TYPE_I8;
504 if (type1 == STACK_TYPE_I4 && (type2 == STACK_TYPE_MP || type2 == STACK_TYPE_I8)) {
505 interp_add_ins (td, MINT_CONV_I8_I4_SP);
506 type1 = STACK_TYPE_I8;
507 td->sp [-2].type = STACK_TYPE_I8;
509 #endif
510 if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
511 interp_add_ins (td, MINT_CONV_R8_R4);
512 type2 = STACK_TYPE_R8;
514 if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
515 interp_add_ins (td, MINT_CONV_R8_R4_SP);
516 type1 = STACK_TYPE_R8;
517 td->sp [-2].type = STACK_TYPE_R8;
519 if (type1 == STACK_TYPE_MP)
520 type1 = STACK_TYPE_I;
521 if (type2 == STACK_TYPE_MP)
522 type2 = STACK_TYPE_I;
523 if (type1 != type2) {
524 g_warning("%s.%s: %04x arith type mismatch %s %d %d",
525 m_class_get_name (td->method->klass), td->method->name,
526 td->ip - td->il_code, mono_interp_opname (mint_op), type1, type2);
528 op = mint_op + type1 - STACK_TYPE_I4;
529 CHECK_STACK(td, 2);
530 interp_add_ins (td, op);
531 --td->sp;
534 static void
535 shift_op(TransformData *td, int mint_op)
537 int op = mint_op + td->sp [-2].type - STACK_TYPE_I4;
538 CHECK_STACK(td, 2);
539 if (td->sp [-1].type != STACK_TYPE_I4) {
540 g_warning("%s.%s: shift type mismatch %d",
541 m_class_get_name (td->method->klass), td->method->name,
542 td->sp [-2].type);
544 interp_add_ins (td, op);
545 --td->sp;
548 static int
549 can_store (int st_value, int vt_value)
551 if (st_value == STACK_TYPE_O || st_value == STACK_TYPE_MP)
552 st_value = STACK_TYPE_I;
553 if (vt_value == STACK_TYPE_O || vt_value == STACK_TYPE_MP)
554 vt_value = STACK_TYPE_I;
555 return st_value == vt_value;
558 #define SET_SIMPLE_TYPE(s, ty) \
559 do { \
560 (s)->type = (ty); \
561 (s)->flags = 0; \
562 (s)->klass = NULL; \
563 } while (0)
565 #define SET_TYPE(s, ty, k) \
566 do { \
567 (s)->type = (ty); \
568 (s)->flags = 0; \
569 (s)->klass = k; \
570 } while (0)
572 #define REALLOC_STACK(td, sppos) \
573 do { \
574 (td)->stack_capacity *= 2; \
575 (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
576 (td)->sp = (td)->stack + (sppos); \
577 } while (0);
579 #define PUSH_SIMPLE_TYPE(td, ty) \
580 do { \
581 int sp_height; \
582 (td)->sp++; \
583 sp_height = (td)->sp - (td)->stack; \
584 if (sp_height > (td)->max_stack_height) \
585 (td)->max_stack_height = sp_height; \
586 if (sp_height > (td)->stack_capacity) \
587 REALLOC_STACK(td, sp_height); \
588 SET_SIMPLE_TYPE((td)->sp - 1, ty); \
589 } while (0)
591 #define PUSH_TYPE(td, ty, k) \
592 do { \
593 int sp_height; \
594 (td)->sp++; \
595 sp_height = (td)->sp - (td)->stack; \
596 if (sp_height > (td)->max_stack_height) \
597 (td)->max_stack_height = sp_height; \
598 if (sp_height > (td)->stack_capacity) \
599 REALLOC_STACK(td, sp_height); \
600 SET_TYPE((td)->sp - 1, ty, k); \
601 } while (0)
603 static void
604 move_stack (TransformData *td, int start, int amount)
606 int sp_height = td->sp - td->stack;
607 int to_move = sp_height - start;
609 td->sp += amount;
610 sp_height += amount;
611 if (amount > 0) {
612 if (sp_height > td->max_stack_height)
613 td->max_stack_height = sp_height;
614 if (sp_height > td->stack_capacity)
615 REALLOC_STACK (td, sp_height);
616 } else {
617 g_assert (td->sp >= td->stack);
620 if (to_move > 0)
621 memmove (td->stack + start + amount, td->stack + start, to_move * sizeof (StackInfo));
624 #define PUSH_VT(td, size) \
625 do { \
626 (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
627 if ((td)->vt_sp > (td)->max_vt_sp) \
628 (td)->max_vt_sp = (td)->vt_sp; \
629 } while (0)
631 #define POP_VT(td, size) \
632 do { \
633 (td)->vt_sp -= ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
634 } while (0)
636 static MonoType*
637 get_arg_type_exact (TransformData *td, int n, int *mt)
639 MonoType *type;
640 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
642 if (hasthis && n == 0)
643 type = m_class_get_byval_arg (td->method->klass);
644 else
645 type = mono_method_signature_internal (td->method)->params [n - !!hasthis];
647 if (mt)
648 *mt = mint_type (type);
650 return type;
653 static void
654 load_arg(TransformData *td, int n)
656 int mt;
657 MonoClass *klass = NULL;
658 MonoType *type;
659 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
661 type = get_arg_type_exact (td, n, &mt);
663 if (mt == MINT_TYPE_VT) {
664 gint32 size;
665 klass = mono_class_from_mono_type_internal (type);
666 if (mono_method_signature_internal (td->method)->pinvoke)
667 size = mono_class_native_size (klass, NULL);
668 else
669 size = mono_class_value_size (klass, NULL);
671 if (hasthis && n == 0) {
672 mt = MINT_TYPE_P;
673 interp_add_ins (td, MINT_LDARG_P);
674 td->last_ins->data [0] = 0;
675 klass = NULL;
676 } else {
677 PUSH_VT (td, size);
678 interp_add_ins (td, MINT_LDARG_VT);
679 td->last_ins->data [0] = n;
680 WRITE32_INS (td->last_ins, 1, &size);
682 } else {
683 if (hasthis && n == 0) {
684 mt = MINT_TYPE_P;
685 interp_add_ins (td, MINT_LDARG_P);
686 td->last_ins->data [0] = n;
687 klass = NULL;
688 } else {
689 interp_add_ins (td, MINT_LDARG_I1 + (mt - MINT_TYPE_I1));
690 td->last_ins->data [0] = n;
691 if (mt == MINT_TYPE_O)
692 klass = mono_class_from_mono_type_internal (type);
695 PUSH_TYPE(td, stack_type[mt], klass);
698 static void
699 store_arg(TransformData *td, int n)
701 int mt;
702 CHECK_STACK (td, 1);
703 MonoType *type;
705 type = get_arg_type_exact (td, n, &mt);
707 if (mt == MINT_TYPE_VT) {
708 gint32 size;
709 MonoClass *klass = mono_class_from_mono_type_internal (type);
710 if (mono_method_signature_internal (td->method)->pinvoke)
711 size = mono_class_native_size (klass, NULL);
712 else
713 size = mono_class_value_size (klass, NULL);
714 interp_add_ins (td, MINT_STARG_VT);
715 td->last_ins->data [0] = n;
716 WRITE32_INS (td->last_ins, 1, &size);
717 if (td->sp [-1].type == STACK_TYPE_VT)
718 POP_VT(td, size);
719 } else {
720 interp_add_ins (td, MINT_STARG_I1 + (mt - MINT_TYPE_I1));
721 td->last_ins->data [0] = n;
723 --td->sp;
726 static void
727 load_local_general (TransformData *td, int offset, MonoType *type)
729 int mt = mint_type (type);
730 MonoClass *klass = NULL;
731 if (mt == MINT_TYPE_VT) {
732 klass = mono_class_from_mono_type_internal (type);
733 gint32 size = mono_class_value_size (klass, NULL);
734 PUSH_VT(td, size);
735 interp_add_ins (td, MINT_LDLOC_VT);
736 td->last_ins->data [0] = offset;
737 WRITE32_INS (td->last_ins, 1, &size);
738 } else {
739 g_assert (mt < MINT_TYPE_VT);
740 if (!td->gen_sdb_seq_points &&
741 mt == MINT_TYPE_I4 && !td->is_bb_start [td->in_start - td->il_code] && td->last_ins != NULL &&
742 td->last_ins->opcode == MINT_STLOC_I4 && td->last_ins->data [0] == offset) {
743 td->last_ins->opcode = MINT_STLOC_NP_I4;
744 } else if (!td->gen_sdb_seq_points &&
745 mt == MINT_TYPE_O && !td->is_bb_start [td->in_start - td->il_code] && td->last_ins != NULL &&
746 td->last_ins->opcode == MINT_STLOC_O && td->last_ins->data [0] == offset) {
747 td->last_ins->opcode = MINT_STLOC_NP_O;
748 } else {
749 interp_add_ins (td, MINT_LDLOC_I1 + (mt - MINT_TYPE_I1));
750 td->last_ins->data [0] = offset; /*FIX for large offset */
752 if (mt == MINT_TYPE_O)
753 klass = mono_class_from_mono_type_internal (type);
755 PUSH_TYPE(td, stack_type[mt], klass);
758 static void
759 load_local (TransformData *td, int n)
761 MonoType *type = td->header->locals [n];
762 int offset = td->rtm->local_offsets [n];
763 load_local_general (td, offset, type);
766 static void
767 store_local_general (TransformData *td, int offset, MonoType *type)
769 int mt = mint_type (type);
770 CHECK_STACK (td, 1);
771 #if SIZEOF_VOID_P == 8
772 if (td->sp [-1].type == STACK_TYPE_I4 && stack_type [mt] == STACK_TYPE_I8) {
773 interp_add_ins (td, MINT_CONV_I8_I4);
774 td->sp [-1].type = STACK_TYPE_I8;
776 #endif
777 if (!can_store(td->sp [-1].type, stack_type [mt])) {
778 g_warning("%s.%s: Store local stack type mismatch %d %d",
779 m_class_get_name (td->method->klass), td->method->name,
780 stack_type [mt], td->sp [-1].type);
782 if (mt == MINT_TYPE_VT) {
783 MonoClass *klass = mono_class_from_mono_type_internal (type);
784 gint32 size = mono_class_value_size (klass, NULL);
785 interp_add_ins (td, MINT_STLOC_VT);
786 td->last_ins->data [0] = offset; /*FIX for large offset */
787 WRITE32_INS (td->last_ins, 1, &size);
788 if (td->sp [-1].type == STACK_TYPE_VT)
789 POP_VT(td, size);
790 } else {
791 g_assert (mt < MINT_TYPE_VT);
792 interp_add_ins (td, MINT_STLOC_I1 + (mt - MINT_TYPE_I1));
793 td->last_ins->data [0] = offset; /*FIX for large offset */
795 --td->sp;
798 static void
799 store_local (TransformData *td, int n)
801 MonoType *type = td->header->locals [n];
802 int offset = td->rtm->local_offsets [n];
803 store_local_general (td, offset, type);
806 #define SIMPLE_OP(td, op) \
807 do { \
808 interp_add_ins (td, op); \
809 ++td->ip; \
810 } while (0)
812 static guint16
813 get_data_item_index (TransformData *td, void *ptr)
815 gpointer p = g_hash_table_lookup (td->data_hash, ptr);
816 guint index;
817 if (p != NULL)
818 return GPOINTER_TO_UINT (p) - 1;
819 if (td->max_data_items == td->n_data_items) {
820 td->max_data_items = td->n_data_items == 0 ? 16 : 2 * td->max_data_items;
821 td->data_items = (gpointer*)g_realloc (td->data_items, td->max_data_items * sizeof(td->data_items [0]));
823 index = td->n_data_items;
824 td->data_items [index] = ptr;
825 ++td->n_data_items;
826 g_hash_table_insert (td->data_hash, ptr, GUINT_TO_POINTER (index + 1));
827 return index;
830 static gboolean
831 jit_call_supported (MonoMethod *method, MonoMethodSignature *sig)
833 GSList *l;
835 if (sig->param_count > 6)
836 return FALSE;
837 if (sig->pinvoke)
838 return FALSE;
839 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
840 return FALSE;
841 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
842 return FALSE;
843 if (method->is_inflated)
844 return FALSE;
845 if (method->string_ctor)
846 return FALSE;
848 if (mono_aot_only && m_class_get_image (method->klass)->aot_module && !(method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) {
849 ERROR_DECL (error);
850 gpointer addr = mono_jit_compile_method_jit_only (method, error);
851 if (addr && is_ok (error))
852 return TRUE;
855 for (l = mono_interp_jit_classes; l; l = l->next) {
856 const char *class_name = (const char*)l->data;
857 // FIXME: Namespaces
858 if (!strcmp (m_class_get_name (method->klass), class_name))
859 return TRUE;
862 //return TRUE;
863 return FALSE;
866 static int mono_class_get_magic_index (MonoClass *k)
868 if (mono_class_is_magic_int (k))
869 return !strcmp ("nint", m_class_get_name (k)) ? 0 : 1;
871 if (mono_class_is_magic_float (k))
872 return 2;
874 return -1;
877 static void
878 interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *target_method)
880 MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_method_access;
882 /* Inject code throwing MethodAccessException */
883 interp_add_ins (td, MINT_MONO_LDPTR);
884 td->last_ins->data [0] = get_data_item_index (td, method);
885 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
887 interp_add_ins (td, MINT_MONO_LDPTR);
888 td->last_ins->data [0] = get_data_item_index (td, target_method);
889 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
891 interp_add_ins (td, MINT_ICALL_PP_V);
892 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
894 td->sp -= 2;
897 static void
898 interp_generate_bie_throw (TransformData *td)
900 MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_bad_image;
902 interp_add_ins (td, MINT_ICALL_V_V);
903 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
907 * These are additional locals that can be allocated as we transform the code.
908 * They are allocated past the method locals so they are accessed in the same
909 * way, with an offset relative to the frame->locals.
911 static int
912 create_interp_local (TransformData *td, MonoType *type)
914 int align, size;
915 int offset = td->total_locals_size;
917 size = mono_type_size (type, &align);
918 offset = ALIGN_TO (offset, align);
920 td->total_locals_size = offset + size;
922 return offset;
925 static void
926 dump_mint_code (const guint16 *start, const guint16* end)
928 const guint16 *p = start;
929 while (p < end) {
930 char *ins = mono_interp_dis_mintop (start, p);
931 g_print ("%s\n", ins);
932 g_free (ins);
933 p = mono_interp_dis_mintop_len (p);
937 /* For debug use */
938 void
939 mono_interp_print_code (InterpMethod *imethod)
941 MonoJitInfo *jinfo = imethod->jinfo;
942 const guint16 *start;
944 if (!jinfo)
945 return;
947 char *name = mono_method_full_name (imethod->method, 1);
948 g_print ("Method : %s\n", name);
949 g_free (name);
951 start = (guint16*) jinfo->code_start;
952 dump_mint_code (start, start + jinfo->code_size);
956 static MonoMethodHeader*
957 interp_method_get_header (MonoMethod* method, MonoError *error)
959 /* An explanation: mono_method_get_header_internal returns an error if
960 * called on a method with no body (e.g. an abstract method, or an
961 * icall). We don't want that.
963 if (mono_method_has_no_body (method))
964 return NULL;
965 else
966 return mono_method_get_header_internal (method, error);
969 /* stores top of stack as local and pushes address of it on stack */
970 static void
971 emit_store_value_as_local (TransformData *td, MonoType *src)
973 int size = mini_magic_type_size (NULL, src);
974 int local_offset = create_interp_local (td, mini_native_type_replace_type (src));
976 store_local_general (td, local_offset, src);
978 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
979 interp_add_ins (td, MINT_LDLOC_VT);
980 td->last_ins->data [0] = local_offset;
981 WRITE32_INS (td->last_ins, 1, &size);
983 PUSH_VT (td, size);
984 PUSH_TYPE (td, STACK_TYPE_VT, NULL);
987 // Returns whether we can optimize away the instructions starting at start.
988 // If any instructions are part of a new basic block, we can't remove them.
989 static gboolean
990 interp_is_bb_start (TransformData *td, InterpInst *start, InterpInst *end)
992 InterpInst *ins = start;
993 while (ins != end) {
994 if (ins->il_offset != -1) {
995 if (td->is_bb_start [ins->il_offset])
996 return TRUE;
998 ins = ins->next;
1000 return FALSE;
1003 static gboolean
1004 interp_ins_is_ldc (InterpInst *ins)
1006 return ins->opcode >= MINT_LDC_I4_M1 && ins->opcode <= MINT_LDC_I8;
1009 static gint32
1010 interp_ldc_i4_get_const (InterpInst *ins)
1012 switch (ins->opcode) {
1013 case MINT_LDC_I4_M1: return -1;
1014 case MINT_LDC_I4_0: return 0;
1015 case MINT_LDC_I4_1: return 1;
1016 case MINT_LDC_I4_2: return 2;
1017 case MINT_LDC_I4_3: return 3;
1018 case MINT_LDC_I4_4: return 4;
1019 case MINT_LDC_I4_5: return 5;
1020 case MINT_LDC_I4_6: return 6;
1021 case MINT_LDC_I4_7: return 7;
1022 case MINT_LDC_I4_8: return 8;
1023 case MINT_LDC_I4_S: return (gint32)(gint8)ins->data [0];
1024 case MINT_LDC_I4: return READ32 (&ins->data [0]);
1025 default:
1026 g_assert_not_reached ();
1030 static int
1031 interp_get_ldind_for_mt (int mt)
1033 switch (mt) {
1034 case MINT_TYPE_I1: return MINT_LDIND_I1_CHECK;
1035 case MINT_TYPE_U1: return MINT_LDIND_U1_CHECK;
1036 case MINT_TYPE_I2: return MINT_LDIND_I2_CHECK;
1037 case MINT_TYPE_U2: return MINT_LDIND_U2_CHECK;
1038 case MINT_TYPE_I4: return MINT_LDIND_I4_CHECK;
1039 case MINT_TYPE_I8: return MINT_LDIND_I8_CHECK;
1040 case MINT_TYPE_R4: return MINT_LDIND_R4_CHECK;
1041 case MINT_TYPE_R8: return MINT_LDIND_R8_CHECK;
1042 case MINT_TYPE_O: return MINT_LDIND_REF;
1043 default:
1044 g_assert_not_reached ();
1046 return -1;
1049 /* Return TRUE if call transformation is finished */
1050 static gboolean
1051 interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClass *constrained_class, MonoMethodSignature *csignature, gboolean readonly, int *op)
1053 const char *tm = target_method->name;
1054 int i;
1055 int type_index = mono_class_get_magic_index (target_method->klass);
1056 gboolean in_corlib = m_class_get_image (target_method->klass) == mono_defaults.corlib;
1057 const char *klass_name_space = m_class_get_name_space (target_method->klass);
1058 const char *klass_name = m_class_get_name (target_method->klass);
1060 if (target_method->klass == mono_defaults.string_class) {
1061 if (tm [0] == 'g') {
1062 if (strcmp (tm, "get_Chars") == 0)
1063 *op = MINT_GETCHR;
1064 else if (strcmp (tm, "get_Length") == 0)
1065 *op = MINT_STRLEN;
1067 } else if (type_index >= 0) {
1068 MonoClass *magic_class = target_method->klass;
1070 const int mt = mint_type (m_class_get_byval_arg (magic_class));
1071 if (!strcmp (".ctor", tm)) {
1072 MonoType *arg = csignature->params [0];
1073 /* depending on SIZEOF_VOID_P and the type of the value passed to the .ctor we either have to CONV it, or do nothing */
1074 int arg_size = mini_magic_type_size (NULL, arg);
1076 if (arg_size > SIZEOF_VOID_P) { // 8 -> 4
1077 switch (type_index) {
1078 case 0: case 1:
1079 interp_add_ins (td, MINT_CONV_I4_I8);
1080 break;
1081 case 2:
1082 interp_add_ins (td, MINT_CONV_R4_R8);
1083 break;
1087 if (arg_size < SIZEOF_VOID_P) { // 4 -> 8
1088 switch (type_index) {
1089 case 0:
1090 interp_add_ins (td, MINT_CONV_I8_I4);
1091 break;
1092 case 1:
1093 interp_add_ins (td, MINT_CONV_I8_U4);
1094 break;
1095 case 2:
1096 interp_add_ins (td, MINT_CONV_R8_R4);
1097 break;
1101 switch (type_index) {
1102 case 0: case 1:
1103 #if SIZEOF_VOID_P == 4
1104 interp_add_ins (td, MINT_STIND_I4);
1105 #else
1106 interp_add_ins (td, MINT_STIND_I8);
1107 #endif
1108 break;
1109 case 2:
1110 #if SIZEOF_VOID_P == 4
1111 interp_add_ins (td, MINT_STIND_R4);
1112 #else
1113 interp_add_ins (td, MINT_STIND_R8);
1114 #endif
1115 break;
1118 td->sp -= 2;
1119 td->ip += 5;
1120 return TRUE;
1121 } else if (!strcmp ("op_Implicit", tm ) || !strcmp ("op_Explicit", tm)) {
1122 MonoType *src = csignature->params [0];
1123 MonoType *dst = csignature->ret;
1124 MonoClass *src_klass = mono_class_from_mono_type_internal (src);
1125 int src_size = mini_magic_type_size (NULL, src);
1126 int dst_size = mini_magic_type_size (NULL, dst);
1128 gboolean store_value_as_local = FALSE;
1130 switch (type_index) {
1131 case 0: case 1:
1132 if (!mini_magic_is_int_type (src) || !mini_magic_is_int_type (dst)) {
1133 if (mini_magic_is_int_type (src))
1134 store_value_as_local = TRUE;
1135 else if (mono_class_is_magic_float (src_klass))
1136 store_value_as_local = TRUE;
1137 else
1138 return FALSE;
1140 break;
1141 case 2:
1142 if (!mini_magic_is_float_type (src) || !mini_magic_is_float_type (dst)) {
1143 if (mini_magic_is_float_type (src))
1144 store_value_as_local = TRUE;
1145 else if (mono_class_is_magic_int (src_klass))
1146 store_value_as_local = TRUE;
1147 else
1148 return FALSE;
1150 break;
1153 if (store_value_as_local) {
1154 emit_store_value_as_local (td, src);
1156 /* emit call to managed conversion method */
1157 return FALSE;
1160 if (src_size > dst_size) { // 8 -> 4
1161 switch (type_index) {
1162 case 0: case 1:
1163 interp_add_ins (td, MINT_CONV_I4_I8);
1164 break;
1165 case 2:
1166 interp_add_ins (td, MINT_CONV_R4_R8);
1167 break;
1171 if (src_size < dst_size) { // 4 -> 8
1172 switch (type_index) {
1173 case 0:
1174 interp_add_ins (td, MINT_CONV_I8_I4);
1175 break;
1176 case 1:
1177 interp_add_ins (td, MINT_CONV_I8_U4);
1178 break;
1179 case 2:
1180 interp_add_ins (td, MINT_CONV_R8_R4);
1181 break;
1185 SET_TYPE (td->sp - 1, stack_type [mint_type (dst)], mono_class_from_mono_type_internal (dst));
1186 td->ip += 5;
1187 return TRUE;
1188 } else if (!strcmp ("op_Increment", tm)) {
1189 g_assert (type_index != 2); // no nfloat
1190 #if SIZEOF_VOID_P == 8
1191 interp_add_ins (td, MINT_ADD1_I8);
1192 #else
1193 interp_add_ins (td, MINT_ADD1_I4);
1194 #endif
1195 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1196 td->ip += 5;
1197 return TRUE;
1198 } else if (!strcmp ("op_Decrement", tm)) {
1199 g_assert (type_index != 2); // no nfloat
1200 #if SIZEOF_VOID_P == 8
1201 interp_add_ins (td, MINT_SUB1_I8);
1202 #else
1203 interp_add_ins (td, MINT_SUB1_I4);
1204 #endif
1205 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1206 td->ip += 5;
1207 return TRUE;
1208 } else if (!strcmp ("CompareTo", tm) || !strcmp ("Equals", tm)) {
1209 MonoType *arg = csignature->params [0];
1211 /* on 'System.n*::{CompareTo,Equals} (System.n*)' variant we need to push managed
1212 * pointer instead of value */
1213 if (arg->type == MONO_TYPE_VALUETYPE)
1214 emit_store_value_as_local (td, arg);
1216 /* emit call to managed conversion method */
1217 return FALSE;
1218 } else if (!strcmp (".cctor", tm)) {
1219 /* white list */
1220 return FALSE;
1221 } else if (!strcmp ("Parse", tm)) {
1222 /* white list */
1223 return FALSE;
1224 } else if (!strcmp ("ToString", tm)) {
1225 /* white list */
1226 return FALSE;
1227 } else if (!strcmp ("GetHashCode", tm)) {
1228 /* white list */
1229 return FALSE;
1230 } else if (!strcmp ("IsNaN", tm) || !strcmp ("IsInfinity", tm) || !strcmp ("IsNegativeInfinity", tm) || !strcmp ("IsPositiveInfinity", tm)) {
1231 g_assert (type_index == 2); // nfloat only
1232 /* white list */
1233 return FALSE;
1236 for (i = 0; i < sizeof (int_unnop) / sizeof (MagicIntrinsic); ++i) {
1237 if (!strcmp (int_unnop [i].op_name, tm)) {
1238 interp_add_ins (td, int_unnop [i].insn [type_index]);
1239 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1240 td->ip += 5;
1241 return TRUE;
1245 for (i = 0; i < sizeof (int_binop) / sizeof (MagicIntrinsic); ++i) {
1246 if (!strcmp (int_binop [i].op_name, tm)) {
1247 interp_add_ins (td, int_binop [i].insn [type_index]);
1248 td->sp -= 1;
1249 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1250 td->ip += 5;
1251 return TRUE;
1255 for (i = 0; i < sizeof (int_cmpop) / sizeof (MagicIntrinsic); ++i) {
1256 if (!strcmp (int_cmpop [i].op_name, tm)) {
1257 MonoClass *k = mono_defaults.boolean_class;
1258 interp_add_ins (td, int_cmpop [i].insn [type_index]);
1259 td->sp -= 1;
1260 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1261 td->ip += 5;
1262 return TRUE;
1266 g_error ("TODO: interp_transform_call %s:%s", m_class_get_name (target_method->klass), tm);
1267 } else if (mono_class_is_subclass_of_internal (target_method->klass, mono_defaults.array_class, FALSE)) {
1268 if (!strcmp (tm, "get_Rank")) {
1269 *op = MINT_ARRAY_RANK;
1270 } else if (!strcmp (tm, "get_Length")) {
1271 *op = MINT_LDLEN;
1272 } else if (!strcmp (tm, "Address")) {
1273 *op = readonly ? MINT_LDELEMA : MINT_LDELEMA_TC;
1274 } else if (!strcmp (tm, "UnsafeMov") || !strcmp (tm, "UnsafeLoad") || !strcmp (tm, "Set") || !strcmp (tm, "Get")) {
1275 *op = MINT_CALLRUN;
1276 } else if (!strcmp (tm, "UnsafeStore")) {
1277 g_error ("TODO ArrayClass::UnsafeStore");
1279 } else if (in_corlib &&
1280 !strcmp (klass_name_space, "System.Diagnostics") &&
1281 !strcmp (klass_name, "Debugger")) {
1282 if (!strcmp (tm, "Break") && csignature->param_count == 0) {
1283 if (mini_should_insert_breakpoint (td->method))
1284 *op = MINT_BREAK;
1286 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "ByReference`1")) {
1287 g_assert (!strcmp (tm, "get_Value"));
1288 *op = MINT_INTRINS_BYREFERENCE_GET_VALUE;
1289 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "Math") && csignature->param_count == 1 && csignature->params [0]->type == MONO_TYPE_R8) {
1290 if (tm [0] == 'A') {
1291 if (strcmp (tm, "Abs") == 0 && csignature->params [0]->type == MONO_TYPE_R8) {
1292 *op = MINT_ABS;
1293 } else if (strcmp (tm, "Asin") == 0){
1294 *op = MINT_ASIN;
1295 } else if (strcmp (tm, "Asinh") == 0){
1296 *op = MINT_ASINH;
1297 } else if (strcmp (tm, "Acos") == 0){
1298 *op = MINT_ACOS;
1299 } else if (strcmp (tm, "Acosh") == 0){
1300 *op = MINT_ACOSH;
1301 } else if (strcmp (tm, "Atan") == 0){
1302 *op = MINT_ATAN;
1303 } else if (strcmp (tm, "Atanh") == 0){
1304 *op = MINT_ATANH;
1306 } else if (tm [0] == 'C') {
1307 if (strcmp (tm, "Cos") == 0) {
1308 *op = MINT_COS;
1309 } else if (strcmp (tm, "Cbrt") == 0){
1310 *op = MINT_CBRT;
1311 } else if (strcmp (tm, "Cosh") == 0){
1312 *op = MINT_COSH;
1314 } else if (tm [0] == 'S') {
1315 if (strcmp (tm, "Sin") == 0) {
1316 *op = MINT_SIN;
1317 } else if (strcmp (tm, "Sqrt") == 0) {
1318 *op = MINT_SQRT;
1319 } else if (strcmp (tm, "Sinh") == 0){
1320 *op = MINT_SINH;
1322 } else if (tm [0] == 'T') {
1323 if (strcmp (tm, "Tan") == 0) {
1324 *op = MINT_TAN;
1325 } else if (strcmp (tm, "Tanh") == 0){
1326 *op = MINT_TANH;
1329 } else if (in_corlib && !strcmp (klass_name_space, "System") && (!strcmp (klass_name, "Span`1") || !strcmp (klass_name, "ReadOnlySpan`1"))) {
1330 if (!strcmp (tm, "get_Item")) {
1331 MonoGenericClass *gclass = mono_class_get_generic_class (target_method->klass);
1332 MonoClass *param_class = mono_class_from_mono_type_internal (gclass->context.class_inst->type_argv [0]);
1334 if (!mini_is_gsharedvt_variable_klass (param_class)) {
1335 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1336 g_assert (length_field);
1337 int offset_length = length_field->offset - sizeof (MonoObject);
1339 MonoClassField *ptr_field = mono_class_get_field_from_name_full (target_method->klass, "_pointer", NULL);
1340 g_assert (ptr_field);
1341 int offset_pointer = ptr_field->offset - sizeof (MonoObject);
1343 int size = mono_class_array_element_size (param_class);
1344 interp_add_ins (td, MINT_GETITEM_SPAN);
1345 td->last_ins->data [0] = size;
1346 td->last_ins->data [1] = offset_length;
1347 td->last_ins->data [2] = offset_pointer;
1349 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1350 td->sp -= 1;
1351 td->ip += 5;
1352 return TRUE;
1354 } else if (!strcmp (tm, "get_Length")) {
1355 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1356 g_assert (length_field);
1357 int offset_length = length_field->offset - sizeof (MonoObject);
1358 interp_add_ins (td, MINT_LDLEN_SPAN);
1359 td->last_ins->data [0] = offset_length;
1360 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1361 td->ip += 5;
1362 return TRUE;
1364 } else if (in_corlib && !strcmp (klass_name_space, "Internal.Runtime.CompilerServices") && !strcmp (klass_name, "Unsafe")) {
1365 #ifdef ENABLE_NETCORE
1366 if (!strcmp (tm, "AddByteOffset"))
1367 *op = MINT_INTRINS_UNSAFE_ADD_BYTE_OFFSET;
1368 else if (!strcmp (tm, "ByteOffset"))
1369 *op = MINT_INTRINS_UNSAFE_BYTE_OFFSET;
1370 else if (!strcmp (tm, "As") || !strcmp (tm, "AsRef"))
1371 *op = MINT_NOP;
1372 else if (!strcmp (tm, "AsPointer")) {
1373 /* NOP */
1374 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1375 td->ip += 5;
1376 return TRUE;
1377 } else if (!strcmp (tm, "IsAddressLessThan")) {
1378 MonoGenericContext *ctx = mono_method_get_context (target_method);
1379 g_assert (ctx);
1380 g_assert (ctx->method_inst);
1381 g_assert (ctx->method_inst->type_argc == 1);
1383 MonoClass *k = mono_defaults.boolean_class;
1384 interp_add_ins (td, MINT_CLT_UN_P);
1385 td->sp -= 1;
1386 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1387 td->ip += 5;
1388 return TRUE;
1389 } else if (!strcmp (tm, "SizeOf")) {
1390 MonoGenericContext *ctx = mono_method_get_context (target_method);
1391 g_assert (ctx);
1392 g_assert (ctx->method_inst);
1393 g_assert (ctx->method_inst->type_argc == 1);
1394 MonoType *t = ctx->method_inst->type_argv [0];
1395 int align;
1396 int esize = mono_type_size (t, &align);
1397 interp_add_ins (td, MINT_LDC_I4);
1398 WRITE32_INS (td->last_ins, 0, &esize);
1399 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1400 td->ip += 5;
1401 return TRUE;
1402 } else if (!strcmp (tm, "AreSame")) {
1403 *op = MINT_CEQ_P;
1405 #endif
1406 } else if (in_corlib && !strcmp (klass_name_space, "System.Runtime.CompilerServices") && !strcmp (klass_name, "RuntimeHelpers")) {
1407 #ifdef ENABLE_NETCORE
1408 if (!strcmp (tm, "IsBitwiseEquatable")) {
1409 g_assert (csignature->param_count == 0);
1410 MonoGenericContext *ctx = mono_method_get_context (target_method);
1411 g_assert (ctx);
1412 g_assert (ctx->method_inst);
1413 g_assert (ctx->method_inst->type_argc == 1);
1414 MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]);
1416 if (MONO_TYPE_IS_PRIMITIVE (t) && t->type != MONO_TYPE_R4 && t->type != MONO_TYPE_R8)
1417 *op = MINT_LDC_I4_1;
1418 else
1419 *op = MINT_LDC_I4_0;
1420 } else if (!strcmp (tm, "ObjectHasComponentSize")) {
1421 *op = MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE;
1423 #endif
1424 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "RuntimeMethodHandle") && !strcmp (tm, "GetFunctionPointer") && csignature->param_count == 1) {
1425 // We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter
1426 *op = MINT_LDFTN_DYNAMIC;
1427 } else if (in_corlib && target_method->klass == mono_defaults.object_class) {
1428 if (!strcmp (tm, "InternalGetHashCode"))
1429 *op = MINT_INTRINS_GET_HASHCODE;
1430 #ifdef DISABLE_REMOTING
1431 else if (!strcmp (tm, "GetType"))
1432 *op = MINT_INTRINS_GET_TYPE;
1433 #endif
1434 #ifdef ENABLE_NETCORE
1435 else if (!strcmp (tm, "GetRawData")) {
1436 #if SIZEOF_VOID_P == 8
1437 interp_add_ins (td, MINT_LDC_I8_S);
1438 #else
1439 interp_add_ins (td, MINT_LDC_I4_S);
1440 #endif
1441 td->last_ins->data [0] = (gint16) MONO_ABI_SIZEOF (MonoObject);
1443 interp_add_ins (td, MINT_ADD_P);
1444 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1446 td->ip += 5;
1447 return TRUE;
1449 #endif
1450 } else if (in_corlib && target_method->klass == mono_defaults.enum_class && !strcmp (tm, "HasFlag")) {
1451 gboolean intrinsify = FALSE;
1452 MonoClass *base_klass = NULL;
1453 if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1454 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1455 td->last_ins->prev->prev && td->last_ins->prev->prev->opcode == MINT_BOX &&
1456 td->sp [-2].klass == td->sp [-1].klass &&
1457 !interp_is_bb_start (td, td->last_ins->prev->prev, NULL) &&
1458 !td->is_bb_start [td->in_start - td->il_code]) {
1459 // csc pattern : box, ldc, box, call HasFlag
1460 g_assert (m_class_is_enumtype (td->sp [-2].klass));
1461 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (td->sp [-2].klass));
1462 base_klass = mono_class_from_mono_type_internal (base_type);
1464 // Remove the boxing of valuetypes
1465 interp_clear_ins (td, td->last_ins->prev->prev);
1466 interp_clear_ins (td, td->last_ins);
1468 intrinsify = TRUE;
1469 } else if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1470 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1471 constrained_class && td->sp [-1].klass == constrained_class &&
1472 !interp_is_bb_start (td, td->last_ins->prev, NULL) &&
1473 !td->is_bb_start [td->in_start - td->il_code]) {
1474 // mcs pattern : ldc, box, constrained Enum, call HasFlag
1475 g_assert (m_class_is_enumtype (constrained_class));
1476 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (constrained_class));
1477 base_klass = mono_class_from_mono_type_internal (base_type);
1478 int mt = mint_type (m_class_get_byval_arg (base_klass));
1480 // Remove boxing and load the value of this
1481 interp_clear_ins (td, td->last_ins);
1482 interp_insert_ins (td, td->last_ins->prev->prev, interp_get_ldind_for_mt (mt));
1484 intrinsify = TRUE;
1486 if (intrinsify) {
1487 interp_add_ins (td, MINT_INTRINS_ENUM_HASFLAG);
1488 td->last_ins->data [0] = get_data_item_index (td, base_klass);
1489 td->sp -= 2;
1490 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1491 td->ip += 5;
1492 return TRUE;
1494 } else if (in_corlib && !strcmp (klass_name_space, "System.Threading") && !strcmp (klass_name, "Interlocked")) {
1495 #if ENABLE_NETCORE
1496 if (!strcmp (tm, "MemoryBarrier") && csignature->param_count == 0)
1497 *op = MINT_MONO_MEMORY_BARRIER;
1498 #endif
1499 } else if (in_corlib &&
1500 !strcmp (klass_name_space, "System.Runtime.CompilerServices") &&
1501 !strcmp (klass_name, "JitHelpers") &&
1502 (!strcmp (tm, "EnumEquals") || !strcmp (tm, "EnumCompareTo"))) {
1503 MonoGenericContext *ctx = mono_method_get_context (target_method);
1504 g_assert (ctx);
1505 g_assert (ctx->method_inst);
1506 g_assert (ctx->method_inst->type_argc == 1);
1507 g_assert (csignature->param_count == 2);
1509 MonoType *t = ctx->method_inst->type_argv [0];
1510 t = mini_get_underlying_type (t);
1512 gboolean is_i8 = (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_U8);
1513 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);
1515 gboolean is_compareto = strcmp (tm, "EnumCompareTo") == 0;
1516 if (is_compareto) {
1517 int offseta, offsetb;
1518 offseta = create_interp_local (td, t);
1519 offsetb = create_interp_local (td, t);
1521 // Save arguments
1522 store_local_general (td, offsetb, t);
1523 store_local_general (td, offseta, t);
1524 // (a > b)
1525 load_local_general (td, offseta, t);
1526 load_local_general (td, offsetb, t);
1527 if (is_unsigned)
1528 interp_add_ins (td, is_i8 ? MINT_CGT_UN_I8 : MINT_CGT_UN_I4);
1529 else
1530 interp_add_ins (td, is_i8 ? MINT_CGT_I8 : MINT_CGT_I4);
1531 td->sp --;
1532 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1533 // (a < b)
1534 load_local_general (td, offseta, t);
1535 load_local_general (td, offsetb, t);
1536 if (is_unsigned)
1537 interp_add_ins (td, is_i8 ? MINT_CLT_UN_I8 : MINT_CLT_UN_I4);
1538 else
1539 interp_add_ins (td, is_i8 ? MINT_CLT_I8 : MINT_CLT_I4);
1540 td->sp --;
1541 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1542 // (a > b) - (a < b)
1543 interp_add_ins (td, MINT_SUB_I4);
1544 td->sp --;
1545 td->ip += 5;
1546 return TRUE;
1547 } else {
1548 if (is_i8) {
1549 *op = MINT_CEQ_I8;
1550 } else {
1551 *op = MINT_CEQ_I4;
1556 return FALSE;
1559 static MonoMethod*
1560 interp_transform_internal_calls (MonoMethod *method, MonoMethod *target_method, MonoMethodSignature *csignature, gboolean is_virtual)
1562 if (method->wrapper_type == MONO_WRAPPER_NONE && target_method != NULL) {
1563 if (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
1564 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1565 if (!is_virtual && target_method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
1566 target_method = mono_marshal_get_synchronized_wrapper (target_method);
1568 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)
1569 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1571 return target_method;
1574 static gboolean
1575 interp_type_as_ptr (MonoType *tp)
1577 if (MONO_TYPE_IS_POINTER (tp))
1578 return TRUE;
1579 if (MONO_TYPE_IS_REFERENCE (tp))
1580 return TRUE;
1581 if ((tp)->type == MONO_TYPE_I4)
1582 return TRUE;
1583 #if SIZEOF_VOID_P == 8
1584 if ((tp)->type == MONO_TYPE_I8)
1585 return TRUE;
1586 #endif
1587 if ((tp)->type == MONO_TYPE_BOOLEAN)
1588 return TRUE;
1589 if ((tp)->type == MONO_TYPE_CHAR)
1590 return TRUE;
1591 if ((tp)->type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (tp->data.klass))
1592 return TRUE;
1593 return FALSE;
1596 #define INTERP_TYPE_AS_PTR(tp) interp_type_as_ptr (tp)
1598 static int
1599 interp_icall_op_for_sig (MonoMethodSignature *sig)
1601 int op = -1;
1602 switch (sig->param_count) {
1603 case 0:
1604 if (MONO_TYPE_IS_VOID (sig->ret))
1605 op = MINT_ICALL_V_V;
1606 else if (INTERP_TYPE_AS_PTR (sig->ret))
1607 op = MINT_ICALL_V_P;
1608 break;
1609 case 1:
1610 if (MONO_TYPE_IS_VOID (sig->ret)) {
1611 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1612 op = MINT_ICALL_P_V;
1613 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1614 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1615 op = MINT_ICALL_P_P;
1617 break;
1618 case 2:
1619 if (MONO_TYPE_IS_VOID (sig->ret)) {
1620 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1621 INTERP_TYPE_AS_PTR (sig->params [1]))
1622 op = MINT_ICALL_PP_V;
1623 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1624 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1625 INTERP_TYPE_AS_PTR (sig->params [1]))
1626 op = MINT_ICALL_PP_P;
1628 break;
1629 case 3:
1630 if (MONO_TYPE_IS_VOID (sig->ret)) {
1631 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1632 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1633 INTERP_TYPE_AS_PTR (sig->params [2]))
1634 op = MINT_ICALL_PPP_V;
1635 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1636 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1637 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1638 INTERP_TYPE_AS_PTR (sig->params [2]))
1639 op = MINT_ICALL_PPP_P;
1641 break;
1642 case 4:
1643 if (MONO_TYPE_IS_VOID (sig->ret)) {
1644 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1645 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1646 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1647 INTERP_TYPE_AS_PTR (sig->params [3]))
1648 op = MINT_ICALL_PPPP_V;
1649 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1650 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1651 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1652 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1653 INTERP_TYPE_AS_PTR (sig->params [3]))
1654 op = MINT_ICALL_PPPP_P;
1656 break;
1657 case 5:
1658 if (MONO_TYPE_IS_VOID (sig->ret)) {
1659 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1660 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1661 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1662 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1663 INTERP_TYPE_AS_PTR (sig->params [4]))
1664 op = MINT_ICALL_PPPPP_V;
1665 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1666 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1667 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1668 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1669 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1670 INTERP_TYPE_AS_PTR (sig->params [4]))
1671 op = MINT_ICALL_PPPPP_P;
1673 break;
1674 case 6:
1675 if (MONO_TYPE_IS_VOID (sig->ret)) {
1676 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1677 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1678 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1679 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1680 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1681 INTERP_TYPE_AS_PTR (sig->params [5]))
1682 op = MINT_ICALL_PPPPPP_V;
1683 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1684 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1685 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1686 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1687 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1688 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1689 INTERP_TYPE_AS_PTR (sig->params [5]))
1690 op = MINT_ICALL_PPPPPP_P;
1692 break;
1694 return op;
1697 #define INLINE_LENGTH_LIMIT 20
1699 static gboolean
1700 interp_method_check_inlining (TransformData *td, MonoMethod *method)
1702 MonoMethodHeaderSummary header;
1704 if (td->method == method)
1705 return FALSE;
1707 if (!mono_method_get_header_summary (method, &header))
1708 return FALSE;
1710 /*runtime, icall and pinvoke are checked by summary call*/
1711 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
1712 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
1713 (mono_class_is_marshalbyref (method->klass)) ||
1714 header.has_clauses)
1715 return FALSE;
1717 if (header.code_size >= INLINE_LENGTH_LIMIT)
1718 return FALSE;
1720 if (mono_class_needs_cctor_run (method->klass, NULL)) {
1721 MonoVTable *vtable;
1722 ERROR_DECL (error);
1723 if (!m_class_get_runtime_info (method->klass))
1724 /* No vtable created yet */
1725 return FALSE;
1726 vtable = mono_class_vtable_checked (td->rtm->domain, method->klass, error);
1727 if (!is_ok (error)) {
1728 mono_error_cleanup (error);
1729 return FALSE;
1731 if (!vtable->initialized)
1732 return FALSE;
1735 /* We currently access at runtime the wrapper data */
1736 if (method->wrapper_type != MONO_WRAPPER_NONE)
1737 return FALSE;
1739 /* Our usage of `emit_store_value_as_local ()` for nint, nuint and nfloat
1740 * is kinda hacky, and doesn't work with the inliner */
1741 if (mono_class_get_magic_index (method->klass) >= 0)
1742 return FALSE;
1744 return TRUE;
1747 static gboolean
1748 interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHeader *header, MonoError *error)
1750 const unsigned char *prev_ip, *prev_il_code, *prev_in_start;
1751 int *prev_in_offsets;
1752 gboolean ret;
1753 unsigned int prev_max_stack_height, prev_max_vt_sp, prev_total_locals_size;
1754 int prev_n_data_items;
1755 int i, prev_vt_sp;
1756 int prev_sp_offset;
1757 MonoGenericContext *generic_context = NULL;
1758 StackInfo *prev_param_area;
1759 MonoMethod *prev_inlined_method;
1760 MonoMethodSignature *csignature = mono_method_signature_internal (target_method);
1761 int nargs = csignature->param_count + !!csignature->hasthis;
1762 InterpInst *prev_last_ins;
1764 if (csignature->is_inflated)
1765 generic_context = mono_method_get_context (target_method);
1766 else {
1767 MonoGenericContainer *generic_container = mono_method_get_generic_container (target_method);
1768 if (generic_container)
1769 generic_context = &generic_container->context;
1772 prev_ip = td->ip;
1773 prev_il_code = td->il_code;
1774 prev_in_start = td->in_start;
1775 prev_sp_offset = td->sp - td->stack;
1776 prev_vt_sp = td->vt_sp;
1777 prev_inlined_method = td->inlined_method;
1778 prev_last_ins = td->last_ins;
1779 td->inlined_method = target_method;
1781 prev_max_stack_height = td->max_stack_height;
1782 prev_max_vt_sp = td->max_vt_sp;
1783 prev_total_locals_size = td->total_locals_size;
1785 prev_n_data_items = td->n_data_items;
1786 prev_in_offsets = td->in_offsets;
1787 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
1789 /* Inlining pops the arguments, restore the stack */
1790 prev_param_area = (StackInfo*)g_malloc (nargs * sizeof (StackInfo));
1791 memcpy (prev_param_area, &td->sp [-nargs], nargs * sizeof (StackInfo));
1793 if (td->verbose_level)
1794 g_print ("Inline start method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1795 ret = generate_code (td, target_method, header, generic_context, error);
1797 if (!ret) {
1798 if (td->verbose_level)
1799 g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1800 td->max_stack_height = prev_max_stack_height;
1801 td->max_vt_sp = prev_max_vt_sp;
1802 td->total_locals_size = prev_total_locals_size;
1805 /* Remove any newly added items */
1806 for (i = prev_n_data_items; i < td->n_data_items; i++) {
1807 g_hash_table_remove (td->data_hash, td->data_items [i]);
1809 td->n_data_items = prev_n_data_items;
1810 td->sp = td->stack + prev_sp_offset;
1811 memcpy (&td->sp [-nargs], prev_param_area, nargs * sizeof (StackInfo));
1812 td->vt_sp = prev_vt_sp;
1813 td->last_ins = prev_last_ins;
1814 if (td->last_ins)
1815 td->last_ins->next = NULL;
1816 UnlockedIncrement (&mono_interp_stats.inline_failures);
1817 } else {
1818 if (td->verbose_level)
1819 g_print ("Inline end method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1820 UnlockedIncrement (&mono_interp_stats.inlined_methods);
1821 // Make sure we have an IR instruction associated with the now removed IL CALL
1822 // FIXME This could be prettier. We might be able to make inlining saner now that
1823 // that we can easily tweak the instruction list.
1824 if (!prev_inlined_method) {
1825 if (prev_last_ins) {
1826 if (prev_last_ins->next)
1827 prev_last_ins->next->il_offset = prev_in_start - prev_il_code;
1828 } else if (td->first_ins) {
1829 td->first_ins->il_offset = prev_in_start - prev_il_code;
1834 td->ip = prev_ip;
1835 td->in_start = prev_in_start;
1836 td->il_code = prev_il_code;
1837 td->inlined_method = prev_inlined_method;
1839 g_free (td->in_offsets);
1840 td->in_offsets = prev_in_offsets;
1842 g_free (prev_param_area);
1843 return ret;
1846 static void
1847 interp_constrained_box (TransformData *td, MonoDomain *domain, MonoClass *constrained_class, MonoMethodSignature *csignature, MonoError *error)
1849 int mt = mint_type (m_class_get_byval_arg (constrained_class));
1850 if (mono_class_is_nullable (constrained_class)) {
1851 g_assert (mt == MINT_TYPE_VT);
1852 interp_add_ins (td, MINT_BOX_NULLABLE);
1853 td->last_ins->data [0] = get_data_item_index (td, constrained_class);
1854 td->last_ins->data [1] = csignature->param_count | ((td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : BOX_NOT_CLEAR_VT_SP);
1855 } else {
1856 MonoVTable *vtable = mono_class_vtable_checked (domain, constrained_class, error);
1857 return_if_nok (error);
1859 if (mt == MINT_TYPE_VT) {
1860 interp_add_ins (td, MINT_BOX_VT);
1861 td->last_ins->data [0] = get_data_item_index (td, vtable);
1862 td->last_ins->data [1] = csignature->param_count | ((td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : BOX_NOT_CLEAR_VT_SP);
1863 } else {
1864 interp_add_ins (td, MINT_BOX);
1865 td->last_ins->data [0] = get_data_item_index (td, vtable);
1866 td->last_ins->data [1] = csignature->param_count;
1871 /* Return FALSE if error, including inline failure */
1872 static gboolean
1873 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)
1875 MonoImage *image = m_class_get_image (method->klass);
1876 MonoMethodSignature *csignature;
1877 int is_virtual = *td->ip == CEE_CALLVIRT;
1878 int calli = *td->ip == CEE_CALLI || *td->ip == CEE_MONO_CALLI_EXTRA_ARG;
1879 int i;
1880 guint32 vt_stack_used = 0;
1881 guint32 vt_res_size = 0;
1882 int op = -1;
1883 int native = 0;
1884 int is_void = 0;
1885 int need_null_check = is_virtual;
1887 guint32 token = read32 (td->ip + 1);
1889 if (target_method == NULL) {
1890 if (calli) {
1891 CHECK_STACK(td, 1);
1892 if (method->wrapper_type != MONO_WRAPPER_NONE)
1893 csignature = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
1894 else {
1895 csignature = mono_metadata_parse_signature_checked (image, token, error);
1896 return_val_if_nok (error, FALSE);
1899 if (generic_context) {
1900 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
1901 return_val_if_nok (error, FALSE);
1905 * The compiled interp entry wrapper is passed to runtime_invoke instead of
1906 * the InterpMethod pointer. FIXME
1908 native = csignature->pinvoke || method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE;
1910 target_method = NULL;
1911 } else {
1912 if (method->wrapper_type == MONO_WRAPPER_NONE) {
1913 target_method = mono_get_method_checked (image, token, NULL, generic_context, error);
1914 return_val_if_nok (error, FALSE);
1915 } else
1916 target_method = (MonoMethod *)mono_method_get_wrapper_data (method, token);
1917 csignature = mono_method_signature_internal (target_method);
1919 if (generic_context) {
1920 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
1921 return_val_if_nok (error, FALSE);
1922 target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, error);
1923 return_val_if_nok (error, FALSE);
1926 } else {
1927 csignature = mono_method_signature_internal (target_method);
1930 if (check_visibility && target_method && !mono_method_can_access_method (method, target_method))
1931 interp_generate_mae_throw (td, method, target_method);
1933 if (target_method && target_method->string_ctor) {
1934 /* Create the real signature */
1935 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (td->mempool, csignature);
1936 ctor_sig->ret = m_class_get_byval_arg (mono_defaults.string_class);
1938 csignature = ctor_sig;
1941 /* Intrinsics */
1942 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
1943 return TRUE;
1945 if (constrained_class) {
1946 if (m_class_is_enumtype (constrained_class) && !strcmp (target_method->name, "GetHashCode")) {
1947 /* Use the corresponding method from the base type to avoid boxing */
1948 MonoType *base_type = mono_class_enum_basetype_internal (constrained_class);
1949 g_assert (base_type);
1950 constrained_class = mono_class_from_mono_type_internal (base_type);
1951 target_method = mono_class_get_method_from_name_checked (constrained_class, target_method->name, 0, 0, error);
1952 mono_error_assert_ok (error);
1953 g_assert (target_method);
1957 if (constrained_class) {
1958 mono_class_setup_vtable (constrained_class);
1959 if (mono_class_has_failure (constrained_class)) {
1960 mono_error_set_for_class_failure (error, constrained_class);
1961 return FALSE;
1963 #if DEBUG_INTERP
1964 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);
1965 #endif
1966 target_method = mono_get_method_constrained_with_method (image, target_method, constrained_class, generic_context, error);
1967 #if DEBUG_INTERP
1968 g_print (" : %s::%s. %s (%p)\n", target_method->klass->name, target_method->name, mono_signature_full_name (target_method->signature), target_method);
1969 #endif
1970 /* Intrinsics: Try again, it could be that `mono_get_method_constrained_with_method` resolves to a method that we can substitute */
1971 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
1972 return TRUE;
1974 return_val_if_nok (error, FALSE);
1975 mono_class_setup_vtable (target_method->klass);
1977 if (!m_class_is_valuetype (constrained_class)) {
1978 /* managed pointer on the stack, we need to deref that puppy */
1979 interp_add_ins (td, MINT_LDIND_I);
1980 td->last_ins->data [0] = csignature->param_count;
1981 } 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) {
1982 if (target_method->klass == mono_defaults.enum_class && (td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
1983 /* managed pointer on the stack, we need to deref that puppy */
1984 /* Always load the entire stackval, to handle also the case where the enum has long storage */
1985 interp_add_ins (td, MINT_LDIND_I8);
1986 td->last_ins->data [0] = csignature->param_count;
1989 interp_constrained_box (td, domain, constrained_class, csignature, error);
1990 return_val_if_nok (error, FALSE);
1991 } else {
1992 if (target_method->klass != constrained_class) {
1994 * The type parameter is instantiated as a valuetype,
1995 * but that type doesn't override the method we're
1996 * calling, so we need to box `this'.
1998 if (target_method->klass == mono_defaults.enum_class && (td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
1999 /* managed pointer on the stack, we need to deref that puppy */
2000 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2001 interp_add_ins (td, MINT_LDIND_I8);
2002 td->last_ins->data [0] = csignature->param_count;
2005 interp_constrained_box (td, domain, constrained_class, csignature, error);
2006 return_val_if_nok (error, FALSE);
2008 is_virtual = FALSE;
2012 if (target_method)
2013 mono_class_init_internal (target_method->klass);
2015 if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
2016 if (!mono_class_is_interface (method->klass))
2017 interp_generate_bie_throw (td);
2018 else
2019 is_virtual = TRUE;
2022 if (is_virtual && target_method && (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2023 (MONO_METHOD_IS_FINAL (target_method) &&
2024 target_method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2025 !(mono_class_is_marshalbyref (target_method->klass))) {
2026 /* Not really virtual, just needs a null check */
2027 is_virtual = FALSE;
2028 need_null_check = TRUE;
2031 CHECK_STACK (td, csignature->param_count + csignature->hasthis);
2032 if (!td->gen_sdb_seq_points && !calli && op == -1 && (!is_virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) &&
2033 (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 &&
2034 (target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0 &&
2035 !(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING)) {
2036 (void)mono_class_vtable_checked (domain, target_method->klass, error);
2037 return_val_if_nok (error, FALSE);
2039 if (method == target_method && *(td->ip + 5) == CEE_RET && !(csignature->hasthis && m_class_is_valuetype (target_method->klass))) {
2040 if (td->inlined_method)
2041 return FALSE;
2043 if (td->verbose_level)
2044 g_print ("Optimize tail call of %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
2046 for (i = csignature->param_count - 1 + !!csignature->hasthis; i >= 0; --i)
2047 store_arg (td, i);
2049 interp_add_ins (td, MINT_BR_S);
2050 // We are branching to the beginning of the method
2051 td->last_ins->data [0] = 0;
2052 if (!is_bb_start [td->ip + 5 - td->il_code])
2053 ++td->ip; /* gobble the CEE_RET if it isn't branched to */
2054 td->ip += 5;
2055 return TRUE;
2059 target_method = interp_transform_internal_calls (method, target_method, csignature, is_virtual);
2061 if (csignature->call_convention == MONO_CALL_VARARG) {
2062 csignature = mono_method_get_signature_checked (target_method, image, token, generic_context, error);
2063 int vararg_stack = 0;
2065 * For vararg calls, ArgIterator expects the signature and the varargs to be
2066 * stored in a linear memory. We allocate the necessary vt_stack space for
2067 * this. All varargs will be pushed to the vt_stack at call site.
2069 vararg_stack += sizeof (gpointer);
2070 for (i = csignature->sentinelpos; i < csignature->param_count; ++i) {
2071 int align, arg_size;
2072 arg_size = mono_type_stack_size (csignature->params [i], &align);
2073 vararg_stack += ALIGN_TO (arg_size, align);
2075 /* allocate space for the pointer to varargs space start */
2076 vararg_stack += sizeof (gpointer);
2077 vt_stack_used += ALIGN_TO (vararg_stack, MINT_VT_ALIGNMENT);
2078 PUSH_VT (td, vararg_stack);
2081 if (need_null_check) {
2082 interp_add_ins (td, MINT_CKNULL_N);
2083 td->last_ins->data [0] = csignature->param_count + 1;
2086 g_assert (csignature->call_convention != MONO_CALL_FASTCALL);
2087 if ((mono_interp_opt & INTERP_OPT_INLINE) && op == -1 && !is_virtual && target_method && interp_method_check_inlining (td, target_method)) {
2088 MonoMethodHeader *mheader = interp_method_get_header (target_method, error);
2089 return_val_if_nok (error, FALSE);
2091 if (interp_inline_method (td, target_method, mheader, error)) {
2092 td->ip += 5;
2093 return TRUE;
2097 /* Don't inline methods that do calls */
2098 if (op == -1 && td->inlined_method)
2099 return FALSE;
2101 /* We need to convert delegate invoke to a indirect call on the interp_invoke_impl field */
2102 if (target_method && m_class_get_parent (target_method->klass) == mono_defaults.multicastdelegate_class) {
2103 const char *name = target_method->name;
2104 if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
2105 calli = TRUE;
2106 interp_add_ins (td, MINT_LD_DELEGATE_INVOKE_IMPL);
2107 td->last_ins->data [0] = csignature->param_count + 1;
2108 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
2112 /* Pop the function pointer */
2113 if (calli)
2114 --td->sp;
2116 td->sp -= csignature->param_count + !!csignature->hasthis;
2117 for (i = 0; i < csignature->param_count; ++i) {
2118 if (td->sp [i + !!csignature->hasthis].type == STACK_TYPE_VT) {
2119 gint32 size;
2120 MonoClass *klass = mono_class_from_mono_type_internal (csignature->params [i]);
2121 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2122 size = mono_class_native_size (klass, NULL);
2123 else
2124 size = mono_class_value_size (klass, NULL);
2125 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
2126 vt_stack_used += size;
2130 /* need to handle typedbyref ... */
2131 if (csignature->ret->type != MONO_TYPE_VOID) {
2132 int mt = mint_type(csignature->ret);
2133 MonoClass *klass = mono_class_from_mono_type_internal (csignature->ret);
2134 if (mt == MINT_TYPE_VT) {
2135 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2136 vt_res_size = mono_class_native_size (klass, NULL);
2137 else
2138 vt_res_size = mono_class_value_size (klass, NULL);
2139 if (mono_class_has_failure (klass)) {
2140 mono_error_set_for_class_failure (error, klass);
2141 return FALSE;
2143 PUSH_VT(td, vt_res_size);
2145 PUSH_TYPE(td, stack_type[mt], klass);
2146 } else
2147 is_void = TRUE;
2149 if (op >= 0) {
2150 interp_add_ins (td, op);
2152 if (op == MINT_LDLEN) {
2153 #ifdef MONO_BIG_ARRAYS
2154 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
2155 #else
2156 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
2157 #endif
2159 if (op == MINT_LDELEMA || op == MINT_LDELEMA_TC) {
2160 td->last_ins->data [0] = get_data_item_index (td, m_class_get_element_class (target_method->klass));
2161 td->last_ins->data [1] = 1 + m_class_get_rank (target_method->klass);
2164 if (op == MINT_CALLRUN) {
2165 td->last_ins->data [0] = get_data_item_index (td, target_method);
2166 td->last_ins->data [1] = get_data_item_index (td, mono_method_signature_internal (target_method));
2168 } else if (!calli && !is_virtual && jit_call_supported (target_method, csignature)) {
2169 interp_add_ins (td, MINT_JIT_CALL);
2170 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2171 mono_error_assert_ok (error);
2172 } else {
2173 #ifndef MONO_ARCH_HAS_NO_PROPER_MONOCTX
2174 /* Try using fast icall path for simple signatures */
2175 if (native && !method->dynamic)
2176 op = interp_icall_op_for_sig (csignature);
2177 #endif
2178 if (csignature->call_convention == MONO_CALL_VARARG)
2179 interp_add_ins (td, MINT_CALL_VARARG);
2180 else if (calli)
2181 interp_add_ins (td, native ? ((op != -1) ? MINT_CALLI_NAT_FAST : MINT_CALLI_NAT) : MINT_CALLI);
2182 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass))
2183 interp_add_ins (td, is_void ? MINT_VCALLVIRT_FAST : MINT_CALLVIRT_FAST);
2184 else if (is_virtual)
2185 interp_add_ins (td, is_void ? MINT_VCALLVIRT : MINT_CALLVIRT);
2186 else
2187 interp_add_ins (td, is_void ? MINT_VCALL : MINT_CALL);
2189 if (calli) {
2190 td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
2191 if (op != -1) {
2192 td->last_ins->data[1] = op;
2193 if (td->last_ins->opcode == MINT_CALLI_NAT_FAST)
2194 td->last_ins->data[2] = save_last_error;
2195 } else if (op == -1 && td->last_ins->opcode == MINT_CALLI_NAT) {
2196 td->last_ins->data[1] = save_last_error;
2198 } else {
2199 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2200 return_val_if_nok (error, FALSE);
2201 if (csignature->call_convention == MONO_CALL_VARARG)
2202 td->last_ins->data [1] = get_data_item_index (td, (void *)csignature);
2203 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass)) {
2204 /* FIXME Use fastpath also for MBRO. Asserts in mono_method_get_vtable_slot */
2205 if (mono_class_is_interface (target_method->klass))
2206 td->last_ins->data [1] = -2 * MONO_IMT_SIZE + mono_method_get_imt_slot (target_method);
2207 else
2208 td->last_ins->data [1] = mono_method_get_vtable_slot (target_method);
2212 td->ip += 5;
2213 if (vt_stack_used != 0 || vt_res_size != 0) {
2214 interp_add_ins (td, MINT_VTRESULT);
2215 td->last_ins->data [0] = vt_res_size;
2216 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
2217 td->vt_sp -= vt_stack_used;
2220 return TRUE;
2223 static MonoClassField *
2224 interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, MonoGenericContext *generic_context, MonoError *error)
2226 MonoClassField *field = NULL;
2227 if (method->wrapper_type != MONO_WRAPPER_NONE) {
2228 field = (MonoClassField *) mono_method_get_wrapper_data (method, token);
2229 *klass = field->parent;
2231 mono_class_setup_fields (field->parent);
2232 } else {
2233 field = mono_field_from_token_checked (m_class_get_image (method->klass), token, klass, generic_context, error);
2234 return_val_if_nok (error, NULL);
2237 if (!method->skip_visibility && !mono_method_can_access_field (method, field)) {
2238 char *method_fname = mono_method_full_name (method, TRUE);
2239 char *field_fname = mono_field_full_name (field);
2240 mono_error_set_generic_error (error, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
2241 g_free (method_fname);
2242 g_free (field_fname);
2243 return NULL;
2246 return field;
2249 static InterpBasicBlock*
2250 get_bb (TransformData *td, InterpBasicBlock *cbb, unsigned char *ip)
2252 int offset = ip - td->il_code;
2253 InterpBasicBlock *bb = td->offset_to_bb [offset];
2255 if (!bb) {
2256 bb = (InterpBasicBlock*)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock));
2257 bb->ip = ip;
2258 td->offset_to_bb [offset] = bb;
2260 td->basic_blocks = g_list_append_mempool (td->mempool, td->basic_blocks, bb);
2263 if (cbb)
2264 bb->preds = g_slist_prepend_mempool (td->mempool, bb->preds, cbb);
2265 return bb;
2269 * get_basic_blocks:
2271 * Compute the set of IL level basic blocks.
2273 static void
2274 get_basic_blocks (TransformData *td)
2276 guint8 *start = (guint8*)td->il_code;
2277 guint8 *end = (guint8*)td->il_code + td->code_size;
2278 guint8 *ip = start;
2279 unsigned char *target;
2280 int i;
2281 guint cli_addr;
2282 const MonoOpcode *opcode;
2283 InterpBasicBlock *cbb;
2285 td->offset_to_bb = (InterpBasicBlock**)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * (end - start + 1));
2286 td->entry_bb = cbb = get_bb (td, NULL, start);
2288 while (ip < end) {
2289 cli_addr = ip - start;
2290 td->offset_to_bb [cli_addr] = cbb;
2291 i = mono_opcode_value ((const guint8 **)&ip, end);
2292 opcode = &mono_opcodes [i];
2293 switch (opcode->argument) {
2294 case MonoInlineNone:
2295 ip++;
2296 break;
2297 case MonoInlineString:
2298 case MonoInlineType:
2299 case MonoInlineField:
2300 case MonoInlineMethod:
2301 case MonoInlineTok:
2302 case MonoInlineSig:
2303 case MonoShortInlineR:
2304 case MonoInlineI:
2305 ip += 5;
2306 break;
2307 case MonoInlineVar:
2308 ip += 3;
2309 break;
2310 case MonoShortInlineVar:
2311 case MonoShortInlineI:
2312 ip += 2;
2313 break;
2314 case MonoShortInlineBrTarget:
2315 target = start + cli_addr + 2 + (signed char)ip [1];
2316 get_bb (td, cbb, target);
2317 ip += 2;
2318 cbb = get_bb (td, cbb, ip);
2319 break;
2320 case MonoInlineBrTarget:
2321 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
2322 get_bb (td, cbb, target);
2323 ip += 5;
2324 cbb = get_bb (td, cbb, ip);
2325 break;
2326 case MonoInlineSwitch: {
2327 guint32 n = read32 (ip + 1);
2328 guint32 j;
2329 ip += 5;
2330 cli_addr += 5 + 4 * n;
2331 target = start + cli_addr;
2332 get_bb (td, cbb, target);
2334 for (j = 0; j < n; ++j) {
2335 target = start + cli_addr + (gint32)read32 (ip);
2336 get_bb (td, cbb, target);
2337 ip += 4;
2339 cbb = get_bb (td, cbb, ip);
2340 break;
2342 case MonoInlineR:
2343 case MonoInlineI8:
2344 ip += 9;
2345 break;
2346 default:
2347 g_assert_not_reached ();
2350 if (i == CEE_THROW)
2351 cbb = get_bb (td, NULL, ip);
2355 static void
2356 interp_save_debug_info (InterpMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers)
2358 MonoDebugMethodJitInfo *dinfo;
2359 int i;
2361 if (!mono_debug_enabled ())
2362 return;
2365 * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
2368 dinfo = g_new0 (MonoDebugMethodJitInfo, 1);
2369 dinfo->num_params = rtm->param_count;
2370 dinfo->params = g_new0 (MonoDebugVarInfo, dinfo->num_params);
2371 dinfo->num_locals = header->num_locals;
2372 dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals);
2373 dinfo->code_start = (guint8*)rtm->code;
2374 dinfo->code_size = td->new_code_end - td->new_code;
2375 dinfo->epilogue_begin = 0;
2376 dinfo->has_var_info = TRUE;
2377 dinfo->num_line_numbers = line_numbers->len;
2378 dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers);
2380 for (i = 0; i < dinfo->num_params; i++) {
2381 MonoDebugVarInfo *var = &dinfo->params [i];
2382 var->type = rtm->param_types [i];
2384 for (i = 0; i < dinfo->num_locals; i++) {
2385 MonoDebugVarInfo *var = &dinfo->locals [i];
2386 var->type = mono_metadata_type_dup (NULL, header->locals [i]);
2389 for (i = 0; i < dinfo->num_line_numbers; i++)
2390 dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i);
2391 mono_debug_add_method (rtm->method, dinfo, rtm->domain);
2393 mono_debug_free_method_jit_info (dinfo);
2396 /* Same as the code in seq-points.c */
2397 static void
2398 insert_pred_seq_point (SeqPoint *last_sp, SeqPoint *sp, GSList **next)
2400 GSList *l;
2401 int src_index = last_sp->next_offset;
2402 int dst_index = sp->next_offset;
2404 /* bb->in_bb might contain duplicates */
2405 for (l = next [src_index]; l; l = l->next)
2406 if (GPOINTER_TO_UINT (l->data) == dst_index)
2407 break;
2408 if (!l)
2409 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
2412 static void
2413 recursively_make_pred_seq_points (TransformData *td, InterpBasicBlock *bb)
2415 SeqPoint ** const MONO_SEQ_SEEN_LOOP = (SeqPoint**)GINT_TO_POINTER(-1);
2416 GSList *l;
2418 GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
2419 GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
2421 // Insert/remove sentinel into the memoize table to detect loops containing bb
2422 bb->pred_seq_points = MONO_SEQ_SEEN_LOOP;
2424 for (l = bb->preds; l; l = l->next) {
2425 InterpBasicBlock *in_bb = (InterpBasicBlock*)l->data;
2427 // This bb has the last seq point, append it and continue
2428 if (in_bb->last_seq_point != NULL) {
2429 predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
2430 continue;
2433 // We've looped or handled this before, exit early.
2434 // No last sequence points to find.
2435 if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
2436 continue;
2438 // Take sequence points from incoming basic blocks
2440 if (in_bb == td->entry_bb)
2441 continue;
2443 if (in_bb->pred_seq_points == NULL)
2444 recursively_make_pred_seq_points (td, in_bb);
2446 // Union sequence points with incoming bb's
2447 for (int i=0; i < in_bb->num_pred_seq_points; i++) {
2448 if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
2449 g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
2450 g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
2453 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
2456 g_hash_table_destroy (seen);
2458 if (predecessors->len != 0) {
2459 bb->pred_seq_points = (SeqPoint**)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint *) * predecessors->len);
2460 bb->num_pred_seq_points = predecessors->len;
2462 for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
2463 bb->pred_seq_points [newer] = (SeqPoint*)g_array_index (predecessors, gpointer, newer);
2467 g_array_free (predecessors, TRUE);
2470 static void
2471 collect_pred_seq_points (TransformData *td, InterpBasicBlock *bb, SeqPoint *seqp, GSList **next)
2473 // Doesn't have a last sequence point, must find from incoming basic blocks
2474 if (bb->pred_seq_points == NULL && bb != td->entry_bb)
2475 recursively_make_pred_seq_points (td, bb);
2477 for (int i = 0; i < bb->num_pred_seq_points; i++)
2478 insert_pred_seq_point (bb->pred_seq_points [i], seqp, next);
2480 return;
2483 static void
2484 save_seq_points (TransformData *td, MonoJitInfo *jinfo)
2486 GByteArray *array;
2487 int i, seq_info_size;
2488 MonoSeqPointInfo *info;
2489 GSList **next = NULL;
2490 GList *bblist;
2492 if (!td->gen_sdb_seq_points)
2493 return;
2496 * For each sequence point, compute the list of sequence points immediately
2497 * following it, this is needed to implement 'step over' in the debugger agent.
2498 * Similar to the code in mono_save_seq_point_info ().
2500 for (i = 0; i < td->seq_points->len; ++i) {
2501 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2503 /* Store the seq point index here temporarily */
2504 sp->next_offset = i;
2506 next = (GSList**)mono_mempool_alloc0 (td->mempool, sizeof (GList*) * td->seq_points->len);
2507 for (bblist = td->basic_blocks; bblist; bblist = bblist->next) {
2508 InterpBasicBlock *bb = (InterpBasicBlock*)bblist->data;
2510 GSList *bb_seq_points = g_slist_reverse (bb->seq_points);
2511 SeqPoint *last = NULL;
2512 for (GSList *l = bb_seq_points; l; l = l->next) {
2513 SeqPoint *sp = (SeqPoint*)l->data;
2515 if (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET)
2516 /* Used to implement method entry/exit events */
2517 continue;
2519 if (last != NULL) {
2520 /* Link with the previous seq point in the same bb */
2521 next [last->next_offset] = g_slist_append_mempool (td->mempool, next [last->next_offset], GINT_TO_POINTER (sp->next_offset));
2522 } else {
2523 /* Link with the last bb in the previous bblocks */
2524 collect_pred_seq_points (td, bb, sp, next);
2526 last = sp;
2530 /* Serialize the seq points into a byte array */
2531 array = g_byte_array_new ();
2532 SeqPoint zero_seq_point = {0};
2533 SeqPoint* last_seq_point = &zero_seq_point;
2534 for (i = 0; i < td->seq_points->len; ++i) {
2535 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2537 sp->next_offset = 0;
2538 if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next [i], TRUE))
2539 last_seq_point = sp;
2542 if (td->verbose_level) {
2543 g_print ("\nSEQ POINT MAP FOR %s: \n", td->method->name);
2545 for (i = 0; i < td->seq_points->len; ++i) {
2546 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2547 GSList *l;
2549 if (!next [i])
2550 continue;
2552 g_print ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
2553 for (l = next [i]; l; l = l->next) {
2554 int next_index = GPOINTER_TO_UINT (l->data);
2555 g_print (" IL0x%x", ((SeqPoint*)g_ptr_array_index (td->seq_points, next_index))->il_offset);
2557 g_print ("\n");
2561 info = mono_seq_point_info_new (array->len, TRUE, array->data, TRUE, &seq_info_size);
2562 mono_atomic_fetch_add_i32 (&mono_jit_stats.allocated_seq_points_size, seq_info_size);
2564 g_byte_array_free (array, TRUE);
2566 jinfo->seq_points = info;
2569 #define BARRIER_IF_VOLATILE(td) \
2570 do { \
2571 if (volatile_) { \
2572 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER); \
2573 volatile_ = FALSE; \
2575 } while (0)
2577 #define INLINE_FAILURE \
2578 do { \
2579 if (inlining) \
2580 goto exit; \
2581 } while (0)
2583 static void
2584 interp_method_compute_offsets (InterpMethod *imethod, MonoMethodSignature *signature, MonoMethodHeader *header)
2586 int i, offset, size, align;
2588 imethod->local_offsets = (guint32*)g_malloc (header->num_locals * sizeof(guint32));
2589 offset = 0;
2590 for (i = 0; i < header->num_locals; ++i) {
2591 size = mono_type_size (header->locals [i], &align);
2592 offset += align - 1;
2593 offset &= ~(align - 1);
2594 imethod->local_offsets [i] = offset;
2595 offset += size;
2597 offset = (offset + 7) & ~7;
2599 imethod->exvar_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32));
2600 for (i = 0; i < header->num_clauses; i++) {
2601 imethod->exvar_offsets [i] = offset;
2602 offset += sizeof (MonoObject*);
2604 offset = (offset + 7) & ~7;
2606 imethod->locals_size = offset;
2607 g_assert (imethod->locals_size < 65536);
2610 static MonoType*
2611 get_arg_type (MonoMethodSignature *signature, int arg_n)
2613 if (signature->hasthis && arg_n == 0)
2614 return mono_get_object_type ();
2615 return signature->params [arg_n - !!signature->hasthis];
2618 /* Return false is failure to init basic blocks due to being in inline method */
2619 static gboolean
2620 init_bb_start (TransformData *td, MonoMethodHeader *header, gboolean inlining)
2622 const unsigned char *ip, *end;
2623 const MonoOpcode *opcode;
2624 int offset, i, in, backwards;
2626 /* intern the strings in the method. */
2627 ip = header->code;
2628 end = ip + header->code_size;
2630 /* inlined method continues the basic block of parent method */
2631 if (!inlining)
2632 td->is_bb_start [0] = 1;
2633 while (ip < end) {
2634 in = *ip;
2635 if (in == 0xfe) {
2636 ip++;
2637 in = *ip + 256;
2639 else if (in == 0xf0) {
2640 ip++;
2641 in = *ip + MONO_CEE_MONO_ICALL;
2643 opcode = &mono_opcodes [in];
2644 switch (opcode->argument) {
2645 case MonoInlineNone:
2646 ++ip;
2647 break;
2648 case MonoInlineString:
2649 ip += 5;
2650 break;
2651 case MonoInlineType:
2652 ip += 5;
2653 break;
2654 case MonoInlineMethod:
2655 ip += 5;
2656 break;
2657 case MonoInlineField:
2658 case MonoInlineSig:
2659 case MonoInlineI:
2660 case MonoInlineTok:
2661 case MonoShortInlineR:
2662 ip += 5;
2663 break;
2664 case MonoInlineBrTarget:
2665 offset = read32 (ip + 1);
2666 ip += 5;
2667 /* this branch is ignored */
2668 if (offset == 0 && in == MONO_CEE_BR)
2669 break;
2670 backwards = offset < 0;
2671 offset += ip - header->code;
2672 g_assert (offset >= 0 && offset < header->code_size);
2673 if (inlining)
2674 return FALSE;
2675 td->is_bb_start [offset] |= backwards ? 2 : 1;
2676 break;
2677 case MonoShortInlineBrTarget:
2678 offset = ((gint8 *)ip) [1];
2679 ip += 2;
2680 /* this branch is ignored */
2681 if (offset == 0 && in == MONO_CEE_BR_S)
2682 break;
2683 backwards = offset < 0;
2684 offset += ip - header->code;
2685 g_assert (offset >= 0 && offset < header->code_size);
2686 if (inlining)
2687 return FALSE;
2688 td->is_bb_start [offset] |= backwards ? 2 : 1;
2689 break;
2690 case MonoInlineVar:
2691 ip += 3;
2692 break;
2693 case MonoShortInlineVar:
2694 case MonoShortInlineI:
2695 ip += 2;
2696 break;
2697 case MonoInlineSwitch: {
2698 guint32 n;
2699 const unsigned char *next_ip;
2700 ++ip;
2701 n = read32 (ip);
2702 ip += 4;
2703 next_ip = ip + 4 * n;
2704 for (i = 0; i < n; i++) {
2705 offset = read32 (ip);
2706 backwards = offset < 0;
2707 offset += next_ip - header->code;
2708 g_assert (offset >= 0 && offset < header->code_size);
2709 if (inlining)
2710 return FALSE;
2711 td->is_bb_start [offset] |= backwards ? 2 : 1;
2712 ip += 4;
2714 break;
2716 case MonoInlineR:
2717 case MonoInlineI8:
2718 ip += 9;
2719 break;
2720 default:
2721 g_assert_not_reached ();
2724 return TRUE;
2727 #ifdef NO_UNALIGNED_ACCESS
2728 static int
2729 get_unaligned_opcode (int opcode)
2731 switch (opcode) {
2732 case MINT_LDFLD_I8:
2733 return MINT_LDFLD_I8_UNALIGNED;
2734 case MINT_LDFLD_R8:
2735 return MINT_LDFLD_R8_UNALIGNED;
2736 case MINT_STFLD_I8:
2737 return MINT_STFLD_I8_UNALIGNED;
2738 case MINT_STFLD_R8:
2739 return MINT_STFLD_R8_UNALIGNED;
2740 default:
2741 g_assert_not_reached ();
2743 return -1;
2745 #endif
2747 static void
2748 interp_handle_isinst (TransformData *td, MonoClass *klass, gboolean isinst_instr)
2750 /* Follow the logic from jit's handle_isinst */
2751 if (!mono_class_has_variant_generic_params (klass)) {
2752 if (mono_class_is_interface (klass))
2753 interp_add_ins (td, isinst_instr ? MINT_ISINST_INTERFACE : MINT_CASTCLASS_INTERFACE);
2754 else if (!mono_class_is_marshalbyref (klass) && m_class_get_rank (klass) == 0 && !mono_class_is_nullable (klass))
2755 interp_add_ins (td, isinst_instr ? MINT_ISINST_COMMON : MINT_CASTCLASS_COMMON);
2756 else
2757 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2758 } else {
2759 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2761 td->last_ins->data [0] = get_data_item_index (td, klass);
2762 td->ip += 5;
2765 static void
2766 interp_emit_ldobj (TransformData *td, MonoClass *klass)
2768 int mt = mint_type (m_class_get_byval_arg (klass));
2769 int size;
2771 if (mt == MINT_TYPE_VT) {
2772 interp_add_ins (td, MINT_LDOBJ_VT);
2773 size = mono_class_value_size (klass, NULL);
2774 WRITE32_INS (td->last_ins, 0, &size);
2775 PUSH_VT (td, size);
2776 } else {
2777 int opcode = interp_get_ldind_for_mt (mt);
2778 interp_add_ins (td, opcode);
2781 SET_TYPE (td->sp - 1, stack_type [mt], klass);
2784 static void
2785 interp_emit_stobj (TransformData *td, MonoClass *klass)
2787 int mt = mint_type (m_class_get_byval_arg (klass));
2789 if (mt == MINT_TYPE_VT) {
2790 int size;
2791 interp_add_ins (td, MINT_STOBJ_VT);
2792 td->last_ins->data [0] = get_data_item_index(td, klass);
2793 size = mono_class_value_size (klass, NULL);
2794 POP_VT (td, size);
2795 } else {
2796 int opcode;
2797 switch (mt) {
2798 case MINT_TYPE_I1:
2799 case MINT_TYPE_U1:
2800 opcode = MINT_STIND_I1;
2801 break;
2802 case MINT_TYPE_I2:
2803 case MINT_TYPE_U2:
2804 opcode = MINT_STIND_I2;
2805 break;
2806 case MINT_TYPE_I4:
2807 opcode = MINT_STIND_I4;
2808 break;
2809 case MINT_TYPE_I8:
2810 opcode = MINT_STIND_I8;
2811 break;
2812 case MINT_TYPE_R4:
2813 opcode = MINT_STIND_R4;
2814 break;
2815 case MINT_TYPE_R8:
2816 opcode = MINT_STIND_R8;
2817 break;
2818 case MINT_TYPE_O:
2819 opcode = MINT_STIND_REF;
2820 break;
2821 default: g_assert_not_reached (); break;
2823 interp_add_ins (td, opcode);
2825 td->sp -= 2;
2828 static void
2829 interp_emit_ldsflda (TransformData *td, MonoClassField *field, MonoError *error)
2831 MonoDomain *domain = td->rtm->domain;
2832 // Initialize the offset for the field
2833 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
2834 return_if_nok (error);
2836 if (mono_class_field_is_special_static (field)) {
2837 guint32 offset;
2839 mono_domain_lock (domain);
2840 g_assert (domain->special_static_fields);
2841 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
2842 mono_domain_unlock (domain);
2843 g_assert (offset);
2845 interp_add_ins (td, MINT_LDSSFLDA);
2846 WRITE32_INS(td->last_ins, 0, &offset);
2847 } else {
2848 interp_add_ins (td, MINT_LDSFLDA);
2849 td->last_ins->data [0] = get_data_item_index (td, vtable);
2850 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
2854 static void
2855 interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, gboolean is_load, MonoError *error)
2857 MonoDomain *domain = td->rtm->domain;
2858 // Initialize the offset for the field
2859 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
2860 return_if_nok (error);
2862 if (mono_class_field_is_special_static (field)) {
2863 guint32 offset;
2865 mono_domain_lock (domain);
2866 g_assert (domain->special_static_fields);
2867 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
2868 mono_domain_unlock (domain);
2869 g_assert (offset);
2871 // Offset is SpecialStaticOffset
2872 if ((offset & 0x80000000) == 0 && mt != MINT_TYPE_VT) {
2873 // This field is thread static
2874 interp_add_ins (td, (is_load ? MINT_LDTSFLD_I1 : MINT_STTSFLD_I1) + mt);
2875 WRITE32_INS(td->last_ins, 0, &offset);
2876 } else {
2877 if (mt == MINT_TYPE_VT) {
2878 interp_add_ins (td, is_load ? MINT_LDSSFLD_VT : MINT_STSSFLD_VT);
2879 WRITE32_INS(td->last_ins, 0, &offset);
2881 int size = mono_class_value_size (field_class, NULL);
2882 WRITE32_INS(td->last_ins, 2, &size);
2883 } else {
2884 interp_add_ins (td, is_load ? MINT_LDSSFLD : MINT_STSSFLD);
2885 td->last_ins->data [0] = get_data_item_index (td, field);
2886 WRITE32_INS(td->last_ins, 1, &offset);
2889 } else {
2890 if (is_load)
2891 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSFLD_VT : (MINT_LDSFLD_I1 + mt - MINT_TYPE_I1));
2892 else
2893 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1));
2895 td->last_ins->data [0] = get_data_item_index (td, vtable);
2896 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
2898 if (mt == MINT_TYPE_VT) {
2899 int size = mono_class_value_size (field_class, NULL);
2900 WRITE32_INS(td->last_ins, 2, &size);
2905 static gboolean
2906 generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error)
2908 int target;
2909 int offset, mt, i, i32;
2910 guint32 token;
2911 int in_offset;
2912 const unsigned char *end;
2913 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
2914 gboolean sym_seq_points = FALSE;
2915 MonoBitSet *seq_point_locs = NULL;
2916 gboolean readonly = FALSE;
2917 gboolean volatile_ = FALSE;
2918 MonoClass *constrained_class = NULL;
2919 MonoClass *klass;
2920 MonoClassField *field;
2921 MonoImage *image = m_class_get_image (method->klass);
2922 InterpMethod *rtm = td->rtm;
2923 MonoDomain *domain = rtm->domain;
2924 MonoMethodSignature *signature = mono_method_signature_internal (method);
2925 gboolean ret = TRUE;
2926 gboolean emitted_funccall_seq_point = FALSE;
2927 guint32 *arg_offsets = NULL;
2928 guint32 *local_offsets = NULL;
2929 InterpInst *last_seq_point = NULL;
2930 gboolean save_last_error = FALSE;
2931 gboolean inlining = td->method != method;
2933 original_bb = bb = mono_basic_block_split (method, error, header);
2934 goto_if_nok (error, exit);
2935 g_assert (bb);
2937 td->il_code = header->code;
2938 td->in_start = td->ip = header->code;
2939 end = td->ip + header->code_size;
2941 if (!init_bb_start (td, header, inlining))
2942 goto exit;
2944 if (!inlining) {
2945 for (i = 0; i < header->code_size; i++) {
2946 td->stack_height [i] = -1;
2947 td->clause_indexes [i] = -1;
2951 for (i = 0; i < header->num_clauses; i++) {
2952 MonoExceptionClause *c = header->clauses + i;
2953 td->stack_height [c->handler_offset] = 0;
2954 td->vt_stack_size [c->handler_offset] = 0;
2955 td->is_bb_start [c->handler_offset] = 1;
2956 td->is_bb_start [c->try_offset] = 1;
2958 td->stack_height [c->handler_offset] = 1;
2959 td->stack_state [c->handler_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
2960 td->stack_state [c->handler_offset][0].type = STACK_TYPE_O;
2961 td->stack_state [c->handler_offset][0].klass = NULL; /*FIX*/
2963 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER) {
2964 td->stack_height [c->data.filter_offset] = 0;
2965 td->vt_stack_size [c->data.filter_offset] = 0;
2966 td->is_bb_start [c->data.filter_offset] = 1;
2968 td->stack_height [c->data.filter_offset] = 1;
2969 td->stack_state [c->data.filter_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
2970 td->stack_state [c->data.filter_offset][0].type = STACK_TYPE_O;
2971 td->stack_state [c->data.filter_offset][0].klass = NULL; /*FIX*/
2974 for (int j = c->handler_offset; j < c->handler_offset + c->handler_len; ++j) {
2975 if (td->clause_indexes [j] == -1)
2976 td->clause_indexes [j] = i;
2980 if (td->gen_sdb_seq_points && !inlining) {
2981 MonoDebugMethodInfo *minfo;
2982 get_basic_blocks (td);
2984 minfo = mono_debug_lookup_method (method);
2986 if (minfo) {
2987 MonoSymSeqPoint *sps;
2988 int i, n_il_offsets;
2990 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
2991 // FIXME: Free
2992 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
2993 sym_seq_points = TRUE;
2995 for (i = 0; i < n_il_offsets; ++i) {
2996 if (sps [i].il_offset < header->code_size)
2997 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
2999 g_free (sps);
3001 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
3002 if (asyncMethod) {
3003 for (i = 0; asyncMethod != NULL && i < asyncMethod->num_awaits; i++) {
3004 mono_bitset_set_fast (seq_point_locs, asyncMethod->resume_offsets [i]);
3005 mono_bitset_set_fast (seq_point_locs, asyncMethod->yield_offsets [i]);
3007 mono_debug_free_method_async_debug_info (asyncMethod);
3009 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (m_class_get_image (method->klass))) {
3010 /* Methods without line number info like auto-generated property accessors */
3011 seq_point_locs = mono_bitset_new (header->code_size, 0);
3012 sym_seq_points = TRUE;
3016 if (sym_seq_points) {
3017 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3018 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY;
3021 if (mono_debugger_method_has_breakpoint (method))
3022 interp_add_ins (td, MINT_BREAKPOINT);
3024 if (!inlining) {
3025 if (td->verbose_level) {
3026 char *tmp = mono_disasm_code (NULL, method, td->ip, end);
3027 char *name = mono_method_full_name (method, TRUE);
3028 g_print ("Method %s, original code:\n", name);
3029 g_print ("%s\n", tmp);
3030 g_free (tmp);
3031 g_free (name);
3034 if (header->num_locals && header->init_locals)
3035 interp_add_ins (td, MINT_INITLOCALS);
3037 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3038 interp_add_ins (td, MINT_TRACE_ENTER);
3039 else if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER)
3040 interp_add_ins (td, MINT_PROF_ENTER);
3042 /* safepoint is required on method entry */
3043 if (mono_threads_are_safepoints_enabled ())
3044 interp_add_ins (td, MINT_SAFEPOINT);
3045 } else {
3046 int offset;
3047 arg_offsets = (guint32*) g_malloc ((!!signature->hasthis + signature->param_count) * sizeof (guint32));
3048 /* Allocate locals to store inlined method args from stack */
3049 for (i = signature->param_count - 1; i >= 0; i--) {
3050 offset = create_interp_local (td, signature->params [i]);
3051 arg_offsets [i + !!signature->hasthis] = offset;
3052 store_local_general (td, offset, signature->params [i]);
3055 if (signature->hasthis) {
3057 * If this is value type, it is passed by address and not by value.
3058 * FIXME We should use MINT_TYPE_P instead of MINT_TYPE_O
3060 MonoType *type = mono_get_object_type ();
3061 offset = create_interp_local (td, type);
3062 arg_offsets [0] = offset;
3063 store_local_general (td, offset, type);
3066 local_offsets = (guint32*) g_malloc (header->num_locals * sizeof (guint32));
3067 /* Allocate locals to store inlined method args from stack */
3068 for (i = 0; i < header->num_locals; i++)
3069 local_offsets [i] = create_interp_local (td, header->locals [i]);
3072 while (td->ip < end) {
3073 g_assert (td->sp >= td->stack);
3074 g_assert (td->vt_sp < 0x10000000);
3075 in_offset = td->ip - header->code;
3076 if (!inlining)
3077 td->current_il_offset = in_offset;
3078 td->in_start = td->ip;
3079 InterpInst *prev_last_ins = td->last_ins;
3081 // Inlined method doesn't have clauses or branches
3082 if (!inlining && td->stack_height [in_offset] >= 0) {
3083 g_assert (td->is_bb_start [in_offset]);
3084 if (td->stack_height [in_offset] > 0)
3085 memcpy (td->stack, td->stack_state [in_offset], td->stack_height [in_offset] * sizeof(td->stack [0]));
3086 td->sp = td->stack + td->stack_height [in_offset];
3087 td->vt_sp = td->vt_stack_size [in_offset];
3090 if (in_offset == bb->end)
3091 bb = bb->next;
3093 if (bb->dead) {
3094 int op_size = mono_opcode_size (td->ip, end);
3095 g_assert (op_size > 0); /* The BB formation pass must catch all bad ops */
3097 if (td->verbose_level > 1)
3098 g_print ("SKIPPING DEAD OP at %x\n", in_offset);
3100 td->ip += op_size;
3101 continue;
3104 if (td->verbose_level > 1) {
3105 g_print ("IL_%04lx %s %-10s, sp %ld, %s %-12s vt_sp %u (max %u)\n",
3106 td->ip - td->il_code,
3107 td->is_bb_start [td->ip - td->il_code] == 3 ? "<>" :
3108 td->is_bb_start [td->ip - td->il_code] == 2 ? "< " :
3109 td->is_bb_start [td->ip - td->il_code] == 1 ? " >" : " ",
3110 mono_opcode_name (*td->ip), td->sp - td->stack,
3111 td->sp > td->stack ? stack_type_string [td->sp [-1].type] : " ",
3112 (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)) : "",
3113 td->vt_sp, td->max_vt_sp);
3116 if (sym_seq_points && mono_bitset_test_fast (seq_point_locs, td->ip - header->code)) {
3117 InterpBasicBlock *cbb = td->offset_to_bb [td->ip - header->code];
3118 g_assert (cbb);
3121 * Make methods interruptable at the beginning, and at the targets of
3122 * backward branches.
3124 if (in_offset == 0 || g_slist_length (cbb->preds) > 1)
3125 interp_add_ins (td, MINT_SDB_INTR_LOC);
3127 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3130 if (!inlining && td->is_bb_start [in_offset]) {
3131 int index = td->clause_indexes [in_offset];
3132 if (index != -1) {
3133 MonoExceptionClause *clause = &header->clauses [index];
3134 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
3135 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) &&
3136 in_offset == clause->handler_offset)
3137 interp_add_ins (td, MINT_START_ABORT_PROT);
3141 switch (*td->ip) {
3142 case CEE_NOP:
3143 /* lose it */
3144 emitted_funccall_seq_point = FALSE;
3145 ++td->ip;
3146 break;
3147 case CEE_BREAK:
3148 SIMPLE_OP(td, MINT_BREAK);
3149 break;
3150 case CEE_LDARG_0:
3151 case CEE_LDARG_1:
3152 case CEE_LDARG_2:
3153 case CEE_LDARG_3: {
3154 int arg_n = *td->ip - CEE_LDARG_0;
3155 if (!inlining)
3156 load_arg (td, arg_n);
3157 else
3158 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
3159 ++td->ip;
3160 break;
3162 case CEE_LDLOC_0:
3163 case CEE_LDLOC_1:
3164 case CEE_LDLOC_2:
3165 case CEE_LDLOC_3: {
3166 int loc_n = *td->ip - CEE_LDLOC_0;
3167 if (!inlining)
3168 load_local (td, loc_n);
3169 else
3170 load_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
3171 ++td->ip;
3172 break;
3174 case CEE_STLOC_0:
3175 case CEE_STLOC_1:
3176 case CEE_STLOC_2:
3177 case CEE_STLOC_3: {
3178 int loc_n = *td->ip - CEE_STLOC_0;
3179 if (!inlining)
3180 store_local (td, loc_n);
3181 else
3182 store_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
3183 ++td->ip;
3184 break;
3186 case CEE_LDARG_S: {
3187 int arg_n = ((guint8 *)td->ip)[1];
3188 if (!inlining)
3189 load_arg (td, arg_n);
3190 else
3191 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
3192 td->ip += 2;
3193 break;
3195 case CEE_LDARGA_S: {
3196 /* NOTE: n includes this */
3197 int n = ((guint8 *) td->ip) [1];
3199 if (!inlining) {
3200 get_arg_type_exact (td, n, &mt);
3201 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
3202 td->last_ins->data [0] = n;
3203 } else {
3204 interp_add_ins (td, MINT_LDLOCA_S);
3205 td->last_ins->data [0] = arg_offsets [n];
3207 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3208 td->ip += 2;
3209 break;
3211 case CEE_STARG_S: {
3212 int arg_n = ((guint8 *)td->ip)[1];
3213 if (!inlining)
3214 store_arg (td, arg_n);
3215 else
3216 store_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
3217 td->ip += 2;
3218 break;
3220 case CEE_LDLOC_S: {
3221 int loc_n = ((guint8 *)td->ip)[1];
3222 if (!inlining)
3223 load_local (td, loc_n);
3224 else
3225 load_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
3226 td->ip += 2;
3227 break;
3229 case CEE_LDLOCA_S: {
3230 int loc_n = ((guint8 *)td->ip)[1];
3231 interp_add_ins (td, MINT_LDLOCA_S);
3232 if (!inlining)
3233 td->last_ins->data [0] = td->rtm->local_offsets [loc_n];
3234 else
3235 td->last_ins->data [0] = local_offsets [loc_n];
3236 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3237 td->ip += 2;
3238 break;
3240 case CEE_STLOC_S: {
3241 int loc_n = ((guint8 *)td->ip)[1];
3242 if (!inlining)
3243 store_local (td, loc_n);
3244 else
3245 store_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
3246 td->ip += 2;
3247 break;
3249 case CEE_LDNULL:
3250 SIMPLE_OP(td, MINT_LDNULL);
3251 PUSH_TYPE(td, STACK_TYPE_O, NULL);
3252 break;
3253 case CEE_LDC_I4_M1:
3254 SIMPLE_OP(td, MINT_LDC_I4_M1);
3255 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3256 break;
3257 case CEE_LDC_I4_0:
3258 if (!td->is_bb_start[td->ip + 1 - td->il_code] && td->ip [1] == 0xfe && td->ip [2] == CEE_CEQ &&
3259 td->sp > td->stack && td->sp [-1].type == STACK_TYPE_I4) {
3260 SIMPLE_OP(td, MINT_CEQ0_I4);
3261 td->ip += 2;
3262 } else {
3263 SIMPLE_OP(td, MINT_LDC_I4_0);
3264 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3266 break;
3267 case CEE_LDC_I4_1:
3268 if (!td->is_bb_start[td->ip + 1 - td->il_code] &&
3269 (td->ip [1] == CEE_ADD || td->ip [1] == CEE_SUB) && td->sp [-1].type == STACK_TYPE_I4) {
3270 interp_add_ins (td, td->ip [1] == CEE_ADD ? MINT_ADD1_I4 : MINT_SUB1_I4);
3271 td->ip += 2;
3272 } else {
3273 SIMPLE_OP(td, MINT_LDC_I4_1);
3274 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3276 break;
3277 case CEE_LDC_I4_2:
3278 case CEE_LDC_I4_3:
3279 case CEE_LDC_I4_4:
3280 case CEE_LDC_I4_5:
3281 case CEE_LDC_I4_6:
3282 case CEE_LDC_I4_7:
3283 case CEE_LDC_I4_8:
3284 SIMPLE_OP(td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0);
3285 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3286 break;
3287 case CEE_LDC_I4_S:
3288 interp_add_ins (td, MINT_LDC_I4_S);
3289 td->last_ins->data [0] = ((gint8 *) td->ip) [1];
3290 td->ip += 2;
3291 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3292 break;
3293 case CEE_LDC_I4:
3294 i32 = read32 (td->ip + 1);
3295 interp_add_ins (td, MINT_LDC_I4);
3296 WRITE32_INS (td->last_ins, 0, &i32);
3297 td->ip += 5;
3298 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3299 break;
3300 case CEE_LDC_I8: {
3301 gint64 val = read64 (td->ip + 1);
3302 interp_add_ins (td, MINT_LDC_I8);
3303 WRITE64_INS (td->last_ins, 0, &val);
3304 td->ip += 9;
3305 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I8);
3306 break;
3308 case CEE_LDC_R4: {
3309 float val;
3310 readr4 (td->ip + 1, &val);
3311 interp_add_ins (td, MINT_LDC_R4);
3312 WRITE32_INS (td->last_ins, 0, &val);
3313 td->ip += 5;
3314 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R4);
3315 break;
3317 case CEE_LDC_R8: {
3318 double val;
3319 readr8 (td->ip + 1, &val);
3320 interp_add_ins (td, MINT_LDC_R8);
3321 WRITE64_INS (td->last_ins, 0, &val);
3322 td->ip += 9;
3323 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R8);
3324 break;
3326 case CEE_DUP: {
3327 int type = td->sp [-1].type;
3328 MonoClass *klass = td->sp [-1].klass;
3329 if (td->sp [-1].type == STACK_TYPE_VT) {
3330 gint32 size = mono_class_value_size (klass, NULL);
3331 PUSH_VT(td, size);
3332 interp_add_ins (td, MINT_DUP_VT);
3333 WRITE32_INS (td->last_ins, 0, &size);
3334 td->ip ++;
3335 } else
3336 SIMPLE_OP(td, MINT_DUP);
3337 PUSH_TYPE(td, type, klass);
3338 break;
3340 case CEE_POP:
3341 CHECK_STACK(td, 1);
3342 SIMPLE_OP(td, MINT_POP);
3343 td->last_ins->data [0] = 0;
3344 if (td->sp [-1].type == STACK_TYPE_VT) {
3345 int size = mono_class_value_size (td->sp [-1].klass, NULL);
3346 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
3347 interp_add_ins (td, MINT_VTRESULT);
3348 td->last_ins->data [0] = 0;
3349 WRITE32_INS (td->last_ins, 1, &size);
3350 td->vt_sp -= size;
3352 --td->sp;
3353 break;
3354 case CEE_JMP: {
3355 MonoMethod *m;
3356 INLINE_FAILURE;
3357 if (td->sp > td->stack)
3358 g_warning ("CEE_JMP: stack must be empty");
3359 token = read32 (td->ip + 1);
3360 m = mono_get_method_checked (image, token, NULL, generic_context, error);
3361 goto_if_nok (error, exit);
3362 interp_add_ins (td, MINT_JMP);
3363 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3364 goto_if_nok (error, exit);
3365 td->ip += 5;
3366 break;
3368 case CEE_CALLVIRT: /* Fall through */
3369 case CEE_CALLI: /* Fall through */
3370 case CEE_CALL: {
3371 gboolean need_seq_point = FALSE;
3373 if (sym_seq_points && !mono_bitset_test_fast (seq_point_locs, td->ip + 5 - header->code))
3374 need_seq_point = TRUE;
3376 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, constrained_class, readonly, error, TRUE, save_last_error))
3377 goto exit;
3379 if (need_seq_point) {
3380 //check is is a nested call and remove the MONO_INST_NONEMPTY_STACK of the last breakpoint, only for non native methods
3381 if (!(method->flags & METHOD_IMPL_ATTRIBUTE_NATIVE)) {
3382 if (emitted_funccall_seq_point) {
3383 if (last_seq_point)
3384 last_seq_point->flags |= INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL;
3386 else
3387 emitted_funccall_seq_point = TRUE;
3389 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3390 // This seq point is actually associated with the instruction following the call
3391 last_seq_point->il_offset = td->ip - header->code;
3392 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK;
3395 constrained_class = NULL;
3396 readonly = FALSE;
3397 save_last_error = FALSE;
3398 break;
3400 case CEE_RET: {
3401 /* Return from inlined method, return value is on top of stack */
3402 if (td->method != method) {
3403 td->ip++;
3404 break;
3407 int vt_size = 0;
3408 MonoType *ult = mini_type_get_underlying_type (signature->ret);
3409 if (ult->type != MONO_TYPE_VOID) {
3410 CHECK_STACK (td, 1);
3411 --td->sp;
3412 if (mint_type (ult) == MINT_TYPE_VT) {
3413 MonoClass *klass = mono_class_from_mono_type_internal (ult);
3414 vt_size = mono_class_value_size (klass, NULL);
3417 if (td->sp > td->stack) {
3418 mono_error_set_generic_error (error, "System", "InvalidProgramException", "");
3419 goto exit;
3421 if (td->vt_sp != ALIGN_TO (vt_size, MINT_VT_ALIGNMENT))
3422 g_error ("%s: CEE_RET: value type stack: %d vs. %d", mono_method_full_name (td->method, TRUE), td->vt_sp, vt_size);
3424 if (sym_seq_points) {
3425 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3426 td->last_ins->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT;
3429 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
3430 /* This does the return as well */
3431 interp_add_ins (td, MINT_TRACE_EXIT);
3432 if (ult->type == MONO_TYPE_VOID)
3433 vt_size = -1;
3434 WRITE32_INS (td->last_ins, 0, &vt_size);
3435 ++td->ip;
3436 } else {
3437 if (vt_size == 0)
3438 SIMPLE_OP(td, ult->type == MONO_TYPE_VOID ? MINT_RET_VOID : MINT_RET);
3439 else {
3440 interp_add_ins (td, MINT_RET_VT);
3441 WRITE32_INS (td->last_ins, 0, &vt_size);
3442 ++td->ip;
3445 break;
3447 case CEE_BR: {
3448 int offset = read32 (td->ip + 1);
3449 if (offset) {
3450 INLINE_FAILURE;
3451 handle_branch (td, MINT_BR_S, MINT_BR, 5 + offset);
3453 td->ip += 5;
3454 break;
3456 case CEE_BR_S: {
3457 int offset = (gint8)td->ip [1];
3458 if (offset) {
3459 INLINE_FAILURE;
3460 handle_branch (td, MINT_BR_S, MINT_BR, 2 + (gint8)td->ip [1]);
3462 td->ip += 2;
3463 break;
3465 case CEE_BRFALSE:
3466 INLINE_FAILURE;
3467 one_arg_branch (td, MINT_BRFALSE_I4, 5 + read32 (td->ip + 1));
3468 td->ip += 5;
3469 break;
3470 case CEE_BRFALSE_S:
3471 INLINE_FAILURE;
3472 one_arg_branch (td, MINT_BRFALSE_I4, 2 + (gint8)td->ip [1]);
3473 td->ip += 2;
3474 break;
3475 case CEE_BRTRUE:
3476 INLINE_FAILURE;
3477 one_arg_branch (td, MINT_BRTRUE_I4, 5 + read32 (td->ip + 1));
3478 td->ip += 5;
3479 break;
3480 case CEE_BRTRUE_S:
3481 INLINE_FAILURE;
3482 one_arg_branch (td, MINT_BRTRUE_I4, 2 + (gint8)td->ip [1]);
3483 td->ip += 2;
3484 break;
3485 case CEE_BEQ:
3486 INLINE_FAILURE;
3487 two_arg_branch (td, MINT_BEQ_I4, 5 + read32 (td->ip + 1));
3488 td->ip += 5;
3489 break;
3490 case CEE_BEQ_S:
3491 INLINE_FAILURE;
3492 two_arg_branch (td, MINT_BEQ_I4, 2 + (gint8) td->ip [1]);
3493 td->ip += 2;
3494 break;
3495 case CEE_BGE:
3496 INLINE_FAILURE;
3497 two_arg_branch (td, MINT_BGE_I4, 5 + read32 (td->ip + 1));
3498 td->ip += 5;
3499 break;
3500 case CEE_BGE_S:
3501 INLINE_FAILURE;
3502 two_arg_branch (td, MINT_BGE_I4, 2 + (gint8) td->ip [1]);
3503 td->ip += 2;
3504 break;
3505 case CEE_BGT:
3506 INLINE_FAILURE;
3507 two_arg_branch (td, MINT_BGT_I4, 5 + read32 (td->ip + 1));
3508 td->ip += 5;
3509 break;
3510 case CEE_BGT_S:
3511 INLINE_FAILURE;
3512 two_arg_branch (td, MINT_BGT_I4, 2 + (gint8) td->ip [1]);
3513 td->ip += 2;
3514 break;
3515 case CEE_BLT:
3516 INLINE_FAILURE;
3517 two_arg_branch (td, MINT_BLT_I4, 5 + read32 (td->ip + 1));
3518 td->ip += 5;
3519 break;
3520 case CEE_BLT_S:
3521 INLINE_FAILURE;
3522 two_arg_branch (td, MINT_BLT_I4, 2 + (gint8) td->ip [1]);
3523 td->ip += 2;
3524 break;
3525 case CEE_BLE:
3526 INLINE_FAILURE;
3527 two_arg_branch (td, MINT_BLE_I4, 5 + read32 (td->ip + 1));
3528 td->ip += 5;
3529 break;
3530 case CEE_BLE_S:
3531 INLINE_FAILURE;
3532 two_arg_branch (td, MINT_BLE_I4, 2 + (gint8) td->ip [1]);
3533 td->ip += 2;
3534 break;
3535 case CEE_BNE_UN:
3536 INLINE_FAILURE;
3537 two_arg_branch (td, MINT_BNE_UN_I4, 5 + read32 (td->ip + 1));
3538 td->ip += 5;
3539 break;
3540 case CEE_BNE_UN_S:
3541 INLINE_FAILURE;
3542 two_arg_branch (td, MINT_BNE_UN_I4, 2 + (gint8) td->ip [1]);
3543 td->ip += 2;
3544 break;
3545 case CEE_BGE_UN:
3546 INLINE_FAILURE;
3547 two_arg_branch (td, MINT_BGE_UN_I4, 5 + read32 (td->ip + 1));
3548 td->ip += 5;
3549 break;
3550 case CEE_BGE_UN_S:
3551 INLINE_FAILURE;
3552 two_arg_branch (td, MINT_BGE_UN_I4, 2 + (gint8) td->ip [1]);
3553 td->ip += 2;
3554 break;
3555 case CEE_BGT_UN:
3556 INLINE_FAILURE;
3557 two_arg_branch (td, MINT_BGT_UN_I4, 5 + read32 (td->ip + 1));
3558 td->ip += 5;
3559 break;
3560 case CEE_BGT_UN_S:
3561 INLINE_FAILURE;
3562 two_arg_branch (td, MINT_BGT_UN_I4, 2 + (gint8) td->ip [1]);
3563 td->ip += 2;
3564 break;
3565 case CEE_BLE_UN:
3566 INLINE_FAILURE;
3567 two_arg_branch (td, MINT_BLE_UN_I4, 5 + read32 (td->ip + 1));
3568 td->ip += 5;
3569 break;
3570 case CEE_BLE_UN_S:
3571 INLINE_FAILURE;
3572 two_arg_branch (td, MINT_BLE_UN_I4, 2 + (gint8) td->ip [1]);
3573 td->ip += 2;
3574 break;
3575 case CEE_BLT_UN:
3576 INLINE_FAILURE;
3577 two_arg_branch (td, MINT_BLT_UN_I4, 5 + read32 (td->ip + 1));
3578 td->ip += 5;
3579 break;
3580 case CEE_BLT_UN_S:
3581 INLINE_FAILURE;
3582 two_arg_branch (td, MINT_BLT_UN_I4, 2 + (gint8) td->ip [1]);
3583 td->ip += 2;
3584 break;
3585 case CEE_SWITCH: {
3586 INLINE_FAILURE;
3587 guint32 n;
3588 const unsigned char *next_ip;
3589 ++td->ip;
3590 n = read32 (td->ip);
3591 interp_add_ins_explicit (td, MINT_SWITCH, MINT_SWITCH_LEN (n));
3592 WRITE32_INS (td->last_ins, 0, &n);
3593 td->ip += 4;
3594 next_ip = td->ip + n * 4;
3595 --td->sp;
3596 int stack_height = td->sp - td->stack;
3597 for (i = 0; i < n; i++) {
3598 offset = read32 (td->ip);
3599 target = next_ip - td->il_code + offset;
3600 if (offset < 0) {
3601 #if DEBUG_INTERP
3602 if (stack_height > 0 && stack_height != td->stack_height [target])
3603 g_warning ("SWITCH with back branch and non-empty stack");
3604 #endif
3605 } else {
3606 td->stack_height [target] = stack_height;
3607 td->vt_stack_size [target] = td->vt_sp;
3608 if (stack_height > 0)
3609 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, stack_height * sizeof (td->stack [0]));
3611 WRITE32_INS (td->last_ins, 2 + i * 2, &target);
3612 td->ip += 4;
3614 break;
3616 case CEE_LDIND_I1:
3617 CHECK_STACK (td, 1);
3618 SIMPLE_OP (td, MINT_LDIND_I1_CHECK);
3619 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3620 BARRIER_IF_VOLATILE (td);
3621 break;
3622 case CEE_LDIND_U1:
3623 CHECK_STACK (td, 1);
3624 SIMPLE_OP (td, MINT_LDIND_U1_CHECK);
3625 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3626 BARRIER_IF_VOLATILE (td);
3627 break;
3628 case CEE_LDIND_I2:
3629 CHECK_STACK (td, 1);
3630 SIMPLE_OP (td, MINT_LDIND_I2_CHECK);
3631 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3632 BARRIER_IF_VOLATILE (td);
3633 break;
3634 case CEE_LDIND_U2:
3635 CHECK_STACK (td, 1);
3636 SIMPLE_OP (td, MINT_LDIND_U2_CHECK);
3637 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3638 BARRIER_IF_VOLATILE (td);
3639 break;
3640 case CEE_LDIND_I4:
3641 CHECK_STACK (td, 1);
3642 SIMPLE_OP (td, MINT_LDIND_I4_CHECK);
3643 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3644 BARRIER_IF_VOLATILE (td);
3645 break;
3646 case CEE_LDIND_U4:
3647 CHECK_STACK (td, 1);
3648 SIMPLE_OP (td, MINT_LDIND_U4_CHECK);
3649 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3650 BARRIER_IF_VOLATILE (td);
3651 break;
3652 case CEE_LDIND_I8:
3653 CHECK_STACK (td, 1);
3654 SIMPLE_OP (td, MINT_LDIND_I8_CHECK);
3655 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3656 BARRIER_IF_VOLATILE (td);
3657 break;
3658 case CEE_LDIND_I:
3659 CHECK_STACK (td, 1);
3660 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3661 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3662 BARRIER_IF_VOLATILE (td);
3663 break;
3664 case CEE_LDIND_R4:
3665 CHECK_STACK (td, 1);
3666 SIMPLE_OP (td, MINT_LDIND_R4_CHECK);
3667 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
3668 BARRIER_IF_VOLATILE (td);
3669 break;
3670 case CEE_LDIND_R8:
3671 CHECK_STACK (td, 1);
3672 SIMPLE_OP (td, MINT_LDIND_R8_CHECK);
3673 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3674 BARRIER_IF_VOLATILE (td);
3675 break;
3676 case CEE_LDIND_REF:
3677 CHECK_STACK (td, 1);
3678 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3679 BARRIER_IF_VOLATILE (td);
3680 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
3681 break;
3682 case CEE_STIND_REF:
3683 CHECK_STACK (td, 2);
3684 BARRIER_IF_VOLATILE (td);
3685 SIMPLE_OP (td, MINT_STIND_REF);
3686 td->sp -= 2;
3687 break;
3688 case CEE_STIND_I1:
3689 CHECK_STACK (td, 2);
3690 BARRIER_IF_VOLATILE (td);
3691 SIMPLE_OP (td, MINT_STIND_I1);
3692 td->sp -= 2;
3693 break;
3694 case CEE_STIND_I2:
3695 CHECK_STACK (td, 2);
3696 BARRIER_IF_VOLATILE (td);
3697 SIMPLE_OP (td, MINT_STIND_I2);
3698 td->sp -= 2;
3699 break;
3700 case CEE_STIND_I4:
3701 CHECK_STACK (td, 2);
3702 BARRIER_IF_VOLATILE (td);
3703 SIMPLE_OP (td, MINT_STIND_I4);
3704 td->sp -= 2;
3705 break;
3706 case CEE_STIND_I:
3707 CHECK_STACK (td, 2);
3708 BARRIER_IF_VOLATILE (td);
3709 SIMPLE_OP (td, MINT_STIND_I);
3710 td->sp -= 2;
3711 break;
3712 case CEE_STIND_I8:
3713 CHECK_STACK (td, 2);
3714 BARRIER_IF_VOLATILE (td);
3715 SIMPLE_OP (td, MINT_STIND_I8);
3716 td->sp -= 2;
3717 break;
3718 case CEE_STIND_R4:
3719 CHECK_STACK (td, 2);
3720 BARRIER_IF_VOLATILE (td);
3721 SIMPLE_OP (td, MINT_STIND_R4);
3722 td->sp -= 2;
3723 break;
3724 case CEE_STIND_R8:
3725 CHECK_STACK (td, 2);
3726 BARRIER_IF_VOLATILE (td);
3727 SIMPLE_OP (td, MINT_STIND_R8);
3728 td->sp -= 2;
3729 break;
3730 case CEE_ADD:
3731 binary_arith_op(td, MINT_ADD_I4);
3732 ++td->ip;
3733 break;
3734 case CEE_SUB:
3735 binary_arith_op(td, MINT_SUB_I4);
3736 ++td->ip;
3737 break;
3738 case CEE_MUL:
3739 binary_arith_op(td, MINT_MUL_I4);
3740 ++td->ip;
3741 break;
3742 case CEE_DIV:
3743 binary_arith_op(td, MINT_DIV_I4);
3744 ++td->ip;
3745 break;
3746 case CEE_DIV_UN:
3747 binary_arith_op(td, MINT_DIV_UN_I4);
3748 ++td->ip;
3749 break;
3750 case CEE_REM:
3751 binary_arith_op (td, MINT_REM_I4);
3752 ++td->ip;
3753 break;
3754 case CEE_REM_UN:
3755 binary_arith_op (td, MINT_REM_UN_I4);
3756 ++td->ip;
3757 break;
3758 case CEE_AND:
3759 binary_arith_op (td, MINT_AND_I4);
3760 ++td->ip;
3761 break;
3762 case CEE_OR:
3763 binary_arith_op (td, MINT_OR_I4);
3764 ++td->ip;
3765 break;
3766 case CEE_XOR:
3767 binary_arith_op (td, MINT_XOR_I4);
3768 ++td->ip;
3769 break;
3770 case CEE_SHL:
3771 shift_op (td, MINT_SHL_I4);
3772 ++td->ip;
3773 break;
3774 case CEE_SHR:
3775 shift_op (td, MINT_SHR_I4);
3776 ++td->ip;
3777 break;
3778 case CEE_SHR_UN:
3779 shift_op (td, MINT_SHR_UN_I4);
3780 ++td->ip;
3781 break;
3782 case CEE_NEG:
3783 unary_arith_op (td, MINT_NEG_I4);
3784 ++td->ip;
3785 break;
3786 case CEE_NOT:
3787 unary_arith_op (td, MINT_NOT_I4);
3788 ++td->ip;
3789 break;
3790 case CEE_CONV_U1:
3791 CHECK_STACK (td, 1);
3792 switch (td->sp [-1].type) {
3793 case STACK_TYPE_R4:
3794 interp_add_ins (td, MINT_CONV_U1_R4);
3795 break;
3796 case STACK_TYPE_R8:
3797 interp_add_ins (td, MINT_CONV_U1_R8);
3798 break;
3799 case STACK_TYPE_I4:
3800 interp_add_ins (td, MINT_CONV_U1_I4);
3801 break;
3802 case STACK_TYPE_I8:
3803 interp_add_ins (td, MINT_CONV_U1_I8);
3804 break;
3805 default:
3806 g_assert_not_reached ();
3808 ++td->ip;
3809 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3810 break;
3811 case CEE_CONV_I1:
3812 CHECK_STACK (td, 1);
3813 switch (td->sp [-1].type) {
3814 case STACK_TYPE_R4:
3815 interp_add_ins (td, MINT_CONV_I1_R4);
3816 break;
3817 case STACK_TYPE_R8:
3818 interp_add_ins (td, MINT_CONV_I1_R8);
3819 break;
3820 case STACK_TYPE_I4:
3821 interp_add_ins (td, MINT_CONV_I1_I4);
3822 break;
3823 case STACK_TYPE_I8:
3824 interp_add_ins (td, MINT_CONV_I1_I8);
3825 break;
3826 default:
3827 g_assert_not_reached ();
3829 ++td->ip;
3830 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3831 break;
3832 case CEE_CONV_U2:
3833 CHECK_STACK (td, 1);
3834 switch (td->sp [-1].type) {
3835 case STACK_TYPE_R4:
3836 interp_add_ins (td, MINT_CONV_U2_R4);
3837 break;
3838 case STACK_TYPE_R8:
3839 interp_add_ins (td, MINT_CONV_U2_R8);
3840 break;
3841 case STACK_TYPE_I4:
3842 interp_add_ins (td, MINT_CONV_U2_I4);
3843 break;
3844 case STACK_TYPE_I8:
3845 interp_add_ins (td, MINT_CONV_U2_I8);
3846 break;
3847 default:
3848 g_assert_not_reached ();
3850 ++td->ip;
3851 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3852 break;
3853 case CEE_CONV_I2:
3854 CHECK_STACK (td, 1);
3855 switch (td->sp [-1].type) {
3856 case STACK_TYPE_R4:
3857 interp_add_ins (td, MINT_CONV_I2_R4);
3858 break;
3859 case STACK_TYPE_R8:
3860 interp_add_ins (td, MINT_CONV_I2_R8);
3861 break;
3862 case STACK_TYPE_I4:
3863 interp_add_ins (td, MINT_CONV_I2_I4);
3864 break;
3865 case STACK_TYPE_I8:
3866 interp_add_ins (td, MINT_CONV_I2_I8);
3867 break;
3868 default:
3869 g_assert_not_reached ();
3871 ++td->ip;
3872 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3873 break;
3874 case CEE_CONV_U:
3875 CHECK_STACK (td, 1);
3876 switch (td->sp [-1].type) {
3877 case STACK_TYPE_R8:
3878 #if SIZEOF_VOID_P == 4
3879 interp_add_ins (td, MINT_CONV_U4_R8);
3880 #else
3881 interp_add_ins (td, MINT_CONV_U8_R8);
3882 #endif
3883 break;
3884 case STACK_TYPE_I4:
3885 #if SIZEOF_VOID_P == 8
3886 interp_add_ins (td, MINT_CONV_U8_I4);
3887 #endif
3888 break;
3889 case STACK_TYPE_I8:
3890 #if SIZEOF_VOID_P == 4
3891 interp_add_ins (td, MINT_CONV_U4_I8);
3892 #endif
3893 break;
3894 case STACK_TYPE_MP:
3895 case STACK_TYPE_O:
3896 break;
3897 default:
3898 g_assert_not_reached ();
3900 ++td->ip;
3901 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3902 break;
3903 case CEE_CONV_I:
3904 CHECK_STACK (td, 1);
3905 switch (td->sp [-1].type) {
3906 case STACK_TYPE_R8:
3907 #if SIZEOF_VOID_P == 8
3908 interp_add_ins (td, MINT_CONV_I8_R8);
3909 #else
3910 interp_add_ins (td, MINT_CONV_I4_R8);
3911 #endif
3912 break;
3913 case STACK_TYPE_I4:
3914 #if SIZEOF_VOID_P == 8
3915 interp_add_ins (td, MINT_CONV_I8_I4);
3916 #endif
3917 break;
3918 case STACK_TYPE_O:
3919 break;
3920 case STACK_TYPE_MP:
3921 break;
3922 case STACK_TYPE_I8:
3923 #if SIZEOF_VOID_P == 4
3924 interp_add_ins (td, MINT_CONV_I4_I8);
3925 #endif
3926 break;
3927 default:
3928 g_assert_not_reached ();
3930 ++td->ip;
3931 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3932 break;
3933 case CEE_CONV_U4:
3934 CHECK_STACK (td, 1);
3935 switch (td->sp [-1].type) {
3936 case STACK_TYPE_R4:
3937 interp_add_ins (td, MINT_CONV_U4_R4);
3938 break;
3939 case STACK_TYPE_R8:
3940 interp_add_ins (td, MINT_CONV_U4_R8);
3941 break;
3942 case STACK_TYPE_I4:
3943 break;
3944 case STACK_TYPE_I8:
3945 interp_add_ins (td, MINT_CONV_U4_I8);
3946 break;
3947 case STACK_TYPE_MP:
3948 #if SIZEOF_VOID_P == 8
3949 interp_add_ins (td, MINT_CONV_U4_I8);
3950 #endif
3951 break;
3952 default:
3953 g_assert_not_reached ();
3955 ++td->ip;
3956 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3957 break;
3958 case CEE_CONV_I4:
3959 CHECK_STACK (td, 1);
3960 switch (td->sp [-1].type) {
3961 case STACK_TYPE_R4:
3962 interp_add_ins (td, MINT_CONV_I4_R4);
3963 break;
3964 case STACK_TYPE_R8:
3965 interp_add_ins (td, MINT_CONV_I4_R8);
3966 break;
3967 case STACK_TYPE_I4:
3968 break;
3969 case STACK_TYPE_I8:
3970 interp_add_ins (td, MINT_CONV_I4_I8);
3971 break;
3972 case STACK_TYPE_MP:
3973 #if SIZEOF_VOID_P == 8
3974 interp_add_ins (td, MINT_CONV_I4_I8);
3975 #endif
3976 break;
3977 default:
3978 g_assert_not_reached ();
3980 ++td->ip;
3981 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3982 break;
3983 case CEE_CONV_I8:
3984 CHECK_STACK (td, 1);
3985 switch (td->sp [-1].type) {
3986 case STACK_TYPE_R4:
3987 interp_add_ins (td, MINT_CONV_I8_R4);
3988 break;
3989 case STACK_TYPE_R8:
3990 interp_add_ins (td, MINT_CONV_I8_R8);
3991 break;
3992 case STACK_TYPE_I4: {
3993 if (interp_ins_is_ldc (td->last_ins) && (inlining || !td->is_bb_start [in_offset])) {
3994 gint64 ct = interp_ldc_i4_get_const (td->last_ins);
3995 interp_clear_ins (td, td->last_ins);
3997 interp_add_ins (td, MINT_LDC_I8);
3998 WRITE64_INS (td->last_ins, 0, &ct);
3999 } else {
4000 interp_add_ins (td, MINT_CONV_I8_I4);
4002 break;
4004 case STACK_TYPE_I8:
4005 break;
4006 case STACK_TYPE_MP:
4007 #if SIZEOF_VOID_P == 4
4008 interp_add_ins (td, MINT_CONV_I8_I4);
4009 #endif
4010 break;
4011 default:
4012 g_assert_not_reached ();
4014 ++td->ip;
4015 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4016 break;
4017 case CEE_CONV_R4:
4018 CHECK_STACK (td, 1);
4019 switch (td->sp [-1].type) {
4020 case STACK_TYPE_R8:
4021 interp_add_ins (td, MINT_CONV_R4_R8);
4022 break;
4023 case STACK_TYPE_I8:
4024 interp_add_ins (td, MINT_CONV_R4_I8);
4025 break;
4026 case STACK_TYPE_I4:
4027 interp_add_ins (td, MINT_CONV_R4_I4);
4028 break;
4029 case STACK_TYPE_R4:
4030 /* no-op */
4031 break;
4032 default:
4033 g_assert_not_reached ();
4035 ++td->ip;
4036 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4037 break;
4038 case CEE_CONV_R8:
4039 CHECK_STACK (td, 1);
4040 switch (td->sp [-1].type) {
4041 case STACK_TYPE_I4:
4042 interp_add_ins (td, MINT_CONV_R8_I4);
4043 break;
4044 case STACK_TYPE_I8:
4045 interp_add_ins (td, MINT_CONV_R8_I8);
4046 break;
4047 case STACK_TYPE_R4:
4048 interp_add_ins (td, MINT_CONV_R8_R4);
4049 break;
4050 case STACK_TYPE_R8:
4051 break;
4052 default:
4053 g_assert_not_reached ();
4055 ++td->ip;
4056 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4057 break;
4058 case CEE_CONV_U8:
4059 CHECK_STACK (td, 1);
4060 switch (td->sp [-1].type) {
4061 case STACK_TYPE_I4:
4062 if (interp_ins_is_ldc (td->last_ins) && (inlining || !td->is_bb_start [in_offset])) {
4063 gint64 ct = (guint32)interp_ldc_i4_get_const (td->last_ins);
4064 interp_clear_ins (td, td->last_ins);
4066 interp_add_ins (td, MINT_LDC_I8);
4067 WRITE64_INS (td->last_ins, 0, &ct);
4068 } else {
4069 interp_add_ins (td, MINT_CONV_U8_I4);
4071 break;
4072 case STACK_TYPE_I8:
4073 break;
4074 case STACK_TYPE_R4:
4075 interp_add_ins (td, MINT_CONV_U8_R4);
4076 break;
4077 case STACK_TYPE_R8:
4078 interp_add_ins (td, MINT_CONV_U8_R8);
4079 break;
4080 case STACK_TYPE_MP:
4081 #if SIZEOF_VOID_P == 4
4082 interp_add_ins (td, MINT_CONV_U8_I4);
4083 #endif
4084 break;
4085 default:
4086 g_assert_not_reached ();
4088 ++td->ip;
4089 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4090 break;
4091 case CEE_CPOBJ: {
4092 CHECK_STACK (td, 2);
4094 token = read32 (td->ip + 1);
4095 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4096 goto_if_nok (error, exit);
4098 if (m_class_is_valuetype (klass)) {
4099 int mt = mint_type (m_class_get_byval_arg (klass));
4100 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_CPOBJ_VT : MINT_CPOBJ);
4101 td->last_ins->data [0] = get_data_item_index(td, klass);
4102 } else {
4103 interp_add_ins (td, MINT_LDIND_REF);
4104 interp_add_ins (td, MINT_STIND_REF);
4106 td->ip += 5;
4107 td->sp -= 2;
4108 break;
4110 case CEE_LDOBJ: {
4111 CHECK_STACK (td, 1);
4113 token = read32 (td->ip + 1);
4115 if (method->wrapper_type != MONO_WRAPPER_NONE)
4116 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4117 else {
4118 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4119 goto_if_nok (error, exit);
4122 interp_emit_ldobj (td, klass);
4124 td->ip += 5;
4125 BARRIER_IF_VOLATILE (td);
4126 break;
4128 case CEE_LDSTR: {
4129 token = mono_metadata_token_index (read32 (td->ip + 1));
4130 td->ip += 5;
4131 if (method->wrapper_type == MONO_WRAPPER_NONE) {
4132 MonoString *s = mono_ldstr_checked (domain, image, token, error);
4133 goto_if_nok (error, exit);
4134 /* GC won't scan code stream, but reference is held by metadata
4135 * machinery so we are good here */
4136 interp_add_ins (td, MINT_LDSTR);
4137 td->last_ins->data [0] = get_data_item_index (td, s);
4138 } else {
4139 /* defer allocation to execution-time */
4140 interp_add_ins (td, MINT_LDSTR_TOKEN);
4141 td->last_ins->data [0] = get_data_item_index (td, GUINT_TO_POINTER (token));
4143 PUSH_TYPE(td, STACK_TYPE_O, mono_defaults.string_class);
4144 break;
4146 case CEE_NEWOBJ: {
4147 MonoMethod *m;
4148 MonoMethodSignature *csignature;
4149 guint32 vt_stack_used = 0;
4150 guint32 vt_res_size = 0;
4152 td->ip++;
4153 token = read32 (td->ip);
4154 td->ip += 4;
4156 if (method->wrapper_type != MONO_WRAPPER_NONE)
4157 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
4158 else {
4159 m = mono_get_method_checked (image, token, NULL, generic_context, error);
4160 goto_if_nok (error, exit);
4163 csignature = mono_method_signature_internal (m);
4164 klass = m->klass;
4166 if (!mono_class_init_internal (klass)) {
4167 mono_error_set_for_class_failure (error, klass);
4168 goto_if_nok (error, exit);
4171 if (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_ABSTRACT) {
4172 char* full_name = mono_type_get_full_name (klass);
4173 mono_error_set_member_access (error, "Cannot create an abstract class: %s", full_name);
4174 g_free (full_name);
4175 goto_if_nok (error, exit);
4178 if (mono_class_is_magic_int (klass) || mono_class_is_magic_float (klass)) {
4179 td->sp -= csignature->param_count;
4180 #if SIZEOF_VOID_P == 8
4181 if (mono_class_is_magic_int (klass) && td->sp [0].type == STACK_TYPE_I4)
4182 interp_add_ins (td, MINT_CONV_I8_I4);
4183 else if (mono_class_is_magic_float (klass) && td->sp [0].type == STACK_TYPE_R4)
4184 interp_add_ins (td, MINT_CONV_R8_R4);
4185 #endif
4186 interp_add_ins (td, MINT_NEWOBJ_MAGIC);
4187 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4188 goto_if_nok (error, exit);
4190 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4191 } else {
4192 if (m_class_get_parent (klass) == mono_defaults.array_class) {
4193 interp_add_ins (td, MINT_NEWOBJ_ARRAY);
4194 td->last_ins->data [0] = get_data_item_index (td, m->klass);
4195 td->last_ins->data [1] = csignature->param_count;
4196 } else if (m_class_get_image (klass) == mono_defaults.corlib &&
4197 !strcmp (m_class_get_name (m->klass), "ByReference`1") &&
4198 !strcmp (m->name, ".ctor")) {
4199 /* public ByReference(ref T value) */
4200 g_assert (csignature->hasthis && csignature->param_count == 1);
4201 interp_add_ins (td, MINT_INTRINS_BYREFERENCE_CTOR);
4202 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4203 } else if (klass != mono_defaults.string_class &&
4204 !mono_class_is_marshalbyref (klass) &&
4205 !mono_class_has_finalizer (klass) &&
4206 !m_class_has_weak_fields (klass)) {
4207 if (!m_class_is_valuetype (klass)) {
4208 InterpInst *newobj_fast = interp_add_ins (td, MINT_NEWOBJ_FAST);
4210 newobj_fast->data [1] = csignature->param_count;
4212 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
4213 goto_if_nok (error, exit);
4214 newobj_fast->data [2] = get_data_item_index (td, vtable);
4216 move_stack (td, (td->sp - td->stack) - csignature->param_count, 2);
4218 StackInfo *tmp_sp = td->sp - csignature->param_count - 2;
4219 SET_TYPE (tmp_sp, STACK_TYPE_O, klass);
4220 SET_TYPE (tmp_sp + 1, STACK_TYPE_O, klass);
4222 if ((mono_interp_opt & INTERP_OPT_INLINE) && interp_method_check_inlining (td, m)) {
4223 MonoMethodHeader *mheader = interp_method_get_header (m, error);
4224 goto_if_nok (error, exit);
4226 if (interp_inline_method (td, m, mheader, error)) {
4227 newobj_fast->data [0] = 0xffff;
4228 break;
4231 // If inlining failed we need to restore the stack
4232 move_stack (td, (td->sp - td->stack) - csignature->param_count, -2);
4233 // Set the method to be executed as part of newobj instruction
4234 newobj_fast->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4235 } else {
4236 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4237 interp_add_ins (td, MINT_NEWOBJ_VTST_FAST);
4238 else
4239 interp_add_ins (td, MINT_NEWOBJ_VT_FAST);
4241 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4242 td->last_ins->data [1] = csignature->param_count;
4244 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4245 td->last_ins->data [2] = mono_class_value_size (klass, NULL);
4248 } else {
4249 interp_add_ins (td, MINT_NEWOBJ);
4250 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4252 goto_if_nok (error, exit);
4253 /* The constructor was not inlined, abort inlining of current method */
4254 INLINE_FAILURE;
4256 td->sp -= csignature->param_count;
4257 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4258 vt_res_size = mono_class_value_size (klass, NULL);
4259 PUSH_VT (td, vt_res_size);
4261 for (i = 0; i < csignature->param_count; ++i) {
4262 int mt = mint_type(csignature->params [i]);
4263 if (mt == MINT_TYPE_VT) {
4264 MonoClass *k = mono_class_from_mono_type_internal (csignature->params [i]);
4265 gint32 size = mono_class_value_size (k, NULL);
4266 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4267 vt_stack_used += size;
4270 if (vt_stack_used != 0 || vt_res_size != 0) {
4271 interp_add_ins (td, MINT_VTRESULT);
4272 td->last_ins->data [0] = vt_res_size;
4273 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
4274 td->vt_sp -= vt_stack_used;
4276 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4278 break;
4280 case CEE_CASTCLASS:
4281 case CEE_ISINST: {
4282 gboolean isinst_instr = *td->ip == CEE_ISINST;
4283 CHECK_STACK (td, 1);
4284 token = read32 (td->ip + 1);
4285 klass = mini_get_class (method, token, generic_context);
4286 CHECK_TYPELOAD (klass);
4287 interp_handle_isinst (td, klass, isinst_instr);
4288 if (!isinst_instr)
4289 td->sp [-1].klass = klass;
4290 break;
4292 case CEE_CONV_R_UN:
4293 switch (td->sp [-1].type) {
4294 case STACK_TYPE_R8:
4295 break;
4296 case STACK_TYPE_I8:
4297 interp_add_ins (td, MINT_CONV_R_UN_I8);
4298 break;
4299 case STACK_TYPE_I4:
4300 interp_add_ins (td, MINT_CONV_R_UN_I4);
4301 break;
4302 default:
4303 g_assert_not_reached ();
4305 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4306 ++td->ip;
4307 break;
4308 case CEE_UNBOX:
4309 CHECK_STACK (td, 1);
4310 token = read32 (td->ip + 1);
4312 if (method->wrapper_type != MONO_WRAPPER_NONE)
4313 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4314 else {
4315 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4316 goto_if_nok (error, exit);
4319 if (mono_class_is_nullable (klass)) {
4320 MonoMethod *target_method;
4321 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4322 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4323 else
4324 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4325 goto_if_nok (error, exit);
4326 /* td->ip is incremented by interp_transform_call */
4327 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4328 goto exit;
4330 * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type
4331 * We create a local variable in the frame so that we can fetch its address.
4333 int local_offset = create_interp_local (td, m_class_get_byval_arg (klass));
4334 store_local_general (td, local_offset, m_class_get_byval_arg (klass));
4335 interp_add_ins (td, MINT_LDLOCA_S);
4336 td->last_ins->data [0] = local_offset;
4337 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
4338 } else {
4339 interp_add_ins (td, MINT_UNBOX);
4340 td->last_ins->data [0] = get_data_item_index (td, klass);
4341 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
4342 td->ip += 5;
4344 break;
4345 case CEE_UNBOX_ANY:
4346 CHECK_STACK (td, 1);
4347 token = read32 (td->ip + 1);
4349 klass = mini_get_class (method, token, generic_context);
4350 CHECK_TYPELOAD (klass);
4352 if (mini_type_is_reference (m_class_get_byval_arg (klass))) {
4353 int mt = mint_type (m_class_get_byval_arg (klass));
4354 interp_handle_isinst (td, klass, FALSE);
4355 SET_TYPE (td->sp - 1, stack_type [mt], klass);
4356 } else if (mono_class_is_nullable (klass)) {
4357 MonoMethod *target_method;
4358 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4359 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4360 else
4361 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4362 goto_if_nok (error, exit);
4363 /* td->ip is incremented by interp_transform_call */
4364 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4365 goto exit;
4366 } else {
4367 interp_add_ins (td, MINT_UNBOX);
4368 td->last_ins->data [0] = get_data_item_index (td, klass);
4370 interp_emit_ldobj (td, klass);
4372 td->ip += 5;
4375 break;
4376 case CEE_THROW:
4377 INLINE_FAILURE;
4378 CHECK_STACK (td, 1);
4379 SIMPLE_OP (td, MINT_THROW);
4380 td->sp = td->stack;
4381 break;
4382 case CEE_LDFLDA: {
4383 CHECK_STACK (td, 1);
4384 token = read32 (td->ip + 1);
4385 field = interp_field_from_token (method, token, &klass, generic_context, error);
4386 goto_if_nok (error, exit);
4387 MonoType *ftype = mono_field_get_type_internal (field);
4388 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4389 mono_class_init_internal (klass);
4390 #ifndef DISABLE_REMOTING
4391 if (m_class_get_marshalbyref (klass) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
4392 g_assert (!is_static);
4393 int offset = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4395 interp_add_ins (td, MINT_MONO_LDPTR);
4396 td->last_ins->data [0] = get_data_item_index (td, klass);
4397 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4398 interp_add_ins (td, MINT_MONO_LDPTR);
4399 td->last_ins->data [0] = get_data_item_index (td, field);
4400 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4401 interp_add_ins (td, MINT_LDC_I4);
4402 WRITE32_INS (td->last_ins, 0, &offset);
4403 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
4404 #if SIZEOF_VOID_P == 8
4405 interp_add_ins (td, MINT_CONV_I8_I4);
4406 #endif
4408 MonoMethod *wrapper = mono_marshal_get_ldflda_wrapper (field->type);
4409 /* td->ip is incremented by interp_transform_call */
4410 if (!interp_transform_call (td, method, wrapper, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4411 goto exit;
4412 } else
4413 #endif
4415 if (is_static) {
4416 interp_add_ins (td, MINT_POP);
4417 td->last_ins->data [0] = 0;
4418 interp_emit_ldsflda (td, field, error);
4419 goto_if_nok (error, exit);
4420 } else {
4421 if ((td->sp - 1)->type == STACK_TYPE_O) {
4422 interp_add_ins (td, MINT_LDFLDA);
4423 } else {
4424 g_assert ((td->sp -1)->type == STACK_TYPE_MP);
4425 interp_add_ins (td, MINT_LDFLDA_UNSAFE);
4427 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4429 td->ip += 5;
4431 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4432 break;
4434 case CEE_LDFLD: {
4435 CHECK_STACK (td, 1);
4436 token = read32 (td->ip + 1);
4437 field = interp_field_from_token (method, token, &klass, generic_context, error);
4438 goto_if_nok (error, exit);
4439 MonoType *ftype = mono_field_get_type_internal (field);
4440 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4441 mono_class_init_internal (klass);
4443 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4444 mt = mint_type (m_class_get_byval_arg (field_klass));
4445 #ifndef DISABLE_REMOTING
4446 if (m_class_get_marshalbyref (klass)) {
4447 g_assert (!is_static);
4448 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDRMFLD_VT : MINT_LDRMFLD);
4449 td->last_ins->data [0] = get_data_item_index (td, field);
4450 } else
4451 #endif
4453 if (is_static) {
4454 interp_add_ins (td, MINT_POP);
4455 td->last_ins->data [0] = 0;
4456 interp_emit_sfld_access (td, field, field_klass, mt, TRUE, error);
4457 goto_if_nok (error, exit);
4458 } else {
4459 int opcode = MINT_LDFLD_I1 + mt - MINT_TYPE_I1;
4460 #ifdef NO_UNALIGNED_ACCESS
4461 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4462 opcode = get_unaligned_opcode (opcode);
4463 #endif
4464 interp_add_ins (td, opcode);
4465 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4466 if (mt == MINT_TYPE_VT) {
4467 int size = mono_class_value_size (field_klass, NULL);
4468 WRITE32_INS (td->last_ins, 1, &size);
4472 if (mt == MINT_TYPE_VT) {
4473 int size = mono_class_value_size (field_klass, NULL);
4474 PUSH_VT (td, size);
4476 if (td->sp [-1].type == STACK_TYPE_VT) {
4477 int size = mono_class_value_size (klass, NULL);
4478 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4479 int field_vt_size = 0;
4480 if (mt == MINT_TYPE_VT) {
4482 * Pop the loaded field from the vtstack (it will still be present
4483 * at the same vtstack address) and we will load it in place of the
4484 * containing valuetype with the second MINT_VTRESULT.
4486 field_vt_size = mono_class_value_size (field_klass, NULL);
4487 field_vt_size = ALIGN_TO (field_vt_size, MINT_VT_ALIGNMENT);
4488 interp_add_ins (td, MINT_VTRESULT);
4489 td->last_ins->data [0] = 0;
4490 WRITE32_INS (td->last_ins, 1, &field_vt_size);
4492 td->vt_sp -= size;
4493 interp_add_ins (td, MINT_VTRESULT);
4494 td->last_ins->data [0] = field_vt_size;
4495 WRITE32_INS (td->last_ins, 1, &size);
4497 td->ip += 5;
4498 SET_TYPE (td->sp - 1, stack_type [mt], field_klass);
4499 BARRIER_IF_VOLATILE (td);
4500 break;
4502 case CEE_STFLD: {
4503 CHECK_STACK (td, 2);
4504 token = read32 (td->ip + 1);
4505 field = interp_field_from_token (method, token, &klass, generic_context, error);
4506 goto_if_nok (error, exit);
4507 MonoType *ftype = mono_field_get_type_internal (field);
4508 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4509 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4510 mono_class_init_internal (klass);
4511 mt = mint_type (ftype);
4513 BARRIER_IF_VOLATILE (td);
4515 #ifndef DISABLE_REMOTING
4516 if (m_class_get_marshalbyref (klass)) {
4517 g_assert (!is_static);
4518 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_STRMFLD_VT : MINT_STRMFLD);
4519 td->last_ins->data [0] = get_data_item_index (td, field);
4520 } else
4521 #endif
4523 if (is_static) {
4524 interp_add_ins (td, MINT_POP);
4525 td->last_ins->data [0] = 1;
4526 interp_emit_sfld_access (td, field, field_klass, mt, FALSE, error);
4527 goto_if_nok (error, exit);
4529 /* the vtable of the field might not be initialized at this point */
4530 mono_class_vtable_checked (domain, field_klass, error);
4531 goto_if_nok (error, exit);
4532 } else {
4533 int opcode = MINT_STFLD_I1 + mt - MINT_TYPE_I1;
4534 #ifdef NO_UNALIGNED_ACCESS
4535 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4536 opcode = get_unaligned_opcode (opcode);
4537 #endif
4538 interp_add_ins (td, opcode);
4539 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4540 if (mt == MINT_TYPE_VT) {
4541 /* the vtable of the field might not be initialized at this point */
4542 mono_class_vtable_checked (domain, field_klass, error);
4543 goto_if_nok (error, exit);
4545 td->last_ins->data [1] = get_data_item_index (td, field_klass);
4549 if (mt == MINT_TYPE_VT) {
4550 int size = mono_class_value_size (field_klass, NULL);
4551 POP_VT (td, size);
4553 td->ip += 5;
4554 td->sp -= 2;
4555 break;
4557 case CEE_LDSFLDA: {
4558 token = read32 (td->ip + 1);
4559 field = interp_field_from_token (method, token, &klass, generic_context, error);
4560 goto_if_nok (error, exit);
4561 interp_emit_ldsflda (td, field, error);
4562 goto_if_nok (error, exit);
4563 td->ip += 5;
4564 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
4565 break;
4567 case CEE_LDSFLD: {
4568 token = read32 (td->ip + 1);
4569 field = interp_field_from_token (method, token, &klass, generic_context, error);
4570 goto_if_nok (error, exit);
4571 MonoType *ftype = mono_field_get_type_internal (field);
4572 mt = mint_type (ftype);
4573 klass = mono_class_from_mono_type_internal (ftype);
4575 interp_emit_sfld_access (td, field, klass, mt, TRUE, error);
4576 goto_if_nok (error, exit);
4578 if (mt == MINT_TYPE_VT) {
4579 int size = mono_class_value_size (klass, NULL);
4580 PUSH_VT(td, size);
4582 td->ip += 5;
4583 PUSH_TYPE(td, stack_type [mt], klass);
4584 break;
4586 case CEE_STSFLD: {
4587 CHECK_STACK (td, 1);
4588 token = read32 (td->ip + 1);
4589 field = interp_field_from_token (method, token, &klass, generic_context, error);
4590 goto_if_nok (error, exit);
4591 MonoType *ftype = mono_field_get_type_internal (field);
4592 mt = mint_type (ftype);
4594 /* the vtable of the field might not be initialized at this point */
4595 MonoClass *fld_klass = mono_class_from_mono_type_internal (ftype);
4596 mono_class_vtable_checked (domain, fld_klass, error);
4597 goto_if_nok (error, exit);
4599 interp_emit_sfld_access (td, field, fld_klass, mt, FALSE, error);
4600 goto_if_nok (error, exit);
4602 if (mt == MINT_TYPE_VT) {
4603 int size = mono_class_value_size (fld_klass, NULL);
4604 POP_VT(td, size);
4606 td->ip += 5;
4607 --td->sp;
4608 break;
4610 case CEE_STOBJ: {
4611 token = read32 (td->ip + 1);
4613 if (method->wrapper_type != MONO_WRAPPER_NONE)
4614 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4615 else
4616 klass = mini_get_class (method, token, generic_context);
4617 CHECK_TYPELOAD (klass);
4619 BARRIER_IF_VOLATILE (td);
4621 interp_emit_stobj (td, klass);
4623 td->ip += 5;
4624 break;
4626 case CEE_CONV_OVF_I_UN:
4627 case CEE_CONV_OVF_U_UN:
4628 CHECK_STACK (td, 1);
4629 switch (td->sp [-1].type) {
4630 case STACK_TYPE_R8:
4631 #if SIZEOF_VOID_P == 8
4632 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4633 #else
4634 interp_add_ins (td, MINT_CONV_OVF_I4_UN_R8);
4635 #endif
4636 break;
4637 case STACK_TYPE_I8:
4638 #if SIZEOF_VOID_P == 4
4639 interp_add_ins (td, MINT_CONV_OVF_I4_UN_I8);
4640 #endif
4641 break;
4642 case STACK_TYPE_I4:
4643 #if SIZEOF_VOID_P == 8
4644 interp_add_ins (td, MINT_CONV_I8_U4);
4645 #elif SIZEOF_VOID_P == 4
4646 if (*td->ip == CEE_CONV_OVF_I_UN)
4647 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
4648 #endif
4649 break;
4650 default:
4651 g_assert_not_reached ();
4652 break;
4654 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4655 ++td->ip;
4656 break;
4657 case CEE_CONV_OVF_I8_UN:
4658 case CEE_CONV_OVF_U8_UN:
4659 CHECK_STACK (td, 1);
4660 switch (td->sp [-1].type) {
4661 case STACK_TYPE_R8:
4662 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4663 break;
4664 case STACK_TYPE_I8:
4665 if (*td->ip == CEE_CONV_OVF_I8_UN)
4666 interp_add_ins (td, MINT_CONV_OVF_I8_U8);
4667 break;
4668 case STACK_TYPE_I4:
4669 interp_add_ins (td, MINT_CONV_I8_U4);
4670 break;
4671 case STACK_TYPE_R4:
4672 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R4);
4673 break;
4674 default:
4675 g_assert_not_reached ();
4676 break;
4678 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4679 ++td->ip;
4680 break;
4681 case CEE_BOX: {
4682 int size;
4683 CHECK_STACK (td, 1);
4684 token = read32 (td->ip + 1);
4685 if (method->wrapper_type != MONO_WRAPPER_NONE)
4686 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4687 else
4688 klass = mini_get_class (method, token, generic_context);
4689 CHECK_TYPELOAD (klass);
4691 if (mono_class_is_nullable (klass)) {
4692 MonoMethod *target_method = mono_class_get_method_from_name_checked (klass, "Box", 1, 0, error);
4693 goto_if_nok (error, exit);
4694 /* td->ip is incremented by interp_transform_call */
4695 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4696 goto exit;
4697 } else if (!m_class_is_valuetype (klass)) {
4698 /* already boxed, do nothing. */
4699 td->ip += 5;
4700 } else {
4701 if (G_UNLIKELY (m_class_is_byreflike (klass))) {
4702 mono_error_set_bad_image (error, image, "Cannot box IsByRefLike type '%s.%s'", m_class_get_name_space (klass), m_class_get_name (klass));
4703 goto exit;
4705 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4706 size = mono_class_value_size (klass, NULL);
4707 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4708 td->vt_sp -= size;
4709 } else if (td->sp [-1].type == STACK_TYPE_R8 && m_class_get_byval_arg (klass)->type == MONO_TYPE_R4) {
4710 interp_add_ins (td, MINT_CONV_R4_R8);
4712 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
4713 goto_if_nok (error, exit);
4715 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4716 interp_add_ins (td, MINT_BOX_VT);
4717 else
4718 interp_add_ins (td, MINT_BOX);
4719 td->last_ins->data [0] = get_data_item_index (td, vtable);
4720 td->last_ins->data [1] = 0;
4721 SET_TYPE(td->sp - 1, STACK_TYPE_O, klass);
4722 td->ip += 5;
4725 break;
4727 case CEE_NEWARR: {
4728 CHECK_STACK (td, 1);
4729 token = read32 (td->ip + 1);
4731 if (method->wrapper_type != MONO_WRAPPER_NONE)
4732 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4733 else
4734 klass = mini_get_class (method, token, generic_context);
4735 CHECK_TYPELOAD (klass);
4737 MonoClass *array_class = mono_class_create_array (klass, 1);
4738 MonoVTable *vtable = mono_class_vtable_checked (domain, array_class, error);
4739 goto_if_nok (error, exit);
4741 unsigned char lentype = (td->sp - 1)->type;
4742 if (lentype == STACK_TYPE_I8) {
4743 /* mimic mini behaviour */
4744 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
4745 } else {
4746 g_assert (lentype == STACK_TYPE_I4);
4747 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
4749 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
4750 interp_add_ins (td, MINT_NEWARR);
4751 td->last_ins->data [0] = get_data_item_index (td, vtable);
4752 SET_TYPE (td->sp - 1, STACK_TYPE_O, klass);
4753 td->ip += 5;
4754 break;
4756 case CEE_LDLEN:
4757 CHECK_STACK (td, 1);
4758 SIMPLE_OP (td, MINT_LDLEN);
4759 #ifdef MONO_BIG_ARRAYS
4760 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
4761 #else
4762 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
4763 #endif
4764 break;
4765 case CEE_LDELEMA: {
4766 gint32 size;
4767 CHECK_STACK (td, 2);
4768 ENSURE_I4 (td, 1);
4769 token = read32 (td->ip + 1);
4771 if (method->wrapper_type != MONO_WRAPPER_NONE)
4772 klass = (MonoClass *) mono_method_get_wrapper_data (method, token);
4773 else
4774 klass = mini_get_class (method, token, generic_context);
4776 CHECK_TYPELOAD (klass);
4778 if (!m_class_is_valuetype (klass) && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
4780 * Check the class for failures before the type check, which can
4781 * throw other exceptions.
4783 mono_class_setup_vtable (klass);
4784 CHECK_TYPELOAD (klass);
4785 interp_add_ins (td, MINT_LDELEMA_TC);
4786 td->last_ins->data [0] = get_data_item_index (td, klass);
4787 td->last_ins->data [1] = 2;
4788 } else {
4789 interp_add_ins (td, MINT_LDELEMA_FAST);
4790 mono_class_init_internal (klass);
4791 size = mono_class_array_element_size (klass);
4792 WRITE32_INS (td->last_ins, 0, &size);
4795 readonly = FALSE;
4797 td->ip += 5;
4798 --td->sp;
4799 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4800 break;
4802 case CEE_LDELEM_I1:
4803 CHECK_STACK (td, 2);
4804 ENSURE_I4 (td, 1);
4805 SIMPLE_OP (td, MINT_LDELEM_I1);
4806 --td->sp;
4807 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4808 break;
4809 case CEE_LDELEM_U1:
4810 CHECK_STACK (td, 2);
4811 ENSURE_I4 (td, 1);
4812 SIMPLE_OP (td, MINT_LDELEM_U1);
4813 --td->sp;
4814 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4815 break;
4816 case CEE_LDELEM_I2:
4817 CHECK_STACK (td, 2);
4818 ENSURE_I4 (td, 1);
4819 SIMPLE_OP (td, MINT_LDELEM_I2);
4820 --td->sp;
4821 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4822 break;
4823 case CEE_LDELEM_U2:
4824 CHECK_STACK (td, 2);
4825 ENSURE_I4 (td, 1);
4826 SIMPLE_OP (td, MINT_LDELEM_U2);
4827 --td->sp;
4828 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4829 break;
4830 case CEE_LDELEM_I4:
4831 CHECK_STACK (td, 2);
4832 ENSURE_I4 (td, 1);
4833 SIMPLE_OP (td, MINT_LDELEM_I4);
4834 --td->sp;
4835 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4836 break;
4837 case CEE_LDELEM_U4:
4838 CHECK_STACK (td, 2);
4839 ENSURE_I4 (td, 1);
4840 SIMPLE_OP (td, MINT_LDELEM_U4);
4841 --td->sp;
4842 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4843 break;
4844 case CEE_LDELEM_I8:
4845 CHECK_STACK (td, 2);
4846 ENSURE_I4 (td, 1);
4847 SIMPLE_OP (td, MINT_LDELEM_I8);
4848 --td->sp;
4849 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4850 break;
4851 case CEE_LDELEM_I:
4852 CHECK_STACK (td, 2);
4853 ENSURE_I4 (td, 1);
4854 SIMPLE_OP (td, MINT_LDELEM_I);
4855 --td->sp;
4856 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4857 break;
4858 case CEE_LDELEM_R4:
4859 CHECK_STACK (td, 2);
4860 ENSURE_I4 (td, 1);
4861 SIMPLE_OP (td, MINT_LDELEM_R4);
4862 --td->sp;
4863 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4864 break;
4865 case CEE_LDELEM_R8:
4866 CHECK_STACK (td, 2);
4867 ENSURE_I4 (td, 1);
4868 SIMPLE_OP (td, MINT_LDELEM_R8);
4869 --td->sp;
4870 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4871 break;
4872 case CEE_LDELEM_REF:
4873 CHECK_STACK (td, 2);
4874 ENSURE_I4 (td, 1);
4875 SIMPLE_OP (td, MINT_LDELEM_REF);
4876 --td->sp;
4877 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
4878 break;
4879 case CEE_LDELEM:
4880 CHECK_STACK (td, 2);
4881 token = read32 (td->ip + 1);
4882 klass = mini_get_class (method, token, generic_context);
4883 CHECK_TYPELOAD (klass);
4884 switch (mint_type (m_class_get_byval_arg (klass))) {
4885 case MINT_TYPE_I1:
4886 ENSURE_I4 (td, 1);
4887 SIMPLE_OP (td, MINT_LDELEM_I1);
4888 --td->sp;
4889 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4890 break;
4891 case MINT_TYPE_U1:
4892 ENSURE_I4 (td, 1);
4893 SIMPLE_OP (td, MINT_LDELEM_U1);
4894 --td->sp;
4895 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4896 break;
4897 case MINT_TYPE_U2:
4898 ENSURE_I4 (td, 1);
4899 SIMPLE_OP (td, MINT_LDELEM_U2);
4900 --td->sp;
4901 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4902 break;
4903 case MINT_TYPE_I2:
4904 ENSURE_I4 (td, 1);
4905 SIMPLE_OP (td, MINT_LDELEM_I2);
4906 --td->sp;
4907 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4908 break;
4909 case MINT_TYPE_I4:
4910 ENSURE_I4 (td, 1);
4911 SIMPLE_OP (td, MINT_LDELEM_I4);
4912 --td->sp;
4913 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4914 break;
4915 case MINT_TYPE_I8:
4916 ENSURE_I4 (td, 1);
4917 SIMPLE_OP (td, MINT_LDELEM_I8);
4918 --td->sp;
4919 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4920 break;
4921 case MINT_TYPE_R4:
4922 ENSURE_I4 (td, 1);
4923 SIMPLE_OP (td, MINT_LDELEM_R4);
4924 --td->sp;
4925 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4926 break;
4927 case MINT_TYPE_R8:
4928 ENSURE_I4 (td, 1);
4929 SIMPLE_OP (td, MINT_LDELEM_R8);
4930 --td->sp;
4931 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4932 break;
4933 case MINT_TYPE_O:
4934 ENSURE_I4 (td, 1);
4935 SIMPLE_OP (td, MINT_LDELEM_REF);
4936 --td->sp;
4937 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
4938 break;
4939 case MINT_TYPE_VT: {
4940 int size = mono_class_value_size (klass, NULL);
4941 ENSURE_I4 (td, 1);
4942 SIMPLE_OP (td, MINT_LDELEM_VT);
4943 WRITE32_INS (td->last_ins, 0, &size);
4944 --td->sp;
4945 SET_TYPE (td->sp - 1, STACK_TYPE_VT, klass);
4946 PUSH_VT (td, size);
4947 break;
4949 default: {
4950 GString *res = g_string_new ("");
4951 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
4952 g_print ("LDELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
4953 g_string_free (res, TRUE);
4954 g_assert (0);
4955 break;
4958 td->ip += 4;
4959 break;
4960 case CEE_STELEM_I:
4961 CHECK_STACK (td, 3);
4962 ENSURE_I4 (td, 2);
4963 SIMPLE_OP (td, MINT_STELEM_I);
4964 td->sp -= 3;
4965 break;
4966 case CEE_STELEM_I1:
4967 CHECK_STACK (td, 3);
4968 ENSURE_I4 (td, 2);
4969 SIMPLE_OP (td, MINT_STELEM_I1);
4970 td->sp -= 3;
4971 break;
4972 case CEE_STELEM_I2:
4973 CHECK_STACK (td, 3);
4974 ENSURE_I4 (td, 2);
4975 SIMPLE_OP (td, MINT_STELEM_I2);
4976 td->sp -= 3;
4977 break;
4978 case CEE_STELEM_I4:
4979 CHECK_STACK (td, 3);
4980 ENSURE_I4 (td, 2);
4981 SIMPLE_OP (td, MINT_STELEM_I4);
4982 td->sp -= 3;
4983 break;
4984 case CEE_STELEM_I8:
4985 CHECK_STACK (td, 3);
4986 ENSURE_I4 (td, 2);
4987 SIMPLE_OP (td, MINT_STELEM_I8);
4988 td->sp -= 3;
4989 break;
4990 case CEE_STELEM_R4:
4991 CHECK_STACK (td, 3);
4992 ENSURE_I4 (td, 2);
4993 SIMPLE_OP (td, MINT_STELEM_R4);
4994 td->sp -= 3;
4995 break;
4996 case CEE_STELEM_R8:
4997 CHECK_STACK (td, 3);
4998 ENSURE_I4 (td, 2);
4999 SIMPLE_OP (td, MINT_STELEM_R8);
5000 td->sp -= 3;
5001 break;
5002 case CEE_STELEM_REF:
5003 CHECK_STACK (td, 3);
5004 ENSURE_I4 (td, 2);
5005 SIMPLE_OP (td, MINT_STELEM_REF);
5006 td->sp -= 3;
5007 break;
5008 case CEE_STELEM:
5009 CHECK_STACK (td, 3);
5010 ENSURE_I4 (td, 2);
5011 token = read32 (td->ip + 1);
5012 klass = mini_get_class (method, token, generic_context);
5013 CHECK_TYPELOAD (klass);
5014 switch (mint_type (m_class_get_byval_arg (klass))) {
5015 case MINT_TYPE_I1:
5016 SIMPLE_OP (td, MINT_STELEM_I1);
5017 break;
5018 case MINT_TYPE_U1:
5019 SIMPLE_OP (td, MINT_STELEM_U1);
5020 break;
5021 case MINT_TYPE_I2:
5022 SIMPLE_OP (td, MINT_STELEM_I2);
5023 break;
5024 case MINT_TYPE_U2:
5025 SIMPLE_OP (td, MINT_STELEM_U2);
5026 break;
5027 case MINT_TYPE_I4:
5028 SIMPLE_OP (td, MINT_STELEM_I4);
5029 break;
5030 case MINT_TYPE_I8:
5031 SIMPLE_OP (td, MINT_STELEM_I8);
5032 break;
5033 case MINT_TYPE_R4:
5034 SIMPLE_OP (td, MINT_STELEM_R4);
5035 break;
5036 case MINT_TYPE_R8:
5037 SIMPLE_OP (td, MINT_STELEM_R8);
5038 break;
5039 case MINT_TYPE_O:
5040 SIMPLE_OP (td, MINT_STELEM_REF);
5041 break;
5042 case MINT_TYPE_VT: {
5043 int size = mono_class_value_size (klass, NULL);
5044 SIMPLE_OP (td, MINT_STELEM_VT);
5045 td->last_ins->data [0] = get_data_item_index (td, klass);
5046 WRITE32_INS (td->last_ins, 1, &size);
5047 POP_VT (td, size);
5048 break;
5050 default: {
5051 GString *res = g_string_new ("");
5052 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
5053 g_print ("STELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
5054 g_string_free (res, TRUE);
5055 g_assert (0);
5056 break;
5059 td->ip += 4;
5060 td->sp -= 3;
5061 break;
5062 #if 0
5063 case CEE_CONV_OVF_U1:
5065 case CEE_CONV_OVF_I8:
5067 #if SIZEOF_VOID_P == 8
5068 case CEE_CONV_OVF_U:
5069 #endif
5070 #endif
5071 case CEE_CKFINITE:
5072 CHECK_STACK (td, 1);
5073 SIMPLE_OP (td, MINT_CKFINITE);
5074 break;
5075 case CEE_MKREFANY:
5076 CHECK_STACK (td, 1);
5078 token = read32 (td->ip + 1);
5079 klass = mini_get_class (method, token, generic_context);
5080 CHECK_TYPELOAD (klass);
5082 interp_add_ins (td, MINT_MKREFANY);
5083 td->last_ins->data [0] = get_data_item_index (td, klass);
5085 td->ip += 5;
5086 PUSH_VT (td, sizeof (MonoTypedRef));
5087 SET_TYPE(td->sp - 1, STACK_TYPE_VT, mono_defaults.typed_reference_class);
5088 break;
5089 case CEE_REFANYVAL: {
5090 CHECK_STACK (td, 1);
5092 token = read32 (td->ip + 1);
5093 klass = mini_get_class (method, token, generic_context);
5094 CHECK_TYPELOAD (klass);
5096 interp_add_ins (td, MINT_REFANYVAL);
5097 td->last_ins->data [0] = get_data_item_index (td, klass);
5099 POP_VT (td, sizeof (MonoTypedRef));
5100 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5102 td->ip += 5;
5103 break;
5105 case CEE_CONV_OVF_I1:
5106 case CEE_CONV_OVF_I1_UN: {
5107 gboolean is_un = *td->ip == CEE_CONV_OVF_I1_UN;
5108 CHECK_STACK (td, 1);
5109 switch (td->sp [-1].type) {
5110 case STACK_TYPE_R8:
5111 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_UN_R8 : MINT_CONV_OVF_I1_R8);
5112 break;
5113 case STACK_TYPE_I4:
5114 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U4 : MINT_CONV_OVF_I1_I4);
5115 break;
5116 case STACK_TYPE_I8:
5117 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U8 : MINT_CONV_OVF_I1_I8);
5118 break;
5119 default:
5120 g_assert_not_reached ();
5122 ++td->ip;
5123 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5124 break;
5126 case CEE_CONV_OVF_U1:
5127 case CEE_CONV_OVF_U1_UN:
5128 CHECK_STACK (td, 1);
5129 switch (td->sp [-1].type) {
5130 case STACK_TYPE_R8:
5131 interp_add_ins (td, MINT_CONV_OVF_U1_R8);
5132 break;
5133 case STACK_TYPE_I4:
5134 interp_add_ins (td, MINT_CONV_OVF_U1_I4);
5135 break;
5136 case STACK_TYPE_I8:
5137 interp_add_ins (td, MINT_CONV_OVF_U1_I8);
5138 break;
5139 default:
5140 g_assert_not_reached ();
5142 ++td->ip;
5143 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5144 break;
5145 case CEE_CONV_OVF_I2:
5146 case CEE_CONV_OVF_I2_UN: {
5147 gboolean is_un = *td->ip == CEE_CONV_OVF_I2_UN;
5148 CHECK_STACK (td, 1);
5149 switch (td->sp [-1].type) {
5150 case STACK_TYPE_R8:
5151 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_UN_R8 : MINT_CONV_OVF_I2_R8);
5152 break;
5153 case STACK_TYPE_I4:
5154 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U4 : MINT_CONV_OVF_I2_I4);
5155 break;
5156 case STACK_TYPE_I8:
5157 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U8 : MINT_CONV_OVF_I2_I8);
5158 break;
5159 default:
5160 g_assert_not_reached ();
5162 ++td->ip;
5163 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5164 break;
5166 case CEE_CONV_OVF_U2_UN:
5167 case CEE_CONV_OVF_U2:
5168 CHECK_STACK (td, 1);
5169 switch (td->sp [-1].type) {
5170 case STACK_TYPE_R8:
5171 interp_add_ins (td, MINT_CONV_OVF_U2_R8);
5172 break;
5173 case STACK_TYPE_I4:
5174 interp_add_ins (td, MINT_CONV_OVF_U2_I4);
5175 break;
5176 case STACK_TYPE_I8:
5177 interp_add_ins (td, MINT_CONV_OVF_U2_I8);
5178 break;
5179 default:
5180 g_assert_not_reached ();
5182 ++td->ip;
5183 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5184 break;
5185 #if SIZEOF_VOID_P == 4
5186 case CEE_CONV_OVF_I:
5187 #endif
5188 case CEE_CONV_OVF_I4:
5189 case CEE_CONV_OVF_I4_UN:
5190 CHECK_STACK (td, 1);
5191 switch (td->sp [-1].type) {
5192 case STACK_TYPE_R4:
5193 interp_add_ins (td, MINT_CONV_OVF_I4_R4);
5194 break;
5195 case STACK_TYPE_R8:
5196 interp_add_ins (td, MINT_CONV_OVF_I4_R8);
5197 break;
5198 case STACK_TYPE_I4:
5199 if (*td->ip == CEE_CONV_OVF_I4_UN)
5200 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
5201 break;
5202 case STACK_TYPE_I8:
5203 if (*td->ip == CEE_CONV_OVF_I4_UN)
5204 interp_add_ins (td, MINT_CONV_OVF_I4_U8);
5205 else
5206 interp_add_ins (td, MINT_CONV_OVF_I4_I8);
5207 break;
5208 default:
5209 g_assert_not_reached ();
5211 ++td->ip;
5212 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5213 break;
5214 #if SIZEOF_VOID_P == 4
5215 case CEE_CONV_OVF_U:
5216 #endif
5217 case CEE_CONV_OVF_U4:
5218 case CEE_CONV_OVF_U4_UN:
5219 CHECK_STACK (td, 1);
5220 switch (td->sp [-1].type) {
5221 case STACK_TYPE_R4:
5222 interp_add_ins (td, MINT_CONV_OVF_U4_R4);
5223 break;
5224 case STACK_TYPE_R8:
5225 interp_add_ins (td, MINT_CONV_OVF_U4_R8);
5226 break;
5227 case STACK_TYPE_I4:
5228 if (*td->ip != CEE_CONV_OVF_U4_UN)
5229 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
5230 break;
5231 case STACK_TYPE_I8:
5232 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
5233 break;
5234 case STACK_TYPE_MP:
5235 interp_add_ins (td, MINT_CONV_OVF_U4_P);
5236 break;
5237 default:
5238 g_assert_not_reached ();
5240 ++td->ip;
5241 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5242 break;
5243 #if SIZEOF_VOID_P == 8
5244 case CEE_CONV_OVF_I:
5245 #endif
5246 case CEE_CONV_OVF_I8:
5247 CHECK_STACK (td, 1);
5248 switch (td->sp [-1].type) {
5249 case STACK_TYPE_R4:
5250 interp_add_ins (td, MINT_CONV_OVF_I8_R4);
5251 break;
5252 case STACK_TYPE_R8:
5253 interp_add_ins (td, MINT_CONV_OVF_I8_R8);
5254 break;
5255 case STACK_TYPE_I4:
5256 interp_add_ins (td, MINT_CONV_I8_I4);
5257 break;
5258 case STACK_TYPE_I8:
5259 break;
5260 default:
5261 g_assert_not_reached ();
5263 ++td->ip;
5264 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5265 break;
5266 #if SIZEOF_VOID_P == 8
5267 case CEE_CONV_OVF_U:
5268 #endif
5269 case CEE_CONV_OVF_U8:
5270 CHECK_STACK (td, 1);
5271 switch (td->sp [-1].type) {
5272 case STACK_TYPE_R4:
5273 interp_add_ins (td, MINT_CONV_OVF_U8_R4);
5274 break;
5275 case STACK_TYPE_R8:
5276 interp_add_ins (td, MINT_CONV_OVF_U8_R8);
5277 break;
5278 case STACK_TYPE_I4:
5279 interp_add_ins (td, MINT_CONV_OVF_U8_I4);
5280 break;
5281 case STACK_TYPE_I8:
5282 interp_add_ins (td, MINT_CONV_OVF_U8_I8);
5283 break;
5284 default:
5285 g_assert_not_reached ();
5287 ++td->ip;
5288 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5289 break;
5290 case CEE_LDTOKEN: {
5291 int size;
5292 gpointer handle;
5293 token = read32 (td->ip + 1);
5294 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
5295 handle = mono_method_get_wrapper_data (method, token);
5296 klass = (MonoClass *) mono_method_get_wrapper_data (method, token + 1);
5297 if (klass == mono_defaults.typehandle_class)
5298 handle = m_class_get_byval_arg ((MonoClass *) handle);
5300 if (generic_context) {
5301 handle = mono_class_inflate_generic_type_checked ((MonoType*)handle, generic_context, error);
5302 goto_if_nok (error, exit);
5304 } else {
5305 handle = mono_ldtoken_checked (image, token, &klass, generic_context, error);
5306 goto_if_nok (error, exit);
5308 mono_class_init_internal (klass);
5309 mt = mint_type (m_class_get_byval_arg (klass));
5310 g_assert (mt == MINT_TYPE_VT);
5311 size = mono_class_value_size (klass, NULL);
5312 g_assert (size == sizeof(gpointer));
5314 const unsigned char *next_ip = td->ip + 5;
5315 MonoMethod *cmethod;
5316 if (next_ip < end &&
5317 !td->is_bb_start [next_ip - td->il_code] &&
5318 (*next_ip == CEE_CALL || *next_ip == CEE_CALLVIRT) &&
5319 (cmethod = mono_get_method_checked (image, read32 (next_ip + 1), NULL, generic_context, error)) &&
5320 (cmethod->klass == mono_defaults.systemtype_class) &&
5321 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
5322 interp_add_ins (td, MINT_MONO_LDPTR);
5323 gpointer systype = mono_type_get_object_checked (domain, (MonoType*)handle, error);
5324 goto_if_nok (error, exit);
5325 td->last_ins->data [0] = get_data_item_index (td, systype);
5326 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5327 td->ip = next_ip + 5;
5328 } else {
5329 PUSH_VT (td, sizeof(gpointer));
5330 interp_add_ins (td, MINT_LDTOKEN);
5331 td->last_ins->data [0] = get_data_item_index (td, handle);
5332 PUSH_TYPE (td, stack_type [mt], klass);
5333 td->ip += 5;
5336 break;
5338 case CEE_ADD_OVF:
5339 binary_arith_op(td, MINT_ADD_OVF_I4);
5340 ++td->ip;
5341 break;
5342 case CEE_ADD_OVF_UN:
5343 binary_arith_op(td, MINT_ADD_OVF_UN_I4);
5344 ++td->ip;
5345 break;
5346 case CEE_MUL_OVF:
5347 binary_arith_op(td, MINT_MUL_OVF_I4);
5348 ++td->ip;
5349 break;
5350 case CEE_MUL_OVF_UN:
5351 binary_arith_op(td, MINT_MUL_OVF_UN_I4);
5352 ++td->ip;
5353 break;
5354 case CEE_SUB_OVF:
5355 binary_arith_op(td, MINT_SUB_OVF_I4);
5356 ++td->ip;
5357 break;
5358 case CEE_SUB_OVF_UN:
5359 binary_arith_op(td, MINT_SUB_OVF_UN_I4);
5360 ++td->ip;
5361 break;
5362 case CEE_ENDFINALLY: {
5363 g_assert (td->clause_indexes [in_offset] != -1);
5364 td->sp = td->stack;
5365 SIMPLE_OP (td, MINT_ENDFINALLY);
5366 td->last_ins->data [0] = td->clause_indexes [in_offset];
5367 // next instructions are always part of new bb
5368 td->is_bb_start [td->ip - header->code] = 1;
5369 break;
5371 case CEE_LEAVE:
5372 case CEE_LEAVE_S: {
5373 int offset;
5375 if (*td->ip == CEE_LEAVE)
5376 offset = 5 + read32 (td->ip + 1);
5377 else
5378 offset = 2 + (gint8)td->ip [1];
5380 td->sp = td->stack;
5381 if (td->clause_indexes [in_offset] != -1) {
5382 /* LEAVE instructions in catch clauses need to check for abort exceptions */
5383 handle_branch (td, MINT_LEAVE_S_CHECK, MINT_LEAVE_CHECK, offset);
5384 } else {
5385 handle_branch (td, MINT_LEAVE_S, MINT_LEAVE, offset);
5388 if (*td->ip == CEE_LEAVE)
5389 td->ip += 5;
5390 else
5391 td->ip += 2;
5392 break;
5394 case MONO_CUSTOM_PREFIX:
5395 ++td->ip;
5396 switch (*td->ip) {
5397 case CEE_MONO_RETHROW:
5398 CHECK_STACK (td, 1);
5399 SIMPLE_OP (td, MINT_MONO_RETHROW);
5400 td->sp = td->stack;
5401 break;
5403 case CEE_MONO_LD_DELEGATE_METHOD_PTR:
5404 --td->sp;
5405 td->ip += 1;
5406 interp_add_ins (td, MINT_LD_DELEGATE_METHOD_PTR);
5407 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5408 break;
5409 case CEE_MONO_CALLI_EXTRA_ARG:
5410 /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
5411 interp_add_ins (td, MINT_POP);
5412 td->last_ins->data [0] = 1;
5413 --td->sp;
5414 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
5415 goto exit;
5416 break;
5417 case CEE_MONO_JIT_ICALL_ADDR: {
5418 const guint32 token = read32 (td->ip + 1);
5419 td->ip += 5;
5420 const gconstpointer func = mono_find_jit_icall_info ((MonoJitICallId)token)->func;
5422 interp_add_ins (td, MINT_LDFTN);
5423 td->last_ins->data [0] = get_data_item_index (td, (gpointer)func);
5424 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5425 break;
5427 case CEE_MONO_ICALL: {
5428 MonoJitICallId const jit_icall_id = (MonoJitICallId)read32 (td->ip + 1);
5429 MonoJitICallInfo const * const info = mono_find_jit_icall_info (jit_icall_id);
5430 td->ip += 5;
5432 CHECK_STACK (td, info->sig->param_count);
5433 if (jit_icall_id == MONO_JIT_ICALL_mono_threads_attach_coop) {
5434 rtm->needs_thread_attach = 1;
5436 /* attach needs two arguments, and has one return value: leave one element on the stack */
5437 interp_add_ins (td, MINT_POP);
5438 td->last_ins->data [0] = 0;
5439 } else if (jit_icall_id == MONO_JIT_ICALL_mono_threads_detach_coop) {
5440 g_assert (rtm->needs_thread_attach);
5442 /* detach consumes two arguments, and no return value: drop both of them */
5443 interp_add_ins (td, MINT_POP);
5444 td->last_ins->data [0] = 0;
5445 interp_add_ins (td, MINT_POP);
5446 td->last_ins->data [0] = 0;
5447 } else {
5448 int const icall_op = interp_icall_op_for_sig (info->sig);
5449 g_assert (icall_op != -1);
5451 interp_add_ins (td, icall_op);
5452 // hash here is overkill
5453 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
5455 td->sp -= info->sig->param_count;
5457 if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
5458 int mt = mint_type (info->sig->ret);
5459 PUSH_SIMPLE_TYPE(td, stack_type [mt]);
5461 break;
5463 case CEE_MONO_VTADDR: {
5464 int size;
5465 CHECK_STACK (td, 1);
5466 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
5467 size = mono_class_native_size(td->sp [-1].klass, NULL);
5468 else
5469 size = mono_class_value_size(td->sp [-1].klass, NULL);
5470 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
5471 interp_add_ins (td, MINT_VTRESULT);
5472 td->last_ins->data [0] = 0;
5473 WRITE32_INS (td->last_ins, 1, &size);
5474 td->vt_sp -= size;
5475 ++td->ip;
5476 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5477 break;
5479 case CEE_MONO_LDPTR:
5480 case CEE_MONO_CLASSCONST:
5481 token = read32 (td->ip + 1);
5482 td->ip += 5;
5483 interp_add_ins (td, MINT_MONO_LDPTR);
5484 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5485 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5486 break;
5487 case CEE_MONO_OBJADDR:
5488 CHECK_STACK (td, 1);
5489 ++td->ip;
5490 td->sp[-1].type = STACK_TYPE_MP;
5491 /* do nothing? */
5492 break;
5493 case CEE_MONO_NEWOBJ:
5494 token = read32 (td->ip + 1);
5495 td->ip += 5;
5496 interp_add_ins (td, MINT_MONO_NEWOBJ);
5497 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5498 PUSH_SIMPLE_TYPE (td, STACK_TYPE_O);
5499 break;
5500 case CEE_MONO_RETOBJ:
5501 CHECK_STACK (td, 1);
5502 token = read32 (td->ip + 1);
5503 td->ip += 5;
5504 interp_add_ins (td, MINT_MONO_RETOBJ);
5505 td->sp--;
5507 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5509 /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/
5511 if (td->sp > td->stack)
5512 g_warning ("CEE_MONO_RETOBJ: more values on stack: %d", td->sp-td->stack);
5513 break;
5514 case CEE_MONO_LDNATIVEOBJ:
5515 token = read32 (td->ip + 1);
5516 td->ip += 5;
5517 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5518 g_assert(m_class_is_valuetype (klass));
5519 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5520 break;
5521 case CEE_MONO_TLS: {
5522 gint32 key = read32 (td->ip + 1);
5523 td->ip += 5;
5524 g_assertf (key == TLS_KEY_SGEN_THREAD_INFO, "%d", key);
5525 interp_add_ins (td, MINT_MONO_SGEN_THREAD_INFO);
5526 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5527 break;
5529 case CEE_MONO_ATOMIC_STORE_I4:
5530 CHECK_STACK (td, 2);
5531 SIMPLE_OP (td, MINT_MONO_ATOMIC_STORE_I4);
5532 td->sp -= 2;
5533 td->ip++;
5534 break;
5535 case CEE_MONO_SAVE_LMF:
5536 case CEE_MONO_RESTORE_LMF:
5537 case CEE_MONO_NOT_TAKEN:
5538 ++td->ip;
5539 break;
5540 case CEE_MONO_LDPTR_INT_REQ_FLAG:
5541 interp_add_ins (td, MINT_MONO_LDPTR);
5542 td->last_ins->data [0] = get_data_item_index (td, mono_thread_interruption_request_flag ());
5543 PUSH_TYPE (td, STACK_TYPE_MP, NULL);
5544 ++td->ip;
5545 break;
5546 case CEE_MONO_MEMORY_BARRIER:
5547 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5548 ++td->ip;
5549 break;
5550 case CEE_MONO_LDDOMAIN:
5551 interp_add_ins (td, MINT_MONO_LDDOMAIN);
5552 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5553 ++td->ip;
5554 break;
5555 case CEE_MONO_SAVE_LAST_ERROR:
5556 save_last_error = TRUE;
5557 ++td->ip;
5558 break;
5559 default:
5560 g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td->ip, td->ip-header->code);
5562 break;
5563 #if 0
5564 case CEE_PREFIX7:
5565 case CEE_PREFIX6:
5566 case CEE_PREFIX5:
5567 case CEE_PREFIX4:
5568 case CEE_PREFIX3:
5569 case CEE_PREFIX2:
5570 case CEE_PREFIXREF: ves_abort(); break;
5571 #endif
5573 * Note: Exceptions thrown when executing a prefixed opcode need
5574 * to take into account the number of prefix bytes (usually the
5575 * throw point is just (ip - n_prefix_bytes).
5577 case CEE_PREFIX1:
5578 ++td->ip;
5579 switch (*td->ip) {
5580 case CEE_ARGLIST:
5581 interp_add_ins (td, MINT_ARGLIST);
5582 PUSH_VT (td, SIZEOF_VOID_P);
5583 PUSH_SIMPLE_TYPE (td, STACK_TYPE_VT);
5584 ++td->ip;
5585 break;
5586 case CEE_CEQ:
5587 CHECK_STACK(td, 2);
5588 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP) {
5589 interp_add_ins (td, MINT_CEQ_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5590 } else {
5591 if (td->sp [-1].type == STACK_TYPE_R4 && td->sp [-2].type == STACK_TYPE_R8)
5592 interp_add_ins (td, MINT_CONV_R8_R4);
5593 if (td->sp [-1].type == STACK_TYPE_R8 && td->sp [-2].type == STACK_TYPE_R4)
5594 interp_add_ins (td, MINT_CONV_R8_R4_SP);
5595 interp_add_ins (td, MINT_CEQ_I4 + td->sp [-1].type - STACK_TYPE_I4);
5597 --td->sp;
5598 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5599 ++td->ip;
5600 break;
5601 case CEE_CGT:
5602 CHECK_STACK(td, 2);
5603 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5604 interp_add_ins (td, MINT_CGT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5605 else
5606 interp_add_ins (td, MINT_CGT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5607 --td->sp;
5608 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5609 ++td->ip;
5610 break;
5611 case CEE_CGT_UN:
5612 CHECK_STACK(td, 2);
5613 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5614 interp_add_ins (td, MINT_CGT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5615 else
5616 interp_add_ins (td, MINT_CGT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5617 --td->sp;
5618 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5619 ++td->ip;
5620 break;
5621 case CEE_CLT:
5622 CHECK_STACK(td, 2);
5623 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5624 interp_add_ins (td, MINT_CLT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5625 else
5626 interp_add_ins (td, MINT_CLT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5627 --td->sp;
5628 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5629 ++td->ip;
5630 break;
5631 case CEE_CLT_UN:
5632 CHECK_STACK(td, 2);
5633 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5634 interp_add_ins (td, MINT_CLT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5635 else
5636 interp_add_ins (td, MINT_CLT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5637 --td->sp;
5638 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5639 ++td->ip;
5640 break;
5641 case CEE_LDVIRTFTN: /* fallthrough */
5642 case CEE_LDFTN: {
5643 MonoMethod *m;
5644 if (*td->ip == CEE_LDVIRTFTN) {
5645 CHECK_STACK (td, 1);
5646 --td->sp;
5648 token = read32 (td->ip + 1);
5649 if (method->wrapper_type != MONO_WRAPPER_NONE)
5650 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
5651 else {
5652 m = mono_get_method_checked (image, token, NULL, generic_context, error);
5653 goto_if_nok (error, exit);
5656 if (!mono_method_can_access_method (method, m))
5657 interp_generate_mae_throw (td, method, m);
5659 if (method->wrapper_type == MONO_WRAPPER_NONE && m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
5660 m = mono_marshal_get_synchronized_wrapper (m);
5662 interp_add_ins (td, *td->ip == CEE_LDFTN ? MINT_LDFTN : MINT_LDVIRTFTN);
5663 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
5664 goto_if_nok (error, exit);
5665 td->ip += 5;
5666 PUSH_SIMPLE_TYPE (td, STACK_TYPE_F);
5667 break;
5669 case CEE_LDARG: {
5670 int arg_n = read16 (td->ip + 1);
5671 if (!inlining)
5672 load_arg (td, arg_n);
5673 else
5674 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
5675 td->ip += 3;
5676 break;
5678 case CEE_LDARGA: {
5679 int n = read16 (td->ip + 1);
5681 if (!inlining) {
5682 get_arg_type_exact (td, n, &mt);
5683 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
5684 td->last_ins->data [0] = n;
5685 } else {
5686 interp_add_ins (td, MINT_LDLOCA_S);
5687 td->last_ins->data [0] = arg_offsets [n];
5689 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5690 td->ip += 3;
5691 break;
5693 case CEE_STARG: {
5694 int arg_n = read16 (td->ip + 1);
5695 if (!inlining)
5696 store_arg (td, arg_n);
5697 else
5698 store_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
5699 td->ip += 3;
5700 break;
5702 case CEE_LDLOC: {
5703 int loc_n = read16 (td->ip + 1);
5704 if (!inlining)
5705 load_local (td, loc_n);
5706 else
5707 load_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
5708 td->ip += 3;
5709 break;
5711 case CEE_LDLOCA: {
5712 int loc_n = read16 (td->ip + 1);
5713 interp_add_ins (td, MINT_LDLOCA_S);
5714 if (!inlining)
5715 td->last_ins->data [0] = td->rtm->local_offsets [loc_n];
5716 else
5717 td->last_ins->data [0] = local_offsets [loc_n];
5718 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5719 td->ip += 3;
5720 break;
5722 case CEE_STLOC: {
5723 int loc_n = read16 (td->ip + 1);
5724 if (!inlining)
5725 store_local (td, loc_n);
5726 else
5727 store_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
5728 td->ip += 3;
5729 break;
5731 case CEE_LOCALLOC:
5732 INLINE_FAILURE;
5733 CHECK_STACK (td, 1);
5734 #if SIZEOF_VOID_P == 8
5735 if (td->sp [-1].type == STACK_TYPE_I8)
5736 interp_add_ins (td, MINT_CONV_I4_I8);
5737 #endif
5738 interp_add_ins (td, MINT_LOCALLOC);
5739 if (td->sp != td->stack + 1)
5740 g_warning("CEE_LOCALLOC: stack not empty");
5741 ++td->ip;
5742 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5743 break;
5744 #if 0
5745 case CEE_UNUSED57: ves_abort(); break;
5746 #endif
5747 case CEE_ENDFILTER:
5748 interp_add_ins (td, MINT_ENDFILTER);
5749 ++td->ip;
5750 break;
5751 case CEE_UNALIGNED_:
5752 td->ip += 2;
5753 break;
5754 case CEE_VOLATILE_:
5755 ++td->ip;
5756 volatile_ = TRUE;
5757 break;
5758 case CEE_TAIL_:
5759 ++td->ip;
5760 /* FIX: should do something? */;
5761 // TODO: This should raise a method_tail_call profiler event.
5762 break;
5763 case CEE_INITOBJ:
5764 CHECK_STACK(td, 1);
5765 token = read32 (td->ip + 1);
5766 klass = mini_get_class (method, token, generic_context);
5767 CHECK_TYPELOAD (klass);
5768 if (m_class_is_valuetype (klass)) {
5769 interp_add_ins (td, MINT_INITOBJ);
5770 i32 = mono_class_value_size (klass, NULL);
5771 WRITE32_INS (td->last_ins, 0, &i32);
5772 --td->sp;
5773 } else {
5774 interp_add_ins (td, MINT_LDNULL);
5775 PUSH_TYPE(td, STACK_TYPE_O, NULL);
5776 interp_add_ins (td, MINT_STIND_REF);
5777 td->sp -= 2;
5779 td->ip += 5;
5780 break;
5781 case CEE_CPBLK:
5782 CHECK_STACK(td, 3);
5783 /* FIX? convert length to I8? */
5784 if (volatile_)
5785 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5786 interp_add_ins (td, MINT_CPBLK);
5787 BARRIER_IF_VOLATILE (td);
5788 td->sp -= 3;
5789 ++td->ip;
5790 break;
5791 case CEE_READONLY_:
5792 readonly = TRUE;
5793 td->ip += 1;
5794 break;
5795 case CEE_CONSTRAINED_:
5796 token = read32 (td->ip + 1);
5797 constrained_class = mini_get_class (method, token, generic_context);
5798 CHECK_TYPELOAD (constrained_class);
5799 td->ip += 5;
5800 break;
5801 case CEE_INITBLK:
5802 CHECK_STACK(td, 3);
5803 BARRIER_IF_VOLATILE (td);
5804 interp_add_ins (td, MINT_INITBLK);
5805 td->sp -= 3;
5806 td->ip += 1;
5807 break;
5808 case CEE_NO_:
5809 /* FIXME: implement */
5810 td->ip += 2;
5811 break;
5812 case CEE_RETHROW: {
5813 int clause_index = td->clause_indexes [in_offset];
5814 g_assert (clause_index != -1);
5815 SIMPLE_OP (td, MINT_RETHROW);
5816 td->last_ins->data [0] = rtm->exvar_offsets [clause_index];
5817 td->sp = td->stack;
5818 break;
5820 case CEE_SIZEOF: {
5821 gint32 size;
5822 token = read32 (td->ip + 1);
5823 td->ip += 5;
5824 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (m_class_get_image (method->klass)) && !generic_context) {
5825 int align;
5826 MonoType *type = mono_type_create_from_typespec_checked (image, token, error);
5827 goto_if_nok (error, exit);
5828 size = mono_type_size (type, &align);
5829 } else {
5830 int align;
5831 MonoClass *szclass = mini_get_class (method, token, generic_context);
5832 CHECK_TYPELOAD (szclass);
5833 #if 0
5834 if (!szclass->valuetype)
5835 THROW_EX (mono_exception_from_name (mono_defaults.corlib, "System", "InvalidProgramException"), ip - 5);
5836 #endif
5837 size = mono_type_size (m_class_get_byval_arg (szclass), &align);
5839 interp_add_ins (td, MINT_LDC_I4);
5840 WRITE32_INS (td->last_ins, 0, &size);
5841 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
5842 break;
5844 case CEE_REFANYTYPE:
5845 interp_add_ins (td, MINT_REFANYTYPE);
5846 td->ip += 1;
5847 POP_VT (td, sizeof (MonoTypedRef));
5848 PUSH_VT (td, sizeof (gpointer));
5849 SET_TYPE(td->sp - 1, STACK_TYPE_VT, NULL);
5850 break;
5851 default:
5852 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);
5854 break;
5855 default:
5856 g_error ("transform.c: Unimplemented opcode: %02x at 0x%x\n", *td->ip, td->ip-header->code);
5859 // No IR instructions were added as part of a bb_start IL instruction. Add a MINT_NOP
5860 // so we always have an instruction associated with a bb_start. This is simple and avoids
5861 // any complications associated with il_offset tracking.
5862 if (prev_last_ins == td->last_ins && (!inlining && td->is_bb_start [in_offset]) && td->ip < end)
5863 interp_add_ins (td, MINT_NOP);
5866 g_assert (td->ip == end);
5868 exit_ret:
5869 g_free (arg_offsets);
5870 g_free (local_offsets);
5871 mono_basic_block_free (original_bb);
5873 return ret;
5874 exit:
5875 ret = FALSE;
5876 goto exit_ret;
5879 // Find the offset of the first interp instruction generated starting il_offset
5880 // This is needed to find the end of clauses.
5881 static int
5882 find_in_offset (TransformData *td, int il_offset)
5884 int i = il_offset;
5885 while (!td->in_offsets [i])
5886 i++;
5887 return td->in_offsets [i] - 1;
5890 // We store in the in_offset array the native_offset + 1, so 0 can mean only that the il
5891 // offset is uninitialized. Otherwise 0 is valid value for first interp instruction.
5892 static int
5893 get_in_offset (TransformData *td, int il_offset)
5895 int target_offset = td->in_offsets [il_offset];
5896 g_assert (target_offset);
5897 return target_offset - 1;
5900 static void
5901 handle_relocations (TransformData *td)
5903 // Handle relocations
5904 for (int i = 0; i < td->relocs->len; ++i) {
5905 Reloc *reloc = (Reloc*)g_ptr_array_index (td->relocs, i);
5906 int offset = get_in_offset (td, reloc->target) - reloc->offset;
5908 switch (reloc->type) {
5909 case RELOC_SHORT_BRANCH:
5910 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
5911 td->new_code [reloc->offset + 1] = offset;
5912 break;
5913 case RELOC_LONG_BRANCH: {
5914 guint16 *v = (guint16 *) &offset;
5915 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
5916 g_assert (td->new_code [reloc->offset + 2] == 0xbeef);
5917 td->new_code [reloc->offset + 1] = *(guint16 *) v;
5918 td->new_code [reloc->offset + 2] = *(guint16 *) (v + 1);
5919 break;
5921 case RELOC_SWITCH: {
5922 guint16 *v = (guint16*)&offset;
5923 g_assert (td->new_code [reloc->offset] == 0xdead);
5924 g_assert (td->new_code [reloc->offset + 1] == 0xbeef);
5925 td->new_code [reloc->offset] = *(guint16*)v;
5926 td->new_code [reloc->offset + 1] = *(guint16*)(v + 1);
5927 break;
5929 default:
5930 g_assert_not_reached ();
5931 break;
5937 static int
5938 get_inst_length (InterpInst *ins)
5940 if (ins->opcode == MINT_SWITCH)
5941 return MINT_SWITCH_LEN (READ32 (&ins->data [0]));
5942 else
5943 return mono_interp_oplen [ins->opcode];
5946 static guint16*
5947 emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *ins)
5949 guint16 opcode = ins->opcode;
5950 guint16 *ip = start_ip;
5952 // We know what IL offset this instruction was created for. We can now map the IL offset
5953 // to the IR offset. We use this array to resolve the relocations, which reference the IL.
5954 if (ins->il_offset != -1 && !td->in_offsets [ins->il_offset]) {
5955 g_assert (ins->il_offset >= 0 && ins->il_offset < td->header->code_size);
5956 td->in_offsets [ins->il_offset] = start_ip - td->new_code + 1;
5958 MonoDebugLineNumberEntry lne;
5959 lne.native_offset = (guint8*)start_ip - (guint8*)td->new_code;
5960 lne.il_offset = ins->il_offset;
5961 g_array_append_val (td->line_numbers, lne);
5964 if (opcode == MINT_NOP)
5965 return ip;
5967 *ip++ = opcode;
5968 if (opcode == MINT_SWITCH) {
5969 int labels = READ32 (&ins->data [0]);
5970 // Write number of switch labels
5971 *ip++ = ins->data [0];
5972 *ip++ = ins->data [1];
5973 // Add relocation for each label
5974 for (int i = 0; i < labels; i++) {
5975 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5976 reloc->type = RELOC_SWITCH;
5977 reloc->offset = ip - td->new_code;
5978 reloc->target = READ32 (&ins->data [2 + i * 2]);
5979 g_ptr_array_add (td->relocs, reloc);
5980 *ip++ = 0xdead;
5981 *ip++ = 0xbeef;
5983 } else if ((opcode >= MINT_BRFALSE_I4_S && opcode <= MINT_BRTRUE_R8_S) ||
5984 (opcode >= MINT_BEQ_I4_S && opcode <= MINT_BLT_UN_R8_S) ||
5985 opcode == MINT_BR_S || opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK) {
5986 const int br_offset = start_ip - td->new_code;
5987 if (ins->data [0] < ins->il_offset) {
5988 // Backwards branch. We can already patch it.
5989 *ip++ = get_in_offset (td, ins->data [0]) - br_offset;
5990 } else {
5991 // We don't know the in_offset of the target, add a reloc
5992 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5993 reloc->type = RELOC_SHORT_BRANCH;
5994 reloc->offset = br_offset;
5995 reloc->target = ins->data [0];
5996 g_ptr_array_add (td->relocs, reloc);
5997 *ip++ = 0xdead;
5999 } else if ((opcode >= MINT_BRFALSE_I4 && opcode <= MINT_BRTRUE_R8) ||
6000 (opcode >= MINT_BEQ_I4 && opcode <= MINT_BLT_UN_R8) ||
6001 opcode == MINT_BR || opcode == MINT_LEAVE || opcode == MINT_LEAVE_CHECK) {
6002 const int br_offset = start_ip - td->new_code;
6003 int target_il = READ32 (&ins->data [0]);
6004 if (target_il < ins->il_offset) {
6005 // Backwards branch. We can already patch it
6006 const int br_offset = start_ip - td->new_code;
6007 int target_offset = get_in_offset (td, target_il) - br_offset;
6008 WRITE32 (ip, &target_offset);
6009 } else {
6010 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
6011 reloc->type = RELOC_LONG_BRANCH;
6012 reloc->offset = br_offset;
6013 reloc->target = target_il;
6014 g_ptr_array_add (td->relocs, reloc);
6015 *ip++ = 0xdead;
6016 *ip++ = 0xbeef;
6018 } else if (opcode == MINT_SDB_SEQ_POINT) {
6019 SeqPoint *seqp = (SeqPoint*)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint));
6020 InterpBasicBlock *cbb;
6022 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY) {
6023 seqp->il_offset = METHOD_ENTRY_IL_OFFSET;
6024 cbb = td->offset_to_bb [0];
6025 } else {
6026 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT)
6027 seqp->il_offset = METHOD_EXIT_IL_OFFSET;
6028 else
6029 seqp->il_offset = ins->il_offset;
6030 cbb = td->offset_to_bb [ins->il_offset];
6032 seqp->native_offset = (guint8*)start_ip - (guint8*)td->new_code;
6033 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK)
6034 seqp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
6035 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL)
6036 seqp->flags |= MONO_SEQ_POINT_FLAG_NESTED_CALL;
6037 g_ptr_array_add (td->seq_points, seqp);
6039 cbb->seq_points = g_slist_prepend_mempool (td->mempool, cbb->seq_points, seqp);
6040 cbb->last_seq_point = seqp;
6041 } else {
6042 int size = get_inst_length (ins) - 1;
6043 // Emit the rest of the data
6044 for (int i = 0; i < size; i++)
6045 *ip++ = ins->data [i];
6047 return ip;
6050 // Generates the final code, after we are done with all the passes
6051 static void
6052 generate_compacted_code (TransformData *td)
6054 guint16 *ip;
6055 int size = 0;
6056 td->relocs = g_ptr_array_new ();
6058 // Iterate once to compute the exact size of the compacted code
6059 InterpInst *ins = td->first_ins;
6060 while (ins) {
6061 size += get_inst_length (ins);
6062 ins = ins->next;
6065 // Generate the compacted stream of instructions
6066 td->new_code = ip = (guint16*)mono_domain_alloc0 (td->rtm->domain, size * sizeof (guint16));
6067 ins = td->first_ins;
6068 while (ins) {
6069 ip = emit_compacted_instruction (td, ip, ins);
6070 ins = ins->next;
6072 td->new_code_end = ip;
6073 td->in_offsets [td->header->code_size] = td->new_code_end - td->new_code;
6075 // Patch all branches
6076 handle_relocations (td);
6078 g_ptr_array_free (td->relocs, TRUE);
6081 static void
6082 generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoGenericContext *generic_context, MonoError *error)
6084 MonoDomain *domain = rtm->domain;
6085 int i;
6086 TransformData transform_data;
6087 TransformData *td;
6088 static gboolean verbose_method_inited;
6089 static char* verbose_method_name;
6091 if (!verbose_method_inited) {
6092 verbose_method_name = g_getenv ("MONO_VERBOSE_METHOD");
6093 verbose_method_inited = TRUE;
6096 memset (&transform_data, 0, sizeof(transform_data));
6097 td = &transform_data;
6099 td->method = method;
6100 td->rtm = rtm;
6101 td->code_size = header->code_size;
6102 td->header = header;
6103 td->max_code_size = td->code_size;
6104 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
6105 td->stack_height = (int*)g_malloc(header->code_size * sizeof(int));
6106 td->stack_state = (StackInfo**)g_malloc0(header->code_size * sizeof(StackInfo *));
6107 td->vt_stack_size = (int*)g_malloc(header->code_size * sizeof(int));
6108 td->clause_indexes = (int*)g_malloc (header->code_size * sizeof (int));
6109 td->is_bb_start = (guint8*)g_malloc0(header->code_size);
6110 td->mempool = mono_mempool_new ();
6111 td->n_data_items = 0;
6112 td->max_data_items = 0;
6113 td->data_items = NULL;
6114 td->data_hash = g_hash_table_new (NULL, NULL);
6115 td->gen_sdb_seq_points = mini_debug_options.gen_sdb_seq_points;
6116 td->seq_points = g_ptr_array_new ();
6117 td->verbose_level = mono_interp_traceopt;
6118 td->total_locals_size = rtm->locals_size;
6119 rtm->data_items = td->data_items;
6121 if (verbose_method_name) {
6122 const char *name = verbose_method_name;
6124 if ((strchr (name, '.') > name) || strchr (name, ':')) {
6125 MonoMethodDesc *desc;
6127 desc = mono_method_desc_new (name, TRUE);
6128 if (mono_method_desc_full_match (desc, method)) {
6129 td->verbose_level = 4;
6131 mono_method_desc_free (desc);
6132 } else {
6133 if (strcmp (method->name, name) == 0)
6134 td->verbose_level = 4;
6138 td->stack = (StackInfo*)g_malloc0 ((header->max_stack + 1) * sizeof (td->stack [0]));
6139 td->stack_capacity = header->max_stack + 1;
6140 td->sp = td->stack;
6141 td->max_stack_height = 0;
6142 td->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
6143 td->current_il_offset = -1;
6145 generate_code (td, method, header, generic_context, error);
6146 goto_if_nok (error, exit);
6148 generate_compacted_code (td);
6150 if (td->verbose_level) {
6151 g_print ("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method, TRUE), rtm, td->max_vt_sp);
6152 g_print ("Calculated stack size: %d, stated size: %d\n", td->max_stack_height, header->max_stack);
6153 dump_mint_code (td->new_code, td->new_code_end);
6156 /* Check if we use excessive stack space */
6157 if (td->max_stack_height > header->max_stack * 3 && header->max_stack > 16)
6158 g_warning ("Excessive stack space usage for method %s, %d/%d", method->name, td->max_stack_height, header->max_stack);
6160 int code_len;
6161 code_len = td->new_code_end - td->new_code;
6163 rtm->clauses = (MonoExceptionClause*)mono_domain_alloc0 (domain, header->num_clauses * sizeof (MonoExceptionClause));
6164 memcpy (rtm->clauses, header->clauses, header->num_clauses * sizeof(MonoExceptionClause));
6165 rtm->code = (gushort*)td->new_code;
6166 rtm->init_locals = header->init_locals;
6167 rtm->num_clauses = header->num_clauses;
6168 for (i = 0; i < header->num_clauses; i++) {
6169 MonoExceptionClause *c = rtm->clauses + i;
6170 int end_off = c->try_offset + c->try_len;
6171 c->try_offset = get_in_offset (td, c->try_offset);
6172 c->try_len = find_in_offset (td, end_off) - c->try_offset;
6173 g_assert ((c->try_offset + c->try_len) < code_len);
6174 end_off = c->handler_offset + c->handler_len;
6175 c->handler_offset = get_in_offset (td, c->handler_offset);
6176 c->handler_len = find_in_offset (td, end_off) - c->handler_offset;
6177 g_assert (c->handler_len >= 0 && (c->handler_offset + c->handler_len) <= code_len);
6178 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER)
6179 c->data.filter_offset = get_in_offset (td, c->data.filter_offset);
6181 rtm->stack_size = (sizeof (stackval)) * (td->max_stack_height + 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/
6182 rtm->stack_size = ALIGN_TO (rtm->stack_size, MINT_VT_ALIGNMENT);
6183 rtm->vt_stack_size = td->max_vt_sp;
6184 rtm->total_locals_size = td->total_locals_size;
6185 rtm->alloca_size = rtm->total_locals_size + rtm->vt_stack_size + rtm->stack_size;
6186 rtm->data_items = (gpointer*)mono_domain_alloc0 (domain, td->n_data_items * sizeof (td->data_items [0]));
6187 memcpy (rtm->data_items, td->data_items, td->n_data_items * sizeof (td->data_items [0]));
6189 /* Save debug info */
6190 interp_save_debug_info (rtm, header, td, td->line_numbers);
6192 /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
6193 int jinfo_len;
6194 jinfo_len = mono_jit_info_size ((MonoJitInfoFlags)0, header->num_clauses, 0);
6195 MonoJitInfo *jinfo;
6196 jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len);
6197 jinfo->is_interp = 1;
6198 rtm->jinfo = jinfo;
6199 mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len, (MonoJitInfoFlags)0, header->num_clauses, 0);
6200 for (i = 0; i < jinfo->num_clauses; ++i) {
6201 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
6202 MonoExceptionClause *c = rtm->clauses + i;
6204 ei->flags = c->flags;
6205 ei->try_start = (guint8*)(rtm->code + c->try_offset);
6206 ei->try_end = (guint8*)(rtm->code + c->try_offset + c->try_len);
6207 ei->handler_start = (guint8*)(rtm->code + c->handler_offset);
6208 ei->exvar_offset = rtm->exvar_offsets [i];
6209 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
6210 ei->data.filter = (guint8*)(rtm->code + c->data.filter_offset);
6211 } else if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
6212 ei->data.handler_end = (guint8*)(rtm->code + c->handler_offset + c->handler_len);
6213 } else {
6214 ei->data.catch_class = c->data.catch_class;
6218 save_seq_points (td, jinfo);
6220 exit:
6221 g_free (td->in_offsets);
6222 g_free (td->stack_height);
6223 for (i = 0; i < header->code_size; ++i)
6224 g_free (td->stack_state [i]);
6225 g_free (td->stack_state);
6226 g_free (td->vt_stack_size);
6227 g_free (td->clause_indexes);
6228 g_free (td->data_items);
6229 g_free (td->stack);
6230 g_free (td->is_bb_start);
6231 g_hash_table_destroy (td->data_hash);
6232 g_ptr_array_free (td->seq_points, TRUE);
6233 g_array_free (td->line_numbers, TRUE);
6234 mono_mempool_destroy (td->mempool);
6237 static mono_mutex_t calc_section;
6239 void
6240 mono_interp_transform_init (void)
6242 mono_os_mutex_init_recursive(&calc_section);
6245 void
6246 mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, MonoError *error)
6248 MonoMethod *method = imethod->method;
6249 MonoMethodHeader *header = NULL;
6250 MonoMethodSignature *signature = mono_method_signature_internal (method);
6251 MonoVTable *method_class_vt;
6252 MonoGenericContext *generic_context = NULL;
6253 MonoDomain *domain = imethod->domain;
6254 InterpMethod tmp_imethod;
6255 InterpMethod *real_imethod;
6257 error_init (error);
6259 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method->klass))) {
6260 mono_error_set_invalid_operation (error, "%s", "Could not execute the method because the containing type is not fully instantiated.");
6261 return;
6264 // g_printerr ("TRANSFORM(0x%016lx): begin %s::%s\n", mono_thread_current (), method->klass->name, method->name);
6265 method_class_vt = mono_class_vtable_checked (domain, imethod->method->klass, error);
6266 return_if_nok (error);
6268 if (!method_class_vt->initialized) {
6269 mono_runtime_class_init_full (method_class_vt, error);
6270 return_if_nok (error);
6273 MONO_PROFILER_RAISE (jit_begin, (method));
6275 if (mono_method_signature_internal (method)->is_inflated)
6276 generic_context = mono_method_get_context (method);
6277 else {
6278 MonoGenericContainer *generic_container = mono_method_get_generic_container (method);
6279 if (generic_container)
6280 generic_context = &generic_container->context;
6283 if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
6284 MonoMethod *nm = NULL;
6285 if (imethod->transformed) {
6286 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));
6287 return;
6290 /* assumes all internal calls with an array this are built in... */
6291 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL && (! mono_method_signature_internal (method)->hasthis || m_class_get_rank (method->klass) == 0)) {
6292 nm = mono_marshal_get_native_wrapper (method, FALSE, FALSE);
6293 signature = mono_method_signature_internal (nm);
6294 } else {
6295 const char *name = method->name;
6296 if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) {
6297 if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
6298 MonoJitICallInfo *mi = &mono_get_jit_icall_info ()->ves_icall_mono_delegate_ctor_interp;
6299 nm = mono_marshal_get_icall_wrapper (mi, TRUE);
6300 } else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
6302 * Usually handled during transformation of the caller, but
6303 * when the caller is handled by another execution engine
6304 * (for example fullAOT) we need to handle it here. That's
6305 * known to be wrong in cases where the reference to
6306 * `MonoDelegate` would be needed (FIXME).
6308 nm = mono_marshal_get_delegate_invoke (method, NULL);
6309 } else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
6310 nm = mono_marshal_get_delegate_begin_invoke (method);
6311 } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
6312 nm = mono_marshal_get_delegate_end_invoke (method);
6315 if (nm == NULL)
6316 g_assert_not_reached ();
6318 if (nm == NULL) {
6319 mono_os_mutex_lock (&calc_section);
6320 imethod->stack_size = sizeof (stackval); /* for tracing */
6321 imethod->alloca_size = imethod->stack_size;
6322 mono_memory_barrier ();
6323 imethod->transformed = TRUE;
6324 mono_os_mutex_unlock (&calc_section);
6325 MONO_PROFILER_RAISE (jit_done, (method, NULL));
6326 return;
6328 method = nm;
6329 header = interp_method_get_header (nm, error);
6330 return_if_nok (error);
6333 if (!header) {
6334 header = mono_method_get_header_checked (method, error);
6335 return_if_nok (error);
6338 g_assert ((signature->param_count + signature->hasthis) < 1000);
6339 // g_printerr ("TRANSFORM(0x%016lx): end %s::%s\n", mono_thread_current (), method->klass->name, method->name);
6341 /* Make modifications to a copy of imethod, copy them back inside the lock */
6342 real_imethod = imethod;
6343 memcpy (&tmp_imethod, imethod, sizeof (InterpMethod));
6344 imethod = &tmp_imethod;
6346 interp_method_compute_offsets (imethod, signature, header);
6348 MONO_TIME_TRACK (mono_interp_stats.transform_time, generate (method, header, imethod, generic_context, error));
6350 mono_metadata_free_mh (header);
6352 return_if_nok (error);
6354 /* Copy changes back */
6355 imethod = real_imethod;
6356 mono_os_mutex_lock (&calc_section);
6357 if (!imethod->transformed) {
6358 // Ignore the first two fields which are unchanged. next_jit_code_hash shouldn't
6359 // be modified because it is racy with internal hash table insert.
6360 const int start_offset = 2 * sizeof (gpointer);
6361 memcpy ((char*)imethod + start_offset, (char*)&tmp_imethod + start_offset, sizeof (InterpMethod) - start_offset);
6362 mono_memory_barrier ();
6363 imethod->transformed = TRUE;
6364 mono_atomic_fetch_add_i32 (&mono_jit_stats.methods_with_interp, 1);
6367 mono_os_mutex_unlock (&calc_section);
6369 mono_domain_lock (domain);
6370 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, imethod->method))
6371 g_hash_table_insert (domain_jit_info (domain)->seq_points, imethod->method, imethod->jinfo->seq_points);
6372 mono_domain_unlock (domain);
6374 // FIXME: Add a different callback ?
6375 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));