[interp] Reduce computation under calc_section mutex
[mono-project.git] / mono / mini / interp / transform.c
blob4c8c8e1f500832758c6a739f2e329da90a90b993
1 /**
2 * \file
3 * transform CIL into different opcodes for more
4 * efficient interpretation
6 * Written by Bernie Solomon (bernard@ugsolutions.com)
7 * Copyright (c) 2004.
8 */
10 #include "config.h"
11 #include <string.h>
12 #include <mono/metadata/appdomain.h>
13 #include <mono/metadata/class-internals.h>
14 #include <mono/metadata/debug-helpers.h>
15 #include <mono/metadata/exception.h>
16 #include <mono/metadata/exception-internals.h>
17 #include <mono/metadata/mono-endian.h>
18 #include <mono/metadata/marshal.h>
19 #include <mono/metadata/profiler-private.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/seq-points-data.h>
22 #include <mono/metadata/mono-basic-block.h>
23 #include <mono/metadata/abi-details.h>
24 #include <mono/metadata/reflection-internals.h>
25 #include <mono/utils/unlocked.h>
27 #include <mono/mini/mini.h>
28 #include <mono/mini/mini-runtime.h>
30 #include "mintops.h"
31 #include "interp-internals.h"
32 #include "interp.h"
34 #define INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK 1
35 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY 2
36 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT 4
37 #define INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL 8
39 MonoInterpStats mono_interp_stats;
41 #define DEBUG 0
43 typedef struct InterpInst InterpInst;
45 typedef struct
47 MonoClass *klass;
48 unsigned char type;
49 unsigned char flags;
50 } StackInfo;
52 struct InterpInst {
53 guint16 opcode;
54 InterpInst *next, *prev;
55 // If this is -1, this instruction is not logically associated with an IL offset, it is
56 // part of the IL instruction associated with the previous interp instruction.
57 int il_offset;
58 guint32 flags;
59 guint16 data [MONO_ZERO_LEN_ARRAY];
62 typedef struct {
63 guint8 *ip;
64 GSList *preds;
65 GSList *seq_points;
66 SeqPoint *last_seq_point;
68 // This will hold a list of last sequence points of incoming basic blocks
69 SeqPoint **pred_seq_points;
70 guint num_pred_seq_points;
71 } InterpBasicBlock;
73 typedef enum {
74 RELOC_SHORT_BRANCH,
75 RELOC_LONG_BRANCH,
76 RELOC_SWITCH
77 } RelocType;
79 typedef struct {
80 RelocType type;
81 /* In the interpreter IR */
82 int offset;
83 /* In the IL code */
84 int target;
85 } Reloc;
87 typedef struct
89 MonoMethod *method;
90 MonoMethod *inlined_method;
91 MonoMethodHeader *header;
92 InterpMethod *rtm;
93 const unsigned char *il_code;
94 const unsigned char *ip;
95 const unsigned char *in_start;
96 InterpInst *last_ins, *first_ins;
97 int code_size;
98 int *in_offsets;
99 StackInfo **stack_state;
100 int *stack_height;
101 int *vt_stack_size;
102 unsigned char *is_bb_start;
103 unsigned short *new_code;
104 unsigned short *new_code_end;
105 unsigned int max_code_size;
106 StackInfo *stack;
107 StackInfo *sp;
108 unsigned int max_stack_height;
109 unsigned int stack_capacity;
110 unsigned int vt_sp;
111 unsigned int max_vt_sp;
112 unsigned int total_locals_size;
113 int n_data_items;
114 int max_data_items;
115 void **data_items;
116 GHashTable *data_hash;
117 int *clause_indexes;
118 gboolean gen_sdb_seq_points;
119 GPtrArray *seq_points;
120 InterpBasicBlock **offset_to_bb;
121 InterpBasicBlock *entry_bb;
122 MonoMemPool *mempool;
123 GList *basic_blocks;
124 GPtrArray *relocs;
125 gboolean verbose_level;
126 GArray *line_numbers;
127 } TransformData;
129 #define STACK_TYPE_I4 0
130 #define STACK_TYPE_I8 1
131 #define STACK_TYPE_R4 2
132 #define STACK_TYPE_R8 3
133 #define STACK_TYPE_O 4
134 #define STACK_TYPE_VT 5
135 #define STACK_TYPE_MP 6
136 #define STACK_TYPE_F 7
138 static const char *stack_type_string [] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " };
140 #if SIZEOF_VOID_P == 8
141 #define STACK_TYPE_I STACK_TYPE_I8
142 #else
143 #define STACK_TYPE_I STACK_TYPE_I4
144 #endif
146 static int stack_type [] = {
147 STACK_TYPE_I4, /*I1*/
148 STACK_TYPE_I4, /*U1*/
149 STACK_TYPE_I4, /*I2*/
150 STACK_TYPE_I4, /*U2*/
151 STACK_TYPE_I4, /*I4*/
152 STACK_TYPE_I8, /*I8*/
153 STACK_TYPE_R4, /*R4*/
154 STACK_TYPE_R8, /*R8*/
155 STACK_TYPE_O, /*O*/
156 STACK_TYPE_MP, /*P*/
157 STACK_TYPE_VT
160 #if SIZEOF_VOID_P == 8
161 #define MINT_NEG_P MINT_NEG_I8
162 #define MINT_NOT_P MINT_NOT_I8
164 #define MINT_NEG_FP MINT_NEG_R8
166 #define MINT_ADD_P MINT_ADD_I8
167 #define MINT_SUB_P MINT_SUB_I8
168 #define MINT_MUL_P MINT_MUL_I8
169 #define MINT_DIV_P MINT_DIV_I8
170 #define MINT_DIV_UN_P MINT_DIV_UN_I8
171 #define MINT_REM_P MINT_REM_I8
172 #define MINT_REM_UN_P MINT_REM_UN_I8
173 #define MINT_AND_P MINT_AND_I8
174 #define MINT_OR_P MINT_OR_I8
175 #define MINT_XOR_P MINT_XOR_I8
176 #define MINT_SHL_P MINT_SHL_I8
177 #define MINT_SHR_P MINT_SHR_I8
178 #define MINT_SHR_UN_P MINT_SHR_UN_I8
180 #define MINT_CEQ_P MINT_CEQ_I8
181 #define MINT_CNE_P MINT_CNE_I8
182 #define MINT_CLT_P MINT_CLT_I8
183 #define MINT_CLT_UN_P MINT_CLT_UN_I8
184 #define MINT_CGT_P MINT_CGT_I8
185 #define MINT_CGT_UN_P MINT_CGT_UN_I8
186 #define MINT_CLE_P MINT_CLE_I8
187 #define MINT_CLE_UN_P MINT_CLE_UN_I8
188 #define MINT_CGE_P MINT_CGE_I8
189 #define MINT_CGE_UN_P MINT_CGE_UN_I8
191 #define MINT_ADD_FP MINT_ADD_R8
192 #define MINT_SUB_FP MINT_SUB_R8
193 #define MINT_MUL_FP MINT_MUL_R8
194 #define MINT_DIV_FP MINT_DIV_R8
195 #define MINT_REM_FP MINT_REM_R8
197 #define MINT_CNE_FP MINT_CNE_R8
198 #define MINT_CEQ_FP MINT_CEQ_R8
199 #define MINT_CGT_FP MINT_CGT_R8
200 #define MINT_CGE_FP MINT_CGE_R8
201 #define MINT_CLT_FP MINT_CLT_R8
202 #define MINT_CLE_FP MINT_CLE_R8
204 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I8
205 #else
207 #define MINT_NEG_P MINT_NEG_I4
208 #define MINT_NOT_P MINT_NOT_I4
210 #define MINT_NEG_FP MINT_NEG_R4
212 #define MINT_ADD_P MINT_ADD_I4
213 #define MINT_SUB_P MINT_SUB_I4
214 #define MINT_MUL_P MINT_MUL_I4
215 #define MINT_DIV_P MINT_DIV_I4
216 #define MINT_DIV_UN_P MINT_DIV_UN_I4
217 #define MINT_REM_P MINT_REM_I4
218 #define MINT_REM_UN_P MINT_REM_UN_I4
219 #define MINT_AND_P MINT_AND_I4
220 #define MINT_OR_P MINT_OR_I4
221 #define MINT_XOR_P MINT_XOR_I4
222 #define MINT_SHL_P MINT_SHL_I4
223 #define MINT_SHR_P MINT_SHR_I4
224 #define MINT_SHR_UN_P MINT_SHR_UN_I4
226 #define MINT_CEQ_P MINT_CEQ_I4
227 #define MINT_CNE_P MINT_CNE_I4
228 #define MINT_CLT_P MINT_CLT_I4
229 #define MINT_CLT_UN_P MINT_CLT_UN_I4
230 #define MINT_CGT_P MINT_CGT_I4
231 #define MINT_CGT_UN_P MINT_CGT_UN_I4
232 #define MINT_CLE_P MINT_CLE_I4
233 #define MINT_CLE_UN_P MINT_CLE_UN_I4
234 #define MINT_CGE_P MINT_CGE_I4
235 #define MINT_CGE_UN_P MINT_CGE_UN_I4
237 #define MINT_ADD_FP MINT_ADD_R4
238 #define MINT_SUB_FP MINT_SUB_R4
239 #define MINT_MUL_FP MINT_MUL_R4
240 #define MINT_DIV_FP MINT_DIV_R4
241 #define MINT_REM_FP MINT_REM_R4
243 #define MINT_CNE_FP MINT_CNE_R4
244 #define MINT_CEQ_FP MINT_CEQ_R4
245 #define MINT_CGT_FP MINT_CGT_R4
246 #define MINT_CGE_FP MINT_CGE_R4
247 #define MINT_CLT_FP MINT_CLT_R4
248 #define MINT_CLE_FP MINT_CLE_R4
250 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I4
251 #endif
253 typedef struct {
254 const gchar *op_name;
255 guint16 insn [3];
256 } MagicIntrinsic;
258 // static const MagicIntrinsic int_binop[] = {
260 static const MagicIntrinsic int_unnop[] = {
261 { "op_UnaryPlus", {MINT_NOP, MINT_NOP, MINT_NOP}},
262 { "op_UnaryNegation", {MINT_NEG_P, MINT_NEG_P, MINT_NEG_FP}},
263 { "op_OnesComplement", {MINT_NOT_P, MINT_NOT_P, MINT_NIY}}
266 static const MagicIntrinsic int_binop[] = {
267 { "op_Addition", {MINT_ADD_P, MINT_ADD_P, MINT_ADD_FP}},
268 { "op_Subtraction", {MINT_SUB_P, MINT_SUB_P, MINT_SUB_FP}},
269 { "op_Multiply", {MINT_MUL_P, MINT_MUL_P, MINT_MUL_FP}},
270 { "op_Division", {MINT_DIV_P, MINT_DIV_UN_P, MINT_DIV_FP}},
271 { "op_Modulus", {MINT_REM_P, MINT_REM_UN_P, MINT_REM_FP}},
272 { "op_BitwiseAnd", {MINT_AND_P, MINT_AND_P, MINT_NIY}},
273 { "op_BitwiseOr", {MINT_OR_P, MINT_OR_P, MINT_NIY}},
274 { "op_ExclusiveOr", {MINT_XOR_P, MINT_XOR_P, MINT_NIY}},
275 { "op_LeftShift", {MINT_SHL_P, MINT_SHL_P, MINT_NIY}},
276 { "op_RightShift", {MINT_SHR_P, MINT_SHR_UN_P, MINT_NIY}},
279 static const MagicIntrinsic int_cmpop[] = {
280 { "op_Inequality", {MINT_CNE_P, MINT_CNE_P, MINT_CNE_FP}},
281 { "op_Equality", {MINT_CEQ_P, MINT_CEQ_P, MINT_CEQ_FP}},
282 { "op_GreaterThan", {MINT_CGT_P, MINT_CGT_UN_P, MINT_CGT_FP}},
283 { "op_GreaterThanOrEqual", {MINT_CGE_P, MINT_CGE_UN_P, MINT_CGE_FP}},
284 { "op_LessThan", {MINT_CLT_P, MINT_CLT_UN_P, MINT_CLT_FP}},
285 { "op_LessThanOrEqual", {MINT_CLE_P, MINT_CLE_UN_P, MINT_CLE_FP}}
288 static gboolean generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error);
290 static InterpInst*
291 interp_new_ins (TransformData *td, guint16 opcode, int len)
293 InterpInst *new_inst;
294 g_assert (len);
295 // Size of data region of instruction is length of instruction minus 1 (the opcode slot)
296 new_inst = mono_mempool_alloc0 (td->mempool, sizeof (InterpInst) + sizeof (guint16) * (len - 1));
297 new_inst->opcode = opcode;
298 // opcodes from inlined methods don't have il offset associated with them since the offset
299 // stored here is relevant only for the original method
300 if (!td->inlined_method)
301 new_inst->il_offset = td->in_start - td->il_code;
302 else
303 new_inst->il_offset = -1;
304 return new_inst;
307 // This version need to be used with switch opcode, which doesn't have constant length
308 static InterpInst*
309 interp_add_ins_explicit (TransformData *td, guint16 opcode, int len)
311 InterpInst *new_inst = interp_new_ins (td, opcode, len);
312 new_inst->prev = td->last_ins;
313 if (td->last_ins)
314 td->last_ins->next = new_inst;
315 else
316 td->first_ins = new_inst;
317 td->last_ins = new_inst;
318 return new_inst;
321 static InterpInst*
322 interp_add_ins (TransformData *td, guint16 opcode)
324 return interp_add_ins_explicit (td, opcode, mono_interp_oplen [opcode]);
327 // Inserts a new instruction inside the instruction stream
328 static InterpInst*
329 interp_insert_ins (TransformData *td, InterpInst *prev_ins, guint16 opcode)
331 InterpInst *new_inst = interp_new_ins (td, opcode, mono_interp_oplen [opcode]);
332 g_assert (prev_ins && prev_ins->next);
334 new_inst->prev = prev_ins;
335 new_inst->next = prev_ins->next;
336 prev_ins->next = new_inst;
337 new_inst->next->prev = new_inst;
339 return new_inst;
342 static void
343 interp_remove_ins (TransformData *td, InterpInst *ins)
345 if (ins->prev) {
346 ins->prev->next = ins->next;
347 } else {
348 td->first_ins = ins->next;
350 if (ins->next) {
351 ins->next->prev = ins->prev;
352 } else {
353 td->last_ins = ins->prev;
357 #define CHECK_STACK(td, n) \
358 do { \
359 int stack_size = (td)->sp - (td)->stack; \
360 if (stack_size < (n)) \
361 g_warning ("%s.%s: not enough values (%d < %d) on stack at %04x", \
362 m_class_get_name ((td)->method->klass), (td)->method->name, \
363 stack_size, n, (td)->ip - (td)->il_code); \
364 } while (0)
366 #define ENSURE_I4(td, sp_off) \
367 do { \
368 if ((td)->sp [-sp_off].type == STACK_TYPE_I8) \
369 interp_add_ins (td, sp_off == 1 ? MINT_CONV_I4_I8 : MINT_CONV_I4_I8_SP); \
370 } while (0)
372 #define CHECK_TYPELOAD(klass) \
373 do { \
374 if (!(klass) || mono_class_has_failure (klass)) { \
375 mono_error_set_for_class_failure (error, klass); \
376 goto exit; \
378 } while (0)
380 #if NO_UNALIGNED_ACCESS
381 #define WRITE32(ip, v) \
382 do { \
383 * (ip) = * (guint16 *)(v); \
384 * ((ip) + 1) = * ((guint16 *)(v) + 1); \
385 (ip) += 2; \
386 } while (0)
388 #define WRITE32_INS(ins, index, v) \
389 do { \
390 (ins)->data [index] = * (guint16 *)(v); \
391 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
392 } while (0)
394 #define WRITE64_INS(ins, index, v) \
395 do { \
396 (ins)->data [index] = * (guint16 *)(v); \
397 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
398 (ins)->data [index + 2] = * ((guint16 *)(v) + 2); \
399 (ins)->data [index + 3] = * ((guint16 *)(v) + 3); \
400 } while (0)
401 #else
402 #define WRITE32(ip, v) \
403 do { \
404 * (guint32*)(ip) = * (guint32 *)(v); \
405 (ip) += 2; \
406 } while (0)
407 #define WRITE32_INS(ins, index, v) \
408 do { \
409 * (guint32 *)(&(ins)->data [index]) = * (guint32 *)(v); \
410 } while (0)
412 #define WRITE64_INS(ins, index, v) \
413 do { \
414 * (guint64 *)(&(ins)->data [index]) = * (guint64 *)(v); \
415 } while (0)
417 #endif
420 static void
421 handle_branch (TransformData *td, int short_op, int long_op, int offset)
423 int shorten_branch = 0;
424 int target = td->ip + offset - td->il_code;
425 if (target < 0 || target >= td->code_size)
426 g_assert_not_reached ();
427 /* Add exception checkpoint or safepoint for backward branches */
428 if (offset < 0) {
429 if (mono_threads_are_safepoints_enabled ())
430 interp_add_ins (td, MINT_SAFEPOINT);
431 else
432 interp_add_ins (td, MINT_CHECKPOINT);
434 if (offset > 0 && td->stack_height [target] < 0) {
435 td->stack_height [target] = td->sp - td->stack;
436 if (td->stack_height [target] > 0)
437 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, td->stack_height [target] * sizeof (td->stack [0]));
438 td->vt_stack_size [target] = td->vt_sp;
441 if (td->header->code_size <= 25000) /* FIX to be precise somehow? */
442 shorten_branch = 1;
444 if (shorten_branch) {
445 interp_add_ins (td, short_op);
446 td->last_ins->data [0] = (guint16) target;
447 } else {
448 interp_add_ins (td, long_op);
449 WRITE32_INS (td->last_ins, 0, &target);
453 static void
454 one_arg_branch(TransformData *td, int mint_op, int offset)
456 int type = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
457 int long_op = mint_op + type - STACK_TYPE_I4;
458 int short_op = long_op + MINT_BRFALSE_I4_S - MINT_BRFALSE_I4;
459 CHECK_STACK(td, 1);
460 --td->sp;
461 handle_branch (td, short_op, long_op, offset);
464 static void
465 two_arg_branch(TransformData *td, int mint_op, int offset)
467 int type1 = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
468 int type2 = td->sp [-2].type == STACK_TYPE_O || td->sp [-2].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-2].type;
469 int long_op = mint_op + type1 - STACK_TYPE_I4;
470 int short_op = long_op + MINT_BEQ_I4_S - MINT_BEQ_I4;
471 CHECK_STACK(td, 2);
472 if (type1 == STACK_TYPE_I4 && type2 == STACK_TYPE_I8) {
473 interp_add_ins (td, MINT_CONV_I8_I4);
474 // The il instruction starts with the actual branch, and not with the conversion opcodes
475 td->last_ins->il_offset = -1;
476 } else if (type1 == STACK_TYPE_I8 && type2 == STACK_TYPE_I4) {
477 interp_add_ins (td, MINT_CONV_I8_I4_SP);
478 td->last_ins->il_offset = -1;
479 } else if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
480 interp_add_ins (td, MINT_CONV_R8_R4);
481 td->last_ins->il_offset = -1;
482 } else if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
483 interp_add_ins (td, MINT_CONV_R8_R4_SP);
484 td->last_ins->il_offset = -1;
485 } else if (type1 != type2) {
486 g_warning("%s.%s: branch type mismatch %d %d",
487 m_class_get_name (td->method->klass), td->method->name,
488 td->sp [-1].type, td->sp [-2].type);
490 td->sp -= 2;
491 handle_branch (td, short_op, long_op, offset);
494 static void
495 unary_arith_op(TransformData *td, int mint_op)
497 int op = mint_op + td->sp [-1].type - STACK_TYPE_I4;
498 CHECK_STACK(td, 1);
499 interp_add_ins (td, op);
502 static void
503 binary_arith_op(TransformData *td, int mint_op)
505 int type1 = td->sp [-2].type;
506 int type2 = td->sp [-1].type;
507 int op;
508 #if SIZEOF_VOID_P == 8
509 if ((type1 == STACK_TYPE_MP || type1 == STACK_TYPE_I8) && type2 == STACK_TYPE_I4) {
510 interp_add_ins (td, MINT_CONV_I8_I4);
511 type2 = STACK_TYPE_I8;
513 if (type1 == STACK_TYPE_I4 && (type2 == STACK_TYPE_MP || type2 == STACK_TYPE_I8)) {
514 interp_add_ins (td, MINT_CONV_I8_I4_SP);
515 type1 = STACK_TYPE_I8;
516 td->sp [-2].type = STACK_TYPE_I8;
518 #endif
519 if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
520 interp_add_ins (td, MINT_CONV_R8_R4);
521 type2 = STACK_TYPE_R8;
523 if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
524 interp_add_ins (td, MINT_CONV_R8_R4_SP);
525 type1 = STACK_TYPE_R8;
526 td->sp [-2].type = STACK_TYPE_R8;
528 if (type1 == STACK_TYPE_MP)
529 type1 = STACK_TYPE_I;
530 if (type2 == STACK_TYPE_MP)
531 type2 = STACK_TYPE_I;
532 if (type1 != type2) {
533 g_warning("%s.%s: %04x arith type mismatch %s %d %d",
534 m_class_get_name (td->method->klass), td->method->name,
535 td->ip - td->il_code, mono_interp_opname[mint_op], type1, type2);
537 op = mint_op + type1 - STACK_TYPE_I4;
538 CHECK_STACK(td, 2);
539 interp_add_ins (td, op);
540 --td->sp;
543 static void
544 shift_op(TransformData *td, int mint_op)
546 int op = mint_op + td->sp [-2].type - STACK_TYPE_I4;
547 CHECK_STACK(td, 2);
548 if (td->sp [-1].type != STACK_TYPE_I4) {
549 g_warning("%s.%s: shift type mismatch %d",
550 m_class_get_name (td->method->klass), td->method->name,
551 td->sp [-2].type);
553 interp_add_ins (td, op);
554 --td->sp;
557 static int
558 can_store (int st_value, int vt_value)
560 if (st_value == STACK_TYPE_O || st_value == STACK_TYPE_MP)
561 st_value = STACK_TYPE_I;
562 if (vt_value == STACK_TYPE_O || vt_value == STACK_TYPE_MP)
563 vt_value = STACK_TYPE_I;
564 return st_value == vt_value;
567 #define SET_SIMPLE_TYPE(s, ty) \
568 do { \
569 (s)->type = (ty); \
570 (s)->flags = 0; \
571 (s)->klass = NULL; \
572 } while (0)
574 #define SET_TYPE(s, ty, k) \
575 do { \
576 (s)->type = (ty); \
577 (s)->flags = 0; \
578 (s)->klass = k; \
579 } while (0)
581 #define REALLOC_STACK(td, sppos) \
582 do { \
583 (td)->stack_capacity *= 2; \
584 (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
585 (td)->sp = (td)->stack + sppos; \
586 } while (0);
588 #define PUSH_SIMPLE_TYPE(td, ty) \
589 do { \
590 int sp_height; \
591 (td)->sp++; \
592 sp_height = (td)->sp - (td)->stack; \
593 if (sp_height > (td)->max_stack_height) \
594 (td)->max_stack_height = sp_height; \
595 if (sp_height > (td)->stack_capacity) \
596 REALLOC_STACK(td, sp_height); \
597 SET_SIMPLE_TYPE((td)->sp - 1, ty); \
598 } while (0)
600 #define PUSH_TYPE(td, ty, k) \
601 do { \
602 int sp_height; \
603 (td)->sp++; \
604 sp_height = (td)->sp - (td)->stack; \
605 if (sp_height > (td)->max_stack_height) \
606 (td)->max_stack_height = sp_height; \
607 if (sp_height > (td)->stack_capacity) \
608 REALLOC_STACK(td, sp_height); \
609 SET_TYPE((td)->sp - 1, ty, k); \
610 } while (0)
612 #define PUSH_VT(td, size) \
613 do { \
614 (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
615 if ((td)->vt_sp > (td)->max_vt_sp) \
616 (td)->max_vt_sp = (td)->vt_sp; \
617 } while (0)
619 #define POP_VT(td, size) \
620 do { \
621 (td)->vt_sp -= ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
622 } while (0)
624 static MonoType*
625 get_arg_type_exact (TransformData *td, int n, int *mt)
627 MonoType *type;
628 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
630 if (hasthis && n == 0)
631 type = m_class_get_byval_arg (td->method->klass);
632 else
633 type = mono_method_signature_internal (td->method)->params [n - !!hasthis];
635 if (mt)
636 *mt = mint_type (type);
638 return type;
641 static void
642 load_arg(TransformData *td, int n)
644 int mt;
645 MonoClass *klass = NULL;
646 MonoType *type;
647 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
649 type = get_arg_type_exact (td, n, &mt);
651 if (mt == MINT_TYPE_VT) {
652 gint32 size;
653 klass = mono_class_from_mono_type_internal (type);
654 if (mono_method_signature_internal (td->method)->pinvoke)
655 size = mono_class_native_size (klass, NULL);
656 else
657 size = mono_class_value_size (klass, NULL);
659 if (hasthis && n == 0) {
660 mt = MINT_TYPE_P;
661 interp_add_ins (td, MINT_LDARG_P);
662 td->last_ins->data [0] = 0;
663 klass = NULL;
664 } else {
665 PUSH_VT (td, size);
666 interp_add_ins (td, MINT_LDARG_VT);
667 td->last_ins->data [0] = n;
668 WRITE32_INS (td->last_ins, 1, &size);
670 } else {
671 if (hasthis && n == 0) {
672 mt = MINT_TYPE_P;
673 interp_add_ins (td, MINT_LDARG_P);
674 td->last_ins->data [0] = n;
675 klass = NULL;
676 } else {
677 interp_add_ins (td, MINT_LDARG_I1 + (mt - MINT_TYPE_I1));
678 td->last_ins->data [0] = n;
679 if (mt == MINT_TYPE_O)
680 klass = mono_class_from_mono_type_internal (type);
683 PUSH_TYPE(td, stack_type[mt], klass);
686 static void
687 store_arg(TransformData *td, int n)
689 int mt;
690 CHECK_STACK (td, 1);
691 MonoType *type;
693 type = get_arg_type_exact (td, n, &mt);
695 if (mt == MINT_TYPE_VT) {
696 gint32 size;
697 MonoClass *klass = mono_class_from_mono_type_internal (type);
698 if (mono_method_signature_internal (td->method)->pinvoke)
699 size = mono_class_native_size (klass, NULL);
700 else
701 size = mono_class_value_size (klass, NULL);
702 interp_add_ins (td, MINT_STARG_VT);
703 td->last_ins->data [0] = n;
704 WRITE32_INS (td->last_ins, 1, &size);
705 if (td->sp [-1].type == STACK_TYPE_VT)
706 POP_VT(td, size);
707 } else {
708 interp_add_ins (td, MINT_STARG_I1 + (mt - MINT_TYPE_I1));
709 td->last_ins->data [0] = n;
711 --td->sp;
714 static void
715 load_local_general (TransformData *td, int offset, MonoType *type)
717 int mt = mint_type (type);
718 MonoClass *klass = NULL;
719 if (mt == MINT_TYPE_VT) {
720 klass = mono_class_from_mono_type_internal (type);
721 gint32 size = mono_class_value_size (klass, NULL);
722 PUSH_VT(td, size);
723 interp_add_ins (td, MINT_LDLOC_VT);
724 td->last_ins->data [0] = offset;
725 WRITE32_INS (td->last_ins, 1, &size);
726 } else {
727 g_assert (mt < MINT_TYPE_VT);
728 if (!td->gen_sdb_seq_points &&
729 mt == MINT_TYPE_I4 && !td->is_bb_start [td->in_start - td->il_code] && td->last_ins != NULL &&
730 td->last_ins->opcode == MINT_STLOC_I4 && td->last_ins->data [0] == offset) {
731 td->last_ins->opcode = MINT_STLOC_NP_I4;
732 } else if (!td->gen_sdb_seq_points &&
733 mt == MINT_TYPE_O && !td->is_bb_start [td->in_start - td->il_code] && td->last_ins != NULL &&
734 td->last_ins->opcode == MINT_STLOC_O && td->last_ins->data [0] == offset) {
735 td->last_ins->opcode = MINT_STLOC_NP_O;
736 } else {
737 interp_add_ins (td, MINT_LDLOC_I1 + (mt - MINT_TYPE_I1));
738 td->last_ins->data [0] = offset; /*FIX for large offset */
740 if (mt == MINT_TYPE_O)
741 klass = mono_class_from_mono_type_internal (type);
743 PUSH_TYPE(td, stack_type[mt], klass);
746 static void
747 load_local (TransformData *td, int n)
749 MonoType *type = td->header->locals [n];
750 int offset = td->rtm->local_offsets [n];
751 load_local_general (td, offset, type);
754 static void
755 store_local_general (TransformData *td, int offset, MonoType *type)
757 int mt = mint_type (type);
758 CHECK_STACK (td, 1);
759 #if SIZEOF_VOID_P == 8
760 if (td->sp [-1].type == STACK_TYPE_I4 && stack_type [mt] == STACK_TYPE_I8) {
761 interp_add_ins (td, MINT_CONV_I8_I4);
762 td->sp [-1].type = STACK_TYPE_I8;
764 #endif
765 if (!can_store(td->sp [-1].type, stack_type [mt])) {
766 g_warning("%s.%s: Store local stack type mismatch %d %d",
767 m_class_get_name (td->method->klass), td->method->name,
768 stack_type [mt], td->sp [-1].type);
770 if (mt == MINT_TYPE_VT) {
771 MonoClass *klass = mono_class_from_mono_type_internal (type);
772 gint32 size = mono_class_value_size (klass, NULL);
773 interp_add_ins (td, MINT_STLOC_VT);
774 td->last_ins->data [0] = offset; /*FIX for large offset */
775 WRITE32_INS (td->last_ins, 1, &size);
776 if (td->sp [-1].type == STACK_TYPE_VT)
777 POP_VT(td, size);
778 } else {
779 g_assert (mt < MINT_TYPE_VT);
780 interp_add_ins (td, MINT_STLOC_I1 + (mt - MINT_TYPE_I1));
781 td->last_ins->data [0] = offset; /*FIX for large offset */
783 --td->sp;
786 static void
787 store_local (TransformData *td, int n)
789 MonoType *type = td->header->locals [n];
790 int offset = td->rtm->local_offsets [n];
791 store_local_general (td, offset, type);
794 #define SIMPLE_OP(td, op) \
795 do { \
796 interp_add_ins (td, op); \
797 ++td->ip; \
798 } while (0)
800 static guint16
801 get_data_item_index (TransformData *td, void *ptr)
803 gpointer p = g_hash_table_lookup (td->data_hash, ptr);
804 guint index;
805 if (p != NULL)
806 return GPOINTER_TO_UINT (p) - 1;
807 if (td->max_data_items == td->n_data_items) {
808 td->max_data_items = td->n_data_items == 0 ? 16 : 2 * td->max_data_items;
809 td->data_items = (gpointer*)g_realloc (td->data_items, td->max_data_items * sizeof(td->data_items [0]));
811 index = td->n_data_items;
812 td->data_items [index] = ptr;
813 ++td->n_data_items;
814 g_hash_table_insert (td->data_hash, ptr, GUINT_TO_POINTER (index + 1));
815 return index;
818 static gboolean
819 jit_call_supported (MonoMethod *method, MonoMethodSignature *sig)
821 GSList *l;
823 if (sig->param_count > 6)
824 return FALSE;
825 if (sig->pinvoke)
826 return FALSE;
827 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
828 return FALSE;
829 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
830 return FALSE;
831 if (method->is_inflated)
832 return FALSE;
833 if (method->string_ctor)
834 return FALSE;
836 if (mono_aot_only && m_class_get_image (method->klass)->aot_module && !(method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) {
837 ERROR_DECL (error);
838 gpointer addr = mono_jit_compile_method_jit_only (method, error);
839 if (addr && mono_error_ok (error))
840 return TRUE;
843 for (l = mono_interp_jit_classes; l; l = l->next) {
844 const char *class_name = (const char*)l->data;
845 // FIXME: Namespaces
846 if (!strcmp (m_class_get_name (method->klass), class_name))
847 return TRUE;
850 //return TRUE;
851 return FALSE;
854 static int mono_class_get_magic_index (MonoClass *k)
856 if (mono_class_is_magic_int (k))
857 return !strcmp ("nint", m_class_get_name (k)) ? 0 : 1;
859 if (mono_class_is_magic_float (k))
860 return 2;
862 return -1;
865 static void
866 interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *target_method)
868 MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_method_access;
870 /* Inject code throwing MethodAccessException */
871 interp_add_ins (td, MINT_MONO_LDPTR);
872 td->last_ins->data [0] = get_data_item_index (td, method);
873 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
875 interp_add_ins (td, MINT_MONO_LDPTR);
876 td->last_ins->data [0] = get_data_item_index (td, target_method);
877 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
879 interp_add_ins (td, MINT_ICALL_PP_V);
880 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
882 td->sp -= 2;
886 * These are additional locals that can be allocated as we transform the code.
887 * They are allocated past the method locals so they are accessed in the same
888 * way, with an offset relative to the frame->locals.
890 static int
891 create_interp_local (TransformData *td, MonoType *type)
893 int align, size;
894 int offset = td->total_locals_size;
896 size = mono_type_size (type, &align);
897 offset = ALIGN_TO (offset, align);
899 td->total_locals_size = offset + size;
901 return offset;
904 static void
905 dump_mint_code (const guint16 *start, const guint16* end)
907 const guint16 *p = start;
908 while (p < end) {
909 char *ins = mono_interp_dis_mintop (start, p);
910 g_print ("%s\n", ins);
911 g_free (ins);
912 p = mono_interp_dis_mintop_len (p);
916 /* For debug use */
917 void
918 mono_interp_print_code (InterpMethod *imethod)
920 MonoJitInfo *jinfo = imethod->jinfo;
921 const guint16 *start;
923 if (!jinfo)
924 return;
926 char *name = mono_method_full_name (imethod->method, 1);
927 g_print ("Method : %s\n", name);
928 g_free (name);
930 start = (guint16*) jinfo->code_start;
931 dump_mint_code (start, start + jinfo->code_size);
935 static MonoMethodHeader*
936 interp_method_get_header (MonoMethod* method, MonoError *error)
938 /* An explanation: mono_method_get_header_internal returns an error if
939 * called on a method with no body (e.g. an abstract method, or an
940 * icall). We don't want that.
942 if (mono_method_has_no_body (method))
943 return NULL;
944 else
945 return mono_method_get_header_internal (method, error);
948 /* stores top of stack as local and pushes address of it on stack */
949 static void
950 emit_store_value_as_local (TransformData *td, MonoType *src)
952 int size = mini_magic_type_size (NULL, src);
953 int local_offset = create_interp_local (td, mini_native_type_replace_type (src));
955 store_local_general (td, local_offset, src);
957 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
958 interp_add_ins (td, MINT_LDLOC_VT);
959 td->last_ins->data [0] = local_offset;
960 WRITE32_INS (td->last_ins, 1, &size);
962 PUSH_VT (td, size);
963 PUSH_TYPE (td, STACK_TYPE_VT, NULL);
966 // Returns whether we can optimize away the instructions starting at start.
967 // If any instructions are part of a new basic block, we can't remove them.
968 static gboolean
969 interp_is_bb_start (TransformData *td, InterpInst *start, InterpInst *end)
971 InterpInst *ins = start;
972 while (ins != end) {
973 if (ins->il_offset != -1) {
974 if (td->is_bb_start [ins->il_offset])
975 return TRUE;
977 ins = ins->next;
979 return FALSE;
982 static gboolean
983 interp_ins_is_ldc (InterpInst *ins)
985 return ins->opcode >= MINT_LDC_I4_M1 && ins->opcode <= MINT_LDC_I8;
988 static gint32
989 interp_ldc_i4_get_const (InterpInst *ins)
991 switch (ins->opcode) {
992 case MINT_LDC_I4_M1: return -1;
993 case MINT_LDC_I4_0: return 0;
994 case MINT_LDC_I4_1: return 1;
995 case MINT_LDC_I4_2: return 2;
996 case MINT_LDC_I4_3: return 3;
997 case MINT_LDC_I4_4: return 4;
998 case MINT_LDC_I4_5: return 5;
999 case MINT_LDC_I4_6: return 6;
1000 case MINT_LDC_I4_7: return 7;
1001 case MINT_LDC_I4_8: return 8;
1002 case MINT_LDC_I4_S: return (gint32)(gint8)ins->data [0];
1003 case MINT_LDC_I4: return READ32 (&ins->data [0]);
1004 default:
1005 g_assert_not_reached ();
1009 static int
1010 interp_get_ldind_for_mt (int mt)
1012 switch (mt) {
1013 case MINT_TYPE_I1: return MINT_LDIND_I1_CHECK;
1014 case MINT_TYPE_U1: return MINT_LDIND_U1_CHECK;
1015 case MINT_TYPE_I2: return MINT_LDIND_I2_CHECK;
1016 case MINT_TYPE_U2: return MINT_LDIND_U2_CHECK;
1017 case MINT_TYPE_I4: return MINT_LDIND_I4_CHECK;
1018 case MINT_TYPE_I8: return MINT_LDIND_I8_CHECK;
1019 case MINT_TYPE_R4: return MINT_LDIND_R4_CHECK;
1020 case MINT_TYPE_R8: return MINT_LDIND_R8_CHECK;
1021 case MINT_TYPE_O: return MINT_LDIND_REF;
1022 default:
1023 g_assert_not_reached ();
1025 return -1;
1028 /* Return TRUE if call transformation is finished */
1029 static gboolean
1030 interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClass *constrained_class, MonoMethodSignature *csignature, gboolean readonly, int *op)
1032 const char *tm = target_method->name;
1033 int i;
1034 int type_index = mono_class_get_magic_index (target_method->klass);
1035 gboolean in_corlib = m_class_get_image (target_method->klass) == mono_defaults.corlib;
1036 const char *klass_name_space = m_class_get_name_space (target_method->klass);
1037 const char *klass_name = m_class_get_name (target_method->klass);
1039 if (target_method->klass == mono_defaults.string_class) {
1040 if (tm [0] == 'g') {
1041 if (strcmp (tm, "get_Chars") == 0)
1042 *op = MINT_GETCHR;
1043 else if (strcmp (tm, "get_Length") == 0)
1044 *op = MINT_STRLEN;
1046 } else if (type_index >= 0) {
1047 MonoClass *magic_class = target_method->klass;
1049 const int mt = mint_type (m_class_get_byval_arg (magic_class));
1050 if (!strcmp (".ctor", tm)) {
1051 MonoType *arg = csignature->params [0];
1052 /* depending on SIZEOF_VOID_P and the type of the value passed to the .ctor we either have to CONV it, or do nothing */
1053 int arg_size = mini_magic_type_size (NULL, arg);
1055 if (arg_size > SIZEOF_VOID_P) { // 8 -> 4
1056 switch (type_index) {
1057 case 0: case 1:
1058 interp_add_ins (td, MINT_CONV_I4_I8);
1059 break;
1060 case 2:
1061 interp_add_ins (td, MINT_CONV_R4_R8);
1062 break;
1066 if (arg_size < SIZEOF_VOID_P) { // 4 -> 8
1067 switch (type_index) {
1068 case 0:
1069 interp_add_ins (td, MINT_CONV_I8_I4);
1070 break;
1071 case 1:
1072 interp_add_ins (td, MINT_CONV_I8_U4);
1073 break;
1074 case 2:
1075 interp_add_ins (td, MINT_CONV_R8_R4);
1076 break;
1080 switch (type_index) {
1081 case 0: case 1:
1082 #if SIZEOF_VOID_P == 4
1083 interp_add_ins (td, MINT_STIND_I4);
1084 #else
1085 interp_add_ins (td, MINT_STIND_I8);
1086 #endif
1087 break;
1088 case 2:
1089 #if SIZEOF_VOID_P == 4
1090 interp_add_ins (td, MINT_STIND_R4);
1091 #else
1092 interp_add_ins (td, MINT_STIND_R8);
1093 #endif
1094 break;
1097 td->sp -= 2;
1098 td->ip += 5;
1099 return TRUE;
1100 } else if (!strcmp ("op_Implicit", tm ) || !strcmp ("op_Explicit", tm)) {
1101 MonoType *src = csignature->params [0];
1102 MonoType *dst = csignature->ret;
1103 MonoClass *src_klass = mono_class_from_mono_type_internal (src);
1104 int src_size = mini_magic_type_size (NULL, src);
1105 int dst_size = mini_magic_type_size (NULL, dst);
1107 gboolean store_value_as_local = FALSE;
1109 switch (type_index) {
1110 case 0: case 1:
1111 if (!mini_magic_is_int_type (src) || !mini_magic_is_int_type (dst)) {
1112 if (mini_magic_is_int_type (src))
1113 store_value_as_local = TRUE;
1114 else if (mono_class_is_magic_float (src_klass))
1115 store_value_as_local = TRUE;
1116 else
1117 return FALSE;
1119 break;
1120 case 2:
1121 if (!mini_magic_is_float_type (src) || !mini_magic_is_float_type (dst)) {
1122 if (mini_magic_is_float_type (src))
1123 store_value_as_local = TRUE;
1124 else if (mono_class_is_magic_int (src_klass))
1125 store_value_as_local = TRUE;
1126 else
1127 return FALSE;
1129 break;
1132 if (store_value_as_local) {
1133 emit_store_value_as_local (td, src);
1135 /* emit call to managed conversion method */
1136 return FALSE;
1139 if (src_size > dst_size) { // 8 -> 4
1140 switch (type_index) {
1141 case 0: case 1:
1142 interp_add_ins (td, MINT_CONV_I4_I8);
1143 break;
1144 case 2:
1145 interp_add_ins (td, MINT_CONV_R4_R8);
1146 break;
1150 if (src_size < dst_size) { // 4 -> 8
1151 switch (type_index) {
1152 case 0:
1153 interp_add_ins (td, MINT_CONV_I8_I4);
1154 break;
1155 case 1:
1156 interp_add_ins (td, MINT_CONV_I8_U4);
1157 break;
1158 case 2:
1159 interp_add_ins (td, MINT_CONV_R8_R4);
1160 break;
1164 SET_TYPE (td->sp - 1, stack_type [mint_type (dst)], mono_class_from_mono_type_internal (dst));
1165 td->ip += 5;
1166 return TRUE;
1167 } else if (!strcmp ("op_Increment", tm)) {
1168 g_assert (type_index != 2); // no nfloat
1169 #if SIZEOF_VOID_P == 8
1170 interp_add_ins (td, MINT_ADD1_I8);
1171 #else
1172 interp_add_ins (td, MINT_ADD1_I4);
1173 #endif
1174 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1175 td->ip += 5;
1176 return TRUE;
1177 } else if (!strcmp ("op_Decrement", tm)) {
1178 g_assert (type_index != 2); // no nfloat
1179 #if SIZEOF_VOID_P == 8
1180 interp_add_ins (td, MINT_SUB1_I8);
1181 #else
1182 interp_add_ins (td, MINT_SUB1_I4);
1183 #endif
1184 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1185 td->ip += 5;
1186 return TRUE;
1187 } else if (!strcmp ("CompareTo", tm) || !strcmp ("Equals", tm)) {
1188 MonoType *arg = csignature->params [0];
1190 /* on 'System.n*::{CompareTo,Equals} (System.n*)' variant we need to push managed
1191 * pointer instead of value */
1192 if (arg->type == MONO_TYPE_VALUETYPE)
1193 emit_store_value_as_local (td, arg);
1195 /* emit call to managed conversion method */
1196 return FALSE;
1197 } else if (!strcmp (".cctor", tm)) {
1198 /* white list */
1199 return FALSE;
1200 } else if (!strcmp ("Parse", tm)) {
1201 /* white list */
1202 return FALSE;
1203 } else if (!strcmp ("ToString", tm)) {
1204 /* white list */
1205 return FALSE;
1206 } else if (!strcmp ("GetHashCode", tm)) {
1207 /* white list */
1208 return FALSE;
1209 } else if (!strcmp ("IsNaN", tm) || !strcmp ("IsInfinity", tm) || !strcmp ("IsNegativeInfinity", tm) || !strcmp ("IsPositiveInfinity", tm)) {
1210 g_assert (type_index == 2); // nfloat only
1211 /* white list */
1212 return FALSE;
1215 for (i = 0; i < sizeof (int_unnop) / sizeof (MagicIntrinsic); ++i) {
1216 if (!strcmp (int_unnop [i].op_name, tm)) {
1217 interp_add_ins (td, int_unnop [i].insn [type_index]);
1218 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1219 td->ip += 5;
1220 return TRUE;
1224 for (i = 0; i < sizeof (int_binop) / sizeof (MagicIntrinsic); ++i) {
1225 if (!strcmp (int_binop [i].op_name, tm)) {
1226 interp_add_ins (td, int_binop [i].insn [type_index]);
1227 td->sp -= 1;
1228 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1229 td->ip += 5;
1230 return TRUE;
1234 for (i = 0; i < sizeof (int_cmpop) / sizeof (MagicIntrinsic); ++i) {
1235 if (!strcmp (int_cmpop [i].op_name, tm)) {
1236 MonoClass *k = mono_defaults.boolean_class;
1237 interp_add_ins (td, int_cmpop [i].insn [type_index]);
1238 td->sp -= 1;
1239 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1240 td->ip += 5;
1241 return TRUE;
1245 g_error ("TODO: interp_transform_call %s:%s", m_class_get_name (target_method->klass), tm);
1246 } else if (mono_class_is_subclass_of_internal (target_method->klass, mono_defaults.array_class, FALSE)) {
1247 if (!strcmp (tm, "get_Rank")) {
1248 *op = MINT_ARRAY_RANK;
1249 } else if (!strcmp (tm, "get_Length")) {
1250 *op = MINT_LDLEN;
1251 } else if (!strcmp (tm, "Address")) {
1252 *op = readonly ? MINT_LDELEMA : MINT_LDELEMA_TC;
1253 } else if (!strcmp (tm, "UnsafeMov") || !strcmp (tm, "UnsafeLoad") || !strcmp (tm, "Set") || !strcmp (tm, "Get")) {
1254 *op = MINT_CALLRUN;
1255 } else if (!strcmp (tm, "UnsafeStore")) {
1256 g_error ("TODO ArrayClass::UnsafeStore");
1258 } else if (in_corlib &&
1259 !strcmp (klass_name_space, "System.Diagnostics") &&
1260 !strcmp (klass_name, "Debugger")) {
1261 if (!strcmp (tm, "Break") && csignature->param_count == 0) {
1262 if (mini_should_insert_breakpoint (td->method))
1263 *op = MINT_BREAK;
1265 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "ByReference`1")) {
1266 g_assert (!strcmp (tm, "get_Value"));
1267 *op = MINT_INTRINS_BYREFERENCE_GET_VALUE;
1268 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "Math") && csignature->param_count == 1 && csignature->params [0]->type == MONO_TYPE_R8) {
1269 if (tm [0] == 'A') {
1270 if (strcmp (tm, "Abs") == 0 && csignature->params [0]->type == MONO_TYPE_R8) {
1271 *op = MINT_ABS;
1272 } else if (strcmp (tm, "Asin") == 0){
1273 *op = MINT_ASIN;
1274 } else if (strcmp (tm, "Asinh") == 0){
1275 *op = MINT_ASINH;
1276 } else if (strcmp (tm, "Acos") == 0){
1277 *op = MINT_ACOS;
1278 } else if (strcmp (tm, "Acosh") == 0){
1279 *op = MINT_ACOSH;
1280 } else if (strcmp (tm, "Atan") == 0){
1281 *op = MINT_ATAN;
1282 } else if (strcmp (tm, "Atanh") == 0){
1283 *op = MINT_ATANH;
1285 } else if (tm [0] == 'C') {
1286 if (strcmp (tm, "Cos") == 0) {
1287 *op = MINT_COS;
1288 } else if (strcmp (tm, "Cbrt") == 0){
1289 *op = MINT_CBRT;
1290 } else if (strcmp (tm, "Cosh") == 0){
1291 *op = MINT_COSH;
1293 } else if (tm [0] == 'S') {
1294 if (strcmp (tm, "Sin") == 0) {
1295 *op = MINT_SIN;
1296 } else if (strcmp (tm, "Sqrt") == 0) {
1297 *op = MINT_SQRT;
1298 } else if (strcmp (tm, "Sinh") == 0){
1299 *op = MINT_SINH;
1301 } else if (tm [0] == 'T') {
1302 if (strcmp (tm, "Tan") == 0) {
1303 *op = MINT_TAN;
1304 } else if (strcmp (tm, "Tanh") == 0){
1305 *op = MINT_TANH;
1308 } else if (in_corlib && !strcmp (klass_name_space, "System") && (!strcmp (klass_name, "Span`1") || !strcmp (klass_name, "ReadOnlySpan`1"))) {
1309 if (!strcmp (tm, "get_Item")) {
1310 MonoGenericClass *gclass = mono_class_get_generic_class (target_method->klass);
1311 MonoClass *param_class = mono_class_from_mono_type_internal (gclass->context.class_inst->type_argv [0]);
1313 if (!mini_is_gsharedvt_variable_klass (param_class)) {
1314 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1315 g_assert (length_field);
1316 int offset_length = length_field->offset - sizeof (MonoObject);
1318 MonoClassField *ptr_field = mono_class_get_field_from_name_full (target_method->klass, "_pointer", NULL);
1319 g_assert (ptr_field);
1320 int offset_pointer = ptr_field->offset - sizeof (MonoObject);
1322 int size = mono_class_array_element_size (param_class);
1323 interp_add_ins (td, MINT_GETITEM_SPAN);
1324 td->last_ins->data [0] = size;
1325 td->last_ins->data [1] = offset_length;
1326 td->last_ins->data [2] = offset_pointer;
1328 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1329 td->sp -= 1;
1330 td->ip += 5;
1331 return TRUE;
1333 } else if (!strcmp (tm, "get_Length")) {
1334 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1335 g_assert (length_field);
1336 int offset_length = length_field->offset - sizeof (MonoObject);
1337 interp_add_ins (td, MINT_LDLEN_SPAN);
1338 td->last_ins->data [0] = offset_length;
1339 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1340 td->ip += 5;
1341 return TRUE;
1343 } else if (in_corlib && !strcmp (klass_name_space, "Internal.Runtime.CompilerServices") && !strcmp (klass_name, "Unsafe")) {
1344 #ifdef ENABLE_NETCORE
1345 if (!strcmp (tm, "AddByteOffset"))
1346 *op = MINT_INTRINS_UNSAFE_ADD_BYTE_OFFSET;
1347 else if (!strcmp (tm, "ByteOffset"))
1348 *op = MINT_INTRINS_UNSAFE_BYTE_OFFSET;
1349 else if (!strcmp (tm, "As") || !strcmp (tm, "AsRef"))
1350 *op = MINT_NOP;
1351 else if (!strcmp (tm, "AsPointer")) {
1352 /* NOP */
1353 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1354 td->ip += 5;
1355 return TRUE;
1356 } else if (!strcmp (tm, "IsAddressLessThan")) {
1357 MonoGenericContext *ctx = mono_method_get_context (target_method);
1358 g_assert (ctx);
1359 g_assert (ctx->method_inst);
1360 g_assert (ctx->method_inst->type_argc == 1);
1362 MonoClass *k = mono_defaults.boolean_class;
1363 interp_add_ins (td, MINT_CLT_UN_P);
1364 td->sp -= 1;
1365 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1366 td->ip += 5;
1367 return TRUE;
1368 } else if (!strcmp (tm, "SizeOf")) {
1369 MonoGenericContext *ctx = mono_method_get_context (target_method);
1370 g_assert (ctx);
1371 g_assert (ctx->method_inst);
1372 g_assert (ctx->method_inst->type_argc == 1);
1373 MonoType *t = ctx->method_inst->type_argv [0];
1374 int align;
1375 int esize = mono_type_size (t, &align);
1376 interp_add_ins (td, MINT_LDC_I4);
1377 WRITE32_INS (td->last_ins, 0, &esize);
1378 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1379 td->ip += 5;
1380 return TRUE;
1381 } else if (!strcmp (tm, "AreSame")) {
1382 *op = MINT_CEQ_P;
1384 #endif
1385 } else if (in_corlib && !strcmp (klass_name_space, "System.Runtime.CompilerServices") && !strcmp (klass_name, "RuntimeHelpers")) {
1386 #ifdef ENABLE_NETCORE
1387 if (!strcmp (tm, "IsBitwiseEquatable")) {
1388 g_assert (csignature->param_count == 0);
1389 MonoGenericContext *ctx = mono_method_get_context (target_method);
1390 g_assert (ctx);
1391 g_assert (ctx->method_inst);
1392 g_assert (ctx->method_inst->type_argc == 1);
1393 MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]);
1395 if (MONO_TYPE_IS_PRIMITIVE (t) && t->type != MONO_TYPE_R4 && t->type != MONO_TYPE_R8)
1396 *op = MINT_LDC_I4_1;
1397 else
1398 *op = MINT_LDC_I4_0;
1399 } else if (!strcmp (tm, "ObjectHasComponentSize")) {
1400 *op = MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE;
1402 #endif
1403 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "RuntimeMethodHandle") && !strcmp (tm, "GetFunctionPointer") && csignature->param_count == 1) {
1404 // We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter
1405 *op = MINT_LDFTN_DYNAMIC;
1406 } else if (in_corlib && target_method->klass == mono_defaults.object_class) {
1407 if (!strcmp (tm, "InternalGetHashCode"))
1408 *op = MINT_INTRINS_GET_HASHCODE;
1409 #ifdef DISABLE_REMOTING
1410 else if (!strcmp (tm, "GetType"))
1411 *op = MINT_INTRINS_GET_TYPE;
1412 #endif
1413 #ifdef ENABLE_NETCORE
1414 else if (!strcmp (tm, "GetRawData")) {
1415 #if SIZEOF_VOID_P == 8
1416 interp_add_ins (td, MINT_LDC_I8_S);
1417 #else
1418 interp_add_ins (td, MINT_LDC_I4_S);
1419 #endif
1420 td->last_ins->data [0] = (gint16) MONO_ABI_SIZEOF (MonoObject);
1422 interp_add_ins (td, MINT_ADD_P);
1423 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1425 td->ip += 5;
1426 return TRUE;
1428 #endif
1429 } else if (in_corlib && target_method->klass == mono_defaults.enum_class && !strcmp (tm, "HasFlag")) {
1430 gboolean intrinsify = FALSE;
1431 MonoClass *base_klass = NULL;
1432 if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1433 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1434 td->last_ins->prev->prev && td->last_ins->prev->prev->opcode == MINT_BOX &&
1435 td->sp [-2].klass == td->sp [-1].klass &&
1436 !interp_is_bb_start (td, td->last_ins->prev->prev, NULL) &&
1437 !td->is_bb_start [td->in_start - td->il_code]) {
1438 // csc pattern : box, ldc, box, call HasFlag
1439 g_assert (m_class_is_enumtype (td->sp [-2].klass));
1440 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (td->sp [-2].klass));
1441 base_klass = mono_class_from_mono_type_internal (base_type);
1443 // Remove the boxing of valuetypes
1444 interp_remove_ins (td, td->last_ins->prev->prev);
1445 interp_remove_ins (td, td->last_ins);
1447 intrinsify = TRUE;
1448 } else if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1449 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1450 constrained_class && td->sp [-1].klass == constrained_class &&
1451 !interp_is_bb_start (td, td->last_ins->prev, NULL) &&
1452 !td->is_bb_start [td->in_start - td->il_code]) {
1453 // mcs pattern : ldc, box, constrained Enum, call HasFlag
1454 g_assert (m_class_is_enumtype (constrained_class));
1455 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (constrained_class));
1456 base_klass = mono_class_from_mono_type_internal (base_type);
1457 int mt = mint_type (m_class_get_byval_arg (base_klass));
1459 // Remove boxing and load the value of this
1460 interp_remove_ins (td, td->last_ins);
1461 interp_insert_ins (td, td->last_ins->prev, interp_get_ldind_for_mt (mt));
1463 intrinsify = TRUE;
1465 if (intrinsify) {
1466 interp_add_ins (td, MINT_INTRINS_ENUM_HASFLAG);
1467 td->last_ins->data [0] = get_data_item_index (td, base_klass);
1468 td->sp -= 2;
1469 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1470 td->ip += 5;
1471 return TRUE;
1473 } else if (in_corlib && !strcmp (klass_name_space, "System.Threading") && !strcmp (klass_name, "Interlocked")) {
1474 #if ENABLE_NETCORE
1475 if (!strcmp (tm, "MemoryBarrier") && csignature->param_count == 0)
1476 *op = MINT_MONO_MEMORY_BARRIER;
1477 #endif
1480 return FALSE;
1483 static MonoMethod*
1484 interp_transform_internal_calls (MonoMethod *method, MonoMethod *target_method, MonoMethodSignature *csignature, gboolean is_virtual)
1486 if (method->wrapper_type == MONO_WRAPPER_NONE && target_method != NULL) {
1487 if (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
1488 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1489 if (!is_virtual && target_method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
1490 target_method = mono_marshal_get_synchronized_wrapper (target_method);
1492 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)
1493 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1495 return target_method;
1498 static gboolean
1499 interp_type_as_ptr (MonoType *tp)
1501 if (MONO_TYPE_IS_POINTER (tp))
1502 return TRUE;
1503 if (MONO_TYPE_IS_REFERENCE (tp))
1504 return TRUE;
1505 if ((tp)->type == MONO_TYPE_I4)
1506 return TRUE;
1507 #if SIZEOF_VOID_P == 8
1508 if ((tp)->type == MONO_TYPE_I8)
1509 return TRUE;
1510 #endif
1511 if ((tp)->type == MONO_TYPE_BOOLEAN)
1512 return TRUE;
1513 if ((tp)->type == MONO_TYPE_CHAR)
1514 return TRUE;
1515 if ((tp)->type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (tp->data.klass))
1516 return TRUE;
1517 return FALSE;
1520 #define INTERP_TYPE_AS_PTR(tp) interp_type_as_ptr (tp)
1522 static int
1523 interp_icall_op_for_sig (MonoMethodSignature *sig)
1525 int op = -1;
1526 switch (sig->param_count) {
1527 case 0:
1528 if (MONO_TYPE_IS_VOID (sig->ret))
1529 op = MINT_ICALL_V_V;
1530 else if (INTERP_TYPE_AS_PTR (sig->ret))
1531 op = MINT_ICALL_V_P;
1532 break;
1533 case 1:
1534 if (MONO_TYPE_IS_VOID (sig->ret)) {
1535 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1536 op = MINT_ICALL_P_V;
1537 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1538 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1539 op = MINT_ICALL_P_P;
1541 break;
1542 case 2:
1543 if (MONO_TYPE_IS_VOID (sig->ret)) {
1544 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1545 INTERP_TYPE_AS_PTR (sig->params [1]))
1546 op = MINT_ICALL_PP_V;
1547 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1548 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1549 INTERP_TYPE_AS_PTR (sig->params [1]))
1550 op = MINT_ICALL_PP_P;
1552 break;
1553 case 3:
1554 if (MONO_TYPE_IS_VOID (sig->ret)) {
1555 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1556 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1557 INTERP_TYPE_AS_PTR (sig->params [2]))
1558 op = MINT_ICALL_PPP_V;
1559 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1560 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1561 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1562 INTERP_TYPE_AS_PTR (sig->params [2]))
1563 op = MINT_ICALL_PPP_P;
1565 break;
1566 case 4:
1567 if (MONO_TYPE_IS_VOID (sig->ret)) {
1568 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1569 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1570 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1571 INTERP_TYPE_AS_PTR (sig->params [3]))
1572 op = MINT_ICALL_PPPP_V;
1573 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1574 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1575 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1576 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1577 INTERP_TYPE_AS_PTR (sig->params [3]))
1578 op = MINT_ICALL_PPPP_P;
1580 break;
1581 case 5:
1582 if (MONO_TYPE_IS_VOID (sig->ret)) {
1583 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1584 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1585 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1586 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1587 INTERP_TYPE_AS_PTR (sig->params [4]))
1588 op = MINT_ICALL_PPPPP_V;
1589 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1590 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1591 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1592 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1593 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1594 INTERP_TYPE_AS_PTR (sig->params [4]))
1595 op = MINT_ICALL_PPPPP_P;
1597 break;
1598 case 6:
1599 if (MONO_TYPE_IS_VOID (sig->ret)) {
1600 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1601 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1602 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1603 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1604 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1605 INTERP_TYPE_AS_PTR (sig->params [5]))
1606 op = MINT_ICALL_PPPPPP_V;
1607 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1608 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1609 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1610 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1611 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1612 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1613 INTERP_TYPE_AS_PTR (sig->params [5]))
1614 op = MINT_ICALL_PPPPPP_P;
1616 break;
1618 return op;
1621 #define INLINE_LENGTH_LIMIT 20
1623 static gboolean
1624 interp_method_check_inlining (TransformData *td, MonoMethod *method)
1626 MonoMethodHeaderSummary header;
1628 if (td->method == method)
1629 return FALSE;
1631 if (!mono_method_get_header_summary (method, &header))
1632 return FALSE;
1634 /*runtime, icall and pinvoke are checked by summary call*/
1635 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
1636 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
1637 (mono_class_is_marshalbyref (method->klass)) ||
1638 header.has_clauses)
1639 return FALSE;
1641 if (header.code_size >= INLINE_LENGTH_LIMIT)
1642 return FALSE;
1644 if (mono_class_needs_cctor_run (method->klass, NULL)) {
1645 MonoVTable *vtable;
1646 ERROR_DECL (error);
1647 if (!m_class_get_runtime_info (method->klass))
1648 /* No vtable created yet */
1649 return FALSE;
1650 vtable = mono_class_vtable_checked (td->rtm->domain, method->klass, error);
1651 if (!is_ok (error)) {
1652 mono_error_cleanup (error);
1653 return FALSE;
1655 if (!vtable->initialized)
1656 return FALSE;
1659 /* We currently access at runtime the wrapper data */
1660 if (method->wrapper_type != MONO_WRAPPER_NONE)
1661 return FALSE;
1663 /* Our usage of `emit_store_value_as_local ()` for nint, nuint and nfloat
1664 * is kinda hacky, and doesn't work with the inliner */
1665 if (mono_class_get_magic_index (method->klass) >= 0)
1666 return FALSE;
1668 return TRUE;
1671 static gboolean
1672 interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHeader *header, MonoError *error)
1674 const unsigned char *prev_ip, *prev_il_code, *prev_in_start;
1675 StackInfo **prev_stack_state;
1676 int *prev_stack_height, *prev_vt_stack_size, *prev_clause_indexes, *prev_in_offsets;
1677 guint8 *prev_is_bb_start;
1678 gboolean ret;
1679 unsigned int prev_max_stack_height, prev_max_vt_sp, prev_total_locals_size;
1680 int prev_n_data_items;
1681 int i, prev_vt_sp;
1682 int prev_sp_offset;
1683 MonoGenericContext *generic_context = NULL;
1684 StackInfo *prev_param_area;
1685 MonoMethod *prev_inlined_method;
1686 MonoMethodSignature *csignature = mono_method_signature_internal (target_method);
1687 int nargs = csignature->param_count + !!csignature->hasthis;
1688 InterpInst *prev_last_ins;
1690 if (csignature->is_inflated)
1691 generic_context = mono_method_get_context (target_method);
1692 else {
1693 MonoGenericContainer *generic_container = mono_method_get_generic_container (target_method);
1694 if (generic_container)
1695 generic_context = &generic_container->context;
1698 prev_ip = td->ip;
1699 prev_il_code = td->il_code;
1700 prev_in_start = td->in_start;
1701 prev_sp_offset = td->sp - td->stack;
1702 prev_vt_sp = td->vt_sp;
1703 prev_stack_state = td->stack_state;
1704 prev_stack_height = td->stack_height;
1705 prev_vt_stack_size = td->vt_stack_size;
1706 prev_clause_indexes = td->clause_indexes;
1707 prev_inlined_method = td->inlined_method;
1708 prev_last_ins = td->last_ins;
1709 td->inlined_method = target_method;
1711 prev_max_stack_height = td->max_stack_height;
1712 prev_max_vt_sp = td->max_vt_sp;
1713 prev_total_locals_size = td->total_locals_size;
1715 prev_n_data_items = td->n_data_items;
1716 prev_in_offsets = td->in_offsets;
1717 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
1719 prev_is_bb_start = td->is_bb_start;
1721 /* Inlining pops the arguments, restore the stack */
1722 prev_param_area = (StackInfo*)g_malloc (nargs * sizeof (StackInfo));
1723 memcpy (prev_param_area, &td->sp [-nargs], nargs * sizeof (StackInfo));
1725 if (td->verbose_level)
1726 g_print ("Inline start method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1727 ret = generate_code (td, target_method, header, generic_context, error);
1729 if (!ret) {
1730 if (td->verbose_level)
1731 g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1732 td->max_stack_height = prev_max_stack_height;
1733 td->max_vt_sp = prev_max_vt_sp;
1734 td->total_locals_size = prev_total_locals_size;
1737 /* Remove any newly added items */
1738 for (i = prev_n_data_items; i < td->n_data_items; i++) {
1739 g_hash_table_remove (td->data_hash, td->data_items [i]);
1741 td->n_data_items = prev_n_data_items;
1742 td->sp = td->stack + prev_sp_offset;
1743 memcpy (&td->sp [-nargs], prev_param_area, nargs * sizeof (StackInfo));
1744 td->vt_sp = prev_vt_sp;
1745 td->last_ins = prev_last_ins;
1746 if (td->last_ins)
1747 td->last_ins->next = NULL;
1748 UnlockedIncrement (&mono_interp_stats.inline_failures);
1749 } else {
1750 if (td->verbose_level)
1751 g_print ("Inline end method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1752 UnlockedIncrement (&mono_interp_stats.inlined_methods);
1753 // Make sure we have an IR instruction associated with the now removed IL CALL
1754 // FIXME This could be prettier. We might be able to make inlining saner now that
1755 // that we can easily tweak the instruction list.
1756 if (!prev_inlined_method) {
1757 if (prev_last_ins) {
1758 if (prev_last_ins->next)
1759 prev_last_ins->next->il_offset = prev_in_start - prev_il_code;
1760 } else if (td->first_ins) {
1761 td->first_ins->il_offset = prev_in_start - prev_il_code;
1766 td->ip = prev_ip;
1767 td->in_start = prev_in_start;
1768 td->il_code = prev_il_code;
1769 td->stack_state = prev_stack_state;
1770 td->stack_height = prev_stack_height;
1771 td->vt_stack_size = prev_vt_stack_size;
1772 td->clause_indexes = prev_clause_indexes;
1773 td->is_bb_start = prev_is_bb_start;
1774 td->inlined_method = prev_inlined_method;
1776 g_free (td->in_offsets);
1777 td->in_offsets = prev_in_offsets;
1779 g_free (prev_param_area);
1780 return ret;
1783 static void
1784 interp_constrained_box (TransformData *td, MonoDomain *domain, MonoClass *constrained_class, MonoMethodSignature *csignature, MonoError *error)
1786 int mt = mint_type (m_class_get_byval_arg (constrained_class));
1787 if (mono_class_is_nullable (constrained_class)) {
1788 g_assert (mt == MINT_TYPE_VT);
1789 interp_add_ins (td, MINT_BOX_NULLABLE);
1790 td->last_ins->data [0] = get_data_item_index (td, constrained_class);
1791 td->last_ins->data [1] = csignature->param_count | ((td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : BOX_NOT_CLEAR_VT_SP);
1792 } else {
1793 MonoVTable *vtable = mono_class_vtable_checked (domain, constrained_class, error);
1794 return_if_nok (error);
1796 if (mt == MINT_TYPE_VT) {
1797 interp_add_ins (td, MINT_BOX_VT);
1798 td->last_ins->data [0] = get_data_item_index (td, vtable);
1799 td->last_ins->data [1] = csignature->param_count | ((td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : BOX_NOT_CLEAR_VT_SP);
1800 } else {
1801 interp_add_ins (td, MINT_BOX);
1802 td->last_ins->data [0] = get_data_item_index (td, vtable);
1803 td->last_ins->data [1] = csignature->param_count;
1808 /* Return FALSE if error, including inline failure */
1809 static gboolean
1810 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)
1812 MonoImage *image = m_class_get_image (method->klass);
1813 MonoMethodSignature *csignature;
1814 int is_virtual = *td->ip == CEE_CALLVIRT;
1815 int calli = *td->ip == CEE_CALLI || *td->ip == CEE_MONO_CALLI_EXTRA_ARG;
1816 int i;
1817 guint32 vt_stack_used = 0;
1818 guint32 vt_res_size = 0;
1819 int op = -1;
1820 int native = 0;
1821 int is_void = 0;
1822 int need_null_check = is_virtual;
1824 guint32 token = read32 (td->ip + 1);
1826 if (target_method == NULL) {
1827 if (calli) {
1828 CHECK_STACK(td, 1);
1829 if (method->wrapper_type != MONO_WRAPPER_NONE)
1830 csignature = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
1831 else {
1832 csignature = mono_metadata_parse_signature_checked (image, token, error);
1833 return_val_if_nok (error, FALSE);
1836 if (generic_context) {
1837 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
1838 return_val_if_nok (error, FALSE);
1842 * The compiled interp entry wrapper is passed to runtime_invoke instead of
1843 * the InterpMethod pointer. FIXME
1845 native = csignature->pinvoke || method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE;
1846 --td->sp;
1848 target_method = NULL;
1849 } else {
1850 if (method->wrapper_type == MONO_WRAPPER_NONE) {
1851 target_method = mono_get_method_checked (image, token, NULL, generic_context, error);
1852 return_val_if_nok (error, FALSE);
1853 } else
1854 target_method = (MonoMethod *)mono_method_get_wrapper_data (method, token);
1855 csignature = mono_method_signature_internal (target_method);
1857 if (generic_context) {
1858 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
1859 return_val_if_nok (error, FALSE);
1860 target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, error);
1861 return_val_if_nok (error, FALSE);
1864 } else {
1865 csignature = mono_method_signature_internal (target_method);
1868 if (check_visibility && target_method && !mono_method_can_access_method (method, target_method))
1869 interp_generate_mae_throw (td, method, target_method);
1871 if (target_method && target_method->string_ctor) {
1872 /* Create the real signature */
1873 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (td->mempool, csignature);
1874 ctor_sig->ret = m_class_get_byval_arg (mono_defaults.string_class);
1876 csignature = ctor_sig;
1879 /* Intrinsics */
1880 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
1881 return TRUE;
1883 if (constrained_class) {
1884 if (m_class_is_enumtype (constrained_class) && !strcmp (target_method->name, "GetHashCode")) {
1885 /* Use the corresponding method from the base type to avoid boxing */
1886 MonoType *base_type = mono_class_enum_basetype_internal (constrained_class);
1887 g_assert (base_type);
1888 constrained_class = mono_class_from_mono_type_internal (base_type);
1889 target_method = mono_class_get_method_from_name_checked (constrained_class, target_method->name, 0, 0, error);
1890 mono_error_assert_ok (error);
1891 g_assert (target_method);
1895 if (constrained_class) {
1896 mono_class_setup_vtable (constrained_class);
1897 if (mono_class_has_failure (constrained_class)) {
1898 mono_error_set_for_class_failure (error, constrained_class);
1899 return FALSE;
1901 #if DEBUG_INTERP
1902 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);
1903 #endif
1904 target_method = mono_get_method_constrained_with_method (image, target_method, constrained_class, generic_context, error);
1905 #if DEBUG_INTERP
1906 g_print (" : %s::%s. %s (%p)\n", target_method->klass->name, target_method->name, mono_signature_full_name (target_method->signature), target_method);
1907 #endif
1908 /* Intrinsics: Try again, it could be that `mono_get_method_constrained_with_method` resolves to a method that we can substitute */
1909 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
1910 return TRUE;
1912 return_val_if_nok (error, FALSE);
1913 mono_class_setup_vtable (target_method->klass);
1915 if (!m_class_is_valuetype (constrained_class)) {
1916 /* managed pointer on the stack, we need to deref that puppy */
1917 interp_add_ins (td, MINT_LDIND_I);
1918 td->last_ins->data [0] = csignature->param_count;
1919 } 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) {
1920 if (target_method->klass == mono_defaults.enum_class && (td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
1921 /* managed pointer on the stack, we need to deref that puppy */
1922 /* Always load the entire stackval, to handle also the case where the enum has long storage */
1923 interp_add_ins (td, MINT_LDIND_I8);
1924 td->last_ins->data [0] = csignature->param_count;
1927 interp_constrained_box (td, domain, constrained_class, csignature, error);
1928 return_val_if_nok (error, FALSE);
1929 } else {
1930 if (target_method->klass != constrained_class) {
1932 * The type parameter is instantiated as a valuetype,
1933 * but that type doesn't override the method we're
1934 * calling, so we need to box `this'.
1936 if (target_method->klass == mono_defaults.enum_class && (td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
1937 /* managed pointer on the stack, we need to deref that puppy */
1938 /* Always load the entire stackval, to handle also the case where the enum has long storage */
1939 interp_add_ins (td, MINT_LDIND_I8);
1940 td->last_ins->data [0] = csignature->param_count;
1943 interp_constrained_box (td, domain, constrained_class, csignature, error);
1944 return_val_if_nok (error, FALSE);
1946 is_virtual = FALSE;
1950 if (target_method)
1951 mono_class_init_internal (target_method->klass);
1953 if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT))
1954 /* MS.NET seems to silently convert this to a callvirt */
1955 is_virtual = TRUE;
1957 if (is_virtual && target_method && (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
1958 (MONO_METHOD_IS_FINAL (target_method) &&
1959 target_method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
1960 !(mono_class_is_marshalbyref (target_method->klass))) {
1961 /* Not really virtual, just needs a null check */
1962 is_virtual = FALSE;
1963 need_null_check = TRUE;
1966 CHECK_STACK (td, csignature->param_count + csignature->hasthis);
1967 if (!td->gen_sdb_seq_points && !calli && op == -1 && (!is_virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) &&
1968 (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 &&
1969 (target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0 &&
1970 !(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING)) {
1971 (void)mono_class_vtable_checked (domain, target_method->klass, error);
1972 return_val_if_nok (error, FALSE);
1974 if (method == target_method && *(td->ip + 5) == CEE_RET && !(csignature->hasthis && m_class_is_valuetype (target_method->klass))) {
1975 if (td->inlined_method)
1976 return FALSE;
1978 if (td->verbose_level)
1979 g_print ("Optimize tail call of %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1981 for (i = csignature->param_count - 1 + !!csignature->hasthis; i >= 0; --i)
1982 store_arg (td, i);
1984 interp_add_ins (td, MINT_BR_S);
1985 // We are branching to the beginning of the method
1986 td->last_ins->data [0] = 0;
1987 if (!is_bb_start [td->ip + 5 - td->il_code])
1988 ++td->ip; /* gobble the CEE_RET if it isn't branched to */
1989 td->ip += 5;
1990 return TRUE;
1994 target_method = interp_transform_internal_calls (method, target_method, csignature, is_virtual);
1996 if (csignature->call_convention == MONO_CALL_VARARG) {
1997 csignature = mono_method_get_signature_checked (target_method, image, token, generic_context, error);
1998 int vararg_stack = 0;
2000 * For vararg calls, ArgIterator expects the signature and the varargs to be
2001 * stored in a linear memory. We allocate the necessary vt_stack space for
2002 * this. All varargs will be pushed to the vt_stack at call site.
2004 vararg_stack += sizeof (gpointer);
2005 for (i = csignature->sentinelpos; i < csignature->param_count; ++i) {
2006 int align, arg_size;
2007 arg_size = mono_type_stack_size (csignature->params [i], &align);
2008 vararg_stack += arg_size;
2010 vt_stack_used += ALIGN_TO (vararg_stack, MINT_VT_ALIGNMENT);
2011 PUSH_VT (td, vararg_stack);
2014 if (need_null_check) {
2015 interp_add_ins (td, MINT_CKNULL_N);
2016 td->last_ins->data [0] = csignature->param_count + 1;
2019 g_assert (csignature->call_convention != MONO_CALL_FASTCALL);
2020 if ((mono_interp_opt & INTERP_OPT_INLINE) && op == -1 && !is_virtual && target_method && interp_method_check_inlining (td, target_method)) {
2021 MonoMethodHeader *mheader = interp_method_get_header (target_method, error);
2022 return_val_if_nok (error, FALSE);
2024 if (interp_inline_method (td, target_method, mheader, error)) {
2025 td->ip += 5;
2026 return TRUE;
2030 /* Don't inline methods that do calls */
2031 if (op == -1 && td->inlined_method)
2032 return FALSE;
2034 td->sp -= csignature->param_count + !!csignature->hasthis;
2035 for (i = 0; i < csignature->param_count; ++i) {
2036 if (td->sp [i + !!csignature->hasthis].type == STACK_TYPE_VT) {
2037 gint32 size;
2038 MonoClass *klass = mono_class_from_mono_type_internal (csignature->params [i]);
2039 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2040 size = mono_class_native_size (klass, NULL);
2041 else
2042 size = mono_class_value_size (klass, NULL);
2043 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
2044 vt_stack_used += size;
2048 /* need to handle typedbyref ... */
2049 if (csignature->ret->type != MONO_TYPE_VOID) {
2050 int mt = mint_type(csignature->ret);
2051 MonoClass *klass = mono_class_from_mono_type_internal (csignature->ret);
2052 if (mt == MINT_TYPE_VT) {
2053 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2054 vt_res_size = mono_class_native_size (klass, NULL);
2055 else
2056 vt_res_size = mono_class_value_size (klass, NULL);
2057 if (mono_class_has_failure (klass)) {
2058 mono_error_set_for_class_failure (error, klass);
2059 return FALSE;
2061 PUSH_VT(td, vt_res_size);
2063 PUSH_TYPE(td, stack_type[mt], klass);
2064 } else
2065 is_void = TRUE;
2067 /* We need to convert delegate invoke to a indirect call on the interp_invoke_impl field */
2068 if (target_method && m_class_get_parent (target_method->klass) == mono_defaults.multicastdelegate_class) {
2069 const char *name = target_method->name;
2070 if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
2071 calli = TRUE;
2072 interp_add_ins (td, MINT_LD_DELEGATE_INVOKE_IMPL);
2073 td->last_ins->data [0] = csignature->param_count + 1;
2077 if (op >= 0) {
2078 interp_add_ins (td, op);
2080 if (op == MINT_LDLEN) {
2081 #ifdef MONO_BIG_ARRAYS
2082 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
2083 #else
2084 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
2085 #endif
2087 if (op == MINT_LDELEMA || op == MINT_LDELEMA_TC) {
2088 td->last_ins->data [0] = get_data_item_index (td, m_class_get_element_class (target_method->klass));
2089 td->last_ins->data [1] = 1 + m_class_get_rank (target_method->klass);
2092 if (op == MINT_CALLRUN) {
2093 td->last_ins->data [0] = get_data_item_index (td, target_method);
2094 td->last_ins->data [1] = get_data_item_index (td, mono_method_signature_internal (target_method));
2096 } else if (!calli && !is_virtual && jit_call_supported (target_method, csignature)) {
2097 interp_add_ins (td, MINT_JIT_CALL);
2098 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2099 mono_error_assert_ok (error);
2100 } else {
2101 #ifndef MONO_ARCH_HAS_NO_PROPER_MONOCTX
2102 /* Try using fast icall path for simple signatures */
2103 if (native && !method->dynamic)
2104 op = interp_icall_op_for_sig (csignature);
2105 #endif
2106 if (csignature->call_convention == MONO_CALL_VARARG)
2107 interp_add_ins (td, MINT_CALL_VARARG);
2108 else if (calli)
2109 interp_add_ins (td, native ? ((op != -1) ? MINT_CALLI_NAT_FAST : MINT_CALLI_NAT) : MINT_CALLI);
2110 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass))
2111 interp_add_ins (td, is_void ? MINT_VCALLVIRT_FAST : MINT_CALLVIRT_FAST);
2112 else if (is_virtual)
2113 interp_add_ins (td, is_void ? MINT_VCALLVIRT : MINT_CALLVIRT);
2114 else
2115 interp_add_ins (td, is_void ? MINT_VCALL : MINT_CALL);
2117 if (calli) {
2118 td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
2119 if (op != -1)
2120 td->last_ins->data [1] = op;
2121 } else {
2122 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2123 return_val_if_nok (error, FALSE);
2124 if (csignature->call_convention == MONO_CALL_VARARG)
2125 td->last_ins->data [1] = get_data_item_index (td, (void *)csignature);
2126 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass)) {
2127 /* FIXME Use fastpath also for MBRO. Asserts in mono_method_get_vtable_slot */
2128 if (mono_class_is_interface (target_method->klass))
2129 td->last_ins->data [1] = -2 * MONO_IMT_SIZE + mono_method_get_imt_slot (target_method);
2130 else
2131 td->last_ins->data [1] = mono_method_get_vtable_slot (target_method);
2135 td->ip += 5;
2136 if (vt_stack_used != 0 || vt_res_size != 0) {
2137 interp_add_ins (td, MINT_VTRESULT);
2138 td->last_ins->data [0] = vt_res_size;
2139 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
2140 td->vt_sp -= vt_stack_used;
2143 return TRUE;
2146 static MonoClassField *
2147 interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, MonoGenericContext *generic_context, MonoError *error)
2149 MonoClassField *field = NULL;
2150 if (method->wrapper_type != MONO_WRAPPER_NONE) {
2151 field = (MonoClassField *) mono_method_get_wrapper_data (method, token);
2152 *klass = field->parent;
2154 mono_class_setup_fields (field->parent);
2155 } else {
2156 field = mono_field_from_token_checked (m_class_get_image (method->klass), token, klass, generic_context, error);
2157 return_val_if_nok (error, NULL);
2160 if (!method->skip_visibility && !mono_method_can_access_field (method, field)) {
2161 char *method_fname = mono_method_full_name (method, TRUE);
2162 char *field_fname = mono_field_full_name (field);
2163 mono_error_set_generic_error (error, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
2164 g_free (method_fname);
2165 g_free (field_fname);
2166 return NULL;
2169 return field;
2172 static InterpBasicBlock*
2173 get_bb (TransformData *td, InterpBasicBlock *cbb, unsigned char *ip)
2175 int offset = ip - td->il_code;
2176 InterpBasicBlock *bb = td->offset_to_bb [offset];
2178 if (!bb) {
2179 bb = (InterpBasicBlock*)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock));
2180 bb->ip = ip;
2181 td->offset_to_bb [offset] = bb;
2183 td->basic_blocks = g_list_append_mempool (td->mempool, td->basic_blocks, bb);
2186 if (cbb)
2187 bb->preds = g_slist_prepend_mempool (td->mempool, bb->preds, cbb);
2188 return bb;
2192 * get_basic_blocks:
2194 * Compute the set of IL level basic blocks.
2196 static void
2197 get_basic_blocks (TransformData *td)
2199 guint8 *start = (guint8*)td->il_code;
2200 guint8 *end = (guint8*)td->il_code + td->code_size;
2201 guint8 *ip = start;
2202 unsigned char *target;
2203 int i;
2204 guint cli_addr;
2205 const MonoOpcode *opcode;
2206 InterpBasicBlock *cbb;
2208 td->offset_to_bb = (InterpBasicBlock**)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * (end - start + 1));
2209 td->entry_bb = cbb = get_bb (td, NULL, start);
2211 while (ip < end) {
2212 cli_addr = ip - start;
2213 td->offset_to_bb [cli_addr] = cbb;
2214 i = mono_opcode_value ((const guint8 **)&ip, end);
2215 opcode = &mono_opcodes [i];
2216 switch (opcode->argument) {
2217 case MonoInlineNone:
2218 ip++;
2219 break;
2220 case MonoInlineString:
2221 case MonoInlineType:
2222 case MonoInlineField:
2223 case MonoInlineMethod:
2224 case MonoInlineTok:
2225 case MonoInlineSig:
2226 case MonoShortInlineR:
2227 case MonoInlineI:
2228 ip += 5;
2229 break;
2230 case MonoInlineVar:
2231 ip += 3;
2232 break;
2233 case MonoShortInlineVar:
2234 case MonoShortInlineI:
2235 ip += 2;
2236 break;
2237 case MonoShortInlineBrTarget:
2238 target = start + cli_addr + 2 + (signed char)ip [1];
2239 get_bb (td, cbb, target);
2240 ip += 2;
2241 cbb = get_bb (td, cbb, ip);
2242 break;
2243 case MonoInlineBrTarget:
2244 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
2245 get_bb (td, cbb, target);
2246 ip += 5;
2247 cbb = get_bb (td, cbb, ip);
2248 break;
2249 case MonoInlineSwitch: {
2250 guint32 n = read32 (ip + 1);
2251 guint32 j;
2252 ip += 5;
2253 cli_addr += 5 + 4 * n;
2254 target = start + cli_addr;
2255 get_bb (td, cbb, target);
2257 for (j = 0; j < n; ++j) {
2258 target = start + cli_addr + (gint32)read32 (ip);
2259 get_bb (td, cbb, target);
2260 ip += 4;
2262 cbb = get_bb (td, cbb, ip);
2263 break;
2265 case MonoInlineR:
2266 case MonoInlineI8:
2267 ip += 9;
2268 break;
2269 default:
2270 g_assert_not_reached ();
2273 if (i == CEE_THROW)
2274 cbb = get_bb (td, NULL, ip);
2278 static void
2279 interp_save_debug_info (InterpMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers)
2281 MonoDebugMethodJitInfo *dinfo;
2282 int i;
2284 if (!mono_debug_enabled ())
2285 return;
2288 * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
2291 dinfo = g_new0 (MonoDebugMethodJitInfo, 1);
2292 dinfo->num_params = rtm->param_count;
2293 dinfo->params = g_new0 (MonoDebugVarInfo, dinfo->num_params);
2294 dinfo->num_locals = header->num_locals;
2295 dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals);
2296 dinfo->code_start = (guint8*)rtm->code;
2297 dinfo->code_size = td->new_code_end - td->new_code;
2298 dinfo->epilogue_begin = 0;
2299 dinfo->has_var_info = TRUE;
2300 dinfo->num_line_numbers = line_numbers->len;
2301 dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers);
2303 for (i = 0; i < dinfo->num_params; i++) {
2304 MonoDebugVarInfo *var = &dinfo->params [i];
2305 var->type = rtm->param_types [i];
2307 for (i = 0; i < dinfo->num_locals; i++) {
2308 MonoDebugVarInfo *var = &dinfo->locals [i];
2309 var->type = mono_metadata_type_dup (NULL, header->locals [i]);
2312 for (i = 0; i < dinfo->num_line_numbers; i++)
2313 dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i);
2314 mono_debug_add_method (rtm->method, dinfo, rtm->domain);
2316 mono_debug_free_method_jit_info (dinfo);
2319 /* Same as the code in seq-points.c */
2320 static void
2321 insert_pred_seq_point (SeqPoint *last_sp, SeqPoint *sp, GSList **next)
2323 GSList *l;
2324 int src_index = last_sp->next_offset;
2325 int dst_index = sp->next_offset;
2327 /* bb->in_bb might contain duplicates */
2328 for (l = next [src_index]; l; l = l->next)
2329 if (GPOINTER_TO_UINT (l->data) == dst_index)
2330 break;
2331 if (!l)
2332 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
2335 static void
2336 recursively_make_pred_seq_points (TransformData *td, InterpBasicBlock *bb)
2338 SeqPoint ** const MONO_SEQ_SEEN_LOOP = (SeqPoint**)GINT_TO_POINTER(-1);
2339 GSList *l;
2341 GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
2342 GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
2344 // Insert/remove sentinel into the memoize table to detect loops containing bb
2345 bb->pred_seq_points = MONO_SEQ_SEEN_LOOP;
2347 for (l = bb->preds; l; l = l->next) {
2348 InterpBasicBlock *in_bb = (InterpBasicBlock*)l->data;
2350 // This bb has the last seq point, append it and continue
2351 if (in_bb->last_seq_point != NULL) {
2352 predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
2353 continue;
2356 // We've looped or handled this before, exit early.
2357 // No last sequence points to find.
2358 if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
2359 continue;
2361 // Take sequence points from incoming basic blocks
2363 if (in_bb == td->entry_bb)
2364 continue;
2366 if (in_bb->pred_seq_points == NULL)
2367 recursively_make_pred_seq_points (td, in_bb);
2369 // Union sequence points with incoming bb's
2370 for (int i=0; i < in_bb->num_pred_seq_points; i++) {
2371 if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
2372 g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
2373 g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
2376 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
2379 g_hash_table_destroy (seen);
2381 if (predecessors->len != 0) {
2382 bb->pred_seq_points = (SeqPoint**)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint *) * predecessors->len);
2383 bb->num_pred_seq_points = predecessors->len;
2385 for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
2386 bb->pred_seq_points [newer] = (SeqPoint*)g_array_index (predecessors, gpointer, newer);
2390 g_array_free (predecessors, TRUE);
2393 static void
2394 collect_pred_seq_points (TransformData *td, InterpBasicBlock *bb, SeqPoint *seqp, GSList **next)
2396 // Doesn't have a last sequence point, must find from incoming basic blocks
2397 if (bb->pred_seq_points == NULL && bb != td->entry_bb)
2398 recursively_make_pred_seq_points (td, bb);
2400 for (int i = 0; i < bb->num_pred_seq_points; i++)
2401 insert_pred_seq_point (bb->pred_seq_points [i], seqp, next);
2403 return;
2406 static void
2407 save_seq_points (TransformData *td, MonoJitInfo *jinfo)
2409 GByteArray *array;
2410 int i, seq_info_size;
2411 MonoSeqPointInfo *info;
2412 GSList **next = NULL;
2413 GList *bblist;
2415 if (!td->gen_sdb_seq_points)
2416 return;
2419 * For each sequence point, compute the list of sequence points immediately
2420 * following it, this is needed to implement 'step over' in the debugger agent.
2421 * Similar to the code in mono_save_seq_point_info ().
2423 for (i = 0; i < td->seq_points->len; ++i) {
2424 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2426 /* Store the seq point index here temporarily */
2427 sp->next_offset = i;
2429 next = (GSList**)mono_mempool_alloc0 (td->mempool, sizeof (GList*) * td->seq_points->len);
2430 for (bblist = td->basic_blocks; bblist; bblist = bblist->next) {
2431 InterpBasicBlock *bb = (InterpBasicBlock*)bblist->data;
2433 GSList *bb_seq_points = g_slist_reverse (bb->seq_points);
2434 SeqPoint *last = NULL;
2435 for (GSList *l = bb_seq_points; l; l = l->next) {
2436 SeqPoint *sp = (SeqPoint*)l->data;
2438 if (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET)
2439 /* Used to implement method entry/exit events */
2440 continue;
2442 if (last != NULL) {
2443 /* Link with the previous seq point in the same bb */
2444 next [last->next_offset] = g_slist_append_mempool (td->mempool, next [last->next_offset], GINT_TO_POINTER (sp->next_offset));
2445 } else {
2446 /* Link with the last bb in the previous bblocks */
2447 collect_pred_seq_points (td, bb, sp, next);
2449 last = sp;
2453 /* Serialize the seq points into a byte array */
2454 array = g_byte_array_new ();
2455 SeqPoint zero_seq_point = {0};
2456 SeqPoint* last_seq_point = &zero_seq_point;
2457 for (i = 0; i < td->seq_points->len; ++i) {
2458 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2460 sp->next_offset = 0;
2461 if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next [i], TRUE))
2462 last_seq_point = sp;
2465 if (td->verbose_level) {
2466 g_print ("\nSEQ POINT MAP FOR %s: \n", td->method->name);
2468 for (i = 0; i < td->seq_points->len; ++i) {
2469 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2470 GSList *l;
2472 if (!next [i])
2473 continue;
2475 g_print ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
2476 for (l = next [i]; l; l = l->next) {
2477 int next_index = GPOINTER_TO_UINT (l->data);
2478 g_print (" IL0x%x", ((SeqPoint*)g_ptr_array_index (td->seq_points, next_index))->il_offset);
2480 g_print ("\n");
2484 info = mono_seq_point_info_new (array->len, TRUE, array->data, TRUE, &seq_info_size);
2485 mono_atomic_fetch_add_i32 (&mono_jit_stats.allocated_seq_points_size, seq_info_size);
2487 g_byte_array_free (array, TRUE);
2489 jinfo->seq_points = info;
2492 #define BARRIER_IF_VOLATILE(td) \
2493 do { \
2494 if (volatile_) { \
2495 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER); \
2496 volatile_ = FALSE; \
2498 } while (0)
2500 #define INLINE_FAILURE \
2501 do { \
2502 if (td->method != method) \
2503 goto exit; \
2504 } while (0)
2506 static void
2507 interp_method_compute_offsets (InterpMethod *imethod, MonoMethodSignature *signature, MonoMethodHeader *header)
2509 int i, offset, size, align;
2511 imethod->local_offsets = (guint32*)g_malloc (header->num_locals * sizeof(guint32));
2512 offset = 0;
2513 for (i = 0; i < header->num_locals; ++i) {
2514 size = mono_type_size (header->locals [i], &align);
2515 offset += align - 1;
2516 offset &= ~(align - 1);
2517 imethod->local_offsets [i] = offset;
2518 offset += size;
2520 offset = (offset + 7) & ~7;
2522 imethod->exvar_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32));
2523 for (i = 0; i < header->num_clauses; i++) {
2524 imethod->exvar_offsets [i] = offset;
2525 offset += sizeof (MonoObject*);
2527 offset = (offset + 7) & ~7;
2529 imethod->locals_size = offset;
2530 g_assert (imethod->locals_size < 65536);
2533 static MonoType*
2534 get_arg_type (MonoMethodSignature *signature, int arg_n)
2536 if (signature->hasthis && arg_n == 0)
2537 return mono_get_object_type ();
2538 return signature->params [arg_n - !!signature->hasthis];
2541 static void
2542 init_bb_start (TransformData *td, MonoMethodHeader *header)
2544 const unsigned char *ip, *end;
2545 const MonoOpcode *opcode;
2546 int offset, i, in, backwards;
2548 /* intern the strings in the method. */
2549 ip = header->code;
2550 end = ip + header->code_size;
2552 td->is_bb_start [0] = 1;
2553 while (ip < end) {
2554 in = *ip;
2555 if (in == 0xfe) {
2556 ip++;
2557 in = *ip + 256;
2559 else if (in == 0xf0) {
2560 ip++;
2561 in = *ip + MONO_CEE_MONO_ICALL;
2563 opcode = &mono_opcodes [in];
2564 switch (opcode->argument) {
2565 case MonoInlineNone:
2566 ++ip;
2567 break;
2568 case MonoInlineString:
2569 ip += 5;
2570 break;
2571 case MonoInlineType:
2572 ip += 5;
2573 break;
2574 case MonoInlineMethod:
2575 ip += 5;
2576 break;
2577 case MonoInlineField:
2578 case MonoInlineSig:
2579 case MonoInlineI:
2580 case MonoInlineTok:
2581 case MonoShortInlineR:
2582 ip += 5;
2583 break;
2584 case MonoInlineBrTarget:
2585 offset = read32 (ip + 1);
2586 ip += 5;
2587 backwards = offset < 0;
2588 offset += ip - header->code;
2589 g_assert (offset >= 0 && offset < header->code_size);
2590 td->is_bb_start [offset] |= backwards ? 2 : 1;
2591 break;
2592 case MonoShortInlineBrTarget:
2593 offset = ((gint8 *)ip) [1];
2594 ip += 2;
2595 backwards = offset < 0;
2596 offset += ip - header->code;
2597 g_assert (offset >= 0 && offset < header->code_size);
2598 td->is_bb_start [offset] |= backwards ? 2 : 1;
2599 break;
2600 case MonoInlineVar:
2601 ip += 3;
2602 break;
2603 case MonoShortInlineVar:
2604 case MonoShortInlineI:
2605 ip += 2;
2606 break;
2607 case MonoInlineSwitch: {
2608 guint32 n;
2609 const unsigned char *next_ip;
2610 ++ip;
2611 n = read32 (ip);
2612 ip += 4;
2613 next_ip = ip + 4 * n;
2614 for (i = 0; i < n; i++) {
2615 offset = read32 (ip);
2616 backwards = offset < 0;
2617 offset += next_ip - header->code;
2618 g_assert (offset >= 0 && offset < header->code_size);
2619 td->is_bb_start [offset] |= backwards ? 2 : 1;
2620 ip += 4;
2622 break;
2624 case MonoInlineR:
2625 case MonoInlineI8:
2626 ip += 9;
2627 break;
2628 default:
2629 g_assert_not_reached ();
2634 #ifdef NO_UNALIGNED_ACCESS
2635 static int
2636 get_unaligned_opcode (int opcode)
2638 switch (opcode) {
2639 case MINT_LDFLD_I8:
2640 return MINT_LDFLD_I8_UNALIGNED;
2641 case MINT_LDFLD_R8:
2642 return MINT_LDFLD_R8_UNALIGNED;
2643 case MINT_STFLD_I8:
2644 return MINT_STFLD_I8_UNALIGNED;
2645 case MINT_STFLD_R8:
2646 return MINT_STFLD_R8_UNALIGNED;
2647 default:
2648 g_assert_not_reached ();
2650 return -1;
2652 #endif
2654 static void
2655 interp_handle_isinst (TransformData *td, MonoClass *klass, gboolean isinst_instr)
2657 /* Follow the logic from jit's handle_isinst */
2658 if (!mono_class_has_variant_generic_params (klass)) {
2659 if (mono_class_is_interface (klass))
2660 interp_add_ins (td, isinst_instr ? MINT_ISINST_INTERFACE : MINT_CASTCLASS_INTERFACE);
2661 else if (!mono_class_is_marshalbyref (klass) && m_class_get_rank (klass) == 0 && !mono_class_is_nullable (klass))
2662 interp_add_ins (td, isinst_instr ? MINT_ISINST_COMMON : MINT_CASTCLASS_COMMON);
2663 else
2664 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2665 } else {
2666 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2668 td->last_ins->data [0] = get_data_item_index (td, klass);
2669 td->ip += 5;
2672 static void
2673 interp_emit_ldobj (TransformData *td, MonoClass *klass)
2675 int mt = mint_type (m_class_get_byval_arg (klass));
2676 int size;
2678 if (mt == MINT_TYPE_VT) {
2679 interp_add_ins (td, MINT_LDOBJ_VT);
2680 size = mono_class_value_size (klass, NULL);
2681 WRITE32_INS (td->last_ins, 0, &size);
2682 PUSH_VT (td, size);
2683 } else {
2684 int opcode = interp_get_ldind_for_mt (mt);
2685 interp_add_ins (td, opcode);
2688 SET_TYPE (td->sp - 1, stack_type [mt], klass);
2691 static void
2692 interp_emit_stobj (TransformData *td, MonoClass *klass)
2694 int mt = mint_type (m_class_get_byval_arg (klass));
2696 if (mt == MINT_TYPE_VT) {
2697 int size;
2698 interp_add_ins (td, MINT_STOBJ_VT);
2699 td->last_ins->data [0] = get_data_item_index(td, klass);
2700 size = mono_class_value_size (klass, NULL);
2701 POP_VT (td, size);
2702 } else {
2703 int opcode;
2704 switch (mt) {
2705 case MINT_TYPE_I1:
2706 case MINT_TYPE_U1:
2707 opcode = MINT_STIND_I1;
2708 break;
2709 case MINT_TYPE_I2:
2710 case MINT_TYPE_U2:
2711 opcode = MINT_STIND_I2;
2712 break;
2713 case MINT_TYPE_I4:
2714 opcode = MINT_STIND_I4;
2715 break;
2716 case MINT_TYPE_I8:
2717 opcode = MINT_STIND_I8;
2718 break;
2719 case MINT_TYPE_R4:
2720 opcode = MINT_STIND_R4;
2721 break;
2722 case MINT_TYPE_R8:
2723 opcode = MINT_STIND_R8;
2724 break;
2725 case MINT_TYPE_O:
2726 opcode = MINT_STIND_REF;
2727 break;
2728 default: g_assert_not_reached (); break;
2730 interp_add_ins (td, opcode);
2732 td->sp -= 2;
2735 static void
2736 interp_emit_ldsflda (TransformData *td, MonoClassField *field, MonoError *error)
2738 MonoDomain *domain = td->rtm->domain;
2739 // Initialize the offset for the field
2740 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
2741 return_if_nok (error);
2743 if (mono_class_field_is_special_static (field)) {
2744 guint32 offset;
2746 mono_domain_lock (domain);
2747 g_assert (domain->special_static_fields);
2748 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
2749 mono_domain_unlock (domain);
2750 g_assert (offset);
2752 interp_add_ins (td, MINT_LDSSFLDA);
2753 WRITE32_INS(td->last_ins, 0, &offset);
2754 } else {
2755 interp_add_ins (td, MINT_LDSFLDA);
2756 td->last_ins->data [0] = get_data_item_index (td, vtable);
2757 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
2761 static void
2762 interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, gboolean is_load, MonoError *error)
2764 MonoDomain *domain = td->rtm->domain;
2765 // Initialize the offset for the field
2766 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
2767 return_if_nok (error);
2769 if (mono_class_field_is_special_static (field)) {
2770 guint32 offset;
2772 mono_domain_lock (domain);
2773 g_assert (domain->special_static_fields);
2774 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
2775 mono_domain_unlock (domain);
2776 g_assert (offset);
2778 // Offset is SpecialStaticOffset
2779 if ((offset & 0x80000000) == 0 && mt != MINT_TYPE_VT) {
2780 // This field is thread static
2781 interp_add_ins (td, (is_load ? MINT_LDTSFLD_I1 : MINT_STTSFLD_I1) + mt);
2782 WRITE32_INS(td->last_ins, 0, &offset);
2783 } else {
2784 if (mt == MINT_TYPE_VT) {
2785 interp_add_ins (td, is_load ? MINT_LDSSFLD_VT : MINT_STSSFLD_VT);
2786 WRITE32_INS(td->last_ins, 0, &offset);
2788 int size = mono_class_value_size (field_class, NULL);
2789 WRITE32_INS(td->last_ins, 2, &size);
2790 } else {
2791 interp_add_ins (td, is_load ? MINT_LDSSFLD : MINT_STSSFLD);
2792 td->last_ins->data [0] = get_data_item_index (td, field);
2793 WRITE32_INS(td->last_ins, 1, &offset);
2796 } else {
2797 if (is_load)
2798 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSFLD_VT : (MINT_LDSFLD_I1 + mt - MINT_TYPE_I1));
2799 else
2800 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1));
2802 td->last_ins->data [0] = get_data_item_index (td, vtable);
2803 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
2805 if (mt == MINT_TYPE_VT) {
2806 int size = mono_class_value_size (field_class, NULL);
2807 WRITE32_INS(td->last_ins, 2, &size);
2812 static gboolean
2813 generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error)
2815 int target;
2816 int offset, mt, i, i32;
2817 guint32 token;
2818 int in_offset;
2819 const unsigned char *end;
2820 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
2821 gboolean sym_seq_points = FALSE;
2822 MonoBitSet *seq_point_locs = NULL;
2823 gboolean readonly = FALSE;
2824 gboolean volatile_ = FALSE;
2825 MonoClass *constrained_class = NULL;
2826 MonoClass *klass;
2827 MonoClassField *field;
2828 MonoImage *image = m_class_get_image (method->klass);
2829 InterpMethod *rtm = td->rtm;
2830 MonoDomain *domain = rtm->domain;
2831 MonoMethodSignature *signature = mono_method_signature_internal (method);
2832 gboolean ret = TRUE;
2833 gboolean emitted_funccall_seq_point = FALSE;
2834 guint32 *arg_offsets = NULL;
2835 guint32 *local_offsets = NULL;
2836 InterpInst *last_seq_point = NULL;
2838 original_bb = bb = mono_basic_block_split (method, error, header);
2839 goto_if_nok (error, exit);
2840 g_assert (bb);
2842 td->il_code = header->code;
2843 td->in_start = td->ip = header->code;
2844 end = td->ip + header->code_size;
2845 td->stack_state = (StackInfo**)g_malloc0(header->code_size * sizeof(StackInfo *));
2846 td->stack_height = (int*)g_malloc(header->code_size * sizeof(int));
2847 td->vt_stack_size = (int*)g_malloc(header->code_size * sizeof(int));
2848 td->clause_indexes = (int*)g_malloc (header->code_size * sizeof (int));
2849 td->is_bb_start = (guint8*)g_malloc0(header->code_size);
2851 init_bb_start (td, header);
2853 for (i = 0; i < header->code_size; i++) {
2854 td->stack_height [i] = -1;
2855 td->clause_indexes [i] = -1;
2858 for (i = 0; i < header->num_clauses; i++) {
2859 MonoExceptionClause *c = header->clauses + i;
2860 td->stack_height [c->handler_offset] = 0;
2861 td->vt_stack_size [c->handler_offset] = 0;
2862 td->is_bb_start [c->handler_offset] = 1;
2864 td->stack_height [c->handler_offset] = 1;
2865 td->stack_state [c->handler_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
2866 td->stack_state [c->handler_offset][0].type = STACK_TYPE_O;
2867 td->stack_state [c->handler_offset][0].klass = NULL; /*FIX*/
2869 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER) {
2870 td->stack_height [c->data.filter_offset] = 0;
2871 td->vt_stack_size [c->data.filter_offset] = 0;
2872 td->is_bb_start [c->data.filter_offset] = 1;
2874 td->stack_height [c->data.filter_offset] = 1;
2875 td->stack_state [c->data.filter_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
2876 td->stack_state [c->data.filter_offset][0].type = STACK_TYPE_O;
2877 td->stack_state [c->data.filter_offset][0].klass = NULL; /*FIX*/
2880 for (int j = c->handler_offset; j < c->handler_offset + c->handler_len; ++j) {
2881 if (td->clause_indexes [j] == -1)
2882 td->clause_indexes [j] = i;
2886 if (td->gen_sdb_seq_points && td->method == method) {
2887 MonoDebugMethodInfo *minfo;
2888 get_basic_blocks (td);
2890 minfo = mono_debug_lookup_method (method);
2892 if (minfo) {
2893 MonoSymSeqPoint *sps;
2894 int i, n_il_offsets;
2896 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
2897 // FIXME: Free
2898 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
2899 sym_seq_points = TRUE;
2901 for (i = 0; i < n_il_offsets; ++i) {
2902 if (sps [i].il_offset < header->code_size)
2903 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
2905 g_free (sps);
2907 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
2908 if (asyncMethod) {
2909 for (i = 0; asyncMethod != NULL && i < asyncMethod->num_awaits; i++) {
2910 mono_bitset_set_fast (seq_point_locs, asyncMethod->resume_offsets [i]);
2911 mono_bitset_set_fast (seq_point_locs, asyncMethod->yield_offsets [i]);
2913 mono_debug_free_method_async_debug_info (asyncMethod);
2915 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (m_class_get_image (method->klass))) {
2916 /* Methods without line number info like auto-generated property accessors */
2917 seq_point_locs = mono_bitset_new (header->code_size, 0);
2918 sym_seq_points = TRUE;
2922 if (sym_seq_points) {
2923 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
2924 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY;
2927 if (mono_debugger_method_has_breakpoint (method))
2928 interp_add_ins (td, MINT_BREAKPOINT);
2930 if (method == td->method) {
2931 if (td->verbose_level) {
2932 char *tmp = mono_disasm_code (NULL, method, td->ip, end);
2933 char *name = mono_method_full_name (method, TRUE);
2934 g_print ("Method %s, original code:\n", name);
2935 g_print ("%s\n", tmp);
2936 g_free (tmp);
2937 g_free (name);
2940 if (header->num_locals && header->init_locals)
2941 interp_add_ins (td, MINT_INITLOCALS);
2943 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
2944 interp_add_ins (td, MINT_TRACE_ENTER);
2945 else if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER)
2946 interp_add_ins (td, MINT_PROF_ENTER);
2948 /* safepoint is required on method entry */
2949 if (mono_threads_are_safepoints_enabled ())
2950 interp_add_ins (td, MINT_SAFEPOINT);
2951 } else {
2952 int offset;
2953 arg_offsets = (guint32*) g_malloc ((!!signature->hasthis + signature->param_count) * sizeof (guint32));
2954 /* Allocate locals to store inlined method args from stack */
2955 for (i = signature->param_count - 1; i >= 0; i--) {
2956 offset = create_interp_local (td, signature->params [i]);
2957 arg_offsets [i + !!signature->hasthis] = offset;
2958 store_local_general (td, offset, signature->params [i]);
2961 if (signature->hasthis) {
2963 * If this is value type, it is passed by address and not by value.
2964 * FIXME We should use MINT_TYPE_P instead of MINT_TYPE_O
2966 MonoType *type = mono_get_object_type ();
2967 offset = create_interp_local (td, type);
2968 arg_offsets [0] = offset;
2969 store_local_general (td, offset, type);
2972 local_offsets = (guint32*) g_malloc (header->num_locals * sizeof (guint32));
2973 /* Allocate locals to store inlined method args from stack */
2974 for (i = 0; i < header->num_locals; i++)
2975 local_offsets [i] = create_interp_local (td, header->locals [i]);
2978 while (td->ip < end) {
2979 g_assert (td->sp >= td->stack);
2980 g_assert (td->vt_sp < 0x10000000);
2981 in_offset = td->ip - header->code;
2982 td->in_start = td->ip;
2983 InterpInst *prev_last_ins = td->last_ins;
2985 if (td->stack_height [in_offset] >= 0) {
2986 g_assert (td->is_bb_start [in_offset]);
2987 if (td->stack_height [in_offset] > 0)
2988 memcpy (td->stack, td->stack_state [in_offset], td->stack_height [in_offset] * sizeof(td->stack [0]));
2989 td->sp = td->stack + td->stack_height [in_offset];
2990 td->vt_sp = td->vt_stack_size [in_offset];
2993 if (in_offset == bb->end)
2994 bb = bb->next;
2996 if (bb->dead) {
2997 int op_size = mono_opcode_size (td->ip, end);
2998 g_assert (op_size > 0); /* The BB formation pass must catch all bad ops */
3000 if (td->verbose_level > 1)
3001 g_print ("SKIPPING DEAD OP at %x\n", in_offset);
3003 td->ip += op_size;
3004 continue;
3007 if (td->verbose_level > 1) {
3008 g_print ("IL_%04lx %s %-10s, sp %ld, %s %-12s vt_sp %u (max %u)\n",
3009 td->ip - td->il_code,
3010 td->is_bb_start [td->ip - td->il_code] == 3 ? "<>" :
3011 td->is_bb_start [td->ip - td->il_code] == 2 ? "< " :
3012 td->is_bb_start [td->ip - td->il_code] == 1 ? " >" : " ",
3013 mono_opcode_name (*td->ip), td->sp - td->stack,
3014 td->sp > td->stack ? stack_type_string [td->sp [-1].type] : " ",
3015 (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)) : "",
3016 td->vt_sp, td->max_vt_sp);
3019 if (sym_seq_points && mono_bitset_test_fast (seq_point_locs, td->ip - header->code)) {
3020 InterpBasicBlock *cbb = td->offset_to_bb [td->ip - header->code];
3021 g_assert (cbb);
3024 * Make methods interruptable at the beginning, and at the targets of
3025 * backward branches.
3027 if (in_offset == 0 || g_slist_length (cbb->preds) > 1)
3028 interp_add_ins (td, MINT_SDB_INTR_LOC);
3030 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3033 if (td->is_bb_start [in_offset]) {
3034 int index = td->clause_indexes [in_offset];
3035 if (index != -1) {
3036 MonoExceptionClause *clause = &header->clauses [index];
3037 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
3038 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) &&
3039 in_offset == clause->handler_offset)
3040 interp_add_ins (td, MINT_START_ABORT_PROT);
3044 switch (*td->ip) {
3045 case CEE_NOP:
3046 /* lose it */
3047 emitted_funccall_seq_point = FALSE;
3048 ++td->ip;
3049 break;
3050 case CEE_BREAK:
3051 SIMPLE_OP(td, MINT_BREAK);
3052 break;
3053 case CEE_LDARG_0:
3054 case CEE_LDARG_1:
3055 case CEE_LDARG_2:
3056 case CEE_LDARG_3: {
3057 int arg_n = *td->ip - CEE_LDARG_0;
3058 if (td->method == method)
3059 load_arg (td, arg_n);
3060 else
3061 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
3062 ++td->ip;
3063 break;
3065 case CEE_LDLOC_0:
3066 case CEE_LDLOC_1:
3067 case CEE_LDLOC_2:
3068 case CEE_LDLOC_3: {
3069 int loc_n = *td->ip - CEE_LDLOC_0;
3070 if (td->method == method)
3071 load_local (td, loc_n);
3072 else
3073 load_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
3074 ++td->ip;
3075 break;
3077 case CEE_STLOC_0:
3078 case CEE_STLOC_1:
3079 case CEE_STLOC_2:
3080 case CEE_STLOC_3: {
3081 int loc_n = *td->ip - CEE_STLOC_0;
3082 if (td->method == method)
3083 store_local (td, loc_n);
3084 else
3085 store_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
3086 ++td->ip;
3087 break;
3089 case CEE_LDARG_S: {
3090 int arg_n = ((guint8 *)td->ip)[1];
3091 if (td->method == method)
3092 load_arg (td, arg_n);
3093 else
3094 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
3095 td->ip += 2;
3096 break;
3098 case CEE_LDARGA_S: {
3099 /* NOTE: n includes this */
3100 int n = ((guint8 *) td->ip) [1];
3102 if (td->method == method) {
3103 get_arg_type_exact (td, n, &mt);
3104 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
3105 td->last_ins->data [0] = n;
3106 } else {
3107 interp_add_ins (td, MINT_LDLOCA_S);
3108 td->last_ins->data [0] = arg_offsets [n];
3110 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3111 td->ip += 2;
3112 break;
3114 case CEE_STARG_S: {
3115 int arg_n = ((guint8 *)td->ip)[1];
3116 if (td->method == method)
3117 store_arg (td, arg_n);
3118 else
3119 store_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
3120 td->ip += 2;
3121 break;
3123 case CEE_LDLOC_S: {
3124 int loc_n = ((guint8 *)td->ip)[1];
3125 if (td->method == method)
3126 load_local (td, loc_n);
3127 else
3128 load_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
3129 td->ip += 2;
3130 break;
3132 case CEE_LDLOCA_S: {
3133 int loc_n = ((guint8 *)td->ip)[1];
3134 interp_add_ins (td, MINT_LDLOCA_S);
3135 if (td->method == method)
3136 td->last_ins->data [0] = td->rtm->local_offsets [loc_n];
3137 else
3138 td->last_ins->data [0] = local_offsets [loc_n];
3139 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3140 td->ip += 2;
3141 break;
3143 case CEE_STLOC_S: {
3144 int loc_n = ((guint8 *)td->ip)[1];
3145 if (td->method == method)
3146 store_local (td, loc_n);
3147 else
3148 store_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
3149 td->ip += 2;
3150 break;
3152 case CEE_LDNULL:
3153 SIMPLE_OP(td, MINT_LDNULL);
3154 PUSH_TYPE(td, STACK_TYPE_O, NULL);
3155 break;
3156 case CEE_LDC_I4_M1:
3157 SIMPLE_OP(td, MINT_LDC_I4_M1);
3158 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3159 break;
3160 case CEE_LDC_I4_0:
3161 if (!td->is_bb_start[td->ip + 1 - td->il_code] && td->ip [1] == 0xfe && td->ip [2] == CEE_CEQ &&
3162 td->sp > td->stack && td->sp [-1].type == STACK_TYPE_I4) {
3163 SIMPLE_OP(td, MINT_CEQ0_I4);
3164 td->ip += 2;
3165 } else {
3166 SIMPLE_OP(td, MINT_LDC_I4_0);
3167 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3169 break;
3170 case CEE_LDC_I4_1:
3171 if (!td->is_bb_start[td->ip + 1 - td->il_code] &&
3172 (td->ip [1] == CEE_ADD || td->ip [1] == CEE_SUB) && td->sp [-1].type == STACK_TYPE_I4) {
3173 interp_add_ins (td, td->ip [1] == CEE_ADD ? MINT_ADD1_I4 : MINT_SUB1_I4);
3174 td->ip += 2;
3175 } else {
3176 SIMPLE_OP(td, MINT_LDC_I4_1);
3177 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3179 break;
3180 case CEE_LDC_I4_2:
3181 case CEE_LDC_I4_3:
3182 case CEE_LDC_I4_4:
3183 case CEE_LDC_I4_5:
3184 case CEE_LDC_I4_6:
3185 case CEE_LDC_I4_7:
3186 case CEE_LDC_I4_8:
3187 SIMPLE_OP(td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0);
3188 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3189 break;
3190 case CEE_LDC_I4_S:
3191 interp_add_ins (td, MINT_LDC_I4_S);
3192 td->last_ins->data [0] = ((gint8 *) td->ip) [1];
3193 td->ip += 2;
3194 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3195 break;
3196 case CEE_LDC_I4:
3197 i32 = read32 (td->ip + 1);
3198 interp_add_ins (td, MINT_LDC_I4);
3199 WRITE32_INS (td->last_ins, 0, &i32);
3200 td->ip += 5;
3201 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3202 break;
3203 case CEE_LDC_I8: {
3204 gint64 val = read64 (td->ip + 1);
3205 interp_add_ins (td, MINT_LDC_I8);
3206 WRITE64_INS (td->last_ins, 0, &val);
3207 td->ip += 9;
3208 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I8);
3209 break;
3211 case CEE_LDC_R4: {
3212 float val;
3213 readr4 (td->ip + 1, &val);
3214 interp_add_ins (td, MINT_LDC_R4);
3215 WRITE32_INS (td->last_ins, 0, &val);
3216 td->ip += 5;
3217 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R4);
3218 break;
3220 case CEE_LDC_R8: {
3221 double val;
3222 readr8 (td->ip + 1, &val);
3223 interp_add_ins (td, MINT_LDC_R8);
3224 WRITE64_INS (td->last_ins, 0, &val);
3225 td->ip += 9;
3226 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R8);
3227 break;
3229 case CEE_DUP: {
3230 int type = td->sp [-1].type;
3231 MonoClass *klass = td->sp [-1].klass;
3232 if (td->sp [-1].type == STACK_TYPE_VT) {
3233 gint32 size = mono_class_value_size (klass, NULL);
3234 PUSH_VT(td, size);
3235 interp_add_ins (td, MINT_DUP_VT);
3236 WRITE32_INS (td->last_ins, 0, &size);
3237 td->ip ++;
3238 } else
3239 SIMPLE_OP(td, MINT_DUP);
3240 PUSH_TYPE(td, type, klass);
3241 break;
3243 case CEE_POP:
3244 CHECK_STACK(td, 1);
3245 SIMPLE_OP(td, MINT_POP);
3246 td->last_ins->data [0] = 0;
3247 if (td->sp [-1].type == STACK_TYPE_VT) {
3248 int size = mono_class_value_size (td->sp [-1].klass, NULL);
3249 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
3250 interp_add_ins (td, MINT_VTRESULT);
3251 td->last_ins->data [0] = 0;
3252 WRITE32_INS (td->last_ins, 1, &size);
3253 td->vt_sp -= size;
3255 --td->sp;
3256 break;
3257 case CEE_JMP: {
3258 MonoMethod *m;
3259 INLINE_FAILURE;
3260 if (td->sp > td->stack)
3261 g_warning ("CEE_JMP: stack must be empty");
3262 token = read32 (td->ip + 1);
3263 m = mono_get_method_checked (image, token, NULL, generic_context, error);
3264 goto_if_nok (error, exit);
3265 interp_add_ins (td, MINT_JMP);
3266 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3267 goto_if_nok (error, exit);
3268 td->ip += 5;
3269 break;
3271 case CEE_CALLVIRT: /* Fall through */
3272 case CEE_CALLI: /* Fall through */
3273 case CEE_CALL: {
3274 gboolean need_seq_point = FALSE;
3276 if (sym_seq_points && !mono_bitset_test_fast (seq_point_locs, td->ip + 5 - header->code))
3277 need_seq_point = TRUE;
3279 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, constrained_class, readonly, error, TRUE))
3280 goto exit;
3282 if (need_seq_point) {
3283 //check is is a nested call and remove the MONO_INST_NONEMPTY_STACK of the last breakpoint, only for non native methods
3284 if (!(method->flags & METHOD_IMPL_ATTRIBUTE_NATIVE)) {
3285 if (emitted_funccall_seq_point) {
3286 if (last_seq_point)
3287 last_seq_point->flags |= INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL;
3289 else
3290 emitted_funccall_seq_point = TRUE;
3292 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3293 // This seq point is actually associated with the instruction following the call
3294 last_seq_point->il_offset = td->ip - header->code;
3295 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK;
3298 constrained_class = NULL;
3299 readonly = FALSE;
3300 break;
3302 case CEE_RET: {
3303 /* Return from inlined method, return value is on top of stack */
3304 if (td->method != method) {
3305 td->ip++;
3306 break;
3309 int vt_size = 0;
3310 MonoType *ult = mini_type_get_underlying_type (signature->ret);
3311 if (ult->type != MONO_TYPE_VOID) {
3312 CHECK_STACK (td, 1);
3313 --td->sp;
3314 if (mint_type (ult) == MINT_TYPE_VT) {
3315 MonoClass *klass = mono_class_from_mono_type_internal (ult);
3316 vt_size = mono_class_value_size (klass, NULL);
3319 if (td->sp > td->stack) {
3320 mono_error_set_generic_error (error, "System", "InvalidProgramException", "");
3321 goto exit;
3323 if (td->vt_sp != ALIGN_TO (vt_size, MINT_VT_ALIGNMENT))
3324 g_error ("%s: CEE_RET: value type stack: %d vs. %d", mono_method_full_name (td->method, TRUE), td->vt_sp, vt_size);
3326 if (sym_seq_points) {
3327 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3328 td->last_ins->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT;
3331 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
3332 /* This does the return as well */
3333 interp_add_ins (td, MINT_TRACE_EXIT);
3334 if (ult->type == MONO_TYPE_VOID)
3335 vt_size = -1;
3336 WRITE32_INS (td->last_ins, 0, &vt_size);
3337 ++td->ip;
3338 } else {
3339 if (vt_size == 0)
3340 SIMPLE_OP(td, ult->type == MONO_TYPE_VOID ? MINT_RET_VOID : MINT_RET);
3341 else {
3342 interp_add_ins (td, MINT_RET_VT);
3343 WRITE32_INS (td->last_ins, 0, &vt_size);
3344 ++td->ip;
3347 break;
3349 case CEE_BR:
3350 INLINE_FAILURE;
3351 handle_branch (td, MINT_BR_S, MINT_BR, 5 + read32 (td->ip + 1));
3352 td->ip += 5;
3353 break;
3354 case CEE_BR_S:
3355 INLINE_FAILURE;
3356 handle_branch (td, MINT_BR_S, MINT_BR, 2 + (gint8)td->ip [1]);
3357 td->ip += 2;
3358 break;
3359 case CEE_BRFALSE:
3360 INLINE_FAILURE;
3361 one_arg_branch (td, MINT_BRFALSE_I4, 5 + read32 (td->ip + 1));
3362 td->ip += 5;
3363 break;
3364 case CEE_BRFALSE_S:
3365 INLINE_FAILURE;
3366 one_arg_branch (td, MINT_BRFALSE_I4, 2 + (gint8)td->ip [1]);
3367 td->ip += 2;
3368 break;
3369 case CEE_BRTRUE:
3370 INLINE_FAILURE;
3371 one_arg_branch (td, MINT_BRTRUE_I4, 5 + read32 (td->ip + 1));
3372 td->ip += 5;
3373 break;
3374 case CEE_BRTRUE_S:
3375 INLINE_FAILURE;
3376 one_arg_branch (td, MINT_BRTRUE_I4, 2 + (gint8)td->ip [1]);
3377 td->ip += 2;
3378 break;
3379 case CEE_BEQ:
3380 INLINE_FAILURE;
3381 two_arg_branch (td, MINT_BEQ_I4, 5 + read32 (td->ip + 1));
3382 td->ip += 5;
3383 break;
3384 case CEE_BEQ_S:
3385 INLINE_FAILURE;
3386 two_arg_branch (td, MINT_BEQ_I4, 2 + (gint8) td->ip [1]);
3387 td->ip += 2;
3388 break;
3389 case CEE_BGE:
3390 INLINE_FAILURE;
3391 two_arg_branch (td, MINT_BGE_I4, 5 + read32 (td->ip + 1));
3392 td->ip += 5;
3393 break;
3394 case CEE_BGE_S:
3395 INLINE_FAILURE;
3396 two_arg_branch (td, MINT_BGE_I4, 2 + (gint8) td->ip [1]);
3397 td->ip += 2;
3398 break;
3399 case CEE_BGT:
3400 INLINE_FAILURE;
3401 two_arg_branch (td, MINT_BGT_I4, 5 + read32 (td->ip + 1));
3402 td->ip += 5;
3403 break;
3404 case CEE_BGT_S:
3405 INLINE_FAILURE;
3406 two_arg_branch (td, MINT_BGT_I4, 2 + (gint8) td->ip [1]);
3407 td->ip += 2;
3408 break;
3409 case CEE_BLT:
3410 INLINE_FAILURE;
3411 two_arg_branch (td, MINT_BLT_I4, 5 + read32 (td->ip + 1));
3412 td->ip += 5;
3413 break;
3414 case CEE_BLT_S:
3415 INLINE_FAILURE;
3416 two_arg_branch (td, MINT_BLT_I4, 2 + (gint8) td->ip [1]);
3417 td->ip += 2;
3418 break;
3419 case CEE_BLE:
3420 INLINE_FAILURE;
3421 two_arg_branch (td, MINT_BLE_I4, 5 + read32 (td->ip + 1));
3422 td->ip += 5;
3423 break;
3424 case CEE_BLE_S:
3425 INLINE_FAILURE;
3426 two_arg_branch (td, MINT_BLE_I4, 2 + (gint8) td->ip [1]);
3427 td->ip += 2;
3428 break;
3429 case CEE_BNE_UN:
3430 INLINE_FAILURE;
3431 two_arg_branch (td, MINT_BNE_UN_I4, 5 + read32 (td->ip + 1));
3432 td->ip += 5;
3433 break;
3434 case CEE_BNE_UN_S:
3435 INLINE_FAILURE;
3436 two_arg_branch (td, MINT_BNE_UN_I4, 2 + (gint8) td->ip [1]);
3437 td->ip += 2;
3438 break;
3439 case CEE_BGE_UN:
3440 INLINE_FAILURE;
3441 two_arg_branch (td, MINT_BGE_UN_I4, 5 + read32 (td->ip + 1));
3442 td->ip += 5;
3443 break;
3444 case CEE_BGE_UN_S:
3445 INLINE_FAILURE;
3446 two_arg_branch (td, MINT_BGE_UN_I4, 2 + (gint8) td->ip [1]);
3447 td->ip += 2;
3448 break;
3449 case CEE_BGT_UN:
3450 INLINE_FAILURE;
3451 two_arg_branch (td, MINT_BGT_UN_I4, 5 + read32 (td->ip + 1));
3452 td->ip += 5;
3453 break;
3454 case CEE_BGT_UN_S:
3455 INLINE_FAILURE;
3456 two_arg_branch (td, MINT_BGT_UN_I4, 2 + (gint8) td->ip [1]);
3457 td->ip += 2;
3458 break;
3459 case CEE_BLE_UN:
3460 INLINE_FAILURE;
3461 two_arg_branch (td, MINT_BLE_UN_I4, 5 + read32 (td->ip + 1));
3462 td->ip += 5;
3463 break;
3464 case CEE_BLE_UN_S:
3465 INLINE_FAILURE;
3466 two_arg_branch (td, MINT_BLE_UN_I4, 2 + (gint8) td->ip [1]);
3467 td->ip += 2;
3468 break;
3469 case CEE_BLT_UN:
3470 INLINE_FAILURE;
3471 two_arg_branch (td, MINT_BLT_UN_I4, 5 + read32 (td->ip + 1));
3472 td->ip += 5;
3473 break;
3474 case CEE_BLT_UN_S:
3475 INLINE_FAILURE;
3476 two_arg_branch (td, MINT_BLT_UN_I4, 2 + (gint8) td->ip [1]);
3477 td->ip += 2;
3478 break;
3479 case CEE_SWITCH: {
3480 INLINE_FAILURE;
3481 guint32 n;
3482 const unsigned char *next_ip;
3483 ++td->ip;
3484 n = read32 (td->ip);
3485 interp_add_ins_explicit (td, MINT_SWITCH, MINT_SWITCH_LEN (n));
3486 WRITE32_INS (td->last_ins, 0, &n);
3487 td->ip += 4;
3488 next_ip = td->ip + n * 4;
3489 --td->sp;
3490 int stack_height = td->sp - td->stack;
3491 for (i = 0; i < n; i++) {
3492 offset = read32 (td->ip);
3493 target = next_ip - td->il_code + offset;
3494 if (offset < 0) {
3495 #if DEBUG_INTERP
3496 if (stack_height > 0 && stack_height != td->stack_height [target])
3497 g_warning ("SWITCH with back branch and non-empty stack");
3498 #endif
3499 } else {
3500 td->stack_height [target] = stack_height;
3501 td->vt_stack_size [target] = td->vt_sp;
3502 if (stack_height > 0)
3503 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, stack_height * sizeof (td->stack [0]));
3505 WRITE32_INS (td->last_ins, 2 + i * 2, &target);
3506 td->ip += 4;
3508 break;
3510 case CEE_LDIND_I1:
3511 CHECK_STACK (td, 1);
3512 SIMPLE_OP (td, MINT_LDIND_I1_CHECK);
3513 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3514 BARRIER_IF_VOLATILE (td);
3515 break;
3516 case CEE_LDIND_U1:
3517 CHECK_STACK (td, 1);
3518 SIMPLE_OP (td, MINT_LDIND_U1_CHECK);
3519 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3520 BARRIER_IF_VOLATILE (td);
3521 break;
3522 case CEE_LDIND_I2:
3523 CHECK_STACK (td, 1);
3524 SIMPLE_OP (td, MINT_LDIND_I2_CHECK);
3525 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3526 BARRIER_IF_VOLATILE (td);
3527 break;
3528 case CEE_LDIND_U2:
3529 CHECK_STACK (td, 1);
3530 SIMPLE_OP (td, MINT_LDIND_U2_CHECK);
3531 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3532 BARRIER_IF_VOLATILE (td);
3533 break;
3534 case CEE_LDIND_I4:
3535 CHECK_STACK (td, 1);
3536 SIMPLE_OP (td, MINT_LDIND_I4_CHECK);
3537 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3538 BARRIER_IF_VOLATILE (td);
3539 break;
3540 case CEE_LDIND_U4:
3541 CHECK_STACK (td, 1);
3542 SIMPLE_OP (td, MINT_LDIND_U4_CHECK);
3543 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3544 BARRIER_IF_VOLATILE (td);
3545 break;
3546 case CEE_LDIND_I8:
3547 CHECK_STACK (td, 1);
3548 SIMPLE_OP (td, MINT_LDIND_I8_CHECK);
3549 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3550 BARRIER_IF_VOLATILE (td);
3551 break;
3552 case CEE_LDIND_I:
3553 CHECK_STACK (td, 1);
3554 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3555 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3556 BARRIER_IF_VOLATILE (td);
3557 break;
3558 case CEE_LDIND_R4:
3559 CHECK_STACK (td, 1);
3560 SIMPLE_OP (td, MINT_LDIND_R4_CHECK);
3561 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
3562 BARRIER_IF_VOLATILE (td);
3563 break;
3564 case CEE_LDIND_R8:
3565 CHECK_STACK (td, 1);
3566 SIMPLE_OP (td, MINT_LDIND_R8_CHECK);
3567 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3568 BARRIER_IF_VOLATILE (td);
3569 break;
3570 case CEE_LDIND_REF:
3571 CHECK_STACK (td, 1);
3572 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3573 BARRIER_IF_VOLATILE (td);
3574 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
3575 break;
3576 case CEE_STIND_REF:
3577 CHECK_STACK (td, 2);
3578 BARRIER_IF_VOLATILE (td);
3579 SIMPLE_OP (td, MINT_STIND_REF);
3580 td->sp -= 2;
3581 break;
3582 case CEE_STIND_I1:
3583 CHECK_STACK (td, 2);
3584 BARRIER_IF_VOLATILE (td);
3585 SIMPLE_OP (td, MINT_STIND_I1);
3586 td->sp -= 2;
3587 break;
3588 case CEE_STIND_I2:
3589 CHECK_STACK (td, 2);
3590 BARRIER_IF_VOLATILE (td);
3591 SIMPLE_OP (td, MINT_STIND_I2);
3592 td->sp -= 2;
3593 break;
3594 case CEE_STIND_I4:
3595 CHECK_STACK (td, 2);
3596 BARRIER_IF_VOLATILE (td);
3597 SIMPLE_OP (td, MINT_STIND_I4);
3598 td->sp -= 2;
3599 break;
3600 case CEE_STIND_I:
3601 CHECK_STACK (td, 2);
3602 BARRIER_IF_VOLATILE (td);
3603 SIMPLE_OP (td, MINT_STIND_I);
3604 td->sp -= 2;
3605 break;
3606 case CEE_STIND_I8:
3607 CHECK_STACK (td, 2);
3608 BARRIER_IF_VOLATILE (td);
3609 SIMPLE_OP (td, MINT_STIND_I8);
3610 td->sp -= 2;
3611 break;
3612 case CEE_STIND_R4:
3613 CHECK_STACK (td, 2);
3614 BARRIER_IF_VOLATILE (td);
3615 SIMPLE_OP (td, MINT_STIND_R4);
3616 td->sp -= 2;
3617 break;
3618 case CEE_STIND_R8:
3619 CHECK_STACK (td, 2);
3620 BARRIER_IF_VOLATILE (td);
3621 SIMPLE_OP (td, MINT_STIND_R8);
3622 td->sp -= 2;
3623 break;
3624 case CEE_ADD:
3625 binary_arith_op(td, MINT_ADD_I4);
3626 ++td->ip;
3627 break;
3628 case CEE_SUB:
3629 binary_arith_op(td, MINT_SUB_I4);
3630 ++td->ip;
3631 break;
3632 case CEE_MUL:
3633 binary_arith_op(td, MINT_MUL_I4);
3634 ++td->ip;
3635 break;
3636 case CEE_DIV:
3637 binary_arith_op(td, MINT_DIV_I4);
3638 ++td->ip;
3639 break;
3640 case CEE_DIV_UN:
3641 binary_arith_op(td, MINT_DIV_UN_I4);
3642 ++td->ip;
3643 break;
3644 case CEE_REM:
3645 binary_arith_op (td, MINT_REM_I4);
3646 ++td->ip;
3647 break;
3648 case CEE_REM_UN:
3649 binary_arith_op (td, MINT_REM_UN_I4);
3650 ++td->ip;
3651 break;
3652 case CEE_AND:
3653 binary_arith_op (td, MINT_AND_I4);
3654 ++td->ip;
3655 break;
3656 case CEE_OR:
3657 binary_arith_op (td, MINT_OR_I4);
3658 ++td->ip;
3659 break;
3660 case CEE_XOR:
3661 binary_arith_op (td, MINT_XOR_I4);
3662 ++td->ip;
3663 break;
3664 case CEE_SHL:
3665 shift_op (td, MINT_SHL_I4);
3666 ++td->ip;
3667 break;
3668 case CEE_SHR:
3669 shift_op (td, MINT_SHR_I4);
3670 ++td->ip;
3671 break;
3672 case CEE_SHR_UN:
3673 shift_op (td, MINT_SHR_UN_I4);
3674 ++td->ip;
3675 break;
3676 case CEE_NEG:
3677 unary_arith_op (td, MINT_NEG_I4);
3678 ++td->ip;
3679 break;
3680 case CEE_NOT:
3681 unary_arith_op (td, MINT_NOT_I4);
3682 ++td->ip;
3683 break;
3684 case CEE_CONV_U1:
3685 CHECK_STACK (td, 1);
3686 switch (td->sp [-1].type) {
3687 case STACK_TYPE_R4:
3688 interp_add_ins (td, MINT_CONV_U1_R4);
3689 break;
3690 case STACK_TYPE_R8:
3691 interp_add_ins (td, MINT_CONV_U1_R8);
3692 break;
3693 case STACK_TYPE_I4:
3694 interp_add_ins (td, MINT_CONV_U1_I4);
3695 break;
3696 case STACK_TYPE_I8:
3697 interp_add_ins (td, MINT_CONV_U1_I8);
3698 break;
3699 default:
3700 g_assert_not_reached ();
3702 ++td->ip;
3703 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3704 break;
3705 case CEE_CONV_I1:
3706 CHECK_STACK (td, 1);
3707 switch (td->sp [-1].type) {
3708 case STACK_TYPE_R4:
3709 interp_add_ins (td, MINT_CONV_I1_R4);
3710 break;
3711 case STACK_TYPE_R8:
3712 interp_add_ins (td, MINT_CONV_I1_R8);
3713 break;
3714 case STACK_TYPE_I4:
3715 interp_add_ins (td, MINT_CONV_I1_I4);
3716 break;
3717 case STACK_TYPE_I8:
3718 interp_add_ins (td, MINT_CONV_I1_I8);
3719 break;
3720 default:
3721 g_assert_not_reached ();
3723 ++td->ip;
3724 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3725 break;
3726 case CEE_CONV_U2:
3727 CHECK_STACK (td, 1);
3728 switch (td->sp [-1].type) {
3729 case STACK_TYPE_R4:
3730 interp_add_ins (td, MINT_CONV_U2_R4);
3731 break;
3732 case STACK_TYPE_R8:
3733 interp_add_ins (td, MINT_CONV_U2_R8);
3734 break;
3735 case STACK_TYPE_I4:
3736 interp_add_ins (td, MINT_CONV_U2_I4);
3737 break;
3738 case STACK_TYPE_I8:
3739 interp_add_ins (td, MINT_CONV_U2_I8);
3740 break;
3741 default:
3742 g_assert_not_reached ();
3744 ++td->ip;
3745 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3746 break;
3747 case CEE_CONV_I2:
3748 CHECK_STACK (td, 1);
3749 switch (td->sp [-1].type) {
3750 case STACK_TYPE_R4:
3751 interp_add_ins (td, MINT_CONV_I2_R4);
3752 break;
3753 case STACK_TYPE_R8:
3754 interp_add_ins (td, MINT_CONV_I2_R8);
3755 break;
3756 case STACK_TYPE_I4:
3757 interp_add_ins (td, MINT_CONV_I2_I4);
3758 break;
3759 case STACK_TYPE_I8:
3760 interp_add_ins (td, MINT_CONV_I2_I8);
3761 break;
3762 default:
3763 g_assert_not_reached ();
3765 ++td->ip;
3766 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3767 break;
3768 case CEE_CONV_U:
3769 CHECK_STACK (td, 1);
3770 switch (td->sp [-1].type) {
3771 case STACK_TYPE_R8:
3772 #if SIZEOF_VOID_P == 4
3773 interp_add_ins (td, MINT_CONV_U4_R8);
3774 #else
3775 interp_add_ins (td, MINT_CONV_U8_R8);
3776 #endif
3777 break;
3778 case STACK_TYPE_I4:
3779 #if SIZEOF_VOID_P == 8
3780 interp_add_ins (td, MINT_CONV_U8_I4);
3781 #endif
3782 break;
3783 case STACK_TYPE_I8:
3784 #if SIZEOF_VOID_P == 4
3785 interp_add_ins (td, MINT_CONV_U4_I8);
3786 #endif
3787 break;
3788 case STACK_TYPE_MP:
3789 case STACK_TYPE_O:
3790 break;
3791 default:
3792 g_assert_not_reached ();
3794 ++td->ip;
3795 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3796 break;
3797 case CEE_CONV_I:
3798 CHECK_STACK (td, 1);
3799 switch (td->sp [-1].type) {
3800 case STACK_TYPE_R8:
3801 #if SIZEOF_VOID_P == 8
3802 interp_add_ins (td, MINT_CONV_I8_R8);
3803 #else
3804 interp_add_ins (td, MINT_CONV_I4_R8);
3805 #endif
3806 break;
3807 case STACK_TYPE_I4:
3808 #if SIZEOF_VOID_P == 8
3809 interp_add_ins (td, MINT_CONV_I8_I4);
3810 #endif
3811 break;
3812 case STACK_TYPE_O:
3813 break;
3814 case STACK_TYPE_MP:
3815 break;
3816 case STACK_TYPE_I8:
3817 #if SIZEOF_VOID_P == 4
3818 interp_add_ins (td, MINT_CONV_I4_I8);
3819 #endif
3820 break;
3821 default:
3822 g_assert_not_reached ();
3824 ++td->ip;
3825 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3826 break;
3827 case CEE_CONV_U4:
3828 CHECK_STACK (td, 1);
3829 switch (td->sp [-1].type) {
3830 case STACK_TYPE_R4:
3831 interp_add_ins (td, MINT_CONV_U4_R4);
3832 break;
3833 case STACK_TYPE_R8:
3834 interp_add_ins (td, MINT_CONV_U4_R8);
3835 break;
3836 case STACK_TYPE_I4:
3837 break;
3838 case STACK_TYPE_I8:
3839 interp_add_ins (td, MINT_CONV_U4_I8);
3840 break;
3841 case STACK_TYPE_MP:
3842 #if SIZEOF_VOID_P == 8
3843 interp_add_ins (td, MINT_CONV_U4_I8);
3844 #endif
3845 break;
3846 default:
3847 g_assert_not_reached ();
3849 ++td->ip;
3850 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3851 break;
3852 case CEE_CONV_I4:
3853 CHECK_STACK (td, 1);
3854 switch (td->sp [-1].type) {
3855 case STACK_TYPE_R4:
3856 interp_add_ins (td, MINT_CONV_I4_R4);
3857 break;
3858 case STACK_TYPE_R8:
3859 interp_add_ins (td, MINT_CONV_I4_R8);
3860 break;
3861 case STACK_TYPE_I4:
3862 break;
3863 case STACK_TYPE_I8:
3864 interp_add_ins (td, MINT_CONV_I4_I8);
3865 break;
3866 case STACK_TYPE_MP:
3867 #if SIZEOF_VOID_P == 8
3868 interp_add_ins (td, MINT_CONV_I4_I8);
3869 #endif
3870 break;
3871 default:
3872 g_assert_not_reached ();
3874 ++td->ip;
3875 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3876 break;
3877 case CEE_CONV_I8:
3878 CHECK_STACK (td, 1);
3879 switch (td->sp [-1].type) {
3880 case STACK_TYPE_R4:
3881 interp_add_ins (td, MINT_CONV_I8_R4);
3882 break;
3883 case STACK_TYPE_R8:
3884 interp_add_ins (td, MINT_CONV_I8_R8);
3885 break;
3886 case STACK_TYPE_I4: {
3887 if (interp_ins_is_ldc (td->last_ins) && !td->is_bb_start [in_offset]) {
3888 gint64 ct = interp_ldc_i4_get_const (td->last_ins);
3889 interp_remove_ins (td, td->last_ins);
3891 interp_add_ins (td, MINT_LDC_I8);
3892 WRITE64_INS (td->last_ins, 0, &ct);
3893 } else {
3894 interp_add_ins (td, MINT_CONV_I8_I4);
3896 break;
3898 case STACK_TYPE_I8:
3899 break;
3900 case STACK_TYPE_MP:
3901 #if SIZEOF_VOID_P == 4
3902 interp_add_ins (td, MINT_CONV_I8_I4);
3903 #endif
3904 break;
3905 default:
3906 g_assert_not_reached ();
3908 ++td->ip;
3909 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3910 break;
3911 case CEE_CONV_R4:
3912 CHECK_STACK (td, 1);
3913 switch (td->sp [-1].type) {
3914 case STACK_TYPE_R8:
3915 interp_add_ins (td, MINT_CONV_R4_R8);
3916 break;
3917 case STACK_TYPE_I8:
3918 interp_add_ins (td, MINT_CONV_R4_I8);
3919 break;
3920 case STACK_TYPE_I4:
3921 interp_add_ins (td, MINT_CONV_R4_I4);
3922 break;
3923 case STACK_TYPE_R4:
3924 /* no-op */
3925 break;
3926 default:
3927 g_assert_not_reached ();
3929 ++td->ip;
3930 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
3931 break;
3932 case CEE_CONV_R8:
3933 CHECK_STACK (td, 1);
3934 switch (td->sp [-1].type) {
3935 case STACK_TYPE_I4:
3936 interp_add_ins (td, MINT_CONV_R8_I4);
3937 break;
3938 case STACK_TYPE_I8:
3939 interp_add_ins (td, MINT_CONV_R8_I8);
3940 break;
3941 case STACK_TYPE_R4:
3942 interp_add_ins (td, MINT_CONV_R8_R4);
3943 break;
3944 case STACK_TYPE_R8:
3945 break;
3946 default:
3947 g_assert_not_reached ();
3949 ++td->ip;
3950 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3951 break;
3952 case CEE_CONV_U8:
3953 CHECK_STACK (td, 1);
3954 switch (td->sp [-1].type) {
3955 case STACK_TYPE_I4:
3956 if (interp_ins_is_ldc (td->last_ins) && !td->is_bb_start [in_offset]) {
3957 gint64 ct = (guint32)interp_ldc_i4_get_const (td->last_ins);
3958 interp_remove_ins (td, td->last_ins);
3960 interp_add_ins (td, MINT_LDC_I8);
3961 WRITE64_INS (td->last_ins, 0, &ct);
3962 } else {
3963 interp_add_ins (td, MINT_CONV_U8_I4);
3965 break;
3966 case STACK_TYPE_I8:
3967 break;
3968 case STACK_TYPE_R4:
3969 interp_add_ins (td, MINT_CONV_U8_R4);
3970 break;
3971 case STACK_TYPE_R8:
3972 interp_add_ins (td, MINT_CONV_U8_R8);
3973 break;
3974 case STACK_TYPE_MP:
3975 #if SIZEOF_VOID_P == 4
3976 interp_add_ins (td, MINT_CONV_U8_I4);
3977 #endif
3978 break;
3979 default:
3980 g_assert_not_reached ();
3982 ++td->ip;
3983 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3984 break;
3985 case CEE_CPOBJ: {
3986 CHECK_STACK (td, 2);
3988 token = read32 (td->ip + 1);
3989 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
3990 goto_if_nok (error, exit);
3992 if (m_class_is_valuetype (klass)) {
3993 int mt = mint_type (m_class_get_byval_arg (klass));
3994 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_CPOBJ_VT : MINT_CPOBJ);
3995 td->last_ins->data [0] = get_data_item_index(td, klass);
3996 } else {
3997 interp_add_ins (td, MINT_LDIND_REF);
3998 interp_add_ins (td, MINT_STIND_REF);
4000 td->ip += 5;
4001 td->sp -= 2;
4002 break;
4004 case CEE_LDOBJ: {
4005 CHECK_STACK (td, 1);
4007 token = read32 (td->ip + 1);
4009 if (method->wrapper_type != MONO_WRAPPER_NONE)
4010 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4011 else {
4012 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4013 goto_if_nok (error, exit);
4016 interp_emit_ldobj (td, klass);
4018 td->ip += 5;
4019 BARRIER_IF_VOLATILE (td);
4020 break;
4022 case CEE_LDSTR: {
4023 token = mono_metadata_token_index (read32 (td->ip + 1));
4024 td->ip += 5;
4025 if (method->wrapper_type == MONO_WRAPPER_NONE) {
4026 MonoString *s = mono_ldstr_checked (domain, image, token, error);
4027 goto_if_nok (error, exit);
4028 /* GC won't scan code stream, but reference is held by metadata
4029 * machinery so we are good here */
4030 interp_add_ins (td, MINT_LDSTR);
4031 td->last_ins->data [0] = get_data_item_index (td, s);
4032 } else {
4033 /* defer allocation to execution-time */
4034 interp_add_ins (td, MINT_LDSTR_TOKEN);
4035 td->last_ins->data [0] = get_data_item_index (td, GUINT_TO_POINTER (token));
4037 PUSH_TYPE(td, STACK_TYPE_O, mono_defaults.string_class);
4038 break;
4040 case CEE_NEWOBJ: {
4041 MonoMethod *m;
4042 MonoMethodSignature *csignature;
4043 guint32 vt_stack_used = 0;
4044 guint32 vt_res_size = 0;
4046 td->ip++;
4047 token = read32 (td->ip);
4048 td->ip += 4;
4050 if (method->wrapper_type != MONO_WRAPPER_NONE)
4051 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
4052 else {
4053 m = mono_get_method_checked (image, token, NULL, generic_context, error);
4054 goto_if_nok (error, exit);
4057 csignature = mono_method_signature_internal (m);
4058 klass = m->klass;
4060 if (!mono_class_init_internal (klass)) {
4061 mono_error_set_for_class_failure (error, klass);
4062 goto_if_nok (error, exit);
4065 if (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_ABSTRACT) {
4066 char* full_name = mono_type_get_full_name (klass);
4067 mono_error_set_member_access (error, "Cannot create an abstract class: %s", full_name);
4068 g_free (full_name);
4069 goto_if_nok (error, exit);
4072 td->sp -= csignature->param_count;
4073 if (mono_class_is_magic_int (klass) || mono_class_is_magic_float (klass)) {
4074 #if SIZEOF_VOID_P == 8
4075 if (mono_class_is_magic_int (klass) && td->sp [0].type == STACK_TYPE_I4)
4076 interp_add_ins (td, MINT_CONV_I8_I4);
4077 else if (mono_class_is_magic_float (klass) && td->sp [0].type == STACK_TYPE_R4)
4078 interp_add_ins (td, MINT_CONV_R8_R4);
4079 #endif
4080 interp_add_ins (td, MINT_NEWOBJ_MAGIC);
4081 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4082 goto_if_nok (error, exit);
4084 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4085 } else {
4086 if (m_class_get_parent (klass) == mono_defaults.array_class) {
4087 interp_add_ins (td, MINT_NEWOBJ_ARRAY);
4088 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4089 td->last_ins->data [1] = csignature->param_count;
4090 } else if (m_class_get_image (klass) == mono_defaults.corlib &&
4091 !strcmp (m_class_get_name (m->klass), "ByReference`1") &&
4092 !strcmp (m->name, ".ctor")) {
4093 /* public ByReference(ref T value) */
4094 g_assert (csignature->hasthis && csignature->param_count == 1);
4095 interp_add_ins (td, MINT_INTRINS_BYREFERENCE_CTOR);
4096 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4097 } else if (klass != mono_defaults.string_class &&
4098 !mono_class_is_marshalbyref (klass) &&
4099 !mono_class_has_finalizer (klass) &&
4100 !m_class_has_weak_fields (klass)) {
4101 if (!m_class_is_valuetype (klass))
4102 interp_add_ins (td, MINT_NEWOBJ_FAST);
4103 else if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4104 interp_add_ins (td, MINT_NEWOBJ_VTST_FAST);
4105 else
4106 interp_add_ins (td, MINT_NEWOBJ_VT_FAST);
4108 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4109 td->last_ins->data [1] = csignature->param_count;
4111 if (!m_class_is_valuetype (klass)) {
4112 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
4113 goto_if_nok (error, exit);
4114 td->last_ins->data [2] = get_data_item_index (td, vtable);
4115 } else if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4116 td->last_ins->data [2] = mono_class_value_size (klass, NULL);
4118 } else {
4119 interp_add_ins (td, MINT_NEWOBJ);
4120 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4122 goto_if_nok (error, exit);
4124 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4125 vt_res_size = mono_class_value_size (klass, NULL);
4126 PUSH_VT (td, vt_res_size);
4128 for (i = 0; i < csignature->param_count; ++i) {
4129 int mt = mint_type(csignature->params [i]);
4130 if (mt == MINT_TYPE_VT) {
4131 MonoClass *k = mono_class_from_mono_type_internal (csignature->params [i]);
4132 gint32 size = mono_class_value_size (k, NULL);
4133 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4134 vt_stack_used += size;
4137 if (vt_stack_used != 0 || vt_res_size != 0) {
4138 interp_add_ins (td, MINT_VTRESULT);
4139 td->last_ins->data [0] = vt_res_size;
4140 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
4141 td->vt_sp -= vt_stack_used;
4143 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4145 break;
4147 case CEE_CASTCLASS:
4148 case CEE_ISINST: {
4149 gboolean isinst_instr = *td->ip == CEE_ISINST;
4150 CHECK_STACK (td, 1);
4151 token = read32 (td->ip + 1);
4152 klass = mini_get_class (method, token, generic_context);
4153 CHECK_TYPELOAD (klass);
4154 interp_handle_isinst (td, klass, isinst_instr);
4155 if (!isinst_instr)
4156 td->sp [-1].klass = klass;
4157 break;
4159 case CEE_CONV_R_UN:
4160 switch (td->sp [-1].type) {
4161 case STACK_TYPE_R8:
4162 break;
4163 case STACK_TYPE_I8:
4164 interp_add_ins (td, MINT_CONV_R_UN_I8);
4165 break;
4166 case STACK_TYPE_I4:
4167 interp_add_ins (td, MINT_CONV_R_UN_I4);
4168 break;
4169 default:
4170 g_assert_not_reached ();
4172 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4173 ++td->ip;
4174 break;
4175 case CEE_UNBOX:
4176 CHECK_STACK (td, 1);
4177 token = read32 (td->ip + 1);
4179 if (method->wrapper_type != MONO_WRAPPER_NONE)
4180 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4181 else {
4182 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4183 goto_if_nok (error, exit);
4186 if (mono_class_is_nullable (klass)) {
4187 MonoMethod *target_method;
4188 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4189 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4190 else
4191 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4192 goto_if_nok (error, exit);
4193 /* td->ip is incremented by interp_transform_call */
4194 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
4195 goto exit;
4197 * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type
4198 * We create a local variable in the frame so that we can fetch its address.
4200 int local_offset = create_interp_local (td, m_class_get_byval_arg (klass));
4201 store_local_general (td, local_offset, m_class_get_byval_arg (klass));
4202 interp_add_ins (td, MINT_LDLOCA_S);
4203 td->last_ins->data [0] = local_offset;
4204 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
4205 } else {
4206 interp_add_ins (td, MINT_UNBOX);
4207 td->last_ins->data [0] = get_data_item_index (td, klass);
4208 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
4209 td->ip += 5;
4211 break;
4212 case CEE_UNBOX_ANY:
4213 CHECK_STACK (td, 1);
4214 token = read32 (td->ip + 1);
4216 klass = mini_get_class (method, token, generic_context);
4217 CHECK_TYPELOAD (klass);
4219 if (mini_type_is_reference (m_class_get_byval_arg (klass))) {
4220 int mt = mint_type (m_class_get_byval_arg (klass));
4221 interp_handle_isinst (td, klass, FALSE);
4222 SET_TYPE (td->sp - 1, stack_type [mt], klass);
4223 } else if (mono_class_is_nullable (klass)) {
4224 MonoMethod *target_method;
4225 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4226 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4227 else
4228 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4229 goto_if_nok (error, exit);
4230 /* td->ip is incremented by interp_transform_call */
4231 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
4232 goto exit;
4233 } else {
4234 interp_add_ins (td, MINT_UNBOX);
4235 td->last_ins->data [0] = get_data_item_index (td, klass);
4237 interp_emit_ldobj (td, klass);
4239 td->ip += 5;
4242 break;
4243 case CEE_THROW:
4244 INLINE_FAILURE;
4245 CHECK_STACK (td, 1);
4246 SIMPLE_OP (td, MINT_THROW);
4247 td->sp = td->stack;
4248 break;
4249 case CEE_LDFLDA: {
4250 CHECK_STACK (td, 1);
4251 token = read32 (td->ip + 1);
4252 field = interp_field_from_token (method, token, &klass, generic_context, error);
4253 goto_if_nok (error, exit);
4254 MonoType *ftype = mono_field_get_type_internal (field);
4255 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4256 mono_class_init_internal (klass);
4257 #ifndef DISABLE_REMOTING
4258 if (m_class_get_marshalbyref (klass) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
4259 g_assert (!is_static);
4260 int offset = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4262 interp_add_ins (td, MINT_MONO_LDPTR);
4263 td->last_ins->data [0] = get_data_item_index (td, klass);
4264 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4265 interp_add_ins (td, MINT_MONO_LDPTR);
4266 td->last_ins->data [0] = get_data_item_index (td, field);
4267 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4268 interp_add_ins (td, MINT_LDC_I4);
4269 WRITE32_INS (td->last_ins, 0, &offset);
4270 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
4271 #if SIZEOF_VOID_P == 8
4272 interp_add_ins (td, MINT_CONV_I8_I4);
4273 #endif
4275 MonoMethod *wrapper = mono_marshal_get_ldflda_wrapper (field->type);
4276 /* td->ip is incremented by interp_transform_call */
4277 if (!interp_transform_call (td, method, wrapper, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
4278 goto exit;
4279 } else
4280 #endif
4282 if (is_static) {
4283 interp_add_ins (td, MINT_POP);
4284 td->last_ins->data [0] = 0;
4285 interp_emit_ldsflda (td, field, error);
4286 goto_if_nok (error, exit);
4287 } else {
4288 if ((td->sp - 1)->type == STACK_TYPE_O) {
4289 interp_add_ins (td, MINT_LDFLDA);
4290 } else {
4291 g_assert ((td->sp -1)->type == STACK_TYPE_MP);
4292 interp_add_ins (td, MINT_LDFLDA_UNSAFE);
4294 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4296 td->ip += 5;
4298 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4299 break;
4301 case CEE_LDFLD: {
4302 CHECK_STACK (td, 1);
4303 token = read32 (td->ip + 1);
4304 field = interp_field_from_token (method, token, &klass, generic_context, error);
4305 goto_if_nok (error, exit);
4306 MonoType *ftype = mono_field_get_type_internal (field);
4307 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4308 mono_class_init_internal (klass);
4310 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4311 mt = mint_type (m_class_get_byval_arg (field_klass));
4312 #ifndef DISABLE_REMOTING
4313 if (m_class_get_marshalbyref (klass)) {
4314 g_assert (!is_static);
4315 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDRMFLD_VT : MINT_LDRMFLD);
4316 td->last_ins->data [0] = get_data_item_index (td, field);
4317 } else
4318 #endif
4320 if (is_static) {
4321 interp_add_ins (td, MINT_POP);
4322 td->last_ins->data [0] = 0;
4323 interp_emit_sfld_access (td, field, field_klass, mt, TRUE, error);
4324 goto_if_nok (error, exit);
4325 } else {
4326 int opcode = MINT_LDFLD_I1 + mt - MINT_TYPE_I1;
4327 #ifdef NO_UNALIGNED_ACCESS
4328 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4329 opcode = get_unaligned_opcode (opcode);
4330 #endif
4331 interp_add_ins (td, opcode);
4332 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4333 if (mt == MINT_TYPE_VT) {
4334 int size = mono_class_value_size (field_klass, NULL);
4335 WRITE32_INS (td->last_ins, 1, &size);
4339 if (mt == MINT_TYPE_VT) {
4340 int size = mono_class_value_size (field_klass, NULL);
4341 PUSH_VT (td, size);
4343 if (td->sp [-1].type == STACK_TYPE_VT) {
4344 int size = mono_class_value_size (klass, NULL);
4345 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4346 int field_vt_size = 0;
4347 if (mt == MINT_TYPE_VT) {
4349 * Pop the loaded field from the vtstack (it will still be present
4350 * at the same vtstack address) and we will load it in place of the
4351 * containing valuetype with the second MINT_VTRESULT.
4353 field_vt_size = mono_class_value_size (field_klass, NULL);
4354 field_vt_size = ALIGN_TO (field_vt_size, MINT_VT_ALIGNMENT);
4355 interp_add_ins (td, MINT_VTRESULT);
4356 td->last_ins->data [0] = 0;
4357 WRITE32_INS (td->last_ins, 1, &field_vt_size);
4359 td->vt_sp -= size;
4360 interp_add_ins (td, MINT_VTRESULT);
4361 td->last_ins->data [0] = field_vt_size;
4362 WRITE32_INS (td->last_ins, 1, &size);
4364 td->ip += 5;
4365 SET_TYPE (td->sp - 1, stack_type [mt], field_klass);
4366 BARRIER_IF_VOLATILE (td);
4367 break;
4369 case CEE_STFLD: {
4370 CHECK_STACK (td, 2);
4371 token = read32 (td->ip + 1);
4372 field = interp_field_from_token (method, token, &klass, generic_context, error);
4373 goto_if_nok (error, exit);
4374 MonoType *ftype = mono_field_get_type_internal (field);
4375 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4376 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4377 mono_class_init_internal (klass);
4378 mt = mint_type (ftype);
4380 BARRIER_IF_VOLATILE (td);
4382 #ifndef DISABLE_REMOTING
4383 if (m_class_get_marshalbyref (klass)) {
4384 g_assert (!is_static);
4385 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_STRMFLD_VT : MINT_STRMFLD);
4386 td->last_ins->data [0] = get_data_item_index (td, field);
4387 } else
4388 #endif
4390 if (is_static) {
4391 interp_add_ins (td, MINT_POP);
4392 td->last_ins->data [0] = 1;
4393 interp_emit_sfld_access (td, field, field_klass, mt, FALSE, error);
4394 goto_if_nok (error, exit);
4396 /* the vtable of the field might not be initialized at this point */
4397 mono_class_vtable_checked (domain, field_klass, error);
4398 goto_if_nok (error, exit);
4399 } else {
4400 int opcode = MINT_STFLD_I1 + mt - MINT_TYPE_I1;
4401 #ifdef NO_UNALIGNED_ACCESS
4402 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4403 opcode = get_unaligned_opcode (opcode);
4404 #endif
4405 interp_add_ins (td, opcode);
4406 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4407 if (mt == MINT_TYPE_VT) {
4408 /* the vtable of the field might not be initialized at this point */
4409 mono_class_vtable_checked (domain, field_klass, error);
4410 goto_if_nok (error, exit);
4412 td->last_ins->data [1] = get_data_item_index (td, field_klass);
4416 if (mt == MINT_TYPE_VT) {
4417 int size = mono_class_value_size (field_klass, NULL);
4418 POP_VT (td, size);
4420 td->ip += 5;
4421 td->sp -= 2;
4422 break;
4424 case CEE_LDSFLDA: {
4425 token = read32 (td->ip + 1);
4426 field = interp_field_from_token (method, token, &klass, generic_context, error);
4427 goto_if_nok (error, exit);
4428 interp_emit_ldsflda (td, field, error);
4429 goto_if_nok (error, exit);
4430 td->ip += 5;
4431 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
4432 break;
4434 case CEE_LDSFLD: {
4435 token = read32 (td->ip + 1);
4436 field = interp_field_from_token (method, token, &klass, generic_context, error);
4437 goto_if_nok (error, exit);
4438 MonoType *ftype = mono_field_get_type_internal (field);
4439 mt = mint_type (ftype);
4440 klass = mono_class_from_mono_type_internal (ftype);
4442 interp_emit_sfld_access (td, field, klass, mt, TRUE, error);
4443 goto_if_nok (error, exit);
4445 if (mt == MINT_TYPE_VT) {
4446 int size = mono_class_value_size (klass, NULL);
4447 PUSH_VT(td, size);
4449 td->ip += 5;
4450 PUSH_TYPE(td, stack_type [mt], klass);
4451 break;
4453 case CEE_STSFLD: {
4454 CHECK_STACK (td, 1);
4455 token = read32 (td->ip + 1);
4456 field = interp_field_from_token (method, token, &klass, generic_context, error);
4457 goto_if_nok (error, exit);
4458 MonoType *ftype = mono_field_get_type_internal (field);
4459 mt = mint_type (ftype);
4461 /* the vtable of the field might not be initialized at this point */
4462 MonoClass *fld_klass = mono_class_from_mono_type_internal (ftype);
4463 mono_class_vtable_checked (domain, fld_klass, error);
4464 goto_if_nok (error, exit);
4466 interp_emit_sfld_access (td, field, fld_klass, mt, FALSE, error);
4467 goto_if_nok (error, exit);
4469 if (mt == MINT_TYPE_VT) {
4470 int size = mono_class_value_size (fld_klass, NULL);
4471 POP_VT(td, size);
4473 td->ip += 5;
4474 --td->sp;
4475 break;
4477 case CEE_STOBJ: {
4478 token = read32 (td->ip + 1);
4480 if (method->wrapper_type != MONO_WRAPPER_NONE)
4481 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4482 else
4483 klass = mini_get_class (method, token, generic_context);
4484 CHECK_TYPELOAD (klass);
4486 BARRIER_IF_VOLATILE (td);
4488 interp_emit_stobj (td, klass);
4490 td->ip += 5;
4491 break;
4493 case CEE_CONV_OVF_I_UN:
4494 case CEE_CONV_OVF_U_UN:
4495 CHECK_STACK (td, 1);
4496 switch (td->sp [-1].type) {
4497 case STACK_TYPE_R8:
4498 #if SIZEOF_VOID_P == 8
4499 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4500 #else
4501 interp_add_ins (td, MINT_CONV_OVF_I4_UN_R8);
4502 #endif
4503 break;
4504 case STACK_TYPE_I8:
4505 #if SIZEOF_VOID_P == 4
4506 interp_add_ins (td, MINT_CONV_OVF_I4_UN_I8);
4507 #endif
4508 break;
4509 case STACK_TYPE_I4:
4510 #if SIZEOF_VOID_P == 8
4511 interp_add_ins (td, MINT_CONV_I8_U4);
4512 #elif SIZEOF_VOID_P == 4
4513 if (*td->ip == CEE_CONV_OVF_I_UN)
4514 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
4515 #endif
4516 break;
4517 default:
4518 g_assert_not_reached ();
4519 break;
4521 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4522 ++td->ip;
4523 break;
4524 case CEE_CONV_OVF_I8_UN:
4525 case CEE_CONV_OVF_U8_UN:
4526 CHECK_STACK (td, 1);
4527 switch (td->sp [-1].type) {
4528 case STACK_TYPE_R8:
4529 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4530 break;
4531 case STACK_TYPE_I8:
4532 if (*td->ip == CEE_CONV_OVF_I8_UN)
4533 interp_add_ins (td, MINT_CONV_OVF_I8_U8);
4534 break;
4535 case STACK_TYPE_I4:
4536 interp_add_ins (td, MINT_CONV_I8_U4);
4537 break;
4538 default:
4539 g_assert_not_reached ();
4540 break;
4542 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4543 ++td->ip;
4544 break;
4545 case CEE_BOX: {
4546 int size;
4547 CHECK_STACK (td, 1);
4548 token = read32 (td->ip + 1);
4549 if (method->wrapper_type != MONO_WRAPPER_NONE)
4550 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4551 else
4552 klass = mini_get_class (method, token, generic_context);
4553 CHECK_TYPELOAD (klass);
4555 if (mono_class_is_nullable (klass)) {
4556 MonoMethod *target_method = mono_class_get_method_from_name_checked (klass, "Box", 1, 0, error);
4557 goto_if_nok (error, exit);
4558 /* td->ip is incremented by interp_transform_call */
4559 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
4560 goto exit;
4561 } else if (!m_class_is_valuetype (klass)) {
4562 /* already boxed, do nothing. */
4563 td->ip += 5;
4564 } else {
4565 if (G_UNLIKELY (m_class_is_byreflike (klass))) {
4566 mono_error_set_bad_image (error, image, "Cannot box IsByRefLike type '%s.%s'", m_class_get_name_space (klass), m_class_get_name (klass));
4567 goto exit;
4569 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4570 size = mono_class_value_size (klass, NULL);
4571 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4572 td->vt_sp -= size;
4573 } else if (td->sp [-1].type == STACK_TYPE_R8 && m_class_get_byval_arg (klass)->type == MONO_TYPE_R4) {
4574 interp_add_ins (td, MINT_CONV_R4_R8);
4576 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
4577 goto_if_nok (error, exit);
4579 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4580 interp_add_ins (td, MINT_BOX_VT);
4581 else
4582 interp_add_ins (td, MINT_BOX);
4583 td->last_ins->data [0] = get_data_item_index (td, vtable);
4584 td->last_ins->data [1] = 0;
4585 SET_TYPE(td->sp - 1, STACK_TYPE_O, klass);
4586 td->ip += 5;
4589 break;
4591 case CEE_NEWARR: {
4592 CHECK_STACK (td, 1);
4593 token = read32 (td->ip + 1);
4595 if (method->wrapper_type != MONO_WRAPPER_NONE)
4596 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4597 else
4598 klass = mini_get_class (method, token, generic_context);
4599 CHECK_TYPELOAD (klass);
4601 unsigned char lentype = (td->sp - 1)->type;
4602 if (lentype == STACK_TYPE_I8) {
4603 /* mimic mini behaviour */
4604 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
4605 } else {
4606 g_assert (lentype == STACK_TYPE_I4);
4607 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
4609 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
4610 interp_add_ins (td, MINT_NEWARR);
4611 td->last_ins->data [0] = get_data_item_index (td, klass);
4612 SET_TYPE (td->sp - 1, STACK_TYPE_O, klass);
4613 td->ip += 5;
4614 break;
4616 case CEE_LDLEN:
4617 CHECK_STACK (td, 1);
4618 SIMPLE_OP (td, MINT_LDLEN);
4619 #ifdef MONO_BIG_ARRAYS
4620 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
4621 #else
4622 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
4623 #endif
4624 break;
4625 case CEE_LDELEMA:
4626 CHECK_STACK (td, 2);
4627 ENSURE_I4 (td, 1);
4628 token = read32 (td->ip + 1);
4630 if (method->wrapper_type != MONO_WRAPPER_NONE)
4631 klass = (MonoClass *) mono_method_get_wrapper_data (method, token);
4632 else
4633 klass = mini_get_class (method, token, generic_context);
4635 CHECK_TYPELOAD (klass);
4637 if (!m_class_is_valuetype (klass) && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
4639 * Check the class for failures before the type check, which can
4640 * throw other exceptions.
4642 mono_class_setup_vtable (klass);
4643 CHECK_TYPELOAD (klass);
4644 interp_add_ins (td, MINT_LDELEMA_TC);
4645 } else {
4646 interp_add_ins (td, MINT_LDELEMA);
4648 td->last_ins->data [0] = get_data_item_index (td, klass);
4649 /* according to spec, ldelema bytecode is only used for 1-dim arrays */
4650 td->last_ins->data [1] = 2;
4651 readonly = FALSE;
4653 td->ip += 5;
4654 --td->sp;
4655 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4656 break;
4657 case CEE_LDELEM_I1:
4658 CHECK_STACK (td, 2);
4659 ENSURE_I4 (td, 1);
4660 SIMPLE_OP (td, MINT_LDELEM_I1);
4661 --td->sp;
4662 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4663 break;
4664 case CEE_LDELEM_U1:
4665 CHECK_STACK (td, 2);
4666 ENSURE_I4 (td, 1);
4667 SIMPLE_OP (td, MINT_LDELEM_U1);
4668 --td->sp;
4669 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4670 break;
4671 case CEE_LDELEM_I2:
4672 CHECK_STACK (td, 2);
4673 ENSURE_I4 (td, 1);
4674 SIMPLE_OP (td, MINT_LDELEM_I2);
4675 --td->sp;
4676 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4677 break;
4678 case CEE_LDELEM_U2:
4679 CHECK_STACK (td, 2);
4680 ENSURE_I4 (td, 1);
4681 SIMPLE_OP (td, MINT_LDELEM_U2);
4682 --td->sp;
4683 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4684 break;
4685 case CEE_LDELEM_I4:
4686 CHECK_STACK (td, 2);
4687 ENSURE_I4 (td, 1);
4688 SIMPLE_OP (td, MINT_LDELEM_I4);
4689 --td->sp;
4690 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4691 break;
4692 case CEE_LDELEM_U4:
4693 CHECK_STACK (td, 2);
4694 ENSURE_I4 (td, 1);
4695 SIMPLE_OP (td, MINT_LDELEM_U4);
4696 --td->sp;
4697 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4698 break;
4699 case CEE_LDELEM_I8:
4700 CHECK_STACK (td, 2);
4701 ENSURE_I4 (td, 1);
4702 SIMPLE_OP (td, MINT_LDELEM_I8);
4703 --td->sp;
4704 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4705 break;
4706 case CEE_LDELEM_I:
4707 CHECK_STACK (td, 2);
4708 ENSURE_I4 (td, 1);
4709 SIMPLE_OP (td, MINT_LDELEM_I);
4710 --td->sp;
4711 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4712 break;
4713 case CEE_LDELEM_R4:
4714 CHECK_STACK (td, 2);
4715 ENSURE_I4 (td, 1);
4716 SIMPLE_OP (td, MINT_LDELEM_R4);
4717 --td->sp;
4718 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4719 break;
4720 case CEE_LDELEM_R8:
4721 CHECK_STACK (td, 2);
4722 ENSURE_I4 (td, 1);
4723 SIMPLE_OP (td, MINT_LDELEM_R8);
4724 --td->sp;
4725 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4726 break;
4727 case CEE_LDELEM_REF:
4728 CHECK_STACK (td, 2);
4729 ENSURE_I4 (td, 1);
4730 SIMPLE_OP (td, MINT_LDELEM_REF);
4731 --td->sp;
4732 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
4733 break;
4734 case CEE_LDELEM:
4735 CHECK_STACK (td, 2);
4736 token = read32 (td->ip + 1);
4737 klass = mini_get_class (method, token, generic_context);
4738 CHECK_TYPELOAD (klass);
4739 switch (mint_type (m_class_get_byval_arg (klass))) {
4740 case MINT_TYPE_I1:
4741 ENSURE_I4 (td, 1);
4742 SIMPLE_OP (td, MINT_LDELEM_I1);
4743 --td->sp;
4744 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4745 break;
4746 case MINT_TYPE_U1:
4747 ENSURE_I4 (td, 1);
4748 SIMPLE_OP (td, MINT_LDELEM_U1);
4749 --td->sp;
4750 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4751 break;
4752 case MINT_TYPE_U2:
4753 ENSURE_I4 (td, 1);
4754 SIMPLE_OP (td, MINT_LDELEM_U2);
4755 --td->sp;
4756 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4757 break;
4758 case MINT_TYPE_I2:
4759 ENSURE_I4 (td, 1);
4760 SIMPLE_OP (td, MINT_LDELEM_I2);
4761 --td->sp;
4762 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4763 break;
4764 case MINT_TYPE_I4:
4765 ENSURE_I4 (td, 1);
4766 SIMPLE_OP (td, MINT_LDELEM_I4);
4767 --td->sp;
4768 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4769 break;
4770 case MINT_TYPE_I8:
4771 ENSURE_I4 (td, 1);
4772 SIMPLE_OP (td, MINT_LDELEM_I8);
4773 --td->sp;
4774 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4775 break;
4776 case MINT_TYPE_R4:
4777 ENSURE_I4 (td, 1);
4778 SIMPLE_OP (td, MINT_LDELEM_R4);
4779 --td->sp;
4780 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4781 break;
4782 case MINT_TYPE_R8:
4783 ENSURE_I4 (td, 1);
4784 SIMPLE_OP (td, MINT_LDELEM_R8);
4785 --td->sp;
4786 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4787 break;
4788 case MINT_TYPE_O:
4789 ENSURE_I4 (td, 1);
4790 SIMPLE_OP (td, MINT_LDELEM_REF);
4791 --td->sp;
4792 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
4793 break;
4794 case MINT_TYPE_VT: {
4795 int size = mono_class_value_size (klass, NULL);
4796 ENSURE_I4 (td, 1);
4797 SIMPLE_OP (td, MINT_LDELEM_VT);
4798 WRITE32_INS (td->last_ins, 0, &size);
4799 --td->sp;
4800 SET_TYPE (td->sp - 1, STACK_TYPE_VT, klass);
4801 PUSH_VT (td, size);
4802 break;
4804 default: {
4805 GString *res = g_string_new ("");
4806 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
4807 g_print ("LDELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
4808 g_string_free (res, TRUE);
4809 g_assert (0);
4810 break;
4813 td->ip += 4;
4814 break;
4815 case CEE_STELEM_I:
4816 CHECK_STACK (td, 3);
4817 ENSURE_I4 (td, 2);
4818 SIMPLE_OP (td, MINT_STELEM_I);
4819 td->sp -= 3;
4820 break;
4821 case CEE_STELEM_I1:
4822 CHECK_STACK (td, 3);
4823 ENSURE_I4 (td, 2);
4824 SIMPLE_OP (td, MINT_STELEM_I1);
4825 td->sp -= 3;
4826 break;
4827 case CEE_STELEM_I2:
4828 CHECK_STACK (td, 3);
4829 ENSURE_I4 (td, 2);
4830 SIMPLE_OP (td, MINT_STELEM_I2);
4831 td->sp -= 3;
4832 break;
4833 case CEE_STELEM_I4:
4834 CHECK_STACK (td, 3);
4835 ENSURE_I4 (td, 2);
4836 SIMPLE_OP (td, MINT_STELEM_I4);
4837 td->sp -= 3;
4838 break;
4839 case CEE_STELEM_I8:
4840 CHECK_STACK (td, 3);
4841 ENSURE_I4 (td, 2);
4842 SIMPLE_OP (td, MINT_STELEM_I8);
4843 td->sp -= 3;
4844 break;
4845 case CEE_STELEM_R4:
4846 CHECK_STACK (td, 3);
4847 ENSURE_I4 (td, 2);
4848 SIMPLE_OP (td, MINT_STELEM_R4);
4849 td->sp -= 3;
4850 break;
4851 case CEE_STELEM_R8:
4852 CHECK_STACK (td, 3);
4853 ENSURE_I4 (td, 2);
4854 SIMPLE_OP (td, MINT_STELEM_R8);
4855 td->sp -= 3;
4856 break;
4857 case CEE_STELEM_REF:
4858 CHECK_STACK (td, 3);
4859 ENSURE_I4 (td, 2);
4860 SIMPLE_OP (td, MINT_STELEM_REF);
4861 td->sp -= 3;
4862 break;
4863 case CEE_STELEM:
4864 CHECK_STACK (td, 3);
4865 ENSURE_I4 (td, 2);
4866 token = read32 (td->ip + 1);
4867 klass = mini_get_class (method, token, generic_context);
4868 CHECK_TYPELOAD (klass);
4869 switch (mint_type (m_class_get_byval_arg (klass))) {
4870 case MINT_TYPE_I1:
4871 SIMPLE_OP (td, MINT_STELEM_I1);
4872 break;
4873 case MINT_TYPE_U1:
4874 SIMPLE_OP (td, MINT_STELEM_U1);
4875 break;
4876 case MINT_TYPE_I2:
4877 SIMPLE_OP (td, MINT_STELEM_I2);
4878 break;
4879 case MINT_TYPE_U2:
4880 SIMPLE_OP (td, MINT_STELEM_U2);
4881 break;
4882 case MINT_TYPE_I4:
4883 SIMPLE_OP (td, MINT_STELEM_I4);
4884 break;
4885 case MINT_TYPE_I8:
4886 SIMPLE_OP (td, MINT_STELEM_I8);
4887 break;
4888 case MINT_TYPE_R4:
4889 SIMPLE_OP (td, MINT_STELEM_R4);
4890 break;
4891 case MINT_TYPE_R8:
4892 SIMPLE_OP (td, MINT_STELEM_R8);
4893 break;
4894 case MINT_TYPE_O:
4895 SIMPLE_OP (td, MINT_STELEM_REF);
4896 break;
4897 case MINT_TYPE_VT: {
4898 int size = mono_class_value_size (klass, NULL);
4899 SIMPLE_OP (td, MINT_STELEM_VT);
4900 td->last_ins->data [0] = get_data_item_index (td, klass);
4901 WRITE32_INS (td->last_ins, 1, &size);
4902 POP_VT (td, size);
4903 break;
4905 default: {
4906 GString *res = g_string_new ("");
4907 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
4908 g_print ("STELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
4909 g_string_free (res, TRUE);
4910 g_assert (0);
4911 break;
4914 td->ip += 4;
4915 td->sp -= 3;
4916 break;
4917 #if 0
4918 case CEE_CONV_OVF_U1:
4920 case CEE_CONV_OVF_I8:
4922 #if SIZEOF_VOID_P == 8
4923 case CEE_CONV_OVF_U:
4924 #endif
4925 #endif
4926 case CEE_CKFINITE:
4927 CHECK_STACK (td, 1);
4928 SIMPLE_OP (td, MINT_CKFINITE);
4929 break;
4930 case CEE_MKREFANY:
4931 CHECK_STACK (td, 1);
4933 token = read32 (td->ip + 1);
4934 klass = mini_get_class (method, token, generic_context);
4935 CHECK_TYPELOAD (klass);
4937 interp_add_ins (td, MINT_MKREFANY);
4938 td->last_ins->data [0] = get_data_item_index (td, klass);
4940 td->ip += 5;
4941 PUSH_VT (td, sizeof (MonoTypedRef));
4942 SET_TYPE(td->sp - 1, STACK_TYPE_VT, mono_defaults.typed_reference_class);
4943 break;
4944 case CEE_REFANYVAL: {
4945 CHECK_STACK (td, 1);
4947 token = read32 (td->ip + 1);
4948 klass = mini_get_class (method, token, generic_context);
4949 CHECK_TYPELOAD (klass);
4951 interp_add_ins (td, MINT_REFANYVAL);
4952 td->last_ins->data [0] = get_data_item_index (td, klass);
4954 POP_VT (td, sizeof (MonoTypedRef));
4955 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4957 td->ip += 5;
4958 break;
4960 case CEE_CONV_OVF_I1:
4961 case CEE_CONV_OVF_I1_UN: {
4962 gboolean is_un = *td->ip == CEE_CONV_OVF_I1_UN;
4963 CHECK_STACK (td, 1);
4964 switch (td->sp [-1].type) {
4965 case STACK_TYPE_R8:
4966 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_UN_R8 : MINT_CONV_OVF_I1_R8);
4967 break;
4968 case STACK_TYPE_I4:
4969 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U4 : MINT_CONV_OVF_I1_I4);
4970 break;
4971 case STACK_TYPE_I8:
4972 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U8 : MINT_CONV_OVF_I1_I8);
4973 break;
4974 default:
4975 g_assert_not_reached ();
4977 ++td->ip;
4978 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4979 break;
4981 case CEE_CONV_OVF_U1:
4982 case CEE_CONV_OVF_U1_UN:
4983 CHECK_STACK (td, 1);
4984 switch (td->sp [-1].type) {
4985 case STACK_TYPE_R8:
4986 interp_add_ins (td, MINT_CONV_OVF_U1_R8);
4987 break;
4988 case STACK_TYPE_I4:
4989 interp_add_ins (td, MINT_CONV_OVF_U1_I4);
4990 break;
4991 case STACK_TYPE_I8:
4992 interp_add_ins (td, MINT_CONV_OVF_U1_I8);
4993 break;
4994 default:
4995 g_assert_not_reached ();
4997 ++td->ip;
4998 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4999 break;
5000 case CEE_CONV_OVF_I2:
5001 case CEE_CONV_OVF_I2_UN: {
5002 gboolean is_un = *td->ip == CEE_CONV_OVF_I2_UN;
5003 CHECK_STACK (td, 1);
5004 switch (td->sp [-1].type) {
5005 case STACK_TYPE_R8:
5006 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_UN_R8 : MINT_CONV_OVF_I2_R8);
5007 break;
5008 case STACK_TYPE_I4:
5009 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U4 : MINT_CONV_OVF_I2_I4);
5010 break;
5011 case STACK_TYPE_I8:
5012 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U8 : MINT_CONV_OVF_I2_I8);
5013 break;
5014 default:
5015 g_assert_not_reached ();
5017 ++td->ip;
5018 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5019 break;
5021 case CEE_CONV_OVF_U2_UN:
5022 case CEE_CONV_OVF_U2:
5023 CHECK_STACK (td, 1);
5024 switch (td->sp [-1].type) {
5025 case STACK_TYPE_R8:
5026 interp_add_ins (td, MINT_CONV_OVF_U2_R8);
5027 break;
5028 case STACK_TYPE_I4:
5029 interp_add_ins (td, MINT_CONV_OVF_U2_I4);
5030 break;
5031 case STACK_TYPE_I8:
5032 interp_add_ins (td, MINT_CONV_OVF_U2_I8);
5033 break;
5034 default:
5035 g_assert_not_reached ();
5037 ++td->ip;
5038 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5039 break;
5040 #if SIZEOF_VOID_P == 4
5041 case CEE_CONV_OVF_I:
5042 #endif
5043 case CEE_CONV_OVF_I4:
5044 case CEE_CONV_OVF_I4_UN:
5045 CHECK_STACK (td, 1);
5046 switch (td->sp [-1].type) {
5047 case STACK_TYPE_R4:
5048 interp_add_ins (td, MINT_CONV_OVF_I4_R4);
5049 break;
5050 case STACK_TYPE_R8:
5051 interp_add_ins (td, MINT_CONV_OVF_I4_R8);
5052 break;
5053 case STACK_TYPE_I4:
5054 if (*td->ip == CEE_CONV_OVF_I4_UN)
5055 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
5056 break;
5057 case STACK_TYPE_I8:
5058 if (*td->ip == CEE_CONV_OVF_I4_UN)
5059 interp_add_ins (td, MINT_CONV_OVF_I4_U8);
5060 else
5061 interp_add_ins (td, MINT_CONV_OVF_I4_I8);
5062 break;
5063 default:
5064 g_assert_not_reached ();
5066 ++td->ip;
5067 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5068 break;
5069 #if SIZEOF_VOID_P == 4
5070 case CEE_CONV_OVF_U:
5071 #endif
5072 case CEE_CONV_OVF_U4:
5073 case CEE_CONV_OVF_U4_UN:
5074 CHECK_STACK (td, 1);
5075 switch (td->sp [-1].type) {
5076 case STACK_TYPE_R4:
5077 interp_add_ins (td, MINT_CONV_OVF_U4_R4);
5078 break;
5079 case STACK_TYPE_R8:
5080 interp_add_ins (td, MINT_CONV_OVF_U4_R8);
5081 break;
5082 case STACK_TYPE_I4:
5083 if (*td->ip != CEE_CONV_OVF_U4_UN)
5084 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
5085 break;
5086 case STACK_TYPE_I8:
5087 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
5088 break;
5089 case STACK_TYPE_MP:
5090 interp_add_ins (td, MINT_CONV_OVF_U4_P);
5091 break;
5092 default:
5093 g_assert_not_reached ();
5095 ++td->ip;
5096 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5097 break;
5098 #if SIZEOF_VOID_P == 8
5099 case CEE_CONV_OVF_I:
5100 #endif
5101 case CEE_CONV_OVF_I8:
5102 CHECK_STACK (td, 1);
5103 switch (td->sp [-1].type) {
5104 case STACK_TYPE_R4:
5105 interp_add_ins (td, MINT_CONV_OVF_I8_R4);
5106 break;
5107 case STACK_TYPE_R8:
5108 interp_add_ins (td, MINT_CONV_OVF_I8_R8);
5109 break;
5110 case STACK_TYPE_I4:
5111 interp_add_ins (td, MINT_CONV_I8_I4);
5112 break;
5113 case STACK_TYPE_I8:
5114 break;
5115 default:
5116 g_assert_not_reached ();
5118 ++td->ip;
5119 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5120 break;
5121 #if SIZEOF_VOID_P == 8
5122 case CEE_CONV_OVF_U:
5123 #endif
5124 case CEE_CONV_OVF_U8:
5125 CHECK_STACK (td, 1);
5126 switch (td->sp [-1].type) {
5127 case STACK_TYPE_R4:
5128 interp_add_ins (td, MINT_CONV_OVF_U8_R4);
5129 break;
5130 case STACK_TYPE_R8:
5131 interp_add_ins (td, MINT_CONV_OVF_U8_R8);
5132 break;
5133 case STACK_TYPE_I4:
5134 interp_add_ins (td, MINT_CONV_OVF_U8_I4);
5135 break;
5136 case STACK_TYPE_I8:
5137 interp_add_ins (td, MINT_CONV_OVF_U8_I8);
5138 break;
5139 default:
5140 g_assert_not_reached ();
5142 ++td->ip;
5143 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5144 break;
5145 case CEE_LDTOKEN: {
5146 int size;
5147 gpointer handle;
5148 token = read32 (td->ip + 1);
5149 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
5150 handle = mono_method_get_wrapper_data (method, token);
5151 klass = (MonoClass *) mono_method_get_wrapper_data (method, token + 1);
5152 if (klass == mono_defaults.typehandle_class)
5153 handle = m_class_get_byval_arg ((MonoClass *) handle);
5155 if (generic_context) {
5156 handle = mono_class_inflate_generic_type_checked ((MonoType*)handle, generic_context, error);
5157 goto_if_nok (error, exit);
5159 } else {
5160 handle = mono_ldtoken_checked (image, token, &klass, generic_context, error);
5161 goto_if_nok (error, exit);
5163 mono_class_init_internal (klass);
5164 mt = mint_type (m_class_get_byval_arg (klass));
5165 g_assert (mt == MINT_TYPE_VT);
5166 size = mono_class_value_size (klass, NULL);
5167 g_assert (size == sizeof(gpointer));
5169 const unsigned char *next_ip = td->ip + 5;
5170 MonoMethod *cmethod;
5171 if (next_ip < end &&
5172 !td->is_bb_start [next_ip - td->il_code] &&
5173 (*next_ip == CEE_CALL || *next_ip == CEE_CALLVIRT) &&
5174 (cmethod = mono_get_method_checked (image, read32 (next_ip + 1), NULL, generic_context, error)) &&
5175 (cmethod->klass == mono_defaults.systemtype_class) &&
5176 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
5177 interp_add_ins (td, MINT_MONO_LDPTR);
5178 gpointer systype = mono_type_get_object_checked (domain, (MonoType*)handle, error);
5179 goto_if_nok (error, exit);
5180 td->last_ins->data [0] = get_data_item_index (td, systype);
5181 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5182 td->ip = next_ip + 5;
5183 } else {
5184 PUSH_VT (td, sizeof(gpointer));
5185 interp_add_ins (td, MINT_LDTOKEN);
5186 td->last_ins->data [0] = get_data_item_index (td, handle);
5187 PUSH_TYPE (td, stack_type [mt], klass);
5188 td->ip += 5;
5191 break;
5193 case CEE_ADD_OVF:
5194 binary_arith_op(td, MINT_ADD_OVF_I4);
5195 ++td->ip;
5196 break;
5197 case CEE_ADD_OVF_UN:
5198 binary_arith_op(td, MINT_ADD_OVF_UN_I4);
5199 ++td->ip;
5200 break;
5201 case CEE_MUL_OVF:
5202 binary_arith_op(td, MINT_MUL_OVF_I4);
5203 ++td->ip;
5204 break;
5205 case CEE_MUL_OVF_UN:
5206 binary_arith_op(td, MINT_MUL_OVF_UN_I4);
5207 ++td->ip;
5208 break;
5209 case CEE_SUB_OVF:
5210 binary_arith_op(td, MINT_SUB_OVF_I4);
5211 ++td->ip;
5212 break;
5213 case CEE_SUB_OVF_UN:
5214 binary_arith_op(td, MINT_SUB_OVF_UN_I4);
5215 ++td->ip;
5216 break;
5217 case CEE_ENDFINALLY: {
5218 g_assert (td->clause_indexes [in_offset] != -1);
5219 td->sp = td->stack;
5220 SIMPLE_OP (td, MINT_ENDFINALLY);
5221 td->last_ins->data [0] = td->clause_indexes [in_offset];
5222 break;
5224 case CEE_LEAVE:
5225 case CEE_LEAVE_S: {
5226 int offset;
5228 if (*td->ip == CEE_LEAVE)
5229 offset = 5 + read32 (td->ip + 1);
5230 else
5231 offset = 2 + (gint8)td->ip [1];
5233 td->sp = td->stack;
5234 if (td->clause_indexes [in_offset] != -1) {
5235 /* LEAVE instructions in catch clauses need to check for abort exceptions */
5236 handle_branch (td, MINT_LEAVE_S_CHECK, MINT_LEAVE_CHECK, offset);
5237 } else {
5238 handle_branch (td, MINT_LEAVE_S, MINT_LEAVE, offset);
5241 if (*td->ip == CEE_LEAVE)
5242 td->ip += 5;
5243 else
5244 td->ip += 2;
5245 break;
5247 case MONO_CUSTOM_PREFIX:
5248 ++td->ip;
5249 switch (*td->ip) {
5250 case CEE_MONO_RETHROW:
5251 CHECK_STACK (td, 1);
5252 SIMPLE_OP (td, MINT_MONO_RETHROW);
5253 td->sp = td->stack;
5254 break;
5256 case CEE_MONO_LD_DELEGATE_METHOD_PTR:
5257 --td->sp;
5258 td->ip += 1;
5259 interp_add_ins (td, MINT_LD_DELEGATE_METHOD_PTR);
5260 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5261 break;
5262 case CEE_MONO_CALLI_EXTRA_ARG:
5263 /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
5264 interp_add_ins (td, MINT_POP);
5265 td->last_ins->data [0] = 1;
5266 --td->sp;
5267 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE))
5268 goto exit;
5269 break;
5270 case CEE_MONO_JIT_ICALL_ADDR: {
5271 const guint32 token = read32 (td->ip + 1);
5272 td->ip += 5;
5273 const gconstpointer func = mono_find_jit_icall_info ((MonoJitICallId)token)->func;
5275 interp_add_ins (td, MINT_LDFTN);
5276 td->last_ins->data [0] = get_data_item_index (td, (gpointer)func);
5277 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5278 break;
5280 case CEE_MONO_ICALL: {
5281 MonoJitICallId const jit_icall_id = (MonoJitICallId)read32 (td->ip + 1);
5282 MonoJitICallInfo const * const info = mono_find_jit_icall_info (jit_icall_id);
5283 td->ip += 5;
5285 CHECK_STACK (td, info->sig->param_count);
5286 if (!strcmp (info->name, "mono_threads_attach_coop")) {
5287 rtm->needs_thread_attach = 1;
5289 /* attach needs two arguments, and has one return value: leave one element on the stack */
5290 interp_add_ins (td, MINT_POP);
5291 td->last_ins->data [0] = 0;
5292 } else if (!strcmp (info->name, "mono_threads_detach_coop")) {
5293 g_assert (rtm->needs_thread_attach);
5295 /* detach consumes two arguments, and no return value: drop both of them */
5296 interp_add_ins (td, MINT_POP);
5297 td->last_ins->data [0] = 0;
5298 interp_add_ins (td, MINT_POP);
5299 td->last_ins->data [0] = 0;
5300 } else {
5301 int const icall_op = interp_icall_op_for_sig (info->sig);
5302 g_assert (icall_op != -1);
5304 interp_add_ins (td, icall_op);
5305 // hash here is overkill
5306 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
5308 td->sp -= info->sig->param_count;
5310 if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
5311 int mt = mint_type (info->sig->ret);
5312 PUSH_SIMPLE_TYPE(td, stack_type [mt]);
5314 break;
5316 case CEE_MONO_VTADDR: {
5317 int size;
5318 CHECK_STACK (td, 1);
5319 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
5320 size = mono_class_native_size(td->sp [-1].klass, NULL);
5321 else
5322 size = mono_class_value_size(td->sp [-1].klass, NULL);
5323 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
5324 interp_add_ins (td, MINT_VTRESULT);
5325 td->last_ins->data [0] = 0;
5326 WRITE32_INS (td->last_ins, 1, &size);
5327 td->vt_sp -= size;
5328 ++td->ip;
5329 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5330 break;
5332 case CEE_MONO_LDPTR:
5333 case CEE_MONO_CLASSCONST:
5334 token = read32 (td->ip + 1);
5335 td->ip += 5;
5336 interp_add_ins (td, MINT_MONO_LDPTR);
5337 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5338 td->sp [0].type = STACK_TYPE_I;
5339 ++td->sp;
5340 break;
5341 case CEE_MONO_OBJADDR:
5342 CHECK_STACK (td, 1);
5343 ++td->ip;
5344 td->sp[-1].type = STACK_TYPE_MP;
5345 /* do nothing? */
5346 break;
5347 case CEE_MONO_NEWOBJ:
5348 token = read32 (td->ip + 1);
5349 td->ip += 5;
5350 interp_add_ins (td, MINT_MONO_NEWOBJ);
5351 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5352 td->sp [0].type = STACK_TYPE_O;
5353 ++td->sp;
5354 break;
5355 case CEE_MONO_RETOBJ:
5356 CHECK_STACK (td, 1);
5357 token = read32 (td->ip + 1);
5358 td->ip += 5;
5359 interp_add_ins (td, MINT_MONO_RETOBJ);
5360 td->sp--;
5362 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5364 /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/
5366 if (td->sp > td->stack)
5367 g_warning ("CEE_MONO_RETOBJ: more values on stack: %d", td->sp-td->stack);
5368 break;
5369 case CEE_MONO_LDNATIVEOBJ:
5370 token = read32 (td->ip + 1);
5371 td->ip += 5;
5372 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5373 g_assert(m_class_is_valuetype (klass));
5374 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5375 break;
5376 case CEE_MONO_TLS: {
5377 gint32 key = read32 (td->ip + 1);
5378 td->ip += 5;
5379 g_assertf (key == TLS_KEY_SGEN_THREAD_INFO, "%d", key);
5380 interp_add_ins (td, MINT_MONO_SGEN_THREAD_INFO);
5381 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5382 break;
5384 case CEE_MONO_ATOMIC_STORE_I4:
5385 CHECK_STACK (td, 2);
5386 SIMPLE_OP (td, MINT_MONO_ATOMIC_STORE_I4);
5387 td->sp -= 2;
5388 td->ip++;
5389 break;
5390 case CEE_MONO_SAVE_LMF:
5391 case CEE_MONO_RESTORE_LMF:
5392 case CEE_MONO_NOT_TAKEN:
5393 ++td->ip;
5394 break;
5395 case CEE_MONO_LDPTR_INT_REQ_FLAG:
5396 interp_add_ins (td, MINT_MONO_LDPTR);
5397 td->last_ins->data [0] = get_data_item_index (td, mono_thread_interruption_request_flag ());
5398 PUSH_TYPE (td, STACK_TYPE_MP, NULL);
5399 ++td->ip;
5400 break;
5401 case CEE_MONO_MEMORY_BARRIER:
5402 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5403 ++td->ip;
5404 break;
5405 case CEE_MONO_LDDOMAIN:
5406 interp_add_ins (td, MINT_MONO_LDDOMAIN);
5407 td->sp [0].type = STACK_TYPE_I;
5408 ++td->sp;
5409 ++td->ip;
5410 break;
5411 default:
5412 g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td->ip, td->ip-header->code);
5414 break;
5415 #if 0
5416 case CEE_PREFIX7:
5417 case CEE_PREFIX6:
5418 case CEE_PREFIX5:
5419 case CEE_PREFIX4:
5420 case CEE_PREFIX3:
5421 case CEE_PREFIX2:
5422 case CEE_PREFIXREF: ves_abort(); break;
5423 #endif
5425 * Note: Exceptions thrown when executing a prefixed opcode need
5426 * to take into account the number of prefix bytes (usually the
5427 * throw point is just (ip - n_prefix_bytes).
5429 case CEE_PREFIX1:
5430 ++td->ip;
5431 switch (*td->ip) {
5432 case CEE_ARGLIST:
5433 interp_add_ins (td, MINT_ARGLIST);
5434 PUSH_VT (td, SIZEOF_VOID_P);
5435 PUSH_SIMPLE_TYPE (td, STACK_TYPE_VT);
5436 ++td->ip;
5437 break;
5438 case CEE_CEQ:
5439 CHECK_STACK(td, 2);
5440 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP) {
5441 interp_add_ins (td, MINT_CEQ_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5442 } else {
5443 if (td->sp [-1].type == STACK_TYPE_R4 && td->sp [-2].type == STACK_TYPE_R8)
5444 interp_add_ins (td, MINT_CONV_R8_R4);
5445 if (td->sp [-1].type == STACK_TYPE_R8 && td->sp [-2].type == STACK_TYPE_R4)
5446 interp_add_ins (td, MINT_CONV_R8_R4_SP);
5447 interp_add_ins (td, MINT_CEQ_I4 + td->sp [-1].type - STACK_TYPE_I4);
5449 --td->sp;
5450 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5451 ++td->ip;
5452 break;
5453 case CEE_CGT:
5454 CHECK_STACK(td, 2);
5455 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5456 interp_add_ins (td, MINT_CGT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5457 else
5458 interp_add_ins (td, MINT_CGT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5459 --td->sp;
5460 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5461 ++td->ip;
5462 break;
5463 case CEE_CGT_UN:
5464 CHECK_STACK(td, 2);
5465 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5466 interp_add_ins (td, MINT_CGT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5467 else
5468 interp_add_ins (td, MINT_CGT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5469 --td->sp;
5470 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5471 ++td->ip;
5472 break;
5473 case CEE_CLT:
5474 CHECK_STACK(td, 2);
5475 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5476 interp_add_ins (td, MINT_CLT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5477 else
5478 interp_add_ins (td, MINT_CLT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5479 --td->sp;
5480 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5481 ++td->ip;
5482 break;
5483 case CEE_CLT_UN:
5484 CHECK_STACK(td, 2);
5485 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5486 interp_add_ins (td, MINT_CLT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5487 else
5488 interp_add_ins (td, MINT_CLT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5489 --td->sp;
5490 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5491 ++td->ip;
5492 break;
5493 case CEE_LDVIRTFTN: /* fallthrough */
5494 case CEE_LDFTN: {
5495 MonoMethod *m;
5496 if (*td->ip == CEE_LDVIRTFTN) {
5497 CHECK_STACK (td, 1);
5498 --td->sp;
5500 token = read32 (td->ip + 1);
5501 if (method->wrapper_type != MONO_WRAPPER_NONE)
5502 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
5503 else {
5504 m = mono_get_method_checked (image, token, NULL, generic_context, error);
5505 goto_if_nok (error, exit);
5508 if (!mono_method_can_access_method (method, m))
5509 interp_generate_mae_throw (td, method, m);
5511 if (method->wrapper_type == MONO_WRAPPER_NONE && m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
5512 m = mono_marshal_get_synchronized_wrapper (m);
5514 interp_add_ins (td, *td->ip == CEE_LDFTN ? MINT_LDFTN : MINT_LDVIRTFTN);
5515 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
5516 goto_if_nok (error, exit);
5517 td->ip += 5;
5518 PUSH_SIMPLE_TYPE (td, STACK_TYPE_F);
5519 break;
5521 case CEE_LDARG: {
5522 int arg_n = read16 (td->ip + 1);
5523 if (td->method == method)
5524 load_arg (td, arg_n);
5525 else
5526 load_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
5527 td->ip += 3;
5528 break;
5530 case CEE_LDARGA: {
5531 int n = read16 (td->ip + 1);
5533 if (td->method == method) {
5534 get_arg_type_exact (td, n, &mt);
5535 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
5536 td->last_ins->data [0] = n;
5537 } else {
5538 interp_add_ins (td, MINT_LDLOCA_S);
5539 td->last_ins->data [0] = arg_offsets [n];
5541 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5542 td->ip += 3;
5543 break;
5545 case CEE_STARG: {
5546 int arg_n = read16 (td->ip + 1);
5547 if (td->method == method)
5548 store_arg (td, arg_n);
5549 else
5550 store_local_general (td, arg_offsets [arg_n], get_arg_type (signature, arg_n));
5551 td->ip += 3;
5552 break;
5554 case CEE_LDLOC: {
5555 int loc_n = read16 (td->ip + 1);
5556 if (td->method == method)
5557 load_local (td, loc_n);
5558 else
5559 load_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
5560 td->ip += 3;
5561 break;
5563 case CEE_LDLOCA: {
5564 int loc_n = read16 (td->ip + 1);
5565 interp_add_ins (td, MINT_LDLOCA_S);
5566 if (td->method == method)
5567 td->last_ins->data [0] = td->rtm->local_offsets [loc_n];
5568 else
5569 td->last_ins->data [0] = local_offsets [loc_n];
5570 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5571 td->ip += 3;
5572 break;
5574 case CEE_STLOC: {
5575 int loc_n = read16 (td->ip + 1);
5576 if (td->method == method)
5577 store_local (td, loc_n);
5578 else
5579 store_local_general (td, local_offsets [loc_n], header->locals [loc_n]);
5580 td->ip += 3;
5581 break;
5583 case CEE_LOCALLOC:
5584 INLINE_FAILURE;
5585 CHECK_STACK (td, 1);
5586 #if SIZEOF_VOID_P == 8
5587 if (td->sp [-1].type == STACK_TYPE_I8)
5588 interp_add_ins (td, MINT_CONV_I4_I8);
5589 #endif
5590 interp_add_ins (td, MINT_LOCALLOC);
5591 if (td->sp != td->stack + 1)
5592 g_warning("CEE_LOCALLOC: stack not empty");
5593 ++td->ip;
5594 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5595 break;
5596 #if 0
5597 case CEE_UNUSED57: ves_abort(); break;
5598 #endif
5599 case CEE_ENDFILTER:
5600 interp_add_ins (td, MINT_ENDFILTER);
5601 ++td->ip;
5602 break;
5603 case CEE_UNALIGNED_:
5604 td->ip += 2;
5605 break;
5606 case CEE_VOLATILE_:
5607 ++td->ip;
5608 volatile_ = TRUE;
5609 break;
5610 case CEE_TAIL_:
5611 ++td->ip;
5612 /* FIX: should do something? */;
5613 // TODO: This should raise a method_tail_call profiler event.
5614 break;
5615 case CEE_INITOBJ:
5616 CHECK_STACK(td, 1);
5617 token = read32 (td->ip + 1);
5618 klass = mini_get_class (method, token, generic_context);
5619 CHECK_TYPELOAD (klass);
5620 if (m_class_is_valuetype (klass)) {
5621 interp_add_ins (td, MINT_INITOBJ);
5622 i32 = mono_class_value_size (klass, NULL);
5623 WRITE32_INS (td->last_ins, 0, &i32);
5624 } else {
5625 interp_add_ins (td, MINT_LDNULL);
5626 interp_add_ins (td, MINT_STIND_REF);
5628 td->ip += 5;
5629 --td->sp;
5630 break;
5631 case CEE_CPBLK:
5632 CHECK_STACK(td, 3);
5633 /* FIX? convert length to I8? */
5634 if (volatile_)
5635 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5636 interp_add_ins (td, MINT_CPBLK);
5637 BARRIER_IF_VOLATILE (td);
5638 td->sp -= 3;
5639 ++td->ip;
5640 break;
5641 case CEE_READONLY_:
5642 readonly = TRUE;
5643 td->ip += 1;
5644 break;
5645 case CEE_CONSTRAINED_:
5646 token = read32 (td->ip + 1);
5647 constrained_class = mini_get_class (method, token, generic_context);
5648 CHECK_TYPELOAD (constrained_class);
5649 td->ip += 5;
5650 break;
5651 case CEE_INITBLK:
5652 CHECK_STACK(td, 3);
5653 BARRIER_IF_VOLATILE (td);
5654 interp_add_ins (td, MINT_INITBLK);
5655 td->sp -= 3;
5656 td->ip += 1;
5657 break;
5658 case CEE_NO_:
5659 /* FIXME: implement */
5660 td->ip += 2;
5661 break;
5662 case CEE_RETHROW: {
5663 int clause_index = td->clause_indexes [in_offset];
5664 g_assert (clause_index != -1);
5665 SIMPLE_OP (td, MINT_RETHROW);
5666 td->last_ins->data [0] = rtm->exvar_offsets [clause_index];
5667 td->sp = td->stack;
5668 break;
5670 case CEE_SIZEOF: {
5671 gint32 size;
5672 token = read32 (td->ip + 1);
5673 td->ip += 5;
5674 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (m_class_get_image (method->klass)) && !generic_context) {
5675 int align;
5676 MonoType *type = mono_type_create_from_typespec_checked (image, token, error);
5677 goto_if_nok (error, exit);
5678 size = mono_type_size (type, &align);
5679 } else {
5680 int align;
5681 MonoClass *szclass = mini_get_class (method, token, generic_context);
5682 CHECK_TYPELOAD (szclass);
5683 #if 0
5684 if (!szclass->valuetype)
5685 THROW_EX (mono_exception_from_name (mono_defaults.corlib, "System", "InvalidProgramException"), ip - 5);
5686 #endif
5687 size = mono_type_size (m_class_get_byval_arg (szclass), &align);
5689 interp_add_ins (td, MINT_LDC_I4);
5690 WRITE32_INS (td->last_ins, 0, &size);
5691 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
5692 break;
5694 case CEE_REFANYTYPE:
5695 interp_add_ins (td, MINT_REFANYTYPE);
5696 td->ip += 1;
5697 POP_VT (td, sizeof (MonoTypedRef));
5698 PUSH_VT (td, sizeof (gpointer));
5699 SET_TYPE(td->sp - 1, STACK_TYPE_VT, NULL);
5700 break;
5701 default:
5702 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);
5704 break;
5705 default:
5706 g_error ("transform.c: Unimplemented opcode: %02x at 0x%x\n", *td->ip, td->ip-header->code);
5709 // No IR instructions were added as part of the IL instruction. Extend bb_start
5710 if (prev_last_ins == td->last_ins && td->is_bb_start [in_offset] && td->ip < end)
5711 td->is_bb_start [td->ip - td->il_code] = 1;
5714 g_assert (td->ip == end);
5716 exit_ret:
5717 g_free (arg_offsets);
5718 g_free (local_offsets);
5719 mono_basic_block_free (original_bb);
5720 for (i = 0; i < header->code_size; ++i)
5721 g_free (td->stack_state [i]);
5722 g_free (td->stack_state);
5723 g_free (td->stack_height);
5724 g_free (td->vt_stack_size);
5725 g_free (td->clause_indexes);
5726 g_free (td->is_bb_start);
5728 return ret;
5729 exit:
5730 ret = FALSE;
5731 goto exit_ret;
5734 // We are trying to branch to an il offset that has no associated ir instruction with it.
5735 // We will branch instead to the next instruction that we find
5736 static int
5737 resolve_in_offset (TransformData *td, int il_offset)
5739 int i = il_offset;
5740 g_assert (!td->in_offsets [il_offset]);
5741 while (!td->in_offsets [i])
5742 i++;
5743 td->in_offsets [il_offset] = td->in_offsets [i];
5744 return td->in_offsets [il_offset];
5747 // We store in the in_offset array the native_offset + 1, so 0 can mean only that the il
5748 // offset is uninitialized. Otherwise 0 is valid value for first interp instruction.
5749 static int
5750 get_in_offset (TransformData *td, int il_offset)
5752 int target_offset = td->in_offsets [il_offset];
5753 if (target_offset)
5754 return target_offset - 1;
5755 return resolve_in_offset (td, il_offset) - 1;
5758 static void
5759 handle_relocations (TransformData *td)
5761 // Handle relocations
5762 for (int i = 0; i < td->relocs->len; ++i) {
5763 Reloc *reloc = (Reloc*)g_ptr_array_index (td->relocs, i);
5764 int offset = get_in_offset (td, reloc->target) - reloc->offset;
5766 switch (reloc->type) {
5767 case RELOC_SHORT_BRANCH:
5768 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
5769 td->new_code [reloc->offset + 1] = offset;
5770 break;
5771 case RELOC_LONG_BRANCH: {
5772 guint16 *v = (guint16 *) &offset;
5773 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
5774 g_assert (td->new_code [reloc->offset + 2] == 0xbeef);
5775 td->new_code [reloc->offset + 1] = *(guint16 *) v;
5776 td->new_code [reloc->offset + 2] = *(guint16 *) (v + 1);
5777 break;
5779 case RELOC_SWITCH: {
5780 guint16 *v = (guint16*)&offset;
5781 g_assert (td->new_code [reloc->offset] == 0xdead);
5782 g_assert (td->new_code [reloc->offset + 1] == 0xbeef);
5783 td->new_code [reloc->offset] = *(guint16*)v;
5784 td->new_code [reloc->offset + 1] = *(guint16*)(v + 1);
5785 break;
5787 default:
5788 g_assert_not_reached ();
5789 break;
5795 static int
5796 get_inst_length (InterpInst *ins)
5798 if (ins->opcode == MINT_SWITCH)
5799 return MINT_SWITCH_LEN (READ32 (&ins->data [0]));
5800 else
5801 return mono_interp_oplen [ins->opcode];
5804 static guint16*
5805 emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *ins)
5807 guint16 opcode = ins->opcode;
5808 guint16 *ip = start_ip;
5810 // We know what IL offset this instruction was created for. We can now map the IL offset
5811 // to the IR offset. We use this array to resolve the relocations, which reference the IL.
5812 if (ins->il_offset != -1 && !td->in_offsets [ins->il_offset]) {
5813 g_assert (ins->il_offset >= 0 && ins->il_offset < td->header->code_size);
5814 td->in_offsets [ins->il_offset] = start_ip - td->new_code + 1;
5816 MonoDebugLineNumberEntry lne;
5817 lne.native_offset = (guint8*)start_ip - (guint8*)td->new_code;
5818 lne.il_offset = ins->il_offset;
5819 g_array_append_val (td->line_numbers, lne);
5822 *ip++ = opcode;
5823 if (opcode == MINT_SWITCH) {
5824 int labels = READ32 (&ins->data [0]);
5825 // Write number of switch labels
5826 *ip++ = ins->data [0];
5827 *ip++ = ins->data [1];
5828 // Add relocation for each label
5829 for (int i = 0; i < labels; i++) {
5830 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5831 reloc->type = RELOC_SWITCH;
5832 reloc->offset = ip - td->new_code;
5833 reloc->target = READ32 (&ins->data [2 + i * 2]);
5834 g_ptr_array_add (td->relocs, reloc);
5835 *ip++ = 0xdead;
5836 *ip++ = 0xbeef;
5838 } else if ((opcode >= MINT_BRFALSE_I4_S && opcode <= MINT_BRTRUE_R8_S) ||
5839 (opcode >= MINT_BEQ_I4_S && opcode <= MINT_BLT_UN_R8_S) ||
5840 opcode == MINT_BR_S || opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK) {
5841 const int br_offset = start_ip - td->new_code;
5842 if (ins->data [0] < ins->il_offset) {
5843 // Backwards branch. We can already patch it.
5844 *ip++ = get_in_offset (td, ins->data [0]) - br_offset;
5845 } else {
5846 // We don't know the in_offset of the target, add a reloc
5847 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5848 reloc->type = RELOC_SHORT_BRANCH;
5849 reloc->offset = br_offset;
5850 reloc->target = ins->data [0];
5851 g_ptr_array_add (td->relocs, reloc);
5852 *ip++ = 0xdead;
5854 } else if ((opcode >= MINT_BRFALSE_I4 && opcode <= MINT_BRTRUE_R8) ||
5855 (opcode >= MINT_BEQ_I4 && opcode <= MINT_BLT_UN_R8) ||
5856 opcode == MINT_BR || opcode == MINT_LEAVE || opcode == MINT_LEAVE_CHECK) {
5857 const int br_offset = start_ip - td->new_code;
5858 int target_il = READ32 (&ins->data [0]);
5859 if (target_il < ins->il_offset) {
5860 // Backwards branch. We can already patch it
5861 const int br_offset = start_ip - td->new_code;
5862 int target_offset = get_in_offset (td, target_il) - br_offset;
5863 WRITE32 (ip, &target_offset);
5864 } else {
5865 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
5866 reloc->type = RELOC_LONG_BRANCH;
5867 reloc->offset = br_offset;
5868 reloc->target = target_il;
5869 g_ptr_array_add (td->relocs, reloc);
5870 *ip++ = 0xdead;
5871 *ip++ = 0xbeef;
5873 } else if (opcode == MINT_SDB_SEQ_POINT) {
5874 SeqPoint *seqp = (SeqPoint*)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint));
5875 InterpBasicBlock *cbb;
5877 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY) {
5878 seqp->il_offset = METHOD_ENTRY_IL_OFFSET;
5879 cbb = td->offset_to_bb [0];
5880 } else {
5881 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT)
5882 seqp->il_offset = METHOD_EXIT_IL_OFFSET;
5883 else
5884 seqp->il_offset = ins->il_offset;
5885 cbb = td->offset_to_bb [ins->il_offset];
5887 seqp->native_offset = (guint8*)start_ip - (guint8*)td->new_code;
5888 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK)
5889 seqp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
5890 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL)
5891 seqp->flags |= MONO_SEQ_POINT_FLAG_NESTED_CALL;
5892 g_ptr_array_add (td->seq_points, seqp);
5894 cbb->seq_points = g_slist_prepend_mempool (td->mempool, cbb->seq_points, seqp);
5895 cbb->last_seq_point = seqp;
5896 } else {
5897 int size = get_inst_length (ins) - 1;
5898 // Emit the rest of the data
5899 for (int i = 0; i < size; i++)
5900 *ip++ = ins->data [i];
5902 return ip;
5905 // Generates the final code, after we are done with all the passes
5906 static void
5907 generate_compacted_code (TransformData *td)
5909 guint16 *ip;
5910 int size = 0;
5911 td->relocs = g_ptr_array_new ();
5913 // Iterate once to compute the exact size of the compacted code
5914 InterpInst *ins = td->first_ins;
5915 while (ins) {
5916 size += get_inst_length (ins);
5917 ins = ins->next;
5920 // Generate the compacted stream of instructions
5921 td->new_code = ip = (guint16*)mono_domain_alloc0 (td->rtm->domain, size * sizeof (guint16));
5922 ins = td->first_ins;
5923 while (ins) {
5924 ip = emit_compacted_instruction (td, ip, ins);
5925 ins = ins->next;
5927 td->new_code_end = ip;
5928 td->in_offsets [td->header->code_size] = td->new_code_end - td->new_code;
5930 // Patch all branches
5931 handle_relocations (td);
5933 g_ptr_array_free (td->relocs, TRUE);
5936 static void
5937 generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoGenericContext *generic_context, MonoError *error)
5939 MonoDomain *domain = rtm->domain;
5940 int i;
5941 TransformData transform_data;
5942 TransformData *td;
5943 static gboolean verbose_method_inited;
5944 static char* verbose_method_name;
5946 if (!verbose_method_inited) {
5947 verbose_method_name = g_getenv ("MONO_VERBOSE_METHOD");
5948 verbose_method_inited = TRUE;
5951 memset (&transform_data, 0, sizeof(transform_data));
5952 td = &transform_data;
5954 td->method = method;
5955 td->rtm = rtm;
5956 td->code_size = header->code_size;
5957 td->header = header;
5958 td->max_code_size = td->code_size;
5959 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
5960 td->mempool = mono_mempool_new ();
5961 td->n_data_items = 0;
5962 td->max_data_items = 0;
5963 td->data_items = NULL;
5964 td->data_hash = g_hash_table_new (NULL, NULL);
5965 td->gen_sdb_seq_points = mini_debug_options.gen_sdb_seq_points;
5966 td->seq_points = g_ptr_array_new ();
5967 td->verbose_level = mono_interp_traceopt;
5968 td->total_locals_size = rtm->locals_size;
5969 rtm->data_items = td->data_items;
5971 if (verbose_method_name) {
5972 const char *name = verbose_method_name;
5974 if ((strchr (name, '.') > name) || strchr (name, ':')) {
5975 MonoMethodDesc *desc;
5977 desc = mono_method_desc_new (name, TRUE);
5978 if (mono_method_desc_full_match (desc, method)) {
5979 td->verbose_level = 4;
5981 mono_method_desc_free (desc);
5982 } else {
5983 if (strcmp (method->name, name) == 0)
5984 td->verbose_level = 4;
5988 td->stack = (StackInfo*)g_malloc0 ((header->max_stack + 1) * sizeof (td->stack [0]));
5989 td->stack_capacity = header->max_stack + 1;
5990 td->sp = td->stack;
5991 td->max_stack_height = 0;
5992 td->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
5994 generate_code (td, method, header, generic_context, error);
5995 goto_if_nok (error, exit);
5997 generate_compacted_code (td);
5999 if (td->verbose_level) {
6000 g_print ("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method, TRUE), rtm, td->max_vt_sp);
6001 g_print ("Calculated stack size: %d, stated size: %d\n", td->max_stack_height, header->max_stack);
6002 dump_mint_code (td->new_code, td->new_code_end);
6005 /* Check if we use excessive stack space */
6006 if (td->max_stack_height > header->max_stack * 3 && header->max_stack > 16)
6007 g_warning ("Excessive stack space usage for method %s, %d/%d", method->name, td->max_stack_height, header->max_stack);
6009 int code_len;
6010 code_len = td->new_code_end - td->new_code;
6012 rtm->clauses = (MonoExceptionClause*)mono_domain_alloc0 (domain, header->num_clauses * sizeof (MonoExceptionClause));
6013 memcpy (rtm->clauses, header->clauses, header->num_clauses * sizeof(MonoExceptionClause));
6014 rtm->code = (gushort*)td->new_code;
6015 rtm->init_locals = header->init_locals;
6016 rtm->num_clauses = header->num_clauses;
6017 for (i = 0; i < header->num_clauses; i++) {
6018 MonoExceptionClause *c = rtm->clauses + i;
6019 int end_off = c->try_offset + c->try_len;
6020 c->try_offset = get_in_offset (td, c->try_offset);
6021 c->try_len = get_in_offset (td, end_off) - c->try_offset;
6022 g_assert ((c->try_offset + c->try_len) < code_len);
6023 end_off = c->handler_offset + c->handler_len;
6024 c->handler_offset = get_in_offset (td, c->handler_offset);
6025 c->handler_len = get_in_offset (td, end_off) - c->handler_offset;
6026 g_assert (c->handler_len >= 0 && (c->handler_offset + c->handler_len) <= code_len);
6027 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER)
6028 c->data.filter_offset = get_in_offset (td, c->data.filter_offset);
6030 rtm->stack_size = (sizeof (stackval)) * (td->max_stack_height + 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/
6031 rtm->stack_size = ALIGN_TO (rtm->stack_size, MINT_VT_ALIGNMENT);
6032 rtm->vt_stack_size = td->max_vt_sp;
6033 rtm->total_locals_size = td->total_locals_size;
6034 rtm->alloca_size = rtm->total_locals_size + rtm->vt_stack_size + rtm->stack_size;
6035 rtm->data_items = (gpointer*)mono_domain_alloc0 (domain, td->n_data_items * sizeof (td->data_items [0]));
6036 memcpy (rtm->data_items, td->data_items, td->n_data_items * sizeof (td->data_items [0]));
6038 /* Save debug info */
6039 interp_save_debug_info (rtm, header, td, td->line_numbers);
6041 /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
6042 int jinfo_len;
6043 jinfo_len = mono_jit_info_size ((MonoJitInfoFlags)0, header->num_clauses, 0);
6044 MonoJitInfo *jinfo;
6045 jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len);
6046 jinfo->is_interp = 1;
6047 rtm->jinfo = jinfo;
6048 mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len, (MonoJitInfoFlags)0, header->num_clauses, 0);
6049 for (i = 0; i < jinfo->num_clauses; ++i) {
6050 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
6051 MonoExceptionClause *c = rtm->clauses + i;
6053 ei->flags = c->flags;
6054 ei->try_start = (guint8*)(rtm->code + c->try_offset);
6055 ei->try_end = (guint8*)(rtm->code + c->try_offset + c->try_len);
6056 ei->handler_start = (guint8*)(rtm->code + c->handler_offset);
6057 ei->exvar_offset = rtm->exvar_offsets [i];
6058 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
6059 ei->data.filter = (guint8*)(rtm->code + c->data.filter_offset);
6060 } else if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
6061 ei->data.handler_end = (guint8*)(rtm->code + c->handler_offset + c->handler_len);
6062 } else {
6063 ei->data.catch_class = c->data.catch_class;
6067 save_seq_points (td, jinfo);
6069 exit:
6070 g_free (td->in_offsets);
6071 g_free (td->data_items);
6072 g_free (td->stack);
6073 g_hash_table_destroy (td->data_hash);
6074 g_ptr_array_free (td->seq_points, TRUE);
6075 g_array_free (td->line_numbers, TRUE);
6076 mono_mempool_destroy (td->mempool);
6079 static mono_mutex_t calc_section;
6081 void
6082 mono_interp_transform_init (void)
6084 mono_os_mutex_init_recursive(&calc_section);
6087 void
6088 mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, MonoError *error)
6090 MonoMethod *method = imethod->method;
6091 MonoMethodHeader *header = NULL;
6092 MonoMethodSignature *signature = mono_method_signature_internal (method);
6093 MonoVTable *method_class_vt;
6094 MonoGenericContext *generic_context = NULL;
6095 MonoDomain *domain = imethod->domain;
6096 InterpMethod tmp_imethod;
6097 InterpMethod *real_imethod;
6099 error_init (error);
6101 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method->klass))) {
6102 mono_error_set_invalid_operation (error, "%s", "Could not execute the method because the containing type is not fully instantiated.");
6103 return;
6106 // g_printerr ("TRANSFORM(0x%016lx): begin %s::%s\n", mono_thread_current (), method->klass->name, method->name);
6107 method_class_vt = mono_class_vtable_checked (domain, imethod->method->klass, error);
6108 return_if_nok (error);
6110 if (!method_class_vt->initialized) {
6111 mono_runtime_class_init_full (method_class_vt, error);
6112 return_if_nok (error);
6115 MONO_PROFILER_RAISE (jit_begin, (method));
6117 if (mono_method_signature_internal (method)->is_inflated)
6118 generic_context = mono_method_get_context (method);
6119 else {
6120 MonoGenericContainer *generic_container = mono_method_get_generic_container (method);
6121 if (generic_container)
6122 generic_context = &generic_container->context;
6125 if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
6126 MonoMethod *nm = NULL;
6127 if (imethod->transformed) {
6128 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));
6129 return;
6132 /* assumes all internal calls with an array this are built in... */
6133 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL && (! mono_method_signature_internal (method)->hasthis || m_class_get_rank (method->klass) == 0)) {
6134 nm = mono_marshal_get_native_wrapper (method, FALSE, FALSE);
6135 signature = mono_method_signature_internal (nm);
6136 } else {
6137 const char *name = method->name;
6138 if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) {
6139 if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
6140 MonoJitICallInfo *mi = &mono_get_jit_icall_info ()->ves_icall_mono_delegate_ctor_interp;
6141 nm = mono_marshal_get_icall_wrapper (mi, TRUE);
6142 } else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
6144 * Usually handled during transformation of the caller, but
6145 * when the caller is handled by another execution engine
6146 * (for example fullAOT) we need to handle it here. That's
6147 * known to be wrong in cases where the reference to
6148 * `MonoDelegate` would be needed (FIXME).
6150 nm = mono_marshal_get_delegate_invoke (method, NULL);
6151 } else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
6152 nm = mono_marshal_get_delegate_begin_invoke (method);
6153 } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
6154 nm = mono_marshal_get_delegate_end_invoke (method);
6157 if (nm == NULL)
6158 g_assert_not_reached ();
6160 if (nm == NULL) {
6161 mono_os_mutex_lock (&calc_section);
6162 imethod->stack_size = sizeof (stackval); /* for tracing */
6163 imethod->alloca_size = imethod->stack_size;
6164 mono_memory_barrier ();
6165 imethod->transformed = TRUE;
6166 mono_os_mutex_unlock (&calc_section);
6167 MONO_PROFILER_RAISE (jit_done, (method, NULL));
6168 return;
6170 method = nm;
6171 header = interp_method_get_header (nm, error);
6172 return_if_nok (error);
6175 if (!header) {
6176 header = mono_method_get_header_checked (method, error);
6177 return_if_nok (error);
6180 g_assert ((signature->param_count + signature->hasthis) < 1000);
6181 // g_printerr ("TRANSFORM(0x%016lx): end %s::%s\n", mono_thread_current (), method->klass->name, method->name);
6183 /* Make modifications to a copy of imethod, copy them back inside the lock */
6184 real_imethod = imethod;
6185 memcpy (&tmp_imethod, imethod, sizeof (InterpMethod));
6186 imethod = &tmp_imethod;
6188 interp_method_compute_offsets (imethod, signature, header);
6190 MONO_TIME_TRACK (mono_interp_stats.transform_time, generate (method, header, imethod, generic_context, error));
6192 mono_metadata_free_mh (header);
6194 return_if_nok (error);
6196 /* Copy changes back */
6197 imethod = real_imethod;
6198 mono_os_mutex_lock (&calc_section);
6199 if (!imethod->transformed) {
6200 // Ignore the first two fields which are unchanged. next_jit_code_hash shouldn't
6201 // be modified because it is racy with internal hash table insert.
6202 const int start_offset = 2 * sizeof (gpointer);
6203 memcpy ((char*)imethod + start_offset, (char*)&tmp_imethod + start_offset, sizeof (InterpMethod) - start_offset);
6204 mono_memory_barrier ();
6205 imethod->transformed = TRUE;
6206 mono_atomic_fetch_add_i32 (&mono_jit_stats.methods_with_interp, 1);
6209 mono_os_mutex_unlock (&calc_section);
6211 mono_domain_lock (domain);
6212 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, imethod->method))
6213 g_hash_table_insert (domain_jit_info (domain)->seq_points, imethod->method, imethod->jinfo->seq_points);
6214 mono_domain_unlock (domain);
6216 // FIXME: Add a different callback ?
6217 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));