[interp] Optimize enum Hasflag (#14173)
[mono-project.git] / mono / mini / interp / transform.c
blob6d51591ab78477b574fb9dee991b9c5c5b747a96
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/debug-helpers.h>
14 #include <mono/metadata/exception.h>
15 #include <mono/metadata/exception-internals.h>
16 #include <mono/metadata/mono-endian.h>
17 #include <mono/metadata/marshal.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/seq-points-data.h>
21 #include <mono/metadata/mono-basic-block.h>
22 #include <mono/metadata/abi-details.h>
23 #include <mono/metadata/reflection-internals.h>
24 #include <mono/utils/unlocked.h>
26 #include <mono/mini/mini.h>
27 #include <mono/mini/mini-runtime.h>
29 #include "mintops.h"
30 #include "interp-internals.h"
31 #include "interp.h"
33 #define INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK 1
34 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY 2
35 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT 4
36 #define INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL 8
38 MonoInterpStats mono_interp_stats;
40 #define DEBUG 0
42 typedef struct InterpInst InterpInst;
44 typedef struct
46 MonoClass *klass;
47 unsigned char type;
48 unsigned char flags;
49 } StackInfo;
51 struct InterpInst {
52 guint16 opcode;
53 InterpInst *next, *prev;
54 // If this is -1, this instruction is not logically associated with an IL offset, it is
55 // part of the IL instruction associated with the previous interp instruction.
56 int il_offset;
57 guint32 flags;
58 guint16 data [MONO_ZERO_LEN_ARRAY];
61 typedef struct {
62 guint8 *ip;
63 GSList *preds;
64 GSList *seq_points;
65 SeqPoint *last_seq_point;
67 // This will hold a list of last sequence points of incoming basic blocks
68 SeqPoint **pred_seq_points;
69 guint num_pred_seq_points;
70 } InterpBasicBlock;
72 typedef enum {
73 RELOC_SHORT_BRANCH,
74 RELOC_LONG_BRANCH,
75 RELOC_SWITCH
76 } RelocType;
78 typedef struct {
79 RelocType type;
80 /* In the interpreter IR */
81 int offset;
82 /* In the IL code */
83 int target;
84 } Reloc;
86 typedef struct
88 MonoMethod *method;
89 MonoMethod *inlined_method;
90 MonoMethodHeader *header;
91 InterpMethod *rtm;
92 const unsigned char *il_code;
93 const unsigned char *ip;
94 const unsigned char *in_start;
95 InterpInst *last_ins, *first_ins;
96 int code_size;
97 int *in_offsets;
98 StackInfo **stack_state;
99 int *stack_height;
100 int *vt_stack_size;
101 unsigned char *is_bb_start;
102 unsigned short *new_code;
103 unsigned short *new_code_end;
104 unsigned int max_code_size;
105 StackInfo *stack;
106 StackInfo *sp;
107 unsigned int max_stack_height;
108 unsigned int stack_capacity;
109 unsigned int vt_sp;
110 unsigned int max_vt_sp;
111 unsigned int total_locals_size;
112 int n_data_items;
113 int max_data_items;
114 void **data_items;
115 GHashTable *data_hash;
116 int *clause_indexes;
117 gboolean gen_sdb_seq_points;
118 GPtrArray *seq_points;
119 InterpBasicBlock **offset_to_bb;
120 InterpBasicBlock *entry_bb;
121 MonoMemPool *mempool;
122 GList *basic_blocks;
123 GPtrArray *relocs;
124 gboolean verbose_level;
125 GArray *line_numbers;
126 } TransformData;
128 #define STACK_TYPE_I4 0
129 #define STACK_TYPE_I8 1
130 #define STACK_TYPE_R4 2
131 #define STACK_TYPE_R8 3
132 #define STACK_TYPE_O 4
133 #define STACK_TYPE_VT 5
134 #define STACK_TYPE_MP 6
135 #define STACK_TYPE_F 7
137 static const char *stack_type_string [] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " };
139 #if SIZEOF_VOID_P == 8
140 #define STACK_TYPE_I STACK_TYPE_I8
141 #else
142 #define STACK_TYPE_I STACK_TYPE_I4
143 #endif
145 static int stack_type [] = {
146 STACK_TYPE_I4, /*I1*/
147 STACK_TYPE_I4, /*U1*/
148 STACK_TYPE_I4, /*I2*/
149 STACK_TYPE_I4, /*U2*/
150 STACK_TYPE_I4, /*I4*/
151 STACK_TYPE_I8, /*I8*/
152 STACK_TYPE_R4, /*R4*/
153 STACK_TYPE_R8, /*R8*/
154 STACK_TYPE_O, /*O*/
155 STACK_TYPE_MP, /*P*/
156 STACK_TYPE_VT
159 #if SIZEOF_VOID_P == 8
160 #define MINT_NEG_P MINT_NEG_I8
161 #define MINT_NOT_P MINT_NOT_I8
163 #define MINT_NEG_FP MINT_NEG_R8
165 #define MINT_ADD_P MINT_ADD_I8
166 #define MINT_SUB_P MINT_SUB_I8
167 #define MINT_MUL_P MINT_MUL_I8
168 #define MINT_DIV_P MINT_DIV_I8
169 #define MINT_DIV_UN_P MINT_DIV_UN_I8
170 #define MINT_REM_P MINT_REM_I8
171 #define MINT_REM_UN_P MINT_REM_UN_I8
172 #define MINT_AND_P MINT_AND_I8
173 #define MINT_OR_P MINT_OR_I8
174 #define MINT_XOR_P MINT_XOR_I8
175 #define MINT_SHL_P MINT_SHL_I8
176 #define MINT_SHR_P MINT_SHR_I8
177 #define MINT_SHR_UN_P MINT_SHR_UN_I8
179 #define MINT_CEQ_P MINT_CEQ_I8
180 #define MINT_CNE_P MINT_CNE_I8
181 #define MINT_CLT_P MINT_CLT_I8
182 #define MINT_CLT_UN_P MINT_CLT_UN_I8
183 #define MINT_CGT_P MINT_CGT_I8
184 #define MINT_CGT_UN_P MINT_CGT_UN_I8
185 #define MINT_CLE_P MINT_CLE_I8
186 #define MINT_CLE_UN_P MINT_CLE_UN_I8
187 #define MINT_CGE_P MINT_CGE_I8
188 #define MINT_CGE_UN_P MINT_CGE_UN_I8
190 #define MINT_ADD_FP MINT_ADD_R8
191 #define MINT_SUB_FP MINT_SUB_R8
192 #define MINT_MUL_FP MINT_MUL_R8
193 #define MINT_DIV_FP MINT_DIV_R8
194 #define MINT_REM_FP MINT_REM_R8
196 #define MINT_CNE_FP MINT_CNE_R8
197 #define MINT_CEQ_FP MINT_CEQ_R8
198 #define MINT_CGT_FP MINT_CGT_R8
199 #define MINT_CGE_FP MINT_CGE_R8
200 #define MINT_CLT_FP MINT_CLT_R8
201 #define MINT_CLE_FP MINT_CLE_R8
203 #else
205 #define MINT_NEG_P MINT_NEG_I4
206 #define MINT_NOT_P MINT_NOT_I4
208 #define MINT_NEG_FP MINT_NEG_R4
210 #define MINT_ADD_P MINT_ADD_I4
211 #define MINT_SUB_P MINT_SUB_I4
212 #define MINT_MUL_P MINT_MUL_I4
213 #define MINT_DIV_P MINT_DIV_I4
214 #define MINT_DIV_UN_P MINT_DIV_UN_I4
215 #define MINT_REM_P MINT_REM_I4
216 #define MINT_REM_UN_P MINT_REM_UN_I4
217 #define MINT_AND_P MINT_AND_I4
218 #define MINT_OR_P MINT_OR_I4
219 #define MINT_XOR_P MINT_XOR_I4
220 #define MINT_SHL_P MINT_SHL_I4
221 #define MINT_SHR_P MINT_SHR_I4
222 #define MINT_SHR_UN_P MINT_SHR_UN_I4
224 #define MINT_CEQ_P MINT_CEQ_I4
225 #define MINT_CNE_P MINT_CNE_I4
226 #define MINT_CLT_P MINT_CLT_I4
227 #define MINT_CLT_UN_P MINT_CLT_UN_I4
228 #define MINT_CGT_P MINT_CGT_I4
229 #define MINT_CGT_UN_P MINT_CGT_UN_I4
230 #define MINT_CLE_P MINT_CLE_I4
231 #define MINT_CLE_UN_P MINT_CLE_UN_I4
232 #define MINT_CGE_P MINT_CGE_I4
233 #define MINT_CGE_UN_P MINT_CGE_UN_I4
235 #define MINT_ADD_FP MINT_ADD_R4
236 #define MINT_SUB_FP MINT_SUB_R4
237 #define MINT_MUL_FP MINT_MUL_R4
238 #define MINT_DIV_FP MINT_DIV_R4
239 #define MINT_REM_FP MINT_REM_R4
241 #define MINT_CNE_FP MINT_CNE_R4
242 #define MINT_CEQ_FP MINT_CEQ_R4
243 #define MINT_CGT_FP MINT_CGT_R4
244 #define MINT_CGE_FP MINT_CGE_R4
245 #define MINT_CLT_FP MINT_CLT_R4
246 #define MINT_CLE_FP MINT_CLE_R4
248 #endif
250 typedef struct {
251 const gchar *op_name;
252 guint16 insn [3];
253 } MagicIntrinsic;
255 // static const MagicIntrinsic int_binop[] = {
257 static const MagicIntrinsic int_unnop[] = {
258 { "op_UnaryPlus", {MINT_NOP, MINT_NOP, MINT_NOP}},
259 { "op_UnaryNegation", {MINT_NEG_P, MINT_NEG_P, MINT_NEG_FP}},
260 { "op_OnesComplement", {MINT_NOT_P, MINT_NOT_P, MINT_NIY}}
263 static const MagicIntrinsic int_binop[] = {
264 { "op_Addition", {MINT_ADD_P, MINT_ADD_P, MINT_ADD_FP}},
265 { "op_Subtraction", {MINT_SUB_P, MINT_SUB_P, MINT_SUB_FP}},
266 { "op_Multiply", {MINT_MUL_P, MINT_MUL_P, MINT_MUL_FP}},
267 { "op_Division", {MINT_DIV_P, MINT_DIV_UN_P, MINT_DIV_FP}},
268 { "op_Modulus", {MINT_REM_P, MINT_REM_UN_P, MINT_REM_FP}},
269 { "op_BitwiseAnd", {MINT_AND_P, MINT_AND_P, MINT_NIY}},
270 { "op_BitwiseOr", {MINT_OR_P, MINT_OR_P, MINT_NIY}},
271 { "op_ExclusiveOr", {MINT_XOR_P, MINT_XOR_P, MINT_NIY}},
272 { "op_LeftShift", {MINT_SHL_P, MINT_SHL_P, MINT_NIY}},
273 { "op_RightShift", {MINT_SHR_P, MINT_SHR_UN_P, MINT_NIY}},
276 static const MagicIntrinsic int_cmpop[] = {
277 { "op_Inequality", {MINT_CNE_P, MINT_CNE_P, MINT_CNE_FP}},
278 { "op_Equality", {MINT_CEQ_P, MINT_CEQ_P, MINT_CEQ_FP}},
279 { "op_GreaterThan", {MINT_CGT_P, MINT_CGT_UN_P, MINT_CGT_FP}},
280 { "op_GreaterThanOrEqual", {MINT_CGE_P, MINT_CGE_UN_P, MINT_CGE_FP}},
281 { "op_LessThan", {MINT_CLT_P, MINT_CLT_UN_P, MINT_CLT_FP}},
282 { "op_LessThanOrEqual", {MINT_CLE_P, MINT_CLE_UN_P, MINT_CLE_FP}}
285 static gboolean generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error);
287 static InterpInst*
288 interp_new_ins (TransformData *td, guint16 opcode, int len)
290 InterpInst *new_inst;
291 g_assert (len);
292 // Size of data region of instruction is length of instruction minus 1 (the opcode slot)
293 new_inst = mono_mempool_alloc0 (td->mempool, sizeof (InterpInst) + sizeof (guint16) * (len - 1));
294 new_inst->opcode = opcode;
295 // opcodes from inlined methods don't have il offset associated with them since the offset
296 // stored here is relevant only for the original method
297 if (!td->inlined_method)
298 new_inst->il_offset = td->in_start - td->il_code;
299 else
300 new_inst->il_offset = -1;
301 return new_inst;
304 // This version need to be used with switch opcode, which doesn't have constant length
305 static InterpInst*
306 interp_add_ins_explicit (TransformData *td, guint16 opcode, int len)
308 InterpInst *new_inst = interp_new_ins (td, opcode, len);
309 new_inst->prev = td->last_ins;
310 if (td->last_ins)
311 td->last_ins->next = new_inst;
312 else
313 td->first_ins = new_inst;
314 td->last_ins = new_inst;
315 return new_inst;
318 static InterpInst*
319 interp_add_ins (TransformData *td, guint16 opcode)
321 return interp_add_ins_explicit (td, opcode, mono_interp_oplen [opcode]);
324 // Inserts a new instruction inside the instruction stream
325 static InterpInst*
326 interp_insert_ins (TransformData *td, InterpInst *prev_ins, guint16 opcode)
328 InterpInst *new_inst = interp_new_ins (td, opcode, mono_interp_oplen [opcode]);
329 g_assert (prev_ins && prev_ins->next);
331 new_inst->prev = prev_ins;
332 new_inst->next = prev_ins->next;
333 prev_ins->next = new_inst;
334 new_inst->next->prev = new_inst;
336 return new_inst;
339 static void
340 interp_remove_ins (TransformData *td, InterpInst *ins)
342 if (ins->prev) {
343 ins->prev->next = ins->next;
344 } else {
345 td->first_ins = ins->next;
347 if (ins->next) {
348 ins->next->prev = ins->prev;
349 } else {
350 td->last_ins = ins->prev;
354 #define CHECK_STACK(td, n) \
355 do { \
356 int stack_size = (td)->sp - (td)->stack; \
357 if (stack_size < (n)) \
358 g_warning ("%s.%s: not enough values (%d < %d) on stack at %04x", \
359 m_class_get_name ((td)->method->klass), (td)->method->name, \
360 stack_size, n, (td)->ip - (td)->il_code); \
361 } while (0)
363 #define ENSURE_I4(td, sp_off) \
364 do { \
365 if ((td)->sp [-sp_off].type == STACK_TYPE_I8) \
366 interp_add_ins (td, sp_off == 1 ? MINT_CONV_I4_I8 : MINT_CONV_I4_I8_SP); \
367 } while (0)
369 #define CHECK_TYPELOAD(klass) \
370 do { \
371 if (!(klass) || mono_class_has_failure (klass)) { \
372 mono_error_set_for_class_failure (error, klass); \
373 goto exit; \
375 } while (0)
377 #if NO_UNALIGNED_ACCESS
378 #define WRITE32(ip, v) \
379 do { \
380 * (ip) = * (guint16 *)(v); \
381 * ((ip) + 1) = * ((guint16 *)(v) + 1); \
382 (ip) += 2; \
383 } while (0)
385 #define WRITE32_INS(ins, index, v) \
386 do { \
387 (ins)->data [index] = * (guint16 *)(v); \
388 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
389 } while (0)
391 #define WRITE64_INS(ins, index, v) \
392 do { \
393 (ins)->data [index] = * (guint16 *)(v); \
394 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
395 (ins)->data [index + 2] = * ((guint16 *)(v) + 2); \
396 (ins)->data [index + 3] = * ((guint16 *)(v) + 3); \
397 } while (0)
398 #else
399 #define WRITE32(ip, v) \
400 do { \
401 * (guint32*)(ip) = * (guint32 *)(v); \
402 (ip) += 2; \
403 } while (0)
404 #define WRITE32_INS(ins, index, v) \
405 do { \
406 * (guint32 *)(&(ins)->data [index]) = * (guint32 *)(v); \
407 } while (0)
409 #define WRITE64_INS(ins, index, v) \
410 do { \
411 * (guint64 *)(&(ins)->data [index]) = * (guint64 *)(v); \
412 } while (0)
414 #endif
417 static void
418 handle_branch (TransformData *td, int short_op, int long_op, int offset)
420 int shorten_branch = 0;
421 int target = td->ip + offset - td->il_code;
422 if (target < 0 || target >= td->code_size)
423 g_assert_not_reached ();
424 /* Add exception checkpoint or safepoint for backward branches */
425 if (offset < 0) {
426 if (mono_threads_are_safepoints_enabled ())
427 interp_add_ins (td, MINT_SAFEPOINT);
428 else
429 interp_add_ins (td, MINT_CHECKPOINT);
431 if (offset > 0 && td->stack_height [target] < 0) {
432 td->stack_height [target] = td->sp - td->stack;
433 if (td->stack_height [target] > 0)
434 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, td->stack_height [target] * sizeof (td->stack [0]));
435 td->vt_stack_size [target] = td->vt_sp;
438 if (td->header->code_size <= 25000) /* FIX to be precise somehow? */
439 shorten_branch = 1;
441 if (shorten_branch) {
442 interp_add_ins (td, short_op);
443 td->last_ins->data [0] = (guint16) target;
444 } else {
445 interp_add_ins (td, long_op);
446 WRITE32_INS (td->last_ins, 0, &target);
450 static void
451 one_arg_branch(TransformData *td, int mint_op, int offset)
453 int type = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
454 int long_op = mint_op + type - STACK_TYPE_I4;
455 int short_op = long_op + MINT_BRFALSE_I4_S - MINT_BRFALSE_I4;
456 CHECK_STACK(td, 1);
457 --td->sp;
458 handle_branch (td, short_op, long_op, offset);
461 static void
462 two_arg_branch(TransformData *td, int mint_op, int offset)
464 int type1 = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
465 int type2 = td->sp [-2].type == STACK_TYPE_O || td->sp [-2].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-2].type;
466 int long_op = mint_op + type1 - STACK_TYPE_I4;
467 int short_op = long_op + MINT_BEQ_I4_S - MINT_BEQ_I4;
468 CHECK_STACK(td, 2);
469 if (type1 == STACK_TYPE_I4 && type2 == STACK_TYPE_I8) {
470 interp_add_ins (td, MINT_CONV_I8_I4);
471 // The il instruction starts with the actual branch, and not with the conversion opcodes
472 td->last_ins->il_offset = -1;
473 } else if (type1 == STACK_TYPE_I8 && type2 == STACK_TYPE_I4) {
474 interp_add_ins (td, MINT_CONV_I8_I4_SP);
475 td->last_ins->il_offset = -1;
476 } else if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
477 interp_add_ins (td, MINT_CONV_R8_R4);
478 td->last_ins->il_offset = -1;
479 } else if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
480 interp_add_ins (td, MINT_CONV_R8_R4_SP);
481 td->last_ins->il_offset = -1;
482 } else if (type1 != type2) {
483 g_warning("%s.%s: branch type mismatch %d %d",
484 m_class_get_name (td->method->klass), td->method->name,
485 td->sp [-1].type, td->sp [-2].type);
487 td->sp -= 2;
488 handle_branch (td, short_op, long_op, offset);
491 static void
492 unary_arith_op(TransformData *td, int mint_op)
494 int op = mint_op + td->sp [-1].type - STACK_TYPE_I4;
495 CHECK_STACK(td, 1);
496 interp_add_ins (td, op);
499 static void
500 binary_arith_op(TransformData *td, int mint_op)
502 int type1 = td->sp [-2].type;
503 int type2 = td->sp [-1].type;
504 int op;
505 #if SIZEOF_VOID_P == 8
506 if ((type1 == STACK_TYPE_MP || type1 == STACK_TYPE_I8) && type2 == STACK_TYPE_I4) {
507 interp_add_ins (td, MINT_CONV_I8_I4);
508 type2 = STACK_TYPE_I8;
510 if (type1 == STACK_TYPE_I4 && (type2 == STACK_TYPE_MP || type2 == STACK_TYPE_I8)) {
511 interp_add_ins (td, MINT_CONV_I8_I4_SP);
512 type1 = STACK_TYPE_I8;
513 td->sp [-2].type = STACK_TYPE_I8;
515 #endif
516 if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
517 interp_add_ins (td, MINT_CONV_R8_R4);
518 type2 = STACK_TYPE_R8;
520 if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
521 interp_add_ins (td, MINT_CONV_R8_R4_SP);
522 type1 = STACK_TYPE_R8;
523 td->sp [-2].type = STACK_TYPE_R8;
525 if (type1 == STACK_TYPE_MP)
526 type1 = STACK_TYPE_I;
527 if (type2 == STACK_TYPE_MP)
528 type2 = STACK_TYPE_I;
529 if (type1 != type2) {
530 g_warning("%s.%s: %04x arith type mismatch %s %d %d",
531 m_class_get_name (td->method->klass), td->method->name,
532 td->ip - td->il_code, mono_interp_opname[mint_op], type1, type2);
534 op = mint_op + type1 - STACK_TYPE_I4;
535 CHECK_STACK(td, 2);
536 interp_add_ins (td, op);
537 --td->sp;
540 static void
541 shift_op(TransformData *td, int mint_op)
543 int op = mint_op + td->sp [-2].type - STACK_TYPE_I4;
544 CHECK_STACK(td, 2);
545 if (td->sp [-1].type != STACK_TYPE_I4) {
546 g_warning("%s.%s: shift type mismatch %d",
547 m_class_get_name (td->method->klass), td->method->name,
548 td->sp [-2].type);
550 interp_add_ins (td, op);
551 --td->sp;
554 static int
555 can_store (int st_value, int vt_value)
557 if (st_value == STACK_TYPE_O || st_value == STACK_TYPE_MP)
558 st_value = STACK_TYPE_I;
559 if (vt_value == STACK_TYPE_O || vt_value == STACK_TYPE_MP)
560 vt_value = STACK_TYPE_I;
561 return st_value == vt_value;
564 #define SET_SIMPLE_TYPE(s, ty) \
565 do { \
566 (s)->type = (ty); \
567 (s)->flags = 0; \
568 (s)->klass = NULL; \
569 } while (0)
571 #define SET_TYPE(s, ty, k) \
572 do { \
573 (s)->type = (ty); \
574 (s)->flags = 0; \
575 (s)->klass = k; \
576 } while (0)
578 #define REALLOC_STACK(td, sppos) \
579 do { \
580 (td)->stack_capacity *= 2; \
581 (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
582 (td)->sp = (td)->stack + sppos; \
583 } while (0);
585 #define PUSH_SIMPLE_TYPE(td, ty) \
586 do { \
587 int sp_height; \
588 (td)->sp++; \
589 sp_height = (td)->sp - (td)->stack; \
590 if (sp_height > (td)->max_stack_height) \
591 (td)->max_stack_height = sp_height; \
592 if (sp_height > (td)->stack_capacity) \
593 REALLOC_STACK(td, sp_height); \
594 SET_SIMPLE_TYPE((td)->sp - 1, ty); \
595 } while (0)
597 #define PUSH_TYPE(td, ty, k) \
598 do { \
599 int sp_height; \
600 (td)->sp++; \
601 sp_height = (td)->sp - (td)->stack; \
602 if (sp_height > (td)->max_stack_height) \
603 (td)->max_stack_height = sp_height; \
604 if (sp_height > (td)->stack_capacity) \
605 REALLOC_STACK(td, sp_height); \
606 SET_TYPE((td)->sp - 1, ty, k); \
607 } while (0)
609 #define PUSH_VT(td, size) \
610 do { \
611 (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
612 if ((td)->vt_sp > (td)->max_vt_sp) \
613 (td)->max_vt_sp = (td)->vt_sp; \
614 } while (0)
616 #define POP_VT(td, size) \
617 do { \
618 (td)->vt_sp -= ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
619 } while (0)
621 static MonoType*
622 get_arg_type_exact (TransformData *td, int n, int *mt)
624 MonoType *type;
625 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
627 if (hasthis && n == 0)
628 type = m_class_get_byval_arg (td->method->klass);
629 else
630 type = mono_method_signature_internal (td->method)->params [n - !!hasthis];
632 if (mt)
633 *mt = mint_type (type);
635 return type;
638 static void
639 load_arg(TransformData *td, int n)
641 int mt;
642 MonoClass *klass = NULL;
643 MonoType *type;
644 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
646 type = get_arg_type_exact (td, n, &mt);
648 if (mt == MINT_TYPE_VT) {
649 gint32 size;
650 klass = mono_class_from_mono_type_internal (type);
651 if (mono_method_signature_internal (td->method)->pinvoke)
652 size = mono_class_native_size (klass, NULL);
653 else
654 size = mono_class_value_size (klass, NULL);
656 if (hasthis && n == 0) {
657 mt = MINT_TYPE_P;
658 interp_add_ins (td, MINT_LDARG_P);
659 td->last_ins->data [0] = 0;
660 klass = NULL;
661 } else {
662 PUSH_VT (td, size);
663 interp_add_ins (td, MINT_LDARG_VT);
664 td->last_ins->data [0] = n;
665 WRITE32_INS (td->last_ins, 1, &size);
667 } else {
668 if (hasthis && n == 0) {
669 mt = MINT_TYPE_P;
670 interp_add_ins (td, MINT_LDARG_P);
671 td->last_ins->data [0] = n;
672 klass = NULL;
673 } else {
674 interp_add_ins (td, MINT_LDARG_I1 + (mt - MINT_TYPE_I1));
675 td->last_ins->data [0] = n;
676 if (mt == MINT_TYPE_O)
677 klass = mono_class_from_mono_type_internal (type);
680 PUSH_TYPE(td, stack_type[mt], klass);
683 static void
684 store_arg(TransformData *td, int n)
686 int mt;
687 CHECK_STACK (td, 1);
688 MonoType *type;
690 type = get_arg_type_exact (td, n, &mt);
692 if (mt == MINT_TYPE_VT) {
693 gint32 size;
694 MonoClass *klass = mono_class_from_mono_type_internal (type);
695 if (mono_method_signature_internal (td->method)->pinvoke)
696 size = mono_class_native_size (klass, NULL);
697 else
698 size = mono_class_value_size (klass, NULL);
699 interp_add_ins (td, MINT_STARG_VT);
700 td->last_ins->data [0] = n;
701 WRITE32_INS (td->last_ins, 1, &size);
702 if (td->sp [-1].type == STACK_TYPE_VT)
703 POP_VT(td, size);
704 } else {
705 interp_add_ins (td, MINT_STARG_I1 + (mt - MINT_TYPE_I1));
706 td->last_ins->data [0] = n;
708 --td->sp;
711 static void
712 load_local_general (TransformData *td, int offset, MonoType *type)
714 int mt = mint_type (type);
715 MonoClass *klass = NULL;
716 if (mt == MINT_TYPE_VT) {
717 klass = mono_class_from_mono_type_internal (type);
718 gint32 size = mono_class_value_size (klass, NULL);
719 PUSH_VT(td, size);
720 interp_add_ins (td, MINT_LDLOC_VT);
721 td->last_ins->data [0] = offset;
722 WRITE32_INS (td->last_ins, 1, &size);
723 } else {
724 g_assert (mt < MINT_TYPE_VT);
725 if (!td->gen_sdb_seq_points &&
726 mt == MINT_TYPE_I4 && !td->is_bb_start [td->in_start - td->il_code] && td->last_ins != NULL &&
727 td->last_ins->opcode == MINT_STLOC_I4 && td->last_ins->data [0] == offset) {
728 td->last_ins->opcode = MINT_STLOC_NP_I4;
729 } else if (!td->gen_sdb_seq_points &&
730 mt == MINT_TYPE_O && !td->is_bb_start [td->in_start - td->il_code] && td->last_ins != NULL &&
731 td->last_ins->opcode == MINT_STLOC_O && td->last_ins->data [0] == offset) {
732 td->last_ins->opcode = MINT_STLOC_NP_O;
733 } else {
734 interp_add_ins (td, MINT_LDLOC_I1 + (mt - MINT_TYPE_I1));
735 td->last_ins->data [0] = offset; /*FIX for large offset */
737 if (mt == MINT_TYPE_O)
738 klass = mono_class_from_mono_type_internal (type);
740 PUSH_TYPE(td, stack_type[mt], klass);
743 static void
744 load_local (TransformData *td, int n)
746 MonoType *type = td->header->locals [n];
747 int offset = td->rtm->local_offsets [n];
748 load_local_general (td, offset, type);
751 static void
752 store_local_general (TransformData *td, int offset, MonoType *type)
754 int mt = mint_type (type);
755 CHECK_STACK (td, 1);
756 #if SIZEOF_VOID_P == 8
757 if (td->sp [-1].type == STACK_TYPE_I4 && stack_type [mt] == STACK_TYPE_I8) {
758 interp_add_ins (td, MINT_CONV_I8_I4);
759 td->sp [-1].type = STACK_TYPE_I8;
761 #endif
762 if (!can_store(td->sp [-1].type, stack_type [mt])) {
763 g_warning("%s.%s: Store local stack type mismatch %d %d",
764 m_class_get_name (td->method->klass), td->method->name,
765 stack_type [mt], td->sp [-1].type);
767 if (mt == MINT_TYPE_VT) {
768 MonoClass *klass = mono_class_from_mono_type_internal (type);
769 gint32 size = mono_class_value_size (klass, NULL);
770 interp_add_ins (td, MINT_STLOC_VT);
771 td->last_ins->data [0] = offset; /*FIX for large offset */
772 WRITE32_INS (td->last_ins, 1, &size);
773 if (td->sp [-1].type == STACK_TYPE_VT)
774 POP_VT(td, size);
775 } else {
776 g_assert (mt < MINT_TYPE_VT);
777 interp_add_ins (td, MINT_STLOC_I1 + (mt - MINT_TYPE_I1));
778 td->last_ins->data [0] = offset; /*FIX for large offset */
780 --td->sp;
783 static void
784 store_local (TransformData *td, int n)
786 MonoType *type = td->header->locals [n];
787 int offset = td->rtm->local_offsets [n];
788 store_local_general (td, offset, type);
791 #define SIMPLE_OP(td, op) \
792 do { \
793 interp_add_ins (td, op); \
794 ++td->ip; \
795 } while (0)
797 static guint16
798 get_data_item_index (TransformData *td, void *ptr)
800 gpointer p = g_hash_table_lookup (td->data_hash, ptr);
801 guint index;
802 if (p != NULL)
803 return GPOINTER_TO_UINT (p) - 1;
804 if (td->max_data_items == td->n_data_items) {
805 td->max_data_items = td->n_data_items == 0 ? 16 : 2 * td->max_data_items;
806 td->data_items = (gpointer*)g_realloc (td->data_items, td->max_data_items * sizeof(td->data_items [0]));
808 index = td->n_data_items;
809 td->data_items [index] = ptr;
810 ++td->n_data_items;
811 g_hash_table_insert (td->data_hash, ptr, GUINT_TO_POINTER (index + 1));
812 return index;
815 static gboolean
816 jit_call_supported (MonoMethod *method, MonoMethodSignature *sig)
818 GSList *l;
820 if (sig->param_count > 6)
821 return FALSE;
822 if (sig->pinvoke)
823 return FALSE;
824 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
825 return FALSE;
826 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
827 return FALSE;
828 if (method->is_inflated)
829 return FALSE;
830 if (method->string_ctor)
831 return FALSE;
833 if (mono_aot_only && m_class_get_image (method->klass)->aot_module && !(method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) {
834 ERROR_DECL (error);
835 gpointer addr = mono_jit_compile_method_jit_only (method, error);
836 if (addr && mono_error_ok (error))
837 return TRUE;
840 for (l = mono_interp_jit_classes; l; l = l->next) {
841 const char *class_name = (const char*)l->data;
842 // FIXME: Namespaces
843 if (!strcmp (m_class_get_name (method->klass), class_name))
844 return TRUE;
847 //return TRUE;
848 return FALSE;
851 static int mono_class_get_magic_index (MonoClass *k)
853 if (mono_class_is_magic_int (k))
854 return !strcmp ("nint", m_class_get_name (k)) ? 0 : 1;
856 if (mono_class_is_magic_float (k))
857 return 2;
859 return -1;
862 static void
863 interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *target_method)
865 MonoJitICallInfo *info = mono_find_jit_icall_by_name ("mono_throw_method_access");
867 /* Inject code throwing MethodAccessException */
868 interp_add_ins (td, MINT_MONO_LDPTR);
869 td->last_ins->data [0] = get_data_item_index (td, method);
870 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
872 interp_add_ins (td, MINT_MONO_LDPTR);
873 td->last_ins->data [0] = get_data_item_index (td, target_method);
874 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
876 interp_add_ins (td, MINT_ICALL_PP_V);
877 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
879 td->sp -= 2;
883 * These are additional locals that can be allocated as we transform the code.
884 * They are allocated past the method locals so they are accessed in the same
885 * way, with an offset relative to the frame->locals.
887 static int
888 create_interp_local (TransformData *td, MonoType *type)
890 int align, size;
891 int offset = td->total_locals_size;
893 size = mono_type_size (type, &align);
894 offset = ALIGN_TO (offset, align);
896 td->total_locals_size = offset + size;
898 return offset;
901 static void
902 dump_mint_code (const guint16 *start, const guint16* end)
904 const guint16 *p = start;
905 while (p < end) {
906 char *ins = mono_interp_dis_mintop (start, p);
907 g_print ("%s\n", ins);
908 g_free (ins);
909 p = mono_interp_dis_mintop_len (p);
913 /* For debug use */
914 void
915 mono_interp_print_code (InterpMethod *imethod)
917 MonoJitInfo *jinfo = imethod->jinfo;
918 const guint16 *start;
920 if (!jinfo)
921 return;
923 char *name = mono_method_full_name (imethod->method, 1);
924 g_print ("Method : %s\n", name);
925 g_free (name);
927 start = (guint16*) jinfo->code_start;
928 dump_mint_code (start, start + jinfo->code_size);
932 static MonoMethodHeader*
933 interp_method_get_header (MonoMethod* method, MonoError *error)
935 /* An explanation: mono_method_get_header_internal returns an error if
936 * called on a method with no body (e.g. an abstract method, or an
937 * icall). We don't want that.
939 if (mono_method_has_no_body (method))
940 return NULL;
941 else
942 return mono_method_get_header_internal (method, error);
945 /* stores top of stack as local and pushes address of it on stack */
946 static void
947 emit_store_value_as_local (TransformData *td, MonoType *src)
949 int size = mini_magic_type_size (NULL, src);
950 int local_offset = create_interp_local (td, mini_native_type_replace_type (src));
952 store_local_general (td, local_offset, src);
954 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
955 interp_add_ins (td, MINT_LDLOC_VT);
956 td->last_ins->data [0] = local_offset;
957 WRITE32_INS (td->last_ins, 1, &size);
959 PUSH_VT (td, size);
960 PUSH_TYPE (td, STACK_TYPE_VT, NULL);
963 // Returns whether we can optimize away the instructions starting at start.
964 // If any instructions are part of a new basic block, we can't remove them.
965 static gboolean
966 interp_is_bb_start (TransformData *td, InterpInst *start, InterpInst *end)
968 InterpInst *ins = start;
969 while (ins != end) {
970 if (ins->il_offset != -1) {
971 if (td->is_bb_start [ins->il_offset])
972 return TRUE;
974 ins = ins->next;
976 return FALSE;
979 static gboolean
980 interp_ins_is_ldc (InterpInst *ins)
982 return ins->opcode >= MINT_LDC_I4_M1 && ins->opcode <= MINT_LDC_I8;
985 static gint32
986 interp_ldc_i4_get_const (InterpInst *ins)
988 switch (ins->opcode) {
989 case MINT_LDC_I4_M1: return -1;
990 case MINT_LDC_I4_0: return 0;
991 case MINT_LDC_I4_1: return 1;
992 case MINT_LDC_I4_2: return 2;
993 case MINT_LDC_I4_3: return 3;
994 case MINT_LDC_I4_4: return 4;
995 case MINT_LDC_I4_5: return 5;
996 case MINT_LDC_I4_6: return 6;
997 case MINT_LDC_I4_7: return 7;
998 case MINT_LDC_I4_8: return 8;
999 case MINT_LDC_I4_S: return (gint32)(gint8)ins->data [0];
1000 case MINT_LDC_I4: return READ32 (&ins->data [0]);
1001 default:
1002 g_assert_not_reached ();
1006 static int
1007 interp_get_ldind_for_mt (int mt)
1009 switch (mt) {
1010 case MINT_TYPE_I1: return MINT_LDIND_I1_CHECK;
1011 case MINT_TYPE_U1: return MINT_LDIND_U1_CHECK;
1012 case MINT_TYPE_I2: return MINT_LDIND_I2_CHECK;
1013 case MINT_TYPE_U2: return MINT_LDIND_U2_CHECK;
1014 case MINT_TYPE_I4: return MINT_LDIND_I4_CHECK;
1015 case MINT_TYPE_I8: return MINT_LDIND_I8_CHECK;
1016 case MINT_TYPE_R4: return MINT_LDIND_R4_CHECK;
1017 case MINT_TYPE_R8: return MINT_LDIND_R8_CHECK;
1018 case MINT_TYPE_O: return MINT_LDIND_REF;
1019 default:
1020 g_assert_not_reached ();
1022 return -1;
1025 /* Return TRUE if call transformation is finished */
1026 static gboolean
1027 interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClass *constrained_class, MonoMethodSignature *csignature, gboolean readonly, int *op)
1029 const char *tm = target_method->name;
1030 int i;
1031 int type_index = mono_class_get_magic_index (target_method->klass);
1032 gboolean in_corlib = m_class_get_image (target_method->klass) == mono_defaults.corlib;
1033 const char *klass_name_space = m_class_get_name_space (target_method->klass);
1034 const char *klass_name = m_class_get_name (target_method->klass);
1036 if (target_method->klass == mono_defaults.string_class) {
1037 if (tm [0] == 'g') {
1038 if (strcmp (tm, "get_Chars") == 0)
1039 *op = MINT_GETCHR;
1040 else if (strcmp (tm, "get_Length") == 0)
1041 *op = MINT_STRLEN;
1043 } else if (type_index >= 0) {
1044 MonoClass *magic_class = target_method->klass;
1046 const int mt = mint_type (m_class_get_byval_arg (magic_class));
1047 if (!strcmp (".ctor", tm)) {
1048 MonoType *arg = csignature->params [0];
1049 /* depending on SIZEOF_VOID_P and the type of the value passed to the .ctor we either have to CONV it, or do nothing */
1050 int arg_size = mini_magic_type_size (NULL, arg);
1052 if (arg_size > SIZEOF_VOID_P) { // 8 -> 4
1053 switch (type_index) {
1054 case 0: case 1:
1055 interp_add_ins (td, MINT_CONV_I4_I8);
1056 break;
1057 case 2:
1058 interp_add_ins (td, MINT_CONV_R4_R8);
1059 break;
1063 if (arg_size < SIZEOF_VOID_P) { // 4 -> 8
1064 switch (type_index) {
1065 case 0:
1066 interp_add_ins (td, MINT_CONV_I8_I4);
1067 break;
1068 case 1:
1069 interp_add_ins (td, MINT_CONV_I8_U4);
1070 break;
1071 case 2:
1072 interp_add_ins (td, MINT_CONV_R8_R4);
1073 break;
1077 switch (type_index) {
1078 case 0: case 1:
1079 #if SIZEOF_VOID_P == 4
1080 interp_add_ins (td, MINT_STIND_I4);
1081 #else
1082 interp_add_ins (td, MINT_STIND_I8);
1083 #endif
1084 break;
1085 case 2:
1086 #if SIZEOF_VOID_P == 4
1087 interp_add_ins (td, MINT_STIND_R4);
1088 #else
1089 interp_add_ins (td, MINT_STIND_R8);
1090 #endif
1091 break;
1094 td->sp -= 2;
1095 td->ip += 5;
1096 return TRUE;
1097 } else if (!strcmp ("op_Implicit", tm ) || !strcmp ("op_Explicit", tm)) {
1098 MonoType *src = csignature->params [0];
1099 MonoType *dst = csignature->ret;
1100 MonoClass *src_klass = mono_class_from_mono_type_internal (src);
1101 int src_size = mini_magic_type_size (NULL, src);
1102 int dst_size = mini_magic_type_size (NULL, dst);
1104 gboolean store_value_as_local = FALSE;
1106 switch (type_index) {
1107 case 0: case 1:
1108 if (!mini_magic_is_int_type (src) || !mini_magic_is_int_type (dst)) {
1109 if (mini_magic_is_int_type (src))
1110 store_value_as_local = TRUE;
1111 else if (mono_class_is_magic_float (src_klass))
1112 store_value_as_local = TRUE;
1113 else
1114 return FALSE;
1116 break;
1117 case 2:
1118 if (!mini_magic_is_float_type (src) || !mini_magic_is_float_type (dst)) {
1119 if (mini_magic_is_float_type (src))
1120 store_value_as_local = TRUE;
1121 else if (mono_class_is_magic_int (src_klass))
1122 store_value_as_local = TRUE;
1123 else
1124 return FALSE;
1126 break;
1129 if (store_value_as_local) {
1130 emit_store_value_as_local (td, src);
1132 /* emit call to managed conversion method */
1133 return FALSE;
1136 #if SIZEOF_VOID_P == 4
1137 if (src_size > dst_size) { // 8 -> 4
1138 switch (type_index) {
1139 case 0: case 1:
1140 interp_add_ins (td, MINT_CONV_I4_I8);
1141 break;
1142 case 2:
1143 interp_add_ins (td, MINT_CONV_R4_R8);
1144 break;
1147 #endif
1149 #if SIZEOF_VOID_P == 8
1150 if (src_size < dst_size) { // 4 -> 8
1151 switch (type_index) {
1152 case 0:
1153 interp_add_ins (td, MINT_CONV_I8_I4);
1154 break;
1155 case 1:
1156 interp_add_ins (td, MINT_CONV_I8_U4);
1157 break;
1158 case 2:
1159 interp_add_ins (td, MINT_CONV_R8_R4);
1160 break;
1163 #endif
1165 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1166 td->ip += 5;
1167 return TRUE;
1168 } else if (!strcmp ("op_Increment", tm)) {
1169 g_assert (type_index != 2); // no nfloat
1170 #if SIZEOF_VOID_P == 8
1171 interp_add_ins (td, MINT_ADD1_I8);
1172 #else
1173 interp_add_ins (td, MINT_ADD1_I4);
1174 #endif
1175 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1176 td->ip += 5;
1177 return TRUE;
1178 } else if (!strcmp ("op_Decrement", tm)) {
1179 g_assert (type_index != 2); // no nfloat
1180 #if SIZEOF_VOID_P == 8
1181 interp_add_ins (td, MINT_SUB1_I8);
1182 #else
1183 interp_add_ins (td, MINT_SUB1_I4);
1184 #endif
1185 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1186 td->ip += 5;
1187 return TRUE;
1188 } else if (!strcmp ("CompareTo", tm) || !strcmp ("Equals", tm)) {
1189 MonoType *arg = csignature->params [0];
1191 /* on 'System.n*::{CompareTo,Equals} (System.n*)' variant we need to push managed
1192 * pointer instead of value */
1193 if (arg->type == MONO_TYPE_VALUETYPE)
1194 emit_store_value_as_local (td, arg);
1196 /* emit call to managed conversion method */
1197 return FALSE;
1198 } else if (!strcmp (".cctor", tm)) {
1199 /* white list */
1200 return FALSE;
1201 } else if (!strcmp ("Parse", tm)) {
1202 /* white list */
1203 return FALSE;
1204 } else if (!strcmp ("ToString", tm)) {
1205 /* white list */
1206 return FALSE;
1207 } else if (!strcmp ("GetHashCode", tm)) {
1208 /* white list */
1209 return FALSE;
1210 } else if (!strcmp ("IsNaN", tm) || !strcmp ("IsInfinity", tm) || !strcmp ("IsNegativeInfinity", tm) || !strcmp ("IsPositiveInfinity", tm)) {
1211 g_assert (type_index == 2); // nfloat only
1212 /* white list */
1213 return FALSE;
1216 for (i = 0; i < sizeof (int_unnop) / sizeof (MagicIntrinsic); ++i) {
1217 if (!strcmp (int_unnop [i].op_name, tm)) {
1218 interp_add_ins (td, int_unnop [i].insn [type_index]);
1219 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1220 td->ip += 5;
1221 return TRUE;
1225 for (i = 0; i < sizeof (int_binop) / sizeof (MagicIntrinsic); ++i) {
1226 if (!strcmp (int_binop [i].op_name, tm)) {
1227 interp_add_ins (td, int_binop [i].insn [type_index]);
1228 td->sp -= 1;
1229 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1230 td->ip += 5;
1231 return TRUE;
1235 for (i = 0; i < sizeof (int_cmpop) / sizeof (MagicIntrinsic); ++i) {
1236 if (!strcmp (int_cmpop [i].op_name, tm)) {
1237 MonoClass *k = mono_defaults.boolean_class;
1238 interp_add_ins (td, int_cmpop [i].insn [type_index]);
1239 td->sp -= 1;
1240 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1241 td->ip += 5;
1242 return TRUE;
1246 g_error ("TODO: interp_transform_call %s:%s", m_class_get_name (target_method->klass), tm);
1247 } else if (mono_class_is_subclass_of (target_method->klass, mono_defaults.array_class, FALSE)) {
1248 if (!strcmp (tm, "get_Rank")) {
1249 *op = MINT_ARRAY_RANK;
1250 } else if (!strcmp (tm, "get_Length")) {
1251 *op = MINT_LDLEN;
1252 } else if (!strcmp (tm, "Address")) {
1253 *op = readonly ? MINT_LDELEMA : MINT_LDELEMA_TC;
1254 } else if (!strcmp (tm, "UnsafeMov") || !strcmp (tm, "UnsafeLoad") || !strcmp (tm, "Set") || !strcmp (tm, "Get")) {
1255 *op = MINT_CALLRUN;
1256 } else if (!strcmp (tm, "UnsafeStore")) {
1257 g_error ("TODO ArrayClass::UnsafeStore");
1259 } else if (in_corlib &&
1260 !strcmp (klass_name_space, "System.Diagnostics") &&
1261 !strcmp (klass_name, "Debugger")) {
1262 if (!strcmp (tm, "Break") && csignature->param_count == 0) {
1263 if (mini_should_insert_breakpoint (td->method))
1264 *op = MINT_BREAK;
1266 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "ByReference`1")) {
1267 *op = MINT_INTRINS_BYREFERENCE_GET_VALUE;
1268 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "Math") && csignature->param_count == 1 && csignature->params [0]->type == MONO_TYPE_R8) {
1269 if (tm [0] == 'A') {
1270 if (strcmp (tm, "Abs") == 0 && csignature->params [0]->type == MONO_TYPE_R8) {
1271 *op = MINT_ABS;
1272 } else if (strcmp (tm, "Asin") == 0){
1273 *op = MINT_ASIN;
1274 } else if (strcmp (tm, "Asinh") == 0){
1275 *op = MINT_ASINH;
1276 } else if (strcmp (tm, "Acos") == 0){
1277 *op = MINT_ACOS;
1278 } else if (strcmp (tm, "Acosh") == 0){
1279 *op = MINT_ACOSH;
1280 } else if (strcmp (tm, "Atan") == 0){
1281 *op = MINT_ATAN;
1282 } else if (strcmp (tm, "Atanh") == 0){
1283 *op = MINT_ATANH;
1285 } else if (tm [0] == 'C') {
1286 if (strcmp (tm, "Cos") == 0) {
1287 *op = MINT_COS;
1288 } else if (strcmp (tm, "Cbrt") == 0){
1289 *op = MINT_CBRT;
1290 } else if (strcmp (tm, "Cosh") == 0){
1291 *op = MINT_COSH;
1293 } else if (tm [0] == 'S') {
1294 if (strcmp (tm, "Sin") == 0) {
1295 *op = MINT_SIN;
1296 } else if (strcmp (tm, "Sqrt") == 0) {
1297 *op = MINT_SQRT;
1298 } else if (strcmp (tm, "Sinh") == 0){
1299 *op = MINT_SINH;
1301 } else if (tm [0] == 'T') {
1302 if (strcmp (tm, "Tan") == 0) {
1303 *op = MINT_TAN;
1304 } else if (strcmp (tm, "Tanh") == 0){
1305 *op = MINT_TANH;
1308 } else if (in_corlib && !strcmp (klass_name_space, "System") && (!strcmp (klass_name, "Span`1") || !strcmp (klass_name, "ReadOnlySpan`1"))) {
1309 if (!strcmp (tm, "get_Item") && csignature->params [0]->type != MONO_TYPE_VALUETYPE) {
1310 MonoGenericClass *gclass = mono_class_get_generic_class (target_method->klass);
1311 MonoClass *param_class = mono_class_from_mono_type_internal (gclass->context.class_inst->type_argv [0]);
1313 if (!mini_is_gsharedvt_variable_klass (param_class)) {
1314 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1315 g_assert (length_field);
1316 int offset_length = length_field->offset - sizeof (MonoObject);
1318 MonoClassField *ptr_field = mono_class_get_field_from_name_full (target_method->klass, "_pointer", NULL);
1319 g_assert (ptr_field);
1320 int offset_pointer = ptr_field->offset - sizeof (MonoObject);
1322 int size = mono_class_array_element_size (param_class);
1323 interp_add_ins (td, MINT_GETITEM_SPAN);
1324 td->last_ins->data [0] = size;
1325 td->last_ins->data [1] = offset_length;
1326 td->last_ins->data [2] = offset_pointer;
1328 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1329 td->sp -= 1;
1330 td->ip += 5;
1331 return TRUE;
1333 } else if (!strcmp (tm, "get_Length")) {
1334 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1335 g_assert (length_field);
1336 int offset_length = length_field->offset - sizeof (MonoObject);
1337 interp_add_ins (td, MINT_LDLEN_SPAN);
1338 td->last_ins->data [0] = offset_length;
1339 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1340 td->ip += 5;
1341 return TRUE;
1343 } else if (in_corlib && !strcmp (klass_name_space, "Internal.Runtime.CompilerServices") && !strcmp (klass_name, "Unsafe")) {
1344 #ifdef ENABLE_NETCORE
1345 if (!strcmp (tm, "AddByteOffset"))
1346 *op = MINT_INTRINS_UNSAFE_ADD_BYTE_OFFSET;
1347 else if (!strcmp (tm, "ByteOffset"))
1348 *op = MINT_INTRINS_UNSAFE_BYTE_OFFSET;
1349 else if (!strcmp (tm, "As") || !strcmp (tm, "AsRef"))
1350 *op = MINT_NOP;
1351 else if (!strcmp (tm, "SizeOf")) {
1352 MonoGenericContext *ctx = mono_method_get_context (target_method);
1353 g_assert (ctx);
1354 g_assert (ctx->method_inst);
1355 g_assert (ctx->method_inst->type_argc == 1);
1356 MonoType *t = ctx->method_inst->type_argv [0];
1357 int align;
1358 int esize = mono_type_size (t, &align);
1359 interp_add_ins (td, MINT_LDC_I4);
1360 WRITE32_INS (td->last_ins, 0, &esize);
1361 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1362 td->ip += 5;
1363 return TRUE;
1364 } else if (!strcmp (tm, "AreSame")) {
1365 *op = MINT_CEQ_P;
1367 #endif
1368 } else if (in_corlib && !strcmp (klass_name_space, "System.Runtime.CompilerServices") && !strcmp (klass_name, "RuntimeHelpers")) {
1369 #ifdef ENABLE_NETCORE
1370 if (!strcmp (tm, "IsBitwiseEquatable")) {
1371 g_assert (csignature->param_count == 0);
1372 MonoGenericContext *ctx = mono_method_get_context (target_method);
1373 g_assert (ctx);
1374 g_assert (ctx->method_inst);
1375 g_assert (ctx->method_inst->type_argc == 1);
1376 MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]);
1378 if (MONO_TYPE_IS_PRIMITIVE (t) && t->type != MONO_TYPE_R4 && t->type != MONO_TYPE_R8)
1379 *op = MINT_LDC_I4_1;
1380 else
1381 *op = MINT_LDC_I4_0;
1382 } else if (!strcmp (tm, "ObjectHasComponentSize")) {
1383 *op = MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE;
1385 #endif
1386 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "RuntimeMethodHandle") && !strcmp (tm, "GetFunctionPointer") && csignature->param_count == 1) {
1387 // We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter
1388 *op = MINT_LDFTN_DYNAMIC;
1389 } else if (target_method->klass == mono_defaults.object_class) {
1390 if (!strcmp (tm, "InternalGetHashCode"))
1391 *op = MINT_INTRINS_GET_HASHCODE;
1392 #ifdef DISABLE_REMOTING
1393 else if (!strcmp (tm, "GetType"))
1394 *op = MINT_INTRINS_GET_TYPE;
1395 #endif
1396 } else if (in_corlib && target_method->klass == mono_defaults.enum_class && !strcmp (tm, "HasFlag")) {
1397 gboolean intrinsify = FALSE;
1398 MonoClass *base_klass = NULL;
1399 if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1400 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1401 td->last_ins->prev->prev && td->last_ins->prev->prev->opcode == MINT_BOX &&
1402 td->sp [-2].klass == td->sp [-1].klass &&
1403 !interp_is_bb_start (td, td->last_ins->prev->prev, NULL) &&
1404 !td->is_bb_start [td->in_start - td->il_code]) {
1405 // csc pattern : box, ldc, box, call HasFlag
1406 g_assert (m_class_is_enumtype (td->sp [-2].klass));
1407 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (td->sp [-2].klass));
1408 base_klass = mono_class_from_mono_type_internal (base_type);
1410 // Remove the boxing of valuetypes
1411 interp_remove_ins (td, td->last_ins->prev->prev);
1412 interp_remove_ins (td, td->last_ins);
1414 intrinsify = TRUE;
1415 } else if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1416 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1417 constrained_class && td->sp [-1].klass == constrained_class &&
1418 !interp_is_bb_start (td, td->last_ins->prev, NULL) &&
1419 !td->is_bb_start [td->in_start - td->il_code]) {
1420 // mcs pattern : ldc, box, constrained Enum, call HasFlag
1421 g_assert (m_class_is_enumtype (constrained_class));
1422 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (constrained_class));
1423 base_klass = mono_class_from_mono_type_internal (base_type);
1424 int mt = mint_type (m_class_get_byval_arg (base_klass));
1426 // Remove boxing and load the value of this
1427 interp_remove_ins (td, td->last_ins);
1428 interp_insert_ins (td, td->last_ins->prev, interp_get_ldind_for_mt (mt));
1430 intrinsify = TRUE;
1432 if (intrinsify) {
1433 interp_add_ins (td, MINT_INTRINS_ENUM_HASFLAG);
1434 td->last_ins->data [0] = get_data_item_index (td, base_klass);
1435 td->sp -= 2;
1436 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1437 td->ip += 5;
1438 return TRUE;
1442 return FALSE;
1445 static MonoMethod*
1446 interp_transform_internal_calls (MonoMethod *method, MonoMethod *target_method, MonoMethodSignature *csignature, gboolean is_virtual)
1448 if (method->wrapper_type == MONO_WRAPPER_NONE && target_method != NULL) {
1449 if (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
1450 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1451 if (!is_virtual && target_method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
1452 target_method = mono_marshal_get_synchronized_wrapper (target_method);
1454 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)
1455 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1457 return target_method;
1460 static gboolean
1461 interp_type_as_ptr (MonoType *tp)
1463 if (MONO_TYPE_IS_POINTER (tp))
1464 return TRUE;
1465 if (MONO_TYPE_IS_REFERENCE (tp))
1466 return TRUE;
1467 if ((tp)->type == MONO_TYPE_I4)
1468 return TRUE;
1469 #if SIZEOF_VOID_P == 8
1470 if ((tp)->type == MONO_TYPE_I8)
1471 return TRUE;
1472 #endif
1473 if ((tp)->type == MONO_TYPE_BOOLEAN)
1474 return TRUE;
1475 if ((tp)->type == MONO_TYPE_CHAR)
1476 return TRUE;
1477 if ((tp)->type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (tp->data.klass))
1478 return TRUE;
1479 return FALSE;
1482 #define INTERP_TYPE_AS_PTR(tp) interp_type_as_ptr (tp)
1484 static int
1485 interp_icall_op_for_sig (MonoMethodSignature *sig)
1487 int op = -1;
1488 switch (sig->param_count) {
1489 case 0:
1490 if (MONO_TYPE_IS_VOID (sig->ret))
1491 op = MINT_ICALL_V_V;
1492 else if (INTERP_TYPE_AS_PTR (sig->ret))
1493 op = MINT_ICALL_V_P;
1494 break;
1495 case 1:
1496 if (MONO_TYPE_IS_VOID (sig->ret)) {
1497 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1498 op = MINT_ICALL_P_V;
1499 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1500 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1501 op = MINT_ICALL_P_P;
1503 break;
1504 case 2:
1505 if (MONO_TYPE_IS_VOID (sig->ret)) {
1506 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1507 INTERP_TYPE_AS_PTR (sig->params [1]))
1508 op = MINT_ICALL_PP_V;
1509 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1510 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1511 INTERP_TYPE_AS_PTR (sig->params [1]))
1512 op = MINT_ICALL_PP_P;
1514 break;
1515 case 3:
1516 if (MONO_TYPE_IS_VOID (sig->ret)) {
1517 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1518 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1519 INTERP_TYPE_AS_PTR (sig->params [2]))
1520 op = MINT_ICALL_PPP_V;
1521 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1522 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1523 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1524 INTERP_TYPE_AS_PTR (sig->params [2]))
1525 op = MINT_ICALL_PPP_P;
1527 break;
1528 case 4:
1529 if (MONO_TYPE_IS_VOID (sig->ret)) {
1530 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1531 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1532 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1533 INTERP_TYPE_AS_PTR (sig->params [3]))
1534 op = MINT_ICALL_PPPP_V;
1535 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1536 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1537 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1538 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1539 INTERP_TYPE_AS_PTR (sig->params [3]))
1540 op = MINT_ICALL_PPPP_P;
1542 break;
1543 case 5:
1544 if (MONO_TYPE_IS_VOID (sig->ret)) {
1545 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1546 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1547 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1548 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1549 INTERP_TYPE_AS_PTR (sig->params [4]))
1550 op = MINT_ICALL_PPPPP_V;
1551 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1552 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1553 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1554 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1555 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1556 INTERP_TYPE_AS_PTR (sig->params [4]))
1557 op = MINT_ICALL_PPPPP_P;
1559 break;
1560 case 6:
1561 if (MONO_TYPE_IS_VOID (sig->ret)) {
1562 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1563 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1564 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1565 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1566 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1567 INTERP_TYPE_AS_PTR (sig->params [5]))
1568 op = MINT_ICALL_PPPPPP_V;
1569 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1570 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1571 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1572 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1573 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1574 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1575 INTERP_TYPE_AS_PTR (sig->params [5]))
1576 op = MINT_ICALL_PPPPPP_P;
1578 break;
1580 return op;
1583 #define INLINE_LENGTH_LIMIT 20
1585 static gboolean
1586 interp_method_check_inlining (TransformData *td, MonoMethod *method)
1588 MonoMethodHeaderSummary header;
1590 if (td->method == method)
1591 return FALSE;
1593 if (!mono_method_get_header_summary (method, &header))
1594 return FALSE;
1596 /*runtime, icall and pinvoke are checked by summary call*/
1597 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
1598 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
1599 (mono_class_is_marshalbyref (method->klass)) ||
1600 header.has_clauses ||
1601 header.has_locals)
1602 return FALSE;
1604 if (header.code_size >= INLINE_LENGTH_LIMIT)
1605 return FALSE;
1607 if (mono_class_needs_cctor_run (method->klass, NULL)) {
1608 MonoVTable *vtable;
1609 ERROR_DECL (error);
1610 if (!m_class_get_runtime_info (method->klass))
1611 /* No vtable created yet */
1612 return FALSE;
1613 vtable = mono_class_vtable_checked (td->rtm->domain, method->klass, error);
1614 if (!is_ok (error)) {
1615 mono_error_cleanup (error);
1616 return FALSE;
1618 if (!vtable->initialized)
1619 return FALSE;
1622 /* We currently access at runtime the wrapper data */
1623 if (method->wrapper_type != MONO_WRAPPER_NONE)
1624 return FALSE;
1626 return TRUE;
1629 static gboolean
1630 interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHeader *header, MonoError *error)
1632 const unsigned char *prev_ip, *prev_il_code, *prev_in_start;
1633 StackInfo **prev_stack_state;
1634 int *prev_stack_height, *prev_vt_stack_size, *prev_clause_indexes, *prev_in_offsets;
1635 guint8 *prev_is_bb_start;
1636 gboolean ret;
1637 unsigned int prev_max_stack_height, prev_max_vt_sp, prev_total_locals_size;
1638 int prev_n_data_items;
1639 int i, prev_vt_sp;
1640 int prev_sp_offset;
1641 MonoGenericContext *generic_context = NULL;
1642 StackInfo *prev_param_area;
1643 MonoMethod *prev_inlined_method;
1644 MonoMethodSignature *csignature = mono_method_signature_internal (target_method);
1645 int nargs = csignature->param_count + !!csignature->hasthis;
1646 InterpInst *prev_last_ins;
1648 if (csignature->is_inflated)
1649 generic_context = mono_method_get_context (target_method);
1650 else {
1651 MonoGenericContainer *generic_container = mono_method_get_generic_container (target_method);
1652 if (generic_container)
1653 generic_context = &generic_container->context;
1656 prev_ip = td->ip;
1657 prev_il_code = td->il_code;
1658 prev_in_start = td->in_start;
1659 prev_sp_offset = td->sp - td->stack;
1660 prev_vt_sp = td->vt_sp;
1661 prev_stack_state = td->stack_state;
1662 prev_stack_height = td->stack_height;
1663 prev_vt_stack_size = td->vt_stack_size;
1664 prev_clause_indexes = td->clause_indexes;
1665 prev_inlined_method = td->inlined_method;
1666 prev_last_ins = td->last_ins;
1667 td->inlined_method = target_method;
1669 prev_max_stack_height = td->max_stack_height;
1670 prev_max_vt_sp = td->max_vt_sp;
1671 prev_total_locals_size = td->total_locals_size;
1673 prev_n_data_items = td->n_data_items;
1674 prev_in_offsets = td->in_offsets;
1675 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
1677 prev_is_bb_start = td->is_bb_start;
1679 /* Inlining pops the arguments, restore the stack */
1680 prev_param_area = (StackInfo*)g_malloc (nargs * sizeof (StackInfo));
1681 memcpy (prev_param_area, &td->sp [-nargs], nargs * sizeof (StackInfo));
1683 if (td->verbose_level)
1684 g_print ("Inline start method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1685 ret = generate_code (td, target_method, header, generic_context, error);
1687 if (!ret) {
1688 if (td->verbose_level)
1689 g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1690 td->max_stack_height = prev_max_stack_height;
1691 td->max_vt_sp = prev_max_vt_sp;
1692 td->total_locals_size = prev_total_locals_size;
1695 /* Remove any newly added items */
1696 for (i = prev_n_data_items; i < td->n_data_items; i++) {
1697 g_hash_table_remove (td->data_hash, td->data_items [i]);
1699 td->n_data_items = prev_n_data_items;
1700 td->sp = td->stack + prev_sp_offset;
1701 memcpy (&td->sp [-nargs], prev_param_area, nargs * sizeof (StackInfo));
1702 td->vt_sp = prev_vt_sp;
1703 td->last_ins = prev_last_ins;
1704 if (td->last_ins)
1705 td->last_ins->next = NULL;
1706 UnlockedIncrement (&mono_interp_stats.inline_failures);
1707 } else {
1708 if (td->verbose_level)
1709 g_print ("Inline end method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1710 UnlockedIncrement (&mono_interp_stats.inlined_methods);
1711 // Make sure we have an IR instruction associated with the now removed IL CALL
1712 // FIXME This could be prettier. We might be able to make inlining saner now that
1713 // that we can easily tweak the instruction list.
1714 if (!prev_inlined_method) {
1715 if (prev_last_ins) {
1716 if (prev_last_ins->next)
1717 prev_last_ins->next->il_offset = prev_in_start - prev_il_code;
1718 } else if (td->first_ins) {
1719 td->first_ins->il_offset = prev_in_start - prev_il_code;
1724 td->ip = prev_ip;
1725 td->in_start = prev_in_start;
1726 td->il_code = prev_il_code;
1727 td->stack_state = prev_stack_state;
1728 td->stack_height = prev_stack_height;
1729 td->vt_stack_size = prev_vt_stack_size;
1730 td->clause_indexes = prev_clause_indexes;
1731 td->is_bb_start = prev_is_bb_start;
1732 td->inlined_method = prev_inlined_method;
1734 g_free (td->in_offsets);
1735 td->in_offsets = prev_in_offsets;
1737 g_free (prev_param_area);
1738 return ret;
1741 static void
1742 interp_constrained_box (TransformData *td, MonoDomain *domain, MonoClass *constrained_class, MonoMethodSignature *csignature, MonoError *error)
1744 int mt = mint_type (m_class_get_byval_arg (constrained_class));
1745 if (mono_class_is_nullable (constrained_class)) {
1746 g_assert (mt == MINT_TYPE_VT);
1747 interp_add_ins (td, MINT_BOX_NULLABLE);
1748 td->last_ins->data [0] = get_data_item_index (td, constrained_class);
1749 td->last_ins->data [1] = csignature->param_count | ((td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : BOX_NOT_CLEAR_VT_SP);
1750 } else {
1751 MonoVTable *vtable = mono_class_vtable_checked (domain, constrained_class, error);
1752 return_if_nok (error);
1754 if (mt == MINT_TYPE_VT) {
1755 interp_add_ins (td, MINT_BOX_VT);
1756 td->last_ins->data [0] = get_data_item_index (td, vtable);
1757 td->last_ins->data [1] = csignature->param_count | ((td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : BOX_NOT_CLEAR_VT_SP);
1758 } else {
1759 interp_add_ins (td, MINT_BOX);
1760 td->last_ins->data [0] = get_data_item_index (td, vtable);
1761 td->last_ins->data [1] = csignature->param_count;
1766 /* Return FALSE if error, including inline failure */
1767 static gboolean
1768 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)
1770 MonoImage *image = m_class_get_image (method->klass);
1771 MonoMethodSignature *csignature;
1772 int is_virtual = *td->ip == CEE_CALLVIRT;
1773 int calli = *td->ip == CEE_CALLI || *td->ip == CEE_MONO_CALLI_EXTRA_ARG;
1774 int i;
1775 guint32 vt_stack_used = 0;
1776 guint32 vt_res_size = 0;
1777 int op = -1;
1778 int native = 0;
1779 int is_void = 0;
1780 int need_null_check = is_virtual;
1782 guint32 token = read32 (td->ip + 1);
1784 if (target_method == NULL) {
1785 if (calli) {
1786 CHECK_STACK(td, 1);
1787 if (method->wrapper_type != MONO_WRAPPER_NONE)
1788 csignature = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
1789 else {
1790 csignature = mono_metadata_parse_signature_checked (image, token, error);
1791 return_val_if_nok (error, FALSE);
1794 if (generic_context) {
1795 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
1796 return_val_if_nok (error, FALSE);
1800 * The compiled interp entry wrapper is passed to runtime_invoke instead of
1801 * the InterpMethod pointer. FIXME
1803 native = csignature->pinvoke || method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE;
1804 --td->sp;
1806 target_method = NULL;
1807 } else {
1808 if (method->wrapper_type == MONO_WRAPPER_NONE) {
1809 target_method = mono_get_method_checked (image, token, NULL, generic_context, error);
1810 return_val_if_nok (error, FALSE);
1811 } else
1812 target_method = (MonoMethod *)mono_method_get_wrapper_data (method, token);
1813 csignature = mono_method_signature_internal (target_method);
1815 if (generic_context) {
1816 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
1817 return_val_if_nok (error, FALSE);
1818 target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, error);
1819 return_val_if_nok (error, FALSE);
1822 } else {
1823 csignature = mono_method_signature_internal (target_method);
1826 if (check_visibility && target_method && !mono_method_can_access_method (method, target_method))
1827 interp_generate_mae_throw (td, method, target_method);
1829 if (target_method && target_method->string_ctor) {
1830 /* Create the real signature */
1831 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (td->mempool, csignature);
1832 ctor_sig->ret = m_class_get_byval_arg (mono_defaults.string_class);
1834 csignature = ctor_sig;
1837 /* Intrinsics */
1838 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
1839 return TRUE;
1841 if (constrained_class) {
1842 if (m_class_is_enumtype (constrained_class) && !strcmp (target_method->name, "GetHashCode")) {
1843 /* Use the corresponding method from the base type to avoid boxing */
1844 MonoType *base_type = mono_class_enum_basetype_internal (constrained_class);
1845 g_assert (base_type);
1846 constrained_class = mono_class_from_mono_type_internal (base_type);
1847 target_method = mono_class_get_method_from_name_checked (constrained_class, target_method->name, 0, 0, error);
1848 mono_error_assert_ok (error);
1849 g_assert (target_method);
1853 if (constrained_class) {
1854 mono_class_setup_vtable (constrained_class);
1855 if (mono_class_has_failure (constrained_class)) {
1856 mono_error_set_for_class_failure (error, constrained_class);
1857 return FALSE;
1859 #if DEBUG_INTERP
1860 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);
1861 #endif
1862 target_method = mono_get_method_constrained_with_method (image, target_method, constrained_class, generic_context, error);
1863 #if DEBUG_INTERP
1864 g_print (" : %s::%s. %s (%p)\n", target_method->klass->name, target_method->name, mono_signature_full_name (target_method->signature), target_method);
1865 #endif
1866 /* Intrinsics: Try again, it could be that `mono_get_method_constrained_with_method` resolves to a method that we can substitute */
1867 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
1868 return TRUE;
1870 return_val_if_nok (error, FALSE);
1871 mono_class_setup_vtable (target_method->klass);
1873 if (!m_class_is_valuetype (constrained_class)) {
1874 /* managed pointer on the stack, we need to deref that puppy */
1875 interp_add_ins (td, MINT_LDIND_I);
1876 td->last_ins->data [0] = csignature->param_count;
1877 } 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) {
1878 if (target_method->klass == mono_defaults.enum_class && (td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
1879 /* managed pointer on the stack, we need to deref that puppy */
1880 /* Always load the entire stackval, to handle also the case where the enum has long storage */
1881 interp_add_ins (td, MINT_LDIND_I8);
1882 td->last_ins->data [0] = csignature->param_count;
1885 interp_constrained_box (td, domain, constrained_class, csignature, error);
1886 return_val_if_nok (error, FALSE);
1887 } else {
1888 if (target_method->klass != constrained_class) {
1890 * The type parameter is instantiated as a valuetype,
1891 * but that type doesn't override the method we're
1892 * calling, so we need to box `this'.
1894 if ((td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
1895 /* managed pointer on the stack, we need to deref that puppy */
1896 /* Always load the entire stackval, to handle also the case where the enum has long storage */
1897 interp_add_ins (td, MINT_LDIND_I8);
1898 td->last_ins->data [0] = csignature->param_count;
1901 interp_constrained_box (td, domain, constrained_class, csignature, error);
1902 return_val_if_nok (error, FALSE);
1904 is_virtual = FALSE;
1908 if (target_method)
1909 mono_class_init_internal (target_method->klass);
1911 if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT))
1912 /* MS.NET seems to silently convert this to a callvirt */
1913 is_virtual = TRUE;
1915 if (is_virtual && target_method && (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
1916 (MONO_METHOD_IS_FINAL (target_method) &&
1917 target_method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
1918 !(mono_class_is_marshalbyref (target_method->klass))) {
1919 /* Not really virtual, just needs a null check */
1920 is_virtual = FALSE;
1921 need_null_check = TRUE;
1924 CHECK_STACK (td, csignature->param_count + csignature->hasthis);
1925 if (!td->gen_sdb_seq_points && !calli && op == -1 && (!is_virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) &&
1926 (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 &&
1927 (target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0 &&
1928 !(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING)) {
1929 (void)mono_class_vtable_checked (domain, target_method->klass, error);
1930 return_val_if_nok (error, FALSE);
1932 if (method == target_method && *(td->ip + 5) == CEE_RET && !(csignature->hasthis && m_class_is_valuetype (target_method->klass))) {
1933 if (td->inlined_method)
1934 return FALSE;
1936 if (td->verbose_level)
1937 g_print ("Optimize tail call of %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1939 for (i = csignature->param_count - 1 + !!csignature->hasthis; i >= 0; --i)
1940 store_arg (td, i);
1942 interp_add_ins (td, MINT_BR_S);
1943 // We are branching to the beginning of the method
1944 td->last_ins->data [0] = 0;
1945 if (!is_bb_start [td->ip + 5 - td->il_code])
1946 ++td->ip; /* gobble the CEE_RET if it isn't branched to */
1947 td->ip += 5;
1948 return TRUE;
1952 target_method = interp_transform_internal_calls (method, target_method, csignature, is_virtual);
1954 if (csignature->call_convention == MONO_CALL_VARARG) {
1955 csignature = mono_method_get_signature_checked (target_method, image, token, generic_context, error);
1956 int vararg_stack = 0;
1958 * For vararg calls, ArgIterator expects the signature and the varargs to be
1959 * stored in a linear memory. We allocate the necessary vt_stack space for
1960 * this. All varargs will be pushed to the vt_stack at call site.
1962 vararg_stack += sizeof (gpointer);
1963 for (i = csignature->sentinelpos; i < csignature->param_count; ++i) {
1964 int align, arg_size;
1965 arg_size = mono_type_stack_size (csignature->params [i], &align);
1966 vararg_stack += arg_size;
1968 vt_stack_used += ALIGN_TO (vararg_stack, MINT_VT_ALIGNMENT);
1969 PUSH_VT (td, vararg_stack);
1972 if (need_null_check) {
1973 interp_add_ins (td, MINT_CKNULL_N);
1974 td->last_ins->data [0] = csignature->param_count + 1;
1977 g_assert (csignature->call_convention != MONO_CALL_FASTCALL);
1978 if ((mono_interp_opt & INTERP_OPT_INLINE) && op == -1 && !is_virtual && target_method && interp_method_check_inlining (td, target_method)) {
1979 MonoMethodHeader *mheader = interp_method_get_header (target_method, error);
1980 return_val_if_nok (error, FALSE);
1982 if (interp_inline_method (td, target_method, mheader, error)) {
1983 td->ip += 5;
1984 return TRUE;
1988 /* Don't inline methods that do calls */
1989 if (op == -1 && td->inlined_method)
1990 return FALSE;
1992 td->sp -= csignature->param_count + !!csignature->hasthis;
1993 for (i = 0; i < csignature->param_count; ++i) {
1994 if (td->sp [i + !!csignature->hasthis].type == STACK_TYPE_VT) {
1995 gint32 size;
1996 MonoClass *klass = mono_class_from_mono_type_internal (csignature->params [i]);
1997 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
1998 size = mono_class_native_size (klass, NULL);
1999 else
2000 size = mono_class_value_size (klass, NULL);
2001 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
2002 vt_stack_used += size;
2006 /* need to handle typedbyref ... */
2007 if (csignature->ret->type != MONO_TYPE_VOID) {
2008 int mt = mint_type(csignature->ret);
2009 MonoClass *klass = mono_class_from_mono_type_internal (csignature->ret);
2010 if (mt == MINT_TYPE_VT) {
2011 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2012 vt_res_size = mono_class_native_size (klass, NULL);
2013 else
2014 vt_res_size = mono_class_value_size (klass, NULL);
2015 if (mono_class_has_failure (klass)) {
2016 mono_error_set_for_class_failure (error, klass);
2017 return FALSE;
2019 PUSH_VT(td, vt_res_size);
2021 PUSH_TYPE(td, stack_type[mt], klass);
2022 } else
2023 is_void = TRUE;
2025 /* We need to convert delegate invoke to a indirect call on the interp_invoke_impl field */
2026 if (target_method && m_class_get_parent (target_method->klass) == mono_defaults.multicastdelegate_class) {
2027 const char *name = target_method->name;
2028 if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
2029 calli = TRUE;
2030 interp_add_ins (td, MINT_LD_DELEGATE_INVOKE_IMPL);
2031 td->last_ins->data [0] = csignature->param_count + 1;
2035 if (op >= 0) {
2036 interp_add_ins (td, op);
2038 if (op == MINT_LDLEN) {
2039 #ifdef MONO_BIG_ARRAYS
2040 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
2041 #else
2042 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
2043 #endif
2045 if (op == MINT_LDELEMA || op == MINT_LDELEMA_TC) {
2046 td->last_ins->data [0] = get_data_item_index (td, m_class_get_element_class (target_method->klass));
2047 td->last_ins->data [1] = 1 + m_class_get_rank (target_method->klass);
2050 if (op == MINT_CALLRUN) {
2051 td->last_ins->data [0] = get_data_item_index (td, target_method);
2052 td->last_ins->data [1] = get_data_item_index (td, mono_method_signature_internal (target_method));
2054 } else if (!calli && !is_virtual && jit_call_supported (target_method, csignature)) {
2055 interp_add_ins (td, MINT_JIT_CALL);
2056 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2057 mono_error_assert_ok (error);
2058 } else {
2059 #ifndef MONO_ARCH_HAS_NO_PROPER_MONOCTX
2060 /* Try using fast icall path for simple signatures */
2061 if (native && !method->dynamic)
2062 op = interp_icall_op_for_sig (csignature);
2063 #endif
2064 if (csignature->call_convention == MONO_CALL_VARARG)
2065 interp_add_ins (td, MINT_CALL_VARARG);
2066 else if (calli)
2067 interp_add_ins (td, native ? ((op != -1) ? MINT_CALLI_NAT_FAST : MINT_CALLI_NAT) : MINT_CALLI);
2068 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass))
2069 interp_add_ins (td, is_void ? MINT_VCALLVIRT_FAST : MINT_CALLVIRT_FAST);
2070 else if (is_virtual)
2071 interp_add_ins (td, is_void ? MINT_VCALLVIRT : MINT_CALLVIRT);
2072 else
2073 interp_add_ins (td, is_void ? MINT_VCALL : MINT_CALL);
2075 if (calli) {
2076 td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
2077 if (op != -1)
2078 td->last_ins->data [1] = op;
2079 } else {
2080 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2081 return_val_if_nok (error, FALSE);
2082 if (csignature->call_convention == MONO_CALL_VARARG)
2083 td->last_ins->data [1] = get_data_item_index (td, (void *)csignature);
2084 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass)) {
2085 /* FIXME Use fastpath also for MBRO. Asserts in mono_method_get_vtable_slot */
2086 if (mono_class_is_interface (target_method->klass))
2087 td->last_ins->data [1] = -2 * MONO_IMT_SIZE + mono_method_get_imt_slot (target_method);
2088 else
2089 td->last_ins->data [1] = mono_method_get_vtable_slot (target_method);
2093 td->ip += 5;
2094 if (vt_stack_used != 0 || vt_res_size != 0) {
2095 interp_add_ins (td, MINT_VTRESULT);
2096 td->last_ins->data [0] = vt_res_size;
2097 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
2098 td->vt_sp -= vt_stack_used;
2101 return TRUE;
2104 static MonoClassField *
2105 interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, MonoGenericContext *generic_context, MonoError *error)
2107 MonoClassField *field = NULL;
2108 if (method->wrapper_type != MONO_WRAPPER_NONE) {
2109 field = (MonoClassField *) mono_method_get_wrapper_data (method, token);
2110 *klass = field->parent;
2112 mono_class_setup_fields (field->parent);
2113 } else {
2114 field = mono_field_from_token_checked (m_class_get_image (method->klass), token, klass, generic_context, error);
2115 return_val_if_nok (error, NULL);
2118 if (!method->skip_visibility && !mono_method_can_access_field (method, field)) {
2119 char *method_fname = mono_method_full_name (method, TRUE);
2120 char *field_fname = mono_field_full_name (field);
2121 mono_error_set_generic_error (error, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
2122 g_free (method_fname);
2123 g_free (field_fname);
2124 return NULL;
2127 return field;
2130 static InterpBasicBlock*
2131 get_bb (TransformData *td, InterpBasicBlock *cbb, unsigned char *ip)
2133 int offset = ip - td->il_code;
2134 InterpBasicBlock *bb = td->offset_to_bb [offset];
2136 if (!bb) {
2137 bb = (InterpBasicBlock*)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock));
2138 bb->ip = ip;
2139 td->offset_to_bb [offset] = bb;
2141 td->basic_blocks = g_list_append_mempool (td->mempool, td->basic_blocks, bb);
2144 if (cbb)
2145 bb->preds = g_slist_prepend_mempool (td->mempool, bb->preds, cbb);
2146 return bb;
2150 * get_basic_blocks:
2152 * Compute the set of IL level basic blocks.
2154 static void
2155 get_basic_blocks (TransformData *td)
2157 guint8 *start = (guint8*)td->il_code;
2158 guint8 *end = (guint8*)td->il_code + td->code_size;
2159 guint8 *ip = start;
2160 unsigned char *target;
2161 int i;
2162 guint cli_addr;
2163 const MonoOpcode *opcode;
2164 InterpBasicBlock *cbb;
2166 td->offset_to_bb = (InterpBasicBlock**)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * (end - start + 1));
2167 td->entry_bb = cbb = get_bb (td, NULL, start);
2169 while (ip < end) {
2170 cli_addr = ip - start;
2171 td->offset_to_bb [cli_addr] = cbb;
2172 i = mono_opcode_value ((const guint8 **)&ip, end);
2173 opcode = &mono_opcodes [i];
2174 switch (opcode->argument) {
2175 case MonoInlineNone:
2176 ip++;
2177 break;
2178 case MonoInlineString:
2179 case MonoInlineType:
2180 case MonoInlineField:
2181 case MonoInlineMethod:
2182 case MonoInlineTok:
2183 case MonoInlineSig:
2184 case MonoShortInlineR:
2185 case MonoInlineI:
2186 ip += 5;
2187 break;
2188 case MonoInlineVar:
2189 ip += 3;
2190 break;
2191 case MonoShortInlineVar:
2192 case MonoShortInlineI:
2193 ip += 2;
2194 break;
2195 case MonoShortInlineBrTarget:
2196 target = start + cli_addr + 2 + (signed char)ip [1];
2197 get_bb (td, cbb, target);
2198 ip += 2;
2199 cbb = get_bb (td, cbb, ip);
2200 break;
2201 case MonoInlineBrTarget:
2202 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
2203 get_bb (td, cbb, target);
2204 ip += 5;
2205 cbb = get_bb (td, cbb, ip);
2206 break;
2207 case MonoInlineSwitch: {
2208 guint32 n = read32 (ip + 1);
2209 guint32 j;
2210 ip += 5;
2211 cli_addr += 5 + 4 * n;
2212 target = start + cli_addr;
2213 get_bb (td, cbb, target);
2215 for (j = 0; j < n; ++j) {
2216 target = start + cli_addr + (gint32)read32 (ip);
2217 get_bb (td, cbb, target);
2218 ip += 4;
2220 cbb = get_bb (td, cbb, ip);
2221 break;
2223 case MonoInlineR:
2224 case MonoInlineI8:
2225 ip += 9;
2226 break;
2227 default:
2228 g_assert_not_reached ();
2231 if (i == CEE_THROW)
2232 cbb = get_bb (td, NULL, ip);
2236 static void
2237 interp_save_debug_info (InterpMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers)
2239 MonoDebugMethodJitInfo *dinfo;
2240 int i;
2242 if (!mono_debug_enabled ())
2243 return;
2246 * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
2249 dinfo = g_new0 (MonoDebugMethodJitInfo, 1);
2250 dinfo->num_params = rtm->param_count;
2251 dinfo->params = g_new0 (MonoDebugVarInfo, dinfo->num_params);
2252 dinfo->num_locals = header->num_locals;
2253 dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals);
2254 dinfo->code_start = (guint8*)rtm->code;
2255 dinfo->code_size = td->new_code_end - td->new_code;
2256 dinfo->epilogue_begin = 0;
2257 dinfo->has_var_info = TRUE;
2258 dinfo->num_line_numbers = line_numbers->len;
2259 dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers);
2261 for (i = 0; i < dinfo->num_params; i++) {
2262 MonoDebugVarInfo *var = &dinfo->params [i];
2263 var->type = rtm->param_types [i];
2265 for (i = 0; i < dinfo->num_locals; i++) {
2266 MonoDebugVarInfo *var = &dinfo->locals [i];
2267 var->type = mono_metadata_type_dup (NULL, header->locals [i]);
2270 for (i = 0; i < dinfo->num_line_numbers; i++)
2271 dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i);
2272 mono_debug_add_method (rtm->method, dinfo, rtm->domain);
2274 mono_debug_free_method_jit_info (dinfo);
2277 /* Same as the code in seq-points.c */
2278 static void
2279 insert_pred_seq_point (SeqPoint *last_sp, SeqPoint *sp, GSList **next)
2281 GSList *l;
2282 int src_index = last_sp->next_offset;
2283 int dst_index = sp->next_offset;
2285 /* bb->in_bb might contain duplicates */
2286 for (l = next [src_index]; l; l = l->next)
2287 if (GPOINTER_TO_UINT (l->data) == dst_index)
2288 break;
2289 if (!l)
2290 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
2293 static void
2294 recursively_make_pred_seq_points (TransformData *td, InterpBasicBlock *bb)
2296 SeqPoint ** const MONO_SEQ_SEEN_LOOP = (SeqPoint**)GINT_TO_POINTER(-1);
2297 GSList *l;
2299 GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
2300 GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
2302 // Insert/remove sentinel into the memoize table to detect loops containing bb
2303 bb->pred_seq_points = MONO_SEQ_SEEN_LOOP;
2305 for (l = bb->preds; l; l = l->next) {
2306 InterpBasicBlock *in_bb = (InterpBasicBlock*)l->data;
2308 // This bb has the last seq point, append it and continue
2309 if (in_bb->last_seq_point != NULL) {
2310 predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
2311 continue;
2314 // We've looped or handled this before, exit early.
2315 // No last sequence points to find.
2316 if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
2317 continue;
2319 // Take sequence points from incoming basic blocks
2321 if (in_bb == td->entry_bb)
2322 continue;
2324 if (in_bb->pred_seq_points == NULL)
2325 recursively_make_pred_seq_points (td, in_bb);
2327 // Union sequence points with incoming bb's
2328 for (int i=0; i < in_bb->num_pred_seq_points; i++) {
2329 if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
2330 g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
2331 g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
2334 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
2337 g_hash_table_destroy (seen);
2339 if (predecessors->len != 0) {
2340 bb->pred_seq_points = (SeqPoint**)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint *) * predecessors->len);
2341 bb->num_pred_seq_points = predecessors->len;
2343 for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
2344 bb->pred_seq_points [newer] = (SeqPoint*)g_array_index (predecessors, gpointer, newer);
2348 g_array_free (predecessors, TRUE);
2351 static void
2352 collect_pred_seq_points (TransformData *td, InterpBasicBlock *bb, SeqPoint *seqp, GSList **next)
2354 // Doesn't have a last sequence point, must find from incoming basic blocks
2355 if (bb->pred_seq_points == NULL && bb != td->entry_bb)
2356 recursively_make_pred_seq_points (td, bb);
2358 for (int i = 0; i < bb->num_pred_seq_points; i++)
2359 insert_pred_seq_point (bb->pred_seq_points [i], seqp, next);
2361 return;
2364 static void
2365 save_seq_points (TransformData *td, MonoJitInfo *jinfo)
2367 GByteArray *array;
2368 int i, seq_info_size;
2369 MonoSeqPointInfo *info;
2370 GSList **next = NULL;
2371 GList *bblist;
2373 if (!td->gen_sdb_seq_points)
2374 return;
2377 * For each sequence point, compute the list of sequence points immediately
2378 * following it, this is needed to implement 'step over' in the debugger agent.
2379 * Similar to the code in mono_save_seq_point_info ().
2381 for (i = 0; i < td->seq_points->len; ++i) {
2382 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2384 /* Store the seq point index here temporarily */
2385 sp->next_offset = i;
2387 next = (GSList**)mono_mempool_alloc0 (td->mempool, sizeof (GList*) * td->seq_points->len);
2388 for (bblist = td->basic_blocks; bblist; bblist = bblist->next) {
2389 InterpBasicBlock *bb = (InterpBasicBlock*)bblist->data;
2391 GSList *bb_seq_points = g_slist_reverse (bb->seq_points);
2392 SeqPoint *last = NULL;
2393 for (GSList *l = bb_seq_points; l; l = l->next) {
2394 SeqPoint *sp = (SeqPoint*)l->data;
2396 if (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET)
2397 /* Used to implement method entry/exit events */
2398 continue;
2400 if (last != NULL) {
2401 /* Link with the previous seq point in the same bb */
2402 next [last->next_offset] = g_slist_append_mempool (td->mempool, next [last->next_offset], GINT_TO_POINTER (sp->next_offset));
2403 } else {
2404 /* Link with the last bb in the previous bblocks */
2405 collect_pred_seq_points (td, bb, sp, next);
2407 last = sp;
2411 /* Serialize the seq points into a byte array */
2412 array = g_byte_array_new ();
2413 SeqPoint zero_seq_point = {0};
2414 SeqPoint* last_seq_point = &zero_seq_point;
2415 for (i = 0; i < td->seq_points->len; ++i) {
2416 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2418 sp->next_offset = 0;
2419 if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next [i], TRUE))
2420 last_seq_point = sp;
2423 if (td->verbose_level) {
2424 g_print ("\nSEQ POINT MAP FOR %s: \n", td->method->name);
2426 for (i = 0; i < td->seq_points->len; ++i) {
2427 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2428 GSList *l;
2430 if (!next [i])
2431 continue;
2433 g_print ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
2434 for (l = next [i]; l; l = l->next) {
2435 int next_index = GPOINTER_TO_UINT (l->data);
2436 g_print (" IL0x%x", ((SeqPoint*)g_ptr_array_index (td->seq_points, next_index))->il_offset);
2438 g_print ("\n");
2442 info = mono_seq_point_info_new (array->len, TRUE, array->data, TRUE, &seq_info_size);
2443 mono_atomic_fetch_add_i32 (&mono_jit_stats.allocated_seq_points_size, seq_info_size);
2445 g_byte_array_free (array, TRUE);
2447 jinfo->seq_points = info;
2450 #define BARRIER_IF_VOLATILE(td) \
2451 do { \
2452 if (volatile_) { \
2453 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER); \
2454 volatile_ = FALSE; \
2456 } while (0)
2458 #define INLINE_FAILURE \
2459 do { \
2460 if (td->method != method) \
2461 goto exit; \
2462 } while (0)
2464 static void
2465 interp_method_compute_offsets (InterpMethod *imethod, MonoMethodSignature *signature, MonoMethodHeader *header)
2467 int i, offset, size, align;
2469 imethod->local_offsets = (guint32*)g_malloc (header->num_locals * sizeof(guint32));
2470 offset = 0;
2471 for (i = 0; i < header->num_locals; ++i) {
2472 size = mono_type_size (header->locals [i], &align);
2473 offset += align - 1;
2474 offset &= ~(align - 1);
2475 imethod->local_offsets [i] = offset;
2476 offset += size;
2478 offset = (offset + 7) & ~7;
2480 imethod->exvar_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32));
2481 for (i = 0; i < header->num_clauses; i++) {
2482 imethod->exvar_offsets [i] = offset;
2483 offset += sizeof (MonoObject*);
2485 offset = (offset + 7) & ~7;
2487 imethod->locals_size = offset;
2488 g_assert (imethod->locals_size < 65536);
2491 static MonoType*
2492 get_arg_type (MonoMethodSignature *signature, int arg_n)
2494 if (signature->hasthis && arg_n == 0)
2495 return mono_get_object_type ();
2496 return signature->params [arg_n - !!signature->hasthis];
2499 static void
2500 init_bb_start (TransformData *td, MonoMethodHeader *header)
2502 const unsigned char *ip, *end;
2503 const MonoOpcode *opcode;
2504 int offset, i, in, backwards;
2506 /* intern the strings in the method. */
2507 ip = header->code;
2508 end = ip + header->code_size;
2510 td->is_bb_start [0] = 1;
2511 while (ip < end) {
2512 in = *ip;
2513 if (in == 0xfe) {
2514 ip++;
2515 in = *ip + 256;
2517 else if (in == 0xf0) {
2518 ip++;
2519 in = *ip + MONO_CEE_MONO_ICALL;
2521 opcode = &mono_opcodes [in];
2522 switch (opcode->argument) {
2523 case MonoInlineNone:
2524 ++ip;
2525 break;
2526 case MonoInlineString:
2527 ip += 5;
2528 break;
2529 case MonoInlineType:
2530 ip += 5;
2531 break;
2532 case MonoInlineMethod:
2533 ip += 5;
2534 break;
2535 case MonoInlineField:
2536 case MonoInlineSig:
2537 case MonoInlineI:
2538 case MonoInlineTok:
2539 case MonoShortInlineR:
2540 ip += 5;
2541 break;
2542 case MonoInlineBrTarget:
2543 offset = read32 (ip + 1);
2544 ip += 5;
2545 backwards = offset < 0;
2546 offset += ip - header->code;
2547 g_assert (offset >= 0 && offset < header->code_size);
2548 td->is_bb_start [offset] |= backwards ? 2 : 1;
2549 break;
2550 case MonoShortInlineBrTarget:
2551 offset = ((gint8 *)ip) [1];
2552 ip += 2;
2553 backwards = offset < 0;
2554 offset += ip - header->code;
2555 g_assert (offset >= 0 && offset < header->code_size);
2556 td->is_bb_start [offset] |= backwards ? 2 : 1;
2557 break;
2558 case MonoInlineVar:
2559 ip += 3;
2560 break;
2561 case MonoShortInlineVar:
2562 case MonoShortInlineI:
2563 ip += 2;
2564 break;
2565 case MonoInlineSwitch: {
2566 guint32 n;
2567 const unsigned char *next_ip;
2568 ++ip;
2569 n = read32 (ip);
2570 ip += 4;
2571 next_ip = ip + 4 * n;
2572 for (i = 0; i < n; i++) {
2573 offset = read32 (ip);
2574 backwards = offset < 0;
2575 offset += next_ip - header->code;
2576 g_assert (offset >= 0 && offset < header->code_size);
2577 td->is_bb_start [offset] |= backwards ? 2 : 1;
2578 ip += 4;
2580 break;
2582 case MonoInlineR:
2583 case MonoInlineI8:
2584 ip += 9;
2585 break;
2586 default:
2587 g_assert_not_reached ();
2592 #ifdef NO_UNALIGNED_ACCESS
2593 static int
2594 get_unaligned_opcode (int opcode)
2596 switch (opcode) {
2597 case MINT_LDFLD_I8:
2598 return MINT_LDFLD_I8_UNALIGNED;
2599 case MINT_LDFLD_R8:
2600 return MINT_LDFLD_R8_UNALIGNED;
2601 case MINT_STFLD_I8:
2602 return MINT_STFLD_I8_UNALIGNED;
2603 case MINT_STFLD_R8:
2604 return MINT_STFLD_R8_UNALIGNED;
2605 default:
2606 g_assert_not_reached ();
2608 return -1;
2610 #endif
2612 static void
2613 interp_handle_isinst (TransformData *td, MonoClass *klass, gboolean isinst_instr)
2615 /* Follow the logic from jit's handle_isinst */
2616 if (!mono_class_has_variant_generic_params (klass)) {
2617 if (mono_class_is_interface (klass))
2618 interp_add_ins (td, isinst_instr ? MINT_ISINST_INTERFACE : MINT_CASTCLASS_INTERFACE);
2619 else if (!mono_class_is_marshalbyref (klass) && m_class_get_rank (klass) == 0 && !mono_class_is_nullable (klass))
2620 interp_add_ins (td, isinst_instr ? MINT_ISINST_COMMON : MINT_CASTCLASS_COMMON);
2621 else
2622 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2623 } else {
2624 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2626 td->last_ins->data [0] = get_data_item_index (td, klass);
2627 td->ip += 5;
2630 static void
2631 interp_emit_ldobj (TransformData *td, MonoClass *klass)
2633 int mt = mint_type (m_class_get_byval_arg (klass));
2634 int size;
2636 if (mt == MINT_TYPE_VT) {
2637 interp_add_ins (td, MINT_LDOBJ_VT);
2638 size = mono_class_value_size (klass, NULL);
2639 WRITE32_INS (td->last_ins, 0, &size);
2640 PUSH_VT (td, size);
2641 } else {
2642 int opcode = interp_get_ldind_for_mt (mt);
2643 interp_add_ins (td, opcode);
2646 SET_TYPE (td->sp - 1, stack_type [mt], klass);
2649 static void
2650 interp_emit_stobj (TransformData *td, MonoClass *klass)
2652 int mt = mint_type (m_class_get_byval_arg (klass));
2654 if (mt == MINT_TYPE_VT) {
2655 int size;
2656 interp_add_ins (td, MINT_STOBJ_VT);
2657 td->last_ins->data [0] = get_data_item_index(td, klass);
2658 size = mono_class_value_size (klass, NULL);
2659 POP_VT (td, size);
2660 } else {
2661 int opcode;
2662 switch (mt) {
2663 case MINT_TYPE_I1:
2664 case MINT_TYPE_U1:
2665 opcode = MINT_STIND_I1;
2666 break;
2667 case MINT_TYPE_I2:
2668 case MINT_TYPE_U2:
2669 opcode = MINT_STIND_I2;
2670 break;
2671 case MINT_TYPE_I4:
2672 opcode = MINT_STIND_I4;
2673 break;
2674 case MINT_TYPE_I8:
2675 opcode = MINT_STIND_I8;
2676 break;
2677 case MINT_TYPE_R4:
2678 opcode = MINT_STIND_R4;
2679 break;
2680 case MINT_TYPE_R8:
2681 opcode = MINT_STIND_R8;
2682 break;
2683 case MINT_TYPE_O:
2684 opcode = MINT_STIND_REF;
2685 break;
2686 default: g_assert_not_reached (); break;
2688 interp_add_ins (td, opcode);
2690 td->sp -= 2;
2693 static void
2694 interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, gboolean is_load, MonoError *error)
2696 MonoDomain *domain = td->rtm->domain;
2697 // Initialize the offset for the field
2698 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
2699 return_if_nok (error);
2701 if (mono_class_field_is_special_static (field)) {
2702 guint32 offset;
2704 mono_domain_lock (domain);
2705 g_assert (domain->special_static_fields);
2706 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
2707 mono_domain_unlock (domain);
2708 g_assert (offset);
2710 // Offset is SpecialStaticOffset
2711 if ((offset & 0x80000000) == 0 && mt != MINT_TYPE_VT) {
2712 // This field is thread static
2713 interp_add_ins (td, (is_load ? MINT_LDTSFLD_I1 : MINT_STTSFLD_I1) + mt);
2714 WRITE32_INS(td->last_ins, 0, &offset);
2715 } else {
2716 if (mt == MINT_TYPE_VT) {
2717 interp_add_ins (td, is_load ? MINT_LDSSFLD_VT : MINT_STSSFLD_VT);
2718 WRITE32_INS(td->last_ins, 0, &offset);
2720 int size = mono_class_value_size (field_class, NULL);
2721 WRITE32_INS(td->last_ins, 2, &size);
2722 } else {
2723 interp_add_ins (td, is_load ? MINT_LDSSFLD : MINT_STSSFLD);
2724 td->last_ins->data [0] = get_data_item_index (td, field);
2725 WRITE32_INS(td->last_ins, 1, &offset);
2728 } else {
2729 if (is_load)
2730 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSFLD_VT : (MINT_LDSFLD_I1 + mt - MINT_TYPE_I1));
2731 else
2732 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1));
2734 td->last_ins->data [0] = get_data_item_index (td, vtable);
2735 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
2737 if (mt == MINT_TYPE_VT) {
2738 int size = mono_class_value_size (field_class, NULL);
2739 WRITE32_INS(td->last_ins, 2, &size);
2744 static gboolean
2745 generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error)
2747 int target;
2748 int offset, mt, i, i32;
2749 guint32 token;
2750 int in_offset;
2751 const unsigned char *end;
2752 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
2753 gboolean sym_seq_points = FALSE;
2754 MonoBitSet *seq_point_locs = NULL;
2755 gboolean readonly = FALSE;
2756 gboolean volatile_ = FALSE;
2757 MonoClass *constrained_class = NULL;
2758 MonoClass *klass;
2759 MonoClassField *field;
2760 MonoImage *image = m_class_get_image (method->klass);
2761 InterpMethod *rtm = td->rtm;
2762 MonoDomain *domain = rtm->domain;
2763 MonoMethodSignature *signature = mono_method_signature_internal (method);
2764 gboolean ret = TRUE;
2765 gboolean emitted_funccall_seq_point = FALSE;
2766 guint32 *arg_offsets = NULL;
2767 InterpInst *last_seq_point = NULL;
2769 original_bb = bb = mono_basic_block_split (method, error, header);
2770 goto_if_nok (error, exit);
2771 g_assert (bb);
2773 td->il_code = header->code;
2774 td->in_start = td->ip = header->code;
2775 end = td->ip + header->code_size;
2776 td->stack_state = (StackInfo**)g_malloc0(header->code_size * sizeof(StackInfo *));
2777 td->stack_height = (int*)g_malloc(header->code_size * sizeof(int));
2778 td->vt_stack_size = (int*)g_malloc(header->code_size * sizeof(int));
2779 td->clause_indexes = (int*)g_malloc (header->code_size * sizeof (int));
2780 td->is_bb_start = (guint8*)g_malloc0(header->code_size);
2782 init_bb_start (td, header);
2784 for (i = 0; i < header->code_size; i++) {
2785 td->stack_height [i] = -1;
2786 td->clause_indexes [i] = -1;
2789 for (i = 0; i < header->num_clauses; i++) {
2790 MonoExceptionClause *c = header->clauses + i;
2791 td->stack_height [c->handler_offset] = 0;
2792 td->vt_stack_size [c->handler_offset] = 0;
2793 td->is_bb_start [c->handler_offset] = 1;
2795 td->stack_height [c->handler_offset] = 1;
2796 td->stack_state [c->handler_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
2797 td->stack_state [c->handler_offset][0].type = STACK_TYPE_O;
2798 td->stack_state [c->handler_offset][0].klass = NULL; /*FIX*/
2800 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER) {
2801 td->stack_height [c->data.filter_offset] = 0;
2802 td->vt_stack_size [c->data.filter_offset] = 0;
2803 td->is_bb_start [c->data.filter_offset] = 1;
2805 td->stack_height [c->data.filter_offset] = 1;
2806 td->stack_state [c->data.filter_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
2807 td->stack_state [c->data.filter_offset][0].type = STACK_TYPE_O;
2808 td->stack_state [c->data.filter_offset][0].klass = NULL; /*FIX*/
2811 for (int j = c->handler_offset; j < c->handler_offset + c->handler_len; ++j) {
2812 if (td->clause_indexes [j] == -1)
2813 td->clause_indexes [j] = i;
2817 if (td->gen_sdb_seq_points && td->method == method) {
2818 MonoDebugMethodInfo *minfo;
2819 get_basic_blocks (td);
2821 minfo = mono_debug_lookup_method (method);
2823 if (minfo) {
2824 MonoSymSeqPoint *sps;
2825 int i, n_il_offsets;
2827 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
2828 // FIXME: Free
2829 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
2830 sym_seq_points = TRUE;
2832 for (i = 0; i < n_il_offsets; ++i) {
2833 if (sps [i].il_offset < header->code_size)
2834 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
2836 g_free (sps);
2838 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
2839 if (asyncMethod) {
2840 for (i = 0; asyncMethod != NULL && i < asyncMethod->num_awaits; i++) {
2841 mono_bitset_set_fast (seq_point_locs, asyncMethod->resume_offsets [i]);
2842 mono_bitset_set_fast (seq_point_locs, asyncMethod->yield_offsets [i]);
2844 mono_debug_free_method_async_debug_info (asyncMethod);
2846 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (m_class_get_image (method->klass))) {
2847 /* Methods without line number info like auto-generated property accessors */
2848 seq_point_locs = mono_bitset_new (header->code_size, 0);
2849 sym_seq_points = TRUE;
2853 if (sym_seq_points) {
2854 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
2855 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY;
2858 if (mono_debugger_method_has_breakpoint (method))
2859 interp_add_ins (td, MINT_BREAKPOINT);
2861 if (method == td->method) {
2862 if (td->verbose_level) {
2863 char *tmp = mono_disasm_code (NULL, method, td->ip, end);
2864 char *name = mono_method_full_name (method, TRUE);
2865 g_print ("Method %s, original code:\n", name);
2866 g_print ("%s\n", tmp);
2867 g_free (tmp);
2868 g_free (name);
2871 if (header->num_locals && header->init_locals)
2872 interp_add_ins (td, MINT_INITLOCALS);
2874 if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER)
2875 interp_add_ins (td, MINT_PROF_ENTER);
2877 /* safepoint is required on method entry */
2878 if (mono_threads_are_safepoints_enabled ())
2879 interp_add_ins (td, MINT_SAFEPOINT);
2880 } else {
2881 int offset;
2882 arg_offsets = (guint32*) g_malloc ((!!signature->hasthis + signature->param_count) * sizeof (guint32));
2883 /* Allocate locals to store inlined method args from stack */
2884 for (i = signature->param_count - 1; i >= 0; i--) {
2885 offset = create_interp_local (td, signature->params [i]);
2886 arg_offsets [i + !!signature->hasthis] = offset;
2887 store_local_general (td, offset, signature->params [i]);
2890 if (signature->hasthis) {
2892 * If this is value type, it is passed by address and not by value.
2893 * FIXME We should use MINT_TYPE_P instead of MINT_TYPE_O
2895 MonoType *type = mono_get_object_type ();
2896 offset = create_interp_local (td, type);
2897 arg_offsets [0] = offset;
2898 store_local_general (td, offset, type);
2902 while (td->ip < end) {
2903 g_assert (td->sp >= td->stack);
2904 g_assert (td->vt_sp < 0x10000000);
2905 in_offset = td->ip - header->code;
2906 td->in_start = td->ip;
2907 InterpInst *prev_last_ins = td->last_ins;
2909 if (td->stack_height [in_offset] >= 0) {
2910 g_assert (td->is_bb_start [in_offset]);
2911 if (td->stack_height [in_offset] > 0)
2912 memcpy (td->stack, td->stack_state [in_offset], td->stack_height [in_offset] * sizeof(td->stack [0]));
2913 td->sp = td->stack + td->stack_height [in_offset];
2914 td->vt_sp = td->vt_stack_size [in_offset];
2917 if (in_offset == bb->end)
2918 bb = bb->next;
2920 if (bb->dead) {
2921 int op_size = mono_opcode_size (td->ip, end);
2922 g_assert (op_size > 0); /* The BB formation pass must catch all bad ops */
2924 if (td->verbose_level > 1)
2925 g_print ("SKIPPING DEAD OP at %x\n", in_offset);
2927 td->ip += op_size;
2928 continue;
2931 if (td->verbose_level > 1) {
2932 g_print ("IL_%04lx %s %-10s, sp %ld, %s %-12s vt_sp %u (max %u)\n",
2933 td->ip - td->il_code,
2934 td->is_bb_start [td->ip - td->il_code] == 3 ? "<>" :
2935 td->is_bb_start [td->ip - td->il_code] == 2 ? "< " :
2936 td->is_bb_start [td->ip - td->il_code] == 1 ? " >" : " ",
2937 mono_opcode_name (*td->ip), td->sp - td->stack,
2938 td->sp > td->stack ? stack_type_string [td->sp [-1].type] : " ",
2939 (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)) : "",
2940 td->vt_sp, td->max_vt_sp);
2943 if (sym_seq_points && mono_bitset_test_fast (seq_point_locs, td->ip - header->code)) {
2944 InterpBasicBlock *cbb = td->offset_to_bb [td->ip - header->code];
2945 g_assert (cbb);
2948 * Make methods interruptable at the beginning, and at the targets of
2949 * backward branches.
2951 if (in_offset == 0 || g_slist_length (cbb->preds) > 1)
2952 interp_add_ins (td, MINT_SDB_INTR_LOC);
2954 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
2957 if (td->is_bb_start [in_offset]) {
2958 int index = td->clause_indexes [in_offset];
2959 if (index != -1) {
2960 MonoExceptionClause *clause = &header->clauses [index];
2961 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
2962 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) &&
2963 in_offset == clause->handler_offset)
2964 interp_add_ins (td, MINT_START_ABORT_PROT);
2968 switch (*td->ip) {
2969 case CEE_NOP:
2970 /* lose it */
2971 emitted_funccall_seq_point = FALSE;
2972 ++td->ip;
2973 break;
2974 case CEE_BREAK:
2975 SIMPLE_OP(td, MINT_BREAK);
2976 break;
2977 case CEE_LDARG_0:
2978 case CEE_LDARG_1:
2979 case CEE_LDARG_2:
2980 case CEE_LDARG_3: {
2981 int arg_n = *td->ip - CEE_LDARG_0;
2982 if (td->method == method)
2983 load_arg (td, arg_n);
2984 else
2985 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
2986 ++td->ip;
2987 break;
2989 case CEE_LDLOC_0:
2990 case CEE_LDLOC_1:
2991 case CEE_LDLOC_2:
2992 case CEE_LDLOC_3:
2993 load_local (td, *td->ip - CEE_LDLOC_0);
2994 ++td->ip;
2995 break;
2996 case CEE_STLOC_0:
2997 case CEE_STLOC_1:
2998 case CEE_STLOC_2:
2999 case CEE_STLOC_3:
3000 store_local (td, *td->ip - CEE_STLOC_0);
3001 ++td->ip;
3002 break;
3003 case CEE_LDARG_S: {
3004 int arg_n = ((guint8 *)td->ip)[1];
3005 if (td->method == method)
3006 load_arg (td, arg_n);
3007 else
3008 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
3009 td->ip += 2;
3010 break;
3012 case CEE_LDARGA_S: {
3013 /* NOTE: n includes this */
3014 INLINE_FAILURE; // probably uncommon
3015 int n = ((guint8 *) td->ip) [1];
3017 get_arg_type_exact (td, n, &mt);
3018 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
3019 td->last_ins->data [0] = n;
3020 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3021 td->ip += 2;
3022 break;
3024 case CEE_STARG_S: {
3025 int arg_n = ((guint8 *)td->ip)[1];
3026 if (td->method == method)
3027 store_arg (td, arg_n);
3028 else
3029 store_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
3030 td->ip += 2;
3031 break;
3033 case CEE_LDLOC_S:
3034 load_local (td, ((guint8 *)td->ip)[1]);
3035 td->ip += 2;
3036 break;
3037 case CEE_LDLOCA_S:
3038 interp_add_ins (td, MINT_LDLOCA_S);
3039 td->last_ins->data [0] = td->rtm->local_offsets [((guint8 *)td->ip)[1]];
3040 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3041 td->ip += 2;
3042 break;
3043 case CEE_STLOC_S:
3044 store_local (td, ((guint8 *)td->ip)[1]);
3045 td->ip += 2;
3046 break;
3047 case CEE_LDNULL:
3048 SIMPLE_OP(td, MINT_LDNULL);
3049 PUSH_TYPE(td, STACK_TYPE_O, NULL);
3050 break;
3051 case CEE_LDC_I4_M1:
3052 SIMPLE_OP(td, MINT_LDC_I4_M1);
3053 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3054 break;
3055 case CEE_LDC_I4_0:
3056 if (!td->is_bb_start[td->ip + 1 - td->il_code] && td->ip [1] == 0xfe && td->ip [2] == CEE_CEQ &&
3057 td->sp > td->stack && td->sp [-1].type == STACK_TYPE_I4) {
3058 SIMPLE_OP(td, MINT_CEQ0_I4);
3059 td->ip += 2;
3060 } else {
3061 SIMPLE_OP(td, MINT_LDC_I4_0);
3062 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3064 break;
3065 case CEE_LDC_I4_1:
3066 if (!td->is_bb_start[td->ip + 1 - td->il_code] &&
3067 (td->ip [1] == CEE_ADD || td->ip [1] == CEE_SUB) && td->sp [-1].type == STACK_TYPE_I4) {
3068 interp_add_ins (td, td->ip [1] == CEE_ADD ? MINT_ADD1_I4 : MINT_SUB1_I4);
3069 td->ip += 2;
3070 } else {
3071 SIMPLE_OP(td, MINT_LDC_I4_1);
3072 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3074 break;
3075 case CEE_LDC_I4_2:
3076 case CEE_LDC_I4_3:
3077 case CEE_LDC_I4_4:
3078 case CEE_LDC_I4_5:
3079 case CEE_LDC_I4_6:
3080 case CEE_LDC_I4_7:
3081 case CEE_LDC_I4_8:
3082 SIMPLE_OP(td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0);
3083 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3084 break;
3085 case CEE_LDC_I4_S:
3086 interp_add_ins (td, MINT_LDC_I4_S);
3087 td->last_ins->data [0] = ((gint8 *) td->ip) [1];
3088 td->ip += 2;
3089 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3090 break;
3091 case CEE_LDC_I4:
3092 i32 = read32 (td->ip + 1);
3093 interp_add_ins (td, MINT_LDC_I4);
3094 WRITE32_INS (td->last_ins, 0, &i32);
3095 td->ip += 5;
3096 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3097 break;
3098 case CEE_LDC_I8: {
3099 gint64 val = read64 (td->ip + 1);
3100 interp_add_ins (td, MINT_LDC_I8);
3101 WRITE64_INS (td->last_ins, 0, &val);
3102 td->ip += 9;
3103 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I8);
3104 break;
3106 case CEE_LDC_R4: {
3107 float val;
3108 readr4 (td->ip + 1, &val);
3109 interp_add_ins (td, MINT_LDC_R4);
3110 WRITE32_INS (td->last_ins, 0, &val);
3111 td->ip += 5;
3112 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R4);
3113 break;
3115 case CEE_LDC_R8: {
3116 double val;
3117 readr8 (td->ip + 1, &val);
3118 interp_add_ins (td, MINT_LDC_R8);
3119 WRITE64_INS (td->last_ins, 0, &val);
3120 td->ip += 9;
3121 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R8);
3122 break;
3124 case CEE_DUP: {
3125 int type = td->sp [-1].type;
3126 MonoClass *klass = td->sp [-1].klass;
3127 if (td->sp [-1].type == STACK_TYPE_VT) {
3128 gint32 size = mono_class_value_size (klass, NULL);
3129 PUSH_VT(td, size);
3130 interp_add_ins (td, MINT_DUP_VT);
3131 WRITE32_INS (td->last_ins, 0, &size);
3132 td->ip ++;
3133 } else
3134 SIMPLE_OP(td, MINT_DUP);
3135 PUSH_TYPE(td, type, klass);
3136 break;
3138 case CEE_POP:
3139 CHECK_STACK(td, 1);
3140 SIMPLE_OP(td, MINT_POP);
3141 td->last_ins->data [0] = 0;
3142 if (td->sp [-1].type == STACK_TYPE_VT) {
3143 int size = mono_class_value_size (td->sp [-1].klass, NULL);
3144 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
3145 interp_add_ins (td, MINT_VTRESULT);
3146 td->last_ins->data [0] = 0;
3147 WRITE32_INS (td->last_ins, 1, &size);
3148 td->vt_sp -= size;
3150 --td->sp;
3151 break;
3152 case CEE_JMP: {
3153 MonoMethod *m;
3154 INLINE_FAILURE;
3155 if (td->sp > td->stack)
3156 g_warning ("CEE_JMP: stack must be empty");
3157 token = read32 (td->ip + 1);
3158 m = mono_get_method_checked (image, token, NULL, generic_context, error);
3159 goto_if_nok (error, exit);
3160 interp_add_ins (td, MINT_JMP);
3161 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3162 goto_if_nok (error, exit);
3163 td->ip += 5;
3164 break;
3166 case CEE_CALLVIRT: /* Fall through */
3167 case CEE_CALLI: /* Fall through */
3168 case CEE_CALL: {
3169 gboolean need_seq_point = FALSE;
3171 if (sym_seq_points && !mono_bitset_test_fast (seq_point_locs, td->ip + 5 - header->code))
3172 need_seq_point = TRUE;
3174 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, constrained_class, readonly, error, TRUE))
3175 goto exit;
3177 if (need_seq_point) {
3178 //check is is a nested call and remove the MONO_INST_NONEMPTY_STACK of the last breakpoint, only for non native methods
3179 if (!(method->flags & METHOD_IMPL_ATTRIBUTE_NATIVE)) {
3180 if (emitted_funccall_seq_point) {
3181 if (last_seq_point)
3182 last_seq_point->flags |= INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL;
3184 else
3185 emitted_funccall_seq_point = TRUE;
3187 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3188 // This seq point is actually associated with the instruction following the call
3189 last_seq_point->il_offset = td->ip - header->code;
3190 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK;
3193 constrained_class = NULL;
3194 readonly = FALSE;
3195 break;
3197 case CEE_RET: {
3198 /* Return from inlined method, return value is on top of stack */
3199 if (td->method != method) {
3200 td->ip++;
3201 break;
3204 int vt_size = 0;
3205 MonoType *ult = mini_type_get_underlying_type (signature->ret);
3206 if (ult->type != MONO_TYPE_VOID) {
3207 CHECK_STACK (td, 1);
3208 --td->sp;
3209 if (mint_type (ult) == MINT_TYPE_VT) {
3210 MonoClass *klass = mono_class_from_mono_type_internal (ult);
3211 vt_size = mono_class_value_size (klass, NULL);
3214 if (td->sp > td->stack) {
3215 mono_error_set_generic_error (error, "System", "InvalidProgramException", "");
3216 goto exit;
3218 if (td->vt_sp != ALIGN_TO (vt_size, MINT_VT_ALIGNMENT))
3219 g_error ("%s: CEE_RET: value type stack: %d vs. %d", mono_method_full_name (td->method, TRUE), td->vt_sp, vt_size);
3221 if (sym_seq_points) {
3222 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3223 td->last_ins->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT;
3226 if (vt_size == 0)
3227 SIMPLE_OP(td, ult->type == MONO_TYPE_VOID ? MINT_RET_VOID : MINT_RET);
3228 else {
3229 interp_add_ins (td, MINT_RET_VT);
3230 WRITE32_INS (td->last_ins, 0, &vt_size);
3231 ++td->ip;
3233 break;
3235 case CEE_BR:
3236 INLINE_FAILURE;
3237 handle_branch (td, MINT_BR_S, MINT_BR, 5 + read32 (td->ip + 1));
3238 td->ip += 5;
3239 break;
3240 case CEE_BR_S:
3241 INLINE_FAILURE;
3242 handle_branch (td, MINT_BR_S, MINT_BR, 2 + (gint8)td->ip [1]);
3243 td->ip += 2;
3244 break;
3245 case CEE_BRFALSE:
3246 INLINE_FAILURE;
3247 one_arg_branch (td, MINT_BRFALSE_I4, 5 + read32 (td->ip + 1));
3248 td->ip += 5;
3249 break;
3250 case CEE_BRFALSE_S:
3251 INLINE_FAILURE;
3252 one_arg_branch (td, MINT_BRFALSE_I4, 2 + (gint8)td->ip [1]);
3253 td->ip += 2;
3254 break;
3255 case CEE_BRTRUE:
3256 INLINE_FAILURE;
3257 one_arg_branch (td, MINT_BRTRUE_I4, 5 + read32 (td->ip + 1));
3258 td->ip += 5;
3259 break;
3260 case CEE_BRTRUE_S:
3261 INLINE_FAILURE;
3262 one_arg_branch (td, MINT_BRTRUE_I4, 2 + (gint8)td->ip [1]);
3263 td->ip += 2;
3264 break;
3265 case CEE_BEQ:
3266 INLINE_FAILURE;
3267 two_arg_branch (td, MINT_BEQ_I4, 5 + read32 (td->ip + 1));
3268 td->ip += 5;
3269 break;
3270 case CEE_BEQ_S:
3271 INLINE_FAILURE;
3272 two_arg_branch (td, MINT_BEQ_I4, 2 + (gint8) td->ip [1]);
3273 td->ip += 2;
3274 break;
3275 case CEE_BGE:
3276 INLINE_FAILURE;
3277 two_arg_branch (td, MINT_BGE_I4, 5 + read32 (td->ip + 1));
3278 td->ip += 5;
3279 break;
3280 case CEE_BGE_S:
3281 INLINE_FAILURE;
3282 two_arg_branch (td, MINT_BGE_I4, 2 + (gint8) td->ip [1]);
3283 td->ip += 2;
3284 break;
3285 case CEE_BGT:
3286 INLINE_FAILURE;
3287 two_arg_branch (td, MINT_BGT_I4, 5 + read32 (td->ip + 1));
3288 td->ip += 5;
3289 break;
3290 case CEE_BGT_S:
3291 INLINE_FAILURE;
3292 two_arg_branch (td, MINT_BGT_I4, 2 + (gint8) td->ip [1]);
3293 td->ip += 2;
3294 break;
3295 case CEE_BLT:
3296 INLINE_FAILURE;
3297 two_arg_branch (td, MINT_BLT_I4, 5 + read32 (td->ip + 1));
3298 td->ip += 5;
3299 break;
3300 case CEE_BLT_S:
3301 INLINE_FAILURE;
3302 two_arg_branch (td, MINT_BLT_I4, 2 + (gint8) td->ip [1]);
3303 td->ip += 2;
3304 break;
3305 case CEE_BLE:
3306 INLINE_FAILURE;
3307 two_arg_branch (td, MINT_BLE_I4, 5 + read32 (td->ip + 1));
3308 td->ip += 5;
3309 break;
3310 case CEE_BLE_S:
3311 INLINE_FAILURE;
3312 two_arg_branch (td, MINT_BLE_I4, 2 + (gint8) td->ip [1]);
3313 td->ip += 2;
3314 break;
3315 case CEE_BNE_UN:
3316 INLINE_FAILURE;
3317 two_arg_branch (td, MINT_BNE_UN_I4, 5 + read32 (td->ip + 1));
3318 td->ip += 5;
3319 break;
3320 case CEE_BNE_UN_S:
3321 INLINE_FAILURE;
3322 two_arg_branch (td, MINT_BNE_UN_I4, 2 + (gint8) td->ip [1]);
3323 td->ip += 2;
3324 break;
3325 case CEE_BGE_UN:
3326 INLINE_FAILURE;
3327 two_arg_branch (td, MINT_BGE_UN_I4, 5 + read32 (td->ip + 1));
3328 td->ip += 5;
3329 break;
3330 case CEE_BGE_UN_S:
3331 INLINE_FAILURE;
3332 two_arg_branch (td, MINT_BGE_UN_I4, 2 + (gint8) td->ip [1]);
3333 td->ip += 2;
3334 break;
3335 case CEE_BGT_UN:
3336 INLINE_FAILURE;
3337 two_arg_branch (td, MINT_BGT_UN_I4, 5 + read32 (td->ip + 1));
3338 td->ip += 5;
3339 break;
3340 case CEE_BGT_UN_S:
3341 INLINE_FAILURE;
3342 two_arg_branch (td, MINT_BGT_UN_I4, 2 + (gint8) td->ip [1]);
3343 td->ip += 2;
3344 break;
3345 case CEE_BLE_UN:
3346 INLINE_FAILURE;
3347 two_arg_branch (td, MINT_BLE_UN_I4, 5 + read32 (td->ip + 1));
3348 td->ip += 5;
3349 break;
3350 case CEE_BLE_UN_S:
3351 INLINE_FAILURE;
3352 two_arg_branch (td, MINT_BLE_UN_I4, 2 + (gint8) td->ip [1]);
3353 td->ip += 2;
3354 break;
3355 case CEE_BLT_UN:
3356 INLINE_FAILURE;
3357 two_arg_branch (td, MINT_BLT_UN_I4, 5 + read32 (td->ip + 1));
3358 td->ip += 5;
3359 break;
3360 case CEE_BLT_UN_S:
3361 INLINE_FAILURE;
3362 two_arg_branch (td, MINT_BLT_UN_I4, 2 + (gint8) td->ip [1]);
3363 td->ip += 2;
3364 break;
3365 case CEE_SWITCH: {
3366 INLINE_FAILURE;
3367 guint32 n;
3368 const unsigned char *next_ip;
3369 ++td->ip;
3370 n = read32 (td->ip);
3371 interp_add_ins_explicit (td, MINT_SWITCH, MINT_SWITCH_LEN (n));
3372 WRITE32_INS (td->last_ins, 0, &n);
3373 td->ip += 4;
3374 next_ip = td->ip + n * 4;
3375 --td->sp;
3376 int stack_height = td->sp - td->stack;
3377 for (i = 0; i < n; i++) {
3378 offset = read32 (td->ip);
3379 target = next_ip - td->il_code + offset;
3380 if (offset < 0) {
3381 #if DEBUG_INTERP
3382 if (stack_height > 0 && stack_height != td->stack_height [target])
3383 g_warning ("SWITCH with back branch and non-empty stack");
3384 #endif
3385 } else {
3386 td->stack_height [target] = stack_height;
3387 td->vt_stack_size [target] = td->vt_sp;
3388 if (stack_height > 0)
3389 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, stack_height * sizeof (td->stack [0]));
3391 WRITE32_INS (td->last_ins, 2 + i * 2, &target);
3392 td->ip += 4;
3394 break;
3396 case CEE_LDIND_I1:
3397 CHECK_STACK (td, 1);
3398 SIMPLE_OP (td, MINT_LDIND_I1_CHECK);
3399 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3400 BARRIER_IF_VOLATILE (td);
3401 break;
3402 case CEE_LDIND_U1:
3403 CHECK_STACK (td, 1);
3404 SIMPLE_OP (td, MINT_LDIND_U1_CHECK);
3405 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3406 BARRIER_IF_VOLATILE (td);
3407 break;
3408 case CEE_LDIND_I2:
3409 CHECK_STACK (td, 1);
3410 SIMPLE_OP (td, MINT_LDIND_I2_CHECK);
3411 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3412 BARRIER_IF_VOLATILE (td);
3413 break;
3414 case CEE_LDIND_U2:
3415 CHECK_STACK (td, 1);
3416 SIMPLE_OP (td, MINT_LDIND_U2_CHECK);
3417 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3418 BARRIER_IF_VOLATILE (td);
3419 break;
3420 case CEE_LDIND_I4:
3421 CHECK_STACK (td, 1);
3422 SIMPLE_OP (td, MINT_LDIND_I4_CHECK);
3423 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3424 BARRIER_IF_VOLATILE (td);
3425 break;
3426 case CEE_LDIND_U4:
3427 CHECK_STACK (td, 1);
3428 SIMPLE_OP (td, MINT_LDIND_U4_CHECK);
3429 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3430 BARRIER_IF_VOLATILE (td);
3431 break;
3432 case CEE_LDIND_I8:
3433 CHECK_STACK (td, 1);
3434 SIMPLE_OP (td, MINT_LDIND_I8_CHECK);
3435 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3436 BARRIER_IF_VOLATILE (td);
3437 break;
3438 case CEE_LDIND_I:
3439 CHECK_STACK (td, 1);
3440 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3441 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3442 BARRIER_IF_VOLATILE (td);
3443 break;
3444 case CEE_LDIND_R4:
3445 CHECK_STACK (td, 1);
3446 SIMPLE_OP (td, MINT_LDIND_R4_CHECK);
3447 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
3448 BARRIER_IF_VOLATILE (td);
3449 break;
3450 case CEE_LDIND_R8:
3451 CHECK_STACK (td, 1);
3452 SIMPLE_OP (td, MINT_LDIND_R8_CHECK);
3453 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3454 BARRIER_IF_VOLATILE (td);
3455 break;
3456 case CEE_LDIND_REF:
3457 CHECK_STACK (td, 1);
3458 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3459 BARRIER_IF_VOLATILE (td);
3460 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
3461 break;
3462 case CEE_STIND_REF:
3463 CHECK_STACK (td, 2);
3464 BARRIER_IF_VOLATILE (td);
3465 SIMPLE_OP (td, MINT_STIND_REF);
3466 td->sp -= 2;
3467 break;
3468 case CEE_STIND_I1:
3469 CHECK_STACK (td, 2);
3470 BARRIER_IF_VOLATILE (td);
3471 SIMPLE_OP (td, MINT_STIND_I1);
3472 td->sp -= 2;
3473 break;
3474 case CEE_STIND_I2:
3475 CHECK_STACK (td, 2);
3476 BARRIER_IF_VOLATILE (td);
3477 SIMPLE_OP (td, MINT_STIND_I2);
3478 td->sp -= 2;
3479 break;
3480 case CEE_STIND_I4:
3481 CHECK_STACK (td, 2);
3482 BARRIER_IF_VOLATILE (td);
3483 SIMPLE_OP (td, MINT_STIND_I4);
3484 td->sp -= 2;
3485 break;
3486 case CEE_STIND_I:
3487 CHECK_STACK (td, 2);
3488 BARRIER_IF_VOLATILE (td);
3489 SIMPLE_OP (td, MINT_STIND_I);
3490 td->sp -= 2;
3491 break;
3492 case CEE_STIND_I8:
3493 CHECK_STACK (td, 2);
3494 BARRIER_IF_VOLATILE (td);
3495 SIMPLE_OP (td, MINT_STIND_I8);
3496 td->sp -= 2;
3497 break;
3498 case CEE_STIND_R4:
3499 CHECK_STACK (td, 2);
3500 BARRIER_IF_VOLATILE (td);
3501 SIMPLE_OP (td, MINT_STIND_R4);
3502 td->sp -= 2;
3503 break;
3504 case CEE_STIND_R8:
3505 CHECK_STACK (td, 2);
3506 BARRIER_IF_VOLATILE (td);
3507 SIMPLE_OP (td, MINT_STIND_R8);
3508 td->sp -= 2;
3509 break;
3510 case CEE_ADD:
3511 binary_arith_op(td, MINT_ADD_I4);
3512 ++td->ip;
3513 break;
3514 case CEE_SUB:
3515 binary_arith_op(td, MINT_SUB_I4);
3516 ++td->ip;
3517 break;
3518 case CEE_MUL:
3519 binary_arith_op(td, MINT_MUL_I4);
3520 ++td->ip;
3521 break;
3522 case CEE_DIV:
3523 binary_arith_op(td, MINT_DIV_I4);
3524 ++td->ip;
3525 break;
3526 case CEE_DIV_UN:
3527 binary_arith_op(td, MINT_DIV_UN_I4);
3528 ++td->ip;
3529 break;
3530 case CEE_REM:
3531 binary_arith_op (td, MINT_REM_I4);
3532 ++td->ip;
3533 break;
3534 case CEE_REM_UN:
3535 binary_arith_op (td, MINT_REM_UN_I4);
3536 ++td->ip;
3537 break;
3538 case CEE_AND:
3539 binary_arith_op (td, MINT_AND_I4);
3540 ++td->ip;
3541 break;
3542 case CEE_OR:
3543 binary_arith_op (td, MINT_OR_I4);
3544 ++td->ip;
3545 break;
3546 case CEE_XOR:
3547 binary_arith_op (td, MINT_XOR_I4);
3548 ++td->ip;
3549 break;
3550 case CEE_SHL:
3551 shift_op (td, MINT_SHL_I4);
3552 ++td->ip;
3553 break;
3554 case CEE_SHR:
3555 shift_op (td, MINT_SHR_I4);
3556 ++td->ip;
3557 break;
3558 case CEE_SHR_UN:
3559 shift_op (td, MINT_SHR_UN_I4);
3560 ++td->ip;
3561 break;
3562 case CEE_NEG:
3563 unary_arith_op (td, MINT_NEG_I4);
3564 ++td->ip;
3565 break;
3566 case CEE_NOT:
3567 unary_arith_op (td, MINT_NOT_I4);
3568 ++td->ip;
3569 break;
3570 case CEE_CONV_U1:
3571 CHECK_STACK (td, 1);
3572 switch (td->sp [-1].type) {
3573 case STACK_TYPE_R4:
3574 interp_add_ins (td, MINT_CONV_U1_R4);
3575 break;
3576 case STACK_TYPE_R8:
3577 interp_add_ins (td, MINT_CONV_U1_R8);
3578 break;
3579 case STACK_TYPE_I4:
3580 interp_add_ins (td, MINT_CONV_U1_I4);
3581 break;
3582 case STACK_TYPE_I8:
3583 interp_add_ins (td, MINT_CONV_U1_I8);
3584 break;
3585 default:
3586 g_assert_not_reached ();
3588 ++td->ip;
3589 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3590 break;
3591 case CEE_CONV_I1:
3592 CHECK_STACK (td, 1);
3593 switch (td->sp [-1].type) {
3594 case STACK_TYPE_R4:
3595 interp_add_ins (td, MINT_CONV_I1_R4);
3596 break;
3597 case STACK_TYPE_R8:
3598 interp_add_ins (td, MINT_CONV_I1_R8);
3599 break;
3600 case STACK_TYPE_I4:
3601 interp_add_ins (td, MINT_CONV_I1_I4);
3602 break;
3603 case STACK_TYPE_I8:
3604 interp_add_ins (td, MINT_CONV_I1_I8);
3605 break;
3606 default:
3607 g_assert_not_reached ();
3609 ++td->ip;
3610 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3611 break;
3612 case CEE_CONV_U2:
3613 CHECK_STACK (td, 1);
3614 switch (td->sp [-1].type) {
3615 case STACK_TYPE_R4:
3616 interp_add_ins (td, MINT_CONV_U2_R4);
3617 break;
3618 case STACK_TYPE_R8:
3619 interp_add_ins (td, MINT_CONV_U2_R8);
3620 break;
3621 case STACK_TYPE_I4:
3622 interp_add_ins (td, MINT_CONV_U2_I4);
3623 break;
3624 case STACK_TYPE_I8:
3625 interp_add_ins (td, MINT_CONV_U2_I8);
3626 break;
3627 default:
3628 g_assert_not_reached ();
3630 ++td->ip;
3631 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3632 break;
3633 case CEE_CONV_I2:
3634 CHECK_STACK (td, 1);
3635 switch (td->sp [-1].type) {
3636 case STACK_TYPE_R4:
3637 interp_add_ins (td, MINT_CONV_I2_R4);
3638 break;
3639 case STACK_TYPE_R8:
3640 interp_add_ins (td, MINT_CONV_I2_R8);
3641 break;
3642 case STACK_TYPE_I4:
3643 interp_add_ins (td, MINT_CONV_I2_I4);
3644 break;
3645 case STACK_TYPE_I8:
3646 interp_add_ins (td, MINT_CONV_I2_I8);
3647 break;
3648 default:
3649 g_assert_not_reached ();
3651 ++td->ip;
3652 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3653 break;
3654 case CEE_CONV_U:
3655 CHECK_STACK (td, 1);
3656 switch (td->sp [-1].type) {
3657 case STACK_TYPE_R8:
3658 #if SIZEOF_VOID_P == 4
3659 interp_add_ins (td, MINT_CONV_U4_R8);
3660 #else
3661 interp_add_ins (td, MINT_CONV_U8_R8);
3662 #endif
3663 break;
3664 case STACK_TYPE_I4:
3665 #if SIZEOF_VOID_P == 8
3666 interp_add_ins (td, MINT_CONV_U8_I4);
3667 #endif
3668 break;
3669 case STACK_TYPE_I8:
3670 #if SIZEOF_VOID_P == 4
3671 interp_add_ins (td, MINT_CONV_U4_I8);
3672 #endif
3673 break;
3674 case STACK_TYPE_MP:
3675 case STACK_TYPE_O:
3676 break;
3677 default:
3678 g_assert_not_reached ();
3680 ++td->ip;
3681 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3682 break;
3683 case CEE_CONV_I:
3684 CHECK_STACK (td, 1);
3685 switch (td->sp [-1].type) {
3686 case STACK_TYPE_R8:
3687 #if SIZEOF_VOID_P == 8
3688 interp_add_ins (td, MINT_CONV_I8_R8);
3689 #else
3690 interp_add_ins (td, MINT_CONV_I4_R8);
3691 #endif
3692 break;
3693 case STACK_TYPE_I4:
3694 #if SIZEOF_VOID_P == 8
3695 interp_add_ins (td, MINT_CONV_I8_I4);
3696 #endif
3697 break;
3698 case STACK_TYPE_O:
3699 break;
3700 case STACK_TYPE_MP:
3701 break;
3702 case STACK_TYPE_I8:
3703 #if SIZEOF_VOID_P == 4
3704 interp_add_ins (td, MINT_CONV_I4_I8);
3705 #endif
3706 break;
3707 default:
3708 g_assert_not_reached ();
3710 ++td->ip;
3711 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3712 break;
3713 case CEE_CONV_U4:
3714 CHECK_STACK (td, 1);
3715 switch (td->sp [-1].type) {
3716 case STACK_TYPE_R4:
3717 interp_add_ins (td, MINT_CONV_U4_R4);
3718 break;
3719 case STACK_TYPE_R8:
3720 interp_add_ins (td, MINT_CONV_U4_R8);
3721 break;
3722 case STACK_TYPE_I4:
3723 break;
3724 case STACK_TYPE_I8:
3725 interp_add_ins (td, MINT_CONV_U4_I8);
3726 break;
3727 case STACK_TYPE_MP:
3728 #if SIZEOF_VOID_P == 8
3729 interp_add_ins (td, MINT_CONV_U4_I8);
3730 #endif
3731 break;
3732 default:
3733 g_assert_not_reached ();
3735 ++td->ip;
3736 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3737 break;
3738 case CEE_CONV_I4:
3739 CHECK_STACK (td, 1);
3740 switch (td->sp [-1].type) {
3741 case STACK_TYPE_R4:
3742 interp_add_ins (td, MINT_CONV_I4_R4);
3743 break;
3744 case STACK_TYPE_R8:
3745 interp_add_ins (td, MINT_CONV_I4_R8);
3746 break;
3747 case STACK_TYPE_I4:
3748 break;
3749 case STACK_TYPE_I8:
3750 interp_add_ins (td, MINT_CONV_I4_I8);
3751 break;
3752 case STACK_TYPE_MP:
3753 #if SIZEOF_VOID_P == 8
3754 interp_add_ins (td, MINT_CONV_I4_I8);
3755 #endif
3756 break;
3757 default:
3758 g_assert_not_reached ();
3760 ++td->ip;
3761 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3762 break;
3763 case CEE_CONV_I8:
3764 CHECK_STACK (td, 1);
3765 switch (td->sp [-1].type) {
3766 case STACK_TYPE_R4:
3767 interp_add_ins (td, MINT_CONV_I8_R4);
3768 break;
3769 case STACK_TYPE_R8:
3770 interp_add_ins (td, MINT_CONV_I8_R8);
3771 break;
3772 case STACK_TYPE_I4: {
3773 if (interp_ins_is_ldc (td->last_ins) && !td->is_bb_start [in_offset]) {
3774 gint64 ct = interp_ldc_i4_get_const (td->last_ins);
3775 interp_remove_ins (td, td->last_ins);
3777 interp_add_ins (td, MINT_LDC_I8);
3778 WRITE64_INS (td->last_ins, 0, &ct);
3779 } else {
3780 interp_add_ins (td, MINT_CONV_I8_I4);
3782 break;
3784 case STACK_TYPE_I8:
3785 break;
3786 case STACK_TYPE_MP:
3787 #if SIZEOF_VOID_P == 4
3788 interp_add_ins (td, MINT_CONV_I8_I4);
3789 #endif
3790 break;
3791 default:
3792 g_assert_not_reached ();
3794 ++td->ip;
3795 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3796 break;
3797 case CEE_CONV_R4:
3798 CHECK_STACK (td, 1);
3799 switch (td->sp [-1].type) {
3800 case STACK_TYPE_R8:
3801 interp_add_ins (td, MINT_CONV_R4_R8);
3802 break;
3803 case STACK_TYPE_I8:
3804 interp_add_ins (td, MINT_CONV_R4_I8);
3805 break;
3806 case STACK_TYPE_I4:
3807 interp_add_ins (td, MINT_CONV_R4_I4);
3808 break;
3809 case STACK_TYPE_R4:
3810 /* no-op */
3811 break;
3812 default:
3813 g_assert_not_reached ();
3815 ++td->ip;
3816 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
3817 break;
3818 case CEE_CONV_R8:
3819 CHECK_STACK (td, 1);
3820 switch (td->sp [-1].type) {
3821 case STACK_TYPE_I4:
3822 interp_add_ins (td, MINT_CONV_R8_I4);
3823 break;
3824 case STACK_TYPE_I8:
3825 interp_add_ins (td, MINT_CONV_R8_I8);
3826 break;
3827 case STACK_TYPE_R4:
3828 interp_add_ins (td, MINT_CONV_R8_R4);
3829 break;
3830 case STACK_TYPE_R8:
3831 break;
3832 default:
3833 g_assert_not_reached ();
3835 ++td->ip;
3836 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3837 break;
3838 case CEE_CONV_U8:
3839 CHECK_STACK (td, 1);
3840 switch (td->sp [-1].type) {
3841 case STACK_TYPE_I4:
3842 if (interp_ins_is_ldc (td->last_ins) && !td->is_bb_start [in_offset]) {
3843 gint64 ct = (guint32)interp_ldc_i4_get_const (td->last_ins);
3844 interp_remove_ins (td, td->last_ins);
3846 interp_add_ins (td, MINT_LDC_I8);
3847 WRITE64_INS (td->last_ins, 0, &ct);
3848 } else {
3849 interp_add_ins (td, MINT_CONV_U8_I4);
3851 break;
3852 case STACK_TYPE_I8:
3853 break;
3854 case STACK_TYPE_R4:
3855 interp_add_ins (td, MINT_CONV_U8_R4);
3856 break;
3857 case STACK_TYPE_R8:
3858 interp_add_ins (td, MINT_CONV_U8_R8);
3859 break;
3860 case STACK_TYPE_MP:
3861 #if SIZEOF_VOID_P == 4
3862 interp_add_ins (td, MINT_CONV_U8_I4);
3863 #endif
3864 break;
3865 default:
3866 g_assert_not_reached ();
3868 ++td->ip;
3869 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3870 break;
3871 case CEE_CPOBJ: {
3872 CHECK_STACK (td, 2);
3874 token = read32 (td->ip + 1);
3875 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
3876 goto_if_nok (error, exit);
3878 if (m_class_is_valuetype (klass)) {
3879 int mt = mint_type (m_class_get_byval_arg (klass));
3880 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_CPOBJ_VT : MINT_CPOBJ);
3881 td->last_ins->data [0] = get_data_item_index(td, klass);
3882 } else {
3883 interp_add_ins (td, MINT_LDIND_REF);
3884 interp_add_ins (td, MINT_STIND_REF);
3886 td->ip += 5;
3887 td->sp -= 2;
3888 break;
3890 case CEE_LDOBJ: {
3891 CHECK_STACK (td, 1);
3893 token = read32 (td->ip + 1);
3895 if (method->wrapper_type != MONO_WRAPPER_NONE)
3896 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
3897 else {
3898 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
3899 goto_if_nok (error, exit);
3902 interp_emit_ldobj (td, klass);
3904 td->ip += 5;
3905 BARRIER_IF_VOLATILE (td);
3906 break;
3908 case CEE_LDSTR: {
3909 token = mono_metadata_token_index (read32 (td->ip + 1));
3910 td->ip += 5;
3911 if (method->wrapper_type == MONO_WRAPPER_NONE) {
3912 MonoString *s = mono_ldstr_checked (domain, image, token, error);
3913 goto_if_nok (error, exit);
3914 /* GC won't scan code stream, but reference is held by metadata
3915 * machinery so we are good here */
3916 interp_add_ins (td, MINT_LDSTR);
3917 td->last_ins->data [0] = get_data_item_index (td, s);
3918 } else {
3919 /* defer allocation to execution-time */
3920 interp_add_ins (td, MINT_LDSTR_TOKEN);
3921 td->last_ins->data [0] = get_data_item_index (td, GUINT_TO_POINTER (token));
3923 PUSH_TYPE(td, STACK_TYPE_O, mono_defaults.string_class);
3924 break;
3926 case CEE_NEWOBJ: {
3927 MonoMethod *m;
3928 MonoMethodSignature *csignature;
3929 guint32 vt_stack_used = 0;
3930 guint32 vt_res_size = 0;
3932 td->ip++;
3933 token = read32 (td->ip);
3934 td->ip += 4;
3936 if (method->wrapper_type != MONO_WRAPPER_NONE)
3937 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
3938 else {
3939 m = mono_get_method_checked (image, token, NULL, generic_context, error);
3940 goto_if_nok (error, exit);
3943 csignature = mono_method_signature_internal (m);
3944 klass = m->klass;
3946 if (!mono_class_init_internal (klass)) {
3947 mono_error_set_for_class_failure (error, klass);
3948 goto_if_nok (error, exit);
3951 if (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_ABSTRACT) {
3952 char* full_name = mono_type_get_full_name (klass);
3953 mono_error_set_member_access (error, "Cannot create an abstract class: %s", full_name);
3954 g_free (full_name);
3955 goto_if_nok (error, exit);
3958 td->sp -= csignature->param_count;
3959 if (mono_class_is_magic_int (klass) || mono_class_is_magic_float (klass)) {
3960 #if SIZEOF_VOID_P == 8
3961 if (mono_class_is_magic_int (klass) && td->sp [0].type == STACK_TYPE_I4)
3962 interp_add_ins (td, MINT_CONV_I8_I4);
3963 else if (mono_class_is_magic_float (klass) && td->sp [0].type == STACK_TYPE_R4)
3964 interp_add_ins (td, MINT_CONV_R8_R4);
3965 #endif
3966 interp_add_ins (td, MINT_NEWOBJ_MAGIC);
3967 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3968 goto_if_nok (error, exit);
3970 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
3971 } else {
3972 if (m_class_get_parent (klass) == mono_defaults.array_class) {
3973 interp_add_ins (td, MINT_NEWOBJ_ARRAY);
3974 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3975 td->last_ins->data [1] = csignature->param_count;
3976 } else if (m_class_get_image (klass) == mono_defaults.corlib &&
3977 !strcmp (m_class_get_name (m->klass), "ByReference`1") &&
3978 !strcmp (m->name, ".ctor")) {
3979 /* public ByReference(ref T value) */
3980 g_assert (csignature->hasthis && csignature->param_count == 1);
3981 interp_add_ins (td, MINT_INTRINS_BYREFERENCE_CTOR);
3982 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3983 } else if (klass != mono_defaults.string_class &&
3984 !mono_class_is_marshalbyref (klass) &&
3985 !mono_class_has_finalizer (klass) &&
3986 !m_class_has_weak_fields (klass)) {
3987 if (!m_class_is_valuetype (klass))
3988 interp_add_ins (td, MINT_NEWOBJ_FAST);
3989 else if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
3990 interp_add_ins (td, MINT_NEWOBJ_VTST_FAST);
3991 else
3992 interp_add_ins (td, MINT_NEWOBJ_VT_FAST);
3994 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3995 td->last_ins->data [1] = csignature->param_count;
3997 if (!m_class_is_valuetype (klass)) {
3998 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
3999 goto_if_nok (error, exit);
4000 td->last_ins->data [2] = get_data_item_index (td, vtable);
4001 } else if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4002 td->last_ins->data [2] = mono_class_value_size (klass, NULL);
4004 } else {
4005 interp_add_ins (td, MINT_NEWOBJ);
4006 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4008 goto_if_nok (error, exit);
4010 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4011 vt_res_size = mono_class_value_size (klass, NULL);
4012 PUSH_VT (td, vt_res_size);
4014 for (i = 0; i < csignature->param_count; ++i) {
4015 int mt = mint_type(csignature->params [i]);
4016 if (mt == MINT_TYPE_VT) {
4017 MonoClass *k = mono_class_from_mono_type_internal (csignature->params [i]);
4018 gint32 size = mono_class_value_size (k, NULL);
4019 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4020 vt_stack_used += size;
4023 if (vt_stack_used != 0 || vt_res_size != 0) {
4024 interp_add_ins (td, MINT_VTRESULT);
4025 td->last_ins->data [0] = vt_res_size;
4026 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
4027 td->vt_sp -= vt_stack_used;
4029 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4031 break;
4033 case CEE_CASTCLASS:
4034 case CEE_ISINST: {
4035 gboolean isinst_instr = *td->ip == CEE_ISINST;
4036 CHECK_STACK (td, 1);
4037 token = read32 (td->ip + 1);
4038 klass = mini_get_class (method, token, generic_context);
4039 CHECK_TYPELOAD (klass);
4040 interp_handle_isinst (td, klass, isinst_instr);
4041 if (!isinst_instr)
4042 td->sp [-1].klass = klass;
4043 break;
4045 case CEE_CONV_R_UN:
4046 switch (td->sp [-1].type) {
4047 case STACK_TYPE_R8:
4048 break;
4049 case STACK_TYPE_I8:
4050 interp_add_ins (td, MINT_CONV_R_UN_I8);
4051 break;
4052 case STACK_TYPE_I4:
4053 interp_add_ins (td, MINT_CONV_R_UN_I4);
4054 break;
4055 default:
4056 g_assert_not_reached ();
4058 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4059 ++td->ip;
4060 break;
4061 case CEE_UNBOX:
4062 CHECK_STACK (td, 1);
4063 token = read32 (td->ip + 1);
4065 if (method->wrapper_type != MONO_WRAPPER_NONE)
4066 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4067 else {
4068 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4069 goto_if_nok (error, exit);
4072 if (mono_class_is_nullable (klass)) {
4073 MonoMethod *target_method;
4074 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4075 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4076 else
4077 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4078 goto_if_nok (error, exit);
4079 /* td->ip is incremented by interp_transform_call */
4080 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
4081 goto exit;
4083 * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type
4084 * We create a local variable in the frame so that we can fetch its address.
4086 int local_offset = create_interp_local (td, m_class_get_byval_arg (klass));
4087 store_local_general (td, local_offset, m_class_get_byval_arg (klass));
4088 interp_add_ins (td, MINT_LDLOCA_S);
4089 td->last_ins->data [0] = local_offset;
4090 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
4091 } else {
4092 interp_add_ins (td, MINT_UNBOX);
4093 td->last_ins->data [0] = get_data_item_index (td, klass);
4094 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
4095 td->ip += 5;
4097 break;
4098 case CEE_UNBOX_ANY:
4099 CHECK_STACK (td, 1);
4100 token = read32 (td->ip + 1);
4102 klass = mini_get_class (method, token, generic_context);
4103 CHECK_TYPELOAD (klass);
4105 if (mini_type_is_reference (m_class_get_byval_arg (klass))) {
4106 int mt = mint_type (m_class_get_byval_arg (klass));
4107 interp_handle_isinst (td, klass, FALSE);
4108 SET_TYPE (td->sp - 1, stack_type [mt], klass);
4109 } else if (mono_class_is_nullable (klass)) {
4110 MonoMethod *target_method;
4111 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4112 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4113 else
4114 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4115 goto_if_nok (error, exit);
4116 /* td->ip is incremented by interp_transform_call */
4117 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
4118 goto exit;
4119 } else {
4120 interp_add_ins (td, MINT_UNBOX);
4121 td->last_ins->data [0] = get_data_item_index (td, klass);
4123 interp_emit_ldobj (td, klass);
4125 td->ip += 5;
4128 break;
4129 case CEE_THROW:
4130 INLINE_FAILURE;
4131 CHECK_STACK (td, 1);
4132 SIMPLE_OP (td, MINT_THROW);
4133 td->sp = td->stack;
4134 break;
4135 case CEE_LDFLDA: {
4136 CHECK_STACK (td, 1);
4137 token = read32 (td->ip + 1);
4138 field = interp_field_from_token (method, token, &klass, generic_context, error);
4139 goto_if_nok (error, exit);
4140 MonoType *ftype = mono_field_get_type_internal (field);
4141 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4142 mono_class_init_internal (klass);
4143 #ifndef DISABLE_REMOTING
4144 if (m_class_get_marshalbyref (klass) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
4145 g_assert (!is_static);
4146 int offset = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4148 interp_add_ins (td, MINT_MONO_LDPTR);
4149 td->last_ins->data [0] = get_data_item_index (td, klass);
4150 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4151 interp_add_ins (td, MINT_MONO_LDPTR);
4152 td->last_ins->data [0] = get_data_item_index (td, field);
4153 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4154 interp_add_ins (td, MINT_LDC_I4);
4155 WRITE32_INS (td->last_ins, 0, &offset);
4156 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
4157 #if SIZEOF_VOID_P == 8
4158 interp_add_ins (td, MINT_CONV_I8_I4);
4159 #endif
4161 MonoMethod *wrapper = mono_marshal_get_ldflda_wrapper (field->type);
4162 /* td->ip is incremented by interp_transform_call */
4163 if (!interp_transform_call (td, method, wrapper, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
4164 goto exit;
4165 } else
4166 #endif
4168 if (is_static) {
4169 interp_add_ins (td, MINT_POP);
4170 td->last_ins->data [0] = 0;
4171 interp_add_ins (td, MINT_LDSFLDA);
4172 td->last_ins->data [0] = get_data_item_index (td, field);
4173 } else {
4174 if ((td->sp - 1)->type == STACK_TYPE_O) {
4175 interp_add_ins (td, MINT_LDFLDA);
4176 } else {
4177 g_assert ((td->sp -1)->type == STACK_TYPE_MP);
4178 interp_add_ins (td, MINT_LDFLDA_UNSAFE);
4180 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4182 td->ip += 5;
4184 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4185 break;
4187 case CEE_LDFLD: {
4188 CHECK_STACK (td, 1);
4189 token = read32 (td->ip + 1);
4190 field = interp_field_from_token (method, token, &klass, generic_context, error);
4191 goto_if_nok (error, exit);
4192 MonoType *ftype = mono_field_get_type_internal (field);
4193 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4194 mono_class_init_internal (klass);
4196 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4197 mt = mint_type (m_class_get_byval_arg (field_klass));
4198 #ifndef DISABLE_REMOTING
4199 if (m_class_get_marshalbyref (klass)) {
4200 g_assert (!is_static);
4201 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDRMFLD_VT : MINT_LDRMFLD);
4202 td->last_ins->data [0] = get_data_item_index (td, field);
4203 } else
4204 #endif
4206 if (is_static) {
4207 interp_add_ins (td, MINT_POP);
4208 td->last_ins->data [0] = 0;
4209 interp_emit_sfld_access (td, field, field_klass, mt, TRUE, error);
4210 goto_if_nok (error, exit);
4211 } else {
4212 int opcode = MINT_LDFLD_I1 + mt - MINT_TYPE_I1;
4213 #ifdef NO_UNALIGNED_ACCESS
4214 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4215 opcode = get_unaligned_opcode (opcode);
4216 #endif
4217 interp_add_ins (td, opcode);
4218 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4219 if (mt == MINT_TYPE_VT) {
4220 int size = mono_class_value_size (field_klass, NULL);
4221 WRITE32_INS (td->last_ins, 1, &size);
4225 if (mt == MINT_TYPE_VT) {
4226 int size = mono_class_value_size (field_klass, NULL);
4227 PUSH_VT (td, size);
4229 if (td->sp [-1].type == STACK_TYPE_VT) {
4230 int size = mono_class_value_size (klass, NULL);
4231 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4232 int field_vt_size = 0;
4233 if (mt == MINT_TYPE_VT) {
4235 * Pop the loaded field from the vtstack (it will still be present
4236 * at the same vtstack address) and we will load it in place of the
4237 * containing valuetype with the second MINT_VTRESULT.
4239 field_vt_size = mono_class_value_size (field_klass, NULL);
4240 field_vt_size = ALIGN_TO (field_vt_size, MINT_VT_ALIGNMENT);
4241 interp_add_ins (td, MINT_VTRESULT);
4242 td->last_ins->data [0] = 0;
4243 WRITE32_INS (td->last_ins, 1, &field_vt_size);
4245 td->vt_sp -= size;
4246 interp_add_ins (td, MINT_VTRESULT);
4247 td->last_ins->data [0] = field_vt_size;
4248 WRITE32_INS (td->last_ins, 1, &size);
4250 td->ip += 5;
4251 SET_TYPE (td->sp - 1, stack_type [mt], field_klass);
4252 BARRIER_IF_VOLATILE (td);
4253 break;
4255 case CEE_STFLD: {
4256 CHECK_STACK (td, 2);
4257 token = read32 (td->ip + 1);
4258 field = interp_field_from_token (method, token, &klass, generic_context, error);
4259 goto_if_nok (error, exit);
4260 MonoType *ftype = mono_field_get_type_internal (field);
4261 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4262 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4263 mono_class_init_internal (klass);
4264 mt = mint_type (ftype);
4266 BARRIER_IF_VOLATILE (td);
4268 #ifndef DISABLE_REMOTING
4269 if (m_class_get_marshalbyref (klass)) {
4270 g_assert (!is_static);
4271 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_STRMFLD_VT : MINT_STRMFLD);
4272 td->last_ins->data [0] = get_data_item_index (td, field);
4273 } else
4274 #endif
4276 if (is_static) {
4277 interp_add_ins (td, MINT_POP);
4278 td->last_ins->data [0] = 1;
4279 interp_emit_sfld_access (td, field, field_klass, mt, FALSE, error);
4280 goto_if_nok (error, exit);
4282 /* the vtable of the field might not be initialized at this point */
4283 mono_class_vtable_checked (domain, field_klass, error);
4284 goto_if_nok (error, exit);
4285 } else {
4286 int opcode = MINT_STFLD_I1 + mt - MINT_TYPE_I1;
4287 #ifdef NO_UNALIGNED_ACCESS
4288 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4289 opcode = get_unaligned_opcode (opcode);
4290 #endif
4291 interp_add_ins (td, opcode);
4292 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4293 if (mt == MINT_TYPE_VT) {
4294 /* the vtable of the field might not be initialized at this point */
4295 mono_class_vtable_checked (domain, field_klass, error);
4296 goto_if_nok (error, exit);
4298 td->last_ins->data [1] = get_data_item_index (td, field_klass);
4302 if (mt == MINT_TYPE_VT) {
4303 int size = mono_class_value_size (field_klass, NULL);
4304 POP_VT (td, size);
4306 td->ip += 5;
4307 td->sp -= 2;
4308 break;
4310 case CEE_LDSFLDA: {
4311 token = read32 (td->ip + 1);
4312 field = interp_field_from_token (method, token, &klass, generic_context, error);
4313 goto_if_nok (error, exit);
4314 mono_field_get_type_internal (field);
4315 interp_add_ins (td, MINT_LDSFLDA);
4316 td->last_ins->data [0] = get_data_item_index (td, field);
4317 td->ip += 5;
4318 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
4319 break;
4321 case CEE_LDSFLD: {
4322 token = read32 (td->ip + 1);
4323 field = interp_field_from_token (method, token, &klass, generic_context, error);
4324 goto_if_nok (error, exit);
4325 MonoType *ftype = mono_field_get_type_internal (field);
4326 mt = mint_type (ftype);
4327 klass = mono_class_from_mono_type_internal (ftype);
4329 interp_emit_sfld_access (td, field, klass, mt, TRUE, error);
4330 goto_if_nok (error, exit);
4332 if (mt == MINT_TYPE_VT) {
4333 int size = mono_class_value_size (klass, NULL);
4334 PUSH_VT(td, size);
4336 td->ip += 5;
4337 PUSH_TYPE(td, stack_type [mt], klass);
4338 break;
4340 case CEE_STSFLD: {
4341 CHECK_STACK (td, 1);
4342 token = read32 (td->ip + 1);
4343 field = interp_field_from_token (method, token, &klass, generic_context, error);
4344 goto_if_nok (error, exit);
4345 MonoType *ftype = mono_field_get_type_internal (field);
4346 mt = mint_type (ftype);
4348 /* the vtable of the field might not be initialized at this point */
4349 MonoClass *fld_klass = mono_class_from_mono_type_internal (ftype);
4350 mono_class_vtable_checked (domain, fld_klass, error);
4351 goto_if_nok (error, exit);
4353 interp_emit_sfld_access (td, field, fld_klass, mt, FALSE, error);
4354 goto_if_nok (error, exit);
4356 if (mt == MINT_TYPE_VT) {
4357 int size = mono_class_value_size (fld_klass, NULL);
4358 POP_VT(td, size);
4360 td->ip += 5;
4361 --td->sp;
4362 break;
4364 case CEE_STOBJ: {
4365 token = read32 (td->ip + 1);
4367 if (method->wrapper_type != MONO_WRAPPER_NONE)
4368 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4369 else
4370 klass = mini_get_class (method, token, generic_context);
4371 CHECK_TYPELOAD (klass);
4373 BARRIER_IF_VOLATILE (td);
4375 interp_emit_stobj (td, klass);
4377 td->ip += 5;
4378 break;
4380 case CEE_CONV_OVF_I_UN:
4381 case CEE_CONV_OVF_U_UN:
4382 CHECK_STACK (td, 1);
4383 switch (td->sp [-1].type) {
4384 case STACK_TYPE_R8:
4385 #if SIZEOF_VOID_P == 8
4386 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4387 #else
4388 interp_add_ins (td, MINT_CONV_OVF_I4_UN_R8);
4389 #endif
4390 break;
4391 case STACK_TYPE_I8:
4392 #if SIZEOF_VOID_P == 4
4393 interp_add_ins (td, MINT_CONV_OVF_I4_UN_I8);
4394 #endif
4395 break;
4396 case STACK_TYPE_I4:
4397 #if SIZEOF_VOID_P == 8
4398 interp_add_ins (td, MINT_CONV_I8_U4);
4399 #elif SIZEOF_VOID_P == 4
4400 if (*td->ip == CEE_CONV_OVF_I_UN)
4401 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
4402 #endif
4403 break;
4404 default:
4405 g_assert_not_reached ();
4406 break;
4408 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4409 ++td->ip;
4410 break;
4411 case CEE_CONV_OVF_I8_UN:
4412 case CEE_CONV_OVF_U8_UN:
4413 CHECK_STACK (td, 1);
4414 switch (td->sp [-1].type) {
4415 case STACK_TYPE_R8:
4416 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4417 break;
4418 case STACK_TYPE_I8:
4419 if (*td->ip == CEE_CONV_OVF_I8_UN)
4420 interp_add_ins (td, MINT_CONV_OVF_I8_U8);
4421 break;
4422 case STACK_TYPE_I4:
4423 interp_add_ins (td, MINT_CONV_I8_U4);
4424 break;
4425 default:
4426 g_assert_not_reached ();
4427 break;
4429 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4430 ++td->ip;
4431 break;
4432 case CEE_BOX: {
4433 int size;
4434 CHECK_STACK (td, 1);
4435 token = read32 (td->ip + 1);
4436 if (method->wrapper_type != MONO_WRAPPER_NONE)
4437 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4438 else
4439 klass = mini_get_class (method, token, generic_context);
4440 CHECK_TYPELOAD (klass);
4442 if (mono_class_is_nullable (klass)) {
4443 MonoMethod *target_method = mono_class_get_method_from_name_checked (klass, "Box", 1, 0, error);
4444 goto_if_nok (error, exit);
4445 /* td->ip is incremented by interp_transform_call */
4446 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
4447 goto exit;
4448 } else if (!m_class_is_valuetype (klass)) {
4449 /* already boxed, do nothing. */
4450 td->ip += 5;
4451 } else {
4452 if (G_UNLIKELY (m_class_is_byreflike (klass))) {
4453 mono_error_set_bad_image (error, image, "Cannot box IsByRefLike type '%s.%s'", m_class_get_name_space (klass), m_class_get_name (klass));
4454 goto exit;
4456 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4457 size = mono_class_value_size (klass, NULL);
4458 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4459 td->vt_sp -= size;
4460 } else if (td->sp [-1].type == STACK_TYPE_R8 && m_class_get_byval_arg (klass)->type == MONO_TYPE_R4) {
4461 interp_add_ins (td, MINT_CONV_R4_R8);
4463 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
4464 goto_if_nok (error, exit);
4466 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4467 interp_add_ins (td, MINT_BOX_VT);
4468 else
4469 interp_add_ins (td, MINT_BOX);
4470 td->last_ins->data [0] = get_data_item_index (td, vtable);
4471 td->last_ins->data [1] = 0;
4472 SET_TYPE(td->sp - 1, STACK_TYPE_O, klass);
4473 td->ip += 5;
4476 break;
4478 case CEE_NEWARR: {
4479 CHECK_STACK (td, 1);
4480 token = read32 (td->ip + 1);
4482 if (method->wrapper_type != MONO_WRAPPER_NONE)
4483 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4484 else
4485 klass = mini_get_class (method, token, generic_context);
4486 CHECK_TYPELOAD (klass);
4488 unsigned char lentype = (td->sp - 1)->type;
4489 if (lentype == STACK_TYPE_I8) {
4490 /* mimic mini behaviour */
4491 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
4492 } else {
4493 g_assert (lentype == STACK_TYPE_I4);
4494 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
4496 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
4497 interp_add_ins (td, MINT_NEWARR);
4498 td->last_ins->data [0] = get_data_item_index (td, klass);
4499 SET_TYPE (td->sp - 1, STACK_TYPE_O, klass);
4500 td->ip += 5;
4501 break;
4503 case CEE_LDLEN:
4504 CHECK_STACK (td, 1);
4505 SIMPLE_OP (td, MINT_LDLEN);
4506 #ifdef MONO_BIG_ARRAYS
4507 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
4508 #else
4509 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
4510 #endif
4511 break;
4512 case CEE_LDELEMA:
4513 CHECK_STACK (td, 2);
4514 ENSURE_I4 (td, 1);
4515 token = read32 (td->ip + 1);
4517 if (method->wrapper_type != MONO_WRAPPER_NONE)
4518 klass = (MonoClass *) mono_method_get_wrapper_data (method, token);
4519 else
4520 klass = mini_get_class (method, token, generic_context);
4522 CHECK_TYPELOAD (klass);
4524 if (!m_class_is_valuetype (klass) && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
4526 * Check the class for failures before the type check, which can
4527 * throw other exceptions.
4529 mono_class_setup_vtable (klass);
4530 CHECK_TYPELOAD (klass);
4531 interp_add_ins (td, MINT_LDELEMA_TC);
4532 } else {
4533 interp_add_ins (td, MINT_LDELEMA);
4535 td->last_ins->data [0] = get_data_item_index (td, klass);
4536 /* according to spec, ldelema bytecode is only used for 1-dim arrays */
4537 td->last_ins->data [1] = 2;
4538 readonly = FALSE;
4540 td->ip += 5;
4541 --td->sp;
4542 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4543 break;
4544 case CEE_LDELEM_I1:
4545 CHECK_STACK (td, 2);
4546 ENSURE_I4 (td, 1);
4547 SIMPLE_OP (td, MINT_LDELEM_I1);
4548 --td->sp;
4549 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4550 break;
4551 case CEE_LDELEM_U1:
4552 CHECK_STACK (td, 2);
4553 ENSURE_I4 (td, 1);
4554 SIMPLE_OP (td, MINT_LDELEM_U1);
4555 --td->sp;
4556 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4557 break;
4558 case CEE_LDELEM_I2:
4559 CHECK_STACK (td, 2);
4560 ENSURE_I4 (td, 1);
4561 SIMPLE_OP (td, MINT_LDELEM_I2);
4562 --td->sp;
4563 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4564 break;
4565 case CEE_LDELEM_U2:
4566 CHECK_STACK (td, 2);
4567 ENSURE_I4 (td, 1);
4568 SIMPLE_OP (td, MINT_LDELEM_U2);
4569 --td->sp;
4570 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4571 break;
4572 case CEE_LDELEM_I4:
4573 CHECK_STACK (td, 2);
4574 ENSURE_I4 (td, 1);
4575 SIMPLE_OP (td, MINT_LDELEM_I4);
4576 --td->sp;
4577 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4578 break;
4579 case CEE_LDELEM_U4:
4580 CHECK_STACK (td, 2);
4581 ENSURE_I4 (td, 1);
4582 SIMPLE_OP (td, MINT_LDELEM_U4);
4583 --td->sp;
4584 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4585 break;
4586 case CEE_LDELEM_I8:
4587 CHECK_STACK (td, 2);
4588 ENSURE_I4 (td, 1);
4589 SIMPLE_OP (td, MINT_LDELEM_I8);
4590 --td->sp;
4591 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4592 break;
4593 case CEE_LDELEM_I:
4594 CHECK_STACK (td, 2);
4595 ENSURE_I4 (td, 1);
4596 SIMPLE_OP (td, MINT_LDELEM_I);
4597 --td->sp;
4598 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4599 break;
4600 case CEE_LDELEM_R4:
4601 CHECK_STACK (td, 2);
4602 ENSURE_I4 (td, 1);
4603 SIMPLE_OP (td, MINT_LDELEM_R4);
4604 --td->sp;
4605 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4606 break;
4607 case CEE_LDELEM_R8:
4608 CHECK_STACK (td, 2);
4609 ENSURE_I4 (td, 1);
4610 SIMPLE_OP (td, MINT_LDELEM_R8);
4611 --td->sp;
4612 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4613 break;
4614 case CEE_LDELEM_REF:
4615 CHECK_STACK (td, 2);
4616 ENSURE_I4 (td, 1);
4617 SIMPLE_OP (td, MINT_LDELEM_REF);
4618 --td->sp;
4619 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
4620 break;
4621 case CEE_LDELEM:
4622 CHECK_STACK (td, 2);
4623 token = read32 (td->ip + 1);
4624 klass = mini_get_class (method, token, generic_context);
4625 CHECK_TYPELOAD (klass);
4626 switch (mint_type (m_class_get_byval_arg (klass))) {
4627 case MINT_TYPE_I1:
4628 ENSURE_I4 (td, 1);
4629 SIMPLE_OP (td, MINT_LDELEM_I1);
4630 --td->sp;
4631 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4632 break;
4633 case MINT_TYPE_U1:
4634 ENSURE_I4 (td, 1);
4635 SIMPLE_OP (td, MINT_LDELEM_U1);
4636 --td->sp;
4637 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4638 break;
4639 case MINT_TYPE_U2:
4640 ENSURE_I4 (td, 1);
4641 SIMPLE_OP (td, MINT_LDELEM_U2);
4642 --td->sp;
4643 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4644 break;
4645 case MINT_TYPE_I2:
4646 ENSURE_I4 (td, 1);
4647 SIMPLE_OP (td, MINT_LDELEM_I2);
4648 --td->sp;
4649 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4650 break;
4651 case MINT_TYPE_I4:
4652 ENSURE_I4 (td, 1);
4653 SIMPLE_OP (td, MINT_LDELEM_I4);
4654 --td->sp;
4655 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4656 break;
4657 case MINT_TYPE_I8:
4658 ENSURE_I4 (td, 1);
4659 SIMPLE_OP (td, MINT_LDELEM_I8);
4660 --td->sp;
4661 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4662 break;
4663 case MINT_TYPE_R4:
4664 ENSURE_I4 (td, 1);
4665 SIMPLE_OP (td, MINT_LDELEM_R4);
4666 --td->sp;
4667 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4668 break;
4669 case MINT_TYPE_R8:
4670 ENSURE_I4 (td, 1);
4671 SIMPLE_OP (td, MINT_LDELEM_R8);
4672 --td->sp;
4673 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4674 break;
4675 case MINT_TYPE_O:
4676 ENSURE_I4 (td, 1);
4677 SIMPLE_OP (td, MINT_LDELEM_REF);
4678 --td->sp;
4679 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
4680 break;
4681 case MINT_TYPE_VT: {
4682 int size = mono_class_value_size (klass, NULL);
4683 ENSURE_I4 (td, 1);
4684 SIMPLE_OP (td, MINT_LDELEM_VT);
4685 WRITE32_INS (td->last_ins, 0, &size);
4686 --td->sp;
4687 SET_TYPE (td->sp - 1, STACK_TYPE_VT, klass);
4688 PUSH_VT (td, size);
4689 break;
4691 default: {
4692 GString *res = g_string_new ("");
4693 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
4694 g_print ("LDELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
4695 g_string_free (res, TRUE);
4696 g_assert (0);
4697 break;
4700 td->ip += 4;
4701 break;
4702 case CEE_STELEM_I:
4703 CHECK_STACK (td, 3);
4704 ENSURE_I4 (td, 2);
4705 SIMPLE_OP (td, MINT_STELEM_I);
4706 td->sp -= 3;
4707 break;
4708 case CEE_STELEM_I1:
4709 CHECK_STACK (td, 3);
4710 ENSURE_I4 (td, 2);
4711 SIMPLE_OP (td, MINT_STELEM_I1);
4712 td->sp -= 3;
4713 break;
4714 case CEE_STELEM_I2:
4715 CHECK_STACK (td, 3);
4716 ENSURE_I4 (td, 2);
4717 SIMPLE_OP (td, MINT_STELEM_I2);
4718 td->sp -= 3;
4719 break;
4720 case CEE_STELEM_I4:
4721 CHECK_STACK (td, 3);
4722 ENSURE_I4 (td, 2);
4723 SIMPLE_OP (td, MINT_STELEM_I4);
4724 td->sp -= 3;
4725 break;
4726 case CEE_STELEM_I8:
4727 CHECK_STACK (td, 3);
4728 ENSURE_I4 (td, 2);
4729 SIMPLE_OP (td, MINT_STELEM_I8);
4730 td->sp -= 3;
4731 break;
4732 case CEE_STELEM_R4:
4733 CHECK_STACK (td, 3);
4734 ENSURE_I4 (td, 2);
4735 SIMPLE_OP (td, MINT_STELEM_R4);
4736 td->sp -= 3;
4737 break;
4738 case CEE_STELEM_R8:
4739 CHECK_STACK (td, 3);
4740 ENSURE_I4 (td, 2);
4741 SIMPLE_OP (td, MINT_STELEM_R8);
4742 td->sp -= 3;
4743 break;
4744 case CEE_STELEM_REF:
4745 CHECK_STACK (td, 3);
4746 ENSURE_I4 (td, 2);
4747 SIMPLE_OP (td, MINT_STELEM_REF);
4748 td->sp -= 3;
4749 break;
4750 case CEE_STELEM:
4751 CHECK_STACK (td, 3);
4752 ENSURE_I4 (td, 2);
4753 token = read32 (td->ip + 1);
4754 klass = mini_get_class (method, token, generic_context);
4755 CHECK_TYPELOAD (klass);
4756 switch (mint_type (m_class_get_byval_arg (klass))) {
4757 case MINT_TYPE_I1:
4758 SIMPLE_OP (td, MINT_STELEM_I1);
4759 break;
4760 case MINT_TYPE_U1:
4761 SIMPLE_OP (td, MINT_STELEM_U1);
4762 break;
4763 case MINT_TYPE_I2:
4764 SIMPLE_OP (td, MINT_STELEM_I2);
4765 break;
4766 case MINT_TYPE_U2:
4767 SIMPLE_OP (td, MINT_STELEM_U2);
4768 break;
4769 case MINT_TYPE_I4:
4770 SIMPLE_OP (td, MINT_STELEM_I4);
4771 break;
4772 case MINT_TYPE_I8:
4773 SIMPLE_OP (td, MINT_STELEM_I8);
4774 break;
4775 case MINT_TYPE_R4:
4776 SIMPLE_OP (td, MINT_STELEM_R4);
4777 break;
4778 case MINT_TYPE_R8:
4779 SIMPLE_OP (td, MINT_STELEM_R8);
4780 break;
4781 case MINT_TYPE_O:
4782 SIMPLE_OP (td, MINT_STELEM_REF);
4783 break;
4784 case MINT_TYPE_VT: {
4785 int size = mono_class_value_size (klass, NULL);
4786 SIMPLE_OP (td, MINT_STELEM_VT);
4787 td->last_ins->data [0] = get_data_item_index (td, klass);
4788 WRITE32_INS (td->last_ins, 1, &size);
4789 POP_VT (td, size);
4790 break;
4792 default: {
4793 GString *res = g_string_new ("");
4794 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
4795 g_print ("STELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
4796 g_string_free (res, TRUE);
4797 g_assert (0);
4798 break;
4801 td->ip += 4;
4802 td->sp -= 3;
4803 break;
4804 #if 0
4805 case CEE_CONV_OVF_U1:
4807 case CEE_CONV_OVF_I8:
4809 #if SIZEOF_VOID_P == 8
4810 case CEE_CONV_OVF_U:
4811 #endif
4812 #endif
4813 case CEE_CKFINITE:
4814 CHECK_STACK (td, 1);
4815 SIMPLE_OP (td, MINT_CKFINITE);
4816 break;
4817 case CEE_MKREFANY:
4818 CHECK_STACK (td, 1);
4820 token = read32 (td->ip + 1);
4821 klass = mini_get_class (method, token, generic_context);
4822 CHECK_TYPELOAD (klass);
4824 interp_add_ins (td, MINT_MKREFANY);
4825 td->last_ins->data [0] = get_data_item_index (td, klass);
4827 td->ip += 5;
4828 PUSH_VT (td, sizeof (MonoTypedRef));
4829 SET_TYPE(td->sp - 1, STACK_TYPE_VT, mono_defaults.typed_reference_class);
4830 break;
4831 case CEE_REFANYVAL: {
4832 CHECK_STACK (td, 1);
4834 token = read32 (td->ip + 1);
4835 klass = mini_get_class (method, token, generic_context);
4836 CHECK_TYPELOAD (klass);
4838 interp_add_ins (td, MINT_REFANYVAL);
4839 td->last_ins->data [0] = get_data_item_index (td, klass);
4841 POP_VT (td, sizeof (MonoTypedRef));
4842 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4844 td->ip += 5;
4845 break;
4847 case CEE_CONV_OVF_I1:
4848 case CEE_CONV_OVF_I1_UN: {
4849 gboolean is_un = *td->ip == CEE_CONV_OVF_I1_UN;
4850 CHECK_STACK (td, 1);
4851 switch (td->sp [-1].type) {
4852 case STACK_TYPE_R8:
4853 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_UN_R8 : MINT_CONV_OVF_I1_R8);
4854 break;
4855 case STACK_TYPE_I4:
4856 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U4 : MINT_CONV_OVF_I1_I4);
4857 break;
4858 case STACK_TYPE_I8:
4859 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U8 : MINT_CONV_OVF_I1_I8);
4860 break;
4861 default:
4862 g_assert_not_reached ();
4864 ++td->ip;
4865 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4866 break;
4868 case CEE_CONV_OVF_U1:
4869 case CEE_CONV_OVF_U1_UN:
4870 CHECK_STACK (td, 1);
4871 switch (td->sp [-1].type) {
4872 case STACK_TYPE_R8:
4873 interp_add_ins (td, MINT_CONV_OVF_U1_R8);
4874 break;
4875 case STACK_TYPE_I4:
4876 interp_add_ins (td, MINT_CONV_OVF_U1_I4);
4877 break;
4878 case STACK_TYPE_I8:
4879 interp_add_ins (td, MINT_CONV_OVF_U1_I8);
4880 break;
4881 default:
4882 g_assert_not_reached ();
4884 ++td->ip;
4885 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4886 break;
4887 case CEE_CONV_OVF_I2:
4888 case CEE_CONV_OVF_I2_UN: {
4889 gboolean is_un = *td->ip == CEE_CONV_OVF_I2_UN;
4890 CHECK_STACK (td, 1);
4891 switch (td->sp [-1].type) {
4892 case STACK_TYPE_R8:
4893 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_UN_R8 : MINT_CONV_OVF_I2_R8);
4894 break;
4895 case STACK_TYPE_I4:
4896 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U4 : MINT_CONV_OVF_I2_I4);
4897 break;
4898 case STACK_TYPE_I8:
4899 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U8 : MINT_CONV_OVF_I2_I8);
4900 break;
4901 default:
4902 g_assert_not_reached ();
4904 ++td->ip;
4905 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4906 break;
4908 case CEE_CONV_OVF_U2_UN:
4909 case CEE_CONV_OVF_U2:
4910 CHECK_STACK (td, 1);
4911 switch (td->sp [-1].type) {
4912 case STACK_TYPE_R8:
4913 interp_add_ins (td, MINT_CONV_OVF_U2_R8);
4914 break;
4915 case STACK_TYPE_I4:
4916 interp_add_ins (td, MINT_CONV_OVF_U2_I4);
4917 break;
4918 case STACK_TYPE_I8:
4919 interp_add_ins (td, MINT_CONV_OVF_U2_I8);
4920 break;
4921 default:
4922 g_assert_not_reached ();
4924 ++td->ip;
4925 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4926 break;
4927 #if SIZEOF_VOID_P == 4
4928 case CEE_CONV_OVF_I:
4929 #endif
4930 case CEE_CONV_OVF_I4:
4931 case CEE_CONV_OVF_I4_UN:
4932 CHECK_STACK (td, 1);
4933 switch (td->sp [-1].type) {
4934 case STACK_TYPE_R4:
4935 interp_add_ins (td, MINT_CONV_OVF_I4_R4);
4936 break;
4937 case STACK_TYPE_R8:
4938 interp_add_ins (td, MINT_CONV_OVF_I4_R8);
4939 break;
4940 case STACK_TYPE_I4:
4941 if (*td->ip == CEE_CONV_OVF_I4_UN)
4942 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
4943 break;
4944 case STACK_TYPE_I8:
4945 if (*td->ip == CEE_CONV_OVF_I4_UN)
4946 interp_add_ins (td, MINT_CONV_OVF_I4_U8);
4947 else
4948 interp_add_ins (td, MINT_CONV_OVF_I4_I8);
4949 break;
4950 default:
4951 g_assert_not_reached ();
4953 ++td->ip;
4954 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4955 break;
4956 #if SIZEOF_VOID_P == 4
4957 case CEE_CONV_OVF_U:
4958 #endif
4959 case CEE_CONV_OVF_U4:
4960 case CEE_CONV_OVF_U4_UN:
4961 CHECK_STACK (td, 1);
4962 switch (td->sp [-1].type) {
4963 case STACK_TYPE_R4:
4964 interp_add_ins (td, MINT_CONV_OVF_U4_R4);
4965 break;
4966 case STACK_TYPE_R8:
4967 interp_add_ins (td, MINT_CONV_OVF_U4_R8);
4968 break;
4969 case STACK_TYPE_I4:
4970 if (*td->ip != CEE_CONV_OVF_U4_UN)
4971 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
4972 break;
4973 case STACK_TYPE_I8:
4974 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
4975 break;
4976 default:
4977 g_assert_not_reached ();
4979 ++td->ip;
4980 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4981 break;
4982 #if SIZEOF_VOID_P == 8
4983 case CEE_CONV_OVF_I:
4984 #endif
4985 case CEE_CONV_OVF_I8:
4986 CHECK_STACK (td, 1);
4987 switch (td->sp [-1].type) {
4988 case STACK_TYPE_R4:
4989 interp_add_ins (td, MINT_CONV_OVF_I8_R4);
4990 break;
4991 case STACK_TYPE_R8:
4992 interp_add_ins (td, MINT_CONV_OVF_I8_R8);
4993 break;
4994 case STACK_TYPE_I4:
4995 interp_add_ins (td, MINT_CONV_I8_I4);
4996 break;
4997 case STACK_TYPE_I8:
4998 break;
4999 default:
5000 g_assert_not_reached ();
5002 ++td->ip;
5003 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5004 break;
5005 #if SIZEOF_VOID_P == 8
5006 case CEE_CONV_OVF_U:
5007 #endif
5008 case CEE_CONV_OVF_U8:
5009 CHECK_STACK (td, 1);
5010 switch (td->sp [-1].type) {
5011 case STACK_TYPE_R4:
5012 interp_add_ins (td, MINT_CONV_OVF_U8_R4);
5013 break;
5014 case STACK_TYPE_R8:
5015 interp_add_ins (td, MINT_CONV_OVF_U8_R8);
5016 break;
5017 case STACK_TYPE_I4:
5018 interp_add_ins (td, MINT_CONV_OVF_U8_I4);
5019 break;
5020 case STACK_TYPE_I8:
5021 interp_add_ins (td, MINT_CONV_OVF_U8_I8);
5022 break;
5023 default:
5024 g_assert_not_reached ();
5026 ++td->ip;
5027 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5028 break;
5029 case CEE_LDTOKEN: {
5030 int size;
5031 gpointer handle;
5032 token = read32 (td->ip + 1);
5033 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
5034 handle = mono_method_get_wrapper_data (method, token);
5035 klass = (MonoClass *) mono_method_get_wrapper_data (method, token + 1);
5036 if (klass == mono_defaults.typehandle_class)
5037 handle = m_class_get_byval_arg ((MonoClass *) handle);
5039 if (generic_context) {
5040 handle = mono_class_inflate_generic_type_checked ((MonoType*)handle, generic_context, error);
5041 goto_if_nok (error, exit);
5043 } else {
5044 handle = mono_ldtoken_checked (image, token, &klass, generic_context, error);
5045 goto_if_nok (error, exit);
5047 mono_class_init_internal (klass);
5048 mt = mint_type (m_class_get_byval_arg (klass));
5049 g_assert (mt == MINT_TYPE_VT);
5050 size = mono_class_value_size (klass, NULL);
5051 g_assert (size == sizeof(gpointer));
5053 const unsigned char *next_ip = td->ip + 5;
5054 MonoMethod *cmethod;
5055 if (next_ip < end &&
5056 !td->is_bb_start [next_ip - td->il_code] &&
5057 (*next_ip == CEE_CALL || *next_ip == CEE_CALLVIRT) &&
5058 (cmethod = mono_get_method_checked (image, read32 (next_ip + 1), NULL, generic_context, error)) &&
5059 (cmethod->klass == mono_defaults.systemtype_class) &&
5060 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
5061 interp_add_ins (td, MINT_MONO_LDPTR);
5062 gpointer systype = mono_type_get_object_checked (domain, (MonoType*)handle, error);
5063 goto_if_nok (error, exit);
5064 td->last_ins->data [0] = get_data_item_index (td, systype);
5065 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5066 td->ip = next_ip + 5;
5067 } else {
5068 PUSH_VT (td, sizeof(gpointer));
5069 interp_add_ins (td, MINT_LDTOKEN);
5070 td->last_ins->data [0] = get_data_item_index (td, handle);
5071 PUSH_TYPE (td, stack_type [mt], klass);
5072 td->ip += 5;
5075 break;
5077 case CEE_ADD_OVF:
5078 binary_arith_op(td, MINT_ADD_OVF_I4);
5079 ++td->ip;
5080 break;
5081 case CEE_ADD_OVF_UN:
5082 binary_arith_op(td, MINT_ADD_OVF_UN_I4);
5083 ++td->ip;
5084 break;
5085 case CEE_MUL_OVF:
5086 binary_arith_op(td, MINT_MUL_OVF_I4);
5087 ++td->ip;
5088 break;
5089 case CEE_MUL_OVF_UN:
5090 binary_arith_op(td, MINT_MUL_OVF_UN_I4);
5091 ++td->ip;
5092 break;
5093 case CEE_SUB_OVF:
5094 binary_arith_op(td, MINT_SUB_OVF_I4);
5095 ++td->ip;
5096 break;
5097 case CEE_SUB_OVF_UN:
5098 binary_arith_op(td, MINT_SUB_OVF_UN_I4);
5099 ++td->ip;
5100 break;
5101 case CEE_ENDFINALLY: {
5102 g_assert (td->clause_indexes [in_offset] != -1);
5103 td->sp = td->stack;
5104 SIMPLE_OP (td, MINT_ENDFINALLY);
5105 td->last_ins->data [0] = td->clause_indexes [in_offset];
5106 break;
5108 case CEE_LEAVE:
5109 case CEE_LEAVE_S: {
5110 int offset;
5112 if (*td->ip == CEE_LEAVE)
5113 offset = 5 + read32 (td->ip + 1);
5114 else
5115 offset = 2 + (gint8)td->ip [1];
5117 td->sp = td->stack;
5118 if (td->clause_indexes [in_offset] != -1) {
5119 /* LEAVE instructions in catch clauses need to check for abort exceptions */
5120 handle_branch (td, MINT_LEAVE_S_CHECK, MINT_LEAVE_CHECK, offset);
5121 } else {
5122 handle_branch (td, MINT_LEAVE_S, MINT_LEAVE, offset);
5125 if (*td->ip == CEE_LEAVE)
5126 td->ip += 5;
5127 else
5128 td->ip += 2;
5129 break;
5131 case MONO_CUSTOM_PREFIX:
5132 ++td->ip;
5133 switch (*td->ip) {
5134 case CEE_MONO_RETHROW:
5135 CHECK_STACK (td, 1);
5136 SIMPLE_OP (td, MINT_MONO_RETHROW);
5137 td->sp = td->stack;
5138 break;
5140 case CEE_MONO_LD_DELEGATE_METHOD_PTR:
5141 --td->sp;
5142 td->ip += 1;
5143 interp_add_ins (td, MINT_LD_DELEGATE_METHOD_PTR);
5144 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5145 break;
5146 case CEE_MONO_CALLI_EXTRA_ARG:
5147 /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
5148 interp_add_ins (td, MINT_POP);
5149 td->last_ins->data [0] = 1;
5150 --td->sp;
5151 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
5152 goto exit;
5153 break;
5154 case CEE_MONO_JIT_ICALL_ADDR: {
5155 guint32 token;
5156 gpointer func;
5158 token = read32 (td->ip + 1);
5159 td->ip += 5;
5160 func = mono_method_get_wrapper_data (method, token);
5162 interp_add_ins (td, MINT_LDFTN);
5163 td->last_ins->data [0] = get_data_item_index (td, func);
5164 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5165 break;
5167 case CEE_MONO_ICALL: {
5168 guint32 token;
5169 gpointer func;
5170 MonoJitICallInfo *info;
5171 int icall_op;
5173 token = read32 (td->ip + 1);
5174 td->ip += 5;
5175 func = mono_method_get_wrapper_data (method, token);
5176 info = mono_find_jit_icall_by_addr (func);
5177 g_assert (info);
5179 CHECK_STACK (td, info->sig->param_count);
5180 if (!strcmp (info->name, "mono_threads_attach_coop")) {
5181 rtm->needs_thread_attach = 1;
5183 /* attach needs two arguments, and has one return value: leave one element on the stack */
5184 interp_add_ins (td, MINT_POP);
5185 td->last_ins->data [0] = 0;
5186 } else if (!strcmp (info->name, "mono_threads_detach_coop")) {
5187 g_assert (rtm->needs_thread_attach);
5189 /* detach consumes two arguments, and no return value: drop both of them */
5190 interp_add_ins (td, MINT_POP);
5191 td->last_ins->data [0] = 0;
5192 interp_add_ins (td, MINT_POP);
5193 td->last_ins->data [0] = 0;
5194 } else {
5195 icall_op = interp_icall_op_for_sig (info->sig);
5196 g_assert (icall_op != -1);
5198 interp_add_ins (td, icall_op);
5199 td->last_ins->data [0] = get_data_item_index (td, func);
5201 td->sp -= info->sig->param_count;
5203 if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
5204 int mt = mint_type (info->sig->ret);
5205 PUSH_SIMPLE_TYPE(td, stack_type [mt]);
5207 break;
5209 case CEE_MONO_VTADDR: {
5210 int size;
5211 CHECK_STACK (td, 1);
5212 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
5213 size = mono_class_native_size(td->sp [-1].klass, NULL);
5214 else
5215 size = mono_class_value_size(td->sp [-1].klass, NULL);
5216 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
5217 interp_add_ins (td, MINT_VTRESULT);
5218 td->last_ins->data [0] = 0;
5219 WRITE32_INS (td->last_ins, 1, &size);
5220 td->vt_sp -= size;
5221 ++td->ip;
5222 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5223 break;
5225 case CEE_MONO_LDPTR:
5226 case CEE_MONO_CLASSCONST:
5227 token = read32 (td->ip + 1);
5228 td->ip += 5;
5229 interp_add_ins (td, MINT_MONO_LDPTR);
5230 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5231 td->sp [0].type = STACK_TYPE_I;
5232 ++td->sp;
5233 break;
5234 case CEE_MONO_OBJADDR:
5235 CHECK_STACK (td, 1);
5236 ++td->ip;
5237 td->sp[-1].type = STACK_TYPE_MP;
5238 /* do nothing? */
5239 break;
5240 case CEE_MONO_NEWOBJ:
5241 token = read32 (td->ip + 1);
5242 td->ip += 5;
5243 interp_add_ins (td, MINT_MONO_NEWOBJ);
5244 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5245 td->sp [0].type = STACK_TYPE_O;
5246 ++td->sp;
5247 break;
5248 case CEE_MONO_RETOBJ:
5249 CHECK_STACK (td, 1);
5250 token = read32 (td->ip + 1);
5251 td->ip += 5;
5252 interp_add_ins (td, MINT_MONO_RETOBJ);
5253 td->sp--;
5255 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5257 /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/
5259 if (td->sp > td->stack)
5260 g_warning ("CEE_MONO_RETOBJ: more values on stack: %d", td->sp-td->stack);
5261 break;
5262 case CEE_MONO_LDNATIVEOBJ:
5263 token = read32 (td->ip + 1);
5264 td->ip += 5;
5265 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5266 g_assert(m_class_is_valuetype (klass));
5267 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5268 break;
5269 case CEE_MONO_TLS: {
5270 gint32 key = read32 (td->ip + 1);
5271 td->ip += 5;
5272 g_assert (key < TLS_KEY_NUM);
5273 interp_add_ins (td, MINT_MONO_TLS);
5274 WRITE32_INS (td->last_ins, 0, &key);
5275 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5276 break;
5278 case CEE_MONO_ATOMIC_STORE_I4:
5279 CHECK_STACK (td, 2);
5280 SIMPLE_OP (td, MINT_MONO_ATOMIC_STORE_I4);
5281 td->sp -= 2;
5282 td->ip++;
5283 break;
5284 case CEE_MONO_SAVE_LMF:
5285 case CEE_MONO_RESTORE_LMF:
5286 case CEE_MONO_NOT_TAKEN:
5287 ++td->ip;
5288 break;
5289 case CEE_MONO_LDPTR_INT_REQ_FLAG:
5290 interp_add_ins (td, MINT_MONO_LDPTR);
5291 td->last_ins->data [0] = get_data_item_index (td, mono_thread_interruption_request_flag ());
5292 PUSH_TYPE (td, STACK_TYPE_MP, NULL);
5293 ++td->ip;
5294 break;
5295 case CEE_MONO_MEMORY_BARRIER:
5296 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5297 ++td->ip;
5298 break;
5299 case CEE_MONO_LDDOMAIN:
5300 interp_add_ins (td, MINT_MONO_LDDOMAIN);
5301 td->sp [0].type = STACK_TYPE_I;
5302 ++td->sp;
5303 ++td->ip;
5304 break;
5305 default:
5306 g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td->ip, td->ip-header->code);
5308 break;
5309 #if 0
5310 case CEE_PREFIX7:
5311 case CEE_PREFIX6:
5312 case CEE_PREFIX5:
5313 case CEE_PREFIX4:
5314 case CEE_PREFIX3:
5315 case CEE_PREFIX2:
5316 case CEE_PREFIXREF: ves_abort(); break;
5317 #endif
5319 * Note: Exceptions thrown when executing a prefixed opcode need
5320 * to take into account the number of prefix bytes (usually the
5321 * throw point is just (ip - n_prefix_bytes).
5323 case CEE_PREFIX1:
5324 ++td->ip;
5325 switch (*td->ip) {
5326 case CEE_ARGLIST:
5327 interp_add_ins (td, MINT_ARGLIST);
5328 PUSH_VT (td, SIZEOF_VOID_P);
5329 PUSH_SIMPLE_TYPE (td, STACK_TYPE_VT);
5330 ++td->ip;
5331 break;
5332 case CEE_CEQ:
5333 CHECK_STACK(td, 2);
5334 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP) {
5335 interp_add_ins (td, MINT_CEQ_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5336 } else {
5337 if (td->sp [-1].type == STACK_TYPE_R4 && td->sp [-2].type == STACK_TYPE_R8)
5338 interp_add_ins (td, MINT_CONV_R8_R4);
5339 if (td->sp [-1].type == STACK_TYPE_R8 && td->sp [-2].type == STACK_TYPE_R4)
5340 interp_add_ins (td, MINT_CONV_R8_R4_SP);
5341 interp_add_ins (td, MINT_CEQ_I4 + td->sp [-1].type - STACK_TYPE_I4);
5343 --td->sp;
5344 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5345 ++td->ip;
5346 break;
5347 case CEE_CGT:
5348 CHECK_STACK(td, 2);
5349 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5350 interp_add_ins (td, MINT_CGT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5351 else
5352 interp_add_ins (td, MINT_CGT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5353 --td->sp;
5354 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5355 ++td->ip;
5356 break;
5357 case CEE_CGT_UN:
5358 CHECK_STACK(td, 2);
5359 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5360 interp_add_ins (td, MINT_CGT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5361 else
5362 interp_add_ins (td, MINT_CGT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5363 --td->sp;
5364 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5365 ++td->ip;
5366 break;
5367 case CEE_CLT:
5368 CHECK_STACK(td, 2);
5369 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5370 interp_add_ins (td, MINT_CLT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5371 else
5372 interp_add_ins (td, MINT_CLT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5373 --td->sp;
5374 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5375 ++td->ip;
5376 break;
5377 case CEE_CLT_UN:
5378 CHECK_STACK(td, 2);
5379 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5380 interp_add_ins (td, MINT_CLT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5381 else
5382 interp_add_ins (td, MINT_CLT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5383 --td->sp;
5384 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5385 ++td->ip;
5386 break;
5387 case CEE_LDVIRTFTN: /* fallthrough */
5388 case CEE_LDFTN: {
5389 MonoMethod *m;
5390 if (*td->ip == CEE_LDVIRTFTN) {
5391 CHECK_STACK (td, 1);
5392 --td->sp;
5394 token = read32 (td->ip + 1);
5395 if (method->wrapper_type != MONO_WRAPPER_NONE)
5396 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
5397 else {
5398 m = mono_get_method_checked (image, token, NULL, generic_context, error);
5399 goto_if_nok (error, exit);
5402 if (!mono_method_can_access_method (method, m))
5403 interp_generate_mae_throw (td, method, m);
5405 if (method->wrapper_type == MONO_WRAPPER_NONE && m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
5406 m = mono_marshal_get_synchronized_wrapper (m);
5408 interp_add_ins (td, *td->ip == CEE_LDFTN ? MINT_LDFTN : MINT_LDVIRTFTN);
5409 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
5410 goto_if_nok (error, exit);
5411 td->ip += 5;
5412 PUSH_SIMPLE_TYPE (td, STACK_TYPE_F);
5413 break;
5415 case CEE_LDARG: {
5416 int arg_n = read16 (td->ip + 1);
5417 if (td->method == method)
5418 load_arg (td, arg_n);
5419 else
5420 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
5421 td->ip += 3;
5422 break;
5424 case CEE_LDARGA: {
5425 INLINE_FAILURE;
5426 int n = read16 (td->ip + 1);
5428 get_arg_type_exact (td, n, &mt);
5429 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
5430 td->last_ins->data [0] = n;
5431 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5432 td->ip += 3;
5433 break;
5435 case CEE_STARG: {
5436 int arg_n = read16 (td->ip + 1);
5437 if (td->method == method)
5438 store_arg (td, arg_n);
5439 else
5440 store_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
5441 td->ip += 3;
5442 break;
5444 case CEE_LDLOC:
5445 load_local (td, read16 (td->ip + 1));
5446 td->ip += 3;
5447 break;
5448 case CEE_LDLOCA:
5449 interp_add_ins (td, MINT_LDLOCA_S);
5450 td->last_ins->data [0] = td->rtm->local_offsets [read16 (td->ip + 1)];
5451 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5452 td->ip += 3;
5453 break;
5454 case CEE_STLOC:
5455 store_local (td, read16 (td->ip + 1));
5456 td->ip += 3;
5457 break;
5458 case CEE_LOCALLOC:
5459 CHECK_STACK (td, 1);
5460 #if SIZEOF_VOID_P == 8
5461 if (td->sp [-1].type == STACK_TYPE_I8)
5462 interp_add_ins (td, MINT_CONV_I4_I8);
5463 #endif
5464 interp_add_ins (td, MINT_LOCALLOC);
5465 if (td->sp != td->stack + 1)
5466 g_warning("CEE_LOCALLOC: stack not empty");
5467 ++td->ip;
5468 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5469 break;
5470 #if 0
5471 case CEE_UNUSED57: ves_abort(); break;
5472 #endif
5473 case CEE_ENDFILTER:
5474 interp_add_ins (td, MINT_ENDFILTER);
5475 ++td->ip;
5476 break;
5477 case CEE_UNALIGNED_:
5478 td->ip += 2;
5479 break;
5480 case CEE_VOLATILE_:
5481 ++td->ip;
5482 volatile_ = TRUE;
5483 break;
5484 case CEE_TAIL_:
5485 ++td->ip;
5486 /* FIX: should do something? */;
5487 // TODO: This should raise a method_tail_call profiler event.
5488 break;
5489 case CEE_INITOBJ:
5490 CHECK_STACK(td, 1);
5491 token = read32 (td->ip + 1);
5492 klass = mini_get_class (method, token, generic_context);
5493 CHECK_TYPELOAD (klass);
5494 if (m_class_is_valuetype (klass)) {
5495 interp_add_ins (td, MINT_INITOBJ);
5496 i32 = mono_class_value_size (klass, NULL);
5497 WRITE32_INS (td->last_ins, 0, &i32);
5498 } else {
5499 interp_add_ins (td, MINT_LDNULL);
5500 interp_add_ins (td, MINT_STIND_REF);
5502 td->ip += 5;
5503 --td->sp;
5504 break;
5505 case CEE_CPBLK:
5506 CHECK_STACK(td, 3);
5507 /* FIX? convert length to I8? */
5508 if (volatile_)
5509 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5510 interp_add_ins (td, MINT_CPBLK);
5511 BARRIER_IF_VOLATILE (td);
5512 td->sp -= 3;
5513 ++td->ip;
5514 break;
5515 case CEE_READONLY_:
5516 readonly = TRUE;
5517 td->ip += 1;
5518 break;
5519 case CEE_CONSTRAINED_:
5520 token = read32 (td->ip + 1);
5521 constrained_class = mini_get_class (method, token, generic_context);
5522 CHECK_TYPELOAD (constrained_class);
5523 td->ip += 5;
5524 break;
5525 case CEE_INITBLK:
5526 CHECK_STACK(td, 3);
5527 BARRIER_IF_VOLATILE (td);
5528 interp_add_ins (td, MINT_INITBLK);
5529 td->sp -= 3;
5530 td->ip += 1;
5531 break;
5532 case CEE_NO_:
5533 /* FIXME: implement */
5534 td->ip += 2;
5535 break;
5536 case CEE_RETHROW: {
5537 int clause_index = td->clause_indexes [in_offset];
5538 g_assert (clause_index != -1);
5539 SIMPLE_OP (td, MINT_RETHROW);
5540 td->last_ins->data [0] = rtm->exvar_offsets [clause_index];
5541 td->sp = td->stack;
5542 break;
5544 case CEE_SIZEOF: {
5545 gint32 size;
5546 token = read32 (td->ip + 1);
5547 td->ip += 5;
5548 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (m_class_get_image (method->klass)) && !generic_context) {
5549 int align;
5550 MonoType *type = mono_type_create_from_typespec_checked (image, token, error);
5551 goto_if_nok (error, exit);
5552 size = mono_type_size (type, &align);
5553 } else {
5554 int align;
5555 MonoClass *szclass = mini_get_class (method, token, generic_context);
5556 CHECK_TYPELOAD (szclass);
5557 #if 0
5558 if (!szclass->valuetype)
5559 THROW_EX (mono_exception_from_name (mono_defaults.corlib, "System", "InvalidProgramException"), ip - 5);
5560 #endif
5561 size = mono_type_size (m_class_get_byval_arg (szclass), &align);
5563 interp_add_ins (td, MINT_LDC_I4);
5564 WRITE32_INS (td->last_ins, 0, &size);
5565 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
5566 break;
5568 case CEE_REFANYTYPE:
5569 interp_add_ins (td, MINT_REFANYTYPE);
5570 td->ip += 1;
5571 POP_VT (td, sizeof (MonoTypedRef));
5572 PUSH_VT (td, sizeof (gpointer));
5573 SET_TYPE(td->sp - 1, STACK_TYPE_VT, NULL);
5574 break;
5575 default:
5576 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);
5578 break;
5579 default:
5580 g_error ("transform.c: Unimplemented opcode: %02x at 0x%x\n", *td->ip, td->ip-header->code);
5583 // No IR instructions were added as part of the IL instruction. Extend bb_start
5584 if (prev_last_ins == td->last_ins && td->is_bb_start [in_offset] && td->ip < end)
5585 td->is_bb_start [td->ip - td->il_code] = 1;
5588 g_assert (td->ip == end);
5590 exit_ret:
5591 g_free (arg_offsets);
5592 mono_basic_block_free (original_bb);
5593 for (i = 0; i < header->code_size; ++i)
5594 g_free (td->stack_state [i]);
5595 g_free (td->stack_state);
5596 g_free (td->stack_height);
5597 g_free (td->vt_stack_size);
5598 g_free (td->clause_indexes);
5599 g_free (td->is_bb_start);
5601 return ret;
5602 exit:
5603 ret = FALSE;
5604 goto exit_ret;
5607 // We are trying to branch to an il offset that has no associated ir instruction with it.
5608 // We will branch instead to the next instruction that we find
5609 static int
5610 resolve_in_offset (TransformData *td, int il_offset)
5612 int i = il_offset;
5613 g_assert (!td->in_offsets [il_offset]);
5614 while (!td->in_offsets [i])
5615 i++;
5616 td->in_offsets [il_offset] = td->in_offsets [i];
5617 return td->in_offsets [il_offset];
5620 // We store in the in_offset array the native_offset + 1, so 0 can mean only that the il
5621 // offset is uninitialized. Otherwise 0 is valid value for first interp instruction.
5622 static int
5623 get_in_offset (TransformData *td, int il_offset)
5625 int target_offset = td->in_offsets [il_offset];
5626 if (target_offset)
5627 return target_offset - 1;
5628 return resolve_in_offset (td, il_offset) - 1;
5631 static void
5632 handle_relocations (TransformData *td)
5634 // Handle relocations
5635 for (int i = 0; i < td->relocs->len; ++i) {
5636 Reloc *reloc = (Reloc*)g_ptr_array_index (td->relocs, i);
5637 int offset = get_in_offset (td, reloc->target) - reloc->offset;
5639 switch (reloc->type) {
5640 case RELOC_SHORT_BRANCH:
5641 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
5642 td->new_code [reloc->offset + 1] = offset;
5643 break;
5644 case RELOC_LONG_BRANCH: {
5645 guint16 *v = (guint16 *) &offset;
5646 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
5647 g_assert (td->new_code [reloc->offset + 2] == 0xbeef);
5648 td->new_code [reloc->offset + 1] = *(guint16 *) v;
5649 td->new_code [reloc->offset + 2] = *(guint16 *) (v + 1);
5650 break;
5652 case RELOC_SWITCH: {
5653 guint16 *v = (guint16*)&offset;
5654 g_assert (td->new_code [reloc->offset] == 0xdead);
5655 g_assert (td->new_code [reloc->offset + 1] == 0xbeef);
5656 td->new_code [reloc->offset] = *(guint16*)v;
5657 td->new_code [reloc->offset + 1] = *(guint16*)(v + 1);
5658 break;
5660 default:
5661 g_assert_not_reached ();
5662 break;
5668 static int
5669 get_inst_length (InterpInst *ins)
5671 if (ins->opcode == MINT_SWITCH)
5672 return MINT_SWITCH_LEN (READ32 (&ins->data [0]));
5673 else
5674 return mono_interp_oplen [ins->opcode];
5677 static guint16*
5678 emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *ins)
5680 guint16 opcode = ins->opcode;
5681 guint16 *ip = start_ip;
5683 // We know what IL offset this instruction was created for. We can now map the IL offset
5684 // to the IR offset. We use this array to resolve the relocations, which reference the IL.
5685 if (ins->il_offset != -1 && !td->in_offsets [ins->il_offset]) {
5686 g_assert (ins->il_offset >= 0 && ins->il_offset < td->header->code_size);
5687 td->in_offsets [ins->il_offset] = start_ip - td->new_code + 1;
5689 MonoDebugLineNumberEntry lne;
5690 lne.native_offset = (guint8*)start_ip - (guint8*)td->new_code;
5691 lne.il_offset = ins->il_offset;
5692 g_array_append_val (td->line_numbers, lne);
5695 *ip++ = opcode;
5696 if (opcode == MINT_SWITCH) {
5697 int labels = READ32 (&ins->data [0]);
5698 // Write number of switch labels
5699 *ip++ = ins->data [0];
5700 *ip++ = ins->data [1];
5701 // Add relocation for each label
5702 for (int i = 0; i < labels; i++) {
5703 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5704 reloc->type = RELOC_SWITCH;
5705 reloc->offset = ip - td->new_code;
5706 reloc->target = READ32 (&ins->data [2 + i * 2]);
5707 g_ptr_array_add (td->relocs, reloc);
5708 *ip++ = 0xdead;
5709 *ip++ = 0xbeef;
5711 } else if ((opcode >= MINT_BRFALSE_I4_S && opcode <= MINT_BRTRUE_R8_S) ||
5712 (opcode >= MINT_BEQ_I4_S && opcode <= MINT_BLT_UN_R8_S) ||
5713 opcode == MINT_BR_S || opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK) {
5714 const int br_offset = start_ip - td->new_code;
5715 if (ins->data [0] < ins->il_offset) {
5716 // Backwards branch. We can already patch it.
5717 *ip++ = get_in_offset (td, ins->data [0]) - br_offset;
5718 } else {
5719 // We don't know the in_offset of the target, add a reloc
5720 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5721 reloc->type = RELOC_SHORT_BRANCH;
5722 reloc->offset = br_offset;
5723 reloc->target = ins->data [0];
5724 g_ptr_array_add (td->relocs, reloc);
5725 *ip++ = 0xdead;
5727 } else if ((opcode >= MINT_BRFALSE_I4 && opcode <= MINT_BRTRUE_R8) ||
5728 (opcode >= MINT_BEQ_I4 && opcode <= MINT_BLT_UN_R8) ||
5729 opcode == MINT_BR || opcode == MINT_LEAVE || opcode == MINT_LEAVE_CHECK) {
5730 const int br_offset = start_ip - td->new_code;
5731 int target_il = READ32 (&ins->data [0]);
5732 if (target_il < ins->il_offset) {
5733 // Backwards branch. We can already patch it
5734 const int br_offset = start_ip - td->new_code;
5735 int target_offset = get_in_offset (td, target_il) - br_offset;
5736 WRITE32 (ip, &target_offset);
5737 } else {
5738 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5739 reloc->type = RELOC_LONG_BRANCH;
5740 reloc->offset = br_offset;
5741 reloc->target = target_il;
5742 g_ptr_array_add (td->relocs, reloc);
5743 *ip++ = 0xdead;
5744 *ip++ = 0xbeef;
5746 } else if (opcode == MINT_SDB_SEQ_POINT) {
5747 SeqPoint *seqp = (SeqPoint*)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint));
5748 InterpBasicBlock *cbb;
5750 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY) {
5751 seqp->il_offset = METHOD_ENTRY_IL_OFFSET;
5752 cbb = td->offset_to_bb [0];
5753 } else {
5754 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT)
5755 seqp->il_offset = METHOD_EXIT_IL_OFFSET;
5756 else
5757 seqp->il_offset = ins->il_offset;
5758 cbb = td->offset_to_bb [ins->il_offset];
5760 seqp->native_offset = (guint8*)start_ip - (guint8*)td->new_code;
5761 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK)
5762 seqp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
5763 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL)
5764 seqp->flags |= MONO_SEQ_POINT_FLAG_NESTED_CALL;
5765 g_ptr_array_add (td->seq_points, seqp);
5767 cbb->seq_points = g_slist_prepend_mempool (td->mempool, cbb->seq_points, seqp);
5768 cbb->last_seq_point = seqp;
5769 } else {
5770 int size = get_inst_length (ins) - 1;
5771 // Emit the rest of the data
5772 for (int i = 0; i < size; i++)
5773 *ip++ = ins->data [i];
5775 return ip;
5778 // Generates the final code, after we are done with all the passes
5779 static void
5780 generate_compacted_code (TransformData *td)
5782 guint16 *ip;
5783 int size = 0;
5784 td->relocs = g_ptr_array_new ();
5786 // Iterate once to compute the exact size of the compacted code
5787 InterpInst *ins = td->first_ins;
5788 while (ins) {
5789 size += get_inst_length (ins);
5790 ins = ins->next;
5793 // Generate the compacted stream of instructions
5794 td->new_code = ip = (guint16*)mono_domain_alloc0 (td->rtm->domain, size * sizeof (guint16));
5795 ins = td->first_ins;
5796 while (ins) {
5797 ip = emit_compacted_instruction (td, ip, ins);
5798 ins = ins->next;
5800 td->new_code_end = ip;
5801 td->in_offsets [td->header->code_size] = td->new_code_end - td->new_code;
5803 // Patch all branches
5804 handle_relocations (td);
5806 g_ptr_array_free (td->relocs, TRUE);
5809 static void
5810 generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoGenericContext *generic_context, MonoError *error)
5812 MonoDomain *domain = rtm->domain;
5813 int i;
5814 TransformData transform_data;
5815 TransformData *td;
5816 static gboolean verbose_method_inited;
5817 static char* verbose_method_name;
5819 if (!verbose_method_inited) {
5820 verbose_method_name = g_getenv ("MONO_VERBOSE_METHOD");
5821 verbose_method_inited = TRUE;
5824 memset (&transform_data, 0, sizeof(transform_data));
5825 td = &transform_data;
5827 td->method = method;
5828 td->rtm = rtm;
5829 td->code_size = header->code_size;
5830 td->header = header;
5831 td->max_code_size = td->code_size;
5832 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
5833 td->mempool = mono_mempool_new ();
5834 td->n_data_items = 0;
5835 td->max_data_items = 0;
5836 td->data_items = NULL;
5837 td->data_hash = g_hash_table_new (NULL, NULL);
5838 td->gen_sdb_seq_points = mini_debug_options.gen_sdb_seq_points;
5839 td->seq_points = g_ptr_array_new ();
5840 td->verbose_level = mono_interp_traceopt;
5841 td->total_locals_size = rtm->locals_size;
5842 rtm->data_items = td->data_items;
5844 if (verbose_method_name) {
5845 const char *name = verbose_method_name;
5847 if ((strchr (name, '.') > name) || strchr (name, ':')) {
5848 MonoMethodDesc *desc;
5850 desc = mono_method_desc_new (name, TRUE);
5851 if (mono_method_desc_full_match (desc, method)) {
5852 td->verbose_level = 4;
5854 mono_method_desc_free (desc);
5855 } else {
5856 if (strcmp (method->name, name) == 0)
5857 td->verbose_level = 4;
5861 td->stack = (StackInfo*)g_malloc0 ((header->max_stack + 1) * sizeof (td->stack [0]));
5862 td->stack_capacity = header->max_stack + 1;
5863 td->sp = td->stack;
5864 td->max_stack_height = 0;
5865 td->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
5867 generate_code (td, method, header, generic_context, error);
5868 goto_if_nok (error, exit);
5870 generate_compacted_code (td);
5872 if (td->verbose_level) {
5873 g_print ("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method, TRUE), rtm, td->max_vt_sp);
5874 g_print ("Calculated stack size: %d, stated size: %d\n", td->max_stack_height, header->max_stack);
5875 dump_mint_code (td->new_code, td->new_code_end);
5878 /* Check if we use excessive stack space */
5879 if (td->max_stack_height > header->max_stack * 3 && header->max_stack > 16)
5880 g_warning ("Excessive stack space usage for method %s, %d/%d", method->name, td->max_stack_height, header->max_stack);
5882 int code_len;
5883 code_len = td->new_code_end - td->new_code;
5885 rtm->clauses = (MonoExceptionClause*)mono_domain_alloc0 (domain, header->num_clauses * sizeof (MonoExceptionClause));
5886 memcpy (rtm->clauses, header->clauses, header->num_clauses * sizeof(MonoExceptionClause));
5887 rtm->code = (gushort*)td->new_code;
5888 rtm->init_locals = header->init_locals;
5889 rtm->num_clauses = header->num_clauses;
5890 for (i = 0; i < header->num_clauses; i++) {
5891 MonoExceptionClause *c = rtm->clauses + i;
5892 int end_off = c->try_offset + c->try_len;
5893 c->try_offset = get_in_offset (td, c->try_offset);
5894 c->try_len = get_in_offset (td, end_off) - c->try_offset;
5895 g_assert ((c->try_offset + c->try_len) < code_len);
5896 end_off = c->handler_offset + c->handler_len;
5897 c->handler_offset = get_in_offset (td, c->handler_offset);
5898 c->handler_len = get_in_offset (td, end_off) - c->handler_offset;
5899 g_assert (c->handler_len >= 0 && (c->handler_offset + c->handler_len) <= code_len);
5900 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER)
5901 c->data.filter_offset = get_in_offset (td, c->data.filter_offset);
5903 rtm->stack_size = (sizeof (stackval)) * (td->max_stack_height + 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/
5904 rtm->stack_size = ALIGN_TO (rtm->stack_size, MINT_VT_ALIGNMENT);
5905 rtm->vt_stack_size = td->max_vt_sp;
5906 rtm->total_locals_size = td->total_locals_size;
5907 rtm->alloca_size = rtm->total_locals_size + rtm->vt_stack_size + rtm->stack_size;
5908 rtm->data_items = (gpointer*)mono_domain_alloc0 (domain, td->n_data_items * sizeof (td->data_items [0]));
5909 memcpy (rtm->data_items, td->data_items, td->n_data_items * sizeof (td->data_items [0]));
5911 /* Save debug info */
5912 interp_save_debug_info (rtm, header, td, td->line_numbers);
5914 /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
5915 int jinfo_len;
5916 jinfo_len = mono_jit_info_size ((MonoJitInfoFlags)0, header->num_clauses, 0);
5917 MonoJitInfo *jinfo;
5918 jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len);
5919 jinfo->is_interp = 1;
5920 rtm->jinfo = jinfo;
5921 mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len, (MonoJitInfoFlags)0, header->num_clauses, 0);
5922 for (i = 0; i < jinfo->num_clauses; ++i) {
5923 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
5924 MonoExceptionClause *c = rtm->clauses + i;
5926 ei->flags = c->flags;
5927 ei->try_start = (guint8*)(rtm->code + c->try_offset);
5928 ei->try_end = (guint8*)(rtm->code + c->try_offset + c->try_len);
5929 ei->handler_start = (guint8*)(rtm->code + c->handler_offset);
5930 ei->exvar_offset = rtm->exvar_offsets [i];
5931 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
5932 ei->data.filter = (guint8*)(rtm->code + c->data.filter_offset);
5933 } else if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
5934 ei->data.handler_end = (guint8*)(rtm->code + c->handler_offset + c->handler_len);
5935 } else {
5936 ei->data.catch_class = c->data.catch_class;
5940 save_seq_points (td, jinfo);
5942 exit:
5943 g_free (td->in_offsets);
5944 g_free (td->data_items);
5945 g_free (td->stack);
5946 g_hash_table_destroy (td->data_hash);
5947 g_ptr_array_free (td->seq_points, TRUE);
5948 g_array_free (td->line_numbers, TRUE);
5949 mono_mempool_destroy (td->mempool);
5952 static mono_mutex_t calc_section;
5954 void
5955 mono_interp_transform_init (void)
5957 mono_os_mutex_init_recursive(&calc_section);
5960 void
5961 mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, MonoError *error)
5963 MonoMethod *method = imethod->method;
5964 MonoMethodHeader *header = NULL;
5965 MonoMethodSignature *signature = mono_method_signature_internal (method);
5966 MonoVTable *method_class_vt;
5967 MonoGenericContext *generic_context = NULL;
5968 MonoDomain *domain = imethod->domain;
5969 InterpMethod tmp_imethod;
5970 InterpMethod *real_imethod;
5972 error_init (error);
5974 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method->klass))) {
5975 mono_error_set_invalid_operation (error, "%s", "Could not execute the method because the containing type is not fully instantiated.");
5976 return;
5979 // g_printerr ("TRANSFORM(0x%016lx): begin %s::%s\n", mono_thread_current (), method->klass->name, method->name);
5980 method_class_vt = mono_class_vtable_checked (domain, imethod->method->klass, error);
5981 return_if_nok (error);
5983 if (!method_class_vt->initialized) {
5984 mono_runtime_class_init_full (method_class_vt, error);
5985 return_if_nok (error);
5988 MONO_PROFILER_RAISE (jit_begin, (method));
5990 if (mono_method_signature_internal (method)->is_inflated)
5991 generic_context = mono_method_get_context (method);
5992 else {
5993 MonoGenericContainer *generic_container = mono_method_get_generic_container (method);
5994 if (generic_container)
5995 generic_context = &generic_container->context;
5998 if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
5999 MonoMethod *nm = NULL;
6000 mono_os_mutex_lock (&calc_section);
6001 if (imethod->transformed) {
6002 mono_os_mutex_unlock (&calc_section);
6003 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));
6004 return;
6007 /* assumes all internal calls with an array this are built in... */
6008 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL && (! mono_method_signature_internal (method)->hasthis || m_class_get_rank (method->klass) == 0)) {
6009 nm = mono_marshal_get_native_wrapper (method, FALSE, FALSE);
6010 signature = mono_method_signature_internal (nm);
6011 } else {
6012 const char *name = method->name;
6013 if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) {
6014 if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
6015 MonoJitICallInfo *mi = mono_find_jit_icall_by_name ("ves_icall_mono_delegate_ctor_interp");
6016 g_assert (mi);
6017 nm = mono_marshal_get_icall_wrapper (mi, TRUE);
6018 } else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
6020 * Usually handled during transformation of the caller, but
6021 * when the caller is handled by another execution engine
6022 * (for example fullAOT) we need to handle it here. That's
6023 * known to be wrong in cases where the reference to
6024 * `MonoDelegate` would be needed (FIXME).
6026 nm = mono_marshal_get_delegate_invoke (method, NULL);
6027 } else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
6028 nm = mono_marshal_get_delegate_begin_invoke (method);
6029 } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
6030 nm = mono_marshal_get_delegate_end_invoke (method);
6033 if (nm == NULL)
6034 g_assert_not_reached ();
6036 if (nm == NULL) {
6037 imethod->stack_size = sizeof (stackval); /* for tracing */
6038 imethod->alloca_size = imethod->stack_size;
6039 imethod->transformed = TRUE;
6040 mono_os_mutex_unlock(&calc_section);
6041 MONO_PROFILER_RAISE (jit_done, (method, NULL));
6042 return;
6044 method = nm;
6045 header = interp_method_get_header (nm, error);
6046 mono_os_mutex_unlock (&calc_section);
6047 return_if_nok (error);
6050 if (!header) {
6051 header = mono_method_get_header_checked (method, error);
6052 return_if_nok (error);
6055 g_assert ((signature->param_count + signature->hasthis) < 1000);
6056 // g_printerr ("TRANSFORM(0x%016lx): end %s::%s\n", mono_thread_current (), method->klass->name, method->name);
6058 /* Make modifications to a copy of imethod, copy them back inside the lock */
6059 real_imethod = imethod;
6060 memcpy (&tmp_imethod, imethod, sizeof (InterpMethod));
6061 imethod = &tmp_imethod;
6063 interp_method_compute_offsets (imethod, signature, header);
6065 MONO_TIME_TRACK (mono_interp_stats.transform_time, generate (method, header, imethod, generic_context, error));
6067 mono_metadata_free_mh (header);
6069 return_if_nok (error);
6071 /* Copy changes back */
6072 imethod = real_imethod;
6073 mono_os_mutex_lock (&calc_section);
6074 if (!imethod->transformed) {
6075 // Ignore the first two fields which are unchanged. next_jit_code_hash shouldn't
6076 // be modified because it is racy with internal hash table insert.
6077 const int start_offset = 2 * sizeof (gpointer);
6078 memcpy ((char*)imethod + start_offset, (char*)&tmp_imethod + start_offset, sizeof (InterpMethod) - start_offset);
6079 mono_memory_barrier ();
6080 imethod->transformed = TRUE;
6081 mono_atomic_fetch_add_i32 (&mono_jit_stats.methods_with_interp, 1);
6084 mono_os_mutex_unlock (&calc_section);
6086 mono_domain_lock (domain);
6087 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, imethod->method))
6088 g_hash_table_insert (domain_jit_info (domain)->seq_points, imethod->method, imethod->jinfo->seq_points);
6089 mono_domain_unlock (domain);
6091 // FIXME: Add a different callback ?
6092 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));