[interp] Static field access fixes (#14009)
[mono-project.git] / mono / mini / interp / transform.c
blob52417642a11c7303d3e7a5f96277203ea701a1e7
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 // This version need to be used with switch opcode, which doesn't have constant length
288 static InterpInst*
289 interp_add_ins_explicit (TransformData *td, guint16 opcode, int len)
291 InterpInst *new_inst;
292 g_assert (len);
293 // Size of data region of instruction is length of instruction minus 1 (the opcode slot)
294 new_inst = mono_mempool_alloc0 (td->mempool, sizeof (InterpInst) + sizeof (guint16) * (len - 1));
295 new_inst->opcode = opcode;
296 // opcodes from inlined methods don't have il offset associated with them since the offset
297 // stored here is relevant only for the original method
298 if (!td->inlined_method)
299 new_inst->il_offset = td->in_start - td->il_code;
300 else
301 new_inst->il_offset = -1;
302 new_inst->prev = td->last_ins;
303 if (td->last_ins)
304 td->last_ins->next = new_inst;
305 else
306 td->first_ins = new_inst;
307 td->last_ins = new_inst;
308 return new_inst;
311 static InterpInst*
312 interp_add_ins (TransformData *td, guint16 opcode)
314 return interp_add_ins_explicit (td, opcode, mono_interp_oplen [opcode]);
317 #define CHECK_STACK(td, n) \
318 do { \
319 int stack_size = (td)->sp - (td)->stack; \
320 if (stack_size < (n)) \
321 g_warning ("%s.%s: not enough values (%d < %d) on stack at %04x", \
322 m_class_get_name ((td)->method->klass), (td)->method->name, \
323 stack_size, n, (td)->ip - (td)->il_code); \
324 } while (0)
326 #define ENSURE_I4(td, sp_off) \
327 do { \
328 if ((td)->sp [-sp_off].type == STACK_TYPE_I8) \
329 interp_add_ins (td, sp_off == 1 ? MINT_CONV_I4_I8 : MINT_CONV_I4_I8_SP); \
330 } while (0)
332 #define CHECK_TYPELOAD(klass) \
333 do { \
334 if (!(klass) || mono_class_has_failure (klass)) { \
335 mono_error_set_for_class_failure (error, klass); \
336 goto exit; \
338 } while (0)
340 #if NO_UNALIGNED_ACCESS
341 #define WRITE32(ip, v) \
342 do { \
343 * (ip) = * (guint16 *)(v); \
344 * ((ip) + 1) = * ((guint16 *)(v) + 1); \
345 (ip) += 2; \
346 } while (0)
348 #define WRITE32_INS(ins, index, v) \
349 do { \
350 (ins)->data [index] = * (guint16 *)(v); \
351 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
352 } while (0)
354 #define WRITE64_INS(ins, index, v) \
355 do { \
356 (ins)->data [index] = * (guint16 *)(v); \
357 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
358 (ins)->data [index + 2] = * ((guint16 *)(v) + 2); \
359 (ins)->data [index + 3] = * ((guint16 *)(v) + 3); \
360 } while (0)
361 #else
362 #define WRITE32(ip, v) \
363 do { \
364 * (guint32*)(ip) = * (guint32 *)(v); \
365 (ip) += 2; \
366 } while (0)
367 #define WRITE32_INS(ins, index, v) \
368 do { \
369 * (guint32 *)(&(ins)->data [index]) = * (guint32 *)(v); \
370 } while (0)
372 #define WRITE64_INS(ins, index, v) \
373 do { \
374 * (guint64 *)(&(ins)->data [index]) = * (guint64 *)(v); \
375 } while (0)
377 #endif
380 static void
381 handle_branch (TransformData *td, int short_op, int long_op, int offset)
383 int shorten_branch = 0;
384 int target = td->ip + offset - td->il_code;
385 if (target < 0 || target >= td->code_size)
386 g_assert_not_reached ();
387 /* Add exception checkpoint or safepoint for backward branches */
388 if (offset < 0) {
389 if (mono_threads_are_safepoints_enabled ())
390 interp_add_ins (td, MINT_SAFEPOINT);
391 else
392 interp_add_ins (td, MINT_CHECKPOINT);
394 if (offset > 0 && td->stack_height [target] < 0) {
395 td->stack_height [target] = td->sp - td->stack;
396 if (td->stack_height [target] > 0)
397 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, td->stack_height [target] * sizeof (td->stack [0]));
398 td->vt_stack_size [target] = td->vt_sp;
401 if (td->header->code_size <= 25000) /* FIX to be precise somehow? */
402 shorten_branch = 1;
404 if (shorten_branch) {
405 interp_add_ins (td, short_op);
406 td->last_ins->data [0] = (guint16) target;
407 } else {
408 interp_add_ins (td, long_op);
409 WRITE32_INS (td->last_ins, 0, &target);
413 static void
414 one_arg_branch(TransformData *td, int mint_op, int offset)
416 int type = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
417 int long_op = mint_op + type - STACK_TYPE_I4;
418 int short_op = long_op + MINT_BRFALSE_I4_S - MINT_BRFALSE_I4;
419 CHECK_STACK(td, 1);
420 --td->sp;
421 handle_branch (td, short_op, long_op, offset);
424 static void
425 two_arg_branch(TransformData *td, int mint_op, int offset)
427 int type1 = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
428 int type2 = td->sp [-2].type == STACK_TYPE_O || td->sp [-2].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-2].type;
429 int long_op = mint_op + type1 - STACK_TYPE_I4;
430 int short_op = long_op + MINT_BEQ_I4_S - MINT_BEQ_I4;
431 CHECK_STACK(td, 2);
432 if (type1 == STACK_TYPE_I4 && type2 == STACK_TYPE_I8) {
433 interp_add_ins (td, MINT_CONV_I8_I4);
434 // The il instruction starts with the actual branch, and not with the conversion opcodes
435 td->last_ins->il_offset = -1;
436 } else if (type1 == STACK_TYPE_I8 && type2 == STACK_TYPE_I4) {
437 interp_add_ins (td, MINT_CONV_I8_I4_SP);
438 td->last_ins->il_offset = -1;
439 } else if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
440 interp_add_ins (td, MINT_CONV_R8_R4);
441 td->last_ins->il_offset = -1;
442 } else if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
443 interp_add_ins (td, MINT_CONV_R8_R4_SP);
444 td->last_ins->il_offset = -1;
445 } else if (type1 != type2) {
446 g_warning("%s.%s: branch type mismatch %d %d",
447 m_class_get_name (td->method->klass), td->method->name,
448 td->sp [-1].type, td->sp [-2].type);
450 td->sp -= 2;
451 handle_branch (td, short_op, long_op, offset);
454 static void
455 unary_arith_op(TransformData *td, int mint_op)
457 int op = mint_op + td->sp [-1].type - STACK_TYPE_I4;
458 CHECK_STACK(td, 1);
459 interp_add_ins (td, op);
462 static void
463 binary_arith_op(TransformData *td, int mint_op)
465 int type1 = td->sp [-2].type;
466 int type2 = td->sp [-1].type;
467 int op;
468 #if SIZEOF_VOID_P == 8
469 if ((type1 == STACK_TYPE_MP || type1 == STACK_TYPE_I8) && type2 == STACK_TYPE_I4) {
470 interp_add_ins (td, MINT_CONV_I8_I4);
471 type2 = STACK_TYPE_I8;
473 if (type1 == STACK_TYPE_I4 && (type2 == STACK_TYPE_MP || type2 == STACK_TYPE_I8)) {
474 interp_add_ins (td, MINT_CONV_I8_I4_SP);
475 type1 = STACK_TYPE_I8;
476 td->sp [-2].type = STACK_TYPE_I8;
478 #endif
479 if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
480 interp_add_ins (td, MINT_CONV_R8_R4);
481 type2 = STACK_TYPE_R8;
483 if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
484 interp_add_ins (td, MINT_CONV_R8_R4_SP);
485 type1 = STACK_TYPE_R8;
486 td->sp [-2].type = STACK_TYPE_R8;
488 if (type1 == STACK_TYPE_MP)
489 type1 = STACK_TYPE_I;
490 if (type2 == STACK_TYPE_MP)
491 type2 = STACK_TYPE_I;
492 if (type1 != type2) {
493 g_warning("%s.%s: %04x arith type mismatch %s %d %d",
494 m_class_get_name (td->method->klass), td->method->name,
495 td->ip - td->il_code, mono_interp_opname[mint_op], type1, type2);
497 op = mint_op + type1 - STACK_TYPE_I4;
498 CHECK_STACK(td, 2);
499 interp_add_ins (td, op);
500 --td->sp;
503 static void
504 shift_op(TransformData *td, int mint_op)
506 int op = mint_op + td->sp [-2].type - STACK_TYPE_I4;
507 CHECK_STACK(td, 2);
508 if (td->sp [-1].type != STACK_TYPE_I4) {
509 g_warning("%s.%s: shift type mismatch %d",
510 m_class_get_name (td->method->klass), td->method->name,
511 td->sp [-2].type);
513 interp_add_ins (td, op);
514 --td->sp;
517 static int
518 can_store (int st_value, int vt_value)
520 if (st_value == STACK_TYPE_O || st_value == STACK_TYPE_MP)
521 st_value = STACK_TYPE_I;
522 if (vt_value == STACK_TYPE_O || vt_value == STACK_TYPE_MP)
523 vt_value = STACK_TYPE_I;
524 return st_value == vt_value;
527 #define SET_SIMPLE_TYPE(s, ty) \
528 do { \
529 (s)->type = (ty); \
530 (s)->flags = 0; \
531 (s)->klass = NULL; \
532 } while (0)
534 #define SET_TYPE(s, ty, k) \
535 do { \
536 (s)->type = (ty); \
537 (s)->flags = 0; \
538 (s)->klass = k; \
539 } while (0)
541 #define REALLOC_STACK(td, sppos) \
542 do { \
543 (td)->stack_capacity *= 2; \
544 (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
545 (td)->sp = (td)->stack + sppos; \
546 } while (0);
548 #define PUSH_SIMPLE_TYPE(td, ty) \
549 do { \
550 int sp_height; \
551 (td)->sp++; \
552 sp_height = (td)->sp - (td)->stack; \
553 if (sp_height > (td)->max_stack_height) \
554 (td)->max_stack_height = sp_height; \
555 if (sp_height > (td)->stack_capacity) \
556 REALLOC_STACK(td, sp_height); \
557 SET_SIMPLE_TYPE((td)->sp - 1, ty); \
558 } while (0)
560 #define PUSH_TYPE(td, ty, k) \
561 do { \
562 int sp_height; \
563 (td)->sp++; \
564 sp_height = (td)->sp - (td)->stack; \
565 if (sp_height > (td)->max_stack_height) \
566 (td)->max_stack_height = sp_height; \
567 if (sp_height > (td)->stack_capacity) \
568 REALLOC_STACK(td, sp_height); \
569 SET_TYPE((td)->sp - 1, ty, k); \
570 } while (0)
572 #define PUSH_VT(td, size) \
573 do { \
574 (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
575 if ((td)->vt_sp > (td)->max_vt_sp) \
576 (td)->max_vt_sp = (td)->vt_sp; \
577 } while (0)
579 #define POP_VT(td, size) \
580 do { \
581 (td)->vt_sp -= ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
582 } while (0)
584 static MonoType*
585 get_arg_type_exact (TransformData *td, int n, int *mt)
587 MonoType *type;
588 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
590 if (hasthis && n == 0)
591 type = m_class_get_byval_arg (td->method->klass);
592 else
593 type = mono_method_signature_internal (td->method)->params [n - !!hasthis];
595 if (mt)
596 *mt = mint_type (type);
598 return type;
601 static void
602 load_arg(TransformData *td, int n)
604 int mt;
605 MonoClass *klass = NULL;
606 MonoType *type;
607 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
609 type = get_arg_type_exact (td, n, &mt);
611 if (mt == MINT_TYPE_VT) {
612 gint32 size;
613 klass = mono_class_from_mono_type_internal (type);
614 if (mono_method_signature_internal (td->method)->pinvoke)
615 size = mono_class_native_size (klass, NULL);
616 else
617 size = mono_class_value_size (klass, NULL);
619 if (hasthis && n == 0) {
620 mt = MINT_TYPE_P;
621 interp_add_ins (td, MINT_LDARG_P);
622 td->last_ins->data [0] = 0;
623 klass = NULL;
624 } else {
625 PUSH_VT (td, size);
626 interp_add_ins (td, MINT_LDARG_VT);
627 td->last_ins->data [0] = n;
628 WRITE32_INS (td->last_ins, 1, &size);
630 } else {
631 if (hasthis && n == 0) {
632 mt = MINT_TYPE_P;
633 interp_add_ins (td, MINT_LDARG_P);
634 td->last_ins->data [0] = n;
635 klass = NULL;
636 } else {
637 interp_add_ins (td, MINT_LDARG_I1 + (mt - MINT_TYPE_I1));
638 td->last_ins->data [0] = n;
639 if (mt == MINT_TYPE_O)
640 klass = mono_class_from_mono_type_internal (type);
643 PUSH_TYPE(td, stack_type[mt], klass);
646 static void
647 store_arg(TransformData *td, int n)
649 int mt;
650 CHECK_STACK (td, 1);
651 MonoType *type;
653 type = get_arg_type_exact (td, n, &mt);
655 if (mt == MINT_TYPE_VT) {
656 gint32 size;
657 MonoClass *klass = mono_class_from_mono_type_internal (type);
658 if (mono_method_signature_internal (td->method)->pinvoke)
659 size = mono_class_native_size (klass, NULL);
660 else
661 size = mono_class_value_size (klass, NULL);
662 interp_add_ins (td, MINT_STARG_VT);
663 td->last_ins->data [0] = n;
664 WRITE32_INS (td->last_ins, 1, &size);
665 if (td->sp [-1].type == STACK_TYPE_VT)
666 POP_VT(td, size);
667 } else {
668 interp_add_ins (td, MINT_STARG_I1 + (mt - MINT_TYPE_I1));
669 td->last_ins->data [0] = n;
671 --td->sp;
674 static void
675 load_local_general (TransformData *td, int offset, MonoType *type)
677 int mt = mint_type (type);
678 MonoClass *klass = NULL;
679 if (mt == MINT_TYPE_VT) {
680 klass = mono_class_from_mono_type_internal (type);
681 gint32 size = mono_class_value_size (klass, NULL);
682 PUSH_VT(td, size);
683 interp_add_ins (td, MINT_LDLOC_VT);
684 td->last_ins->data [0] = offset;
685 WRITE32_INS (td->last_ins, 1, &size);
686 } else {
687 g_assert (mt < MINT_TYPE_VT);
688 if (!td->gen_sdb_seq_points &&
689 mt == MINT_TYPE_I4 && !td->is_bb_start [td->in_start - td->il_code] && td->last_ins != NULL &&
690 td->last_ins->opcode == MINT_STLOC_I4 && td->last_ins->data [0] == offset) {
691 td->last_ins->opcode = MINT_STLOC_NP_I4;
692 } else if (!td->gen_sdb_seq_points &&
693 mt == MINT_TYPE_O && !td->is_bb_start [td->in_start - td->il_code] && td->last_ins != NULL &&
694 td->last_ins->opcode == MINT_STLOC_O && td->last_ins->data [0] == offset) {
695 td->last_ins->opcode = MINT_STLOC_NP_O;
696 } else {
697 interp_add_ins (td, MINT_LDLOC_I1 + (mt - MINT_TYPE_I1));
698 td->last_ins->data [0] = offset; /*FIX for large offset */
700 if (mt == MINT_TYPE_O)
701 klass = mono_class_from_mono_type_internal (type);
703 PUSH_TYPE(td, stack_type[mt], klass);
706 static void
707 load_local (TransformData *td, int n)
709 MonoType *type = td->header->locals [n];
710 int offset = td->rtm->local_offsets [n];
711 load_local_general (td, offset, type);
714 static void
715 store_local_general (TransformData *td, int offset, MonoType *type)
717 int mt = mint_type (type);
718 CHECK_STACK (td, 1);
719 #if SIZEOF_VOID_P == 8
720 if (td->sp [-1].type == STACK_TYPE_I4 && stack_type [mt] == STACK_TYPE_I8) {
721 interp_add_ins (td, MINT_CONV_I8_I4);
722 td->sp [-1].type = STACK_TYPE_I8;
724 #endif
725 if (!can_store(td->sp [-1].type, stack_type [mt])) {
726 g_warning("%s.%s: Store local stack type mismatch %d %d",
727 m_class_get_name (td->method->klass), td->method->name,
728 stack_type [mt], td->sp [-1].type);
730 if (mt == MINT_TYPE_VT) {
731 MonoClass *klass = mono_class_from_mono_type_internal (type);
732 gint32 size = mono_class_value_size (klass, NULL);
733 interp_add_ins (td, MINT_STLOC_VT);
734 td->last_ins->data [0] = offset; /*FIX for large offset */
735 WRITE32_INS (td->last_ins, 1, &size);
736 if (td->sp [-1].type == STACK_TYPE_VT)
737 POP_VT(td, size);
738 } else {
739 g_assert (mt < MINT_TYPE_VT);
740 interp_add_ins (td, MINT_STLOC_I1 + (mt - MINT_TYPE_I1));
741 td->last_ins->data [0] = offset; /*FIX for large offset */
743 --td->sp;
746 static void
747 store_local (TransformData *td, int n)
749 MonoType *type = td->header->locals [n];
750 int offset = td->rtm->local_offsets [n];
751 store_local_general (td, offset, type);
754 #define SIMPLE_OP(td, op) \
755 do { \
756 interp_add_ins (td, op); \
757 ++td->ip; \
758 } while (0)
760 static guint16
761 get_data_item_index (TransformData *td, void *ptr)
763 gpointer p = g_hash_table_lookup (td->data_hash, ptr);
764 guint index;
765 if (p != NULL)
766 return GPOINTER_TO_UINT (p) - 1;
767 if (td->max_data_items == td->n_data_items) {
768 td->max_data_items = td->n_data_items == 0 ? 16 : 2 * td->max_data_items;
769 td->data_items = (gpointer*)g_realloc (td->data_items, td->max_data_items * sizeof(td->data_items [0]));
771 index = td->n_data_items;
772 td->data_items [index] = ptr;
773 ++td->n_data_items;
774 g_hash_table_insert (td->data_hash, ptr, GUINT_TO_POINTER (index + 1));
775 return index;
778 static gboolean
779 jit_call_supported (MonoMethod *method, MonoMethodSignature *sig)
781 GSList *l;
783 if (sig->param_count > 6)
784 return FALSE;
785 if (sig->pinvoke)
786 return FALSE;
787 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
788 return FALSE;
789 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
790 return FALSE;
791 if (method->is_inflated)
792 return FALSE;
793 if (method->string_ctor)
794 return FALSE;
796 if (mono_aot_only && m_class_get_image (method->klass)->aot_module && !(method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) {
797 ERROR_DECL (error);
798 gpointer addr = mono_jit_compile_method_jit_only (method, error);
799 if (addr && mono_error_ok (error))
800 return TRUE;
803 for (l = mono_interp_jit_classes; l; l = l->next) {
804 const char *class_name = (const char*)l->data;
805 // FIXME: Namespaces
806 if (!strcmp (m_class_get_name (method->klass), class_name))
807 return TRUE;
810 //return TRUE;
811 return FALSE;
814 static int mono_class_get_magic_index (MonoClass *k)
816 if (mono_class_is_magic_int (k))
817 return !strcmp ("nint", m_class_get_name (k)) ? 0 : 1;
819 if (mono_class_is_magic_float (k))
820 return 2;
822 return -1;
825 static void
826 interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *target_method)
828 MonoJitICallInfo *info = mono_find_jit_icall_by_name ("mono_throw_method_access");
830 /* Inject code throwing MethodAccessException */
831 interp_add_ins (td, MINT_MONO_LDPTR);
832 td->last_ins->data [0] = get_data_item_index (td, method);
833 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
835 interp_add_ins (td, MINT_MONO_LDPTR);
836 td->last_ins->data [0] = get_data_item_index (td, target_method);
837 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
839 interp_add_ins (td, MINT_ICALL_PP_V);
840 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
842 td->sp -= 2;
846 * These are additional locals that can be allocated as we transform the code.
847 * They are allocated past the method locals so they are accessed in the same
848 * way, with an offset relative to the frame->locals.
850 static int
851 create_interp_local (TransformData *td, MonoType *type)
853 int align, size;
854 int offset = td->total_locals_size;
856 size = mono_type_size (type, &align);
857 offset = ALIGN_TO (offset, align);
859 td->total_locals_size = offset + size;
861 return offset;
864 static void
865 dump_mint_code (const guint16 *start, const guint16* end)
867 const guint16 *p = start;
868 while (p < end) {
869 char *ins = mono_interp_dis_mintop (start, p);
870 g_print ("%s\n", ins);
871 g_free (ins);
872 p = mono_interp_dis_mintop_len (p);
876 /* For debug use */
877 void
878 mono_interp_print_code (InterpMethod *imethod)
880 MonoJitInfo *jinfo = imethod->jinfo;
881 const guint16 *start;
883 if (!jinfo)
884 return;
886 char *name = mono_method_full_name (imethod->method, 1);
887 g_print ("Method : %s\n", name);
888 g_free (name);
890 start = (guint16*) jinfo->code_start;
891 dump_mint_code (start, start + jinfo->code_size);
895 static MonoMethodHeader*
896 interp_method_get_header (MonoMethod* method, MonoError *error)
898 /* An explanation: mono_method_get_header_internal returns an error if
899 * called on a method with no body (e.g. an abstract method, or an
900 * icall). We don't want that.
902 if (mono_method_has_no_body (method))
903 return NULL;
904 else
905 return mono_method_get_header_internal (method, error);
908 /* stores top of stack as local and pushes address of it on stack */
909 static void
910 emit_store_value_as_local (TransformData *td, MonoType *src)
912 int size = mini_magic_type_size (NULL, src);
913 int local_offset = create_interp_local (td, mini_native_type_replace_type (src));
915 store_local_general (td, local_offset, src);
917 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
918 interp_add_ins (td, MINT_LDLOC_VT);
919 td->last_ins->data [0] = local_offset;
920 WRITE32_INS (td->last_ins, 1, &size);
922 PUSH_VT (td, size);
923 PUSH_TYPE (td, STACK_TYPE_VT, NULL);
926 /* Return TRUE if call transformation is finished */
927 static gboolean
928 interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoMethodSignature *csignature, gboolean readonly, int *op)
930 const char *tm = target_method->name;
931 int i;
932 int type_index = mono_class_get_magic_index (target_method->klass);
933 gboolean in_corlib = m_class_get_image (target_method->klass) == mono_defaults.corlib;
934 const char *klass_name_space = m_class_get_name_space (target_method->klass);
935 const char *klass_name = m_class_get_name (target_method->klass);
937 if (target_method->klass == mono_defaults.string_class) {
938 if (tm [0] == 'g') {
939 if (strcmp (tm, "get_Chars") == 0)
940 *op = MINT_GETCHR;
941 else if (strcmp (tm, "get_Length") == 0)
942 *op = MINT_STRLEN;
944 } else if (type_index >= 0) {
945 MonoClass *magic_class = target_method->klass;
947 const int mt = mint_type (m_class_get_byval_arg (magic_class));
948 if (!strcmp (".ctor", tm)) {
949 MonoType *arg = csignature->params [0];
950 /* depending on SIZEOF_VOID_P and the type of the value passed to the .ctor we either have to CONV it, or do nothing */
951 int arg_size = mini_magic_type_size (NULL, arg);
953 if (arg_size > SIZEOF_VOID_P) { // 8 -> 4
954 switch (type_index) {
955 case 0: case 1:
956 interp_add_ins (td, MINT_CONV_I4_I8);
957 break;
958 case 2:
959 interp_add_ins (td, MINT_CONV_R4_R8);
960 break;
964 if (arg_size < SIZEOF_VOID_P) { // 4 -> 8
965 switch (type_index) {
966 case 0:
967 interp_add_ins (td, MINT_CONV_I8_I4);
968 break;
969 case 1:
970 interp_add_ins (td, MINT_CONV_I8_U4);
971 break;
972 case 2:
973 interp_add_ins (td, MINT_CONV_R8_R4);
974 break;
978 switch (type_index) {
979 case 0: case 1:
980 #if SIZEOF_VOID_P == 4
981 interp_add_ins (td, MINT_STIND_I4);
982 #else
983 interp_add_ins (td, MINT_STIND_I8);
984 #endif
985 break;
986 case 2:
987 #if SIZEOF_VOID_P == 4
988 interp_add_ins (td, MINT_STIND_R4);
989 #else
990 interp_add_ins (td, MINT_STIND_R8);
991 #endif
992 break;
995 td->sp -= 2;
996 td->ip += 5;
997 return TRUE;
998 } else if (!strcmp ("op_Implicit", tm ) || !strcmp ("op_Explicit", tm)) {
999 MonoType *src = csignature->params [0];
1000 MonoType *dst = csignature->ret;
1001 int src_size = mini_magic_type_size (NULL, src);
1002 int dst_size = mini_magic_type_size (NULL, dst);
1004 gboolean store_value_as_local = FALSE;
1006 switch (type_index) {
1007 case 0: case 1:
1008 if (!mini_magic_is_int_type (src) || !mini_magic_is_int_type (dst)) {
1009 if (mini_magic_is_int_type (src))
1010 store_value_as_local = TRUE;
1011 else
1012 return FALSE;
1014 break;
1015 case 2:
1016 if (!mini_magic_is_float_type (src) || !mini_magic_is_float_type (dst)) {
1017 if (mini_magic_is_float_type (src))
1018 store_value_as_local = TRUE;
1019 else
1020 return FALSE;
1022 break;
1025 if (store_value_as_local) {
1026 emit_store_value_as_local (td, src);
1028 /* emit call to managed conversion method */
1029 return FALSE;
1032 #if SIZEOF_VOID_P == 4
1033 if (src_size > dst_size) { // 8 -> 4
1034 switch (type_index) {
1035 case 0: case 1:
1036 interp_add_ins (td, MINT_CONV_I4_I8);
1037 break;
1038 case 2:
1039 interp_add_ins (td, MINT_CONV_R4_R8);
1040 break;
1043 #endif
1045 #if SIZEOF_VOID_P == 8
1046 if (src_size < dst_size) { // 4 -> 8
1047 switch (type_index) {
1048 case 0:
1049 interp_add_ins (td, MINT_CONV_I8_I4);
1050 break;
1051 case 1:
1052 interp_add_ins (td, MINT_CONV_I8_U4);
1053 break;
1054 case 2:
1055 interp_add_ins (td, MINT_CONV_R8_R4);
1056 break;
1059 #endif
1061 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1062 td->ip += 5;
1063 return TRUE;
1064 } else if (!strcmp ("op_Increment", tm)) {
1065 g_assert (type_index != 2); // no nfloat
1066 #if SIZEOF_VOID_P == 8
1067 interp_add_ins (td, MINT_ADD1_I8);
1068 #else
1069 interp_add_ins (td, MINT_ADD1_I4);
1070 #endif
1071 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1072 td->ip += 5;
1073 return TRUE;
1074 } else if (!strcmp ("op_Decrement", tm)) {
1075 g_assert (type_index != 2); // no nfloat
1076 #if SIZEOF_VOID_P == 8
1077 interp_add_ins (td, MINT_SUB1_I8);
1078 #else
1079 interp_add_ins (td, MINT_SUB1_I4);
1080 #endif
1081 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1082 td->ip += 5;
1083 return TRUE;
1084 } else if (!strcmp ("CompareTo", tm) || !strcmp ("Equals", tm)) {
1085 MonoType *arg = csignature->params [0];
1087 /* on 'System.n*::{CompareTo,Equals} (System.n*)' variant we need to push managed
1088 * pointer instead of value */
1089 if (arg->type == MONO_TYPE_VALUETYPE)
1090 emit_store_value_as_local (td, arg);
1092 /* emit call to managed conversion method */
1093 return FALSE;
1094 } else if (!strcmp (".cctor", tm)) {
1095 /* white list */
1096 return FALSE;
1097 } else if (!strcmp ("Parse", tm)) {
1098 /* white list */
1099 return FALSE;
1100 } else if (!strcmp ("ToString", tm)) {
1101 /* white list */
1102 return FALSE;
1103 } else if (!strcmp ("GetHashCode", tm)) {
1104 /* white list */
1105 return FALSE;
1106 } else if (!strcmp ("IsNaN", tm) || !strcmp ("IsInfinity", tm) || !strcmp ("IsNegativeInfinity", tm) || !strcmp ("IsPositiveInfinity", tm)) {
1107 g_assert (type_index == 2); // nfloat only
1108 /* white list */
1109 return FALSE;
1112 for (i = 0; i < sizeof (int_unnop) / sizeof (MagicIntrinsic); ++i) {
1113 if (!strcmp (int_unnop [i].op_name, tm)) {
1114 interp_add_ins (td, int_unnop [i].insn [type_index]);
1115 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1116 td->ip += 5;
1117 return TRUE;
1121 for (i = 0; i < sizeof (int_binop) / sizeof (MagicIntrinsic); ++i) {
1122 if (!strcmp (int_binop [i].op_name, tm)) {
1123 interp_add_ins (td, int_binop [i].insn [type_index]);
1124 td->sp -= 1;
1125 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1126 td->ip += 5;
1127 return TRUE;
1131 for (i = 0; i < sizeof (int_cmpop) / sizeof (MagicIntrinsic); ++i) {
1132 if (!strcmp (int_cmpop [i].op_name, tm)) {
1133 MonoClass *k = mono_defaults.boolean_class;
1134 interp_add_ins (td, int_cmpop [i].insn [type_index]);
1135 td->sp -= 1;
1136 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1137 td->ip += 5;
1138 return TRUE;
1142 g_error ("TODO: interp_transform_call %s:%s", m_class_get_name (target_method->klass), tm);
1143 } else if (mono_class_is_subclass_of (target_method->klass, mono_defaults.array_class, FALSE)) {
1144 if (!strcmp (tm, "get_Rank")) {
1145 *op = MINT_ARRAY_RANK;
1146 } else if (!strcmp (tm, "get_Length")) {
1147 *op = MINT_LDLEN;
1148 } else if (!strcmp (tm, "Address")) {
1149 *op = readonly ? MINT_LDELEMA : MINT_LDELEMA_TC;
1150 } else if (!strcmp (tm, "UnsafeMov") || !strcmp (tm, "UnsafeLoad") || !strcmp (tm, "Set") || !strcmp (tm, "Get")) {
1151 *op = MINT_CALLRUN;
1152 } else if (!strcmp (tm, "UnsafeStore")) {
1153 g_error ("TODO ArrayClass::UnsafeStore");
1155 } else if (in_corlib &&
1156 !strcmp (klass_name_space, "System.Diagnostics") &&
1157 !strcmp (klass_name, "Debugger")) {
1158 if (!strcmp (tm, "Break") && csignature->param_count == 0) {
1159 if (mini_should_insert_breakpoint (td->method))
1160 *op = MINT_BREAK;
1162 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "ByReference`1")) {
1163 *op = MINT_INTRINS_BYREFERENCE_GET_VALUE;
1164 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "Math") && csignature->param_count == 1 && csignature->params [0]->type == MONO_TYPE_R8) {
1165 if (tm [0] == 'A') {
1166 if (strcmp (tm, "Abs") == 0 && csignature->params [0]->type == MONO_TYPE_R8) {
1167 *op = MINT_ABS;
1168 } else if (strcmp (tm, "Asin") == 0){
1169 *op = MINT_ASIN;
1170 } else if (strcmp (tm, "Asinh") == 0){
1171 *op = MINT_ASINH;
1172 } else if (strcmp (tm, "Acos") == 0){
1173 *op = MINT_ACOS;
1174 } else if (strcmp (tm, "Acosh") == 0){
1175 *op = MINT_ACOSH;
1176 } else if (strcmp (tm, "Atan") == 0){
1177 *op = MINT_ATAN;
1178 } else if (strcmp (tm, "Atanh") == 0){
1179 *op = MINT_ATANH;
1181 } else if (tm [0] == 'C') {
1182 if (strcmp (tm, "Cos") == 0) {
1183 *op = MINT_COS;
1184 } else if (strcmp (tm, "Cbrt") == 0){
1185 *op = MINT_CBRT;
1186 } else if (strcmp (tm, "Cosh") == 0){
1187 *op = MINT_COSH;
1189 } else if (tm [0] == 'S') {
1190 if (strcmp (tm, "Sin") == 0) {
1191 *op = MINT_SIN;
1192 } else if (strcmp (tm, "Sqrt") == 0) {
1193 *op = MINT_SQRT;
1194 } else if (strcmp (tm, "Sinh") == 0){
1195 *op = MINT_SINH;
1197 } else if (tm [0] == 'T') {
1198 if (strcmp (tm, "Tan") == 0) {
1199 *op = MINT_TAN;
1200 } else if (strcmp (tm, "Tanh") == 0){
1201 *op = MINT_TANH;
1204 } else if (in_corlib && !strcmp (klass_name_space, "System") && (!strcmp (klass_name, "Span`1") || !strcmp (klass_name, "ReadOnlySpan`1"))) {
1205 if (!strcmp (tm, "get_Item") && csignature->params [0]->type != MONO_TYPE_VALUETYPE) {
1206 MonoGenericClass *gclass = mono_class_get_generic_class (target_method->klass);
1207 MonoClass *param_class = mono_class_from_mono_type_internal (gclass->context.class_inst->type_argv [0]);
1209 if (!mini_is_gsharedvt_variable_klass (param_class)) {
1210 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1211 g_assert (length_field);
1212 int offset_length = length_field->offset - sizeof (MonoObject);
1214 MonoClassField *ptr_field = mono_class_get_field_from_name_full (target_method->klass, "_pointer", NULL);
1215 g_assert (ptr_field);
1216 int offset_pointer = ptr_field->offset - sizeof (MonoObject);
1218 int size = mono_class_array_element_size (param_class);
1219 interp_add_ins (td, MINT_GETITEM_SPAN);
1220 td->last_ins->data [0] = size;
1221 td->last_ins->data [1] = offset_length;
1222 td->last_ins->data [2] = offset_pointer;
1224 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1225 td->sp -= 1;
1226 td->ip += 5;
1227 return TRUE;
1229 } else if (!strcmp (tm, "get_Length")) {
1230 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1231 g_assert (length_field);
1232 int offset_length = length_field->offset - sizeof (MonoObject);
1233 interp_add_ins (td, MINT_LDLEN_SPAN);
1234 td->last_ins->data [0] = offset_length;
1235 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1236 td->ip += 5;
1237 return TRUE;
1239 } else if (in_corlib && !strcmp (klass_name_space, "Internal.Runtime.CompilerServices") && !strcmp (klass_name, "Unsafe")) {
1240 #ifdef ENABLE_NETCORE
1241 if (!strcmp (tm, "AddByteOffset"))
1242 *op = MINT_INTRINS_UNSAFE_ADD_BYTE_OFFSET;
1243 else if (!strcmp (tm, "ByteOffset"))
1244 *op = MINT_INTRINS_UNSAFE_BYTE_OFFSET;
1245 else if (!strcmp (tm, "As") || !strcmp (tm, "AsRef"))
1246 *op = MINT_NOP;
1247 else if (!strcmp (tm, "SizeOf")) {
1248 MonoGenericContext *ctx = mono_method_get_context (target_method);
1249 g_assert (ctx);
1250 g_assert (ctx->method_inst);
1251 g_assert (ctx->method_inst->type_argc == 1);
1252 MonoType *t = ctx->method_inst->type_argv [0];
1253 int align;
1254 int esize = mono_type_size (t, &align);
1255 interp_add_ins (td, MINT_LDC_I4);
1256 WRITE32_INS (td->last_ins, 0, &esize);
1257 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1258 td->ip += 5;
1259 return TRUE;
1260 } else if (!strcmp (tm, "AreSame")) {
1261 *op = MINT_CEQ_P;
1263 #endif
1264 } else if (in_corlib && !strcmp (klass_name_space, "System.Runtime.CompilerServices") && !strcmp (klass_name, "RuntimeHelpers")) {
1265 #ifdef ENABLE_NETCORE
1266 if (!strcmp (tm, "IsBitwiseEquatable")) {
1267 g_assert (csignature->param_count == 0);
1268 MonoGenericContext *ctx = mono_method_get_context (target_method);
1269 g_assert (ctx);
1270 g_assert (ctx->method_inst);
1271 g_assert (ctx->method_inst->type_argc == 1);
1272 MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]);
1274 if (MONO_TYPE_IS_PRIMITIVE (t) && t->type != MONO_TYPE_R4 && t->type != MONO_TYPE_R8)
1275 *op = MINT_LDC_I4_1;
1276 else
1277 *op = MINT_LDC_I4_0;
1278 } else if (!strcmp (tm, "ObjectHasComponentSize")) {
1279 *op = MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE;
1281 #endif
1282 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "RuntimeMethodHandle") && !strcmp (tm, "GetFunctionPointer") && csignature->param_count == 1) {
1283 // We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter
1284 *op = MINT_LDFTN_DYNAMIC;
1285 } else if (target_method->klass == mono_defaults.object_class) {
1286 if (!strcmp (tm, "InternalGetHashCode"))
1287 *op = MINT_INTRINS_GET_HASHCODE;
1288 #ifdef DISABLE_REMOTING
1289 else if (!strcmp (tm, "GetType"))
1290 *op = MINT_INTRINS_GET_TYPE;
1291 #endif
1294 return FALSE;
1297 static MonoMethod*
1298 interp_transform_internal_calls (MonoMethod *method, MonoMethod *target_method, MonoMethodSignature *csignature, gboolean is_virtual)
1300 if (method->wrapper_type == MONO_WRAPPER_NONE && target_method != NULL) {
1301 if (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
1302 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1303 if (!is_virtual && target_method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
1304 target_method = mono_marshal_get_synchronized_wrapper (target_method);
1306 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)
1307 target_method = mono_marshal_get_native_wrapper (target_method, TRUE, FALSE);
1309 return target_method;
1312 static gboolean
1313 interp_type_as_ptr (MonoType *tp)
1315 if (MONO_TYPE_IS_POINTER (tp))
1316 return TRUE;
1317 if (MONO_TYPE_IS_REFERENCE (tp))
1318 return TRUE;
1319 if ((tp)->type == MONO_TYPE_I4)
1320 return TRUE;
1321 #if SIZEOF_VOID_P == 8
1322 if ((tp)->type == MONO_TYPE_I8)
1323 return TRUE;
1324 #endif
1325 if ((tp)->type == MONO_TYPE_BOOLEAN)
1326 return TRUE;
1327 if ((tp)->type == MONO_TYPE_CHAR)
1328 return TRUE;
1329 if ((tp)->type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (tp->data.klass))
1330 return TRUE;
1331 return FALSE;
1334 #define INTERP_TYPE_AS_PTR(tp) interp_type_as_ptr (tp)
1336 static int
1337 interp_icall_op_for_sig (MonoMethodSignature *sig)
1339 int op = -1;
1340 switch (sig->param_count) {
1341 case 0:
1342 if (MONO_TYPE_IS_VOID (sig->ret))
1343 op = MINT_ICALL_V_V;
1344 else if (INTERP_TYPE_AS_PTR (sig->ret))
1345 op = MINT_ICALL_V_P;
1346 break;
1347 case 1:
1348 if (MONO_TYPE_IS_VOID (sig->ret)) {
1349 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1350 op = MINT_ICALL_P_V;
1351 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1352 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1353 op = MINT_ICALL_P_P;
1355 break;
1356 case 2:
1357 if (MONO_TYPE_IS_VOID (sig->ret)) {
1358 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1359 INTERP_TYPE_AS_PTR (sig->params [1]))
1360 op = MINT_ICALL_PP_V;
1361 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1362 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1363 INTERP_TYPE_AS_PTR (sig->params [1]))
1364 op = MINT_ICALL_PP_P;
1366 break;
1367 case 3:
1368 if (MONO_TYPE_IS_VOID (sig->ret)) {
1369 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1370 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1371 INTERP_TYPE_AS_PTR (sig->params [2]))
1372 op = MINT_ICALL_PPP_V;
1373 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1374 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1375 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1376 INTERP_TYPE_AS_PTR (sig->params [2]))
1377 op = MINT_ICALL_PPP_P;
1379 break;
1380 case 4:
1381 if (MONO_TYPE_IS_VOID (sig->ret)) {
1382 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1383 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1384 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1385 INTERP_TYPE_AS_PTR (sig->params [3]))
1386 op = MINT_ICALL_PPPP_V;
1387 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1388 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1389 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1390 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1391 INTERP_TYPE_AS_PTR (sig->params [3]))
1392 op = MINT_ICALL_PPPP_P;
1394 break;
1395 case 5:
1396 if (MONO_TYPE_IS_VOID (sig->ret)) {
1397 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1398 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1399 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1400 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1401 INTERP_TYPE_AS_PTR (sig->params [4]))
1402 op = MINT_ICALL_PPPPP_V;
1403 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1404 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1405 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1406 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1407 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1408 INTERP_TYPE_AS_PTR (sig->params [4]))
1409 op = MINT_ICALL_PPPPP_P;
1411 break;
1412 case 6:
1413 if (MONO_TYPE_IS_VOID (sig->ret)) {
1414 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1415 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1416 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1417 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1418 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1419 INTERP_TYPE_AS_PTR (sig->params [5]))
1420 op = MINT_ICALL_PPPPPP_V;
1421 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1422 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1423 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1424 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1425 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1426 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1427 INTERP_TYPE_AS_PTR (sig->params [5]))
1428 op = MINT_ICALL_PPPPPP_P;
1430 break;
1432 return op;
1435 #define INLINE_LENGTH_LIMIT 20
1437 static gboolean
1438 interp_method_check_inlining (TransformData *td, MonoMethod *method)
1440 MonoMethodHeaderSummary header;
1442 if (td->method == method)
1443 return FALSE;
1445 if (!mono_method_get_header_summary (method, &header))
1446 return FALSE;
1448 /*runtime, icall and pinvoke are checked by summary call*/
1449 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
1450 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
1451 (mono_class_is_marshalbyref (method->klass)) ||
1452 header.has_clauses ||
1453 header.has_locals)
1454 return FALSE;
1456 if (header.code_size >= INLINE_LENGTH_LIMIT)
1457 return FALSE;
1459 if (mono_class_needs_cctor_run (method->klass, NULL)) {
1460 MonoVTable *vtable;
1461 ERROR_DECL (error);
1462 if (!m_class_get_runtime_info (method->klass))
1463 /* No vtable created yet */
1464 return FALSE;
1465 vtable = mono_class_vtable_checked (td->rtm->domain, method->klass, error);
1466 if (!is_ok (error)) {
1467 mono_error_cleanup (error);
1468 return FALSE;
1470 if (!vtable->initialized)
1471 return FALSE;
1474 /* We currently access at runtime the wrapper data */
1475 if (method->wrapper_type != MONO_WRAPPER_NONE)
1476 return FALSE;
1478 return TRUE;
1481 static gboolean
1482 interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHeader *header, MonoError *error)
1484 const unsigned char *prev_ip, *prev_il_code, *prev_in_start;
1485 StackInfo **prev_stack_state;
1486 int *prev_stack_height, *prev_vt_stack_size, *prev_clause_indexes, *prev_in_offsets;
1487 guint8 *prev_is_bb_start;
1488 gboolean ret;
1489 unsigned int prev_max_stack_height, prev_max_vt_sp, prev_total_locals_size;
1490 int prev_n_data_items;
1491 int i, prev_vt_sp;
1492 int prev_sp_offset;
1493 MonoGenericContext *generic_context = NULL;
1494 StackInfo *prev_param_area;
1495 MonoMethod *prev_inlined_method;
1496 MonoMethodSignature *csignature = mono_method_signature_internal (target_method);
1497 int nargs = csignature->param_count + !!csignature->hasthis;
1498 InterpInst *prev_last_ins;
1500 if (csignature->is_inflated)
1501 generic_context = mono_method_get_context (target_method);
1502 else {
1503 MonoGenericContainer *generic_container = mono_method_get_generic_container (target_method);
1504 if (generic_container)
1505 generic_context = &generic_container->context;
1508 prev_ip = td->ip;
1509 prev_il_code = td->il_code;
1510 prev_in_start = td->in_start;
1511 prev_sp_offset = td->sp - td->stack;
1512 prev_vt_sp = td->vt_sp;
1513 prev_stack_state = td->stack_state;
1514 prev_stack_height = td->stack_height;
1515 prev_vt_stack_size = td->vt_stack_size;
1516 prev_clause_indexes = td->clause_indexes;
1517 prev_inlined_method = td->inlined_method;
1518 prev_last_ins = td->last_ins;
1519 td->inlined_method = target_method;
1521 prev_max_stack_height = td->max_stack_height;
1522 prev_max_vt_sp = td->max_vt_sp;
1523 prev_total_locals_size = td->total_locals_size;
1525 prev_n_data_items = td->n_data_items;
1526 prev_in_offsets = td->in_offsets;
1527 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
1529 prev_is_bb_start = td->is_bb_start;
1531 /* Inlining pops the arguments, restore the stack */
1532 prev_param_area = (StackInfo*)g_malloc (nargs * sizeof (StackInfo));
1533 memcpy (prev_param_area, &td->sp [-nargs], nargs * sizeof (StackInfo));
1535 if (td->verbose_level)
1536 g_print ("Inline start method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1537 ret = generate_code (td, target_method, header, generic_context, error);
1539 if (!ret) {
1540 if (td->verbose_level)
1541 g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1542 td->max_stack_height = prev_max_stack_height;
1543 td->max_vt_sp = prev_max_vt_sp;
1544 td->total_locals_size = prev_total_locals_size;
1547 /* Remove any newly added items */
1548 for (i = prev_n_data_items; i < td->n_data_items; i++) {
1549 g_hash_table_remove (td->data_hash, td->data_items [i]);
1551 td->n_data_items = prev_n_data_items;
1552 td->sp = td->stack + prev_sp_offset;
1553 memcpy (&td->sp [-nargs], prev_param_area, nargs * sizeof (StackInfo));
1554 td->vt_sp = prev_vt_sp;
1555 td->last_ins = prev_last_ins;
1556 if (td->last_ins)
1557 td->last_ins->next = NULL;
1558 UnlockedIncrement (&mono_interp_stats.inline_failures);
1559 } else {
1560 if (td->verbose_level)
1561 g_print ("Inline end method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1562 UnlockedIncrement (&mono_interp_stats.inlined_methods);
1563 // Make sure we have an IR instruction associated with the now removed IL CALL
1564 // FIXME This could be prettier. We might be able to make inlining saner now that
1565 // that we can easily tweak the instruction list.
1566 if (!prev_inlined_method) {
1567 if (prev_last_ins) {
1568 if (prev_last_ins->next)
1569 prev_last_ins->next->il_offset = prev_in_start - prev_il_code;
1570 } else if (td->first_ins) {
1571 td->first_ins->il_offset = prev_in_start - prev_il_code;
1576 td->ip = prev_ip;
1577 td->in_start = prev_in_start;
1578 td->il_code = prev_il_code;
1579 td->stack_state = prev_stack_state;
1580 td->stack_height = prev_stack_height;
1581 td->vt_stack_size = prev_vt_stack_size;
1582 td->clause_indexes = prev_clause_indexes;
1583 td->is_bb_start = prev_is_bb_start;
1584 td->inlined_method = prev_inlined_method;
1586 g_free (td->in_offsets);
1587 td->in_offsets = prev_in_offsets;
1589 g_free (prev_param_area);
1590 return ret;
1593 /* Return FALSE if error, including inline failure */
1594 static gboolean
1595 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)
1597 MonoImage *image = m_class_get_image (method->klass);
1598 MonoMethodSignature *csignature;
1599 int is_virtual = *td->ip == CEE_CALLVIRT;
1600 int calli = *td->ip == CEE_CALLI || *td->ip == CEE_MONO_CALLI_EXTRA_ARG;
1601 int i;
1602 guint32 vt_stack_used = 0;
1603 guint32 vt_res_size = 0;
1604 int op = -1;
1605 int native = 0;
1606 int is_void = 0;
1607 int need_null_check = is_virtual;
1609 guint32 token = read32 (td->ip + 1);
1611 if (target_method == NULL) {
1612 if (calli) {
1613 CHECK_STACK(td, 1);
1614 if (method->wrapper_type != MONO_WRAPPER_NONE)
1615 csignature = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
1616 else {
1617 csignature = mono_metadata_parse_signature_checked (image, token, error);
1618 return_val_if_nok (error, FALSE);
1621 if (generic_context) {
1622 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
1623 return_val_if_nok (error, FALSE);
1627 * The compiled interp entry wrapper is passed to runtime_invoke instead of
1628 * the InterpMethod pointer. FIXME
1630 native = csignature->pinvoke || method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE;
1631 --td->sp;
1633 target_method = NULL;
1634 } else {
1635 if (method->wrapper_type == MONO_WRAPPER_NONE) {
1636 target_method = mono_get_method_checked (image, token, NULL, generic_context, error);
1637 return_val_if_nok (error, FALSE);
1638 } else
1639 target_method = (MonoMethod *)mono_method_get_wrapper_data (method, token);
1640 csignature = mono_method_signature_internal (target_method);
1642 if (generic_context) {
1643 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
1644 return_val_if_nok (error, FALSE);
1645 target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, error);
1646 return_val_if_nok (error, FALSE);
1649 } else {
1650 csignature = mono_method_signature_internal (target_method);
1653 if (check_visibility && target_method && !mono_method_can_access_method (method, target_method))
1654 interp_generate_mae_throw (td, method, target_method);
1656 if (target_method && target_method->string_ctor) {
1657 /* Create the real signature */
1658 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (td->mempool, csignature);
1659 ctor_sig->ret = m_class_get_byval_arg (mono_defaults.string_class);
1661 csignature = ctor_sig;
1664 /* Intrinsics */
1665 if (target_method && interp_handle_intrinsics (td, target_method, csignature, readonly, &op))
1666 return TRUE;
1668 if (constrained_class) {
1669 if (m_class_is_enumtype (constrained_class) && !strcmp (target_method->name, "GetHashCode")) {
1670 /* Use the corresponding method from the base type to avoid boxing */
1671 MonoType *base_type = mono_class_enum_basetype_internal (constrained_class);
1672 g_assert (base_type);
1673 constrained_class = mono_class_from_mono_type_internal (base_type);
1674 target_method = mono_class_get_method_from_name_checked (constrained_class, target_method->name, 0, 0, error);
1675 mono_error_assert_ok (error);
1676 g_assert (target_method);
1680 if (constrained_class) {
1681 mono_class_setup_vtable (constrained_class);
1682 if (mono_class_has_failure (constrained_class)) {
1683 mono_error_set_for_class_failure (error, constrained_class);
1684 return FALSE;
1686 #if DEBUG_INTERP
1687 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);
1688 #endif
1689 target_method = mono_get_method_constrained_with_method (image, target_method, constrained_class, generic_context, error);
1690 #if DEBUG_INTERP
1691 g_print (" : %s::%s. %s (%p)\n", target_method->klass->name, target_method->name, mono_signature_full_name (target_method->signature), target_method);
1692 #endif
1693 /* Intrinsics: Try again, it could be that `mono_get_method_constrained_with_method` resolves to a method that we can substitute */
1694 if (target_method && interp_handle_intrinsics (td, target_method, csignature, readonly, &op))
1695 return TRUE;
1697 return_val_if_nok (error, FALSE);
1698 mono_class_setup_vtable (target_method->klass);
1700 if (!m_class_is_valuetype (constrained_class)) {
1701 /* managed pointer on the stack, we need to deref that puppy */
1702 interp_add_ins (td, MINT_LDIND_I);
1703 td->last_ins->data [0] = csignature->param_count;
1704 } 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) {
1705 if (target_method->klass == mono_defaults.enum_class && (td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
1706 /* managed pointer on the stack, we need to deref that puppy */
1707 /* Always load the entire stackval, to handle also the case where the enum has long storage */
1708 interp_add_ins (td, MINT_LDIND_I8);
1709 td->last_ins->data [0] = csignature->param_count;
1711 if (mint_type (m_class_get_byval_arg (constrained_class)) == MINT_TYPE_VT) {
1712 interp_add_ins (td, MINT_BOX_VT);
1713 td->last_ins->data [0] = get_data_item_index (td, constrained_class);
1714 td->last_ins->data [1] = csignature->param_count | ((td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : BOX_NOT_CLEAR_VT_SP);
1715 } else {
1716 interp_add_ins (td, MINT_BOX);
1717 td->last_ins->data [0] = get_data_item_index (td, constrained_class);
1718 td->last_ins->data [1] = csignature->param_count;
1720 } else {
1721 if (target_method->klass != constrained_class) {
1723 * The type parameter is instantiated as a valuetype,
1724 * but that type doesn't override the method we're
1725 * calling, so we need to box `this'.
1727 if ((td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
1728 /* managed pointer on the stack, we need to deref that puppy */
1729 /* Always load the entire stackval, to handle also the case where the enum has long storage */
1730 interp_add_ins (td, MINT_LDIND_I8);
1731 td->last_ins->data [0] = csignature->param_count;
1733 if (mint_type (m_class_get_byval_arg (constrained_class)) == MINT_TYPE_VT) {
1734 interp_add_ins (td, MINT_BOX_VT);
1735 td->last_ins->data [0] = get_data_item_index (td, constrained_class);
1736 td->last_ins->data [1] = csignature->param_count | ((td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : BOX_NOT_CLEAR_VT_SP);
1737 } else {
1738 interp_add_ins (td, MINT_BOX);
1739 td->last_ins->data [0] = get_data_item_index (td, constrained_class);
1740 td->last_ins->data [1] = csignature->param_count;
1743 is_virtual = FALSE;
1747 if (target_method)
1748 mono_class_init_internal (target_method->klass);
1750 if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT))
1751 /* MS.NET seems to silently convert this to a callvirt */
1752 is_virtual = TRUE;
1754 if (is_virtual && target_method && (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
1755 (MONO_METHOD_IS_FINAL (target_method) &&
1756 target_method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
1757 !(mono_class_is_marshalbyref (target_method->klass))) {
1758 /* Not really virtual, just needs a null check */
1759 is_virtual = FALSE;
1760 need_null_check = TRUE;
1763 CHECK_STACK (td, csignature->param_count + csignature->hasthis);
1764 if (!calli && op == -1 && (!is_virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) &&
1765 (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 &&
1766 (target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0 &&
1767 !(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING)) {
1768 (void)mono_class_vtable_checked (domain, target_method->klass, error);
1769 return_val_if_nok (error, FALSE);
1771 if (method == target_method && *(td->ip + 5) == CEE_RET && !(csignature->hasthis && m_class_is_valuetype (target_method->klass))) {
1772 if (td->inlined_method)
1773 return FALSE;
1775 if (td->verbose_level)
1776 g_print ("Optimize tail call of %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1778 for (i = csignature->param_count - 1 + !!csignature->hasthis; i >= 0; --i)
1779 store_arg (td, i);
1781 interp_add_ins (td, MINT_BR_S);
1782 // We are branching to the beginning of the method
1783 td->last_ins->data [0] = 0;
1784 if (!is_bb_start [td->ip + 5 - td->il_code])
1785 ++td->ip; /* gobble the CEE_RET if it isn't branched to */
1786 td->ip += 5;
1787 return TRUE;
1791 target_method = interp_transform_internal_calls (method, target_method, csignature, is_virtual);
1793 if (csignature->call_convention == MONO_CALL_VARARG) {
1794 csignature = mono_method_get_signature_checked (target_method, image, token, generic_context, error);
1795 int vararg_stack = 0;
1797 * For vararg calls, ArgIterator expects the signature and the varargs to be
1798 * stored in a linear memory. We allocate the necessary vt_stack space for
1799 * this. All varargs will be pushed to the vt_stack at call site.
1801 vararg_stack += sizeof (gpointer);
1802 for (i = csignature->sentinelpos; i < csignature->param_count; ++i) {
1803 int align, arg_size;
1804 arg_size = mono_type_stack_size (csignature->params [i], &align);
1805 vararg_stack += arg_size;
1807 vt_stack_used += ALIGN_TO (vararg_stack, MINT_VT_ALIGNMENT);
1808 PUSH_VT (td, vararg_stack);
1811 if (need_null_check) {
1812 interp_add_ins (td, MINT_CKNULL_N);
1813 td->last_ins->data [0] = csignature->param_count + 1;
1816 g_assert (csignature->call_convention != MONO_CALL_FASTCALL);
1817 if ((mono_interp_opt & INTERP_OPT_INLINE) && op == -1 && !is_virtual && target_method && interp_method_check_inlining (td, target_method)) {
1818 MonoMethodHeader *mheader = interp_method_get_header (target_method, error);
1819 return_val_if_nok (error, FALSE);
1821 if (interp_inline_method (td, target_method, mheader, error)) {
1822 td->ip += 5;
1823 return TRUE;
1827 /* Don't inline methods that do calls */
1828 if (op == -1 && td->inlined_method)
1829 return FALSE;
1831 td->sp -= csignature->param_count + !!csignature->hasthis;
1832 for (i = 0; i < csignature->param_count; ++i) {
1833 if (td->sp [i + !!csignature->hasthis].type == STACK_TYPE_VT) {
1834 gint32 size;
1835 MonoClass *klass = mono_class_from_mono_type_internal (csignature->params [i]);
1836 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
1837 size = mono_class_native_size (klass, NULL);
1838 else
1839 size = mono_class_value_size (klass, NULL);
1840 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
1841 vt_stack_used += size;
1845 /* need to handle typedbyref ... */
1846 if (csignature->ret->type != MONO_TYPE_VOID) {
1847 int mt = mint_type(csignature->ret);
1848 MonoClass *klass = mono_class_from_mono_type_internal (csignature->ret);
1849 if (mt == MINT_TYPE_VT) {
1850 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
1851 vt_res_size = mono_class_native_size (klass, NULL);
1852 else
1853 vt_res_size = mono_class_value_size (klass, NULL);
1854 if (mono_class_has_failure (klass)) {
1855 mono_error_set_for_class_failure (error, klass);
1856 return FALSE;
1858 PUSH_VT(td, vt_res_size);
1860 PUSH_TYPE(td, stack_type[mt], klass);
1861 } else
1862 is_void = TRUE;
1864 /* We need to convert delegate invoke to a indirect call on the interp_invoke_impl field */
1865 if (target_method && m_class_get_parent (target_method->klass) == mono_defaults.multicastdelegate_class) {
1866 const char *name = target_method->name;
1867 if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
1868 calli = TRUE;
1869 interp_add_ins (td, MINT_LD_DELEGATE_INVOKE_IMPL);
1870 td->last_ins->data [0] = csignature->param_count + 1;
1874 if (op >= 0) {
1875 interp_add_ins (td, op);
1877 if (op == MINT_LDLEN) {
1878 #ifdef MONO_BIG_ARRAYS
1879 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
1880 #else
1881 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1882 #endif
1884 if (op == MINT_LDELEMA || op == MINT_LDELEMA_TC) {
1885 td->last_ins->data [0] = get_data_item_index (td, m_class_get_element_class (target_method->klass));
1886 td->last_ins->data [1] = 1 + m_class_get_rank (target_method->klass);
1889 if (op == MINT_CALLRUN) {
1890 td->last_ins->data [0] = get_data_item_index (td, target_method);
1891 td->last_ins->data [1] = get_data_item_index (td, mono_method_signature_internal (target_method));
1893 } else if (!calli && !is_virtual && jit_call_supported (target_method, csignature)) {
1894 interp_add_ins (td, MINT_JIT_CALL);
1895 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
1896 mono_error_assert_ok (error);
1897 } else {
1898 #ifndef MONO_ARCH_HAS_NO_PROPER_MONOCTX
1899 /* Try using fast icall path for simple signatures */
1900 if (native && !method->dynamic)
1901 op = interp_icall_op_for_sig (csignature);
1902 #endif
1903 if (csignature->call_convention == MONO_CALL_VARARG)
1904 interp_add_ins (td, MINT_CALL_VARARG);
1905 else if (calli)
1906 interp_add_ins (td, native ? ((op != -1) ? MINT_CALLI_NAT_FAST : MINT_CALLI_NAT) : MINT_CALLI);
1907 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass))
1908 interp_add_ins (td, is_void ? MINT_VCALLVIRT_FAST : MINT_CALLVIRT_FAST);
1909 else if (is_virtual)
1910 interp_add_ins (td, is_void ? MINT_VCALLVIRT : MINT_CALLVIRT);
1911 else
1912 interp_add_ins (td, is_void ? MINT_VCALL : MINT_CALL);
1914 if (calli) {
1915 td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
1916 if (op != -1)
1917 td->last_ins->data [1] = op;
1918 } else {
1919 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
1920 return_val_if_nok (error, FALSE);
1921 if (csignature->call_convention == MONO_CALL_VARARG)
1922 td->last_ins->data [1] = get_data_item_index (td, (void *)csignature);
1923 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass)) {
1924 /* FIXME Use fastpath also for MBRO. Asserts in mono_method_get_vtable_slot */
1925 if (mono_class_is_interface (target_method->klass))
1926 td->last_ins->data [1] = -2 * MONO_IMT_SIZE + mono_method_get_imt_slot (target_method);
1927 else
1928 td->last_ins->data [1] = mono_method_get_vtable_slot (target_method);
1932 td->ip += 5;
1933 if (vt_stack_used != 0 || vt_res_size != 0) {
1934 interp_add_ins (td, MINT_VTRESULT);
1935 td->last_ins->data [0] = vt_res_size;
1936 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
1937 td->vt_sp -= vt_stack_used;
1940 return TRUE;
1943 static MonoClassField *
1944 interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, MonoGenericContext *generic_context, MonoError *error)
1946 MonoClassField *field = NULL;
1947 if (method->wrapper_type != MONO_WRAPPER_NONE) {
1948 field = (MonoClassField *) mono_method_get_wrapper_data (method, token);
1949 *klass = field->parent;
1951 mono_class_setup_fields (field->parent);
1952 } else {
1953 field = mono_field_from_token_checked (m_class_get_image (method->klass), token, klass, generic_context, error);
1954 return_val_if_nok (error, NULL);
1957 if (!method->skip_visibility && !mono_method_can_access_field (method, field)) {
1958 char *method_fname = mono_method_full_name (method, TRUE);
1959 char *field_fname = mono_field_full_name (field);
1960 mono_error_set_generic_error (error, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
1961 g_free (method_fname);
1962 g_free (field_fname);
1963 return NULL;
1966 return field;
1969 static InterpBasicBlock*
1970 get_bb (TransformData *td, InterpBasicBlock *cbb, unsigned char *ip)
1972 int offset = ip - td->il_code;
1973 InterpBasicBlock *bb = td->offset_to_bb [offset];
1975 if (!bb) {
1976 bb = (InterpBasicBlock*)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock));
1977 bb->ip = ip;
1978 td->offset_to_bb [offset] = bb;
1980 td->basic_blocks = g_list_append_mempool (td->mempool, td->basic_blocks, bb);
1983 if (cbb)
1984 bb->preds = g_slist_prepend_mempool (td->mempool, bb->preds, cbb);
1985 return bb;
1989 * get_basic_blocks:
1991 * Compute the set of IL level basic blocks.
1993 static void
1994 get_basic_blocks (TransformData *td)
1996 guint8 *start = (guint8*)td->il_code;
1997 guint8 *end = (guint8*)td->il_code + td->code_size;
1998 guint8 *ip = start;
1999 unsigned char *target;
2000 int i;
2001 guint cli_addr;
2002 const MonoOpcode *opcode;
2003 InterpBasicBlock *cbb;
2005 td->offset_to_bb = (InterpBasicBlock**)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * (end - start + 1));
2006 td->entry_bb = cbb = get_bb (td, NULL, start);
2008 while (ip < end) {
2009 cli_addr = ip - start;
2010 td->offset_to_bb [cli_addr] = cbb;
2011 i = mono_opcode_value ((const guint8 **)&ip, end);
2012 opcode = &mono_opcodes [i];
2013 switch (opcode->argument) {
2014 case MonoInlineNone:
2015 ip++;
2016 break;
2017 case MonoInlineString:
2018 case MonoInlineType:
2019 case MonoInlineField:
2020 case MonoInlineMethod:
2021 case MonoInlineTok:
2022 case MonoInlineSig:
2023 case MonoShortInlineR:
2024 case MonoInlineI:
2025 ip += 5;
2026 break;
2027 case MonoInlineVar:
2028 ip += 3;
2029 break;
2030 case MonoShortInlineVar:
2031 case MonoShortInlineI:
2032 ip += 2;
2033 break;
2034 case MonoShortInlineBrTarget:
2035 target = start + cli_addr + 2 + (signed char)ip [1];
2036 get_bb (td, cbb, target);
2037 ip += 2;
2038 cbb = get_bb (td, cbb, ip);
2039 break;
2040 case MonoInlineBrTarget:
2041 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
2042 get_bb (td, cbb, target);
2043 ip += 5;
2044 cbb = get_bb (td, cbb, ip);
2045 break;
2046 case MonoInlineSwitch: {
2047 guint32 n = read32 (ip + 1);
2048 guint32 j;
2049 ip += 5;
2050 cli_addr += 5 + 4 * n;
2051 target = start + cli_addr;
2052 get_bb (td, cbb, target);
2054 for (j = 0; j < n; ++j) {
2055 target = start + cli_addr + (gint32)read32 (ip);
2056 get_bb (td, cbb, target);
2057 ip += 4;
2059 cbb = get_bb (td, cbb, ip);
2060 break;
2062 case MonoInlineR:
2063 case MonoInlineI8:
2064 ip += 9;
2065 break;
2066 default:
2067 g_assert_not_reached ();
2070 if (i == CEE_THROW)
2071 cbb = get_bb (td, NULL, ip);
2075 static void
2076 interp_save_debug_info (InterpMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers)
2078 MonoDebugMethodJitInfo *dinfo;
2079 int i;
2081 if (!mono_debug_enabled ())
2082 return;
2085 * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
2088 dinfo = g_new0 (MonoDebugMethodJitInfo, 1);
2089 dinfo->num_params = rtm->param_count;
2090 dinfo->params = g_new0 (MonoDebugVarInfo, dinfo->num_params);
2091 dinfo->num_locals = header->num_locals;
2092 dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals);
2093 dinfo->code_start = (guint8*)rtm->code;
2094 dinfo->code_size = td->new_code_end - td->new_code;
2095 dinfo->epilogue_begin = 0;
2096 dinfo->has_var_info = TRUE;
2097 dinfo->num_line_numbers = line_numbers->len;
2098 dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers);
2100 for (i = 0; i < dinfo->num_params; i++) {
2101 MonoDebugVarInfo *var = &dinfo->params [i];
2102 var->type = rtm->param_types [i];
2104 for (i = 0; i < dinfo->num_locals; i++) {
2105 MonoDebugVarInfo *var = &dinfo->locals [i];
2106 var->type = mono_metadata_type_dup (NULL, header->locals [i]);
2109 for (i = 0; i < dinfo->num_line_numbers; i++)
2110 dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i);
2111 mono_debug_add_method (rtm->method, dinfo, rtm->domain);
2113 mono_debug_free_method_jit_info (dinfo);
2116 /* Same as the code in seq-points.c */
2117 static void
2118 insert_pred_seq_point (SeqPoint *last_sp, SeqPoint *sp, GSList **next)
2120 GSList *l;
2121 int src_index = last_sp->next_offset;
2122 int dst_index = sp->next_offset;
2124 /* bb->in_bb might contain duplicates */
2125 for (l = next [src_index]; l; l = l->next)
2126 if (GPOINTER_TO_UINT (l->data) == dst_index)
2127 break;
2128 if (!l)
2129 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
2132 static void
2133 recursively_make_pred_seq_points (TransformData *td, InterpBasicBlock *bb)
2135 SeqPoint ** const MONO_SEQ_SEEN_LOOP = (SeqPoint**)GINT_TO_POINTER(-1);
2136 GSList *l;
2138 GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
2139 GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
2141 // Insert/remove sentinel into the memoize table to detect loops containing bb
2142 bb->pred_seq_points = MONO_SEQ_SEEN_LOOP;
2144 for (l = bb->preds; l; l = l->next) {
2145 InterpBasicBlock *in_bb = (InterpBasicBlock*)l->data;
2147 // This bb has the last seq point, append it and continue
2148 if (in_bb->last_seq_point != NULL) {
2149 predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
2150 continue;
2153 // We've looped or handled this before, exit early.
2154 // No last sequence points to find.
2155 if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
2156 continue;
2158 // Take sequence points from incoming basic blocks
2160 if (in_bb == td->entry_bb)
2161 continue;
2163 if (in_bb->pred_seq_points == NULL)
2164 recursively_make_pred_seq_points (td, in_bb);
2166 // Union sequence points with incoming bb's
2167 for (int i=0; i < in_bb->num_pred_seq_points; i++) {
2168 if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
2169 g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
2170 g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
2173 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
2176 g_hash_table_destroy (seen);
2178 if (predecessors->len != 0) {
2179 bb->pred_seq_points = (SeqPoint**)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint *) * predecessors->len);
2180 bb->num_pred_seq_points = predecessors->len;
2182 for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
2183 bb->pred_seq_points [newer] = (SeqPoint*)g_array_index (predecessors, gpointer, newer);
2187 g_array_free (predecessors, TRUE);
2190 static void
2191 collect_pred_seq_points (TransformData *td, InterpBasicBlock *bb, SeqPoint *seqp, GSList **next)
2193 // Doesn't have a last sequence point, must find from incoming basic blocks
2194 if (bb->pred_seq_points == NULL && bb != td->entry_bb)
2195 recursively_make_pred_seq_points (td, bb);
2197 for (int i = 0; i < bb->num_pred_seq_points; i++)
2198 insert_pred_seq_point (bb->pred_seq_points [i], seqp, next);
2200 return;
2203 static void
2204 save_seq_points (TransformData *td, MonoJitInfo *jinfo)
2206 GByteArray *array;
2207 int i, seq_info_size;
2208 MonoSeqPointInfo *info;
2209 GSList **next = NULL;
2210 GList *bblist;
2212 if (!td->gen_sdb_seq_points)
2213 return;
2216 * For each sequence point, compute the list of sequence points immediately
2217 * following it, this is needed to implement 'step over' in the debugger agent.
2218 * Similar to the code in mono_save_seq_point_info ().
2220 for (i = 0; i < td->seq_points->len; ++i) {
2221 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2223 /* Store the seq point index here temporarily */
2224 sp->next_offset = i;
2226 next = (GSList**)mono_mempool_alloc0 (td->mempool, sizeof (GList*) * td->seq_points->len);
2227 for (bblist = td->basic_blocks; bblist; bblist = bblist->next) {
2228 InterpBasicBlock *bb = (InterpBasicBlock*)bblist->data;
2230 GSList *bb_seq_points = g_slist_reverse (bb->seq_points);
2231 SeqPoint *last = NULL;
2232 for (GSList *l = bb_seq_points; l; l = l->next) {
2233 SeqPoint *sp = (SeqPoint*)l->data;
2235 if (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET)
2236 /* Used to implement method entry/exit events */
2237 continue;
2239 if (last != NULL) {
2240 /* Link with the previous seq point in the same bb */
2241 next [last->next_offset] = g_slist_append_mempool (td->mempool, next [last->next_offset], GINT_TO_POINTER (sp->next_offset));
2242 } else {
2243 /* Link with the last bb in the previous bblocks */
2244 collect_pred_seq_points (td, bb, sp, next);
2246 last = sp;
2250 /* Serialize the seq points into a byte array */
2251 array = g_byte_array_new ();
2252 SeqPoint zero_seq_point = {0};
2253 SeqPoint* last_seq_point = &zero_seq_point;
2254 for (i = 0; i < td->seq_points->len; ++i) {
2255 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2257 sp->next_offset = 0;
2258 if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next [i], TRUE))
2259 last_seq_point = sp;
2262 if (td->verbose_level) {
2263 g_print ("\nSEQ POINT MAP FOR %s: \n", td->method->name);
2265 for (i = 0; i < td->seq_points->len; ++i) {
2266 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2267 GSList *l;
2269 if (!next [i])
2270 continue;
2272 g_print ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
2273 for (l = next [i]; l; l = l->next) {
2274 int next_index = GPOINTER_TO_UINT (l->data);
2275 g_print (" IL0x%x", ((SeqPoint*)g_ptr_array_index (td->seq_points, next_index))->il_offset);
2277 g_print ("\n");
2281 info = mono_seq_point_info_new (array->len, TRUE, array->data, TRUE, &seq_info_size);
2282 mono_atomic_fetch_add_i32 (&mono_jit_stats.allocated_seq_points_size, seq_info_size);
2284 g_byte_array_free (array, TRUE);
2286 jinfo->seq_points = info;
2289 #define BARRIER_IF_VOLATILE(td) \
2290 do { \
2291 if (volatile_) { \
2292 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER); \
2293 volatile_ = FALSE; \
2295 } while (0)
2297 #define INLINE_FAILURE \
2298 do { \
2299 if (td->method != method) \
2300 goto exit; \
2301 } while (0)
2303 static void
2304 interp_method_compute_offsets (InterpMethod *imethod, MonoMethodSignature *signature, MonoMethodHeader *header)
2306 int i, offset, size, align;
2308 imethod->local_offsets = (guint32*)g_malloc (header->num_locals * sizeof(guint32));
2309 offset = 0;
2310 for (i = 0; i < header->num_locals; ++i) {
2311 size = mono_type_size (header->locals [i], &align);
2312 offset += align - 1;
2313 offset &= ~(align - 1);
2314 imethod->local_offsets [i] = offset;
2315 offset += size;
2317 offset = (offset + 7) & ~7;
2319 imethod->exvar_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32));
2320 for (i = 0; i < header->num_clauses; i++) {
2321 imethod->exvar_offsets [i] = offset;
2322 offset += sizeof (MonoObject*);
2324 offset = (offset + 7) & ~7;
2326 imethod->locals_size = offset;
2327 g_assert (imethod->locals_size < 65536);
2330 static MonoType*
2331 get_arg_type (MonoMethodSignature *signature, int arg_n)
2333 if (signature->hasthis && arg_n == 0)
2334 return mono_get_object_type ();
2335 return signature->params [arg_n - !!signature->hasthis];
2338 static void
2339 init_bb_start (TransformData *td, MonoMethodHeader *header)
2341 const unsigned char *ip, *end;
2342 const MonoOpcode *opcode;
2343 int offset, i, in, backwards;
2345 /* intern the strings in the method. */
2346 ip = header->code;
2347 end = ip + header->code_size;
2349 td->is_bb_start [0] = 1;
2350 while (ip < end) {
2351 in = *ip;
2352 if (in == 0xfe) {
2353 ip++;
2354 in = *ip + 256;
2356 else if (in == 0xf0) {
2357 ip++;
2358 in = *ip + MONO_CEE_MONO_ICALL;
2360 opcode = &mono_opcodes [in];
2361 switch (opcode->argument) {
2362 case MonoInlineNone:
2363 ++ip;
2364 break;
2365 case MonoInlineString:
2366 ip += 5;
2367 break;
2368 case MonoInlineType:
2369 ip += 5;
2370 break;
2371 case MonoInlineMethod:
2372 ip += 5;
2373 break;
2374 case MonoInlineField:
2375 case MonoInlineSig:
2376 case MonoInlineI:
2377 case MonoInlineTok:
2378 case MonoShortInlineR:
2379 ip += 5;
2380 break;
2381 case MonoInlineBrTarget:
2382 offset = read32 (ip + 1);
2383 ip += 5;
2384 backwards = offset < 0;
2385 offset += ip - header->code;
2386 g_assert (offset >= 0 && offset < header->code_size);
2387 td->is_bb_start [offset] |= backwards ? 2 : 1;
2388 break;
2389 case MonoShortInlineBrTarget:
2390 offset = ((gint8 *)ip) [1];
2391 ip += 2;
2392 backwards = offset < 0;
2393 offset += ip - header->code;
2394 g_assert (offset >= 0 && offset < header->code_size);
2395 td->is_bb_start [offset] |= backwards ? 2 : 1;
2396 break;
2397 case MonoInlineVar:
2398 ip += 3;
2399 break;
2400 case MonoShortInlineVar:
2401 case MonoShortInlineI:
2402 ip += 2;
2403 break;
2404 case MonoInlineSwitch: {
2405 guint32 n;
2406 const unsigned char *next_ip;
2407 ++ip;
2408 n = read32 (ip);
2409 ip += 4;
2410 next_ip = ip + 4 * n;
2411 for (i = 0; i < n; i++) {
2412 offset = read32 (ip);
2413 backwards = offset < 0;
2414 offset += next_ip - header->code;
2415 g_assert (offset >= 0 && offset < header->code_size);
2416 td->is_bb_start [offset] |= backwards ? 2 : 1;
2417 ip += 4;
2419 break;
2421 case MonoInlineR:
2422 case MonoInlineI8:
2423 ip += 9;
2424 break;
2425 default:
2426 g_assert_not_reached ();
2431 #ifdef NO_UNALIGNED_ACCESS
2432 static int
2433 get_unaligned_opcode (int opcode)
2435 switch (opcode) {
2436 case MINT_LDFLD_I8:
2437 return MINT_LDFLD_I8_UNALIGNED;
2438 case MINT_LDFLD_R8:
2439 return MINT_LDFLD_R8_UNALIGNED;
2440 case MINT_STFLD_I8:
2441 return MINT_STFLD_I8_UNALIGNED;
2442 case MINT_STFLD_R8:
2443 return MINT_STFLD_R8_UNALIGNED;
2444 default:
2445 g_assert_not_reached ();
2447 return -1;
2449 #endif
2451 static void
2452 interp_handle_isinst (TransformData *td, MonoClass *klass, gboolean isinst_instr)
2454 /* Follow the logic from jit's handle_isinst */
2455 if (!mono_class_has_variant_generic_params (klass)) {
2456 if (mono_class_is_interface (klass))
2457 interp_add_ins (td, isinst_instr ? MINT_ISINST_INTERFACE : MINT_CASTCLASS_INTERFACE);
2458 else if (!mono_class_is_marshalbyref (klass) && m_class_get_rank (klass) == 0 && !mono_class_is_nullable (klass))
2459 interp_add_ins (td, isinst_instr ? MINT_ISINST_COMMON : MINT_CASTCLASS_COMMON);
2460 else
2461 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2462 } else {
2463 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2465 td->last_ins->data [0] = get_data_item_index (td, klass);
2466 td->ip += 5;
2469 static void
2470 interp_emit_ldobj (TransformData *td, MonoClass *klass)
2472 int mt = mint_type (m_class_get_byval_arg (klass));
2473 int size;
2475 if (mt == MINT_TYPE_VT) {
2476 interp_add_ins (td, MINT_LDOBJ_VT);
2477 td->last_ins->data [0] = get_data_item_index(td, klass);
2478 size = mono_class_value_size (klass, NULL);
2479 PUSH_VT (td, size);
2480 } else {
2481 int opcode;
2482 switch (mt) {
2483 case MINT_TYPE_I1: opcode = MINT_LDIND_I1_CHECK; break;
2484 case MINT_TYPE_U1: opcode = MINT_LDIND_U1_CHECK; break;
2485 case MINT_TYPE_I2: opcode = MINT_LDIND_I2_CHECK; break;
2486 case MINT_TYPE_U2: opcode = MINT_LDIND_U2_CHECK; break;
2487 case MINT_TYPE_I4: opcode = MINT_LDIND_I4_CHECK; break;
2488 case MINT_TYPE_I8: opcode = MINT_LDIND_I8_CHECK; break;
2489 case MINT_TYPE_R4: opcode = MINT_LDIND_R4_CHECK; break;
2490 case MINT_TYPE_R8: opcode = MINT_LDIND_R8_CHECK; break;
2491 case MINT_TYPE_O: opcode = MINT_LDIND_REF; break;
2492 default: g_assert_not_reached (); break;
2494 interp_add_ins (td, opcode);
2497 SET_TYPE (td->sp - 1, stack_type [mt], klass);
2500 static void
2501 interp_emit_stobj (TransformData *td, MonoClass *klass)
2503 int mt = mint_type (m_class_get_byval_arg (klass));
2505 if (mt == MINT_TYPE_VT) {
2506 int size;
2507 interp_add_ins (td, MINT_STOBJ_VT);
2508 td->last_ins->data [0] = get_data_item_index(td, klass);
2509 size = mono_class_value_size (klass, NULL);
2510 POP_VT (td, size);
2511 } else {
2512 int opcode;
2513 switch (mt) {
2514 case MINT_TYPE_I1:
2515 case MINT_TYPE_U1:
2516 opcode = MINT_STIND_I1;
2517 break;
2518 case MINT_TYPE_I2:
2519 case MINT_TYPE_U2:
2520 opcode = MINT_STIND_I2;
2521 break;
2522 case MINT_TYPE_I4:
2523 opcode = MINT_STIND_I4;
2524 break;
2525 case MINT_TYPE_I8:
2526 opcode = MINT_STIND_I8;
2527 break;
2528 case MINT_TYPE_R4:
2529 opcode = MINT_STIND_R4;
2530 break;
2531 case MINT_TYPE_R8:
2532 opcode = MINT_STIND_R8;
2533 break;
2534 case MINT_TYPE_O:
2535 opcode = MINT_STIND_REF;
2536 break;
2537 default: g_assert_not_reached (); break;
2539 interp_add_ins (td, opcode);
2541 td->sp -= 2;
2544 static void
2545 interp_emit_ldsfld (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, MonoError *error)
2547 if (mono_class_field_is_special_static (field)) {
2548 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSSFLD_VT_SLOW : MINT_LDSSFLD_SLOW);
2549 td->last_ins->data [0] = get_data_item_index (td, field);
2550 if (mt == MINT_TYPE_VT) {
2551 int size = mono_class_value_size (field_class, NULL);
2552 WRITE32_INS(td->last_ins, 1, &size);
2554 } else {
2555 MonoVTable *vtable = mono_class_vtable_checked (td->rtm->domain, field->parent, error);
2556 return_if_nok (error);
2558 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSFLD_VT : (MINT_LDSFLD_I1 + mt - MINT_TYPE_I1));
2559 td->last_ins->data [0] = get_data_item_index (td, vtable);
2560 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
2562 if (mt == MINT_TYPE_VT) {
2563 int size = mono_class_value_size (field_class, NULL);
2564 WRITE32_INS(td->last_ins, 2, &size);
2569 static void
2570 interp_emit_stsfld (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, MonoError *error)
2572 if (mono_class_field_is_special_static (field)) {
2573 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSSFLD_VT_SLOW : MINT_STSSFLD_SLOW);
2574 td->last_ins->data [0] = get_data_item_index (td, field);
2575 if (mt == MINT_TYPE_VT) {
2576 int size = mono_class_value_size (field_class, NULL);
2577 WRITE32_INS(td->last_ins, 1, &size);
2579 } else {
2580 MonoVTable *vtable = mono_class_vtable_checked (td->rtm->domain, field->parent, error);
2581 return_if_nok (error);
2583 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1));
2584 td->last_ins->data [0] = get_data_item_index (td, vtable);
2585 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
2587 if (mt == MINT_TYPE_VT) {
2588 int size = mono_class_value_size (field_class, NULL);
2589 WRITE32_INS(td->last_ins, 2, &size);
2594 static gboolean
2595 generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error)
2597 int target;
2598 int offset, mt, i, i32;
2599 guint32 token;
2600 int in_offset;
2601 const unsigned char *end;
2602 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
2603 gboolean sym_seq_points = FALSE;
2604 MonoBitSet *seq_point_locs = NULL;
2605 gboolean readonly = FALSE;
2606 gboolean volatile_ = FALSE;
2607 MonoClass *constrained_class = NULL;
2608 MonoClass *klass;
2609 MonoClassField *field;
2610 MonoImage *image = m_class_get_image (method->klass);
2611 InterpMethod *rtm = td->rtm;
2612 MonoDomain *domain = rtm->domain;
2613 MonoMethodSignature *signature = mono_method_signature_internal (method);
2614 gboolean ret = TRUE;
2615 gboolean emitted_funccall_seq_point = FALSE;
2616 guint32 *arg_offsets = NULL;
2617 InterpInst *last_seq_point = NULL;
2619 original_bb = bb = mono_basic_block_split (method, error, header);
2620 goto_if_nok (error, exit);
2621 g_assert (bb);
2623 td->il_code = header->code;
2624 td->in_start = td->ip = header->code;
2625 end = td->ip + header->code_size;
2626 td->stack_state = (StackInfo**)g_malloc0(header->code_size * sizeof(StackInfo *));
2627 td->stack_height = (int*)g_malloc(header->code_size * sizeof(int));
2628 td->vt_stack_size = (int*)g_malloc(header->code_size * sizeof(int));
2629 td->clause_indexes = (int*)g_malloc (header->code_size * sizeof (int));
2630 td->is_bb_start = (guint8*)g_malloc0(header->code_size);
2632 init_bb_start (td, header);
2634 for (i = 0; i < header->code_size; i++) {
2635 td->stack_height [i] = -1;
2636 td->clause_indexes [i] = -1;
2639 for (i = 0; i < header->num_clauses; i++) {
2640 MonoExceptionClause *c = header->clauses + i;
2641 td->stack_height [c->handler_offset] = 0;
2642 td->vt_stack_size [c->handler_offset] = 0;
2643 td->is_bb_start [c->handler_offset] = 1;
2645 td->stack_height [c->handler_offset] = 1;
2646 td->stack_state [c->handler_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
2647 td->stack_state [c->handler_offset][0].type = STACK_TYPE_O;
2648 td->stack_state [c->handler_offset][0].klass = NULL; /*FIX*/
2650 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER) {
2651 td->stack_height [c->data.filter_offset] = 0;
2652 td->vt_stack_size [c->data.filter_offset] = 0;
2653 td->is_bb_start [c->data.filter_offset] = 1;
2655 td->stack_height [c->data.filter_offset] = 1;
2656 td->stack_state [c->data.filter_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
2657 td->stack_state [c->data.filter_offset][0].type = STACK_TYPE_O;
2658 td->stack_state [c->data.filter_offset][0].klass = NULL; /*FIX*/
2661 for (int j = c->handler_offset; j < c->handler_offset + c->handler_len; ++j) {
2662 if (td->clause_indexes [j] == -1)
2663 td->clause_indexes [j] = i;
2667 if (td->gen_sdb_seq_points && td->method == method) {
2668 MonoDebugMethodInfo *minfo;
2669 get_basic_blocks (td);
2671 minfo = mono_debug_lookup_method (method);
2673 if (minfo) {
2674 MonoSymSeqPoint *sps;
2675 int i, n_il_offsets;
2677 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
2678 // FIXME: Free
2679 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
2680 sym_seq_points = TRUE;
2682 for (i = 0; i < n_il_offsets; ++i) {
2683 if (sps [i].il_offset < header->code_size)
2684 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
2686 g_free (sps);
2688 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
2689 if (asyncMethod) {
2690 for (i = 0; asyncMethod != NULL && i < asyncMethod->num_awaits; i++) {
2691 mono_bitset_set_fast (seq_point_locs, asyncMethod->resume_offsets [i]);
2692 mono_bitset_set_fast (seq_point_locs, asyncMethod->yield_offsets [i]);
2694 mono_debug_free_method_async_debug_info (asyncMethod);
2696 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (m_class_get_image (method->klass))) {
2697 /* Methods without line number info like auto-generated property accessors */
2698 seq_point_locs = mono_bitset_new (header->code_size, 0);
2699 sym_seq_points = TRUE;
2703 if (sym_seq_points) {
2704 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
2705 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY;
2708 if (mono_debugger_method_has_breakpoint (method))
2709 interp_add_ins (td, MINT_BREAKPOINT);
2711 if (method == td->method) {
2712 if (td->verbose_level) {
2713 char *tmp = mono_disasm_code (NULL, method, td->ip, end);
2714 char *name = mono_method_full_name (method, TRUE);
2715 g_print ("Method %s, original code:\n", name);
2716 g_print ("%s\n", tmp);
2717 g_free (tmp);
2718 g_free (name);
2721 if (header->num_locals && header->init_locals)
2722 interp_add_ins (td, MINT_INITLOCALS);
2724 if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER)
2725 interp_add_ins (td, MINT_PROF_ENTER);
2727 /* safepoint is required on method entry */
2728 if (mono_threads_are_safepoints_enabled ())
2729 interp_add_ins (td, MINT_SAFEPOINT);
2730 } else {
2731 int offset;
2732 arg_offsets = (guint32*) g_malloc ((!!signature->hasthis + signature->param_count) * sizeof (guint32));
2733 /* Allocate locals to store inlined method args from stack */
2734 for (i = signature->param_count - 1; i >= 0; i--) {
2735 offset = create_interp_local (td, signature->params [i]);
2736 arg_offsets [i + !!signature->hasthis] = offset;
2737 store_local_general (td, offset, signature->params [i]);
2740 if (signature->hasthis) {
2742 * If this is value type, it is passed by address and not by value.
2743 * FIXME We should use MINT_TYPE_P instead of MINT_TYPE_O
2745 MonoType *type = mono_get_object_type ();
2746 offset = create_interp_local (td, type);
2747 arg_offsets [0] = offset;
2748 store_local_general (td, offset, type);
2752 while (td->ip < end) {
2753 g_assert (td->sp >= td->stack);
2754 g_assert (td->vt_sp < 0x10000000);
2755 in_offset = td->ip - header->code;
2756 td->in_start = td->ip;
2757 InterpInst *prev_last_ins = td->last_ins;
2759 if (td->stack_height [in_offset] >= 0) {
2760 g_assert (td->is_bb_start [in_offset]);
2761 if (td->stack_height [in_offset] > 0)
2762 memcpy (td->stack, td->stack_state [in_offset], td->stack_height [in_offset] * sizeof(td->stack [0]));
2763 td->sp = td->stack + td->stack_height [in_offset];
2764 td->vt_sp = td->vt_stack_size [in_offset];
2767 if (in_offset == bb->end)
2768 bb = bb->next;
2770 if (bb->dead) {
2771 int op_size = mono_opcode_size (td->ip, end);
2772 g_assert (op_size > 0); /* The BB formation pass must catch all bad ops */
2774 if (td->verbose_level > 1)
2775 g_print ("SKIPPING DEAD OP at %x\n", in_offset);
2777 td->ip += op_size;
2778 continue;
2781 if (td->verbose_level > 1) {
2782 g_print ("IL_%04lx %s %-10s, sp %ld, %s %-12s vt_sp %u (max %u)\n",
2783 td->ip - td->il_code,
2784 td->is_bb_start [td->ip - td->il_code] == 3 ? "<>" :
2785 td->is_bb_start [td->ip - td->il_code] == 2 ? "< " :
2786 td->is_bb_start [td->ip - td->il_code] == 1 ? " >" : " ",
2787 mono_opcode_name (*td->ip), td->sp - td->stack,
2788 td->sp > td->stack ? stack_type_string [td->sp [-1].type] : " ",
2789 (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)) : "",
2790 td->vt_sp, td->max_vt_sp);
2793 if (sym_seq_points && mono_bitset_test_fast (seq_point_locs, td->ip - header->code)) {
2794 InterpBasicBlock *cbb = td->offset_to_bb [td->ip - header->code];
2795 g_assert (cbb);
2798 * Make methods interruptable at the beginning, and at the targets of
2799 * backward branches.
2801 if (in_offset == 0 || g_slist_length (cbb->preds) > 1)
2802 interp_add_ins (td, MINT_SDB_INTR_LOC);
2804 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
2807 if (td->is_bb_start [in_offset]) {
2808 int index = td->clause_indexes [in_offset];
2809 if (index != -1) {
2810 MonoExceptionClause *clause = &header->clauses [index];
2811 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
2812 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) &&
2813 in_offset == clause->handler_offset)
2814 interp_add_ins (td, MINT_START_ABORT_PROT);
2818 switch (*td->ip) {
2819 case CEE_NOP:
2820 /* lose it */
2821 emitted_funccall_seq_point = FALSE;
2822 ++td->ip;
2823 break;
2824 case CEE_BREAK:
2825 SIMPLE_OP(td, MINT_BREAK);
2826 break;
2827 case CEE_LDARG_0:
2828 case CEE_LDARG_1:
2829 case CEE_LDARG_2:
2830 case CEE_LDARG_3: {
2831 int arg_n = *td->ip - CEE_LDARG_0;
2832 if (td->method == method)
2833 load_arg (td, arg_n);
2834 else
2835 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
2836 ++td->ip;
2837 break;
2839 case CEE_LDLOC_0:
2840 case CEE_LDLOC_1:
2841 case CEE_LDLOC_2:
2842 case CEE_LDLOC_3:
2843 load_local (td, *td->ip - CEE_LDLOC_0);
2844 ++td->ip;
2845 break;
2846 case CEE_STLOC_0:
2847 case CEE_STLOC_1:
2848 case CEE_STLOC_2:
2849 case CEE_STLOC_3:
2850 store_local (td, *td->ip - CEE_STLOC_0);
2851 ++td->ip;
2852 break;
2853 case CEE_LDARG_S: {
2854 int arg_n = ((guint8 *)td->ip)[1];
2855 if (td->method == method)
2856 load_arg (td, arg_n);
2857 else
2858 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
2859 td->ip += 2;
2860 break;
2862 case CEE_LDARGA_S: {
2863 /* NOTE: n includes this */
2864 INLINE_FAILURE; // probably uncommon
2865 int n = ((guint8 *) td->ip) [1];
2867 get_arg_type_exact (td, n, &mt);
2868 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
2869 td->last_ins->data [0] = n;
2870 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
2871 td->ip += 2;
2872 break;
2874 case CEE_STARG_S: {
2875 int arg_n = ((guint8 *)td->ip)[1];
2876 if (td->method == method)
2877 store_arg (td, arg_n);
2878 else
2879 store_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
2880 td->ip += 2;
2881 break;
2883 case CEE_LDLOC_S:
2884 load_local (td, ((guint8 *)td->ip)[1]);
2885 td->ip += 2;
2886 break;
2887 case CEE_LDLOCA_S:
2888 interp_add_ins (td, MINT_LDLOCA_S);
2889 td->last_ins->data [0] = td->rtm->local_offsets [((guint8 *)td->ip)[1]];
2890 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
2891 td->ip += 2;
2892 break;
2893 case CEE_STLOC_S:
2894 store_local (td, ((guint8 *)td->ip)[1]);
2895 td->ip += 2;
2896 break;
2897 case CEE_LDNULL:
2898 SIMPLE_OP(td, MINT_LDNULL);
2899 PUSH_TYPE(td, STACK_TYPE_O, NULL);
2900 break;
2901 case CEE_LDC_I4_M1:
2902 SIMPLE_OP(td, MINT_LDC_I4_M1);
2903 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
2904 break;
2905 case CEE_LDC_I4_0:
2906 if (!td->is_bb_start[td->ip + 1 - td->il_code] && td->ip [1] == 0xfe && td->ip [2] == CEE_CEQ &&
2907 td->sp > td->stack && td->sp [-1].type == STACK_TYPE_I4) {
2908 SIMPLE_OP(td, MINT_CEQ0_I4);
2909 td->ip += 2;
2910 } else {
2911 SIMPLE_OP(td, MINT_LDC_I4_0);
2912 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
2914 break;
2915 case CEE_LDC_I4_1:
2916 if (!td->is_bb_start[td->ip + 1 - td->il_code] &&
2917 (td->ip [1] == CEE_ADD || td->ip [1] == CEE_SUB) && td->sp [-1].type == STACK_TYPE_I4) {
2918 interp_add_ins (td, td->ip [1] == CEE_ADD ? MINT_ADD1_I4 : MINT_SUB1_I4);
2919 td->ip += 2;
2920 } else {
2921 SIMPLE_OP(td, MINT_LDC_I4_1);
2922 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
2924 break;
2925 case CEE_LDC_I4_2:
2926 case CEE_LDC_I4_3:
2927 case CEE_LDC_I4_4:
2928 case CEE_LDC_I4_5:
2929 case CEE_LDC_I4_6:
2930 case CEE_LDC_I4_7:
2931 case CEE_LDC_I4_8:
2932 SIMPLE_OP(td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0);
2933 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
2934 break;
2935 case CEE_LDC_I4_S:
2936 interp_add_ins (td, MINT_LDC_I4_S);
2937 td->last_ins->data [0] = ((gint8 *) td->ip) [1];
2938 td->ip += 2;
2939 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
2940 break;
2941 case CEE_LDC_I4:
2942 i32 = read32 (td->ip + 1);
2943 interp_add_ins (td, MINT_LDC_I4);
2944 WRITE32_INS (td->last_ins, 0, &i32);
2945 td->ip += 5;
2946 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
2947 break;
2948 case CEE_LDC_I8: {
2949 gint64 val = read64 (td->ip + 1);
2950 interp_add_ins (td, MINT_LDC_I8);
2951 WRITE64_INS (td->last_ins, 0, &val);
2952 td->ip += 9;
2953 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I8);
2954 break;
2956 case CEE_LDC_R4: {
2957 float val;
2958 readr4 (td->ip + 1, &val);
2959 interp_add_ins (td, MINT_LDC_R4);
2960 WRITE32_INS (td->last_ins, 0, &val);
2961 td->ip += 5;
2962 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R4);
2963 break;
2965 case CEE_LDC_R8: {
2966 double val;
2967 readr8 (td->ip + 1, &val);
2968 interp_add_ins (td, MINT_LDC_R8);
2969 WRITE64_INS (td->last_ins, 0, &val);
2970 td->ip += 9;
2971 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R8);
2972 break;
2974 case CEE_DUP: {
2975 int type = td->sp [-1].type;
2976 MonoClass *klass = td->sp [-1].klass;
2977 if (td->sp [-1].type == STACK_TYPE_VT) {
2978 gint32 size = mono_class_value_size (klass, NULL);
2979 PUSH_VT(td, size);
2980 interp_add_ins (td, MINT_DUP_VT);
2981 WRITE32_INS (td->last_ins, 0, &size);
2982 td->ip ++;
2983 } else
2984 SIMPLE_OP(td, MINT_DUP);
2985 PUSH_TYPE(td, type, klass);
2986 break;
2988 case CEE_POP:
2989 CHECK_STACK(td, 1);
2990 SIMPLE_OP(td, MINT_POP);
2991 td->last_ins->data [0] = 0;
2992 if (td->sp [-1].type == STACK_TYPE_VT) {
2993 int size = mono_class_value_size (td->sp [-1].klass, NULL);
2994 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
2995 interp_add_ins (td, MINT_VTRESULT);
2996 td->last_ins->data [0] = 0;
2997 WRITE32_INS (td->last_ins, 1, &size);
2998 td->vt_sp -= size;
3000 --td->sp;
3001 break;
3002 case CEE_JMP: {
3003 MonoMethod *m;
3004 INLINE_FAILURE;
3005 if (td->sp > td->stack)
3006 g_warning ("CEE_JMP: stack must be empty");
3007 token = read32 (td->ip + 1);
3008 m = mono_get_method_checked (image, token, NULL, generic_context, error);
3009 goto_if_nok (error, exit);
3010 interp_add_ins (td, MINT_JMP);
3011 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3012 goto_if_nok (error, exit);
3013 td->ip += 5;
3014 break;
3016 case CEE_CALLVIRT: /* Fall through */
3017 case CEE_CALLI: /* Fall through */
3018 case CEE_CALL: {
3019 gboolean need_seq_point = FALSE;
3021 if (sym_seq_points && !mono_bitset_test_fast (seq_point_locs, td->ip + 5 - header->code))
3022 need_seq_point = TRUE;
3024 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, constrained_class, readonly, error, TRUE))
3025 goto exit;
3027 if (need_seq_point) {
3028 //check is is a nested call and remove the MONO_INST_NONEMPTY_STACK of the last breakpoint, only for non native methods
3029 if (!(method->flags & METHOD_IMPL_ATTRIBUTE_NATIVE)) {
3030 if (emitted_funccall_seq_point) {
3031 if (last_seq_point)
3032 last_seq_point->flags |= INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL;
3034 else
3035 emitted_funccall_seq_point = TRUE;
3037 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3038 // This seq point is actually associated with the instruction following the call
3039 last_seq_point->il_offset = td->ip - header->code;
3040 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK;
3043 constrained_class = NULL;
3044 readonly = FALSE;
3045 break;
3047 case CEE_RET: {
3048 /* Return from inlined method, return value is on top of stack */
3049 if (td->method != method) {
3050 td->ip++;
3051 break;
3054 int vt_size = 0;
3055 MonoType *ult = mini_type_get_underlying_type (signature->ret);
3056 if (ult->type != MONO_TYPE_VOID) {
3057 CHECK_STACK (td, 1);
3058 --td->sp;
3059 if (mint_type (ult) == MINT_TYPE_VT) {
3060 MonoClass *klass = mono_class_from_mono_type_internal (ult);
3061 vt_size = mono_class_value_size (klass, NULL);
3064 if (td->sp > td->stack) {
3065 mono_error_set_generic_error (error, "System", "InvalidProgramException", "");
3066 goto exit;
3068 if (td->vt_sp != ALIGN_TO (vt_size, MINT_VT_ALIGNMENT))
3069 g_error ("%s: CEE_RET: value type stack: %d vs. %d", mono_method_full_name (td->method, TRUE), td->vt_sp, vt_size);
3071 if (sym_seq_points) {
3072 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3073 td->last_ins->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT;
3076 if (vt_size == 0)
3077 SIMPLE_OP(td, ult->type == MONO_TYPE_VOID ? MINT_RET_VOID : MINT_RET);
3078 else {
3079 interp_add_ins (td, MINT_RET_VT);
3080 WRITE32_INS (td->last_ins, 0, &vt_size);
3081 ++td->ip;
3083 break;
3085 case CEE_BR:
3086 INLINE_FAILURE;
3087 handle_branch (td, MINT_BR_S, MINT_BR, 5 + read32 (td->ip + 1));
3088 td->ip += 5;
3089 break;
3090 case CEE_BR_S:
3091 INLINE_FAILURE;
3092 handle_branch (td, MINT_BR_S, MINT_BR, 2 + (gint8)td->ip [1]);
3093 td->ip += 2;
3094 break;
3095 case CEE_BRFALSE:
3096 INLINE_FAILURE;
3097 one_arg_branch (td, MINT_BRFALSE_I4, 5 + read32 (td->ip + 1));
3098 td->ip += 5;
3099 break;
3100 case CEE_BRFALSE_S:
3101 INLINE_FAILURE;
3102 one_arg_branch (td, MINT_BRFALSE_I4, 2 + (gint8)td->ip [1]);
3103 td->ip += 2;
3104 break;
3105 case CEE_BRTRUE:
3106 INLINE_FAILURE;
3107 one_arg_branch (td, MINT_BRTRUE_I4, 5 + read32 (td->ip + 1));
3108 td->ip += 5;
3109 break;
3110 case CEE_BRTRUE_S:
3111 INLINE_FAILURE;
3112 one_arg_branch (td, MINT_BRTRUE_I4, 2 + (gint8)td->ip [1]);
3113 td->ip += 2;
3114 break;
3115 case CEE_BEQ:
3116 INLINE_FAILURE;
3117 two_arg_branch (td, MINT_BEQ_I4, 5 + read32 (td->ip + 1));
3118 td->ip += 5;
3119 break;
3120 case CEE_BEQ_S:
3121 INLINE_FAILURE;
3122 two_arg_branch (td, MINT_BEQ_I4, 2 + (gint8) td->ip [1]);
3123 td->ip += 2;
3124 break;
3125 case CEE_BGE:
3126 INLINE_FAILURE;
3127 two_arg_branch (td, MINT_BGE_I4, 5 + read32 (td->ip + 1));
3128 td->ip += 5;
3129 break;
3130 case CEE_BGE_S:
3131 INLINE_FAILURE;
3132 two_arg_branch (td, MINT_BGE_I4, 2 + (gint8) td->ip [1]);
3133 td->ip += 2;
3134 break;
3135 case CEE_BGT:
3136 INLINE_FAILURE;
3137 two_arg_branch (td, MINT_BGT_I4, 5 + read32 (td->ip + 1));
3138 td->ip += 5;
3139 break;
3140 case CEE_BGT_S:
3141 INLINE_FAILURE;
3142 two_arg_branch (td, MINT_BGT_I4, 2 + (gint8) td->ip [1]);
3143 td->ip += 2;
3144 break;
3145 case CEE_BLT:
3146 INLINE_FAILURE;
3147 two_arg_branch (td, MINT_BLT_I4, 5 + read32 (td->ip + 1));
3148 td->ip += 5;
3149 break;
3150 case CEE_BLT_S:
3151 INLINE_FAILURE;
3152 two_arg_branch (td, MINT_BLT_I4, 2 + (gint8) td->ip [1]);
3153 td->ip += 2;
3154 break;
3155 case CEE_BLE:
3156 INLINE_FAILURE;
3157 two_arg_branch (td, MINT_BLE_I4, 5 + read32 (td->ip + 1));
3158 td->ip += 5;
3159 break;
3160 case CEE_BLE_S:
3161 INLINE_FAILURE;
3162 two_arg_branch (td, MINT_BLE_I4, 2 + (gint8) td->ip [1]);
3163 td->ip += 2;
3164 break;
3165 case CEE_BNE_UN:
3166 INLINE_FAILURE;
3167 two_arg_branch (td, MINT_BNE_UN_I4, 5 + read32 (td->ip + 1));
3168 td->ip += 5;
3169 break;
3170 case CEE_BNE_UN_S:
3171 INLINE_FAILURE;
3172 two_arg_branch (td, MINT_BNE_UN_I4, 2 + (gint8) td->ip [1]);
3173 td->ip += 2;
3174 break;
3175 case CEE_BGE_UN:
3176 INLINE_FAILURE;
3177 two_arg_branch (td, MINT_BGE_UN_I4, 5 + read32 (td->ip + 1));
3178 td->ip += 5;
3179 break;
3180 case CEE_BGE_UN_S:
3181 INLINE_FAILURE;
3182 two_arg_branch (td, MINT_BGE_UN_I4, 2 + (gint8) td->ip [1]);
3183 td->ip += 2;
3184 break;
3185 case CEE_BGT_UN:
3186 INLINE_FAILURE;
3187 two_arg_branch (td, MINT_BGT_UN_I4, 5 + read32 (td->ip + 1));
3188 td->ip += 5;
3189 break;
3190 case CEE_BGT_UN_S:
3191 INLINE_FAILURE;
3192 two_arg_branch (td, MINT_BGT_UN_I4, 2 + (gint8) td->ip [1]);
3193 td->ip += 2;
3194 break;
3195 case CEE_BLE_UN:
3196 INLINE_FAILURE;
3197 two_arg_branch (td, MINT_BLE_UN_I4, 5 + read32 (td->ip + 1));
3198 td->ip += 5;
3199 break;
3200 case CEE_BLE_UN_S:
3201 INLINE_FAILURE;
3202 two_arg_branch (td, MINT_BLE_UN_I4, 2 + (gint8) td->ip [1]);
3203 td->ip += 2;
3204 break;
3205 case CEE_BLT_UN:
3206 INLINE_FAILURE;
3207 two_arg_branch (td, MINT_BLT_UN_I4, 5 + read32 (td->ip + 1));
3208 td->ip += 5;
3209 break;
3210 case CEE_BLT_UN_S:
3211 INLINE_FAILURE;
3212 two_arg_branch (td, MINT_BLT_UN_I4, 2 + (gint8) td->ip [1]);
3213 td->ip += 2;
3214 break;
3215 case CEE_SWITCH: {
3216 INLINE_FAILURE;
3217 guint32 n;
3218 const unsigned char *next_ip;
3219 ++td->ip;
3220 n = read32 (td->ip);
3221 interp_add_ins_explicit (td, MINT_SWITCH, MINT_SWITCH_LEN (n));
3222 WRITE32_INS (td->last_ins, 0, &n);
3223 td->ip += 4;
3224 next_ip = td->ip + n * 4;
3225 --td->sp;
3226 int stack_height = td->sp - td->stack;
3227 for (i = 0; i < n; i++) {
3228 offset = read32 (td->ip);
3229 target = next_ip - td->il_code + offset;
3230 if (offset < 0) {
3231 #if DEBUG_INTERP
3232 if (stack_height > 0 && stack_height != td->stack_height [target])
3233 g_warning ("SWITCH with back branch and non-empty stack");
3234 #endif
3235 } else {
3236 td->stack_height [target] = stack_height;
3237 td->vt_stack_size [target] = td->vt_sp;
3238 if (stack_height > 0)
3239 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, stack_height * sizeof (td->stack [0]));
3241 WRITE32_INS (td->last_ins, 2 + i * 2, &target);
3242 td->ip += 4;
3244 break;
3246 case CEE_LDIND_I1:
3247 CHECK_STACK (td, 1);
3248 SIMPLE_OP (td, MINT_LDIND_I1_CHECK);
3249 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3250 BARRIER_IF_VOLATILE (td);
3251 break;
3252 case CEE_LDIND_U1:
3253 CHECK_STACK (td, 1);
3254 SIMPLE_OP (td, MINT_LDIND_U1_CHECK);
3255 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3256 BARRIER_IF_VOLATILE (td);
3257 break;
3258 case CEE_LDIND_I2:
3259 CHECK_STACK (td, 1);
3260 SIMPLE_OP (td, MINT_LDIND_I2_CHECK);
3261 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3262 BARRIER_IF_VOLATILE (td);
3263 break;
3264 case CEE_LDIND_U2:
3265 CHECK_STACK (td, 1);
3266 SIMPLE_OP (td, MINT_LDIND_U2_CHECK);
3267 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3268 BARRIER_IF_VOLATILE (td);
3269 break;
3270 case CEE_LDIND_I4:
3271 CHECK_STACK (td, 1);
3272 SIMPLE_OP (td, MINT_LDIND_I4_CHECK);
3273 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3274 BARRIER_IF_VOLATILE (td);
3275 break;
3276 case CEE_LDIND_U4:
3277 CHECK_STACK (td, 1);
3278 SIMPLE_OP (td, MINT_LDIND_U4_CHECK);
3279 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3280 BARRIER_IF_VOLATILE (td);
3281 break;
3282 case CEE_LDIND_I8:
3283 CHECK_STACK (td, 1);
3284 SIMPLE_OP (td, MINT_LDIND_I8_CHECK);
3285 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3286 BARRIER_IF_VOLATILE (td);
3287 break;
3288 case CEE_LDIND_I:
3289 CHECK_STACK (td, 1);
3290 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3291 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3292 BARRIER_IF_VOLATILE (td);
3293 break;
3294 case CEE_LDIND_R4:
3295 CHECK_STACK (td, 1);
3296 SIMPLE_OP (td, MINT_LDIND_R4_CHECK);
3297 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
3298 BARRIER_IF_VOLATILE (td);
3299 break;
3300 case CEE_LDIND_R8:
3301 CHECK_STACK (td, 1);
3302 SIMPLE_OP (td, MINT_LDIND_R8_CHECK);
3303 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3304 BARRIER_IF_VOLATILE (td);
3305 break;
3306 case CEE_LDIND_REF:
3307 CHECK_STACK (td, 1);
3308 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3309 BARRIER_IF_VOLATILE (td);
3310 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
3311 break;
3312 case CEE_STIND_REF:
3313 CHECK_STACK (td, 2);
3314 BARRIER_IF_VOLATILE (td);
3315 SIMPLE_OP (td, MINT_STIND_REF);
3316 td->sp -= 2;
3317 break;
3318 case CEE_STIND_I1:
3319 CHECK_STACK (td, 2);
3320 BARRIER_IF_VOLATILE (td);
3321 SIMPLE_OP (td, MINT_STIND_I1);
3322 td->sp -= 2;
3323 break;
3324 case CEE_STIND_I2:
3325 CHECK_STACK (td, 2);
3326 BARRIER_IF_VOLATILE (td);
3327 SIMPLE_OP (td, MINT_STIND_I2);
3328 td->sp -= 2;
3329 break;
3330 case CEE_STIND_I4:
3331 CHECK_STACK (td, 2);
3332 BARRIER_IF_VOLATILE (td);
3333 SIMPLE_OP (td, MINT_STIND_I4);
3334 td->sp -= 2;
3335 break;
3336 case CEE_STIND_I:
3337 CHECK_STACK (td, 2);
3338 BARRIER_IF_VOLATILE (td);
3339 SIMPLE_OP (td, MINT_STIND_I);
3340 td->sp -= 2;
3341 break;
3342 case CEE_STIND_I8:
3343 CHECK_STACK (td, 2);
3344 BARRIER_IF_VOLATILE (td);
3345 SIMPLE_OP (td, MINT_STIND_I8);
3346 td->sp -= 2;
3347 break;
3348 case CEE_STIND_R4:
3349 CHECK_STACK (td, 2);
3350 BARRIER_IF_VOLATILE (td);
3351 SIMPLE_OP (td, MINT_STIND_R4);
3352 td->sp -= 2;
3353 break;
3354 case CEE_STIND_R8:
3355 CHECK_STACK (td, 2);
3356 BARRIER_IF_VOLATILE (td);
3357 SIMPLE_OP (td, MINT_STIND_R8);
3358 td->sp -= 2;
3359 break;
3360 case CEE_ADD:
3361 binary_arith_op(td, MINT_ADD_I4);
3362 ++td->ip;
3363 break;
3364 case CEE_SUB:
3365 binary_arith_op(td, MINT_SUB_I4);
3366 ++td->ip;
3367 break;
3368 case CEE_MUL:
3369 binary_arith_op(td, MINT_MUL_I4);
3370 ++td->ip;
3371 break;
3372 case CEE_DIV:
3373 binary_arith_op(td, MINT_DIV_I4);
3374 ++td->ip;
3375 break;
3376 case CEE_DIV_UN:
3377 binary_arith_op(td, MINT_DIV_UN_I4);
3378 ++td->ip;
3379 break;
3380 case CEE_REM:
3381 binary_arith_op (td, MINT_REM_I4);
3382 ++td->ip;
3383 break;
3384 case CEE_REM_UN:
3385 binary_arith_op (td, MINT_REM_UN_I4);
3386 ++td->ip;
3387 break;
3388 case CEE_AND:
3389 binary_arith_op (td, MINT_AND_I4);
3390 ++td->ip;
3391 break;
3392 case CEE_OR:
3393 binary_arith_op (td, MINT_OR_I4);
3394 ++td->ip;
3395 break;
3396 case CEE_XOR:
3397 binary_arith_op (td, MINT_XOR_I4);
3398 ++td->ip;
3399 break;
3400 case CEE_SHL:
3401 shift_op (td, MINT_SHL_I4);
3402 ++td->ip;
3403 break;
3404 case CEE_SHR:
3405 shift_op (td, MINT_SHR_I4);
3406 ++td->ip;
3407 break;
3408 case CEE_SHR_UN:
3409 shift_op (td, MINT_SHR_UN_I4);
3410 ++td->ip;
3411 break;
3412 case CEE_NEG:
3413 unary_arith_op (td, MINT_NEG_I4);
3414 ++td->ip;
3415 break;
3416 case CEE_NOT:
3417 unary_arith_op (td, MINT_NOT_I4);
3418 ++td->ip;
3419 break;
3420 case CEE_CONV_U1:
3421 CHECK_STACK (td, 1);
3422 switch (td->sp [-1].type) {
3423 case STACK_TYPE_R4:
3424 interp_add_ins (td, MINT_CONV_U1_R4);
3425 break;
3426 case STACK_TYPE_R8:
3427 interp_add_ins (td, MINT_CONV_U1_R8);
3428 break;
3429 case STACK_TYPE_I4:
3430 interp_add_ins (td, MINT_CONV_U1_I4);
3431 break;
3432 case STACK_TYPE_I8:
3433 interp_add_ins (td, MINT_CONV_U1_I8);
3434 break;
3435 default:
3436 g_assert_not_reached ();
3438 ++td->ip;
3439 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3440 break;
3441 case CEE_CONV_I1:
3442 CHECK_STACK (td, 1);
3443 switch (td->sp [-1].type) {
3444 case STACK_TYPE_R4:
3445 interp_add_ins (td, MINT_CONV_I1_R4);
3446 break;
3447 case STACK_TYPE_R8:
3448 interp_add_ins (td, MINT_CONV_I1_R8);
3449 break;
3450 case STACK_TYPE_I4:
3451 interp_add_ins (td, MINT_CONV_I1_I4);
3452 break;
3453 case STACK_TYPE_I8:
3454 interp_add_ins (td, MINT_CONV_I1_I8);
3455 break;
3456 default:
3457 g_assert_not_reached ();
3459 ++td->ip;
3460 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3461 break;
3462 case CEE_CONV_U2:
3463 CHECK_STACK (td, 1);
3464 switch (td->sp [-1].type) {
3465 case STACK_TYPE_R4:
3466 interp_add_ins (td, MINT_CONV_U2_R4);
3467 break;
3468 case STACK_TYPE_R8:
3469 interp_add_ins (td, MINT_CONV_U2_R8);
3470 break;
3471 case STACK_TYPE_I4:
3472 interp_add_ins (td, MINT_CONV_U2_I4);
3473 break;
3474 case STACK_TYPE_I8:
3475 interp_add_ins (td, MINT_CONV_U2_I8);
3476 break;
3477 default:
3478 g_assert_not_reached ();
3480 ++td->ip;
3481 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3482 break;
3483 case CEE_CONV_I2:
3484 CHECK_STACK (td, 1);
3485 switch (td->sp [-1].type) {
3486 case STACK_TYPE_R4:
3487 interp_add_ins (td, MINT_CONV_I2_R4);
3488 break;
3489 case STACK_TYPE_R8:
3490 interp_add_ins (td, MINT_CONV_I2_R8);
3491 break;
3492 case STACK_TYPE_I4:
3493 interp_add_ins (td, MINT_CONV_I2_I4);
3494 break;
3495 case STACK_TYPE_I8:
3496 interp_add_ins (td, MINT_CONV_I2_I8);
3497 break;
3498 default:
3499 g_assert_not_reached ();
3501 ++td->ip;
3502 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3503 break;
3504 case CEE_CONV_U:
3505 CHECK_STACK (td, 1);
3506 switch (td->sp [-1].type) {
3507 case STACK_TYPE_R8:
3508 #if SIZEOF_VOID_P == 4
3509 interp_add_ins (td, MINT_CONV_U4_R8);
3510 #else
3511 interp_add_ins (td, MINT_CONV_U8_R8);
3512 #endif
3513 break;
3514 case STACK_TYPE_I4:
3515 #if SIZEOF_VOID_P == 8
3516 interp_add_ins (td, MINT_CONV_U8_I4);
3517 #endif
3518 break;
3519 case STACK_TYPE_I8:
3520 #if SIZEOF_VOID_P == 4
3521 interp_add_ins (td, MINT_CONV_U4_I8);
3522 #endif
3523 break;
3524 case STACK_TYPE_MP:
3525 case STACK_TYPE_O:
3526 break;
3527 default:
3528 g_assert_not_reached ();
3530 ++td->ip;
3531 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3532 break;
3533 case CEE_CONV_I:
3534 CHECK_STACK (td, 1);
3535 switch (td->sp [-1].type) {
3536 case STACK_TYPE_R8:
3537 #if SIZEOF_VOID_P == 8
3538 interp_add_ins (td, MINT_CONV_I8_R8);
3539 #else
3540 interp_add_ins (td, MINT_CONV_I4_R8);
3541 #endif
3542 break;
3543 case STACK_TYPE_I4:
3544 #if SIZEOF_VOID_P == 8
3545 interp_add_ins (td, MINT_CONV_I8_I4);
3546 #endif
3547 break;
3548 case STACK_TYPE_O:
3549 break;
3550 case STACK_TYPE_MP:
3551 break;
3552 case STACK_TYPE_I8:
3553 #if SIZEOF_VOID_P == 4
3554 interp_add_ins (td, MINT_CONV_I4_I8);
3555 #endif
3556 break;
3557 default:
3558 g_assert_not_reached ();
3560 ++td->ip;
3561 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3562 break;
3563 case CEE_CONV_U4:
3564 CHECK_STACK (td, 1);
3565 switch (td->sp [-1].type) {
3566 case STACK_TYPE_R4:
3567 interp_add_ins (td, MINT_CONV_U4_R4);
3568 break;
3569 case STACK_TYPE_R8:
3570 interp_add_ins (td, MINT_CONV_U4_R8);
3571 break;
3572 case STACK_TYPE_I4:
3573 break;
3574 case STACK_TYPE_I8:
3575 interp_add_ins (td, MINT_CONV_U4_I8);
3576 break;
3577 case STACK_TYPE_MP:
3578 #if SIZEOF_VOID_P == 8
3579 interp_add_ins (td, MINT_CONV_U4_I8);
3580 #endif
3581 break;
3582 default:
3583 g_assert_not_reached ();
3585 ++td->ip;
3586 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3587 break;
3588 case CEE_CONV_I4:
3589 CHECK_STACK (td, 1);
3590 switch (td->sp [-1].type) {
3591 case STACK_TYPE_R4:
3592 interp_add_ins (td, MINT_CONV_I4_R4);
3593 break;
3594 case STACK_TYPE_R8:
3595 interp_add_ins (td, MINT_CONV_I4_R8);
3596 break;
3597 case STACK_TYPE_I4:
3598 break;
3599 case STACK_TYPE_I8:
3600 interp_add_ins (td, MINT_CONV_I4_I8);
3601 break;
3602 case STACK_TYPE_MP:
3603 #if SIZEOF_VOID_P == 8
3604 interp_add_ins (td, MINT_CONV_I4_I8);
3605 #endif
3606 break;
3607 default:
3608 g_assert_not_reached ();
3610 ++td->ip;
3611 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3612 break;
3613 case CEE_CONV_I8:
3614 CHECK_STACK (td, 1);
3615 switch (td->sp [-1].type) {
3616 case STACK_TYPE_R4:
3617 interp_add_ins (td, MINT_CONV_I8_R4);
3618 break;
3619 case STACK_TYPE_R8:
3620 interp_add_ins (td, MINT_CONV_I8_R8);
3621 break;
3622 case STACK_TYPE_I4:
3623 interp_add_ins (td, MINT_CONV_I8_I4);
3624 break;
3625 case STACK_TYPE_I8:
3626 break;
3627 case STACK_TYPE_MP:
3628 #if SIZEOF_VOID_P == 4
3629 interp_add_ins (td, MINT_CONV_I8_I4);
3630 #endif
3631 break;
3632 default:
3633 g_assert_not_reached ();
3635 ++td->ip;
3636 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3637 break;
3638 case CEE_CONV_R4:
3639 CHECK_STACK (td, 1);
3640 switch (td->sp [-1].type) {
3641 case STACK_TYPE_R8:
3642 interp_add_ins (td, MINT_CONV_R4_R8);
3643 break;
3644 case STACK_TYPE_I8:
3645 interp_add_ins (td, MINT_CONV_R4_I8);
3646 break;
3647 case STACK_TYPE_I4:
3648 interp_add_ins (td, MINT_CONV_R4_I4);
3649 break;
3650 case STACK_TYPE_R4:
3651 /* no-op */
3652 break;
3653 default:
3654 g_assert_not_reached ();
3656 ++td->ip;
3657 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
3658 break;
3659 case CEE_CONV_R8:
3660 CHECK_STACK (td, 1);
3661 switch (td->sp [-1].type) {
3662 case STACK_TYPE_I4:
3663 interp_add_ins (td, MINT_CONV_R8_I4);
3664 break;
3665 case STACK_TYPE_I8:
3666 interp_add_ins (td, MINT_CONV_R8_I8);
3667 break;
3668 case STACK_TYPE_R4:
3669 interp_add_ins (td, MINT_CONV_R8_R4);
3670 break;
3671 case STACK_TYPE_R8:
3672 break;
3673 default:
3674 g_assert_not_reached ();
3676 ++td->ip;
3677 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3678 break;
3679 case CEE_CONV_U8:
3680 CHECK_STACK (td, 1);
3681 switch (td->sp [-1].type) {
3682 case STACK_TYPE_I4:
3683 interp_add_ins (td, MINT_CONV_U8_I4);
3684 break;
3685 case STACK_TYPE_I8:
3686 break;
3687 case STACK_TYPE_R4:
3688 interp_add_ins (td, MINT_CONV_U8_R4);
3689 break;
3690 case STACK_TYPE_R8:
3691 interp_add_ins (td, MINT_CONV_U8_R8);
3692 break;
3693 case STACK_TYPE_MP:
3694 #if SIZEOF_VOID_P == 4
3695 interp_add_ins (td, MINT_CONV_U8_I4);
3696 #endif
3697 break;
3698 default:
3699 g_assert_not_reached ();
3701 ++td->ip;
3702 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3703 break;
3704 case CEE_CPOBJ: {
3705 CHECK_STACK (td, 2);
3707 token = read32 (td->ip + 1);
3708 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
3709 goto_if_nok (error, exit);
3711 if (m_class_is_valuetype (klass)) {
3712 int mt = mint_type (m_class_get_byval_arg (klass));
3713 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_CPOBJ_VT : MINT_CPOBJ);
3714 td->last_ins->data [0] = get_data_item_index(td, klass);
3715 } else {
3716 interp_add_ins (td, MINT_LDIND_REF);
3717 interp_add_ins (td, MINT_STIND_REF);
3719 td->ip += 5;
3720 td->sp -= 2;
3721 break;
3723 case CEE_LDOBJ: {
3724 CHECK_STACK (td, 1);
3726 token = read32 (td->ip + 1);
3728 if (method->wrapper_type != MONO_WRAPPER_NONE)
3729 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
3730 else {
3731 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
3732 goto_if_nok (error, exit);
3735 interp_emit_ldobj (td, klass);
3737 td->ip += 5;
3738 BARRIER_IF_VOLATILE (td);
3739 break;
3741 case CEE_LDSTR: {
3742 token = mono_metadata_token_index (read32 (td->ip + 1));
3743 td->ip += 5;
3744 if (method->wrapper_type == MONO_WRAPPER_NONE) {
3745 MonoString *s = mono_ldstr_checked (domain, image, token, error);
3746 goto_if_nok (error, exit);
3747 /* GC won't scan code stream, but reference is held by metadata
3748 * machinery so we are good here */
3749 interp_add_ins (td, MINT_LDSTR);
3750 td->last_ins->data [0] = get_data_item_index (td, s);
3751 } else {
3752 /* defer allocation to execution-time */
3753 interp_add_ins (td, MINT_LDSTR_TOKEN);
3754 td->last_ins->data [0] = get_data_item_index (td, GUINT_TO_POINTER (token));
3756 PUSH_TYPE(td, STACK_TYPE_O, mono_defaults.string_class);
3757 break;
3759 case CEE_NEWOBJ: {
3760 MonoMethod *m;
3761 MonoMethodSignature *csignature;
3762 guint32 vt_stack_used = 0;
3763 guint32 vt_res_size = 0;
3765 td->ip++;
3766 token = read32 (td->ip);
3767 td->ip += 4;
3769 if (method->wrapper_type != MONO_WRAPPER_NONE)
3770 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
3771 else {
3772 m = mono_get_method_checked (image, token, NULL, generic_context, error);
3773 goto_if_nok (error, exit);
3776 csignature = mono_method_signature_internal (m);
3777 klass = m->klass;
3779 if (!mono_class_init_internal (klass)) {
3780 mono_error_set_for_class_failure (error, klass);
3781 goto_if_nok (error, exit);
3784 if (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_ABSTRACT) {
3785 char* full_name = mono_type_get_full_name (klass);
3786 mono_error_set_member_access (error, "Cannot create an abstract class: %s", full_name);
3787 g_free (full_name);
3788 goto_if_nok (error, exit);
3791 td->sp -= csignature->param_count;
3792 if (mono_class_is_magic_int (klass) || mono_class_is_magic_float (klass)) {
3793 #if SIZEOF_VOID_P == 8
3794 if (mono_class_is_magic_int (klass) && td->sp [0].type == STACK_TYPE_I4)
3795 interp_add_ins (td, MINT_CONV_I8_I4);
3796 else if (mono_class_is_magic_float (klass) && td->sp [0].type == STACK_TYPE_R4)
3797 interp_add_ins (td, MINT_CONV_R8_R4);
3798 #endif
3799 interp_add_ins (td, MINT_NEWOBJ_MAGIC);
3800 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3801 goto_if_nok (error, exit);
3803 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
3804 } else {
3805 if (m_class_get_parent (klass) == mono_defaults.array_class) {
3806 interp_add_ins (td, MINT_NEWOBJ_ARRAY);
3807 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3808 td->last_ins->data [1] = csignature->param_count;
3809 } else if (m_class_get_image (klass) == mono_defaults.corlib &&
3810 !strcmp (m_class_get_name (m->klass), "ByReference`1") &&
3811 !strcmp (m->name, ".ctor")) {
3812 /* public ByReference(ref T value) */
3813 g_assert (csignature->hasthis && csignature->param_count == 1);
3814 interp_add_ins (td, MINT_INTRINS_BYREFERENCE_CTOR);
3815 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3816 } else if (klass != mono_defaults.string_class &&
3817 !mono_class_is_marshalbyref (klass) &&
3818 !mono_class_has_finalizer (klass) &&
3819 !m_class_has_weak_fields (klass)) {
3820 if (!m_class_is_valuetype (klass))
3821 interp_add_ins (td, MINT_NEWOBJ_FAST);
3822 else if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
3823 interp_add_ins (td, MINT_NEWOBJ_VTST_FAST);
3824 else
3825 interp_add_ins (td, MINT_NEWOBJ_VT_FAST);
3827 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3828 td->last_ins->data [1] = csignature->param_count;
3830 if (!m_class_is_valuetype (klass)) {
3831 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
3832 goto_if_nok (error, exit);
3833 td->last_ins->data [2] = get_data_item_index (td, vtable);
3834 } else if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
3835 td->last_ins->data [2] = mono_class_value_size (klass, NULL);
3837 } else {
3838 interp_add_ins (td, MINT_NEWOBJ);
3839 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3841 goto_if_nok (error, exit);
3843 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
3844 vt_res_size = mono_class_value_size (klass, NULL);
3845 PUSH_VT (td, vt_res_size);
3847 for (i = 0; i < csignature->param_count; ++i) {
3848 int mt = mint_type(csignature->params [i]);
3849 if (mt == MINT_TYPE_VT) {
3850 MonoClass *k = mono_class_from_mono_type_internal (csignature->params [i]);
3851 gint32 size = mono_class_value_size (k, NULL);
3852 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
3853 vt_stack_used += size;
3856 if (vt_stack_used != 0 || vt_res_size != 0) {
3857 interp_add_ins (td, MINT_VTRESULT);
3858 td->last_ins->data [0] = vt_res_size;
3859 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
3860 td->vt_sp -= vt_stack_used;
3862 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
3864 break;
3866 case CEE_CASTCLASS:
3867 case CEE_ISINST: {
3868 gboolean isinst_instr = *td->ip == CEE_ISINST;
3869 CHECK_STACK (td, 1);
3870 token = read32 (td->ip + 1);
3871 klass = mini_get_class (method, token, generic_context);
3872 CHECK_TYPELOAD (klass);
3873 interp_handle_isinst (td, klass, isinst_instr);
3874 if (!isinst_instr)
3875 td->sp [-1].klass = klass;
3876 break;
3878 case CEE_CONV_R_UN:
3879 switch (td->sp [-1].type) {
3880 case STACK_TYPE_R8:
3881 break;
3882 case STACK_TYPE_I8:
3883 interp_add_ins (td, MINT_CONV_R_UN_I8);
3884 break;
3885 case STACK_TYPE_I4:
3886 interp_add_ins (td, MINT_CONV_R_UN_I4);
3887 break;
3888 default:
3889 g_assert_not_reached ();
3891 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3892 ++td->ip;
3893 break;
3894 case CEE_UNBOX:
3895 CHECK_STACK (td, 1);
3896 token = read32 (td->ip + 1);
3898 if (method->wrapper_type != MONO_WRAPPER_NONE)
3899 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
3900 else {
3901 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
3902 goto_if_nok (error, exit);
3905 if (mono_class_is_nullable (klass)) {
3906 MonoMethod *target_method;
3907 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
3908 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
3909 else
3910 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
3911 goto_if_nok (error, exit);
3912 /* td->ip is incremented by interp_transform_call */
3913 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
3914 goto exit;
3916 * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type
3917 * We create a local variable in the frame so that we can fetch its address.
3919 int local_offset = create_interp_local (td, m_class_get_byval_arg (klass));
3920 store_local_general (td, local_offset, m_class_get_byval_arg (klass));
3921 interp_add_ins (td, MINT_LDLOCA_S);
3922 td->last_ins->data [0] = local_offset;
3923 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
3924 } else {
3925 interp_add_ins (td, MINT_UNBOX);
3926 td->last_ins->data [0] = get_data_item_index (td, klass);
3927 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
3928 td->ip += 5;
3930 break;
3931 case CEE_UNBOX_ANY:
3932 CHECK_STACK (td, 1);
3933 token = read32 (td->ip + 1);
3935 klass = mini_get_class (method, token, generic_context);
3936 CHECK_TYPELOAD (klass);
3938 if (mini_type_is_reference (m_class_get_byval_arg (klass))) {
3939 int mt = mint_type (m_class_get_byval_arg (klass));
3940 interp_handle_isinst (td, klass, FALSE);
3941 SET_TYPE (td->sp - 1, stack_type [mt], klass);
3942 } else if (mono_class_is_nullable (klass)) {
3943 MonoMethod *target_method;
3944 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
3945 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
3946 else
3947 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
3948 goto_if_nok (error, exit);
3949 /* td->ip is incremented by interp_transform_call */
3950 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
3951 goto exit;
3952 } else {
3953 interp_add_ins (td, MINT_UNBOX);
3954 td->last_ins->data [0] = get_data_item_index (td, klass);
3956 interp_emit_ldobj (td, klass);
3958 td->ip += 5;
3961 break;
3962 case CEE_THROW:
3963 INLINE_FAILURE;
3964 CHECK_STACK (td, 1);
3965 SIMPLE_OP (td, MINT_THROW);
3966 td->sp = td->stack;
3967 break;
3968 case CEE_LDFLDA: {
3969 CHECK_STACK (td, 1);
3970 token = read32 (td->ip + 1);
3971 field = interp_field_from_token (method, token, &klass, generic_context, error);
3972 goto_if_nok (error, exit);
3973 MonoType *ftype = mono_field_get_type_internal (field);
3974 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
3975 mono_class_init_internal (klass);
3976 #ifndef DISABLE_REMOTING
3977 if (m_class_get_marshalbyref (klass) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
3978 g_assert (!is_static);
3979 int offset = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
3981 interp_add_ins (td, MINT_MONO_LDPTR);
3982 td->last_ins->data [0] = get_data_item_index (td, klass);
3983 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
3984 interp_add_ins (td, MINT_MONO_LDPTR);
3985 td->last_ins->data [0] = get_data_item_index (td, field);
3986 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
3987 interp_add_ins (td, MINT_LDC_I4);
3988 WRITE32_INS (td->last_ins, 0, &offset);
3989 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
3990 #if SIZEOF_VOID_P == 8
3991 interp_add_ins (td, MINT_CONV_I8_I4);
3992 #endif
3994 MonoMethod *wrapper = mono_marshal_get_ldflda_wrapper (field->type);
3995 /* td->ip is incremented by interp_transform_call */
3996 if (!interp_transform_call (td, method, wrapper, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
3997 goto exit;
3998 } else
3999 #endif
4001 if (is_static) {
4002 interp_add_ins (td, MINT_POP);
4003 td->last_ins->data [0] = 0;
4004 interp_add_ins (td, MINT_LDSFLDA);
4005 td->last_ins->data [0] = get_data_item_index (td, field);
4006 } else {
4007 if ((td->sp - 1)->type == STACK_TYPE_O) {
4008 interp_add_ins (td, MINT_LDFLDA);
4009 } else {
4010 g_assert ((td->sp -1)->type == STACK_TYPE_MP);
4011 interp_add_ins (td, MINT_LDFLDA_UNSAFE);
4013 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4015 td->ip += 5;
4017 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4018 break;
4020 case CEE_LDFLD: {
4021 CHECK_STACK (td, 1);
4022 token = read32 (td->ip + 1);
4023 field = interp_field_from_token (method, token, &klass, generic_context, error);
4024 goto_if_nok (error, exit);
4025 MonoType *ftype = mono_field_get_type_internal (field);
4026 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4027 mono_class_init_internal (klass);
4029 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4030 mt = mint_type (m_class_get_byval_arg (field_klass));
4031 #ifndef DISABLE_REMOTING
4032 if (m_class_get_marshalbyref (klass)) {
4033 g_assert (!is_static);
4034 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDRMFLD_VT : MINT_LDRMFLD);
4035 td->last_ins->data [0] = get_data_item_index (td, field);
4036 } else
4037 #endif
4039 if (is_static) {
4040 interp_add_ins (td, MINT_POP);
4041 td->last_ins->data [0] = 0;
4042 interp_emit_ldsfld (td, field, field_klass, mt, error);
4043 goto_if_nok (error, exit);
4044 } else {
4045 int opcode = MINT_LDFLD_I1 + mt - MINT_TYPE_I1;
4046 #ifdef NO_UNALIGNED_ACCESS
4047 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4048 opcode = get_unaligned_opcode (opcode);
4049 #endif
4050 interp_add_ins (td, opcode);
4051 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4052 if (mt == MINT_TYPE_VT)
4053 td->last_ins->data [1] = get_data_item_index (td, field);
4056 if (mt == MINT_TYPE_VT) {
4057 int size = mono_class_value_size (field_klass, NULL);
4058 PUSH_VT (td, size);
4060 if (td->sp [-1].type == STACK_TYPE_VT) {
4061 int size = mono_class_value_size (klass, NULL);
4062 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4063 int field_vt_size = 0;
4064 if (mt == MINT_TYPE_VT) {
4066 * Pop the loaded field from the vtstack (it will still be present
4067 * at the same vtstack address) and we will load it in place of the
4068 * containing valuetype with the second MINT_VTRESULT.
4070 field_vt_size = mono_class_value_size (field_klass, NULL);
4071 field_vt_size = ALIGN_TO (field_vt_size, MINT_VT_ALIGNMENT);
4072 interp_add_ins (td, MINT_VTRESULT);
4073 td->last_ins->data [0] = 0;
4074 WRITE32_INS (td->last_ins, 1, &field_vt_size);
4076 td->vt_sp -= size;
4077 interp_add_ins (td, MINT_VTRESULT);
4078 td->last_ins->data [0] = field_vt_size;
4079 WRITE32_INS (td->last_ins, 1, &size);
4081 td->ip += 5;
4082 SET_TYPE (td->sp - 1, stack_type [mt], field_klass);
4083 BARRIER_IF_VOLATILE (td);
4084 break;
4086 case CEE_STFLD: {
4087 CHECK_STACK (td, 2);
4088 token = read32 (td->ip + 1);
4089 field = interp_field_from_token (method, token, &klass, generic_context, error);
4090 goto_if_nok (error, exit);
4091 MonoType *ftype = mono_field_get_type_internal (field);
4092 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4093 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4094 mono_class_init_internal (klass);
4095 mt = mint_type (ftype);
4097 BARRIER_IF_VOLATILE (td);
4099 #ifndef DISABLE_REMOTING
4100 if (m_class_get_marshalbyref (klass)) {
4101 g_assert (!is_static);
4102 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_STRMFLD_VT : MINT_STRMFLD);
4103 td->last_ins->data [0] = get_data_item_index (td, field);
4104 } else
4105 #endif
4107 if (is_static) {
4108 interp_add_ins (td, MINT_POP);
4109 td->last_ins->data [0] = 1;
4110 interp_emit_stsfld (td, field, field_klass, mt, error);
4111 goto_if_nok (error, exit);
4113 /* the vtable of the field might not be initialized at this point */
4114 mono_class_vtable_checked (domain, field_klass, error);
4115 goto_if_nok (error, exit);
4116 } else {
4117 int opcode = MINT_STFLD_I1 + mt - MINT_TYPE_I1;
4118 #ifdef NO_UNALIGNED_ACCESS
4119 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4120 opcode = get_unaligned_opcode (opcode);
4121 #endif
4122 interp_add_ins (td, opcode);
4123 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4124 if (mt == MINT_TYPE_VT) {
4125 td->last_ins->data [1] = get_data_item_index (td, field);
4127 /* the vtable of the field might not be initialized at this point */
4128 mono_class_vtable_checked (domain, field_klass, error);
4129 goto_if_nok (error, exit);
4133 if (mt == MINT_TYPE_VT) {
4134 int size = mono_class_value_size (field_klass, NULL);
4135 POP_VT (td, size);
4137 td->ip += 5;
4138 td->sp -= 2;
4139 break;
4141 case CEE_LDSFLDA: {
4142 token = read32 (td->ip + 1);
4143 field = interp_field_from_token (method, token, &klass, generic_context, error);
4144 goto_if_nok (error, exit);
4145 mono_field_get_type_internal (field);
4146 interp_add_ins (td, MINT_LDSFLDA);
4147 td->last_ins->data [0] = get_data_item_index (td, field);
4148 td->ip += 5;
4149 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
4150 break;
4152 case CEE_LDSFLD: {
4153 token = read32 (td->ip + 1);
4154 field = interp_field_from_token (method, token, &klass, generic_context, error);
4155 goto_if_nok (error, exit);
4156 MonoType *ftype = mono_field_get_type_internal (field);
4157 mt = mint_type (ftype);
4158 klass = mono_class_from_mono_type_internal (ftype);
4160 interp_emit_ldsfld (td, field, klass, mt, error);
4161 goto_if_nok (error, exit);
4163 if (mt == MINT_TYPE_VT) {
4164 int size = mono_class_value_size (klass, NULL);
4165 PUSH_VT(td, size);
4167 td->ip += 5;
4168 PUSH_TYPE(td, stack_type [mt], klass);
4169 break;
4171 case CEE_STSFLD: {
4172 CHECK_STACK (td, 1);
4173 token = read32 (td->ip + 1);
4174 field = interp_field_from_token (method, token, &klass, generic_context, error);
4175 goto_if_nok (error, exit);
4176 MonoType *ftype = mono_field_get_type_internal (field);
4177 mt = mint_type (ftype);
4179 /* the vtable of the field might not be initialized at this point */
4180 MonoClass *fld_klass = mono_class_from_mono_type_internal (ftype);
4181 mono_class_vtable_checked (domain, fld_klass, error);
4182 goto_if_nok (error, exit);
4184 interp_emit_stsfld (td, field, fld_klass, mt, error);
4185 goto_if_nok (error, exit);
4187 if (mt == MINT_TYPE_VT) {
4188 int size = mono_class_value_size (fld_klass, NULL);
4189 POP_VT(td, size);
4191 td->ip += 5;
4192 --td->sp;
4193 break;
4195 case CEE_STOBJ: {
4196 token = read32 (td->ip + 1);
4198 if (method->wrapper_type != MONO_WRAPPER_NONE)
4199 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4200 else
4201 klass = mini_get_class (method, token, generic_context);
4202 CHECK_TYPELOAD (klass);
4204 BARRIER_IF_VOLATILE (td);
4206 interp_emit_stobj (td, klass);
4208 td->ip += 5;
4209 break;
4211 case CEE_CONV_OVF_I_UN:
4212 case CEE_CONV_OVF_U_UN:
4213 CHECK_STACK (td, 1);
4214 switch (td->sp [-1].type) {
4215 case STACK_TYPE_R8:
4216 #if SIZEOF_VOID_P == 8
4217 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4218 #else
4219 interp_add_ins (td, MINT_CONV_OVF_I4_UN_R8);
4220 #endif
4221 break;
4222 case STACK_TYPE_I8:
4223 #if SIZEOF_VOID_P == 4
4224 interp_add_ins (td, MINT_CONV_OVF_I4_UN_I8);
4225 #endif
4226 break;
4227 case STACK_TYPE_I4:
4228 #if SIZEOF_VOID_P == 8
4229 interp_add_ins (td, MINT_CONV_I8_U4);
4230 #elif SIZEOF_VOID_P == 4
4231 if (*td->ip == CEE_CONV_OVF_I_UN)
4232 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
4233 #endif
4234 break;
4235 default:
4236 g_assert_not_reached ();
4237 break;
4239 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4240 ++td->ip;
4241 break;
4242 case CEE_CONV_OVF_I8_UN:
4243 case CEE_CONV_OVF_U8_UN:
4244 CHECK_STACK (td, 1);
4245 switch (td->sp [-1].type) {
4246 case STACK_TYPE_R8:
4247 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4248 break;
4249 case STACK_TYPE_I8:
4250 if (*td->ip == CEE_CONV_OVF_I8_UN)
4251 interp_add_ins (td, MINT_CONV_OVF_I8_U8);
4252 break;
4253 case STACK_TYPE_I4:
4254 interp_add_ins (td, MINT_CONV_I8_U4);
4255 break;
4256 default:
4257 g_assert_not_reached ();
4258 break;
4260 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4261 ++td->ip;
4262 break;
4263 case CEE_BOX: {
4264 int size;
4265 CHECK_STACK (td, 1);
4266 token = read32 (td->ip + 1);
4267 if (method->wrapper_type != MONO_WRAPPER_NONE)
4268 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4269 else
4270 klass = mini_get_class (method, token, generic_context);
4271 CHECK_TYPELOAD (klass);
4273 if (mono_class_is_nullable (klass)) {
4274 MonoMethod *target_method = mono_class_get_method_from_name_checked (klass, "Box", 1, 0, error);
4275 goto_if_nok (error, exit);
4276 /* td->ip is incremented by interp_transform_call */
4277 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
4278 goto exit;
4279 } else if (!m_class_is_valuetype (klass)) {
4280 /* already boxed, do nothing. */
4281 td->ip += 5;
4282 } else {
4283 if (G_UNLIKELY (m_class_is_byreflike (klass))) {
4284 mono_error_set_bad_image (error, image, "Cannot box IsByRefLike type '%s.%s'", m_class_get_name_space (klass), m_class_get_name (klass));
4285 goto exit;
4287 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT && !m_class_is_enumtype (klass)) {
4288 size = mono_class_value_size (klass, NULL);
4289 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4290 td->vt_sp -= size;
4291 } else if (td->sp [-1].type == STACK_TYPE_R8 && m_class_get_byval_arg (klass)->type == MONO_TYPE_R4) {
4292 interp_add_ins (td, MINT_CONV_R4_R8);
4294 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4295 interp_add_ins (td, MINT_BOX_VT);
4296 else
4297 interp_add_ins (td, MINT_BOX);
4298 td->last_ins->data [0] = get_data_item_index (td, klass);
4299 td->last_ins->data [1] = 0;
4300 SET_TYPE(td->sp - 1, STACK_TYPE_O, klass);
4301 td->ip += 5;
4304 break;
4306 case CEE_NEWARR: {
4307 CHECK_STACK (td, 1);
4308 token = read32 (td->ip + 1);
4310 if (method->wrapper_type != MONO_WRAPPER_NONE)
4311 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4312 else
4313 klass = mini_get_class (method, token, generic_context);
4314 CHECK_TYPELOAD (klass);
4316 unsigned char lentype = (td->sp - 1)->type;
4317 if (lentype == STACK_TYPE_I8) {
4318 /* mimic mini behaviour */
4319 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
4320 } else {
4321 g_assert (lentype == STACK_TYPE_I4);
4322 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
4324 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
4325 interp_add_ins (td, MINT_NEWARR);
4326 td->last_ins->data [0] = get_data_item_index (td, klass);
4327 SET_TYPE (td->sp - 1, STACK_TYPE_O, klass);
4328 td->ip += 5;
4329 break;
4331 case CEE_LDLEN:
4332 CHECK_STACK (td, 1);
4333 SIMPLE_OP (td, MINT_LDLEN);
4334 #ifdef MONO_BIG_ARRAYS
4335 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
4336 #else
4337 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
4338 #endif
4339 break;
4340 case CEE_LDELEMA:
4341 CHECK_STACK (td, 2);
4342 ENSURE_I4 (td, 1);
4343 token = read32 (td->ip + 1);
4345 if (method->wrapper_type != MONO_WRAPPER_NONE)
4346 klass = (MonoClass *) mono_method_get_wrapper_data (method, token);
4347 else
4348 klass = mini_get_class (method, token, generic_context);
4350 CHECK_TYPELOAD (klass);
4352 if (!m_class_is_valuetype (klass) && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
4354 * Check the class for failures before the type check, which can
4355 * throw other exceptions.
4357 mono_class_setup_vtable (klass);
4358 CHECK_TYPELOAD (klass);
4359 interp_add_ins (td, MINT_LDELEMA_TC);
4360 } else {
4361 interp_add_ins (td, MINT_LDELEMA);
4363 td->last_ins->data [0] = get_data_item_index (td, klass);
4364 /* according to spec, ldelema bytecode is only used for 1-dim arrays */
4365 td->last_ins->data [1] = 2;
4366 readonly = FALSE;
4368 td->ip += 5;
4369 --td->sp;
4370 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4371 break;
4372 case CEE_LDELEM_I1:
4373 CHECK_STACK (td, 2);
4374 ENSURE_I4 (td, 1);
4375 SIMPLE_OP (td, MINT_LDELEM_I1);
4376 --td->sp;
4377 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4378 break;
4379 case CEE_LDELEM_U1:
4380 CHECK_STACK (td, 2);
4381 ENSURE_I4 (td, 1);
4382 SIMPLE_OP (td, MINT_LDELEM_U1);
4383 --td->sp;
4384 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4385 break;
4386 case CEE_LDELEM_I2:
4387 CHECK_STACK (td, 2);
4388 ENSURE_I4 (td, 1);
4389 SIMPLE_OP (td, MINT_LDELEM_I2);
4390 --td->sp;
4391 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4392 break;
4393 case CEE_LDELEM_U2:
4394 CHECK_STACK (td, 2);
4395 ENSURE_I4 (td, 1);
4396 SIMPLE_OP (td, MINT_LDELEM_U2);
4397 --td->sp;
4398 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4399 break;
4400 case CEE_LDELEM_I4:
4401 CHECK_STACK (td, 2);
4402 ENSURE_I4 (td, 1);
4403 SIMPLE_OP (td, MINT_LDELEM_I4);
4404 --td->sp;
4405 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4406 break;
4407 case CEE_LDELEM_U4:
4408 CHECK_STACK (td, 2);
4409 ENSURE_I4 (td, 1);
4410 SIMPLE_OP (td, MINT_LDELEM_U4);
4411 --td->sp;
4412 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4413 break;
4414 case CEE_LDELEM_I8:
4415 CHECK_STACK (td, 2);
4416 ENSURE_I4 (td, 1);
4417 SIMPLE_OP (td, MINT_LDELEM_I8);
4418 --td->sp;
4419 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4420 break;
4421 case CEE_LDELEM_I:
4422 CHECK_STACK (td, 2);
4423 ENSURE_I4 (td, 1);
4424 SIMPLE_OP (td, MINT_LDELEM_I);
4425 --td->sp;
4426 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4427 break;
4428 case CEE_LDELEM_R4:
4429 CHECK_STACK (td, 2);
4430 ENSURE_I4 (td, 1);
4431 SIMPLE_OP (td, MINT_LDELEM_R4);
4432 --td->sp;
4433 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4434 break;
4435 case CEE_LDELEM_R8:
4436 CHECK_STACK (td, 2);
4437 ENSURE_I4 (td, 1);
4438 SIMPLE_OP (td, MINT_LDELEM_R8);
4439 --td->sp;
4440 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4441 break;
4442 case CEE_LDELEM_REF:
4443 CHECK_STACK (td, 2);
4444 ENSURE_I4 (td, 1);
4445 SIMPLE_OP (td, MINT_LDELEM_REF);
4446 --td->sp;
4447 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
4448 break;
4449 case CEE_LDELEM:
4450 CHECK_STACK (td, 2);
4451 token = read32 (td->ip + 1);
4452 klass = mini_get_class (method, token, generic_context);
4453 CHECK_TYPELOAD (klass);
4454 switch (mint_type (m_class_get_byval_arg (klass))) {
4455 case MINT_TYPE_I1:
4456 ENSURE_I4 (td, 1);
4457 SIMPLE_OP (td, MINT_LDELEM_I1);
4458 --td->sp;
4459 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4460 break;
4461 case MINT_TYPE_U1:
4462 ENSURE_I4 (td, 1);
4463 SIMPLE_OP (td, MINT_LDELEM_U1);
4464 --td->sp;
4465 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4466 break;
4467 case MINT_TYPE_U2:
4468 ENSURE_I4 (td, 1);
4469 SIMPLE_OP (td, MINT_LDELEM_U2);
4470 --td->sp;
4471 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4472 break;
4473 case MINT_TYPE_I2:
4474 ENSURE_I4 (td, 1);
4475 SIMPLE_OP (td, MINT_LDELEM_I2);
4476 --td->sp;
4477 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4478 break;
4479 case MINT_TYPE_I4:
4480 ENSURE_I4 (td, 1);
4481 SIMPLE_OP (td, MINT_LDELEM_I4);
4482 --td->sp;
4483 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4484 break;
4485 case MINT_TYPE_I8:
4486 ENSURE_I4 (td, 1);
4487 SIMPLE_OP (td, MINT_LDELEM_I8);
4488 --td->sp;
4489 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4490 break;
4491 case MINT_TYPE_R4:
4492 ENSURE_I4 (td, 1);
4493 SIMPLE_OP (td, MINT_LDELEM_R4);
4494 --td->sp;
4495 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4496 break;
4497 case MINT_TYPE_R8:
4498 ENSURE_I4 (td, 1);
4499 SIMPLE_OP (td, MINT_LDELEM_R8);
4500 --td->sp;
4501 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4502 break;
4503 case MINT_TYPE_O:
4504 ENSURE_I4 (td, 1);
4505 SIMPLE_OP (td, MINT_LDELEM_REF);
4506 --td->sp;
4507 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
4508 break;
4509 case MINT_TYPE_VT: {
4510 int size = mono_class_value_size (klass, NULL);
4511 ENSURE_I4 (td, 1);
4512 SIMPLE_OP (td, MINT_LDELEM_VT);
4513 td->last_ins->data [0] = get_data_item_index (td, klass);
4514 WRITE32_INS (td->last_ins, 1, &size);
4515 --td->sp;
4516 SET_TYPE (td->sp - 1, STACK_TYPE_VT, klass);
4517 PUSH_VT (td, size);
4518 break;
4520 default: {
4521 GString *res = g_string_new ("");
4522 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
4523 g_print ("LDELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
4524 g_string_free (res, TRUE);
4525 g_assert (0);
4526 break;
4529 td->ip += 4;
4530 break;
4531 case CEE_STELEM_I:
4532 CHECK_STACK (td, 3);
4533 ENSURE_I4 (td, 2);
4534 SIMPLE_OP (td, MINT_STELEM_I);
4535 td->sp -= 3;
4536 break;
4537 case CEE_STELEM_I1:
4538 CHECK_STACK (td, 3);
4539 ENSURE_I4 (td, 2);
4540 SIMPLE_OP (td, MINT_STELEM_I1);
4541 td->sp -= 3;
4542 break;
4543 case CEE_STELEM_I2:
4544 CHECK_STACK (td, 3);
4545 ENSURE_I4 (td, 2);
4546 SIMPLE_OP (td, MINT_STELEM_I2);
4547 td->sp -= 3;
4548 break;
4549 case CEE_STELEM_I4:
4550 CHECK_STACK (td, 3);
4551 ENSURE_I4 (td, 2);
4552 SIMPLE_OP (td, MINT_STELEM_I4);
4553 td->sp -= 3;
4554 break;
4555 case CEE_STELEM_I8:
4556 CHECK_STACK (td, 3);
4557 ENSURE_I4 (td, 2);
4558 SIMPLE_OP (td, MINT_STELEM_I8);
4559 td->sp -= 3;
4560 break;
4561 case CEE_STELEM_R4:
4562 CHECK_STACK (td, 3);
4563 ENSURE_I4 (td, 2);
4564 SIMPLE_OP (td, MINT_STELEM_R4);
4565 td->sp -= 3;
4566 break;
4567 case CEE_STELEM_R8:
4568 CHECK_STACK (td, 3);
4569 ENSURE_I4 (td, 2);
4570 SIMPLE_OP (td, MINT_STELEM_R8);
4571 td->sp -= 3;
4572 break;
4573 case CEE_STELEM_REF:
4574 CHECK_STACK (td, 3);
4575 ENSURE_I4 (td, 2);
4576 SIMPLE_OP (td, MINT_STELEM_REF);
4577 td->sp -= 3;
4578 break;
4579 case CEE_STELEM:
4580 CHECK_STACK (td, 3);
4581 ENSURE_I4 (td, 2);
4582 token = read32 (td->ip + 1);
4583 klass = mini_get_class (method, token, generic_context);
4584 CHECK_TYPELOAD (klass);
4585 switch (mint_type (m_class_get_byval_arg (klass))) {
4586 case MINT_TYPE_I1:
4587 SIMPLE_OP (td, MINT_STELEM_I1);
4588 break;
4589 case MINT_TYPE_U1:
4590 SIMPLE_OP (td, MINT_STELEM_U1);
4591 break;
4592 case MINT_TYPE_I2:
4593 SIMPLE_OP (td, MINT_STELEM_I2);
4594 break;
4595 case MINT_TYPE_U2:
4596 SIMPLE_OP (td, MINT_STELEM_U2);
4597 break;
4598 case MINT_TYPE_I4:
4599 SIMPLE_OP (td, MINT_STELEM_I4);
4600 break;
4601 case MINT_TYPE_I8:
4602 SIMPLE_OP (td, MINT_STELEM_I8);
4603 break;
4604 case MINT_TYPE_R4:
4605 SIMPLE_OP (td, MINT_STELEM_R4);
4606 break;
4607 case MINT_TYPE_R8:
4608 SIMPLE_OP (td, MINT_STELEM_R8);
4609 break;
4610 case MINT_TYPE_O:
4611 SIMPLE_OP (td, MINT_STELEM_REF);
4612 break;
4613 case MINT_TYPE_VT: {
4614 int size = mono_class_value_size (klass, NULL);
4615 SIMPLE_OP (td, MINT_STELEM_VT);
4616 td->last_ins->data [0] = get_data_item_index (td, klass);
4617 WRITE32_INS (td->last_ins, 1, &size);
4618 POP_VT (td, size);
4619 break;
4621 default: {
4622 GString *res = g_string_new ("");
4623 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
4624 g_print ("STELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
4625 g_string_free (res, TRUE);
4626 g_assert (0);
4627 break;
4630 td->ip += 4;
4631 td->sp -= 3;
4632 break;
4633 #if 0
4634 case CEE_CONV_OVF_U1:
4636 case CEE_CONV_OVF_I8:
4638 #if SIZEOF_VOID_P == 8
4639 case CEE_CONV_OVF_U:
4640 #endif
4641 #endif
4642 case CEE_CKFINITE:
4643 CHECK_STACK (td, 1);
4644 SIMPLE_OP (td, MINT_CKFINITE);
4645 break;
4646 case CEE_MKREFANY:
4647 CHECK_STACK (td, 1);
4649 token = read32 (td->ip + 1);
4650 klass = mini_get_class (method, token, generic_context);
4651 CHECK_TYPELOAD (klass);
4653 interp_add_ins (td, MINT_MKREFANY);
4654 td->last_ins->data [0] = get_data_item_index (td, klass);
4656 td->ip += 5;
4657 PUSH_VT (td, sizeof (MonoTypedRef));
4658 SET_TYPE(td->sp - 1, STACK_TYPE_VT, mono_defaults.typed_reference_class);
4659 break;
4660 case CEE_REFANYVAL: {
4661 CHECK_STACK (td, 1);
4663 token = read32 (td->ip + 1);
4664 klass = mini_get_class (method, token, generic_context);
4665 CHECK_TYPELOAD (klass);
4667 interp_add_ins (td, MINT_REFANYVAL);
4668 td->last_ins->data [0] = get_data_item_index (td, klass);
4670 POP_VT (td, sizeof (MonoTypedRef));
4671 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4673 td->ip += 5;
4674 break;
4676 case CEE_CONV_OVF_I1:
4677 case CEE_CONV_OVF_I1_UN: {
4678 gboolean is_un = *td->ip == CEE_CONV_OVF_I1_UN;
4679 CHECK_STACK (td, 1);
4680 switch (td->sp [-1].type) {
4681 case STACK_TYPE_R8:
4682 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_UN_R8 : MINT_CONV_OVF_I1_R8);
4683 break;
4684 case STACK_TYPE_I4:
4685 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U4 : MINT_CONV_OVF_I1_I4);
4686 break;
4687 case STACK_TYPE_I8:
4688 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U8 : MINT_CONV_OVF_I1_I8);
4689 break;
4690 default:
4691 g_assert_not_reached ();
4693 ++td->ip;
4694 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4695 break;
4697 case CEE_CONV_OVF_U1:
4698 case CEE_CONV_OVF_U1_UN:
4699 CHECK_STACK (td, 1);
4700 switch (td->sp [-1].type) {
4701 case STACK_TYPE_R8:
4702 interp_add_ins (td, MINT_CONV_OVF_U1_R8);
4703 break;
4704 case STACK_TYPE_I4:
4705 interp_add_ins (td, MINT_CONV_OVF_U1_I4);
4706 break;
4707 case STACK_TYPE_I8:
4708 interp_add_ins (td, MINT_CONV_OVF_U1_I8);
4709 break;
4710 default:
4711 g_assert_not_reached ();
4713 ++td->ip;
4714 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4715 break;
4716 case CEE_CONV_OVF_I2:
4717 case CEE_CONV_OVF_I2_UN: {
4718 gboolean is_un = *td->ip == CEE_CONV_OVF_I2_UN;
4719 CHECK_STACK (td, 1);
4720 switch (td->sp [-1].type) {
4721 case STACK_TYPE_R8:
4722 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_UN_R8 : MINT_CONV_OVF_I2_R8);
4723 break;
4724 case STACK_TYPE_I4:
4725 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U4 : MINT_CONV_OVF_I2_I4);
4726 break;
4727 case STACK_TYPE_I8:
4728 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U8 : MINT_CONV_OVF_I2_I8);
4729 break;
4730 default:
4731 g_assert_not_reached ();
4733 ++td->ip;
4734 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4735 break;
4737 case CEE_CONV_OVF_U2_UN:
4738 case CEE_CONV_OVF_U2:
4739 CHECK_STACK (td, 1);
4740 switch (td->sp [-1].type) {
4741 case STACK_TYPE_R8:
4742 interp_add_ins (td, MINT_CONV_OVF_U2_R8);
4743 break;
4744 case STACK_TYPE_I4:
4745 interp_add_ins (td, MINT_CONV_OVF_U2_I4);
4746 break;
4747 case STACK_TYPE_I8:
4748 interp_add_ins (td, MINT_CONV_OVF_U2_I8);
4749 break;
4750 default:
4751 g_assert_not_reached ();
4753 ++td->ip;
4754 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4755 break;
4756 #if SIZEOF_VOID_P == 4
4757 case CEE_CONV_OVF_I:
4758 #endif
4759 case CEE_CONV_OVF_I4:
4760 case CEE_CONV_OVF_I4_UN:
4761 CHECK_STACK (td, 1);
4762 switch (td->sp [-1].type) {
4763 case STACK_TYPE_R4:
4764 interp_add_ins (td, MINT_CONV_OVF_I4_R4);
4765 break;
4766 case STACK_TYPE_R8:
4767 interp_add_ins (td, MINT_CONV_OVF_I4_R8);
4768 break;
4769 case STACK_TYPE_I4:
4770 if (*td->ip == CEE_CONV_OVF_I4_UN)
4771 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
4772 break;
4773 case STACK_TYPE_I8:
4774 if (*td->ip == CEE_CONV_OVF_I4_UN)
4775 interp_add_ins (td, MINT_CONV_OVF_I4_U8);
4776 else
4777 interp_add_ins (td, MINT_CONV_OVF_I4_I8);
4778 break;
4779 default:
4780 g_assert_not_reached ();
4782 ++td->ip;
4783 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4784 break;
4785 #if SIZEOF_VOID_P == 4
4786 case CEE_CONV_OVF_U:
4787 #endif
4788 case CEE_CONV_OVF_U4:
4789 case CEE_CONV_OVF_U4_UN:
4790 CHECK_STACK (td, 1);
4791 switch (td->sp [-1].type) {
4792 case STACK_TYPE_R4:
4793 interp_add_ins (td, MINT_CONV_OVF_U4_R4);
4794 break;
4795 case STACK_TYPE_R8:
4796 interp_add_ins (td, MINT_CONV_OVF_U4_R8);
4797 break;
4798 case STACK_TYPE_I4:
4799 if (*td->ip != CEE_CONV_OVF_U4_UN)
4800 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
4801 break;
4802 case STACK_TYPE_I8:
4803 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
4804 break;
4805 default:
4806 g_assert_not_reached ();
4808 ++td->ip;
4809 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4810 break;
4811 #if SIZEOF_VOID_P == 8
4812 case CEE_CONV_OVF_I:
4813 #endif
4814 case CEE_CONV_OVF_I8:
4815 CHECK_STACK (td, 1);
4816 switch (td->sp [-1].type) {
4817 case STACK_TYPE_R4:
4818 interp_add_ins (td, MINT_CONV_OVF_I8_R4);
4819 break;
4820 case STACK_TYPE_R8:
4821 interp_add_ins (td, MINT_CONV_OVF_I8_R8);
4822 break;
4823 case STACK_TYPE_I4:
4824 interp_add_ins (td, MINT_CONV_I8_I4);
4825 break;
4826 case STACK_TYPE_I8:
4827 break;
4828 default:
4829 g_assert_not_reached ();
4831 ++td->ip;
4832 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4833 break;
4834 #if SIZEOF_VOID_P == 8
4835 case CEE_CONV_OVF_U:
4836 #endif
4837 case CEE_CONV_OVF_U8:
4838 CHECK_STACK (td, 1);
4839 switch (td->sp [-1].type) {
4840 case STACK_TYPE_R4:
4841 interp_add_ins (td, MINT_CONV_OVF_U8_R4);
4842 break;
4843 case STACK_TYPE_R8:
4844 interp_add_ins (td, MINT_CONV_OVF_U8_R8);
4845 break;
4846 case STACK_TYPE_I4:
4847 interp_add_ins (td, MINT_CONV_OVF_U8_I4);
4848 break;
4849 case STACK_TYPE_I8:
4850 interp_add_ins (td, MINT_CONV_OVF_U8_I8);
4851 break;
4852 default:
4853 g_assert_not_reached ();
4855 ++td->ip;
4856 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4857 break;
4858 case CEE_LDTOKEN: {
4859 int size;
4860 gpointer handle;
4861 token = read32 (td->ip + 1);
4862 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
4863 handle = mono_method_get_wrapper_data (method, token);
4864 klass = (MonoClass *) mono_method_get_wrapper_data (method, token + 1);
4865 if (klass == mono_defaults.typehandle_class)
4866 handle = m_class_get_byval_arg ((MonoClass *) handle);
4868 if (generic_context) {
4869 handle = mono_class_inflate_generic_type_checked ((MonoType*)handle, generic_context, error);
4870 goto_if_nok (error, exit);
4872 } else {
4873 handle = mono_ldtoken_checked (image, token, &klass, generic_context, error);
4874 goto_if_nok (error, exit);
4876 mono_class_init_internal (klass);
4877 mt = mint_type (m_class_get_byval_arg (klass));
4878 g_assert (mt == MINT_TYPE_VT);
4879 size = mono_class_value_size (klass, NULL);
4880 g_assert (size == sizeof(gpointer));
4882 const unsigned char *next_ip = td->ip + 5;
4883 MonoMethod *cmethod;
4884 if (next_ip < end &&
4885 !td->is_bb_start [next_ip - td->il_code] &&
4886 (*next_ip == CEE_CALL || *next_ip == CEE_CALLVIRT) &&
4887 (cmethod = mono_get_method_checked (image, read32 (next_ip + 1), NULL, generic_context, error)) &&
4888 (cmethod->klass == mono_defaults.systemtype_class) &&
4889 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
4890 interp_add_ins (td, MINT_MONO_LDPTR);
4891 gpointer systype = mono_type_get_object_checked (domain, (MonoType*)handle, error);
4892 goto_if_nok (error, exit);
4893 td->last_ins->data [0] = get_data_item_index (td, systype);
4894 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
4895 td->ip = next_ip + 5;
4896 } else {
4897 PUSH_VT (td, sizeof(gpointer));
4898 interp_add_ins (td, MINT_LDTOKEN);
4899 td->last_ins->data [0] = get_data_item_index (td, handle);
4900 SET_TYPE (td->sp, stack_type [mt], klass);
4901 td->sp++;
4902 td->ip += 5;
4905 break;
4907 case CEE_ADD_OVF:
4908 binary_arith_op(td, MINT_ADD_OVF_I4);
4909 ++td->ip;
4910 break;
4911 case CEE_ADD_OVF_UN:
4912 binary_arith_op(td, MINT_ADD_OVF_UN_I4);
4913 ++td->ip;
4914 break;
4915 case CEE_MUL_OVF:
4916 binary_arith_op(td, MINT_MUL_OVF_I4);
4917 ++td->ip;
4918 break;
4919 case CEE_MUL_OVF_UN:
4920 binary_arith_op(td, MINT_MUL_OVF_UN_I4);
4921 ++td->ip;
4922 break;
4923 case CEE_SUB_OVF:
4924 binary_arith_op(td, MINT_SUB_OVF_I4);
4925 ++td->ip;
4926 break;
4927 case CEE_SUB_OVF_UN:
4928 binary_arith_op(td, MINT_SUB_OVF_UN_I4);
4929 ++td->ip;
4930 break;
4931 case CEE_ENDFINALLY: {
4932 g_assert (td->clause_indexes [in_offset] != -1);
4933 td->sp = td->stack;
4934 SIMPLE_OP (td, MINT_ENDFINALLY);
4935 td->last_ins->data [0] = td->clause_indexes [in_offset];
4936 break;
4938 case CEE_LEAVE:
4939 case CEE_LEAVE_S: {
4940 int offset;
4942 if (*td->ip == CEE_LEAVE)
4943 offset = 5 + read32 (td->ip + 1);
4944 else
4945 offset = 2 + (gint8)td->ip [1];
4947 td->sp = td->stack;
4948 if (td->clause_indexes [in_offset] != -1) {
4949 /* LEAVE instructions in catch clauses need to check for abort exceptions */
4950 handle_branch (td, MINT_LEAVE_S_CHECK, MINT_LEAVE_CHECK, offset);
4951 } else {
4952 handle_branch (td, MINT_LEAVE_S, MINT_LEAVE, offset);
4955 if (*td->ip == CEE_LEAVE)
4956 td->ip += 5;
4957 else
4958 td->ip += 2;
4959 break;
4961 case MONO_CUSTOM_PREFIX:
4962 ++td->ip;
4963 switch (*td->ip) {
4964 case CEE_MONO_RETHROW:
4965 CHECK_STACK (td, 1);
4966 SIMPLE_OP (td, MINT_MONO_RETHROW);
4967 td->sp = td->stack;
4968 break;
4970 case CEE_MONO_LD_DELEGATE_METHOD_PTR:
4971 --td->sp;
4972 td->ip += 1;
4973 interp_add_ins (td, MINT_LD_DELEGATE_METHOD_PTR);
4974 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4975 break;
4976 case CEE_MONO_CALLI_EXTRA_ARG:
4977 /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
4978 interp_add_ins (td, MINT_POP);
4979 td->last_ins->data [0] = 1;
4980 --td->sp;
4981 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
4982 goto exit;
4983 break;
4984 case CEE_MONO_JIT_ICALL_ADDR: {
4985 guint32 token;
4986 gpointer func;
4988 token = read32 (td->ip + 1);
4989 td->ip += 5;
4990 func = mono_method_get_wrapper_data (method, token);
4992 interp_add_ins (td, MINT_LDFTN);
4993 td->last_ins->data [0] = get_data_item_index (td, func);
4994 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4995 break;
4997 case CEE_MONO_ICALL: {
4998 guint32 token;
4999 gpointer func;
5000 MonoJitICallInfo *info;
5001 int icall_op;
5003 token = read32 (td->ip + 1);
5004 td->ip += 5;
5005 func = mono_method_get_wrapper_data (method, token);
5006 info = mono_find_jit_icall_by_addr (func);
5007 g_assert (info);
5009 CHECK_STACK (td, info->sig->param_count);
5010 if (!strcmp (info->name, "mono_threads_attach_coop")) {
5011 rtm->needs_thread_attach = 1;
5013 /* attach needs two arguments, and has one return value: leave one element on the stack */
5014 interp_add_ins (td, MINT_POP);
5015 td->last_ins->data [0] = 0;
5016 } else if (!strcmp (info->name, "mono_threads_detach_coop")) {
5017 g_assert (rtm->needs_thread_attach);
5019 /* detach consumes two arguments, and no return value: drop both of them */
5020 interp_add_ins (td, MINT_POP);
5021 td->last_ins->data [0] = 0;
5022 interp_add_ins (td, MINT_POP);
5023 td->last_ins->data [0] = 0;
5024 } else {
5025 icall_op = interp_icall_op_for_sig (info->sig);
5026 g_assert (icall_op != -1);
5028 interp_add_ins (td, icall_op);
5029 td->last_ins->data [0] = get_data_item_index (td, func);
5031 td->sp -= info->sig->param_count;
5033 if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
5034 int mt = mint_type (info->sig->ret);
5035 td->sp ++;
5036 SET_SIMPLE_TYPE(td->sp - 1, stack_type [mt]);
5038 break;
5040 case CEE_MONO_VTADDR: {
5041 int size;
5042 CHECK_STACK (td, 1);
5043 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
5044 size = mono_class_native_size(td->sp [-1].klass, NULL);
5045 else
5046 size = mono_class_value_size(td->sp [-1].klass, NULL);
5047 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
5048 interp_add_ins (td, MINT_VTRESULT);
5049 td->last_ins->data [0] = 0;
5050 WRITE32_INS (td->last_ins, 1, &size);
5051 td->vt_sp -= size;
5052 ++td->ip;
5053 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5054 break;
5056 case CEE_MONO_LDPTR:
5057 case CEE_MONO_CLASSCONST:
5058 token = read32 (td->ip + 1);
5059 td->ip += 5;
5060 interp_add_ins (td, MINT_MONO_LDPTR);
5061 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5062 td->sp [0].type = STACK_TYPE_I;
5063 ++td->sp;
5064 break;
5065 case CEE_MONO_OBJADDR:
5066 CHECK_STACK (td, 1);
5067 ++td->ip;
5068 td->sp[-1].type = STACK_TYPE_MP;
5069 /* do nothing? */
5070 break;
5071 case CEE_MONO_NEWOBJ:
5072 token = read32 (td->ip + 1);
5073 td->ip += 5;
5074 interp_add_ins (td, MINT_MONO_NEWOBJ);
5075 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5076 td->sp [0].type = STACK_TYPE_O;
5077 ++td->sp;
5078 break;
5079 case CEE_MONO_RETOBJ:
5080 CHECK_STACK (td, 1);
5081 token = read32 (td->ip + 1);
5082 td->ip += 5;
5083 interp_add_ins (td, MINT_MONO_RETOBJ);
5084 td->sp--;
5086 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5088 /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/
5090 if (td->sp > td->stack)
5091 g_warning ("CEE_MONO_RETOBJ: more values on stack: %d", td->sp-td->stack);
5092 break;
5093 case CEE_MONO_LDNATIVEOBJ:
5094 token = read32 (td->ip + 1);
5095 td->ip += 5;
5096 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5097 g_assert(m_class_is_valuetype (klass));
5098 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5099 break;
5100 case CEE_MONO_TLS: {
5101 gint32 key = read32 (td->ip + 1);
5102 td->ip += 5;
5103 g_assert (key < TLS_KEY_NUM);
5104 interp_add_ins (td, MINT_MONO_TLS);
5105 WRITE32_INS (td->last_ins, 0, &key);
5106 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5107 break;
5109 case CEE_MONO_ATOMIC_STORE_I4:
5110 CHECK_STACK (td, 2);
5111 SIMPLE_OP (td, MINT_MONO_ATOMIC_STORE_I4);
5112 td->sp -= 2;
5113 td->ip++;
5114 break;
5115 case CEE_MONO_SAVE_LMF:
5116 case CEE_MONO_RESTORE_LMF:
5117 case CEE_MONO_NOT_TAKEN:
5118 ++td->ip;
5119 break;
5120 case CEE_MONO_LDPTR_INT_REQ_FLAG:
5121 interp_add_ins (td, MINT_MONO_LDPTR);
5122 td->last_ins->data [0] = get_data_item_index (td, mono_thread_interruption_request_flag ());
5123 PUSH_TYPE (td, STACK_TYPE_MP, NULL);
5124 ++td->ip;
5125 break;
5126 case CEE_MONO_MEMORY_BARRIER:
5127 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5128 ++td->ip;
5129 break;
5130 case CEE_MONO_LDDOMAIN:
5131 interp_add_ins (td, MINT_MONO_LDDOMAIN);
5132 td->sp [0].type = STACK_TYPE_I;
5133 ++td->sp;
5134 ++td->ip;
5135 break;
5136 default:
5137 g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td->ip, td->ip-header->code);
5139 break;
5140 #if 0
5141 case CEE_PREFIX7:
5142 case CEE_PREFIX6:
5143 case CEE_PREFIX5:
5144 case CEE_PREFIX4:
5145 case CEE_PREFIX3:
5146 case CEE_PREFIX2:
5147 case CEE_PREFIXREF: ves_abort(); break;
5148 #endif
5150 * Note: Exceptions thrown when executing a prefixed opcode need
5151 * to take into account the number of prefix bytes (usually the
5152 * throw point is just (ip - n_prefix_bytes).
5154 case CEE_PREFIX1:
5155 ++td->ip;
5156 switch (*td->ip) {
5157 case CEE_ARGLIST:
5158 interp_add_ins (td, MINT_ARGLIST);
5159 PUSH_VT (td, SIZEOF_VOID_P);
5160 PUSH_SIMPLE_TYPE (td, STACK_TYPE_VT);
5161 ++td->ip;
5162 break;
5163 case CEE_CEQ:
5164 CHECK_STACK(td, 2);
5165 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP) {
5166 interp_add_ins (td, MINT_CEQ_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5167 } else {
5168 if (td->sp [-1].type == STACK_TYPE_R4 && td->sp [-2].type == STACK_TYPE_R8)
5169 interp_add_ins (td, MINT_CONV_R8_R4);
5170 if (td->sp [-1].type == STACK_TYPE_R8 && td->sp [-2].type == STACK_TYPE_R4)
5171 interp_add_ins (td, MINT_CONV_R8_R4_SP);
5172 interp_add_ins (td, MINT_CEQ_I4 + td->sp [-1].type - STACK_TYPE_I4);
5174 --td->sp;
5175 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5176 ++td->ip;
5177 break;
5178 case CEE_CGT:
5179 CHECK_STACK(td, 2);
5180 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5181 interp_add_ins (td, MINT_CGT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5182 else
5183 interp_add_ins (td, MINT_CGT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5184 --td->sp;
5185 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5186 ++td->ip;
5187 break;
5188 case CEE_CGT_UN:
5189 CHECK_STACK(td, 2);
5190 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5191 interp_add_ins (td, MINT_CGT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5192 else
5193 interp_add_ins (td, MINT_CGT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5194 --td->sp;
5195 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5196 ++td->ip;
5197 break;
5198 case CEE_CLT:
5199 CHECK_STACK(td, 2);
5200 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5201 interp_add_ins (td, MINT_CLT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5202 else
5203 interp_add_ins (td, MINT_CLT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5204 --td->sp;
5205 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5206 ++td->ip;
5207 break;
5208 case CEE_CLT_UN:
5209 CHECK_STACK(td, 2);
5210 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5211 interp_add_ins (td, MINT_CLT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5212 else
5213 interp_add_ins (td, MINT_CLT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5214 --td->sp;
5215 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5216 ++td->ip;
5217 break;
5218 case CEE_LDVIRTFTN: /* fallthrough */
5219 case CEE_LDFTN: {
5220 MonoMethod *m;
5221 if (*td->ip == CEE_LDVIRTFTN) {
5222 CHECK_STACK (td, 1);
5223 --td->sp;
5225 token = read32 (td->ip + 1);
5226 if (method->wrapper_type != MONO_WRAPPER_NONE)
5227 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
5228 else {
5229 m = mono_get_method_checked (image, token, NULL, generic_context, error);
5230 goto_if_nok (error, exit);
5233 if (!mono_method_can_access_method (method, m))
5234 interp_generate_mae_throw (td, method, m);
5236 if (method->wrapper_type == MONO_WRAPPER_NONE && m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
5237 m = mono_marshal_get_synchronized_wrapper (m);
5239 interp_add_ins (td, *td->ip == CEE_LDFTN ? MINT_LDFTN : MINT_LDVIRTFTN);
5240 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
5241 goto_if_nok (error, exit);
5242 td->ip += 5;
5243 PUSH_SIMPLE_TYPE (td, STACK_TYPE_F);
5244 break;
5246 case CEE_LDARG: {
5247 int arg_n = read16 (td->ip + 1);
5248 if (td->method == method)
5249 load_arg (td, arg_n);
5250 else
5251 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
5252 td->ip += 3;
5253 break;
5255 case CEE_LDARGA: {
5256 INLINE_FAILURE;
5257 int n = read16 (td->ip + 1);
5259 get_arg_type_exact (td, n, &mt);
5260 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
5261 td->last_ins->data [0] = n;
5262 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5263 td->ip += 3;
5264 break;
5266 case CEE_STARG: {
5267 int arg_n = read16 (td->ip + 1);
5268 if (td->method == method)
5269 store_arg (td, arg_n);
5270 else
5271 store_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
5272 td->ip += 3;
5273 break;
5275 case CEE_LDLOC:
5276 load_local (td, read16 (td->ip + 1));
5277 td->ip += 3;
5278 break;
5279 case CEE_LDLOCA:
5280 interp_add_ins (td, MINT_LDLOCA_S);
5281 td->last_ins->data [0] = td->rtm->local_offsets [read16 (td->ip + 1)];
5282 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5283 td->ip += 3;
5284 break;
5285 case CEE_STLOC:
5286 store_local (td, read16 (td->ip + 1));
5287 td->ip += 3;
5288 break;
5289 case CEE_LOCALLOC:
5290 CHECK_STACK (td, 1);
5291 #if SIZEOF_VOID_P == 8
5292 if (td->sp [-1].type == STACK_TYPE_I8)
5293 interp_add_ins (td, MINT_CONV_I4_I8);
5294 #endif
5295 interp_add_ins (td, MINT_LOCALLOC);
5296 if (td->sp != td->stack + 1)
5297 g_warning("CEE_LOCALLOC: stack not empty");
5298 ++td->ip;
5299 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5300 break;
5301 #if 0
5302 case CEE_UNUSED57: ves_abort(); break;
5303 #endif
5304 case CEE_ENDFILTER:
5305 interp_add_ins (td, MINT_ENDFILTER);
5306 ++td->ip;
5307 break;
5308 case CEE_UNALIGNED_:
5309 td->ip += 2;
5310 break;
5311 case CEE_VOLATILE_:
5312 ++td->ip;
5313 volatile_ = TRUE;
5314 break;
5315 case CEE_TAIL_:
5316 ++td->ip;
5317 /* FIX: should do something? */;
5318 // TODO: This should raise a method_tail_call profiler event.
5319 break;
5320 case CEE_INITOBJ:
5321 CHECK_STACK(td, 1);
5322 token = read32 (td->ip + 1);
5323 klass = mini_get_class (method, token, generic_context);
5324 CHECK_TYPELOAD (klass);
5325 if (m_class_is_valuetype (klass)) {
5326 interp_add_ins (td, MINT_INITOBJ);
5327 i32 = mono_class_value_size (klass, NULL);
5328 WRITE32_INS (td->last_ins, 0, &i32);
5329 } else {
5330 interp_add_ins (td, MINT_LDNULL);
5331 interp_add_ins (td, MINT_STIND_REF);
5333 td->ip += 5;
5334 --td->sp;
5335 break;
5336 case CEE_CPBLK:
5337 CHECK_STACK(td, 3);
5338 /* FIX? convert length to I8? */
5339 if (volatile_)
5340 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5341 interp_add_ins (td, MINT_CPBLK);
5342 BARRIER_IF_VOLATILE (td);
5343 td->sp -= 3;
5344 ++td->ip;
5345 break;
5346 case CEE_READONLY_:
5347 readonly = TRUE;
5348 td->ip += 1;
5349 break;
5350 case CEE_CONSTRAINED_:
5351 token = read32 (td->ip + 1);
5352 constrained_class = mini_get_class (method, token, generic_context);
5353 CHECK_TYPELOAD (constrained_class);
5354 td->ip += 5;
5355 break;
5356 case CEE_INITBLK:
5357 CHECK_STACK(td, 3);
5358 BARRIER_IF_VOLATILE (td);
5359 interp_add_ins (td, MINT_INITBLK);
5360 td->sp -= 3;
5361 td->ip += 1;
5362 break;
5363 case CEE_NO_:
5364 /* FIXME: implement */
5365 td->ip += 2;
5366 break;
5367 case CEE_RETHROW: {
5368 int clause_index = td->clause_indexes [in_offset];
5369 g_assert (clause_index != -1);
5370 SIMPLE_OP (td, MINT_RETHROW);
5371 td->last_ins->data [0] = rtm->exvar_offsets [clause_index];
5372 td->sp = td->stack;
5373 break;
5375 case CEE_SIZEOF: {
5376 gint32 size;
5377 token = read32 (td->ip + 1);
5378 td->ip += 5;
5379 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (m_class_get_image (method->klass)) && !generic_context) {
5380 int align;
5381 MonoType *type = mono_type_create_from_typespec_checked (image, token, error);
5382 goto_if_nok (error, exit);
5383 size = mono_type_size (type, &align);
5384 } else {
5385 int align;
5386 MonoClass *szclass = mini_get_class (method, token, generic_context);
5387 CHECK_TYPELOAD (szclass);
5388 #if 0
5389 if (!szclass->valuetype)
5390 THROW_EX (mono_exception_from_name (mono_defaults.corlib, "System", "InvalidProgramException"), ip - 5);
5391 #endif
5392 size = mono_type_size (m_class_get_byval_arg (szclass), &align);
5394 interp_add_ins (td, MINT_LDC_I4);
5395 WRITE32_INS (td->last_ins, 0, &size);
5396 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
5397 break;
5399 case CEE_REFANYTYPE:
5400 interp_add_ins (td, MINT_REFANYTYPE);
5401 td->ip += 1;
5402 POP_VT (td, sizeof (MonoTypedRef));
5403 PUSH_VT (td, sizeof (gpointer));
5404 SET_TYPE(td->sp - 1, STACK_TYPE_VT, NULL);
5405 break;
5406 default:
5407 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);
5409 break;
5410 default:
5411 g_error ("transform.c: Unimplemented opcode: %02x at 0x%x\n", *td->ip, td->ip-header->code);
5414 // No IR instructions were added as part of the IL instruction. Extend bb_start
5415 if (prev_last_ins == td->last_ins && td->is_bb_start [in_offset] && td->ip < end)
5416 td->is_bb_start [td->ip - td->il_code] = 1;
5419 g_assert (td->ip == end);
5421 exit_ret:
5422 g_free (arg_offsets);
5423 mono_basic_block_free (original_bb);
5424 for (i = 0; i < header->code_size; ++i)
5425 g_free (td->stack_state [i]);
5426 g_free (td->stack_state);
5427 g_free (td->stack_height);
5428 g_free (td->vt_stack_size);
5429 g_free (td->clause_indexes);
5430 g_free (td->is_bb_start);
5432 return ret;
5433 exit:
5434 ret = FALSE;
5435 goto exit_ret;
5438 // We are trying to branch to an il offset that has no associated ir instruction with it.
5439 // We will branch instead to the next instruction that we find
5440 static int
5441 resolve_in_offset (TransformData *td, int il_offset)
5443 int i = il_offset;
5444 g_assert (!td->in_offsets [il_offset]);
5445 while (!td->in_offsets [i])
5446 i++;
5447 td->in_offsets [il_offset] = td->in_offsets [i];
5448 return td->in_offsets [il_offset];
5451 // We store in the in_offset array the native_offset + 1, so 0 can mean only that the il
5452 // offset is uninitialized. Otherwise 0 is valid value for first interp instruction.
5453 static int
5454 get_in_offset (TransformData *td, int il_offset)
5456 int target_offset = td->in_offsets [il_offset];
5457 if (target_offset)
5458 return target_offset - 1;
5459 return resolve_in_offset (td, il_offset) - 1;
5462 static void
5463 handle_relocations (TransformData *td)
5465 // Handle relocations
5466 for (int i = 0; i < td->relocs->len; ++i) {
5467 Reloc *reloc = (Reloc*)g_ptr_array_index (td->relocs, i);
5468 int offset = get_in_offset (td, reloc->target) - reloc->offset;
5470 switch (reloc->type) {
5471 case RELOC_SHORT_BRANCH:
5472 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
5473 td->new_code [reloc->offset + 1] = offset;
5474 break;
5475 case RELOC_LONG_BRANCH: {
5476 guint16 *v = (guint16 *) &offset;
5477 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
5478 g_assert (td->new_code [reloc->offset + 2] == 0xbeef);
5479 td->new_code [reloc->offset + 1] = *(guint16 *) v;
5480 td->new_code [reloc->offset + 2] = *(guint16 *) (v + 1);
5481 break;
5483 case RELOC_SWITCH: {
5484 guint16 *v = (guint16*)&offset;
5485 g_assert (td->new_code [reloc->offset] == 0xdead);
5486 g_assert (td->new_code [reloc->offset + 1] == 0xbeef);
5487 td->new_code [reloc->offset] = *(guint16*)v;
5488 td->new_code [reloc->offset + 1] = *(guint16*)(v + 1);
5489 break;
5491 default:
5492 g_assert_not_reached ();
5493 break;
5499 static int
5500 get_inst_length (InterpInst *ins)
5502 if (ins->opcode == MINT_SWITCH)
5503 return MINT_SWITCH_LEN (READ32 (&ins->data [0]));
5504 else
5505 return mono_interp_oplen [ins->opcode];
5508 static guint16*
5509 emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *ins)
5511 guint16 opcode = ins->opcode;
5512 guint16 *ip = start_ip;
5514 // We know what IL offset this instruction was created for. We can now map the IL offset
5515 // to the IR offset. We use this array to resolve the relocations, which reference the IL.
5516 if (ins->il_offset != -1 && !td->in_offsets [ins->il_offset]) {
5517 g_assert (ins->il_offset >= 0 && ins->il_offset < td->header->code_size);
5518 td->in_offsets [ins->il_offset] = start_ip - td->new_code + 1;
5520 MonoDebugLineNumberEntry lne;
5521 lne.native_offset = (guint8*)start_ip - (guint8*)td->new_code;
5522 lne.il_offset = ins->il_offset;
5523 g_array_append_val (td->line_numbers, lne);
5526 *ip++ = opcode;
5527 if (opcode == MINT_SWITCH) {
5528 int labels = READ32 (&ins->data [0]);
5529 // Write number of switch labels
5530 *ip++ = ins->data [0];
5531 *ip++ = ins->data [1];
5532 // Add relocation for each label
5533 for (int i = 0; i < labels; i++) {
5534 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5535 reloc->type = RELOC_SWITCH;
5536 reloc->offset = ip - td->new_code;
5537 reloc->target = READ32 (&ins->data [2 + i * 2]);
5538 g_ptr_array_add (td->relocs, reloc);
5539 *ip++ = 0xdead;
5540 *ip++ = 0xbeef;
5542 } else if ((opcode >= MINT_BRFALSE_I4_S && opcode <= MINT_BRTRUE_R8_S) ||
5543 (opcode >= MINT_BEQ_I4_S && opcode <= MINT_BLT_UN_R8_S) ||
5544 opcode == MINT_BR_S || opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK) {
5545 const int br_offset = start_ip - td->new_code;
5546 if (ins->data [0] < ins->il_offset) {
5547 // Backwards branch. We can already patch it.
5548 *ip++ = get_in_offset (td, ins->data [0]) - br_offset;
5549 } else {
5550 // We don't know the in_offset of the target, add a reloc
5551 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5552 reloc->type = RELOC_SHORT_BRANCH;
5553 reloc->offset = br_offset;
5554 reloc->target = ins->data [0];
5555 g_ptr_array_add (td->relocs, reloc);
5556 *ip++ = 0xdead;
5558 } else if ((opcode >= MINT_BRFALSE_I4 && opcode <= MINT_BRTRUE_R8) ||
5559 (opcode >= MINT_BEQ_I4 && opcode <= MINT_BLT_UN_R8) ||
5560 opcode == MINT_BR || opcode == MINT_LEAVE || opcode == MINT_LEAVE_CHECK) {
5561 const int br_offset = start_ip - td->new_code;
5562 int target_il = READ32 (&ins->data [0]);
5563 if (target_il < ins->il_offset) {
5564 // Backwards branch. We can already patch it
5565 const int br_offset = start_ip - td->new_code;
5566 int target_offset = get_in_offset (td, target_il) - br_offset;
5567 WRITE32 (ip, &target_offset);
5568 } else {
5569 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5570 reloc->type = RELOC_LONG_BRANCH;
5571 reloc->offset = br_offset;
5572 reloc->target = target_il;
5573 g_ptr_array_add (td->relocs, reloc);
5574 *ip++ = 0xdead;
5575 *ip++ = 0xbeef;
5577 } else if (opcode == MINT_SDB_SEQ_POINT) {
5578 SeqPoint *seqp = (SeqPoint*)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint));
5579 InterpBasicBlock *cbb;
5581 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY) {
5582 seqp->il_offset = METHOD_ENTRY_IL_OFFSET;
5583 cbb = td->offset_to_bb [0];
5584 } else {
5585 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT)
5586 seqp->il_offset = METHOD_EXIT_IL_OFFSET;
5587 else
5588 seqp->il_offset = ins->il_offset;
5589 cbb = td->offset_to_bb [ins->il_offset];
5591 seqp->native_offset = (guint8*)start_ip - (guint8*)td->new_code;
5592 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK)
5593 seqp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
5594 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL)
5595 seqp->flags |= MONO_SEQ_POINT_FLAG_NESTED_CALL;
5596 g_ptr_array_add (td->seq_points, seqp);
5598 cbb->seq_points = g_slist_prepend_mempool (td->mempool, cbb->seq_points, seqp);
5599 cbb->last_seq_point = seqp;
5600 } else {
5601 int size = get_inst_length (ins) - 1;
5602 // Emit the rest of the data
5603 for (int i = 0; i < size; i++)
5604 *ip++ = ins->data [i];
5606 return ip;
5609 // Generates the final code, after we are done with all the passes
5610 static void
5611 generate_compacted_code (TransformData *td)
5613 guint16 *ip;
5614 int size = 0;
5615 td->relocs = g_ptr_array_new ();
5617 // Iterate once to compute the exact size of the compacted code
5618 InterpInst *ins = td->first_ins;
5619 while (ins) {
5620 size += get_inst_length (ins);
5621 ins = ins->next;
5624 // Generate the compacted stream of instructions
5625 td->new_code = ip = (guint16*)mono_domain_alloc0 (td->rtm->domain, size * sizeof (guint16));
5626 ins = td->first_ins;
5627 while (ins) {
5628 ip = emit_compacted_instruction (td, ip, ins);
5629 ins = ins->next;
5631 td->new_code_end = ip;
5632 td->in_offsets [td->header->code_size] = td->new_code_end - td->new_code;
5634 // Patch all branches
5635 handle_relocations (td);
5637 g_ptr_array_free (td->relocs, TRUE);
5640 static void
5641 generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoGenericContext *generic_context, MonoError *error)
5643 MonoDomain *domain = rtm->domain;
5644 int i;
5645 TransformData transform_data;
5646 TransformData *td;
5647 static gboolean verbose_method_inited;
5648 static char* verbose_method_name;
5650 if (!verbose_method_inited) {
5651 verbose_method_name = g_getenv ("MONO_VERBOSE_METHOD");
5652 verbose_method_inited = TRUE;
5655 memset (&transform_data, 0, sizeof(transform_data));
5656 td = &transform_data;
5658 td->method = method;
5659 td->rtm = rtm;
5660 td->code_size = header->code_size;
5661 td->header = header;
5662 td->max_code_size = td->code_size;
5663 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
5664 td->mempool = mono_mempool_new ();
5665 td->n_data_items = 0;
5666 td->max_data_items = 0;
5667 td->data_items = NULL;
5668 td->data_hash = g_hash_table_new (NULL, NULL);
5669 td->gen_sdb_seq_points = mini_debug_options.gen_sdb_seq_points;
5670 td->seq_points = g_ptr_array_new ();
5671 td->verbose_level = mono_interp_traceopt;
5672 td->total_locals_size = rtm->locals_size;
5673 rtm->data_items = td->data_items;
5675 if (verbose_method_name) {
5676 const char *name = verbose_method_name;
5678 if ((strchr (name, '.') > name) || strchr (name, ':')) {
5679 MonoMethodDesc *desc;
5681 desc = mono_method_desc_new (name, TRUE);
5682 if (mono_method_desc_full_match (desc, method)) {
5683 td->verbose_level = 4;
5685 mono_method_desc_free (desc);
5686 } else {
5687 if (strcmp (method->name, name) == 0)
5688 td->verbose_level = 4;
5692 td->stack = (StackInfo*)g_malloc0 ((header->max_stack + 1) * sizeof (td->stack [0]));
5693 td->stack_capacity = header->max_stack + 1;
5694 td->sp = td->stack;
5695 td->max_stack_height = 0;
5696 td->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
5698 generate_code (td, method, header, generic_context, error);
5699 goto_if_nok (error, exit);
5701 generate_compacted_code (td);
5703 if (td->verbose_level) {
5704 g_print ("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method, TRUE), rtm, td->max_vt_sp);
5705 g_print ("Calculated stack size: %d, stated size: %d\n", td->max_stack_height, header->max_stack);
5706 dump_mint_code (td->new_code, td->new_code_end);
5709 /* Check if we use excessive stack space */
5710 if (td->max_stack_height > header->max_stack * 3 && header->max_stack > 16)
5711 g_warning ("Excessive stack space usage for method %s, %d/%d", method->name, td->max_stack_height, header->max_stack);
5713 int code_len;
5714 code_len = td->new_code_end - td->new_code;
5716 rtm->clauses = (MonoExceptionClause*)mono_domain_alloc0 (domain, header->num_clauses * sizeof (MonoExceptionClause));
5717 memcpy (rtm->clauses, header->clauses, header->num_clauses * sizeof(MonoExceptionClause));
5718 rtm->code = (gushort*)td->new_code;
5719 rtm->init_locals = header->init_locals;
5720 rtm->num_clauses = header->num_clauses;
5721 for (i = 0; i < header->num_clauses; i++) {
5722 MonoExceptionClause *c = rtm->clauses + i;
5723 int end_off = c->try_offset + c->try_len;
5724 c->try_offset = get_in_offset (td, c->try_offset);
5725 c->try_len = get_in_offset (td, end_off) - c->try_offset;
5726 g_assert ((c->try_offset + c->try_len) < code_len);
5727 end_off = c->handler_offset + c->handler_len;
5728 c->handler_offset = get_in_offset (td, c->handler_offset);
5729 c->handler_len = get_in_offset (td, end_off) - c->handler_offset;
5730 g_assert (c->handler_len >= 0 && (c->handler_offset + c->handler_len) <= code_len);
5731 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER)
5732 c->data.filter_offset = get_in_offset (td, c->data.filter_offset);
5734 rtm->stack_size = (sizeof (stackval)) * (td->max_stack_height + 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/
5735 rtm->stack_size = ALIGN_TO (rtm->stack_size, MINT_VT_ALIGNMENT);
5736 rtm->vt_stack_size = td->max_vt_sp;
5737 rtm->total_locals_size = td->total_locals_size;
5738 rtm->alloca_size = rtm->total_locals_size + rtm->vt_stack_size + rtm->stack_size;
5739 rtm->data_items = (gpointer*)mono_domain_alloc0 (domain, td->n_data_items * sizeof (td->data_items [0]));
5740 memcpy (rtm->data_items, td->data_items, td->n_data_items * sizeof (td->data_items [0]));
5742 /* Save debug info */
5743 interp_save_debug_info (rtm, header, td, td->line_numbers);
5745 /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
5746 int jinfo_len;
5747 jinfo_len = mono_jit_info_size ((MonoJitInfoFlags)0, header->num_clauses, 0);
5748 MonoJitInfo *jinfo;
5749 jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len);
5750 jinfo->is_interp = 1;
5751 rtm->jinfo = jinfo;
5752 mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len, (MonoJitInfoFlags)0, header->num_clauses, 0);
5753 for (i = 0; i < jinfo->num_clauses; ++i) {
5754 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
5755 MonoExceptionClause *c = rtm->clauses + i;
5757 ei->flags = c->flags;
5758 ei->try_start = (guint8*)(rtm->code + c->try_offset);
5759 ei->try_end = (guint8*)(rtm->code + c->try_offset + c->try_len);
5760 ei->handler_start = (guint8*)(rtm->code + c->handler_offset);
5761 ei->exvar_offset = rtm->exvar_offsets [i];
5762 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
5763 ei->data.filter = (guint8*)(rtm->code + c->data.filter_offset);
5764 } else if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
5765 ei->data.handler_end = (guint8*)(rtm->code + c->handler_offset + c->handler_len);
5766 } else {
5767 ei->data.catch_class = c->data.catch_class;
5771 save_seq_points (td, jinfo);
5773 exit:
5774 g_free (td->in_offsets);
5775 g_free (td->data_items);
5776 g_free (td->stack);
5777 g_hash_table_destroy (td->data_hash);
5778 g_ptr_array_free (td->seq_points, TRUE);
5779 g_array_free (td->line_numbers, TRUE);
5780 mono_mempool_destroy (td->mempool);
5783 static mono_mutex_t calc_section;
5785 void
5786 mono_interp_transform_init (void)
5788 mono_os_mutex_init_recursive(&calc_section);
5791 void
5792 mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, MonoError *error)
5794 MonoMethod *method = imethod->method;
5795 MonoMethodHeader *header = NULL;
5796 MonoMethodSignature *signature = mono_method_signature_internal (method);
5797 MonoVTable *method_class_vt;
5798 MonoGenericContext *generic_context = NULL;
5799 MonoDomain *domain = imethod->domain;
5800 InterpMethod tmp_imethod;
5801 InterpMethod *real_imethod;
5803 error_init (error);
5805 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method->klass))) {
5806 mono_error_set_invalid_operation (error, "%s", "Could not execute the method because the containing type is not fully instantiated.");
5807 return;
5810 // g_printerr ("TRANSFORM(0x%016lx): begin %s::%s\n", mono_thread_current (), method->klass->name, method->name);
5811 method_class_vt = mono_class_vtable_checked (domain, imethod->method->klass, error);
5812 return_if_nok (error);
5814 if (!method_class_vt->initialized) {
5815 mono_runtime_class_init_full (method_class_vt, error);
5816 return_if_nok (error);
5819 MONO_PROFILER_RAISE (jit_begin, (method));
5821 if (mono_method_signature_internal (method)->is_inflated)
5822 generic_context = mono_method_get_context (method);
5823 else {
5824 MonoGenericContainer *generic_container = mono_method_get_generic_container (method);
5825 if (generic_container)
5826 generic_context = &generic_container->context;
5829 if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
5830 MonoMethod *nm = NULL;
5831 mono_os_mutex_lock (&calc_section);
5832 if (imethod->transformed) {
5833 mono_os_mutex_unlock (&calc_section);
5834 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));
5835 return;
5838 /* assumes all internal calls with an array this are built in... */
5839 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL && (! mono_method_signature_internal (method)->hasthis || m_class_get_rank (method->klass) == 0)) {
5840 nm = mono_marshal_get_native_wrapper (method, TRUE, FALSE);
5841 signature = mono_method_signature_internal (nm);
5842 } else {
5843 const char *name = method->name;
5844 if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) {
5845 if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
5846 MonoJitICallInfo *mi = mono_find_jit_icall_by_name ("ves_icall_mono_delegate_ctor_interp");
5847 g_assert (mi);
5848 nm = mono_marshal_get_icall_wrapper (mi, TRUE);
5849 } else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
5851 * Usually handled during transformation of the caller, but
5852 * when the caller is handled by another execution engine
5853 * (for example fullAOT) we need to handle it here. That's
5854 * known to be wrong in cases where the reference to
5855 * `MonoDelegate` would be needed (FIXME).
5857 nm = mono_marshal_get_delegate_invoke (method, NULL);
5858 } else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
5859 nm = mono_marshal_get_delegate_begin_invoke (method);
5860 } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
5861 nm = mono_marshal_get_delegate_end_invoke (method);
5864 if (nm == NULL)
5865 g_assert_not_reached ();
5867 if (nm == NULL) {
5868 imethod->stack_size = sizeof (stackval); /* for tracing */
5869 imethod->alloca_size = imethod->stack_size;
5870 imethod->transformed = TRUE;
5871 mono_os_mutex_unlock(&calc_section);
5872 MONO_PROFILER_RAISE (jit_done, (method, NULL));
5873 return;
5875 method = nm;
5876 header = interp_method_get_header (nm, error);
5877 mono_os_mutex_unlock (&calc_section);
5878 return_if_nok (error);
5881 if (!header) {
5882 header = mono_method_get_header_checked (method, error);
5883 return_if_nok (error);
5886 g_assert ((signature->param_count + signature->hasthis) < 1000);
5887 // g_printerr ("TRANSFORM(0x%016lx): end %s::%s\n", mono_thread_current (), method->klass->name, method->name);
5889 /* Make modifications to a copy of imethod, copy them back inside the lock */
5890 real_imethod = imethod;
5891 memcpy (&tmp_imethod, imethod, sizeof (InterpMethod));
5892 imethod = &tmp_imethod;
5894 interp_method_compute_offsets (imethod, signature, header);
5896 MONO_TIME_TRACK (mono_interp_stats.transform_time, generate (method, header, imethod, generic_context, error));
5898 mono_metadata_free_mh (header);
5900 return_if_nok (error);
5902 /* Copy changes back */
5903 imethod = real_imethod;
5904 mono_os_mutex_lock (&calc_section);
5905 if (!imethod->transformed) {
5906 InterpMethod *hash = imethod->next_jit_code_hash;
5907 memcpy (imethod, &tmp_imethod, sizeof (InterpMethod));
5908 imethod->next_jit_code_hash = hash;
5909 mono_memory_barrier ();
5910 imethod->transformed = TRUE;
5911 mono_atomic_fetch_add_i32 (&mono_jit_stats.methods_with_interp, 1);
5914 mono_os_mutex_unlock (&calc_section);
5916 mono_domain_lock (domain);
5917 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, imethod->method))
5918 g_hash_table_insert (domain_jit_info (domain)->seq_points, imethod->method, imethod->jinfo->seq_points);
5919 mono_domain_unlock (domain);
5921 // FIXME: Add a different callback ?
5922 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));