[interp] Fix interp logging (#17636)
[mono-project.git] / mono / mini / interp / transform.c
blob5bd9f5477b4ace2dcbcf5503cddb123259398f85
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>
26 #include <mono/utils/mono-memory-model.h>
28 #include <mono/mini/mini.h>
29 #include <mono/mini/mini-runtime.h>
31 #include "mintops.h"
32 #include "interp-internals.h"
33 #include "interp.h"
35 #define INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK 1
36 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY 2
37 #define INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT 4
38 #define INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL 8
39 #define INTERP_INST_FLAG_RECORD_CALL_PATCH 16
41 #define INTERP_LOCAL_FLAG_INDIRECT 1
42 #define INTERP_LOCAL_FLAG_DEAD 2
44 MonoInterpStats mono_interp_stats;
46 #define DEBUG 0
48 typedef struct InterpInst InterpInst;
50 typedef struct
52 MonoClass *klass;
53 unsigned char type;
54 unsigned char flags;
55 } StackInfo;
57 #define STACK_VALUE_NONE 0
58 #define STACK_VALUE_LOCAL 1
59 #define STACK_VALUE_I4 2
60 #define STACK_VALUE_I8 3
62 // StackValue contains data to construct an InterpInst that is equivalent with the contents
63 // of the stack slot / local.
64 typedef struct {
65 // Indicates the type of the stored information. It can be a local or a constant
66 int type;
67 // Holds the local index or the actual constant value
68 union {
69 int local;
70 gint32 i;
71 gint64 l;
73 } StackValue;
75 typedef struct
77 // This indicates what is currently stored in this stack slot. This can be a constant
78 // or the copy of a local.
79 StackValue val;
80 // The instruction that pushed this stack slot. If ins is null, we can't remove the usage
81 // of the stack slot, because we can't clear the instruction that set it.
82 InterpInst *ins;
83 } StackContentInfo;
85 struct InterpInst {
86 guint16 opcode;
87 InterpInst *next, *prev;
88 // If this is -1, this instruction is not logically associated with an IL offset, it is
89 // part of the IL instruction associated with the previous interp instruction.
90 int il_offset;
91 guint32 flags;
92 guint16 data [MONO_ZERO_LEN_ARRAY];
95 typedef struct {
96 guint8 *ip;
97 GSList *preds;
98 GSList *seq_points;
99 SeqPoint *last_seq_point;
101 // This will hold a list of last sequence points of incoming basic blocks
102 SeqPoint **pred_seq_points;
103 guint num_pred_seq_points;
104 } InterpBasicBlock;
106 typedef enum {
107 RELOC_SHORT_BRANCH,
108 RELOC_LONG_BRANCH,
109 RELOC_SWITCH
110 } RelocType;
112 typedef struct {
113 RelocType type;
114 /* In the interpreter IR */
115 int offset;
116 /* In the IL code */
117 int target;
118 } Reloc;
120 typedef struct {
121 MonoType *type;
122 int mt;
123 int flags;
124 int offset;
125 } InterpLocal;
127 typedef struct
129 MonoMethod *method;
130 MonoMethod *inlined_method;
131 MonoMethodHeader *header;
132 InterpMethod *rtm;
133 const unsigned char *il_code;
134 const unsigned char *ip;
135 const unsigned char *in_start;
136 InterpInst *last_ins, *first_ins;
137 int code_size;
138 int *in_offsets;
139 int current_il_offset;
140 StackInfo **stack_state;
141 int *stack_height;
142 int *vt_stack_size;
143 unsigned char *is_bb_start;
144 unsigned short *new_code;
145 unsigned short *new_code_end;
146 unsigned int max_code_size;
147 StackInfo *stack;
148 StackInfo *sp;
149 unsigned int max_stack_height;
150 unsigned int stack_capacity;
151 unsigned int vt_sp;
152 unsigned int max_vt_sp;
153 unsigned int total_locals_size;
154 InterpLocal *locals;
155 unsigned int locals_size;
156 unsigned int locals_capacity;
157 int n_data_items;
158 int max_data_items;
159 void **data_items;
160 GHashTable *data_hash;
161 #ifdef ENABLE_EXPERIMENT_TIERED
162 GHashTable *patchsite_hash;
163 #endif
164 int *clause_indexes;
165 gboolean gen_sdb_seq_points;
166 GPtrArray *seq_points;
167 InterpBasicBlock **offset_to_bb;
168 InterpBasicBlock *entry_bb;
169 MonoMemPool *mempool;
170 GList *basic_blocks;
171 GPtrArray *relocs;
172 gboolean verbose_level;
173 GArray *line_numbers;
174 } TransformData;
176 #define STACK_TYPE_I4 0
177 #define STACK_TYPE_I8 1
178 #define STACK_TYPE_R4 2
179 #define STACK_TYPE_R8 3
180 #define STACK_TYPE_O 4
181 #define STACK_TYPE_VT 5
182 #define STACK_TYPE_MP 6
183 #define STACK_TYPE_F 7
185 static const char *stack_type_string [] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " };
187 #if SIZEOF_VOID_P == 8
188 #define STACK_TYPE_I STACK_TYPE_I8
189 #else
190 #define STACK_TYPE_I STACK_TYPE_I4
191 #endif
193 static int stack_type [] = {
194 STACK_TYPE_I4, /*I1*/
195 STACK_TYPE_I4, /*U1*/
196 STACK_TYPE_I4, /*I2*/
197 STACK_TYPE_I4, /*U2*/
198 STACK_TYPE_I4, /*I4*/
199 STACK_TYPE_I8, /*I8*/
200 STACK_TYPE_R4, /*R4*/
201 STACK_TYPE_R8, /*R8*/
202 STACK_TYPE_O, /*O*/
203 STACK_TYPE_MP, /*P*/
204 STACK_TYPE_VT
207 #if SIZEOF_VOID_P == 8
208 #define MINT_NEG_P MINT_NEG_I8
209 #define MINT_NOT_P MINT_NOT_I8
211 #define MINT_NEG_FP MINT_NEG_R8
213 #define MINT_ADD_P MINT_ADD_I8
214 #define MINT_SUB_P MINT_SUB_I8
215 #define MINT_MUL_P MINT_MUL_I8
216 #define MINT_DIV_P MINT_DIV_I8
217 #define MINT_DIV_UN_P MINT_DIV_UN_I8
218 #define MINT_REM_P MINT_REM_I8
219 #define MINT_REM_UN_P MINT_REM_UN_I8
220 #define MINT_AND_P MINT_AND_I8
221 #define MINT_OR_P MINT_OR_I8
222 #define MINT_XOR_P MINT_XOR_I8
223 #define MINT_SHL_P MINT_SHL_I8
224 #define MINT_SHR_P MINT_SHR_I8
225 #define MINT_SHR_UN_P MINT_SHR_UN_I8
227 #define MINT_CEQ_P MINT_CEQ_I8
228 #define MINT_CNE_P MINT_CNE_I8
229 #define MINT_CLT_P MINT_CLT_I8
230 #define MINT_CLT_UN_P MINT_CLT_UN_I8
231 #define MINT_CGT_P MINT_CGT_I8
232 #define MINT_CGT_UN_P MINT_CGT_UN_I8
233 #define MINT_CLE_P MINT_CLE_I8
234 #define MINT_CLE_UN_P MINT_CLE_UN_I8
235 #define MINT_CGE_P MINT_CGE_I8
236 #define MINT_CGE_UN_P MINT_CGE_UN_I8
238 #define MINT_ADD_FP MINT_ADD_R8
239 #define MINT_SUB_FP MINT_SUB_R8
240 #define MINT_MUL_FP MINT_MUL_R8
241 #define MINT_DIV_FP MINT_DIV_R8
242 #define MINT_REM_FP MINT_REM_R8
244 #define MINT_CNE_FP MINT_CNE_R8
245 #define MINT_CEQ_FP MINT_CEQ_R8
246 #define MINT_CGT_FP MINT_CGT_R8
247 #define MINT_CGE_FP MINT_CGE_R8
248 #define MINT_CLT_FP MINT_CLT_R8
249 #define MINT_CLE_FP MINT_CLE_R8
251 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I8
252 #else
254 #define MINT_NEG_P MINT_NEG_I4
255 #define MINT_NOT_P MINT_NOT_I4
257 #define MINT_NEG_FP MINT_NEG_R4
259 #define MINT_ADD_P MINT_ADD_I4
260 #define MINT_SUB_P MINT_SUB_I4
261 #define MINT_MUL_P MINT_MUL_I4
262 #define MINT_DIV_P MINT_DIV_I4
263 #define MINT_DIV_UN_P MINT_DIV_UN_I4
264 #define MINT_REM_P MINT_REM_I4
265 #define MINT_REM_UN_P MINT_REM_UN_I4
266 #define MINT_AND_P MINT_AND_I4
267 #define MINT_OR_P MINT_OR_I4
268 #define MINT_XOR_P MINT_XOR_I4
269 #define MINT_SHL_P MINT_SHL_I4
270 #define MINT_SHR_P MINT_SHR_I4
271 #define MINT_SHR_UN_P MINT_SHR_UN_I4
273 #define MINT_CEQ_P MINT_CEQ_I4
274 #define MINT_CNE_P MINT_CNE_I4
275 #define MINT_CLT_P MINT_CLT_I4
276 #define MINT_CLT_UN_P MINT_CLT_UN_I4
277 #define MINT_CGT_P MINT_CGT_I4
278 #define MINT_CGT_UN_P MINT_CGT_UN_I4
279 #define MINT_CLE_P MINT_CLE_I4
280 #define MINT_CLE_UN_P MINT_CLE_UN_I4
281 #define MINT_CGE_P MINT_CGE_I4
282 #define MINT_CGE_UN_P MINT_CGE_UN_I4
284 #define MINT_ADD_FP MINT_ADD_R4
285 #define MINT_SUB_FP MINT_SUB_R4
286 #define MINT_MUL_FP MINT_MUL_R4
287 #define MINT_DIV_FP MINT_DIV_R4
288 #define MINT_REM_FP MINT_REM_R4
290 #define MINT_CNE_FP MINT_CNE_R4
291 #define MINT_CEQ_FP MINT_CEQ_R4
292 #define MINT_CGT_FP MINT_CGT_R4
293 #define MINT_CGE_FP MINT_CGE_R4
294 #define MINT_CLT_FP MINT_CLT_R4
295 #define MINT_CLE_FP MINT_CLE_R4
297 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I4
298 #endif
300 typedef struct {
301 const gchar *op_name;
302 guint16 insn [3];
303 } MagicIntrinsic;
305 // static const MagicIntrinsic int_binop[] = {
307 static const MagicIntrinsic int_unnop[] = {
308 { "op_UnaryPlus", {MINT_NOP, MINT_NOP, MINT_NOP}},
309 { "op_UnaryNegation", {MINT_NEG_P, MINT_NEG_P, MINT_NEG_FP}},
310 { "op_OnesComplement", {MINT_NOT_P, MINT_NOT_P, MINT_NIY}}
313 static const MagicIntrinsic int_binop[] = {
314 { "op_Addition", {MINT_ADD_P, MINT_ADD_P, MINT_ADD_FP}},
315 { "op_Subtraction", {MINT_SUB_P, MINT_SUB_P, MINT_SUB_FP}},
316 { "op_Multiply", {MINT_MUL_P, MINT_MUL_P, MINT_MUL_FP}},
317 { "op_Division", {MINT_DIV_P, MINT_DIV_UN_P, MINT_DIV_FP}},
318 { "op_Modulus", {MINT_REM_P, MINT_REM_UN_P, MINT_REM_FP}},
319 { "op_BitwiseAnd", {MINT_AND_P, MINT_AND_P, MINT_NIY}},
320 { "op_BitwiseOr", {MINT_OR_P, MINT_OR_P, MINT_NIY}},
321 { "op_ExclusiveOr", {MINT_XOR_P, MINT_XOR_P, MINT_NIY}},
322 { "op_LeftShift", {MINT_SHL_P, MINT_SHL_P, MINT_NIY}},
323 { "op_RightShift", {MINT_SHR_P, MINT_SHR_UN_P, MINT_NIY}},
326 static const MagicIntrinsic int_cmpop[] = {
327 { "op_Inequality", {MINT_CNE_P, MINT_CNE_P, MINT_CNE_FP}},
328 { "op_Equality", {MINT_CEQ_P, MINT_CEQ_P, MINT_CEQ_FP}},
329 { "op_GreaterThan", {MINT_CGT_P, MINT_CGT_UN_P, MINT_CGT_FP}},
330 { "op_GreaterThanOrEqual", {MINT_CGE_P, MINT_CGE_UN_P, MINT_CGE_FP}},
331 { "op_LessThan", {MINT_CLT_P, MINT_CLT_UN_P, MINT_CLT_FP}},
332 { "op_LessThanOrEqual", {MINT_CLE_P, MINT_CLE_UN_P, MINT_CLE_FP}}
335 static gboolean generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error);
337 static InterpInst*
338 interp_new_ins (TransformData *td, guint16 opcode, int len)
340 InterpInst *new_inst;
341 // Size of data region of instruction is length of instruction minus 1 (the opcode slot)
342 new_inst = mono_mempool_alloc0 (td->mempool, sizeof (InterpInst) + sizeof (guint16) * ((len > 0) ? (len - 1) : 0));
343 new_inst->opcode = opcode;
344 new_inst->il_offset = td->current_il_offset;
345 return new_inst;
348 // This version need to be used with switch opcode, which doesn't have constant length
349 static InterpInst*
350 interp_add_ins_explicit (TransformData *td, guint16 opcode, int len)
352 InterpInst *new_inst = interp_new_ins (td, opcode, len);
353 new_inst->prev = td->last_ins;
354 if (td->last_ins)
355 td->last_ins->next = new_inst;
356 else
357 td->first_ins = new_inst;
358 td->last_ins = new_inst;
359 return new_inst;
362 static InterpInst*
363 interp_add_ins (TransformData *td, guint16 opcode)
365 return interp_add_ins_explicit (td, opcode, mono_interp_oplen [opcode]);
368 // This instruction will have the il_offset of the previous instruction
369 static InterpInst*
370 interp_insert_ins (TransformData *td, InterpInst *prev_ins, guint16 opcode)
372 InterpInst *new_inst = interp_new_ins (td, opcode, mono_interp_oplen [opcode]);
373 g_assert (prev_ins);
374 new_inst->il_offset = prev_ins->il_offset;
376 new_inst->prev = prev_ins;
377 new_inst->next = prev_ins->next;
378 prev_ins->next = new_inst;
380 if (new_inst->next == NULL)
381 td->last_ins = new_inst;
382 else
383 new_inst->next->prev = new_inst;
385 return new_inst;
388 static void
389 interp_clear_ins (TransformData *td, InterpInst *ins)
391 // Clearing instead of removing from the list makes everything easier.
392 // We don't change structure of the instruction list, we don't need
393 // to worry about updating the il_offset, or whether this instruction
394 // was at the start of a basic block etc.
395 ins->opcode = MINT_NOP;
398 static InterpInst*
399 interp_prev_ins (InterpInst *ins)
401 ins = ins->prev;
402 while (ins && ins->opcode == MINT_NOP)
403 ins = ins->prev;
404 return ins;
407 #define CHECK_STACK(td, n) \
408 do { \
409 int stack_size = (td)->sp - (td)->stack; \
410 if (stack_size < (n)) \
411 g_warning ("%s.%s: not enough values (%d < %d) on stack at %04x", \
412 m_class_get_name ((td)->method->klass), (td)->method->name, \
413 stack_size, n, (td)->ip - (td)->il_code); \
414 } while (0)
416 #define ENSURE_I4(td, sp_off) \
417 do { \
418 if ((td)->sp [-sp_off].type == STACK_TYPE_I8) \
419 interp_add_ins (td, sp_off == 1 ? MINT_CONV_I4_I8 : MINT_CONV_I4_I8_SP); \
420 } while (0)
422 #define CHECK_TYPELOAD(klass) \
423 do { \
424 if (!(klass) || mono_class_has_failure (klass)) { \
425 mono_error_set_for_class_failure (error, klass); \
426 goto exit; \
428 } while (0)
430 #if NO_UNALIGNED_ACCESS
431 #define WRITE32(ip, v) \
432 do { \
433 * (ip) = * (guint16 *)(v); \
434 * ((ip) + 1) = * ((guint16 *)(v) + 1); \
435 (ip) += 2; \
436 } while (0)
438 #define WRITE32_INS(ins, index, v) \
439 do { \
440 (ins)->data [index] = * (guint16 *)(v); \
441 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
442 } while (0)
444 #define WRITE64(ins, v) \
445 do { \
446 *((ins) + 0) = * ((guint16 *)(v) + 0); \
447 *((ins) + 1) = * ((guint16 *)(v) + 1); \
448 *((ins) + 2) = * ((guint16 *)(v) + 2); \
449 *((ins) + 3) = * ((guint16 *)(v) + 3); \
450 } while (0)
452 #define WRITE64_INS(ins, index, v) \
453 do { \
454 (ins)->data [index] = * (guint16 *)(v); \
455 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
456 (ins)->data [index + 2] = * ((guint16 *)(v) + 2); \
457 (ins)->data [index + 3] = * ((guint16 *)(v) + 3); \
458 } while (0)
459 #else
460 #define WRITE32(ip, v) \
461 do { \
462 * (guint32*)(ip) = * (guint32 *)(v); \
463 (ip) += 2; \
464 } while (0)
465 #define WRITE32_INS(ins, index, v) \
466 do { \
467 * (guint32 *)(&(ins)->data [index]) = * (guint32 *)(v); \
468 } while (0)
470 #define WRITE64(ip, v) \
471 do { \
472 * (guint64*)(ip) = * (guint64 *)(v); \
473 (ip) += 4; \
474 } while (0)
475 #define WRITE64_INS(ins, index, v) \
476 do { \
477 * (guint64 *)(&(ins)->data [index]) = * (guint64 *)(v); \
478 } while (0)
480 #endif
483 static void
484 handle_branch (TransformData *td, int short_op, int long_op, int offset)
486 int shorten_branch = 0;
487 int target = td->ip + offset - td->il_code;
488 if (target < 0 || target >= td->code_size)
489 g_assert_not_reached ();
490 /* Add exception checkpoint or safepoint for backward branches */
491 if (offset < 0) {
492 if (mono_threads_are_safepoints_enabled ())
493 interp_add_ins (td, MINT_SAFEPOINT);
494 else
495 interp_add_ins (td, MINT_CHECKPOINT);
497 if (offset > 0 && td->stack_height [target] < 0) {
498 td->stack_height [target] = td->sp - td->stack;
499 if (td->stack_height [target] > 0)
500 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, td->stack_height [target] * sizeof (td->stack [0]));
501 td->vt_stack_size [target] = td->vt_sp;
504 if (td->header->code_size <= 25000) /* FIX to be precise somehow? */
505 shorten_branch = 1;
507 if (shorten_branch) {
508 interp_add_ins (td, short_op);
509 td->last_ins->data [0] = (guint16) target;
510 } else {
511 interp_add_ins (td, long_op);
512 WRITE32_INS (td->last_ins, 0, &target);
516 static void
517 one_arg_branch(TransformData *td, int mint_op, int offset)
519 int type = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
520 int long_op = mint_op + type - STACK_TYPE_I4;
521 int short_op = long_op + MINT_BRFALSE_I4_S - MINT_BRFALSE_I4;
522 CHECK_STACK(td, 1);
523 --td->sp;
524 handle_branch (td, short_op, long_op, offset);
527 static void
528 two_arg_branch(TransformData *td, int mint_op, int offset)
530 int type1 = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
531 int type2 = td->sp [-2].type == STACK_TYPE_O || td->sp [-2].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-2].type;
532 int long_op = mint_op + type1 - STACK_TYPE_I4;
533 int short_op = long_op + MINT_BEQ_I4_S - MINT_BEQ_I4;
534 CHECK_STACK(td, 2);
535 if (type1 == STACK_TYPE_I4 && type2 == STACK_TYPE_I8) {
536 // The il instruction starts with the actual branch, and not with the conversion opcodes
537 interp_insert_ins (td, td->last_ins, MINT_CONV_I8_I4);
538 } else if (type1 == STACK_TYPE_I8 && type2 == STACK_TYPE_I4) {
539 interp_insert_ins (td, td->last_ins, MINT_CONV_I8_I4_SP);
540 } else if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
541 interp_insert_ins (td, td->last_ins, MINT_CONV_R8_R4);
542 } else if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
543 interp_insert_ins (td, td->last_ins, MINT_CONV_R8_R4_SP);
544 } else if (type1 != type2) {
545 g_warning("%s.%s: branch type mismatch %d %d",
546 m_class_get_name (td->method->klass), td->method->name,
547 td->sp [-1].type, td->sp [-2].type);
549 td->sp -= 2;
550 handle_branch (td, short_op, long_op, offset);
553 static void
554 unary_arith_op(TransformData *td, int mint_op)
556 int op = mint_op + td->sp [-1].type - STACK_TYPE_I4;
557 CHECK_STACK(td, 1);
558 interp_add_ins (td, op);
561 static void
562 binary_arith_op(TransformData *td, int mint_op)
564 int type1 = td->sp [-2].type;
565 int type2 = td->sp [-1].type;
566 int op;
567 #if SIZEOF_VOID_P == 8
568 if ((type1 == STACK_TYPE_MP || type1 == STACK_TYPE_I8) && type2 == STACK_TYPE_I4) {
569 interp_add_ins (td, MINT_CONV_I8_I4);
570 type2 = STACK_TYPE_I8;
572 if (type1 == STACK_TYPE_I4 && (type2 == STACK_TYPE_MP || type2 == STACK_TYPE_I8)) {
573 interp_add_ins (td, MINT_CONV_I8_I4_SP);
574 type1 = STACK_TYPE_I8;
575 td->sp [-2].type = STACK_TYPE_I8;
577 #endif
578 if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
579 interp_add_ins (td, MINT_CONV_R8_R4);
580 type2 = STACK_TYPE_R8;
582 if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
583 interp_add_ins (td, MINT_CONV_R8_R4_SP);
584 type1 = STACK_TYPE_R8;
585 td->sp [-2].type = STACK_TYPE_R8;
587 if (type1 == STACK_TYPE_MP)
588 type1 = STACK_TYPE_I;
589 if (type2 == STACK_TYPE_MP)
590 type2 = STACK_TYPE_I;
591 if (type1 != type2) {
592 g_warning("%s.%s: %04x arith type mismatch %s %d %d",
593 m_class_get_name (td->method->klass), td->method->name,
594 td->ip - td->il_code, mono_interp_opname (mint_op), type1, type2);
596 op = mint_op + type1 - STACK_TYPE_I4;
597 CHECK_STACK(td, 2);
598 interp_add_ins (td, op);
599 --td->sp;
602 static void
603 shift_op(TransformData *td, int mint_op)
605 int op = mint_op + td->sp [-2].type - STACK_TYPE_I4;
606 CHECK_STACK(td, 2);
607 if (td->sp [-1].type != STACK_TYPE_I4) {
608 g_warning("%s.%s: shift type mismatch %d",
609 m_class_get_name (td->method->klass), td->method->name,
610 td->sp [-2].type);
612 interp_add_ins (td, op);
613 --td->sp;
616 static int
617 can_store (int st_value, int vt_value)
619 if (st_value == STACK_TYPE_O || st_value == STACK_TYPE_MP)
620 st_value = STACK_TYPE_I;
621 if (vt_value == STACK_TYPE_O || vt_value == STACK_TYPE_MP)
622 vt_value = STACK_TYPE_I;
623 return st_value == vt_value;
626 #define SET_SIMPLE_TYPE(s, ty) \
627 do { \
628 (s)->type = (ty); \
629 (s)->flags = 0; \
630 (s)->klass = NULL; \
631 } while (0)
633 #define SET_TYPE(s, ty, k) \
634 do { \
635 (s)->type = (ty); \
636 (s)->flags = 0; \
637 (s)->klass = k; \
638 } while (0)
640 #define REALLOC_STACK(td, sppos) \
641 do { \
642 (td)->stack_capacity *= 2; \
643 (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
644 (td)->sp = (td)->stack + (sppos); \
645 } while (0);
647 #define PUSH_SIMPLE_TYPE(td, ty) \
648 do { \
649 int sp_height; \
650 (td)->sp++; \
651 sp_height = (td)->sp - (td)->stack; \
652 if (sp_height > (td)->max_stack_height) \
653 (td)->max_stack_height = sp_height; \
654 if (sp_height > (td)->stack_capacity) \
655 REALLOC_STACK(td, sp_height); \
656 SET_SIMPLE_TYPE((td)->sp - 1, ty); \
657 } while (0)
659 #define PUSH_TYPE(td, ty, k) \
660 do { \
661 int sp_height; \
662 (td)->sp++; \
663 sp_height = (td)->sp - (td)->stack; \
664 if (sp_height > (td)->max_stack_height) \
665 (td)->max_stack_height = sp_height; \
666 if (sp_height > (td)->stack_capacity) \
667 REALLOC_STACK(td, sp_height); \
668 SET_TYPE((td)->sp - 1, ty, k); \
669 } while (0)
671 static void
672 move_stack (TransformData *td, int start, int amount)
674 int sp_height = td->sp - td->stack;
675 int to_move = sp_height - start;
677 td->sp += amount;
678 sp_height += amount;
679 if (amount > 0) {
680 if (sp_height > td->max_stack_height)
681 td->max_stack_height = sp_height;
682 if (sp_height > td->stack_capacity)
683 REALLOC_STACK (td, sp_height);
684 } else {
685 g_assert (td->sp >= td->stack);
688 if (to_move > 0)
689 memmove (td->stack + start + amount, td->stack + start, to_move * sizeof (StackInfo));
692 #define PUSH_VT(td, size) \
693 do { \
694 (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
695 if ((td)->vt_sp > (td)->max_vt_sp) \
696 (td)->max_vt_sp = (td)->vt_sp; \
697 } while (0)
699 #define POP_VT(td, size) \
700 do { \
701 (td)->vt_sp -= ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
702 } while (0)
704 static MonoType*
705 get_arg_type_exact (TransformData *td, int n, int *mt)
707 MonoType *type;
708 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
710 if (hasthis && n == 0)
711 type = m_class_get_byval_arg (td->method->klass);
712 else
713 type = mono_method_signature_internal (td->method)->params [n - !!hasthis];
715 if (mt)
716 *mt = mint_type (type);
718 return type;
721 static void
722 load_arg(TransformData *td, int n)
724 int mt;
725 MonoClass *klass = NULL;
726 MonoType *type;
727 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
729 type = get_arg_type_exact (td, n, &mt);
731 if (mt == MINT_TYPE_VT) {
732 gint32 size;
733 klass = mono_class_from_mono_type_internal (type);
734 if (mono_method_signature_internal (td->method)->pinvoke)
735 size = mono_class_native_size (klass, NULL);
736 else
737 size = mono_class_value_size (klass, NULL);
739 if (hasthis && n == 0) {
740 mt = MINT_TYPE_P;
741 interp_add_ins (td, MINT_LDARG_P0);
742 klass = NULL;
743 } else {
744 PUSH_VT (td, size);
745 interp_add_ins (td, MINT_LDARG_VT);
746 td->last_ins->data [0] = n;
747 WRITE32_INS (td->last_ins, 1, &size);
749 } else {
750 if ((hasthis || mt == MINT_TYPE_P) && n == 0) {
751 mt = MINT_TYPE_P;
752 interp_add_ins (td, MINT_LDARG_P0);
753 } else {
754 interp_add_ins (td, MINT_LDARG_I1 + (mt - MINT_TYPE_I1));
755 td->last_ins->data [0] = n;
756 if (mt == MINT_TYPE_O)
757 klass = mono_class_from_mono_type_internal (type);
760 PUSH_TYPE(td, stack_type[mt], klass);
763 static void
764 store_arg(TransformData *td, int n)
766 int mt;
767 CHECK_STACK (td, 1);
768 MonoType *type;
770 type = get_arg_type_exact (td, n, &mt);
772 if (mt == MINT_TYPE_VT) {
773 gint32 size;
774 MonoClass *klass = mono_class_from_mono_type_internal (type);
775 if (mono_method_signature_internal (td->method)->pinvoke)
776 size = mono_class_native_size (klass, NULL);
777 else
778 size = mono_class_value_size (klass, NULL);
779 interp_add_ins (td, MINT_STARG_VT);
780 td->last_ins->data [0] = n;
781 WRITE32_INS (td->last_ins, 1, &size);
782 if (td->sp [-1].type == STACK_TYPE_VT)
783 POP_VT(td, size);
784 } else {
785 interp_add_ins (td, MINT_STARG_I1 + (mt - MINT_TYPE_I1));
786 td->last_ins->data [0] = n;
788 --td->sp;
791 static void
792 load_local (TransformData *td, int local)
794 MonoType *type = td->locals [local].type;
795 int mt = td->locals [local].mt;
796 MonoClass *klass = NULL;
797 if (mt == MINT_TYPE_VT) {
798 klass = mono_class_from_mono_type_internal (type);
799 gint32 size = mono_class_value_size (klass, NULL);
800 PUSH_VT(td, size);
801 interp_add_ins (td, MINT_LDLOC_VT);
802 td->last_ins->data [0] = local;
803 WRITE32_INS (td->last_ins, 1, &size);
804 } else {
805 g_assert (mt < MINT_TYPE_VT);
806 interp_add_ins (td, MINT_LDLOC_I1 + (mt - MINT_TYPE_I1));
807 td->last_ins->data [0] = local;
808 if (mt == MINT_TYPE_O)
809 klass = mono_class_from_mono_type_internal (type);
811 PUSH_TYPE(td, stack_type[mt], klass);
814 static void
815 store_local (TransformData *td, int local)
817 MonoType *type = td->locals [local].type;
818 int mt = td->locals [local].mt;
819 CHECK_STACK (td, 1);
820 #if SIZEOF_VOID_P == 8
821 if (td->sp [-1].type == STACK_TYPE_I4 && stack_type [mt] == STACK_TYPE_I8) {
822 interp_add_ins (td, MINT_CONV_I8_I4);
823 td->sp [-1].type = STACK_TYPE_I8;
825 #endif
826 if (!can_store(td->sp [-1].type, stack_type [mt])) {
827 g_warning("%s.%s: Store local stack type mismatch %d %d",
828 m_class_get_name (td->method->klass), td->method->name,
829 stack_type [mt], td->sp [-1].type);
831 if (mt == MINT_TYPE_VT) {
832 MonoClass *klass = mono_class_from_mono_type_internal (type);
833 gint32 size = mono_class_value_size (klass, NULL);
834 interp_add_ins (td, MINT_STLOC_VT);
835 td->last_ins->data [0] = local;
836 WRITE32_INS (td->last_ins, 1, &size);
837 if (td->sp [-1].type == STACK_TYPE_VT)
838 POP_VT(td, size);
839 } else {
840 g_assert (mt < MINT_TYPE_VT);
841 interp_add_ins (td, MINT_STLOC_I1 + (mt - MINT_TYPE_I1));
842 td->last_ins->data [0] = local;
844 --td->sp;
847 #define SIMPLE_OP(td, op) \
848 do { \
849 interp_add_ins (td, op); \
850 ++td->ip; \
851 } while (0)
853 static guint16
854 get_data_item_index (TransformData *td, void *ptr)
856 gpointer p = g_hash_table_lookup (td->data_hash, ptr);
857 guint index;
858 if (p != NULL)
859 return GPOINTER_TO_UINT (p) - 1;
860 if (td->max_data_items == td->n_data_items) {
861 td->max_data_items = td->n_data_items == 0 ? 16 : 2 * td->max_data_items;
862 td->data_items = (gpointer*)g_realloc (td->data_items, td->max_data_items * sizeof(td->data_items [0]));
864 index = td->n_data_items;
865 td->data_items [index] = ptr;
866 ++td->n_data_items;
867 g_hash_table_insert (td->data_hash, ptr, GUINT_TO_POINTER (index + 1));
868 return index;
871 static gboolean
872 jit_call_supported (MonoMethod *method, MonoMethodSignature *sig)
874 GSList *l;
876 if (sig->param_count > 6)
877 return FALSE;
878 if (sig->pinvoke)
879 return FALSE;
880 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
881 return FALSE;
882 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
883 return FALSE;
884 if (method->is_inflated)
885 return FALSE;
886 if (method->string_ctor)
887 return FALSE;
889 if (mono_aot_only && m_class_get_image (method->klass)->aot_module && !(method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) {
890 ERROR_DECL (error);
891 gpointer addr = mono_jit_compile_method_jit_only (method, error);
892 if (addr && is_ok (error))
893 return TRUE;
896 for (l = mono_interp_jit_classes; l; l = l->next) {
897 const char *class_name = (const char*)l->data;
898 // FIXME: Namespaces
899 if (!strcmp (m_class_get_name (method->klass), class_name))
900 return TRUE;
903 //return TRUE;
904 return FALSE;
907 #ifdef ENABLE_EXPERIMENT_TIERED
908 static gboolean
909 jit_call2_supported (MonoMethod *method, MonoMethodSignature *sig)
911 if (sig->param_count > 6)
912 return FALSE;
913 if (sig->pinvoke)
914 return FALSE;
915 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
916 return FALSE;
917 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
918 return FALSE;
919 if (method->is_inflated)
920 return FALSE;
921 if (method->string_ctor)
922 return FALSE;
924 return TRUE;
926 #endif
928 static int mono_class_get_magic_index (MonoClass *k)
930 if (mono_class_is_magic_int (k))
931 return !strcmp ("nint", m_class_get_name (k)) ? 0 : 1;
933 if (mono_class_is_magic_float (k))
934 return 2;
936 return -1;
939 static void
940 interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *target_method)
942 MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_method_access;
944 /* Inject code throwing MethodAccessException */
945 interp_add_ins (td, MINT_MONO_LDPTR);
946 td->last_ins->data [0] = get_data_item_index (td, method);
947 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
949 interp_add_ins (td, MINT_MONO_LDPTR);
950 td->last_ins->data [0] = get_data_item_index (td, target_method);
951 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
953 interp_add_ins (td, MINT_ICALL_PP_V);
954 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
956 td->sp -= 2;
959 static void
960 interp_generate_bie_throw (TransformData *td)
962 MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_bad_image;
964 interp_add_ins (td, MINT_ICALL_V_V);
965 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
969 * These are additional locals that can be allocated as we transform the code.
970 * They are allocated past the method locals so they are accessed in the same
971 * way, with an offset relative to the frame->locals.
973 static int
974 create_interp_local (TransformData *td, MonoType *type)
976 if (td->locals_size == td->locals_capacity) {
977 td->locals_capacity *= 2;
978 if (td->locals_capacity == 0)
979 td->locals_capacity = 2;
980 td->locals = (InterpLocal*) g_realloc (td->locals, td->locals_capacity * sizeof (InterpLocal));
982 td->locals [td->locals_size].type = type;
983 td->locals [td->locals_size].mt = mint_type (type);
984 td->locals [td->locals_size].flags = 0;
985 td->locals [td->locals_size].offset = -1;
986 td->locals_size++;
987 return td->locals_size - 1;
990 static int
991 get_interp_local_offset (TransformData *td, int local)
993 int align, size, offset;
995 if (td->locals [local].offset != -1)
996 return td->locals [local].offset;
998 offset = td->total_locals_size;
999 size = mono_type_size (td->locals [local].type, &align);
1000 offset = ALIGN_TO (offset, align);
1002 td->locals [local].offset = offset;
1004 td->total_locals_size = offset + size;
1005 g_assert (td->total_locals_size < G_MAXUINT16);
1007 return offset;
1010 static void
1011 dump_mint_code (const guint16 *start, const guint16* end)
1013 const guint16 *p = start;
1014 while (p < end) {
1015 char *ins = mono_interp_dis_mintop ((gint32)(p - start), TRUE, p + 1, *p);
1016 g_print ("%s\n", ins);
1017 g_free (ins);
1018 p = mono_interp_dis_mintop_len (p);
1022 static void
1023 dump_interp_inst (InterpInst *ins)
1025 char *descr = mono_interp_dis_mintop (ins->il_offset, FALSE, &ins->data [0], ins->opcode);
1026 g_print ("%s", descr);
1027 g_free (descr);
1030 static void
1031 dump_interp_inst_newline (InterpInst *ins)
1033 dump_interp_inst (ins);
1034 g_print ("\n");
1037 /* For debug use */
1038 void
1039 mono_interp_print_code (InterpMethod *imethod)
1041 MonoJitInfo *jinfo = imethod->jinfo;
1042 const guint8 *start;
1044 if (!jinfo)
1045 return;
1047 char *name = mono_method_full_name (imethod->method, 1);
1048 g_print ("Method : %s\n", name);
1049 g_free (name);
1051 start = (guint8*) jinfo->code_start;
1052 dump_mint_code ((const guint16*)start, (const guint16*)(start + jinfo->code_size));
1056 static MonoMethodHeader*
1057 interp_method_get_header (MonoMethod* method, MonoError *error)
1059 /* An explanation: mono_method_get_header_internal returns an error if
1060 * called on a method with no body (e.g. an abstract method, or an
1061 * icall). We don't want that.
1063 if (mono_method_has_no_body (method))
1064 return NULL;
1065 else
1066 return mono_method_get_header_internal (method, error);
1069 /* stores top of stack as local and pushes address of it on stack */
1070 static void
1071 emit_store_value_as_local (TransformData *td, MonoType *src)
1073 int size = mini_magic_type_size (NULL, src);
1074 int local = create_interp_local (td, mini_native_type_replace_type (src));
1076 store_local (td, local);
1078 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
1079 interp_add_ins (td, MINT_LDLOC_VT);
1080 td->last_ins->data [0] = local;
1081 WRITE32_INS (td->last_ins, 1, &size);
1083 PUSH_VT (td, size);
1084 PUSH_TYPE (td, STACK_TYPE_VT, NULL);
1087 // Returns whether we can optimize away the instructions starting at start.
1088 // If any instructions are part of a new basic block, we can't remove them.
1089 static gboolean
1090 interp_is_bb_start (TransformData *td, InterpInst *start, InterpInst *end)
1092 InterpInst *ins = start;
1093 while (ins != end) {
1094 if (ins->il_offset != -1) {
1095 if (td->is_bb_start [ins->il_offset])
1096 return TRUE;
1098 ins = ins->next;
1101 // Also check if end is bb start
1102 if (ins != NULL && ins->il_offset != -1 && td->is_bb_start [ins->il_offset])
1103 return TRUE;
1104 return FALSE;
1107 static gboolean
1108 interp_ins_is_ldc (InterpInst *ins)
1110 return ins->opcode >= MINT_LDC_I4_M1 && ins->opcode <= MINT_LDC_I8;
1113 static gint32
1114 interp_get_const_from_ldc_i4 (InterpInst *ins)
1116 switch (ins->opcode) {
1117 case MINT_LDC_I4_M1: return -1;
1118 case MINT_LDC_I4_0: return 0;
1119 case MINT_LDC_I4_1: return 1;
1120 case MINT_LDC_I4_2: return 2;
1121 case MINT_LDC_I4_3: return 3;
1122 case MINT_LDC_I4_4: return 4;
1123 case MINT_LDC_I4_5: return 5;
1124 case MINT_LDC_I4_6: return 6;
1125 case MINT_LDC_I4_7: return 7;
1126 case MINT_LDC_I4_8: return 8;
1127 case MINT_LDC_I4_S: return (gint32)(gint8)ins->data [0];
1128 case MINT_LDC_I4: return READ32 (&ins->data [0]);
1129 default:
1130 g_assert_not_reached ();
1134 /* If ins is not null, it will replace it with the ldc */
1135 static InterpInst*
1136 interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct)
1138 int opcode;
1139 switch (ct) {
1140 case -1: opcode = MINT_LDC_I4_M1; break;
1141 case 0: opcode = MINT_LDC_I4_0; break;
1142 case 1: opcode = MINT_LDC_I4_1; break;
1143 case 2: opcode = MINT_LDC_I4_2; break;
1144 case 3: opcode = MINT_LDC_I4_3; break;
1145 case 4: opcode = MINT_LDC_I4_4; break;
1146 case 5: opcode = MINT_LDC_I4_5; break;
1147 case 6: opcode = MINT_LDC_I4_6; break;
1148 case 7: opcode = MINT_LDC_I4_7; break;
1149 case 8: opcode = MINT_LDC_I4_8; break;
1150 default:
1151 if (ct >= -128 && ct <= 127)
1152 opcode = MINT_LDC_I4_S;
1153 else
1154 opcode = MINT_LDC_I4;
1155 break;
1158 int new_size = mono_interp_oplen [opcode];
1160 if (ins == NULL)
1161 ins = interp_add_ins (td, opcode);
1163 int ins_size = mono_interp_oplen [ins->opcode];
1164 if (ins_size < new_size) {
1165 // We can't replace the passed instruction, discard it and emit a new one
1166 ins = interp_insert_ins (td, ins, opcode);
1167 interp_clear_ins (td, ins->prev);
1168 } else {
1169 ins->opcode = opcode;
1172 if (new_size == 2)
1173 ins->data [0] = (gint8)ct;
1174 else if (new_size == 3)
1175 WRITE32_INS (ins, 0, &ct);
1177 return ins;
1180 static InterpInst*
1181 interp_inst_replace_with_i8_const (TransformData *td, InterpInst *ins, gint64 ct)
1183 int size = mono_interp_oplen [ins->opcode];
1185 if (size < 5) {
1186 ins = interp_insert_ins (td, ins, MINT_LDC_I8);
1187 interp_clear_ins (td, ins->prev);
1188 } else {
1189 ins->opcode = MINT_LDC_I8;
1191 WRITE64_INS (ins, 0, &ct);
1193 return ins;
1196 static int
1197 interp_get_ldind_for_mt (int mt)
1199 switch (mt) {
1200 case MINT_TYPE_I1: return MINT_LDIND_I1_CHECK;
1201 case MINT_TYPE_U1: return MINT_LDIND_U1_CHECK;
1202 case MINT_TYPE_I2: return MINT_LDIND_I2_CHECK;
1203 case MINT_TYPE_U2: return MINT_LDIND_U2_CHECK;
1204 case MINT_TYPE_I4: return MINT_LDIND_I4_CHECK;
1205 case MINT_TYPE_I8: return MINT_LDIND_I8_CHECK;
1206 case MINT_TYPE_R4: return MINT_LDIND_R4_CHECK;
1207 case MINT_TYPE_R8: return MINT_LDIND_R8_CHECK;
1208 case MINT_TYPE_O: return MINT_LDIND_REF;
1209 default:
1210 g_assert_not_reached ();
1212 return -1;
1215 static void
1216 interp_emit_ldobj (TransformData *td, MonoClass *klass)
1218 int mt = mint_type (m_class_get_byval_arg (klass));
1219 int size;
1221 if (mt == MINT_TYPE_VT) {
1222 interp_add_ins (td, MINT_LDOBJ_VT);
1223 size = mono_class_value_size (klass, NULL);
1224 WRITE32_INS (td->last_ins, 0, &size);
1225 PUSH_VT (td, size);
1226 } else {
1227 int opcode = interp_get_ldind_for_mt (mt);
1228 interp_add_ins (td, opcode);
1231 SET_TYPE (td->sp - 1, stack_type [mt], klass);
1234 static void
1235 interp_emit_stobj (TransformData *td, MonoClass *klass)
1237 int mt = mint_type (m_class_get_byval_arg (klass));
1239 if (mt == MINT_TYPE_VT) {
1240 int size;
1241 interp_add_ins (td, MINT_STOBJ_VT);
1242 td->last_ins->data [0] = get_data_item_index(td, klass);
1243 size = mono_class_value_size (klass, NULL);
1244 POP_VT (td, size);
1245 } else {
1246 int opcode;
1247 switch (mt) {
1248 case MINT_TYPE_I1:
1249 case MINT_TYPE_U1:
1250 opcode = MINT_STIND_I1;
1251 break;
1252 case MINT_TYPE_I2:
1253 case MINT_TYPE_U2:
1254 opcode = MINT_STIND_I2;
1255 break;
1256 case MINT_TYPE_I4:
1257 opcode = MINT_STIND_I4;
1258 break;
1259 case MINT_TYPE_I8:
1260 opcode = MINT_STIND_I8;
1261 break;
1262 case MINT_TYPE_R4:
1263 opcode = MINT_STIND_R4;
1264 break;
1265 case MINT_TYPE_R8:
1266 opcode = MINT_STIND_R8;
1267 break;
1268 case MINT_TYPE_O:
1269 opcode = MINT_STIND_REF;
1270 break;
1271 default: g_assert_not_reached (); break;
1273 interp_add_ins (td, opcode);
1275 td->sp -= 2;
1278 static void
1279 interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check_class)
1281 MonoClass *element_class = m_class_get_element_class (array_class);
1282 int rank = m_class_get_rank (array_class);
1283 int size = mono_class_array_element_size (element_class);
1285 // We only need type checks when writing to array of references
1286 if (!check_class || m_class_is_valuetype (element_class)) {
1287 if (rank == 1) {
1288 interp_add_ins (td, MINT_LDELEMA1);
1289 WRITE32_INS (td->last_ins, 0, &size);
1290 } else {
1291 interp_add_ins (td, MINT_LDELEMA);
1292 td->last_ins->data [0] = rank;
1293 WRITE32_INS (td->last_ins, 1, &size);
1295 } else {
1296 interp_add_ins (td, MINT_LDELEMA_TC);
1297 td->last_ins->data [0] = rank;
1298 td->last_ins->data [1] = get_data_item_index (td, check_class);
1301 td->sp -= rank;
1302 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1305 /* Return TRUE if call transformation is finished */
1306 static gboolean
1307 interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClass *constrained_class, MonoMethodSignature *csignature, gboolean readonly, int *op)
1309 const char *tm = target_method->name;
1310 int i;
1311 int type_index = mono_class_get_magic_index (target_method->klass);
1312 gboolean in_corlib = m_class_get_image (target_method->klass) == mono_defaults.corlib;
1313 const char *klass_name_space = m_class_get_name_space (target_method->klass);
1314 const char *klass_name = m_class_get_name (target_method->klass);
1316 if (target_method->klass == mono_defaults.string_class) {
1317 if (tm [0] == 'g') {
1318 if (strcmp (tm, "get_Chars") == 0)
1319 *op = MINT_GETCHR;
1320 else if (strcmp (tm, "get_Length") == 0)
1321 *op = MINT_STRLEN;
1323 } else if (type_index >= 0) {
1324 MonoClass *magic_class = target_method->klass;
1326 const int mt = mint_type (m_class_get_byval_arg (magic_class));
1327 if (!strcmp (".ctor", tm)) {
1328 MonoType *arg = csignature->params [0];
1329 /* depending on SIZEOF_VOID_P and the type of the value passed to the .ctor we either have to CONV it, or do nothing */
1330 int arg_size = mini_magic_type_size (NULL, arg);
1332 if (arg_size > SIZEOF_VOID_P) { // 8 -> 4
1333 switch (type_index) {
1334 case 0: case 1:
1335 interp_add_ins (td, MINT_CONV_I4_I8);
1336 break;
1337 case 2:
1338 interp_add_ins (td, MINT_CONV_R4_R8);
1339 break;
1343 if (arg_size < SIZEOF_VOID_P) { // 4 -> 8
1344 switch (type_index) {
1345 case 0:
1346 interp_add_ins (td, MINT_CONV_I8_I4);
1347 break;
1348 case 1:
1349 interp_add_ins (td, MINT_CONV_I8_U4);
1350 break;
1351 case 2:
1352 interp_add_ins (td, MINT_CONV_R8_R4);
1353 break;
1357 switch (type_index) {
1358 case 0: case 1:
1359 #if SIZEOF_VOID_P == 4
1360 interp_add_ins (td, MINT_STIND_I4);
1361 #else
1362 interp_add_ins (td, MINT_STIND_I8);
1363 #endif
1364 break;
1365 case 2:
1366 #if SIZEOF_VOID_P == 4
1367 interp_add_ins (td, MINT_STIND_R4);
1368 #else
1369 interp_add_ins (td, MINT_STIND_R8);
1370 #endif
1371 break;
1374 td->sp -= 2;
1375 td->ip += 5;
1376 return TRUE;
1377 } else if (!strcmp ("op_Implicit", tm ) || !strcmp ("op_Explicit", tm)) {
1378 MonoType *src = csignature->params [0];
1379 MonoType *dst = csignature->ret;
1380 MonoClass *src_klass = mono_class_from_mono_type_internal (src);
1381 int src_size = mini_magic_type_size (NULL, src);
1382 int dst_size = mini_magic_type_size (NULL, dst);
1384 gboolean store_value_as_local = FALSE;
1386 switch (type_index) {
1387 case 0: case 1:
1388 if (!mini_magic_is_int_type (src) || !mini_magic_is_int_type (dst)) {
1389 if (mini_magic_is_int_type (src))
1390 store_value_as_local = TRUE;
1391 else if (mono_class_is_magic_float (src_klass))
1392 store_value_as_local = TRUE;
1393 else
1394 return FALSE;
1396 break;
1397 case 2:
1398 if (!mini_magic_is_float_type (src) || !mini_magic_is_float_type (dst)) {
1399 if (mini_magic_is_float_type (src))
1400 store_value_as_local = TRUE;
1401 else if (mono_class_is_magic_int (src_klass))
1402 store_value_as_local = TRUE;
1403 else
1404 return FALSE;
1406 break;
1409 if (store_value_as_local) {
1410 emit_store_value_as_local (td, src);
1412 /* emit call to managed conversion method */
1413 return FALSE;
1416 if (src_size > dst_size) { // 8 -> 4
1417 switch (type_index) {
1418 case 0: case 1:
1419 interp_add_ins (td, MINT_CONV_I4_I8);
1420 break;
1421 case 2:
1422 interp_add_ins (td, MINT_CONV_R4_R8);
1423 break;
1427 if (src_size < dst_size) { // 4 -> 8
1428 switch (type_index) {
1429 case 0:
1430 interp_add_ins (td, MINT_CONV_I8_I4);
1431 break;
1432 case 1:
1433 interp_add_ins (td, MINT_CONV_I8_U4);
1434 break;
1435 case 2:
1436 interp_add_ins (td, MINT_CONV_R8_R4);
1437 break;
1441 SET_TYPE (td->sp - 1, stack_type [mint_type (dst)], mono_class_from_mono_type_internal (dst));
1442 td->ip += 5;
1443 return TRUE;
1444 } else if (!strcmp ("op_Increment", tm)) {
1445 g_assert (type_index != 2); // no nfloat
1446 #if SIZEOF_VOID_P == 8
1447 interp_add_ins (td, MINT_ADD1_I8);
1448 #else
1449 interp_add_ins (td, MINT_ADD1_I4);
1450 #endif
1451 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1452 td->ip += 5;
1453 return TRUE;
1454 } else if (!strcmp ("op_Decrement", tm)) {
1455 g_assert (type_index != 2); // no nfloat
1456 #if SIZEOF_VOID_P == 8
1457 interp_add_ins (td, MINT_SUB1_I8);
1458 #else
1459 interp_add_ins (td, MINT_SUB1_I4);
1460 #endif
1461 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1462 td->ip += 5;
1463 return TRUE;
1464 } else if (!strcmp ("CompareTo", tm) || !strcmp ("Equals", tm)) {
1465 MonoType *arg = csignature->params [0];
1467 /* on 'System.n*::{CompareTo,Equals} (System.n*)' variant we need to push managed
1468 * pointer instead of value */
1469 if (arg->type == MONO_TYPE_VALUETYPE)
1470 emit_store_value_as_local (td, arg);
1472 /* emit call to managed conversion method */
1473 return FALSE;
1474 } else if (!strcmp (".cctor", tm)) {
1475 /* white list */
1476 return FALSE;
1477 } else if (!strcmp ("Parse", tm)) {
1478 /* white list */
1479 return FALSE;
1480 } else if (!strcmp ("ToString", tm)) {
1481 /* white list */
1482 return FALSE;
1483 } else if (!strcmp ("GetHashCode", tm)) {
1484 /* white list */
1485 return FALSE;
1486 } else if (!strcmp ("IsNaN", tm) || !strcmp ("IsInfinity", tm) || !strcmp ("IsNegativeInfinity", tm) || !strcmp ("IsPositiveInfinity", tm)) {
1487 g_assert (type_index == 2); // nfloat only
1488 /* white list */
1489 return FALSE;
1492 for (i = 0; i < sizeof (int_unnop) / sizeof (MagicIntrinsic); ++i) {
1493 if (!strcmp (int_unnop [i].op_name, tm)) {
1494 interp_add_ins (td, int_unnop [i].insn [type_index]);
1495 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1496 td->ip += 5;
1497 return TRUE;
1501 for (i = 0; i < sizeof (int_binop) / sizeof (MagicIntrinsic); ++i) {
1502 if (!strcmp (int_binop [i].op_name, tm)) {
1503 interp_add_ins (td, int_binop [i].insn [type_index]);
1504 td->sp -= 1;
1505 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1506 td->ip += 5;
1507 return TRUE;
1511 for (i = 0; i < sizeof (int_cmpop) / sizeof (MagicIntrinsic); ++i) {
1512 if (!strcmp (int_cmpop [i].op_name, tm)) {
1513 MonoClass *k = mono_defaults.boolean_class;
1514 interp_add_ins (td, int_cmpop [i].insn [type_index]);
1515 td->sp -= 1;
1516 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1517 td->ip += 5;
1518 return TRUE;
1522 g_error ("TODO: interp_transform_call %s:%s", m_class_get_name (target_method->klass), tm);
1523 } else if (mono_class_is_subclass_of_internal (target_method->klass, mono_defaults.array_class, FALSE)) {
1524 if (!strcmp (tm, "get_Rank")) {
1525 *op = MINT_ARRAY_RANK;
1526 } else if (!strcmp (tm, "get_Length")) {
1527 *op = MINT_LDLEN;
1528 } else if (!strcmp (tm, "GetElementSize")) {
1529 *op = MINT_ARRAY_ELEMENT_SIZE;
1530 } else if (!strcmp (tm, "Address")) {
1531 MonoClass *check_class = readonly ? NULL : m_class_get_element_class (target_method->klass);
1532 interp_emit_ldelema (td, target_method->klass, check_class);
1533 td->ip += 5;
1534 return TRUE;
1535 #ifndef ENABLE_NETCORE
1536 } else if (!strcmp (tm, "UnsafeMov") || !strcmp (tm, "UnsafeLoad")) {
1537 *op = MINT_CALLRUN;
1538 #endif
1539 } else if (!strcmp (tm, "Get")) {
1540 interp_emit_ldelema (td, target_method->klass, NULL);
1541 interp_emit_ldobj (td, m_class_get_element_class (target_method->klass));
1542 td->ip += 5;
1543 return TRUE;
1544 } else if (!strcmp (tm, "Set")) {
1545 MonoClass *element_class = m_class_get_element_class (target_method->klass);
1546 MonoType *local_type = m_class_get_byval_arg (element_class);
1547 MonoClass *value_class = td->sp [-1].klass;
1548 // If value_class is NULL it means the top of stack is a simple type (valuetype)
1549 // which doesn't require type checks, or that we have no type information because
1550 // the code is unsafe (like in some wrappers). In that case we assume the type
1551 // of the array and don't do any checks.
1553 int local = create_interp_local (td, local_type);
1555 store_local (td, local);
1556 interp_emit_ldelema (td, target_method->klass, value_class);
1557 load_local (td, local);
1558 interp_emit_stobj (td, element_class);
1559 td->ip += 5;
1560 return TRUE;
1561 } else if (!strcmp (tm, "UnsafeStore")) {
1562 g_error ("TODO ArrayClass::UnsafeStore");
1564 } else if (in_corlib &&
1565 !strcmp (klass_name_space, "System.Diagnostics") &&
1566 !strcmp (klass_name, "Debugger")) {
1567 if (!strcmp (tm, "Break") && csignature->param_count == 0) {
1568 if (mini_should_insert_breakpoint (td->method))
1569 *op = MINT_BREAK;
1571 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "ByReference`1")) {
1572 g_assert (!strcmp (tm, "get_Value"));
1573 *op = MINT_INTRINS_BYREFERENCE_GET_VALUE;
1574 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "Math") && csignature->param_count == 1 && csignature->params [0]->type == MONO_TYPE_R8) {
1575 if (tm [0] == 'A') {
1576 if (strcmp (tm, "Abs") == 0 && csignature->params [0]->type == MONO_TYPE_R8) {
1577 *op = MINT_ABS;
1578 } else if (strcmp (tm, "Asin") == 0){
1579 *op = MINT_ASIN;
1580 } else if (strcmp (tm, "Asinh") == 0){
1581 *op = MINT_ASINH;
1582 } else if (strcmp (tm, "Acos") == 0){
1583 *op = MINT_ACOS;
1584 } else if (strcmp (tm, "Acosh") == 0){
1585 *op = MINT_ACOSH;
1586 } else if (strcmp (tm, "Atan") == 0){
1587 *op = MINT_ATAN;
1588 } else if (strcmp (tm, "Atanh") == 0){
1589 *op = MINT_ATANH;
1591 } else if (tm [0] == 'C') {
1592 if (strcmp (tm, "Cos") == 0) {
1593 *op = MINT_COS;
1594 } else if (strcmp (tm, "Cbrt") == 0){
1595 *op = MINT_CBRT;
1596 } else if (strcmp (tm, "Cosh") == 0){
1597 *op = MINT_COSH;
1599 } else if (tm [0] == 'S') {
1600 if (strcmp (tm, "Sin") == 0) {
1601 *op = MINT_SIN;
1602 } else if (strcmp (tm, "Sqrt") == 0) {
1603 *op = MINT_SQRT;
1604 } else if (strcmp (tm, "Sinh") == 0){
1605 *op = MINT_SINH;
1607 } else if (tm [0] == 'T') {
1608 if (strcmp (tm, "Tan") == 0) {
1609 *op = MINT_TAN;
1610 } else if (strcmp (tm, "Tanh") == 0){
1611 *op = MINT_TANH;
1614 } else if (in_corlib && !strcmp (klass_name_space, "System") && (!strcmp (klass_name, "Span`1") || !strcmp (klass_name, "ReadOnlySpan`1"))) {
1615 if (!strcmp (tm, "get_Item")) {
1616 MonoGenericClass *gclass = mono_class_get_generic_class (target_method->klass);
1617 MonoClass *param_class = mono_class_from_mono_type_internal (gclass->context.class_inst->type_argv [0]);
1619 if (!mini_is_gsharedvt_variable_klass (param_class)) {
1620 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1621 g_assert (length_field);
1622 int offset_length = length_field->offset - sizeof (MonoObject);
1624 MonoClassField *ptr_field = mono_class_get_field_from_name_full (target_method->klass, "_pointer", NULL);
1625 g_assert (ptr_field);
1626 int offset_pointer = ptr_field->offset - sizeof (MonoObject);
1628 int size = mono_class_array_element_size (param_class);
1629 interp_add_ins (td, MINT_GETITEM_SPAN);
1630 td->last_ins->data [0] = size;
1631 td->last_ins->data [1] = offset_length;
1632 td->last_ins->data [2] = offset_pointer;
1634 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1635 td->sp -= 1;
1636 td->ip += 5;
1637 return TRUE;
1639 } else if (!strcmp (tm, "get_Length")) {
1640 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1641 g_assert (length_field);
1642 int offset_length = length_field->offset - sizeof (MonoObject);
1643 interp_add_ins (td, MINT_LDLEN_SPAN);
1644 td->last_ins->data [0] = offset_length;
1645 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1646 td->ip += 5;
1647 return TRUE;
1649 } else if (in_corlib && !strcmp (klass_name_space, "Internal.Runtime.CompilerServices") && !strcmp (klass_name, "Unsafe")) {
1650 #ifdef ENABLE_NETCORE
1651 if (!strcmp (tm, "AddByteOffset"))
1652 *op = MINT_INTRINS_UNSAFE_ADD_BYTE_OFFSET;
1653 else if (!strcmp (tm, "ByteOffset"))
1654 *op = MINT_INTRINS_UNSAFE_BYTE_OFFSET;
1655 else if (!strcmp (tm, "As") || !strcmp (tm, "AsRef"))
1656 *op = MINT_NOP;
1657 else if (!strcmp (tm, "AsPointer")) {
1658 /* NOP */
1659 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1660 td->ip += 5;
1661 return TRUE;
1662 } else if (!strcmp (tm, "IsAddressLessThan")) {
1663 MonoGenericContext *ctx = mono_method_get_context (target_method);
1664 g_assert (ctx);
1665 g_assert (ctx->method_inst);
1666 g_assert (ctx->method_inst->type_argc == 1);
1668 MonoClass *k = mono_defaults.boolean_class;
1669 interp_add_ins (td, MINT_CLT_UN_P);
1670 td->sp -= 1;
1671 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1672 td->ip += 5;
1673 return TRUE;
1674 } else if (!strcmp (tm, "SizeOf")) {
1675 MonoGenericContext *ctx = mono_method_get_context (target_method);
1676 g_assert (ctx);
1677 g_assert (ctx->method_inst);
1678 g_assert (ctx->method_inst->type_argc == 1);
1679 MonoType *t = ctx->method_inst->type_argv [0];
1680 int align;
1681 int esize = mono_type_size (t, &align);
1682 interp_add_ins (td, MINT_LDC_I4);
1683 WRITE32_INS (td->last_ins, 0, &esize);
1684 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1685 td->ip += 5;
1686 return TRUE;
1687 } else if (!strcmp (tm, "AreSame")) {
1688 *op = MINT_CEQ_P;
1689 } else if (!strcmp (tm, "SkipInit")) {
1690 *op = MINT_POP;
1692 #endif
1693 } else if (in_corlib && !strcmp (klass_name_space, "System.Runtime.CompilerServices") && !strcmp (klass_name, "RuntimeHelpers")) {
1694 #ifdef ENABLE_NETCORE
1695 if (!strcmp (tm, "IsBitwiseEquatable")) {
1696 g_assert (csignature->param_count == 0);
1697 MonoGenericContext *ctx = mono_method_get_context (target_method);
1698 g_assert (ctx);
1699 g_assert (ctx->method_inst);
1700 g_assert (ctx->method_inst->type_argc == 1);
1701 MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]);
1703 if (MONO_TYPE_IS_PRIMITIVE (t) && t->type != MONO_TYPE_R4 && t->type != MONO_TYPE_R8)
1704 *op = MINT_LDC_I4_1;
1705 else
1706 *op = MINT_LDC_I4_0;
1707 } else if (!strcmp (tm, "ObjectHasComponentSize")) {
1708 *op = MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE;
1710 #endif
1711 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "RuntimeMethodHandle") && !strcmp (tm, "GetFunctionPointer") && csignature->param_count == 1) {
1712 // We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter
1713 *op = MINT_LDFTN_DYNAMIC;
1714 } else if (in_corlib && target_method->klass == mono_defaults.object_class) {
1715 if (!strcmp (tm, "InternalGetHashCode"))
1716 *op = MINT_INTRINS_GET_HASHCODE;
1717 #ifdef DISABLE_REMOTING
1718 else if (!strcmp (tm, "GetType"))
1719 *op = MINT_INTRINS_GET_TYPE;
1720 #endif
1721 #ifdef ENABLE_NETCORE
1722 else if (!strcmp (tm, "GetRawData")) {
1723 #if SIZEOF_VOID_P == 8
1724 interp_add_ins (td, MINT_LDC_I8_S);
1725 #else
1726 interp_add_ins (td, MINT_LDC_I4_S);
1727 #endif
1728 td->last_ins->data [0] = (gint16) MONO_ABI_SIZEOF (MonoObject);
1730 interp_add_ins (td, MINT_ADD_P);
1731 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1733 td->ip += 5;
1734 return TRUE;
1736 #endif
1737 } else if (in_corlib && target_method->klass == mono_defaults.enum_class && !strcmp (tm, "HasFlag")) {
1738 gboolean intrinsify = FALSE;
1739 MonoClass *base_klass = NULL;
1740 if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1741 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1742 td->last_ins->prev->prev && td->last_ins->prev->prev->opcode == MINT_BOX &&
1743 td->sp [-2].klass == td->sp [-1].klass &&
1744 !interp_is_bb_start (td, td->last_ins->prev->prev, NULL) &&
1745 !td->is_bb_start [td->in_start - td->il_code]) {
1746 // csc pattern : box, ldc, box, call HasFlag
1747 g_assert (m_class_is_enumtype (td->sp [-2].klass));
1748 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (td->sp [-2].klass));
1749 base_klass = mono_class_from_mono_type_internal (base_type);
1751 // Remove the boxing of valuetypes
1752 interp_clear_ins (td, td->last_ins->prev->prev);
1753 interp_clear_ins (td, td->last_ins);
1755 intrinsify = TRUE;
1756 } else if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1757 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1758 constrained_class && td->sp [-1].klass == constrained_class &&
1759 !interp_is_bb_start (td, td->last_ins->prev, NULL) &&
1760 !td->is_bb_start [td->in_start - td->il_code]) {
1761 // mcs pattern : ldc, box, constrained Enum, call HasFlag
1762 g_assert (m_class_is_enumtype (constrained_class));
1763 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (constrained_class));
1764 base_klass = mono_class_from_mono_type_internal (base_type);
1765 int mt = mint_type (m_class_get_byval_arg (base_klass));
1767 // Remove boxing and load the value of this
1768 interp_clear_ins (td, td->last_ins);
1769 interp_insert_ins (td, td->last_ins->prev->prev, interp_get_ldind_for_mt (mt));
1771 intrinsify = TRUE;
1773 if (intrinsify) {
1774 interp_add_ins (td, MINT_INTRINS_ENUM_HASFLAG);
1775 td->last_ins->data [0] = get_data_item_index (td, base_klass);
1776 td->sp -= 2;
1777 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1778 td->ip += 5;
1779 return TRUE;
1781 } else if (in_corlib && !strcmp (klass_name_space, "System.Threading") && !strcmp (klass_name, "Interlocked")) {
1782 if (!strcmp (tm, "MemoryBarrier") && csignature->param_count == 0)
1783 *op = MINT_MONO_MEMORY_BARRIER;
1784 } else if (in_corlib && !strcmp (klass_name_space, "System.Threading") && !strcmp (klass_name, "Thread")) {
1785 if (!strcmp (tm, "MemoryBarrier") && csignature->param_count == 0)
1786 *op = MINT_MONO_MEMORY_BARRIER;
1787 } else if (in_corlib &&
1788 !strcmp (klass_name_space, "System.Runtime.CompilerServices") &&
1789 !strcmp (klass_name, "JitHelpers") &&
1790 (!strcmp (tm, "EnumEquals") || !strcmp (tm, "EnumCompareTo"))) {
1791 MonoGenericContext *ctx = mono_method_get_context (target_method);
1792 g_assert (ctx);
1793 g_assert (ctx->method_inst);
1794 g_assert (ctx->method_inst->type_argc == 1);
1795 g_assert (csignature->param_count == 2);
1797 MonoType *t = ctx->method_inst->type_argv [0];
1798 t = mini_get_underlying_type (t);
1800 gboolean is_i8 = (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_U8);
1801 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);
1803 gboolean is_compareto = strcmp (tm, "EnumCompareTo") == 0;
1804 if (is_compareto) {
1805 int locala, localb;
1806 locala = create_interp_local (td, t);
1807 localb = create_interp_local (td, t);
1809 // Save arguments
1810 store_local (td, localb);
1811 store_local (td, locala);
1812 // (a > b)
1813 load_local (td, locala);
1814 load_local (td, localb);
1815 if (is_unsigned)
1816 interp_add_ins (td, is_i8 ? MINT_CGT_UN_I8 : MINT_CGT_UN_I4);
1817 else
1818 interp_add_ins (td, is_i8 ? MINT_CGT_I8 : MINT_CGT_I4);
1819 td->sp --;
1820 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1821 // (a < b)
1822 load_local (td, locala);
1823 load_local (td, localb);
1824 if (is_unsigned)
1825 interp_add_ins (td, is_i8 ? MINT_CLT_UN_I8 : MINT_CLT_UN_I4);
1826 else
1827 interp_add_ins (td, is_i8 ? MINT_CLT_I8 : MINT_CLT_I4);
1828 td->sp --;
1829 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1830 // (a > b) - (a < b)
1831 interp_add_ins (td, MINT_SUB_I4);
1832 td->sp --;
1833 td->ip += 5;
1834 return TRUE;
1835 } else {
1836 if (is_i8) {
1837 *op = MINT_CEQ_I8;
1838 } else {
1839 *op = MINT_CEQ_I4;
1844 return FALSE;
1847 static MonoMethod*
1848 interp_transform_internal_calls (MonoMethod *method, MonoMethod *target_method, MonoMethodSignature *csignature, gboolean is_virtual)
1850 if (method->wrapper_type == MONO_WRAPPER_NONE && target_method != NULL) {
1851 if (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
1852 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1853 if (!is_virtual && target_method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
1854 target_method = mono_marshal_get_synchronized_wrapper (target_method);
1856 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)
1857 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1859 return target_method;
1862 static gboolean
1863 interp_type_as_ptr (MonoType *tp)
1865 if (MONO_TYPE_IS_POINTER (tp))
1866 return TRUE;
1867 if (MONO_TYPE_IS_REFERENCE (tp))
1868 return TRUE;
1869 if ((tp)->type == MONO_TYPE_I4)
1870 return TRUE;
1871 #if SIZEOF_VOID_P == 8
1872 if ((tp)->type == MONO_TYPE_I8)
1873 return TRUE;
1874 #endif
1875 if ((tp)->type == MONO_TYPE_BOOLEAN)
1876 return TRUE;
1877 if ((tp)->type == MONO_TYPE_CHAR)
1878 return TRUE;
1879 if ((tp)->type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (tp->data.klass))
1880 return TRUE;
1881 return FALSE;
1884 #define INTERP_TYPE_AS_PTR(tp) interp_type_as_ptr (tp)
1886 static int
1887 interp_icall_op_for_sig (MonoMethodSignature *sig)
1889 int op = -1;
1890 switch (sig->param_count) {
1891 case 0:
1892 if (MONO_TYPE_IS_VOID (sig->ret))
1893 op = MINT_ICALL_V_V;
1894 else if (INTERP_TYPE_AS_PTR (sig->ret))
1895 op = MINT_ICALL_V_P;
1896 break;
1897 case 1:
1898 if (MONO_TYPE_IS_VOID (sig->ret)) {
1899 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1900 op = MINT_ICALL_P_V;
1901 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1902 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1903 op = MINT_ICALL_P_P;
1905 break;
1906 case 2:
1907 if (MONO_TYPE_IS_VOID (sig->ret)) {
1908 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1909 INTERP_TYPE_AS_PTR (sig->params [1]))
1910 op = MINT_ICALL_PP_V;
1911 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1912 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1913 INTERP_TYPE_AS_PTR (sig->params [1]))
1914 op = MINT_ICALL_PP_P;
1916 break;
1917 case 3:
1918 if (MONO_TYPE_IS_VOID (sig->ret)) {
1919 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1920 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1921 INTERP_TYPE_AS_PTR (sig->params [2]))
1922 op = MINT_ICALL_PPP_V;
1923 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1924 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1925 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1926 INTERP_TYPE_AS_PTR (sig->params [2]))
1927 op = MINT_ICALL_PPP_P;
1929 break;
1930 case 4:
1931 if (MONO_TYPE_IS_VOID (sig->ret)) {
1932 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1933 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1934 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1935 INTERP_TYPE_AS_PTR (sig->params [3]))
1936 op = MINT_ICALL_PPPP_V;
1937 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1938 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1939 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1940 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1941 INTERP_TYPE_AS_PTR (sig->params [3]))
1942 op = MINT_ICALL_PPPP_P;
1944 break;
1945 case 5:
1946 if (MONO_TYPE_IS_VOID (sig->ret)) {
1947 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1948 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1949 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1950 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1951 INTERP_TYPE_AS_PTR (sig->params [4]))
1952 op = MINT_ICALL_PPPPP_V;
1953 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1954 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1955 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1956 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1957 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1958 INTERP_TYPE_AS_PTR (sig->params [4]))
1959 op = MINT_ICALL_PPPPP_P;
1961 break;
1962 case 6:
1963 if (MONO_TYPE_IS_VOID (sig->ret)) {
1964 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1965 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1966 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1967 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1968 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1969 INTERP_TYPE_AS_PTR (sig->params [5]))
1970 op = MINT_ICALL_PPPPPP_V;
1971 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1972 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1973 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1974 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1975 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1976 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1977 INTERP_TYPE_AS_PTR (sig->params [5]))
1978 op = MINT_ICALL_PPPPPP_P;
1980 break;
1982 return op;
1985 #define INLINE_LENGTH_LIMIT 20
1987 static gboolean
1988 interp_method_check_inlining (TransformData *td, MonoMethod *method)
1990 MonoMethodHeaderSummary header;
1992 if (td->method == method)
1993 return FALSE;
1995 if (!mono_method_get_header_summary (method, &header))
1996 return FALSE;
1998 /*runtime, icall and pinvoke are checked by summary call*/
1999 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
2000 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
2001 (mono_class_is_marshalbyref (method->klass)) ||
2002 header.has_clauses)
2003 return FALSE;
2005 if (header.code_size >= INLINE_LENGTH_LIMIT)
2006 return FALSE;
2008 if (mono_class_needs_cctor_run (method->klass, NULL)) {
2009 MonoVTable *vtable;
2010 ERROR_DECL (error);
2011 if (!m_class_get_runtime_info (method->klass))
2012 /* No vtable created yet */
2013 return FALSE;
2014 vtable = mono_class_vtable_checked (td->rtm->domain, method->klass, error);
2015 if (!is_ok (error)) {
2016 mono_error_cleanup (error);
2017 return FALSE;
2019 if (!vtable->initialized)
2020 return FALSE;
2023 /* We currently access at runtime the wrapper data */
2024 if (method->wrapper_type != MONO_WRAPPER_NONE)
2025 return FALSE;
2027 /* Our usage of `emit_store_value_as_local ()` for nint, nuint and nfloat
2028 * is kinda hacky, and doesn't work with the inliner */
2029 if (mono_class_get_magic_index (method->klass) >= 0)
2030 return FALSE;
2032 return TRUE;
2035 static gboolean
2036 interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHeader *header, MonoError *error)
2038 const unsigned char *prev_ip, *prev_il_code, *prev_in_start;
2039 int *prev_in_offsets;
2040 gboolean ret;
2041 unsigned int prev_max_stack_height, prev_max_vt_sp, prev_locals_size;
2042 int prev_n_data_items;
2043 int i, prev_vt_sp;
2044 int prev_sp_offset;
2045 MonoGenericContext *generic_context = NULL;
2046 StackInfo *prev_param_area;
2047 MonoMethod *prev_inlined_method;
2048 MonoMethodSignature *csignature = mono_method_signature_internal (target_method);
2049 int nargs = csignature->param_count + !!csignature->hasthis;
2050 InterpInst *prev_last_ins;
2052 if (csignature->is_inflated)
2053 generic_context = mono_method_get_context (target_method);
2054 else {
2055 MonoGenericContainer *generic_container = mono_method_get_generic_container (target_method);
2056 if (generic_container)
2057 generic_context = &generic_container->context;
2060 prev_ip = td->ip;
2061 prev_il_code = td->il_code;
2062 prev_in_start = td->in_start;
2063 prev_sp_offset = td->sp - td->stack;
2064 prev_vt_sp = td->vt_sp;
2065 prev_inlined_method = td->inlined_method;
2066 prev_last_ins = td->last_ins;
2067 td->inlined_method = target_method;
2069 prev_max_stack_height = td->max_stack_height;
2070 prev_max_vt_sp = td->max_vt_sp;
2071 prev_locals_size = td->locals_size;
2073 prev_n_data_items = td->n_data_items;
2074 prev_in_offsets = td->in_offsets;
2075 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
2077 /* Inlining pops the arguments, restore the stack */
2078 prev_param_area = (StackInfo*)g_malloc (nargs * sizeof (StackInfo));
2079 memcpy (prev_param_area, &td->sp [-nargs], nargs * sizeof (StackInfo));
2081 int const prev_code_size = td->code_size;
2082 td->code_size = header->code_size;
2084 if (td->verbose_level)
2085 g_print ("Inline start method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
2086 ret = generate_code (td, target_method, header, generic_context, error);
2088 if (!ret) {
2089 if (td->verbose_level)
2090 g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
2091 td->max_stack_height = prev_max_stack_height;
2092 td->max_vt_sp = prev_max_vt_sp;
2093 td->locals_size = prev_locals_size;
2096 /* Remove any newly added items */
2097 for (i = prev_n_data_items; i < td->n_data_items; i++) {
2098 g_hash_table_remove (td->data_hash, td->data_items [i]);
2100 td->n_data_items = prev_n_data_items;
2101 td->sp = td->stack + prev_sp_offset;
2102 memcpy (&td->sp [-nargs], prev_param_area, nargs * sizeof (StackInfo));
2103 td->vt_sp = prev_vt_sp;
2104 td->last_ins = prev_last_ins;
2105 if (td->last_ins)
2106 td->last_ins->next = NULL;
2107 UnlockedIncrement (&mono_interp_stats.inline_failures);
2108 } else {
2109 if (td->verbose_level)
2110 g_print ("Inline end method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
2111 UnlockedIncrement (&mono_interp_stats.inlined_methods);
2112 // Make sure we have an IR instruction associated with the now removed IL CALL
2113 // FIXME This could be prettier. We might be able to make inlining saner now that
2114 // that we can easily tweak the instruction list.
2115 if (!prev_inlined_method) {
2116 if (prev_last_ins) {
2117 if (prev_last_ins->next)
2118 prev_last_ins->next->il_offset = prev_in_start - prev_il_code;
2119 } else if (td->first_ins) {
2120 td->first_ins->il_offset = prev_in_start - prev_il_code;
2125 td->ip = prev_ip;
2126 td->in_start = prev_in_start;
2127 td->il_code = prev_il_code;
2128 td->inlined_method = prev_inlined_method;
2129 td->code_size = prev_code_size;
2131 g_free (td->in_offsets);
2132 td->in_offsets = prev_in_offsets;
2134 g_free (prev_param_area);
2135 return ret;
2138 static void
2139 interp_constrained_box (TransformData *td, MonoDomain *domain, MonoClass *constrained_class, MonoMethodSignature *csignature, MonoError *error)
2141 int mt = mint_type (m_class_get_byval_arg (constrained_class));
2142 if (mono_class_is_nullable (constrained_class)) {
2143 g_assert (mt == MINT_TYPE_VT);
2144 interp_add_ins (td, MINT_BOX_NULLABLE);
2145 td->last_ins->data [0] = get_data_item_index (td, constrained_class);
2146 td->last_ins->data [1] = csignature->param_count;
2147 td->last_ins->data [2] = (td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : 1;
2148 } else {
2149 MonoVTable *vtable = mono_class_vtable_checked (domain, constrained_class, error);
2150 return_if_nok (error);
2152 if (mt == MINT_TYPE_VT) {
2153 interp_add_ins (td, MINT_BOX_VT);
2154 td->last_ins->data [0] = get_data_item_index (td, vtable);
2155 td->last_ins->data [1] = csignature->param_count;
2156 td->last_ins->data [2] = (td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : 1;
2157 } else {
2158 interp_add_ins (td, MINT_BOX);
2159 td->last_ins->data [0] = get_data_item_index (td, vtable);
2160 td->last_ins->data [1] = csignature->param_count;
2165 /* Return FALSE if error, including inline failure */
2166 static gboolean
2167 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)
2169 MonoImage *image = m_class_get_image (method->klass);
2170 MonoMethodSignature *csignature;
2171 int is_virtual = *td->ip == CEE_CALLVIRT;
2172 int calli = *td->ip == CEE_CALLI || *td->ip == CEE_MONO_CALLI_EXTRA_ARG;
2173 int i;
2174 guint32 vt_stack_used = 0;
2175 guint32 vt_res_size = 0;
2176 int op = -1;
2177 int native = 0;
2178 int is_void = 0;
2179 int need_null_check = is_virtual;
2181 guint32 token = read32 (td->ip + 1);
2183 if (target_method == NULL) {
2184 if (calli) {
2185 CHECK_STACK(td, 1);
2186 if (method->wrapper_type != MONO_WRAPPER_NONE)
2187 csignature = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
2188 else {
2189 csignature = mono_metadata_parse_signature_checked (image, token, error);
2190 return_val_if_nok (error, FALSE);
2193 if (generic_context) {
2194 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
2195 return_val_if_nok (error, FALSE);
2199 * The compiled interp entry wrapper is passed to runtime_invoke instead of
2200 * the InterpMethod pointer. FIXME
2202 native = csignature->pinvoke || method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE;
2204 target_method = NULL;
2205 } else {
2206 if (method->wrapper_type == MONO_WRAPPER_NONE) {
2207 target_method = mono_get_method_checked (image, token, NULL, generic_context, error);
2208 return_val_if_nok (error, FALSE);
2209 } else
2210 target_method = (MonoMethod *)mono_method_get_wrapper_data (method, token);
2211 csignature = mono_method_signature_internal (target_method);
2213 if (generic_context) {
2214 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
2215 return_val_if_nok (error, FALSE);
2216 target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, error);
2217 return_val_if_nok (error, FALSE);
2220 } else {
2221 csignature = mono_method_signature_internal (target_method);
2224 if (check_visibility && target_method && !mono_method_can_access_method (method, target_method))
2225 interp_generate_mae_throw (td, method, target_method);
2227 if (target_method && target_method->string_ctor) {
2228 /* Create the real signature */
2229 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (td->mempool, csignature);
2230 ctor_sig->ret = m_class_get_byval_arg (mono_defaults.string_class);
2232 csignature = ctor_sig;
2235 /* Intrinsics */
2236 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
2237 return TRUE;
2239 if (constrained_class) {
2240 if (m_class_is_enumtype (constrained_class) && !strcmp (target_method->name, "GetHashCode")) {
2241 /* Use the corresponding method from the base type to avoid boxing */
2242 MonoType *base_type = mono_class_enum_basetype_internal (constrained_class);
2243 g_assert (base_type);
2244 constrained_class = mono_class_from_mono_type_internal (base_type);
2245 target_method = mono_class_get_method_from_name_checked (constrained_class, target_method->name, 0, 0, error);
2246 mono_error_assert_ok (error);
2247 g_assert (target_method);
2251 if (constrained_class) {
2252 mono_class_setup_vtable (constrained_class);
2253 if (mono_class_has_failure (constrained_class)) {
2254 mono_error_set_for_class_failure (error, constrained_class);
2255 return FALSE;
2257 #if DEBUG_INTERP
2258 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);
2259 #endif
2260 target_method = mono_get_method_constrained_with_method (image, target_method, constrained_class, generic_context, error);
2261 #if DEBUG_INTERP
2262 g_print (" : %s::%s. %s (%p)\n", target_method->klass->name, target_method->name, mono_signature_full_name (target_method->signature), target_method);
2263 #endif
2264 /* Intrinsics: Try again, it could be that `mono_get_method_constrained_with_method` resolves to a method that we can substitute */
2265 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
2266 return TRUE;
2268 return_val_if_nok (error, FALSE);
2269 mono_class_setup_vtable (target_method->klass);
2271 if (!m_class_is_valuetype (constrained_class)) {
2272 /* managed pointer on the stack, we need to deref that puppy */
2273 interp_add_ins (td, MINT_LDIND_I);
2274 td->last_ins->data [0] = csignature->param_count;
2275 } 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) {
2276 if (target_method->klass == mono_defaults.enum_class && (td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
2277 /* managed pointer on the stack, we need to deref that puppy */
2278 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2279 interp_add_ins (td, MINT_LDIND_I8);
2280 td->last_ins->data [0] = csignature->param_count;
2283 interp_constrained_box (td, domain, constrained_class, csignature, error);
2284 return_val_if_nok (error, FALSE);
2285 } else {
2286 if (target_method->klass != constrained_class) {
2288 * The type parameter is instantiated as a valuetype,
2289 * but that type doesn't override the method we're
2290 * calling, so we need to box `this'.
2292 if (target_method->klass == mono_defaults.enum_class && (td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP) {
2293 /* managed pointer on the stack, we need to deref that puppy */
2294 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2295 interp_add_ins (td, MINT_LDIND_I8);
2296 td->last_ins->data [0] = csignature->param_count;
2299 interp_constrained_box (td, domain, constrained_class, csignature, error);
2300 return_val_if_nok (error, FALSE);
2302 is_virtual = FALSE;
2306 if (target_method)
2307 mono_class_init_internal (target_method->klass);
2309 if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
2310 if (!mono_class_is_interface (method->klass))
2311 interp_generate_bie_throw (td);
2312 else
2313 is_virtual = TRUE;
2316 if (is_virtual && target_method && (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2317 (MONO_METHOD_IS_FINAL (target_method) &&
2318 target_method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2319 !(mono_class_is_marshalbyref (target_method->klass))) {
2320 /* Not really virtual, just needs a null check */
2321 is_virtual = FALSE;
2322 need_null_check = TRUE;
2325 CHECK_STACK (td, csignature->param_count + csignature->hasthis);
2326 if (!td->gen_sdb_seq_points && !calli && op == -1 && (!is_virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) &&
2327 (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 &&
2328 (target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0 &&
2329 !(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING)) {
2330 (void)mono_class_vtable_checked (domain, target_method->klass, error);
2331 return_val_if_nok (error, FALSE);
2333 if (method == target_method && *(td->ip + 5) == CEE_RET && !(csignature->hasthis && m_class_is_valuetype (target_method->klass))) {
2334 if (td->inlined_method)
2335 return FALSE;
2337 if (td->verbose_level)
2338 g_print ("Optimize tail call of %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
2340 for (i = csignature->param_count - 1 + !!csignature->hasthis; i >= 0; --i)
2341 store_arg (td, i);
2343 interp_add_ins (td, MINT_BR_S);
2344 // We are branching to the beginning of the method
2345 td->last_ins->data [0] = 0;
2346 if (!is_bb_start [td->ip + 5 - td->il_code])
2347 ++td->ip; /* gobble the CEE_RET if it isn't branched to */
2348 td->ip += 5;
2349 return TRUE;
2353 target_method = interp_transform_internal_calls (method, target_method, csignature, is_virtual);
2355 if (csignature->call_convention == MONO_CALL_VARARG) {
2356 csignature = mono_method_get_signature_checked (target_method, image, token, generic_context, error);
2357 int vararg_stack = 0;
2359 * For vararg calls, ArgIterator expects the signature and the varargs to be
2360 * stored in a linear memory. We allocate the necessary vt_stack space for
2361 * this. All varargs will be pushed to the vt_stack at call site.
2363 vararg_stack += sizeof (gpointer);
2364 for (i = csignature->sentinelpos; i < csignature->param_count; ++i) {
2365 int align, arg_size;
2366 arg_size = mono_type_stack_size (csignature->params [i], &align);
2367 vararg_stack += ALIGN_TO (arg_size, align);
2369 /* allocate space for the pointer to varargs space start */
2370 vararg_stack += sizeof (gpointer);
2371 vt_stack_used += ALIGN_TO (vararg_stack, MINT_VT_ALIGNMENT);
2372 PUSH_VT (td, vararg_stack);
2375 if (need_null_check) {
2376 interp_add_ins (td, MINT_CKNULL_N);
2377 td->last_ins->data [0] = csignature->param_count + 1;
2380 g_assert (csignature->call_convention != MONO_CALL_FASTCALL);
2381 if ((mono_interp_opt & INTERP_OPT_INLINE) && op == -1 && !is_virtual && target_method && interp_method_check_inlining (td, target_method)) {
2382 MonoMethodHeader *mheader = interp_method_get_header (target_method, error);
2383 return_val_if_nok (error, FALSE);
2385 if (interp_inline_method (td, target_method, mheader, error)) {
2386 td->ip += 5;
2387 return TRUE;
2391 /* Don't inline methods that do calls */
2392 if (op == -1 && td->inlined_method)
2393 return FALSE;
2395 /* We need to convert delegate invoke to a indirect call on the interp_invoke_impl field */
2396 if (target_method && m_class_get_parent (target_method->klass) == mono_defaults.multicastdelegate_class) {
2397 const char *name = target_method->name;
2398 if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
2399 calli = TRUE;
2400 interp_add_ins (td, MINT_LD_DELEGATE_INVOKE_IMPL);
2401 td->last_ins->data [0] = csignature->param_count + 1;
2402 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
2406 /* Pop the function pointer */
2407 if (calli)
2408 --td->sp;
2410 td->sp -= csignature->param_count + !!csignature->hasthis;
2411 for (i = 0; i < csignature->param_count; ++i) {
2412 if (td->sp [i + !!csignature->hasthis].type == STACK_TYPE_VT) {
2413 gint32 size;
2414 MonoClass *klass = mono_class_from_mono_type_internal (csignature->params [i]);
2415 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2416 size = mono_class_native_size (klass, NULL);
2417 else
2418 size = mono_class_value_size (klass, NULL);
2419 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
2420 vt_stack_used += size;
2424 /* need to handle typedbyref ... */
2425 if (csignature->ret->type != MONO_TYPE_VOID) {
2426 int mt = mint_type(csignature->ret);
2427 MonoClass *klass = mono_class_from_mono_type_internal (csignature->ret);
2428 if (mt == MINT_TYPE_VT) {
2429 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2430 vt_res_size = mono_class_native_size (klass, NULL);
2431 else
2432 vt_res_size = mono_class_value_size (klass, NULL);
2433 if (mono_class_has_failure (klass)) {
2434 mono_error_set_for_class_failure (error, klass);
2435 return FALSE;
2437 PUSH_VT(td, vt_res_size);
2439 PUSH_TYPE(td, stack_type[mt], klass);
2440 } else
2441 is_void = TRUE;
2443 if (op >= 0) {
2444 interp_add_ins (td, op);
2446 if (op == MINT_LDLEN) {
2447 #ifdef MONO_BIG_ARRAYS
2448 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
2449 #else
2450 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
2451 #endif
2454 #ifndef ENABLE_NETCORE
2455 if (op == MINT_CALLRUN) {
2456 td->last_ins->data [0] = get_data_item_index (td, target_method);
2457 td->last_ins->data [1] = get_data_item_index (td, mono_method_signature_internal (target_method));
2459 #endif
2460 } else if (!calli && !is_virtual && jit_call_supported (target_method, csignature)) {
2461 interp_add_ins (td, MINT_JIT_CALL);
2462 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2463 mono_error_assert_ok (error);
2464 } else {
2465 #ifndef MONO_ARCH_HAS_NO_PROPER_MONOCTX
2466 /* Try using fast icall path for simple signatures */
2467 if (native && !method->dynamic)
2468 op = interp_icall_op_for_sig (csignature);
2469 #endif
2470 if (csignature->call_convention == MONO_CALL_VARARG)
2471 interp_add_ins (td, MINT_CALL_VARARG);
2472 else if (calli)
2473 interp_add_ins (td, native ? ((op != -1) ? MINT_CALLI_NAT_FAST : MINT_CALLI_NAT) : MINT_CALLI);
2474 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass))
2475 interp_add_ins (td, is_void ? MINT_VCALLVIRT_FAST : MINT_CALLVIRT_FAST);
2476 else if (is_virtual)
2477 interp_add_ins (td, is_void ? MINT_VCALLVIRT : MINT_CALLVIRT);
2478 else
2479 interp_add_ins (td, is_void ? MINT_VCALL : MINT_CALL);
2481 if (calli) {
2482 td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
2483 if (op != -1) {
2484 td->last_ins->data[1] = op;
2485 if (td->last_ins->opcode == MINT_CALLI_NAT_FAST)
2486 td->last_ins->data[2] = save_last_error;
2487 } else if (op == -1 && td->last_ins->opcode == MINT_CALLI_NAT) {
2488 td->last_ins->data[1] = save_last_error;
2490 } else {
2491 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2492 #ifdef ENABLE_EXPERIMENT_TIERED
2493 if (MINT_IS_PATCHABLE_CALL (td->last_ins->opcode)) {
2494 g_assert (!calli && !is_virtual);
2495 td->last_ins->flags |= INTERP_INST_FLAG_RECORD_CALL_PATCH;
2496 g_hash_table_insert (td->patchsite_hash, td->last_ins, target_method);
2498 #endif
2499 return_val_if_nok (error, FALSE);
2500 if (csignature->call_convention == MONO_CALL_VARARG)
2501 td->last_ins->data [1] = get_data_item_index (td, (void *)csignature);
2502 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass)) {
2503 /* FIXME Use fastpath also for MBRO. Asserts in mono_method_get_vtable_slot */
2504 if (mono_class_is_interface (target_method->klass))
2505 td->last_ins->data [1] = -2 * MONO_IMT_SIZE + mono_method_get_imt_slot (target_method);
2506 else
2507 td->last_ins->data [1] = mono_method_get_vtable_slot (target_method);
2511 td->ip += 5;
2512 if (vt_stack_used != 0 || vt_res_size != 0) {
2513 interp_add_ins (td, MINT_VTRESULT);
2514 td->last_ins->data [0] = vt_res_size;
2515 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
2516 td->vt_sp -= vt_stack_used;
2519 return TRUE;
2522 static MonoClassField *
2523 interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, MonoGenericContext *generic_context, MonoError *error)
2525 MonoClassField *field = NULL;
2526 if (method->wrapper_type != MONO_WRAPPER_NONE) {
2527 field = (MonoClassField *) mono_method_get_wrapper_data (method, token);
2528 *klass = field->parent;
2530 mono_class_setup_fields (field->parent);
2531 } else {
2532 field = mono_field_from_token_checked (m_class_get_image (method->klass), token, klass, generic_context, error);
2533 return_val_if_nok (error, NULL);
2536 if (!method->skip_visibility && !mono_method_can_access_field (method, field)) {
2537 char *method_fname = mono_method_full_name (method, TRUE);
2538 char *field_fname = mono_field_full_name (field);
2539 mono_error_set_generic_error (error, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
2540 g_free (method_fname);
2541 g_free (field_fname);
2542 return NULL;
2545 return field;
2548 static InterpBasicBlock*
2549 get_bb (TransformData *td, InterpBasicBlock *cbb, unsigned char *ip)
2551 int offset = ip - td->il_code;
2552 InterpBasicBlock *bb = td->offset_to_bb [offset];
2554 if (!bb) {
2555 bb = (InterpBasicBlock*)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock));
2556 bb->ip = ip;
2557 td->offset_to_bb [offset] = bb;
2559 td->basic_blocks = g_list_append_mempool (td->mempool, td->basic_blocks, bb);
2562 if (cbb)
2563 bb->preds = g_slist_prepend_mempool (td->mempool, bb->preds, cbb);
2564 return bb;
2568 * get_basic_blocks:
2570 * Compute the set of IL level basic blocks.
2572 static void
2573 get_basic_blocks (TransformData *td)
2575 guint8 *start = (guint8*)td->il_code;
2576 guint8 *end = (guint8*)td->il_code + td->code_size;
2577 guint8 *ip = start;
2578 unsigned char *target;
2579 int i;
2580 guint cli_addr;
2581 const MonoOpcode *opcode;
2582 InterpBasicBlock *cbb;
2584 td->offset_to_bb = (InterpBasicBlock**)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * (end - start + 1));
2585 td->entry_bb = cbb = get_bb (td, NULL, start);
2587 while (ip < end) {
2588 cli_addr = ip - start;
2589 td->offset_to_bb [cli_addr] = cbb;
2590 i = mono_opcode_value ((const guint8 **)&ip, end);
2591 opcode = &mono_opcodes [i];
2592 switch (opcode->argument) {
2593 case MonoInlineNone:
2594 ip++;
2595 break;
2596 case MonoInlineString:
2597 case MonoInlineType:
2598 case MonoInlineField:
2599 case MonoInlineMethod:
2600 case MonoInlineTok:
2601 case MonoInlineSig:
2602 case MonoShortInlineR:
2603 case MonoInlineI:
2604 ip += 5;
2605 break;
2606 case MonoInlineVar:
2607 ip += 3;
2608 break;
2609 case MonoShortInlineVar:
2610 case MonoShortInlineI:
2611 ip += 2;
2612 break;
2613 case MonoShortInlineBrTarget:
2614 target = start + cli_addr + 2 + (signed char)ip [1];
2615 get_bb (td, cbb, target);
2616 ip += 2;
2617 cbb = get_bb (td, cbb, ip);
2618 break;
2619 case MonoInlineBrTarget:
2620 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
2621 get_bb (td, cbb, target);
2622 ip += 5;
2623 cbb = get_bb (td, cbb, ip);
2624 break;
2625 case MonoInlineSwitch: {
2626 guint32 n = read32 (ip + 1);
2627 guint32 j;
2628 ip += 5;
2629 cli_addr += 5 + 4 * n;
2630 target = start + cli_addr;
2631 get_bb (td, cbb, target);
2633 for (j = 0; j < n; ++j) {
2634 target = start + cli_addr + (gint32)read32 (ip);
2635 get_bb (td, cbb, target);
2636 ip += 4;
2638 cbb = get_bb (td, cbb, ip);
2639 break;
2641 case MonoInlineR:
2642 case MonoInlineI8:
2643 ip += 9;
2644 break;
2645 default:
2646 g_assert_not_reached ();
2649 if (i == CEE_THROW)
2650 cbb = get_bb (td, NULL, ip);
2654 static void
2655 interp_save_debug_info (InterpMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers)
2657 MonoDebugMethodJitInfo *dinfo;
2658 int i;
2660 if (!mono_debug_enabled ())
2661 return;
2664 * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
2667 dinfo = g_new0 (MonoDebugMethodJitInfo, 1);
2668 dinfo->num_params = rtm->param_count;
2669 dinfo->params = g_new0 (MonoDebugVarInfo, dinfo->num_params);
2670 dinfo->num_locals = header->num_locals;
2671 dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals);
2672 dinfo->code_start = (guint8*)rtm->code;
2673 dinfo->code_size = td->new_code_end - td->new_code;
2674 dinfo->epilogue_begin = 0;
2675 dinfo->has_var_info = TRUE;
2676 dinfo->num_line_numbers = line_numbers->len;
2677 dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers);
2679 for (i = 0; i < dinfo->num_params; i++) {
2680 MonoDebugVarInfo *var = &dinfo->params [i];
2681 var->type = rtm->param_types [i];
2683 for (i = 0; i < dinfo->num_locals; i++) {
2684 MonoDebugVarInfo *var = &dinfo->locals [i];
2685 var->type = mono_metadata_type_dup (NULL, header->locals [i]);
2688 for (i = 0; i < dinfo->num_line_numbers; i++)
2689 dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i);
2690 mono_debug_add_method (rtm->method, dinfo, rtm->domain);
2692 mono_debug_free_method_jit_info (dinfo);
2695 /* Same as the code in seq-points.c */
2696 static void
2697 insert_pred_seq_point (SeqPoint *last_sp, SeqPoint *sp, GSList **next)
2699 GSList *l;
2700 int src_index = last_sp->next_offset;
2701 int dst_index = sp->next_offset;
2703 /* bb->in_bb might contain duplicates */
2704 for (l = next [src_index]; l; l = l->next)
2705 if (GPOINTER_TO_UINT (l->data) == dst_index)
2706 break;
2707 if (!l)
2708 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
2711 static void
2712 recursively_make_pred_seq_points (TransformData *td, InterpBasicBlock *bb)
2714 SeqPoint ** const MONO_SEQ_SEEN_LOOP = (SeqPoint**)GINT_TO_POINTER(-1);
2715 GSList *l;
2717 GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
2718 GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
2720 // Insert/remove sentinel into the memoize table to detect loops containing bb
2721 bb->pred_seq_points = MONO_SEQ_SEEN_LOOP;
2723 for (l = bb->preds; l; l = l->next) {
2724 InterpBasicBlock *in_bb = (InterpBasicBlock*)l->data;
2726 // This bb has the last seq point, append it and continue
2727 if (in_bb->last_seq_point != NULL) {
2728 predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
2729 continue;
2732 // We've looped or handled this before, exit early.
2733 // No last sequence points to find.
2734 if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
2735 continue;
2737 // Take sequence points from incoming basic blocks
2739 if (in_bb == td->entry_bb)
2740 continue;
2742 if (in_bb->pred_seq_points == NULL)
2743 recursively_make_pred_seq_points (td, in_bb);
2745 // Union sequence points with incoming bb's
2746 for (int i=0; i < in_bb->num_pred_seq_points; i++) {
2747 if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
2748 g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
2749 g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
2752 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
2755 g_hash_table_destroy (seen);
2757 if (predecessors->len != 0) {
2758 bb->pred_seq_points = (SeqPoint**)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint *) * predecessors->len);
2759 bb->num_pred_seq_points = predecessors->len;
2761 for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
2762 bb->pred_seq_points [newer] = (SeqPoint*)g_array_index (predecessors, gpointer, newer);
2766 g_array_free (predecessors, TRUE);
2769 static void
2770 collect_pred_seq_points (TransformData *td, InterpBasicBlock *bb, SeqPoint *seqp, GSList **next)
2772 // Doesn't have a last sequence point, must find from incoming basic blocks
2773 if (bb->pred_seq_points == NULL && bb != td->entry_bb)
2774 recursively_make_pred_seq_points (td, bb);
2776 for (int i = 0; i < bb->num_pred_seq_points; i++)
2777 insert_pred_seq_point (bb->pred_seq_points [i], seqp, next);
2779 return;
2782 static void
2783 save_seq_points (TransformData *td, MonoJitInfo *jinfo)
2785 GByteArray *array;
2786 int i, seq_info_size;
2787 MonoSeqPointInfo *info;
2788 GSList **next = NULL;
2789 GList *bblist;
2791 if (!td->gen_sdb_seq_points)
2792 return;
2795 * For each sequence point, compute the list of sequence points immediately
2796 * following it, this is needed to implement 'step over' in the debugger agent.
2797 * Similar to the code in mono_save_seq_point_info ().
2799 for (i = 0; i < td->seq_points->len; ++i) {
2800 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2802 /* Store the seq point index here temporarily */
2803 sp->next_offset = i;
2805 next = (GSList**)mono_mempool_alloc0 (td->mempool, sizeof (GList*) * td->seq_points->len);
2806 for (bblist = td->basic_blocks; bblist; bblist = bblist->next) {
2807 InterpBasicBlock *bb = (InterpBasicBlock*)bblist->data;
2809 GSList *bb_seq_points = g_slist_reverse (bb->seq_points);
2810 SeqPoint *last = NULL;
2811 for (GSList *l = bb_seq_points; l; l = l->next) {
2812 SeqPoint *sp = (SeqPoint*)l->data;
2814 if (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET)
2815 /* Used to implement method entry/exit events */
2816 continue;
2818 if (last != NULL) {
2819 /* Link with the previous seq point in the same bb */
2820 next [last->next_offset] = g_slist_append_mempool (td->mempool, next [last->next_offset], GINT_TO_POINTER (sp->next_offset));
2821 } else {
2822 /* Link with the last bb in the previous bblocks */
2823 collect_pred_seq_points (td, bb, sp, next);
2825 last = sp;
2829 /* Serialize the seq points into a byte array */
2830 array = g_byte_array_new ();
2831 SeqPoint zero_seq_point = {0};
2832 SeqPoint* last_seq_point = &zero_seq_point;
2833 for (i = 0; i < td->seq_points->len; ++i) {
2834 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2836 sp->next_offset = 0;
2837 if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next [i], TRUE))
2838 last_seq_point = sp;
2841 if (td->verbose_level) {
2842 g_print ("\nSEQ POINT MAP FOR %s: \n", td->method->name);
2844 for (i = 0; i < td->seq_points->len; ++i) {
2845 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2846 GSList *l;
2848 if (!next [i])
2849 continue;
2851 g_print ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
2852 for (l = next [i]; l; l = l->next) {
2853 int next_index = GPOINTER_TO_UINT (l->data);
2854 g_print (" IL0x%x", ((SeqPoint*)g_ptr_array_index (td->seq_points, next_index))->il_offset);
2856 g_print ("\n");
2860 info = mono_seq_point_info_new (array->len, TRUE, array->data, TRUE, &seq_info_size);
2861 mono_atomic_fetch_add_i32 (&mono_jit_stats.allocated_seq_points_size, seq_info_size);
2863 g_byte_array_free (array, TRUE);
2865 jinfo->seq_points = info;
2868 static void
2869 interp_emit_memory_barrier (TransformData *td, int kind)
2871 #if defined(TARGET_WASM)
2872 // mono_memory_barrier is dummy on wasm
2873 #elif defined(TARGET_X86) || defined(TARGET_AMD64)
2874 if (kind == MONO_MEMORY_BARRIER_SEQ)
2875 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
2876 #else
2877 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
2878 #endif
2881 #define BARRIER_IF_VOLATILE(td, kind) \
2882 do { \
2883 if (volatile_) { \
2884 interp_emit_memory_barrier (td, kind); \
2885 volatile_ = FALSE; \
2887 } while (0)
2889 #define INLINE_FAILURE \
2890 do { \
2891 if (inlining) \
2892 goto exit; \
2893 } while (0)
2895 static void
2896 interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMethodSignature *signature, MonoMethodHeader *header)
2898 int i, offset, size, align;
2900 imethod->local_offsets = (guint32*)g_malloc (header->num_locals * sizeof(guint32));
2901 td->locals = (InterpLocal*)g_malloc (header->num_locals * sizeof (InterpLocal));
2902 td->locals_size = header->num_locals;
2903 td->locals_capacity = td->locals_size;
2904 offset = 0;
2905 for (i = 0; i < header->num_locals; ++i) {
2906 size = mono_type_size (header->locals [i], &align);
2907 offset += align - 1;
2908 offset &= ~(align - 1);
2909 imethod->local_offsets [i] = offset;
2910 td->locals [i].offset = offset;
2911 td->locals [i].flags = 0;
2912 td->locals [i].type = header->locals [i];
2913 td->locals [i].mt = mint_type (header->locals [i]);
2914 offset += size;
2916 offset = (offset + 7) & ~7;
2918 imethod->exvar_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32));
2919 for (i = 0; i < header->num_clauses; i++) {
2920 imethod->exvar_offsets [i] = offset;
2921 offset += sizeof (MonoObject*);
2923 offset = (offset + 7) & ~7;
2925 imethod->locals_size = offset;
2926 g_assert (imethod->locals_size < 65536);
2927 td->total_locals_size = offset;
2930 /* Return false is failure to init basic blocks due to being in inline method */
2931 static gboolean
2932 init_bb_start (TransformData *td, MonoMethodHeader *header, gboolean inlining)
2934 const unsigned char *ip, *end;
2935 const MonoOpcode *opcode;
2936 int offset, i, in, backwards;
2938 /* intern the strings in the method. */
2939 ip = header->code;
2940 end = ip + header->code_size;
2942 /* inlined method continues the basic block of parent method */
2943 if (!inlining)
2944 td->is_bb_start [0] = 1;
2945 while (ip < end) {
2946 in = *ip;
2947 if (in == 0xfe) {
2948 ip++;
2949 in = *ip + 256;
2951 else if (in == 0xf0) {
2952 ip++;
2953 in = *ip + MONO_CEE_MONO_ICALL;
2955 opcode = &mono_opcodes [in];
2956 switch (opcode->argument) {
2957 case MonoInlineNone:
2958 ++ip;
2959 break;
2960 case MonoInlineString:
2961 ip += 5;
2962 break;
2963 case MonoInlineType:
2964 ip += 5;
2965 break;
2966 case MonoInlineMethod:
2967 ip += 5;
2968 break;
2969 case MonoInlineField:
2970 case MonoInlineSig:
2971 case MonoInlineI:
2972 case MonoInlineTok:
2973 case MonoShortInlineR:
2974 ip += 5;
2975 break;
2976 case MonoInlineBrTarget:
2977 offset = read32 (ip + 1);
2978 ip += 5;
2979 /* this branch is ignored */
2980 if (offset == 0 && in == MONO_CEE_BR)
2981 break;
2982 backwards = offset < 0;
2983 offset += ip - header->code;
2984 g_assert (offset >= 0 && offset < header->code_size);
2985 if (inlining)
2986 return FALSE;
2987 td->is_bb_start [offset] |= backwards ? 2 : 1;
2988 break;
2989 case MonoShortInlineBrTarget:
2990 offset = ((gint8 *)ip) [1];
2991 ip += 2;
2992 /* this branch is ignored */
2993 if (offset == 0 && in == MONO_CEE_BR_S)
2994 break;
2995 backwards = offset < 0;
2996 offset += ip - header->code;
2997 g_assert (offset >= 0 && offset < header->code_size);
2998 if (inlining)
2999 return FALSE;
3000 td->is_bb_start [offset] |= backwards ? 2 : 1;
3001 break;
3002 case MonoInlineVar:
3003 ip += 3;
3004 break;
3005 case MonoShortInlineVar:
3006 case MonoShortInlineI:
3007 ip += 2;
3008 break;
3009 case MonoInlineSwitch: {
3010 guint32 n;
3011 const unsigned char *next_ip;
3012 ++ip;
3013 n = read32 (ip);
3014 ip += 4;
3015 next_ip = ip + 4 * n;
3016 for (i = 0; i < n; i++) {
3017 offset = read32 (ip);
3018 backwards = offset < 0;
3019 offset += next_ip - header->code;
3020 g_assert (offset >= 0 && offset < header->code_size);
3021 if (inlining)
3022 return FALSE;
3023 td->is_bb_start [offset] |= backwards ? 2 : 1;
3024 ip += 4;
3026 break;
3028 case MonoInlineR:
3029 case MonoInlineI8:
3030 ip += 9;
3031 break;
3032 default:
3033 g_assert_not_reached ();
3036 return TRUE;
3039 #ifdef NO_UNALIGNED_ACCESS
3040 static int
3041 get_unaligned_opcode (int opcode)
3043 switch (opcode) {
3044 case MINT_LDFLD_I8:
3045 return MINT_LDFLD_I8_UNALIGNED;
3046 case MINT_LDFLD_R8:
3047 return MINT_LDFLD_R8_UNALIGNED;
3048 case MINT_STFLD_I8:
3049 return MINT_STFLD_I8_UNALIGNED;
3050 case MINT_STFLD_R8:
3051 return MINT_STFLD_R8_UNALIGNED;
3052 default:
3053 g_assert_not_reached ();
3055 return -1;
3057 #endif
3059 static void
3060 interp_handle_isinst (TransformData *td, MonoClass *klass, gboolean isinst_instr)
3062 /* Follow the logic from jit's handle_isinst */
3063 if (!mono_class_has_variant_generic_params (klass)) {
3064 if (mono_class_is_interface (klass))
3065 interp_add_ins (td, isinst_instr ? MINT_ISINST_INTERFACE : MINT_CASTCLASS_INTERFACE);
3066 else if (!mono_class_is_marshalbyref (klass) && m_class_get_rank (klass) == 0 && !mono_class_is_nullable (klass))
3067 interp_add_ins (td, isinst_instr ? MINT_ISINST_COMMON : MINT_CASTCLASS_COMMON);
3068 else
3069 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
3070 } else {
3071 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
3073 td->last_ins->data [0] = get_data_item_index (td, klass);
3074 td->ip += 5;
3077 static void
3078 interp_emit_ldsflda (TransformData *td, MonoClassField *field, MonoError *error)
3080 MonoDomain *domain = td->rtm->domain;
3081 // Initialize the offset for the field
3082 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
3083 return_if_nok (error);
3085 if (mono_class_field_is_special_static (field)) {
3086 guint32 offset;
3088 mono_domain_lock (domain);
3089 g_assert (domain->special_static_fields);
3090 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
3091 mono_domain_unlock (domain);
3092 g_assert (offset);
3094 interp_add_ins (td, MINT_LDSSFLDA);
3095 WRITE32_INS(td->last_ins, 0, &offset);
3096 } else {
3097 interp_add_ins (td, MINT_LDSFLDA);
3098 td->last_ins->data [0] = get_data_item_index (td, vtable);
3099 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
3103 static gboolean
3104 interp_emit_load_const (TransformData *td, gpointer field_addr, int mt)
3106 if (mt >= MINT_TYPE_I1 && mt <= MINT_TYPE_I4
3107 #if SIZEOF_VOID_P == 4
3108 || mt == MINT_TYPE_P
3109 #endif
3111 gint32 val;
3112 switch (mt) {
3113 case MINT_TYPE_I1:
3114 val = *(gint8*)field_addr;
3115 break;
3116 case MINT_TYPE_U1:
3117 val = *(guint8*)field_addr;
3118 break;
3119 case MINT_TYPE_I2:
3120 val = *(gint16*)field_addr;
3121 break;
3122 case MINT_TYPE_U2:
3123 val = *(guint16*)field_addr;
3124 break;
3125 default:
3126 val = *(gint32*)field_addr;
3128 interp_get_ldc_i4_from_const (td, NULL, val);
3129 } else if (mt == MINT_TYPE_I8
3130 #if SIZEOF_VOID_P == 8
3131 || mt == MINT_TYPE_P
3132 #endif
3134 gint64 val = *(gint64*)field_addr;
3135 interp_add_ins (td, MINT_LDC_I8);
3136 WRITE64_INS (td->last_ins, 0, &val);
3137 } else if (mt == MINT_TYPE_R4) {
3138 float val = *(float*)field_addr;
3139 interp_add_ins (td, MINT_LDC_R4);
3140 WRITE32_INS (td->last_ins, 0, &val);
3141 } else if (mt == MINT_TYPE_R8) {
3142 double val = *(double*)field_addr;
3143 interp_add_ins (td, MINT_LDC_R8);
3144 WRITE64_INS (td->last_ins, 0, &val);
3145 } else {
3146 return FALSE;
3148 return TRUE;
3151 static void
3152 interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, gboolean is_load, MonoError *error)
3154 MonoDomain *domain = td->rtm->domain;
3155 // Initialize the offset for the field
3156 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
3157 return_if_nok (error);
3159 if (mono_class_field_is_special_static (field)) {
3160 guint32 offset;
3162 mono_domain_lock (domain);
3163 g_assert (domain->special_static_fields);
3164 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
3165 mono_domain_unlock (domain);
3166 g_assert (offset);
3168 // Offset is SpecialStaticOffset
3169 if ((offset & 0x80000000) == 0 && mt != MINT_TYPE_VT) {
3170 // This field is thread static
3171 interp_add_ins (td, (is_load ? MINT_LDTSFLD_I1 : MINT_STTSFLD_I1) + mt);
3172 WRITE32_INS(td->last_ins, 0, &offset);
3173 } else {
3174 if (mt == MINT_TYPE_VT) {
3175 interp_add_ins (td, is_load ? MINT_LDSSFLD_VT : MINT_STSSFLD_VT);
3176 WRITE32_INS(td->last_ins, 0, &offset);
3178 int size = mono_class_value_size (field_class, NULL);
3179 WRITE32_INS(td->last_ins, 2, &size);
3180 } else {
3181 interp_add_ins (td, is_load ? MINT_LDSSFLD : MINT_STSSFLD);
3182 td->last_ins->data [0] = get_data_item_index (td, field);
3183 WRITE32_INS(td->last_ins, 1, &offset);
3186 } else {
3187 gpointer field_addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
3188 if (is_load) {
3189 MonoType *ftype = mono_field_get_type_internal (field);
3190 if (ftype->attrs & FIELD_ATTRIBUTE_INIT_ONLY && vtable->initialized) {
3191 if (interp_emit_load_const (td, field_addr, mt))
3192 return;
3194 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSFLD_VT : (MINT_LDSFLD_I1 + mt - MINT_TYPE_I1));
3195 } else {
3196 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1));
3199 td->last_ins->data [0] = get_data_item_index (td, vtable);
3200 td->last_ins->data [1] = get_data_item_index (td, (char*)field_addr);
3202 if (mt == MINT_TYPE_VT) {
3203 int size = mono_class_value_size (field_class, NULL);
3204 WRITE32_INS(td->last_ins, 2, &size);
3209 static gboolean
3210 generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error)
3212 int target;
3213 int offset, mt, i, i32;
3214 guint32 token;
3215 int in_offset;
3216 const unsigned char *end;
3217 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
3218 gboolean sym_seq_points = FALSE;
3219 MonoBitSet *seq_point_locs = NULL;
3220 gboolean readonly = FALSE;
3221 gboolean volatile_ = FALSE;
3222 MonoClass *constrained_class = NULL;
3223 MonoClass *klass;
3224 MonoClassField *field;
3225 MonoImage *image = m_class_get_image (method->klass);
3226 InterpMethod *rtm = td->rtm;
3227 MonoDomain *domain = rtm->domain;
3228 MonoMethodSignature *signature = mono_method_signature_internal (method);
3229 gboolean ret = TRUE;
3230 gboolean emitted_funccall_seq_point = FALSE;
3231 guint32 *arg_locals = NULL;
3232 guint32 *local_locals = NULL;
3233 InterpInst *last_seq_point = NULL;
3234 gboolean save_last_error = FALSE;
3235 gboolean inlining = td->method != method;
3237 original_bb = bb = mono_basic_block_split (method, error, header);
3238 goto_if_nok (error, exit);
3239 g_assert (bb);
3241 td->il_code = header->code;
3242 td->in_start = td->ip = header->code;
3243 end = td->ip + header->code_size;
3245 if (!init_bb_start (td, header, inlining))
3246 goto exit;
3248 if (!inlining) {
3249 for (i = 0; i < header->code_size; i++) {
3250 td->stack_height [i] = -1;
3251 td->clause_indexes [i] = -1;
3255 for (i = 0; i < header->num_clauses; i++) {
3256 MonoExceptionClause *c = header->clauses + i;
3257 td->stack_height [c->handler_offset] = 0;
3258 td->vt_stack_size [c->handler_offset] = 0;
3259 td->is_bb_start [c->handler_offset] = 1;
3260 td->is_bb_start [c->try_offset] = 1;
3262 td->stack_height [c->handler_offset] = 1;
3263 td->stack_state [c->handler_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
3264 td->stack_state [c->handler_offset][0].type = STACK_TYPE_O;
3265 td->stack_state [c->handler_offset][0].klass = NULL; /*FIX*/
3267 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER) {
3268 td->stack_height [c->data.filter_offset] = 0;
3269 td->vt_stack_size [c->data.filter_offset] = 0;
3270 td->is_bb_start [c->data.filter_offset] = 1;
3272 td->stack_height [c->data.filter_offset] = 1;
3273 td->stack_state [c->data.filter_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
3274 td->stack_state [c->data.filter_offset][0].type = STACK_TYPE_O;
3275 td->stack_state [c->data.filter_offset][0].klass = NULL; /*FIX*/
3278 for (int j = c->handler_offset; j < c->handler_offset + c->handler_len; ++j) {
3279 if (td->clause_indexes [j] == -1)
3280 td->clause_indexes [j] = i;
3284 if (td->gen_sdb_seq_points && !inlining) {
3285 MonoDebugMethodInfo *minfo;
3286 get_basic_blocks (td);
3288 minfo = mono_debug_lookup_method (method);
3290 if (minfo) {
3291 MonoSymSeqPoint *sps;
3292 int i, n_il_offsets;
3294 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
3295 // FIXME: Free
3296 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
3297 sym_seq_points = TRUE;
3299 for (i = 0; i < n_il_offsets; ++i) {
3300 if (sps [i].il_offset < header->code_size)
3301 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
3303 g_free (sps);
3305 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
3306 if (asyncMethod) {
3307 for (i = 0; asyncMethod != NULL && i < asyncMethod->num_awaits; i++) {
3308 mono_bitset_set_fast (seq_point_locs, asyncMethod->resume_offsets [i]);
3309 mono_bitset_set_fast (seq_point_locs, asyncMethod->yield_offsets [i]);
3311 mono_debug_free_method_async_debug_info (asyncMethod);
3313 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (m_class_get_image (method->klass))) {
3314 /* Methods without line number info like auto-generated property accessors */
3315 seq_point_locs = mono_bitset_new (header->code_size, 0);
3316 sym_seq_points = TRUE;
3320 if (sym_seq_points) {
3321 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3322 last_seq_point->flags |= INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY;
3325 if (mono_debugger_method_has_breakpoint (method))
3326 interp_add_ins (td, MINT_BREAKPOINT);
3328 if (!inlining) {
3329 if (td->verbose_level) {
3330 char *tmp = mono_disasm_code (NULL, method, td->ip, end);
3331 char *name = mono_method_full_name (method, TRUE);
3332 g_print ("Method %s, original code:\n", name);
3333 g_print ("%s\n", tmp);
3334 g_free (tmp);
3335 g_free (name);
3338 if (header->num_locals && header->init_locals)
3339 interp_add_ins (td, MINT_INITLOCALS);
3341 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3342 interp_add_ins (td, MINT_TRACE_ENTER);
3343 else if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER)
3344 interp_add_ins (td, MINT_PROF_ENTER);
3346 /* safepoint is required on method entry */
3347 if (mono_threads_are_safepoints_enabled ())
3348 interp_add_ins (td, MINT_SAFEPOINT);
3349 } else {
3350 int local;
3351 arg_locals = (guint32*) g_malloc ((!!signature->hasthis + signature->param_count) * sizeof (guint32));
3352 /* Allocate locals to store inlined method args from stack */
3353 for (i = signature->param_count - 1; i >= 0; i--) {
3354 local = create_interp_local (td, signature->params [i]);
3355 arg_locals [i + !!signature->hasthis] = local;
3356 store_local (td, local);
3359 if (signature->hasthis) {
3361 * If this is value type, it is passed by address and not by value.
3362 * FIXME We should use MINT_TYPE_P instead of MINT_TYPE_O
3364 MonoType *type = mono_get_object_type ();
3365 local = create_interp_local (td, type);
3366 arg_locals [0] = local;
3367 store_local (td, local);
3370 local_locals = (guint32*) g_malloc (header->num_locals * sizeof (guint32));
3371 /* Allocate locals to store inlined method args from stack */
3372 for (i = 0; i < header->num_locals; i++)
3373 local_locals [i] = create_interp_local (td, header->locals [i]);
3376 while (td->ip < end) {
3377 g_assert (td->sp >= td->stack);
3378 g_assert (td->vt_sp < 0x10000000);
3379 in_offset = td->ip - header->code;
3380 if (!inlining)
3381 td->current_il_offset = in_offset;
3382 td->in_start = td->ip;
3383 InterpInst *prev_last_ins = td->last_ins;
3385 // Inlined method doesn't have clauses or branches
3386 if (!inlining && td->stack_height [in_offset] >= 0) {
3387 g_assert (td->is_bb_start [in_offset]);
3388 if (td->stack_height [in_offset] > 0)
3389 memcpy (td->stack, td->stack_state [in_offset], td->stack_height [in_offset] * sizeof(td->stack [0]));
3390 td->sp = td->stack + td->stack_height [in_offset];
3391 td->vt_sp = td->vt_stack_size [in_offset];
3394 if (in_offset == bb->end)
3395 bb = bb->next;
3397 if (bb->dead) {
3398 int op_size = mono_opcode_size (td->ip, end);
3399 g_assert (op_size > 0); /* The BB formation pass must catch all bad ops */
3401 if (td->verbose_level > 1)
3402 g_print ("SKIPPING DEAD OP at %x\n", in_offset);
3404 td->ip += op_size;
3405 continue;
3408 if (td->verbose_level > 1) {
3409 g_print ("IL_%04lx %s %-10s, sp %ld, %s %-12s vt_sp %u (max %u)\n",
3410 td->ip - td->il_code,
3411 td->is_bb_start [td->ip - td->il_code] == 3 ? "<>" :
3412 td->is_bb_start [td->ip - td->il_code] == 2 ? "< " :
3413 td->is_bb_start [td->ip - td->il_code] == 1 ? " >" : " ",
3414 mono_opcode_name (*td->ip), td->sp - td->stack,
3415 td->sp > td->stack ? stack_type_string [td->sp [-1].type] : " ",
3416 (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)) : "",
3417 td->vt_sp, td->max_vt_sp);
3420 if (sym_seq_points && mono_bitset_test_fast (seq_point_locs, td->ip - header->code)) {
3421 InterpBasicBlock *cbb = td->offset_to_bb [td->ip - header->code];
3422 g_assert (cbb);
3425 * Make methods interruptable at the beginning, and at the targets of
3426 * backward branches.
3428 if (in_offset == 0 || g_slist_length (cbb->preds) > 1)
3429 interp_add_ins (td, MINT_SDB_INTR_LOC);
3431 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3434 if (!inlining && td->is_bb_start [in_offset]) {
3435 int index = td->clause_indexes [in_offset];
3436 if (index != -1) {
3437 MonoExceptionClause *clause = &header->clauses [index];
3438 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
3439 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) &&
3440 in_offset == clause->handler_offset)
3441 interp_add_ins (td, MINT_START_ABORT_PROT);
3445 switch (*td->ip) {
3446 case CEE_NOP:
3447 /* lose it */
3448 emitted_funccall_seq_point = FALSE;
3449 ++td->ip;
3450 break;
3451 case CEE_BREAK:
3452 SIMPLE_OP(td, MINT_BREAK);
3453 break;
3454 case CEE_LDARG_0:
3455 case CEE_LDARG_1:
3456 case CEE_LDARG_2:
3457 case CEE_LDARG_3: {
3458 int arg_n = *td->ip - CEE_LDARG_0;
3459 if (!inlining)
3460 load_arg (td, arg_n);
3461 else
3462 load_local (td, arg_locals [arg_n]);
3463 ++td->ip;
3464 break;
3466 case CEE_LDLOC_0:
3467 case CEE_LDLOC_1:
3468 case CEE_LDLOC_2:
3469 case CEE_LDLOC_3: {
3470 int loc_n = *td->ip - CEE_LDLOC_0;
3471 if (!inlining)
3472 load_local (td, loc_n);
3473 else
3474 load_local (td, local_locals [loc_n]);
3475 ++td->ip;
3476 break;
3478 case CEE_STLOC_0:
3479 case CEE_STLOC_1:
3480 case CEE_STLOC_2:
3481 case CEE_STLOC_3: {
3482 int loc_n = *td->ip - CEE_STLOC_0;
3483 if (!inlining)
3484 store_local (td, loc_n);
3485 else
3486 store_local (td, local_locals [loc_n]);
3487 ++td->ip;
3488 break;
3490 case CEE_LDARG_S: {
3491 int arg_n = ((guint8 *)td->ip)[1];
3492 if (!inlining)
3493 load_arg (td, arg_n);
3494 else
3495 load_local (td, arg_locals [arg_n]);
3496 td->ip += 2;
3497 break;
3499 case CEE_LDARGA_S: {
3500 /* NOTE: n includes this */
3501 int n = ((guint8 *) td->ip) [1];
3503 if (!inlining) {
3504 get_arg_type_exact (td, n, &mt);
3505 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
3506 td->last_ins->data [0] = n;
3507 } else {
3508 int loc_n = arg_locals [n];
3509 interp_add_ins (td, MINT_LDLOCA_S);
3510 td->last_ins->data [0] = loc_n;
3511 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
3513 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3514 td->ip += 2;
3515 break;
3517 case CEE_STARG_S: {
3518 int arg_n = ((guint8 *)td->ip)[1];
3519 if (!inlining)
3520 store_arg (td, arg_n);
3521 else
3522 store_local (td, arg_locals [arg_n]);
3523 td->ip += 2;
3524 break;
3526 case CEE_LDLOC_S: {
3527 int loc_n = ((guint8 *)td->ip)[1];
3528 if (!inlining)
3529 load_local (td, loc_n);
3530 else
3531 load_local (td, local_locals [loc_n]);
3532 td->ip += 2;
3533 break;
3535 case CEE_LDLOCA_S: {
3536 int loc_n = ((guint8 *)td->ip)[1];
3537 interp_add_ins (td, MINT_LDLOCA_S);
3538 if (inlining)
3539 loc_n = local_locals [loc_n];
3540 td->last_ins->data [0] = loc_n;
3541 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
3542 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3543 td->ip += 2;
3544 break;
3546 case CEE_STLOC_S: {
3547 int loc_n = ((guint8 *)td->ip)[1];
3548 if (!inlining)
3549 store_local (td, loc_n);
3550 else
3551 store_local (td, local_locals [loc_n]);
3552 td->ip += 2;
3553 break;
3555 case CEE_LDNULL:
3556 SIMPLE_OP(td, MINT_LDNULL);
3557 PUSH_TYPE(td, STACK_TYPE_O, NULL);
3558 break;
3559 case CEE_LDC_I4_M1:
3560 SIMPLE_OP(td, MINT_LDC_I4_M1);
3561 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3562 break;
3563 case CEE_LDC_I4_0:
3564 // Only single basic block functions are inlined.
3565 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 &&
3566 td->sp > td->stack && td->sp [-1].type == STACK_TYPE_I4) {
3567 SIMPLE_OP(td, MINT_CEQ0_I4);
3568 td->ip += 2;
3569 } else {
3570 SIMPLE_OP(td, MINT_LDC_I4_0);
3571 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3573 break;
3574 case CEE_LDC_I4_1:
3575 // Only single basic block functions are inlined.
3576 if (td->ip - td->il_code + 1 < td->code_size && (inlining || !td->is_bb_start [td->ip + 1 - td->il_code]) &&
3577 (td->ip [1] == CEE_ADD || td->ip [1] == CEE_SUB) && td->sp [-1].type == STACK_TYPE_I4) {
3578 interp_add_ins (td, td->ip [1] == CEE_ADD ? MINT_ADD1_I4 : MINT_SUB1_I4);
3579 td->ip += 2;
3580 } else {
3581 SIMPLE_OP(td, MINT_LDC_I4_1);
3582 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3584 break;
3585 case CEE_LDC_I4_2:
3586 case CEE_LDC_I4_3:
3587 case CEE_LDC_I4_4:
3588 case CEE_LDC_I4_5:
3589 case CEE_LDC_I4_6:
3590 case CEE_LDC_I4_7:
3591 case CEE_LDC_I4_8:
3592 SIMPLE_OP(td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0);
3593 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3594 break;
3595 case CEE_LDC_I4_S:
3596 interp_add_ins (td, MINT_LDC_I4_S);
3597 td->last_ins->data [0] = ((gint8 *) td->ip) [1];
3598 td->ip += 2;
3599 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3600 break;
3601 case CEE_LDC_I4:
3602 i32 = read32 (td->ip + 1);
3603 interp_add_ins (td, MINT_LDC_I4);
3604 WRITE32_INS (td->last_ins, 0, &i32);
3605 td->ip += 5;
3606 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3607 break;
3608 case CEE_LDC_I8: {
3609 gint64 val = read64 (td->ip + 1);
3610 interp_add_ins (td, MINT_LDC_I8);
3611 WRITE64_INS (td->last_ins, 0, &val);
3612 td->ip += 9;
3613 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I8);
3614 break;
3616 case CEE_LDC_R4: {
3617 float val;
3618 readr4 (td->ip + 1, &val);
3619 interp_add_ins (td, MINT_LDC_R4);
3620 WRITE32_INS (td->last_ins, 0, &val);
3621 td->ip += 5;
3622 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R4);
3623 break;
3625 case CEE_LDC_R8: {
3626 double val;
3627 readr8 (td->ip + 1, &val);
3628 interp_add_ins (td, MINT_LDC_R8);
3629 WRITE64_INS (td->last_ins, 0, &val);
3630 td->ip += 9;
3631 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R8);
3632 break;
3634 case CEE_DUP: {
3635 int type = td->sp [-1].type;
3636 MonoClass *klass = td->sp [-1].klass;
3637 if (td->sp [-1].type == STACK_TYPE_VT) {
3638 gint32 size = mono_class_value_size (klass, NULL);
3639 PUSH_VT(td, size);
3640 interp_add_ins (td, MINT_DUP_VT);
3641 WRITE32_INS (td->last_ins, 0, &size);
3642 td->ip ++;
3643 } else
3644 SIMPLE_OP(td, MINT_DUP);
3645 PUSH_TYPE(td, type, klass);
3646 break;
3648 case CEE_POP:
3649 CHECK_STACK(td, 1);
3650 SIMPLE_OP(td, MINT_POP);
3651 if (td->sp [-1].type == STACK_TYPE_VT) {
3652 int size = mono_class_value_size (td->sp [-1].klass, NULL);
3653 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
3654 interp_add_ins (td, MINT_VTRESULT);
3655 td->last_ins->data [0] = 0;
3656 WRITE32_INS (td->last_ins, 1, &size);
3657 td->vt_sp -= size;
3659 --td->sp;
3660 break;
3661 case CEE_JMP: {
3662 MonoMethod *m;
3663 INLINE_FAILURE;
3664 if (td->sp > td->stack)
3665 g_warning ("CEE_JMP: stack must be empty");
3666 token = read32 (td->ip + 1);
3667 m = mono_get_method_checked (image, token, NULL, generic_context, error);
3668 goto_if_nok (error, exit);
3669 interp_add_ins (td, MINT_JMP);
3670 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3671 goto_if_nok (error, exit);
3672 td->ip += 5;
3673 break;
3675 case CEE_CALLVIRT: /* Fall through */
3676 case CEE_CALLI: /* Fall through */
3677 case CEE_CALL: {
3678 gboolean need_seq_point = FALSE;
3680 if (sym_seq_points && !mono_bitset_test_fast (seq_point_locs, td->ip + 5 - header->code))
3681 need_seq_point = TRUE;
3683 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, constrained_class, readonly, error, TRUE, save_last_error))
3684 goto exit;
3686 if (need_seq_point) {
3687 //check is is a nested call and remove the MONO_INST_NONEMPTY_STACK of the last breakpoint, only for non native methods
3688 if (!(method->flags & METHOD_IMPL_ATTRIBUTE_NATIVE)) {
3689 if (emitted_funccall_seq_point) {
3690 if (last_seq_point)
3691 last_seq_point->flags |= INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL;
3693 else
3694 emitted_funccall_seq_point = TRUE;
3696 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3697 // This seq point is actually associated with the instruction following the call
3698 last_seq_point->il_offset = td->ip - header->code;
3699 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK;
3702 constrained_class = NULL;
3703 readonly = FALSE;
3704 save_last_error = FALSE;
3705 break;
3707 case CEE_RET: {
3708 /* Return from inlined method, return value is on top of stack */
3709 if (td->method != method) {
3710 td->ip++;
3711 break;
3714 int vt_size = 0;
3715 MonoType *ult = mini_type_get_underlying_type (signature->ret);
3716 if (ult->type != MONO_TYPE_VOID) {
3717 CHECK_STACK (td, 1);
3718 --td->sp;
3719 if (mint_type (ult) == MINT_TYPE_VT) {
3720 MonoClass *klass = mono_class_from_mono_type_internal (ult);
3721 vt_size = mono_class_value_size (klass, NULL);
3724 if (td->sp > td->stack) {
3725 mono_error_set_generic_error (error, "System", "InvalidProgramException", "");
3726 goto exit;
3728 if (td->vt_sp != ALIGN_TO (vt_size, MINT_VT_ALIGNMENT))
3729 g_error ("%s: CEE_RET: value type stack: %d vs. %d", mono_method_full_name (td->method, TRUE), td->vt_sp, vt_size);
3731 if (sym_seq_points) {
3732 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3733 td->last_ins->flags |= INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT;
3736 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
3737 /* This does the return as well */
3738 if (ult->type == MONO_TYPE_VOID) {
3739 interp_add_ins (td, MINT_TRACE_EXIT_VOID);
3740 vt_size = -1;
3741 } else {
3742 interp_add_ins (td, MINT_TRACE_EXIT);
3744 WRITE32_INS (td->last_ins, 0, &vt_size);
3745 ++td->ip;
3746 } else {
3747 if (vt_size == 0)
3748 SIMPLE_OP(td, ult->type == MONO_TYPE_VOID ? MINT_RET_VOID : MINT_RET);
3749 else {
3750 interp_add_ins (td, MINT_RET_VT);
3751 WRITE32_INS (td->last_ins, 0, &vt_size);
3752 ++td->ip;
3755 break;
3757 case CEE_BR: {
3758 int offset = read32 (td->ip + 1);
3759 if (offset) {
3760 INLINE_FAILURE;
3761 handle_branch (td, MINT_BR_S, MINT_BR, 5 + offset);
3763 td->ip += 5;
3764 break;
3766 case CEE_BR_S: {
3767 int offset = (gint8)td->ip [1];
3768 if (offset) {
3769 INLINE_FAILURE;
3770 handle_branch (td, MINT_BR_S, MINT_BR, 2 + (gint8)td->ip [1]);
3772 td->ip += 2;
3773 break;
3775 case CEE_BRFALSE:
3776 INLINE_FAILURE;
3777 one_arg_branch (td, MINT_BRFALSE_I4, 5 + read32 (td->ip + 1));
3778 td->ip += 5;
3779 break;
3780 case CEE_BRFALSE_S:
3781 INLINE_FAILURE;
3782 one_arg_branch (td, MINT_BRFALSE_I4, 2 + (gint8)td->ip [1]);
3783 td->ip += 2;
3784 break;
3785 case CEE_BRTRUE:
3786 INLINE_FAILURE;
3787 one_arg_branch (td, MINT_BRTRUE_I4, 5 + read32 (td->ip + 1));
3788 td->ip += 5;
3789 break;
3790 case CEE_BRTRUE_S:
3791 INLINE_FAILURE;
3792 one_arg_branch (td, MINT_BRTRUE_I4, 2 + (gint8)td->ip [1]);
3793 td->ip += 2;
3794 break;
3795 case CEE_BEQ:
3796 INLINE_FAILURE;
3797 two_arg_branch (td, MINT_BEQ_I4, 5 + read32 (td->ip + 1));
3798 td->ip += 5;
3799 break;
3800 case CEE_BEQ_S:
3801 INLINE_FAILURE;
3802 two_arg_branch (td, MINT_BEQ_I4, 2 + (gint8) td->ip [1]);
3803 td->ip += 2;
3804 break;
3805 case CEE_BGE:
3806 INLINE_FAILURE;
3807 two_arg_branch (td, MINT_BGE_I4, 5 + read32 (td->ip + 1));
3808 td->ip += 5;
3809 break;
3810 case CEE_BGE_S:
3811 INLINE_FAILURE;
3812 two_arg_branch (td, MINT_BGE_I4, 2 + (gint8) td->ip [1]);
3813 td->ip += 2;
3814 break;
3815 case CEE_BGT:
3816 INLINE_FAILURE;
3817 two_arg_branch (td, MINT_BGT_I4, 5 + read32 (td->ip + 1));
3818 td->ip += 5;
3819 break;
3820 case CEE_BGT_S:
3821 INLINE_FAILURE;
3822 two_arg_branch (td, MINT_BGT_I4, 2 + (gint8) td->ip [1]);
3823 td->ip += 2;
3824 break;
3825 case CEE_BLT:
3826 INLINE_FAILURE;
3827 two_arg_branch (td, MINT_BLT_I4, 5 + read32 (td->ip + 1));
3828 td->ip += 5;
3829 break;
3830 case CEE_BLT_S:
3831 INLINE_FAILURE;
3832 two_arg_branch (td, MINT_BLT_I4, 2 + (gint8) td->ip [1]);
3833 td->ip += 2;
3834 break;
3835 case CEE_BLE:
3836 INLINE_FAILURE;
3837 two_arg_branch (td, MINT_BLE_I4, 5 + read32 (td->ip + 1));
3838 td->ip += 5;
3839 break;
3840 case CEE_BLE_S:
3841 INLINE_FAILURE;
3842 two_arg_branch (td, MINT_BLE_I4, 2 + (gint8) td->ip [1]);
3843 td->ip += 2;
3844 break;
3845 case CEE_BNE_UN:
3846 INLINE_FAILURE;
3847 two_arg_branch (td, MINT_BNE_UN_I4, 5 + read32 (td->ip + 1));
3848 td->ip += 5;
3849 break;
3850 case CEE_BNE_UN_S:
3851 INLINE_FAILURE;
3852 two_arg_branch (td, MINT_BNE_UN_I4, 2 + (gint8) td->ip [1]);
3853 td->ip += 2;
3854 break;
3855 case CEE_BGE_UN:
3856 INLINE_FAILURE;
3857 two_arg_branch (td, MINT_BGE_UN_I4, 5 + read32 (td->ip + 1));
3858 td->ip += 5;
3859 break;
3860 case CEE_BGE_UN_S:
3861 INLINE_FAILURE;
3862 two_arg_branch (td, MINT_BGE_UN_I4, 2 + (gint8) td->ip [1]);
3863 td->ip += 2;
3864 break;
3865 case CEE_BGT_UN:
3866 INLINE_FAILURE;
3867 two_arg_branch (td, MINT_BGT_UN_I4, 5 + read32 (td->ip + 1));
3868 td->ip += 5;
3869 break;
3870 case CEE_BGT_UN_S:
3871 INLINE_FAILURE;
3872 two_arg_branch (td, MINT_BGT_UN_I4, 2 + (gint8) td->ip [1]);
3873 td->ip += 2;
3874 break;
3875 case CEE_BLE_UN:
3876 INLINE_FAILURE;
3877 two_arg_branch (td, MINT_BLE_UN_I4, 5 + read32 (td->ip + 1));
3878 td->ip += 5;
3879 break;
3880 case CEE_BLE_UN_S:
3881 INLINE_FAILURE;
3882 two_arg_branch (td, MINT_BLE_UN_I4, 2 + (gint8) td->ip [1]);
3883 td->ip += 2;
3884 break;
3885 case CEE_BLT_UN:
3886 INLINE_FAILURE;
3887 two_arg_branch (td, MINT_BLT_UN_I4, 5 + read32 (td->ip + 1));
3888 td->ip += 5;
3889 break;
3890 case CEE_BLT_UN_S:
3891 INLINE_FAILURE;
3892 two_arg_branch (td, MINT_BLT_UN_I4, 2 + (gint8) td->ip [1]);
3893 td->ip += 2;
3894 break;
3895 case CEE_SWITCH: {
3896 INLINE_FAILURE;
3897 guint32 n;
3898 const unsigned char *next_ip;
3899 ++td->ip;
3900 n = read32 (td->ip);
3901 interp_add_ins_explicit (td, MINT_SWITCH, MINT_SWITCH_LEN (n));
3902 WRITE32_INS (td->last_ins, 0, &n);
3903 td->ip += 4;
3904 next_ip = td->ip + n * 4;
3905 --td->sp;
3906 int stack_height = td->sp - td->stack;
3907 for (i = 0; i < n; i++) {
3908 offset = read32 (td->ip);
3909 target = next_ip - td->il_code + offset;
3910 if (offset < 0) {
3911 #if DEBUG_INTERP
3912 if (stack_height > 0 && stack_height != td->stack_height [target])
3913 g_warning ("SWITCH with back branch and non-empty stack");
3914 #endif
3915 } else {
3916 td->stack_height [target] = stack_height;
3917 td->vt_stack_size [target] = td->vt_sp;
3918 if (stack_height > 0)
3919 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, stack_height * sizeof (td->stack [0]));
3921 WRITE32_INS (td->last_ins, 2 + i * 2, &target);
3922 td->ip += 4;
3924 break;
3926 case CEE_LDIND_I1:
3927 CHECK_STACK (td, 1);
3928 SIMPLE_OP (td, MINT_LDIND_I1_CHECK);
3929 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3930 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3931 break;
3932 case CEE_LDIND_U1:
3933 CHECK_STACK (td, 1);
3934 SIMPLE_OP (td, MINT_LDIND_U1_CHECK);
3935 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3936 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3937 break;
3938 case CEE_LDIND_I2:
3939 CHECK_STACK (td, 1);
3940 SIMPLE_OP (td, MINT_LDIND_I2_CHECK);
3941 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3942 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3943 break;
3944 case CEE_LDIND_U2:
3945 CHECK_STACK (td, 1);
3946 SIMPLE_OP (td, MINT_LDIND_U2_CHECK);
3947 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3948 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3949 break;
3950 case CEE_LDIND_I4:
3951 CHECK_STACK (td, 1);
3952 SIMPLE_OP (td, MINT_LDIND_I4_CHECK);
3953 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3954 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3955 break;
3956 case CEE_LDIND_U4:
3957 CHECK_STACK (td, 1);
3958 SIMPLE_OP (td, MINT_LDIND_U4_CHECK);
3959 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3960 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3961 break;
3962 case CEE_LDIND_I8:
3963 CHECK_STACK (td, 1);
3964 SIMPLE_OP (td, MINT_LDIND_I8_CHECK);
3965 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3966 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3967 break;
3968 case CEE_LDIND_I:
3969 CHECK_STACK (td, 1);
3970 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3971 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3972 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3973 break;
3974 case CEE_LDIND_R4:
3975 CHECK_STACK (td, 1);
3976 SIMPLE_OP (td, MINT_LDIND_R4_CHECK);
3977 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
3978 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3979 break;
3980 case CEE_LDIND_R8:
3981 CHECK_STACK (td, 1);
3982 SIMPLE_OP (td, MINT_LDIND_R8_CHECK);
3983 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3984 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3985 break;
3986 case CEE_LDIND_REF:
3987 CHECK_STACK (td, 1);
3988 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3989 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3990 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
3991 break;
3992 case CEE_STIND_REF:
3993 CHECK_STACK (td, 2);
3994 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
3995 SIMPLE_OP (td, MINT_STIND_REF);
3996 td->sp -= 2;
3997 break;
3998 case CEE_STIND_I1:
3999 CHECK_STACK (td, 2);
4000 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4001 SIMPLE_OP (td, MINT_STIND_I1);
4002 td->sp -= 2;
4003 break;
4004 case CEE_STIND_I2:
4005 CHECK_STACK (td, 2);
4006 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4007 SIMPLE_OP (td, MINT_STIND_I2);
4008 td->sp -= 2;
4009 break;
4010 case CEE_STIND_I4:
4011 CHECK_STACK (td, 2);
4012 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4013 SIMPLE_OP (td, MINT_STIND_I4);
4014 td->sp -= 2;
4015 break;
4016 case CEE_STIND_I:
4017 CHECK_STACK (td, 2);
4018 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4019 SIMPLE_OP (td, MINT_STIND_I);
4020 td->sp -= 2;
4021 break;
4022 case CEE_STIND_I8:
4023 CHECK_STACK (td, 2);
4024 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4025 SIMPLE_OP (td, MINT_STIND_I8);
4026 td->sp -= 2;
4027 break;
4028 case CEE_STIND_R4:
4029 CHECK_STACK (td, 2);
4030 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4031 SIMPLE_OP (td, MINT_STIND_R4);
4032 td->sp -= 2;
4033 break;
4034 case CEE_STIND_R8:
4035 CHECK_STACK (td, 2);
4036 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4037 SIMPLE_OP (td, MINT_STIND_R8);
4038 td->sp -= 2;
4039 break;
4040 case CEE_ADD:
4041 binary_arith_op(td, MINT_ADD_I4);
4042 ++td->ip;
4043 break;
4044 case CEE_SUB:
4045 binary_arith_op(td, MINT_SUB_I4);
4046 ++td->ip;
4047 break;
4048 case CEE_MUL:
4049 binary_arith_op(td, MINT_MUL_I4);
4050 ++td->ip;
4051 break;
4052 case CEE_DIV:
4053 binary_arith_op(td, MINT_DIV_I4);
4054 ++td->ip;
4055 break;
4056 case CEE_DIV_UN:
4057 binary_arith_op(td, MINT_DIV_UN_I4);
4058 ++td->ip;
4059 break;
4060 case CEE_REM:
4061 binary_arith_op (td, MINT_REM_I4);
4062 ++td->ip;
4063 break;
4064 case CEE_REM_UN:
4065 binary_arith_op (td, MINT_REM_UN_I4);
4066 ++td->ip;
4067 break;
4068 case CEE_AND:
4069 binary_arith_op (td, MINT_AND_I4);
4070 ++td->ip;
4071 break;
4072 case CEE_OR:
4073 binary_arith_op (td, MINT_OR_I4);
4074 ++td->ip;
4075 break;
4076 case CEE_XOR:
4077 binary_arith_op (td, MINT_XOR_I4);
4078 ++td->ip;
4079 break;
4080 case CEE_SHL:
4081 shift_op (td, MINT_SHL_I4);
4082 ++td->ip;
4083 break;
4084 case CEE_SHR:
4085 shift_op (td, MINT_SHR_I4);
4086 ++td->ip;
4087 break;
4088 case CEE_SHR_UN:
4089 shift_op (td, MINT_SHR_UN_I4);
4090 ++td->ip;
4091 break;
4092 case CEE_NEG:
4093 unary_arith_op (td, MINT_NEG_I4);
4094 ++td->ip;
4095 break;
4096 case CEE_NOT:
4097 unary_arith_op (td, MINT_NOT_I4);
4098 ++td->ip;
4099 break;
4100 case CEE_CONV_U1:
4101 CHECK_STACK (td, 1);
4102 switch (td->sp [-1].type) {
4103 case STACK_TYPE_R4:
4104 interp_add_ins (td, MINT_CONV_U1_R4);
4105 break;
4106 case STACK_TYPE_R8:
4107 interp_add_ins (td, MINT_CONV_U1_R8);
4108 break;
4109 case STACK_TYPE_I4:
4110 interp_add_ins (td, MINT_CONV_U1_I4);
4111 break;
4112 case STACK_TYPE_I8:
4113 interp_add_ins (td, MINT_CONV_U1_I8);
4114 break;
4115 default:
4116 g_assert_not_reached ();
4118 ++td->ip;
4119 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4120 break;
4121 case CEE_CONV_I1:
4122 CHECK_STACK (td, 1);
4123 switch (td->sp [-1].type) {
4124 case STACK_TYPE_R4:
4125 interp_add_ins (td, MINT_CONV_I1_R4);
4126 break;
4127 case STACK_TYPE_R8:
4128 interp_add_ins (td, MINT_CONV_I1_R8);
4129 break;
4130 case STACK_TYPE_I4:
4131 interp_add_ins (td, MINT_CONV_I1_I4);
4132 break;
4133 case STACK_TYPE_I8:
4134 interp_add_ins (td, MINT_CONV_I1_I8);
4135 break;
4136 default:
4137 g_assert_not_reached ();
4139 ++td->ip;
4140 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4141 break;
4142 case CEE_CONV_U2:
4143 CHECK_STACK (td, 1);
4144 switch (td->sp [-1].type) {
4145 case STACK_TYPE_R4:
4146 interp_add_ins (td, MINT_CONV_U2_R4);
4147 break;
4148 case STACK_TYPE_R8:
4149 interp_add_ins (td, MINT_CONV_U2_R8);
4150 break;
4151 case STACK_TYPE_I4:
4152 interp_add_ins (td, MINT_CONV_U2_I4);
4153 break;
4154 case STACK_TYPE_I8:
4155 interp_add_ins (td, MINT_CONV_U2_I8);
4156 break;
4157 default:
4158 g_assert_not_reached ();
4160 ++td->ip;
4161 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4162 break;
4163 case CEE_CONV_I2:
4164 CHECK_STACK (td, 1);
4165 switch (td->sp [-1].type) {
4166 case STACK_TYPE_R4:
4167 interp_add_ins (td, MINT_CONV_I2_R4);
4168 break;
4169 case STACK_TYPE_R8:
4170 interp_add_ins (td, MINT_CONV_I2_R8);
4171 break;
4172 case STACK_TYPE_I4:
4173 interp_add_ins (td, MINT_CONV_I2_I4);
4174 break;
4175 case STACK_TYPE_I8:
4176 interp_add_ins (td, MINT_CONV_I2_I8);
4177 break;
4178 default:
4179 g_assert_not_reached ();
4181 ++td->ip;
4182 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4183 break;
4184 case CEE_CONV_U:
4185 CHECK_STACK (td, 1);
4186 switch (td->sp [-1].type) {
4187 case STACK_TYPE_R8:
4188 #if SIZEOF_VOID_P == 4
4189 interp_add_ins (td, MINT_CONV_U4_R8);
4190 #else
4191 interp_add_ins (td, MINT_CONV_U8_R8);
4192 #endif
4193 break;
4194 case STACK_TYPE_I4:
4195 #if SIZEOF_VOID_P == 8
4196 interp_add_ins (td, MINT_CONV_I8_U4);
4197 #endif
4198 break;
4199 case STACK_TYPE_I8:
4200 #if SIZEOF_VOID_P == 4
4201 interp_add_ins (td, MINT_CONV_U4_I8);
4202 #endif
4203 break;
4204 case STACK_TYPE_MP:
4205 case STACK_TYPE_O:
4206 break;
4207 default:
4208 g_assert_not_reached ();
4210 ++td->ip;
4211 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4212 break;
4213 case CEE_CONV_I:
4214 CHECK_STACK (td, 1);
4215 switch (td->sp [-1].type) {
4216 case STACK_TYPE_R8:
4217 #if SIZEOF_VOID_P == 8
4218 interp_add_ins (td, MINT_CONV_I8_R8);
4219 #else
4220 interp_add_ins (td, MINT_CONV_I4_R8);
4221 #endif
4222 break;
4223 case STACK_TYPE_I4:
4224 #if SIZEOF_VOID_P == 8
4225 interp_add_ins (td, MINT_CONV_I8_I4);
4226 #endif
4227 break;
4228 case STACK_TYPE_O:
4229 break;
4230 case STACK_TYPE_MP:
4231 break;
4232 case STACK_TYPE_I8:
4233 #if SIZEOF_VOID_P == 4
4234 interp_add_ins (td, MINT_CONV_I4_I8);
4235 #endif
4236 break;
4237 default:
4238 g_assert_not_reached ();
4240 ++td->ip;
4241 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4242 break;
4243 case CEE_CONV_U4:
4244 CHECK_STACK (td, 1);
4245 switch (td->sp [-1].type) {
4246 case STACK_TYPE_R4:
4247 interp_add_ins (td, MINT_CONV_U4_R4);
4248 break;
4249 case STACK_TYPE_R8:
4250 interp_add_ins (td, MINT_CONV_U4_R8);
4251 break;
4252 case STACK_TYPE_I4:
4253 break;
4254 case STACK_TYPE_I8:
4255 interp_add_ins (td, MINT_CONV_U4_I8);
4256 break;
4257 case STACK_TYPE_MP:
4258 #if SIZEOF_VOID_P == 8
4259 interp_add_ins (td, MINT_CONV_U4_I8);
4260 #endif
4261 break;
4262 default:
4263 g_assert_not_reached ();
4265 ++td->ip;
4266 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4267 break;
4268 case CEE_CONV_I4:
4269 CHECK_STACK (td, 1);
4270 switch (td->sp [-1].type) {
4271 case STACK_TYPE_R4:
4272 interp_add_ins (td, MINT_CONV_I4_R4);
4273 break;
4274 case STACK_TYPE_R8:
4275 interp_add_ins (td, MINT_CONV_I4_R8);
4276 break;
4277 case STACK_TYPE_I4:
4278 break;
4279 case STACK_TYPE_I8:
4280 interp_add_ins (td, MINT_CONV_I4_I8);
4281 break;
4282 case STACK_TYPE_MP:
4283 #if SIZEOF_VOID_P == 8
4284 interp_add_ins (td, MINT_CONV_I4_I8);
4285 #endif
4286 break;
4287 default:
4288 g_assert_not_reached ();
4290 ++td->ip;
4291 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4292 break;
4293 case CEE_CONV_I8:
4294 CHECK_STACK (td, 1);
4295 switch (td->sp [-1].type) {
4296 case STACK_TYPE_R4:
4297 interp_add_ins (td, MINT_CONV_I8_R4);
4298 break;
4299 case STACK_TYPE_R8:
4300 interp_add_ins (td, MINT_CONV_I8_R8);
4301 break;
4302 case STACK_TYPE_I4: {
4303 if (interp_ins_is_ldc (td->last_ins) && (inlining || !td->is_bb_start [in_offset])) {
4304 gint64 ct = interp_get_const_from_ldc_i4 (td->last_ins);
4305 interp_clear_ins (td, td->last_ins);
4307 interp_add_ins (td, MINT_LDC_I8);
4308 WRITE64_INS (td->last_ins, 0, &ct);
4309 } else {
4310 interp_add_ins (td, MINT_CONV_I8_I4);
4312 break;
4314 case STACK_TYPE_I8:
4315 break;
4316 case STACK_TYPE_MP:
4317 #if SIZEOF_VOID_P == 4
4318 interp_add_ins (td, MINT_CONV_I8_I4);
4319 #endif
4320 break;
4321 default:
4322 g_assert_not_reached ();
4324 ++td->ip;
4325 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4326 break;
4327 case CEE_CONV_R4:
4328 CHECK_STACK (td, 1);
4329 switch (td->sp [-1].type) {
4330 case STACK_TYPE_R8:
4331 interp_add_ins (td, MINT_CONV_R4_R8);
4332 break;
4333 case STACK_TYPE_I8:
4334 interp_add_ins (td, MINT_CONV_R4_I8);
4335 break;
4336 case STACK_TYPE_I4:
4337 interp_add_ins (td, MINT_CONV_R4_I4);
4338 break;
4339 case STACK_TYPE_R4:
4340 /* no-op */
4341 break;
4342 default:
4343 g_assert_not_reached ();
4345 ++td->ip;
4346 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4347 break;
4348 case CEE_CONV_R8:
4349 CHECK_STACK (td, 1);
4350 switch (td->sp [-1].type) {
4351 case STACK_TYPE_I4:
4352 interp_add_ins (td, MINT_CONV_R8_I4);
4353 break;
4354 case STACK_TYPE_I8:
4355 interp_add_ins (td, MINT_CONV_R8_I8);
4356 break;
4357 case STACK_TYPE_R4:
4358 interp_add_ins (td, MINT_CONV_R8_R4);
4359 break;
4360 case STACK_TYPE_R8:
4361 break;
4362 default:
4363 g_assert_not_reached ();
4365 ++td->ip;
4366 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4367 break;
4368 case CEE_CONV_U8:
4369 CHECK_STACK (td, 1);
4370 switch (td->sp [-1].type) {
4371 case STACK_TYPE_I4:
4372 if (interp_ins_is_ldc (td->last_ins) && (inlining || !td->is_bb_start [in_offset])) {
4373 gint64 ct = (guint32)interp_get_const_from_ldc_i4 (td->last_ins);
4374 interp_clear_ins (td, td->last_ins);
4376 interp_add_ins (td, MINT_LDC_I8);
4377 WRITE64_INS (td->last_ins, 0, &ct);
4378 } else {
4379 interp_add_ins (td, MINT_CONV_I8_U4);
4381 break;
4382 case STACK_TYPE_I8:
4383 break;
4384 case STACK_TYPE_R4:
4385 interp_add_ins (td, MINT_CONV_U8_R4);
4386 break;
4387 case STACK_TYPE_R8:
4388 interp_add_ins (td, MINT_CONV_U8_R8);
4389 break;
4390 case STACK_TYPE_MP:
4391 #if SIZEOF_VOID_P == 4
4392 interp_add_ins (td, MINT_CONV_I8_U4);
4393 #endif
4394 break;
4395 default:
4396 g_assert_not_reached ();
4398 ++td->ip;
4399 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4400 break;
4401 case CEE_CPOBJ: {
4402 CHECK_STACK (td, 2);
4404 token = read32 (td->ip + 1);
4405 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4406 goto_if_nok (error, exit);
4408 if (m_class_is_valuetype (klass)) {
4409 int mt = mint_type (m_class_get_byval_arg (klass));
4410 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_CPOBJ_VT : MINT_CPOBJ);
4411 td->last_ins->data [0] = get_data_item_index(td, klass);
4412 } else {
4413 interp_add_ins (td, MINT_LDIND_REF);
4414 interp_add_ins (td, MINT_STIND_REF);
4416 td->ip += 5;
4417 td->sp -= 2;
4418 break;
4420 case CEE_LDOBJ: {
4421 CHECK_STACK (td, 1);
4423 token = read32 (td->ip + 1);
4425 if (method->wrapper_type != MONO_WRAPPER_NONE)
4426 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4427 else {
4428 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4429 goto_if_nok (error, exit);
4432 interp_emit_ldobj (td, klass);
4434 td->ip += 5;
4435 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
4436 break;
4438 case CEE_LDSTR: {
4439 token = mono_metadata_token_index (read32 (td->ip + 1));
4440 td->ip += 5;
4441 if (method->wrapper_type == MONO_WRAPPER_NONE) {
4442 MonoString *s = mono_ldstr_checked (domain, image, token, error);
4443 goto_if_nok (error, exit);
4444 /* GC won't scan code stream, but reference is held by metadata
4445 * machinery so we are good here */
4446 interp_add_ins (td, MINT_LDSTR);
4447 td->last_ins->data [0] = get_data_item_index (td, s);
4448 } else {
4449 /* defer allocation to execution-time */
4450 interp_add_ins (td, MINT_LDSTR_TOKEN);
4451 td->last_ins->data [0] = get_data_item_index (td, GUINT_TO_POINTER (token));
4453 PUSH_TYPE(td, STACK_TYPE_O, mono_defaults.string_class);
4454 break;
4456 case CEE_NEWOBJ: {
4457 MonoMethod *m;
4458 MonoMethodSignature *csignature;
4459 guint32 vt_stack_used = 0;
4460 guint32 vt_res_size = 0;
4462 td->ip++;
4463 token = read32 (td->ip);
4464 td->ip += 4;
4466 if (method->wrapper_type != MONO_WRAPPER_NONE)
4467 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
4468 else {
4469 m = mono_get_method_checked (image, token, NULL, generic_context, error);
4470 goto_if_nok (error, exit);
4473 csignature = mono_method_signature_internal (m);
4474 klass = m->klass;
4476 if (!mono_class_init_internal (klass)) {
4477 mono_error_set_for_class_failure (error, klass);
4478 goto_if_nok (error, exit);
4481 if (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_ABSTRACT) {
4482 char* full_name = mono_type_get_full_name (klass);
4483 mono_error_set_member_access (error, "Cannot create an abstract class: %s", full_name);
4484 g_free (full_name);
4485 goto_if_nok (error, exit);
4488 if (mono_class_is_magic_int (klass) || mono_class_is_magic_float (klass)) {
4489 td->sp -= csignature->param_count;
4490 #if SIZEOF_VOID_P == 8
4491 if (mono_class_is_magic_int (klass) && td->sp [0].type == STACK_TYPE_I4)
4492 interp_add_ins (td, MINT_CONV_I8_I4);
4493 else if (mono_class_is_magic_float (klass) && td->sp [0].type == STACK_TYPE_R4)
4494 interp_add_ins (td, MINT_CONV_R8_R4);
4495 #endif
4496 interp_add_ins (td, MINT_NEWOBJ_MAGIC);
4497 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4498 goto_if_nok (error, exit);
4500 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4501 } else {
4502 if (m_class_get_parent (klass) == mono_defaults.array_class) {
4503 interp_add_ins (td, MINT_NEWOBJ_ARRAY);
4504 td->last_ins->data [0] = get_data_item_index (td, m->klass);
4505 td->last_ins->data [1] = csignature->param_count;
4506 } else if (m_class_get_image (klass) == mono_defaults.corlib &&
4507 !strcmp (m_class_get_name (m->klass), "ByReference`1") &&
4508 !strcmp (m->name, ".ctor")) {
4509 /* public ByReference(ref T value) */
4510 g_assert (csignature->hasthis && csignature->param_count == 1);
4511 interp_add_ins (td, MINT_INTRINS_BYREFERENCE_CTOR);
4512 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4513 } else if (klass != mono_defaults.string_class &&
4514 !mono_class_is_marshalbyref (klass) &&
4515 !mono_class_has_finalizer (klass) &&
4516 !m_class_has_weak_fields (klass)) {
4517 if (!m_class_is_valuetype (klass)) {
4518 InterpInst *newobj_fast = interp_add_ins (td, MINT_NEWOBJ_FAST);
4520 newobj_fast->data [1] = csignature->param_count;
4522 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
4523 goto_if_nok (error, exit);
4524 newobj_fast->data [2] = get_data_item_index (td, vtable);
4526 move_stack (td, (td->sp - td->stack) - csignature->param_count, 2);
4528 StackInfo *tmp_sp = td->sp - csignature->param_count - 2;
4529 SET_TYPE (tmp_sp, STACK_TYPE_O, klass);
4530 SET_TYPE (tmp_sp + 1, STACK_TYPE_O, klass);
4532 if ((mono_interp_opt & INTERP_OPT_INLINE) && interp_method_check_inlining (td, m)) {
4533 MonoMethodHeader *mheader = interp_method_get_header (m, error);
4534 goto_if_nok (error, exit);
4536 if (interp_inline_method (td, m, mheader, error)) {
4537 newobj_fast->data [0] = INLINED_METHOD_FLAG;
4538 break;
4541 // If inlining failed we need to restore the stack
4542 move_stack (td, (td->sp - td->stack) - csignature->param_count, -2);
4543 // Set the method to be executed as part of newobj instruction
4544 newobj_fast->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4545 } else {
4546 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4547 interp_add_ins (td, MINT_NEWOBJ_VTST_FAST);
4548 else
4549 interp_add_ins (td, MINT_NEWOBJ_VT_FAST);
4551 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4552 td->last_ins->data [1] = csignature->param_count;
4554 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4555 td->last_ins->data [2] = mono_class_value_size (klass, NULL);
4558 } else {
4559 interp_add_ins (td, MINT_NEWOBJ);
4560 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4562 goto_if_nok (error, exit);
4563 /* The constructor was not inlined, abort inlining of current method */
4564 INLINE_FAILURE;
4566 td->sp -= csignature->param_count;
4567 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4568 vt_res_size = mono_class_value_size (klass, NULL);
4569 PUSH_VT (td, vt_res_size);
4571 for (i = 0; i < csignature->param_count; ++i) {
4572 int mt = mint_type(csignature->params [i]);
4573 if (mt == MINT_TYPE_VT) {
4574 MonoClass *k = mono_class_from_mono_type_internal (csignature->params [i]);
4575 gint32 size = mono_class_value_size (k, NULL);
4576 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4577 vt_stack_used += size;
4580 if (vt_stack_used != 0 || vt_res_size != 0) {
4581 interp_add_ins (td, MINT_VTRESULT);
4582 td->last_ins->data [0] = vt_res_size;
4583 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
4584 td->vt_sp -= vt_stack_used;
4586 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4588 break;
4590 case CEE_CASTCLASS:
4591 case CEE_ISINST: {
4592 gboolean isinst_instr = *td->ip == CEE_ISINST;
4593 CHECK_STACK (td, 1);
4594 token = read32 (td->ip + 1);
4595 klass = mini_get_class (method, token, generic_context);
4596 CHECK_TYPELOAD (klass);
4597 interp_handle_isinst (td, klass, isinst_instr);
4598 if (!isinst_instr)
4599 td->sp [-1].klass = klass;
4600 break;
4602 case CEE_CONV_R_UN:
4603 switch (td->sp [-1].type) {
4604 case STACK_TYPE_R8:
4605 break;
4606 case STACK_TYPE_I8:
4607 interp_add_ins (td, MINT_CONV_R_UN_I8);
4608 break;
4609 case STACK_TYPE_I4:
4610 interp_add_ins (td, MINT_CONV_R_UN_I4);
4611 break;
4612 default:
4613 g_assert_not_reached ();
4615 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4616 ++td->ip;
4617 break;
4618 case CEE_UNBOX:
4619 CHECK_STACK (td, 1);
4620 token = read32 (td->ip + 1);
4622 if (method->wrapper_type != MONO_WRAPPER_NONE)
4623 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4624 else {
4625 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4626 goto_if_nok (error, exit);
4629 if (mono_class_is_nullable (klass)) {
4630 MonoMethod *target_method;
4631 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4632 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4633 else
4634 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4635 goto_if_nok (error, exit);
4636 /* td->ip is incremented by interp_transform_call */
4637 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4638 goto exit;
4640 * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type
4641 * We create a local variable in the frame so that we can fetch its address.
4643 int local = create_interp_local (td, m_class_get_byval_arg (klass));
4644 store_local (td, local);
4645 interp_add_ins (td, MINT_LDLOCA_S);
4646 td->last_ins->data [0] = local;
4647 td->locals [local].flags |= INTERP_LOCAL_FLAG_INDIRECT;
4648 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
4649 } else {
4650 interp_add_ins (td, MINT_UNBOX);
4651 td->last_ins->data [0] = get_data_item_index (td, klass);
4652 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
4653 td->ip += 5;
4655 break;
4656 case CEE_UNBOX_ANY:
4657 CHECK_STACK (td, 1);
4658 token = read32 (td->ip + 1);
4660 klass = mini_get_class (method, token, generic_context);
4661 CHECK_TYPELOAD (klass);
4663 if (mini_type_is_reference (m_class_get_byval_arg (klass))) {
4664 int mt = mint_type (m_class_get_byval_arg (klass));
4665 interp_handle_isinst (td, klass, FALSE);
4666 SET_TYPE (td->sp - 1, stack_type [mt], klass);
4667 } else if (mono_class_is_nullable (klass)) {
4668 MonoMethod *target_method;
4669 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4670 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4671 else
4672 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4673 goto_if_nok (error, exit);
4674 /* td->ip is incremented by interp_transform_call */
4675 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4676 goto exit;
4677 } else {
4678 interp_add_ins (td, MINT_UNBOX);
4679 td->last_ins->data [0] = get_data_item_index (td, klass);
4681 interp_emit_ldobj (td, klass);
4683 td->ip += 5;
4686 break;
4687 case CEE_THROW:
4688 INLINE_FAILURE;
4689 CHECK_STACK (td, 1);
4690 SIMPLE_OP (td, MINT_THROW);
4691 td->sp = td->stack;
4692 break;
4693 case CEE_LDFLDA: {
4694 CHECK_STACK (td, 1);
4695 token = read32 (td->ip + 1);
4696 field = interp_field_from_token (method, token, &klass, generic_context, error);
4697 goto_if_nok (error, exit);
4698 MonoType *ftype = mono_field_get_type_internal (field);
4699 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4700 mono_class_init_internal (klass);
4701 #ifndef DISABLE_REMOTING
4702 if (m_class_get_marshalbyref (klass) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
4703 g_assert (!is_static);
4704 int offset = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4706 interp_add_ins (td, MINT_MONO_LDPTR);
4707 td->last_ins->data [0] = get_data_item_index (td, klass);
4708 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4709 interp_add_ins (td, MINT_MONO_LDPTR);
4710 td->last_ins->data [0] = get_data_item_index (td, field);
4711 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4712 interp_add_ins (td, MINT_LDC_I4);
4713 WRITE32_INS (td->last_ins, 0, &offset);
4714 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
4715 #if SIZEOF_VOID_P == 8
4716 interp_add_ins (td, MINT_CONV_I8_I4);
4717 #endif
4719 MonoMethod *wrapper = mono_marshal_get_ldflda_wrapper (field->type);
4720 /* td->ip is incremented by interp_transform_call */
4721 if (!interp_transform_call (td, method, wrapper, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4722 goto exit;
4723 } else
4724 #endif
4726 if (is_static) {
4727 interp_add_ins (td, MINT_POP);
4728 interp_emit_ldsflda (td, field, error);
4729 goto_if_nok (error, exit);
4730 } else {
4731 if ((td->sp - 1)->type == STACK_TYPE_O) {
4732 interp_add_ins (td, MINT_LDFLDA);
4733 } else {
4734 g_assert ((td->sp -1)->type == STACK_TYPE_MP);
4735 interp_add_ins (td, MINT_LDFLDA_UNSAFE);
4737 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4739 td->ip += 5;
4741 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4742 break;
4744 case CEE_LDFLD: {
4745 CHECK_STACK (td, 1);
4746 token = read32 (td->ip + 1);
4747 field = interp_field_from_token (method, token, &klass, generic_context, error);
4748 goto_if_nok (error, exit);
4749 MonoType *ftype = mono_field_get_type_internal (field);
4750 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4751 mono_class_init_internal (klass);
4753 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4754 mt = mint_type (m_class_get_byval_arg (field_klass));
4755 #ifndef DISABLE_REMOTING
4756 if (m_class_get_marshalbyref (klass)) {
4757 g_assert (!is_static);
4758 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDRMFLD_VT : MINT_LDRMFLD);
4759 td->last_ins->data [0] = get_data_item_index (td, field);
4760 } else
4761 #endif
4763 if (is_static) {
4764 interp_add_ins (td, MINT_POP);
4765 interp_emit_sfld_access (td, field, field_klass, mt, TRUE, error);
4766 goto_if_nok (error, exit);
4767 } else {
4768 int opcode = MINT_LDFLD_I1 + mt - MINT_TYPE_I1;
4769 #ifdef NO_UNALIGNED_ACCESS
4770 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4771 opcode = get_unaligned_opcode (opcode);
4772 #endif
4773 interp_add_ins (td, opcode);
4774 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4775 if (mt == MINT_TYPE_VT) {
4776 int size = mono_class_value_size (field_klass, NULL);
4777 WRITE32_INS (td->last_ins, 1, &size);
4781 if (mt == MINT_TYPE_VT) {
4782 int size = mono_class_value_size (field_klass, NULL);
4783 PUSH_VT (td, size);
4785 if (td->sp [-1].type == STACK_TYPE_VT) {
4786 int size = mono_class_value_size (klass, NULL);
4787 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4788 int field_vt_size = 0;
4789 if (mt == MINT_TYPE_VT) {
4791 * Pop the loaded field from the vtstack (it will still be present
4792 * at the same vtstack address) and we will load it in place of the
4793 * containing valuetype with the second MINT_VTRESULT.
4795 field_vt_size = mono_class_value_size (field_klass, NULL);
4796 field_vt_size = ALIGN_TO (field_vt_size, MINT_VT_ALIGNMENT);
4797 interp_add_ins (td, MINT_VTRESULT);
4798 td->last_ins->data [0] = 0;
4799 WRITE32_INS (td->last_ins, 1, &field_vt_size);
4801 td->vt_sp -= size;
4802 interp_add_ins (td, MINT_VTRESULT);
4803 td->last_ins->data [0] = field_vt_size;
4804 WRITE32_INS (td->last_ins, 1, &size);
4806 td->ip += 5;
4807 SET_TYPE (td->sp - 1, stack_type [mt], field_klass);
4808 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
4809 break;
4811 case CEE_STFLD: {
4812 CHECK_STACK (td, 2);
4813 token = read32 (td->ip + 1);
4814 field = interp_field_from_token (method, token, &klass, generic_context, error);
4815 goto_if_nok (error, exit);
4816 MonoType *ftype = mono_field_get_type_internal (field);
4817 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4818 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4819 mono_class_init_internal (klass);
4820 mt = mint_type (ftype);
4822 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4824 #ifndef DISABLE_REMOTING
4825 if (m_class_get_marshalbyref (klass)) {
4826 g_assert (!is_static);
4827 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_STRMFLD_VT : MINT_STRMFLD);
4828 td->last_ins->data [0] = get_data_item_index (td, field);
4829 } else
4830 #endif
4832 if (is_static) {
4833 interp_emit_sfld_access (td, field, field_klass, mt, FALSE, error);
4834 goto_if_nok (error, exit);
4836 /* pop the unused object reference */
4837 interp_add_ins (td, MINT_POP);
4839 /* the vtable of the field might not be initialized at this point */
4840 mono_class_vtable_checked (domain, field_klass, error);
4841 goto_if_nok (error, exit);
4842 } else {
4843 int opcode = MINT_STFLD_I1 + mt - MINT_TYPE_I1;
4844 #ifdef NO_UNALIGNED_ACCESS
4845 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4846 opcode = get_unaligned_opcode (opcode);
4847 #endif
4848 interp_add_ins (td, opcode);
4849 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4850 if (mt == MINT_TYPE_VT) {
4851 /* the vtable of the field might not be initialized at this point */
4852 mono_class_vtable_checked (domain, field_klass, error);
4853 goto_if_nok (error, exit);
4855 td->last_ins->data [1] = get_data_item_index (td, field_klass);
4859 if (mt == MINT_TYPE_VT) {
4860 int size = mono_class_value_size (field_klass, NULL);
4861 POP_VT (td, size);
4863 td->ip += 5;
4864 td->sp -= 2;
4865 break;
4867 case CEE_LDSFLDA: {
4868 token = read32 (td->ip + 1);
4869 field = interp_field_from_token (method, token, &klass, generic_context, error);
4870 goto_if_nok (error, exit);
4871 interp_emit_ldsflda (td, field, error);
4872 goto_if_nok (error, exit);
4873 td->ip += 5;
4874 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
4875 break;
4877 case CEE_LDSFLD: {
4878 token = read32 (td->ip + 1);
4879 field = interp_field_from_token (method, token, &klass, generic_context, error);
4880 goto_if_nok (error, exit);
4881 MonoType *ftype = mono_field_get_type_internal (field);
4882 mt = mint_type (ftype);
4883 klass = mono_class_from_mono_type_internal (ftype);
4885 interp_emit_sfld_access (td, field, klass, mt, TRUE, error);
4886 goto_if_nok (error, exit);
4888 if (mt == MINT_TYPE_VT) {
4889 int size = mono_class_value_size (klass, NULL);
4890 PUSH_VT(td, size);
4892 td->ip += 5;
4893 PUSH_TYPE(td, stack_type [mt], klass);
4894 break;
4896 case CEE_STSFLD: {
4897 CHECK_STACK (td, 1);
4898 token = read32 (td->ip + 1);
4899 field = interp_field_from_token (method, token, &klass, generic_context, error);
4900 goto_if_nok (error, exit);
4901 MonoType *ftype = mono_field_get_type_internal (field);
4902 mt = mint_type (ftype);
4904 /* the vtable of the field might not be initialized at this point */
4905 MonoClass *fld_klass = mono_class_from_mono_type_internal (ftype);
4906 mono_class_vtable_checked (domain, fld_klass, error);
4907 goto_if_nok (error, exit);
4909 interp_emit_sfld_access (td, field, fld_klass, mt, FALSE, error);
4910 goto_if_nok (error, exit);
4912 if (mt == MINT_TYPE_VT) {
4913 int size = mono_class_value_size (fld_klass, NULL);
4914 POP_VT(td, size);
4916 td->ip += 5;
4917 --td->sp;
4918 break;
4920 case CEE_STOBJ: {
4921 token = read32 (td->ip + 1);
4923 if (method->wrapper_type != MONO_WRAPPER_NONE)
4924 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4925 else
4926 klass = mini_get_class (method, token, generic_context);
4927 CHECK_TYPELOAD (klass);
4929 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4931 interp_emit_stobj (td, klass);
4933 td->ip += 5;
4934 break;
4936 case CEE_CONV_OVF_I_UN:
4937 case CEE_CONV_OVF_U_UN:
4938 CHECK_STACK (td, 1);
4939 switch (td->sp [-1].type) {
4940 case STACK_TYPE_R8:
4941 #if SIZEOF_VOID_P == 8
4942 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4943 #else
4944 interp_add_ins (td, MINT_CONV_OVF_I4_UN_R8);
4945 #endif
4946 break;
4947 case STACK_TYPE_I8:
4948 #if SIZEOF_VOID_P == 4
4949 interp_add_ins (td, MINT_CONV_OVF_I4_UN_I8);
4950 #endif
4951 break;
4952 case STACK_TYPE_I4:
4953 #if SIZEOF_VOID_P == 8
4954 interp_add_ins (td, MINT_CONV_I8_U4);
4955 #elif SIZEOF_VOID_P == 4
4956 if (*td->ip == CEE_CONV_OVF_I_UN)
4957 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
4958 #endif
4959 break;
4960 default:
4961 g_assert_not_reached ();
4962 break;
4964 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4965 ++td->ip;
4966 break;
4967 case CEE_CONV_OVF_I8_UN:
4968 case CEE_CONV_OVF_U8_UN:
4969 CHECK_STACK (td, 1);
4970 switch (td->sp [-1].type) {
4971 case STACK_TYPE_R8:
4972 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4973 break;
4974 case STACK_TYPE_I8:
4975 if (*td->ip == CEE_CONV_OVF_I8_UN)
4976 interp_add_ins (td, MINT_CONV_OVF_I8_U8);
4977 break;
4978 case STACK_TYPE_I4:
4979 interp_add_ins (td, MINT_CONV_I8_U4);
4980 break;
4981 case STACK_TYPE_R4:
4982 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R4);
4983 break;
4984 default:
4985 g_assert_not_reached ();
4986 break;
4988 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4989 ++td->ip;
4990 break;
4991 case CEE_BOX: {
4992 int size;
4993 CHECK_STACK (td, 1);
4994 token = read32 (td->ip + 1);
4995 if (method->wrapper_type != MONO_WRAPPER_NONE)
4996 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4997 else
4998 klass = mini_get_class (method, token, generic_context);
4999 CHECK_TYPELOAD (klass);
5001 if (mono_class_is_nullable (klass)) {
5002 MonoMethod *target_method = mono_class_get_method_from_name_checked (klass, "Box", 1, 0, error);
5003 goto_if_nok (error, exit);
5004 /* td->ip is incremented by interp_transform_call */
5005 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
5006 goto exit;
5007 } else if (!m_class_is_valuetype (klass)) {
5008 /* already boxed, do nothing. */
5009 td->ip += 5;
5010 } else {
5011 if (G_UNLIKELY (m_class_is_byreflike (klass))) {
5012 mono_error_set_bad_image (error, image, "Cannot box IsByRefLike type '%s.%s'", m_class_get_name_space (klass), m_class_get_name (klass));
5013 goto exit;
5015 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
5016 size = mono_class_value_size (klass, NULL);
5017 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
5018 td->vt_sp -= size;
5019 } else if (td->sp [-1].type == STACK_TYPE_R8 && m_class_get_byval_arg (klass)->type == MONO_TYPE_R4) {
5020 interp_add_ins (td, MINT_CONV_R4_R8);
5022 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
5023 goto_if_nok (error, exit);
5025 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
5026 interp_add_ins (td, MINT_BOX_VT);
5027 else
5028 interp_add_ins (td, MINT_BOX);
5029 td->last_ins->data [0] = get_data_item_index (td, vtable);
5030 td->last_ins->data [1] = 0;
5031 SET_TYPE(td->sp - 1, STACK_TYPE_O, klass);
5032 td->ip += 5;
5035 break;
5037 case CEE_NEWARR: {
5038 CHECK_STACK (td, 1);
5039 token = read32 (td->ip + 1);
5041 if (method->wrapper_type != MONO_WRAPPER_NONE)
5042 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5043 else
5044 klass = mini_get_class (method, token, generic_context);
5045 CHECK_TYPELOAD (klass);
5047 MonoClass *array_class = mono_class_create_array (klass, 1);
5048 MonoVTable *vtable = mono_class_vtable_checked (domain, array_class, error);
5049 goto_if_nok (error, exit);
5051 unsigned char lentype = (td->sp - 1)->type;
5052 if (lentype == STACK_TYPE_I8) {
5053 /* mimic mini behaviour */
5054 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
5055 } else {
5056 g_assert (lentype == STACK_TYPE_I4);
5057 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
5059 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
5060 interp_add_ins (td, MINT_NEWARR);
5061 td->last_ins->data [0] = get_data_item_index (td, vtable);
5062 SET_TYPE (td->sp - 1, STACK_TYPE_O, array_class);
5063 td->ip += 5;
5064 break;
5066 case CEE_LDLEN:
5067 CHECK_STACK (td, 1);
5068 SIMPLE_OP (td, MINT_LDLEN);
5069 #ifdef MONO_BIG_ARRAYS
5070 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
5071 #else
5072 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
5073 #endif
5074 break;
5075 case CEE_LDELEMA: {
5076 gint32 size;
5077 CHECK_STACK (td, 2);
5078 ENSURE_I4 (td, 1);
5079 token = read32 (td->ip + 1);
5081 if (method->wrapper_type != MONO_WRAPPER_NONE)
5082 klass = (MonoClass *) mono_method_get_wrapper_data (method, token);
5083 else
5084 klass = mini_get_class (method, token, generic_context);
5086 CHECK_TYPELOAD (klass);
5088 if (!m_class_is_valuetype (klass) && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
5090 * Check the class for failures before the type check, which can
5091 * throw other exceptions.
5093 mono_class_setup_vtable (klass);
5094 CHECK_TYPELOAD (klass);
5095 interp_add_ins (td, MINT_LDELEMA_TC);
5096 td->last_ins->data [0] = 1;
5097 td->last_ins->data [1] = get_data_item_index (td, klass);
5098 } else {
5099 interp_add_ins (td, MINT_LDELEMA1);
5100 mono_class_init_internal (klass);
5101 size = mono_class_array_element_size (klass);
5102 WRITE32_INS (td->last_ins, 0, &size);
5105 readonly = FALSE;
5107 td->ip += 5;
5108 --td->sp;
5109 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5110 break;
5112 case CEE_LDELEM_I1:
5113 CHECK_STACK (td, 2);
5114 ENSURE_I4 (td, 1);
5115 SIMPLE_OP (td, MINT_LDELEM_I1);
5116 --td->sp;
5117 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5118 break;
5119 case CEE_LDELEM_U1:
5120 CHECK_STACK (td, 2);
5121 ENSURE_I4 (td, 1);
5122 SIMPLE_OP (td, MINT_LDELEM_U1);
5123 --td->sp;
5124 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5125 break;
5126 case CEE_LDELEM_I2:
5127 CHECK_STACK (td, 2);
5128 ENSURE_I4 (td, 1);
5129 SIMPLE_OP (td, MINT_LDELEM_I2);
5130 --td->sp;
5131 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5132 break;
5133 case CEE_LDELEM_U2:
5134 CHECK_STACK (td, 2);
5135 ENSURE_I4 (td, 1);
5136 SIMPLE_OP (td, MINT_LDELEM_U2);
5137 --td->sp;
5138 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5139 break;
5140 case CEE_LDELEM_I4:
5141 CHECK_STACK (td, 2);
5142 ENSURE_I4 (td, 1);
5143 SIMPLE_OP (td, MINT_LDELEM_I4);
5144 --td->sp;
5145 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5146 break;
5147 case CEE_LDELEM_U4:
5148 CHECK_STACK (td, 2);
5149 ENSURE_I4 (td, 1);
5150 SIMPLE_OP (td, MINT_LDELEM_U4);
5151 --td->sp;
5152 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5153 break;
5154 case CEE_LDELEM_I8:
5155 CHECK_STACK (td, 2);
5156 ENSURE_I4 (td, 1);
5157 SIMPLE_OP (td, MINT_LDELEM_I8);
5158 --td->sp;
5159 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5160 break;
5161 case CEE_LDELEM_I:
5162 CHECK_STACK (td, 2);
5163 ENSURE_I4 (td, 1);
5164 SIMPLE_OP (td, MINT_LDELEM_I);
5165 --td->sp;
5166 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
5167 break;
5168 case CEE_LDELEM_R4:
5169 CHECK_STACK (td, 2);
5170 ENSURE_I4 (td, 1);
5171 SIMPLE_OP (td, MINT_LDELEM_R4);
5172 --td->sp;
5173 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
5174 break;
5175 case CEE_LDELEM_R8:
5176 CHECK_STACK (td, 2);
5177 ENSURE_I4 (td, 1);
5178 SIMPLE_OP (td, MINT_LDELEM_R8);
5179 --td->sp;
5180 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
5181 break;
5182 case CEE_LDELEM_REF:
5183 CHECK_STACK (td, 2);
5184 ENSURE_I4 (td, 1);
5185 SIMPLE_OP (td, MINT_LDELEM_REF);
5186 --td->sp;
5187 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
5188 break;
5189 case CEE_LDELEM:
5190 CHECK_STACK (td, 2);
5191 token = read32 (td->ip + 1);
5192 klass = mini_get_class (method, token, generic_context);
5193 CHECK_TYPELOAD (klass);
5194 switch (mint_type (m_class_get_byval_arg (klass))) {
5195 case MINT_TYPE_I1:
5196 ENSURE_I4 (td, 1);
5197 SIMPLE_OP (td, MINT_LDELEM_I1);
5198 --td->sp;
5199 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5200 break;
5201 case MINT_TYPE_U1:
5202 ENSURE_I4 (td, 1);
5203 SIMPLE_OP (td, MINT_LDELEM_U1);
5204 --td->sp;
5205 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5206 break;
5207 case MINT_TYPE_U2:
5208 ENSURE_I4 (td, 1);
5209 SIMPLE_OP (td, MINT_LDELEM_U2);
5210 --td->sp;
5211 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5212 break;
5213 case MINT_TYPE_I2:
5214 ENSURE_I4 (td, 1);
5215 SIMPLE_OP (td, MINT_LDELEM_I2);
5216 --td->sp;
5217 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5218 break;
5219 case MINT_TYPE_I4:
5220 ENSURE_I4 (td, 1);
5221 SIMPLE_OP (td, MINT_LDELEM_I4);
5222 --td->sp;
5223 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5224 break;
5225 case MINT_TYPE_I8:
5226 ENSURE_I4 (td, 1);
5227 SIMPLE_OP (td, MINT_LDELEM_I8);
5228 --td->sp;
5229 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5230 break;
5231 case MINT_TYPE_R4:
5232 ENSURE_I4 (td, 1);
5233 SIMPLE_OP (td, MINT_LDELEM_R4);
5234 --td->sp;
5235 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
5236 break;
5237 case MINT_TYPE_R8:
5238 ENSURE_I4 (td, 1);
5239 SIMPLE_OP (td, MINT_LDELEM_R8);
5240 --td->sp;
5241 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
5242 break;
5243 case MINT_TYPE_O:
5244 ENSURE_I4 (td, 1);
5245 SIMPLE_OP (td, MINT_LDELEM_REF);
5246 --td->sp;
5247 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
5248 break;
5249 case MINT_TYPE_VT: {
5250 int size = mono_class_value_size (klass, NULL);
5251 ENSURE_I4 (td, 1);
5252 SIMPLE_OP (td, MINT_LDELEM_VT);
5253 WRITE32_INS (td->last_ins, 0, &size);
5254 --td->sp;
5255 SET_TYPE (td->sp - 1, STACK_TYPE_VT, klass);
5256 PUSH_VT (td, size);
5257 break;
5259 default: {
5260 GString *res = g_string_new ("");
5261 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
5262 g_print ("LDELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
5263 g_string_free (res, TRUE);
5264 g_assert (0);
5265 break;
5268 td->ip += 4;
5269 break;
5270 case CEE_STELEM_I:
5271 CHECK_STACK (td, 3);
5272 ENSURE_I4 (td, 2);
5273 SIMPLE_OP (td, MINT_STELEM_I);
5274 td->sp -= 3;
5275 break;
5276 case CEE_STELEM_I1:
5277 CHECK_STACK (td, 3);
5278 ENSURE_I4 (td, 2);
5279 SIMPLE_OP (td, MINT_STELEM_I1);
5280 td->sp -= 3;
5281 break;
5282 case CEE_STELEM_I2:
5283 CHECK_STACK (td, 3);
5284 ENSURE_I4 (td, 2);
5285 SIMPLE_OP (td, MINT_STELEM_I2);
5286 td->sp -= 3;
5287 break;
5288 case CEE_STELEM_I4:
5289 CHECK_STACK (td, 3);
5290 ENSURE_I4 (td, 2);
5291 SIMPLE_OP (td, MINT_STELEM_I4);
5292 td->sp -= 3;
5293 break;
5294 case CEE_STELEM_I8:
5295 CHECK_STACK (td, 3);
5296 ENSURE_I4 (td, 2);
5297 SIMPLE_OP (td, MINT_STELEM_I8);
5298 td->sp -= 3;
5299 break;
5300 case CEE_STELEM_R4:
5301 CHECK_STACK (td, 3);
5302 ENSURE_I4 (td, 2);
5303 SIMPLE_OP (td, MINT_STELEM_R4);
5304 td->sp -= 3;
5305 break;
5306 case CEE_STELEM_R8:
5307 CHECK_STACK (td, 3);
5308 ENSURE_I4 (td, 2);
5309 SIMPLE_OP (td, MINT_STELEM_R8);
5310 td->sp -= 3;
5311 break;
5312 case CEE_STELEM_REF:
5313 CHECK_STACK (td, 3);
5314 ENSURE_I4 (td, 2);
5315 SIMPLE_OP (td, MINT_STELEM_REF);
5316 td->sp -= 3;
5317 break;
5318 case CEE_STELEM:
5319 CHECK_STACK (td, 3);
5320 ENSURE_I4 (td, 2);
5321 token = read32 (td->ip + 1);
5322 klass = mini_get_class (method, token, generic_context);
5323 CHECK_TYPELOAD (klass);
5324 switch (mint_type (m_class_get_byval_arg (klass))) {
5325 case MINT_TYPE_I1:
5326 SIMPLE_OP (td, MINT_STELEM_I1);
5327 break;
5328 case MINT_TYPE_U1:
5329 SIMPLE_OP (td, MINT_STELEM_U1);
5330 break;
5331 case MINT_TYPE_I2:
5332 SIMPLE_OP (td, MINT_STELEM_I2);
5333 break;
5334 case MINT_TYPE_U2:
5335 SIMPLE_OP (td, MINT_STELEM_U2);
5336 break;
5337 case MINT_TYPE_I4:
5338 SIMPLE_OP (td, MINT_STELEM_I4);
5339 break;
5340 case MINT_TYPE_I8:
5341 SIMPLE_OP (td, MINT_STELEM_I8);
5342 break;
5343 case MINT_TYPE_R4:
5344 SIMPLE_OP (td, MINT_STELEM_R4);
5345 break;
5346 case MINT_TYPE_R8:
5347 SIMPLE_OP (td, MINT_STELEM_R8);
5348 break;
5349 case MINT_TYPE_O:
5350 SIMPLE_OP (td, MINT_STELEM_REF);
5351 break;
5352 case MINT_TYPE_VT: {
5353 int size = mono_class_value_size (klass, NULL);
5354 SIMPLE_OP (td, MINT_STELEM_VT);
5355 td->last_ins->data [0] = get_data_item_index (td, klass);
5356 WRITE32_INS (td->last_ins, 1, &size);
5357 POP_VT (td, size);
5358 break;
5360 default: {
5361 GString *res = g_string_new ("");
5362 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
5363 g_print ("STELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
5364 g_string_free (res, TRUE);
5365 g_assert (0);
5366 break;
5369 td->ip += 4;
5370 td->sp -= 3;
5371 break;
5372 #if 0
5373 case CEE_CONV_OVF_U1:
5375 case CEE_CONV_OVF_I8:
5377 #if SIZEOF_VOID_P == 8
5378 case CEE_CONV_OVF_U:
5379 #endif
5380 #endif
5381 case CEE_CKFINITE:
5382 CHECK_STACK (td, 1);
5383 SIMPLE_OP (td, MINT_CKFINITE);
5384 break;
5385 case CEE_MKREFANY:
5386 CHECK_STACK (td, 1);
5388 token = read32 (td->ip + 1);
5389 klass = mini_get_class (method, token, generic_context);
5390 CHECK_TYPELOAD (klass);
5392 interp_add_ins (td, MINT_MKREFANY);
5393 td->last_ins->data [0] = get_data_item_index (td, klass);
5395 td->ip += 5;
5396 PUSH_VT (td, sizeof (MonoTypedRef));
5397 SET_TYPE(td->sp - 1, STACK_TYPE_VT, mono_defaults.typed_reference_class);
5398 break;
5399 case CEE_REFANYVAL: {
5400 CHECK_STACK (td, 1);
5402 token = read32 (td->ip + 1);
5403 klass = mini_get_class (method, token, generic_context);
5404 CHECK_TYPELOAD (klass);
5406 interp_add_ins (td, MINT_REFANYVAL);
5407 td->last_ins->data [0] = get_data_item_index (td, klass);
5409 POP_VT (td, sizeof (MonoTypedRef));
5410 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5412 td->ip += 5;
5413 break;
5415 case CEE_CONV_OVF_I1:
5416 case CEE_CONV_OVF_I1_UN: {
5417 gboolean is_un = *td->ip == CEE_CONV_OVF_I1_UN;
5418 CHECK_STACK (td, 1);
5419 switch (td->sp [-1].type) {
5420 case STACK_TYPE_R8:
5421 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_UN_R8 : MINT_CONV_OVF_I1_R8);
5422 break;
5423 case STACK_TYPE_I4:
5424 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U4 : MINT_CONV_OVF_I1_I4);
5425 break;
5426 case STACK_TYPE_I8:
5427 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U8 : MINT_CONV_OVF_I1_I8);
5428 break;
5429 default:
5430 g_assert_not_reached ();
5432 ++td->ip;
5433 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5434 break;
5436 case CEE_CONV_OVF_U1:
5437 case CEE_CONV_OVF_U1_UN:
5438 CHECK_STACK (td, 1);
5439 switch (td->sp [-1].type) {
5440 case STACK_TYPE_R8:
5441 interp_add_ins (td, MINT_CONV_OVF_U1_R8);
5442 break;
5443 case STACK_TYPE_I4:
5444 interp_add_ins (td, MINT_CONV_OVF_U1_I4);
5445 break;
5446 case STACK_TYPE_I8:
5447 interp_add_ins (td, MINT_CONV_OVF_U1_I8);
5448 break;
5449 default:
5450 g_assert_not_reached ();
5452 ++td->ip;
5453 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5454 break;
5455 case CEE_CONV_OVF_I2:
5456 case CEE_CONV_OVF_I2_UN: {
5457 gboolean is_un = *td->ip == CEE_CONV_OVF_I2_UN;
5458 CHECK_STACK (td, 1);
5459 switch (td->sp [-1].type) {
5460 case STACK_TYPE_R8:
5461 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_UN_R8 : MINT_CONV_OVF_I2_R8);
5462 break;
5463 case STACK_TYPE_I4:
5464 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U4 : MINT_CONV_OVF_I2_I4);
5465 break;
5466 case STACK_TYPE_I8:
5467 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U8 : MINT_CONV_OVF_I2_I8);
5468 break;
5469 default:
5470 g_assert_not_reached ();
5472 ++td->ip;
5473 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5474 break;
5476 case CEE_CONV_OVF_U2_UN:
5477 case CEE_CONV_OVF_U2:
5478 CHECK_STACK (td, 1);
5479 switch (td->sp [-1].type) {
5480 case STACK_TYPE_R8:
5481 interp_add_ins (td, MINT_CONV_OVF_U2_R8);
5482 break;
5483 case STACK_TYPE_I4:
5484 interp_add_ins (td, MINT_CONV_OVF_U2_I4);
5485 break;
5486 case STACK_TYPE_I8:
5487 interp_add_ins (td, MINT_CONV_OVF_U2_I8);
5488 break;
5489 default:
5490 g_assert_not_reached ();
5492 ++td->ip;
5493 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5494 break;
5495 #if SIZEOF_VOID_P == 4
5496 case CEE_CONV_OVF_I:
5497 #endif
5498 case CEE_CONV_OVF_I4:
5499 case CEE_CONV_OVF_I4_UN:
5500 CHECK_STACK (td, 1);
5501 switch (td->sp [-1].type) {
5502 case STACK_TYPE_R4:
5503 interp_add_ins (td, MINT_CONV_OVF_I4_R4);
5504 break;
5505 case STACK_TYPE_R8:
5506 interp_add_ins (td, MINT_CONV_OVF_I4_R8);
5507 break;
5508 case STACK_TYPE_I4:
5509 if (*td->ip == CEE_CONV_OVF_I4_UN)
5510 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
5511 break;
5512 case STACK_TYPE_I8:
5513 if (*td->ip == CEE_CONV_OVF_I4_UN)
5514 interp_add_ins (td, MINT_CONV_OVF_I4_U8);
5515 else
5516 interp_add_ins (td, MINT_CONV_OVF_I4_I8);
5517 break;
5518 default:
5519 g_assert_not_reached ();
5521 ++td->ip;
5522 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5523 break;
5524 #if SIZEOF_VOID_P == 4
5525 case CEE_CONV_OVF_U:
5526 #endif
5527 case CEE_CONV_OVF_U4:
5528 case CEE_CONV_OVF_U4_UN:
5529 CHECK_STACK (td, 1);
5530 switch (td->sp [-1].type) {
5531 case STACK_TYPE_R4:
5532 interp_add_ins (td, MINT_CONV_OVF_U4_R4);
5533 break;
5534 case STACK_TYPE_R8:
5535 interp_add_ins (td, MINT_CONV_OVF_U4_R8);
5536 break;
5537 case STACK_TYPE_I4:
5538 if (*td->ip != CEE_CONV_OVF_U4_UN)
5539 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
5540 break;
5541 case STACK_TYPE_I8:
5542 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
5543 break;
5544 case STACK_TYPE_MP:
5545 interp_add_ins (td, MINT_CONV_OVF_U4_P);
5546 break;
5547 default:
5548 g_assert_not_reached ();
5550 ++td->ip;
5551 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5552 break;
5553 #if SIZEOF_VOID_P == 8
5554 case CEE_CONV_OVF_I:
5555 #endif
5556 case CEE_CONV_OVF_I8:
5557 CHECK_STACK (td, 1);
5558 switch (td->sp [-1].type) {
5559 case STACK_TYPE_R4:
5560 interp_add_ins (td, MINT_CONV_OVF_I8_R4);
5561 break;
5562 case STACK_TYPE_R8:
5563 interp_add_ins (td, MINT_CONV_OVF_I8_R8);
5564 break;
5565 case STACK_TYPE_I4:
5566 interp_add_ins (td, MINT_CONV_I8_I4);
5567 break;
5568 case STACK_TYPE_I8:
5569 break;
5570 default:
5571 g_assert_not_reached ();
5573 ++td->ip;
5574 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5575 break;
5576 #if SIZEOF_VOID_P == 8
5577 case CEE_CONV_OVF_U:
5578 #endif
5579 case CEE_CONV_OVF_U8:
5580 CHECK_STACK (td, 1);
5581 switch (td->sp [-1].type) {
5582 case STACK_TYPE_R4:
5583 interp_add_ins (td, MINT_CONV_OVF_U8_R4);
5584 break;
5585 case STACK_TYPE_R8:
5586 interp_add_ins (td, MINT_CONV_OVF_U8_R8);
5587 break;
5588 case STACK_TYPE_I4:
5589 interp_add_ins (td, MINT_CONV_OVF_U8_I4);
5590 break;
5591 case STACK_TYPE_I8:
5592 interp_add_ins (td, MINT_CONV_OVF_U8_I8);
5593 break;
5594 default:
5595 g_assert_not_reached ();
5597 ++td->ip;
5598 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5599 break;
5600 case CEE_LDTOKEN: {
5601 int size;
5602 gpointer handle;
5603 token = read32 (td->ip + 1);
5604 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
5605 handle = mono_method_get_wrapper_data (method, token);
5606 klass = (MonoClass *) mono_method_get_wrapper_data (method, token + 1);
5607 if (klass == mono_defaults.typehandle_class)
5608 handle = m_class_get_byval_arg ((MonoClass *) handle);
5610 if (generic_context) {
5611 handle = mono_class_inflate_generic_type_checked ((MonoType*)handle, generic_context, error);
5612 goto_if_nok (error, exit);
5614 } else {
5615 handle = mono_ldtoken_checked (image, token, &klass, generic_context, error);
5616 goto_if_nok (error, exit);
5618 mono_class_init_internal (klass);
5619 mt = mint_type (m_class_get_byval_arg (klass));
5620 g_assert (mt == MINT_TYPE_VT);
5621 size = mono_class_value_size (klass, NULL);
5622 g_assert (size == sizeof(gpointer));
5624 const unsigned char *next_ip = td->ip + 5;
5625 MonoMethod *cmethod;
5626 if (next_ip < end &&
5627 (inlining || !td->is_bb_start [next_ip - td->il_code]) &&
5628 (*next_ip == CEE_CALL || *next_ip == CEE_CALLVIRT) &&
5629 (cmethod = mono_get_method_checked (image, read32 (next_ip + 1), NULL, generic_context, error)) &&
5630 (cmethod->klass == mono_defaults.systemtype_class) &&
5631 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
5632 interp_add_ins (td, MINT_MONO_LDPTR);
5633 gpointer systype = mono_type_get_object_checked (domain, (MonoType*)handle, error);
5634 goto_if_nok (error, exit);
5635 td->last_ins->data [0] = get_data_item_index (td, systype);
5636 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5637 td->ip = next_ip + 5;
5638 } else {
5639 PUSH_VT (td, sizeof(gpointer));
5640 interp_add_ins (td, MINT_LDTOKEN);
5641 td->last_ins->data [0] = get_data_item_index (td, handle);
5642 PUSH_TYPE (td, stack_type [mt], klass);
5643 td->ip += 5;
5646 break;
5648 case CEE_ADD_OVF:
5649 binary_arith_op(td, MINT_ADD_OVF_I4);
5650 ++td->ip;
5651 break;
5652 case CEE_ADD_OVF_UN:
5653 binary_arith_op(td, MINT_ADD_OVF_UN_I4);
5654 ++td->ip;
5655 break;
5656 case CEE_MUL_OVF:
5657 binary_arith_op(td, MINT_MUL_OVF_I4);
5658 ++td->ip;
5659 break;
5660 case CEE_MUL_OVF_UN:
5661 binary_arith_op(td, MINT_MUL_OVF_UN_I4);
5662 ++td->ip;
5663 break;
5664 case CEE_SUB_OVF:
5665 binary_arith_op(td, MINT_SUB_OVF_I4);
5666 ++td->ip;
5667 break;
5668 case CEE_SUB_OVF_UN:
5669 binary_arith_op(td, MINT_SUB_OVF_UN_I4);
5670 ++td->ip;
5671 break;
5672 case CEE_ENDFINALLY: {
5673 g_assert (td->clause_indexes [in_offset] != -1);
5674 td->sp = td->stack;
5675 SIMPLE_OP (td, MINT_ENDFINALLY);
5676 td->last_ins->data [0] = td->clause_indexes [in_offset];
5677 // next instructions, if they exist, are always part of new bb
5678 // endfinally can be the last instruction in a function.
5679 // functions with clauses/endfinally are never inlined.
5680 // is_bb_start is not valid while inlining.
5681 g_assert (!inlining);
5682 if (td->ip - td->il_code < td->code_size)
5683 td->is_bb_start [td->ip - header->code] = 1;
5684 break;
5686 case CEE_LEAVE:
5687 case CEE_LEAVE_S: {
5688 int offset;
5690 if (*td->ip == CEE_LEAVE)
5691 offset = 5 + read32 (td->ip + 1);
5692 else
5693 offset = 2 + (gint8)td->ip [1];
5695 td->sp = td->stack;
5696 if (td->clause_indexes [in_offset] != -1) {
5697 /* LEAVE instructions in catch clauses need to check for abort exceptions */
5698 handle_branch (td, MINT_LEAVE_S_CHECK, MINT_LEAVE_CHECK, offset);
5699 } else {
5700 handle_branch (td, MINT_LEAVE_S, MINT_LEAVE, offset);
5703 if (*td->ip == CEE_LEAVE)
5704 td->ip += 5;
5705 else
5706 td->ip += 2;
5707 break;
5709 case MONO_CUSTOM_PREFIX:
5710 ++td->ip;
5711 switch (*td->ip) {
5712 case CEE_MONO_RETHROW:
5713 CHECK_STACK (td, 1);
5714 SIMPLE_OP (td, MINT_MONO_RETHROW);
5715 td->sp = td->stack;
5716 break;
5718 case CEE_MONO_LD_DELEGATE_METHOD_PTR:
5719 --td->sp;
5720 td->ip += 1;
5721 interp_add_ins (td, MINT_LD_DELEGATE_METHOD_PTR);
5722 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5723 break;
5724 case CEE_MONO_CALLI_EXTRA_ARG:
5725 /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
5726 interp_add_ins (td, MINT_POP1);
5727 --td->sp;
5728 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
5729 goto exit;
5730 break;
5731 case CEE_MONO_JIT_ICALL_ADDR: {
5732 const guint32 token = read32 (td->ip + 1);
5733 td->ip += 5;
5734 const gconstpointer func = mono_find_jit_icall_info ((MonoJitICallId)token)->func;
5736 interp_add_ins (td, MINT_LDFTN);
5737 td->last_ins->data [0] = get_data_item_index (td, (gpointer)func);
5738 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5739 break;
5741 case CEE_MONO_ICALL: {
5742 MonoJitICallId const jit_icall_id = (MonoJitICallId)read32 (td->ip + 1);
5743 MonoJitICallInfo const * const info = mono_find_jit_icall_info (jit_icall_id);
5744 td->ip += 5;
5746 CHECK_STACK (td, info->sig->param_count);
5747 if (jit_icall_id == MONO_JIT_ICALL_mono_threads_attach_coop) {
5748 rtm->needs_thread_attach = 1;
5750 /* attach needs two arguments, and has one return value: leave one element on the stack */
5751 interp_add_ins (td, MINT_POP);
5752 } else if (jit_icall_id == MONO_JIT_ICALL_mono_threads_detach_coop) {
5753 g_assert (rtm->needs_thread_attach);
5755 /* detach consumes two arguments, and no return value: drop both of them */
5756 interp_add_ins (td, MINT_POP);
5757 interp_add_ins (td, MINT_POP);
5758 } else {
5759 int const icall_op = interp_icall_op_for_sig (info->sig);
5760 g_assert (icall_op != -1);
5762 interp_add_ins (td, icall_op);
5763 // hash here is overkill
5764 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
5766 td->sp -= info->sig->param_count;
5768 if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
5769 int mt = mint_type (info->sig->ret);
5770 PUSH_SIMPLE_TYPE(td, stack_type [mt]);
5772 break;
5774 case CEE_MONO_VTADDR: {
5775 int size;
5776 CHECK_STACK (td, 1);
5777 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
5778 size = mono_class_native_size(td->sp [-1].klass, NULL);
5779 else
5780 size = mono_class_value_size(td->sp [-1].klass, NULL);
5781 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
5782 interp_add_ins (td, MINT_VTRESULT);
5783 td->last_ins->data [0] = 0;
5784 WRITE32_INS (td->last_ins, 1, &size);
5785 td->vt_sp -= size;
5786 ++td->ip;
5787 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5788 break;
5790 case CEE_MONO_LDPTR:
5791 case CEE_MONO_CLASSCONST:
5792 token = read32 (td->ip + 1);
5793 td->ip += 5;
5794 interp_add_ins (td, MINT_MONO_LDPTR);
5795 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5796 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5797 break;
5798 case CEE_MONO_OBJADDR:
5799 CHECK_STACK (td, 1);
5800 ++td->ip;
5801 td->sp[-1].type = STACK_TYPE_MP;
5802 /* do nothing? */
5803 break;
5804 case CEE_MONO_NEWOBJ:
5805 token = read32 (td->ip + 1);
5806 td->ip += 5;
5807 interp_add_ins (td, MINT_MONO_NEWOBJ);
5808 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5809 PUSH_SIMPLE_TYPE (td, STACK_TYPE_O);
5810 break;
5811 case CEE_MONO_RETOBJ:
5812 CHECK_STACK (td, 1);
5813 token = read32 (td->ip + 1);
5814 td->ip += 5;
5815 interp_add_ins (td, MINT_MONO_RETOBJ);
5816 td->sp--;
5818 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5820 /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/
5822 if (td->sp > td->stack)
5823 g_warning ("CEE_MONO_RETOBJ: more values on stack: %d", td->sp-td->stack);
5824 break;
5825 case CEE_MONO_LDNATIVEOBJ:
5826 token = read32 (td->ip + 1);
5827 td->ip += 5;
5828 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5829 g_assert(m_class_is_valuetype (klass));
5830 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5831 break;
5832 case CEE_MONO_TLS: {
5833 gint32 key = read32 (td->ip + 1);
5834 td->ip += 5;
5835 g_assertf (key == TLS_KEY_SGEN_THREAD_INFO, "%d", key);
5836 interp_add_ins (td, MINT_MONO_SGEN_THREAD_INFO);
5837 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5838 break;
5840 case CEE_MONO_ATOMIC_STORE_I4:
5841 CHECK_STACK (td, 2);
5842 SIMPLE_OP (td, MINT_MONO_ATOMIC_STORE_I4);
5843 td->sp -= 2;
5844 td->ip++;
5845 break;
5846 case CEE_MONO_SAVE_LMF:
5847 case CEE_MONO_RESTORE_LMF:
5848 case CEE_MONO_NOT_TAKEN:
5849 ++td->ip;
5850 break;
5851 case CEE_MONO_LDPTR_INT_REQ_FLAG:
5852 interp_add_ins (td, MINT_MONO_LDPTR);
5853 td->last_ins->data [0] = get_data_item_index (td, &mono_thread_interruption_request_flag);
5854 PUSH_TYPE (td, STACK_TYPE_MP, NULL);
5855 ++td->ip;
5856 break;
5857 case CEE_MONO_MEMORY_BARRIER:
5858 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5859 ++td->ip;
5860 break;
5861 case CEE_MONO_LDDOMAIN:
5862 interp_add_ins (td, MINT_MONO_LDDOMAIN);
5863 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5864 ++td->ip;
5865 break;
5866 case CEE_MONO_SAVE_LAST_ERROR:
5867 save_last_error = TRUE;
5868 ++td->ip;
5869 break;
5870 default:
5871 g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td->ip, td->ip-header->code);
5873 break;
5874 #if 0
5875 case CEE_PREFIX7:
5876 case CEE_PREFIX6:
5877 case CEE_PREFIX5:
5878 case CEE_PREFIX4:
5879 case CEE_PREFIX3:
5880 case CEE_PREFIX2:
5881 case CEE_PREFIXREF: ves_abort(); break;
5882 #endif
5884 * Note: Exceptions thrown when executing a prefixed opcode need
5885 * to take into account the number of prefix bytes (usually the
5886 * throw point is just (ip - n_prefix_bytes).
5888 case CEE_PREFIX1:
5889 ++td->ip;
5890 switch (*td->ip) {
5891 case CEE_ARGLIST:
5892 interp_add_ins (td, MINT_ARGLIST);
5893 PUSH_VT (td, SIZEOF_VOID_P);
5894 PUSH_SIMPLE_TYPE (td, STACK_TYPE_VT);
5895 ++td->ip;
5896 break;
5897 case CEE_CEQ:
5898 CHECK_STACK(td, 2);
5899 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP) {
5900 interp_add_ins (td, MINT_CEQ_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5901 } else {
5902 if (td->sp [-1].type == STACK_TYPE_R4 && td->sp [-2].type == STACK_TYPE_R8)
5903 interp_add_ins (td, MINT_CONV_R8_R4);
5904 if (td->sp [-1].type == STACK_TYPE_R8 && td->sp [-2].type == STACK_TYPE_R4)
5905 interp_add_ins (td, MINT_CONV_R8_R4_SP);
5906 interp_add_ins (td, MINT_CEQ_I4 + td->sp [-1].type - STACK_TYPE_I4);
5908 --td->sp;
5909 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5910 ++td->ip;
5911 break;
5912 case CEE_CGT:
5913 CHECK_STACK(td, 2);
5914 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5915 interp_add_ins (td, MINT_CGT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5916 else
5917 interp_add_ins (td, MINT_CGT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5918 --td->sp;
5919 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5920 ++td->ip;
5921 break;
5922 case CEE_CGT_UN:
5923 CHECK_STACK(td, 2);
5924 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5925 interp_add_ins (td, MINT_CGT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5926 else
5927 interp_add_ins (td, MINT_CGT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5928 --td->sp;
5929 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5930 ++td->ip;
5931 break;
5932 case CEE_CLT:
5933 CHECK_STACK(td, 2);
5934 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5935 interp_add_ins (td, MINT_CLT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5936 else
5937 interp_add_ins (td, MINT_CLT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5938 --td->sp;
5939 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5940 ++td->ip;
5941 break;
5942 case CEE_CLT_UN:
5943 CHECK_STACK(td, 2);
5944 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5945 interp_add_ins (td, MINT_CLT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5946 else
5947 interp_add_ins (td, MINT_CLT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5948 --td->sp;
5949 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5950 ++td->ip;
5951 break;
5952 case CEE_LDVIRTFTN: /* fallthrough */
5953 case CEE_LDFTN: {
5954 MonoMethod *m;
5955 if (*td->ip == CEE_LDVIRTFTN) {
5956 CHECK_STACK (td, 1);
5957 --td->sp;
5959 token = read32 (td->ip + 1);
5960 if (method->wrapper_type != MONO_WRAPPER_NONE)
5961 m = (MonoMethod *)mono_method_get_wrapper_data (method, token);
5962 else {
5963 m = mono_get_method_checked (image, token, NULL, generic_context, error);
5964 goto_if_nok (error, exit);
5967 if (!mono_method_can_access_method (method, m))
5968 interp_generate_mae_throw (td, method, m);
5970 if (method->wrapper_type == MONO_WRAPPER_NONE && m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
5971 m = mono_marshal_get_synchronized_wrapper (m);
5973 interp_add_ins (td, *td->ip == CEE_LDFTN ? MINT_LDFTN : MINT_LDVIRTFTN);
5974 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
5975 goto_if_nok (error, exit);
5976 td->ip += 5;
5977 PUSH_SIMPLE_TYPE (td, STACK_TYPE_F);
5978 break;
5980 case CEE_LDARG: {
5981 int arg_n = read16 (td->ip + 1);
5982 if (!inlining)
5983 load_arg (td, arg_n);
5984 else
5985 load_local (td, arg_locals [arg_n]);
5986 td->ip += 3;
5987 break;
5989 case CEE_LDARGA: {
5990 int n = read16 (td->ip + 1);
5992 if (!inlining) {
5993 get_arg_type_exact (td, n, &mt);
5994 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
5995 td->last_ins->data [0] = n;
5996 } else {
5997 int loc_n = arg_locals [n];
5998 interp_add_ins (td, MINT_LDLOCA_S);
5999 td->last_ins->data [0] = loc_n;
6000 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
6002 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
6003 td->ip += 3;
6004 break;
6006 case CEE_STARG: {
6007 int arg_n = read16 (td->ip + 1);
6008 if (!inlining)
6009 store_arg (td, arg_n);
6010 else
6011 store_local (td, arg_locals [arg_n]);
6012 td->ip += 3;
6013 break;
6015 case CEE_LDLOC: {
6016 int loc_n = read16 (td->ip + 1);
6017 if (!inlining)
6018 load_local (td, loc_n);
6019 else
6020 load_local (td, local_locals [loc_n]);
6021 td->ip += 3;
6022 break;
6024 case CEE_LDLOCA: {
6025 int loc_n = read16 (td->ip + 1);
6026 interp_add_ins (td, MINT_LDLOCA_S);
6027 if (inlining)
6028 loc_n = local_locals [loc_n];
6029 td->last_ins->data [0] = loc_n;
6030 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
6031 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
6032 td->ip += 3;
6033 break;
6035 case CEE_STLOC: {
6036 int loc_n = read16 (td->ip + 1);
6037 if (!inlining)
6038 store_local (td, loc_n);
6039 else
6040 store_local (td, local_locals [loc_n]);
6041 td->ip += 3;
6042 break;
6044 case CEE_LOCALLOC:
6045 INLINE_FAILURE;
6046 CHECK_STACK (td, 1);
6047 #if SIZEOF_VOID_P == 8
6048 if (td->sp [-1].type == STACK_TYPE_I8)
6049 interp_add_ins (td, MINT_CONV_I4_I8);
6050 #endif
6051 interp_add_ins (td, MINT_LOCALLOC);
6052 if (td->sp != td->stack + 1)
6053 g_warning("CEE_LOCALLOC: stack not empty");
6054 ++td->ip;
6055 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
6056 break;
6057 #if 0
6058 case CEE_UNUSED57: ves_abort(); break;
6059 #endif
6060 case CEE_ENDFILTER:
6061 interp_add_ins (td, MINT_ENDFILTER);
6062 ++td->ip;
6063 break;
6064 case CEE_UNALIGNED_:
6065 td->ip += 2;
6066 break;
6067 case CEE_VOLATILE_:
6068 ++td->ip;
6069 volatile_ = TRUE;
6070 break;
6071 case CEE_TAIL_:
6072 ++td->ip;
6073 /* FIX: should do something? */;
6074 // TODO: This should raise a method_tail_call profiler event.
6075 break;
6076 case CEE_INITOBJ:
6077 CHECK_STACK(td, 1);
6078 token = read32 (td->ip + 1);
6079 klass = mini_get_class (method, token, generic_context);
6080 CHECK_TYPELOAD (klass);
6081 if (m_class_is_valuetype (klass)) {
6082 interp_add_ins (td, MINT_INITOBJ);
6083 i32 = mono_class_value_size (klass, NULL);
6084 WRITE32_INS (td->last_ins, 0, &i32);
6085 --td->sp;
6086 } else {
6087 interp_add_ins (td, MINT_LDNULL);
6088 PUSH_TYPE(td, STACK_TYPE_O, NULL);
6089 interp_add_ins (td, MINT_STIND_REF);
6090 td->sp -= 2;
6092 td->ip += 5;
6093 break;
6094 case CEE_CPBLK:
6095 CHECK_STACK(td, 3);
6096 /* FIX? convert length to I8? */
6097 if (volatile_)
6098 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
6099 interp_add_ins (td, MINT_CPBLK);
6100 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_SEQ);
6101 td->sp -= 3;
6102 ++td->ip;
6103 break;
6104 case CEE_READONLY_:
6105 readonly = TRUE;
6106 td->ip += 1;
6107 break;
6108 case CEE_CONSTRAINED_:
6109 token = read32 (td->ip + 1);
6110 constrained_class = mini_get_class (method, token, generic_context);
6111 CHECK_TYPELOAD (constrained_class);
6112 td->ip += 5;
6113 break;
6114 case CEE_INITBLK:
6115 CHECK_STACK(td, 3);
6116 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
6117 interp_add_ins (td, MINT_INITBLK);
6118 td->sp -= 3;
6119 td->ip += 1;
6120 break;
6121 case CEE_NO_:
6122 /* FIXME: implement */
6123 td->ip += 2;
6124 break;
6125 case CEE_RETHROW: {
6126 int clause_index = td->clause_indexes [in_offset];
6127 g_assert (clause_index != -1);
6128 SIMPLE_OP (td, MINT_RETHROW);
6129 td->last_ins->data [0] = rtm->exvar_offsets [clause_index];
6130 td->sp = td->stack;
6131 break;
6133 case CEE_SIZEOF: {
6134 gint32 size;
6135 token = read32 (td->ip + 1);
6136 td->ip += 5;
6137 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (m_class_get_image (method->klass)) && !generic_context) {
6138 int align;
6139 MonoType *type = mono_type_create_from_typespec_checked (image, token, error);
6140 goto_if_nok (error, exit);
6141 size = mono_type_size (type, &align);
6142 } else {
6143 int align;
6144 MonoClass *szclass = mini_get_class (method, token, generic_context);
6145 CHECK_TYPELOAD (szclass);
6146 #if 0
6147 if (!szclass->valuetype)
6148 THROW_EX (mono_exception_from_name (mono_defaults.corlib, "System", "InvalidProgramException"), ip - 5);
6149 #endif
6150 size = mono_type_size (m_class_get_byval_arg (szclass), &align);
6152 interp_add_ins (td, MINT_LDC_I4);
6153 WRITE32_INS (td->last_ins, 0, &size);
6154 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
6155 break;
6157 case CEE_REFANYTYPE:
6158 interp_add_ins (td, MINT_REFANYTYPE);
6159 td->ip += 1;
6160 POP_VT (td, sizeof (MonoTypedRef));
6161 PUSH_VT (td, sizeof (gpointer));
6162 SET_TYPE(td->sp - 1, STACK_TYPE_VT, NULL);
6163 break;
6164 default:
6165 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);
6167 break;
6168 default:
6169 g_error ("transform.c: Unimplemented opcode: %02x at 0x%x\n", *td->ip, td->ip-header->code);
6172 // No IR instructions were added as part of a bb_start IL instruction. Add a MINT_NOP
6173 // so we always have an instruction associated with a bb_start. This is simple and avoids
6174 // any complications associated with il_offset tracking.
6175 if (prev_last_ins == td->last_ins && (!inlining && td->is_bb_start [in_offset]) && td->ip < end)
6176 interp_add_ins (td, MINT_NOP);
6179 g_assert (td->ip == end);
6181 exit_ret:
6182 g_free (arg_locals);
6183 g_free (local_locals);
6184 mono_basic_block_free (original_bb);
6186 return ret;
6187 exit:
6188 ret = FALSE;
6189 goto exit_ret;
6192 // Find the offset of the first interp instruction generated starting il_offset
6193 // This is needed to find the end of clauses.
6194 static int
6195 find_in_offset (TransformData *td, int il_offset)
6197 int i = il_offset;
6198 while (!td->in_offsets [i])
6199 i++;
6200 return td->in_offsets [i] - 1;
6203 // We store in the in_offset array the native_offset + 1, so 0 can mean only that the il
6204 // offset is uninitialized. Otherwise 0 is valid value for first interp instruction.
6205 static int
6206 get_in_offset (TransformData *td, int il_offset)
6208 int target_offset = td->in_offsets [il_offset];
6209 g_assert (target_offset);
6210 return target_offset - 1;
6213 static void
6214 handle_relocations (TransformData *td)
6216 // Handle relocations
6217 for (int i = 0; i < td->relocs->len; ++i) {
6218 Reloc *reloc = (Reloc*)g_ptr_array_index (td->relocs, i);
6219 int offset = get_in_offset (td, reloc->target) - reloc->offset;
6221 switch (reloc->type) {
6222 case RELOC_SHORT_BRANCH:
6223 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
6224 td->new_code [reloc->offset + 1] = offset;
6225 break;
6226 case RELOC_LONG_BRANCH: {
6227 guint16 *v = (guint16 *) &offset;
6228 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
6229 g_assert (td->new_code [reloc->offset + 2] == 0xbeef);
6230 td->new_code [reloc->offset + 1] = *(guint16 *) v;
6231 td->new_code [reloc->offset + 2] = *(guint16 *) (v + 1);
6232 break;
6234 case RELOC_SWITCH: {
6235 guint16 *v = (guint16*)&offset;
6236 g_assert (td->new_code [reloc->offset] == 0xdead);
6237 g_assert (td->new_code [reloc->offset + 1] == 0xbeef);
6238 td->new_code [reloc->offset] = *(guint16*)v;
6239 td->new_code [reloc->offset + 1] = *(guint16*)(v + 1);
6240 break;
6242 default:
6243 g_assert_not_reached ();
6244 break;
6250 static int
6251 get_inst_length (InterpInst *ins)
6253 if (ins->opcode == MINT_SWITCH)
6254 return MINT_SWITCH_LEN (READ32 (&ins->data [0]));
6255 #ifdef ENABLE_EXPERIMENT_TIERED
6256 else if (MINT_IS_PATCHABLE_CALL (ins->opcode))
6257 return MAX (mono_interp_oplen [MINT_JIT_CALL2], mono_interp_oplen [ins->opcode]);
6258 #endif
6259 else
6260 return mono_interp_oplen [ins->opcode];
6263 static void
6264 get_inst_stack_usage (TransformData *td, InterpInst *ins, int *pop, int *push)
6266 guint16 opcode = ins->opcode;
6267 if (mono_interp_oppop [opcode] == MINT_VAR_POP ||
6268 mono_interp_oppush [opcode] == MINT_VAR_PUSH) {
6269 switch (opcode) {
6270 case MINT_JIT_CALL:
6271 case MINT_CALL:
6272 case MINT_CALLVIRT:
6273 case MINT_CALLVIRT_FAST:
6274 case MINT_VCALL:
6275 case MINT_VCALLVIRT:
6276 case MINT_VCALLVIRT_FAST: {
6277 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6278 *pop = imethod->param_count + imethod->hasthis;
6279 if (opcode == MINT_JIT_CALL)
6280 *push = imethod->rtype->type != MONO_TYPE_VOID;
6281 else
6282 *push = opcode == MINT_CALL || opcode == MINT_CALLVIRT || opcode == MINT_CALLVIRT_FAST;
6283 break;
6285 #ifndef ENABLE_NETCORE
6286 case MINT_CALLRUN: {
6287 MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [1]];
6288 *pop = csignature->param_count + csignature->hasthis;
6289 *push = csignature->ret->type != MONO_TYPE_VOID;
6290 break;
6292 #endif
6293 case MINT_CALLI:
6294 case MINT_CALLI_NAT:
6295 case MINT_CALLI_NAT_FAST: {
6296 MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [0]];
6297 *pop = csignature->param_count + csignature->hasthis + 1;
6298 *push = csignature->ret->type != MONO_TYPE_VOID;
6299 break;
6301 case MINT_CALL_VARARG: {
6302 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6303 MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [1]];
6304 *pop = imethod->param_count + imethod->hasthis + csignature->param_count - csignature->sentinelpos;
6305 *push = imethod->rtype->type != MONO_TYPE_VOID;
6306 break;
6308 case MINT_NEWOBJ_FAST: {
6309 int param_count = ins->data [1];
6310 gboolean is_inlined = ins->data [0] == INLINED_METHOD_FLAG;
6311 if (is_inlined) {
6312 // This needs to be handled explictly during cprop, in order to properly
6313 // keep track of stack contents
6314 *pop = 0;
6315 *push = 2;
6316 } else {
6317 *pop = param_count;
6318 *push = 1;
6320 break;
6322 case MINT_NEWOBJ_ARRAY:
6323 case MINT_NEWOBJ_VT_FAST:
6324 case MINT_NEWOBJ_VTST_FAST:
6325 *pop = ins->data [1];
6326 *push = 1;
6327 break;
6328 case MINT_LDELEMA:
6329 case MINT_LDELEMA_TC:
6330 *pop = ins->data [0] + 1;
6331 *push = 1;
6332 break;
6333 case MINT_NEWOBJ: {
6334 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6335 *pop = imethod->param_count;
6336 *push = 1;
6337 break;
6339 case MINT_INTRINS_BYREFERENCE_CTOR: {
6340 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6341 *pop = imethod->param_count;
6342 *push = 1;
6343 break;
6345 default:
6346 g_assert_not_reached ();
6348 } else {
6349 *pop = mono_interp_oppop [opcode];
6350 *push = mono_interp_oppush [opcode];
6354 static guint16*
6355 emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *ins)
6357 guint16 opcode = ins->opcode;
6358 guint16 *ip = start_ip;
6360 // We know what IL offset this instruction was created for. We can now map the IL offset
6361 // to the IR offset. We use this array to resolve the relocations, which reference the IL.
6362 if (ins->il_offset != -1 && !td->in_offsets [ins->il_offset]) {
6363 g_assert (ins->il_offset >= 0 && ins->il_offset < td->header->code_size);
6364 td->in_offsets [ins->il_offset] = start_ip - td->new_code + 1;
6366 MonoDebugLineNumberEntry lne;
6367 lne.native_offset = (guint8*)start_ip - (guint8*)td->new_code;
6368 lne.il_offset = ins->il_offset;
6369 g_array_append_val (td->line_numbers, lne);
6372 if (opcode == MINT_NOP)
6373 return ip;
6375 *ip++ = opcode;
6376 if (opcode == MINT_SWITCH) {
6377 int labels = READ32 (&ins->data [0]);
6378 // Write number of switch labels
6379 *ip++ = ins->data [0];
6380 *ip++ = ins->data [1];
6381 // Add relocation for each label
6382 for (int i = 0; i < labels; i++) {
6383 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
6384 reloc->type = RELOC_SWITCH;
6385 reloc->offset = ip - td->new_code;
6386 reloc->target = READ32 (&ins->data [2 + i * 2]);
6387 g_ptr_array_add (td->relocs, reloc);
6388 *ip++ = 0xdead;
6389 *ip++ = 0xbeef;
6391 } else if ((opcode >= MINT_BRFALSE_I4_S && opcode <= MINT_BRTRUE_R8_S) ||
6392 (opcode >= MINT_BEQ_I4_S && opcode <= MINT_BLT_UN_R8_S) ||
6393 opcode == MINT_BR_S || opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK) {
6394 const int br_offset = start_ip - td->new_code;
6395 if (ins->data [0] < ins->il_offset) {
6396 // Backwards branch. We can already patch it.
6397 *ip++ = get_in_offset (td, ins->data [0]) - br_offset;
6398 } else {
6399 // We don't know the in_offset of the target, add a reloc
6400 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
6401 reloc->type = RELOC_SHORT_BRANCH;
6402 reloc->offset = br_offset;
6403 reloc->target = ins->data [0];
6404 g_ptr_array_add (td->relocs, reloc);
6405 *ip++ = 0xdead;
6407 } else if ((opcode >= MINT_BRFALSE_I4 && opcode <= MINT_BRTRUE_R8) ||
6408 (opcode >= MINT_BEQ_I4 && opcode <= MINT_BLT_UN_R8) ||
6409 opcode == MINT_BR || opcode == MINT_LEAVE || opcode == MINT_LEAVE_CHECK) {
6410 const int br_offset = start_ip - td->new_code;
6411 int target_il = READ32 (&ins->data [0]);
6412 if (target_il < ins->il_offset) {
6413 // Backwards branch. We can already patch it
6414 const int br_offset = start_ip - td->new_code;
6415 int target_offset = get_in_offset (td, target_il) - br_offset;
6416 WRITE32 (ip, &target_offset);
6417 } else {
6418 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
6419 reloc->type = RELOC_LONG_BRANCH;
6420 reloc->offset = br_offset;
6421 reloc->target = target_il;
6422 g_ptr_array_add (td->relocs, reloc);
6423 *ip++ = 0xdead;
6424 *ip++ = 0xbeef;
6426 } else if (opcode == MINT_SDB_SEQ_POINT) {
6427 SeqPoint *seqp = (SeqPoint*)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint));
6428 InterpBasicBlock *cbb;
6430 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY) {
6431 seqp->il_offset = METHOD_ENTRY_IL_OFFSET;
6432 cbb = td->offset_to_bb [0];
6433 } else {
6434 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT)
6435 seqp->il_offset = METHOD_EXIT_IL_OFFSET;
6436 else
6437 seqp->il_offset = ins->il_offset;
6438 cbb = td->offset_to_bb [ins->il_offset];
6440 seqp->native_offset = (guint8*)start_ip - (guint8*)td->new_code;
6441 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK)
6442 seqp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
6443 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL)
6444 seqp->flags |= MONO_SEQ_POINT_FLAG_NESTED_CALL;
6445 g_ptr_array_add (td->seq_points, seqp);
6447 cbb->seq_points = g_slist_prepend_mempool (td->mempool, cbb->seq_points, seqp);
6448 cbb->last_seq_point = seqp;
6449 #ifdef ENABLE_EXPERIMENT_TIERED
6450 } else if (ins->flags & INTERP_INST_FLAG_RECORD_CALL_PATCH) {
6451 g_assert (MINT_IS_PATCHABLE_CALL (opcode));
6453 /* TODO: could `ins` be removed by any interp optimization? */
6454 MonoMethod *target_method = (MonoMethod *) g_hash_table_lookup (td->patchsite_hash, ins);
6455 g_assert (target_method);
6456 g_hash_table_remove (td->patchsite_hash, ins);
6458 mini_tiered_record_callsite (start_ip, target_method, TIERED_PATCH_KIND_INTERP);
6460 int size = mono_interp_oplen [ins->opcode];
6461 int jit_call2_size = mono_interp_oplen [MINT_JIT_CALL2];
6463 g_assert (size < jit_call2_size);
6465 // Emit the rest of the data
6466 for (int i = 0; i < size - 1; i++)
6467 *ip++ = ins->data [i];
6469 /* intentional padding so we can patch a MINT_JIT_CALL2 here */
6470 for (int i = size - 1; i < (jit_call2_size - 1); i++)
6471 *ip++ = MINT_NIY;
6472 #endif
6473 } else {
6474 if (MINT_IS_LDLOC (opcode) || MINT_IS_STLOC (opcode) || MINT_IS_STLOC_NP (opcode) || opcode == MINT_LDLOCA_S ||
6475 MINT_IS_LDLOCFLD (opcode) || MINT_IS_LOCUNOP (opcode) || MINT_IS_STLOCFLD (opcode)) {
6476 ins->data [0] = get_interp_local_offset (td, ins->data [0]);
6477 } else if (MINT_IS_MOVLOC (opcode)) {
6478 ins->data [0] = get_interp_local_offset (td, ins->data [0]);
6479 ins->data [1] = get_interp_local_offset (td, ins->data [1]);
6482 int size = get_inst_length (ins) - 1;
6483 // Emit the rest of the data
6484 for (int i = 0; i < size; i++)
6485 *ip++ = ins->data [i];
6487 mono_interp_stats.emitted_instructions++;
6488 return ip;
6491 // Generates the final code, after we are done with all the passes
6492 static void
6493 generate_compacted_code (TransformData *td)
6495 guint16 *ip;
6496 int size = 0;
6497 td->relocs = g_ptr_array_new ();
6499 // Iterate once to compute the exact size of the compacted code
6500 InterpInst *ins = td->first_ins;
6501 while (ins) {
6502 size += get_inst_length (ins);
6503 ins = ins->next;
6506 // Generate the compacted stream of instructions
6507 td->new_code = ip = (guint16*)mono_domain_alloc0 (td->rtm->domain, size * sizeof (guint16));
6508 ins = td->first_ins;
6509 while (ins) {
6510 ip = emit_compacted_instruction (td, ip, ins);
6511 ins = ins->next;
6513 td->new_code_end = ip;
6514 td->in_offsets [td->header->code_size] = td->new_code_end - td->new_code;
6516 // Patch all branches
6517 handle_relocations (td);
6519 g_ptr_array_free (td->relocs, TRUE);
6522 static int
6523 get_movloc_for_type (int mt)
6525 switch (mt) {
6526 case MINT_TYPE_I1:
6527 case MINT_TYPE_U1:
6528 return MINT_MOVLOC_1;
6529 case MINT_TYPE_I2:
6530 case MINT_TYPE_U2:
6531 return MINT_MOVLOC_2;
6532 case MINT_TYPE_I4:
6533 case MINT_TYPE_R4:
6534 return MINT_MOVLOC_4;
6535 case MINT_TYPE_I8:
6536 case MINT_TYPE_R8:
6537 return MINT_MOVLOC_8;
6538 case MINT_TYPE_O:
6539 case MINT_TYPE_P:
6540 #if SIZEOF_VOID_P == 8
6541 return MINT_MOVLOC_8;
6542 #else
6543 return MINT_MOVLOC_4;
6544 #endif
6545 case MINT_TYPE_VT:
6546 return MINT_MOVLOC_VT;
6548 g_assert_not_reached ();
6551 // The value of local has changed. This means the contents of the stack where the
6552 // local was loaded, no longer contain the value of the local. Clear them.
6553 static void
6554 clear_stack_content_info_for_local (StackContentInfo *start, StackContentInfo *end, int local)
6556 StackContentInfo *si;
6557 for (si = start; si < end; si++) {
6558 if (si->val.type == STACK_VALUE_LOCAL && si->val.local == local)
6559 si->val.type = STACK_VALUE_NONE;
6563 // The value of local has changed. This means we can no longer assume that any other local
6564 // is a copy of this local.
6565 static void
6566 clear_local_content_info_for_local (StackValue *start, StackValue *end, int local)
6568 StackValue *sval;
6569 for (sval = start; sval < end; sval++) {
6570 if (sval->type == STACK_VALUE_LOCAL && sval->local == local)
6571 sval->type = STACK_VALUE_NONE;
6575 static gboolean
6576 interp_local_deadce (TransformData *td, int *local_ref_count)
6578 InterpInst *ins;
6579 gboolean needs_dce = FALSE;
6580 gboolean needs_cprop = FALSE;
6582 for (int i = 0; i < td->locals_size; i++) {
6583 g_assert (local_ref_count [i] >= 0);
6584 if (!local_ref_count [i] &&
6585 (td->locals [i].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0 &&
6586 (td->locals [i].flags & INTERP_LOCAL_FLAG_DEAD) == 0) {
6587 needs_dce = TRUE;
6588 // If we do another deadce iteration over the code, make sure we don't try
6589 // to kill instructions accessing locals that have already been handled in
6590 // a previous iteration.
6591 td->locals [i].flags |= INTERP_LOCAL_FLAG_DEAD;
6592 break;
6596 // Return early if all locals are alive
6597 if (!needs_dce)
6598 return FALSE;
6600 // Kill instructions that don't use stack and are storing into dead locals
6601 for (ins = td->first_ins; ins != NULL; ins = ins->next) {
6602 if (MINT_IS_STLOC_NP (ins->opcode)) {
6603 if (!local_ref_count [ins->data [0]] && (td->locals [ins->data [0]].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0) {
6604 interp_clear_ins (td, ins);
6605 mono_interp_stats.killed_instructions++;
6606 // We killed an instruction that makes use of the stack. This might uncover new optimizations
6607 needs_cprop = TRUE;
6609 } else if (MINT_IS_MOVLOC (ins->opcode)) {
6610 if (!local_ref_count [ins->data [1]] && (td->locals [ins->data [1]].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0) {
6611 interp_clear_ins (td, ins);
6612 mono_interp_stats.killed_instructions++;
6614 } else if (MINT_IS_STLOC (ins->opcode) && ins->opcode != MINT_STLOC_VT) {
6615 if (!local_ref_count [ins->data [0]] && (td->locals [ins->data [0]].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0) {
6616 // We store to a dead stloc, we can replace it with a POP to save local space
6617 ins->opcode = MINT_POP;
6618 mono_interp_stats.added_pop_count++;
6619 // We might to be able to kill both the pop and the instruction pushing the value
6620 needs_cprop = TRUE;
6624 return needs_cprop;
6627 #define INTERP_FOLD_UNOP(opcode,stack_type,field,op) \
6628 case opcode: \
6629 g_assert (sp->val.type == stack_type); \
6630 result.type = stack_type; \
6631 result.field = op sp->val.field; \
6632 break;
6634 #define INTERP_FOLD_CONV(opcode,stack_type_dst,field_dst,stack_type_src,field_src,cast_type) \
6635 case opcode: \
6636 g_assert (sp->val.type == stack_type_src); \
6637 result.type = stack_type_dst; \
6638 result.field_dst = (cast_type)sp->val.field_src; \
6639 break;
6641 static InterpInst*
6642 interp_fold_unop (TransformData *td, StackContentInfo *sp, InterpInst *ins)
6644 StackValue result;
6645 // Decrement sp so it's easier to access top of the stack
6646 sp--;
6647 if (sp->val.type != STACK_VALUE_I4 && sp->val.type != STACK_VALUE_I8)
6648 goto cfold_failed;
6650 // Top of the stack is a constant
6651 switch (ins->opcode) {
6652 INTERP_FOLD_UNOP (MINT_ADD1_I4, STACK_VALUE_I4, i, 1+);
6653 INTERP_FOLD_UNOP (MINT_ADD1_I8, STACK_VALUE_I8, l, 1+);
6654 INTERP_FOLD_UNOP (MINT_SUB1_I4, STACK_VALUE_I4, i, -1+);
6655 INTERP_FOLD_UNOP (MINT_SUB1_I8, STACK_VALUE_I8, l, -1+);
6656 INTERP_FOLD_UNOP (MINT_NEG_I4, STACK_VALUE_I4, i, -);
6657 INTERP_FOLD_UNOP (MINT_NEG_I8, STACK_VALUE_I8, l, -);
6658 INTERP_FOLD_UNOP (MINT_NOT_I4, STACK_VALUE_I4, i, ~);
6659 INTERP_FOLD_UNOP (MINT_NOT_I8, STACK_VALUE_I8, l, ~);
6660 INTERP_FOLD_UNOP (MINT_CEQ0_I4, STACK_VALUE_I4, i, 0 ==);
6662 INTERP_FOLD_CONV (MINT_CONV_I1_I4, STACK_VALUE_I4, i, STACK_VALUE_I4, i, gint8);
6663 INTERP_FOLD_CONV (MINT_CONV_I1_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, gint8);
6664 INTERP_FOLD_CONV (MINT_CONV_U1_I4, STACK_VALUE_I4, i, STACK_VALUE_I4, i, guint8);
6665 INTERP_FOLD_CONV (MINT_CONV_U1_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, guint8);
6667 INTERP_FOLD_CONV (MINT_CONV_I2_I4, STACK_VALUE_I4, i, STACK_VALUE_I4, i, gint16);
6668 INTERP_FOLD_CONV (MINT_CONV_I2_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, gint16);
6669 INTERP_FOLD_CONV (MINT_CONV_U2_I4, STACK_VALUE_I4, i, STACK_VALUE_I4, i, guint16);
6670 INTERP_FOLD_CONV (MINT_CONV_U2_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, guint16);
6672 INTERP_FOLD_CONV (MINT_CONV_I4_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, gint32);
6673 INTERP_FOLD_CONV (MINT_CONV_U4_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, gint32);
6675 INTERP_FOLD_CONV (MINT_CONV_I8_I4, STACK_VALUE_I8, l, STACK_VALUE_I4, i, gint32);
6676 INTERP_FOLD_CONV (MINT_CONV_I8_U4, STACK_VALUE_I8, l, STACK_VALUE_I4, i, guint32);
6678 default:
6679 goto cfold_failed;
6682 // We were able to compute the result of the ins instruction. We store the
6683 // current value for the top of the stack and, if possible, try to replace the
6684 // instructions that are part of this unary operation with a single LDC.
6685 mono_interp_stats.constant_folds++;
6686 if (sp->ins != NULL) {
6687 interp_clear_ins (td, sp->ins);
6688 mono_interp_stats.killed_instructions++;
6689 if (result.type == STACK_VALUE_I4)
6690 ins = interp_get_ldc_i4_from_const (td, ins, result.i);
6691 else if (result.type == STACK_VALUE_I8)
6692 ins = interp_inst_replace_with_i8_const (td, ins, result.l);
6693 else
6694 g_assert_not_reached ();
6695 if (td->verbose_level) {
6696 g_print ("Fold unop :\n\t");
6697 dump_interp_inst_newline (ins);
6699 sp->ins = ins;
6701 sp->val = result;
6702 return ins;
6704 cfold_failed:
6705 sp->ins = NULL;
6706 sp->val.type = STACK_VALUE_NONE;
6707 return ins;
6710 #define INTERP_FOLD_BINOP(opcode,stack_type,field,op) \
6711 case opcode: \
6712 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6713 result.type = stack_type; \
6714 result.field = sp [0].val.field op sp [1].val.field; \
6715 break;
6717 #define INTERP_FOLD_BINOP_FULL(opcode,stack_type,field,op,cast_type,cond) \
6718 case opcode: \
6719 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6720 if (!(cond)) goto cfold_failed; \
6721 result.type = stack_type; \
6722 result.field = (cast_type)sp [0].val.field op (cast_type)sp [1].val.field; \
6723 break;
6725 #define INTERP_FOLD_SHIFTOP(opcode,stack_type,field,shift_op,cast_type) \
6726 case opcode: \
6727 g_assert (sp [1].val.type == STACK_VALUE_I4); \
6728 result.type = stack_type; \
6729 result.field = (cast_type)sp [0].val.field shift_op sp [1].val.i; \
6730 break;
6732 #define INTERP_FOLD_RELOP(opcode,stack_type,field,relop,cast_type) \
6733 case opcode: \
6734 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6735 result.type = STACK_VALUE_I4; \
6736 result.field = (cast_type) sp [0].val.field relop (cast_type) sp [1].val.field; \
6737 break;
6740 static InterpInst*
6741 interp_fold_binop (TransformData *td, StackContentInfo *sp, InterpInst *ins)
6743 StackValue result;
6744 // Decrement sp so it's easier to access top of the stack
6745 sp -= 2;
6746 if (sp [0].val.type != STACK_VALUE_I4 && sp [0].val.type != STACK_VALUE_I8)
6747 goto cfold_failed;
6748 if (sp [1].val.type != STACK_VALUE_I4 && sp [1].val.type != STACK_VALUE_I8)
6749 goto cfold_failed;
6751 // Top two values of the stack are constants
6752 switch (ins->opcode) {
6753 INTERP_FOLD_BINOP (MINT_ADD_I4, STACK_VALUE_I4, i, +);
6754 INTERP_FOLD_BINOP (MINT_ADD_I8, STACK_VALUE_I8, l, +);
6755 INTERP_FOLD_BINOP (MINT_SUB_I4, STACK_VALUE_I4, i, -);
6756 INTERP_FOLD_BINOP (MINT_SUB_I8, STACK_VALUE_I8, l, -);
6757 INTERP_FOLD_BINOP (MINT_MUL_I4, STACK_VALUE_I4, i, *);
6758 INTERP_FOLD_BINOP (MINT_MUL_I8, STACK_VALUE_I8, l, *);
6760 INTERP_FOLD_BINOP (MINT_AND_I4, STACK_VALUE_I4, i, &);
6761 INTERP_FOLD_BINOP (MINT_AND_I8, STACK_VALUE_I8, l, &);
6762 INTERP_FOLD_BINOP (MINT_OR_I4, STACK_VALUE_I4, i, |);
6763 INTERP_FOLD_BINOP (MINT_OR_I8, STACK_VALUE_I8, l, |);
6764 INTERP_FOLD_BINOP (MINT_XOR_I4, STACK_VALUE_I4, i, ^);
6765 INTERP_FOLD_BINOP (MINT_XOR_I8, STACK_VALUE_I8, l, ^);
6767 INTERP_FOLD_SHIFTOP (MINT_SHL_I4, STACK_VALUE_I4, i, <<, gint32);
6768 INTERP_FOLD_SHIFTOP (MINT_SHL_I8, STACK_VALUE_I8, l, <<, gint64);
6769 INTERP_FOLD_SHIFTOP (MINT_SHR_I4, STACK_VALUE_I4, i, >>, gint32);
6770 INTERP_FOLD_SHIFTOP (MINT_SHR_I8, STACK_VALUE_I8, l, >>, gint64);
6771 INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I4, STACK_VALUE_I4, i, >>, guint32);
6772 INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I8, STACK_VALUE_I8, l, >>, guint64);
6774 INTERP_FOLD_RELOP (MINT_CEQ_I4, STACK_VALUE_I4, i, ==, gint32);
6775 INTERP_FOLD_RELOP (MINT_CEQ_I8, STACK_VALUE_I8, l, ==, gint64);
6776 INTERP_FOLD_RELOP (MINT_CNE_I4, STACK_VALUE_I4, i, !=, gint32);
6777 INTERP_FOLD_RELOP (MINT_CNE_I8, STACK_VALUE_I8, l, !=, gint64);
6779 INTERP_FOLD_RELOP (MINT_CGT_I4, STACK_VALUE_I4, i, >, gint32);
6780 INTERP_FOLD_RELOP (MINT_CGT_I8, STACK_VALUE_I8, l, >, gint64);
6781 INTERP_FOLD_RELOP (MINT_CGT_UN_I4, STACK_VALUE_I4, i, >, guint32);
6782 INTERP_FOLD_RELOP (MINT_CGT_UN_I8, STACK_VALUE_I8, l, >, guint64);
6784 INTERP_FOLD_RELOP (MINT_CGE_I4, STACK_VALUE_I4, i, >=, gint32);
6785 INTERP_FOLD_RELOP (MINT_CGE_I8, STACK_VALUE_I8, l, >=, gint64);
6786 INTERP_FOLD_RELOP (MINT_CGE_UN_I4, STACK_VALUE_I4, i, >=, guint32);
6787 INTERP_FOLD_RELOP (MINT_CGE_UN_I8, STACK_VALUE_I8, l, >=, guint64);
6789 INTERP_FOLD_RELOP (MINT_CLT_I4, STACK_VALUE_I4, i, <, gint32);
6790 INTERP_FOLD_RELOP (MINT_CLT_I8, STACK_VALUE_I8, l, <, gint64);
6791 INTERP_FOLD_RELOP (MINT_CLT_UN_I4, STACK_VALUE_I4, i, <, guint32);
6792 INTERP_FOLD_RELOP (MINT_CLT_UN_I8, STACK_VALUE_I8, l, <, guint64);
6794 INTERP_FOLD_RELOP (MINT_CLE_I4, STACK_VALUE_I4, i, <=, gint32);
6795 INTERP_FOLD_RELOP (MINT_CLE_I8, STACK_VALUE_I8, l, <=, gint64);
6796 INTERP_FOLD_RELOP (MINT_CLE_UN_I4, STACK_VALUE_I4, i, <=, guint32);
6797 INTERP_FOLD_RELOP (MINT_CLE_UN_I8, STACK_VALUE_I8, l, <=, guint64);
6799 INTERP_FOLD_BINOP_FULL (MINT_DIV_I4, STACK_VALUE_I4, i, /, gint32, sp [1].val.i != 0 && (sp [0].val.i != G_MININT32 || sp [1].val.i != -1));
6800 INTERP_FOLD_BINOP_FULL (MINT_DIV_I8, STACK_VALUE_I8, l, /, gint64, sp [1].val.l != 0 && (sp [0].val.l != G_MININT64 || sp [1].val.l != -1));
6801 INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I4, STACK_VALUE_I4, i, /, guint32, sp [1].val.i != 0);
6802 INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I8, STACK_VALUE_I8, l, /, guint64, sp [1].val.l != 0);
6804 INTERP_FOLD_BINOP_FULL (MINT_REM_I4, STACK_VALUE_I4, i, %, gint32, sp [1].val.i != 0 && (sp [0].val.i != G_MININT32 || sp [1].val.i != -1));
6805 INTERP_FOLD_BINOP_FULL (MINT_REM_I8, STACK_VALUE_I8, l, %, gint64, sp [1].val.l != 0 && (sp [0].val.l != G_MININT64 || sp [1].val.l != -1));
6806 INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I4, STACK_VALUE_I4, i, %, guint32, sp [1].val.i != 0);
6807 INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I8, STACK_VALUE_I8, l, %, guint64, sp [1].val.l != 0);
6809 default:
6810 goto cfold_failed;
6813 // We were able to compute the result of the ins instruction. We store the
6814 // current value for the top of the stack and, if possible, try to replace the
6815 // instructions that are part of this unary operation with a single LDC.
6816 mono_interp_stats.constant_folds++;
6817 if (sp [0].ins != NULL && sp [1].ins != NULL) {
6818 interp_clear_ins (td, sp [0].ins);
6819 interp_clear_ins (td, sp [1].ins);
6820 mono_interp_stats.killed_instructions += 2;
6821 if (result.type == STACK_VALUE_I4)
6822 ins = interp_get_ldc_i4_from_const (td, ins, result.i);
6823 else if (result.type == STACK_VALUE_I8)
6824 ins = interp_inst_replace_with_i8_const (td, ins, result.l);
6825 else
6826 g_assert_not_reached ();
6827 if (td->verbose_level) {
6828 g_print ("Fold binop :\n\t");
6829 dump_interp_inst_newline (ins);
6831 sp [0].ins = ins;
6832 } else {
6833 sp [0].ins = NULL;
6835 sp [0].val = result;
6836 return ins;
6838 cfold_failed:
6839 sp->ins = NULL;
6840 sp->val.type = STACK_VALUE_NONE;
6841 return ins;
6844 static gboolean
6845 interp_local_equal (StackValue *locals, int local1, int local2)
6847 if (local1 == local2)
6848 return TRUE;
6849 if (locals [local1].type == STACK_VALUE_LOCAL && locals [local1].local == local2) {
6850 // local1 is a copy of local2
6851 return TRUE;
6853 if (locals [local2].type == STACK_VALUE_LOCAL && locals [local2].local == local1) {
6854 // local2 is a copy of local1
6855 return TRUE;
6857 if (locals [local1].type == STACK_VALUE_I4 && locals [local2].type == STACK_VALUE_I4)
6858 return locals [local1].i == locals [local2].i;
6859 if (locals [local1].type == STACK_VALUE_I8 && locals [local2].type == STACK_VALUE_I8)
6860 return locals [local1].l == locals [local2].l;
6861 return FALSE;
6864 static void
6865 interp_cprop (TransformData *td)
6867 if (!td->max_stack_height)
6868 return;
6869 StackContentInfo *stack = (StackContentInfo*) g_malloc (td->max_stack_height * sizeof (StackContentInfo));
6870 StackContentInfo *stack_end = stack + td->max_stack_height;
6871 StackContentInfo *sp;
6872 StackValue *locals = (StackValue*) g_malloc (td->locals_size * sizeof (StackValue));
6873 int *local_ref_count = (int*) g_malloc (td->locals_size * sizeof (int));
6874 InterpInst *ins;
6875 int last_il_offset;
6877 retry:
6878 sp = stack;
6879 last_il_offset = -1;
6880 memset (local_ref_count, 0, td->locals_size * sizeof (int));
6882 for (ins = td->first_ins; ins != NULL; ins = ins->next) {
6883 int pop, push;
6884 int il_offset = ins->il_offset;
6885 // Optimizations take place only inside a single basic block
6886 // If two instructions have the same il_offset, then the second one
6887 // cannot be part the start of a basic block.
6888 gboolean is_bb_start = il_offset != -1 && td->is_bb_start [il_offset] && il_offset != last_il_offset;
6889 if (is_bb_start) {
6890 if (td->stack_height [il_offset] >= 0) {
6891 sp = stack + td->stack_height [il_offset];
6892 g_assert (sp <= stack_end);
6893 memset (stack, 0, (sp - stack) * sizeof (StackContentInfo));
6895 memset (locals, 0, td->locals_size * sizeof (StackValue));
6897 // The instruction pops some values then pushes some other
6898 get_inst_stack_usage (td, ins, &pop, &push);
6899 if (td->verbose_level && ins->opcode != MINT_NOP) {
6900 dump_interp_inst (ins);
6901 g_print (", sp %d, (pop %d, push %d)\n", sp - stack, pop, push);
6903 if (MINT_IS_LDLOC (ins->opcode)) {
6904 int replace_op = 0;
6905 int loaded_local = ins->data [0];
6906 local_ref_count [loaded_local]++;
6907 InterpInst *prev_ins = interp_prev_ins (ins);
6908 if (MINT_IS_STLOC (prev_ins->opcode) && !interp_is_bb_start (td, prev_ins, ins) && interp_local_equal (locals, prev_ins->data [0], loaded_local)) {
6909 int mt = prev_ins->opcode - MINT_STLOC_I1;
6910 if (ins->opcode - MINT_LDLOC_I1 == mt) {
6911 if (mt == MINT_TYPE_I4)
6912 replace_op = MINT_STLOC_NP_I4;
6913 else if (mt == MINT_TYPE_I8)
6914 replace_op = MINT_STLOC_NP_I8;
6915 else if (mt == MINT_TYPE_R4)
6916 replace_op = MINT_STLOC_NP_R4;
6917 else if (mt == MINT_TYPE_R8)
6918 replace_op = MINT_STLOC_NP_R8;
6919 else if (mt == MINT_TYPE_O || mt == MINT_TYPE_P)
6920 replace_op = MINT_STLOC_NP_O;
6921 if (replace_op) {
6922 int stored_local = prev_ins->data [0];
6923 sp->ins = NULL;
6924 if (sp->val.type == STACK_VALUE_NONE) {
6925 // We know what local is on the stack now. Track it
6926 sp->val.type = STACK_VALUE_LOCAL;
6927 sp->val.local = stored_local;
6930 // Clear the previous stloc instruction
6931 interp_clear_ins (td, prev_ins);
6932 ins->opcode = replace_op;
6933 ins->data [0] = stored_local;
6934 local_ref_count [loaded_local]--;
6935 if (td->verbose_level) {
6936 g_print ("Add stloc.np :\n\t");
6937 dump_interp_inst_newline (ins);
6939 mono_interp_stats.stloc_nps++;
6940 mono_interp_stats.killed_instructions++;
6943 } else if (locals [loaded_local].type == STACK_VALUE_LOCAL) {
6944 g_assert (locals [loaded_local].type == STACK_VALUE_LOCAL);
6945 g_assert (!(td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT));
6946 // do copy propagation of the original source
6947 mono_interp_stats.copy_propagations++;
6948 local_ref_count [loaded_local]--;
6949 ins->data [0] = locals [loaded_local].local;
6950 local_ref_count [ins->data [0]]++;
6951 if (td->verbose_level) {
6952 g_print ("cprop loc %d -> loc %d :\n\t", loaded_local, locals [loaded_local].local);
6953 dump_interp_inst_newline (ins);
6955 } else if (locals [loaded_local].type == STACK_VALUE_I4 || locals [loaded_local].type == STACK_VALUE_I8) {
6956 gboolean is_i4 = locals [loaded_local].type == STACK_VALUE_I4;
6957 g_assert (!(td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT));
6958 if (is_i4)
6959 ins = interp_get_ldc_i4_from_const (td, ins, locals [loaded_local].i);
6960 else
6961 ins = interp_inst_replace_with_i8_const (td, ins, locals [loaded_local].l);
6962 sp->ins = ins;
6963 sp->val = locals [loaded_local];
6964 local_ref_count [loaded_local]--;
6965 mono_interp_stats.copy_propagations++;
6966 if (td->verbose_level) {
6967 g_print ("cprop loc %d -> ct :\n\t", loaded_local);
6968 dump_interp_inst_newline (ins);
6970 // FIXME this replace_op got ugly
6971 replace_op = ins->opcode;
6973 if (!replace_op) {
6974 // Save the ldloc on the stack if it wasn't optimized away
6975 // For simplicity we don't track locals that have their address taken
6976 // since it is hard to detect instructions that change the local value.
6977 if (td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT) {
6978 sp->val.type = STACK_VALUE_NONE;
6979 } else {
6980 sp->val.type = STACK_VALUE_LOCAL;
6981 sp->val.local = ins->data [0];
6983 sp->ins = ins;
6985 sp++;
6986 } else if (MINT_IS_STLOC (ins->opcode)) {
6987 int dest_local = ins->data [0];
6988 sp--;
6989 if (sp->val.type == STACK_VALUE_LOCAL) {
6990 int src_local = sp->val.local;
6991 if (td->locals [src_local].mt == td->locals [dest_local].mt) {
6992 // The locals have the same type. We can propagate the value
6993 int vtsize = (ins->opcode == MINT_STLOC_VT) ? ins->data [1] : 0;
6995 if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
6996 // Track what exactly is stored into local
6997 locals [dest_local].type = STACK_VALUE_LOCAL;
6998 locals [dest_local].local = src_local;
7001 if (sp->ins) {
7002 interp_clear_ins (td, sp->ins);
7003 interp_clear_ins (td, ins);
7005 ins = interp_insert_ins (td, ins, get_movloc_for_type (td->locals [src_local].mt));
7006 ins->data [0] = src_local;
7007 ins->data [1] = dest_local;
7008 if (vtsize)
7009 ins->data [2] = vtsize;
7010 // Clear ldloc / stloc pair and replace it with movloc superinstruction
7011 if (td->verbose_level) {
7012 g_print ("Add movloc (ldloc off %d) :\n\t", sp->ins->il_offset);
7013 dump_interp_inst_newline (ins);
7015 mono_interp_stats.movlocs++;
7016 mono_interp_stats.killed_instructions++;
7018 } else {
7019 locals [dest_local].type = STACK_VALUE_NONE;
7021 } else if (sp->val.type == STACK_VALUE_NONE) {
7022 locals [dest_local].type = STACK_VALUE_NONE;
7023 } else {
7024 g_assert (sp->val.type == STACK_VALUE_I4 || sp->val.type == STACK_VALUE_I8);
7025 if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT))
7026 locals [dest_local] = sp->val;
7028 clear_stack_content_info_for_local (stack, sp, dest_local);
7029 clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
7030 } else if (MINT_IS_LDC_I4 (ins->opcode) || ins->opcode == MINT_LDC_I8) {
7031 StackValue val;
7032 gboolean is_i8 = ins->opcode == MINT_LDC_I8;
7033 InterpInst *prev_ins = interp_prev_ins (ins);
7035 if (is_i8) {
7036 val.type = STACK_VALUE_I8;
7037 val.l = READ64 (&ins->data [0]);
7038 } else {
7039 val.type = STACK_VALUE_I4;
7040 val.i = interp_get_const_from_ldc_i4 (ins);
7043 if (prev_ins && prev_ins->opcode == MINT_POP &&
7044 ((is_i8 && sp->val.type == STACK_VALUE_I8 && sp->val.l == val.l) ||
7045 (!is_i8 && sp->val.type == STACK_VALUE_I4 && sp->val.i == val.i)) &&
7046 !interp_is_bb_start (td, prev_ins, ins)) {
7047 // The previous instruction pops the stack of the value we are pushing
7048 // right now. We can kill both instructions
7049 if (td->verbose_level)
7050 g_print ("Kill redundant pop/ldc pair: pop (off %p), ldc (off %p)\n", prev_ins->il_offset, ins->il_offset);
7051 interp_clear_ins (td, prev_ins);
7052 interp_clear_ins (td, ins);
7053 mono_interp_stats.killed_instructions += 2;
7054 } else {
7055 sp->ins = ins;
7056 sp->val = val;
7058 sp++;
7059 } else if (MINT_IS_MOVLOC (ins->opcode)) {
7060 int src_local = ins->data [0];
7061 int dest_local = ins->data [1];
7062 local_ref_count [src_local]++;
7063 if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
7064 if (locals [src_local].type != STACK_VALUE_NONE) {
7065 locals [dest_local] = locals [src_local];
7066 } else {
7067 locals [dest_local].type = STACK_VALUE_LOCAL;
7068 locals [dest_local].local = src_local;
7070 clear_stack_content_info_for_local (stack, sp, dest_local);
7071 clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
7073 } else if (MINT_IS_STLOC_NP (ins->opcode)) {
7074 int dest_local = ins->data [0];
7075 // Prevent optimizing away the instruction that pushed the value on the stack
7076 sp [-1].ins = NULL;
7077 // The local contains the value of the top of stack
7078 if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
7079 locals [dest_local] = sp [-1].val;
7080 clear_stack_content_info_for_local (stack, sp, dest_local);
7081 clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
7083 } else if (ins->opcode == MINT_DUP || ins->opcode == MINT_DUP_VT) {
7084 sp [0].val = sp [-1].val;
7085 sp [0].ins = ins;
7086 // If top of stack is known, we could also replace dup with an explicit
7087 // propagated instruction, so we remove the top of stack dependency
7088 sp [-1].ins = NULL;
7089 sp++;
7090 } else if (ins->opcode >= MINT_BOX && ins->opcode <= MINT_BOX_NULLABLE) {
7091 int offset = ins->data [1];
7092 // Clear the stack slot that is boxed
7093 memset (&sp [-1 - offset], 0, sizeof (StackContentInfo));
7094 // Make sure that the instructions that pushed this stack slot can't be
7095 // optimized away. If we would optimize them away, we would also need to
7096 // update the offset in the box instruction, which we can't, for now.
7097 for (int i = 1; i <= offset; i++)
7098 sp [-i].ins = NULL;
7099 } else if (ins->opcode == MINT_CKNULL_N) {
7100 int offset = ins->data [0];
7101 for (int i = 1; i <= offset; i++)
7102 sp [-i].ins = NULL;
7103 } else if (ins->opcode == MINT_LD_DELEGATE_INVOKE_IMPL) {
7104 int offset = ins->data [0];
7105 for (int i = 1; i <= offset; i++)
7106 sp [-i].ins = NULL;
7107 memset (sp, 0, sizeof (StackContentInfo));
7108 sp++;
7109 } else if (ins->opcode == MINT_POP) {
7110 sp--;
7111 if (sp->ins) {
7112 // The top of the stack is not used by any instructions. Kill both the
7113 // instruction that pushed it and the pop.
7114 interp_clear_ins (td, sp->ins);
7115 interp_clear_ins (td, ins);
7116 mono_interp_stats.killed_instructions += 2;
7117 // The value pop-ed by this instruction can still be accessed. If we also
7118 // kill the instruction pushing the value, then we need to empty the
7119 // value of the stack, so it is not considered for further optimizations.
7120 sp->val.type = STACK_VALUE_NONE;
7122 } else if (ins->opcode == MINT_NEWOBJ_FAST && ins->data [0] == INLINED_METHOD_FLAG) {
7123 int param_count = ins->data [1];
7124 // memmove the stack values while clearing ins, to prevent instruction removal
7125 for (int i = 1; i <= param_count; i++) {
7126 sp [-i + 2] = sp [-i];
7127 sp [-i + 2].ins = NULL;
7129 // clear stack information for the slots where the allocated object resides
7130 memset (&sp [-param_count], 0, 2 * sizeof (StackContentInfo));
7131 sp += 2;
7132 } else if (ins->opcode == MINT_CASTCLASS || ins->opcode == MINT_CASTCLASS_COMMON || ins->opcode == MINT_CASTCLASS_INTERFACE) {
7133 // Keep the value on the stack, but prevent optimizing away
7134 sp [-1].ins = NULL;
7135 } else if (MINT_IS_CONDITIONAL_BRANCH (ins->opcode)) {
7136 sp -= pop;
7137 g_assert (push == 0);
7138 // We can't clear any instruction that pushes the stack, because the
7139 // branched code will expect a certain stack size.
7140 for (StackContentInfo *sp_iter = stack; sp_iter < sp; sp_iter++)
7141 sp_iter->ins = NULL;
7142 } else if (MINT_IS_UNOP (ins->opcode)) {
7143 ins = interp_fold_unop (td, sp, ins);
7144 } else if (MINT_IS_BINOP (ins->opcode)) {
7145 ins = interp_fold_binop (td, sp, ins);
7146 sp--;
7147 } else if (ins->opcode >= MINT_STFLD_I1 && ins->opcode <= MINT_STFLD_P && (mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS)) {
7148 if (sp [-2].ins) {
7149 InterpInst *obj_ins = sp [-2].ins;
7150 if (obj_ins->opcode == MINT_LDLOC_O) {
7151 int loc_index = obj_ins->data [0];
7152 int fld_offset = ins->data [0];
7153 int mt = ins->opcode - MINT_STFLD_I1;
7154 ins = interp_insert_ins (td, ins, MINT_STLOCFLD_I1 + mt);
7155 ins->data [0] = loc_index;
7156 ins->data [1] = fld_offset;
7157 interp_clear_ins (td, ins->prev);
7158 interp_clear_ins (td, obj_ins);
7159 mono_interp_stats.super_instructions++;
7160 mono_interp_stats.killed_instructions++;
7161 } else if (obj_ins->opcode == MINT_LDARG_O || obj_ins->opcode == MINT_LDARG_P0) {
7162 int arg_index = 0;
7163 int fld_offset = ins->data [0];
7164 int mt = ins->opcode - MINT_STFLD_I1;
7165 if (obj_ins->opcode == MINT_LDARG_O)
7166 arg_index = obj_ins->data [0];
7167 ins = interp_insert_ins (td, ins, MINT_STARGFLD_I1 + mt);
7168 ins->data [0] = arg_index;
7169 ins->data [1] = fld_offset;
7170 interp_clear_ins (td, ins->prev);
7171 interp_clear_ins (td, obj_ins);
7172 mono_interp_stats.super_instructions++;
7173 mono_interp_stats.killed_instructions++;
7176 sp -= 2;
7177 } else if (MINT_IS_STLOCFLD (ins->opcode)) {
7178 local_ref_count [ins->data [0]]++;
7179 sp--;
7180 } else {
7181 if (pop == MINT_POP_ALL)
7182 pop = sp - stack;
7183 sp += push - pop;
7184 g_assert (sp >= stack && sp <= stack_end);
7185 g_assert ((sp - push) >= stack && (sp - push) <= stack_end);
7186 memset (sp - push, 0, push * sizeof (StackContentInfo));
7187 // If this instruction only pushes a single value, make it a candidate for
7188 // removal, if its value is not used anywhere.
7189 if (push == 1 && pop == 0 && !MINT_IS_CALL (ins->opcode) && !MINT_IS_NEWOBJ (ins->opcode))
7190 sp [-1].ins = ins;
7192 last_il_offset = ins->il_offset;
7195 if (interp_local_deadce (td, local_ref_count))
7196 goto retry;
7198 g_free (stack);
7199 g_free (locals);
7200 g_free (local_ref_count);
7203 static void
7204 interp_super_instructions (TransformData *td)
7206 InterpInst *ins;
7207 InterpInst *prev1_ins = NULL;
7208 InterpInst *prev2_ins = NULL;
7209 int last_il_offset = -1;
7210 for (ins = td->first_ins; ins != NULL; ins = ins->next) {
7211 int il_offset = ins->il_offset;
7212 // If two instructions have the same il_offset, then the second one
7213 // cannot be the start of a basic block.
7214 gboolean is_bb_start = il_offset != -1 && td->is_bb_start [il_offset] && il_offset != last_il_offset;
7215 last_il_offset = il_offset;
7216 if (ins->opcode == MINT_NOP)
7217 continue;
7218 if (is_bb_start) {
7219 // Prevent optimizations spanning multiple basic blocks
7220 prev2_ins = NULL;
7221 prev1_ins = NULL;
7223 if (ins->opcode >= MINT_LDFLD_I1 && ins->opcode <= MINT_LDFLD_P && prev1_ins) {
7224 if (prev1_ins->opcode == MINT_LDLOC_O) {
7225 int loc_index = prev1_ins->data [0];
7226 int fld_offset = ins->data [0];
7227 int mt = ins->opcode - MINT_LDFLD_I1;
7228 ins = interp_insert_ins (td, ins, MINT_LDLOCFLD_I1 + mt);
7229 ins->data [0] = loc_index;
7230 ins->data [1] = fld_offset;
7231 interp_clear_ins (td, ins->prev);
7232 interp_clear_ins (td, prev1_ins);
7233 prev1_ins = NULL;
7234 mono_interp_stats.super_instructions++;
7235 mono_interp_stats.killed_instructions++;
7236 } else if (prev1_ins->opcode == MINT_LDARG_O || prev1_ins->opcode == MINT_LDARG_P0) {
7237 int arg_index = 0;
7238 int fld_offset = ins->data [0];
7239 int mt = ins->opcode - MINT_LDFLD_I1;
7240 if (prev1_ins->opcode == MINT_LDARG_O)
7241 arg_index = prev1_ins->data [0];
7242 ins = interp_insert_ins (td, ins, MINT_LDARGFLD_I1 + mt);
7243 ins->data [0] = arg_index;
7244 ins->data [1] = fld_offset;
7245 interp_clear_ins (td, ins->prev);
7246 interp_clear_ins (td, prev1_ins);
7247 prev1_ins = NULL;
7248 mono_interp_stats.super_instructions++;
7249 mono_interp_stats.killed_instructions++;
7251 } else if (MINT_IS_STLOC (ins->opcode) && prev1_ins && prev2_ins) {
7252 if (prev1_ins->opcode == MINT_ADD1_I4 || prev1_ins->opcode == MINT_ADD1_I8 ||
7253 prev1_ins->opcode == MINT_SUB1_I4 || prev1_ins->opcode == MINT_SUB1_I8) {
7254 if (MINT_IS_LDLOC (prev2_ins->opcode) && prev2_ins->data [0] == ins->data [0]) {
7255 if (prev1_ins->opcode == MINT_ADD1_I4)
7256 ins->opcode = MINT_LOCADD1_I4;
7257 else if (prev1_ins->opcode == MINT_ADD1_I8)
7258 ins->opcode = MINT_LOCADD1_I8;
7259 else if (prev1_ins->opcode == MINT_SUB1_I4)
7260 ins->opcode = MINT_LOCSUB1_I4;
7261 else
7262 ins->opcode = MINT_LOCSUB1_I8;
7263 // the local index is already set inside the replaced STLOC instruction
7264 interp_clear_ins (td, prev1_ins);
7265 interp_clear_ins (td, prev2_ins);
7266 prev1_ins = NULL;
7267 mono_interp_stats.super_instructions++;
7268 mono_interp_stats.killed_instructions += 2;
7272 prev2_ins = prev1_ins;
7273 prev1_ins = ins;
7277 static void
7278 interp_optimize_code (TransformData *td)
7280 if (mono_interp_opt & INTERP_OPT_CPROP)
7281 MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td));
7283 if (mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS)
7284 MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td));
7287 static void
7288 generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoGenericContext *generic_context, MonoError *error)
7290 MonoDomain *domain = rtm->domain;
7291 int i;
7292 TransformData transform_data;
7293 TransformData *td;
7294 static gboolean verbose_method_inited;
7295 static char* verbose_method_name;
7297 if (!verbose_method_inited) {
7298 verbose_method_name = g_getenv ("MONO_VERBOSE_METHOD");
7299 verbose_method_inited = TRUE;
7302 memset (&transform_data, 0, sizeof(transform_data));
7303 td = &transform_data;
7305 td->method = method;
7306 td->rtm = rtm;
7307 td->code_size = header->code_size;
7308 td->header = header;
7309 td->max_code_size = td->code_size;
7310 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
7311 td->stack_height = (int*)g_malloc(header->code_size * sizeof(int));
7312 td->stack_state = (StackInfo**)g_malloc0(header->code_size * sizeof(StackInfo *));
7313 td->vt_stack_size = (int*)g_malloc(header->code_size * sizeof(int));
7314 td->clause_indexes = (int*)g_malloc (header->code_size * sizeof (int));
7315 td->is_bb_start = (guint8*)g_malloc0(header->code_size);
7316 td->mempool = mono_mempool_new ();
7317 td->n_data_items = 0;
7318 td->max_data_items = 0;
7319 td->data_items = NULL;
7320 td->data_hash = g_hash_table_new (NULL, NULL);
7321 #ifdef ENABLE_EXPERIMENT_TIERED
7322 td->patchsite_hash = g_hash_table_new (NULL, NULL);
7323 #endif
7324 td->gen_sdb_seq_points = mini_debug_options.gen_sdb_seq_points;
7325 td->seq_points = g_ptr_array_new ();
7326 td->verbose_level = mono_interp_traceopt;
7327 rtm->data_items = td->data_items;
7329 interp_method_compute_offsets (td, rtm, mono_method_signature_internal (method), header);
7331 if (verbose_method_name) {
7332 const char *name = verbose_method_name;
7334 if ((strchr (name, '.') > name) || strchr (name, ':')) {
7335 MonoMethodDesc *desc;
7337 desc = mono_method_desc_new (name, TRUE);
7338 if (mono_method_desc_full_match (desc, method)) {
7339 td->verbose_level = 4;
7341 mono_method_desc_free (desc);
7342 } else {
7343 if (strcmp (method->name, name) == 0)
7344 td->verbose_level = 4;
7348 td->stack = (StackInfo*)g_malloc0 ((header->max_stack + 1) * sizeof (td->stack [0]));
7349 td->stack_capacity = header->max_stack + 1;
7350 td->sp = td->stack;
7351 td->max_stack_height = 0;
7352 td->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
7353 td->current_il_offset = -1;
7355 generate_code (td, method, header, generic_context, error);
7356 goto_if_nok (error, exit);
7358 interp_optimize_code (td);
7360 generate_compacted_code (td);
7362 if (td->verbose_level) {
7363 g_print ("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method, TRUE), rtm, td->max_vt_sp);
7364 g_print ("Calculated stack size: %d, stated size: %d\n", td->max_stack_height, header->max_stack);
7365 dump_mint_code (td->new_code, td->new_code_end);
7368 /* Check if we use excessive stack space */
7369 if (td->max_stack_height > header->max_stack * 3 && header->max_stack > 16)
7370 g_warning ("Excessive stack space usage for method %s, %d/%d", method->name, td->max_stack_height, header->max_stack);
7372 int code_len_u8, code_len_u16;
7373 code_len_u8 = (guint8 *) td->new_code_end - (guint8 *) td->new_code;
7374 code_len_u16 = td->new_code_end - td->new_code;
7376 rtm->clauses = (MonoExceptionClause*)mono_domain_alloc0 (domain, header->num_clauses * sizeof (MonoExceptionClause));
7377 memcpy (rtm->clauses, header->clauses, header->num_clauses * sizeof(MonoExceptionClause));
7378 rtm->code = (gushort*)td->new_code;
7379 rtm->init_locals = header->init_locals;
7380 rtm->num_clauses = header->num_clauses;
7381 for (i = 0; i < header->num_clauses; i++) {
7382 MonoExceptionClause *c = rtm->clauses + i;
7383 int end_off = c->try_offset + c->try_len;
7384 c->try_offset = get_in_offset (td, c->try_offset);
7385 c->try_len = find_in_offset (td, end_off) - c->try_offset;
7386 g_assert ((c->try_offset + c->try_len) < code_len_u16);
7387 end_off = c->handler_offset + c->handler_len;
7388 c->handler_offset = get_in_offset (td, c->handler_offset);
7389 c->handler_len = find_in_offset (td, end_off) - c->handler_offset;
7390 g_assert (c->handler_len >= 0 && (c->handler_offset + c->handler_len) <= code_len_u16);
7391 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER)
7392 c->data.filter_offset = get_in_offset (td, c->data.filter_offset);
7394 rtm->stack_size = (sizeof (stackval)) * (td->max_stack_height + 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/
7395 rtm->stack_size = ALIGN_TO (rtm->stack_size, MINT_VT_ALIGNMENT);
7396 rtm->vt_stack_size = td->max_vt_sp;
7397 rtm->total_locals_size = td->total_locals_size;
7398 rtm->alloca_size = rtm->total_locals_size + rtm->vt_stack_size + rtm->stack_size;
7399 rtm->data_items = (gpointer*)mono_domain_alloc0 (domain, td->n_data_items * sizeof (td->data_items [0]));
7400 memcpy (rtm->data_items, td->data_items, td->n_data_items * sizeof (td->data_items [0]));
7402 /* Save debug info */
7403 interp_save_debug_info (rtm, header, td, td->line_numbers);
7405 /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
7406 int jinfo_len;
7407 jinfo_len = mono_jit_info_size ((MonoJitInfoFlags)0, header->num_clauses, 0);
7408 MonoJitInfo *jinfo;
7409 jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len);
7410 jinfo->is_interp = 1;
7411 rtm->jinfo = jinfo;
7412 mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len_u8, (MonoJitInfoFlags)0, header->num_clauses, 0);
7413 for (i = 0; i < jinfo->num_clauses; ++i) {
7414 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
7415 MonoExceptionClause *c = rtm->clauses + i;
7417 ei->flags = c->flags;
7418 ei->try_start = (guint8*)(rtm->code + c->try_offset);
7419 ei->try_end = (guint8*)(rtm->code + c->try_offset + c->try_len);
7420 ei->handler_start = (guint8*)(rtm->code + c->handler_offset);
7421 ei->exvar_offset = rtm->exvar_offsets [i];
7422 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
7423 ei->data.filter = (guint8*)(rtm->code + c->data.filter_offset);
7424 } else if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
7425 ei->data.handler_end = (guint8*)(rtm->code + c->handler_offset + c->handler_len);
7426 } else {
7427 ei->data.catch_class = c->data.catch_class;
7431 save_seq_points (td, jinfo);
7432 #ifdef ENABLE_EXPERIMENT_TIERED
7433 /* debugging aid, it makes `mono_pmip` work. */
7434 mono_jit_info_table_add (domain, jinfo);
7435 #endif
7437 exit:
7438 g_free (td->in_offsets);
7439 g_free (td->stack_height);
7440 for (i = 0; i < header->code_size; ++i)
7441 g_free (td->stack_state [i]);
7442 g_free (td->stack_state);
7443 g_free (td->vt_stack_size);
7444 g_free (td->clause_indexes);
7445 g_free (td->data_items);
7446 g_free (td->stack);
7447 g_free (td->is_bb_start);
7448 g_free (td->locals);
7449 g_hash_table_destroy (td->data_hash);
7450 #ifdef ENABLE_EXPERIMENT_TIERED
7451 g_hash_table_destroy (td->patchsite_hash);
7452 #endif
7453 g_ptr_array_free (td->seq_points, TRUE);
7454 g_array_free (td->line_numbers, TRUE);
7455 mono_mempool_destroy (td->mempool);
7458 static mono_mutex_t calc_section;
7460 #ifdef ENABLE_EXPERIMENT_TIERED
7461 static gboolean
7462 tiered_patcher (MiniTieredPatchPointContext *ctx, gpointer patchsite)
7464 ERROR_DECL (error);
7465 MonoMethod *m = ctx->target_method;
7467 if (!jit_call2_supported (m, mono_method_signature_internal (m)))
7468 return FALSE;
7470 /* TODO: Force compilation here. Currently the JIT will be invoked upon
7471 * first execution of `MINT_JIT_CALL2`. */
7472 InterpMethod *rmethod = mono_interp_get_imethod (ctx->domain, m, error);
7473 mono_error_assert_ok (error);
7475 guint16 *ip = ((guint16 *) patchsite);
7476 *ip++ = MINT_JIT_CALL2;
7477 /* FIXME: this only works on 64bit */
7478 WRITE64 (ip, &rmethod);
7479 mono_memory_barrier ();
7481 return TRUE;
7483 #endif
7486 void
7487 mono_interp_transform_init (void)
7489 mono_os_mutex_init_recursive(&calc_section);
7491 #ifdef ENABLE_EXPERIMENT_TIERED
7492 mini_tiered_register_callsite_patcher (tiered_patcher, TIERED_PATCH_KIND_INTERP);
7493 #endif
7496 void
7497 mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, MonoError *error)
7499 MonoMethod *method = imethod->method;
7500 MonoMethodHeader *header = NULL;
7501 MonoMethodSignature *signature = mono_method_signature_internal (method);
7502 MonoVTable *method_class_vt;
7503 MonoGenericContext *generic_context = NULL;
7504 MonoDomain *domain = imethod->domain;
7505 InterpMethod tmp_imethod;
7506 InterpMethod *real_imethod;
7508 error_init (error);
7510 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method->klass))) {
7511 mono_error_set_invalid_operation (error, "%s", "Could not execute the method because the containing type is not fully instantiated.");
7512 return;
7515 // g_printerr ("TRANSFORM(0x%016lx): begin %s::%s\n", mono_thread_current (), method->klass->name, method->name);
7516 method_class_vt = mono_class_vtable_checked (domain, imethod->method->klass, error);
7517 return_if_nok (error);
7519 if (!method_class_vt->initialized) {
7520 mono_runtime_class_init_full (method_class_vt, error);
7521 return_if_nok (error);
7524 MONO_PROFILER_RAISE (jit_begin, (method));
7526 if (mono_method_signature_internal (method)->is_inflated)
7527 generic_context = mono_method_get_context (method);
7528 else {
7529 MonoGenericContainer *generic_container = mono_method_get_generic_container (method);
7530 if (generic_container)
7531 generic_context = &generic_container->context;
7534 if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
7535 MonoMethod *nm = NULL;
7536 if (imethod->transformed) {
7537 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));
7538 return;
7541 /* assumes all internal calls with an array this are built in... */
7542 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL && (! mono_method_signature_internal (method)->hasthis || m_class_get_rank (method->klass) == 0)) {
7543 nm = mono_marshal_get_native_wrapper (method, FALSE, FALSE);
7544 signature = mono_method_signature_internal (nm);
7545 } else {
7546 const char *name = method->name;
7547 if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) {
7548 if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
7549 MonoJitICallInfo *mi = &mono_get_jit_icall_info ()->ves_icall_mono_delegate_ctor_interp;
7550 nm = mono_marshal_get_icall_wrapper (mi, TRUE);
7551 } else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
7553 * Usually handled during transformation of the caller, but
7554 * when the caller is handled by another execution engine
7555 * (for example fullAOT) we need to handle it here. That's
7556 * known to be wrong in cases where the reference to
7557 * `MonoDelegate` would be needed (FIXME).
7559 nm = mono_marshal_get_delegate_invoke (method, NULL);
7560 } else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
7561 nm = mono_marshal_get_delegate_begin_invoke (method);
7562 } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
7563 nm = mono_marshal_get_delegate_end_invoke (method);
7566 if (nm == NULL)
7567 g_assert_not_reached ();
7569 if (nm == NULL) {
7570 mono_os_mutex_lock (&calc_section);
7571 imethod->stack_size = sizeof (stackval); /* for tracing */
7572 imethod->alloca_size = imethod->stack_size;
7573 mono_memory_barrier ();
7574 imethod->transformed = TRUE;
7575 mono_interp_stats.methods_transformed++;
7576 mono_os_mutex_unlock (&calc_section);
7577 MONO_PROFILER_RAISE (jit_done, (method, NULL));
7578 return;
7580 method = nm;
7581 header = interp_method_get_header (nm, error);
7582 return_if_nok (error);
7585 if (!header) {
7586 header = mono_method_get_header_checked (method, error);
7587 return_if_nok (error);
7590 g_assert ((signature->param_count + signature->hasthis) < 1000);
7591 // g_printerr ("TRANSFORM(0x%016lx): end %s::%s\n", mono_thread_current (), method->klass->name, method->name);
7593 /* Make modifications to a copy of imethod, copy them back inside the lock */
7594 real_imethod = imethod;
7595 memcpy (&tmp_imethod, imethod, sizeof (InterpMethod));
7596 imethod = &tmp_imethod;
7598 MONO_TIME_TRACK (mono_interp_stats.transform_time, generate (method, header, imethod, generic_context, error));
7600 mono_metadata_free_mh (header);
7602 return_if_nok (error);
7604 /* Copy changes back */
7605 imethod = real_imethod;
7606 mono_os_mutex_lock (&calc_section);
7607 if (!imethod->transformed) {
7608 // Ignore the first two fields which are unchanged. next_jit_code_hash shouldn't
7609 // be modified because it is racy with internal hash table insert.
7610 const int start_offset = 2 * sizeof (gpointer);
7611 memcpy ((char*)imethod + start_offset, (char*)&tmp_imethod + start_offset, sizeof (InterpMethod) - start_offset);
7612 mono_memory_barrier ();
7613 imethod->transformed = TRUE;
7614 mono_interp_stats.methods_transformed++;
7615 mono_atomic_fetch_add_i32 (&mono_jit_stats.methods_with_interp, 1);
7618 mono_os_mutex_unlock (&calc_section);
7620 mono_domain_lock (domain);
7621 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, imethod->method))
7622 g_hash_table_insert (domain_jit_info (domain)->seq_points, imethod->method, imethod->jinfo->seq_points);
7623 mono_domain_unlock (domain);
7625 // FIXME: Add a different callback ?
7626 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));