[interp] Optimize multidimensional array access (#16822)
[mono-project.git] / mono / mini / interp / transform.c
blob974dba93a5064f20462a24d7149682405874d956
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 #define INTERP_LOCAL_FLAG_INDIRECT 1
41 MonoInterpStats mono_interp_stats;
43 #define DEBUG 0
45 typedef struct InterpInst InterpInst;
47 typedef struct
49 MonoClass *klass;
50 unsigned char type;
51 unsigned char flags;
52 } StackInfo;
54 typedef struct
56 InterpInst *ins;
57 } StackContentInfo;
59 struct InterpInst {
60 guint16 opcode;
61 InterpInst *next, *prev;
62 // If this is -1, this instruction is not logically associated with an IL offset, it is
63 // part of the IL instruction associated with the previous interp instruction.
64 int il_offset;
65 guint32 flags;
66 guint16 data [MONO_ZERO_LEN_ARRAY];
69 typedef struct {
70 guint8 *ip;
71 GSList *preds;
72 GSList *seq_points;
73 SeqPoint *last_seq_point;
75 // This will hold a list of last sequence points of incoming basic blocks
76 SeqPoint **pred_seq_points;
77 guint num_pred_seq_points;
78 } InterpBasicBlock;
80 typedef enum {
81 RELOC_SHORT_BRANCH,
82 RELOC_LONG_BRANCH,
83 RELOC_SWITCH
84 } RelocType;
86 typedef struct {
87 RelocType type;
88 /* In the interpreter IR */
89 int offset;
90 /* In the IL code */
91 int target;
92 } Reloc;
94 typedef struct {
95 MonoType *type;
96 int flags;
97 int offset;
98 } InterpLocal;
100 typedef struct
102 MonoMethod *method;
103 MonoMethod *inlined_method;
104 MonoMethodHeader *header;
105 InterpMethod *rtm;
106 const unsigned char *il_code;
107 const unsigned char *ip;
108 const unsigned char *in_start;
109 InterpInst *last_ins, *first_ins;
110 int code_size;
111 int *in_offsets;
112 int current_il_offset;
113 StackInfo **stack_state;
114 int *stack_height;
115 int *vt_stack_size;
116 unsigned char *is_bb_start;
117 unsigned short *new_code;
118 unsigned short *new_code_end;
119 unsigned int max_code_size;
120 StackInfo *stack;
121 StackInfo *sp;
122 unsigned int max_stack_height;
123 unsigned int stack_capacity;
124 unsigned int vt_sp;
125 unsigned int max_vt_sp;
126 unsigned int total_locals_size;
127 InterpLocal *locals;
128 unsigned int locals_size;
129 unsigned int locals_capacity;
130 int n_data_items;
131 int max_data_items;
132 void **data_items;
133 GHashTable *data_hash;
134 int *clause_indexes;
135 gboolean gen_sdb_seq_points;
136 GPtrArray *seq_points;
137 InterpBasicBlock **offset_to_bb;
138 InterpBasicBlock *entry_bb;
139 MonoMemPool *mempool;
140 GList *basic_blocks;
141 GPtrArray *relocs;
142 gboolean verbose_level;
143 GArray *line_numbers;
144 } TransformData;
146 #define STACK_TYPE_I4 0
147 #define STACK_TYPE_I8 1
148 #define STACK_TYPE_R4 2
149 #define STACK_TYPE_R8 3
150 #define STACK_TYPE_O 4
151 #define STACK_TYPE_VT 5
152 #define STACK_TYPE_MP 6
153 #define STACK_TYPE_F 7
155 static const char *stack_type_string [] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " };
157 #if SIZEOF_VOID_P == 8
158 #define STACK_TYPE_I STACK_TYPE_I8
159 #else
160 #define STACK_TYPE_I STACK_TYPE_I4
161 #endif
163 static int stack_type [] = {
164 STACK_TYPE_I4, /*I1*/
165 STACK_TYPE_I4, /*U1*/
166 STACK_TYPE_I4, /*I2*/
167 STACK_TYPE_I4, /*U2*/
168 STACK_TYPE_I4, /*I4*/
169 STACK_TYPE_I8, /*I8*/
170 STACK_TYPE_R4, /*R4*/
171 STACK_TYPE_R8, /*R8*/
172 STACK_TYPE_O, /*O*/
173 STACK_TYPE_MP, /*P*/
174 STACK_TYPE_VT
177 #if SIZEOF_VOID_P == 8
178 #define MINT_NEG_P MINT_NEG_I8
179 #define MINT_NOT_P MINT_NOT_I8
181 #define MINT_NEG_FP MINT_NEG_R8
183 #define MINT_ADD_P MINT_ADD_I8
184 #define MINT_SUB_P MINT_SUB_I8
185 #define MINT_MUL_P MINT_MUL_I8
186 #define MINT_DIV_P MINT_DIV_I8
187 #define MINT_DIV_UN_P MINT_DIV_UN_I8
188 #define MINT_REM_P MINT_REM_I8
189 #define MINT_REM_UN_P MINT_REM_UN_I8
190 #define MINT_AND_P MINT_AND_I8
191 #define MINT_OR_P MINT_OR_I8
192 #define MINT_XOR_P MINT_XOR_I8
193 #define MINT_SHL_P MINT_SHL_I8
194 #define MINT_SHR_P MINT_SHR_I8
195 #define MINT_SHR_UN_P MINT_SHR_UN_I8
197 #define MINT_CEQ_P MINT_CEQ_I8
198 #define MINT_CNE_P MINT_CNE_I8
199 #define MINT_CLT_P MINT_CLT_I8
200 #define MINT_CLT_UN_P MINT_CLT_UN_I8
201 #define MINT_CGT_P MINT_CGT_I8
202 #define MINT_CGT_UN_P MINT_CGT_UN_I8
203 #define MINT_CLE_P MINT_CLE_I8
204 #define MINT_CLE_UN_P MINT_CLE_UN_I8
205 #define MINT_CGE_P MINT_CGE_I8
206 #define MINT_CGE_UN_P MINT_CGE_UN_I8
208 #define MINT_ADD_FP MINT_ADD_R8
209 #define MINT_SUB_FP MINT_SUB_R8
210 #define MINT_MUL_FP MINT_MUL_R8
211 #define MINT_DIV_FP MINT_DIV_R8
212 #define MINT_REM_FP MINT_REM_R8
214 #define MINT_CNE_FP MINT_CNE_R8
215 #define MINT_CEQ_FP MINT_CEQ_R8
216 #define MINT_CGT_FP MINT_CGT_R8
217 #define MINT_CGE_FP MINT_CGE_R8
218 #define MINT_CLT_FP MINT_CLT_R8
219 #define MINT_CLE_FP MINT_CLE_R8
221 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I8
222 #else
224 #define MINT_NEG_P MINT_NEG_I4
225 #define MINT_NOT_P MINT_NOT_I4
227 #define MINT_NEG_FP MINT_NEG_R4
229 #define MINT_ADD_P MINT_ADD_I4
230 #define MINT_SUB_P MINT_SUB_I4
231 #define MINT_MUL_P MINT_MUL_I4
232 #define MINT_DIV_P MINT_DIV_I4
233 #define MINT_DIV_UN_P MINT_DIV_UN_I4
234 #define MINT_REM_P MINT_REM_I4
235 #define MINT_REM_UN_P MINT_REM_UN_I4
236 #define MINT_AND_P MINT_AND_I4
237 #define MINT_OR_P MINT_OR_I4
238 #define MINT_XOR_P MINT_XOR_I4
239 #define MINT_SHL_P MINT_SHL_I4
240 #define MINT_SHR_P MINT_SHR_I4
241 #define MINT_SHR_UN_P MINT_SHR_UN_I4
243 #define MINT_CEQ_P MINT_CEQ_I4
244 #define MINT_CNE_P MINT_CNE_I4
245 #define MINT_CLT_P MINT_CLT_I4
246 #define MINT_CLT_UN_P MINT_CLT_UN_I4
247 #define MINT_CGT_P MINT_CGT_I4
248 #define MINT_CGT_UN_P MINT_CGT_UN_I4
249 #define MINT_CLE_P MINT_CLE_I4
250 #define MINT_CLE_UN_P MINT_CLE_UN_I4
251 #define MINT_CGE_P MINT_CGE_I4
252 #define MINT_CGE_UN_P MINT_CGE_UN_I4
254 #define MINT_ADD_FP MINT_ADD_R4
255 #define MINT_SUB_FP MINT_SUB_R4
256 #define MINT_MUL_FP MINT_MUL_R4
257 #define MINT_DIV_FP MINT_DIV_R4
258 #define MINT_REM_FP MINT_REM_R4
260 #define MINT_CNE_FP MINT_CNE_R4
261 #define MINT_CEQ_FP MINT_CEQ_R4
262 #define MINT_CGT_FP MINT_CGT_R4
263 #define MINT_CGE_FP MINT_CGE_R4
264 #define MINT_CLT_FP MINT_CLT_R4
265 #define MINT_CLE_FP MINT_CLE_R4
267 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I4
268 #endif
270 typedef struct {
271 const gchar *op_name;
272 guint16 insn [3];
273 } MagicIntrinsic;
275 // static const MagicIntrinsic int_binop[] = {
277 static const MagicIntrinsic int_unnop[] = {
278 { "op_UnaryPlus", {MINT_NOP, MINT_NOP, MINT_NOP}},
279 { "op_UnaryNegation", {MINT_NEG_P, MINT_NEG_P, MINT_NEG_FP}},
280 { "op_OnesComplement", {MINT_NOT_P, MINT_NOT_P, MINT_NIY}}
283 static const MagicIntrinsic int_binop[] = {
284 { "op_Addition", {MINT_ADD_P, MINT_ADD_P, MINT_ADD_FP}},
285 { "op_Subtraction", {MINT_SUB_P, MINT_SUB_P, MINT_SUB_FP}},
286 { "op_Multiply", {MINT_MUL_P, MINT_MUL_P, MINT_MUL_FP}},
287 { "op_Division", {MINT_DIV_P, MINT_DIV_UN_P, MINT_DIV_FP}},
288 { "op_Modulus", {MINT_REM_P, MINT_REM_UN_P, MINT_REM_FP}},
289 { "op_BitwiseAnd", {MINT_AND_P, MINT_AND_P, MINT_NIY}},
290 { "op_BitwiseOr", {MINT_OR_P, MINT_OR_P, MINT_NIY}},
291 { "op_ExclusiveOr", {MINT_XOR_P, MINT_XOR_P, MINT_NIY}},
292 { "op_LeftShift", {MINT_SHL_P, MINT_SHL_P, MINT_NIY}},
293 { "op_RightShift", {MINT_SHR_P, MINT_SHR_UN_P, MINT_NIY}},
296 static const MagicIntrinsic int_cmpop[] = {
297 { "op_Inequality", {MINT_CNE_P, MINT_CNE_P, MINT_CNE_FP}},
298 { "op_Equality", {MINT_CEQ_P, MINT_CEQ_P, MINT_CEQ_FP}},
299 { "op_GreaterThan", {MINT_CGT_P, MINT_CGT_UN_P, MINT_CGT_FP}},
300 { "op_GreaterThanOrEqual", {MINT_CGE_P, MINT_CGE_UN_P, MINT_CGE_FP}},
301 { "op_LessThan", {MINT_CLT_P, MINT_CLT_UN_P, MINT_CLT_FP}},
302 { "op_LessThanOrEqual", {MINT_CLE_P, MINT_CLE_UN_P, MINT_CLE_FP}}
305 static gboolean generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error);
307 static InterpInst*
308 interp_new_ins (TransformData *td, guint16 opcode, int len)
310 InterpInst *new_inst;
311 // Size of data region of instruction is length of instruction minus 1 (the opcode slot)
312 new_inst = mono_mempool_alloc0 (td->mempool, sizeof (InterpInst) + sizeof (guint16) * ((len > 0) ? (len - 1) : 0));
313 new_inst->opcode = opcode;
314 new_inst->il_offset = td->current_il_offset;
315 return new_inst;
318 // This version need to be used with switch opcode, which doesn't have constant length
319 static InterpInst*
320 interp_add_ins_explicit (TransformData *td, guint16 opcode, int len)
322 InterpInst *new_inst = interp_new_ins (td, opcode, len);
323 new_inst->prev = td->last_ins;
324 if (td->last_ins)
325 td->last_ins->next = new_inst;
326 else
327 td->first_ins = new_inst;
328 td->last_ins = new_inst;
329 return new_inst;
332 static InterpInst*
333 interp_add_ins (TransformData *td, guint16 opcode)
335 return interp_add_ins_explicit (td, opcode, mono_interp_oplen [opcode]);
338 // This instruction will have the il_offset of the previous instruction
339 static InterpInst*
340 interp_insert_ins (TransformData *td, InterpInst *prev_ins, guint16 opcode)
342 InterpInst *new_inst = interp_new_ins (td, opcode, mono_interp_oplen [opcode]);
343 g_assert (prev_ins);
344 new_inst->il_offset = prev_ins->il_offset;
346 new_inst->prev = prev_ins;
347 new_inst->next = prev_ins->next;
348 prev_ins->next = new_inst;
350 if (new_inst->next == NULL)
351 td->last_ins = new_inst;
352 else
353 new_inst->next->prev = new_inst;
355 return new_inst;
358 static void
359 interp_clear_ins (TransformData *td, InterpInst *ins)
361 // Clearing instead of removing from the list makes everything easier.
362 // We don't change structure of the instruction list, we don't need
363 // to worry about updating the il_offset, or whether this instruction
364 // was at the start of a basic block etc.
365 ins->opcode = MINT_NOP;
368 #define CHECK_STACK(td, n) \
369 do { \
370 int stack_size = (td)->sp - (td)->stack; \
371 if (stack_size < (n)) \
372 g_warning ("%s.%s: not enough values (%d < %d) on stack at %04x", \
373 m_class_get_name ((td)->method->klass), (td)->method->name, \
374 stack_size, n, (td)->ip - (td)->il_code); \
375 } while (0)
377 #define ENSURE_I4(td, sp_off) \
378 do { \
379 if ((td)->sp [-sp_off].type == STACK_TYPE_I8) \
380 interp_add_ins (td, sp_off == 1 ? MINT_CONV_I4_I8 : MINT_CONV_I4_I8_SP); \
381 } while (0)
383 #define CHECK_TYPELOAD(klass) \
384 do { \
385 if (!(klass) || mono_class_has_failure (klass)) { \
386 mono_error_set_for_class_failure (error, klass); \
387 goto exit; \
389 } while (0)
391 #if NO_UNALIGNED_ACCESS
392 #define WRITE32(ip, v) \
393 do { \
394 * (ip) = * (guint16 *)(v); \
395 * ((ip) + 1) = * ((guint16 *)(v) + 1); \
396 (ip) += 2; \
397 } while (0)
399 #define WRITE32_INS(ins, index, v) \
400 do { \
401 (ins)->data [index] = * (guint16 *)(v); \
402 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
403 } while (0)
405 #define WRITE64_INS(ins, index, v) \
406 do { \
407 (ins)->data [index] = * (guint16 *)(v); \
408 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
409 (ins)->data [index + 2] = * ((guint16 *)(v) + 2); \
410 (ins)->data [index + 3] = * ((guint16 *)(v) + 3); \
411 } while (0)
412 #else
413 #define WRITE32(ip, v) \
414 do { \
415 * (guint32*)(ip) = * (guint32 *)(v); \
416 (ip) += 2; \
417 } while (0)
418 #define WRITE32_INS(ins, index, v) \
419 do { \
420 * (guint32 *)(&(ins)->data [index]) = * (guint32 *)(v); \
421 } while (0)
423 #define WRITE64_INS(ins, index, v) \
424 do { \
425 * (guint64 *)(&(ins)->data [index]) = * (guint64 *)(v); \
426 } while (0)
428 #endif
431 static void
432 handle_branch (TransformData *td, int short_op, int long_op, int offset)
434 int shorten_branch = 0;
435 int target = td->ip + offset - td->il_code;
436 if (target < 0 || target >= td->code_size)
437 g_assert_not_reached ();
438 /* Add exception checkpoint or safepoint for backward branches */
439 if (offset < 0) {
440 if (mono_threads_are_safepoints_enabled ())
441 interp_add_ins (td, MINT_SAFEPOINT);
442 else
443 interp_add_ins (td, MINT_CHECKPOINT);
445 if (offset > 0 && td->stack_height [target] < 0) {
446 td->stack_height [target] = td->sp - td->stack;
447 if (td->stack_height [target] > 0)
448 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, td->stack_height [target] * sizeof (td->stack [0]));
449 td->vt_stack_size [target] = td->vt_sp;
452 if (td->header->code_size <= 25000) /* FIX to be precise somehow? */
453 shorten_branch = 1;
455 if (shorten_branch) {
456 interp_add_ins (td, short_op);
457 td->last_ins->data [0] = (guint16) target;
458 } else {
459 interp_add_ins (td, long_op);
460 WRITE32_INS (td->last_ins, 0, &target);
464 static void
465 one_arg_branch(TransformData *td, int mint_op, int offset)
467 int type = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
468 int long_op = mint_op + type - STACK_TYPE_I4;
469 int short_op = long_op + MINT_BRFALSE_I4_S - MINT_BRFALSE_I4;
470 CHECK_STACK(td, 1);
471 --td->sp;
472 handle_branch (td, short_op, long_op, offset);
475 static void
476 two_arg_branch(TransformData *td, int mint_op, int offset)
478 int type1 = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
479 int type2 = td->sp [-2].type == STACK_TYPE_O || td->sp [-2].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-2].type;
480 int long_op = mint_op + type1 - STACK_TYPE_I4;
481 int short_op = long_op + MINT_BEQ_I4_S - MINT_BEQ_I4;
482 CHECK_STACK(td, 2);
483 if (type1 == STACK_TYPE_I4 && type2 == STACK_TYPE_I8) {
484 // The il instruction starts with the actual branch, and not with the conversion opcodes
485 interp_insert_ins (td, td->last_ins, MINT_CONV_I8_I4);
486 } else if (type1 == STACK_TYPE_I8 && type2 == STACK_TYPE_I4) {
487 interp_insert_ins (td, td->last_ins, MINT_CONV_I8_I4_SP);
488 } else if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
489 interp_insert_ins (td, td->last_ins, MINT_CONV_R8_R4);
490 } else if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
491 interp_insert_ins (td, td->last_ins, MINT_CONV_R8_R4_SP);
492 } else if (type1 != type2) {
493 g_warning("%s.%s: branch type mismatch %d %d",
494 m_class_get_name (td->method->klass), td->method->name,
495 td->sp [-1].type, td->sp [-2].type);
497 td->sp -= 2;
498 handle_branch (td, short_op, long_op, offset);
501 static void
502 unary_arith_op(TransformData *td, int mint_op)
504 int op = mint_op + td->sp [-1].type - STACK_TYPE_I4;
505 CHECK_STACK(td, 1);
506 interp_add_ins (td, op);
509 static void
510 binary_arith_op(TransformData *td, int mint_op)
512 int type1 = td->sp [-2].type;
513 int type2 = td->sp [-1].type;
514 int op;
515 #if SIZEOF_VOID_P == 8
516 if ((type1 == STACK_TYPE_MP || type1 == STACK_TYPE_I8) && type2 == STACK_TYPE_I4) {
517 interp_add_ins (td, MINT_CONV_I8_I4);
518 type2 = STACK_TYPE_I8;
520 if (type1 == STACK_TYPE_I4 && (type2 == STACK_TYPE_MP || type2 == STACK_TYPE_I8)) {
521 interp_add_ins (td, MINT_CONV_I8_I4_SP);
522 type1 = STACK_TYPE_I8;
523 td->sp [-2].type = STACK_TYPE_I8;
525 #endif
526 if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
527 interp_add_ins (td, MINT_CONV_R8_R4);
528 type2 = STACK_TYPE_R8;
530 if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
531 interp_add_ins (td, MINT_CONV_R8_R4_SP);
532 type1 = STACK_TYPE_R8;
533 td->sp [-2].type = STACK_TYPE_R8;
535 if (type1 == STACK_TYPE_MP)
536 type1 = STACK_TYPE_I;
537 if (type2 == STACK_TYPE_MP)
538 type2 = STACK_TYPE_I;
539 if (type1 != type2) {
540 g_warning("%s.%s: %04x arith type mismatch %s %d %d",
541 m_class_get_name (td->method->klass), td->method->name,
542 td->ip - td->il_code, mono_interp_opname (mint_op), type1, type2);
544 op = mint_op + type1 - STACK_TYPE_I4;
545 CHECK_STACK(td, 2);
546 interp_add_ins (td, op);
547 --td->sp;
550 static void
551 shift_op(TransformData *td, int mint_op)
553 int op = mint_op + td->sp [-2].type - STACK_TYPE_I4;
554 CHECK_STACK(td, 2);
555 if (td->sp [-1].type != STACK_TYPE_I4) {
556 g_warning("%s.%s: shift type mismatch %d",
557 m_class_get_name (td->method->klass), td->method->name,
558 td->sp [-2].type);
560 interp_add_ins (td, op);
561 --td->sp;
564 static int
565 can_store (int st_value, int vt_value)
567 if (st_value == STACK_TYPE_O || st_value == STACK_TYPE_MP)
568 st_value = STACK_TYPE_I;
569 if (vt_value == STACK_TYPE_O || vt_value == STACK_TYPE_MP)
570 vt_value = STACK_TYPE_I;
571 return st_value == vt_value;
574 #define SET_SIMPLE_TYPE(s, ty) \
575 do { \
576 (s)->type = (ty); \
577 (s)->flags = 0; \
578 (s)->klass = NULL; \
579 } while (0)
581 #define SET_TYPE(s, ty, k) \
582 do { \
583 (s)->type = (ty); \
584 (s)->flags = 0; \
585 (s)->klass = k; \
586 } while (0)
588 #define REALLOC_STACK(td, sppos) \
589 do { \
590 (td)->stack_capacity *= 2; \
591 (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
592 (td)->sp = (td)->stack + (sppos); \
593 } while (0);
595 #define PUSH_SIMPLE_TYPE(td, ty) \
596 do { \
597 int sp_height; \
598 (td)->sp++; \
599 sp_height = (td)->sp - (td)->stack; \
600 if (sp_height > (td)->max_stack_height) \
601 (td)->max_stack_height = sp_height; \
602 if (sp_height > (td)->stack_capacity) \
603 REALLOC_STACK(td, sp_height); \
604 SET_SIMPLE_TYPE((td)->sp - 1, ty); \
605 } while (0)
607 #define PUSH_TYPE(td, ty, k) \
608 do { \
609 int sp_height; \
610 (td)->sp++; \
611 sp_height = (td)->sp - (td)->stack; \
612 if (sp_height > (td)->max_stack_height) \
613 (td)->max_stack_height = sp_height; \
614 if (sp_height > (td)->stack_capacity) \
615 REALLOC_STACK(td, sp_height); \
616 SET_TYPE((td)->sp - 1, ty, k); \
617 } while (0)
619 static void
620 move_stack (TransformData *td, int start, int amount)
622 int sp_height = td->sp - td->stack;
623 int to_move = sp_height - start;
625 td->sp += amount;
626 sp_height += amount;
627 if (amount > 0) {
628 if (sp_height > td->max_stack_height)
629 td->max_stack_height = sp_height;
630 if (sp_height > td->stack_capacity)
631 REALLOC_STACK (td, sp_height);
632 } else {
633 g_assert (td->sp >= td->stack);
636 if (to_move > 0)
637 memmove (td->stack + start + amount, td->stack + start, to_move * sizeof (StackInfo));
640 #define PUSH_VT(td, size) \
641 do { \
642 (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
643 if ((td)->vt_sp > (td)->max_vt_sp) \
644 (td)->max_vt_sp = (td)->vt_sp; \
645 } while (0)
647 #define POP_VT(td, size) \
648 do { \
649 (td)->vt_sp -= ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
650 } while (0)
652 static MonoType*
653 get_arg_type_exact (TransformData *td, int n, int *mt)
655 MonoType *type;
656 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
658 if (hasthis && n == 0)
659 type = m_class_get_byval_arg (td->method->klass);
660 else
661 type = mono_method_signature_internal (td->method)->params [n - !!hasthis];
663 if (mt)
664 *mt = mint_type (type);
666 return type;
669 static void
670 load_arg(TransformData *td, int n)
672 int mt;
673 MonoClass *klass = NULL;
674 MonoType *type;
675 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
677 type = get_arg_type_exact (td, n, &mt);
679 if (mt == MINT_TYPE_VT) {
680 gint32 size;
681 klass = mono_class_from_mono_type_internal (type);
682 if (mono_method_signature_internal (td->method)->pinvoke)
683 size = mono_class_native_size (klass, NULL);
684 else
685 size = mono_class_value_size (klass, NULL);
687 if (hasthis && n == 0) {
688 mt = MINT_TYPE_P;
689 interp_add_ins (td, MINT_LDARG_P0);
690 klass = NULL;
691 } else {
692 PUSH_VT (td, size);
693 interp_add_ins (td, MINT_LDARG_VT);
694 td->last_ins->data [0] = n;
695 WRITE32_INS (td->last_ins, 1, &size);
697 } else {
698 if ((hasthis || mt == MINT_TYPE_P) && n == 0) {
699 mt = MINT_TYPE_P;
700 interp_add_ins (td, MINT_LDARG_P0);
701 } else {
702 interp_add_ins (td, MINT_LDARG_I1 + (mt - MINT_TYPE_I1));
703 td->last_ins->data [0] = n;
704 if (mt == MINT_TYPE_O)
705 klass = mono_class_from_mono_type_internal (type);
708 PUSH_TYPE(td, stack_type[mt], klass);
711 static void
712 store_arg(TransformData *td, int n)
714 int mt;
715 CHECK_STACK (td, 1);
716 MonoType *type;
718 type = get_arg_type_exact (td, n, &mt);
720 if (mt == MINT_TYPE_VT) {
721 gint32 size;
722 MonoClass *klass = mono_class_from_mono_type_internal (type);
723 if (mono_method_signature_internal (td->method)->pinvoke)
724 size = mono_class_native_size (klass, NULL);
725 else
726 size = mono_class_value_size (klass, NULL);
727 interp_add_ins (td, MINT_STARG_VT);
728 td->last_ins->data [0] = n;
729 WRITE32_INS (td->last_ins, 1, &size);
730 if (td->sp [-1].type == STACK_TYPE_VT)
731 POP_VT(td, size);
732 } else {
733 interp_add_ins (td, MINT_STARG_I1 + (mt - MINT_TYPE_I1));
734 td->last_ins->data [0] = n;
736 --td->sp;
739 static void
740 load_local_general (TransformData *td, int local, MonoType *type)
742 int mt = mint_type (type);
743 MonoClass *klass = NULL;
744 if (mt == MINT_TYPE_VT) {
745 klass = mono_class_from_mono_type_internal (type);
746 gint32 size = mono_class_value_size (klass, NULL);
747 PUSH_VT(td, size);
748 interp_add_ins (td, MINT_LDLOC_VT);
749 td->last_ins->data [0] = local;
750 WRITE32_INS (td->last_ins, 1, &size);
751 } else {
752 g_assert (mt < MINT_TYPE_VT);
753 interp_add_ins (td, MINT_LDLOC_I1 + (mt - MINT_TYPE_I1));
754 td->last_ins->data [0] = local;
755 if (mt == MINT_TYPE_O)
756 klass = mono_class_from_mono_type_internal (type);
758 PUSH_TYPE(td, stack_type[mt], klass);
761 static void
762 load_local (TransformData *td, int n)
764 MonoType *type = td->header->locals [n];
765 load_local_general (td, n, type);
768 static void
769 store_local_general (TransformData *td, int local, MonoType *type)
771 int mt = mint_type (type);
772 CHECK_STACK (td, 1);
773 #if SIZEOF_VOID_P == 8
774 if (td->sp [-1].type == STACK_TYPE_I4 && stack_type [mt] == STACK_TYPE_I8) {
775 interp_add_ins (td, MINT_CONV_I8_I4);
776 td->sp [-1].type = STACK_TYPE_I8;
778 #endif
779 if (!can_store(td->sp [-1].type, stack_type [mt])) {
780 g_warning("%s.%s: Store local stack type mismatch %d %d",
781 m_class_get_name (td->method->klass), td->method->name,
782 stack_type [mt], td->sp [-1].type);
784 if (mt == MINT_TYPE_VT) {
785 MonoClass *klass = mono_class_from_mono_type_internal (type);
786 gint32 size = mono_class_value_size (klass, NULL);
787 interp_add_ins (td, MINT_STLOC_VT);
788 td->last_ins->data [0] = local;
789 WRITE32_INS (td->last_ins, 1, &size);
790 if (td->sp [-1].type == STACK_TYPE_VT)
791 POP_VT(td, size);
792 } else {
793 g_assert (mt < MINT_TYPE_VT);
794 interp_add_ins (td, MINT_STLOC_I1 + (mt - MINT_TYPE_I1));
795 td->last_ins->data [0] = local;
797 --td->sp;
800 static void
801 store_local (TransformData *td, int n)
803 MonoType *type = td->header->locals [n];
804 store_local_general (td, n, type);
807 #define SIMPLE_OP(td, op) \
808 do { \
809 interp_add_ins (td, op); \
810 ++td->ip; \
811 } while (0)
813 static guint16
814 get_data_item_index (TransformData *td, void *ptr)
816 gpointer p = g_hash_table_lookup (td->data_hash, ptr);
817 guint index;
818 if (p != NULL)
819 return GPOINTER_TO_UINT (p) - 1;
820 if (td->max_data_items == td->n_data_items) {
821 td->max_data_items = td->n_data_items == 0 ? 16 : 2 * td->max_data_items;
822 td->data_items = (gpointer*)g_realloc (td->data_items, td->max_data_items * sizeof(td->data_items [0]));
824 index = td->n_data_items;
825 td->data_items [index] = ptr;
826 ++td->n_data_items;
827 g_hash_table_insert (td->data_hash, ptr, GUINT_TO_POINTER (index + 1));
828 return index;
831 static gboolean
832 jit_call_supported (MonoMethod *method, MonoMethodSignature *sig)
834 GSList *l;
836 if (sig->param_count > 6)
837 return FALSE;
838 if (sig->pinvoke)
839 return FALSE;
840 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
841 return FALSE;
842 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
843 return FALSE;
844 if (method->is_inflated)
845 return FALSE;
846 if (method->string_ctor)
847 return FALSE;
849 if (mono_aot_only && m_class_get_image (method->klass)->aot_module && !(method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) {
850 ERROR_DECL (error);
851 gpointer addr = mono_jit_compile_method_jit_only (method, error);
852 if (addr && is_ok (error))
853 return TRUE;
856 for (l = mono_interp_jit_classes; l; l = l->next) {
857 const char *class_name = (const char*)l->data;
858 // FIXME: Namespaces
859 if (!strcmp (m_class_get_name (method->klass), class_name))
860 return TRUE;
863 //return TRUE;
864 return FALSE;
867 static int mono_class_get_magic_index (MonoClass *k)
869 if (mono_class_is_magic_int (k))
870 return !strcmp ("nint", m_class_get_name (k)) ? 0 : 1;
872 if (mono_class_is_magic_float (k))
873 return 2;
875 return -1;
878 static void
879 interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *target_method)
881 MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_method_access;
883 /* Inject code throwing MethodAccessException */
884 interp_add_ins (td, MINT_MONO_LDPTR);
885 td->last_ins->data [0] = get_data_item_index (td, method);
886 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
888 interp_add_ins (td, MINT_MONO_LDPTR);
889 td->last_ins->data [0] = get_data_item_index (td, target_method);
890 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
892 interp_add_ins (td, MINT_ICALL_PP_V);
893 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
895 td->sp -= 2;
898 static void
899 interp_generate_bie_throw (TransformData *td)
901 MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_bad_image;
903 interp_add_ins (td, MINT_ICALL_V_V);
904 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
908 * These are additional locals that can be allocated as we transform the code.
909 * They are allocated past the method locals so they are accessed in the same
910 * way, with an offset relative to the frame->locals.
912 static int
913 create_interp_local (TransformData *td, MonoType *type)
915 if (td->locals_size == td->locals_capacity) {
916 td->locals_capacity *= 2;
917 if (td->locals_capacity == 0)
918 td->locals_capacity = 2;
919 td->locals = (InterpLocal*) g_realloc (td->locals, td->locals_capacity * sizeof (InterpLocal));
921 td->locals [td->locals_size].type = type;
922 td->locals [td->locals_size].flags = 0;
923 td->locals [td->locals_size].offset = -1;
924 td->locals_size++;
925 return td->locals_size - 1;
928 static int
929 get_interp_local_offset (TransformData *td, int local)
931 int align, size, offset;
933 if (td->locals [local].offset != -1)
934 return td->locals [local].offset;
936 offset = td->total_locals_size;
937 size = mono_type_size (td->locals [local].type, &align);
938 offset = ALIGN_TO (offset, align);
940 td->locals [local].offset = offset;
942 td->total_locals_size = offset + size;
943 g_assert (td->total_locals_size < G_MAXUINT16);
945 return offset;
948 static void
949 dump_mint_code (const guint16 *start, const guint16* end)
951 const guint16 *p = start;
952 while (p < end) {
953 char *ins = mono_interp_dis_mintop (start, p);
954 g_print ("%s\n", ins);
955 g_free (ins);
956 p = mono_interp_dis_mintop_len (p);
960 /* For debug use */
961 void
962 mono_interp_print_code (InterpMethod *imethod)
964 MonoJitInfo *jinfo = imethod->jinfo;
965 const guint16 *start;
967 if (!jinfo)
968 return;
970 char *name = mono_method_full_name (imethod->method, 1);
971 g_print ("Method : %s\n", name);
972 g_free (name);
974 start = (guint16*) jinfo->code_start;
975 dump_mint_code (start, start + jinfo->code_size);
979 static MonoMethodHeader*
980 interp_method_get_header (MonoMethod* method, MonoError *error)
982 /* An explanation: mono_method_get_header_internal returns an error if
983 * called on a method with no body (e.g. an abstract method, or an
984 * icall). We don't want that.
986 if (mono_method_has_no_body (method))
987 return NULL;
988 else
989 return mono_method_get_header_internal (method, error);
992 /* stores top of stack as local and pushes address of it on stack */
993 static void
994 emit_store_value_as_local (TransformData *td, MonoType *src)
996 int size = mini_magic_type_size (NULL, src);
997 int local = create_interp_local (td, mini_native_type_replace_type (src));
999 store_local_general (td, local, src);
1001 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
1002 interp_add_ins (td, MINT_LDLOC_VT);
1003 td->last_ins->data [0] = local;
1004 WRITE32_INS (td->last_ins, 1, &size);
1006 PUSH_VT (td, size);
1007 PUSH_TYPE (td, STACK_TYPE_VT, NULL);
1010 // Returns whether we can optimize away the instructions starting at start.
1011 // If any instructions are part of a new basic block, we can't remove them.
1012 static gboolean
1013 interp_is_bb_start (TransformData *td, InterpInst *start, InterpInst *end)
1015 InterpInst *ins = start;
1016 while (ins != end) {
1017 if (ins->il_offset != -1) {
1018 if (td->is_bb_start [ins->il_offset])
1019 return TRUE;
1021 ins = ins->next;
1023 return FALSE;
1026 static gboolean
1027 interp_ins_is_ldc (InterpInst *ins)
1029 return ins->opcode >= MINT_LDC_I4_M1 && ins->opcode <= MINT_LDC_I8;
1032 static gint32
1033 interp_ldc_i4_get_const (InterpInst *ins)
1035 switch (ins->opcode) {
1036 case MINT_LDC_I4_M1: return -1;
1037 case MINT_LDC_I4_0: return 0;
1038 case MINT_LDC_I4_1: return 1;
1039 case MINT_LDC_I4_2: return 2;
1040 case MINT_LDC_I4_3: return 3;
1041 case MINT_LDC_I4_4: return 4;
1042 case MINT_LDC_I4_5: return 5;
1043 case MINT_LDC_I4_6: return 6;
1044 case MINT_LDC_I4_7: return 7;
1045 case MINT_LDC_I4_8: return 8;
1046 case MINT_LDC_I4_S: return (gint32)(gint8)ins->data [0];
1047 case MINT_LDC_I4: return READ32 (&ins->data [0]);
1048 default:
1049 g_assert_not_reached ();
1053 static int
1054 interp_get_ldind_for_mt (int mt)
1056 switch (mt) {
1057 case MINT_TYPE_I1: return MINT_LDIND_I1_CHECK;
1058 case MINT_TYPE_U1: return MINT_LDIND_U1_CHECK;
1059 case MINT_TYPE_I2: return MINT_LDIND_I2_CHECK;
1060 case MINT_TYPE_U2: return MINT_LDIND_U2_CHECK;
1061 case MINT_TYPE_I4: return MINT_LDIND_I4_CHECK;
1062 case MINT_TYPE_I8: return MINT_LDIND_I8_CHECK;
1063 case MINT_TYPE_R4: return MINT_LDIND_R4_CHECK;
1064 case MINT_TYPE_R8: return MINT_LDIND_R8_CHECK;
1065 case MINT_TYPE_O: return MINT_LDIND_REF;
1066 default:
1067 g_assert_not_reached ();
1069 return -1;
1072 static void
1073 interp_emit_ldobj (TransformData *td, MonoClass *klass)
1075 int mt = mint_type (m_class_get_byval_arg (klass));
1076 int size;
1078 if (mt == MINT_TYPE_VT) {
1079 interp_add_ins (td, MINT_LDOBJ_VT);
1080 size = mono_class_value_size (klass, NULL);
1081 WRITE32_INS (td->last_ins, 0, &size);
1082 PUSH_VT (td, size);
1083 } else {
1084 int opcode = interp_get_ldind_for_mt (mt);
1085 interp_add_ins (td, opcode);
1088 SET_TYPE (td->sp - 1, stack_type [mt], klass);
1091 static void
1092 interp_emit_stobj (TransformData *td, MonoClass *klass)
1094 int mt = mint_type (m_class_get_byval_arg (klass));
1096 if (mt == MINT_TYPE_VT) {
1097 int size;
1098 interp_add_ins (td, MINT_STOBJ_VT);
1099 td->last_ins->data [0] = get_data_item_index(td, klass);
1100 size = mono_class_value_size (klass, NULL);
1101 POP_VT (td, size);
1102 } else {
1103 int opcode;
1104 switch (mt) {
1105 case MINT_TYPE_I1:
1106 case MINT_TYPE_U1:
1107 opcode = MINT_STIND_I1;
1108 break;
1109 case MINT_TYPE_I2:
1110 case MINT_TYPE_U2:
1111 opcode = MINT_STIND_I2;
1112 break;
1113 case MINT_TYPE_I4:
1114 opcode = MINT_STIND_I4;
1115 break;
1116 case MINT_TYPE_I8:
1117 opcode = MINT_STIND_I8;
1118 break;
1119 case MINT_TYPE_R4:
1120 opcode = MINT_STIND_R4;
1121 break;
1122 case MINT_TYPE_R8:
1123 opcode = MINT_STIND_R8;
1124 break;
1125 case MINT_TYPE_O:
1126 opcode = MINT_STIND_REF;
1127 break;
1128 default: g_assert_not_reached (); break;
1130 interp_add_ins (td, opcode);
1132 td->sp -= 2;
1135 static void
1136 interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check_class)
1138 MonoClass *element_class = m_class_get_element_class (array_class);
1139 int rank = m_class_get_rank (array_class);
1140 int size = mono_class_array_element_size (element_class);
1142 // We only need type checks when writing to array of references
1143 if (!check_class || m_class_is_valuetype (element_class)) {
1144 if (rank == 1) {
1145 interp_add_ins (td, MINT_LDELEMA1);
1146 WRITE32_INS (td->last_ins, 0, &size);
1147 } else {
1148 interp_add_ins (td, MINT_LDELEMA);
1149 td->last_ins->data [0] = rank;
1150 WRITE32_INS (td->last_ins, 1, &size);
1152 } else {
1153 interp_add_ins (td, MINT_LDELEMA_TC);
1154 td->last_ins->data [0] = rank;
1155 td->last_ins->data [1] = get_data_item_index (td, check_class);
1158 td->sp -= rank;
1159 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1162 /* Return TRUE if call transformation is finished */
1163 static gboolean
1164 interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClass *constrained_class, MonoMethodSignature *csignature, gboolean readonly, int *op)
1166 const char *tm = target_method->name;
1167 int i;
1168 int type_index = mono_class_get_magic_index (target_method->klass);
1169 gboolean in_corlib = m_class_get_image (target_method->klass) == mono_defaults.corlib;
1170 const char *klass_name_space = m_class_get_name_space (target_method->klass);
1171 const char *klass_name = m_class_get_name (target_method->klass);
1173 if (target_method->klass == mono_defaults.string_class) {
1174 if (tm [0] == 'g') {
1175 if (strcmp (tm, "get_Chars") == 0)
1176 *op = MINT_GETCHR;
1177 else if (strcmp (tm, "get_Length") == 0)
1178 *op = MINT_STRLEN;
1180 } else if (type_index >= 0) {
1181 MonoClass *magic_class = target_method->klass;
1183 const int mt = mint_type (m_class_get_byval_arg (magic_class));
1184 if (!strcmp (".ctor", tm)) {
1185 MonoType *arg = csignature->params [0];
1186 /* depending on SIZEOF_VOID_P and the type of the value passed to the .ctor we either have to CONV it, or do nothing */
1187 int arg_size = mini_magic_type_size (NULL, arg);
1189 if (arg_size > SIZEOF_VOID_P) { // 8 -> 4
1190 switch (type_index) {
1191 case 0: case 1:
1192 interp_add_ins (td, MINT_CONV_I4_I8);
1193 break;
1194 case 2:
1195 interp_add_ins (td, MINT_CONV_R4_R8);
1196 break;
1200 if (arg_size < SIZEOF_VOID_P) { // 4 -> 8
1201 switch (type_index) {
1202 case 0:
1203 interp_add_ins (td, MINT_CONV_I8_I4);
1204 break;
1205 case 1:
1206 interp_add_ins (td, MINT_CONV_I8_U4);
1207 break;
1208 case 2:
1209 interp_add_ins (td, MINT_CONV_R8_R4);
1210 break;
1214 switch (type_index) {
1215 case 0: case 1:
1216 #if SIZEOF_VOID_P == 4
1217 interp_add_ins (td, MINT_STIND_I4);
1218 #else
1219 interp_add_ins (td, MINT_STIND_I8);
1220 #endif
1221 break;
1222 case 2:
1223 #if SIZEOF_VOID_P == 4
1224 interp_add_ins (td, MINT_STIND_R4);
1225 #else
1226 interp_add_ins (td, MINT_STIND_R8);
1227 #endif
1228 break;
1231 td->sp -= 2;
1232 td->ip += 5;
1233 return TRUE;
1234 } else if (!strcmp ("op_Implicit", tm ) || !strcmp ("op_Explicit", tm)) {
1235 MonoType *src = csignature->params [0];
1236 MonoType *dst = csignature->ret;
1237 MonoClass *src_klass = mono_class_from_mono_type_internal (src);
1238 int src_size = mini_magic_type_size (NULL, src);
1239 int dst_size = mini_magic_type_size (NULL, dst);
1241 gboolean store_value_as_local = FALSE;
1243 switch (type_index) {
1244 case 0: case 1:
1245 if (!mini_magic_is_int_type (src) || !mini_magic_is_int_type (dst)) {
1246 if (mini_magic_is_int_type (src))
1247 store_value_as_local = TRUE;
1248 else if (mono_class_is_magic_float (src_klass))
1249 store_value_as_local = TRUE;
1250 else
1251 return FALSE;
1253 break;
1254 case 2:
1255 if (!mini_magic_is_float_type (src) || !mini_magic_is_float_type (dst)) {
1256 if (mini_magic_is_float_type (src))
1257 store_value_as_local = TRUE;
1258 else if (mono_class_is_magic_int (src_klass))
1259 store_value_as_local = TRUE;
1260 else
1261 return FALSE;
1263 break;
1266 if (store_value_as_local) {
1267 emit_store_value_as_local (td, src);
1269 /* emit call to managed conversion method */
1270 return FALSE;
1273 if (src_size > dst_size) { // 8 -> 4
1274 switch (type_index) {
1275 case 0: case 1:
1276 interp_add_ins (td, MINT_CONV_I4_I8);
1277 break;
1278 case 2:
1279 interp_add_ins (td, MINT_CONV_R4_R8);
1280 break;
1284 if (src_size < dst_size) { // 4 -> 8
1285 switch (type_index) {
1286 case 0:
1287 interp_add_ins (td, MINT_CONV_I8_I4);
1288 break;
1289 case 1:
1290 interp_add_ins (td, MINT_CONV_I8_U4);
1291 break;
1292 case 2:
1293 interp_add_ins (td, MINT_CONV_R8_R4);
1294 break;
1298 SET_TYPE (td->sp - 1, stack_type [mint_type (dst)], mono_class_from_mono_type_internal (dst));
1299 td->ip += 5;
1300 return TRUE;
1301 } else if (!strcmp ("op_Increment", tm)) {
1302 g_assert (type_index != 2); // no nfloat
1303 #if SIZEOF_VOID_P == 8
1304 interp_add_ins (td, MINT_ADD1_I8);
1305 #else
1306 interp_add_ins (td, MINT_ADD1_I4);
1307 #endif
1308 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1309 td->ip += 5;
1310 return TRUE;
1311 } else if (!strcmp ("op_Decrement", tm)) {
1312 g_assert (type_index != 2); // no nfloat
1313 #if SIZEOF_VOID_P == 8
1314 interp_add_ins (td, MINT_SUB1_I8);
1315 #else
1316 interp_add_ins (td, MINT_SUB1_I4);
1317 #endif
1318 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1319 td->ip += 5;
1320 return TRUE;
1321 } else if (!strcmp ("CompareTo", tm) || !strcmp ("Equals", tm)) {
1322 MonoType *arg = csignature->params [0];
1324 /* on 'System.n*::{CompareTo,Equals} (System.n*)' variant we need to push managed
1325 * pointer instead of value */
1326 if (arg->type == MONO_TYPE_VALUETYPE)
1327 emit_store_value_as_local (td, arg);
1329 /* emit call to managed conversion method */
1330 return FALSE;
1331 } else if (!strcmp (".cctor", tm)) {
1332 /* white list */
1333 return FALSE;
1334 } else if (!strcmp ("Parse", tm)) {
1335 /* white list */
1336 return FALSE;
1337 } else if (!strcmp ("ToString", tm)) {
1338 /* white list */
1339 return FALSE;
1340 } else if (!strcmp ("GetHashCode", tm)) {
1341 /* white list */
1342 return FALSE;
1343 } else if (!strcmp ("IsNaN", tm) || !strcmp ("IsInfinity", tm) || !strcmp ("IsNegativeInfinity", tm) || !strcmp ("IsPositiveInfinity", tm)) {
1344 g_assert (type_index == 2); // nfloat only
1345 /* white list */
1346 return FALSE;
1349 for (i = 0; i < sizeof (int_unnop) / sizeof (MagicIntrinsic); ++i) {
1350 if (!strcmp (int_unnop [i].op_name, tm)) {
1351 interp_add_ins (td, int_unnop [i].insn [type_index]);
1352 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1353 td->ip += 5;
1354 return TRUE;
1358 for (i = 0; i < sizeof (int_binop) / sizeof (MagicIntrinsic); ++i) {
1359 if (!strcmp (int_binop [i].op_name, tm)) {
1360 interp_add_ins (td, int_binop [i].insn [type_index]);
1361 td->sp -= 1;
1362 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1363 td->ip += 5;
1364 return TRUE;
1368 for (i = 0; i < sizeof (int_cmpop) / sizeof (MagicIntrinsic); ++i) {
1369 if (!strcmp (int_cmpop [i].op_name, tm)) {
1370 MonoClass *k = mono_defaults.boolean_class;
1371 interp_add_ins (td, int_cmpop [i].insn [type_index]);
1372 td->sp -= 1;
1373 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1374 td->ip += 5;
1375 return TRUE;
1379 g_error ("TODO: interp_transform_call %s:%s", m_class_get_name (target_method->klass), tm);
1380 } else if (mono_class_is_subclass_of_internal (target_method->klass, mono_defaults.array_class, FALSE)) {
1381 if (!strcmp (tm, "get_Rank")) {
1382 *op = MINT_ARRAY_RANK;
1383 } else if (!strcmp (tm, "get_Length")) {
1384 *op = MINT_LDLEN;
1385 } else if (!strcmp (tm, "Address")) {
1386 MonoClass *check_class = readonly ? NULL : m_class_get_element_class (target_method->klass);
1387 interp_emit_ldelema (td, target_method->klass, check_class);
1388 td->ip += 5;
1389 return TRUE;
1390 } else if (!strcmp (tm, "UnsafeMov") || !strcmp (tm, "UnsafeLoad")) {
1391 *op = MINT_CALLRUN;
1392 } else if (!strcmp (tm, "Get")) {
1393 interp_emit_ldelema (td, target_method->klass, NULL);
1394 interp_emit_ldobj (td, m_class_get_element_class (target_method->klass));
1395 td->ip += 5;
1396 return TRUE;
1397 } else if (!strcmp (tm, "Set")) {
1398 MonoClass *element_class = m_class_get_element_class (target_method->klass);
1399 MonoType *local_type = m_class_get_byval_arg (element_class);
1400 MonoClass *value_class = td->sp [-1].klass;
1401 // If value_class is NULL it means the top of stack is a simple type (valuetype)
1402 // which doesn't require type checks, or that we have no type information because
1403 // the code is unsafe (like in some wrappers). In that case we assume the type
1404 // of the array and don't do any checks.
1406 int local = create_interp_local (td, local_type);
1408 store_local_general (td, local, local_type);
1409 interp_emit_ldelema (td, target_method->klass, value_class);
1410 load_local_general (td, local, local_type);
1411 interp_emit_stobj (td, element_class);
1412 td->ip += 5;
1413 return TRUE;
1414 } else if (!strcmp (tm, "UnsafeStore")) {
1415 g_error ("TODO ArrayClass::UnsafeStore");
1417 } else if (in_corlib &&
1418 !strcmp (klass_name_space, "System.Diagnostics") &&
1419 !strcmp (klass_name, "Debugger")) {
1420 if (!strcmp (tm, "Break") && csignature->param_count == 0) {
1421 if (mini_should_insert_breakpoint (td->method))
1422 *op = MINT_BREAK;
1424 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "ByReference`1")) {
1425 g_assert (!strcmp (tm, "get_Value"));
1426 *op = MINT_INTRINS_BYREFERENCE_GET_VALUE;
1427 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "Math") && csignature->param_count == 1 && csignature->params [0]->type == MONO_TYPE_R8) {
1428 if (tm [0] == 'A') {
1429 if (strcmp (tm, "Abs") == 0 && csignature->params [0]->type == MONO_TYPE_R8) {
1430 *op = MINT_ABS;
1431 } else if (strcmp (tm, "Asin") == 0){
1432 *op = MINT_ASIN;
1433 } else if (strcmp (tm, "Asinh") == 0){
1434 *op = MINT_ASINH;
1435 } else if (strcmp (tm, "Acos") == 0){
1436 *op = MINT_ACOS;
1437 } else if (strcmp (tm, "Acosh") == 0){
1438 *op = MINT_ACOSH;
1439 } else if (strcmp (tm, "Atan") == 0){
1440 *op = MINT_ATAN;
1441 } else if (strcmp (tm, "Atanh") == 0){
1442 *op = MINT_ATANH;
1444 } else if (tm [0] == 'C') {
1445 if (strcmp (tm, "Cos") == 0) {
1446 *op = MINT_COS;
1447 } else if (strcmp (tm, "Cbrt") == 0){
1448 *op = MINT_CBRT;
1449 } else if (strcmp (tm, "Cosh") == 0){
1450 *op = MINT_COSH;
1452 } else if (tm [0] == 'S') {
1453 if (strcmp (tm, "Sin") == 0) {
1454 *op = MINT_SIN;
1455 } else if (strcmp (tm, "Sqrt") == 0) {
1456 *op = MINT_SQRT;
1457 } else if (strcmp (tm, "Sinh") == 0){
1458 *op = MINT_SINH;
1460 } else if (tm [0] == 'T') {
1461 if (strcmp (tm, "Tan") == 0) {
1462 *op = MINT_TAN;
1463 } else if (strcmp (tm, "Tanh") == 0){
1464 *op = MINT_TANH;
1467 } else if (in_corlib && !strcmp (klass_name_space, "System") && (!strcmp (klass_name, "Span`1") || !strcmp (klass_name, "ReadOnlySpan`1"))) {
1468 if (!strcmp (tm, "get_Item")) {
1469 MonoGenericClass *gclass = mono_class_get_generic_class (target_method->klass);
1470 MonoClass *param_class = mono_class_from_mono_type_internal (gclass->context.class_inst->type_argv [0]);
1472 if (!mini_is_gsharedvt_variable_klass (param_class)) {
1473 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1474 g_assert (length_field);
1475 int offset_length = length_field->offset - sizeof (MonoObject);
1477 MonoClassField *ptr_field = mono_class_get_field_from_name_full (target_method->klass, "_pointer", NULL);
1478 g_assert (ptr_field);
1479 int offset_pointer = ptr_field->offset - sizeof (MonoObject);
1481 int size = mono_class_array_element_size (param_class);
1482 interp_add_ins (td, MINT_GETITEM_SPAN);
1483 td->last_ins->data [0] = size;
1484 td->last_ins->data [1] = offset_length;
1485 td->last_ins->data [2] = offset_pointer;
1487 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1488 td->sp -= 1;
1489 td->ip += 5;
1490 return TRUE;
1492 } else if (!strcmp (tm, "get_Length")) {
1493 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1494 g_assert (length_field);
1495 int offset_length = length_field->offset - sizeof (MonoObject);
1496 interp_add_ins (td, MINT_LDLEN_SPAN);
1497 td->last_ins->data [0] = offset_length;
1498 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1499 td->ip += 5;
1500 return TRUE;
1502 } else if (in_corlib && !strcmp (klass_name_space, "Internal.Runtime.CompilerServices") && !strcmp (klass_name, "Unsafe")) {
1503 #ifdef ENABLE_NETCORE
1504 if (!strcmp (tm, "AddByteOffset"))
1505 *op = MINT_INTRINS_UNSAFE_ADD_BYTE_OFFSET;
1506 else if (!strcmp (tm, "ByteOffset"))
1507 *op = MINT_INTRINS_UNSAFE_BYTE_OFFSET;
1508 else if (!strcmp (tm, "As") || !strcmp (tm, "AsRef"))
1509 *op = MINT_NOP;
1510 else if (!strcmp (tm, "AsPointer")) {
1511 /* NOP */
1512 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1513 td->ip += 5;
1514 return TRUE;
1515 } else if (!strcmp (tm, "IsAddressLessThan")) {
1516 MonoGenericContext *ctx = mono_method_get_context (target_method);
1517 g_assert (ctx);
1518 g_assert (ctx->method_inst);
1519 g_assert (ctx->method_inst->type_argc == 1);
1521 MonoClass *k = mono_defaults.boolean_class;
1522 interp_add_ins (td, MINT_CLT_UN_P);
1523 td->sp -= 1;
1524 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1525 td->ip += 5;
1526 return TRUE;
1527 } else if (!strcmp (tm, "SizeOf")) {
1528 MonoGenericContext *ctx = mono_method_get_context (target_method);
1529 g_assert (ctx);
1530 g_assert (ctx->method_inst);
1531 g_assert (ctx->method_inst->type_argc == 1);
1532 MonoType *t = ctx->method_inst->type_argv [0];
1533 int align;
1534 int esize = mono_type_size (t, &align);
1535 interp_add_ins (td, MINT_LDC_I4);
1536 WRITE32_INS (td->last_ins, 0, &esize);
1537 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1538 td->ip += 5;
1539 return TRUE;
1540 } else if (!strcmp (tm, "AreSame")) {
1541 *op = MINT_CEQ_P;
1543 #endif
1544 } else if (in_corlib && !strcmp (klass_name_space, "System.Runtime.CompilerServices") && !strcmp (klass_name, "RuntimeHelpers")) {
1545 #ifdef ENABLE_NETCORE
1546 if (!strcmp (tm, "IsBitwiseEquatable")) {
1547 g_assert (csignature->param_count == 0);
1548 MonoGenericContext *ctx = mono_method_get_context (target_method);
1549 g_assert (ctx);
1550 g_assert (ctx->method_inst);
1551 g_assert (ctx->method_inst->type_argc == 1);
1552 MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]);
1554 if (MONO_TYPE_IS_PRIMITIVE (t) && t->type != MONO_TYPE_R4 && t->type != MONO_TYPE_R8)
1555 *op = MINT_LDC_I4_1;
1556 else
1557 *op = MINT_LDC_I4_0;
1558 } else if (!strcmp (tm, "ObjectHasComponentSize")) {
1559 *op = MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE;
1561 #endif
1562 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "RuntimeMethodHandle") && !strcmp (tm, "GetFunctionPointer") && csignature->param_count == 1) {
1563 // We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter
1564 *op = MINT_LDFTN_DYNAMIC;
1565 } else if (in_corlib && target_method->klass == mono_defaults.object_class) {
1566 if (!strcmp (tm, "InternalGetHashCode"))
1567 *op = MINT_INTRINS_GET_HASHCODE;
1568 #ifdef DISABLE_REMOTING
1569 else if (!strcmp (tm, "GetType"))
1570 *op = MINT_INTRINS_GET_TYPE;
1571 #endif
1572 #ifdef ENABLE_NETCORE
1573 else if (!strcmp (tm, "GetRawData")) {
1574 #if SIZEOF_VOID_P == 8
1575 interp_add_ins (td, MINT_LDC_I8_S);
1576 #else
1577 interp_add_ins (td, MINT_LDC_I4_S);
1578 #endif
1579 td->last_ins->data [0] = (gint16) MONO_ABI_SIZEOF (MonoObject);
1581 interp_add_ins (td, MINT_ADD_P);
1582 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1584 td->ip += 5;
1585 return TRUE;
1587 #endif
1588 } else if (in_corlib && target_method->klass == mono_defaults.enum_class && !strcmp (tm, "HasFlag")) {
1589 gboolean intrinsify = FALSE;
1590 MonoClass *base_klass = NULL;
1591 if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1592 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1593 td->last_ins->prev->prev && td->last_ins->prev->prev->opcode == MINT_BOX &&
1594 td->sp [-2].klass == td->sp [-1].klass &&
1595 !interp_is_bb_start (td, td->last_ins->prev->prev, NULL) &&
1596 !td->is_bb_start [td->in_start - td->il_code]) {
1597 // csc pattern : box, ldc, box, call HasFlag
1598 g_assert (m_class_is_enumtype (td->sp [-2].klass));
1599 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (td->sp [-2].klass));
1600 base_klass = mono_class_from_mono_type_internal (base_type);
1602 // Remove the boxing of valuetypes
1603 interp_clear_ins (td, td->last_ins->prev->prev);
1604 interp_clear_ins (td, td->last_ins);
1606 intrinsify = TRUE;
1607 } else if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1608 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1609 constrained_class && td->sp [-1].klass == constrained_class &&
1610 !interp_is_bb_start (td, td->last_ins->prev, NULL) &&
1611 !td->is_bb_start [td->in_start - td->il_code]) {
1612 // mcs pattern : ldc, box, constrained Enum, call HasFlag
1613 g_assert (m_class_is_enumtype (constrained_class));
1614 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (constrained_class));
1615 base_klass = mono_class_from_mono_type_internal (base_type);
1616 int mt = mint_type (m_class_get_byval_arg (base_klass));
1618 // Remove boxing and load the value of this
1619 interp_clear_ins (td, td->last_ins);
1620 interp_insert_ins (td, td->last_ins->prev->prev, interp_get_ldind_for_mt (mt));
1622 intrinsify = TRUE;
1624 if (intrinsify) {
1625 interp_add_ins (td, MINT_INTRINS_ENUM_HASFLAG);
1626 td->last_ins->data [0] = get_data_item_index (td, base_klass);
1627 td->sp -= 2;
1628 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1629 td->ip += 5;
1630 return TRUE;
1632 } else if (in_corlib && !strcmp (klass_name_space, "System.Threading") && !strcmp (klass_name, "Interlocked")) {
1633 #if ENABLE_NETCORE
1634 if (!strcmp (tm, "MemoryBarrier") && csignature->param_count == 0)
1635 *op = MINT_MONO_MEMORY_BARRIER;
1636 #endif
1637 } else if (in_corlib &&
1638 !strcmp (klass_name_space, "System.Runtime.CompilerServices") &&
1639 !strcmp (klass_name, "JitHelpers") &&
1640 (!strcmp (tm, "EnumEquals") || !strcmp (tm, "EnumCompareTo"))) {
1641 MonoGenericContext *ctx = mono_method_get_context (target_method);
1642 g_assert (ctx);
1643 g_assert (ctx->method_inst);
1644 g_assert (ctx->method_inst->type_argc == 1);
1645 g_assert (csignature->param_count == 2);
1647 MonoType *t = ctx->method_inst->type_argv [0];
1648 t = mini_get_underlying_type (t);
1650 gboolean is_i8 = (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_U8);
1651 gboolean is_unsigned = (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_U2 || t->type == MONO_TYPE_U4 || t->type == MONO_TYPE_U8 || t->type == MONO_TYPE_U);
1653 gboolean is_compareto = strcmp (tm, "EnumCompareTo") == 0;
1654 if (is_compareto) {
1655 int locala, localb;
1656 locala = create_interp_local (td, t);
1657 localb = create_interp_local (td, t);
1659 // Save arguments
1660 store_local_general (td, localb, t);
1661 store_local_general (td, locala, t);
1662 // (a > b)
1663 load_local_general (td, locala, t);
1664 load_local_general (td, localb, t);
1665 if (is_unsigned)
1666 interp_add_ins (td, is_i8 ? MINT_CGT_UN_I8 : MINT_CGT_UN_I4);
1667 else
1668 interp_add_ins (td, is_i8 ? MINT_CGT_I8 : MINT_CGT_I4);
1669 td->sp --;
1670 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1671 // (a < b)
1672 load_local_general (td, locala, t);
1673 load_local_general (td, localb, t);
1674 if (is_unsigned)
1675 interp_add_ins (td, is_i8 ? MINT_CLT_UN_I8 : MINT_CLT_UN_I4);
1676 else
1677 interp_add_ins (td, is_i8 ? MINT_CLT_I8 : MINT_CLT_I4);
1678 td->sp --;
1679 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1680 // (a > b) - (a < b)
1681 interp_add_ins (td, MINT_SUB_I4);
1682 td->sp --;
1683 td->ip += 5;
1684 return TRUE;
1685 } else {
1686 if (is_i8) {
1687 *op = MINT_CEQ_I8;
1688 } else {
1689 *op = MINT_CEQ_I4;
1694 return FALSE;
1697 static MonoMethod*
1698 interp_transform_internal_calls (MonoMethod *method, MonoMethod *target_method, MonoMethodSignature *csignature, gboolean is_virtual)
1700 if (method->wrapper_type == MONO_WRAPPER_NONE && target_method != NULL) {
1701 if (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
1702 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1703 if (!is_virtual && target_method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
1704 target_method = mono_marshal_get_synchronized_wrapper (target_method);
1706 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)
1707 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1709 return target_method;
1712 static gboolean
1713 interp_type_as_ptr (MonoType *tp)
1715 if (MONO_TYPE_IS_POINTER (tp))
1716 return TRUE;
1717 if (MONO_TYPE_IS_REFERENCE (tp))
1718 return TRUE;
1719 if ((tp)->type == MONO_TYPE_I4)
1720 return TRUE;
1721 #if SIZEOF_VOID_P == 8
1722 if ((tp)->type == MONO_TYPE_I8)
1723 return TRUE;
1724 #endif
1725 if ((tp)->type == MONO_TYPE_BOOLEAN)
1726 return TRUE;
1727 if ((tp)->type == MONO_TYPE_CHAR)
1728 return TRUE;
1729 if ((tp)->type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (tp->data.klass))
1730 return TRUE;
1731 return FALSE;
1734 #define INTERP_TYPE_AS_PTR(tp) interp_type_as_ptr (tp)
1736 static int
1737 interp_icall_op_for_sig (MonoMethodSignature *sig)
1739 int op = -1;
1740 switch (sig->param_count) {
1741 case 0:
1742 if (MONO_TYPE_IS_VOID (sig->ret))
1743 op = MINT_ICALL_V_V;
1744 else if (INTERP_TYPE_AS_PTR (sig->ret))
1745 op = MINT_ICALL_V_P;
1746 break;
1747 case 1:
1748 if (MONO_TYPE_IS_VOID (sig->ret)) {
1749 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1750 op = MINT_ICALL_P_V;
1751 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1752 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1753 op = MINT_ICALL_P_P;
1755 break;
1756 case 2:
1757 if (MONO_TYPE_IS_VOID (sig->ret)) {
1758 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1759 INTERP_TYPE_AS_PTR (sig->params [1]))
1760 op = MINT_ICALL_PP_V;
1761 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1762 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1763 INTERP_TYPE_AS_PTR (sig->params [1]))
1764 op = MINT_ICALL_PP_P;
1766 break;
1767 case 3:
1768 if (MONO_TYPE_IS_VOID (sig->ret)) {
1769 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1770 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1771 INTERP_TYPE_AS_PTR (sig->params [2]))
1772 op = MINT_ICALL_PPP_V;
1773 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1774 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1775 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1776 INTERP_TYPE_AS_PTR (sig->params [2]))
1777 op = MINT_ICALL_PPP_P;
1779 break;
1780 case 4:
1781 if (MONO_TYPE_IS_VOID (sig->ret)) {
1782 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1783 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1784 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1785 INTERP_TYPE_AS_PTR (sig->params [3]))
1786 op = MINT_ICALL_PPPP_V;
1787 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1788 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1789 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1790 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1791 INTERP_TYPE_AS_PTR (sig->params [3]))
1792 op = MINT_ICALL_PPPP_P;
1794 break;
1795 case 5:
1796 if (MONO_TYPE_IS_VOID (sig->ret)) {
1797 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1798 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1799 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1800 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1801 INTERP_TYPE_AS_PTR (sig->params [4]))
1802 op = MINT_ICALL_PPPPP_V;
1803 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1804 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1805 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1806 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1807 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1808 INTERP_TYPE_AS_PTR (sig->params [4]))
1809 op = MINT_ICALL_PPPPP_P;
1811 break;
1812 case 6:
1813 if (MONO_TYPE_IS_VOID (sig->ret)) {
1814 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1815 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1816 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1817 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1818 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1819 INTERP_TYPE_AS_PTR (sig->params [5]))
1820 op = MINT_ICALL_PPPPPP_V;
1821 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1822 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1823 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1824 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1825 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1826 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1827 INTERP_TYPE_AS_PTR (sig->params [5]))
1828 op = MINT_ICALL_PPPPPP_P;
1830 break;
1832 return op;
1835 #define INLINE_LENGTH_LIMIT 20
1837 static gboolean
1838 interp_method_check_inlining (TransformData *td, MonoMethod *method)
1840 MonoMethodHeaderSummary header;
1842 if (td->method == method)
1843 return FALSE;
1845 if (!mono_method_get_header_summary (method, &header))
1846 return FALSE;
1848 /*runtime, icall and pinvoke are checked by summary call*/
1849 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
1850 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
1851 (mono_class_is_marshalbyref (method->klass)) ||
1852 header.has_clauses)
1853 return FALSE;
1855 if (header.code_size >= INLINE_LENGTH_LIMIT)
1856 return FALSE;
1858 if (mono_class_needs_cctor_run (method->klass, NULL)) {
1859 MonoVTable *vtable;
1860 ERROR_DECL (error);
1861 if (!m_class_get_runtime_info (method->klass))
1862 /* No vtable created yet */
1863 return FALSE;
1864 vtable = mono_class_vtable_checked (td->rtm->domain, method->klass, error);
1865 if (!is_ok (error)) {
1866 mono_error_cleanup (error);
1867 return FALSE;
1869 if (!vtable->initialized)
1870 return FALSE;
1873 /* We currently access at runtime the wrapper data */
1874 if (method->wrapper_type != MONO_WRAPPER_NONE)
1875 return FALSE;
1877 /* Our usage of `emit_store_value_as_local ()` for nint, nuint and nfloat
1878 * is kinda hacky, and doesn't work with the inliner */
1879 if (mono_class_get_magic_index (method->klass) >= 0)
1880 return FALSE;
1882 return TRUE;
1885 static gboolean
1886 interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHeader *header, MonoError *error)
1888 const unsigned char *prev_ip, *prev_il_code, *prev_in_start;
1889 int *prev_in_offsets;
1890 gboolean ret;
1891 unsigned int prev_max_stack_height, prev_max_vt_sp, prev_locals_size;
1892 int prev_n_data_items;
1893 int i, prev_vt_sp;
1894 int prev_sp_offset;
1895 MonoGenericContext *generic_context = NULL;
1896 StackInfo *prev_param_area;
1897 MonoMethod *prev_inlined_method;
1898 MonoMethodSignature *csignature = mono_method_signature_internal (target_method);
1899 int nargs = csignature->param_count + !!csignature->hasthis;
1900 InterpInst *prev_last_ins;
1902 if (csignature->is_inflated)
1903 generic_context = mono_method_get_context (target_method);
1904 else {
1905 MonoGenericContainer *generic_container = mono_method_get_generic_container (target_method);
1906 if (generic_container)
1907 generic_context = &generic_container->context;
1910 prev_ip = td->ip;
1911 prev_il_code = td->il_code;
1912 prev_in_start = td->in_start;
1913 prev_sp_offset = td->sp - td->stack;
1914 prev_vt_sp = td->vt_sp;
1915 prev_inlined_method = td->inlined_method;
1916 prev_last_ins = td->last_ins;
1917 td->inlined_method = target_method;
1919 prev_max_stack_height = td->max_stack_height;
1920 prev_max_vt_sp = td->max_vt_sp;
1921 prev_locals_size = td->locals_size;
1923 prev_n_data_items = td->n_data_items;
1924 prev_in_offsets = td->in_offsets;
1925 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
1927 /* Inlining pops the arguments, restore the stack */
1928 prev_param_area = (StackInfo*)g_malloc (nargs * sizeof (StackInfo));
1929 memcpy (prev_param_area, &td->sp [-nargs], nargs * sizeof (StackInfo));
1931 int const prev_code_size = td->code_size;
1932 td->code_size = header->code_size;
1934 if (td->verbose_level)
1935 g_print ("Inline start method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1936 ret = generate_code (td, target_method, header, generic_context, error);
1938 if (!ret) {
1939 if (td->verbose_level)
1940 g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1941 td->max_stack_height = prev_max_stack_height;
1942 td->max_vt_sp = prev_max_vt_sp;
1943 td->locals_size = prev_locals_size;
1946 /* Remove any newly added items */
1947 for (i = prev_n_data_items; i < td->n_data_items; i++) {
1948 g_hash_table_remove (td->data_hash, td->data_items [i]);
1950 td->n_data_items = prev_n_data_items;
1951 td->sp = td->stack + prev_sp_offset;
1952 memcpy (&td->sp [-nargs], prev_param_area, nargs * sizeof (StackInfo));
1953 td->vt_sp = prev_vt_sp;
1954 td->last_ins = prev_last_ins;
1955 if (td->last_ins)
1956 td->last_ins->next = NULL;
1957 UnlockedIncrement (&mono_interp_stats.inline_failures);
1958 } else {
1959 if (td->verbose_level)
1960 g_print ("Inline end method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1961 UnlockedIncrement (&mono_interp_stats.inlined_methods);
1962 // Make sure we have an IR instruction associated with the now removed IL CALL
1963 // FIXME This could be prettier. We might be able to make inlining saner now that
1964 // that we can easily tweak the instruction list.
1965 if (!prev_inlined_method) {
1966 if (prev_last_ins) {
1967 if (prev_last_ins->next)
1968 prev_last_ins->next->il_offset = prev_in_start - prev_il_code;
1969 } else if (td->first_ins) {
1970 td->first_ins->il_offset = prev_in_start - prev_il_code;
1975 td->ip = prev_ip;
1976 td->in_start = prev_in_start;
1977 td->il_code = prev_il_code;
1978 td->inlined_method = prev_inlined_method;
1979 td->code_size = prev_code_size;
1981 g_free (td->in_offsets);
1982 td->in_offsets = prev_in_offsets;
1984 g_free (prev_param_area);
1985 return ret;
1988 static void
1989 interp_constrained_box (TransformData *td, MonoDomain *domain, MonoClass *constrained_class, MonoMethodSignature *csignature, MonoError *error)
1991 int mt = mint_type (m_class_get_byval_arg (constrained_class));
1992 if (mono_class_is_nullable (constrained_class)) {
1993 g_assert (mt == MINT_TYPE_VT);
1994 interp_add_ins (td, MINT_BOX_NULLABLE);
1995 td->last_ins->data [0] = get_data_item_index (td, constrained_class);
1996 td->last_ins->data [1] = csignature->param_count | ((td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : BOX_NOT_CLEAR_VT_SP);
1997 } else {
1998 MonoVTable *vtable = mono_class_vtable_checked (domain, constrained_class, error);
1999 return_if_nok (error);
2001 if (mt == MINT_TYPE_VT) {
2002 interp_add_ins (td, MINT_BOX_VT);
2003 td->last_ins->data [0] = get_data_item_index (td, vtable);
2004 td->last_ins->data [1] = csignature->param_count | ((td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : BOX_NOT_CLEAR_VT_SP);
2005 } else {
2006 interp_add_ins (td, MINT_BOX);
2007 td->last_ins->data [0] = get_data_item_index (td, vtable);
2008 td->last_ins->data [1] = csignature->param_count;
2013 /* Return FALSE if error, including inline failure */
2014 static gboolean
2015 interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target_method, MonoDomain *domain, MonoGenericContext *generic_context, unsigned char *is_bb_start, MonoClass *constrained_class, gboolean readonly, MonoError *error, gboolean check_visibility, gboolean save_last_error)
2017 MonoImage *image = m_class_get_image (method->klass);
2018 MonoMethodSignature *csignature;
2019 int is_virtual = *td->ip == CEE_CALLVIRT;
2020 int calli = *td->ip == CEE_CALLI || *td->ip == CEE_MONO_CALLI_EXTRA_ARG;
2021 int i;
2022 guint32 vt_stack_used = 0;
2023 guint32 vt_res_size = 0;
2024 int op = -1;
2025 int native = 0;
2026 int is_void = 0;
2027 int need_null_check = is_virtual;
2029 guint32 token = read32 (td->ip + 1);
2031 if (target_method == NULL) {
2032 if (calli) {
2033 CHECK_STACK(td, 1);
2034 if (method->wrapper_type != MONO_WRAPPER_NONE)
2035 csignature = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
2036 else {
2037 csignature = mono_metadata_parse_signature_checked (image, token, error);
2038 return_val_if_nok (error, FALSE);
2041 if (generic_context) {
2042 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
2043 return_val_if_nok (error, FALSE);
2047 * The compiled interp entry wrapper is passed to runtime_invoke instead of
2048 * the InterpMethod pointer. FIXME
2050 native = csignature->pinvoke || method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE;
2052 target_method = NULL;
2053 } else {
2054 if (method->wrapper_type == MONO_WRAPPER_NONE) {
2055 target_method = mono_get_method_checked (image, token, NULL, generic_context, error);
2056 return_val_if_nok (error, FALSE);
2057 } else
2058 target_method = (MonoMethod *)mono_method_get_wrapper_data (method, token);
2059 csignature = mono_method_signature_internal (target_method);
2061 if (generic_context) {
2062 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
2063 return_val_if_nok (error, FALSE);
2064 target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, error);
2065 return_val_if_nok (error, FALSE);
2068 } else {
2069 csignature = mono_method_signature_internal (target_method);
2072 if (check_visibility && target_method && !mono_method_can_access_method (method, target_method))
2073 interp_generate_mae_throw (td, method, target_method);
2075 if (target_method && target_method->string_ctor) {
2076 /* Create the real signature */
2077 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (td->mempool, csignature);
2078 ctor_sig->ret = m_class_get_byval_arg (mono_defaults.string_class);
2080 csignature = ctor_sig;
2083 /* Intrinsics */
2084 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
2085 return TRUE;
2087 if (constrained_class) {
2088 if (m_class_is_enumtype (constrained_class) && !strcmp (target_method->name, "GetHashCode")) {
2089 /* Use the corresponding method from the base type to avoid boxing */
2090 MonoType *base_type = mono_class_enum_basetype_internal (constrained_class);
2091 g_assert (base_type);
2092 constrained_class = mono_class_from_mono_type_internal (base_type);
2093 target_method = mono_class_get_method_from_name_checked (constrained_class, target_method->name, 0, 0, error);
2094 mono_error_assert_ok (error);
2095 g_assert (target_method);
2099 if (constrained_class) {
2100 mono_class_setup_vtable (constrained_class);
2101 if (mono_class_has_failure (constrained_class)) {
2102 mono_error_set_for_class_failure (error, constrained_class);
2103 return FALSE;
2105 #if DEBUG_INTERP
2106 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);
2107 #endif
2108 target_method = mono_get_method_constrained_with_method (image, target_method, constrained_class, generic_context, error);
2109 #if DEBUG_INTERP
2110 g_print (" : %s::%s. %s (%p)\n", target_method->klass->name, target_method->name, mono_signature_full_name (target_method->signature), target_method);
2111 #endif
2112 /* Intrinsics: Try again, it could be that `mono_get_method_constrained_with_method` resolves to a method that we can substitute */
2113 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
2114 return TRUE;
2116 return_val_if_nok (error, FALSE);
2117 mono_class_setup_vtable (target_method->klass);
2119 if (!m_class_is_valuetype (constrained_class)) {
2120 /* managed pointer on the stack, we need to deref that puppy */
2121 interp_add_ins (td, MINT_LDIND_I);
2122 td->last_ins->data [0] = csignature->param_count;
2123 } 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) {
2124 if (target_method->klass == mono_defaults.enum_class && (td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
2125 /* managed pointer on the stack, we need to deref that puppy */
2126 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2127 interp_add_ins (td, MINT_LDIND_I8);
2128 td->last_ins->data [0] = csignature->param_count;
2131 interp_constrained_box (td, domain, constrained_class, csignature, error);
2132 return_val_if_nok (error, FALSE);
2133 } else {
2134 if (target_method->klass != constrained_class) {
2136 * The type parameter is instantiated as a valuetype,
2137 * but that type doesn't override the method we're
2138 * calling, so we need to box `this'.
2140 if (target_method->klass == mono_defaults.enum_class && (td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
2141 /* managed pointer on the stack, we need to deref that puppy */
2142 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2143 interp_add_ins (td, MINT_LDIND_I8);
2144 td->last_ins->data [0] = csignature->param_count;
2147 interp_constrained_box (td, domain, constrained_class, csignature, error);
2148 return_val_if_nok (error, FALSE);
2150 is_virtual = FALSE;
2154 if (target_method)
2155 mono_class_init_internal (target_method->klass);
2157 if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
2158 if (!mono_class_is_interface (method->klass))
2159 interp_generate_bie_throw (td);
2160 else
2161 is_virtual = TRUE;
2164 if (is_virtual && target_method && (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2165 (MONO_METHOD_IS_FINAL (target_method) &&
2166 target_method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2167 !(mono_class_is_marshalbyref (target_method->klass))) {
2168 /* Not really virtual, just needs a null check */
2169 is_virtual = FALSE;
2170 need_null_check = TRUE;
2173 CHECK_STACK (td, csignature->param_count + csignature->hasthis);
2174 if (!td->gen_sdb_seq_points && !calli && op == -1 && (!is_virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) &&
2175 (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 &&
2176 (target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0 &&
2177 !(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING)) {
2178 (void)mono_class_vtable_checked (domain, target_method->klass, error);
2179 return_val_if_nok (error, FALSE);
2181 if (method == target_method && *(td->ip + 5) == CEE_RET && !(csignature->hasthis && m_class_is_valuetype (target_method->klass))) {
2182 if (td->inlined_method)
2183 return FALSE;
2185 if (td->verbose_level)
2186 g_print ("Optimize tail call of %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
2188 for (i = csignature->param_count - 1 + !!csignature->hasthis; i >= 0; --i)
2189 store_arg (td, i);
2191 interp_add_ins (td, MINT_BR_S);
2192 // We are branching to the beginning of the method
2193 td->last_ins->data [0] = 0;
2194 if (!is_bb_start [td->ip + 5 - td->il_code])
2195 ++td->ip; /* gobble the CEE_RET if it isn't branched to */
2196 td->ip += 5;
2197 return TRUE;
2201 target_method = interp_transform_internal_calls (method, target_method, csignature, is_virtual);
2203 if (csignature->call_convention == MONO_CALL_VARARG) {
2204 csignature = mono_method_get_signature_checked (target_method, image, token, generic_context, error);
2205 int vararg_stack = 0;
2207 * For vararg calls, ArgIterator expects the signature and the varargs to be
2208 * stored in a linear memory. We allocate the necessary vt_stack space for
2209 * this. All varargs will be pushed to the vt_stack at call site.
2211 vararg_stack += sizeof (gpointer);
2212 for (i = csignature->sentinelpos; i < csignature->param_count; ++i) {
2213 int align, arg_size;
2214 arg_size = mono_type_stack_size (csignature->params [i], &align);
2215 vararg_stack += ALIGN_TO (arg_size, align);
2217 /* allocate space for the pointer to varargs space start */
2218 vararg_stack += sizeof (gpointer);
2219 vt_stack_used += ALIGN_TO (vararg_stack, MINT_VT_ALIGNMENT);
2220 PUSH_VT (td, vararg_stack);
2223 if (need_null_check) {
2224 interp_add_ins (td, MINT_CKNULL_N);
2225 td->last_ins->data [0] = csignature->param_count + 1;
2228 g_assert (csignature->call_convention != MONO_CALL_FASTCALL);
2229 if ((mono_interp_opt & INTERP_OPT_INLINE) && op == -1 && !is_virtual && target_method && interp_method_check_inlining (td, target_method)) {
2230 MonoMethodHeader *mheader = interp_method_get_header (target_method, error);
2231 return_val_if_nok (error, FALSE);
2233 if (interp_inline_method (td, target_method, mheader, error)) {
2234 td->ip += 5;
2235 return TRUE;
2239 /* Don't inline methods that do calls */
2240 if (op == -1 && td->inlined_method)
2241 return FALSE;
2243 /* We need to convert delegate invoke to a indirect call on the interp_invoke_impl field */
2244 if (target_method && m_class_get_parent (target_method->klass) == mono_defaults.multicastdelegate_class) {
2245 const char *name = target_method->name;
2246 if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
2247 calli = TRUE;
2248 interp_add_ins (td, MINT_LD_DELEGATE_INVOKE_IMPL);
2249 td->last_ins->data [0] = csignature->param_count + 1;
2250 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
2254 /* Pop the function pointer */
2255 if (calli)
2256 --td->sp;
2258 td->sp -= csignature->param_count + !!csignature->hasthis;
2259 for (i = 0; i < csignature->param_count; ++i) {
2260 if (td->sp [i + !!csignature->hasthis].type == STACK_TYPE_VT) {
2261 gint32 size;
2262 MonoClass *klass = mono_class_from_mono_type_internal (csignature->params [i]);
2263 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2264 size = mono_class_native_size (klass, NULL);
2265 else
2266 size = mono_class_value_size (klass, NULL);
2267 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
2268 vt_stack_used += size;
2272 /* need to handle typedbyref ... */
2273 if (csignature->ret->type != MONO_TYPE_VOID) {
2274 int mt = mint_type(csignature->ret);
2275 MonoClass *klass = mono_class_from_mono_type_internal (csignature->ret);
2276 if (mt == MINT_TYPE_VT) {
2277 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2278 vt_res_size = mono_class_native_size (klass, NULL);
2279 else
2280 vt_res_size = mono_class_value_size (klass, NULL);
2281 if (mono_class_has_failure (klass)) {
2282 mono_error_set_for_class_failure (error, klass);
2283 return FALSE;
2285 PUSH_VT(td, vt_res_size);
2287 PUSH_TYPE(td, stack_type[mt], klass);
2288 } else
2289 is_void = TRUE;
2291 if (op >= 0) {
2292 interp_add_ins (td, op);
2294 if (op == MINT_LDLEN) {
2295 #ifdef MONO_BIG_ARRAYS
2296 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
2297 #else
2298 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
2299 #endif
2302 if (op == MINT_CALLRUN) {
2303 td->last_ins->data [0] = get_data_item_index (td, target_method);
2304 td->last_ins->data [1] = get_data_item_index (td, mono_method_signature_internal (target_method));
2306 } else if (!calli && !is_virtual && jit_call_supported (target_method, csignature)) {
2307 interp_add_ins (td, MINT_JIT_CALL);
2308 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2309 mono_error_assert_ok (error);
2310 } else {
2311 #ifndef MONO_ARCH_HAS_NO_PROPER_MONOCTX
2312 /* Try using fast icall path for simple signatures */
2313 if (native && !method->dynamic)
2314 op = interp_icall_op_for_sig (csignature);
2315 #endif
2316 if (csignature->call_convention == MONO_CALL_VARARG)
2317 interp_add_ins (td, MINT_CALL_VARARG);
2318 else if (calli)
2319 interp_add_ins (td, native ? ((op != -1) ? MINT_CALLI_NAT_FAST : MINT_CALLI_NAT) : MINT_CALLI);
2320 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass))
2321 interp_add_ins (td, is_void ? MINT_VCALLVIRT_FAST : MINT_CALLVIRT_FAST);
2322 else if (is_virtual)
2323 interp_add_ins (td, is_void ? MINT_VCALLVIRT : MINT_CALLVIRT);
2324 else
2325 interp_add_ins (td, is_void ? MINT_VCALL : MINT_CALL);
2327 if (calli) {
2328 td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
2329 if (op != -1) {
2330 td->last_ins->data[1] = op;
2331 if (td->last_ins->opcode == MINT_CALLI_NAT_FAST)
2332 td->last_ins->data[2] = save_last_error;
2333 } else if (op == -1 && td->last_ins->opcode == MINT_CALLI_NAT) {
2334 td->last_ins->data[1] = save_last_error;
2336 } else {
2337 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2338 return_val_if_nok (error, FALSE);
2339 if (csignature->call_convention == MONO_CALL_VARARG)
2340 td->last_ins->data [1] = get_data_item_index (td, (void *)csignature);
2341 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass)) {
2342 /* FIXME Use fastpath also for MBRO. Asserts in mono_method_get_vtable_slot */
2343 if (mono_class_is_interface (target_method->klass))
2344 td->last_ins->data [1] = -2 * MONO_IMT_SIZE + mono_method_get_imt_slot (target_method);
2345 else
2346 td->last_ins->data [1] = mono_method_get_vtable_slot (target_method);
2350 td->ip += 5;
2351 if (vt_stack_used != 0 || vt_res_size != 0) {
2352 interp_add_ins (td, MINT_VTRESULT);
2353 td->last_ins->data [0] = vt_res_size;
2354 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
2355 td->vt_sp -= vt_stack_used;
2358 return TRUE;
2361 static MonoClassField *
2362 interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, MonoGenericContext *generic_context, MonoError *error)
2364 MonoClassField *field = NULL;
2365 if (method->wrapper_type != MONO_WRAPPER_NONE) {
2366 field = (MonoClassField *) mono_method_get_wrapper_data (method, token);
2367 *klass = field->parent;
2369 mono_class_setup_fields (field->parent);
2370 } else {
2371 field = mono_field_from_token_checked (m_class_get_image (method->klass), token, klass, generic_context, error);
2372 return_val_if_nok (error, NULL);
2375 if (!method->skip_visibility && !mono_method_can_access_field (method, field)) {
2376 char *method_fname = mono_method_full_name (method, TRUE);
2377 char *field_fname = mono_field_full_name (field);
2378 mono_error_set_generic_error (error, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
2379 g_free (method_fname);
2380 g_free (field_fname);
2381 return NULL;
2384 return field;
2387 static InterpBasicBlock*
2388 get_bb (TransformData *td, InterpBasicBlock *cbb, unsigned char *ip)
2390 int offset = ip - td->il_code;
2391 InterpBasicBlock *bb = td->offset_to_bb [offset];
2393 if (!bb) {
2394 bb = (InterpBasicBlock*)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock));
2395 bb->ip = ip;
2396 td->offset_to_bb [offset] = bb;
2398 td->basic_blocks = g_list_append_mempool (td->mempool, td->basic_blocks, bb);
2401 if (cbb)
2402 bb->preds = g_slist_prepend_mempool (td->mempool, bb->preds, cbb);
2403 return bb;
2407 * get_basic_blocks:
2409 * Compute the set of IL level basic blocks.
2411 static void
2412 get_basic_blocks (TransformData *td)
2414 guint8 *start = (guint8*)td->il_code;
2415 guint8 *end = (guint8*)td->il_code + td->code_size;
2416 guint8 *ip = start;
2417 unsigned char *target;
2418 int i;
2419 guint cli_addr;
2420 const MonoOpcode *opcode;
2421 InterpBasicBlock *cbb;
2423 td->offset_to_bb = (InterpBasicBlock**)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * (end - start + 1));
2424 td->entry_bb = cbb = get_bb (td, NULL, start);
2426 while (ip < end) {
2427 cli_addr = ip - start;
2428 td->offset_to_bb [cli_addr] = cbb;
2429 i = mono_opcode_value ((const guint8 **)&ip, end);
2430 opcode = &mono_opcodes [i];
2431 switch (opcode->argument) {
2432 case MonoInlineNone:
2433 ip++;
2434 break;
2435 case MonoInlineString:
2436 case MonoInlineType:
2437 case MonoInlineField:
2438 case MonoInlineMethod:
2439 case MonoInlineTok:
2440 case MonoInlineSig:
2441 case MonoShortInlineR:
2442 case MonoInlineI:
2443 ip += 5;
2444 break;
2445 case MonoInlineVar:
2446 ip += 3;
2447 break;
2448 case MonoShortInlineVar:
2449 case MonoShortInlineI:
2450 ip += 2;
2451 break;
2452 case MonoShortInlineBrTarget:
2453 target = start + cli_addr + 2 + (signed char)ip [1];
2454 get_bb (td, cbb, target);
2455 ip += 2;
2456 cbb = get_bb (td, cbb, ip);
2457 break;
2458 case MonoInlineBrTarget:
2459 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
2460 get_bb (td, cbb, target);
2461 ip += 5;
2462 cbb = get_bb (td, cbb, ip);
2463 break;
2464 case MonoInlineSwitch: {
2465 guint32 n = read32 (ip + 1);
2466 guint32 j;
2467 ip += 5;
2468 cli_addr += 5 + 4 * n;
2469 target = start + cli_addr;
2470 get_bb (td, cbb, target);
2472 for (j = 0; j < n; ++j) {
2473 target = start + cli_addr + (gint32)read32 (ip);
2474 get_bb (td, cbb, target);
2475 ip += 4;
2477 cbb = get_bb (td, cbb, ip);
2478 break;
2480 case MonoInlineR:
2481 case MonoInlineI8:
2482 ip += 9;
2483 break;
2484 default:
2485 g_assert_not_reached ();
2488 if (i == CEE_THROW)
2489 cbb = get_bb (td, NULL, ip);
2493 static void
2494 interp_save_debug_info (InterpMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers)
2496 MonoDebugMethodJitInfo *dinfo;
2497 int i;
2499 if (!mono_debug_enabled ())
2500 return;
2503 * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
2506 dinfo = g_new0 (MonoDebugMethodJitInfo, 1);
2507 dinfo->num_params = rtm->param_count;
2508 dinfo->params = g_new0 (MonoDebugVarInfo, dinfo->num_params);
2509 dinfo->num_locals = header->num_locals;
2510 dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals);
2511 dinfo->code_start = (guint8*)rtm->code;
2512 dinfo->code_size = td->new_code_end - td->new_code;
2513 dinfo->epilogue_begin = 0;
2514 dinfo->has_var_info = TRUE;
2515 dinfo->num_line_numbers = line_numbers->len;
2516 dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers);
2518 for (i = 0; i < dinfo->num_params; i++) {
2519 MonoDebugVarInfo *var = &dinfo->params [i];
2520 var->type = rtm->param_types [i];
2522 for (i = 0; i < dinfo->num_locals; i++) {
2523 MonoDebugVarInfo *var = &dinfo->locals [i];
2524 var->type = mono_metadata_type_dup (NULL, header->locals [i]);
2527 for (i = 0; i < dinfo->num_line_numbers; i++)
2528 dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i);
2529 mono_debug_add_method (rtm->method, dinfo, rtm->domain);
2531 mono_debug_free_method_jit_info (dinfo);
2534 /* Same as the code in seq-points.c */
2535 static void
2536 insert_pred_seq_point (SeqPoint *last_sp, SeqPoint *sp, GSList **next)
2538 GSList *l;
2539 int src_index = last_sp->next_offset;
2540 int dst_index = sp->next_offset;
2542 /* bb->in_bb might contain duplicates */
2543 for (l = next [src_index]; l; l = l->next)
2544 if (GPOINTER_TO_UINT (l->data) == dst_index)
2545 break;
2546 if (!l)
2547 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
2550 static void
2551 recursively_make_pred_seq_points (TransformData *td, InterpBasicBlock *bb)
2553 SeqPoint ** const MONO_SEQ_SEEN_LOOP = (SeqPoint**)GINT_TO_POINTER(-1);
2554 GSList *l;
2556 GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
2557 GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
2559 // Insert/remove sentinel into the memoize table to detect loops containing bb
2560 bb->pred_seq_points = MONO_SEQ_SEEN_LOOP;
2562 for (l = bb->preds; l; l = l->next) {
2563 InterpBasicBlock *in_bb = (InterpBasicBlock*)l->data;
2565 // This bb has the last seq point, append it and continue
2566 if (in_bb->last_seq_point != NULL) {
2567 predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
2568 continue;
2571 // We've looped or handled this before, exit early.
2572 // No last sequence points to find.
2573 if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
2574 continue;
2576 // Take sequence points from incoming basic blocks
2578 if (in_bb == td->entry_bb)
2579 continue;
2581 if (in_bb->pred_seq_points == NULL)
2582 recursively_make_pred_seq_points (td, in_bb);
2584 // Union sequence points with incoming bb's
2585 for (int i=0; i < in_bb->num_pred_seq_points; i++) {
2586 if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
2587 g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
2588 g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
2591 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
2594 g_hash_table_destroy (seen);
2596 if (predecessors->len != 0) {
2597 bb->pred_seq_points = (SeqPoint**)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint *) * predecessors->len);
2598 bb->num_pred_seq_points = predecessors->len;
2600 for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
2601 bb->pred_seq_points [newer] = (SeqPoint*)g_array_index (predecessors, gpointer, newer);
2605 g_array_free (predecessors, TRUE);
2608 static void
2609 collect_pred_seq_points (TransformData *td, InterpBasicBlock *bb, SeqPoint *seqp, GSList **next)
2611 // Doesn't have a last sequence point, must find from incoming basic blocks
2612 if (bb->pred_seq_points == NULL && bb != td->entry_bb)
2613 recursively_make_pred_seq_points (td, bb);
2615 for (int i = 0; i < bb->num_pred_seq_points; i++)
2616 insert_pred_seq_point (bb->pred_seq_points [i], seqp, next);
2618 return;
2621 static void
2622 save_seq_points (TransformData *td, MonoJitInfo *jinfo)
2624 GByteArray *array;
2625 int i, seq_info_size;
2626 MonoSeqPointInfo *info;
2627 GSList **next = NULL;
2628 GList *bblist;
2630 if (!td->gen_sdb_seq_points)
2631 return;
2634 * For each sequence point, compute the list of sequence points immediately
2635 * following it, this is needed to implement 'step over' in the debugger agent.
2636 * Similar to the code in mono_save_seq_point_info ().
2638 for (i = 0; i < td->seq_points->len; ++i) {
2639 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2641 /* Store the seq point index here temporarily */
2642 sp->next_offset = i;
2644 next = (GSList**)mono_mempool_alloc0 (td->mempool, sizeof (GList*) * td->seq_points->len);
2645 for (bblist = td->basic_blocks; bblist; bblist = bblist->next) {
2646 InterpBasicBlock *bb = (InterpBasicBlock*)bblist->data;
2648 GSList *bb_seq_points = g_slist_reverse (bb->seq_points);
2649 SeqPoint *last = NULL;
2650 for (GSList *l = bb_seq_points; l; l = l->next) {
2651 SeqPoint *sp = (SeqPoint*)l->data;
2653 if (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET)
2654 /* Used to implement method entry/exit events */
2655 continue;
2657 if (last != NULL) {
2658 /* Link with the previous seq point in the same bb */
2659 next [last->next_offset] = g_slist_append_mempool (td->mempool, next [last->next_offset], GINT_TO_POINTER (sp->next_offset));
2660 } else {
2661 /* Link with the last bb in the previous bblocks */
2662 collect_pred_seq_points (td, bb, sp, next);
2664 last = sp;
2668 /* Serialize the seq points into a byte array */
2669 array = g_byte_array_new ();
2670 SeqPoint zero_seq_point = {0};
2671 SeqPoint* last_seq_point = &zero_seq_point;
2672 for (i = 0; i < td->seq_points->len; ++i) {
2673 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2675 sp->next_offset = 0;
2676 if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next [i], TRUE))
2677 last_seq_point = sp;
2680 if (td->verbose_level) {
2681 g_print ("\nSEQ POINT MAP FOR %s: \n", td->method->name);
2683 for (i = 0; i < td->seq_points->len; ++i) {
2684 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2685 GSList *l;
2687 if (!next [i])
2688 continue;
2690 g_print ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
2691 for (l = next [i]; l; l = l->next) {
2692 int next_index = GPOINTER_TO_UINT (l->data);
2693 g_print (" IL0x%x", ((SeqPoint*)g_ptr_array_index (td->seq_points, next_index))->il_offset);
2695 g_print ("\n");
2699 info = mono_seq_point_info_new (array->len, TRUE, array->data, TRUE, &seq_info_size);
2700 mono_atomic_fetch_add_i32 (&mono_jit_stats.allocated_seq_points_size, seq_info_size);
2702 g_byte_array_free (array, TRUE);
2704 jinfo->seq_points = info;
2707 #define BARRIER_IF_VOLATILE(td) \
2708 do { \
2709 if (volatile_) { \
2710 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER); \
2711 volatile_ = FALSE; \
2713 } while (0)
2715 #define INLINE_FAILURE \
2716 do { \
2717 if (inlining) \
2718 goto exit; \
2719 } while (0)
2721 static void
2722 interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMethodSignature *signature, MonoMethodHeader *header)
2724 int i, offset, size, align;
2726 imethod->local_offsets = (guint32*)g_malloc (header->num_locals * sizeof(guint32));
2727 td->locals = (InterpLocal*)g_malloc (header->num_locals * sizeof (InterpLocal));
2728 td->locals_size = header->num_locals;
2729 td->locals_capacity = td->locals_size;
2730 offset = 0;
2731 for (i = 0; i < header->num_locals; ++i) {
2732 size = mono_type_size (header->locals [i], &align);
2733 offset += align - 1;
2734 offset &= ~(align - 1);
2735 imethod->local_offsets [i] = offset;
2736 td->locals [i].offset = offset;
2737 td->locals [i].flags = 0;
2738 offset += size;
2740 offset = (offset + 7) & ~7;
2742 imethod->exvar_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32));
2743 for (i = 0; i < header->num_clauses; i++) {
2744 imethod->exvar_offsets [i] = offset;
2745 offset += sizeof (MonoObject*);
2747 offset = (offset + 7) & ~7;
2749 imethod->locals_size = offset;
2750 g_assert (imethod->locals_size < 65536);
2751 td->total_locals_size = offset;
2754 static MonoType*
2755 get_arg_type (MonoMethodSignature *signature, int arg_n)
2757 if (signature->hasthis && arg_n == 0)
2758 return mono_get_object_type ();
2759 return signature->params [arg_n - !!signature->hasthis];
2762 /* Return false is failure to init basic blocks due to being in inline method */
2763 static gboolean
2764 init_bb_start (TransformData *td, MonoMethodHeader *header, gboolean inlining)
2766 const unsigned char *ip, *end;
2767 const MonoOpcode *opcode;
2768 int offset, i, in, backwards;
2770 /* intern the strings in the method. */
2771 ip = header->code;
2772 end = ip + header->code_size;
2774 /* inlined method continues the basic block of parent method */
2775 if (!inlining)
2776 td->is_bb_start [0] = 1;
2777 while (ip < end) {
2778 in = *ip;
2779 if (in == 0xfe) {
2780 ip++;
2781 in = *ip + 256;
2783 else if (in == 0xf0) {
2784 ip++;
2785 in = *ip + MONO_CEE_MONO_ICALL;
2787 opcode = &mono_opcodes [in];
2788 switch (opcode->argument) {
2789 case MonoInlineNone:
2790 ++ip;
2791 break;
2792 case MonoInlineString:
2793 ip += 5;
2794 break;
2795 case MonoInlineType:
2796 ip += 5;
2797 break;
2798 case MonoInlineMethod:
2799 ip += 5;
2800 break;
2801 case MonoInlineField:
2802 case MonoInlineSig:
2803 case MonoInlineI:
2804 case MonoInlineTok:
2805 case MonoShortInlineR:
2806 ip += 5;
2807 break;
2808 case MonoInlineBrTarget:
2809 offset = read32 (ip + 1);
2810 ip += 5;
2811 /* this branch is ignored */
2812 if (offset == 0 && in == MONO_CEE_BR)
2813 break;
2814 backwards = offset < 0;
2815 offset += ip - header->code;
2816 g_assert (offset >= 0 && offset < header->code_size);
2817 if (inlining)
2818 return FALSE;
2819 td->is_bb_start [offset] |= backwards ? 2 : 1;
2820 break;
2821 case MonoShortInlineBrTarget:
2822 offset = ((gint8 *)ip) [1];
2823 ip += 2;
2824 /* this branch is ignored */
2825 if (offset == 0 && in == MONO_CEE_BR_S)
2826 break;
2827 backwards = offset < 0;
2828 offset += ip - header->code;
2829 g_assert (offset >= 0 && offset < header->code_size);
2830 if (inlining)
2831 return FALSE;
2832 td->is_bb_start [offset] |= backwards ? 2 : 1;
2833 break;
2834 case MonoInlineVar:
2835 ip += 3;
2836 break;
2837 case MonoShortInlineVar:
2838 case MonoShortInlineI:
2839 ip += 2;
2840 break;
2841 case MonoInlineSwitch: {
2842 guint32 n;
2843 const unsigned char *next_ip;
2844 ++ip;
2845 n = read32 (ip);
2846 ip += 4;
2847 next_ip = ip + 4 * n;
2848 for (i = 0; i < n; i++) {
2849 offset = read32 (ip);
2850 backwards = offset < 0;
2851 offset += next_ip - header->code;
2852 g_assert (offset >= 0 && offset < header->code_size);
2853 if (inlining)
2854 return FALSE;
2855 td->is_bb_start [offset] |= backwards ? 2 : 1;
2856 ip += 4;
2858 break;
2860 case MonoInlineR:
2861 case MonoInlineI8:
2862 ip += 9;
2863 break;
2864 default:
2865 g_assert_not_reached ();
2868 return TRUE;
2871 #ifdef NO_UNALIGNED_ACCESS
2872 static int
2873 get_unaligned_opcode (int opcode)
2875 switch (opcode) {
2876 case MINT_LDFLD_I8:
2877 return MINT_LDFLD_I8_UNALIGNED;
2878 case MINT_LDFLD_R8:
2879 return MINT_LDFLD_R8_UNALIGNED;
2880 case MINT_STFLD_I8:
2881 return MINT_STFLD_I8_UNALIGNED;
2882 case MINT_STFLD_R8:
2883 return MINT_STFLD_R8_UNALIGNED;
2884 default:
2885 g_assert_not_reached ();
2887 return -1;
2889 #endif
2891 static void
2892 interp_handle_isinst (TransformData *td, MonoClass *klass, gboolean isinst_instr)
2894 /* Follow the logic from jit's handle_isinst */
2895 if (!mono_class_has_variant_generic_params (klass)) {
2896 if (mono_class_is_interface (klass))
2897 interp_add_ins (td, isinst_instr ? MINT_ISINST_INTERFACE : MINT_CASTCLASS_INTERFACE);
2898 else if (!mono_class_is_marshalbyref (klass) && m_class_get_rank (klass) == 0 && !mono_class_is_nullable (klass))
2899 interp_add_ins (td, isinst_instr ? MINT_ISINST_COMMON : MINT_CASTCLASS_COMMON);
2900 else
2901 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2902 } else {
2903 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2905 td->last_ins->data [0] = get_data_item_index (td, klass);
2906 td->ip += 5;
2909 static void
2910 interp_emit_ldsflda (TransformData *td, MonoClassField *field, MonoError *error)
2912 MonoDomain *domain = td->rtm->domain;
2913 // Initialize the offset for the field
2914 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
2915 return_if_nok (error);
2917 if (mono_class_field_is_special_static (field)) {
2918 guint32 offset;
2920 mono_domain_lock (domain);
2921 g_assert (domain->special_static_fields);
2922 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
2923 mono_domain_unlock (domain);
2924 g_assert (offset);
2926 interp_add_ins (td, MINT_LDSSFLDA);
2927 WRITE32_INS(td->last_ins, 0, &offset);
2928 } else {
2929 interp_add_ins (td, MINT_LDSFLDA);
2930 td->last_ins->data [0] = get_data_item_index (td, vtable);
2931 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
2935 static void
2936 interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, gboolean is_load, MonoError *error)
2938 MonoDomain *domain = td->rtm->domain;
2939 // Initialize the offset for the field
2940 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
2941 return_if_nok (error);
2943 if (mono_class_field_is_special_static (field)) {
2944 guint32 offset;
2946 mono_domain_lock (domain);
2947 g_assert (domain->special_static_fields);
2948 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
2949 mono_domain_unlock (domain);
2950 g_assert (offset);
2952 // Offset is SpecialStaticOffset
2953 if ((offset & 0x80000000) == 0 && mt != MINT_TYPE_VT) {
2954 // This field is thread static
2955 interp_add_ins (td, (is_load ? MINT_LDTSFLD_I1 : MINT_STTSFLD_I1) + mt);
2956 WRITE32_INS(td->last_ins, 0, &offset);
2957 } else {
2958 if (mt == MINT_TYPE_VT) {
2959 interp_add_ins (td, is_load ? MINT_LDSSFLD_VT : MINT_STSSFLD_VT);
2960 WRITE32_INS(td->last_ins, 0, &offset);
2962 int size = mono_class_value_size (field_class, NULL);
2963 WRITE32_INS(td->last_ins, 2, &size);
2964 } else {
2965 interp_add_ins (td, is_load ? MINT_LDSSFLD : MINT_STSSFLD);
2966 td->last_ins->data [0] = get_data_item_index (td, field);
2967 WRITE32_INS(td->last_ins, 1, &offset);
2970 } else {
2971 if (is_load)
2972 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSFLD_VT : (MINT_LDSFLD_I1 + mt - MINT_TYPE_I1));
2973 else
2974 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1));
2976 td->last_ins->data [0] = get_data_item_index (td, vtable);
2977 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
2979 if (mt == MINT_TYPE_VT) {
2980 int size = mono_class_value_size (field_class, NULL);
2981 WRITE32_INS(td->last_ins, 2, &size);
2986 static gboolean
2987 generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error)
2989 int target;
2990 int offset, mt, i, i32;
2991 guint32 token;
2992 int in_offset;
2993 const unsigned char *end;
2994 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
2995 gboolean sym_seq_points = FALSE;
2996 MonoBitSet *seq_point_locs = NULL;
2997 gboolean readonly = FALSE;
2998 gboolean volatile_ = FALSE;
2999 MonoClass *constrained_class = NULL;
3000 MonoClass *klass;
3001 MonoClassField *field;
3002 MonoImage *image = m_class_get_image (method->klass);
3003 InterpMethod *rtm = td->rtm;
3004 MonoDomain *domain = rtm->domain;
3005 MonoMethodSignature *signature = mono_method_signature_internal (method);
3006 gboolean ret = TRUE;
3007 gboolean emitted_funccall_seq_point = FALSE;
3008 guint32 *arg_locals = NULL;
3009 guint32 *local_locals = NULL;
3010 InterpInst *last_seq_point = NULL;
3011 gboolean save_last_error = FALSE;
3012 gboolean inlining = td->method != method;
3014 original_bb = bb = mono_basic_block_split (method, error, header);
3015 goto_if_nok (error, exit);
3016 g_assert (bb);
3018 td->il_code = header->code;
3019 td->in_start = td->ip = header->code;
3020 end = td->ip + header->code_size;
3022 if (!init_bb_start (td, header, inlining))
3023 goto exit;
3025 if (!inlining) {
3026 for (i = 0; i < header->code_size; i++) {
3027 td->stack_height [i] = -1;
3028 td->clause_indexes [i] = -1;
3032 for (i = 0; i < header->num_clauses; i++) {
3033 MonoExceptionClause *c = header->clauses + i;
3034 td->stack_height [c->handler_offset] = 0;
3035 td->vt_stack_size [c->handler_offset] = 0;
3036 td->is_bb_start [c->handler_offset] = 1;
3037 td->is_bb_start [c->try_offset] = 1;
3039 td->stack_height [c->handler_offset] = 1;
3040 td->stack_state [c->handler_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
3041 td->stack_state [c->handler_offset][0].type = STACK_TYPE_O;
3042 td->stack_state [c->handler_offset][0].klass = NULL; /*FIX*/
3044 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER) {
3045 td->stack_height [c->data.filter_offset] = 0;
3046 td->vt_stack_size [c->data.filter_offset] = 0;
3047 td->is_bb_start [c->data.filter_offset] = 1;
3049 td->stack_height [c->data.filter_offset] = 1;
3050 td->stack_state [c->data.filter_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
3051 td->stack_state [c->data.filter_offset][0].type = STACK_TYPE_O;
3052 td->stack_state [c->data.filter_offset][0].klass = NULL; /*FIX*/
3055 for (int j = c->handler_offset; j < c->handler_offset + c->handler_len; ++j) {
3056 if (td->clause_indexes [j] == -1)
3057 td->clause_indexes [j] = i;
3061 if (td->gen_sdb_seq_points && !inlining) {
3062 MonoDebugMethodInfo *minfo;
3063 get_basic_blocks (td);
3065 minfo = mono_debug_lookup_method (method);
3067 if (minfo) {
3068 MonoSymSeqPoint *sps;
3069 int i, n_il_offsets;
3071 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
3072 // FIXME: Free
3073 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
3074 sym_seq_points = TRUE;
3076 for (i = 0; i < n_il_offsets; ++i) {
3077 if (sps [i].il_offset < header->code_size)
3078 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
3080 g_free (sps);
3082 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
3083 if (asyncMethod) {
3084 for (i = 0; asyncMethod != NULL && i < asyncMethod->num_awaits; i++) {
3085 mono_bitset_set_fast (seq_point_locs, asyncMethod->resume_offsets [i]);
3086 mono_bitset_set_fast (seq_point_locs, asyncMethod->yield_offsets [i]);
3088 mono_debug_free_method_async_debug_info (asyncMethod);
3090 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (m_class_get_image (method->klass))) {
3091 /* Methods without line number info like auto-generated property accessors */
3092 seq_point_locs = mono_bitset_new (header->code_size, 0);
3093 sym_seq_points = TRUE;
3097 if (sym_seq_points) {
3098 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3099 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY;
3102 if (mono_debugger_method_has_breakpoint (method))
3103 interp_add_ins (td, MINT_BREAKPOINT);
3105 if (!inlining) {
3106 if (td->verbose_level) {
3107 char *tmp = mono_disasm_code (NULL, method, td->ip, end);
3108 char *name = mono_method_full_name (method, TRUE);
3109 g_print ("Method %s, original code:\n", name);
3110 g_print ("%s\n", tmp);
3111 g_free (tmp);
3112 g_free (name);
3115 if (header->num_locals && header->init_locals)
3116 interp_add_ins (td, MINT_INITLOCALS);
3118 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3119 interp_add_ins (td, MINT_TRACE_ENTER);
3120 else if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER)
3121 interp_add_ins (td, MINT_PROF_ENTER);
3123 /* safepoint is required on method entry */
3124 if (mono_threads_are_safepoints_enabled ())
3125 interp_add_ins (td, MINT_SAFEPOINT);
3126 } else {
3127 int local;
3128 arg_locals = (guint32*) g_malloc ((!!signature->hasthis + signature->param_count) * sizeof (guint32));
3129 /* Allocate locals to store inlined method args from stack */
3130 for (i = signature->param_count - 1; i >= 0; i--) {
3131 local = create_interp_local (td, signature->params [i]);
3132 arg_locals [i + !!signature->hasthis] = local;
3133 store_local_general (td, local, signature->params [i]);
3136 if (signature->hasthis) {
3138 * If this is value type, it is passed by address and not by value.
3139 * FIXME We should use MINT_TYPE_P instead of MINT_TYPE_O
3141 MonoType *type = mono_get_object_type ();
3142 local = create_interp_local (td, type);
3143 arg_locals [0] = local;
3144 store_local_general (td, local, type);
3147 local_locals = (guint32*) g_malloc (header->num_locals * sizeof (guint32));
3148 /* Allocate locals to store inlined method args from stack */
3149 for (i = 0; i < header->num_locals; i++)
3150 local_locals [i] = create_interp_local (td, header->locals [i]);
3153 while (td->ip < end) {
3154 g_assert (td->sp >= td->stack);
3155 g_assert (td->vt_sp < 0x10000000);
3156 in_offset = td->ip - header->code;
3157 if (!inlining)
3158 td->current_il_offset = in_offset;
3159 td->in_start = td->ip;
3160 InterpInst *prev_last_ins = td->last_ins;
3162 // Inlined method doesn't have clauses or branches
3163 if (!inlining && td->stack_height [in_offset] >= 0) {
3164 g_assert (td->is_bb_start [in_offset]);
3165 if (td->stack_height [in_offset] > 0)
3166 memcpy (td->stack, td->stack_state [in_offset], td->stack_height [in_offset] * sizeof(td->stack [0]));
3167 td->sp = td->stack + td->stack_height [in_offset];
3168 td->vt_sp = td->vt_stack_size [in_offset];
3171 if (in_offset == bb->end)
3172 bb = bb->next;
3174 if (bb->dead) {
3175 int op_size = mono_opcode_size (td->ip, end);
3176 g_assert (op_size > 0); /* The BB formation pass must catch all bad ops */
3178 if (td->verbose_level > 1)
3179 g_print ("SKIPPING DEAD OP at %x\n", in_offset);
3181 td->ip += op_size;
3182 continue;
3185 if (td->verbose_level > 1) {
3186 g_print ("IL_%04lx %s %-10s, sp %ld, %s %-12s vt_sp %u (max %u)\n",
3187 td->ip - td->il_code,
3188 td->is_bb_start [td->ip - td->il_code] == 3 ? "<>" :
3189 td->is_bb_start [td->ip - td->il_code] == 2 ? "< " :
3190 td->is_bb_start [td->ip - td->il_code] == 1 ? " >" : " ",
3191 mono_opcode_name (*td->ip), td->sp - td->stack,
3192 td->sp > td->stack ? stack_type_string [td->sp [-1].type] : " ",
3193 (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)) : "",
3194 td->vt_sp, td->max_vt_sp);
3197 if (sym_seq_points && mono_bitset_test_fast (seq_point_locs, td->ip - header->code)) {
3198 InterpBasicBlock *cbb = td->offset_to_bb [td->ip - header->code];
3199 g_assert (cbb);
3202 * Make methods interruptable at the beginning, and at the targets of
3203 * backward branches.
3205 if (in_offset == 0 || g_slist_length (cbb->preds) > 1)
3206 interp_add_ins (td, MINT_SDB_INTR_LOC);
3208 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3211 if (!inlining && td->is_bb_start [in_offset]) {
3212 int index = td->clause_indexes [in_offset];
3213 if (index != -1) {
3214 MonoExceptionClause *clause = &header->clauses [index];
3215 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
3216 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) &&
3217 in_offset == clause->handler_offset)
3218 interp_add_ins (td, MINT_START_ABORT_PROT);
3222 switch (*td->ip) {
3223 case CEE_NOP:
3224 /* lose it */
3225 emitted_funccall_seq_point = FALSE;
3226 ++td->ip;
3227 break;
3228 case CEE_BREAK:
3229 SIMPLE_OP(td, MINT_BREAK);
3230 break;
3231 case CEE_LDARG_0:
3232 case CEE_LDARG_1:
3233 case CEE_LDARG_2:
3234 case CEE_LDARG_3: {
3235 int arg_n = *td->ip - CEE_LDARG_0;
3236 if (!inlining)
3237 load_arg (td, arg_n);
3238 else
3239 load_local_general (td, arg_locals [arg_n], get_arg_type (signature, arg_n));
3240 ++td->ip;
3241 break;
3243 case CEE_LDLOC_0:
3244 case CEE_LDLOC_1:
3245 case CEE_LDLOC_2:
3246 case CEE_LDLOC_3: {
3247 int loc_n = *td->ip - CEE_LDLOC_0;
3248 if (!inlining)
3249 load_local (td, loc_n);
3250 else
3251 load_local_general (td, local_locals [loc_n], header->locals [loc_n]);
3252 ++td->ip;
3253 break;
3255 case CEE_STLOC_0:
3256 case CEE_STLOC_1:
3257 case CEE_STLOC_2:
3258 case CEE_STLOC_3: {
3259 int loc_n = *td->ip - CEE_STLOC_0;
3260 if (!inlining)
3261 store_local (td, loc_n);
3262 else
3263 store_local_general (td, local_locals [loc_n], header->locals [loc_n]);
3264 ++td->ip;
3265 break;
3267 case CEE_LDARG_S: {
3268 int arg_n = ((guint8 *)td->ip)[1];
3269 if (!inlining)
3270 load_arg (td, arg_n);
3271 else
3272 load_local_general (td, arg_locals [arg_n], get_arg_type (signature, arg_n));
3273 td->ip += 2;
3274 break;
3276 case CEE_LDARGA_S: {
3277 /* NOTE: n includes this */
3278 int n = ((guint8 *) td->ip) [1];
3280 if (!inlining) {
3281 get_arg_type_exact (td, n, &mt);
3282 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
3283 td->last_ins->data [0] = n;
3284 } else {
3285 int loc_n = arg_locals [n];
3286 interp_add_ins (td, MINT_LDLOCA_S);
3287 td->last_ins->data [0] = loc_n;
3288 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
3290 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3291 td->ip += 2;
3292 break;
3294 case CEE_STARG_S: {
3295 int arg_n = ((guint8 *)td->ip)[1];
3296 if (!inlining)
3297 store_arg (td, arg_n);
3298 else
3299 store_local_general (td, arg_locals [arg_n], get_arg_type (signature, arg_n));
3300 td->ip += 2;
3301 break;
3303 case CEE_LDLOC_S: {
3304 int loc_n = ((guint8 *)td->ip)[1];
3305 if (!inlining)
3306 load_local (td, loc_n);
3307 else
3308 load_local_general (td, local_locals [loc_n], header->locals [loc_n]);
3309 td->ip += 2;
3310 break;
3312 case CEE_LDLOCA_S: {
3313 int loc_n = ((guint8 *)td->ip)[1];
3314 interp_add_ins (td, MINT_LDLOCA_S);
3315 if (inlining)
3316 loc_n = local_locals [loc_n];
3317 td->last_ins->data [0] = loc_n;
3318 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
3319 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3320 td->ip += 2;
3321 break;
3323 case CEE_STLOC_S: {
3324 int loc_n = ((guint8 *)td->ip)[1];
3325 if (!inlining)
3326 store_local (td, loc_n);
3327 else
3328 store_local_general (td, local_locals [loc_n], header->locals [loc_n]);
3329 td->ip += 2;
3330 break;
3332 case CEE_LDNULL:
3333 SIMPLE_OP(td, MINT_LDNULL);
3334 PUSH_TYPE(td, STACK_TYPE_O, NULL);
3335 break;
3336 case CEE_LDC_I4_M1:
3337 SIMPLE_OP(td, MINT_LDC_I4_M1);
3338 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3339 break;
3340 case CEE_LDC_I4_0:
3341 // Only single basic block functions are inlined.
3342 if (td->ip - td->il_code + 2 < td->code_size && (inlining || !td->is_bb_start [td->ip + 1 - td->il_code]) && td->ip [1] == 0xfe && td->ip [2] == CEE_CEQ &&
3343 td->sp > td->stack && td->sp [-1].type == STACK_TYPE_I4) {
3344 SIMPLE_OP(td, MINT_CEQ0_I4);
3345 td->ip += 2;
3346 } else {
3347 SIMPLE_OP(td, MINT_LDC_I4_0);
3348 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3350 break;
3351 case CEE_LDC_I4_1:
3352 // Only single basic block functions are inlined.
3353 if (td->ip - td->il_code + 1 < td->code_size && (inlining || !td->is_bb_start [td->ip + 1 - td->il_code]) &&
3354 (td->ip [1] == CEE_ADD || td->ip [1] == CEE_SUB) && td->sp [-1].type == STACK_TYPE_I4) {
3355 interp_add_ins (td, td->ip [1] == CEE_ADD ? MINT_ADD1_I4 : MINT_SUB1_I4);
3356 td->ip += 2;
3357 } else {
3358 SIMPLE_OP(td, MINT_LDC_I4_1);
3359 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3361 break;
3362 case CEE_LDC_I4_2:
3363 case CEE_LDC_I4_3:
3364 case CEE_LDC_I4_4:
3365 case CEE_LDC_I4_5:
3366 case CEE_LDC_I4_6:
3367 case CEE_LDC_I4_7:
3368 case CEE_LDC_I4_8:
3369 SIMPLE_OP(td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0);
3370 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3371 break;
3372 case CEE_LDC_I4_S:
3373 interp_add_ins (td, MINT_LDC_I4_S);
3374 td->last_ins->data [0] = ((gint8 *) td->ip) [1];
3375 td->ip += 2;
3376 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3377 break;
3378 case CEE_LDC_I4:
3379 i32 = read32 (td->ip + 1);
3380 interp_add_ins (td, MINT_LDC_I4);
3381 WRITE32_INS (td->last_ins, 0, &i32);
3382 td->ip += 5;
3383 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3384 break;
3385 case CEE_LDC_I8: {
3386 gint64 val = read64 (td->ip + 1);
3387 interp_add_ins (td, MINT_LDC_I8);
3388 WRITE64_INS (td->last_ins, 0, &val);
3389 td->ip += 9;
3390 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I8);
3391 break;
3393 case CEE_LDC_R4: {
3394 float val;
3395 readr4 (td->ip + 1, &val);
3396 interp_add_ins (td, MINT_LDC_R4);
3397 WRITE32_INS (td->last_ins, 0, &val);
3398 td->ip += 5;
3399 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R4);
3400 break;
3402 case CEE_LDC_R8: {
3403 double val;
3404 readr8 (td->ip + 1, &val);
3405 interp_add_ins (td, MINT_LDC_R8);
3406 WRITE64_INS (td->last_ins, 0, &val);
3407 td->ip += 9;
3408 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R8);
3409 break;
3411 case CEE_DUP: {
3412 int type = td->sp [-1].type;
3413 MonoClass *klass = td->sp [-1].klass;
3414 if (td->sp [-1].type == STACK_TYPE_VT) {
3415 gint32 size = mono_class_value_size (klass, NULL);
3416 PUSH_VT(td, size);
3417 interp_add_ins (td, MINT_DUP_VT);
3418 WRITE32_INS (td->last_ins, 0, &size);
3419 td->ip ++;
3420 } else
3421 SIMPLE_OP(td, MINT_DUP);
3422 PUSH_TYPE(td, type, klass);
3423 break;
3425 case CEE_POP:
3426 CHECK_STACK(td, 1);
3427 SIMPLE_OP(td, MINT_POP);
3428 td->last_ins->data [0] = 0;
3429 if (td->sp [-1].type == STACK_TYPE_VT) {
3430 int size = mono_class_value_size (td->sp [-1].klass, NULL);
3431 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
3432 interp_add_ins (td, MINT_VTRESULT);
3433 td->last_ins->data [0] = 0;
3434 WRITE32_INS (td->last_ins, 1, &size);
3435 td->vt_sp -= size;
3437 --td->sp;
3438 break;
3439 case CEE_JMP: {
3440 MonoMethod *m;
3441 INLINE_FAILURE;
3442 if (td->sp > td->stack)
3443 g_warning ("CEE_JMP: stack must be empty");
3444 token = read32 (td->ip + 1);
3445 m = mono_get_method_checked (image, token, NULL, generic_context, error);
3446 goto_if_nok (error, exit);
3447 interp_add_ins (td, MINT_JMP);
3448 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3449 goto_if_nok (error, exit);
3450 td->ip += 5;
3451 break;
3453 case CEE_CALLVIRT: /* Fall through */
3454 case CEE_CALLI: /* Fall through */
3455 case CEE_CALL: {
3456 gboolean need_seq_point = FALSE;
3458 if (sym_seq_points && !mono_bitset_test_fast (seq_point_locs, td->ip + 5 - header->code))
3459 need_seq_point = TRUE;
3461 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, constrained_class, readonly, error, TRUE, save_last_error))
3462 goto exit;
3464 if (need_seq_point) {
3465 //check is is a nested call and remove the MONO_INST_NONEMPTY_STACK of the last breakpoint, only for non native methods
3466 if (!(method->flags & METHOD_IMPL_ATTRIBUTE_NATIVE)) {
3467 if (emitted_funccall_seq_point) {
3468 if (last_seq_point)
3469 last_seq_point->flags |= INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL;
3471 else
3472 emitted_funccall_seq_point = TRUE;
3474 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3475 // This seq point is actually associated with the instruction following the call
3476 last_seq_point->il_offset = td->ip - header->code;
3477 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK;
3480 constrained_class = NULL;
3481 readonly = FALSE;
3482 save_last_error = FALSE;
3483 break;
3485 case CEE_RET: {
3486 /* Return from inlined method, return value is on top of stack */
3487 if (td->method != method) {
3488 td->ip++;
3489 break;
3492 int vt_size = 0;
3493 MonoType *ult = mini_type_get_underlying_type (signature->ret);
3494 if (ult->type != MONO_TYPE_VOID) {
3495 CHECK_STACK (td, 1);
3496 --td->sp;
3497 if (mint_type (ult) == MINT_TYPE_VT) {
3498 MonoClass *klass = mono_class_from_mono_type_internal (ult);
3499 vt_size = mono_class_value_size (klass, NULL);
3502 if (td->sp > td->stack) {
3503 mono_error_set_generic_error (error, "System", "InvalidProgramException", "");
3504 goto exit;
3506 if (td->vt_sp != ALIGN_TO (vt_size, MINT_VT_ALIGNMENT))
3507 g_error ("%s: CEE_RET: value type stack: %d vs. %d", mono_method_full_name (td->method, TRUE), td->vt_sp, vt_size);
3509 if (sym_seq_points) {
3510 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3511 td->last_ins->flags = INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT;
3514 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
3515 /* This does the return as well */
3516 interp_add_ins (td, MINT_TRACE_EXIT);
3517 if (ult->type == MONO_TYPE_VOID)
3518 vt_size = -1;
3519 WRITE32_INS (td->last_ins, 0, &vt_size);
3520 ++td->ip;
3521 } else {
3522 if (vt_size == 0)
3523 SIMPLE_OP(td, ult->type == MONO_TYPE_VOID ? MINT_RET_VOID : MINT_RET);
3524 else {
3525 interp_add_ins (td, MINT_RET_VT);
3526 WRITE32_INS (td->last_ins, 0, &vt_size);
3527 ++td->ip;
3530 break;
3532 case CEE_BR: {
3533 int offset = read32 (td->ip + 1);
3534 if (offset) {
3535 INLINE_FAILURE;
3536 handle_branch (td, MINT_BR_S, MINT_BR, 5 + offset);
3538 td->ip += 5;
3539 break;
3541 case CEE_BR_S: {
3542 int offset = (gint8)td->ip [1];
3543 if (offset) {
3544 INLINE_FAILURE;
3545 handle_branch (td, MINT_BR_S, MINT_BR, 2 + (gint8)td->ip [1]);
3547 td->ip += 2;
3548 break;
3550 case CEE_BRFALSE:
3551 INLINE_FAILURE;
3552 one_arg_branch (td, MINT_BRFALSE_I4, 5 + read32 (td->ip + 1));
3553 td->ip += 5;
3554 break;
3555 case CEE_BRFALSE_S:
3556 INLINE_FAILURE;
3557 one_arg_branch (td, MINT_BRFALSE_I4, 2 + (gint8)td->ip [1]);
3558 td->ip += 2;
3559 break;
3560 case CEE_BRTRUE:
3561 INLINE_FAILURE;
3562 one_arg_branch (td, MINT_BRTRUE_I4, 5 + read32 (td->ip + 1));
3563 td->ip += 5;
3564 break;
3565 case CEE_BRTRUE_S:
3566 INLINE_FAILURE;
3567 one_arg_branch (td, MINT_BRTRUE_I4, 2 + (gint8)td->ip [1]);
3568 td->ip += 2;
3569 break;
3570 case CEE_BEQ:
3571 INLINE_FAILURE;
3572 two_arg_branch (td, MINT_BEQ_I4, 5 + read32 (td->ip + 1));
3573 td->ip += 5;
3574 break;
3575 case CEE_BEQ_S:
3576 INLINE_FAILURE;
3577 two_arg_branch (td, MINT_BEQ_I4, 2 + (gint8) td->ip [1]);
3578 td->ip += 2;
3579 break;
3580 case CEE_BGE:
3581 INLINE_FAILURE;
3582 two_arg_branch (td, MINT_BGE_I4, 5 + read32 (td->ip + 1));
3583 td->ip += 5;
3584 break;
3585 case CEE_BGE_S:
3586 INLINE_FAILURE;
3587 two_arg_branch (td, MINT_BGE_I4, 2 + (gint8) td->ip [1]);
3588 td->ip += 2;
3589 break;
3590 case CEE_BGT:
3591 INLINE_FAILURE;
3592 two_arg_branch (td, MINT_BGT_I4, 5 + read32 (td->ip + 1));
3593 td->ip += 5;
3594 break;
3595 case CEE_BGT_S:
3596 INLINE_FAILURE;
3597 two_arg_branch (td, MINT_BGT_I4, 2 + (gint8) td->ip [1]);
3598 td->ip += 2;
3599 break;
3600 case CEE_BLT:
3601 INLINE_FAILURE;
3602 two_arg_branch (td, MINT_BLT_I4, 5 + read32 (td->ip + 1));
3603 td->ip += 5;
3604 break;
3605 case CEE_BLT_S:
3606 INLINE_FAILURE;
3607 two_arg_branch (td, MINT_BLT_I4, 2 + (gint8) td->ip [1]);
3608 td->ip += 2;
3609 break;
3610 case CEE_BLE:
3611 INLINE_FAILURE;
3612 two_arg_branch (td, MINT_BLE_I4, 5 + read32 (td->ip + 1));
3613 td->ip += 5;
3614 break;
3615 case CEE_BLE_S:
3616 INLINE_FAILURE;
3617 two_arg_branch (td, MINT_BLE_I4, 2 + (gint8) td->ip [1]);
3618 td->ip += 2;
3619 break;
3620 case CEE_BNE_UN:
3621 INLINE_FAILURE;
3622 two_arg_branch (td, MINT_BNE_UN_I4, 5 + read32 (td->ip + 1));
3623 td->ip += 5;
3624 break;
3625 case CEE_BNE_UN_S:
3626 INLINE_FAILURE;
3627 two_arg_branch (td, MINT_BNE_UN_I4, 2 + (gint8) td->ip [1]);
3628 td->ip += 2;
3629 break;
3630 case CEE_BGE_UN:
3631 INLINE_FAILURE;
3632 two_arg_branch (td, MINT_BGE_UN_I4, 5 + read32 (td->ip + 1));
3633 td->ip += 5;
3634 break;
3635 case CEE_BGE_UN_S:
3636 INLINE_FAILURE;
3637 two_arg_branch (td, MINT_BGE_UN_I4, 2 + (gint8) td->ip [1]);
3638 td->ip += 2;
3639 break;
3640 case CEE_BGT_UN:
3641 INLINE_FAILURE;
3642 two_arg_branch (td, MINT_BGT_UN_I4, 5 + read32 (td->ip + 1));
3643 td->ip += 5;
3644 break;
3645 case CEE_BGT_UN_S:
3646 INLINE_FAILURE;
3647 two_arg_branch (td, MINT_BGT_UN_I4, 2 + (gint8) td->ip [1]);
3648 td->ip += 2;
3649 break;
3650 case CEE_BLE_UN:
3651 INLINE_FAILURE;
3652 two_arg_branch (td, MINT_BLE_UN_I4, 5 + read32 (td->ip + 1));
3653 td->ip += 5;
3654 break;
3655 case CEE_BLE_UN_S:
3656 INLINE_FAILURE;
3657 two_arg_branch (td, MINT_BLE_UN_I4, 2 + (gint8) td->ip [1]);
3658 td->ip += 2;
3659 break;
3660 case CEE_BLT_UN:
3661 INLINE_FAILURE;
3662 two_arg_branch (td, MINT_BLT_UN_I4, 5 + read32 (td->ip + 1));
3663 td->ip += 5;
3664 break;
3665 case CEE_BLT_UN_S:
3666 INLINE_FAILURE;
3667 two_arg_branch (td, MINT_BLT_UN_I4, 2 + (gint8) td->ip [1]);
3668 td->ip += 2;
3669 break;
3670 case CEE_SWITCH: {
3671 INLINE_FAILURE;
3672 guint32 n;
3673 const unsigned char *next_ip;
3674 ++td->ip;
3675 n = read32 (td->ip);
3676 interp_add_ins_explicit (td, MINT_SWITCH, MINT_SWITCH_LEN (n));
3677 WRITE32_INS (td->last_ins, 0, &n);
3678 td->ip += 4;
3679 next_ip = td->ip + n * 4;
3680 --td->sp;
3681 int stack_height = td->sp - td->stack;
3682 for (i = 0; i < n; i++) {
3683 offset = read32 (td->ip);
3684 target = next_ip - td->il_code + offset;
3685 if (offset < 0) {
3686 #if DEBUG_INTERP
3687 if (stack_height > 0 && stack_height != td->stack_height [target])
3688 g_warning ("SWITCH with back branch and non-empty stack");
3689 #endif
3690 } else {
3691 td->stack_height [target] = stack_height;
3692 td->vt_stack_size [target] = td->vt_sp;
3693 if (stack_height > 0)
3694 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, stack_height * sizeof (td->stack [0]));
3696 WRITE32_INS (td->last_ins, 2 + i * 2, &target);
3697 td->ip += 4;
3699 break;
3701 case CEE_LDIND_I1:
3702 CHECK_STACK (td, 1);
3703 SIMPLE_OP (td, MINT_LDIND_I1_CHECK);
3704 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3705 BARRIER_IF_VOLATILE (td);
3706 break;
3707 case CEE_LDIND_U1:
3708 CHECK_STACK (td, 1);
3709 SIMPLE_OP (td, MINT_LDIND_U1_CHECK);
3710 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3711 BARRIER_IF_VOLATILE (td);
3712 break;
3713 case CEE_LDIND_I2:
3714 CHECK_STACK (td, 1);
3715 SIMPLE_OP (td, MINT_LDIND_I2_CHECK);
3716 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3717 BARRIER_IF_VOLATILE (td);
3718 break;
3719 case CEE_LDIND_U2:
3720 CHECK_STACK (td, 1);
3721 SIMPLE_OP (td, MINT_LDIND_U2_CHECK);
3722 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3723 BARRIER_IF_VOLATILE (td);
3724 break;
3725 case CEE_LDIND_I4:
3726 CHECK_STACK (td, 1);
3727 SIMPLE_OP (td, MINT_LDIND_I4_CHECK);
3728 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3729 BARRIER_IF_VOLATILE (td);
3730 break;
3731 case CEE_LDIND_U4:
3732 CHECK_STACK (td, 1);
3733 SIMPLE_OP (td, MINT_LDIND_U4_CHECK);
3734 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3735 BARRIER_IF_VOLATILE (td);
3736 break;
3737 case CEE_LDIND_I8:
3738 CHECK_STACK (td, 1);
3739 SIMPLE_OP (td, MINT_LDIND_I8_CHECK);
3740 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3741 BARRIER_IF_VOLATILE (td);
3742 break;
3743 case CEE_LDIND_I:
3744 CHECK_STACK (td, 1);
3745 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3746 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3747 BARRIER_IF_VOLATILE (td);
3748 break;
3749 case CEE_LDIND_R4:
3750 CHECK_STACK (td, 1);
3751 SIMPLE_OP (td, MINT_LDIND_R4_CHECK);
3752 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
3753 BARRIER_IF_VOLATILE (td);
3754 break;
3755 case CEE_LDIND_R8:
3756 CHECK_STACK (td, 1);
3757 SIMPLE_OP (td, MINT_LDIND_R8_CHECK);
3758 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3759 BARRIER_IF_VOLATILE (td);
3760 break;
3761 case CEE_LDIND_REF:
3762 CHECK_STACK (td, 1);
3763 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3764 BARRIER_IF_VOLATILE (td);
3765 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
3766 break;
3767 case CEE_STIND_REF:
3768 CHECK_STACK (td, 2);
3769 BARRIER_IF_VOLATILE (td);
3770 SIMPLE_OP (td, MINT_STIND_REF);
3771 td->sp -= 2;
3772 break;
3773 case CEE_STIND_I1:
3774 CHECK_STACK (td, 2);
3775 BARRIER_IF_VOLATILE (td);
3776 SIMPLE_OP (td, MINT_STIND_I1);
3777 td->sp -= 2;
3778 break;
3779 case CEE_STIND_I2:
3780 CHECK_STACK (td, 2);
3781 BARRIER_IF_VOLATILE (td);
3782 SIMPLE_OP (td, MINT_STIND_I2);
3783 td->sp -= 2;
3784 break;
3785 case CEE_STIND_I4:
3786 CHECK_STACK (td, 2);
3787 BARRIER_IF_VOLATILE (td);
3788 SIMPLE_OP (td, MINT_STIND_I4);
3789 td->sp -= 2;
3790 break;
3791 case CEE_STIND_I:
3792 CHECK_STACK (td, 2);
3793 BARRIER_IF_VOLATILE (td);
3794 SIMPLE_OP (td, MINT_STIND_I);
3795 td->sp -= 2;
3796 break;
3797 case CEE_STIND_I8:
3798 CHECK_STACK (td, 2);
3799 BARRIER_IF_VOLATILE (td);
3800 SIMPLE_OP (td, MINT_STIND_I8);
3801 td->sp -= 2;
3802 break;
3803 case CEE_STIND_R4:
3804 CHECK_STACK (td, 2);
3805 BARRIER_IF_VOLATILE (td);
3806 SIMPLE_OP (td, MINT_STIND_R4);
3807 td->sp -= 2;
3808 break;
3809 case CEE_STIND_R8:
3810 CHECK_STACK (td, 2);
3811 BARRIER_IF_VOLATILE (td);
3812 SIMPLE_OP (td, MINT_STIND_R8);
3813 td->sp -= 2;
3814 break;
3815 case CEE_ADD:
3816 binary_arith_op(td, MINT_ADD_I4);
3817 ++td->ip;
3818 break;
3819 case CEE_SUB:
3820 binary_arith_op(td, MINT_SUB_I4);
3821 ++td->ip;
3822 break;
3823 case CEE_MUL:
3824 binary_arith_op(td, MINT_MUL_I4);
3825 ++td->ip;
3826 break;
3827 case CEE_DIV:
3828 binary_arith_op(td, MINT_DIV_I4);
3829 ++td->ip;
3830 break;
3831 case CEE_DIV_UN:
3832 binary_arith_op(td, MINT_DIV_UN_I4);
3833 ++td->ip;
3834 break;
3835 case CEE_REM:
3836 binary_arith_op (td, MINT_REM_I4);
3837 ++td->ip;
3838 break;
3839 case CEE_REM_UN:
3840 binary_arith_op (td, MINT_REM_UN_I4);
3841 ++td->ip;
3842 break;
3843 case CEE_AND:
3844 binary_arith_op (td, MINT_AND_I4);
3845 ++td->ip;
3846 break;
3847 case CEE_OR:
3848 binary_arith_op (td, MINT_OR_I4);
3849 ++td->ip;
3850 break;
3851 case CEE_XOR:
3852 binary_arith_op (td, MINT_XOR_I4);
3853 ++td->ip;
3854 break;
3855 case CEE_SHL:
3856 shift_op (td, MINT_SHL_I4);
3857 ++td->ip;
3858 break;
3859 case CEE_SHR:
3860 shift_op (td, MINT_SHR_I4);
3861 ++td->ip;
3862 break;
3863 case CEE_SHR_UN:
3864 shift_op (td, MINT_SHR_UN_I4);
3865 ++td->ip;
3866 break;
3867 case CEE_NEG:
3868 unary_arith_op (td, MINT_NEG_I4);
3869 ++td->ip;
3870 break;
3871 case CEE_NOT:
3872 unary_arith_op (td, MINT_NOT_I4);
3873 ++td->ip;
3874 break;
3875 case CEE_CONV_U1:
3876 CHECK_STACK (td, 1);
3877 switch (td->sp [-1].type) {
3878 case STACK_TYPE_R4:
3879 interp_add_ins (td, MINT_CONV_U1_R4);
3880 break;
3881 case STACK_TYPE_R8:
3882 interp_add_ins (td, MINT_CONV_U1_R8);
3883 break;
3884 case STACK_TYPE_I4:
3885 interp_add_ins (td, MINT_CONV_U1_I4);
3886 break;
3887 case STACK_TYPE_I8:
3888 interp_add_ins (td, MINT_CONV_U1_I8);
3889 break;
3890 default:
3891 g_assert_not_reached ();
3893 ++td->ip;
3894 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3895 break;
3896 case CEE_CONV_I1:
3897 CHECK_STACK (td, 1);
3898 switch (td->sp [-1].type) {
3899 case STACK_TYPE_R4:
3900 interp_add_ins (td, MINT_CONV_I1_R4);
3901 break;
3902 case STACK_TYPE_R8:
3903 interp_add_ins (td, MINT_CONV_I1_R8);
3904 break;
3905 case STACK_TYPE_I4:
3906 interp_add_ins (td, MINT_CONV_I1_I4);
3907 break;
3908 case STACK_TYPE_I8:
3909 interp_add_ins (td, MINT_CONV_I1_I8);
3910 break;
3911 default:
3912 g_assert_not_reached ();
3914 ++td->ip;
3915 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3916 break;
3917 case CEE_CONV_U2:
3918 CHECK_STACK (td, 1);
3919 switch (td->sp [-1].type) {
3920 case STACK_TYPE_R4:
3921 interp_add_ins (td, MINT_CONV_U2_R4);
3922 break;
3923 case STACK_TYPE_R8:
3924 interp_add_ins (td, MINT_CONV_U2_R8);
3925 break;
3926 case STACK_TYPE_I4:
3927 interp_add_ins (td, MINT_CONV_U2_I4);
3928 break;
3929 case STACK_TYPE_I8:
3930 interp_add_ins (td, MINT_CONV_U2_I8);
3931 break;
3932 default:
3933 g_assert_not_reached ();
3935 ++td->ip;
3936 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3937 break;
3938 case CEE_CONV_I2:
3939 CHECK_STACK (td, 1);
3940 switch (td->sp [-1].type) {
3941 case STACK_TYPE_R4:
3942 interp_add_ins (td, MINT_CONV_I2_R4);
3943 break;
3944 case STACK_TYPE_R8:
3945 interp_add_ins (td, MINT_CONV_I2_R8);
3946 break;
3947 case STACK_TYPE_I4:
3948 interp_add_ins (td, MINT_CONV_I2_I4);
3949 break;
3950 case STACK_TYPE_I8:
3951 interp_add_ins (td, MINT_CONV_I2_I8);
3952 break;
3953 default:
3954 g_assert_not_reached ();
3956 ++td->ip;
3957 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3958 break;
3959 case CEE_CONV_U:
3960 CHECK_STACK (td, 1);
3961 switch (td->sp [-1].type) {
3962 case STACK_TYPE_R8:
3963 #if SIZEOF_VOID_P == 4
3964 interp_add_ins (td, MINT_CONV_U4_R8);
3965 #else
3966 interp_add_ins (td, MINT_CONV_U8_R8);
3967 #endif
3968 break;
3969 case STACK_TYPE_I4:
3970 #if SIZEOF_VOID_P == 8
3971 interp_add_ins (td, MINT_CONV_U8_I4);
3972 #endif
3973 break;
3974 case STACK_TYPE_I8:
3975 #if SIZEOF_VOID_P == 4
3976 interp_add_ins (td, MINT_CONV_U4_I8);
3977 #endif
3978 break;
3979 case STACK_TYPE_MP:
3980 case STACK_TYPE_O:
3981 break;
3982 default:
3983 g_assert_not_reached ();
3985 ++td->ip;
3986 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3987 break;
3988 case CEE_CONV_I:
3989 CHECK_STACK (td, 1);
3990 switch (td->sp [-1].type) {
3991 case STACK_TYPE_R8:
3992 #if SIZEOF_VOID_P == 8
3993 interp_add_ins (td, MINT_CONV_I8_R8);
3994 #else
3995 interp_add_ins (td, MINT_CONV_I4_R8);
3996 #endif
3997 break;
3998 case STACK_TYPE_I4:
3999 #if SIZEOF_VOID_P == 8
4000 interp_add_ins (td, MINT_CONV_I8_I4);
4001 #endif
4002 break;
4003 case STACK_TYPE_O:
4004 break;
4005 case STACK_TYPE_MP:
4006 break;
4007 case STACK_TYPE_I8:
4008 #if SIZEOF_VOID_P == 4
4009 interp_add_ins (td, MINT_CONV_I4_I8);
4010 #endif
4011 break;
4012 default:
4013 g_assert_not_reached ();
4015 ++td->ip;
4016 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4017 break;
4018 case CEE_CONV_U4:
4019 CHECK_STACK (td, 1);
4020 switch (td->sp [-1].type) {
4021 case STACK_TYPE_R4:
4022 interp_add_ins (td, MINT_CONV_U4_R4);
4023 break;
4024 case STACK_TYPE_R8:
4025 interp_add_ins (td, MINT_CONV_U4_R8);
4026 break;
4027 case STACK_TYPE_I4:
4028 break;
4029 case STACK_TYPE_I8:
4030 interp_add_ins (td, MINT_CONV_U4_I8);
4031 break;
4032 case STACK_TYPE_MP:
4033 #if SIZEOF_VOID_P == 8
4034 interp_add_ins (td, MINT_CONV_U4_I8);
4035 #endif
4036 break;
4037 default:
4038 g_assert_not_reached ();
4040 ++td->ip;
4041 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4042 break;
4043 case CEE_CONV_I4:
4044 CHECK_STACK (td, 1);
4045 switch (td->sp [-1].type) {
4046 case STACK_TYPE_R4:
4047 interp_add_ins (td, MINT_CONV_I4_R4);
4048 break;
4049 case STACK_TYPE_R8:
4050 interp_add_ins (td, MINT_CONV_I4_R8);
4051 break;
4052 case STACK_TYPE_I4:
4053 break;
4054 case STACK_TYPE_I8:
4055 interp_add_ins (td, MINT_CONV_I4_I8);
4056 break;
4057 case STACK_TYPE_MP:
4058 #if SIZEOF_VOID_P == 8
4059 interp_add_ins (td, MINT_CONV_I4_I8);
4060 #endif
4061 break;
4062 default:
4063 g_assert_not_reached ();
4065 ++td->ip;
4066 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4067 break;
4068 case CEE_CONV_I8:
4069 CHECK_STACK (td, 1);
4070 switch (td->sp [-1].type) {
4071 case STACK_TYPE_R4:
4072 interp_add_ins (td, MINT_CONV_I8_R4);
4073 break;
4074 case STACK_TYPE_R8:
4075 interp_add_ins (td, MINT_CONV_I8_R8);
4076 break;
4077 case STACK_TYPE_I4: {
4078 if (interp_ins_is_ldc (td->last_ins) && (inlining || !td->is_bb_start [in_offset])) {
4079 gint64 ct = interp_ldc_i4_get_const (td->last_ins);
4080 interp_clear_ins (td, td->last_ins);
4082 interp_add_ins (td, MINT_LDC_I8);
4083 WRITE64_INS (td->last_ins, 0, &ct);
4084 } else {
4085 interp_add_ins (td, MINT_CONV_I8_I4);
4087 break;
4089 case STACK_TYPE_I8:
4090 break;
4091 case STACK_TYPE_MP:
4092 #if SIZEOF_VOID_P == 4
4093 interp_add_ins (td, MINT_CONV_I8_I4);
4094 #endif
4095 break;
4096 default:
4097 g_assert_not_reached ();
4099 ++td->ip;
4100 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4101 break;
4102 case CEE_CONV_R4:
4103 CHECK_STACK (td, 1);
4104 switch (td->sp [-1].type) {
4105 case STACK_TYPE_R8:
4106 interp_add_ins (td, MINT_CONV_R4_R8);
4107 break;
4108 case STACK_TYPE_I8:
4109 interp_add_ins (td, MINT_CONV_R4_I8);
4110 break;
4111 case STACK_TYPE_I4:
4112 interp_add_ins (td, MINT_CONV_R4_I4);
4113 break;
4114 case STACK_TYPE_R4:
4115 /* no-op */
4116 break;
4117 default:
4118 g_assert_not_reached ();
4120 ++td->ip;
4121 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4122 break;
4123 case CEE_CONV_R8:
4124 CHECK_STACK (td, 1);
4125 switch (td->sp [-1].type) {
4126 case STACK_TYPE_I4:
4127 interp_add_ins (td, MINT_CONV_R8_I4);
4128 break;
4129 case STACK_TYPE_I8:
4130 interp_add_ins (td, MINT_CONV_R8_I8);
4131 break;
4132 case STACK_TYPE_R4:
4133 interp_add_ins (td, MINT_CONV_R8_R4);
4134 break;
4135 case STACK_TYPE_R8:
4136 break;
4137 default:
4138 g_assert_not_reached ();
4140 ++td->ip;
4141 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4142 break;
4143 case CEE_CONV_U8:
4144 CHECK_STACK (td, 1);
4145 switch (td->sp [-1].type) {
4146 case STACK_TYPE_I4:
4147 if (interp_ins_is_ldc (td->last_ins) && (inlining || !td->is_bb_start [in_offset])) {
4148 gint64 ct = (guint32)interp_ldc_i4_get_const (td->last_ins);
4149 interp_clear_ins (td, td->last_ins);
4151 interp_add_ins (td, MINT_LDC_I8);
4152 WRITE64_INS (td->last_ins, 0, &ct);
4153 } else {
4154 interp_add_ins (td, MINT_CONV_U8_I4);
4156 break;
4157 case STACK_TYPE_I8:
4158 break;
4159 case STACK_TYPE_R4:
4160 interp_add_ins (td, MINT_CONV_U8_R4);
4161 break;
4162 case STACK_TYPE_R8:
4163 interp_add_ins (td, MINT_CONV_U8_R8);
4164 break;
4165 case STACK_TYPE_MP:
4166 #if SIZEOF_VOID_P == 4
4167 interp_add_ins (td, MINT_CONV_U8_I4);
4168 #endif
4169 break;
4170 default:
4171 g_assert_not_reached ();
4173 ++td->ip;
4174 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4175 break;
4176 case CEE_CPOBJ: {
4177 CHECK_STACK (td, 2);
4179 token = read32 (td->ip + 1);
4180 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4181 goto_if_nok (error, exit);
4183 if (m_class_is_valuetype (klass)) {
4184 int mt = mint_type (m_class_get_byval_arg (klass));
4185 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_CPOBJ_VT : MINT_CPOBJ);
4186 td->last_ins->data [0] = get_data_item_index(td, klass);
4187 } else {
4188 interp_add_ins (td, MINT_LDIND_REF);
4189 interp_add_ins (td, MINT_STIND_REF);
4191 td->ip += 5;
4192 td->sp -= 2;
4193 break;
4195 case CEE_LDOBJ: {
4196 CHECK_STACK (td, 1);
4198 token = read32 (td->ip + 1);
4200 if (method->wrapper_type != MONO_WRAPPER_NONE)
4201 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4202 else {
4203 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4204 goto_if_nok (error, exit);
4207 interp_emit_ldobj (td, klass);
4209 td->ip += 5;
4210 BARRIER_IF_VOLATILE (td);
4211 break;
4213 case CEE_LDSTR: {
4214 token = mono_metadata_token_index (read32 (td->ip + 1));
4215 td->ip += 5;
4216 if (method->wrapper_type == MONO_WRAPPER_NONE) {
4217 MonoString *s = mono_ldstr_checked (domain, image, token, error);
4218 goto_if_nok (error, exit);
4219 /* GC won't scan code stream, but reference is held by metadata
4220 * machinery so we are good here */
4221 interp_add_ins (td, MINT_LDSTR);
4222 td->last_ins->data [0] = get_data_item_index (td, s);
4223 } else {
4224 /* defer allocation to execution-time */
4225 interp_add_ins (td, MINT_LDSTR_TOKEN);
4226 td->last_ins->data [0] = get_data_item_index (td, GUINT_TO_POINTER (token));
4228 PUSH_TYPE(td, STACK_TYPE_O, mono_defaults.string_class);
4229 break;
4231 case CEE_NEWOBJ: {
4232 MonoMethod *m;
4233 MonoMethodSignature *csignature;
4234 guint32 vt_stack_used = 0;
4235 guint32 vt_res_size = 0;
4237 td->ip++;
4238 token = read32 (td->ip);
4239 td->ip += 4;
4241 if (method->wrapper_type != MONO_WRAPPER_NONE)
4242 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
4243 else {
4244 m = mono_get_method_checked (image, token, NULL, generic_context, error);
4245 goto_if_nok (error, exit);
4248 csignature = mono_method_signature_internal (m);
4249 klass = m->klass;
4251 if (!mono_class_init_internal (klass)) {
4252 mono_error_set_for_class_failure (error, klass);
4253 goto_if_nok (error, exit);
4256 if (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_ABSTRACT) {
4257 char* full_name = mono_type_get_full_name (klass);
4258 mono_error_set_member_access (error, "Cannot create an abstract class: %s", full_name);
4259 g_free (full_name);
4260 goto_if_nok (error, exit);
4263 if (mono_class_is_magic_int (klass) || mono_class_is_magic_float (klass)) {
4264 td->sp -= csignature->param_count;
4265 #if SIZEOF_VOID_P == 8
4266 if (mono_class_is_magic_int (klass) && td->sp [0].type == STACK_TYPE_I4)
4267 interp_add_ins (td, MINT_CONV_I8_I4);
4268 else if (mono_class_is_magic_float (klass) && td->sp [0].type == STACK_TYPE_R4)
4269 interp_add_ins (td, MINT_CONV_R8_R4);
4270 #endif
4271 interp_add_ins (td, MINT_NEWOBJ_MAGIC);
4272 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4273 goto_if_nok (error, exit);
4275 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4276 } else {
4277 if (m_class_get_parent (klass) == mono_defaults.array_class) {
4278 interp_add_ins (td, MINT_NEWOBJ_ARRAY);
4279 td->last_ins->data [0] = get_data_item_index (td, m->klass);
4280 td->last_ins->data [1] = csignature->param_count;
4281 } else if (m_class_get_image (klass) == mono_defaults.corlib &&
4282 !strcmp (m_class_get_name (m->klass), "ByReference`1") &&
4283 !strcmp (m->name, ".ctor")) {
4284 /* public ByReference(ref T value) */
4285 g_assert (csignature->hasthis && csignature->param_count == 1);
4286 interp_add_ins (td, MINT_INTRINS_BYREFERENCE_CTOR);
4287 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4288 } else if (klass != mono_defaults.string_class &&
4289 !mono_class_is_marshalbyref (klass) &&
4290 !mono_class_has_finalizer (klass) &&
4291 !m_class_has_weak_fields (klass)) {
4292 if (!m_class_is_valuetype (klass)) {
4293 InterpInst *newobj_fast = interp_add_ins (td, MINT_NEWOBJ_FAST);
4295 newobj_fast->data [1] = csignature->param_count;
4297 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
4298 goto_if_nok (error, exit);
4299 newobj_fast->data [2] = get_data_item_index (td, vtable);
4301 move_stack (td, (td->sp - td->stack) - csignature->param_count, 2);
4303 StackInfo *tmp_sp = td->sp - csignature->param_count - 2;
4304 SET_TYPE (tmp_sp, STACK_TYPE_O, klass);
4305 SET_TYPE (tmp_sp + 1, STACK_TYPE_O, klass);
4307 if ((mono_interp_opt & INTERP_OPT_INLINE) && interp_method_check_inlining (td, m)) {
4308 MonoMethodHeader *mheader = interp_method_get_header (m, error);
4309 goto_if_nok (error, exit);
4311 if (interp_inline_method (td, m, mheader, error)) {
4312 newobj_fast->data [0] = 0xffff;
4313 break;
4316 // If inlining failed we need to restore the stack
4317 move_stack (td, (td->sp - td->stack) - csignature->param_count, -2);
4318 // Set the method to be executed as part of newobj instruction
4319 newobj_fast->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4320 } else {
4321 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4322 interp_add_ins (td, MINT_NEWOBJ_VTST_FAST);
4323 else
4324 interp_add_ins (td, MINT_NEWOBJ_VT_FAST);
4326 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4327 td->last_ins->data [1] = csignature->param_count;
4329 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4330 td->last_ins->data [2] = mono_class_value_size (klass, NULL);
4333 } else {
4334 interp_add_ins (td, MINT_NEWOBJ);
4335 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4337 goto_if_nok (error, exit);
4338 /* The constructor was not inlined, abort inlining of current method */
4339 INLINE_FAILURE;
4341 td->sp -= csignature->param_count;
4342 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4343 vt_res_size = mono_class_value_size (klass, NULL);
4344 PUSH_VT (td, vt_res_size);
4346 for (i = 0; i < csignature->param_count; ++i) {
4347 int mt = mint_type(csignature->params [i]);
4348 if (mt == MINT_TYPE_VT) {
4349 MonoClass *k = mono_class_from_mono_type_internal (csignature->params [i]);
4350 gint32 size = mono_class_value_size (k, NULL);
4351 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4352 vt_stack_used += size;
4355 if (vt_stack_used != 0 || vt_res_size != 0) {
4356 interp_add_ins (td, MINT_VTRESULT);
4357 td->last_ins->data [0] = vt_res_size;
4358 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
4359 td->vt_sp -= vt_stack_used;
4361 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4363 break;
4365 case CEE_CASTCLASS:
4366 case CEE_ISINST: {
4367 gboolean isinst_instr = *td->ip == CEE_ISINST;
4368 CHECK_STACK (td, 1);
4369 token = read32 (td->ip + 1);
4370 klass = mini_get_class (method, token, generic_context);
4371 CHECK_TYPELOAD (klass);
4372 interp_handle_isinst (td, klass, isinst_instr);
4373 if (!isinst_instr)
4374 td->sp [-1].klass = klass;
4375 break;
4377 case CEE_CONV_R_UN:
4378 switch (td->sp [-1].type) {
4379 case STACK_TYPE_R8:
4380 break;
4381 case STACK_TYPE_I8:
4382 interp_add_ins (td, MINT_CONV_R_UN_I8);
4383 break;
4384 case STACK_TYPE_I4:
4385 interp_add_ins (td, MINT_CONV_R_UN_I4);
4386 break;
4387 default:
4388 g_assert_not_reached ();
4390 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4391 ++td->ip;
4392 break;
4393 case CEE_UNBOX:
4394 CHECK_STACK (td, 1);
4395 token = read32 (td->ip + 1);
4397 if (method->wrapper_type != MONO_WRAPPER_NONE)
4398 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4399 else {
4400 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4401 goto_if_nok (error, exit);
4404 if (mono_class_is_nullable (klass)) {
4405 MonoMethod *target_method;
4406 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4407 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4408 else
4409 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4410 goto_if_nok (error, exit);
4411 /* td->ip is incremented by interp_transform_call */
4412 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4413 goto exit;
4415 * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type
4416 * We create a local variable in the frame so that we can fetch its address.
4418 int local = create_interp_local (td, m_class_get_byval_arg (klass));
4419 store_local_general (td, local, m_class_get_byval_arg (klass));
4420 interp_add_ins (td, MINT_LDLOCA_S);
4421 td->last_ins->data [0] = local;
4422 td->locals [local].flags |= INTERP_LOCAL_FLAG_INDIRECT;
4423 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
4424 } else {
4425 interp_add_ins (td, MINT_UNBOX);
4426 td->last_ins->data [0] = get_data_item_index (td, klass);
4427 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
4428 td->ip += 5;
4430 break;
4431 case CEE_UNBOX_ANY:
4432 CHECK_STACK (td, 1);
4433 token = read32 (td->ip + 1);
4435 klass = mini_get_class (method, token, generic_context);
4436 CHECK_TYPELOAD (klass);
4438 if (mini_type_is_reference (m_class_get_byval_arg (klass))) {
4439 int mt = mint_type (m_class_get_byval_arg (klass));
4440 interp_handle_isinst (td, klass, FALSE);
4441 SET_TYPE (td->sp - 1, stack_type [mt], klass);
4442 } else if (mono_class_is_nullable (klass)) {
4443 MonoMethod *target_method;
4444 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4445 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4446 else
4447 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4448 goto_if_nok (error, exit);
4449 /* td->ip is incremented by interp_transform_call */
4450 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4451 goto exit;
4452 } else {
4453 interp_add_ins (td, MINT_UNBOX);
4454 td->last_ins->data [0] = get_data_item_index (td, klass);
4456 interp_emit_ldobj (td, klass);
4458 td->ip += 5;
4461 break;
4462 case CEE_THROW:
4463 INLINE_FAILURE;
4464 CHECK_STACK (td, 1);
4465 SIMPLE_OP (td, MINT_THROW);
4466 td->sp = td->stack;
4467 break;
4468 case CEE_LDFLDA: {
4469 CHECK_STACK (td, 1);
4470 token = read32 (td->ip + 1);
4471 field = interp_field_from_token (method, token, &klass, generic_context, error);
4472 goto_if_nok (error, exit);
4473 MonoType *ftype = mono_field_get_type_internal (field);
4474 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4475 mono_class_init_internal (klass);
4476 #ifndef DISABLE_REMOTING
4477 if (m_class_get_marshalbyref (klass) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
4478 g_assert (!is_static);
4479 int offset = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4481 interp_add_ins (td, MINT_MONO_LDPTR);
4482 td->last_ins->data [0] = get_data_item_index (td, klass);
4483 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4484 interp_add_ins (td, MINT_MONO_LDPTR);
4485 td->last_ins->data [0] = get_data_item_index (td, field);
4486 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4487 interp_add_ins (td, MINT_LDC_I4);
4488 WRITE32_INS (td->last_ins, 0, &offset);
4489 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
4490 #if SIZEOF_VOID_P == 8
4491 interp_add_ins (td, MINT_CONV_I8_I4);
4492 #endif
4494 MonoMethod *wrapper = mono_marshal_get_ldflda_wrapper (field->type);
4495 /* td->ip is incremented by interp_transform_call */
4496 if (!interp_transform_call (td, method, wrapper, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4497 goto exit;
4498 } else
4499 #endif
4501 if (is_static) {
4502 interp_add_ins (td, MINT_POP);
4503 td->last_ins->data [0] = 0;
4504 interp_emit_ldsflda (td, field, error);
4505 goto_if_nok (error, exit);
4506 } else {
4507 if ((td->sp - 1)->type == STACK_TYPE_O) {
4508 interp_add_ins (td, MINT_LDFLDA);
4509 } else {
4510 g_assert ((td->sp -1)->type == STACK_TYPE_MP);
4511 interp_add_ins (td, MINT_LDFLDA_UNSAFE);
4513 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4515 td->ip += 5;
4517 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4518 break;
4520 case CEE_LDFLD: {
4521 CHECK_STACK (td, 1);
4522 token = read32 (td->ip + 1);
4523 field = interp_field_from_token (method, token, &klass, generic_context, error);
4524 goto_if_nok (error, exit);
4525 MonoType *ftype = mono_field_get_type_internal (field);
4526 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4527 mono_class_init_internal (klass);
4529 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4530 mt = mint_type (m_class_get_byval_arg (field_klass));
4531 #ifndef DISABLE_REMOTING
4532 if (m_class_get_marshalbyref (klass)) {
4533 g_assert (!is_static);
4534 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDRMFLD_VT : MINT_LDRMFLD);
4535 td->last_ins->data [0] = get_data_item_index (td, field);
4536 } else
4537 #endif
4539 if (is_static) {
4540 interp_add_ins (td, MINT_POP);
4541 td->last_ins->data [0] = 0;
4542 interp_emit_sfld_access (td, field, field_klass, mt, TRUE, error);
4543 goto_if_nok (error, exit);
4544 } else {
4545 int opcode = MINT_LDFLD_I1 + mt - MINT_TYPE_I1;
4546 #ifdef NO_UNALIGNED_ACCESS
4547 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4548 opcode = get_unaligned_opcode (opcode);
4549 #endif
4550 interp_add_ins (td, opcode);
4551 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4552 if (mt == MINT_TYPE_VT) {
4553 int size = mono_class_value_size (field_klass, NULL);
4554 WRITE32_INS (td->last_ins, 1, &size);
4558 if (mt == MINT_TYPE_VT) {
4559 int size = mono_class_value_size (field_klass, NULL);
4560 PUSH_VT (td, size);
4562 if (td->sp [-1].type == STACK_TYPE_VT) {
4563 int size = mono_class_value_size (klass, NULL);
4564 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4565 int field_vt_size = 0;
4566 if (mt == MINT_TYPE_VT) {
4568 * Pop the loaded field from the vtstack (it will still be present
4569 * at the same vtstack address) and we will load it in place of the
4570 * containing valuetype with the second MINT_VTRESULT.
4572 field_vt_size = mono_class_value_size (field_klass, NULL);
4573 field_vt_size = ALIGN_TO (field_vt_size, MINT_VT_ALIGNMENT);
4574 interp_add_ins (td, MINT_VTRESULT);
4575 td->last_ins->data [0] = 0;
4576 WRITE32_INS (td->last_ins, 1, &field_vt_size);
4578 td->vt_sp -= size;
4579 interp_add_ins (td, MINT_VTRESULT);
4580 td->last_ins->data [0] = field_vt_size;
4581 WRITE32_INS (td->last_ins, 1, &size);
4583 td->ip += 5;
4584 SET_TYPE (td->sp - 1, stack_type [mt], field_klass);
4585 BARRIER_IF_VOLATILE (td);
4586 break;
4588 case CEE_STFLD: {
4589 CHECK_STACK (td, 2);
4590 token = read32 (td->ip + 1);
4591 field = interp_field_from_token (method, token, &klass, generic_context, error);
4592 goto_if_nok (error, exit);
4593 MonoType *ftype = mono_field_get_type_internal (field);
4594 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4595 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4596 mono_class_init_internal (klass);
4597 mt = mint_type (ftype);
4599 BARRIER_IF_VOLATILE (td);
4601 #ifndef DISABLE_REMOTING
4602 if (m_class_get_marshalbyref (klass)) {
4603 g_assert (!is_static);
4604 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_STRMFLD_VT : MINT_STRMFLD);
4605 td->last_ins->data [0] = get_data_item_index (td, field);
4606 } else
4607 #endif
4609 if (is_static) {
4610 interp_add_ins (td, MINT_POP);
4611 td->last_ins->data [0] = 1;
4612 interp_emit_sfld_access (td, field, field_klass, mt, FALSE, error);
4613 goto_if_nok (error, exit);
4615 /* the vtable of the field might not be initialized at this point */
4616 mono_class_vtable_checked (domain, field_klass, error);
4617 goto_if_nok (error, exit);
4618 } else {
4619 int opcode = MINT_STFLD_I1 + mt - MINT_TYPE_I1;
4620 #ifdef NO_UNALIGNED_ACCESS
4621 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4622 opcode = get_unaligned_opcode (opcode);
4623 #endif
4624 interp_add_ins (td, opcode);
4625 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4626 if (mt == MINT_TYPE_VT) {
4627 /* the vtable of the field might not be initialized at this point */
4628 mono_class_vtable_checked (domain, field_klass, error);
4629 goto_if_nok (error, exit);
4631 td->last_ins->data [1] = get_data_item_index (td, field_klass);
4635 if (mt == MINT_TYPE_VT) {
4636 int size = mono_class_value_size (field_klass, NULL);
4637 POP_VT (td, size);
4639 td->ip += 5;
4640 td->sp -= 2;
4641 break;
4643 case CEE_LDSFLDA: {
4644 token = read32 (td->ip + 1);
4645 field = interp_field_from_token (method, token, &klass, generic_context, error);
4646 goto_if_nok (error, exit);
4647 interp_emit_ldsflda (td, field, error);
4648 goto_if_nok (error, exit);
4649 td->ip += 5;
4650 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
4651 break;
4653 case CEE_LDSFLD: {
4654 token = read32 (td->ip + 1);
4655 field = interp_field_from_token (method, token, &klass, generic_context, error);
4656 goto_if_nok (error, exit);
4657 MonoType *ftype = mono_field_get_type_internal (field);
4658 mt = mint_type (ftype);
4659 klass = mono_class_from_mono_type_internal (ftype);
4661 interp_emit_sfld_access (td, field, klass, mt, TRUE, error);
4662 goto_if_nok (error, exit);
4664 if (mt == MINT_TYPE_VT) {
4665 int size = mono_class_value_size (klass, NULL);
4666 PUSH_VT(td, size);
4668 td->ip += 5;
4669 PUSH_TYPE(td, stack_type [mt], klass);
4670 break;
4672 case CEE_STSFLD: {
4673 CHECK_STACK (td, 1);
4674 token = read32 (td->ip + 1);
4675 field = interp_field_from_token (method, token, &klass, generic_context, error);
4676 goto_if_nok (error, exit);
4677 MonoType *ftype = mono_field_get_type_internal (field);
4678 mt = mint_type (ftype);
4680 /* the vtable of the field might not be initialized at this point */
4681 MonoClass *fld_klass = mono_class_from_mono_type_internal (ftype);
4682 mono_class_vtable_checked (domain, fld_klass, error);
4683 goto_if_nok (error, exit);
4685 interp_emit_sfld_access (td, field, fld_klass, mt, FALSE, error);
4686 goto_if_nok (error, exit);
4688 if (mt == MINT_TYPE_VT) {
4689 int size = mono_class_value_size (fld_klass, NULL);
4690 POP_VT(td, size);
4692 td->ip += 5;
4693 --td->sp;
4694 break;
4696 case CEE_STOBJ: {
4697 token = read32 (td->ip + 1);
4699 if (method->wrapper_type != MONO_WRAPPER_NONE)
4700 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4701 else
4702 klass = mini_get_class (method, token, generic_context);
4703 CHECK_TYPELOAD (klass);
4705 BARRIER_IF_VOLATILE (td);
4707 interp_emit_stobj (td, klass);
4709 td->ip += 5;
4710 break;
4712 case CEE_CONV_OVF_I_UN:
4713 case CEE_CONV_OVF_U_UN:
4714 CHECK_STACK (td, 1);
4715 switch (td->sp [-1].type) {
4716 case STACK_TYPE_R8:
4717 #if SIZEOF_VOID_P == 8
4718 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4719 #else
4720 interp_add_ins (td, MINT_CONV_OVF_I4_UN_R8);
4721 #endif
4722 break;
4723 case STACK_TYPE_I8:
4724 #if SIZEOF_VOID_P == 4
4725 interp_add_ins (td, MINT_CONV_OVF_I4_UN_I8);
4726 #endif
4727 break;
4728 case STACK_TYPE_I4:
4729 #if SIZEOF_VOID_P == 8
4730 interp_add_ins (td, MINT_CONV_I8_U4);
4731 #elif SIZEOF_VOID_P == 4
4732 if (*td->ip == CEE_CONV_OVF_I_UN)
4733 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
4734 #endif
4735 break;
4736 default:
4737 g_assert_not_reached ();
4738 break;
4740 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4741 ++td->ip;
4742 break;
4743 case CEE_CONV_OVF_I8_UN:
4744 case CEE_CONV_OVF_U8_UN:
4745 CHECK_STACK (td, 1);
4746 switch (td->sp [-1].type) {
4747 case STACK_TYPE_R8:
4748 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4749 break;
4750 case STACK_TYPE_I8:
4751 if (*td->ip == CEE_CONV_OVF_I8_UN)
4752 interp_add_ins (td, MINT_CONV_OVF_I8_U8);
4753 break;
4754 case STACK_TYPE_I4:
4755 interp_add_ins (td, MINT_CONV_I8_U4);
4756 break;
4757 case STACK_TYPE_R4:
4758 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R4);
4759 break;
4760 default:
4761 g_assert_not_reached ();
4762 break;
4764 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4765 ++td->ip;
4766 break;
4767 case CEE_BOX: {
4768 int size;
4769 CHECK_STACK (td, 1);
4770 token = read32 (td->ip + 1);
4771 if (method->wrapper_type != MONO_WRAPPER_NONE)
4772 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4773 else
4774 klass = mini_get_class (method, token, generic_context);
4775 CHECK_TYPELOAD (klass);
4777 if (mono_class_is_nullable (klass)) {
4778 MonoMethod *target_method = mono_class_get_method_from_name_checked (klass, "Box", 1, 0, error);
4779 goto_if_nok (error, exit);
4780 /* td->ip is incremented by interp_transform_call */
4781 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4782 goto exit;
4783 } else if (!m_class_is_valuetype (klass)) {
4784 /* already boxed, do nothing. */
4785 td->ip += 5;
4786 } else {
4787 if (G_UNLIKELY (m_class_is_byreflike (klass))) {
4788 mono_error_set_bad_image (error, image, "Cannot box IsByRefLike type '%s.%s'", m_class_get_name_space (klass), m_class_get_name (klass));
4789 goto exit;
4791 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4792 size = mono_class_value_size (klass, NULL);
4793 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4794 td->vt_sp -= size;
4795 } else if (td->sp [-1].type == STACK_TYPE_R8 && m_class_get_byval_arg (klass)->type == MONO_TYPE_R4) {
4796 interp_add_ins (td, MINT_CONV_R4_R8);
4798 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
4799 goto_if_nok (error, exit);
4801 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4802 interp_add_ins (td, MINT_BOX_VT);
4803 else
4804 interp_add_ins (td, MINT_BOX);
4805 td->last_ins->data [0] = get_data_item_index (td, vtable);
4806 td->last_ins->data [1] = 0;
4807 SET_TYPE(td->sp - 1, STACK_TYPE_O, klass);
4808 td->ip += 5;
4811 break;
4813 case CEE_NEWARR: {
4814 CHECK_STACK (td, 1);
4815 token = read32 (td->ip + 1);
4817 if (method->wrapper_type != MONO_WRAPPER_NONE)
4818 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4819 else
4820 klass = mini_get_class (method, token, generic_context);
4821 CHECK_TYPELOAD (klass);
4823 MonoClass *array_class = mono_class_create_array (klass, 1);
4824 MonoVTable *vtable = mono_class_vtable_checked (domain, array_class, error);
4825 goto_if_nok (error, exit);
4827 unsigned char lentype = (td->sp - 1)->type;
4828 if (lentype == STACK_TYPE_I8) {
4829 /* mimic mini behaviour */
4830 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
4831 } else {
4832 g_assert (lentype == STACK_TYPE_I4);
4833 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
4835 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
4836 interp_add_ins (td, MINT_NEWARR);
4837 td->last_ins->data [0] = get_data_item_index (td, vtable);
4838 SET_TYPE (td->sp - 1, STACK_TYPE_O, array_class);
4839 td->ip += 5;
4840 break;
4842 case CEE_LDLEN:
4843 CHECK_STACK (td, 1);
4844 SIMPLE_OP (td, MINT_LDLEN);
4845 #ifdef MONO_BIG_ARRAYS
4846 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
4847 #else
4848 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
4849 #endif
4850 break;
4851 case CEE_LDELEMA: {
4852 gint32 size;
4853 CHECK_STACK (td, 2);
4854 ENSURE_I4 (td, 1);
4855 token = read32 (td->ip + 1);
4857 if (method->wrapper_type != MONO_WRAPPER_NONE)
4858 klass = (MonoClass *) mono_method_get_wrapper_data (method, token);
4859 else
4860 klass = mini_get_class (method, token, generic_context);
4862 CHECK_TYPELOAD (klass);
4864 if (!m_class_is_valuetype (klass) && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
4866 * Check the class for failures before the type check, which can
4867 * throw other exceptions.
4869 mono_class_setup_vtable (klass);
4870 CHECK_TYPELOAD (klass);
4871 interp_add_ins (td, MINT_LDELEMA_TC);
4872 td->last_ins->data [0] = 1;
4873 td->last_ins->data [1] = get_data_item_index (td, klass);
4874 } else {
4875 interp_add_ins (td, MINT_LDELEMA1);
4876 mono_class_init_internal (klass);
4877 size = mono_class_array_element_size (klass);
4878 WRITE32_INS (td->last_ins, 0, &size);
4881 readonly = FALSE;
4883 td->ip += 5;
4884 --td->sp;
4885 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4886 break;
4888 case CEE_LDELEM_I1:
4889 CHECK_STACK (td, 2);
4890 ENSURE_I4 (td, 1);
4891 SIMPLE_OP (td, MINT_LDELEM_I1);
4892 --td->sp;
4893 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4894 break;
4895 case CEE_LDELEM_U1:
4896 CHECK_STACK (td, 2);
4897 ENSURE_I4 (td, 1);
4898 SIMPLE_OP (td, MINT_LDELEM_U1);
4899 --td->sp;
4900 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4901 break;
4902 case CEE_LDELEM_I2:
4903 CHECK_STACK (td, 2);
4904 ENSURE_I4 (td, 1);
4905 SIMPLE_OP (td, MINT_LDELEM_I2);
4906 --td->sp;
4907 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4908 break;
4909 case CEE_LDELEM_U2:
4910 CHECK_STACK (td, 2);
4911 ENSURE_I4 (td, 1);
4912 SIMPLE_OP (td, MINT_LDELEM_U2);
4913 --td->sp;
4914 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4915 break;
4916 case CEE_LDELEM_I4:
4917 CHECK_STACK (td, 2);
4918 ENSURE_I4 (td, 1);
4919 SIMPLE_OP (td, MINT_LDELEM_I4);
4920 --td->sp;
4921 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4922 break;
4923 case CEE_LDELEM_U4:
4924 CHECK_STACK (td, 2);
4925 ENSURE_I4 (td, 1);
4926 SIMPLE_OP (td, MINT_LDELEM_U4);
4927 --td->sp;
4928 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4929 break;
4930 case CEE_LDELEM_I8:
4931 CHECK_STACK (td, 2);
4932 ENSURE_I4 (td, 1);
4933 SIMPLE_OP (td, MINT_LDELEM_I8);
4934 --td->sp;
4935 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4936 break;
4937 case CEE_LDELEM_I:
4938 CHECK_STACK (td, 2);
4939 ENSURE_I4 (td, 1);
4940 SIMPLE_OP (td, MINT_LDELEM_I);
4941 --td->sp;
4942 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4943 break;
4944 case CEE_LDELEM_R4:
4945 CHECK_STACK (td, 2);
4946 ENSURE_I4 (td, 1);
4947 SIMPLE_OP (td, MINT_LDELEM_R4);
4948 --td->sp;
4949 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4950 break;
4951 case CEE_LDELEM_R8:
4952 CHECK_STACK (td, 2);
4953 ENSURE_I4 (td, 1);
4954 SIMPLE_OP (td, MINT_LDELEM_R8);
4955 --td->sp;
4956 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4957 break;
4958 case CEE_LDELEM_REF:
4959 CHECK_STACK (td, 2);
4960 ENSURE_I4 (td, 1);
4961 SIMPLE_OP (td, MINT_LDELEM_REF);
4962 --td->sp;
4963 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
4964 break;
4965 case CEE_LDELEM:
4966 CHECK_STACK (td, 2);
4967 token = read32 (td->ip + 1);
4968 klass = mini_get_class (method, token, generic_context);
4969 CHECK_TYPELOAD (klass);
4970 switch (mint_type (m_class_get_byval_arg (klass))) {
4971 case MINT_TYPE_I1:
4972 ENSURE_I4 (td, 1);
4973 SIMPLE_OP (td, MINT_LDELEM_I1);
4974 --td->sp;
4975 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4976 break;
4977 case MINT_TYPE_U1:
4978 ENSURE_I4 (td, 1);
4979 SIMPLE_OP (td, MINT_LDELEM_U1);
4980 --td->sp;
4981 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4982 break;
4983 case MINT_TYPE_U2:
4984 ENSURE_I4 (td, 1);
4985 SIMPLE_OP (td, MINT_LDELEM_U2);
4986 --td->sp;
4987 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4988 break;
4989 case MINT_TYPE_I2:
4990 ENSURE_I4 (td, 1);
4991 SIMPLE_OP (td, MINT_LDELEM_I2);
4992 --td->sp;
4993 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4994 break;
4995 case MINT_TYPE_I4:
4996 ENSURE_I4 (td, 1);
4997 SIMPLE_OP (td, MINT_LDELEM_I4);
4998 --td->sp;
4999 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5000 break;
5001 case MINT_TYPE_I8:
5002 ENSURE_I4 (td, 1);
5003 SIMPLE_OP (td, MINT_LDELEM_I8);
5004 --td->sp;
5005 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5006 break;
5007 case MINT_TYPE_R4:
5008 ENSURE_I4 (td, 1);
5009 SIMPLE_OP (td, MINT_LDELEM_R4);
5010 --td->sp;
5011 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
5012 break;
5013 case MINT_TYPE_R8:
5014 ENSURE_I4 (td, 1);
5015 SIMPLE_OP (td, MINT_LDELEM_R8);
5016 --td->sp;
5017 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
5018 break;
5019 case MINT_TYPE_O:
5020 ENSURE_I4 (td, 1);
5021 SIMPLE_OP (td, MINT_LDELEM_REF);
5022 --td->sp;
5023 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
5024 break;
5025 case MINT_TYPE_VT: {
5026 int size = mono_class_value_size (klass, NULL);
5027 ENSURE_I4 (td, 1);
5028 SIMPLE_OP (td, MINT_LDELEM_VT);
5029 WRITE32_INS (td->last_ins, 0, &size);
5030 --td->sp;
5031 SET_TYPE (td->sp - 1, STACK_TYPE_VT, klass);
5032 PUSH_VT (td, size);
5033 break;
5035 default: {
5036 GString *res = g_string_new ("");
5037 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
5038 g_print ("LDELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
5039 g_string_free (res, TRUE);
5040 g_assert (0);
5041 break;
5044 td->ip += 4;
5045 break;
5046 case CEE_STELEM_I:
5047 CHECK_STACK (td, 3);
5048 ENSURE_I4 (td, 2);
5049 SIMPLE_OP (td, MINT_STELEM_I);
5050 td->sp -= 3;
5051 break;
5052 case CEE_STELEM_I1:
5053 CHECK_STACK (td, 3);
5054 ENSURE_I4 (td, 2);
5055 SIMPLE_OP (td, MINT_STELEM_I1);
5056 td->sp -= 3;
5057 break;
5058 case CEE_STELEM_I2:
5059 CHECK_STACK (td, 3);
5060 ENSURE_I4 (td, 2);
5061 SIMPLE_OP (td, MINT_STELEM_I2);
5062 td->sp -= 3;
5063 break;
5064 case CEE_STELEM_I4:
5065 CHECK_STACK (td, 3);
5066 ENSURE_I4 (td, 2);
5067 SIMPLE_OP (td, MINT_STELEM_I4);
5068 td->sp -= 3;
5069 break;
5070 case CEE_STELEM_I8:
5071 CHECK_STACK (td, 3);
5072 ENSURE_I4 (td, 2);
5073 SIMPLE_OP (td, MINT_STELEM_I8);
5074 td->sp -= 3;
5075 break;
5076 case CEE_STELEM_R4:
5077 CHECK_STACK (td, 3);
5078 ENSURE_I4 (td, 2);
5079 SIMPLE_OP (td, MINT_STELEM_R4);
5080 td->sp -= 3;
5081 break;
5082 case CEE_STELEM_R8:
5083 CHECK_STACK (td, 3);
5084 ENSURE_I4 (td, 2);
5085 SIMPLE_OP (td, MINT_STELEM_R8);
5086 td->sp -= 3;
5087 break;
5088 case CEE_STELEM_REF:
5089 CHECK_STACK (td, 3);
5090 ENSURE_I4 (td, 2);
5091 SIMPLE_OP (td, MINT_STELEM_REF);
5092 td->sp -= 3;
5093 break;
5094 case CEE_STELEM:
5095 CHECK_STACK (td, 3);
5096 ENSURE_I4 (td, 2);
5097 token = read32 (td->ip + 1);
5098 klass = mini_get_class (method, token, generic_context);
5099 CHECK_TYPELOAD (klass);
5100 switch (mint_type (m_class_get_byval_arg (klass))) {
5101 case MINT_TYPE_I1:
5102 SIMPLE_OP (td, MINT_STELEM_I1);
5103 break;
5104 case MINT_TYPE_U1:
5105 SIMPLE_OP (td, MINT_STELEM_U1);
5106 break;
5107 case MINT_TYPE_I2:
5108 SIMPLE_OP (td, MINT_STELEM_I2);
5109 break;
5110 case MINT_TYPE_U2:
5111 SIMPLE_OP (td, MINT_STELEM_U2);
5112 break;
5113 case MINT_TYPE_I4:
5114 SIMPLE_OP (td, MINT_STELEM_I4);
5115 break;
5116 case MINT_TYPE_I8:
5117 SIMPLE_OP (td, MINT_STELEM_I8);
5118 break;
5119 case MINT_TYPE_R4:
5120 SIMPLE_OP (td, MINT_STELEM_R4);
5121 break;
5122 case MINT_TYPE_R8:
5123 SIMPLE_OP (td, MINT_STELEM_R8);
5124 break;
5125 case MINT_TYPE_O:
5126 SIMPLE_OP (td, MINT_STELEM_REF);
5127 break;
5128 case MINT_TYPE_VT: {
5129 int size = mono_class_value_size (klass, NULL);
5130 SIMPLE_OP (td, MINT_STELEM_VT);
5131 td->last_ins->data [0] = get_data_item_index (td, klass);
5132 WRITE32_INS (td->last_ins, 1, &size);
5133 POP_VT (td, size);
5134 break;
5136 default: {
5137 GString *res = g_string_new ("");
5138 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
5139 g_print ("STELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
5140 g_string_free (res, TRUE);
5141 g_assert (0);
5142 break;
5145 td->ip += 4;
5146 td->sp -= 3;
5147 break;
5148 #if 0
5149 case CEE_CONV_OVF_U1:
5151 case CEE_CONV_OVF_I8:
5153 #if SIZEOF_VOID_P == 8
5154 case CEE_CONV_OVF_U:
5155 #endif
5156 #endif
5157 case CEE_CKFINITE:
5158 CHECK_STACK (td, 1);
5159 SIMPLE_OP (td, MINT_CKFINITE);
5160 break;
5161 case CEE_MKREFANY:
5162 CHECK_STACK (td, 1);
5164 token = read32 (td->ip + 1);
5165 klass = mini_get_class (method, token, generic_context);
5166 CHECK_TYPELOAD (klass);
5168 interp_add_ins (td, MINT_MKREFANY);
5169 td->last_ins->data [0] = get_data_item_index (td, klass);
5171 td->ip += 5;
5172 PUSH_VT (td, sizeof (MonoTypedRef));
5173 SET_TYPE(td->sp - 1, STACK_TYPE_VT, mono_defaults.typed_reference_class);
5174 break;
5175 case CEE_REFANYVAL: {
5176 CHECK_STACK (td, 1);
5178 token = read32 (td->ip + 1);
5179 klass = mini_get_class (method, token, generic_context);
5180 CHECK_TYPELOAD (klass);
5182 interp_add_ins (td, MINT_REFANYVAL);
5183 td->last_ins->data [0] = get_data_item_index (td, klass);
5185 POP_VT (td, sizeof (MonoTypedRef));
5186 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5188 td->ip += 5;
5189 break;
5191 case CEE_CONV_OVF_I1:
5192 case CEE_CONV_OVF_I1_UN: {
5193 gboolean is_un = *td->ip == CEE_CONV_OVF_I1_UN;
5194 CHECK_STACK (td, 1);
5195 switch (td->sp [-1].type) {
5196 case STACK_TYPE_R8:
5197 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_UN_R8 : MINT_CONV_OVF_I1_R8);
5198 break;
5199 case STACK_TYPE_I4:
5200 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U4 : MINT_CONV_OVF_I1_I4);
5201 break;
5202 case STACK_TYPE_I8:
5203 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U8 : MINT_CONV_OVF_I1_I8);
5204 break;
5205 default:
5206 g_assert_not_reached ();
5208 ++td->ip;
5209 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5210 break;
5212 case CEE_CONV_OVF_U1:
5213 case CEE_CONV_OVF_U1_UN:
5214 CHECK_STACK (td, 1);
5215 switch (td->sp [-1].type) {
5216 case STACK_TYPE_R8:
5217 interp_add_ins (td, MINT_CONV_OVF_U1_R8);
5218 break;
5219 case STACK_TYPE_I4:
5220 interp_add_ins (td, MINT_CONV_OVF_U1_I4);
5221 break;
5222 case STACK_TYPE_I8:
5223 interp_add_ins (td, MINT_CONV_OVF_U1_I8);
5224 break;
5225 default:
5226 g_assert_not_reached ();
5228 ++td->ip;
5229 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5230 break;
5231 case CEE_CONV_OVF_I2:
5232 case CEE_CONV_OVF_I2_UN: {
5233 gboolean is_un = *td->ip == CEE_CONV_OVF_I2_UN;
5234 CHECK_STACK (td, 1);
5235 switch (td->sp [-1].type) {
5236 case STACK_TYPE_R8:
5237 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_UN_R8 : MINT_CONV_OVF_I2_R8);
5238 break;
5239 case STACK_TYPE_I4:
5240 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U4 : MINT_CONV_OVF_I2_I4);
5241 break;
5242 case STACK_TYPE_I8:
5243 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U8 : MINT_CONV_OVF_I2_I8);
5244 break;
5245 default:
5246 g_assert_not_reached ();
5248 ++td->ip;
5249 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5250 break;
5252 case CEE_CONV_OVF_U2_UN:
5253 case CEE_CONV_OVF_U2:
5254 CHECK_STACK (td, 1);
5255 switch (td->sp [-1].type) {
5256 case STACK_TYPE_R8:
5257 interp_add_ins (td, MINT_CONV_OVF_U2_R8);
5258 break;
5259 case STACK_TYPE_I4:
5260 interp_add_ins (td, MINT_CONV_OVF_U2_I4);
5261 break;
5262 case STACK_TYPE_I8:
5263 interp_add_ins (td, MINT_CONV_OVF_U2_I8);
5264 break;
5265 default:
5266 g_assert_not_reached ();
5268 ++td->ip;
5269 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5270 break;
5271 #if SIZEOF_VOID_P == 4
5272 case CEE_CONV_OVF_I:
5273 #endif
5274 case CEE_CONV_OVF_I4:
5275 case CEE_CONV_OVF_I4_UN:
5276 CHECK_STACK (td, 1);
5277 switch (td->sp [-1].type) {
5278 case STACK_TYPE_R4:
5279 interp_add_ins (td, MINT_CONV_OVF_I4_R4);
5280 break;
5281 case STACK_TYPE_R8:
5282 interp_add_ins (td, MINT_CONV_OVF_I4_R8);
5283 break;
5284 case STACK_TYPE_I4:
5285 if (*td->ip == CEE_CONV_OVF_I4_UN)
5286 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
5287 break;
5288 case STACK_TYPE_I8:
5289 if (*td->ip == CEE_CONV_OVF_I4_UN)
5290 interp_add_ins (td, MINT_CONV_OVF_I4_U8);
5291 else
5292 interp_add_ins (td, MINT_CONV_OVF_I4_I8);
5293 break;
5294 default:
5295 g_assert_not_reached ();
5297 ++td->ip;
5298 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5299 break;
5300 #if SIZEOF_VOID_P == 4
5301 case CEE_CONV_OVF_U:
5302 #endif
5303 case CEE_CONV_OVF_U4:
5304 case CEE_CONV_OVF_U4_UN:
5305 CHECK_STACK (td, 1);
5306 switch (td->sp [-1].type) {
5307 case STACK_TYPE_R4:
5308 interp_add_ins (td, MINT_CONV_OVF_U4_R4);
5309 break;
5310 case STACK_TYPE_R8:
5311 interp_add_ins (td, MINT_CONV_OVF_U4_R8);
5312 break;
5313 case STACK_TYPE_I4:
5314 if (*td->ip != CEE_CONV_OVF_U4_UN)
5315 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
5316 break;
5317 case STACK_TYPE_I8:
5318 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
5319 break;
5320 case STACK_TYPE_MP:
5321 interp_add_ins (td, MINT_CONV_OVF_U4_P);
5322 break;
5323 default:
5324 g_assert_not_reached ();
5326 ++td->ip;
5327 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5328 break;
5329 #if SIZEOF_VOID_P == 8
5330 case CEE_CONV_OVF_I:
5331 #endif
5332 case CEE_CONV_OVF_I8:
5333 CHECK_STACK (td, 1);
5334 switch (td->sp [-1].type) {
5335 case STACK_TYPE_R4:
5336 interp_add_ins (td, MINT_CONV_OVF_I8_R4);
5337 break;
5338 case STACK_TYPE_R8:
5339 interp_add_ins (td, MINT_CONV_OVF_I8_R8);
5340 break;
5341 case STACK_TYPE_I4:
5342 interp_add_ins (td, MINT_CONV_I8_I4);
5343 break;
5344 case STACK_TYPE_I8:
5345 break;
5346 default:
5347 g_assert_not_reached ();
5349 ++td->ip;
5350 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5351 break;
5352 #if SIZEOF_VOID_P == 8
5353 case CEE_CONV_OVF_U:
5354 #endif
5355 case CEE_CONV_OVF_U8:
5356 CHECK_STACK (td, 1);
5357 switch (td->sp [-1].type) {
5358 case STACK_TYPE_R4:
5359 interp_add_ins (td, MINT_CONV_OVF_U8_R4);
5360 break;
5361 case STACK_TYPE_R8:
5362 interp_add_ins (td, MINT_CONV_OVF_U8_R8);
5363 break;
5364 case STACK_TYPE_I4:
5365 interp_add_ins (td, MINT_CONV_OVF_U8_I4);
5366 break;
5367 case STACK_TYPE_I8:
5368 interp_add_ins (td, MINT_CONV_OVF_U8_I8);
5369 break;
5370 default:
5371 g_assert_not_reached ();
5373 ++td->ip;
5374 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5375 break;
5376 case CEE_LDTOKEN: {
5377 int size;
5378 gpointer handle;
5379 token = read32 (td->ip + 1);
5380 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
5381 handle = mono_method_get_wrapper_data (method, token);
5382 klass = (MonoClass *) mono_method_get_wrapper_data (method, token + 1);
5383 if (klass == mono_defaults.typehandle_class)
5384 handle = m_class_get_byval_arg ((MonoClass *) handle);
5386 if (generic_context) {
5387 handle = mono_class_inflate_generic_type_checked ((MonoType*)handle, generic_context, error);
5388 goto_if_nok (error, exit);
5390 } else {
5391 handle = mono_ldtoken_checked (image, token, &klass, generic_context, error);
5392 goto_if_nok (error, exit);
5394 mono_class_init_internal (klass);
5395 mt = mint_type (m_class_get_byval_arg (klass));
5396 g_assert (mt == MINT_TYPE_VT);
5397 size = mono_class_value_size (klass, NULL);
5398 g_assert (size == sizeof(gpointer));
5400 const unsigned char *next_ip = td->ip + 5;
5401 MonoMethod *cmethod;
5402 if (next_ip < end &&
5403 (inlining || !td->is_bb_start [next_ip - td->il_code]) &&
5404 (*next_ip == CEE_CALL || *next_ip == CEE_CALLVIRT) &&
5405 (cmethod = mono_get_method_checked (image, read32 (next_ip + 1), NULL, generic_context, error)) &&
5406 (cmethod->klass == mono_defaults.systemtype_class) &&
5407 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
5408 interp_add_ins (td, MINT_MONO_LDPTR);
5409 gpointer systype = mono_type_get_object_checked (domain, (MonoType*)handle, error);
5410 goto_if_nok (error, exit);
5411 td->last_ins->data [0] = get_data_item_index (td, systype);
5412 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5413 td->ip = next_ip + 5;
5414 } else {
5415 PUSH_VT (td, sizeof(gpointer));
5416 interp_add_ins (td, MINT_LDTOKEN);
5417 td->last_ins->data [0] = get_data_item_index (td, handle);
5418 PUSH_TYPE (td, stack_type [mt], klass);
5419 td->ip += 5;
5422 break;
5424 case CEE_ADD_OVF:
5425 binary_arith_op(td, MINT_ADD_OVF_I4);
5426 ++td->ip;
5427 break;
5428 case CEE_ADD_OVF_UN:
5429 binary_arith_op(td, MINT_ADD_OVF_UN_I4);
5430 ++td->ip;
5431 break;
5432 case CEE_MUL_OVF:
5433 binary_arith_op(td, MINT_MUL_OVF_I4);
5434 ++td->ip;
5435 break;
5436 case CEE_MUL_OVF_UN:
5437 binary_arith_op(td, MINT_MUL_OVF_UN_I4);
5438 ++td->ip;
5439 break;
5440 case CEE_SUB_OVF:
5441 binary_arith_op(td, MINT_SUB_OVF_I4);
5442 ++td->ip;
5443 break;
5444 case CEE_SUB_OVF_UN:
5445 binary_arith_op(td, MINT_SUB_OVF_UN_I4);
5446 ++td->ip;
5447 break;
5448 case CEE_ENDFINALLY: {
5449 g_assert (td->clause_indexes [in_offset] != -1);
5450 td->sp = td->stack;
5451 SIMPLE_OP (td, MINT_ENDFINALLY);
5452 td->last_ins->data [0] = td->clause_indexes [in_offset];
5453 // next instructions, if they exist, are always part of new bb
5454 // endfinally can be the last instruction in a function.
5455 // functions with clauses/endfinally are never inlined.
5456 // is_bb_start is not valid while inlining.
5457 g_assert (!inlining);
5458 if (td->ip - td->il_code < td->code_size)
5459 td->is_bb_start [td->ip - header->code] = 1;
5460 break;
5462 case CEE_LEAVE:
5463 case CEE_LEAVE_S: {
5464 int offset;
5466 if (*td->ip == CEE_LEAVE)
5467 offset = 5 + read32 (td->ip + 1);
5468 else
5469 offset = 2 + (gint8)td->ip [1];
5471 td->sp = td->stack;
5472 if (td->clause_indexes [in_offset] != -1) {
5473 /* LEAVE instructions in catch clauses need to check for abort exceptions */
5474 handle_branch (td, MINT_LEAVE_S_CHECK, MINT_LEAVE_CHECK, offset);
5475 } else {
5476 handle_branch (td, MINT_LEAVE_S, MINT_LEAVE, offset);
5479 if (*td->ip == CEE_LEAVE)
5480 td->ip += 5;
5481 else
5482 td->ip += 2;
5483 break;
5485 case MONO_CUSTOM_PREFIX:
5486 ++td->ip;
5487 switch (*td->ip) {
5488 case CEE_MONO_RETHROW:
5489 CHECK_STACK (td, 1);
5490 SIMPLE_OP (td, MINT_MONO_RETHROW);
5491 td->sp = td->stack;
5492 break;
5494 case CEE_MONO_LD_DELEGATE_METHOD_PTR:
5495 --td->sp;
5496 td->ip += 1;
5497 interp_add_ins (td, MINT_LD_DELEGATE_METHOD_PTR);
5498 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5499 break;
5500 case CEE_MONO_CALLI_EXTRA_ARG:
5501 /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
5502 interp_add_ins (td, MINT_POP);
5503 td->last_ins->data [0] = 1;
5504 --td->sp;
5505 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
5506 goto exit;
5507 break;
5508 case CEE_MONO_JIT_ICALL_ADDR: {
5509 const guint32 token = read32 (td->ip + 1);
5510 td->ip += 5;
5511 const gconstpointer func = mono_find_jit_icall_info ((MonoJitICallId)token)->func;
5513 interp_add_ins (td, MINT_LDFTN);
5514 td->last_ins->data [0] = get_data_item_index (td, (gpointer)func);
5515 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5516 break;
5518 case CEE_MONO_ICALL: {
5519 MonoJitICallId const jit_icall_id = (MonoJitICallId)read32 (td->ip + 1);
5520 MonoJitICallInfo const * const info = mono_find_jit_icall_info (jit_icall_id);
5521 td->ip += 5;
5523 CHECK_STACK (td, info->sig->param_count);
5524 if (jit_icall_id == MONO_JIT_ICALL_mono_threads_attach_coop) {
5525 rtm->needs_thread_attach = 1;
5527 /* attach needs two arguments, and has one return value: leave one element on the stack */
5528 interp_add_ins (td, MINT_POP);
5529 td->last_ins->data [0] = 0;
5530 } else if (jit_icall_id == MONO_JIT_ICALL_mono_threads_detach_coop) {
5531 g_assert (rtm->needs_thread_attach);
5533 /* detach consumes two arguments, and no return value: drop both of them */
5534 interp_add_ins (td, MINT_POP);
5535 td->last_ins->data [0] = 0;
5536 interp_add_ins (td, MINT_POP);
5537 td->last_ins->data [0] = 0;
5538 } else {
5539 int const icall_op = interp_icall_op_for_sig (info->sig);
5540 g_assert (icall_op != -1);
5542 interp_add_ins (td, icall_op);
5543 // hash here is overkill
5544 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
5546 td->sp -= info->sig->param_count;
5548 if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
5549 int mt = mint_type (info->sig->ret);
5550 PUSH_SIMPLE_TYPE(td, stack_type [mt]);
5552 break;
5554 case CEE_MONO_VTADDR: {
5555 int size;
5556 CHECK_STACK (td, 1);
5557 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
5558 size = mono_class_native_size(td->sp [-1].klass, NULL);
5559 else
5560 size = mono_class_value_size(td->sp [-1].klass, NULL);
5561 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
5562 interp_add_ins (td, MINT_VTRESULT);
5563 td->last_ins->data [0] = 0;
5564 WRITE32_INS (td->last_ins, 1, &size);
5565 td->vt_sp -= size;
5566 ++td->ip;
5567 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5568 break;
5570 case CEE_MONO_LDPTR:
5571 case CEE_MONO_CLASSCONST:
5572 token = read32 (td->ip + 1);
5573 td->ip += 5;
5574 interp_add_ins (td, MINT_MONO_LDPTR);
5575 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5576 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5577 break;
5578 case CEE_MONO_OBJADDR:
5579 CHECK_STACK (td, 1);
5580 ++td->ip;
5581 td->sp[-1].type = STACK_TYPE_MP;
5582 /* do nothing? */
5583 break;
5584 case CEE_MONO_NEWOBJ:
5585 token = read32 (td->ip + 1);
5586 td->ip += 5;
5587 interp_add_ins (td, MINT_MONO_NEWOBJ);
5588 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5589 PUSH_SIMPLE_TYPE (td, STACK_TYPE_O);
5590 break;
5591 case CEE_MONO_RETOBJ:
5592 CHECK_STACK (td, 1);
5593 token = read32 (td->ip + 1);
5594 td->ip += 5;
5595 interp_add_ins (td, MINT_MONO_RETOBJ);
5596 td->sp--;
5598 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5600 /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/
5602 if (td->sp > td->stack)
5603 g_warning ("CEE_MONO_RETOBJ: more values on stack: %d", td->sp-td->stack);
5604 break;
5605 case CEE_MONO_LDNATIVEOBJ:
5606 token = read32 (td->ip + 1);
5607 td->ip += 5;
5608 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5609 g_assert(m_class_is_valuetype (klass));
5610 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5611 break;
5612 case CEE_MONO_TLS: {
5613 gint32 key = read32 (td->ip + 1);
5614 td->ip += 5;
5615 g_assertf (key == TLS_KEY_SGEN_THREAD_INFO, "%d", key);
5616 interp_add_ins (td, MINT_MONO_SGEN_THREAD_INFO);
5617 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5618 break;
5620 case CEE_MONO_ATOMIC_STORE_I4:
5621 CHECK_STACK (td, 2);
5622 SIMPLE_OP (td, MINT_MONO_ATOMIC_STORE_I4);
5623 td->sp -= 2;
5624 td->ip++;
5625 break;
5626 case CEE_MONO_SAVE_LMF:
5627 case CEE_MONO_RESTORE_LMF:
5628 case CEE_MONO_NOT_TAKEN:
5629 ++td->ip;
5630 break;
5631 case CEE_MONO_LDPTR_INT_REQ_FLAG:
5632 interp_add_ins (td, MINT_MONO_LDPTR);
5633 td->last_ins->data [0] = get_data_item_index (td, mono_thread_interruption_request_flag ());
5634 PUSH_TYPE (td, STACK_TYPE_MP, NULL);
5635 ++td->ip;
5636 break;
5637 case CEE_MONO_MEMORY_BARRIER:
5638 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5639 ++td->ip;
5640 break;
5641 case CEE_MONO_LDDOMAIN:
5642 interp_add_ins (td, MINT_MONO_LDDOMAIN);
5643 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5644 ++td->ip;
5645 break;
5646 case CEE_MONO_SAVE_LAST_ERROR:
5647 save_last_error = TRUE;
5648 ++td->ip;
5649 break;
5650 default:
5651 g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td->ip, td->ip-header->code);
5653 break;
5654 #if 0
5655 case CEE_PREFIX7:
5656 case CEE_PREFIX6:
5657 case CEE_PREFIX5:
5658 case CEE_PREFIX4:
5659 case CEE_PREFIX3:
5660 case CEE_PREFIX2:
5661 case CEE_PREFIXREF: ves_abort(); break;
5662 #endif
5664 * Note: Exceptions thrown when executing a prefixed opcode need
5665 * to take into account the number of prefix bytes (usually the
5666 * throw point is just (ip - n_prefix_bytes).
5668 case CEE_PREFIX1:
5669 ++td->ip;
5670 switch (*td->ip) {
5671 case CEE_ARGLIST:
5672 interp_add_ins (td, MINT_ARGLIST);
5673 PUSH_VT (td, SIZEOF_VOID_P);
5674 PUSH_SIMPLE_TYPE (td, STACK_TYPE_VT);
5675 ++td->ip;
5676 break;
5677 case CEE_CEQ:
5678 CHECK_STACK(td, 2);
5679 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP) {
5680 interp_add_ins (td, MINT_CEQ_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5681 } else {
5682 if (td->sp [-1].type == STACK_TYPE_R4 && td->sp [-2].type == STACK_TYPE_R8)
5683 interp_add_ins (td, MINT_CONV_R8_R4);
5684 if (td->sp [-1].type == STACK_TYPE_R8 && td->sp [-2].type == STACK_TYPE_R4)
5685 interp_add_ins (td, MINT_CONV_R8_R4_SP);
5686 interp_add_ins (td, MINT_CEQ_I4 + td->sp [-1].type - STACK_TYPE_I4);
5688 --td->sp;
5689 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5690 ++td->ip;
5691 break;
5692 case CEE_CGT:
5693 CHECK_STACK(td, 2);
5694 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5695 interp_add_ins (td, MINT_CGT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5696 else
5697 interp_add_ins (td, MINT_CGT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5698 --td->sp;
5699 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5700 ++td->ip;
5701 break;
5702 case CEE_CGT_UN:
5703 CHECK_STACK(td, 2);
5704 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5705 interp_add_ins (td, MINT_CGT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5706 else
5707 interp_add_ins (td, MINT_CGT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5708 --td->sp;
5709 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5710 ++td->ip;
5711 break;
5712 case CEE_CLT:
5713 CHECK_STACK(td, 2);
5714 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5715 interp_add_ins (td, MINT_CLT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5716 else
5717 interp_add_ins (td, MINT_CLT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5718 --td->sp;
5719 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5720 ++td->ip;
5721 break;
5722 case CEE_CLT_UN:
5723 CHECK_STACK(td, 2);
5724 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5725 interp_add_ins (td, MINT_CLT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5726 else
5727 interp_add_ins (td, MINT_CLT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5728 --td->sp;
5729 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5730 ++td->ip;
5731 break;
5732 case CEE_LDVIRTFTN: /* fallthrough */
5733 case CEE_LDFTN: {
5734 MonoMethod *m;
5735 if (*td->ip == CEE_LDVIRTFTN) {
5736 CHECK_STACK (td, 1);
5737 --td->sp;
5739 token = read32 (td->ip + 1);
5740 if (method->wrapper_type != MONO_WRAPPER_NONE)
5741 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
5742 else {
5743 m = mono_get_method_checked (image, token, NULL, generic_context, error);
5744 goto_if_nok (error, exit);
5747 if (!mono_method_can_access_method (method, m))
5748 interp_generate_mae_throw (td, method, m);
5750 if (method->wrapper_type == MONO_WRAPPER_NONE && m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
5751 m = mono_marshal_get_synchronized_wrapper (m);
5753 interp_add_ins (td, *td->ip == CEE_LDFTN ? MINT_LDFTN : MINT_LDVIRTFTN);
5754 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
5755 goto_if_nok (error, exit);
5756 td->ip += 5;
5757 PUSH_SIMPLE_TYPE (td, STACK_TYPE_F);
5758 break;
5760 case CEE_LDARG: {
5761 int arg_n = read16 (td->ip + 1);
5762 if (!inlining)
5763 load_arg (td, arg_n);
5764 else
5765 load_local_general (td, arg_locals [arg_n], get_arg_type (signature, arg_n));
5766 td->ip += 3;
5767 break;
5769 case CEE_LDARGA: {
5770 int n = read16 (td->ip + 1);
5772 if (!inlining) {
5773 get_arg_type_exact (td, n, &mt);
5774 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
5775 td->last_ins->data [0] = n;
5776 } else {
5777 int loc_n = arg_locals [n];
5778 interp_add_ins (td, MINT_LDLOCA_S);
5779 td->last_ins->data [0] = loc_n;
5780 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
5782 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5783 td->ip += 3;
5784 break;
5786 case CEE_STARG: {
5787 int arg_n = read16 (td->ip + 1);
5788 if (!inlining)
5789 store_arg (td, arg_n);
5790 else
5791 store_local_general (td, arg_locals [arg_n], get_arg_type (signature, arg_n));
5792 td->ip += 3;
5793 break;
5795 case CEE_LDLOC: {
5796 int loc_n = read16 (td->ip + 1);
5797 if (!inlining)
5798 load_local (td, loc_n);
5799 else
5800 load_local_general (td, local_locals [loc_n], header->locals [loc_n]);
5801 td->ip += 3;
5802 break;
5804 case CEE_LDLOCA: {
5805 int loc_n = read16 (td->ip + 1);
5806 interp_add_ins (td, MINT_LDLOCA_S);
5807 if (inlining)
5808 loc_n = local_locals [loc_n];
5809 td->last_ins->data [0] = loc_n;
5810 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
5811 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5812 td->ip += 3;
5813 break;
5815 case CEE_STLOC: {
5816 int loc_n = read16 (td->ip + 1);
5817 if (!inlining)
5818 store_local (td, loc_n);
5819 else
5820 store_local_general (td, local_locals [loc_n], header->locals [loc_n]);
5821 td->ip += 3;
5822 break;
5824 case CEE_LOCALLOC:
5825 INLINE_FAILURE;
5826 CHECK_STACK (td, 1);
5827 #if SIZEOF_VOID_P == 8
5828 if (td->sp [-1].type == STACK_TYPE_I8)
5829 interp_add_ins (td, MINT_CONV_I4_I8);
5830 #endif
5831 interp_add_ins (td, MINT_LOCALLOC);
5832 if (td->sp != td->stack + 1)
5833 g_warning("CEE_LOCALLOC: stack not empty");
5834 ++td->ip;
5835 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5836 break;
5837 #if 0
5838 case CEE_UNUSED57: ves_abort(); break;
5839 #endif
5840 case CEE_ENDFILTER:
5841 interp_add_ins (td, MINT_ENDFILTER);
5842 ++td->ip;
5843 break;
5844 case CEE_UNALIGNED_:
5845 td->ip += 2;
5846 break;
5847 case CEE_VOLATILE_:
5848 ++td->ip;
5849 volatile_ = TRUE;
5850 break;
5851 case CEE_TAIL_:
5852 ++td->ip;
5853 /* FIX: should do something? */;
5854 // TODO: This should raise a method_tail_call profiler event.
5855 break;
5856 case CEE_INITOBJ:
5857 CHECK_STACK(td, 1);
5858 token = read32 (td->ip + 1);
5859 klass = mini_get_class (method, token, generic_context);
5860 CHECK_TYPELOAD (klass);
5861 if (m_class_is_valuetype (klass)) {
5862 interp_add_ins (td, MINT_INITOBJ);
5863 i32 = mono_class_value_size (klass, NULL);
5864 WRITE32_INS (td->last_ins, 0, &i32);
5865 --td->sp;
5866 } else {
5867 interp_add_ins (td, MINT_LDNULL);
5868 PUSH_TYPE(td, STACK_TYPE_O, NULL);
5869 interp_add_ins (td, MINT_STIND_REF);
5870 td->sp -= 2;
5872 td->ip += 5;
5873 break;
5874 case CEE_CPBLK:
5875 CHECK_STACK(td, 3);
5876 /* FIX? convert length to I8? */
5877 if (volatile_)
5878 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5879 interp_add_ins (td, MINT_CPBLK);
5880 BARRIER_IF_VOLATILE (td);
5881 td->sp -= 3;
5882 ++td->ip;
5883 break;
5884 case CEE_READONLY_:
5885 readonly = TRUE;
5886 td->ip += 1;
5887 break;
5888 case CEE_CONSTRAINED_:
5889 token = read32 (td->ip + 1);
5890 constrained_class = mini_get_class (method, token, generic_context);
5891 CHECK_TYPELOAD (constrained_class);
5892 td->ip += 5;
5893 break;
5894 case CEE_INITBLK:
5895 CHECK_STACK(td, 3);
5896 BARRIER_IF_VOLATILE (td);
5897 interp_add_ins (td, MINT_INITBLK);
5898 td->sp -= 3;
5899 td->ip += 1;
5900 break;
5901 case CEE_NO_:
5902 /* FIXME: implement */
5903 td->ip += 2;
5904 break;
5905 case CEE_RETHROW: {
5906 int clause_index = td->clause_indexes [in_offset];
5907 g_assert (clause_index != -1);
5908 SIMPLE_OP (td, MINT_RETHROW);
5909 td->last_ins->data [0] = rtm->exvar_offsets [clause_index];
5910 td->sp = td->stack;
5911 break;
5913 case CEE_SIZEOF: {
5914 gint32 size;
5915 token = read32 (td->ip + 1);
5916 td->ip += 5;
5917 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (m_class_get_image (method->klass)) && !generic_context) {
5918 int align;
5919 MonoType *type = mono_type_create_from_typespec_checked (image, token, error);
5920 goto_if_nok (error, exit);
5921 size = mono_type_size (type, &align);
5922 } else {
5923 int align;
5924 MonoClass *szclass = mini_get_class (method, token, generic_context);
5925 CHECK_TYPELOAD (szclass);
5926 #if 0
5927 if (!szclass->valuetype)
5928 THROW_EX (mono_exception_from_name (mono_defaults.corlib, "System", "InvalidProgramException"), ip - 5);
5929 #endif
5930 size = mono_type_size (m_class_get_byval_arg (szclass), &align);
5932 interp_add_ins (td, MINT_LDC_I4);
5933 WRITE32_INS (td->last_ins, 0, &size);
5934 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
5935 break;
5937 case CEE_REFANYTYPE:
5938 interp_add_ins (td, MINT_REFANYTYPE);
5939 td->ip += 1;
5940 POP_VT (td, sizeof (MonoTypedRef));
5941 PUSH_VT (td, sizeof (gpointer));
5942 SET_TYPE(td->sp - 1, STACK_TYPE_VT, NULL);
5943 break;
5944 default:
5945 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);
5947 break;
5948 default:
5949 g_error ("transform.c: Unimplemented opcode: %02x at 0x%x\n", *td->ip, td->ip-header->code);
5952 // No IR instructions were added as part of a bb_start IL instruction. Add a MINT_NOP
5953 // so we always have an instruction associated with a bb_start. This is simple and avoids
5954 // any complications associated with il_offset tracking.
5955 if (prev_last_ins == td->last_ins && (!inlining && td->is_bb_start [in_offset]) && td->ip < end)
5956 interp_add_ins (td, MINT_NOP);
5959 g_assert (td->ip == end);
5961 exit_ret:
5962 g_free (arg_locals);
5963 g_free (local_locals);
5964 mono_basic_block_free (original_bb);
5966 return ret;
5967 exit:
5968 ret = FALSE;
5969 goto exit_ret;
5972 // Find the offset of the first interp instruction generated starting il_offset
5973 // This is needed to find the end of clauses.
5974 static int
5975 find_in_offset (TransformData *td, int il_offset)
5977 int i = il_offset;
5978 while (!td->in_offsets [i])
5979 i++;
5980 return td->in_offsets [i] - 1;
5983 // We store in the in_offset array the native_offset + 1, so 0 can mean only that the il
5984 // offset is uninitialized. Otherwise 0 is valid value for first interp instruction.
5985 static int
5986 get_in_offset (TransformData *td, int il_offset)
5988 int target_offset = td->in_offsets [il_offset];
5989 g_assert (target_offset);
5990 return target_offset - 1;
5993 static void
5994 handle_relocations (TransformData *td)
5996 // Handle relocations
5997 for (int i = 0; i < td->relocs->len; ++i) {
5998 Reloc *reloc = (Reloc*)g_ptr_array_index (td->relocs, i);
5999 int offset = get_in_offset (td, reloc->target) - reloc->offset;
6001 switch (reloc->type) {
6002 case RELOC_SHORT_BRANCH:
6003 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
6004 td->new_code [reloc->offset + 1] = offset;
6005 break;
6006 case RELOC_LONG_BRANCH: {
6007 guint16 *v = (guint16 *) &offset;
6008 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
6009 g_assert (td->new_code [reloc->offset + 2] == 0xbeef);
6010 td->new_code [reloc->offset + 1] = *(guint16 *) v;
6011 td->new_code [reloc->offset + 2] = *(guint16 *) (v + 1);
6012 break;
6014 case RELOC_SWITCH: {
6015 guint16 *v = (guint16*)&offset;
6016 g_assert (td->new_code [reloc->offset] == 0xdead);
6017 g_assert (td->new_code [reloc->offset + 1] == 0xbeef);
6018 td->new_code [reloc->offset] = *(guint16*)v;
6019 td->new_code [reloc->offset + 1] = *(guint16*)(v + 1);
6020 break;
6022 default:
6023 g_assert_not_reached ();
6024 break;
6030 static int
6031 get_inst_length (InterpInst *ins)
6033 if (ins->opcode == MINT_SWITCH)
6034 return MINT_SWITCH_LEN (READ32 (&ins->data [0]));
6035 else
6036 return mono_interp_oplen [ins->opcode];
6039 static void
6040 get_inst_stack_usage (TransformData *td, InterpInst *ins, int *pop, int *push)
6042 guint16 opcode = ins->opcode;
6043 if (mono_interp_oppop [opcode] == MINT_VAR_POP ||
6044 mono_interp_oppush [opcode] == MINT_VAR_PUSH) {
6045 switch (opcode) {
6046 case MINT_JIT_CALL:
6047 case MINT_CALL:
6048 case MINT_CALLVIRT:
6049 case MINT_CALLVIRT_FAST:
6050 case MINT_VCALL:
6051 case MINT_VCALLVIRT:
6052 case MINT_VCALLVIRT_FAST: {
6053 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6054 *pop = imethod->param_count + imethod->hasthis;
6055 if (opcode == MINT_JIT_CALL)
6056 *push = imethod->rtype->type != MONO_TYPE_VOID;
6057 else
6058 *push = opcode == MINT_CALL || opcode == MINT_CALLVIRT || opcode == MINT_CALLVIRT_FAST;
6059 break;
6061 case MINT_CALLRUN: {
6062 MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [1]];
6063 *pop = csignature->param_count + csignature->hasthis;
6064 *push = csignature->ret->type != MONO_TYPE_VOID;
6065 break;
6067 case MINT_CALLI:
6068 case MINT_CALLI_NAT:
6069 case MINT_CALLI_NAT_FAST: {
6070 MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [0]];
6071 *pop = csignature->param_count + csignature->hasthis + 1;
6072 *push = csignature->ret->type != MONO_TYPE_VOID;
6073 break;
6075 case MINT_CALL_VARARG: {
6076 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6077 MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [1]];
6078 *pop = imethod->param_count + imethod->hasthis + csignature->param_count - csignature->sentinelpos;
6079 *push = imethod->rtype->type != MONO_TYPE_VOID;
6080 break;
6082 case MINT_NEWOBJ_FAST: {
6083 int param_count = ins->data [1];
6084 gboolean is_inlined = ins->data [0] == 0xffff;
6085 if (is_inlined) {
6086 // FIXME
6087 // We lose track of the contents of the stack because the newobj references are pushed below
6088 // the ctor arguments. We should keep track of stack contents to enable ctor optimization.
6089 *pop = param_count;
6090 *push = param_count + 2;
6091 } else {
6092 *pop = param_count;
6093 *push = 1;
6095 break;
6097 case MINT_NEWOBJ_ARRAY:
6098 case MINT_NEWOBJ_VT_FAST:
6099 case MINT_NEWOBJ_VTST_FAST:
6100 *pop = ins->data [1];
6101 *push = 1;
6102 break;
6103 case MINT_LDELEMA:
6104 case MINT_LDELEMA_TC:
6105 *pop = ins->data [0] + 1;
6106 *push = 1;
6107 break;
6108 case MINT_NEWOBJ: {
6109 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6110 *pop = imethod->param_count;
6111 *push = 1;
6112 break;
6114 case MINT_BOX:
6115 case MINT_BOX_VT:
6116 case MINT_BOX_NULLABLE:
6117 *pop = (ins->data [1] & ~BOX_NOT_CLEAR_VT_SP) + 1;
6118 *push = *pop;
6119 break;
6120 case MINT_CKNULL_N:
6121 *pop = ins->data [0];
6122 *push = ins->data [0];
6123 break;
6124 case MINT_LD_DELEGATE_INVOKE_IMPL:
6125 *pop = ins->data [0];
6126 *push = ins->data [0] + 1;
6127 break;
6128 case MINT_INTRINS_BYREFERENCE_CTOR: {
6129 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6130 *pop = imethod->param_count;
6131 *push = 1;
6132 break;
6134 default:
6135 g_assert_not_reached ();
6137 } else {
6138 *pop = mono_interp_oppop [opcode];
6139 *push = mono_interp_oppush [opcode];
6143 static guint16*
6144 emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *ins)
6146 guint16 opcode = ins->opcode;
6147 guint16 *ip = start_ip;
6149 // We know what IL offset this instruction was created for. We can now map the IL offset
6150 // to the IR offset. We use this array to resolve the relocations, which reference the IL.
6151 if (ins->il_offset != -1 && !td->in_offsets [ins->il_offset]) {
6152 g_assert (ins->il_offset >= 0 && ins->il_offset < td->header->code_size);
6153 td->in_offsets [ins->il_offset] = start_ip - td->new_code + 1;
6155 MonoDebugLineNumberEntry lne;
6156 lne.native_offset = (guint8*)start_ip - (guint8*)td->new_code;
6157 lne.il_offset = ins->il_offset;
6158 g_array_append_val (td->line_numbers, lne);
6161 if (opcode == MINT_NOP)
6162 return ip;
6164 *ip++ = opcode;
6165 if (opcode == MINT_SWITCH) {
6166 int labels = READ32 (&ins->data [0]);
6167 // Write number of switch labels
6168 *ip++ = ins->data [0];
6169 *ip++ = ins->data [1];
6170 // Add relocation for each label
6171 for (int i = 0; i < labels; i++) {
6172 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
6173 reloc->type = RELOC_SWITCH;
6174 reloc->offset = ip - td->new_code;
6175 reloc->target = READ32 (&ins->data [2 + i * 2]);
6176 g_ptr_array_add (td->relocs, reloc);
6177 *ip++ = 0xdead;
6178 *ip++ = 0xbeef;
6180 } else if ((opcode >= MINT_BRFALSE_I4_S && opcode <= MINT_BRTRUE_R8_S) ||
6181 (opcode >= MINT_BEQ_I4_S && opcode <= MINT_BLT_UN_R8_S) ||
6182 opcode == MINT_BR_S || opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK) {
6183 const int br_offset = start_ip - td->new_code;
6184 if (ins->data [0] < ins->il_offset) {
6185 // Backwards branch. We can already patch it.
6186 *ip++ = get_in_offset (td, ins->data [0]) - br_offset;
6187 } else {
6188 // We don't know the in_offset of the target, add a reloc
6189 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
6190 reloc->type = RELOC_SHORT_BRANCH;
6191 reloc->offset = br_offset;
6192 reloc->target = ins->data [0];
6193 g_ptr_array_add (td->relocs, reloc);
6194 *ip++ = 0xdead;
6196 } else if ((opcode >= MINT_BRFALSE_I4 && opcode <= MINT_BRTRUE_R8) ||
6197 (opcode >= MINT_BEQ_I4 && opcode <= MINT_BLT_UN_R8) ||
6198 opcode == MINT_BR || opcode == MINT_LEAVE || opcode == MINT_LEAVE_CHECK) {
6199 const int br_offset = start_ip - td->new_code;
6200 int target_il = READ32 (&ins->data [0]);
6201 if (target_il < ins->il_offset) {
6202 // Backwards branch. We can already patch it
6203 const int br_offset = start_ip - td->new_code;
6204 int target_offset = get_in_offset (td, target_il) - br_offset;
6205 WRITE32 (ip, &target_offset);
6206 } else {
6207 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
6208 reloc->type = RELOC_LONG_BRANCH;
6209 reloc->offset = br_offset;
6210 reloc->target = target_il;
6211 g_ptr_array_add (td->relocs, reloc);
6212 *ip++ = 0xdead;
6213 *ip++ = 0xbeef;
6215 } else if (opcode == MINT_SDB_SEQ_POINT) {
6216 SeqPoint *seqp = (SeqPoint*)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint));
6217 InterpBasicBlock *cbb;
6219 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY) {
6220 seqp->il_offset = METHOD_ENTRY_IL_OFFSET;
6221 cbb = td->offset_to_bb [0];
6222 } else {
6223 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT)
6224 seqp->il_offset = METHOD_EXIT_IL_OFFSET;
6225 else
6226 seqp->il_offset = ins->il_offset;
6227 cbb = td->offset_to_bb [ins->il_offset];
6229 seqp->native_offset = (guint8*)start_ip - (guint8*)td->new_code;
6230 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK)
6231 seqp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
6232 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL)
6233 seqp->flags |= MONO_SEQ_POINT_FLAG_NESTED_CALL;
6234 g_ptr_array_add (td->seq_points, seqp);
6236 cbb->seq_points = g_slist_prepend_mempool (td->mempool, cbb->seq_points, seqp);
6237 cbb->last_seq_point = seqp;
6238 } else {
6239 if (MINT_IS_LDLOC (opcode) || MINT_IS_STLOC (opcode) || MINT_IS_STLOC_NP (opcode) || opcode == MINT_LDLOCA_S) {
6240 ins->data [0] = get_interp_local_offset (td, ins->data [0]);
6241 } else if (MINT_IS_MOVLOC (opcode)) {
6242 ins->data [0] = get_interp_local_offset (td, ins->data [0]);
6243 ins->data [1] = get_interp_local_offset (td, ins->data [1]);
6246 int size = get_inst_length (ins) - 1;
6247 // Emit the rest of the data
6248 for (int i = 0; i < size; i++)
6249 *ip++ = ins->data [i];
6251 return ip;
6254 // Generates the final code, after we are done with all the passes
6255 static void
6256 generate_compacted_code (TransformData *td)
6258 guint16 *ip;
6259 int size = 0;
6260 td->relocs = g_ptr_array_new ();
6262 // Iterate once to compute the exact size of the compacted code
6263 InterpInst *ins = td->first_ins;
6264 while (ins) {
6265 size += get_inst_length (ins);
6266 ins = ins->next;
6269 // Generate the compacted stream of instructions
6270 td->new_code = ip = (guint16*)mono_domain_alloc0 (td->rtm->domain, size * sizeof (guint16));
6271 ins = td->first_ins;
6272 while (ins) {
6273 ip = emit_compacted_instruction (td, ip, ins);
6274 ins = ins->next;
6276 td->new_code_end = ip;
6277 td->in_offsets [td->header->code_size] = td->new_code_end - td->new_code;
6279 // Patch all branches
6280 handle_relocations (td);
6282 g_ptr_array_free (td->relocs, TRUE);
6285 static int
6286 get_movloc_for_type (int mt)
6288 switch (mt) {
6289 case MINT_TYPE_I1:
6290 case MINT_TYPE_U1:
6291 return MINT_MOVLOC_1;
6292 case MINT_TYPE_I2:
6293 case MINT_TYPE_U2:
6294 return MINT_MOVLOC_2;
6295 case MINT_TYPE_I4:
6296 case MINT_TYPE_R4:
6297 return MINT_MOVLOC_4;
6298 case MINT_TYPE_I8:
6299 case MINT_TYPE_R8:
6300 return MINT_MOVLOC_8;
6301 case MINT_TYPE_O:
6302 case MINT_TYPE_P:
6303 #if SIZEOF_VOID_P == 8
6304 return MINT_MOVLOC_8;
6305 #else
6306 return MINT_MOVLOC_4;
6307 #endif
6308 case MINT_TYPE_VT:
6309 return MINT_MOVLOC_VT;
6311 g_assert_not_reached ();
6314 // The value of local has changed. This means the contents of the stack where the
6315 // local was loaded, no longer contain the value of the local. Clear them.
6316 static void
6317 clear_stack_content_info_for_local (StackContentInfo *start, StackContentInfo *end, int local)
6319 StackContentInfo *si;
6320 for (si = start; si < end; si++) {
6321 if (si->ins) {
6322 g_assert (MINT_IS_LDLOC (si->ins->opcode));
6323 if (si->ins->data [0] == local)
6324 si->ins = NULL;
6329 // The value of local has changed. This means we can no longer assume that any other local
6330 // is a copy of this local.
6331 static void
6332 clear_local_content_info_for_local (StackContentInfo *start, StackContentInfo *end, int local)
6334 StackContentInfo *si;
6335 for (si = start; si < end; si++) {
6336 if (si->ins) {
6337 g_assert (MINT_IS_MOVLOC (si->ins->opcode));
6338 g_assert (si->ins->data [1] == (guint16)(si - start));
6339 if (si->ins->data [0] == local)
6340 si->ins = NULL;
6345 static void
6346 interp_local_deadce (TransformData *td, int *local_ref_count)
6348 InterpInst *ins;
6349 gboolean needs_dce = FALSE;
6351 for (int i = 0; i < td->locals_size; i++) {
6352 g_assert (local_ref_count [i] >= 0);
6353 if (!local_ref_count [i] && (td->locals [i].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0) {
6354 needs_dce = TRUE;
6355 break;
6359 // Return early if all locals are alive
6360 if (!needs_dce)
6361 return;
6363 // Kill instructions that don't use stack and are storing into dead locals
6364 for (ins = td->first_ins; ins != NULL; ins = ins->next) {
6365 if (MINT_IS_STLOC_NP (ins->opcode)) {
6366 if (!local_ref_count [ins->data [0]] && (td->locals [ins->data [0]].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0) {
6367 interp_clear_ins (td, ins);
6368 mono_interp_stats.killed_instructions++;
6370 } else if (MINT_IS_MOVLOC (ins->opcode)) {
6371 if (!local_ref_count [ins->data [1]] && (td->locals [ins->data [1]].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0) {
6372 interp_clear_ins (td, ins);
6373 mono_interp_stats.killed_instructions++;
6379 static void
6380 interp_cprop (TransformData *td)
6382 if (!td->max_stack_height || !td->locals_size)
6383 return;
6384 StackContentInfo *stack = (StackContentInfo*) g_malloc (td->max_stack_height * sizeof (StackContentInfo));
6385 StackContentInfo *stack_end = stack + td->max_stack_height;
6386 StackContentInfo *sp = stack;
6387 StackContentInfo *locals = (StackContentInfo*) g_malloc (td->locals_size * sizeof (StackContentInfo));
6388 int *local_ref_count = (int*) g_malloc0 (td->locals_size * sizeof (int));
6389 InterpInst *ins;
6390 int last_il_offset = -1;
6392 for (ins = td->first_ins; ins != NULL; ins = ins->next) {
6393 int pop, push;
6394 int il_offset = ins->il_offset;
6395 // Optimizations take place only inside a single basic block
6396 // If two instructions have the same il_offset, then the second one
6397 // cannot be part the start of a basic block.
6398 gboolean is_bb_start = il_offset != -1 && td->is_bb_start [il_offset] && il_offset != last_il_offset;
6399 if (is_bb_start) {
6400 if (td->stack_height [il_offset] >= 0) {
6401 sp = stack + td->stack_height [il_offset];
6402 g_assert (sp >= stack);
6403 memset (stack, 0, (sp - stack) * sizeof (StackContentInfo));
6405 memset (locals, 0, td->locals_size * sizeof (StackContentInfo));
6407 // The instruction pops some values then pushes some other
6408 get_inst_stack_usage (td, ins, &pop, &push);
6409 if (MINT_IS_LDLOC (ins->opcode)) {
6410 int replace_op = 0;
6411 int loaded_local = ins->data [0];
6412 local_ref_count [loaded_local]++;
6413 if (!is_bb_start && MINT_IS_STLOC (ins->prev->opcode) && ins->prev->data [0] == loaded_local) {
6414 int mt = ins->prev->opcode - MINT_STLOC_I1;
6415 if (ins->opcode - MINT_LDLOC_I1 == mt) {
6416 if (mt == MINT_TYPE_I4)
6417 replace_op = MINT_STLOC_NP_I4;
6418 else if (mt == MINT_TYPE_O || mt == MINT_TYPE_P)
6419 replace_op = MINT_STLOC_NP_O;
6420 if (replace_op) {
6421 if (td->verbose_level)
6422 g_print ("Add stloc.np : ldloc (off %p), stloc (off %p)\n", ins->il_offset, ins->prev->il_offset);
6423 interp_clear_ins (td, ins->prev);
6424 ins->opcode = replace_op;
6425 mono_interp_stats.stloc_nps++;
6426 local_ref_count [loaded_local]--;
6427 // FIXME We know what local is on the stack now. Track it
6430 } else if (locals [loaded_local].ins != NULL && !(td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
6431 g_assert (MINT_IS_MOVLOC (locals [loaded_local].ins->opcode));
6432 // do copy propagation of the original source
6433 if (td->verbose_level)
6434 g_print ("cprop %d -> %d\n", loaded_local, locals [loaded_local].ins->data [0]);
6435 mono_interp_stats.copy_propagations++;
6436 local_ref_count [loaded_local]--;
6437 ins->data [0] = locals [loaded_local].ins->data [0];
6438 local_ref_count [ins->data [0]]++;
6440 if (!replace_op) {
6441 // Save the ldloc on the stack if it wasn't optimized away
6442 // For simplicity we don't track locals that have their address taken
6443 // since it is hard to detect instructions that change the local value.
6444 if (td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT)
6445 sp->ins = NULL;
6446 else
6447 sp->ins = ins;
6449 sp++;
6450 } else if (MINT_IS_STLOC (ins->opcode)) {
6451 int dest_local = ins->data [0];
6452 sp--;
6453 if (sp->ins != NULL) {
6454 int mt = sp->ins->opcode - MINT_LDLOC_I1;
6455 if (ins->opcode - MINT_STLOC_I1 == mt) {
6456 // Same local, same type of load and store, convert to movloc
6457 if (td->verbose_level)
6458 g_print ("Add movloc : ldloc (off %p), stloc (off %p)\n", sp->ins->il_offset, ins->il_offset);
6459 int src_local = sp->ins->data [0];
6460 interp_clear_ins (td, sp->ins);
6461 interp_clear_ins (td, ins);
6463 ins = interp_insert_ins (td, ins, get_movloc_for_type (mt));
6464 ins->data [0] = src_local;
6465 ins->data [1] = dest_local;
6466 if (ins->opcode == MINT_MOVLOC_VT)
6467 ins->data [2] = sp->ins->data [1];
6468 mono_interp_stats.movlocs++;
6469 // Track what exactly is stored into local
6470 locals [dest_local].ins = ins;
6471 } else {
6472 locals [dest_local].ins = NULL;
6474 } else {
6475 locals [dest_local].ins = NULL;
6477 clear_stack_content_info_for_local (stack, sp, dest_local);
6478 clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
6479 } else {
6480 if (pop == MINT_POP_ALL)
6481 pop = sp - stack;
6482 sp += push - pop;
6483 g_assert (sp >= stack && sp <= stack_end);
6484 g_assert ((sp - push) >= stack && (sp - push) <= stack_end);
6485 memset (sp - push, 0, push * sizeof (StackContentInfo));
6487 // TODO handle dup
6488 last_il_offset = ins->il_offset;
6491 interp_local_deadce (td, local_ref_count);
6493 g_free (stack);
6494 g_free (locals);
6495 g_free (local_ref_count);
6498 static void
6499 interp_optimize_code (TransformData *td)
6501 if (mono_interp_opt & INTERP_OPT_CPROP)
6502 MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td));
6505 static void
6506 generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoGenericContext *generic_context, MonoError *error)
6508 MonoDomain *domain = rtm->domain;
6509 int i;
6510 TransformData transform_data;
6511 TransformData *td;
6512 static gboolean verbose_method_inited;
6513 static char* verbose_method_name;
6515 if (!verbose_method_inited) {
6516 verbose_method_name = g_getenv ("MONO_VERBOSE_METHOD");
6517 verbose_method_inited = TRUE;
6520 memset (&transform_data, 0, sizeof(transform_data));
6521 td = &transform_data;
6523 td->method = method;
6524 td->rtm = rtm;
6525 td->code_size = header->code_size;
6526 td->header = header;
6527 td->max_code_size = td->code_size;
6528 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
6529 td->stack_height = (int*)g_malloc(header->code_size * sizeof(int));
6530 td->stack_state = (StackInfo**)g_malloc0(header->code_size * sizeof(StackInfo *));
6531 td->vt_stack_size = (int*)g_malloc(header->code_size * sizeof(int));
6532 td->clause_indexes = (int*)g_malloc (header->code_size * sizeof (int));
6533 td->is_bb_start = (guint8*)g_malloc0(header->code_size);
6534 td->mempool = mono_mempool_new ();
6535 td->n_data_items = 0;
6536 td->max_data_items = 0;
6537 td->data_items = NULL;
6538 td->data_hash = g_hash_table_new (NULL, NULL);
6539 td->gen_sdb_seq_points = mini_debug_options.gen_sdb_seq_points;
6540 td->seq_points = g_ptr_array_new ();
6541 td->verbose_level = mono_interp_traceopt;
6542 rtm->data_items = td->data_items;
6544 interp_method_compute_offsets (td, rtm, mono_method_signature_internal (method), header);
6546 if (verbose_method_name) {
6547 const char *name = verbose_method_name;
6549 if ((strchr (name, '.') > name) || strchr (name, ':')) {
6550 MonoMethodDesc *desc;
6552 desc = mono_method_desc_new (name, TRUE);
6553 if (mono_method_desc_full_match (desc, method)) {
6554 td->verbose_level = 4;
6556 mono_method_desc_free (desc);
6557 } else {
6558 if (strcmp (method->name, name) == 0)
6559 td->verbose_level = 4;
6563 td->stack = (StackInfo*)g_malloc0 ((header->max_stack + 1) * sizeof (td->stack [0]));
6564 td->stack_capacity = header->max_stack + 1;
6565 td->sp = td->stack;
6566 td->max_stack_height = 0;
6567 td->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
6568 td->current_il_offset = -1;
6570 generate_code (td, method, header, generic_context, error);
6571 goto_if_nok (error, exit);
6573 interp_optimize_code (td);
6575 generate_compacted_code (td);
6577 if (td->verbose_level) {
6578 g_print ("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method, TRUE), rtm, td->max_vt_sp);
6579 g_print ("Calculated stack size: %d, stated size: %d\n", td->max_stack_height, header->max_stack);
6580 dump_mint_code (td->new_code, td->new_code_end);
6583 /* Check if we use excessive stack space */
6584 if (td->max_stack_height > header->max_stack * 3 && header->max_stack > 16)
6585 g_warning ("Excessive stack space usage for method %s, %d/%d", method->name, td->max_stack_height, header->max_stack);
6587 int code_len;
6588 code_len = td->new_code_end - td->new_code;
6590 rtm->clauses = (MonoExceptionClause*)mono_domain_alloc0 (domain, header->num_clauses * sizeof (MonoExceptionClause));
6591 memcpy (rtm->clauses, header->clauses, header->num_clauses * sizeof(MonoExceptionClause));
6592 rtm->code = (gushort*)td->new_code;
6593 rtm->init_locals = header->init_locals;
6594 rtm->num_clauses = header->num_clauses;
6595 for (i = 0; i < header->num_clauses; i++) {
6596 MonoExceptionClause *c = rtm->clauses + i;
6597 int end_off = c->try_offset + c->try_len;
6598 c->try_offset = get_in_offset (td, c->try_offset);
6599 c->try_len = find_in_offset (td, end_off) - c->try_offset;
6600 g_assert ((c->try_offset + c->try_len) < code_len);
6601 end_off = c->handler_offset + c->handler_len;
6602 c->handler_offset = get_in_offset (td, c->handler_offset);
6603 c->handler_len = find_in_offset (td, end_off) - c->handler_offset;
6604 g_assert (c->handler_len >= 0 && (c->handler_offset + c->handler_len) <= code_len);
6605 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER)
6606 c->data.filter_offset = get_in_offset (td, c->data.filter_offset);
6608 rtm->stack_size = (sizeof (stackval)) * (td->max_stack_height + 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/
6609 rtm->stack_size = ALIGN_TO (rtm->stack_size, MINT_VT_ALIGNMENT);
6610 rtm->vt_stack_size = td->max_vt_sp;
6611 rtm->total_locals_size = td->total_locals_size;
6612 rtm->alloca_size = rtm->total_locals_size + rtm->vt_stack_size + rtm->stack_size;
6613 rtm->data_items = (gpointer*)mono_domain_alloc0 (domain, td->n_data_items * sizeof (td->data_items [0]));
6614 memcpy (rtm->data_items, td->data_items, td->n_data_items * sizeof (td->data_items [0]));
6616 /* Save debug info */
6617 interp_save_debug_info (rtm, header, td, td->line_numbers);
6619 /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
6620 int jinfo_len;
6621 jinfo_len = mono_jit_info_size ((MonoJitInfoFlags)0, header->num_clauses, 0);
6622 MonoJitInfo *jinfo;
6623 jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len);
6624 jinfo->is_interp = 1;
6625 rtm->jinfo = jinfo;
6626 mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len, (MonoJitInfoFlags)0, header->num_clauses, 0);
6627 for (i = 0; i < jinfo->num_clauses; ++i) {
6628 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
6629 MonoExceptionClause *c = rtm->clauses + i;
6631 ei->flags = c->flags;
6632 ei->try_start = (guint8*)(rtm->code + c->try_offset);
6633 ei->try_end = (guint8*)(rtm->code + c->try_offset + c->try_len);
6634 ei->handler_start = (guint8*)(rtm->code + c->handler_offset);
6635 ei->exvar_offset = rtm->exvar_offsets [i];
6636 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
6637 ei->data.filter = (guint8*)(rtm->code + c->data.filter_offset);
6638 } else if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
6639 ei->data.handler_end = (guint8*)(rtm->code + c->handler_offset + c->handler_len);
6640 } else {
6641 ei->data.catch_class = c->data.catch_class;
6645 save_seq_points (td, jinfo);
6647 exit:
6648 g_free (td->in_offsets);
6649 g_free (td->stack_height);
6650 for (i = 0; i < header->code_size; ++i)
6651 g_free (td->stack_state [i]);
6652 g_free (td->stack_state);
6653 g_free (td->vt_stack_size);
6654 g_free (td->clause_indexes);
6655 g_free (td->data_items);
6656 g_free (td->stack);
6657 g_free (td->is_bb_start);
6658 g_free (td->locals);
6659 g_hash_table_destroy (td->data_hash);
6660 g_ptr_array_free (td->seq_points, TRUE);
6661 g_array_free (td->line_numbers, TRUE);
6662 mono_mempool_destroy (td->mempool);
6665 static mono_mutex_t calc_section;
6667 void
6668 mono_interp_transform_init (void)
6670 mono_os_mutex_init_recursive(&calc_section);
6673 void
6674 mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, MonoError *error)
6676 MonoMethod *method = imethod->method;
6677 MonoMethodHeader *header = NULL;
6678 MonoMethodSignature *signature = mono_method_signature_internal (method);
6679 MonoVTable *method_class_vt;
6680 MonoGenericContext *generic_context = NULL;
6681 MonoDomain *domain = imethod->domain;
6682 InterpMethod tmp_imethod;
6683 InterpMethod *real_imethod;
6685 error_init (error);
6687 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method->klass))) {
6688 mono_error_set_invalid_operation (error, "%s", "Could not execute the method because the containing type is not fully instantiated.");
6689 return;
6692 // g_printerr ("TRANSFORM(0x%016lx): begin %s::%s\n", mono_thread_current (), method->klass->name, method->name);
6693 method_class_vt = mono_class_vtable_checked (domain, imethod->method->klass, error);
6694 return_if_nok (error);
6696 if (!method_class_vt->initialized) {
6697 mono_runtime_class_init_full (method_class_vt, error);
6698 return_if_nok (error);
6701 MONO_PROFILER_RAISE (jit_begin, (method));
6703 if (mono_method_signature_internal (method)->is_inflated)
6704 generic_context = mono_method_get_context (method);
6705 else {
6706 MonoGenericContainer *generic_container = mono_method_get_generic_container (method);
6707 if (generic_container)
6708 generic_context = &generic_container->context;
6711 if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
6712 MonoMethod *nm = NULL;
6713 if (imethod->transformed) {
6714 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));
6715 return;
6718 /* assumes all internal calls with an array this are built in... */
6719 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL && (! mono_method_signature_internal (method)->hasthis || m_class_get_rank (method->klass) == 0)) {
6720 nm = mono_marshal_get_native_wrapper (method, FALSE, FALSE);
6721 signature = mono_method_signature_internal (nm);
6722 } else {
6723 const char *name = method->name;
6724 if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) {
6725 if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
6726 MonoJitICallInfo *mi = &mono_get_jit_icall_info ()->ves_icall_mono_delegate_ctor_interp;
6727 nm = mono_marshal_get_icall_wrapper (mi, TRUE);
6728 } else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
6730 * Usually handled during transformation of the caller, but
6731 * when the caller is handled by another execution engine
6732 * (for example fullAOT) we need to handle it here. That's
6733 * known to be wrong in cases where the reference to
6734 * `MonoDelegate` would be needed (FIXME).
6736 nm = mono_marshal_get_delegate_invoke (method, NULL);
6737 } else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
6738 nm = mono_marshal_get_delegate_begin_invoke (method);
6739 } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
6740 nm = mono_marshal_get_delegate_end_invoke (method);
6743 if (nm == NULL)
6744 g_assert_not_reached ();
6746 if (nm == NULL) {
6747 mono_os_mutex_lock (&calc_section);
6748 imethod->stack_size = sizeof (stackval); /* for tracing */
6749 imethod->alloca_size = imethod->stack_size;
6750 mono_memory_barrier ();
6751 imethod->transformed = TRUE;
6752 mono_os_mutex_unlock (&calc_section);
6753 MONO_PROFILER_RAISE (jit_done, (method, NULL));
6754 return;
6756 method = nm;
6757 header = interp_method_get_header (nm, error);
6758 return_if_nok (error);
6761 if (!header) {
6762 header = mono_method_get_header_checked (method, error);
6763 return_if_nok (error);
6766 g_assert ((signature->param_count + signature->hasthis) < 1000);
6767 // g_printerr ("TRANSFORM(0x%016lx): end %s::%s\n", mono_thread_current (), method->klass->name, method->name);
6769 /* Make modifications to a copy of imethod, copy them back inside the lock */
6770 real_imethod = imethod;
6771 memcpy (&tmp_imethod, imethod, sizeof (InterpMethod));
6772 imethod = &tmp_imethod;
6774 MONO_TIME_TRACK (mono_interp_stats.transform_time, generate (method, header, imethod, generic_context, error));
6776 mono_metadata_free_mh (header);
6778 return_if_nok (error);
6780 /* Copy changes back */
6781 imethod = real_imethod;
6782 mono_os_mutex_lock (&calc_section);
6783 if (!imethod->transformed) {
6784 // Ignore the first two fields which are unchanged. next_jit_code_hash shouldn't
6785 // be modified because it is racy with internal hash table insert.
6786 const int start_offset = 2 * sizeof (gpointer);
6787 memcpy ((char*)imethod + start_offset, (char*)&tmp_imethod + start_offset, sizeof (InterpMethod) - start_offset);
6788 mono_memory_barrier ();
6789 imethod->transformed = TRUE;
6790 mono_atomic_fetch_add_i32 (&mono_jit_stats.methods_with_interp, 1);
6793 mono_os_mutex_unlock (&calc_section);
6795 mono_domain_lock (domain);
6796 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, imethod->method))
6797 g_hash_table_insert (domain_jit_info (domain)->seq_points, imethod->method, imethod->jinfo->seq_points);
6798 mono_domain_unlock (domain);
6800 // FIXME: Add a different callback ?
6801 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));