[interp] Remove hack for nint/nfloat (#21395)
[mono-project.git] / mono / mini / interp / transform.c
blobde7725aa8ccdbff1b59545a901d65e58555ce27f
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/mono-basic-block.h>
22 #include <mono/metadata/abi-details.h>
23 #include <mono/metadata/reflection-internals.h>
24 #include <mono/utils/unlocked.h>
25 #include <mono/utils/mono-memory-model.h>
27 #include <mono/mini/mini.h>
28 #include <mono/mini/mini-runtime.h>
30 #include "mintops.h"
31 #include "interp-internals.h"
32 #include "interp.h"
33 #include "transform.h"
35 MonoInterpStats mono_interp_stats;
37 #define DEBUG 0
39 #if SIZEOF_VOID_P == 8
40 #define MINT_NEG_P MINT_NEG_I8
41 #define MINT_NOT_P MINT_NOT_I8
43 #define MINT_NEG_FP MINT_NEG_R8
45 #define MINT_ADD_P MINT_ADD_I8
46 #define MINT_SUB_P MINT_SUB_I8
47 #define MINT_MUL_P MINT_MUL_I8
48 #define MINT_DIV_P MINT_DIV_I8
49 #define MINT_DIV_UN_P MINT_DIV_UN_I8
50 #define MINT_REM_P MINT_REM_I8
51 #define MINT_REM_UN_P MINT_REM_UN_I8
52 #define MINT_AND_P MINT_AND_I8
53 #define MINT_OR_P MINT_OR_I8
54 #define MINT_XOR_P MINT_XOR_I8
55 #define MINT_SHL_P MINT_SHL_I8
56 #define MINT_SHR_P MINT_SHR_I8
57 #define MINT_SHR_UN_P MINT_SHR_UN_I8
59 #define MINT_CEQ_P MINT_CEQ_I8
60 #define MINT_CNE_P MINT_CNE_I8
61 #define MINT_CLT_P MINT_CLT_I8
62 #define MINT_CLT_UN_P MINT_CLT_UN_I8
63 #define MINT_CGT_P MINT_CGT_I8
64 #define MINT_CGT_UN_P MINT_CGT_UN_I8
65 #define MINT_CLE_P MINT_CLE_I8
66 #define MINT_CLE_UN_P MINT_CLE_UN_I8
67 #define MINT_CGE_P MINT_CGE_I8
68 #define MINT_CGE_UN_P MINT_CGE_UN_I8
70 #define MINT_ADD_FP MINT_ADD_R8
71 #define MINT_SUB_FP MINT_SUB_R8
72 #define MINT_MUL_FP MINT_MUL_R8
73 #define MINT_DIV_FP MINT_DIV_R8
74 #define MINT_REM_FP MINT_REM_R8
76 #define MINT_CNE_FP MINT_CNE_R8
77 #define MINT_CEQ_FP MINT_CEQ_R8
78 #define MINT_CGT_FP MINT_CGT_R8
79 #define MINT_CGE_FP MINT_CGE_R8
80 #define MINT_CLT_FP MINT_CLT_R8
81 #define MINT_CLE_FP MINT_CLE_R8
83 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I8
84 #else
86 #define MINT_NEG_P MINT_NEG_I4
87 #define MINT_NOT_P MINT_NOT_I4
89 #define MINT_NEG_FP MINT_NEG_R4
91 #define MINT_ADD_P MINT_ADD_I4
92 #define MINT_SUB_P MINT_SUB_I4
93 #define MINT_MUL_P MINT_MUL_I4
94 #define MINT_DIV_P MINT_DIV_I4
95 #define MINT_DIV_UN_P MINT_DIV_UN_I4
96 #define MINT_REM_P MINT_REM_I4
97 #define MINT_REM_UN_P MINT_REM_UN_I4
98 #define MINT_AND_P MINT_AND_I4
99 #define MINT_OR_P MINT_OR_I4
100 #define MINT_XOR_P MINT_XOR_I4
101 #define MINT_SHL_P MINT_SHL_I4
102 #define MINT_SHR_P MINT_SHR_I4
103 #define MINT_SHR_UN_P MINT_SHR_UN_I4
105 #define MINT_CEQ_P MINT_CEQ_I4
106 #define MINT_CNE_P MINT_CNE_I4
107 #define MINT_CLT_P MINT_CLT_I4
108 #define MINT_CLT_UN_P MINT_CLT_UN_I4
109 #define MINT_CGT_P MINT_CGT_I4
110 #define MINT_CGT_UN_P MINT_CGT_UN_I4
111 #define MINT_CLE_P MINT_CLE_I4
112 #define MINT_CLE_UN_P MINT_CLE_UN_I4
113 #define MINT_CGE_P MINT_CGE_I4
114 #define MINT_CGE_UN_P MINT_CGE_UN_I4
116 #define MINT_ADD_FP MINT_ADD_R4
117 #define MINT_SUB_FP MINT_SUB_R4
118 #define MINT_MUL_FP MINT_MUL_R4
119 #define MINT_DIV_FP MINT_DIV_R4
120 #define MINT_REM_FP MINT_REM_R4
122 #define MINT_CNE_FP MINT_CNE_R4
123 #define MINT_CEQ_FP MINT_CEQ_R4
124 #define MINT_CGT_FP MINT_CGT_R4
125 #define MINT_CGE_FP MINT_CGE_R4
126 #define MINT_CLT_FP MINT_CLT_R4
127 #define MINT_CLE_FP MINT_CLE_R4
129 #define MINT_CONV_OVF_U4_P MINT_CONV_OVF_U4_I4
130 #endif
132 typedef struct {
133 const gchar *op_name;
134 guint16 insn [3];
135 } MagicIntrinsic;
137 // static const MagicIntrinsic int_binop[] = {
139 static const MagicIntrinsic int_unnop[] = {
140 { "op_UnaryPlus", {MINT_NOP, MINT_NOP, MINT_NOP}},
141 { "op_UnaryNegation", {MINT_NEG_P, MINT_NEG_P, MINT_NEG_FP}},
142 { "op_OnesComplement", {MINT_NOT_P, MINT_NOT_P, MINT_NIY}}
145 static const MagicIntrinsic int_binop[] = {
146 { "op_Addition", {MINT_ADD_P, MINT_ADD_P, MINT_ADD_FP}},
147 { "op_Subtraction", {MINT_SUB_P, MINT_SUB_P, MINT_SUB_FP}},
148 { "op_Multiply", {MINT_MUL_P, MINT_MUL_P, MINT_MUL_FP}},
149 { "op_Division", {MINT_DIV_P, MINT_DIV_UN_P, MINT_DIV_FP}},
150 { "op_Modulus", {MINT_REM_P, MINT_REM_UN_P, MINT_REM_FP}},
151 { "op_BitwiseAnd", {MINT_AND_P, MINT_AND_P, MINT_NIY}},
152 { "op_BitwiseOr", {MINT_OR_P, MINT_OR_P, MINT_NIY}},
153 { "op_ExclusiveOr", {MINT_XOR_P, MINT_XOR_P, MINT_NIY}},
154 { "op_LeftShift", {MINT_SHL_P, MINT_SHL_P, MINT_NIY}},
155 { "op_RightShift", {MINT_SHR_P, MINT_SHR_UN_P, MINT_NIY}},
158 static const MagicIntrinsic int_cmpop[] = {
159 { "op_Inequality", {MINT_CNE_P, MINT_CNE_P, MINT_CNE_FP}},
160 { "op_Equality", {MINT_CEQ_P, MINT_CEQ_P, MINT_CEQ_FP}},
161 { "op_GreaterThan", {MINT_CGT_P, MINT_CGT_UN_P, MINT_CGT_FP}},
162 { "op_GreaterThanOrEqual", {MINT_CGE_P, MINT_CGE_UN_P, MINT_CGE_FP}},
163 { "op_LessThan", {MINT_CLT_P, MINT_CLT_UN_P, MINT_CLT_FP}},
164 { "op_LessThanOrEqual", {MINT_CLE_P, MINT_CLE_UN_P, MINT_CLE_FP}}
167 static const char *stack_type_string [] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " };
169 static int stack_type [] = {
170 STACK_TYPE_I4, /*I1*/
171 STACK_TYPE_I4, /*U1*/
172 STACK_TYPE_I4, /*I2*/
173 STACK_TYPE_I4, /*U2*/
174 STACK_TYPE_I4, /*I4*/
175 STACK_TYPE_I8, /*I8*/
176 STACK_TYPE_R4, /*R4*/
177 STACK_TYPE_R8, /*R8*/
178 STACK_TYPE_O, /*O*/
179 STACK_TYPE_MP, /*P*/
180 STACK_TYPE_VT
183 static gboolean generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error);
185 static InterpInst*
186 interp_new_ins (TransformData *td, guint16 opcode, int len)
188 InterpInst *new_inst;
189 // Size of data region of instruction is length of instruction minus 1 (the opcode slot)
190 new_inst = mono_mempool_alloc0 (td->mempool, sizeof (InterpInst) + sizeof (guint16) * ((len > 0) ? (len - 1) : 0));
191 new_inst->opcode = opcode;
192 new_inst->il_offset = td->current_il_offset;
193 return new_inst;
196 // This version need to be used with switch opcode, which doesn't have constant length
197 static InterpInst*
198 interp_add_ins_explicit (TransformData *td, guint16 opcode, int len)
200 InterpInst *new_inst = interp_new_ins (td, opcode, len);
201 new_inst->prev = td->last_ins;
202 if (td->last_ins)
203 td->last_ins->next = new_inst;
204 else
205 td->first_ins = new_inst;
206 td->last_ins = new_inst;
207 return new_inst;
210 static InterpInst*
211 interp_add_ins (TransformData *td, guint16 opcode)
213 return interp_add_ins_explicit (td, opcode, mono_interp_oplen [opcode]);
216 // This instruction will have the il_offset of the previous instruction
217 static InterpInst*
218 interp_insert_ins (TransformData *td, InterpInst *prev_ins, guint16 opcode)
220 InterpInst *new_inst = interp_new_ins (td, opcode, mono_interp_oplen [opcode]);
221 g_assert (prev_ins);
222 new_inst->il_offset = prev_ins->il_offset;
224 new_inst->prev = prev_ins;
225 new_inst->next = prev_ins->next;
226 prev_ins->next = new_inst;
228 if (new_inst->next == NULL)
229 td->last_ins = new_inst;
230 else
231 new_inst->next->prev = new_inst;
233 return new_inst;
236 static void
237 interp_clear_ins (TransformData *td, InterpInst *ins)
239 // Clearing instead of removing from the list makes everything easier.
240 // We don't change structure of the instruction list, we don't need
241 // to worry about updating the il_offset, or whether this instruction
242 // was at the start of a basic block etc.
243 ins->opcode = MINT_NOP;
246 static InterpInst*
247 interp_prev_ins (InterpInst *ins)
249 ins = ins->prev;
250 while (ins && ins->opcode == MINT_NOP)
251 ins = ins->prev;
252 return ins;
255 #define CHECK_STACK(td, n) \
256 do { \
257 int stack_size = (td)->sp - (td)->stack; \
258 if (stack_size < (n)) \
259 g_warning ("%s.%s: not enough values (%d < %d) on stack at %04x", \
260 m_class_get_name ((td)->method->klass), (td)->method->name, \
261 stack_size, n, (td)->ip - (td)->il_code); \
262 } while (0)
264 #define ENSURE_I4(td, sp_off) \
265 do { \
266 if ((td)->sp [-sp_off].type == STACK_TYPE_I8) \
267 interp_add_ins (td, sp_off == 1 ? MINT_CONV_I4_I8 : MINT_CONV_I4_I8_SP); \
268 } while (0)
270 #define CHECK_TYPELOAD(klass) \
271 do { \
272 if (!(klass) || mono_class_has_failure (klass)) { \
273 mono_error_set_for_class_failure (error, klass); \
274 goto exit; \
276 } while (0)
278 #if NO_UNALIGNED_ACCESS
279 #define WRITE32(ip, v) \
280 do { \
281 * (ip) = * (guint16 *)(v); \
282 * ((ip) + 1) = * ((guint16 *)(v) + 1); \
283 (ip) += 2; \
284 } while (0)
286 #define WRITE32_INS(ins, index, v) \
287 do { \
288 (ins)->data [index] = * (guint16 *)(v); \
289 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
290 } while (0)
292 #define WRITE64(ins, v) \
293 do { \
294 *((ins) + 0) = * ((guint16 *)(v) + 0); \
295 *((ins) + 1) = * ((guint16 *)(v) + 1); \
296 *((ins) + 2) = * ((guint16 *)(v) + 2); \
297 *((ins) + 3) = * ((guint16 *)(v) + 3); \
298 } while (0)
300 #define WRITE64_INS(ins, index, v) \
301 do { \
302 (ins)->data [index] = * (guint16 *)(v); \
303 (ins)->data [index + 1] = * ((guint16 *)(v) + 1); \
304 (ins)->data [index + 2] = * ((guint16 *)(v) + 2); \
305 (ins)->data [index + 3] = * ((guint16 *)(v) + 3); \
306 } while (0)
307 #else
308 #define WRITE32(ip, v) \
309 do { \
310 * (guint32*)(ip) = * (guint32 *)(v); \
311 (ip) += 2; \
312 } while (0)
313 #define WRITE32_INS(ins, index, v) \
314 do { \
315 * (guint32 *)(&(ins)->data [index]) = * (guint32 *)(v); \
316 } while (0)
318 #define WRITE64(ip, v) \
319 do { \
320 * (guint64*)(ip) = * (guint64 *)(v); \
321 (ip) += 4; \
322 } while (0)
323 #define WRITE64_INS(ins, index, v) \
324 do { \
325 * (guint64 *)(&(ins)->data [index]) = * (guint64 *)(v); \
326 } while (0)
328 #endif
331 static void
332 handle_branch (TransformData *td, int short_op, int long_op, int offset)
334 int shorten_branch = 0;
335 int target = td->ip + offset - td->il_code;
336 if (target < 0 || target >= td->code_size)
337 g_assert_not_reached ();
338 /* Add exception checkpoint or safepoint for backward branches */
339 if (offset < 0) {
340 if (mono_threads_are_safepoints_enabled ())
341 interp_add_ins (td, MINT_SAFEPOINT);
342 else
343 interp_add_ins (td, MINT_CHECKPOINT);
345 if (offset > 0 && td->stack_height [target] < 0) {
346 td->stack_height [target] = td->sp - td->stack;
347 if (td->stack_height [target] > 0)
348 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, td->stack_height [target] * sizeof (td->stack [0]));
349 td->vt_stack_size [target] = td->vt_sp;
352 if (td->header->code_size <= 25000) /* FIX to be precise somehow? */
353 shorten_branch = 1;
355 if (shorten_branch) {
356 interp_add_ins (td, short_op);
357 td->last_ins->data [0] = (guint16) target;
358 } else {
359 interp_add_ins (td, long_op);
360 WRITE32_INS (td->last_ins, 0, &target);
364 static void
365 one_arg_branch(TransformData *td, int mint_op, int offset)
367 int type = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
368 int long_op = mint_op + type - STACK_TYPE_I4;
369 int short_op = long_op + MINT_BRFALSE_I4_S - MINT_BRFALSE_I4;
370 CHECK_STACK(td, 1);
371 --td->sp;
372 handle_branch (td, short_op, long_op, offset);
375 static void
376 two_arg_branch(TransformData *td, int mint_op, int offset)
378 int type1 = td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-1].type;
379 int type2 = td->sp [-2].type == STACK_TYPE_O || td->sp [-2].type == STACK_TYPE_MP ? STACK_TYPE_I : td->sp [-2].type;
380 int long_op = mint_op + type1 - STACK_TYPE_I4;
381 int short_op = long_op + MINT_BEQ_I4_S - MINT_BEQ_I4;
382 CHECK_STACK(td, 2);
383 if (type1 == STACK_TYPE_I4 && type2 == STACK_TYPE_I8) {
384 // The il instruction starts with the actual branch, and not with the conversion opcodes
385 interp_insert_ins (td, td->last_ins, MINT_CONV_I8_I4);
386 } else if (type1 == STACK_TYPE_I8 && type2 == STACK_TYPE_I4) {
387 interp_insert_ins (td, td->last_ins, MINT_CONV_I8_I4_SP);
388 } else if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
389 interp_insert_ins (td, td->last_ins, MINT_CONV_R8_R4);
390 } else if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
391 interp_insert_ins (td, td->last_ins, MINT_CONV_R8_R4_SP);
392 } else if (type1 != type2) {
393 g_warning("%s.%s: branch type mismatch %d %d",
394 m_class_get_name (td->method->klass), td->method->name,
395 td->sp [-1].type, td->sp [-2].type);
397 td->sp -= 2;
398 handle_branch (td, short_op, long_op, offset);
401 static void
402 unary_arith_op(TransformData *td, int mint_op)
404 int op = mint_op + td->sp [-1].type - STACK_TYPE_I4;
405 CHECK_STACK(td, 1);
406 interp_add_ins (td, op);
409 static void
410 binary_arith_op(TransformData *td, int mint_op)
412 int type1 = td->sp [-2].type;
413 int type2 = td->sp [-1].type;
414 int op;
415 #if SIZEOF_VOID_P == 8
416 if ((type1 == STACK_TYPE_MP || type1 == STACK_TYPE_I8) && type2 == STACK_TYPE_I4) {
417 interp_add_ins (td, MINT_CONV_I8_I4);
418 type2 = STACK_TYPE_I8;
420 if (type1 == STACK_TYPE_I4 && (type2 == STACK_TYPE_MP || type2 == STACK_TYPE_I8)) {
421 interp_add_ins (td, MINT_CONV_I8_I4_SP);
422 type1 = STACK_TYPE_I8;
423 td->sp [-2].type = STACK_TYPE_I8;
425 #endif
426 if (type1 == STACK_TYPE_R8 && type2 == STACK_TYPE_R4) {
427 interp_add_ins (td, MINT_CONV_R8_R4);
428 type2 = STACK_TYPE_R8;
430 if (type1 == STACK_TYPE_R4 && type2 == STACK_TYPE_R8) {
431 interp_add_ins (td, MINT_CONV_R8_R4_SP);
432 type1 = STACK_TYPE_R8;
433 td->sp [-2].type = STACK_TYPE_R8;
435 if (type1 == STACK_TYPE_MP)
436 type1 = STACK_TYPE_I;
437 if (type2 == STACK_TYPE_MP)
438 type2 = STACK_TYPE_I;
439 if (type1 != type2) {
440 g_warning("%s.%s: %04x arith type mismatch %s %d %d",
441 m_class_get_name (td->method->klass), td->method->name,
442 td->ip - td->il_code, mono_interp_opname (mint_op), type1, type2);
444 op = mint_op + type1 - STACK_TYPE_I4;
445 CHECK_STACK(td, 2);
446 interp_add_ins (td, op);
447 --td->sp;
450 static void
451 shift_op(TransformData *td, int mint_op)
453 int op = mint_op + td->sp [-2].type - STACK_TYPE_I4;
454 CHECK_STACK(td, 2);
455 if (td->sp [-1].type != STACK_TYPE_I4) {
456 g_warning("%s.%s: shift type mismatch %d",
457 m_class_get_name (td->method->klass), td->method->name,
458 td->sp [-2].type);
460 interp_add_ins (td, op);
461 --td->sp;
464 static int
465 can_store (int st_value, int vt_value)
467 if (st_value == STACK_TYPE_O || st_value == STACK_TYPE_MP)
468 st_value = STACK_TYPE_I;
469 if (vt_value == STACK_TYPE_O || vt_value == STACK_TYPE_MP)
470 vt_value = STACK_TYPE_I;
471 return st_value == vt_value;
474 #define SET_SIMPLE_TYPE(s, ty) \
475 do { \
476 (s)->type = (ty); \
477 (s)->flags = 0; \
478 (s)->klass = NULL; \
479 } while (0)
481 #define SET_TYPE(s, ty, k) \
482 do { \
483 (s)->type = (ty); \
484 (s)->flags = 0; \
485 (s)->klass = k; \
486 } while (0)
488 #define REALLOC_STACK(td, sppos) \
489 do { \
490 (td)->stack_capacity *= 2; \
491 (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
492 (td)->sp = (td)->stack + (sppos); \
493 } while (0);
495 #define PUSH_SIMPLE_TYPE(td, ty) \
496 do { \
497 int sp_height; \
498 (td)->sp++; \
499 sp_height = (td)->sp - (td)->stack; \
500 if (sp_height > (td)->max_stack_height) \
501 (td)->max_stack_height = sp_height; \
502 if (sp_height > (td)->stack_capacity) \
503 REALLOC_STACK(td, sp_height); \
504 SET_SIMPLE_TYPE((td)->sp - 1, ty); \
505 } while (0)
507 #define PUSH_TYPE(td, ty, k) \
508 do { \
509 int sp_height; \
510 (td)->sp++; \
511 sp_height = (td)->sp - (td)->stack; \
512 if (sp_height > (td)->max_stack_height) \
513 (td)->max_stack_height = sp_height; \
514 if (sp_height > (td)->stack_capacity) \
515 REALLOC_STACK(td, sp_height); \
516 SET_TYPE((td)->sp - 1, ty, k); \
517 } while (0)
519 static void
520 move_stack (TransformData *td, int start, int amount)
522 int sp_height = td->sp - td->stack;
523 int to_move = sp_height - start;
525 td->sp += amount;
526 sp_height += amount;
527 if (amount > 0) {
528 if (sp_height > td->max_stack_height)
529 td->max_stack_height = sp_height;
530 if (sp_height > td->stack_capacity)
531 REALLOC_STACK (td, sp_height);
532 } else {
533 g_assert (td->sp >= td->stack);
536 if (to_move > 0)
537 memmove (td->stack + start + amount, td->stack + start, to_move * sizeof (StackInfo));
540 #define PUSH_VT(td, size) \
541 do { \
542 (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
543 if ((td)->vt_sp > (td)->max_vt_sp) \
544 (td)->max_vt_sp = (td)->vt_sp; \
545 } while (0)
547 #define POP_VT(td, size) \
548 do { \
549 (td)->vt_sp -= ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
550 } while (0)
552 static MonoType*
553 get_arg_type_exact (TransformData *td, int n, int *mt)
555 MonoType *type;
556 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
558 if (hasthis && n == 0)
559 type = m_class_get_byval_arg (td->method->klass);
560 else
561 type = mono_method_signature_internal (td->method)->params [n - !!hasthis];
563 if (mt)
564 *mt = mint_type (type);
566 return type;
569 static void
570 load_arg(TransformData *td, int n)
572 int mt;
573 MonoClass *klass = NULL;
574 MonoType *type;
575 gboolean hasthis = mono_method_signature_internal (td->method)->hasthis;
577 type = get_arg_type_exact (td, n, &mt);
579 if (mt == MINT_TYPE_VT) {
580 gint32 size;
581 klass = mono_class_from_mono_type_internal (type);
582 if (mono_method_signature_internal (td->method)->pinvoke)
583 size = mono_class_native_size (klass, NULL);
584 else
585 size = mono_class_value_size (klass, NULL);
587 if (hasthis && n == 0) {
588 mt = MINT_TYPE_P;
589 interp_add_ins (td, MINT_LDARG_P0);
590 klass = NULL;
591 } else {
592 PUSH_VT (td, size);
593 interp_add_ins (td, MINT_LDARG_VT);
594 td->last_ins->data [0] = n;
595 WRITE32_INS (td->last_ins, 1, &size);
597 } else {
598 if ((hasthis || mt == MINT_TYPE_P) && n == 0) {
599 mt = MINT_TYPE_P;
600 interp_add_ins (td, MINT_LDARG_P0);
601 } else {
602 interp_add_ins (td, MINT_LDARG_I1 + (mt - MINT_TYPE_I1));
603 td->last_ins->data [0] = n;
604 if (mt == MINT_TYPE_O)
605 klass = mono_class_from_mono_type_internal (type);
608 PUSH_TYPE(td, stack_type[mt], klass);
611 static void
612 store_arg(TransformData *td, int n)
614 int mt;
615 CHECK_STACK (td, 1);
616 MonoType *type;
618 type = get_arg_type_exact (td, n, &mt);
620 if (mt == MINT_TYPE_VT) {
621 gint32 size;
622 MonoClass *klass = mono_class_from_mono_type_internal (type);
623 if (mono_method_signature_internal (td->method)->pinvoke)
624 size = mono_class_native_size (klass, NULL);
625 else
626 size = mono_class_value_size (klass, NULL);
627 interp_add_ins (td, MINT_STARG_VT);
628 td->last_ins->data [0] = n;
629 WRITE32_INS (td->last_ins, 1, &size);
630 if (td->sp [-1].type == STACK_TYPE_VT)
631 POP_VT(td, size);
632 } else {
633 interp_add_ins (td, MINT_STARG_I1 + (mt - MINT_TYPE_I1));
634 td->last_ins->data [0] = n;
636 --td->sp;
639 static void
640 load_local (TransformData *td, int local)
642 MonoType *type = td->locals [local].type;
643 int mt = td->locals [local].mt;
644 MonoClass *klass = NULL;
645 if (mt == MINT_TYPE_VT) {
646 klass = mono_class_from_mono_type_internal (type);
647 gint32 size = mono_class_value_size (klass, NULL);
648 PUSH_VT(td, size);
649 interp_add_ins (td, MINT_LDLOC_VT);
650 td->last_ins->data [0] = local;
651 WRITE32_INS (td->last_ins, 1, &size);
652 } else {
653 g_assert (mt < MINT_TYPE_VT);
654 interp_add_ins (td, MINT_LDLOC_I1 + (mt - MINT_TYPE_I1));
655 td->last_ins->data [0] = local;
656 if (mt == MINT_TYPE_O)
657 klass = mono_class_from_mono_type_internal (type);
659 PUSH_TYPE(td, stack_type[mt], klass);
662 static void
663 store_local (TransformData *td, int local)
665 MonoType *type = td->locals [local].type;
666 int mt = td->locals [local].mt;
667 CHECK_STACK (td, 1);
668 #if SIZEOF_VOID_P == 8
669 if (td->sp [-1].type == STACK_TYPE_I4 && stack_type [mt] == STACK_TYPE_I8) {
670 interp_add_ins (td, MINT_CONV_I8_I4);
671 td->sp [-1].type = STACK_TYPE_I8;
673 #endif
674 if (!can_store(td->sp [-1].type, stack_type [mt])) {
675 g_warning("%s.%s: Store local stack type mismatch %d %d",
676 m_class_get_name (td->method->klass), td->method->name,
677 stack_type [mt], td->sp [-1].type);
679 if (mt == MINT_TYPE_VT) {
680 MonoClass *klass = mono_class_from_mono_type_internal (type);
681 gint32 size = mono_class_value_size (klass, NULL);
682 interp_add_ins (td, MINT_STLOC_VT);
683 td->last_ins->data [0] = local;
684 WRITE32_INS (td->last_ins, 1, &size);
685 if (td->sp [-1].type == STACK_TYPE_VT)
686 POP_VT(td, size);
687 } else {
688 g_assert (mt < MINT_TYPE_VT);
689 interp_add_ins (td, MINT_STLOC_I1 + (mt - MINT_TYPE_I1));
690 td->last_ins->data [0] = local;
692 --td->sp;
695 #define SIMPLE_OP(td, op) \
696 do { \
697 interp_add_ins (td, op); \
698 ++td->ip; \
699 } while (0)
701 static guint16
702 get_data_item_index (TransformData *td, void *ptr)
704 gpointer p = g_hash_table_lookup (td->data_hash, ptr);
705 guint index;
706 if (p != NULL)
707 return GPOINTER_TO_UINT (p) - 1;
708 if (td->max_data_items == td->n_data_items) {
709 td->max_data_items = td->n_data_items == 0 ? 16 : 2 * td->max_data_items;
710 td->data_items = (gpointer*)g_realloc (td->data_items, td->max_data_items * sizeof(td->data_items [0]));
712 index = td->n_data_items;
713 td->data_items [index] = ptr;
714 ++td->n_data_items;
715 g_hash_table_insert (td->data_hash, ptr, GUINT_TO_POINTER (index + 1));
716 return index;
719 gboolean
720 mono_interp_jit_call_supported (MonoMethod *method, MonoMethodSignature *sig)
722 GSList *l;
724 if (sig->param_count > 6)
725 return FALSE;
726 if (sig->pinvoke)
727 return FALSE;
728 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
729 return FALSE;
730 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
731 return FALSE;
732 if (method->is_inflated)
733 return FALSE;
734 if (method->string_ctor)
735 return FALSE;
736 if (method->wrapper_type != MONO_WRAPPER_NONE)
737 return FALSE;
739 if (mono_aot_only && m_class_get_image (method->klass)->aot_module && !(method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) {
740 ERROR_DECL (error);
741 gpointer addr = mono_jit_compile_method_jit_only (method, error);
742 if (addr && is_ok (error))
743 return TRUE;
746 for (l = mono_interp_jit_classes; l; l = l->next) {
747 const char *class_name = (const char*)l->data;
748 // FIXME: Namespaces
749 if (!strcmp (m_class_get_name (method->klass), class_name))
750 return TRUE;
753 //return TRUE;
754 return FALSE;
757 #ifdef ENABLE_EXPERIMENT_TIERED
758 static gboolean
759 jit_call2_supported (MonoMethod *method, MonoMethodSignature *sig)
761 if (sig->param_count > 6)
762 return FALSE;
763 if (sig->pinvoke)
764 return FALSE;
765 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
766 return FALSE;
767 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
768 return FALSE;
769 if (method->is_inflated)
770 return FALSE;
771 if (method->string_ctor)
772 return FALSE;
774 return TRUE;
776 #endif
778 static int mono_class_get_magic_index (MonoClass *k)
780 if (mono_class_is_magic_int (k))
781 return !strcmp ("nint", m_class_get_name (k)) ? 0 : 1;
783 if (mono_class_is_magic_float (k))
784 return 2;
786 return -1;
789 static void
790 interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *target_method)
792 MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_method_access;
794 /* Inject code throwing MethodAccessException */
795 interp_add_ins (td, MINT_MONO_LDPTR);
796 td->last_ins->data [0] = get_data_item_index (td, method);
797 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
799 interp_add_ins (td, MINT_MONO_LDPTR);
800 td->last_ins->data [0] = get_data_item_index (td, target_method);
801 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
803 interp_add_ins (td, MINT_ICALL_PP_V);
804 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
806 td->sp -= 2;
809 static void
810 interp_generate_bie_throw (TransformData *td)
812 MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_bad_image;
814 interp_add_ins (td, MINT_ICALL_V_V);
815 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
819 * These are additional locals that can be allocated as we transform the code.
820 * They are allocated past the method locals so they are accessed in the same
821 * way, with an offset relative to the frame->locals.
823 static int
824 create_interp_local (TransformData *td, MonoType *type)
826 if (td->locals_size == td->locals_capacity) {
827 td->locals_capacity *= 2;
828 if (td->locals_capacity == 0)
829 td->locals_capacity = 2;
830 td->locals = (InterpLocal*) g_realloc (td->locals, td->locals_capacity * sizeof (InterpLocal));
832 td->locals [td->locals_size].type = type;
833 td->locals [td->locals_size].mt = mint_type (type);
834 td->locals [td->locals_size].flags = 0;
835 td->locals [td->locals_size].offset = -1;
836 td->locals_size++;
837 return td->locals_size - 1;
840 static int
841 get_interp_local_offset (TransformData *td, int local)
843 int align, size, offset;
845 if (td->locals [local].offset != -1)
846 return td->locals [local].offset;
848 offset = td->total_locals_size;
849 size = mono_type_size (td->locals [local].type, &align);
850 offset = ALIGN_TO (offset, align);
852 td->locals [local].offset = offset;
854 td->total_locals_size = offset + size;
855 g_assert (td->total_locals_size < G_MAXUINT16);
857 return offset;
860 static void
861 dump_mint_code (const guint16 *start, const guint16* end)
863 const guint16 *p = start;
864 while (p < end) {
865 char *ins = mono_interp_dis_mintop ((gint32)(p - start), TRUE, p + 1, *p);
866 g_print ("%s\n", ins);
867 g_free (ins);
868 p = mono_interp_dis_mintop_len (p);
872 static void
873 dump_interp_inst (InterpInst *ins)
875 char *descr = mono_interp_dis_mintop (ins->il_offset, FALSE, &ins->data [0], ins->opcode);
876 g_print ("%s", descr);
877 g_free (descr);
880 static void
881 dump_interp_inst_newline (InterpInst *ins)
883 dump_interp_inst (ins);
884 g_print ("\n");
887 /* For debug use */
888 void
889 mono_interp_print_code (InterpMethod *imethod)
891 MonoJitInfo *jinfo = imethod->jinfo;
892 const guint8 *start;
894 if (!jinfo)
895 return;
897 char *name = mono_method_full_name (imethod->method, 1);
898 g_print ("Method : %s\n", name);
899 g_free (name);
901 start = (guint8*) jinfo->code_start;
902 dump_mint_code ((const guint16*)start, (const guint16*)(start + jinfo->code_size));
905 /* For debug use */
906 void
907 mono_interp_print_td_code (TransformData *td)
909 InterpInst *ins = td->first_ins;
911 char *name = mono_method_full_name (td->method, TRUE);
912 g_print ("IR for \"%s\"\n", name);
913 g_free (name);
914 while (ins) {
915 dump_interp_inst_newline (ins);
916 ins = ins->next;
921 static MonoMethodHeader*
922 interp_method_get_header (MonoMethod* method, MonoError *error)
924 /* An explanation: mono_method_get_header_internal returns an error if
925 * called on a method with no body (e.g. an abstract method, or an
926 * icall). We don't want that.
928 if (mono_method_has_no_body (method))
929 return NULL;
930 else
931 return mono_method_get_header_internal (method, error);
934 // Returns whether we can optimize away the instructions starting at start.
935 // If any instructions are part of a new basic block, we can't remove them.
936 static gboolean
937 interp_is_bb_start (TransformData *td, InterpInst *start, InterpInst *end)
939 InterpInst *ins = start;
940 while (ins != end) {
941 if (ins->il_offset != -1) {
942 if (td->is_bb_start [ins->il_offset])
943 return TRUE;
945 ins = ins->next;
948 // Also check if end is bb start
949 if (ins != NULL && ins->il_offset != -1 && td->is_bb_start [ins->il_offset])
950 return TRUE;
951 return FALSE;
954 static gboolean
955 interp_ins_is_ldc (InterpInst *ins)
957 return ins->opcode >= MINT_LDC_I4_M1 && ins->opcode <= MINT_LDC_I8;
960 static gint32
961 interp_get_const_from_ldc_i4 (InterpInst *ins)
963 switch (ins->opcode) {
964 case MINT_LDC_I4_M1: return -1;
965 case MINT_LDC_I4_0: return 0;
966 case MINT_LDC_I4_1: return 1;
967 case MINT_LDC_I4_2: return 2;
968 case MINT_LDC_I4_3: return 3;
969 case MINT_LDC_I4_4: return 4;
970 case MINT_LDC_I4_5: return 5;
971 case MINT_LDC_I4_6: return 6;
972 case MINT_LDC_I4_7: return 7;
973 case MINT_LDC_I4_8: return 8;
974 case MINT_LDC_I4_S: return (gint32)(gint8)ins->data [0];
975 case MINT_LDC_I4: return READ32 (&ins->data [0]);
976 default:
977 g_assert_not_reached ();
981 /* If ins is not null, it will replace it with the ldc */
982 static InterpInst*
983 interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct)
985 int opcode;
986 switch (ct) {
987 case -1: opcode = MINT_LDC_I4_M1; break;
988 case 0: opcode = MINT_LDC_I4_0; break;
989 case 1: opcode = MINT_LDC_I4_1; break;
990 case 2: opcode = MINT_LDC_I4_2; break;
991 case 3: opcode = MINT_LDC_I4_3; break;
992 case 4: opcode = MINT_LDC_I4_4; break;
993 case 5: opcode = MINT_LDC_I4_5; break;
994 case 6: opcode = MINT_LDC_I4_6; break;
995 case 7: opcode = MINT_LDC_I4_7; break;
996 case 8: opcode = MINT_LDC_I4_8; break;
997 default:
998 if (ct >= -128 && ct <= 127)
999 opcode = MINT_LDC_I4_S;
1000 else
1001 opcode = MINT_LDC_I4;
1002 break;
1005 int new_size = mono_interp_oplen [opcode];
1007 if (ins == NULL)
1008 ins = interp_add_ins (td, opcode);
1010 int ins_size = mono_interp_oplen [ins->opcode];
1011 if (ins_size < new_size) {
1012 // We can't replace the passed instruction, discard it and emit a new one
1013 ins = interp_insert_ins (td, ins, opcode);
1014 interp_clear_ins (td, ins->prev);
1015 } else {
1016 ins->opcode = opcode;
1019 if (new_size == 2)
1020 ins->data [0] = (gint8)ct;
1021 else if (new_size == 3)
1022 WRITE32_INS (ins, 0, &ct);
1024 return ins;
1027 static InterpInst*
1028 interp_inst_replace_with_i8_const (TransformData *td, InterpInst *ins, gint64 ct)
1030 int size = mono_interp_oplen [ins->opcode];
1032 if (size < 5) {
1033 ins = interp_insert_ins (td, ins, MINT_LDC_I8);
1034 interp_clear_ins (td, ins->prev);
1035 } else {
1036 ins->opcode = MINT_LDC_I8;
1038 WRITE64_INS (ins, 0, &ct);
1040 return ins;
1043 static int
1044 interp_get_ldind_for_mt (int mt)
1046 switch (mt) {
1047 case MINT_TYPE_I1: return MINT_LDIND_I1_CHECK;
1048 case MINT_TYPE_U1: return MINT_LDIND_U1_CHECK;
1049 case MINT_TYPE_I2: return MINT_LDIND_I2_CHECK;
1050 case MINT_TYPE_U2: return MINT_LDIND_U2_CHECK;
1051 case MINT_TYPE_I4: return MINT_LDIND_I4_CHECK;
1052 case MINT_TYPE_I8: return MINT_LDIND_I8_CHECK;
1053 case MINT_TYPE_R4: return MINT_LDIND_R4_CHECK;
1054 case MINT_TYPE_R8: return MINT_LDIND_R8_CHECK;
1055 case MINT_TYPE_O: return MINT_LDIND_REF;
1056 default:
1057 g_assert_not_reached ();
1059 return -1;
1062 static void
1063 interp_emit_ldobj (TransformData *td, MonoClass *klass)
1065 int mt = mint_type (m_class_get_byval_arg (klass));
1066 int size;
1068 if (mt == MINT_TYPE_VT) {
1069 interp_add_ins (td, MINT_LDOBJ_VT);
1070 size = mono_class_value_size (klass, NULL);
1071 WRITE32_INS (td->last_ins, 0, &size);
1072 PUSH_VT (td, size);
1073 } else {
1074 int opcode = interp_get_ldind_for_mt (mt);
1075 interp_add_ins (td, opcode);
1078 SET_TYPE (td->sp - 1, stack_type [mt], klass);
1081 static void
1082 interp_emit_stobj (TransformData *td, MonoClass *klass)
1084 int mt = mint_type (m_class_get_byval_arg (klass));
1086 if (mt == MINT_TYPE_VT) {
1087 int size;
1088 interp_add_ins (td, MINT_STOBJ_VT);
1089 td->last_ins->data [0] = get_data_item_index(td, klass);
1090 size = mono_class_value_size (klass, NULL);
1091 POP_VT (td, size);
1092 } else {
1093 int opcode;
1094 switch (mt) {
1095 case MINT_TYPE_I1:
1096 case MINT_TYPE_U1:
1097 opcode = MINT_STIND_I1;
1098 break;
1099 case MINT_TYPE_I2:
1100 case MINT_TYPE_U2:
1101 opcode = MINT_STIND_I2;
1102 break;
1103 case MINT_TYPE_I4:
1104 opcode = MINT_STIND_I4;
1105 break;
1106 case MINT_TYPE_I8:
1107 opcode = MINT_STIND_I8;
1108 break;
1109 case MINT_TYPE_R4:
1110 opcode = MINT_STIND_R4;
1111 break;
1112 case MINT_TYPE_R8:
1113 opcode = MINT_STIND_R8;
1114 break;
1115 case MINT_TYPE_O:
1116 opcode = MINT_STIND_REF;
1117 break;
1118 default: g_assert_not_reached (); break;
1120 interp_add_ins (td, opcode);
1122 td->sp -= 2;
1125 static void
1126 interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check_class)
1128 MonoClass *element_class = m_class_get_element_class (array_class);
1129 int rank = m_class_get_rank (array_class);
1130 int size = mono_class_array_element_size (element_class);
1132 // We only need type checks when writing to array of references
1133 if (!check_class || m_class_is_valuetype (element_class)) {
1134 if (rank == 1) {
1135 interp_add_ins (td, MINT_LDELEMA1);
1136 WRITE32_INS (td->last_ins, 0, &size);
1137 } else {
1138 interp_add_ins (td, MINT_LDELEMA);
1139 td->last_ins->data [0] = rank;
1140 WRITE32_INS (td->last_ins, 1, &size);
1142 } else {
1143 interp_add_ins (td, MINT_LDELEMA_TC);
1144 td->last_ins->data [0] = rank;
1145 td->last_ins->data [1] = get_data_item_index (td, check_class);
1148 td->sp -= rank;
1149 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1152 /* Return TRUE if call transformation is finished */
1153 static gboolean
1154 interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClass *constrained_class, MonoMethodSignature *csignature, gboolean readonly, int *op)
1156 const char *tm = target_method->name;
1157 int i;
1158 int type_index = mono_class_get_magic_index (target_method->klass);
1159 gboolean in_corlib = m_class_get_image (target_method->klass) == mono_defaults.corlib;
1160 const char *klass_name_space = m_class_get_name_space (target_method->klass);
1161 const char *klass_name = m_class_get_name (target_method->klass);
1163 if (target_method->klass == mono_defaults.string_class) {
1164 if (tm [0] == 'g') {
1165 if (strcmp (tm, "get_Chars") == 0)
1166 *op = MINT_GETCHR;
1167 else if (strcmp (tm, "get_Length") == 0)
1168 *op = MINT_STRLEN;
1170 } else if (type_index >= 0) {
1171 MonoClass *magic_class = target_method->klass;
1173 const int mt = mint_type (m_class_get_byval_arg (magic_class));
1174 if (!strcmp (".ctor", tm)) {
1175 MonoType *arg = csignature->params [0];
1176 /* depending on SIZEOF_VOID_P and the type of the value passed to the .ctor we either have to CONV it, or do nothing */
1177 int arg_size = mini_magic_type_size (NULL, arg);
1179 if (arg_size > SIZEOF_VOID_P) { // 8 -> 4
1180 switch (type_index) {
1181 case 0: case 1:
1182 interp_add_ins (td, MINT_CONV_I4_I8);
1183 break;
1184 case 2:
1185 interp_add_ins (td, MINT_CONV_R4_R8);
1186 break;
1190 if (arg_size < SIZEOF_VOID_P) { // 4 -> 8
1191 switch (type_index) {
1192 case 0:
1193 interp_add_ins (td, MINT_CONV_I8_I4);
1194 break;
1195 case 1:
1196 interp_add_ins (td, MINT_CONV_I8_U4);
1197 break;
1198 case 2:
1199 interp_add_ins (td, MINT_CONV_R8_R4);
1200 break;
1204 switch (type_index) {
1205 case 0: case 1:
1206 #if SIZEOF_VOID_P == 4
1207 interp_add_ins (td, MINT_STIND_I4);
1208 #else
1209 interp_add_ins (td, MINT_STIND_I8);
1210 #endif
1211 break;
1212 case 2:
1213 #if SIZEOF_VOID_P == 4
1214 interp_add_ins (td, MINT_STIND_R4);
1215 #else
1216 interp_add_ins (td, MINT_STIND_R8);
1217 #endif
1218 break;
1221 td->sp -= 2;
1222 td->ip += 5;
1223 return TRUE;
1224 } else if (!strcmp ("op_Implicit", tm ) || !strcmp ("op_Explicit", tm)) {
1225 MonoType *src = csignature->params [0];
1226 MonoType *dst = csignature->ret;
1227 MonoClass *src_klass = mono_class_from_mono_type_internal (src);
1228 int src_size = mini_magic_type_size (NULL, src);
1229 int dst_size = mini_magic_type_size (NULL, dst);
1231 gboolean managed_fallback = FALSE;
1233 switch (type_index) {
1234 case 0: case 1:
1235 if (!mini_magic_is_int_type (src) || !mini_magic_is_int_type (dst)) {
1236 if (mini_magic_is_int_type (src))
1237 managed_fallback = TRUE;
1238 else if (mono_class_is_magic_float (src_klass))
1239 managed_fallback = TRUE;
1240 else
1241 return FALSE;
1243 break;
1244 case 2:
1245 if (!mini_magic_is_float_type (src) || !mini_magic_is_float_type (dst)) {
1246 if (mini_magic_is_float_type (src))
1247 managed_fallback = TRUE;
1248 else if (mono_class_is_magic_int (src_klass))
1249 managed_fallback = TRUE;
1250 else
1251 return FALSE;
1253 break;
1256 if (managed_fallback)
1257 return FALSE;
1259 if (src_size > dst_size) { // 8 -> 4
1260 switch (type_index) {
1261 case 0: case 1:
1262 interp_add_ins (td, MINT_CONV_I4_I8);
1263 break;
1264 case 2:
1265 interp_add_ins (td, MINT_CONV_R4_R8);
1266 break;
1270 if (src_size < dst_size) { // 4 -> 8
1271 switch (type_index) {
1272 case 0:
1273 interp_add_ins (td, MINT_CONV_I8_I4);
1274 break;
1275 case 1:
1276 interp_add_ins (td, MINT_CONV_I8_U4);
1277 break;
1278 case 2:
1279 interp_add_ins (td, MINT_CONV_R8_R4);
1280 break;
1284 SET_TYPE (td->sp - 1, stack_type [mint_type (dst)], mono_class_from_mono_type_internal (dst));
1285 td->ip += 5;
1286 return TRUE;
1287 } else if (!strcmp ("op_Increment", tm)) {
1288 g_assert (type_index != 2); // no nfloat
1289 #if SIZEOF_VOID_P == 8
1290 interp_add_ins (td, MINT_ADD1_I8);
1291 #else
1292 interp_add_ins (td, MINT_ADD1_I4);
1293 #endif
1294 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1295 td->ip += 5;
1296 return TRUE;
1297 } else if (!strcmp ("op_Decrement", tm)) {
1298 g_assert (type_index != 2); // no nfloat
1299 #if SIZEOF_VOID_P == 8
1300 interp_add_ins (td, MINT_SUB1_I8);
1301 #else
1302 interp_add_ins (td, MINT_SUB1_I4);
1303 #endif
1304 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1305 td->ip += 5;
1306 return TRUE;
1307 } else if (!strcmp ("CompareTo", tm) || !strcmp ("Equals", tm)) {
1308 return FALSE;
1309 } else if (!strcmp (".cctor", tm)) {
1310 /* white list */
1311 return FALSE;
1312 } else if (!strcmp ("Parse", tm)) {
1313 /* white list */
1314 return FALSE;
1315 } else if (!strcmp ("ToString", tm)) {
1316 /* white list */
1317 return FALSE;
1318 } else if (!strcmp ("GetHashCode", tm)) {
1319 /* white list */
1320 return FALSE;
1321 } else if (!strcmp ("IsNaN", tm) || !strcmp ("IsInfinity", tm) || !strcmp ("IsNegativeInfinity", tm) || !strcmp ("IsPositiveInfinity", tm)) {
1322 g_assert (type_index == 2); // nfloat only
1323 /* white list */
1324 return FALSE;
1327 for (i = 0; i < sizeof (int_unnop) / sizeof (MagicIntrinsic); ++i) {
1328 if (!strcmp (int_unnop [i].op_name, tm)) {
1329 interp_add_ins (td, int_unnop [i].insn [type_index]);
1330 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1331 td->ip += 5;
1332 return TRUE;
1336 for (i = 0; i < sizeof (int_binop) / sizeof (MagicIntrinsic); ++i) {
1337 if (!strcmp (int_binop [i].op_name, tm)) {
1338 interp_add_ins (td, int_binop [i].insn [type_index]);
1339 td->sp -= 1;
1340 SET_TYPE (td->sp - 1, stack_type [mt], magic_class);
1341 td->ip += 5;
1342 return TRUE;
1346 for (i = 0; i < sizeof (int_cmpop) / sizeof (MagicIntrinsic); ++i) {
1347 if (!strcmp (int_cmpop [i].op_name, tm)) {
1348 MonoClass *k = mono_defaults.boolean_class;
1349 interp_add_ins (td, int_cmpop [i].insn [type_index]);
1350 td->sp -= 1;
1351 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1352 td->ip += 5;
1353 return TRUE;
1357 g_error ("TODO: interp_transform_call %s:%s", m_class_get_name (target_method->klass), tm);
1358 } else if (mono_class_is_subclass_of_internal (target_method->klass, mono_defaults.array_class, FALSE)) {
1359 if (!strcmp (tm, "get_Rank")) {
1360 *op = MINT_ARRAY_RANK;
1361 } else if (!strcmp (tm, "get_Length")) {
1362 *op = MINT_LDLEN;
1363 } else if (!strcmp (tm, "GetElementSize")) {
1364 *op = MINT_ARRAY_ELEMENT_SIZE;
1365 } else if (!strcmp (tm, "IsPrimitive")) {
1366 *op = MINT_ARRAY_IS_PRIMITIVE;
1367 } else if (!strcmp (tm, "Address")) {
1368 MonoClass *check_class = readonly ? NULL : m_class_get_element_class (target_method->klass);
1369 interp_emit_ldelema (td, target_method->klass, check_class);
1370 td->ip += 5;
1371 return TRUE;
1372 #ifndef ENABLE_NETCORE
1373 } else if (!strcmp (tm, "UnsafeMov") || !strcmp (tm, "UnsafeLoad")) {
1374 *op = MINT_CALLRUN;
1375 #endif
1376 } else if (!strcmp (tm, "Get")) {
1377 interp_emit_ldelema (td, target_method->klass, NULL);
1378 interp_emit_ldobj (td, m_class_get_element_class (target_method->klass));
1379 td->ip += 5;
1380 return TRUE;
1381 } else if (!strcmp (tm, "Set")) {
1382 MonoClass *element_class = m_class_get_element_class (target_method->klass);
1383 MonoType *local_type = m_class_get_byval_arg (element_class);
1384 MonoClass *value_class = td->sp [-1].klass;
1385 // If value_class is NULL it means the top of stack is a simple type (valuetype)
1386 // which doesn't require type checks, or that we have no type information because
1387 // the code is unsafe (like in some wrappers). In that case we assume the type
1388 // of the array and don't do any checks.
1390 int local = create_interp_local (td, local_type);
1392 store_local (td, local);
1393 interp_emit_ldelema (td, target_method->klass, value_class);
1394 load_local (td, local);
1395 interp_emit_stobj (td, element_class);
1396 td->ip += 5;
1397 return TRUE;
1398 } else if (!strcmp (tm, "UnsafeStore")) {
1399 g_error ("TODO ArrayClass::UnsafeStore");
1401 } else if (in_corlib &&
1402 !strcmp (klass_name_space, "System.Diagnostics") &&
1403 !strcmp (klass_name, "Debugger")) {
1404 if (!strcmp (tm, "Break") && csignature->param_count == 0) {
1405 if (mini_should_insert_breakpoint (td->method))
1406 *op = MINT_BREAK;
1408 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "ByReference`1")) {
1409 g_assert (!strcmp (tm, "get_Value"));
1410 *op = MINT_INTRINS_BYREFERENCE_GET_VALUE;
1411 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "Math") && csignature->param_count == 1 && csignature->params [0]->type == MONO_TYPE_R8) {
1412 if (tm [0] == 'A') {
1413 if (strcmp (tm, "Abs") == 0 && csignature->params [0]->type == MONO_TYPE_R8) {
1414 *op = MINT_ABS;
1415 } else if (strcmp (tm, "Asin") == 0){
1416 *op = MINT_ASIN;
1417 } else if (strcmp (tm, "Asinh") == 0){
1418 *op = MINT_ASINH;
1419 } else if (strcmp (tm, "Acos") == 0){
1420 *op = MINT_ACOS;
1421 } else if (strcmp (tm, "Acosh") == 0){
1422 *op = MINT_ACOSH;
1423 } else if (strcmp (tm, "Atan") == 0){
1424 *op = MINT_ATAN;
1425 } else if (strcmp (tm, "Atanh") == 0){
1426 *op = MINT_ATANH;
1428 } else if (tm [0] == 'C') {
1429 if (strcmp (tm, "Cos") == 0) {
1430 *op = MINT_COS;
1431 } else if (strcmp (tm, "Cbrt") == 0){
1432 *op = MINT_CBRT;
1433 } else if (strcmp (tm, "Cosh") == 0){
1434 *op = MINT_COSH;
1436 } else if (tm [0] == 'S') {
1437 if (strcmp (tm, "Sin") == 0) {
1438 *op = MINT_SIN;
1439 } else if (strcmp (tm, "Sqrt") == 0) {
1440 *op = MINT_SQRT;
1441 } else if (strcmp (tm, "Sinh") == 0){
1442 *op = MINT_SINH;
1444 } else if (tm [0] == 'T') {
1445 if (strcmp (tm, "Tan") == 0) {
1446 *op = MINT_TAN;
1447 } else if (strcmp (tm, "Tanh") == 0){
1448 *op = MINT_TANH;
1451 } else if (in_corlib && !strcmp (klass_name_space, "System") && (!strcmp (klass_name, "Span`1") || !strcmp (klass_name, "ReadOnlySpan`1"))) {
1452 if (!strcmp (tm, "get_Item")) {
1453 MonoGenericClass *gclass = mono_class_get_generic_class (target_method->klass);
1454 MonoClass *param_class = mono_class_from_mono_type_internal (gclass->context.class_inst->type_argv [0]);
1456 if (!mini_is_gsharedvt_variable_klass (param_class)) {
1457 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1458 g_assert (length_field);
1459 int offset_length = length_field->offset - sizeof (MonoObject);
1461 MonoClassField *ptr_field = mono_class_get_field_from_name_full (target_method->klass, "_pointer", NULL);
1462 g_assert (ptr_field);
1463 int offset_pointer = ptr_field->offset - sizeof (MonoObject);
1465 int size = mono_class_array_element_size (param_class);
1466 interp_add_ins (td, MINT_GETITEM_SPAN);
1467 td->last_ins->data [0] = size;
1468 td->last_ins->data [1] = offset_length;
1469 td->last_ins->data [2] = offset_pointer;
1471 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1472 td->sp -= 1;
1473 td->ip += 5;
1474 return TRUE;
1476 } else if (!strcmp (tm, "get_Length")) {
1477 MonoClassField *length_field = mono_class_get_field_from_name_full (target_method->klass, "_length", NULL);
1478 g_assert (length_field);
1479 int offset_length = length_field->offset - sizeof (MonoObject);
1480 interp_add_ins (td, MINT_LDLEN_SPAN);
1481 td->last_ins->data [0] = offset_length;
1482 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1483 td->ip += 5;
1484 return TRUE;
1486 } else if (((in_corlib && !strcmp (klass_name_space, "Internal.Runtime.CompilerServices"))
1487 || !strcmp (klass_name_space, "System.Runtime.CompilerServices"))
1488 && !strcmp (klass_name, "Unsafe")) {
1489 #ifdef ENABLE_NETCORE
1490 if (!strcmp (tm, "AddByteOffset"))
1491 *op = MINT_INTRINS_UNSAFE_ADD_BYTE_OFFSET;
1492 else if (!strcmp (tm, "ByteOffset"))
1493 *op = MINT_INTRINS_UNSAFE_BYTE_OFFSET;
1494 else if (!strcmp (tm, "As") || !strcmp (tm, "AsRef"))
1495 *op = MINT_NOP;
1496 else if (!strcmp (tm, "AsPointer")) {
1497 /* NOP */
1498 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1499 td->ip += 5;
1500 return TRUE;
1501 } else if (!strcmp (tm, "IsAddressLessThan")) {
1502 MonoGenericContext *ctx = mono_method_get_context (target_method);
1503 g_assert (ctx);
1504 g_assert (ctx->method_inst);
1505 g_assert (ctx->method_inst->type_argc == 1);
1507 MonoClass *k = mono_defaults.boolean_class;
1508 interp_add_ins (td, MINT_CLT_UN_P);
1509 td->sp -= 1;
1510 SET_TYPE (td->sp - 1, stack_type [mint_type (m_class_get_byval_arg (k))], k);
1511 td->ip += 5;
1512 return TRUE;
1513 } else if (!strcmp (tm, "SizeOf")) {
1514 MonoGenericContext *ctx = mono_method_get_context (target_method);
1515 g_assert (ctx);
1516 g_assert (ctx->method_inst);
1517 g_assert (ctx->method_inst->type_argc == 1);
1518 MonoType *t = ctx->method_inst->type_argv [0];
1519 int align;
1520 int esize = mono_type_size (t, &align);
1521 interp_add_ins (td, MINT_LDC_I4);
1522 WRITE32_INS (td->last_ins, 0, &esize);
1523 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1524 td->ip += 5;
1525 return TRUE;
1526 } else if (!strcmp (tm, "AreSame")) {
1527 *op = MINT_CEQ_P;
1528 } else if (!strcmp (tm, "SkipInit")) {
1529 *op = MINT_POP;
1531 #endif
1532 } else if (in_corlib && !strcmp (klass_name_space, "System.Runtime.CompilerServices") && !strcmp (klass_name, "RuntimeHelpers")) {
1533 #ifdef ENABLE_NETCORE
1534 if (!strcmp (tm, "get_OffsetToStringData")) {
1535 g_assert (csignature->param_count == 0);
1536 int offset = MONO_STRUCT_OFFSET (MonoString, chars);
1537 interp_add_ins (td, MINT_LDC_I4);
1538 WRITE32_INS (td->last_ins, 0, &offset);
1539 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1540 td->ip += 5;
1541 return TRUE;
1542 } else if (!strcmp (tm, "IsBitwiseEquatable")) {
1543 g_assert (csignature->param_count == 0);
1544 MonoGenericContext *ctx = mono_method_get_context (target_method);
1545 g_assert (ctx);
1546 g_assert (ctx->method_inst);
1547 g_assert (ctx->method_inst->type_argc == 1);
1548 MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]);
1550 if (MONO_TYPE_IS_PRIMITIVE (t) && t->type != MONO_TYPE_R4 && t->type != MONO_TYPE_R8)
1551 *op = MINT_LDC_I4_1;
1552 else
1553 *op = MINT_LDC_I4_0;
1554 } else if (!strcmp (tm, "ObjectHasComponentSize")) {
1555 *op = MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE;
1556 } else if (!strcmp (tm, "IsReferenceOrContainsReferences")) {
1557 g_assert (csignature->param_count == 0);
1558 MonoGenericContext *ctx = mono_method_get_context (target_method);
1559 g_assert (ctx);
1560 g_assert (ctx->method_inst);
1561 g_assert (ctx->method_inst->type_argc == 1);
1562 MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]);
1564 gboolean has_refs;
1566 MonoClass *klass = mono_class_from_mono_type_internal (t);
1567 mono_class_init_internal (klass);
1568 if (MONO_TYPE_IS_REFERENCE (t))
1569 has_refs = TRUE;
1570 else if (MONO_TYPE_IS_PRIMITIVE (t))
1571 has_refs = FALSE;
1572 else
1573 has_refs = m_class_has_references (klass);
1575 *op = has_refs ? MINT_LDC_I4_1 : MINT_LDC_I4_0;
1577 #endif
1578 } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "RuntimeMethodHandle") && !strcmp (tm, "GetFunctionPointer") && csignature->param_count == 1) {
1579 // We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter
1580 *op = MINT_LDFTN_DYNAMIC;
1581 } else if (in_corlib && target_method->klass == mono_defaults.systemtype_class && !strcmp (target_method->name, "op_Equality")) {
1582 *op = MINT_CEQ_P;
1583 } else if (in_corlib && target_method->klass == mono_defaults.object_class) {
1584 if (!strcmp (tm, "InternalGetHashCode"))
1585 *op = MINT_INTRINS_GET_HASHCODE;
1586 else if (!strcmp (tm, "GetType")
1587 #ifndef DISABLE_REMOTING
1588 // Invoking GetType via reflection on proxies has some special semantics
1589 // See InterfaceProxyGetTypeViaReflectionOkay corlib test
1590 && td->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE
1591 #endif
1593 *op = MINT_INTRINS_GET_TYPE;
1594 #ifdef ENABLE_NETCORE
1595 else if (!strcmp (tm, "GetRawData")) {
1596 #if SIZEOF_VOID_P == 8
1597 interp_add_ins (td, MINT_LDC_I8_S);
1598 #else
1599 interp_add_ins (td, MINT_LDC_I4_S);
1600 #endif
1601 td->last_ins->data [0] = (gint16) MONO_ABI_SIZEOF (MonoObject);
1603 interp_add_ins (td, MINT_ADD_P);
1604 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
1606 td->ip += 5;
1607 return TRUE;
1609 #endif
1610 } else if (in_corlib && target_method->klass == mono_defaults.enum_class && !strcmp (tm, "HasFlag")) {
1611 gboolean intrinsify = FALSE;
1612 MonoClass *base_klass = NULL;
1613 if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1614 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1615 td->last_ins->prev->prev && td->last_ins->prev->prev->opcode == MINT_BOX &&
1616 td->sp [-2].klass == td->sp [-1].klass &&
1617 !interp_is_bb_start (td, td->last_ins->prev->prev, NULL) &&
1618 !td->is_bb_start [td->in_start - td->il_code]) {
1619 // csc pattern : box, ldc, box, call HasFlag
1620 g_assert (m_class_is_enumtype (td->sp [-2].klass));
1621 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (td->sp [-2].klass));
1622 base_klass = mono_class_from_mono_type_internal (base_type);
1624 // Remove the boxing of valuetypes
1625 interp_clear_ins (td, td->last_ins->prev->prev);
1626 interp_clear_ins (td, td->last_ins);
1628 intrinsify = TRUE;
1629 } else if (td->last_ins && td->last_ins->opcode == MINT_BOX &&
1630 td->last_ins->prev && interp_ins_is_ldc (td->last_ins->prev) &&
1631 constrained_class && td->sp [-1].klass == constrained_class &&
1632 !interp_is_bb_start (td, td->last_ins->prev, NULL) &&
1633 !td->is_bb_start [td->in_start - td->il_code]) {
1634 // mcs pattern : ldc, box, constrained Enum, call HasFlag
1635 g_assert (m_class_is_enumtype (constrained_class));
1636 MonoType *base_type = mono_type_get_underlying_type (m_class_get_byval_arg (constrained_class));
1637 base_klass = mono_class_from_mono_type_internal (base_type);
1638 int mt = mint_type (m_class_get_byval_arg (base_klass));
1640 // Remove boxing and load the value of this
1641 interp_clear_ins (td, td->last_ins);
1642 interp_insert_ins (td, td->last_ins->prev->prev, interp_get_ldind_for_mt (mt));
1644 intrinsify = TRUE;
1646 if (intrinsify) {
1647 interp_add_ins (td, MINT_INTRINS_ENUM_HASFLAG);
1648 td->last_ins->data [0] = get_data_item_index (td, base_klass);
1649 td->sp -= 2;
1650 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
1651 td->ip += 5;
1652 return TRUE;
1654 } else if (in_corlib && !strcmp (klass_name_space, "System.Threading") && !strcmp (klass_name, "Interlocked")) {
1655 if (!strcmp (tm, "MemoryBarrier") && csignature->param_count == 0)
1656 *op = MINT_MONO_MEMORY_BARRIER;
1657 } else if (in_corlib && !strcmp (klass_name_space, "System.Threading") && !strcmp (klass_name, "Thread")) {
1658 if (!strcmp (tm, "MemoryBarrier") && csignature->param_count == 0)
1659 *op = MINT_MONO_MEMORY_BARRIER;
1660 } else if (in_corlib &&
1661 !strcmp (klass_name_space, "System.Runtime.CompilerServices") &&
1662 !strcmp (klass_name, "JitHelpers") &&
1663 (!strcmp (tm, "EnumEquals") || !strcmp (tm, "EnumCompareTo"))) {
1664 MonoGenericContext *ctx = mono_method_get_context (target_method);
1665 g_assert (ctx);
1666 g_assert (ctx->method_inst);
1667 g_assert (ctx->method_inst->type_argc == 1);
1668 g_assert (csignature->param_count == 2);
1670 MonoType *t = ctx->method_inst->type_argv [0];
1671 t = mini_get_underlying_type (t);
1673 gboolean is_i8 = (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_U8);
1674 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);
1676 gboolean is_compareto = strcmp (tm, "EnumCompareTo") == 0;
1677 if (is_compareto) {
1678 int locala, localb;
1679 locala = create_interp_local (td, t);
1680 localb = create_interp_local (td, t);
1682 // Save arguments
1683 store_local (td, localb);
1684 store_local (td, locala);
1685 // (a > b)
1686 load_local (td, locala);
1687 load_local (td, localb);
1688 if (is_unsigned)
1689 interp_add_ins (td, is_i8 ? MINT_CGT_UN_I8 : MINT_CGT_UN_I4);
1690 else
1691 interp_add_ins (td, is_i8 ? MINT_CGT_I8 : MINT_CGT_I4);
1692 td->sp --;
1693 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1694 // (a < b)
1695 load_local (td, locala);
1696 load_local (td, localb);
1697 if (is_unsigned)
1698 interp_add_ins (td, is_i8 ? MINT_CLT_UN_I8 : MINT_CLT_UN_I4);
1699 else
1700 interp_add_ins (td, is_i8 ? MINT_CLT_I8 : MINT_CLT_I4);
1701 td->sp --;
1702 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
1703 // (a > b) - (a < b)
1704 interp_add_ins (td, MINT_SUB_I4);
1705 td->sp --;
1706 td->ip += 5;
1707 return TRUE;
1708 } else {
1709 if (is_i8) {
1710 *op = MINT_CEQ_I8;
1711 } else {
1712 *op = MINT_CEQ_I4;
1715 } else if (in_corlib &&
1716 !strcmp ("System.Runtime.CompilerServices", klass_name_space) &&
1717 !strcmp ("RuntimeFeature", klass_name)) {
1718 if (!strcmp (tm, "get_IsDynamicCodeSupported"))
1719 *op = MINT_LDC_I4_1;
1720 else if (!strcmp (tm, "get_IsDynamicCodeCompiled"))
1721 *op = MINT_LDC_I4_0;
1724 return FALSE;
1727 static MonoMethod*
1728 interp_transform_internal_calls (MonoMethod *method, MonoMethod *target_method, MonoMethodSignature *csignature, gboolean is_virtual)
1730 if (method->wrapper_type == MONO_WRAPPER_NONE && target_method != NULL) {
1731 if (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
1732 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1733 if (!is_virtual && target_method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
1734 target_method = mono_marshal_get_synchronized_wrapper (target_method);
1736 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)
1737 target_method = mono_marshal_get_native_wrapper (target_method, FALSE, FALSE);
1739 return target_method;
1742 static gboolean
1743 interp_type_as_ptr (MonoType *tp)
1745 if (MONO_TYPE_IS_POINTER (tp))
1746 return TRUE;
1747 if (MONO_TYPE_IS_REFERENCE (tp))
1748 return TRUE;
1749 if ((tp)->type == MONO_TYPE_I4)
1750 return TRUE;
1751 #if SIZEOF_VOID_P == 8
1752 if ((tp)->type == MONO_TYPE_I8)
1753 return TRUE;
1754 #endif
1755 if ((tp)->type == MONO_TYPE_BOOLEAN)
1756 return TRUE;
1757 if ((tp)->type == MONO_TYPE_CHAR)
1758 return TRUE;
1759 if ((tp)->type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (tp->data.klass))
1760 return TRUE;
1761 return FALSE;
1764 #define INTERP_TYPE_AS_PTR(tp) interp_type_as_ptr (tp)
1766 static int
1767 interp_icall_op_for_sig (MonoMethodSignature *sig)
1769 int op = -1;
1770 switch (sig->param_count) {
1771 case 0:
1772 if (MONO_TYPE_IS_VOID (sig->ret))
1773 op = MINT_ICALL_V_V;
1774 else if (INTERP_TYPE_AS_PTR (sig->ret))
1775 op = MINT_ICALL_V_P;
1776 break;
1777 case 1:
1778 if (MONO_TYPE_IS_VOID (sig->ret)) {
1779 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1780 op = MINT_ICALL_P_V;
1781 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1782 if (INTERP_TYPE_AS_PTR (sig->params [0]))
1783 op = MINT_ICALL_P_P;
1785 break;
1786 case 2:
1787 if (MONO_TYPE_IS_VOID (sig->ret)) {
1788 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1789 INTERP_TYPE_AS_PTR (sig->params [1]))
1790 op = MINT_ICALL_PP_V;
1791 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1792 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1793 INTERP_TYPE_AS_PTR (sig->params [1]))
1794 op = MINT_ICALL_PP_P;
1796 break;
1797 case 3:
1798 if (MONO_TYPE_IS_VOID (sig->ret)) {
1799 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1800 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1801 INTERP_TYPE_AS_PTR (sig->params [2]))
1802 op = MINT_ICALL_PPP_V;
1803 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1804 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1805 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1806 INTERP_TYPE_AS_PTR (sig->params [2]))
1807 op = MINT_ICALL_PPP_P;
1809 break;
1810 case 4:
1811 if (MONO_TYPE_IS_VOID (sig->ret)) {
1812 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1813 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1814 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1815 INTERP_TYPE_AS_PTR (sig->params [3]))
1816 op = MINT_ICALL_PPPP_V;
1817 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1818 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1819 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1820 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1821 INTERP_TYPE_AS_PTR (sig->params [3]))
1822 op = MINT_ICALL_PPPP_P;
1824 break;
1825 case 5:
1826 if (MONO_TYPE_IS_VOID (sig->ret)) {
1827 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1828 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1829 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1830 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1831 INTERP_TYPE_AS_PTR (sig->params [4]))
1832 op = MINT_ICALL_PPPPP_V;
1833 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1834 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1835 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1836 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1837 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1838 INTERP_TYPE_AS_PTR (sig->params [4]))
1839 op = MINT_ICALL_PPPPP_P;
1841 break;
1842 case 6:
1843 if (MONO_TYPE_IS_VOID (sig->ret)) {
1844 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1845 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1846 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1847 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1848 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1849 INTERP_TYPE_AS_PTR (sig->params [5]))
1850 op = MINT_ICALL_PPPPPP_V;
1851 } else if (INTERP_TYPE_AS_PTR (sig->ret)) {
1852 if (INTERP_TYPE_AS_PTR (sig->params [0]) &&
1853 INTERP_TYPE_AS_PTR (sig->params [1]) &&
1854 INTERP_TYPE_AS_PTR (sig->params [2]) &&
1855 INTERP_TYPE_AS_PTR (sig->params [3]) &&
1856 INTERP_TYPE_AS_PTR (sig->params [4]) &&
1857 INTERP_TYPE_AS_PTR (sig->params [5]))
1858 op = MINT_ICALL_PPPPPP_P;
1860 break;
1862 return op;
1865 #define INLINE_LENGTH_LIMIT 20
1867 static gboolean
1868 interp_method_check_inlining (TransformData *td, MonoMethod *method)
1870 MonoMethodHeaderSummary header;
1872 if (td->method == method)
1873 return FALSE;
1875 if (method->flags & METHOD_ATTRIBUTE_REQSECOBJ)
1876 /* Used to mark methods containing StackCrawlMark locals */
1877 return FALSE;
1879 if (!mono_method_get_header_summary (method, &header))
1880 return FALSE;
1882 /*runtime, icall and pinvoke are checked by summary call*/
1883 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
1884 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
1885 (mono_class_is_marshalbyref (method->klass)) ||
1886 header.has_clauses)
1887 return FALSE;
1889 if (header.code_size >= INLINE_LENGTH_LIMIT && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))
1890 return FALSE;
1892 if (mono_class_needs_cctor_run (method->klass, NULL)) {
1893 MonoVTable *vtable;
1894 ERROR_DECL (error);
1895 if (!m_class_get_runtime_info (method->klass))
1896 /* No vtable created yet */
1897 return FALSE;
1898 vtable = mono_class_vtable_checked (td->rtm->domain, method->klass, error);
1899 if (!is_ok (error)) {
1900 mono_error_cleanup (error);
1901 return FALSE;
1903 if (!vtable->initialized)
1904 return FALSE;
1907 /* We currently access at runtime the wrapper data */
1908 if (method->wrapper_type != MONO_WRAPPER_NONE)
1909 return FALSE;
1911 // FIXME Re-enable this
1912 if (mono_class_get_magic_index (method->klass) >= 0)
1913 return FALSE;
1915 return TRUE;
1918 static gboolean
1919 interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHeader *header, MonoError *error)
1921 const unsigned char *prev_ip, *prev_il_code, *prev_in_start;
1922 int *prev_in_offsets;
1923 gboolean ret;
1924 unsigned int prev_max_stack_height, prev_max_vt_sp, prev_locals_size;
1925 int prev_n_data_items;
1926 int i, prev_vt_sp;
1927 int prev_sp_offset;
1928 MonoGenericContext *generic_context = NULL;
1929 StackInfo *prev_param_area;
1930 MonoMethod *prev_inlined_method;
1931 MonoMethodSignature *csignature = mono_method_signature_internal (target_method);
1932 int nargs = csignature->param_count + !!csignature->hasthis;
1933 InterpInst *prev_last_ins;
1935 if (csignature->is_inflated)
1936 generic_context = mono_method_get_context (target_method);
1937 else {
1938 MonoGenericContainer *generic_container = mono_method_get_generic_container (target_method);
1939 if (generic_container)
1940 generic_context = &generic_container->context;
1943 prev_ip = td->ip;
1944 prev_il_code = td->il_code;
1945 prev_in_start = td->in_start;
1946 prev_sp_offset = td->sp - td->stack;
1947 prev_vt_sp = td->vt_sp;
1948 prev_inlined_method = td->inlined_method;
1949 prev_last_ins = td->last_ins;
1950 td->inlined_method = target_method;
1952 prev_max_stack_height = td->max_stack_height;
1953 prev_max_vt_sp = td->max_vt_sp;
1954 prev_locals_size = td->locals_size;
1956 prev_n_data_items = td->n_data_items;
1957 prev_in_offsets = td->in_offsets;
1958 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
1960 /* Inlining pops the arguments, restore the stack */
1961 prev_param_area = (StackInfo*)g_malloc (nargs * sizeof (StackInfo));
1962 memcpy (prev_param_area, &td->sp [-nargs], nargs * sizeof (StackInfo));
1964 int const prev_code_size = td->code_size;
1965 td->code_size = header->code_size;
1967 if (td->verbose_level)
1968 g_print ("Inline start method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1969 ret = generate_code (td, target_method, header, generic_context, error);
1971 if (!ret) {
1972 if (td->verbose_level)
1973 g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1974 td->max_stack_height = prev_max_stack_height;
1975 td->max_vt_sp = prev_max_vt_sp;
1976 td->locals_size = prev_locals_size;
1979 /* Remove any newly added items */
1980 for (i = prev_n_data_items; i < td->n_data_items; i++) {
1981 g_hash_table_remove (td->data_hash, td->data_items [i]);
1983 td->n_data_items = prev_n_data_items;
1984 td->sp = td->stack + prev_sp_offset;
1985 memcpy (&td->sp [-nargs], prev_param_area, nargs * sizeof (StackInfo));
1986 td->vt_sp = prev_vt_sp;
1987 td->last_ins = prev_last_ins;
1988 if (td->last_ins)
1989 td->last_ins->next = NULL;
1990 UnlockedIncrement (&mono_interp_stats.inline_failures);
1991 } else {
1992 if (td->verbose_level)
1993 g_print ("Inline end method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
1994 UnlockedIncrement (&mono_interp_stats.inlined_methods);
1995 // Make sure we have an IR instruction associated with the now removed IL CALL
1996 // FIXME This could be prettier. We might be able to make inlining saner now that
1997 // that we can easily tweak the instruction list.
1998 if (!prev_inlined_method) {
1999 if (prev_last_ins) {
2000 if (prev_last_ins->next)
2001 prev_last_ins->next->il_offset = prev_in_start - prev_il_code;
2002 } else if (td->first_ins) {
2003 td->first_ins->il_offset = prev_in_start - prev_il_code;
2008 td->ip = prev_ip;
2009 td->in_start = prev_in_start;
2010 td->il_code = prev_il_code;
2011 td->inlined_method = prev_inlined_method;
2012 td->code_size = prev_code_size;
2014 g_free (td->in_offsets);
2015 td->in_offsets = prev_in_offsets;
2017 g_free (prev_param_area);
2018 return ret;
2021 static void
2022 interp_constrained_box (TransformData *td, MonoDomain *domain, MonoClass *constrained_class, MonoMethodSignature *csignature, MonoError *error)
2024 int mt = mint_type (m_class_get_byval_arg (constrained_class));
2025 if (mono_class_is_nullable (constrained_class)) {
2026 g_assert (mt == MINT_TYPE_VT);
2027 interp_add_ins (td, MINT_BOX_NULLABLE);
2028 td->last_ins->data [0] = get_data_item_index (td, constrained_class);
2029 td->last_ins->data [1] = csignature->param_count;
2030 td->last_ins->data [2] = (td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : 1;
2031 } else {
2032 MonoVTable *vtable = mono_class_vtable_checked (domain, constrained_class, error);
2033 return_if_nok (error);
2035 if (mt == MINT_TYPE_VT) {
2036 interp_add_ins (td, MINT_BOX_VT);
2037 td->last_ins->data [0] = get_data_item_index (td, vtable);
2038 td->last_ins->data [1] = csignature->param_count;
2039 td->last_ins->data [2] = (td->sp - 1 - csignature->param_count)->type != STACK_TYPE_MP ? 0 : 1;
2040 } else {
2041 interp_add_ins (td, MINT_BOX);
2042 td->last_ins->data [0] = get_data_item_index (td, vtable);
2043 td->last_ins->data [1] = csignature->param_count;
2048 static MonoMethod*
2049 interp_get_method (MonoMethod *method, guint32 token, MonoImage *image, MonoGenericContext *generic_context, MonoError *error)
2051 if (method->wrapper_type == MONO_WRAPPER_NONE)
2052 return mono_get_method_checked (image, token, NULL, generic_context, error);
2053 else
2054 return (MonoMethod *)mono_method_get_wrapper_data (method, token);
2057 /* Return FALSE if error, including inline failure */
2058 static gboolean
2059 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)
2061 MonoImage *image = m_class_get_image (method->klass);
2062 MonoMethodSignature *csignature;
2063 int is_virtual = *td->ip == CEE_CALLVIRT;
2064 int calli = *td->ip == CEE_CALLI || *td->ip == CEE_MONO_CALLI_EXTRA_ARG;
2065 int i;
2066 guint32 vt_stack_used = 0;
2067 guint32 vt_res_size = 0;
2068 int op = -1;
2069 int native = 0;
2070 int is_void = 0;
2071 int need_null_check = is_virtual;
2073 guint32 token = read32 (td->ip + 1);
2075 if (target_method == NULL) {
2076 if (calli) {
2077 CHECK_STACK(td, 1);
2078 if (method->wrapper_type != MONO_WRAPPER_NONE)
2079 csignature = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
2080 else {
2081 csignature = mono_metadata_parse_signature_checked (image, token, error);
2082 return_val_if_nok (error, FALSE);
2085 if (generic_context) {
2086 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
2087 return_val_if_nok (error, FALSE);
2091 * The compiled interp entry wrapper is passed to runtime_invoke instead of
2092 * the InterpMethod pointer. FIXME
2094 native = csignature->pinvoke || method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE;
2096 target_method = NULL;
2097 } else {
2098 target_method = interp_get_method (method, token, image, generic_context, error);
2099 return_val_if_nok (error, FALSE);
2100 csignature = mono_method_signature_internal (target_method);
2102 if (generic_context) {
2103 csignature = mono_inflate_generic_signature (csignature, generic_context, error);
2104 return_val_if_nok (error, FALSE);
2105 target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, error);
2106 return_val_if_nok (error, FALSE);
2109 } else {
2110 csignature = mono_method_signature_internal (target_method);
2113 if (check_visibility && target_method && !mono_method_can_access_method (method, target_method))
2114 interp_generate_mae_throw (td, method, target_method);
2116 if (target_method && target_method->string_ctor) {
2117 /* Create the real signature */
2118 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (td->mempool, csignature);
2119 ctor_sig->ret = m_class_get_byval_arg (mono_defaults.string_class);
2121 csignature = ctor_sig;
2124 /* Intrinsics */
2125 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
2126 return TRUE;
2128 if (constrained_class) {
2129 if (m_class_is_enumtype (constrained_class) && !strcmp (target_method->name, "GetHashCode")) {
2130 /* Use the corresponding method from the base type to avoid boxing */
2131 MonoType *base_type = mono_class_enum_basetype_internal (constrained_class);
2132 g_assert (base_type);
2133 constrained_class = mono_class_from_mono_type_internal (base_type);
2134 target_method = mono_class_get_method_from_name_checked (constrained_class, target_method->name, 0, 0, error);
2135 mono_error_assert_ok (error);
2136 g_assert (target_method);
2140 if (constrained_class) {
2141 mono_class_setup_vtable (constrained_class);
2142 if (mono_class_has_failure (constrained_class)) {
2143 mono_error_set_for_class_failure (error, constrained_class);
2144 return FALSE;
2146 #if DEBUG_INTERP
2147 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);
2148 #endif
2149 target_method = mono_get_method_constrained_with_method (image, target_method, constrained_class, generic_context, error);
2150 #if DEBUG_INTERP
2151 g_print (" : %s::%s. %s (%p)\n", target_method->klass->name, target_method->name, mono_signature_full_name (target_method->signature), target_method);
2152 #endif
2153 /* Intrinsics: Try again, it could be that `mono_get_method_constrained_with_method` resolves to a method that we can substitute */
2154 if (target_method && interp_handle_intrinsics (td, target_method, constrained_class, csignature, readonly, &op))
2155 return TRUE;
2157 return_val_if_nok (error, FALSE);
2158 mono_class_setup_vtable (target_method->klass);
2160 if (!m_class_is_valuetype (constrained_class)) {
2161 /* managed pointer on the stack, we need to deref that puppy */
2162 interp_add_ins (td, MINT_LDIND_I);
2163 td->last_ins->data [0] = csignature->param_count;
2164 } 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) {
2166 * Constrained expects a managed pointer that normally needs dereferencing.
2167 * For value types that have their storage on the vtstack, a managed pointer
2168 * to it is identical to the internal pointer that is passed on the stack
2169 * when using the value type, not needing any dereferencing.
2171 g_assert ((td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP);
2172 if (mint_type (m_class_get_byval_arg (constrained_class)) != MINT_TYPE_VT) {
2173 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2174 interp_add_ins (td, MINT_LDIND_I8);
2175 td->last_ins->data [0] = csignature->param_count;
2178 interp_constrained_box (td, domain, constrained_class, csignature, error);
2179 return_val_if_nok (error, FALSE);
2180 } else {
2181 if (target_method->klass != constrained_class) {
2183 * The type parameter is instantiated as a valuetype,
2184 * but that type doesn't override the method we're
2185 * calling, so we need to box `this'.
2187 g_assert ((td->sp - csignature->param_count - 1)->type == STACK_TYPE_MP);
2188 if (mint_type (m_class_get_byval_arg (constrained_class)) != MINT_TYPE_VT) {
2189 /* managed pointer on the stack, we need to deref that puppy */
2190 /* Always load the entire stackval, to handle also the case where the enum has long storage */
2191 interp_add_ins (td, MINT_LDIND_I8);
2192 td->last_ins->data [0] = csignature->param_count;
2195 interp_constrained_box (td, domain, constrained_class, csignature, error);
2196 return_val_if_nok (error, FALSE);
2198 is_virtual = FALSE;
2202 if (target_method)
2203 mono_class_init_internal (target_method->klass);
2205 if (!is_virtual && target_method && (target_method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
2206 if (!mono_class_is_interface (method->klass))
2207 interp_generate_bie_throw (td);
2208 else
2209 is_virtual = TRUE;
2212 if (is_virtual && target_method && (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2213 (MONO_METHOD_IS_FINAL (target_method) &&
2214 target_method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2215 !(mono_class_is_marshalbyref (target_method->klass))) {
2216 /* Not really virtual, just needs a null check */
2217 is_virtual = FALSE;
2218 need_null_check = TRUE;
2221 CHECK_STACK (td, csignature->param_count + csignature->hasthis);
2222 if (!td->gen_sdb_seq_points && !calli && op == -1 && (!is_virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) &&
2223 (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 &&
2224 (target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0 &&
2225 !(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING)) {
2226 (void)mono_class_vtable_checked (domain, target_method->klass, error);
2227 return_val_if_nok (error, FALSE);
2229 if (method == target_method && *(td->ip + 5) == CEE_RET && !(csignature->hasthis && m_class_is_valuetype (target_method->klass))) {
2230 if (td->inlined_method)
2231 return FALSE;
2233 if (td->verbose_level)
2234 g_print ("Optimize tail call of %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
2236 for (i = csignature->param_count - 1 + !!csignature->hasthis; i >= 0; --i)
2237 store_arg (td, i);
2239 interp_add_ins (td, MINT_BR_S);
2240 // We are branching to the beginning of the method
2241 td->last_ins->data [0] = 0;
2242 if (!is_bb_start [td->ip + 5 - td->il_code])
2243 ++td->ip; /* gobble the CEE_RET if it isn't branched to */
2244 td->ip += 5;
2245 return TRUE;
2249 target_method = interp_transform_internal_calls (method, target_method, csignature, is_virtual);
2251 if (csignature->call_convention == MONO_CALL_VARARG) {
2252 csignature = mono_method_get_signature_checked (target_method, image, token, generic_context, error);
2253 int vararg_stack = 0;
2255 * For vararg calls, ArgIterator expects the signature and the varargs to be
2256 * stored in a linear memory. We allocate the necessary vt_stack space for
2257 * this. All varargs will be pushed to the vt_stack at call site.
2259 vararg_stack += sizeof (gpointer);
2260 for (i = csignature->sentinelpos; i < csignature->param_count; ++i) {
2261 int align, arg_size;
2262 arg_size = mono_type_stack_size (csignature->params [i], &align);
2263 vararg_stack = ALIGN_TO (vararg_stack, align);
2264 vararg_stack += arg_size;
2266 /* allocate space for the pointer to varargs space start */
2267 vararg_stack += sizeof (gpointer);
2268 vt_stack_used += ALIGN_TO (vararg_stack, MINT_VT_ALIGNMENT);
2269 PUSH_VT (td, vararg_stack);
2272 if (need_null_check) {
2273 interp_add_ins (td, MINT_CKNULL_N);
2274 td->last_ins->data [0] = csignature->param_count + 1;
2277 g_assert (csignature->call_convention != MONO_CALL_FASTCALL);
2278 if ((mono_interp_opt & INTERP_OPT_INLINE) && op == -1 && !is_virtual && target_method && interp_method_check_inlining (td, target_method)) {
2279 MonoMethodHeader *mheader = interp_method_get_header (target_method, error);
2280 return_val_if_nok (error, FALSE);
2282 if (interp_inline_method (td, target_method, mheader, error)) {
2283 td->ip += 5;
2284 return TRUE;
2288 /* Don't inline methods that do calls */
2289 if (op == -1 && td->inlined_method)
2290 return FALSE;
2292 /* We need to convert delegate invoke to a indirect call on the interp_invoke_impl field */
2293 if (target_method && m_class_get_parent (target_method->klass) == mono_defaults.multicastdelegate_class) {
2294 const char *name = target_method->name;
2295 if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
2296 calli = TRUE;
2297 interp_add_ins (td, MINT_LD_DELEGATE_INVOKE_IMPL);
2298 td->last_ins->data [0] = csignature->param_count + 1;
2299 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
2303 /* Pop the function pointer */
2304 if (calli)
2305 --td->sp;
2307 td->sp -= csignature->param_count + !!csignature->hasthis;
2308 for (i = 0; i < csignature->param_count; ++i) {
2309 if (td->sp [i + !!csignature->hasthis].type == STACK_TYPE_VT) {
2310 gint32 size;
2311 MonoClass *klass = mono_class_from_mono_type_internal (csignature->params [i]);
2312 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2313 size = mono_class_native_size (klass, NULL);
2314 else
2315 size = mono_class_value_size (klass, NULL);
2316 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
2317 vt_stack_used += size;
2321 /* need to handle typedbyref ... */
2322 if (csignature->ret->type != MONO_TYPE_VOID) {
2323 int mt = mint_type(csignature->ret);
2324 MonoClass *klass = mono_class_from_mono_type_internal (csignature->ret);
2325 if (mt == MINT_TYPE_VT) {
2326 if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
2327 vt_res_size = mono_class_native_size (klass, NULL);
2328 else
2329 vt_res_size = mono_class_value_size (klass, NULL);
2330 if (mono_class_has_failure (klass)) {
2331 mono_error_set_for_class_failure (error, klass);
2332 return FALSE;
2334 PUSH_VT(td, vt_res_size);
2336 PUSH_TYPE(td, stack_type[mt], klass);
2337 } else
2338 is_void = TRUE;
2340 if (op >= 0) {
2341 interp_add_ins (td, op);
2343 if (op == MINT_LDLEN) {
2344 #ifdef MONO_BIG_ARRAYS
2345 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
2346 #else
2347 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
2348 #endif
2351 #ifndef ENABLE_NETCORE
2352 if (op == MINT_CALLRUN) {
2353 td->last_ins->data [0] = get_data_item_index (td, target_method);
2354 td->last_ins->data [1] = get_data_item_index (td, mono_method_signature_internal (target_method));
2356 #endif
2357 } else if (!calli && !is_virtual && mono_interp_jit_call_supported (target_method, csignature)) {
2358 interp_add_ins (td, MINT_JIT_CALL);
2359 td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
2360 mono_error_assert_ok (error);
2361 } else {
2362 #ifndef MONO_ARCH_HAS_NO_PROPER_MONOCTX
2363 /* Try using fast icall path for simple signatures */
2364 if (native && !method->dynamic)
2365 op = interp_icall_op_for_sig (csignature);
2366 #endif
2367 if (csignature->call_convention == MONO_CALL_VARARG)
2368 interp_add_ins (td, MINT_CALL_VARARG);
2369 else if (calli)
2370 interp_add_ins (td, native ? ((op != -1) ? MINT_CALLI_NAT_FAST : MINT_CALLI_NAT) : MINT_CALLI);
2371 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass))
2372 interp_add_ins (td, is_void ? MINT_VCALLVIRT_FAST : MINT_CALLVIRT_FAST);
2373 else if (is_virtual)
2374 interp_add_ins (td, is_void ? MINT_VCALLVIRT : MINT_CALLVIRT);
2375 else
2376 interp_add_ins (td, is_void ? MINT_VCALL : MINT_CALL);
2378 if (calli) {
2379 td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
2380 if (op != -1) {
2381 td->last_ins->data[1] = op;
2382 if (td->last_ins->opcode == MINT_CALLI_NAT_FAST)
2383 td->last_ins->data[2] = save_last_error;
2384 } else if (op == -1 && td->last_ins->opcode == MINT_CALLI_NAT) {
2385 td->last_ins->data[1] = save_last_error;
2387 } else {
2388 InterpMethod *imethod = mono_interp_get_imethod (domain, target_method, error);
2389 td->last_ins->data [0] = get_data_item_index (td, (void *)imethod);
2390 td->last_ins->data [1] = imethod->param_count + imethod->hasthis;
2391 #ifdef ENABLE_EXPERIMENT_TIERED
2392 if (MINT_IS_PATCHABLE_CALL (td->last_ins->opcode)) {
2393 g_assert (!calli && !is_virtual);
2394 td->last_ins->flags |= INTERP_INST_FLAG_RECORD_CALL_PATCH;
2395 g_hash_table_insert (td->patchsite_hash, td->last_ins, target_method);
2397 #endif
2398 return_val_if_nok (error, FALSE);
2399 if (csignature->call_convention == MONO_CALL_VARARG)
2400 td->last_ins->data [1] = get_data_item_index (td, (void *)csignature);
2401 else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass)) {
2402 /* FIXME Use fastpath also for MBRO. Asserts in mono_method_get_vtable_slot */
2403 if (mono_class_is_interface (target_method->klass))
2404 td->last_ins->data [1] = -2 * MONO_IMT_SIZE + mono_method_get_imt_slot (target_method);
2405 else
2406 td->last_ins->data [1] = mono_method_get_vtable_slot (target_method);
2410 td->ip += 5;
2411 if (vt_stack_used != 0 || vt_res_size != 0) {
2412 interp_add_ins (td, MINT_VTRESULT);
2413 td->last_ins->data [0] = vt_res_size;
2414 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
2415 td->vt_sp -= vt_stack_used;
2418 return TRUE;
2421 static MonoClassField *
2422 interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, MonoGenericContext *generic_context, MonoError *error)
2424 MonoClassField *field = NULL;
2425 if (method->wrapper_type != MONO_WRAPPER_NONE) {
2426 field = (MonoClassField *) mono_method_get_wrapper_data (method, token);
2427 *klass = field->parent;
2429 mono_class_setup_fields (field->parent);
2430 } else {
2431 field = mono_field_from_token_checked (m_class_get_image (method->klass), token, klass, generic_context, error);
2432 return_val_if_nok (error, NULL);
2435 if (!method->skip_visibility && !mono_method_can_access_field (method, field)) {
2436 char *method_fname = mono_method_full_name (method, TRUE);
2437 char *field_fname = mono_field_full_name (field);
2438 mono_error_set_generic_error (error, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
2439 g_free (method_fname);
2440 g_free (field_fname);
2441 return NULL;
2444 return field;
2447 static InterpBasicBlock*
2448 get_bb (TransformData *td, InterpBasicBlock *cbb, unsigned char *ip)
2450 int offset = ip - td->il_code;
2451 InterpBasicBlock *bb = td->offset_to_bb [offset];
2453 if (!bb) {
2454 bb = (InterpBasicBlock*)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock));
2455 bb->ip = ip;
2456 td->offset_to_bb [offset] = bb;
2458 td->basic_blocks = g_list_append_mempool (td->mempool, td->basic_blocks, bb);
2461 if (cbb)
2462 bb->preds = g_slist_prepend_mempool (td->mempool, bb->preds, cbb);
2463 return bb;
2467 * get_basic_blocks:
2469 * Compute the set of IL level basic blocks.
2471 static void
2472 get_basic_blocks (TransformData *td)
2474 guint8 *start = (guint8*)td->il_code;
2475 guint8 *end = (guint8*)td->il_code + td->code_size;
2476 guint8 *ip = start;
2477 unsigned char *target;
2478 int i;
2479 guint cli_addr;
2480 const MonoOpcode *opcode;
2481 InterpBasicBlock *cbb;
2483 td->offset_to_bb = (InterpBasicBlock**)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * (end - start + 1));
2484 td->entry_bb = cbb = get_bb (td, NULL, start);
2486 while (ip < end) {
2487 cli_addr = ip - start;
2488 td->offset_to_bb [cli_addr] = cbb;
2489 i = mono_opcode_value ((const guint8 **)&ip, end);
2490 opcode = &mono_opcodes [i];
2491 switch (opcode->argument) {
2492 case MonoInlineNone:
2493 ip++;
2494 break;
2495 case MonoInlineString:
2496 case MonoInlineType:
2497 case MonoInlineField:
2498 case MonoInlineMethod:
2499 case MonoInlineTok:
2500 case MonoInlineSig:
2501 case MonoShortInlineR:
2502 case MonoInlineI:
2503 ip += 5;
2504 break;
2505 case MonoInlineVar:
2506 ip += 3;
2507 break;
2508 case MonoShortInlineVar:
2509 case MonoShortInlineI:
2510 ip += 2;
2511 break;
2512 case MonoShortInlineBrTarget:
2513 target = start + cli_addr + 2 + (signed char)ip [1];
2514 get_bb (td, cbb, target);
2515 ip += 2;
2516 cbb = get_bb (td, cbb, ip);
2517 break;
2518 case MonoInlineBrTarget:
2519 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
2520 get_bb (td, cbb, target);
2521 ip += 5;
2522 cbb = get_bb (td, cbb, ip);
2523 break;
2524 case MonoInlineSwitch: {
2525 guint32 n = read32 (ip + 1);
2526 guint32 j;
2527 ip += 5;
2528 cli_addr += 5 + 4 * n;
2529 target = start + cli_addr;
2530 get_bb (td, cbb, target);
2532 for (j = 0; j < n; ++j) {
2533 target = start + cli_addr + (gint32)read32 (ip);
2534 get_bb (td, cbb, target);
2535 ip += 4;
2537 cbb = get_bb (td, cbb, ip);
2538 break;
2540 case MonoInlineR:
2541 case MonoInlineI8:
2542 ip += 9;
2543 break;
2544 default:
2545 g_assert_not_reached ();
2548 if (i == CEE_THROW)
2549 cbb = get_bb (td, NULL, ip);
2553 static void
2554 interp_save_debug_info (InterpMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers)
2556 MonoDebugMethodJitInfo *dinfo;
2557 int i;
2559 if (!mono_debug_enabled ())
2560 return;
2563 * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
2566 dinfo = g_new0 (MonoDebugMethodJitInfo, 1);
2567 dinfo->num_params = rtm->param_count;
2568 dinfo->params = g_new0 (MonoDebugVarInfo, dinfo->num_params);
2569 dinfo->num_locals = header->num_locals;
2570 dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals);
2571 dinfo->code_start = (guint8*)rtm->code;
2572 dinfo->code_size = td->new_code_end - td->new_code;
2573 dinfo->epilogue_begin = 0;
2574 dinfo->has_var_info = TRUE;
2575 dinfo->num_line_numbers = line_numbers->len;
2576 dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers);
2578 for (i = 0; i < dinfo->num_params; i++) {
2579 MonoDebugVarInfo *var = &dinfo->params [i];
2580 var->type = rtm->param_types [i];
2582 for (i = 0; i < dinfo->num_locals; i++) {
2583 MonoDebugVarInfo *var = &dinfo->locals [i];
2584 var->type = mono_metadata_type_dup (NULL, header->locals [i]);
2587 for (i = 0; i < dinfo->num_line_numbers; i++)
2588 dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i);
2589 mono_debug_add_method (rtm->method, dinfo, rtm->domain);
2591 mono_debug_free_method_jit_info (dinfo);
2594 /* Same as the code in seq-points.c */
2595 static void
2596 insert_pred_seq_point (SeqPoint *last_sp, SeqPoint *sp, GSList **next)
2598 GSList *l;
2599 int src_index = last_sp->next_offset;
2600 int dst_index = sp->next_offset;
2602 /* bb->in_bb might contain duplicates */
2603 for (l = next [src_index]; l; l = l->next)
2604 if (GPOINTER_TO_UINT (l->data) == dst_index)
2605 break;
2606 if (!l)
2607 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
2610 static void
2611 recursively_make_pred_seq_points (TransformData *td, InterpBasicBlock *bb)
2613 SeqPoint ** const MONO_SEQ_SEEN_LOOP = (SeqPoint**)GINT_TO_POINTER(-1);
2614 GSList *l;
2616 GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
2617 GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
2619 // Insert/remove sentinel into the memoize table to detect loops containing bb
2620 bb->pred_seq_points = MONO_SEQ_SEEN_LOOP;
2622 for (l = bb->preds; l; l = l->next) {
2623 InterpBasicBlock *in_bb = (InterpBasicBlock*)l->data;
2625 // This bb has the last seq point, append it and continue
2626 if (in_bb->last_seq_point != NULL) {
2627 predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
2628 continue;
2631 // We've looped or handled this before, exit early.
2632 // No last sequence points to find.
2633 if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
2634 continue;
2636 // Take sequence points from incoming basic blocks
2638 if (in_bb == td->entry_bb)
2639 continue;
2641 if (in_bb->pred_seq_points == NULL)
2642 recursively_make_pred_seq_points (td, in_bb);
2644 // Union sequence points with incoming bb's
2645 for (int i=0; i < in_bb->num_pred_seq_points; i++) {
2646 if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
2647 g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
2648 g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
2651 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
2654 g_hash_table_destroy (seen);
2656 if (predecessors->len != 0) {
2657 bb->pred_seq_points = (SeqPoint**)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint *) * predecessors->len);
2658 bb->num_pred_seq_points = predecessors->len;
2660 for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
2661 bb->pred_seq_points [newer] = (SeqPoint*)g_array_index (predecessors, gpointer, newer);
2665 g_array_free (predecessors, TRUE);
2668 static void
2669 collect_pred_seq_points (TransformData *td, InterpBasicBlock *bb, SeqPoint *seqp, GSList **next)
2671 // Doesn't have a last sequence point, must find from incoming basic blocks
2672 if (bb->pred_seq_points == NULL && bb != td->entry_bb)
2673 recursively_make_pred_seq_points (td, bb);
2675 for (int i = 0; i < bb->num_pred_seq_points; i++)
2676 insert_pred_seq_point (bb->pred_seq_points [i], seqp, next);
2678 return;
2681 static void
2682 save_seq_points (TransformData *td, MonoJitInfo *jinfo)
2684 GByteArray *array;
2685 int i, seq_info_size;
2686 MonoSeqPointInfo *info;
2687 GSList **next = NULL;
2688 GList *bblist;
2690 if (!td->gen_sdb_seq_points)
2691 return;
2694 * For each sequence point, compute the list of sequence points immediately
2695 * following it, this is needed to implement 'step over' in the debugger agent.
2696 * Similar to the code in mono_save_seq_point_info ().
2698 for (i = 0; i < td->seq_points->len; ++i) {
2699 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2701 /* Store the seq point index here temporarily */
2702 sp->next_offset = i;
2704 next = (GSList**)mono_mempool_alloc0 (td->mempool, sizeof (GList*) * td->seq_points->len);
2705 for (bblist = td->basic_blocks; bblist; bblist = bblist->next) {
2706 InterpBasicBlock *bb = (InterpBasicBlock*)bblist->data;
2708 GSList *bb_seq_points = g_slist_reverse (bb->seq_points);
2709 SeqPoint *last = NULL;
2710 for (GSList *l = bb_seq_points; l; l = l->next) {
2711 SeqPoint *sp = (SeqPoint*)l->data;
2713 if (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET)
2714 /* Used to implement method entry/exit events */
2715 continue;
2717 if (last != NULL) {
2718 /* Link with the previous seq point in the same bb */
2719 next [last->next_offset] = g_slist_append_mempool (td->mempool, next [last->next_offset], GINT_TO_POINTER (sp->next_offset));
2720 } else {
2721 /* Link with the last bb in the previous bblocks */
2722 collect_pred_seq_points (td, bb, sp, next);
2724 last = sp;
2728 /* Serialize the seq points into a byte array */
2729 array = g_byte_array_new ();
2730 SeqPoint zero_seq_point = {0};
2731 SeqPoint* last_seq_point = &zero_seq_point;
2732 for (i = 0; i < td->seq_points->len; ++i) {
2733 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2735 sp->next_offset = 0;
2736 if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next [i], TRUE))
2737 last_seq_point = sp;
2740 if (td->verbose_level) {
2741 g_print ("\nSEQ POINT MAP FOR %s: \n", td->method->name);
2743 for (i = 0; i < td->seq_points->len; ++i) {
2744 SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
2745 GSList *l;
2747 if (!next [i])
2748 continue;
2750 g_print ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
2751 for (l = next [i]; l; l = l->next) {
2752 int next_index = GPOINTER_TO_UINT (l->data);
2753 g_print (" IL0x%x", ((SeqPoint*)g_ptr_array_index (td->seq_points, next_index))->il_offset);
2755 g_print ("\n");
2759 info = mono_seq_point_info_new (array->len, TRUE, array->data, TRUE, &seq_info_size);
2760 mono_atomic_fetch_add_i32 (&mono_jit_stats.allocated_seq_points_size, seq_info_size);
2762 g_byte_array_free (array, TRUE);
2764 jinfo->seq_points = info;
2767 static void
2768 interp_emit_memory_barrier (TransformData *td, int kind)
2770 #if defined(TARGET_WASM)
2771 // mono_memory_barrier is dummy on wasm
2772 #elif defined(TARGET_X86) || defined(TARGET_AMD64)
2773 if (kind == MONO_MEMORY_BARRIER_SEQ)
2774 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
2775 #else
2776 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
2777 #endif
2780 #define BARRIER_IF_VOLATILE(td, kind) \
2781 do { \
2782 if (volatile_) { \
2783 interp_emit_memory_barrier (td, kind); \
2784 volatile_ = FALSE; \
2786 } while (0)
2788 #define INLINE_FAILURE \
2789 do { \
2790 if (inlining) \
2791 goto exit; \
2792 } while (0)
2794 static void
2795 interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMethodSignature *signature, MonoMethodHeader *header)
2797 int i, offset, size, align;
2799 imethod->local_offsets = (guint32*)g_malloc (header->num_locals * sizeof(guint32));
2800 td->locals = (InterpLocal*)g_malloc (header->num_locals * sizeof (InterpLocal));
2801 td->locals_size = header->num_locals;
2802 td->locals_capacity = td->locals_size;
2803 offset = 0;
2804 for (i = 0; i < header->num_locals; ++i) {
2805 size = mono_type_size (header->locals [i], &align);
2806 offset += align - 1;
2807 offset &= ~(align - 1);
2808 imethod->local_offsets [i] = offset;
2809 td->locals [i].offset = offset;
2810 td->locals [i].flags = 0;
2811 td->locals [i].type = header->locals [i];
2812 td->locals [i].mt = mint_type (header->locals [i]);
2813 offset += size;
2815 offset = (offset + 7) & ~7;
2817 imethod->exvar_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32));
2818 for (i = 0; i < header->num_clauses; i++) {
2819 imethod->exvar_offsets [i] = offset;
2820 offset += sizeof (MonoObject*);
2822 offset = (offset + 7) & ~7;
2824 imethod->locals_size = offset;
2825 g_assert (imethod->locals_size < 65536);
2826 td->total_locals_size = offset;
2829 void
2830 mono_test_interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMethodSignature *signature, MonoMethodHeader *header)
2832 interp_method_compute_offsets (td, imethod, signature, header);
2835 /* Return false is failure to init basic blocks due to being in inline method */
2836 static gboolean
2837 init_bb_start (TransformData *td, MonoMethodHeader *header, gboolean inlining)
2839 const unsigned char *ip, *end;
2840 const MonoOpcode *opcode;
2841 int offset, i, in, backwards;
2843 /* intern the strings in the method. */
2844 ip = header->code;
2845 end = ip + header->code_size;
2847 /* inlined method continues the basic block of parent method */
2848 if (!inlining)
2849 td->is_bb_start [0] = 1;
2850 while (ip < end) {
2851 in = *ip;
2852 if (in == 0xfe) {
2853 ip++;
2854 in = *ip + 256;
2856 else if (in == 0xf0) {
2857 ip++;
2858 in = *ip + MONO_CEE_MONO_ICALL;
2860 opcode = &mono_opcodes [in];
2861 switch (opcode->argument) {
2862 case MonoInlineNone:
2863 ++ip;
2864 break;
2865 case MonoInlineString:
2866 ip += 5;
2867 break;
2868 case MonoInlineType:
2869 ip += 5;
2870 break;
2871 case MonoInlineMethod:
2872 ip += 5;
2873 break;
2874 case MonoInlineField:
2875 case MonoInlineSig:
2876 case MonoInlineI:
2877 case MonoInlineTok:
2878 case MonoShortInlineR:
2879 ip += 5;
2880 break;
2881 case MonoInlineBrTarget:
2882 offset = read32 (ip + 1);
2883 ip += 5;
2884 /* this branch is ignored */
2885 if (offset == 0 && in == MONO_CEE_BR)
2886 break;
2887 backwards = offset < 0;
2888 offset += ip - header->code;
2889 g_assert (offset >= 0 && offset < header->code_size);
2890 if (inlining)
2891 return FALSE;
2892 td->is_bb_start [offset] |= backwards ? 2 : 1;
2893 break;
2894 case MonoShortInlineBrTarget:
2895 offset = ((gint8 *)ip) [1];
2896 ip += 2;
2897 /* this branch is ignored */
2898 if (offset == 0 && in == MONO_CEE_BR_S)
2899 break;
2900 backwards = offset < 0;
2901 offset += ip - header->code;
2902 g_assert (offset >= 0 && offset < header->code_size);
2903 if (inlining)
2904 return FALSE;
2905 td->is_bb_start [offset] |= backwards ? 2 : 1;
2906 break;
2907 case MonoInlineVar:
2908 ip += 3;
2909 break;
2910 case MonoShortInlineVar:
2911 case MonoShortInlineI:
2912 ip += 2;
2913 break;
2914 case MonoInlineSwitch: {
2915 guint32 n;
2916 const unsigned char *next_ip;
2917 ++ip;
2918 n = read32 (ip);
2919 ip += 4;
2920 next_ip = ip + 4 * n;
2921 for (i = 0; i < n; i++) {
2922 offset = read32 (ip);
2923 backwards = offset < 0;
2924 offset += next_ip - header->code;
2925 g_assert (offset >= 0 && offset < header->code_size);
2926 if (inlining)
2927 return FALSE;
2928 td->is_bb_start [offset] |= backwards ? 2 : 1;
2929 ip += 4;
2931 break;
2933 case MonoInlineR:
2934 case MonoInlineI8:
2935 ip += 9;
2936 break;
2937 default:
2938 g_assert_not_reached ();
2941 return TRUE;
2944 #ifdef NO_UNALIGNED_ACCESS
2945 static int
2946 get_unaligned_opcode (int opcode)
2948 switch (opcode) {
2949 case MINT_LDFLD_I8:
2950 return MINT_LDFLD_I8_UNALIGNED;
2951 case MINT_LDFLD_R8:
2952 return MINT_LDFLD_R8_UNALIGNED;
2953 case MINT_STFLD_I8:
2954 return MINT_STFLD_I8_UNALIGNED;
2955 case MINT_STFLD_R8:
2956 return MINT_STFLD_R8_UNALIGNED;
2957 default:
2958 g_assert_not_reached ();
2960 return -1;
2962 #endif
2964 static void
2965 interp_handle_isinst (TransformData *td, MonoClass *klass, gboolean isinst_instr)
2967 /* Follow the logic from jit's handle_isinst */
2968 if (!mono_class_has_variant_generic_params (klass)) {
2969 if (mono_class_is_interface (klass))
2970 interp_add_ins (td, isinst_instr ? MINT_ISINST_INTERFACE : MINT_CASTCLASS_INTERFACE);
2971 else if (!mono_class_is_marshalbyref (klass) && m_class_get_rank (klass) == 0 && !mono_class_is_nullable (klass))
2972 interp_add_ins (td, isinst_instr ? MINT_ISINST_COMMON : MINT_CASTCLASS_COMMON);
2973 else
2974 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2975 } else {
2976 interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS);
2978 td->last_ins->data [0] = get_data_item_index (td, klass);
2979 td->ip += 5;
2982 static void
2983 interp_emit_ldsflda (TransformData *td, MonoClassField *field, MonoError *error)
2985 MonoDomain *domain = td->rtm->domain;
2986 // Initialize the offset for the field
2987 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
2988 return_if_nok (error);
2990 if (mono_class_field_is_special_static (field)) {
2991 guint32 offset;
2993 mono_domain_lock (domain);
2994 g_assert (domain->special_static_fields);
2995 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
2996 mono_domain_unlock (domain);
2997 g_assert (offset);
2999 interp_add_ins (td, MINT_LDSSFLDA);
3000 WRITE32_INS(td->last_ins, 0, &offset);
3001 } else {
3002 interp_add_ins (td, MINT_LDSFLDA);
3003 td->last_ins->data [0] = get_data_item_index (td, vtable);
3004 td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
3008 static gboolean
3009 interp_emit_load_const (TransformData *td, gpointer field_addr, int mt)
3011 if (mt >= MINT_TYPE_I1 && mt <= MINT_TYPE_I4
3012 #if SIZEOF_VOID_P == 4
3013 || mt == MINT_TYPE_P
3014 #endif
3016 gint32 val;
3017 switch (mt) {
3018 case MINT_TYPE_I1:
3019 val = *(gint8*)field_addr;
3020 break;
3021 case MINT_TYPE_U1:
3022 val = *(guint8*)field_addr;
3023 break;
3024 case MINT_TYPE_I2:
3025 val = *(gint16*)field_addr;
3026 break;
3027 case MINT_TYPE_U2:
3028 val = *(guint16*)field_addr;
3029 break;
3030 default:
3031 val = *(gint32*)field_addr;
3033 interp_get_ldc_i4_from_const (td, NULL, val);
3034 } else if (mt == MINT_TYPE_I8
3035 #if SIZEOF_VOID_P == 8
3036 || mt == MINT_TYPE_P
3037 #endif
3039 gint64 val = *(gint64*)field_addr;
3040 interp_add_ins (td, MINT_LDC_I8);
3041 WRITE64_INS (td->last_ins, 0, &val);
3042 } else if (mt == MINT_TYPE_R4) {
3043 float val = *(float*)field_addr;
3044 interp_add_ins (td, MINT_LDC_R4);
3045 WRITE32_INS (td->last_ins, 0, &val);
3046 } else if (mt == MINT_TYPE_R8) {
3047 double val = *(double*)field_addr;
3048 interp_add_ins (td, MINT_LDC_R8);
3049 WRITE64_INS (td->last_ins, 0, &val);
3050 } else {
3051 return FALSE;
3053 return TRUE;
3057 * emit_convert:
3059 * Emit some implicit conversions which are not part of the .net spec, but are allowed by MS.NET.
3061 static void
3062 emit_convert (TransformData *td, int stack_type, MonoType *ftype)
3064 ftype = mini_get_underlying_type (ftype);
3066 // FIXME: Add more
3067 switch (ftype->type) {
3068 case MONO_TYPE_I8: {
3069 switch (stack_type) {
3070 case STACK_TYPE_I4:
3071 interp_add_ins (td, MINT_CONV_I8_I4);
3072 break;
3073 default:
3074 break;
3076 break;
3078 default:
3079 break;
3083 static void
3084 interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, gboolean is_load, MonoError *error)
3086 MonoDomain *domain = td->rtm->domain;
3087 // Initialize the offset for the field
3088 MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
3089 return_if_nok (error);
3091 if (mono_class_field_is_special_static (field)) {
3092 guint32 offset;
3094 mono_domain_lock (domain);
3095 g_assert (domain->special_static_fields);
3096 offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
3097 mono_domain_unlock (domain);
3098 g_assert (offset);
3100 // Offset is SpecialStaticOffset
3101 if ((offset & 0x80000000) == 0 && mt != MINT_TYPE_VT) {
3102 // This field is thread static
3103 interp_add_ins (td, (is_load ? MINT_LDTSFLD_I1 : MINT_STTSFLD_I1) + mt);
3104 WRITE32_INS(td->last_ins, 0, &offset);
3105 } else {
3106 if (mt == MINT_TYPE_VT) {
3107 interp_add_ins (td, is_load ? MINT_LDSSFLD_VT : MINT_STSSFLD_VT);
3108 WRITE32_INS(td->last_ins, 0, &offset);
3110 int size = mono_class_value_size (field_class, NULL);
3111 WRITE32_INS(td->last_ins, 2, &size);
3112 } else {
3113 interp_add_ins (td, is_load ? MINT_LDSSFLD : MINT_STSSFLD);
3114 td->last_ins->data [0] = get_data_item_index (td, field);
3115 WRITE32_INS(td->last_ins, 1, &offset);
3118 } else {
3119 gpointer field_addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
3120 if (is_load) {
3121 MonoType *ftype = mono_field_get_type_internal (field);
3122 if (ftype->attrs & FIELD_ATTRIBUTE_INIT_ONLY && vtable->initialized) {
3123 if (interp_emit_load_const (td, field_addr, mt))
3124 return;
3126 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSFLD_VT : (MINT_LDSFLD_I1 + mt - MINT_TYPE_I1));
3127 } else {
3128 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1));
3131 td->last_ins->data [0] = get_data_item_index (td, vtable);
3132 td->last_ins->data [1] = get_data_item_index (td, (char*)field_addr);
3134 if (mt == MINT_TYPE_VT) {
3135 int size = mono_class_value_size (field_class, NULL);
3136 WRITE32_INS(td->last_ins, 2, &size);
3141 static gboolean
3142 generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error)
3144 int target;
3145 int offset, mt, i, i32;
3146 guint32 token;
3147 int in_offset;
3148 const unsigned char *end;
3149 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
3150 gboolean sym_seq_points = FALSE;
3151 MonoBitSet *seq_point_locs = NULL;
3152 gboolean readonly = FALSE;
3153 gboolean volatile_ = FALSE;
3154 MonoClass *constrained_class = NULL;
3155 MonoClass *klass;
3156 MonoClassField *field;
3157 MonoImage *image = m_class_get_image (method->klass);
3158 InterpMethod *rtm = td->rtm;
3159 MonoDomain *domain = rtm->domain;
3160 MonoMethodSignature *signature = mono_method_signature_internal (method);
3161 gboolean ret = TRUE;
3162 gboolean emitted_funccall_seq_point = FALSE;
3163 guint32 *arg_locals = NULL;
3164 guint32 *local_locals = NULL;
3165 InterpInst *last_seq_point = NULL;
3166 gboolean save_last_error = FALSE;
3167 gboolean inlining = td->method != method;
3169 original_bb = bb = mono_basic_block_split (method, error, header);
3170 goto_if_nok (error, exit);
3171 g_assert (bb);
3173 td->il_code = header->code;
3174 td->in_start = td->ip = header->code;
3175 end = td->ip + header->code_size;
3177 if (!init_bb_start (td, header, inlining))
3178 goto exit;
3180 if (!inlining) {
3181 for (i = 0; i < header->code_size; i++) {
3182 td->stack_height [i] = -1;
3183 td->clause_indexes [i] = -1;
3187 for (i = 0; i < header->num_clauses; i++) {
3188 MonoExceptionClause *c = header->clauses + i;
3189 td->stack_height [c->handler_offset] = 0;
3190 td->vt_stack_size [c->handler_offset] = 0;
3191 td->is_bb_start [c->handler_offset] = 1;
3192 td->is_bb_start [c->try_offset] = 1;
3194 td->stack_height [c->handler_offset] = 1;
3195 td->stack_state [c->handler_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
3196 td->stack_state [c->handler_offset][0].type = STACK_TYPE_O;
3197 td->stack_state [c->handler_offset][0].klass = NULL; /*FIX*/
3199 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER) {
3200 td->stack_height [c->data.filter_offset] = 0;
3201 td->vt_stack_size [c->data.filter_offset] = 0;
3202 td->is_bb_start [c->data.filter_offset] = 1;
3204 td->stack_height [c->data.filter_offset] = 1;
3205 td->stack_state [c->data.filter_offset] = (StackInfo*)g_malloc0(sizeof(StackInfo));
3206 td->stack_state [c->data.filter_offset][0].type = STACK_TYPE_O;
3207 td->stack_state [c->data.filter_offset][0].klass = NULL; /*FIX*/
3210 for (int j = c->handler_offset; j < c->handler_offset + c->handler_len; ++j) {
3211 if (td->clause_indexes [j] == -1)
3212 td->clause_indexes [j] = i;
3216 if (td->gen_sdb_seq_points && !inlining) {
3217 MonoDebugMethodInfo *minfo;
3218 get_basic_blocks (td);
3220 minfo = mono_debug_lookup_method (method);
3222 if (minfo) {
3223 MonoSymSeqPoint *sps;
3224 int i, n_il_offsets;
3226 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
3227 // FIXME: Free
3228 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
3229 sym_seq_points = TRUE;
3231 for (i = 0; i < n_il_offsets; ++i) {
3232 if (sps [i].il_offset < header->code_size)
3233 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
3235 g_free (sps);
3237 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
3238 if (asyncMethod) {
3239 for (i = 0; asyncMethod != NULL && i < asyncMethod->num_awaits; i++) {
3240 mono_bitset_set_fast (seq_point_locs, asyncMethod->resume_offsets [i]);
3241 mono_bitset_set_fast (seq_point_locs, asyncMethod->yield_offsets [i]);
3243 mono_debug_free_method_async_debug_info (asyncMethod);
3245 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (m_class_get_image (method->klass))) {
3246 /* Methods without line number info like auto-generated property accessors */
3247 seq_point_locs = mono_bitset_new (header->code_size, 0);
3248 sym_seq_points = TRUE;
3252 if (sym_seq_points) {
3253 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3254 last_seq_point->flags |= INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY;
3257 if (mono_debugger_method_has_breakpoint (method))
3258 interp_add_ins (td, MINT_BREAKPOINT);
3260 if (!inlining) {
3261 if (td->verbose_level) {
3262 char *tmp = mono_disasm_code (NULL, method, td->ip, end);
3263 char *name = mono_method_full_name (method, TRUE);
3264 g_print ("Method %s, original code:\n", name);
3265 g_print ("%s\n", tmp);
3266 g_free (tmp);
3267 g_free (name);
3270 if (header->num_locals && header->init_locals)
3271 interp_add_ins (td, MINT_INITLOCALS);
3273 guint16 enter_profiling = 0;
3274 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3275 enter_profiling |= TRACING_FLAG;
3276 if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER)
3277 enter_profiling |= PROFILING_FLAG;
3278 if (enter_profiling) {
3279 interp_add_ins (td, MINT_PROF_ENTER);
3280 td->last_ins->data [0] = enter_profiling;
3284 * If safepoints are required by default, always check for polling,
3285 * without emitting new instructions. This optimizes method entry in
3286 * the common scenario, which is coop.
3288 #if !defined(ENABLE_HYBRID_SUSPEND) && !defined(ENABLE_COOP_SUSPEND)
3289 /* safepoint is required on method entry */
3290 if (mono_threads_are_safepoints_enabled ())
3291 interp_add_ins (td, MINT_SAFEPOINT);
3292 #endif
3293 } else {
3294 int local;
3295 arg_locals = (guint32*) g_malloc ((!!signature->hasthis + signature->param_count) * sizeof (guint32));
3296 /* Allocate locals to store inlined method args from stack */
3297 for (i = signature->param_count - 1; i >= 0; i--) {
3298 local = create_interp_local (td, signature->params [i]);
3299 arg_locals [i + !!signature->hasthis] = local;
3300 store_local (td, local);
3303 if (signature->hasthis) {
3305 * If this is value type, it is passed by address and not by value.
3306 * FIXME We should use MINT_TYPE_P instead of MINT_TYPE_O
3308 MonoType *type = mono_get_object_type ();
3309 local = create_interp_local (td, type);
3310 arg_locals [0] = local;
3311 store_local (td, local);
3314 local_locals = (guint32*) g_malloc (header->num_locals * sizeof (guint32));
3315 /* Allocate locals to store inlined method args from stack */
3316 for (i = 0; i < header->num_locals; i++)
3317 local_locals [i] = create_interp_local (td, header->locals [i]);
3320 while (td->ip < end) {
3321 g_assert (td->sp >= td->stack);
3322 g_assert (td->vt_sp < 0x10000000);
3323 in_offset = td->ip - header->code;
3324 if (!inlining)
3325 td->current_il_offset = in_offset;
3326 td->in_start = td->ip;
3327 InterpInst *prev_last_ins = td->last_ins;
3329 // Inlined method doesn't have clauses or branches
3330 if (!inlining && td->stack_height [in_offset] >= 0) {
3331 g_assert (td->is_bb_start [in_offset]);
3332 if (td->stack_height [in_offset] > 0)
3333 memcpy (td->stack, td->stack_state [in_offset], td->stack_height [in_offset] * sizeof(td->stack [0]));
3334 td->sp = td->stack + td->stack_height [in_offset];
3335 td->vt_sp = td->vt_stack_size [in_offset];
3338 if (in_offset == bb->end)
3339 bb = bb->next;
3341 if (bb->dead) {
3342 int op_size = mono_opcode_size (td->ip, end);
3343 g_assert (op_size > 0); /* The BB formation pass must catch all bad ops */
3345 if (td->verbose_level > 1)
3346 g_print ("SKIPPING DEAD OP at %x\n", in_offset);
3348 td->ip += op_size;
3349 continue;
3352 if (td->verbose_level > 1) {
3353 g_print ("IL_%04lx %s %-10s, sp %ld, %s %-12s vt_sp %u (max %u)\n",
3354 td->ip - td->il_code,
3355 td->is_bb_start [td->ip - td->il_code] == 3 ? "<>" :
3356 td->is_bb_start [td->ip - td->il_code] == 2 ? "< " :
3357 td->is_bb_start [td->ip - td->il_code] == 1 ? " >" : " ",
3358 mono_opcode_name (*td->ip), td->sp - td->stack,
3359 td->sp > td->stack ? stack_type_string [td->sp [-1].type] : " ",
3360 (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)) : "",
3361 td->vt_sp, td->max_vt_sp);
3364 if (sym_seq_points && mono_bitset_test_fast (seq_point_locs, td->ip - header->code)) {
3365 InterpBasicBlock *cbb = td->offset_to_bb [td->ip - header->code];
3366 g_assert (cbb);
3369 * Make methods interruptable at the beginning, and at the targets of
3370 * backward branches.
3372 if (in_offset == 0 || g_slist_length (cbb->preds) > 1)
3373 interp_add_ins (td, MINT_SDB_INTR_LOC);
3375 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3378 if (!inlining && td->is_bb_start [in_offset]) {
3379 int index = td->clause_indexes [in_offset];
3380 if (index != -1) {
3381 MonoExceptionClause *clause = &header->clauses [index];
3382 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
3383 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) &&
3384 in_offset == clause->handler_offset)
3385 interp_add_ins (td, MINT_START_ABORT_PROT);
3389 switch (*td->ip) {
3390 case CEE_NOP:
3391 /* lose it */
3392 emitted_funccall_seq_point = FALSE;
3393 ++td->ip;
3394 break;
3395 case CEE_BREAK:
3396 SIMPLE_OP(td, MINT_BREAK);
3397 break;
3398 case CEE_LDARG_0:
3399 case CEE_LDARG_1:
3400 case CEE_LDARG_2:
3401 case CEE_LDARG_3: {
3402 int arg_n = *td->ip - CEE_LDARG_0;
3403 if (!inlining)
3404 load_arg (td, arg_n);
3405 else
3406 load_local (td, arg_locals [arg_n]);
3407 ++td->ip;
3408 break;
3410 case CEE_LDLOC_0:
3411 case CEE_LDLOC_1:
3412 case CEE_LDLOC_2:
3413 case CEE_LDLOC_3: {
3414 int loc_n = *td->ip - CEE_LDLOC_0;
3415 if (!inlining)
3416 load_local (td, loc_n);
3417 else
3418 load_local (td, local_locals [loc_n]);
3419 ++td->ip;
3420 break;
3422 case CEE_STLOC_0:
3423 case CEE_STLOC_1:
3424 case CEE_STLOC_2:
3425 case CEE_STLOC_3: {
3426 int loc_n = *td->ip - CEE_STLOC_0;
3427 if (!inlining)
3428 store_local (td, loc_n);
3429 else
3430 store_local (td, local_locals [loc_n]);
3431 ++td->ip;
3432 break;
3434 case CEE_LDARG_S: {
3435 int arg_n = ((guint8 *)td->ip)[1];
3436 if (!inlining)
3437 load_arg (td, arg_n);
3438 else
3439 load_local (td, arg_locals [arg_n]);
3440 td->ip += 2;
3441 break;
3443 case CEE_LDARGA_S: {
3444 /* NOTE: n includes this */
3445 int n = ((guint8 *) td->ip) [1];
3447 if (!inlining) {
3448 get_arg_type_exact (td, n, &mt);
3449 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
3450 td->last_ins->data [0] = n;
3451 } else {
3452 int loc_n = arg_locals [n];
3453 interp_add_ins (td, MINT_LDLOCA_S);
3454 td->last_ins->data [0] = loc_n;
3455 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
3457 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3458 td->ip += 2;
3459 break;
3461 case CEE_STARG_S: {
3462 int arg_n = ((guint8 *)td->ip)[1];
3463 if (!inlining)
3464 store_arg (td, arg_n);
3465 else
3466 store_local (td, arg_locals [arg_n]);
3467 td->ip += 2;
3468 break;
3470 case CEE_LDLOC_S: {
3471 int loc_n = ((guint8 *)td->ip)[1];
3472 if (!inlining)
3473 load_local (td, loc_n);
3474 else
3475 load_local (td, local_locals [loc_n]);
3476 td->ip += 2;
3477 break;
3479 case CEE_LDLOCA_S: {
3480 int loc_n = ((guint8 *)td->ip)[1];
3481 interp_add_ins (td, MINT_LDLOCA_S);
3482 if (inlining)
3483 loc_n = local_locals [loc_n];
3484 td->last_ins->data [0] = loc_n;
3485 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
3486 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
3487 td->ip += 2;
3488 break;
3490 case CEE_STLOC_S: {
3491 int loc_n = ((guint8 *)td->ip)[1];
3492 if (!inlining)
3493 store_local (td, loc_n);
3494 else
3495 store_local (td, local_locals [loc_n]);
3496 td->ip += 2;
3497 break;
3499 case CEE_LDNULL:
3500 SIMPLE_OP(td, MINT_LDNULL);
3501 PUSH_TYPE(td, STACK_TYPE_O, NULL);
3502 break;
3503 case CEE_LDC_I4_M1:
3504 SIMPLE_OP(td, MINT_LDC_I4_M1);
3505 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3506 break;
3507 case CEE_LDC_I4_0:
3508 // Only single basic block functions are inlined.
3509 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 &&
3510 td->sp > td->stack && td->sp [-1].type == STACK_TYPE_I4) {
3511 SIMPLE_OP(td, MINT_CEQ0_I4);
3512 td->ip += 2;
3513 } else {
3514 SIMPLE_OP(td, MINT_LDC_I4_0);
3515 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3517 break;
3518 case CEE_LDC_I4_1:
3519 // Only single basic block functions are inlined.
3520 if (td->ip - td->il_code + 1 < td->code_size && (inlining || !td->is_bb_start [td->ip + 1 - td->il_code]) &&
3521 (td->ip [1] == CEE_ADD || td->ip [1] == CEE_SUB) && td->sp [-1].type == STACK_TYPE_I4) {
3522 interp_add_ins (td, td->ip [1] == CEE_ADD ? MINT_ADD1_I4 : MINT_SUB1_I4);
3523 td->ip += 2;
3524 } else {
3525 SIMPLE_OP(td, MINT_LDC_I4_1);
3526 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3528 break;
3529 case CEE_LDC_I4_2:
3530 case CEE_LDC_I4_3:
3531 case CEE_LDC_I4_4:
3532 case CEE_LDC_I4_5:
3533 case CEE_LDC_I4_6:
3534 case CEE_LDC_I4_7:
3535 case CEE_LDC_I4_8:
3536 SIMPLE_OP(td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0);
3537 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3538 break;
3539 case CEE_LDC_I4_S:
3540 interp_add_ins (td, MINT_LDC_I4_S);
3541 td->last_ins->data [0] = ((gint8 *) td->ip) [1];
3542 td->ip += 2;
3543 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3544 break;
3545 case CEE_LDC_I4:
3546 i32 = read32 (td->ip + 1);
3547 interp_add_ins (td, MINT_LDC_I4);
3548 WRITE32_INS (td->last_ins, 0, &i32);
3549 td->ip += 5;
3550 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
3551 break;
3552 case CEE_LDC_I8: {
3553 gint64 val = read64 (td->ip + 1);
3554 interp_add_ins (td, MINT_LDC_I8);
3555 WRITE64_INS (td->last_ins, 0, &val);
3556 td->ip += 9;
3557 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I8);
3558 break;
3560 case CEE_LDC_R4: {
3561 float val;
3562 readr4 (td->ip + 1, &val);
3563 interp_add_ins (td, MINT_LDC_R4);
3564 WRITE32_INS (td->last_ins, 0, &val);
3565 td->ip += 5;
3566 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R4);
3567 break;
3569 case CEE_LDC_R8: {
3570 double val;
3571 readr8 (td->ip + 1, &val);
3572 interp_add_ins (td, MINT_LDC_R8);
3573 WRITE64_INS (td->last_ins, 0, &val);
3574 td->ip += 9;
3575 PUSH_SIMPLE_TYPE(td, STACK_TYPE_R8);
3576 break;
3578 case CEE_DUP: {
3579 int type = td->sp [-1].type;
3580 MonoClass *klass = td->sp [-1].klass;
3581 if (td->sp [-1].type == STACK_TYPE_VT) {
3582 gint32 size = mono_class_value_size (klass, NULL);
3583 PUSH_VT(td, size);
3584 interp_add_ins (td, MINT_DUP_VT);
3585 WRITE32_INS (td->last_ins, 0, &size);
3586 td->ip ++;
3587 } else
3588 SIMPLE_OP(td, MINT_DUP);
3589 PUSH_TYPE(td, type, klass);
3590 break;
3592 case CEE_POP:
3593 CHECK_STACK(td, 1);
3594 SIMPLE_OP(td, MINT_POP);
3595 if (td->sp [-1].type == STACK_TYPE_VT) {
3596 int size = mono_class_value_size (td->sp [-1].klass, NULL);
3597 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
3598 interp_add_ins (td, MINT_VTRESULT);
3599 td->last_ins->data [0] = 0;
3600 WRITE32_INS (td->last_ins, 1, &size);
3601 td->vt_sp -= size;
3603 --td->sp;
3604 break;
3605 case CEE_JMP: {
3606 MonoMethod *m;
3607 INLINE_FAILURE;
3608 if (td->sp > td->stack)
3609 g_warning ("CEE_JMP: stack must be empty");
3610 token = read32 (td->ip + 1);
3611 m = mono_get_method_checked (image, token, NULL, generic_context, error);
3612 goto_if_nok (error, exit);
3613 interp_add_ins (td, MINT_JMP);
3614 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
3615 goto_if_nok (error, exit);
3616 td->ip += 5;
3617 break;
3619 case CEE_CALLVIRT: /* Fall through */
3620 case CEE_CALLI: /* Fall through */
3621 case CEE_CALL: {
3622 gboolean need_seq_point = FALSE;
3624 if (sym_seq_points && !mono_bitset_test_fast (seq_point_locs, td->ip + 5 - header->code))
3625 need_seq_point = TRUE;
3627 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, constrained_class, readonly, error, TRUE, save_last_error))
3628 goto exit;
3630 if (need_seq_point) {
3631 //check is is a nested call and remove the MONO_INST_NONEMPTY_STACK of the last breakpoint, only for non native methods
3632 if (!(method->flags & METHOD_IMPL_ATTRIBUTE_NATIVE)) {
3633 if (emitted_funccall_seq_point) {
3634 if (last_seq_point)
3635 last_seq_point->flags |= INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL;
3637 else
3638 emitted_funccall_seq_point = TRUE;
3640 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3641 // This seq point is actually associated with the instruction following the call
3642 last_seq_point->il_offset = td->ip - header->code;
3643 last_seq_point->flags = INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK;
3646 constrained_class = NULL;
3647 readonly = FALSE;
3648 save_last_error = FALSE;
3649 break;
3651 case CEE_RET: {
3652 /* Return from inlined method, return value is on top of stack */
3653 if (td->method != method) {
3654 td->ip++;
3655 break;
3658 int vt_size = 0;
3659 MonoType *ult = mini_type_get_underlying_type (signature->ret);
3660 if (ult->type != MONO_TYPE_VOID) {
3661 CHECK_STACK (td, 1);
3662 --td->sp;
3663 if (mint_type (ult) == MINT_TYPE_VT) {
3664 MonoClass *klass = mono_class_from_mono_type_internal (ult);
3665 vt_size = mono_class_value_size (klass, NULL);
3668 if (td->sp > td->stack) {
3669 mono_error_set_generic_error (error, "System", "InvalidProgramException", "");
3670 goto exit;
3672 if (td->vt_sp != ALIGN_TO (vt_size, MINT_VT_ALIGNMENT))
3673 g_error ("%s: CEE_RET: value type stack: %d vs. %d", mono_method_full_name (td->method, TRUE), td->vt_sp, vt_size);
3675 if (sym_seq_points) {
3676 last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
3677 td->last_ins->flags |= INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT;
3680 guint16 exit_profiling = 0;
3681 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3682 exit_profiling |= TRACING_FLAG;
3683 if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE)
3684 exit_profiling |= PROFILING_FLAG;
3685 if (exit_profiling) {
3686 /* This does the return as well */
3687 if (ult->type == MONO_TYPE_VOID) {
3688 interp_add_ins (td, MINT_PROF_EXIT_VOID);
3689 vt_size = -1;
3690 } else {
3691 interp_add_ins (td, MINT_PROF_EXIT);
3693 td->last_ins->data [0] = exit_profiling;
3694 WRITE32_INS (td->last_ins, 1, &vt_size);
3695 POP_VT (td, vt_size);
3696 ++td->ip;
3697 } else {
3698 if (vt_size == 0)
3699 SIMPLE_OP(td, ult->type == MONO_TYPE_VOID ? MINT_RET_VOID : MINT_RET);
3700 else {
3701 interp_add_ins (td, MINT_RET_VT);
3702 WRITE32_INS (td->last_ins, 0, &vt_size);
3703 POP_VT (td, vt_size);
3704 ++td->ip;
3707 break;
3709 case CEE_BR: {
3710 int offset = read32 (td->ip + 1);
3711 if (offset) {
3712 INLINE_FAILURE;
3713 handle_branch (td, MINT_BR_S, MINT_BR, 5 + offset);
3715 td->ip += 5;
3716 break;
3718 case CEE_BR_S: {
3719 int offset = (gint8)td->ip [1];
3720 if (offset) {
3721 INLINE_FAILURE;
3722 handle_branch (td, MINT_BR_S, MINT_BR, 2 + (gint8)td->ip [1]);
3724 td->ip += 2;
3725 break;
3727 case CEE_BRFALSE:
3728 INLINE_FAILURE;
3729 one_arg_branch (td, MINT_BRFALSE_I4, 5 + read32 (td->ip + 1));
3730 td->ip += 5;
3731 break;
3732 case CEE_BRFALSE_S:
3733 INLINE_FAILURE;
3734 one_arg_branch (td, MINT_BRFALSE_I4, 2 + (gint8)td->ip [1]);
3735 td->ip += 2;
3736 break;
3737 case CEE_BRTRUE:
3738 INLINE_FAILURE;
3739 one_arg_branch (td, MINT_BRTRUE_I4, 5 + read32 (td->ip + 1));
3740 td->ip += 5;
3741 break;
3742 case CEE_BRTRUE_S:
3743 INLINE_FAILURE;
3744 one_arg_branch (td, MINT_BRTRUE_I4, 2 + (gint8)td->ip [1]);
3745 td->ip += 2;
3746 break;
3747 case CEE_BEQ:
3748 INLINE_FAILURE;
3749 two_arg_branch (td, MINT_BEQ_I4, 5 + read32 (td->ip + 1));
3750 td->ip += 5;
3751 break;
3752 case CEE_BEQ_S:
3753 INLINE_FAILURE;
3754 two_arg_branch (td, MINT_BEQ_I4, 2 + (gint8) td->ip [1]);
3755 td->ip += 2;
3756 break;
3757 case CEE_BGE:
3758 INLINE_FAILURE;
3759 two_arg_branch (td, MINT_BGE_I4, 5 + read32 (td->ip + 1));
3760 td->ip += 5;
3761 break;
3762 case CEE_BGE_S:
3763 INLINE_FAILURE;
3764 two_arg_branch (td, MINT_BGE_I4, 2 + (gint8) td->ip [1]);
3765 td->ip += 2;
3766 break;
3767 case CEE_BGT:
3768 INLINE_FAILURE;
3769 two_arg_branch (td, MINT_BGT_I4, 5 + read32 (td->ip + 1));
3770 td->ip += 5;
3771 break;
3772 case CEE_BGT_S:
3773 INLINE_FAILURE;
3774 two_arg_branch (td, MINT_BGT_I4, 2 + (gint8) td->ip [1]);
3775 td->ip += 2;
3776 break;
3777 case CEE_BLT:
3778 INLINE_FAILURE;
3779 two_arg_branch (td, MINT_BLT_I4, 5 + read32 (td->ip + 1));
3780 td->ip += 5;
3781 break;
3782 case CEE_BLT_S:
3783 INLINE_FAILURE;
3784 two_arg_branch (td, MINT_BLT_I4, 2 + (gint8) td->ip [1]);
3785 td->ip += 2;
3786 break;
3787 case CEE_BLE:
3788 INLINE_FAILURE;
3789 two_arg_branch (td, MINT_BLE_I4, 5 + read32 (td->ip + 1));
3790 td->ip += 5;
3791 break;
3792 case CEE_BLE_S:
3793 INLINE_FAILURE;
3794 two_arg_branch (td, MINT_BLE_I4, 2 + (gint8) td->ip [1]);
3795 td->ip += 2;
3796 break;
3797 case CEE_BNE_UN:
3798 INLINE_FAILURE;
3799 two_arg_branch (td, MINT_BNE_UN_I4, 5 + read32 (td->ip + 1));
3800 td->ip += 5;
3801 break;
3802 case CEE_BNE_UN_S:
3803 INLINE_FAILURE;
3804 two_arg_branch (td, MINT_BNE_UN_I4, 2 + (gint8) td->ip [1]);
3805 td->ip += 2;
3806 break;
3807 case CEE_BGE_UN:
3808 INLINE_FAILURE;
3809 two_arg_branch (td, MINT_BGE_UN_I4, 5 + read32 (td->ip + 1));
3810 td->ip += 5;
3811 break;
3812 case CEE_BGE_UN_S:
3813 INLINE_FAILURE;
3814 two_arg_branch (td, MINT_BGE_UN_I4, 2 + (gint8) td->ip [1]);
3815 td->ip += 2;
3816 break;
3817 case CEE_BGT_UN:
3818 INLINE_FAILURE;
3819 two_arg_branch (td, MINT_BGT_UN_I4, 5 + read32 (td->ip + 1));
3820 td->ip += 5;
3821 break;
3822 case CEE_BGT_UN_S:
3823 INLINE_FAILURE;
3824 two_arg_branch (td, MINT_BGT_UN_I4, 2 + (gint8) td->ip [1]);
3825 td->ip += 2;
3826 break;
3827 case CEE_BLE_UN:
3828 INLINE_FAILURE;
3829 two_arg_branch (td, MINT_BLE_UN_I4, 5 + read32 (td->ip + 1));
3830 td->ip += 5;
3831 break;
3832 case CEE_BLE_UN_S:
3833 INLINE_FAILURE;
3834 two_arg_branch (td, MINT_BLE_UN_I4, 2 + (gint8) td->ip [1]);
3835 td->ip += 2;
3836 break;
3837 case CEE_BLT_UN:
3838 INLINE_FAILURE;
3839 two_arg_branch (td, MINT_BLT_UN_I4, 5 + read32 (td->ip + 1));
3840 td->ip += 5;
3841 break;
3842 case CEE_BLT_UN_S:
3843 INLINE_FAILURE;
3844 two_arg_branch (td, MINT_BLT_UN_I4, 2 + (gint8) td->ip [1]);
3845 td->ip += 2;
3846 break;
3847 case CEE_SWITCH: {
3848 INLINE_FAILURE;
3849 guint32 n;
3850 const unsigned char *next_ip;
3851 ++td->ip;
3852 n = read32 (td->ip);
3853 interp_add_ins_explicit (td, MINT_SWITCH, MINT_SWITCH_LEN (n));
3854 WRITE32_INS (td->last_ins, 0, &n);
3855 td->ip += 4;
3856 next_ip = td->ip + n * 4;
3857 --td->sp;
3858 int stack_height = td->sp - td->stack;
3859 for (i = 0; i < n; i++) {
3860 offset = read32 (td->ip);
3861 target = next_ip - td->il_code + offset;
3862 if (offset < 0) {
3863 #if DEBUG_INTERP
3864 if (stack_height > 0 && stack_height != td->stack_height [target])
3865 g_warning ("SWITCH with back branch and non-empty stack");
3866 #endif
3867 } else {
3868 td->stack_height [target] = stack_height;
3869 td->vt_stack_size [target] = td->vt_sp;
3870 if (stack_height > 0)
3871 td->stack_state [target] = (StackInfo*)g_memdup (td->stack, stack_height * sizeof (td->stack [0]));
3873 WRITE32_INS (td->last_ins, 2 + i * 2, &target);
3874 td->ip += 4;
3876 break;
3878 case CEE_LDIND_I1:
3879 CHECK_STACK (td, 1);
3880 SIMPLE_OP (td, MINT_LDIND_I1_CHECK);
3881 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3882 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3883 break;
3884 case CEE_LDIND_U1:
3885 CHECK_STACK (td, 1);
3886 SIMPLE_OP (td, MINT_LDIND_U1_CHECK);
3887 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3888 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3889 break;
3890 case CEE_LDIND_I2:
3891 CHECK_STACK (td, 1);
3892 SIMPLE_OP (td, MINT_LDIND_I2_CHECK);
3893 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3894 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3895 break;
3896 case CEE_LDIND_U2:
3897 CHECK_STACK (td, 1);
3898 SIMPLE_OP (td, MINT_LDIND_U2_CHECK);
3899 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3900 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3901 break;
3902 case CEE_LDIND_I4:
3903 CHECK_STACK (td, 1);
3904 SIMPLE_OP (td, MINT_LDIND_I4_CHECK);
3905 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3906 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3907 break;
3908 case CEE_LDIND_U4:
3909 CHECK_STACK (td, 1);
3910 SIMPLE_OP (td, MINT_LDIND_U4_CHECK);
3911 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
3912 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3913 break;
3914 case CEE_LDIND_I8:
3915 CHECK_STACK (td, 1);
3916 SIMPLE_OP (td, MINT_LDIND_I8_CHECK);
3917 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
3918 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3919 break;
3920 case CEE_LDIND_I:
3921 CHECK_STACK (td, 1);
3922 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3923 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
3924 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3925 break;
3926 case CEE_LDIND_R4:
3927 CHECK_STACK (td, 1);
3928 SIMPLE_OP (td, MINT_LDIND_R4_CHECK);
3929 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
3930 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3931 break;
3932 case CEE_LDIND_R8:
3933 CHECK_STACK (td, 1);
3934 SIMPLE_OP (td, MINT_LDIND_R8_CHECK);
3935 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
3936 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3937 break;
3938 case CEE_LDIND_REF:
3939 CHECK_STACK (td, 1);
3940 SIMPLE_OP (td, MINT_LDIND_REF_CHECK);
3941 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
3942 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
3943 break;
3944 case CEE_STIND_REF:
3945 CHECK_STACK (td, 2);
3946 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
3947 SIMPLE_OP (td, MINT_STIND_REF);
3948 td->sp -= 2;
3949 break;
3950 case CEE_STIND_I1:
3951 CHECK_STACK (td, 2);
3952 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
3953 SIMPLE_OP (td, MINT_STIND_I1);
3954 td->sp -= 2;
3955 break;
3956 case CEE_STIND_I2:
3957 CHECK_STACK (td, 2);
3958 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
3959 SIMPLE_OP (td, MINT_STIND_I2);
3960 td->sp -= 2;
3961 break;
3962 case CEE_STIND_I4:
3963 CHECK_STACK (td, 2);
3964 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
3965 SIMPLE_OP (td, MINT_STIND_I4);
3966 td->sp -= 2;
3967 break;
3968 case CEE_STIND_I:
3969 CHECK_STACK (td, 2);
3970 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
3971 SIMPLE_OP (td, MINT_STIND_I);
3972 td->sp -= 2;
3973 break;
3974 case CEE_STIND_I8:
3975 CHECK_STACK (td, 2);
3976 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
3977 SIMPLE_OP (td, MINT_STIND_I8);
3978 td->sp -= 2;
3979 break;
3980 case CEE_STIND_R4:
3981 CHECK_STACK (td, 2);
3982 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
3983 SIMPLE_OP (td, MINT_STIND_R4);
3984 td->sp -= 2;
3985 break;
3986 case CEE_STIND_R8:
3987 CHECK_STACK (td, 2);
3988 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
3989 SIMPLE_OP (td, MINT_STIND_R8);
3990 td->sp -= 2;
3991 break;
3992 case CEE_ADD:
3993 binary_arith_op(td, MINT_ADD_I4);
3994 ++td->ip;
3995 break;
3996 case CEE_SUB:
3997 binary_arith_op(td, MINT_SUB_I4);
3998 ++td->ip;
3999 break;
4000 case CEE_MUL:
4001 binary_arith_op(td, MINT_MUL_I4);
4002 ++td->ip;
4003 break;
4004 case CEE_DIV:
4005 binary_arith_op(td, MINT_DIV_I4);
4006 ++td->ip;
4007 break;
4008 case CEE_DIV_UN:
4009 binary_arith_op(td, MINT_DIV_UN_I4);
4010 ++td->ip;
4011 break;
4012 case CEE_REM:
4013 binary_arith_op (td, MINT_REM_I4);
4014 ++td->ip;
4015 break;
4016 case CEE_REM_UN:
4017 binary_arith_op (td, MINT_REM_UN_I4);
4018 ++td->ip;
4019 break;
4020 case CEE_AND:
4021 binary_arith_op (td, MINT_AND_I4);
4022 ++td->ip;
4023 break;
4024 case CEE_OR:
4025 binary_arith_op (td, MINT_OR_I4);
4026 ++td->ip;
4027 break;
4028 case CEE_XOR:
4029 binary_arith_op (td, MINT_XOR_I4);
4030 ++td->ip;
4031 break;
4032 case CEE_SHL:
4033 shift_op (td, MINT_SHL_I4);
4034 ++td->ip;
4035 break;
4036 case CEE_SHR:
4037 shift_op (td, MINT_SHR_I4);
4038 ++td->ip;
4039 break;
4040 case CEE_SHR_UN:
4041 shift_op (td, MINT_SHR_UN_I4);
4042 ++td->ip;
4043 break;
4044 case CEE_NEG:
4045 unary_arith_op (td, MINT_NEG_I4);
4046 ++td->ip;
4047 break;
4048 case CEE_NOT:
4049 unary_arith_op (td, MINT_NOT_I4);
4050 ++td->ip;
4051 break;
4052 case CEE_CONV_U1:
4053 CHECK_STACK (td, 1);
4054 switch (td->sp [-1].type) {
4055 case STACK_TYPE_R4:
4056 interp_add_ins (td, MINT_CONV_U1_R4);
4057 break;
4058 case STACK_TYPE_R8:
4059 interp_add_ins (td, MINT_CONV_U1_R8);
4060 break;
4061 case STACK_TYPE_I4:
4062 interp_add_ins (td, MINT_CONV_U1_I4);
4063 break;
4064 case STACK_TYPE_I8:
4065 interp_add_ins (td, MINT_CONV_U1_I8);
4066 break;
4067 default:
4068 g_assert_not_reached ();
4070 ++td->ip;
4071 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4072 break;
4073 case CEE_CONV_I1:
4074 CHECK_STACK (td, 1);
4075 switch (td->sp [-1].type) {
4076 case STACK_TYPE_R4:
4077 interp_add_ins (td, MINT_CONV_I1_R4);
4078 break;
4079 case STACK_TYPE_R8:
4080 interp_add_ins (td, MINT_CONV_I1_R8);
4081 break;
4082 case STACK_TYPE_I4:
4083 interp_add_ins (td, MINT_CONV_I1_I4);
4084 break;
4085 case STACK_TYPE_I8:
4086 interp_add_ins (td, MINT_CONV_I1_I8);
4087 break;
4088 default:
4089 g_assert_not_reached ();
4091 ++td->ip;
4092 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4093 break;
4094 case CEE_CONV_U2:
4095 CHECK_STACK (td, 1);
4096 switch (td->sp [-1].type) {
4097 case STACK_TYPE_R4:
4098 interp_add_ins (td, MINT_CONV_U2_R4);
4099 break;
4100 case STACK_TYPE_R8:
4101 interp_add_ins (td, MINT_CONV_U2_R8);
4102 break;
4103 case STACK_TYPE_I4:
4104 interp_add_ins (td, MINT_CONV_U2_I4);
4105 break;
4106 case STACK_TYPE_I8:
4107 interp_add_ins (td, MINT_CONV_U2_I8);
4108 break;
4109 default:
4110 g_assert_not_reached ();
4112 ++td->ip;
4113 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4114 break;
4115 case CEE_CONV_I2:
4116 CHECK_STACK (td, 1);
4117 switch (td->sp [-1].type) {
4118 case STACK_TYPE_R4:
4119 interp_add_ins (td, MINT_CONV_I2_R4);
4120 break;
4121 case STACK_TYPE_R8:
4122 interp_add_ins (td, MINT_CONV_I2_R8);
4123 break;
4124 case STACK_TYPE_I4:
4125 interp_add_ins (td, MINT_CONV_I2_I4);
4126 break;
4127 case STACK_TYPE_I8:
4128 interp_add_ins (td, MINT_CONV_I2_I8);
4129 break;
4130 default:
4131 g_assert_not_reached ();
4133 ++td->ip;
4134 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4135 break;
4136 case CEE_CONV_U:
4137 CHECK_STACK (td, 1);
4138 switch (td->sp [-1].type) {
4139 case STACK_TYPE_R8:
4140 #if SIZEOF_VOID_P == 4
4141 interp_add_ins (td, MINT_CONV_U4_R8);
4142 #else
4143 interp_add_ins (td, MINT_CONV_U8_R8);
4144 #endif
4145 break;
4146 case STACK_TYPE_I4:
4147 #if SIZEOF_VOID_P == 8
4148 interp_add_ins (td, MINT_CONV_I8_U4);
4149 #endif
4150 break;
4151 case STACK_TYPE_I8:
4152 #if SIZEOF_VOID_P == 4
4153 interp_add_ins (td, MINT_CONV_U4_I8);
4154 #endif
4155 break;
4156 case STACK_TYPE_MP:
4157 case STACK_TYPE_O:
4158 break;
4159 default:
4160 g_assert_not_reached ();
4162 ++td->ip;
4163 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4164 break;
4165 case CEE_CONV_I:
4166 CHECK_STACK (td, 1);
4167 switch (td->sp [-1].type) {
4168 case STACK_TYPE_R8:
4169 #if SIZEOF_VOID_P == 8
4170 interp_add_ins (td, MINT_CONV_I8_R8);
4171 #else
4172 interp_add_ins (td, MINT_CONV_I4_R8);
4173 #endif
4174 break;
4175 case STACK_TYPE_R4:
4176 #if SIZEOF_VOID_P == 8
4177 interp_add_ins (td, MINT_CONV_I8_R4);
4178 #else
4179 interp_add_ins (td, MINT_CONV_I4_R4);
4180 #endif
4181 break;
4182 case STACK_TYPE_I4:
4183 #if SIZEOF_VOID_P == 8
4184 interp_add_ins (td, MINT_CONV_I8_I4);
4185 #endif
4186 break;
4187 case STACK_TYPE_O:
4188 break;
4189 case STACK_TYPE_MP:
4190 break;
4191 case STACK_TYPE_I8:
4192 #if SIZEOF_VOID_P == 4
4193 interp_add_ins (td, MINT_CONV_I4_I8);
4194 #endif
4195 break;
4196 default:
4197 g_assert_not_reached ();
4199 ++td->ip;
4200 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4201 break;
4202 case CEE_CONV_U4:
4203 CHECK_STACK (td, 1);
4204 switch (td->sp [-1].type) {
4205 case STACK_TYPE_R4:
4206 interp_add_ins (td, MINT_CONV_U4_R4);
4207 break;
4208 case STACK_TYPE_R8:
4209 interp_add_ins (td, MINT_CONV_U4_R8);
4210 break;
4211 case STACK_TYPE_I4:
4212 break;
4213 case STACK_TYPE_I8:
4214 interp_add_ins (td, MINT_CONV_U4_I8);
4215 break;
4216 case STACK_TYPE_MP:
4217 #if SIZEOF_VOID_P == 8
4218 interp_add_ins (td, MINT_CONV_U4_I8);
4219 #endif
4220 break;
4221 default:
4222 g_assert_not_reached ();
4224 ++td->ip;
4225 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4226 break;
4227 case CEE_CONV_I4:
4228 CHECK_STACK (td, 1);
4229 switch (td->sp [-1].type) {
4230 case STACK_TYPE_R4:
4231 interp_add_ins (td, MINT_CONV_I4_R4);
4232 break;
4233 case STACK_TYPE_R8:
4234 interp_add_ins (td, MINT_CONV_I4_R8);
4235 break;
4236 case STACK_TYPE_I4:
4237 break;
4238 case STACK_TYPE_I8:
4239 interp_add_ins (td, MINT_CONV_I4_I8);
4240 break;
4241 case STACK_TYPE_MP:
4242 #if SIZEOF_VOID_P == 8
4243 interp_add_ins (td, MINT_CONV_I4_I8);
4244 #endif
4245 break;
4246 default:
4247 g_assert_not_reached ();
4249 ++td->ip;
4250 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
4251 break;
4252 case CEE_CONV_I8:
4253 CHECK_STACK (td, 1);
4254 switch (td->sp [-1].type) {
4255 case STACK_TYPE_R4:
4256 interp_add_ins (td, MINT_CONV_I8_R4);
4257 break;
4258 case STACK_TYPE_R8:
4259 interp_add_ins (td, MINT_CONV_I8_R8);
4260 break;
4261 case STACK_TYPE_I4: {
4262 if (interp_ins_is_ldc (td->last_ins) && (inlining || !td->is_bb_start [in_offset])) {
4263 gint64 ct = interp_get_const_from_ldc_i4 (td->last_ins);
4264 interp_clear_ins (td, td->last_ins);
4266 interp_add_ins (td, MINT_LDC_I8);
4267 WRITE64_INS (td->last_ins, 0, &ct);
4268 } else {
4269 interp_add_ins (td, MINT_CONV_I8_I4);
4271 break;
4273 case STACK_TYPE_I8:
4274 break;
4275 case STACK_TYPE_MP:
4276 #if SIZEOF_VOID_P == 4
4277 interp_add_ins (td, MINT_CONV_I8_I4);
4278 #endif
4279 break;
4280 default:
4281 g_assert_not_reached ();
4283 ++td->ip;
4284 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4285 break;
4286 case CEE_CONV_R4:
4287 CHECK_STACK (td, 1);
4288 switch (td->sp [-1].type) {
4289 case STACK_TYPE_R8:
4290 interp_add_ins (td, MINT_CONV_R4_R8);
4291 break;
4292 case STACK_TYPE_I8:
4293 interp_add_ins (td, MINT_CONV_R4_I8);
4294 break;
4295 case STACK_TYPE_I4:
4296 interp_add_ins (td, MINT_CONV_R4_I4);
4297 break;
4298 case STACK_TYPE_R4:
4299 /* no-op */
4300 break;
4301 default:
4302 g_assert_not_reached ();
4304 ++td->ip;
4305 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
4306 break;
4307 case CEE_CONV_R8:
4308 CHECK_STACK (td, 1);
4309 switch (td->sp [-1].type) {
4310 case STACK_TYPE_I4:
4311 interp_add_ins (td, MINT_CONV_R8_I4);
4312 break;
4313 case STACK_TYPE_I8:
4314 interp_add_ins (td, MINT_CONV_R8_I8);
4315 break;
4316 case STACK_TYPE_R4:
4317 interp_add_ins (td, MINT_CONV_R8_R4);
4318 break;
4319 case STACK_TYPE_R8:
4320 break;
4321 default:
4322 g_assert_not_reached ();
4324 ++td->ip;
4325 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4326 break;
4327 case CEE_CONV_U8:
4328 CHECK_STACK (td, 1);
4329 switch (td->sp [-1].type) {
4330 case STACK_TYPE_I4:
4331 if (interp_ins_is_ldc (td->last_ins) && (inlining || !td->is_bb_start [in_offset])) {
4332 gint64 ct = (guint32)interp_get_const_from_ldc_i4 (td->last_ins);
4333 interp_clear_ins (td, td->last_ins);
4335 interp_add_ins (td, MINT_LDC_I8);
4336 WRITE64_INS (td->last_ins, 0, &ct);
4337 } else {
4338 interp_add_ins (td, MINT_CONV_I8_U4);
4340 break;
4341 case STACK_TYPE_I8:
4342 break;
4343 case STACK_TYPE_R4:
4344 interp_add_ins (td, MINT_CONV_U8_R4);
4345 break;
4346 case STACK_TYPE_R8:
4347 interp_add_ins (td, MINT_CONV_U8_R8);
4348 break;
4349 case STACK_TYPE_MP:
4350 #if SIZEOF_VOID_P == 4
4351 interp_add_ins (td, MINT_CONV_I8_U4);
4352 #endif
4353 break;
4354 default:
4355 g_assert_not_reached ();
4357 ++td->ip;
4358 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4359 break;
4360 case CEE_CPOBJ: {
4361 CHECK_STACK (td, 2);
4363 token = read32 (td->ip + 1);
4364 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4365 goto_if_nok (error, exit);
4367 if (m_class_is_valuetype (klass)) {
4368 int mt = mint_type (m_class_get_byval_arg (klass));
4369 interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_CPOBJ_VT : MINT_CPOBJ);
4370 td->last_ins->data [0] = get_data_item_index(td, klass);
4371 } else {
4372 interp_add_ins (td, MINT_LDIND_REF);
4373 interp_add_ins (td, MINT_STIND_REF);
4375 td->ip += 5;
4376 td->sp -= 2;
4377 break;
4379 case CEE_LDOBJ: {
4380 CHECK_STACK (td, 1);
4382 token = read32 (td->ip + 1);
4384 if (method->wrapper_type != MONO_WRAPPER_NONE)
4385 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4386 else {
4387 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4388 goto_if_nok (error, exit);
4391 interp_emit_ldobj (td, klass);
4393 td->ip += 5;
4394 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
4395 break;
4397 case CEE_LDSTR: {
4398 token = mono_metadata_token_index (read32 (td->ip + 1));
4399 td->ip += 5;
4400 if (method->wrapper_type == MONO_WRAPPER_NONE) {
4401 MonoString *s = mono_ldstr_checked (domain, image, token, error);
4402 goto_if_nok (error, exit);
4403 /* GC won't scan code stream, but reference is held by metadata
4404 * machinery so we are good here */
4405 interp_add_ins (td, MINT_LDSTR);
4406 td->last_ins->data [0] = get_data_item_index (td, s);
4407 } else {
4408 /* defer allocation to execution-time */
4409 interp_add_ins (td, MINT_LDSTR_TOKEN);
4410 td->last_ins->data [0] = get_data_item_index (td, GUINT_TO_POINTER (token));
4412 PUSH_TYPE(td, STACK_TYPE_O, mono_defaults.string_class);
4413 break;
4415 case CEE_NEWOBJ: {
4416 MonoMethod *m;
4417 MonoMethodSignature *csignature;
4418 guint32 vt_stack_used = 0;
4419 guint32 vt_res_size = 0;
4421 td->ip++;
4422 token = read32 (td->ip);
4423 td->ip += 4;
4425 m = interp_get_method (method, token, image, generic_context, error);
4426 goto_if_nok (error, exit);
4428 csignature = mono_method_signature_internal (m);
4429 klass = m->klass;
4431 if (!mono_class_init_internal (klass)) {
4432 mono_error_set_for_class_failure (error, klass);
4433 goto_if_nok (error, exit);
4436 if (mono_class_get_flags (klass) & TYPE_ATTRIBUTE_ABSTRACT) {
4437 char* full_name = mono_type_get_full_name (klass);
4438 mono_error_set_member_access (error, "Cannot create an abstract class: %s", full_name);
4439 g_free (full_name);
4440 goto_if_nok (error, exit);
4443 if (mono_class_is_magic_int (klass) || mono_class_is_magic_float (klass)) {
4444 td->sp -= csignature->param_count;
4445 #if SIZEOF_VOID_P == 8
4446 if (mono_class_is_magic_int (klass) && td->sp [0].type == STACK_TYPE_I4)
4447 interp_add_ins (td, MINT_CONV_I8_I4);
4448 else if (mono_class_is_magic_float (klass) && td->sp [0].type == STACK_TYPE_R4)
4449 interp_add_ins (td, MINT_CONV_R8_R4);
4450 #endif
4451 interp_add_ins (td, MINT_NEWOBJ_MAGIC);
4452 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4453 goto_if_nok (error, exit);
4455 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4456 } else {
4457 if (m_class_get_parent (klass) == mono_defaults.array_class) {
4458 interp_add_ins (td, MINT_NEWOBJ_ARRAY);
4459 td->last_ins->data [0] = get_data_item_index (td, m->klass);
4460 td->last_ins->data [1] = csignature->param_count;
4461 } else if (m_class_get_image (klass) == mono_defaults.corlib &&
4462 !strcmp (m_class_get_name (m->klass), "ByReference`1") &&
4463 !strcmp (m->name, ".ctor")) {
4464 /* public ByReference(ref T value) */
4465 g_assert (csignature->hasthis && csignature->param_count == 1);
4466 interp_add_ins (td, MINT_INTRINS_BYREFERENCE_CTOR);
4467 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4468 } else if (klass != mono_defaults.string_class &&
4469 !mono_class_is_marshalbyref (klass) &&
4470 !mono_class_has_finalizer (klass) &&
4471 !m_class_has_weak_fields (klass)) {
4472 if (!m_class_is_valuetype (klass)) {
4473 InterpInst *newobj_fast = interp_add_ins (td, MINT_NEWOBJ_FAST);
4475 newobj_fast->data [1] = csignature->param_count;
4477 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
4478 goto_if_nok (error, exit);
4479 newobj_fast->data [2] = get_data_item_index (td, vtable);
4481 move_stack (td, (td->sp - td->stack) - csignature->param_count, 2);
4483 StackInfo *tmp_sp = td->sp - csignature->param_count - 2;
4484 SET_TYPE (tmp_sp, STACK_TYPE_O, klass);
4485 SET_TYPE (tmp_sp + 1, STACK_TYPE_O, klass);
4487 if ((mono_interp_opt & INTERP_OPT_INLINE) && interp_method_check_inlining (td, m)) {
4488 MonoMethodHeader *mheader = interp_method_get_header (m, error);
4489 goto_if_nok (error, exit);
4491 if (interp_inline_method (td, m, mheader, error)) {
4492 newobj_fast->data [0] = INLINED_METHOD_FLAG;
4493 break;
4496 // If inlining failed we need to restore the stack
4497 move_stack (td, (td->sp - td->stack) - csignature->param_count, -2);
4498 // Set the method to be executed as part of newobj instruction
4499 newobj_fast->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4500 } else {
4501 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4502 interp_add_ins (td, MINT_NEWOBJ_VTST_FAST);
4503 else
4504 interp_add_ins (td, MINT_NEWOBJ_VT_FAST);
4506 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4507 td->last_ins->data [1] = csignature->param_count;
4509 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4510 td->last_ins->data [2] = mono_class_value_size (klass, NULL);
4513 } else {
4514 interp_add_ins (td, MINT_NEWOBJ);
4515 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
4517 goto_if_nok (error, exit);
4518 /* The constructor was not inlined, abort inlining of current method */
4519 INLINE_FAILURE;
4521 td->sp -= csignature->param_count;
4522 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4523 vt_res_size = mono_class_value_size (klass, NULL);
4524 PUSH_VT (td, vt_res_size);
4526 for (i = 0; i < csignature->param_count; ++i) {
4527 int mt = mint_type(csignature->params [i]);
4528 if (mt == MINT_TYPE_VT) {
4529 MonoClass *k = mono_class_from_mono_type_internal (csignature->params [i]);
4530 gint32 size = mono_class_value_size (k, NULL);
4531 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4532 vt_stack_used += size;
4535 if (vt_stack_used != 0 || vt_res_size != 0) {
4536 interp_add_ins (td, MINT_VTRESULT);
4537 td->last_ins->data [0] = vt_res_size;
4538 WRITE32_INS (td->last_ins, 1, &vt_stack_used);
4539 td->vt_sp -= vt_stack_used;
4541 PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
4543 break;
4545 case CEE_CASTCLASS:
4546 case CEE_ISINST: {
4547 gboolean isinst_instr = *td->ip == CEE_ISINST;
4548 CHECK_STACK (td, 1);
4549 token = read32 (td->ip + 1);
4550 klass = mini_get_class (method, token, generic_context);
4551 CHECK_TYPELOAD (klass);
4552 interp_handle_isinst (td, klass, isinst_instr);
4553 if (!isinst_instr)
4554 td->sp [-1].klass = klass;
4555 break;
4557 case CEE_CONV_R_UN:
4558 switch (td->sp [-1].type) {
4559 case STACK_TYPE_R8:
4560 break;
4561 case STACK_TYPE_I8:
4562 interp_add_ins (td, MINT_CONV_R_UN_I8);
4563 break;
4564 case STACK_TYPE_I4:
4565 interp_add_ins (td, MINT_CONV_R_UN_I4);
4566 break;
4567 default:
4568 g_assert_not_reached ();
4570 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
4571 ++td->ip;
4572 break;
4573 case CEE_UNBOX:
4574 CHECK_STACK (td, 1);
4575 token = read32 (td->ip + 1);
4577 if (method->wrapper_type != MONO_WRAPPER_NONE)
4578 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4579 else {
4580 klass = mono_class_get_and_inflate_typespec_checked (image, token, generic_context, error);
4581 goto_if_nok (error, exit);
4584 if (mono_class_is_nullable (klass)) {
4585 MonoMethod *target_method;
4586 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4587 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4588 else
4589 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4590 goto_if_nok (error, exit);
4591 /* td->ip is incremented by interp_transform_call */
4592 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4593 goto exit;
4595 * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type
4596 * We create a local variable in the frame so that we can fetch its address.
4598 int local = create_interp_local (td, m_class_get_byval_arg (klass));
4599 store_local (td, local);
4600 interp_add_ins (td, MINT_LDLOCA_S);
4601 td->last_ins->data [0] = local;
4602 td->locals [local].flags |= INTERP_LOCAL_FLAG_INDIRECT;
4603 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
4604 } else {
4605 interp_add_ins (td, MINT_UNBOX);
4606 td->last_ins->data [0] = get_data_item_index (td, klass);
4607 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
4608 td->ip += 5;
4610 break;
4611 case CEE_UNBOX_ANY:
4612 CHECK_STACK (td, 1);
4613 token = read32 (td->ip + 1);
4615 klass = mini_get_class (method, token, generic_context);
4616 CHECK_TYPELOAD (klass);
4618 if (mini_type_is_reference (m_class_get_byval_arg (klass))) {
4619 int mt = mint_type (m_class_get_byval_arg (klass));
4620 interp_handle_isinst (td, klass, FALSE);
4621 SET_TYPE (td->sp - 1, stack_type [mt], klass);
4622 } else if (mono_class_is_nullable (klass)) {
4623 MonoMethod *target_method;
4624 if (m_class_is_enumtype (mono_class_get_nullable_param_internal (klass)))
4625 target_method = mono_class_get_method_from_name_checked (klass, "UnboxExact", 1, 0, error);
4626 else
4627 target_method = mono_class_get_method_from_name_checked (klass, "Unbox", 1, 0, error);
4628 goto_if_nok (error, exit);
4629 /* td->ip is incremented by interp_transform_call */
4630 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4631 goto exit;
4632 } else {
4633 interp_add_ins (td, MINT_UNBOX);
4634 td->last_ins->data [0] = get_data_item_index (td, klass);
4636 interp_emit_ldobj (td, klass);
4638 td->ip += 5;
4641 break;
4642 case CEE_THROW:
4643 INLINE_FAILURE;
4644 CHECK_STACK (td, 1);
4645 SIMPLE_OP (td, MINT_THROW);
4646 td->sp = td->stack;
4647 break;
4648 case CEE_LDFLDA: {
4649 CHECK_STACK (td, 1);
4650 token = read32 (td->ip + 1);
4651 field = interp_field_from_token (method, token, &klass, generic_context, error);
4652 goto_if_nok (error, exit);
4653 MonoType *ftype = mono_field_get_type_internal (field);
4654 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4655 mono_class_init_internal (klass);
4656 #ifndef DISABLE_REMOTING
4657 if (m_class_get_marshalbyref (klass) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
4658 g_assert (!is_static);
4659 int offset = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4661 interp_add_ins (td, MINT_MONO_LDPTR);
4662 td->last_ins->data [0] = get_data_item_index (td, klass);
4663 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4664 interp_add_ins (td, MINT_MONO_LDPTR);
4665 td->last_ins->data [0] = get_data_item_index (td, field);
4666 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
4667 interp_add_ins (td, MINT_LDC_I4);
4668 WRITE32_INS (td->last_ins, 0, &offset);
4669 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
4670 #if SIZEOF_VOID_P == 8
4671 interp_add_ins (td, MINT_CONV_I8_I4);
4672 #endif
4674 MonoMethod *wrapper = mono_marshal_get_ldflda_wrapper (field->type);
4675 /* td->ip is incremented by interp_transform_call */
4676 if (!interp_transform_call (td, method, wrapper, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4677 goto exit;
4678 } else
4679 #endif
4681 if (is_static) {
4682 interp_add_ins (td, MINT_POP);
4683 interp_emit_ldsflda (td, field, error);
4684 goto_if_nok (error, exit);
4685 } else {
4686 if ((td->sp - 1)->type == STACK_TYPE_O) {
4687 interp_add_ins (td, MINT_LDFLDA);
4688 } else {
4689 g_assert ((td->sp -1)->type == STACK_TYPE_MP);
4690 interp_add_ins (td, MINT_LDFLDA_UNSAFE);
4692 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4694 td->ip += 5;
4696 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
4697 break;
4699 case CEE_LDFLD: {
4700 CHECK_STACK (td, 1);
4701 token = read32 (td->ip + 1);
4702 field = interp_field_from_token (method, token, &klass, generic_context, error);
4703 goto_if_nok (error, exit);
4704 MonoType *ftype = mono_field_get_type_internal (field);
4705 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4706 mono_class_init_internal (klass);
4708 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4709 mt = mint_type (m_class_get_byval_arg (field_klass));
4710 #ifndef DISABLE_REMOTING
4711 if ((m_class_get_marshalbyref (klass) && !(signature->hasthis && td->last_ins->opcode == MINT_LDARG_P0)) ||
4712 mono_class_is_contextbound (klass) ||
4713 klass == mono_defaults.marshalbyrefobject_class) {
4714 g_assert (!is_static);
4715 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDRMFLD_VT : MINT_LDRMFLD);
4716 td->last_ins->data [0] = get_data_item_index (td, field);
4717 } else
4718 #endif
4720 if (is_static) {
4721 interp_add_ins (td, MINT_POP);
4722 interp_emit_sfld_access (td, field, field_klass, mt, TRUE, error);
4723 goto_if_nok (error, exit);
4724 } else if (td->sp [-1].type != STACK_TYPE_O && td->sp [-1].type != STACK_TYPE_MP && (mono_class_is_magic_int (klass) || mono_class_is_magic_float (klass))) {
4725 // No need to load anything, the value is already on the execution stack
4726 } else {
4727 int opcode = MINT_LDFLD_I1 + mt - MINT_TYPE_I1;
4728 #ifdef NO_UNALIGNED_ACCESS
4729 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4730 opcode = get_unaligned_opcode (opcode);
4731 #endif
4732 interp_add_ins (td, opcode);
4733 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4734 if (mt == MINT_TYPE_VT) {
4735 int size = mono_class_value_size (field_klass, NULL);
4736 WRITE32_INS (td->last_ins, 1, &size);
4740 if (mt == MINT_TYPE_VT) {
4741 int size = mono_class_value_size (field_klass, NULL);
4742 PUSH_VT (td, size);
4744 if (td->sp [-1].type == STACK_TYPE_VT) {
4745 int size = mono_class_value_size (klass, NULL);
4746 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4747 int field_vt_size = 0;
4748 if (mt == MINT_TYPE_VT) {
4750 * Pop the loaded field from the vtstack (it will still be present
4751 * at the same vtstack address) and we will load it in place of the
4752 * containing valuetype with the second MINT_VTRESULT.
4754 field_vt_size = mono_class_value_size (field_klass, NULL);
4755 field_vt_size = ALIGN_TO (field_vt_size, MINT_VT_ALIGNMENT);
4756 interp_add_ins (td, MINT_VTRESULT);
4757 td->last_ins->data [0] = 0;
4758 WRITE32_INS (td->last_ins, 1, &field_vt_size);
4760 td->vt_sp -= size;
4761 interp_add_ins (td, MINT_VTRESULT);
4762 td->last_ins->data [0] = field_vt_size;
4763 WRITE32_INS (td->last_ins, 1, &size);
4765 td->ip += 5;
4766 SET_TYPE (td->sp - 1, stack_type [mt], field_klass);
4767 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
4768 break;
4770 case CEE_STFLD: {
4771 CHECK_STACK (td, 2);
4772 token = read32 (td->ip + 1);
4773 field = interp_field_from_token (method, token, &klass, generic_context, error);
4774 goto_if_nok (error, exit);
4775 MonoType *ftype = mono_field_get_type_internal (field);
4776 gboolean is_static = !!(ftype->attrs & FIELD_ATTRIBUTE_STATIC);
4777 MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
4778 mono_class_init_internal (klass);
4779 mt = mint_type (ftype);
4781 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4783 #ifndef DISABLE_REMOTING
4784 if (m_class_get_marshalbyref (klass)) {
4785 g_assert (!is_static);
4786 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_STRMFLD_VT : MINT_STRMFLD);
4787 td->last_ins->data [0] = get_data_item_index (td, field);
4788 } else
4789 #endif
4791 if (is_static) {
4792 interp_emit_sfld_access (td, field, field_klass, mt, FALSE, error);
4793 goto_if_nok (error, exit);
4795 /* pop the unused object reference */
4796 interp_add_ins (td, MINT_POP);
4798 /* the vtable of the field might not be initialized at this point */
4799 mono_class_vtable_checked (domain, field_klass, error);
4800 goto_if_nok (error, exit);
4801 } else {
4802 int opcode = MINT_STFLD_I1 + mt - MINT_TYPE_I1;
4803 #ifdef NO_UNALIGNED_ACCESS
4804 if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && field->offset % SIZEOF_VOID_P != 0)
4805 opcode = get_unaligned_opcode (opcode);
4806 #endif
4807 interp_add_ins (td, opcode);
4808 td->last_ins->data [0] = m_class_is_valuetype (klass) ? field->offset - MONO_ABI_SIZEOF (MonoObject) : field->offset;
4809 if (mt == MINT_TYPE_VT) {
4810 /* the vtable of the field might not be initialized at this point */
4811 mono_class_vtable_checked (domain, field_klass, error);
4812 goto_if_nok (error, exit);
4814 td->last_ins->data [1] = get_data_item_index (td, field_klass);
4818 if (mt == MINT_TYPE_VT) {
4819 int size = mono_class_value_size (field_klass, NULL);
4820 POP_VT (td, size);
4822 td->ip += 5;
4823 td->sp -= 2;
4824 break;
4826 case CEE_LDSFLDA: {
4827 token = read32 (td->ip + 1);
4828 field = interp_field_from_token (method, token, &klass, generic_context, error);
4829 goto_if_nok (error, exit);
4830 interp_emit_ldsflda (td, field, error);
4831 goto_if_nok (error, exit);
4832 td->ip += 5;
4833 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
4834 break;
4836 case CEE_LDSFLD: {
4837 token = read32 (td->ip + 1);
4838 field = interp_field_from_token (method, token, &klass, generic_context, error);
4839 goto_if_nok (error, exit);
4840 MonoType *ftype = mono_field_get_type_internal (field);
4841 mt = mint_type (ftype);
4842 klass = mono_class_from_mono_type_internal (ftype);
4844 interp_emit_sfld_access (td, field, klass, mt, TRUE, error);
4845 goto_if_nok (error, exit);
4847 if (mt == MINT_TYPE_VT) {
4848 int size = mono_class_value_size (klass, NULL);
4849 PUSH_VT(td, size);
4851 td->ip += 5;
4852 PUSH_TYPE(td, stack_type [mt], klass);
4853 break;
4855 case CEE_STSFLD: {
4856 CHECK_STACK (td, 1);
4857 token = read32 (td->ip + 1);
4858 field = interp_field_from_token (method, token, &klass, generic_context, error);
4859 goto_if_nok (error, exit);
4860 MonoType *ftype = mono_field_get_type_internal (field);
4861 mt = mint_type (ftype);
4863 emit_convert (td, td->sp [-1].type, ftype);
4865 /* the vtable of the field might not be initialized at this point */
4866 MonoClass *fld_klass = mono_class_from_mono_type_internal (ftype);
4867 mono_class_vtable_checked (domain, fld_klass, error);
4868 goto_if_nok (error, exit);
4870 interp_emit_sfld_access (td, field, fld_klass, mt, FALSE, error);
4871 goto_if_nok (error, exit);
4873 if (mt == MINT_TYPE_VT) {
4874 int size = mono_class_value_size (fld_klass, NULL);
4875 POP_VT(td, size);
4877 td->ip += 5;
4878 --td->sp;
4879 break;
4881 case CEE_STOBJ: {
4882 token = read32 (td->ip + 1);
4884 if (method->wrapper_type != MONO_WRAPPER_NONE)
4885 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4886 else
4887 klass = mini_get_class (method, token, generic_context);
4888 CHECK_TYPELOAD (klass);
4890 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
4892 interp_emit_stobj (td, klass);
4894 td->ip += 5;
4895 break;
4897 case CEE_CONV_OVF_I_UN:
4898 case CEE_CONV_OVF_U_UN:
4899 CHECK_STACK (td, 1);
4900 switch (td->sp [-1].type) {
4901 case STACK_TYPE_R8:
4902 #if SIZEOF_VOID_P == 8
4903 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4904 #else
4905 interp_add_ins (td, MINT_CONV_OVF_I4_UN_R8);
4906 #endif
4907 break;
4908 case STACK_TYPE_I8:
4909 #if SIZEOF_VOID_P == 4
4910 interp_add_ins (td, MINT_CONV_OVF_I4_UN_I8);
4911 #endif
4912 break;
4913 case STACK_TYPE_I4:
4914 #if SIZEOF_VOID_P == 8
4915 interp_add_ins (td, MINT_CONV_I8_U4);
4916 #elif SIZEOF_VOID_P == 4
4917 if (*td->ip == CEE_CONV_OVF_I_UN)
4918 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
4919 #endif
4920 break;
4921 default:
4922 g_assert_not_reached ();
4923 break;
4925 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
4926 ++td->ip;
4927 break;
4928 case CEE_CONV_OVF_I8_UN:
4929 case CEE_CONV_OVF_U8_UN:
4930 CHECK_STACK (td, 1);
4931 switch (td->sp [-1].type) {
4932 case STACK_TYPE_R8:
4933 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R8);
4934 break;
4935 case STACK_TYPE_I8:
4936 if (*td->ip == CEE_CONV_OVF_I8_UN)
4937 interp_add_ins (td, MINT_CONV_OVF_I8_U8);
4938 break;
4939 case STACK_TYPE_I4:
4940 interp_add_ins (td, MINT_CONV_I8_U4);
4941 break;
4942 case STACK_TYPE_R4:
4943 interp_add_ins (td, MINT_CONV_OVF_I8_UN_R4);
4944 break;
4945 default:
4946 g_assert_not_reached ();
4947 break;
4949 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
4950 ++td->ip;
4951 break;
4952 case CEE_BOX: {
4953 int size;
4954 CHECK_STACK (td, 1);
4955 token = read32 (td->ip + 1);
4956 if (method->wrapper_type != MONO_WRAPPER_NONE)
4957 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
4958 else
4959 klass = mini_get_class (method, token, generic_context);
4960 CHECK_TYPELOAD (klass);
4962 if (mono_class_is_nullable (klass)) {
4963 MonoMethod *target_method = mono_class_get_method_from_name_checked (klass, "Box", 1, 0, error);
4964 goto_if_nok (error, exit);
4965 /* td->ip is incremented by interp_transform_call */
4966 if (!interp_transform_call (td, method, target_method, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
4967 goto exit;
4968 } else if (!m_class_is_valuetype (klass)) {
4969 /* already boxed, do nothing. */
4970 td->ip += 5;
4971 } else {
4972 if (G_UNLIKELY (m_class_is_byreflike (klass))) {
4973 mono_error_set_bad_image (error, image, "Cannot box IsByRefLike type '%s.%s'", m_class_get_name_space (klass), m_class_get_name (klass));
4974 goto exit;
4976 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
4977 size = mono_class_value_size (klass, NULL);
4978 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
4979 td->vt_sp -= size;
4980 } else if (td->sp [-1].type == STACK_TYPE_R8 && m_class_get_byval_arg (klass)->type == MONO_TYPE_R4) {
4981 interp_add_ins (td, MINT_CONV_R4_R8);
4983 MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
4984 goto_if_nok (error, exit);
4986 if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
4987 interp_add_ins (td, MINT_BOX_VT);
4988 else
4989 interp_add_ins (td, MINT_BOX);
4990 td->last_ins->data [0] = get_data_item_index (td, vtable);
4991 td->last_ins->data [1] = 0;
4992 SET_TYPE(td->sp - 1, STACK_TYPE_O, klass);
4993 td->ip += 5;
4996 break;
4998 case CEE_NEWARR: {
4999 CHECK_STACK (td, 1);
5000 token = read32 (td->ip + 1);
5002 if (method->wrapper_type != MONO_WRAPPER_NONE)
5003 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5004 else
5005 klass = mini_get_class (method, token, generic_context);
5006 CHECK_TYPELOAD (klass);
5008 MonoClass *array_class = mono_class_create_array (klass, 1);
5009 MonoVTable *vtable = mono_class_vtable_checked (domain, array_class, error);
5010 goto_if_nok (error, exit);
5012 unsigned char lentype = (td->sp - 1)->type;
5013 if (lentype == STACK_TYPE_I8) {
5014 /* mimic mini behaviour */
5015 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
5016 } else {
5017 g_assert (lentype == STACK_TYPE_I4);
5018 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
5020 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
5021 interp_add_ins (td, MINT_NEWARR);
5022 td->last_ins->data [0] = get_data_item_index (td, vtable);
5023 SET_TYPE (td->sp - 1, STACK_TYPE_O, array_class);
5024 td->ip += 5;
5025 break;
5027 case CEE_LDLEN:
5028 CHECK_STACK (td, 1);
5029 SIMPLE_OP (td, MINT_LDLEN);
5030 #ifdef MONO_BIG_ARRAYS
5031 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I8);
5032 #else
5033 SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
5034 #endif
5035 break;
5036 case CEE_LDELEMA: {
5037 gint32 size;
5038 CHECK_STACK (td, 2);
5039 ENSURE_I4 (td, 1);
5040 token = read32 (td->ip + 1);
5042 if (method->wrapper_type != MONO_WRAPPER_NONE)
5043 klass = (MonoClass *) mono_method_get_wrapper_data (method, token);
5044 else
5045 klass = mini_get_class (method, token, generic_context);
5047 CHECK_TYPELOAD (klass);
5049 if (!m_class_is_valuetype (klass) && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
5051 * Check the class for failures before the type check, which can
5052 * throw other exceptions.
5054 mono_class_setup_vtable (klass);
5055 CHECK_TYPELOAD (klass);
5056 interp_add_ins (td, MINT_LDELEMA_TC);
5057 td->last_ins->data [0] = 1;
5058 td->last_ins->data [1] = get_data_item_index (td, klass);
5059 } else {
5060 interp_add_ins (td, MINT_LDELEMA1);
5061 mono_class_init_internal (klass);
5062 size = mono_class_array_element_size (klass);
5063 WRITE32_INS (td->last_ins, 0, &size);
5066 readonly = FALSE;
5068 td->ip += 5;
5069 --td->sp;
5070 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5071 break;
5073 case CEE_LDELEM_I1:
5074 CHECK_STACK (td, 2);
5075 ENSURE_I4 (td, 1);
5076 SIMPLE_OP (td, MINT_LDELEM_I1);
5077 --td->sp;
5078 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5079 break;
5080 case CEE_LDELEM_U1:
5081 CHECK_STACK (td, 2);
5082 ENSURE_I4 (td, 1);
5083 SIMPLE_OP (td, MINT_LDELEM_U1);
5084 --td->sp;
5085 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5086 break;
5087 case CEE_LDELEM_I2:
5088 CHECK_STACK (td, 2);
5089 ENSURE_I4 (td, 1);
5090 SIMPLE_OP (td, MINT_LDELEM_I2);
5091 --td->sp;
5092 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5093 break;
5094 case CEE_LDELEM_U2:
5095 CHECK_STACK (td, 2);
5096 ENSURE_I4 (td, 1);
5097 SIMPLE_OP (td, MINT_LDELEM_U2);
5098 --td->sp;
5099 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5100 break;
5101 case CEE_LDELEM_I4:
5102 CHECK_STACK (td, 2);
5103 ENSURE_I4 (td, 1);
5104 SIMPLE_OP (td, MINT_LDELEM_I4);
5105 --td->sp;
5106 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5107 break;
5108 case CEE_LDELEM_U4:
5109 CHECK_STACK (td, 2);
5110 ENSURE_I4 (td, 1);
5111 SIMPLE_OP (td, MINT_LDELEM_U4);
5112 --td->sp;
5113 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5114 break;
5115 case CEE_LDELEM_I8:
5116 CHECK_STACK (td, 2);
5117 ENSURE_I4 (td, 1);
5118 SIMPLE_OP (td, MINT_LDELEM_I8);
5119 --td->sp;
5120 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5121 break;
5122 case CEE_LDELEM_I:
5123 CHECK_STACK (td, 2);
5124 ENSURE_I4 (td, 1);
5125 SIMPLE_OP (td, MINT_LDELEM_I);
5126 --td->sp;
5127 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I);
5128 break;
5129 case CEE_LDELEM_R4:
5130 CHECK_STACK (td, 2);
5131 ENSURE_I4 (td, 1);
5132 SIMPLE_OP (td, MINT_LDELEM_R4);
5133 --td->sp;
5134 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
5135 break;
5136 case CEE_LDELEM_R8:
5137 CHECK_STACK (td, 2);
5138 ENSURE_I4 (td, 1);
5139 SIMPLE_OP (td, MINT_LDELEM_R8);
5140 --td->sp;
5141 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
5142 break;
5143 case CEE_LDELEM_REF:
5144 CHECK_STACK (td, 2);
5145 ENSURE_I4 (td, 1);
5146 SIMPLE_OP (td, MINT_LDELEM_REF);
5147 --td->sp;
5148 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
5149 break;
5150 case CEE_LDELEM:
5151 CHECK_STACK (td, 2);
5152 token = read32 (td->ip + 1);
5153 klass = mini_get_class (method, token, generic_context);
5154 CHECK_TYPELOAD (klass);
5155 switch (mint_type (m_class_get_byval_arg (klass))) {
5156 case MINT_TYPE_I1:
5157 ENSURE_I4 (td, 1);
5158 SIMPLE_OP (td, MINT_LDELEM_I1);
5159 --td->sp;
5160 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5161 break;
5162 case MINT_TYPE_U1:
5163 ENSURE_I4 (td, 1);
5164 SIMPLE_OP (td, MINT_LDELEM_U1);
5165 --td->sp;
5166 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5167 break;
5168 case MINT_TYPE_U2:
5169 ENSURE_I4 (td, 1);
5170 SIMPLE_OP (td, MINT_LDELEM_U2);
5171 --td->sp;
5172 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5173 break;
5174 case MINT_TYPE_I2:
5175 ENSURE_I4 (td, 1);
5176 SIMPLE_OP (td, MINT_LDELEM_I2);
5177 --td->sp;
5178 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5179 break;
5180 case MINT_TYPE_I4:
5181 ENSURE_I4 (td, 1);
5182 SIMPLE_OP (td, MINT_LDELEM_I4);
5183 --td->sp;
5184 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5185 break;
5186 case MINT_TYPE_I8:
5187 ENSURE_I4 (td, 1);
5188 SIMPLE_OP (td, MINT_LDELEM_I8);
5189 --td->sp;
5190 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5191 break;
5192 case MINT_TYPE_R4:
5193 ENSURE_I4 (td, 1);
5194 SIMPLE_OP (td, MINT_LDELEM_R4);
5195 --td->sp;
5196 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R4);
5197 break;
5198 case MINT_TYPE_R8:
5199 ENSURE_I4 (td, 1);
5200 SIMPLE_OP (td, MINT_LDELEM_R8);
5201 --td->sp;
5202 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_R8);
5203 break;
5204 case MINT_TYPE_O:
5205 ENSURE_I4 (td, 1);
5206 SIMPLE_OP (td, MINT_LDELEM_REF);
5207 --td->sp;
5208 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_O);
5209 break;
5210 case MINT_TYPE_VT: {
5211 int size = mono_class_value_size (klass, NULL);
5212 ENSURE_I4 (td, 1);
5213 SIMPLE_OP (td, MINT_LDELEM_VT);
5214 WRITE32_INS (td->last_ins, 0, &size);
5215 --td->sp;
5216 SET_TYPE (td->sp - 1, STACK_TYPE_VT, klass);
5217 PUSH_VT (td, size);
5218 break;
5220 default: {
5221 GString *res = g_string_new ("");
5222 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
5223 g_print ("LDELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
5224 g_string_free (res, TRUE);
5225 g_assert (0);
5226 break;
5229 td->ip += 4;
5230 break;
5231 case CEE_STELEM_I:
5232 CHECK_STACK (td, 3);
5233 ENSURE_I4 (td, 2);
5234 SIMPLE_OP (td, MINT_STELEM_I);
5235 td->sp -= 3;
5236 break;
5237 case CEE_STELEM_I1:
5238 CHECK_STACK (td, 3);
5239 ENSURE_I4 (td, 2);
5240 SIMPLE_OP (td, MINT_STELEM_I1);
5241 td->sp -= 3;
5242 break;
5243 case CEE_STELEM_I2:
5244 CHECK_STACK (td, 3);
5245 ENSURE_I4 (td, 2);
5246 SIMPLE_OP (td, MINT_STELEM_I2);
5247 td->sp -= 3;
5248 break;
5249 case CEE_STELEM_I4:
5250 CHECK_STACK (td, 3);
5251 ENSURE_I4 (td, 2);
5252 SIMPLE_OP (td, MINT_STELEM_I4);
5253 td->sp -= 3;
5254 break;
5255 case CEE_STELEM_I8:
5256 CHECK_STACK (td, 3);
5257 ENSURE_I4 (td, 2);
5258 SIMPLE_OP (td, MINT_STELEM_I8);
5259 td->sp -= 3;
5260 break;
5261 case CEE_STELEM_R4:
5262 CHECK_STACK (td, 3);
5263 ENSURE_I4 (td, 2);
5264 SIMPLE_OP (td, MINT_STELEM_R4);
5265 td->sp -= 3;
5266 break;
5267 case CEE_STELEM_R8:
5268 CHECK_STACK (td, 3);
5269 ENSURE_I4 (td, 2);
5270 SIMPLE_OP (td, MINT_STELEM_R8);
5271 td->sp -= 3;
5272 break;
5273 case CEE_STELEM_REF:
5274 CHECK_STACK (td, 3);
5275 ENSURE_I4 (td, 2);
5276 SIMPLE_OP (td, MINT_STELEM_REF);
5277 td->sp -= 3;
5278 break;
5279 case CEE_STELEM:
5280 CHECK_STACK (td, 3);
5281 ENSURE_I4 (td, 2);
5282 token = read32 (td->ip + 1);
5283 klass = mini_get_class (method, token, generic_context);
5284 CHECK_TYPELOAD (klass);
5285 switch (mint_type (m_class_get_byval_arg (klass))) {
5286 case MINT_TYPE_I1:
5287 SIMPLE_OP (td, MINT_STELEM_I1);
5288 break;
5289 case MINT_TYPE_U1:
5290 SIMPLE_OP (td, MINT_STELEM_U1);
5291 break;
5292 case MINT_TYPE_I2:
5293 SIMPLE_OP (td, MINT_STELEM_I2);
5294 break;
5295 case MINT_TYPE_U2:
5296 SIMPLE_OP (td, MINT_STELEM_U2);
5297 break;
5298 case MINT_TYPE_I4:
5299 SIMPLE_OP (td, MINT_STELEM_I4);
5300 break;
5301 case MINT_TYPE_I8:
5302 SIMPLE_OP (td, MINT_STELEM_I8);
5303 break;
5304 case MINT_TYPE_R4:
5305 SIMPLE_OP (td, MINT_STELEM_R4);
5306 break;
5307 case MINT_TYPE_R8:
5308 SIMPLE_OP (td, MINT_STELEM_R8);
5309 break;
5310 case MINT_TYPE_O:
5311 SIMPLE_OP (td, MINT_STELEM_REF);
5312 break;
5313 case MINT_TYPE_VT: {
5314 int size = mono_class_value_size (klass, NULL);
5315 SIMPLE_OP (td, MINT_STELEM_VT);
5316 td->last_ins->data [0] = get_data_item_index (td, klass);
5317 WRITE32_INS (td->last_ins, 1, &size);
5318 POP_VT (td, size);
5319 break;
5321 default: {
5322 GString *res = g_string_new ("");
5323 mono_type_get_desc (res, m_class_get_byval_arg (klass), TRUE);
5324 g_print ("STELEM: %s -> %d (%s)\n", m_class_get_name (klass), mint_type (m_class_get_byval_arg (klass)), res->str);
5325 g_string_free (res, TRUE);
5326 g_assert (0);
5327 break;
5330 td->ip += 4;
5331 td->sp -= 3;
5332 break;
5333 #if 0
5334 case CEE_CONV_OVF_U1:
5336 case CEE_CONV_OVF_I8:
5338 #if SIZEOF_VOID_P == 8
5339 case CEE_CONV_OVF_U:
5340 #endif
5341 #endif
5342 case CEE_CKFINITE:
5343 CHECK_STACK (td, 1);
5344 SIMPLE_OP (td, MINT_CKFINITE);
5345 break;
5346 case CEE_MKREFANY:
5347 CHECK_STACK (td, 1);
5349 token = read32 (td->ip + 1);
5350 klass = mini_get_class (method, token, generic_context);
5351 CHECK_TYPELOAD (klass);
5353 interp_add_ins (td, MINT_MKREFANY);
5354 td->last_ins->data [0] = get_data_item_index (td, klass);
5356 td->ip += 5;
5357 PUSH_VT (td, sizeof (MonoTypedRef));
5358 SET_TYPE(td->sp - 1, STACK_TYPE_VT, mono_defaults.typed_reference_class);
5359 break;
5360 case CEE_REFANYVAL: {
5361 CHECK_STACK (td, 1);
5363 token = read32 (td->ip + 1);
5364 klass = mini_get_class (method, token, generic_context);
5365 CHECK_TYPELOAD (klass);
5367 interp_add_ins (td, MINT_REFANYVAL);
5368 td->last_ins->data [0] = get_data_item_index (td, klass);
5370 POP_VT (td, sizeof (MonoTypedRef));
5371 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5373 td->ip += 5;
5374 break;
5376 case CEE_CONV_OVF_I1:
5377 case CEE_CONV_OVF_I1_UN: {
5378 gboolean is_un = *td->ip == CEE_CONV_OVF_I1_UN;
5379 CHECK_STACK (td, 1);
5380 switch (td->sp [-1].type) {
5381 case STACK_TYPE_R8:
5382 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_UN_R8 : MINT_CONV_OVF_I1_R8);
5383 break;
5384 case STACK_TYPE_I4:
5385 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U4 : MINT_CONV_OVF_I1_I4);
5386 break;
5387 case STACK_TYPE_I8:
5388 interp_add_ins (td, is_un ? MINT_CONV_OVF_I1_U8 : MINT_CONV_OVF_I1_I8);
5389 break;
5390 default:
5391 g_assert_not_reached ();
5393 ++td->ip;
5394 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5395 break;
5397 case CEE_CONV_OVF_U1:
5398 case CEE_CONV_OVF_U1_UN:
5399 CHECK_STACK (td, 1);
5400 switch (td->sp [-1].type) {
5401 case STACK_TYPE_R8:
5402 interp_add_ins (td, MINT_CONV_OVF_U1_R8);
5403 break;
5404 case STACK_TYPE_I4:
5405 interp_add_ins (td, MINT_CONV_OVF_U1_I4);
5406 break;
5407 case STACK_TYPE_I8:
5408 interp_add_ins (td, MINT_CONV_OVF_U1_I8);
5409 break;
5410 default:
5411 g_assert_not_reached ();
5413 ++td->ip;
5414 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5415 break;
5416 case CEE_CONV_OVF_I2:
5417 case CEE_CONV_OVF_I2_UN: {
5418 gboolean is_un = *td->ip == CEE_CONV_OVF_I2_UN;
5419 CHECK_STACK (td, 1);
5420 switch (td->sp [-1].type) {
5421 case STACK_TYPE_R8:
5422 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_UN_R8 : MINT_CONV_OVF_I2_R8);
5423 break;
5424 case STACK_TYPE_I4:
5425 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U4 : MINT_CONV_OVF_I2_I4);
5426 break;
5427 case STACK_TYPE_I8:
5428 interp_add_ins (td, is_un ? MINT_CONV_OVF_I2_U8 : MINT_CONV_OVF_I2_I8);
5429 break;
5430 default:
5431 g_assert_not_reached ();
5433 ++td->ip;
5434 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5435 break;
5437 case CEE_CONV_OVF_U2_UN:
5438 case CEE_CONV_OVF_U2:
5439 CHECK_STACK (td, 1);
5440 switch (td->sp [-1].type) {
5441 case STACK_TYPE_R8:
5442 interp_add_ins (td, MINT_CONV_OVF_U2_R8);
5443 break;
5444 case STACK_TYPE_I4:
5445 interp_add_ins (td, MINT_CONV_OVF_U2_I4);
5446 break;
5447 case STACK_TYPE_I8:
5448 interp_add_ins (td, MINT_CONV_OVF_U2_I8);
5449 break;
5450 default:
5451 g_assert_not_reached ();
5453 ++td->ip;
5454 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5455 break;
5456 #if SIZEOF_VOID_P == 4
5457 case CEE_CONV_OVF_I:
5458 #endif
5459 case CEE_CONV_OVF_I4:
5460 case CEE_CONV_OVF_I4_UN:
5461 CHECK_STACK (td, 1);
5462 switch (td->sp [-1].type) {
5463 case STACK_TYPE_R4:
5464 interp_add_ins (td, MINT_CONV_OVF_I4_R4);
5465 break;
5466 case STACK_TYPE_R8:
5467 interp_add_ins (td, MINT_CONV_OVF_I4_R8);
5468 break;
5469 case STACK_TYPE_I4:
5470 if (*td->ip == CEE_CONV_OVF_I4_UN)
5471 interp_add_ins (td, MINT_CONV_OVF_I4_U4);
5472 break;
5473 case STACK_TYPE_I8:
5474 if (*td->ip == CEE_CONV_OVF_I4_UN)
5475 interp_add_ins (td, MINT_CONV_OVF_I4_U8);
5476 else
5477 interp_add_ins (td, MINT_CONV_OVF_I4_I8);
5478 break;
5479 default:
5480 g_assert_not_reached ();
5482 ++td->ip;
5483 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5484 break;
5485 #if SIZEOF_VOID_P == 4
5486 case CEE_CONV_OVF_U:
5487 #endif
5488 case CEE_CONV_OVF_U4:
5489 case CEE_CONV_OVF_U4_UN:
5490 CHECK_STACK (td, 1);
5491 switch (td->sp [-1].type) {
5492 case STACK_TYPE_R4:
5493 interp_add_ins (td, MINT_CONV_OVF_U4_R4);
5494 break;
5495 case STACK_TYPE_R8:
5496 interp_add_ins (td, MINT_CONV_OVF_U4_R8);
5497 break;
5498 case STACK_TYPE_I4:
5499 if (*td->ip != CEE_CONV_OVF_U4_UN)
5500 interp_add_ins (td, MINT_CONV_OVF_U4_I4);
5501 break;
5502 case STACK_TYPE_I8:
5503 interp_add_ins (td, MINT_CONV_OVF_U4_I8);
5504 break;
5505 case STACK_TYPE_MP:
5506 interp_add_ins (td, MINT_CONV_OVF_U4_P);
5507 break;
5508 default:
5509 g_assert_not_reached ();
5511 ++td->ip;
5512 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5513 break;
5514 #if SIZEOF_VOID_P == 8
5515 case CEE_CONV_OVF_I:
5516 #endif
5517 case CEE_CONV_OVF_I8:
5518 CHECK_STACK (td, 1);
5519 switch (td->sp [-1].type) {
5520 case STACK_TYPE_R4:
5521 interp_add_ins (td, MINT_CONV_OVF_I8_R4);
5522 break;
5523 case STACK_TYPE_R8:
5524 interp_add_ins (td, MINT_CONV_OVF_I8_R8);
5525 break;
5526 case STACK_TYPE_I4:
5527 interp_add_ins (td, MINT_CONV_I8_I4);
5528 break;
5529 case STACK_TYPE_I8:
5530 break;
5531 default:
5532 g_assert_not_reached ();
5534 ++td->ip;
5535 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5536 break;
5537 #if SIZEOF_VOID_P == 8
5538 case CEE_CONV_OVF_U:
5539 #endif
5540 case CEE_CONV_OVF_U8:
5541 CHECK_STACK (td, 1);
5542 switch (td->sp [-1].type) {
5543 case STACK_TYPE_R4:
5544 interp_add_ins (td, MINT_CONV_OVF_U8_R4);
5545 break;
5546 case STACK_TYPE_R8:
5547 interp_add_ins (td, MINT_CONV_OVF_U8_R8);
5548 break;
5549 case STACK_TYPE_I4:
5550 interp_add_ins (td, MINT_CONV_OVF_U8_I4);
5551 break;
5552 case STACK_TYPE_I8:
5553 interp_add_ins (td, MINT_CONV_OVF_U8_I8);
5554 break;
5555 default:
5556 g_assert_not_reached ();
5558 ++td->ip;
5559 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I8);
5560 break;
5561 case CEE_LDTOKEN: {
5562 int size;
5563 gpointer handle;
5564 token = read32 (td->ip + 1);
5565 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
5566 handle = mono_method_get_wrapper_data (method, token);
5567 klass = (MonoClass *) mono_method_get_wrapper_data (method, token + 1);
5568 if (klass == mono_defaults.typehandle_class)
5569 handle = m_class_get_byval_arg ((MonoClass *) handle);
5571 if (generic_context) {
5572 handle = mono_class_inflate_generic_type_checked ((MonoType*)handle, generic_context, error);
5573 goto_if_nok (error, exit);
5575 } else {
5576 handle = mono_ldtoken_checked (image, token, &klass, generic_context, error);
5577 goto_if_nok (error, exit);
5579 mono_class_init_internal (klass);
5580 mt = mint_type (m_class_get_byval_arg (klass));
5581 g_assert (mt == MINT_TYPE_VT);
5582 size = mono_class_value_size (klass, NULL);
5583 g_assert (size == sizeof(gpointer));
5585 const unsigned char *next_ip = td->ip + 5;
5586 MonoMethod *cmethod;
5587 if (next_ip < end &&
5588 (inlining || !td->is_bb_start [next_ip - td->il_code]) &&
5589 (*next_ip == CEE_CALL || *next_ip == CEE_CALLVIRT) &&
5590 (cmethod = interp_get_method (method, read32 (next_ip + 1), image, generic_context, error)) &&
5591 (cmethod->klass == mono_defaults.systemtype_class) &&
5592 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
5593 interp_add_ins (td, MINT_MONO_LDPTR);
5594 gpointer systype = mono_type_get_object_checked (domain, (MonoType*)handle, error);
5595 goto_if_nok (error, exit);
5596 td->last_ins->data [0] = get_data_item_index (td, systype);
5597 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5598 td->ip = next_ip + 5;
5599 } else {
5600 PUSH_VT (td, sizeof(gpointer));
5601 interp_add_ins (td, MINT_LDTOKEN);
5602 td->last_ins->data [0] = get_data_item_index (td, handle);
5603 PUSH_TYPE (td, stack_type [mt], klass);
5604 td->ip += 5;
5607 break;
5609 case CEE_ADD_OVF:
5610 binary_arith_op(td, MINT_ADD_OVF_I4);
5611 ++td->ip;
5612 break;
5613 case CEE_ADD_OVF_UN:
5614 binary_arith_op(td, MINT_ADD_OVF_UN_I4);
5615 ++td->ip;
5616 break;
5617 case CEE_MUL_OVF:
5618 binary_arith_op(td, MINT_MUL_OVF_I4);
5619 ++td->ip;
5620 break;
5621 case CEE_MUL_OVF_UN:
5622 binary_arith_op(td, MINT_MUL_OVF_UN_I4);
5623 ++td->ip;
5624 break;
5625 case CEE_SUB_OVF:
5626 binary_arith_op(td, MINT_SUB_OVF_I4);
5627 ++td->ip;
5628 break;
5629 case CEE_SUB_OVF_UN:
5630 binary_arith_op(td, MINT_SUB_OVF_UN_I4);
5631 ++td->ip;
5632 break;
5633 case CEE_ENDFINALLY: {
5634 g_assert (td->clause_indexes [in_offset] != -1);
5635 td->sp = td->stack;
5636 SIMPLE_OP (td, MINT_ENDFINALLY);
5637 td->last_ins->data [0] = td->clause_indexes [in_offset];
5638 // next instructions, if they exist, are always part of new bb
5639 // endfinally can be the last instruction in a function.
5640 // functions with clauses/endfinally are never inlined.
5641 // is_bb_start is not valid while inlining.
5642 g_assert (!inlining);
5643 if (td->ip - td->il_code < td->code_size)
5644 td->is_bb_start [td->ip - header->code] = 1;
5645 break;
5647 case CEE_LEAVE:
5648 case CEE_LEAVE_S: {
5649 int offset;
5651 if (*td->ip == CEE_LEAVE)
5652 offset = 5 + read32 (td->ip + 1);
5653 else
5654 offset = 2 + (gint8)td->ip [1];
5656 td->sp = td->stack;
5657 if (td->clause_indexes [in_offset] != -1) {
5658 /* LEAVE instructions in catch clauses need to check for abort exceptions */
5659 handle_branch (td, MINT_LEAVE_S_CHECK, MINT_LEAVE_CHECK, offset);
5660 } else {
5661 handle_branch (td, MINT_LEAVE_S, MINT_LEAVE, offset);
5664 if (*td->ip == CEE_LEAVE)
5665 td->ip += 5;
5666 else
5667 td->ip += 2;
5668 break;
5670 case MONO_CUSTOM_PREFIX:
5671 ++td->ip;
5672 switch (*td->ip) {
5673 case CEE_MONO_RETHROW:
5674 CHECK_STACK (td, 1);
5675 SIMPLE_OP (td, MINT_MONO_RETHROW);
5676 td->sp = td->stack;
5677 break;
5679 case CEE_MONO_LD_DELEGATE_METHOD_PTR:
5680 --td->sp;
5681 td->ip += 1;
5682 interp_add_ins (td, MINT_LD_DELEGATE_METHOD_PTR);
5683 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5684 break;
5685 case CEE_MONO_CALLI_EXTRA_ARG:
5686 /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
5687 interp_add_ins (td, MINT_POP1);
5688 --td->sp;
5689 if (!interp_transform_call (td, method, NULL, domain, generic_context, td->is_bb_start, NULL, FALSE, error, FALSE, FALSE))
5690 goto exit;
5691 break;
5692 case CEE_MONO_JIT_ICALL_ADDR: {
5693 const guint32 token = read32 (td->ip + 1);
5694 td->ip += 5;
5695 const gconstpointer func = mono_find_jit_icall_info ((MonoJitICallId)token)->func;
5697 interp_add_ins (td, MINT_LDFTN);
5698 td->last_ins->data [0] = get_data_item_index (td, (gpointer)func);
5699 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5700 break;
5702 case CEE_MONO_ICALL: {
5703 MonoJitICallId const jit_icall_id = (MonoJitICallId)read32 (td->ip + 1);
5704 MonoJitICallInfo const * const info = mono_find_jit_icall_info (jit_icall_id);
5705 td->ip += 5;
5707 CHECK_STACK (td, info->sig->param_count);
5708 if (jit_icall_id == MONO_JIT_ICALL_mono_threads_attach_coop) {
5709 rtm->needs_thread_attach = 1;
5711 /* attach needs two arguments, and has one return value: leave one element on the stack */
5712 interp_add_ins (td, MINT_POP);
5713 } else if (jit_icall_id == MONO_JIT_ICALL_mono_threads_detach_coop) {
5714 g_assert (rtm->needs_thread_attach);
5716 /* detach consumes two arguments, and no return value: drop both of them */
5717 interp_add_ins (td, MINT_POP);
5718 interp_add_ins (td, MINT_POP);
5719 } else {
5720 int const icall_op = interp_icall_op_for_sig (info->sig);
5721 g_assert (icall_op != -1);
5723 interp_add_ins (td, icall_op);
5724 // hash here is overkill
5725 td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
5727 td->sp -= info->sig->param_count;
5729 if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
5730 int mt = mint_type (info->sig->ret);
5731 PUSH_SIMPLE_TYPE(td, stack_type [mt]);
5733 break;
5735 case CEE_MONO_VTADDR: {
5736 int size;
5737 CHECK_STACK (td, 1);
5738 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
5739 size = mono_class_native_size(td->sp [-1].klass, NULL);
5740 else
5741 size = mono_class_value_size(td->sp [-1].klass, NULL);
5742 size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
5743 interp_add_ins (td, MINT_VTRESULT);
5744 td->last_ins->data [0] = 0;
5745 WRITE32_INS (td->last_ins, 1, &size);
5746 td->vt_sp -= size;
5747 ++td->ip;
5748 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5749 break;
5751 case CEE_MONO_LDPTR:
5752 case CEE_MONO_CLASSCONST:
5753 token = read32 (td->ip + 1);
5754 td->ip += 5;
5755 interp_add_ins (td, MINT_MONO_LDPTR);
5756 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5757 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5758 break;
5759 case CEE_MONO_OBJADDR:
5760 CHECK_STACK (td, 1);
5761 ++td->ip;
5762 td->sp[-1].type = STACK_TYPE_MP;
5763 /* do nothing? */
5764 break;
5765 case CEE_MONO_NEWOBJ:
5766 token = read32 (td->ip + 1);
5767 td->ip += 5;
5768 interp_add_ins (td, MINT_MONO_NEWOBJ);
5769 td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
5770 PUSH_SIMPLE_TYPE (td, STACK_TYPE_O);
5771 break;
5772 case CEE_MONO_RETOBJ:
5773 CHECK_STACK (td, 1);
5774 token = read32 (td->ip + 1);
5775 td->ip += 5;
5776 interp_add_ins (td, MINT_MONO_RETOBJ);
5777 td->sp--;
5779 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5781 /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/
5783 if (td->sp > td->stack)
5784 g_warning ("CEE_MONO_RETOBJ: more values on stack: %d", td->sp-td->stack);
5785 break;
5786 case CEE_MONO_LDNATIVEOBJ:
5787 token = read32 (td->ip + 1);
5788 td->ip += 5;
5789 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
5790 g_assert(m_class_is_valuetype (klass));
5791 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
5792 break;
5793 case CEE_MONO_TLS: {
5794 gint32 key = read32 (td->ip + 1);
5795 td->ip += 5;
5796 g_assertf (key == TLS_KEY_SGEN_THREAD_INFO, "%d", key);
5797 interp_add_ins (td, MINT_MONO_SGEN_THREAD_INFO);
5798 PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
5799 break;
5801 case CEE_MONO_ATOMIC_STORE_I4:
5802 CHECK_STACK (td, 2);
5803 SIMPLE_OP (td, MINT_MONO_ATOMIC_STORE_I4);
5804 td->sp -= 2;
5805 td->ip++;
5806 break;
5807 case CEE_MONO_SAVE_LMF:
5808 case CEE_MONO_RESTORE_LMF:
5809 case CEE_MONO_NOT_TAKEN:
5810 ++td->ip;
5811 break;
5812 case CEE_MONO_LDPTR_INT_REQ_FLAG:
5813 interp_add_ins (td, MINT_MONO_LDPTR);
5814 td->last_ins->data [0] = get_data_item_index (td, &mono_thread_interruption_request_flag);
5815 PUSH_TYPE (td, STACK_TYPE_MP, NULL);
5816 ++td->ip;
5817 break;
5818 case CEE_MONO_MEMORY_BARRIER:
5819 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
5820 ++td->ip;
5821 break;
5822 case CEE_MONO_LDDOMAIN:
5823 interp_add_ins (td, MINT_MONO_LDDOMAIN);
5824 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5825 ++td->ip;
5826 break;
5827 case CEE_MONO_SAVE_LAST_ERROR:
5828 save_last_error = TRUE;
5829 ++td->ip;
5830 break;
5831 case CEE_MONO_GET_SP:
5832 interp_add_ins (td, MINT_MONO_GET_SP);
5833 PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
5834 ++td->ip;
5835 break;
5836 default:
5837 g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td->ip, td->ip-header->code);
5839 break;
5840 #if 0
5841 case CEE_PREFIX7:
5842 case CEE_PREFIX6:
5843 case CEE_PREFIX5:
5844 case CEE_PREFIX4:
5845 case CEE_PREFIX3:
5846 case CEE_PREFIX2:
5847 case CEE_PREFIXREF: ves_abort(); break;
5848 #endif
5850 * Note: Exceptions thrown when executing a prefixed opcode need
5851 * to take into account the number of prefix bytes (usually the
5852 * throw point is just (ip - n_prefix_bytes).
5854 case CEE_PREFIX1:
5855 ++td->ip;
5856 switch (*td->ip) {
5857 case CEE_ARGLIST:
5858 interp_add_ins (td, MINT_ARGLIST);
5859 PUSH_VT (td, SIZEOF_VOID_P);
5860 PUSH_SIMPLE_TYPE (td, STACK_TYPE_VT);
5861 ++td->ip;
5862 break;
5863 case CEE_CEQ:
5864 CHECK_STACK(td, 2);
5865 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP) {
5866 interp_add_ins (td, MINT_CEQ_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5867 } else {
5868 if (td->sp [-1].type == STACK_TYPE_R4 && td->sp [-2].type == STACK_TYPE_R8)
5869 interp_add_ins (td, MINT_CONV_R8_R4);
5870 if (td->sp [-1].type == STACK_TYPE_R8 && td->sp [-2].type == STACK_TYPE_R4)
5871 interp_add_ins (td, MINT_CONV_R8_R4_SP);
5872 interp_add_ins (td, MINT_CEQ_I4 + td->sp [-1].type - STACK_TYPE_I4);
5874 --td->sp;
5875 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5876 ++td->ip;
5877 break;
5878 case CEE_CGT:
5879 CHECK_STACK(td, 2);
5880 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5881 interp_add_ins (td, MINT_CGT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5882 else
5883 interp_add_ins (td, MINT_CGT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5884 --td->sp;
5885 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5886 ++td->ip;
5887 break;
5888 case CEE_CGT_UN:
5889 CHECK_STACK(td, 2);
5890 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5891 interp_add_ins (td, MINT_CGT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5892 else
5893 interp_add_ins (td, MINT_CGT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5894 --td->sp;
5895 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5896 ++td->ip;
5897 break;
5898 case CEE_CLT:
5899 CHECK_STACK(td, 2);
5900 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5901 interp_add_ins (td, MINT_CLT_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5902 else
5903 interp_add_ins (td, MINT_CLT_I4 + td->sp [-1].type - STACK_TYPE_I4);
5904 --td->sp;
5905 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5906 ++td->ip;
5907 break;
5908 case CEE_CLT_UN:
5909 CHECK_STACK(td, 2);
5910 if (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_MP)
5911 interp_add_ins (td, MINT_CLT_UN_I4 + STACK_TYPE_I - STACK_TYPE_I4);
5912 else
5913 interp_add_ins (td, MINT_CLT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4);
5914 --td->sp;
5915 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_I4);
5916 ++td->ip;
5917 break;
5918 case CEE_LDVIRTFTN: /* fallthrough */
5919 case CEE_LDFTN: {
5920 MonoMethod *m;
5921 if (*td->ip == CEE_LDVIRTFTN) {
5922 CHECK_STACK (td, 1);
5923 --td->sp;
5925 token = read32 (td->ip + 1);
5926 m = interp_get_method (method, token, image, generic_context, error);
5927 goto_if_nok (error, exit);
5929 if (!mono_method_can_access_method (method, m))
5930 interp_generate_mae_throw (td, method, m);
5932 if (method->wrapper_type == MONO_WRAPPER_NONE && m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
5933 m = mono_marshal_get_synchronized_wrapper (m);
5935 interp_add_ins (td, *td->ip == CEE_LDFTN ? MINT_LDFTN : MINT_LDVIRTFTN);
5936 td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
5937 goto_if_nok (error, exit);
5938 td->ip += 5;
5939 PUSH_SIMPLE_TYPE (td, STACK_TYPE_F);
5940 break;
5942 case CEE_LDARG: {
5943 int arg_n = read16 (td->ip + 1);
5944 if (!inlining)
5945 load_arg (td, arg_n);
5946 else
5947 load_local (td, arg_locals [arg_n]);
5948 td->ip += 3;
5949 break;
5951 case CEE_LDARGA: {
5952 int n = read16 (td->ip + 1);
5954 if (!inlining) {
5955 get_arg_type_exact (td, n, &mt);
5956 interp_add_ins (td, mt == MINT_TYPE_VT ? MINT_LDARGA_VT : MINT_LDARGA);
5957 td->last_ins->data [0] = n;
5958 } else {
5959 int loc_n = arg_locals [n];
5960 interp_add_ins (td, MINT_LDLOCA_S);
5961 td->last_ins->data [0] = loc_n;
5962 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
5964 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5965 td->ip += 3;
5966 break;
5968 case CEE_STARG: {
5969 int arg_n = read16 (td->ip + 1);
5970 if (!inlining)
5971 store_arg (td, arg_n);
5972 else
5973 store_local (td, arg_locals [arg_n]);
5974 td->ip += 3;
5975 break;
5977 case CEE_LDLOC: {
5978 int loc_n = read16 (td->ip + 1);
5979 if (!inlining)
5980 load_local (td, loc_n);
5981 else
5982 load_local (td, local_locals [loc_n]);
5983 td->ip += 3;
5984 break;
5986 case CEE_LDLOCA: {
5987 int loc_n = read16 (td->ip + 1);
5988 interp_add_ins (td, MINT_LDLOCA_S);
5989 if (inlining)
5990 loc_n = local_locals [loc_n];
5991 td->last_ins->data [0] = loc_n;
5992 td->locals [loc_n].flags |= INTERP_LOCAL_FLAG_INDIRECT;
5993 PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
5994 td->ip += 3;
5995 break;
5997 case CEE_STLOC: {
5998 int loc_n = read16 (td->ip + 1);
5999 if (!inlining)
6000 store_local (td, loc_n);
6001 else
6002 store_local (td, local_locals [loc_n]);
6003 td->ip += 3;
6004 break;
6006 case CEE_LOCALLOC:
6007 INLINE_FAILURE;
6008 CHECK_STACK (td, 1);
6009 #if SIZEOF_VOID_P == 8
6010 if (td->sp [-1].type == STACK_TYPE_I8)
6011 interp_add_ins (td, MINT_CONV_I4_I8);
6012 #endif
6013 interp_add_ins (td, MINT_LOCALLOC);
6014 if (td->sp != td->stack + 1)
6015 g_warning("CEE_LOCALLOC: stack not empty");
6016 ++td->ip;
6017 SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
6018 break;
6019 #if 0
6020 case CEE_UNUSED57: ves_abort(); break;
6021 #endif
6022 case CEE_ENDFILTER:
6023 interp_add_ins (td, MINT_ENDFILTER);
6024 ++td->ip;
6025 break;
6026 case CEE_UNALIGNED_:
6027 td->ip += 2;
6028 break;
6029 case CEE_VOLATILE_:
6030 ++td->ip;
6031 volatile_ = TRUE;
6032 break;
6033 case CEE_TAIL_:
6034 ++td->ip;
6035 /* FIX: should do something? */;
6036 // TODO: This should raise a method_tail_call profiler event.
6037 break;
6038 case CEE_INITOBJ:
6039 CHECK_STACK(td, 1);
6040 token = read32 (td->ip + 1);
6041 klass = mini_get_class (method, token, generic_context);
6042 CHECK_TYPELOAD (klass);
6043 if (m_class_is_valuetype (klass)) {
6044 interp_add_ins (td, MINT_INITOBJ);
6045 i32 = mono_class_value_size (klass, NULL);
6046 WRITE32_INS (td->last_ins, 0, &i32);
6047 --td->sp;
6048 } else {
6049 interp_add_ins (td, MINT_LDNULL);
6050 PUSH_TYPE(td, STACK_TYPE_O, NULL);
6051 interp_add_ins (td, MINT_STIND_REF);
6052 td->sp -= 2;
6054 td->ip += 5;
6055 break;
6056 case CEE_CPBLK:
6057 CHECK_STACK(td, 3);
6058 /* FIX? convert length to I8? */
6059 if (volatile_)
6060 interp_add_ins (td, MINT_MONO_MEMORY_BARRIER);
6061 interp_add_ins (td, MINT_CPBLK);
6062 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_SEQ);
6063 td->sp -= 3;
6064 ++td->ip;
6065 break;
6066 case CEE_READONLY_:
6067 readonly = TRUE;
6068 td->ip += 1;
6069 break;
6070 case CEE_CONSTRAINED_:
6071 token = read32 (td->ip + 1);
6072 constrained_class = mini_get_class (method, token, generic_context);
6073 CHECK_TYPELOAD (constrained_class);
6074 td->ip += 5;
6075 break;
6076 case CEE_INITBLK:
6077 CHECK_STACK(td, 3);
6078 BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL);
6079 interp_add_ins (td, MINT_INITBLK);
6080 td->sp -= 3;
6081 td->ip += 1;
6082 break;
6083 case CEE_NO_:
6084 /* FIXME: implement */
6085 td->ip += 2;
6086 break;
6087 case CEE_RETHROW: {
6088 int clause_index = td->clause_indexes [in_offset];
6089 g_assert (clause_index != -1);
6090 SIMPLE_OP (td, MINT_RETHROW);
6091 td->last_ins->data [0] = rtm->exvar_offsets [clause_index];
6092 td->sp = td->stack;
6093 break;
6095 case CEE_SIZEOF: {
6096 gint32 size;
6097 token = read32 (td->ip + 1);
6098 td->ip += 5;
6099 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (m_class_get_image (method->klass)) && !generic_context) {
6100 int align;
6101 MonoType *type = mono_type_create_from_typespec_checked (image, token, error);
6102 goto_if_nok (error, exit);
6103 size = mono_type_size (type, &align);
6104 } else {
6105 int align;
6106 MonoClass *szclass = mini_get_class (method, token, generic_context);
6107 CHECK_TYPELOAD (szclass);
6108 #if 0
6109 if (!szclass->valuetype)
6110 THROW_EX (mono_exception_from_name (mono_defaults.corlib, "System", "InvalidProgramException"), ip - 5);
6111 #endif
6112 size = mono_type_size (m_class_get_byval_arg (szclass), &align);
6114 interp_add_ins (td, MINT_LDC_I4);
6115 WRITE32_INS (td->last_ins, 0, &size);
6116 PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
6117 break;
6119 case CEE_REFANYTYPE:
6120 interp_add_ins (td, MINT_REFANYTYPE);
6121 td->ip += 1;
6122 POP_VT (td, sizeof (MonoTypedRef));
6123 PUSH_VT (td, sizeof (gpointer));
6124 SET_TYPE(td->sp - 1, STACK_TYPE_VT, NULL);
6125 break;
6126 default:
6127 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);
6129 break;
6130 default:
6131 g_error ("transform.c: Unimplemented opcode: %02x at 0x%x\n", *td->ip, td->ip-header->code);
6134 // No IR instructions were added as part of a bb_start IL instruction. Add a MINT_NOP
6135 // so we always have an instruction associated with a bb_start. This is simple and avoids
6136 // any complications associated with il_offset tracking.
6137 if (prev_last_ins == td->last_ins && (!inlining && td->is_bb_start [in_offset]) && td->ip < end)
6138 interp_add_ins (td, MINT_NOP);
6141 g_assert (td->ip == end);
6143 exit_ret:
6144 g_free (arg_locals);
6145 g_free (local_locals);
6146 mono_basic_block_free (original_bb);
6148 return ret;
6149 exit:
6150 ret = FALSE;
6151 goto exit_ret;
6154 // Find the offset of the first interp instruction generated starting il_offset
6155 // This is needed to find the end of clauses.
6156 static int
6157 find_in_offset (TransformData *td, int il_offset)
6159 int i = il_offset;
6160 while (!td->in_offsets [i])
6161 i++;
6162 return td->in_offsets [i] - 1;
6165 // We store in the in_offset array the native_offset + 1, so 0 can mean only that the il
6166 // offset is uninitialized. Otherwise 0 is valid value for first interp instruction.
6167 static int
6168 get_in_offset (TransformData *td, int il_offset)
6170 int target_offset = td->in_offsets [il_offset];
6171 g_assert (target_offset);
6172 return target_offset - 1;
6175 static void
6176 handle_relocations (TransformData *td)
6178 // Handle relocations
6179 for (int i = 0; i < td->relocs->len; ++i) {
6180 Reloc *reloc = (Reloc*)g_ptr_array_index (td->relocs, i);
6181 int offset = get_in_offset (td, reloc->target) - reloc->offset;
6183 switch (reloc->type) {
6184 case RELOC_SHORT_BRANCH:
6185 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
6186 td->new_code [reloc->offset + 1] = offset;
6187 break;
6188 case RELOC_LONG_BRANCH: {
6189 guint16 *v = (guint16 *) &offset;
6190 g_assert (td->new_code [reloc->offset + 1] == 0xdead);
6191 g_assert (td->new_code [reloc->offset + 2] == 0xbeef);
6192 td->new_code [reloc->offset + 1] = *(guint16 *) v;
6193 td->new_code [reloc->offset + 2] = *(guint16 *) (v + 1);
6194 break;
6196 case RELOC_SWITCH: {
6197 guint16 *v = (guint16*)&offset;
6198 g_assert (td->new_code [reloc->offset] == 0xdead);
6199 g_assert (td->new_code [reloc->offset + 1] == 0xbeef);
6200 td->new_code [reloc->offset] = *(guint16*)v;
6201 td->new_code [reloc->offset + 1] = *(guint16*)(v + 1);
6202 break;
6204 default:
6205 g_assert_not_reached ();
6206 break;
6212 static int
6213 get_inst_length (InterpInst *ins)
6215 if (ins->opcode == MINT_SWITCH)
6216 return MINT_SWITCH_LEN (READ32 (&ins->data [0]));
6217 #ifdef ENABLE_EXPERIMENT_TIERED
6218 else if (MINT_IS_PATCHABLE_CALL (ins->opcode))
6219 return MAX (mono_interp_oplen [MINT_JIT_CALL2], mono_interp_oplen [ins->opcode]);
6220 #endif
6221 else
6222 return mono_interp_oplen [ins->opcode];
6225 static void
6226 get_inst_stack_usage (TransformData *td, InterpInst *ins, int *pop, int *push)
6228 guint16 opcode = ins->opcode;
6229 if (mono_interp_oppop [opcode] == MINT_VAR_POP ||
6230 mono_interp_oppush [opcode] == MINT_VAR_PUSH) {
6231 switch (opcode) {
6232 case MINT_JIT_CALL:
6233 case MINT_CALL:
6234 case MINT_CALLVIRT:
6235 case MINT_CALLVIRT_FAST:
6236 case MINT_VCALL:
6237 case MINT_VCALLVIRT:
6238 case MINT_VCALLVIRT_FAST: {
6239 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6240 *pop = imethod->param_count + imethod->hasthis;
6241 if (opcode == MINT_JIT_CALL)
6242 *push = imethod->rtype->type != MONO_TYPE_VOID;
6243 else
6244 *push = opcode == MINT_CALL || opcode == MINT_CALLVIRT || opcode == MINT_CALLVIRT_FAST;
6245 break;
6247 #ifndef ENABLE_NETCORE
6248 case MINT_CALLRUN: {
6249 MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [1]];
6250 *pop = csignature->param_count + csignature->hasthis;
6251 *push = csignature->ret->type != MONO_TYPE_VOID;
6252 break;
6254 #endif
6255 case MINT_CALLI:
6256 case MINT_CALLI_NAT:
6257 case MINT_CALLI_NAT_FAST: {
6258 MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [0]];
6259 *pop = csignature->param_count + csignature->hasthis + 1;
6260 *push = csignature->ret->type != MONO_TYPE_VOID;
6261 break;
6263 case MINT_CALL_VARARG: {
6264 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6265 MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [1]];
6266 *pop = imethod->param_count + imethod->hasthis + csignature->param_count - csignature->sentinelpos;
6267 *push = imethod->rtype->type != MONO_TYPE_VOID;
6268 break;
6270 case MINT_NEWOBJ_FAST: {
6271 int param_count = ins->data [1];
6272 gboolean is_inlined = ins->data [0] == INLINED_METHOD_FLAG;
6273 if (is_inlined) {
6274 // This needs to be handled explictly during cprop, in order to properly
6275 // keep track of stack contents
6276 *pop = 0;
6277 *push = 2;
6278 } else {
6279 *pop = param_count;
6280 *push = 1;
6282 break;
6284 case MINT_NEWOBJ_ARRAY:
6285 case MINT_NEWOBJ_VT_FAST:
6286 case MINT_NEWOBJ_VTST_FAST:
6287 *pop = ins->data [1];
6288 *push = 1;
6289 break;
6290 case MINT_LDELEMA:
6291 case MINT_LDELEMA_TC:
6292 *pop = ins->data [0] + 1;
6293 *push = 1;
6294 break;
6295 case MINT_NEWOBJ: {
6296 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6297 *pop = imethod->param_count;
6298 *push = 1;
6299 break;
6301 case MINT_INTRINS_BYREFERENCE_CTOR: {
6302 InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
6303 *pop = imethod->param_count;
6304 *push = 1;
6305 break;
6307 default:
6308 g_assert_not_reached ();
6310 } else {
6311 *pop = mono_interp_oppop [opcode];
6312 *push = mono_interp_oppush [opcode];
6316 static guint16*
6317 emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *ins)
6319 guint16 opcode = ins->opcode;
6320 guint16 *ip = start_ip;
6322 // We know what IL offset this instruction was created for. We can now map the IL offset
6323 // to the IR offset. We use this array to resolve the relocations, which reference the IL.
6324 if (ins->il_offset != -1 && !td->in_offsets [ins->il_offset]) {
6325 g_assert (ins->il_offset >= 0 && ins->il_offset < td->header->code_size);
6326 td->in_offsets [ins->il_offset] = start_ip - td->new_code + 1;
6328 MonoDebugLineNumberEntry lne;
6329 lne.native_offset = (guint8*)start_ip - (guint8*)td->new_code;
6330 lne.il_offset = ins->il_offset;
6331 g_array_append_val (td->line_numbers, lne);
6334 if (opcode == MINT_NOP)
6335 return ip;
6337 *ip++ = opcode;
6338 if (opcode == MINT_SWITCH) {
6339 int labels = READ32 (&ins->data [0]);
6340 // Write number of switch labels
6341 *ip++ = ins->data [0];
6342 *ip++ = ins->data [1];
6343 // Add relocation for each label
6344 for (int i = 0; i < labels; i++) {
6345 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
6346 reloc->type = RELOC_SWITCH;
6347 reloc->offset = ip - td->new_code;
6348 reloc->target = READ32 (&ins->data [2 + i * 2]);
6349 g_ptr_array_add (td->relocs, reloc);
6350 *ip++ = 0xdead;
6351 *ip++ = 0xbeef;
6353 } else if ((opcode >= MINT_BRFALSE_I4_S && opcode <= MINT_BRTRUE_R8_S) ||
6354 (opcode >= MINT_BEQ_I4_S && opcode <= MINT_BLT_UN_R8_S) ||
6355 opcode == MINT_BR_S || opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK) {
6356 const int br_offset = start_ip - td->new_code;
6357 if (ins->data [0] < ins->il_offset) {
6358 // Backwards branch. We can already patch it.
6359 *ip++ = get_in_offset (td, ins->data [0]) - br_offset;
6360 } else {
6361 // We don't know the in_offset of the target, add a reloc
6362 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
6363 reloc->type = RELOC_SHORT_BRANCH;
6364 reloc->offset = br_offset;
6365 reloc->target = ins->data [0];
6366 g_ptr_array_add (td->relocs, reloc);
6367 *ip++ = 0xdead;
6369 } else if ((opcode >= MINT_BRFALSE_I4 && opcode <= MINT_BRTRUE_R8) ||
6370 (opcode >= MINT_BEQ_I4 && opcode <= MINT_BLT_UN_R8) ||
6371 opcode == MINT_BR || opcode == MINT_LEAVE || opcode == MINT_LEAVE_CHECK) {
6372 const int br_offset = start_ip - td->new_code;
6373 int target_il = READ32 (&ins->data [0]);
6374 if (target_il < ins->il_offset) {
6375 // Backwards branch. We can already patch it
6376 const int br_offset = start_ip - td->new_code;
6377 int target_offset = get_in_offset (td, target_il) - br_offset;
6378 WRITE32 (ip, &target_offset);
6379 } else {
6380 Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
6381 reloc->type = RELOC_LONG_BRANCH;
6382 reloc->offset = br_offset;
6383 reloc->target = target_il;
6384 g_ptr_array_add (td->relocs, reloc);
6385 *ip++ = 0xdead;
6386 *ip++ = 0xbeef;
6388 } else if (opcode == MINT_SDB_SEQ_POINT) {
6389 SeqPoint *seqp = (SeqPoint*)mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint));
6390 InterpBasicBlock *cbb;
6392 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_ENTRY) {
6393 seqp->il_offset = METHOD_ENTRY_IL_OFFSET;
6394 cbb = td->offset_to_bb [0];
6395 } else {
6396 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT)
6397 seqp->il_offset = METHOD_EXIT_IL_OFFSET;
6398 else
6399 seqp->il_offset = ins->il_offset;
6400 cbb = td->offset_to_bb [ins->il_offset];
6402 seqp->native_offset = (guint8*)start_ip - (guint8*)td->new_code;
6403 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NONEMPTY_STACK)
6404 seqp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
6405 if (ins->flags & INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL)
6406 seqp->flags |= MONO_SEQ_POINT_FLAG_NESTED_CALL;
6407 g_ptr_array_add (td->seq_points, seqp);
6409 cbb->seq_points = g_slist_prepend_mempool (td->mempool, cbb->seq_points, seqp);
6410 cbb->last_seq_point = seqp;
6411 #ifdef ENABLE_EXPERIMENT_TIERED
6412 } else if (ins->flags & INTERP_INST_FLAG_RECORD_CALL_PATCH) {
6413 g_assert (MINT_IS_PATCHABLE_CALL (opcode));
6415 /* TODO: could `ins` be removed by any interp optimization? */
6416 MonoMethod *target_method = (MonoMethod *) g_hash_table_lookup (td->patchsite_hash, ins);
6417 g_assert (target_method);
6418 g_hash_table_remove (td->patchsite_hash, ins);
6420 mini_tiered_record_callsite (start_ip, target_method, TIERED_PATCH_KIND_INTERP);
6422 int size = mono_interp_oplen [ins->opcode];
6423 int jit_call2_size = mono_interp_oplen [MINT_JIT_CALL2];
6425 g_assert (size < jit_call2_size);
6427 // Emit the rest of the data
6428 for (int i = 0; i < size - 1; i++)
6429 *ip++ = ins->data [i];
6431 /* intentional padding so we can patch a MINT_JIT_CALL2 here */
6432 for (int i = size - 1; i < (jit_call2_size - 1); i++)
6433 *ip++ = MINT_NIY;
6434 #endif
6435 } else {
6436 if (MINT_IS_LDLOC (opcode) || MINT_IS_STLOC (opcode) || MINT_IS_STLOC_NP (opcode) || opcode == MINT_LDLOCA_S ||
6437 MINT_IS_LDLOCFLD (opcode) || MINT_IS_LOCUNOP (opcode) || MINT_IS_STLOCFLD (opcode)) {
6438 ins->data [0] = get_interp_local_offset (td, ins->data [0]);
6439 } else if (MINT_IS_MOVLOC (opcode)) {
6440 ins->data [0] = get_interp_local_offset (td, ins->data [0]);
6441 ins->data [1] = get_interp_local_offset (td, ins->data [1]);
6444 int size = get_inst_length (ins) - 1;
6445 // Emit the rest of the data
6446 for (int i = 0; i < size; i++)
6447 *ip++ = ins->data [i];
6449 mono_interp_stats.emitted_instructions++;
6450 return ip;
6453 // Generates the final code, after we are done with all the passes
6454 static void
6455 generate_compacted_code (TransformData *td)
6457 guint16 *ip;
6458 int size = 0;
6459 td->relocs = g_ptr_array_new ();
6461 // Iterate once to compute the exact size of the compacted code
6462 InterpInst *ins = td->first_ins;
6463 while (ins) {
6464 size += get_inst_length (ins);
6465 ins = ins->next;
6468 // Generate the compacted stream of instructions
6469 td->new_code = ip = (guint16*)mono_domain_alloc0 (td->rtm->domain, size * sizeof (guint16));
6470 ins = td->first_ins;
6471 while (ins) {
6472 ip = emit_compacted_instruction (td, ip, ins);
6473 ins = ins->next;
6475 td->new_code_end = ip;
6476 td->in_offsets [td->header->code_size] = td->new_code_end - td->new_code;
6478 // Patch all branches
6479 handle_relocations (td);
6481 g_ptr_array_free (td->relocs, TRUE);
6484 static int
6485 get_movloc_for_type (int mt)
6487 switch (mt) {
6488 case MINT_TYPE_I1:
6489 case MINT_TYPE_U1:
6490 return MINT_MOVLOC_1;
6491 case MINT_TYPE_I2:
6492 case MINT_TYPE_U2:
6493 return MINT_MOVLOC_2;
6494 case MINT_TYPE_I4:
6495 case MINT_TYPE_R4:
6496 return MINT_MOVLOC_4;
6497 case MINT_TYPE_I8:
6498 case MINT_TYPE_R8:
6499 return MINT_MOVLOC_8;
6500 case MINT_TYPE_O:
6501 case MINT_TYPE_P:
6502 #if SIZEOF_VOID_P == 8
6503 return MINT_MOVLOC_8;
6504 #else
6505 return MINT_MOVLOC_4;
6506 #endif
6507 case MINT_TYPE_VT:
6508 return MINT_MOVLOC_VT;
6510 g_assert_not_reached ();
6513 // The value of local has changed. This means the contents of the stack where the
6514 // local was loaded, no longer contain the value of the local. Clear them.
6515 static void
6516 clear_stack_content_info_for_local (StackContentInfo *start, StackContentInfo *end, int local)
6518 StackContentInfo *si;
6519 for (si = start; si < end; si++) {
6520 if (si->val.type == STACK_VALUE_LOCAL && si->val.local == local)
6521 si->val.type = STACK_VALUE_NONE;
6525 // The value of argument has changed. This means the contents of the stack where the
6526 // argument was loaded, no longer contain the value of the argument. Clear them.
6527 static void
6528 clear_stack_content_info_for_argument (StackContentInfo *start, StackContentInfo *end, int argument)
6530 StackContentInfo *si;
6531 for (si = start; si < end; si++) {
6532 if (si->val.type == STACK_VALUE_ARG && si->val.arg == argument)
6533 si->val.type = STACK_VALUE_NONE;
6537 // The value of local has changed. This means we can no longer assume that any other local
6538 // is a copy of this local.
6539 static void
6540 clear_local_content_info_for_local (StackValue *start, StackValue *end, int local)
6542 StackValue *sval;
6543 for (sval = start; sval < end; sval++) {
6544 if (sval->type == STACK_VALUE_LOCAL && sval->local == local)
6545 sval->type = STACK_VALUE_NONE;
6549 static gboolean
6550 interp_local_deadce (TransformData *td, int *local_ref_count)
6552 InterpInst *ins;
6553 gboolean needs_dce = FALSE;
6554 gboolean needs_cprop = FALSE;
6556 for (int i = 0; i < td->locals_size; i++) {
6557 g_assert (local_ref_count [i] >= 0);
6558 if (!local_ref_count [i] &&
6559 (td->locals [i].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0 &&
6560 (td->locals [i].flags & INTERP_LOCAL_FLAG_DEAD) == 0) {
6561 needs_dce = TRUE;
6562 // If we do another deadce iteration over the code, make sure we don't try
6563 // to kill instructions accessing locals that have already been handled in
6564 // a previous iteration.
6565 td->locals [i].flags |= INTERP_LOCAL_FLAG_DEAD;
6566 break;
6570 // Return early if all locals are alive
6571 if (!needs_dce)
6572 return FALSE;
6574 // Kill instructions that don't use stack and are storing into dead locals
6575 for (ins = td->first_ins; ins != NULL; ins = ins->next) {
6576 if (MINT_IS_STLOC_NP (ins->opcode)) {
6577 if (!local_ref_count [ins->data [0]] && (td->locals [ins->data [0]].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0) {
6578 interp_clear_ins (td, ins);
6579 mono_interp_stats.killed_instructions++;
6580 // We killed an instruction that makes use of the stack. This might uncover new optimizations
6581 needs_cprop = TRUE;
6583 } else if (MINT_IS_MOVLOC (ins->opcode)) {
6584 if (!local_ref_count [ins->data [1]] && (td->locals [ins->data [1]].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0) {
6585 interp_clear_ins (td, ins);
6586 mono_interp_stats.killed_instructions++;
6588 } else if (MINT_IS_STLOC (ins->opcode) && ins->opcode != MINT_STLOC_VT) {
6589 if (!local_ref_count [ins->data [0]] && (td->locals [ins->data [0]].flags & INTERP_LOCAL_FLAG_INDIRECT) == 0) {
6590 // We store to a dead stloc, we can replace it with a POP to save local space
6591 ins->opcode = MINT_POP;
6592 mono_interp_stats.added_pop_count++;
6593 // We might to be able to kill both the pop and the instruction pushing the value
6594 needs_cprop = TRUE;
6598 return needs_cprop;
6601 #define INTERP_FOLD_UNOP(opcode,stack_type,field,op) \
6602 case opcode: \
6603 g_assert (sp->val.type == stack_type); \
6604 result.type = stack_type; \
6605 result.field = op sp->val.field; \
6606 break;
6608 #define INTERP_FOLD_CONV(opcode,stack_type_dst,field_dst,stack_type_src,field_src,cast_type) \
6609 case opcode: \
6610 g_assert (sp->val.type == stack_type_src); \
6611 result.type = stack_type_dst; \
6612 result.field_dst = (cast_type)sp->val.field_src; \
6613 break;
6615 static InterpInst*
6616 interp_fold_unop (TransformData *td, StackContentInfo *sp, InterpInst *ins)
6618 StackValue result;
6619 // Decrement sp so it's easier to access top of the stack
6620 sp--;
6621 if (sp->val.type != STACK_VALUE_I4 && sp->val.type != STACK_VALUE_I8)
6622 goto cfold_failed;
6624 // Top of the stack is a constant
6625 switch (ins->opcode) {
6626 INTERP_FOLD_UNOP (MINT_ADD1_I4, STACK_VALUE_I4, i, 1+);
6627 INTERP_FOLD_UNOP (MINT_ADD1_I8, STACK_VALUE_I8, l, 1+);
6628 INTERP_FOLD_UNOP (MINT_SUB1_I4, STACK_VALUE_I4, i, -1+);
6629 INTERP_FOLD_UNOP (MINT_SUB1_I8, STACK_VALUE_I8, l, -1+);
6630 INTERP_FOLD_UNOP (MINT_NEG_I4, STACK_VALUE_I4, i, -);
6631 INTERP_FOLD_UNOP (MINT_NEG_I8, STACK_VALUE_I8, l, -);
6632 INTERP_FOLD_UNOP (MINT_NOT_I4, STACK_VALUE_I4, i, ~);
6633 INTERP_FOLD_UNOP (MINT_NOT_I8, STACK_VALUE_I8, l, ~);
6634 INTERP_FOLD_UNOP (MINT_CEQ0_I4, STACK_VALUE_I4, i, 0 ==);
6636 INTERP_FOLD_CONV (MINT_CONV_I1_I4, STACK_VALUE_I4, i, STACK_VALUE_I4, i, gint8);
6637 INTERP_FOLD_CONV (MINT_CONV_I1_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, gint8);
6638 INTERP_FOLD_CONV (MINT_CONV_U1_I4, STACK_VALUE_I4, i, STACK_VALUE_I4, i, guint8);
6639 INTERP_FOLD_CONV (MINT_CONV_U1_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, guint8);
6641 INTERP_FOLD_CONV (MINT_CONV_I2_I4, STACK_VALUE_I4, i, STACK_VALUE_I4, i, gint16);
6642 INTERP_FOLD_CONV (MINT_CONV_I2_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, gint16);
6643 INTERP_FOLD_CONV (MINT_CONV_U2_I4, STACK_VALUE_I4, i, STACK_VALUE_I4, i, guint16);
6644 INTERP_FOLD_CONV (MINT_CONV_U2_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, guint16);
6646 INTERP_FOLD_CONV (MINT_CONV_I4_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, gint32);
6647 INTERP_FOLD_CONV (MINT_CONV_U4_I8, STACK_VALUE_I4, i, STACK_VALUE_I8, l, gint32);
6649 INTERP_FOLD_CONV (MINT_CONV_I8_I4, STACK_VALUE_I8, l, STACK_VALUE_I4, i, gint32);
6650 INTERP_FOLD_CONV (MINT_CONV_I8_U4, STACK_VALUE_I8, l, STACK_VALUE_I4, i, guint32);
6652 default:
6653 goto cfold_failed;
6656 // We were able to compute the result of the ins instruction. We store the
6657 // current value for the top of the stack and, if possible, try to replace the
6658 // instructions that are part of this unary operation with a single LDC.
6659 mono_interp_stats.constant_folds++;
6660 if (sp->ins != NULL) {
6661 interp_clear_ins (td, sp->ins);
6662 mono_interp_stats.killed_instructions++;
6663 if (result.type == STACK_VALUE_I4)
6664 ins = interp_get_ldc_i4_from_const (td, ins, result.i);
6665 else if (result.type == STACK_VALUE_I8)
6666 ins = interp_inst_replace_with_i8_const (td, ins, result.l);
6667 else
6668 g_assert_not_reached ();
6669 if (td->verbose_level) {
6670 g_print ("Fold unop :\n\t");
6671 dump_interp_inst_newline (ins);
6673 sp->ins = ins;
6675 sp->val = result;
6676 return ins;
6678 cfold_failed:
6679 sp->ins = NULL;
6680 sp->val.type = STACK_VALUE_NONE;
6681 return ins;
6684 #define INTERP_FOLD_BINOP(opcode,stack_type,field,op) \
6685 case opcode: \
6686 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6687 result.type = stack_type; \
6688 result.field = sp [0].val.field op sp [1].val.field; \
6689 break;
6691 #define INTERP_FOLD_BINOP_FULL(opcode,stack_type,field,op,cast_type,cond) \
6692 case opcode: \
6693 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6694 if (!(cond)) goto cfold_failed; \
6695 result.type = stack_type; \
6696 result.field = (cast_type)sp [0].val.field op (cast_type)sp [1].val.field; \
6697 break;
6699 #define INTERP_FOLD_SHIFTOP(opcode,stack_type,field,shift_op,cast_type) \
6700 case opcode: \
6701 g_assert (sp [1].val.type == STACK_VALUE_I4); \
6702 result.type = stack_type; \
6703 result.field = (cast_type)sp [0].val.field shift_op sp [1].val.i; \
6704 break;
6706 #define INTERP_FOLD_RELOP(opcode,stack_type,field,relop,cast_type) \
6707 case opcode: \
6708 g_assert (sp [0].val.type == stack_type && sp [1].val.type == stack_type); \
6709 result.type = STACK_VALUE_I4; \
6710 result.i = (cast_type) sp [0].val.field relop (cast_type) sp [1].val.field; \
6711 break;
6714 static InterpInst*
6715 interp_fold_binop (TransformData *td, StackContentInfo *sp, InterpInst *ins)
6717 StackValue result;
6718 // Decrement sp so it's easier to access top of the stack
6719 sp -= 2;
6720 if (sp [0].val.type != STACK_VALUE_I4 && sp [0].val.type != STACK_VALUE_I8)
6721 goto cfold_failed;
6722 if (sp [1].val.type != STACK_VALUE_I4 && sp [1].val.type != STACK_VALUE_I8)
6723 goto cfold_failed;
6725 // Top two values of the stack are constants
6726 switch (ins->opcode) {
6727 INTERP_FOLD_BINOP (MINT_ADD_I4, STACK_VALUE_I4, i, +);
6728 INTERP_FOLD_BINOP (MINT_ADD_I8, STACK_VALUE_I8, l, +);
6729 INTERP_FOLD_BINOP (MINT_SUB_I4, STACK_VALUE_I4, i, -);
6730 INTERP_FOLD_BINOP (MINT_SUB_I8, STACK_VALUE_I8, l, -);
6731 INTERP_FOLD_BINOP (MINT_MUL_I4, STACK_VALUE_I4, i, *);
6732 INTERP_FOLD_BINOP (MINT_MUL_I8, STACK_VALUE_I8, l, *);
6734 INTERP_FOLD_BINOP (MINT_AND_I4, STACK_VALUE_I4, i, &);
6735 INTERP_FOLD_BINOP (MINT_AND_I8, STACK_VALUE_I8, l, &);
6736 INTERP_FOLD_BINOP (MINT_OR_I4, STACK_VALUE_I4, i, |);
6737 INTERP_FOLD_BINOP (MINT_OR_I8, STACK_VALUE_I8, l, |);
6738 INTERP_FOLD_BINOP (MINT_XOR_I4, STACK_VALUE_I4, i, ^);
6739 INTERP_FOLD_BINOP (MINT_XOR_I8, STACK_VALUE_I8, l, ^);
6741 INTERP_FOLD_SHIFTOP (MINT_SHL_I4, STACK_VALUE_I4, i, <<, gint32);
6742 INTERP_FOLD_SHIFTOP (MINT_SHL_I8, STACK_VALUE_I8, l, <<, gint64);
6743 INTERP_FOLD_SHIFTOP (MINT_SHR_I4, STACK_VALUE_I4, i, >>, gint32);
6744 INTERP_FOLD_SHIFTOP (MINT_SHR_I8, STACK_VALUE_I8, l, >>, gint64);
6745 INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I4, STACK_VALUE_I4, i, >>, guint32);
6746 INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I8, STACK_VALUE_I8, l, >>, guint64);
6748 INTERP_FOLD_RELOP (MINT_CEQ_I4, STACK_VALUE_I4, i, ==, gint32);
6749 INTERP_FOLD_RELOP (MINT_CEQ_I8, STACK_VALUE_I8, l, ==, gint64);
6750 INTERP_FOLD_RELOP (MINT_CNE_I4, STACK_VALUE_I4, i, !=, gint32);
6751 INTERP_FOLD_RELOP (MINT_CNE_I8, STACK_VALUE_I8, l, !=, gint64);
6753 INTERP_FOLD_RELOP (MINT_CGT_I4, STACK_VALUE_I4, i, >, gint32);
6754 INTERP_FOLD_RELOP (MINT_CGT_I8, STACK_VALUE_I8, l, >, gint64);
6755 INTERP_FOLD_RELOP (MINT_CGT_UN_I4, STACK_VALUE_I4, i, >, guint32);
6756 INTERP_FOLD_RELOP (MINT_CGT_UN_I8, STACK_VALUE_I8, l, >, guint64);
6758 INTERP_FOLD_RELOP (MINT_CGE_I4, STACK_VALUE_I4, i, >=, gint32);
6759 INTERP_FOLD_RELOP (MINT_CGE_I8, STACK_VALUE_I8, l, >=, gint64);
6760 INTERP_FOLD_RELOP (MINT_CGE_UN_I4, STACK_VALUE_I4, i, >=, guint32);
6761 INTERP_FOLD_RELOP (MINT_CGE_UN_I8, STACK_VALUE_I8, l, >=, guint64);
6763 INTERP_FOLD_RELOP (MINT_CLT_I4, STACK_VALUE_I4, i, <, gint32);
6764 INTERP_FOLD_RELOP (MINT_CLT_I8, STACK_VALUE_I8, l, <, gint64);
6765 INTERP_FOLD_RELOP (MINT_CLT_UN_I4, STACK_VALUE_I4, i, <, guint32);
6766 INTERP_FOLD_RELOP (MINT_CLT_UN_I8, STACK_VALUE_I8, l, <, guint64);
6768 INTERP_FOLD_RELOP (MINT_CLE_I4, STACK_VALUE_I4, i, <=, gint32);
6769 INTERP_FOLD_RELOP (MINT_CLE_I8, STACK_VALUE_I8, l, <=, gint64);
6770 INTERP_FOLD_RELOP (MINT_CLE_UN_I4, STACK_VALUE_I4, i, <=, guint32);
6771 INTERP_FOLD_RELOP (MINT_CLE_UN_I8, STACK_VALUE_I8, l, <=, guint64);
6773 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));
6774 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));
6775 INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I4, STACK_VALUE_I4, i, /, guint32, sp [1].val.i != 0);
6776 INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I8, STACK_VALUE_I8, l, /, guint64, sp [1].val.l != 0);
6778 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));
6779 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));
6780 INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I4, STACK_VALUE_I4, i, %, guint32, sp [1].val.i != 0);
6781 INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I8, STACK_VALUE_I8, l, %, guint64, sp [1].val.l != 0);
6783 default:
6784 goto cfold_failed;
6787 // We were able to compute the result of the ins instruction. We store the
6788 // current value for the top of the stack and, if possible, try to replace the
6789 // instructions that are part of this unary operation with a single LDC.
6790 mono_interp_stats.constant_folds++;
6791 if (sp [0].ins != NULL && sp [1].ins != NULL) {
6792 interp_clear_ins (td, sp [0].ins);
6793 interp_clear_ins (td, sp [1].ins);
6794 mono_interp_stats.killed_instructions += 2;
6795 if (result.type == STACK_VALUE_I4)
6796 ins = interp_get_ldc_i4_from_const (td, ins, result.i);
6797 else if (result.type == STACK_VALUE_I8)
6798 ins = interp_inst_replace_with_i8_const (td, ins, result.l);
6799 else
6800 g_assert_not_reached ();
6801 if (td->verbose_level) {
6802 g_print ("Fold binop :\n\t");
6803 dump_interp_inst_newline (ins);
6805 sp [0].ins = ins;
6806 } else {
6807 sp [0].ins = NULL;
6809 sp [0].val = result;
6810 return ins;
6812 cfold_failed:
6813 sp->ins = NULL;
6814 sp->val.type = STACK_VALUE_NONE;
6815 return ins;
6818 static gboolean
6819 interp_local_equal (StackValue *locals, int local1, int local2)
6821 if (local1 == local2)
6822 return TRUE;
6823 if (locals [local1].type == STACK_VALUE_LOCAL && locals [local1].local == local2) {
6824 // local1 is a copy of local2
6825 return TRUE;
6827 if (locals [local2].type == STACK_VALUE_LOCAL && locals [local2].local == local1) {
6828 // local2 is a copy of local1
6829 return TRUE;
6831 if (locals [local1].type == STACK_VALUE_I4 && locals [local2].type == STACK_VALUE_I4)
6832 return locals [local1].i == locals [local2].i;
6833 if (locals [local1].type == STACK_VALUE_I8 && locals [local2].type == STACK_VALUE_I8)
6834 return locals [local1].l == locals [local2].l;
6835 return FALSE;
6838 static void
6839 interp_cprop (TransformData *td)
6841 if (!td->max_stack_height)
6842 return;
6843 StackContentInfo *stack = (StackContentInfo*) g_malloc (td->max_stack_height * sizeof (StackContentInfo));
6844 StackContentInfo *stack_end = stack + td->max_stack_height;
6845 StackContentInfo *sp;
6846 StackValue *locals = (StackValue*) g_malloc (td->locals_size * sizeof (StackValue));
6847 int *local_ref_count = (int*) g_malloc (td->locals_size * sizeof (int));
6848 InterpInst *ins;
6849 int last_il_offset;
6851 retry:
6852 sp = stack;
6853 last_il_offset = -1;
6854 memset (local_ref_count, 0, td->locals_size * sizeof (int));
6856 for (ins = td->first_ins; ins != NULL; ins = ins->next) {
6857 int pop, push;
6858 int il_offset = ins->il_offset;
6859 // Optimizations take place only inside a single basic block
6860 // If two instructions have the same il_offset, then the second one
6861 // cannot be part the start of a basic block.
6862 gboolean is_bb_start = il_offset != -1 && td->is_bb_start [il_offset] && il_offset != last_il_offset;
6863 if (is_bb_start) {
6864 if (td->stack_height [il_offset] >= 0) {
6865 sp = stack + td->stack_height [il_offset];
6866 g_assert (sp <= stack_end);
6867 memset (stack, 0, (sp - stack) * sizeof (StackContentInfo));
6869 memset (locals, 0, td->locals_size * sizeof (StackValue));
6871 // The instruction pops some values then pushes some other
6872 get_inst_stack_usage (td, ins, &pop, &push);
6873 if (td->verbose_level && ins->opcode != MINT_NOP) {
6874 dump_interp_inst (ins);
6875 g_print (", sp %d, (pop %d, push %d)\n", sp - stack, pop, push);
6877 if (MINT_IS_LDLOC (ins->opcode)) {
6878 int replace_op = 0;
6879 int loaded_local = ins->data [0];
6880 local_ref_count [loaded_local]++;
6881 InterpInst *prev_ins = interp_prev_ins (ins);
6882 if (prev_ins && MINT_IS_STLOC (prev_ins->opcode) && !interp_is_bb_start (td, prev_ins, ins) && interp_local_equal (locals, prev_ins->data [0], loaded_local)) {
6883 int mt = prev_ins->opcode - MINT_STLOC_I1;
6884 if (ins->opcode - MINT_LDLOC_I1 == mt) {
6885 if (mt == MINT_TYPE_I4)
6886 replace_op = MINT_STLOC_NP_I4;
6887 else if (mt == MINT_TYPE_I8)
6888 replace_op = MINT_STLOC_NP_I8;
6889 else if (mt == MINT_TYPE_R4)
6890 replace_op = MINT_STLOC_NP_R4;
6891 else if (mt == MINT_TYPE_R8)
6892 replace_op = MINT_STLOC_NP_R8;
6893 else if (mt == MINT_TYPE_O || mt == MINT_TYPE_P)
6894 replace_op = MINT_STLOC_NP_O;
6895 if (replace_op) {
6896 int stored_local = prev_ins->data [0];
6897 sp->ins = NULL;
6898 if (sp->val.type == STACK_VALUE_NONE) {
6899 // We know what local is on the stack now. Track it
6900 sp->val.type = STACK_VALUE_LOCAL;
6901 sp->val.local = stored_local;
6904 // Clear the previous stloc instruction
6905 interp_clear_ins (td, prev_ins);
6906 ins->opcode = replace_op;
6907 ins->data [0] = stored_local;
6908 local_ref_count [loaded_local]--;
6909 if (td->verbose_level) {
6910 g_print ("Add stloc.np :\n\t");
6911 dump_interp_inst_newline (ins);
6913 mono_interp_stats.stloc_nps++;
6914 mono_interp_stats.killed_instructions++;
6917 } else if (locals [loaded_local].type == STACK_VALUE_LOCAL) {
6918 g_assert (locals [loaded_local].type == STACK_VALUE_LOCAL);
6919 g_assert (!(td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT));
6920 // do copy propagation of the original source
6921 mono_interp_stats.copy_propagations++;
6922 local_ref_count [loaded_local]--;
6923 ins->data [0] = locals [loaded_local].local;
6924 local_ref_count [ins->data [0]]++;
6925 if (td->verbose_level) {
6926 g_print ("cprop loc %d -> loc %d :\n\t", loaded_local, locals [loaded_local].local);
6927 dump_interp_inst_newline (ins);
6929 } else if (locals [loaded_local].type == STACK_VALUE_I4 || locals [loaded_local].type == STACK_VALUE_I8) {
6930 gboolean is_i4 = locals [loaded_local].type == STACK_VALUE_I4;
6931 g_assert (!(td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT));
6932 if (is_i4)
6933 ins = interp_get_ldc_i4_from_const (td, ins, locals [loaded_local].i);
6934 else
6935 ins = interp_inst_replace_with_i8_const (td, ins, locals [loaded_local].l);
6936 sp->ins = ins;
6937 sp->val = locals [loaded_local];
6938 local_ref_count [loaded_local]--;
6939 mono_interp_stats.copy_propagations++;
6940 if (td->verbose_level) {
6941 g_print ("cprop loc %d -> ct :\n\t", loaded_local);
6942 dump_interp_inst_newline (ins);
6944 // FIXME this replace_op got ugly
6945 replace_op = ins->opcode;
6947 if (!replace_op) {
6948 // Save the ldloc on the stack if it wasn't optimized away
6949 // For simplicity we don't track locals that have their address taken
6950 // since it is hard to detect instructions that change the local value.
6951 if (td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT) {
6952 sp->val.type = STACK_VALUE_NONE;
6953 } else {
6954 sp->val.type = STACK_VALUE_LOCAL;
6955 sp->val.local = ins->data [0];
6957 sp->ins = ins;
6959 sp++;
6960 } else if (MINT_IS_STLOC (ins->opcode)) {
6961 int dest_local = ins->data [0];
6962 sp--;
6963 if (sp->val.type == STACK_VALUE_LOCAL) {
6964 int src_local = sp->val.local;
6965 if (td->locals [src_local].mt == td->locals [dest_local].mt) {
6966 // The locals have the same type. We can propagate the value
6967 int vtsize = (ins->opcode == MINT_STLOC_VT) ? ins->data [1] : 0;
6969 if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
6970 // Track what exactly is stored into local
6971 locals [dest_local].type = STACK_VALUE_LOCAL;
6972 locals [dest_local].local = src_local;
6975 if (sp->ins) {
6976 interp_clear_ins (td, sp->ins);
6977 interp_clear_ins (td, ins);
6979 ins = interp_insert_ins (td, ins, get_movloc_for_type (td->locals [src_local].mt));
6980 ins->data [0] = src_local;
6981 ins->data [1] = dest_local;
6982 if (vtsize)
6983 ins->data [2] = vtsize;
6984 // Clear ldloc / stloc pair and replace it with movloc superinstruction
6985 if (td->verbose_level) {
6986 g_print ("Add movloc (ldloc off %d) :\n\t", sp->ins->il_offset);
6987 dump_interp_inst_newline (ins);
6989 mono_interp_stats.movlocs++;
6990 mono_interp_stats.killed_instructions++;
6992 } else {
6993 locals [dest_local].type = STACK_VALUE_NONE;
6995 } else if (sp->val.type == STACK_VALUE_NONE || sp->val.type == STACK_VALUE_ARG) {
6996 locals [dest_local].type = STACK_VALUE_NONE;
6997 } else {
6998 g_assert (sp->val.type == STACK_VALUE_I4 || sp->val.type == STACK_VALUE_I8);
6999 if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT))
7000 locals [dest_local] = sp->val;
7002 clear_stack_content_info_for_local (stack, sp, dest_local);
7003 clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
7004 } else if (MINT_IS_LDC_I4 (ins->opcode) || ins->opcode == MINT_LDC_I8) {
7005 StackValue val;
7006 gboolean is_i8 = ins->opcode == MINT_LDC_I8;
7007 InterpInst *prev_ins = interp_prev_ins (ins);
7009 if (is_i8) {
7010 val.type = STACK_VALUE_I8;
7011 val.l = READ64 (&ins->data [0]);
7012 } else {
7013 val.type = STACK_VALUE_I4;
7014 val.i = interp_get_const_from_ldc_i4 (ins);
7017 if (prev_ins && prev_ins->opcode == MINT_POP &&
7018 ((is_i8 && sp->val.type == STACK_VALUE_I8 && sp->val.l == val.l) ||
7019 (!is_i8 && sp->val.type == STACK_VALUE_I4 && sp->val.i == val.i)) &&
7020 !interp_is_bb_start (td, prev_ins, ins)) {
7021 // The previous instruction pops the stack of the value we are pushing
7022 // right now. We can kill both instructions
7023 if (td->verbose_level)
7024 g_print ("Kill redundant pop/ldc pair: pop (off %p), ldc (off %p)\n", prev_ins->il_offset, ins->il_offset);
7025 interp_clear_ins (td, prev_ins);
7026 interp_clear_ins (td, ins);
7027 mono_interp_stats.killed_instructions += 2;
7028 } else {
7029 sp->ins = ins;
7030 sp->val = val;
7032 sp++;
7033 } else if (MINT_IS_MOVLOC (ins->opcode)) {
7034 int src_local = ins->data [0];
7035 int dest_local = ins->data [1];
7036 local_ref_count [src_local]++;
7037 if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
7038 if (locals [src_local].type != STACK_VALUE_NONE) {
7039 locals [dest_local] = locals [src_local];
7040 } else {
7041 locals [dest_local].type = STACK_VALUE_LOCAL;
7042 locals [dest_local].local = src_local;
7044 clear_stack_content_info_for_local (stack, sp, dest_local);
7045 clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
7047 } else if (MINT_IS_STLOC_NP (ins->opcode)) {
7048 int dest_local = ins->data [0];
7049 // Prevent optimizing away the instruction that pushed the value on the stack
7050 sp [-1].ins = NULL;
7051 // The local contains the value of the top of stack
7052 if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
7053 locals [dest_local] = sp [-1].val;
7054 clear_stack_content_info_for_local (stack, sp, dest_local);
7055 clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
7057 } else if (ins->opcode == MINT_DUP || ins->opcode == MINT_DUP_VT) {
7058 sp [0].val = sp [-1].val;
7059 sp [0].ins = ins;
7060 // If top of stack is known, we could also replace dup with an explicit
7061 // propagated instruction, so we remove the top of stack dependency
7062 sp [-1].ins = NULL;
7063 sp++;
7064 } else if (MINT_IS_LDARG (ins->opcode)) {
7065 sp->ins = ins;
7066 sp->val.type = STACK_VALUE_ARG;
7067 sp->val.arg = ins->opcode == MINT_LDARG_P0 ? 0 : ins->data [0];
7068 sp++;
7069 } else if (MINT_IS_STARG (ins->opcode)) {
7070 int dest_arg = ins->data [0];
7071 sp--;
7072 clear_stack_content_info_for_argument (stack, sp, dest_arg);
7073 } else if (ins->opcode >= MINT_BOX && ins->opcode <= MINT_BOX_NULLABLE) {
7074 int offset = ins->data [1];
7075 // Clear the stack slot that is boxed
7076 memset (&sp [-1 - offset], 0, sizeof (StackContentInfo));
7077 // Make sure that the instructions that pushed this stack slot can't be
7078 // optimized away. If we would optimize them away, we would also need to
7079 // update the offset in the box instruction, which we can't, for now.
7080 for (int i = 1; i <= offset; i++)
7081 sp [-i].ins = NULL;
7082 } else if (ins->opcode == MINT_CKNULL_N) {
7083 int offset = ins->data [0];
7084 for (int i = 1; i <= offset; i++)
7085 sp [-i].ins = NULL;
7086 } else if (ins->opcode == MINT_LD_DELEGATE_INVOKE_IMPL) {
7087 int offset = ins->data [0];
7088 for (int i = 1; i <= offset; i++)
7089 sp [-i].ins = NULL;
7090 memset (sp, 0, sizeof (StackContentInfo));
7091 sp++;
7092 } else if (ins->opcode == MINT_POP) {
7093 sp--;
7094 if (sp->ins) {
7095 // The top of the stack is not used by any instructions. Kill both the
7096 // instruction that pushed it and the pop.
7097 interp_clear_ins (td, sp->ins);
7098 interp_clear_ins (td, ins);
7099 mono_interp_stats.killed_instructions += 2;
7100 // The value pop-ed by this instruction can still be accessed. If we also
7101 // kill the instruction pushing the value, then we need to empty the
7102 // value of the stack, so it is not considered for further optimizations.
7103 sp->val.type = STACK_VALUE_NONE;
7105 } else if (ins->opcode == MINT_NEWOBJ_FAST && ins->data [0] == INLINED_METHOD_FLAG) {
7106 int param_count = ins->data [1];
7107 // memmove the stack values while clearing ins, to prevent instruction removal
7108 for (int i = 1; i <= param_count; i++) {
7109 sp [-i + 2] = sp [-i];
7110 sp [-i + 2].ins = NULL;
7112 // clear stack information for the slots where the allocated object resides
7113 memset (&sp [-param_count], 0, 2 * sizeof (StackContentInfo));
7114 sp += 2;
7115 } else if (ins->opcode == MINT_CASTCLASS || ins->opcode == MINT_CASTCLASS_COMMON || ins->opcode == MINT_CASTCLASS_INTERFACE) {
7116 // Keep the value on the stack, but prevent optimizing away
7117 sp [-1].ins = NULL;
7118 } else if (MINT_IS_CONDITIONAL_BRANCH (ins->opcode)) {
7119 sp -= pop;
7120 g_assert (push == 0);
7121 // We can't clear any instruction that pushes the stack, because the
7122 // branched code will expect a certain stack size.
7123 for (StackContentInfo *sp_iter = stack; sp_iter < sp; sp_iter++)
7124 sp_iter->ins = NULL;
7125 } else if (MINT_IS_UNOP (ins->opcode)) {
7126 ins = interp_fold_unop (td, sp, ins);
7127 } else if (MINT_IS_BINOP (ins->opcode)) {
7128 ins = interp_fold_binop (td, sp, ins);
7129 sp--;
7130 } else if (ins->opcode >= MINT_STFLD_I1 && ins->opcode <= MINT_STFLD_P && (mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS)) {
7131 StackContentInfo *src = &sp [-2];
7132 if (src->ins) {
7133 if (src->val.type == STACK_VALUE_LOCAL) {
7134 int loc_index = src->val.local;
7135 int fld_offset = ins->data [0];
7136 int mt = ins->opcode - MINT_STFLD_I1;
7137 ins = interp_insert_ins (td, ins, MINT_STLOCFLD_I1 + mt);
7138 ins->data [0] = loc_index;
7139 ins->data [1] = fld_offset;
7140 local_ref_count [loc_index]++;
7141 interp_clear_ins (td, ins->prev);
7142 interp_clear_ins (td, src->ins);
7143 mono_interp_stats.super_instructions++;
7144 mono_interp_stats.killed_instructions++;
7145 } else if (src->val.type == STACK_VALUE_ARG) {
7146 int arg_index = src->val.arg;
7147 int fld_offset = ins->data [0];
7148 int mt = ins->opcode - MINT_STFLD_I1;
7149 ins = interp_insert_ins (td, ins, MINT_STARGFLD_I1 + mt);
7150 ins->data [0] = arg_index;
7151 ins->data [1] = fld_offset;
7152 interp_clear_ins (td, ins->prev);
7153 interp_clear_ins (td, src->ins);
7154 mono_interp_stats.super_instructions++;
7155 mono_interp_stats.killed_instructions++;
7158 sp -= 2;
7159 } else if (MINT_IS_STLOCFLD (ins->opcode)) {
7160 local_ref_count [ins->data [0]]++;
7161 sp--;
7162 } else {
7163 if (pop == MINT_POP_ALL)
7164 pop = sp - stack;
7165 sp += push - pop;
7166 g_assert (sp >= stack && sp <= stack_end);
7167 g_assert ((sp - push) >= stack && (sp - push) <= stack_end);
7168 memset (sp - push, 0, push * sizeof (StackContentInfo));
7169 // If this instruction only pushes a single value, make it a candidate for
7170 // removal, if its value is not used anywhere.
7171 if (push == 1 && pop == 0 && !MINT_IS_CALL (ins->opcode) && !MINT_IS_NEWOBJ (ins->opcode))
7172 sp [-1].ins = ins;
7174 last_il_offset = ins->il_offset;
7177 if (interp_local_deadce (td, local_ref_count))
7178 goto retry;
7180 g_free (stack);
7181 g_free (locals);
7182 g_free (local_ref_count);
7185 void
7186 mono_test_interp_cprop (TransformData *td)
7188 interp_cprop (td);
7191 static void
7192 interp_super_instructions (TransformData *td)
7194 InterpInst *ins;
7195 InterpInst *prev1_ins = NULL;
7196 InterpInst *prev2_ins = NULL;
7197 int last_il_offset = -1;
7198 for (ins = td->first_ins; ins != NULL; ins = ins->next) {
7199 int il_offset = ins->il_offset;
7200 // If two instructions have the same il_offset, then the second one
7201 // cannot be the start of a basic block.
7202 gboolean is_bb_start = il_offset != -1 && td->is_bb_start [il_offset] && il_offset != last_il_offset;
7203 last_il_offset = il_offset;
7204 if (ins->opcode == MINT_NOP)
7205 continue;
7206 if (is_bb_start) {
7207 // Prevent optimizations spanning multiple basic blocks
7208 prev2_ins = NULL;
7209 prev1_ins = NULL;
7211 if (ins->opcode >= MINT_LDFLD_I1 && ins->opcode <= MINT_LDFLD_P && prev1_ins) {
7212 if (prev1_ins->opcode == MINT_LDLOC_O) {
7213 int loc_index = prev1_ins->data [0];
7214 int fld_offset = ins->data [0];
7215 int mt = ins->opcode - MINT_LDFLD_I1;
7216 ins = interp_insert_ins (td, ins, MINT_LDLOCFLD_I1 + mt);
7217 ins->data [0] = loc_index;
7218 ins->data [1] = fld_offset;
7219 interp_clear_ins (td, ins->prev);
7220 interp_clear_ins (td, prev1_ins);
7221 prev1_ins = NULL;
7222 mono_interp_stats.super_instructions++;
7223 mono_interp_stats.killed_instructions++;
7224 } else if (prev1_ins->opcode == MINT_LDARG_O || prev1_ins->opcode == MINT_LDARG_P0) {
7225 int arg_index = 0;
7226 int fld_offset = ins->data [0];
7227 int mt = ins->opcode - MINT_LDFLD_I1;
7228 if (prev1_ins->opcode == MINT_LDARG_O)
7229 arg_index = prev1_ins->data [0];
7230 ins = interp_insert_ins (td, ins, MINT_LDARGFLD_I1 + mt);
7231 ins->data [0] = arg_index;
7232 ins->data [1] = fld_offset;
7233 interp_clear_ins (td, ins->prev);
7234 interp_clear_ins (td, prev1_ins);
7235 prev1_ins = NULL;
7236 mono_interp_stats.super_instructions++;
7237 mono_interp_stats.killed_instructions++;
7239 } else if (MINT_IS_STLOC (ins->opcode) && prev1_ins && prev2_ins) {
7240 if (prev1_ins->opcode == MINT_ADD1_I4 || prev1_ins->opcode == MINT_ADD1_I8 ||
7241 prev1_ins->opcode == MINT_SUB1_I4 || prev1_ins->opcode == MINT_SUB1_I8) {
7242 if (MINT_IS_LDLOC (prev2_ins->opcode) && prev2_ins->data [0] == ins->data [0]) {
7243 if (prev1_ins->opcode == MINT_ADD1_I4)
7244 ins->opcode = MINT_LOCADD1_I4;
7245 else if (prev1_ins->opcode == MINT_ADD1_I8)
7246 ins->opcode = MINT_LOCADD1_I8;
7247 else if (prev1_ins->opcode == MINT_SUB1_I4)
7248 ins->opcode = MINT_LOCSUB1_I4;
7249 else
7250 ins->opcode = MINT_LOCSUB1_I8;
7251 // the local index is already set inside the replaced STLOC instruction
7252 interp_clear_ins (td, prev1_ins);
7253 interp_clear_ins (td, prev2_ins);
7254 prev1_ins = NULL;
7255 mono_interp_stats.super_instructions++;
7256 mono_interp_stats.killed_instructions += 2;
7260 prev2_ins = prev1_ins;
7261 prev1_ins = ins;
7265 static void
7266 interp_optimize_code (TransformData *td)
7268 if (mono_interp_opt & INTERP_OPT_CPROP)
7269 MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td));
7271 if (mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS)
7272 MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td));
7275 static void
7276 generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoGenericContext *generic_context, MonoError *error)
7278 MonoDomain *domain = rtm->domain;
7279 int i;
7280 TransformData transform_data;
7281 TransformData *td;
7282 static gboolean verbose_method_inited;
7283 static char* verbose_method_name;
7285 if (!verbose_method_inited) {
7286 verbose_method_name = g_getenv ("MONO_VERBOSE_METHOD");
7287 verbose_method_inited = TRUE;
7290 memset (&transform_data, 0, sizeof(transform_data));
7291 td = &transform_data;
7293 td->method = method;
7294 td->rtm = rtm;
7295 td->code_size = header->code_size;
7296 td->header = header;
7297 td->max_code_size = td->code_size;
7298 td->in_offsets = (int*)g_malloc0((header->code_size + 1) * sizeof(int));
7299 td->stack_height = (int*)g_malloc(header->code_size * sizeof(int));
7300 td->stack_state = (StackInfo**)g_malloc0(header->code_size * sizeof(StackInfo *));
7301 td->vt_stack_size = (int*)g_malloc(header->code_size * sizeof(int));
7302 td->clause_indexes = (int*)g_malloc (header->code_size * sizeof (int));
7303 td->is_bb_start = (guint8*)g_malloc0(header->code_size);
7304 td->mempool = mono_mempool_new ();
7305 td->n_data_items = 0;
7306 td->max_data_items = 0;
7307 td->data_items = NULL;
7308 td->data_hash = g_hash_table_new (NULL, NULL);
7309 #ifdef ENABLE_EXPERIMENT_TIERED
7310 td->patchsite_hash = g_hash_table_new (NULL, NULL);
7311 #endif
7312 td->gen_sdb_seq_points = mini_debug_options.gen_sdb_seq_points;
7313 td->seq_points = g_ptr_array_new ();
7314 td->verbose_level = mono_interp_traceopt;
7315 rtm->data_items = td->data_items;
7317 interp_method_compute_offsets (td, rtm, mono_method_signature_internal (method), header);
7319 if (verbose_method_name) {
7320 const char *name = verbose_method_name;
7322 if ((strchr (name, '.') > name) || strchr (name, ':')) {
7323 MonoMethodDesc *desc;
7325 desc = mono_method_desc_new (name, TRUE);
7326 if (mono_method_desc_full_match (desc, method)) {
7327 td->verbose_level = 4;
7329 mono_method_desc_free (desc);
7330 } else {
7331 if (strcmp (method->name, name) == 0)
7332 td->verbose_level = 4;
7336 td->stack = (StackInfo*)g_malloc0 ((header->max_stack + 1) * sizeof (td->stack [0]));
7337 td->stack_capacity = header->max_stack + 1;
7338 td->sp = td->stack;
7339 td->max_stack_height = 0;
7340 td->line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
7341 td->current_il_offset = -1;
7343 generate_code (td, method, header, generic_context, error);
7344 goto_if_nok (error, exit);
7346 interp_optimize_code (td);
7348 generate_compacted_code (td);
7350 if (td->verbose_level) {
7351 g_print ("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method, TRUE), rtm, td->max_vt_sp);
7352 g_print ("Calculated stack size: %d, stated size: %d\n", td->max_stack_height, header->max_stack);
7353 dump_mint_code (td->new_code, td->new_code_end);
7356 /* Check if we use excessive stack space */
7357 if (td->max_stack_height > header->max_stack * 3 && header->max_stack > 16)
7358 g_warning ("Excessive stack space usage for method %s, %d/%d", method->name, td->max_stack_height, header->max_stack);
7360 int code_len_u8, code_len_u16;
7361 code_len_u8 = (guint8 *) td->new_code_end - (guint8 *) td->new_code;
7362 code_len_u16 = td->new_code_end - td->new_code;
7364 rtm->clauses = (MonoExceptionClause*)mono_domain_alloc0 (domain, header->num_clauses * sizeof (MonoExceptionClause));
7365 memcpy (rtm->clauses, header->clauses, header->num_clauses * sizeof(MonoExceptionClause));
7366 rtm->code = (gushort*)td->new_code;
7367 rtm->init_locals = header->init_locals;
7368 rtm->num_clauses = header->num_clauses;
7369 for (i = 0; i < header->num_clauses; i++) {
7370 MonoExceptionClause *c = rtm->clauses + i;
7371 int end_off = c->try_offset + c->try_len;
7372 c->try_offset = get_in_offset (td, c->try_offset);
7373 c->try_len = find_in_offset (td, end_off) - c->try_offset;
7374 g_assert ((c->try_offset + c->try_len) < code_len_u16);
7375 end_off = c->handler_offset + c->handler_len;
7376 c->handler_offset = get_in_offset (td, c->handler_offset);
7377 c->handler_len = find_in_offset (td, end_off) - c->handler_offset;
7378 g_assert (c->handler_len >= 0 && (c->handler_offset + c->handler_len) <= code_len_u16);
7379 if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER)
7380 c->data.filter_offset = get_in_offset (td, c->data.filter_offset);
7382 rtm->stack_size = (sizeof (stackval)) * (td->max_stack_height + 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/
7383 rtm->stack_size = ALIGN_TO (rtm->stack_size, MINT_VT_ALIGNMENT);
7384 rtm->vt_stack_size = td->max_vt_sp;
7385 rtm->total_locals_size = td->total_locals_size;
7386 rtm->alloca_size = ALIGN_TO (rtm->total_locals_size + rtm->vt_stack_size + rtm->stack_size, 8);
7387 rtm->data_items = (gpointer*)mono_domain_alloc0 (domain, td->n_data_items * sizeof (td->data_items [0]));
7388 memcpy (rtm->data_items, td->data_items, td->n_data_items * sizeof (td->data_items [0]));
7390 /* Save debug info */
7391 interp_save_debug_info (rtm, header, td, td->line_numbers);
7393 /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
7394 int jinfo_len;
7395 jinfo_len = mono_jit_info_size ((MonoJitInfoFlags)0, header->num_clauses, 0);
7396 MonoJitInfo *jinfo;
7397 jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len);
7398 jinfo->is_interp = 1;
7399 rtm->jinfo = jinfo;
7400 mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len_u8, (MonoJitInfoFlags)0, header->num_clauses, 0);
7401 for (i = 0; i < jinfo->num_clauses; ++i) {
7402 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
7403 MonoExceptionClause *c = rtm->clauses + i;
7405 ei->flags = c->flags;
7406 ei->try_start = (guint8*)(rtm->code + c->try_offset);
7407 ei->try_end = (guint8*)(rtm->code + c->try_offset + c->try_len);
7408 ei->handler_start = (guint8*)(rtm->code + c->handler_offset);
7409 ei->exvar_offset = rtm->exvar_offsets [i];
7410 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
7411 ei->data.filter = (guint8*)(rtm->code + c->data.filter_offset);
7412 } else if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
7413 ei->data.handler_end = (guint8*)(rtm->code + c->handler_offset + c->handler_len);
7414 } else {
7415 ei->data.catch_class = c->data.catch_class;
7419 save_seq_points (td, jinfo);
7420 #ifdef ENABLE_EXPERIMENT_TIERED
7421 /* debugging aid, it makes `mono_pmip` work. */
7422 mono_jit_info_table_add (domain, jinfo);
7423 #endif
7425 exit:
7426 g_free (td->in_offsets);
7427 g_free (td->stack_height);
7428 for (i = 0; i < header->code_size; ++i)
7429 g_free (td->stack_state [i]);
7430 g_free (td->stack_state);
7431 g_free (td->vt_stack_size);
7432 g_free (td->clause_indexes);
7433 g_free (td->data_items);
7434 g_free (td->stack);
7435 g_free (td->is_bb_start);
7436 g_free (td->locals);
7437 g_hash_table_destroy (td->data_hash);
7438 #ifdef ENABLE_EXPERIMENT_TIERED
7439 g_hash_table_destroy (td->patchsite_hash);
7440 #endif
7441 g_ptr_array_free (td->seq_points, TRUE);
7442 g_array_free (td->line_numbers, TRUE);
7443 mono_mempool_destroy (td->mempool);
7446 gboolean
7447 mono_test_interp_generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error)
7449 return generate_code (td, method, header, generic_context, error);
7452 static mono_mutex_t calc_section;
7454 #ifdef ENABLE_EXPERIMENT_TIERED
7455 static gboolean
7456 tiered_patcher (MiniTieredPatchPointContext *ctx, gpointer patchsite)
7458 ERROR_DECL (error);
7459 MonoMethod *m = ctx->target_method;
7461 if (!jit_call2_supported (m, mono_method_signature_internal (m)))
7462 return FALSE;
7464 /* TODO: Force compilation here. Currently the JIT will be invoked upon
7465 * first execution of `MINT_JIT_CALL2`. */
7466 InterpMethod *rmethod = mono_interp_get_imethod (ctx->domain, m, error);
7467 mono_error_assert_ok (error);
7469 guint16 *ip = ((guint16 *) patchsite);
7470 *ip++ = MINT_JIT_CALL2;
7471 /* FIXME: this only works on 64bit */
7472 WRITE64 (ip, &rmethod);
7473 mono_memory_barrier ();
7475 return TRUE;
7477 #endif
7480 void
7481 mono_interp_transform_init (void)
7483 mono_os_mutex_init_recursive(&calc_section);
7485 #ifdef ENABLE_EXPERIMENT_TIERED
7486 mini_tiered_register_callsite_patcher (tiered_patcher, TIERED_PATCH_KIND_INTERP);
7487 #endif
7490 void
7491 mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, MonoError *error)
7493 MonoMethod *method = imethod->method;
7494 MonoMethodHeader *header = NULL;
7495 MonoMethodSignature *signature = mono_method_signature_internal (method);
7496 MonoVTable *method_class_vt;
7497 MonoGenericContext *generic_context = NULL;
7498 MonoDomain *domain = imethod->domain;
7499 InterpMethod tmp_imethod;
7500 InterpMethod *real_imethod;
7502 error_init (error);
7504 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method->klass))) {
7505 mono_error_set_invalid_operation (error, "%s", "Could not execute the method because the containing type is not fully instantiated.");
7506 return;
7509 // g_printerr ("TRANSFORM(0x%016lx): begin %s::%s\n", mono_thread_current (), method->klass->name, method->name);
7510 method_class_vt = mono_class_vtable_checked (domain, imethod->method->klass, error);
7511 return_if_nok (error);
7513 if (!method_class_vt->initialized) {
7514 mono_runtime_class_init_full (method_class_vt, error);
7515 return_if_nok (error);
7518 MONO_PROFILER_RAISE (jit_begin, (method));
7520 if (mono_method_signature_internal (method)->is_inflated)
7521 generic_context = mono_method_get_context (method);
7522 else {
7523 MonoGenericContainer *generic_container = mono_method_get_generic_container (method);
7524 if (generic_container)
7525 generic_context = &generic_container->context;
7528 if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
7529 MonoMethod *nm = NULL;
7530 if (imethod->transformed) {
7531 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));
7532 return;
7535 /* assumes all internal calls with an array this are built in... */
7536 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL && (! mono_method_signature_internal (method)->hasthis || m_class_get_rank (method->klass) == 0)) {
7537 nm = mono_marshal_get_native_wrapper (method, FALSE, FALSE);
7538 signature = mono_method_signature_internal (nm);
7539 } else {
7540 const char *name = method->name;
7541 if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) {
7542 if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
7543 MonoJitICallInfo *mi = &mono_get_jit_icall_info ()->ves_icall_mono_delegate_ctor_interp;
7544 nm = mono_marshal_get_icall_wrapper (mi, TRUE);
7545 } else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
7547 * Usually handled during transformation of the caller, but
7548 * when the caller is handled by another execution engine
7549 * (for example fullAOT) we need to handle it here. That's
7550 * known to be wrong in cases where the reference to
7551 * `MonoDelegate` would be needed (FIXME).
7553 nm = mono_marshal_get_delegate_invoke (method, NULL);
7554 } else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
7555 nm = mono_marshal_get_delegate_begin_invoke (method);
7556 } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
7557 nm = mono_marshal_get_delegate_end_invoke (method);
7560 if (nm == NULL)
7561 g_assert_not_reached ();
7563 if (nm == NULL) {
7564 mono_os_mutex_lock (&calc_section);
7565 imethod->stack_size = sizeof (stackval); /* for tracing */
7566 imethod->alloca_size = imethod->stack_size;
7567 mono_memory_barrier ();
7568 imethod->transformed = TRUE;
7569 mono_interp_stats.methods_transformed++;
7570 mono_os_mutex_unlock (&calc_section);
7571 MONO_PROFILER_RAISE (jit_done, (method, NULL));
7572 return;
7574 method = nm;
7575 header = interp_method_get_header (nm, error);
7576 return_if_nok (error);
7579 if (!header) {
7580 header = mono_method_get_header_checked (method, error);
7581 return_if_nok (error);
7584 g_assert ((signature->param_count + signature->hasthis) < 1000);
7585 // g_printerr ("TRANSFORM(0x%016lx): end %s::%s\n", mono_thread_current (), method->klass->name, method->name);
7587 /* Make modifications to a copy of imethod, copy them back inside the lock */
7588 real_imethod = imethod;
7589 memcpy (&tmp_imethod, imethod, sizeof (InterpMethod));
7590 imethod = &tmp_imethod;
7592 MONO_TIME_TRACK (mono_interp_stats.transform_time, generate (method, header, imethod, generic_context, error));
7594 mono_metadata_free_mh (header);
7596 return_if_nok (error);
7598 /* Copy changes back */
7599 imethod = real_imethod;
7600 mono_os_mutex_lock (&calc_section);
7601 if (!imethod->transformed) {
7602 // Ignore the first two fields which are unchanged. next_jit_code_hash shouldn't
7603 // be modified because it is racy with internal hash table insert.
7604 const int start_offset = 2 * sizeof (gpointer);
7605 memcpy ((char*)imethod + start_offset, (char*)&tmp_imethod + start_offset, sizeof (InterpMethod) - start_offset);
7606 mono_memory_barrier ();
7607 imethod->transformed = TRUE;
7608 mono_interp_stats.methods_transformed++;
7609 mono_atomic_fetch_add_i32 (&mono_jit_stats.methods_with_interp, 1);
7612 mono_os_mutex_unlock (&calc_section);
7614 mono_domain_lock (domain);
7615 if (mono_stats_method_desc && mono_method_desc_full_match (mono_stats_method_desc, imethod->method)) {
7616 g_printf ("Printing runtime stats at method: %s\n", mono_method_get_full_name (imethod->method));
7617 mono_runtime_print_stats ();
7619 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, imethod->method))
7620 g_hash_table_insert (domain_jit_info (domain)->seq_points, imethod->method, imethod->jinfo->seq_points);
7621 mono_domain_unlock (domain);
7623 // FIXME: Add a different callback ?
7624 MONO_PROFILER_RAISE (jit_done, (method, imethod->jinfo));