* TextControl.cs: Make PageUp and PageDown more like the
[mono-project.git] / mono / mini / mini.c
blob881a32f7f7f6fc6252653f4ca09977fa2c788ec6
1 /*
2 * mini.c: The new Mono code generator.
4 * Author:
5 * Paolo Molaro (lupus@ximian.com)
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2002 Ximian, Inc.
9 */
11 #include <config.h>
12 #include <signal.h>
13 #include <unistd.h>
14 #include <math.h>
15 #include <sys/time.h>
17 #ifdef PLATFORM_MACOSX
18 #include <mach/mach.h>
19 #include <mach/mach_error.h>
20 #include <mach/exception.h>
21 #include <mach/task.h>
22 #include <pthread.h>
23 #endif
25 #ifdef HAVE_VALGRIND_MEMCHECK_H
26 #include <valgrind/memcheck.h>
27 #endif
29 #include <mono/metadata/assembly.h>
30 #include <mono/metadata/loader.h>
31 #include <mono/metadata/cil-coff.h>
32 #include <mono/metadata/tabledefs.h>
33 #include <mono/metadata/class.h>
34 #include <mono/metadata/object.h>
35 #include <mono/metadata/exception.h>
36 #include <mono/metadata/opcodes.h>
37 #include <mono/metadata/mono-endian.h>
38 #include <mono/metadata/tokentype.h>
39 #include <mono/metadata/tabledefs.h>
40 #include <mono/metadata/threads.h>
41 #include <mono/metadata/marshal.h>
42 #include <mono/metadata/socket-io.h>
43 #include <mono/metadata/appdomain.h>
44 #include <mono/metadata/debug-helpers.h>
45 #include <mono/io-layer/io-layer.h>
46 #include "mono/metadata/profiler.h"
47 #include <mono/metadata/profiler-private.h>
48 #include <mono/metadata/mono-config.h>
49 #include <mono/metadata/environment.h>
50 #include <mono/metadata/mono-debug.h>
51 #include <mono/metadata/monitor.h>
52 #include <mono/metadata/security-manager.h>
53 #include <mono/metadata/threads-types.h>
54 #include <mono/metadata/rawbuffer.h>
55 #include <mono/utils/mono-math.h>
56 #include <mono/utils/mono-compiler.h>
57 #include <mono/utils/mono-counters.h>
58 #include <mono/utils/mono-logger.h>
59 #include <mono/os/gc_wrapper.h>
61 #include "mini.h"
62 #include <string.h>
63 #include <ctype.h>
64 #include "inssel.h"
65 #include "trace.h"
67 #include "jit-icalls.h"
69 #include "aliasing.h"
71 #define BRANCH_COST 100
72 #define INLINE_LENGTH_LIMIT 20
73 #define INLINE_FAILURE do {\
74 if ((cfg->method != method) && (method->wrapper_type == MONO_WRAPPER_NONE))\
75 goto inline_failure;\
76 } while (0)
78 /*
79 * this is used to determine when some branch optimizations are possible: we exclude FP compares
80 * because they have weird semantics with NaNs.
82 #define MONO_IS_COND_BRANCH_OP(ins) (((ins)->opcode >= CEE_BEQ && (ins)->opcode <= CEE_BLT_UN) || ((ins)->opcode >= OP_LBEQ && (ins)->opcode <= OP_LBLT_UN) || ((ins)->opcode >= OP_FBEQ && (ins)->opcode <= OP_FBLT_UN) || ((ins)->opcode >= OP_IBEQ && (ins)->opcode <= OP_IBLT_UN))
83 #define MONO_IS_COND_BRANCH_NOFP(ins) (MONO_IS_COND_BRANCH_OP(ins) && (ins)->inst_left->inst_left->type != STACK_R8)
85 #define MONO_IS_BRANCH_OP(ins) (MONO_IS_COND_BRANCH_OP(ins) || ((ins)->opcode == CEE_BR) || ((ins)->opcode == OP_BR_REG) || ((ins)->opcode == CEE_SWITCH))
87 #define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && (ins)->ssa_op == MONO_SSA_LOAD && (ins)->inst_left->inst_c0 == 0)
89 static void setup_stat_profiler (void);
90 gboolean mono_arch_print_tree(MonoInst *tree, int arity);
91 static gpointer mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt);
92 static gpointer mono_jit_compile_method (MonoMethod *method);
93 static gpointer mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method);
94 static gpointer mono_create_jit_trampoline_in_domain (MonoDomain *domain, MonoMethod *method);
96 static void handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src,
97 const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native, gboolean write_barrier);
99 static void dec_foreach (MonoInst *tree, MonoCompile *cfg);
101 static int mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
102 int locals_offset, MonoInst *return_var, GList *dont_inline, MonoInst **inline_args,
103 guint inline_offset, gboolean is_virtual_call);
105 /* helper methods signature */
106 static MonoMethodSignature *helper_sig_class_init_trampoline = NULL;
107 static MonoMethodSignature *helper_sig_domain_get = NULL;
109 static guint32 default_opt = 0;
110 static gboolean default_opt_set = FALSE;
112 guint32 mono_jit_tls_id = -1;
113 MonoTraceSpec *mono_jit_trace_calls = NULL;
114 gboolean mono_break_on_exc = FALSE;
115 #ifndef DISABLE_AOT
116 gboolean mono_compile_aot = FALSE;
117 #endif
118 gboolean mono_use_security_manager = FALSE;
120 static int mini_verbose = 0;
122 #define mono_jit_lock() EnterCriticalSection (&jit_mutex)
123 #define mono_jit_unlock() LeaveCriticalSection (&jit_mutex)
124 static CRITICAL_SECTION jit_mutex;
126 static GHashTable *class_init_hash_addr = NULL;
128 static MonoCodeManager *global_codeman = NULL;
130 static GHashTable *jit_icall_name_hash = NULL;
132 static MonoDebugOptions debug_options;
135 * Address of the trampoline code. This is used by the debugger to check
136 * whether a method is a trampoline.
138 guint8* mono_trampoline_code [MONO_TRAMPOLINE_NUM];
140 gboolean
141 mono_running_on_valgrind (void)
143 #ifdef HAVE_VALGRIND_MEMCHECK_H
144 if (RUNNING_ON_VALGRIND)
145 return TRUE;
146 else
147 return FALSE;
148 #else
149 return FALSE;
150 #endif
154 * mono_create_ftnptr:
156 * Given a function address, create a function descriptor for it.
157 * This is only needed on IA64.
159 gpointer
160 mono_create_ftnptr (MonoDomain *domain, gpointer addr)
162 #ifdef __ia64__
163 gpointer *desc;
165 mono_domain_lock (domain);
166 desc = mono_code_manager_reserve (domain->code_mp, 2 * sizeof (gpointer));
167 mono_domain_unlock (domain);
169 desc [0] = addr;
170 desc [1] = NULL;
172 return desc;
173 #else
174 return addr;
175 #endif
178 typedef struct {
179 void *ip;
180 MonoMethod *method;
181 } FindTrampUserData;
183 static void
184 find_tramp (gpointer key, gpointer value, gpointer user_data)
186 FindTrampUserData *ud = (FindTrampUserData*)user_data;
188 if (value == ud->ip)
189 ud->method = (MonoMethod*)key;
192 /* debug function */
193 G_GNUC_UNUSED static char*
194 get_method_from_ip (void *ip)
196 MonoJitInfo *ji;
197 char *method;
198 char *res;
199 MonoDomain *domain = mono_domain_get ();
200 MonoDebugSourceLocation *location;
201 FindTrampUserData user_data;
203 ji = mono_jit_info_table_find (domain, ip);
204 if (!ji) {
205 user_data.ip = ip;
206 user_data.method = NULL;
207 mono_domain_lock (domain);
208 g_hash_table_foreach (domain->jit_trampoline_hash, find_tramp, &user_data);
209 mono_domain_unlock (domain);
210 if (user_data.method) {
211 char *mname = mono_method_full_name (user_data.method, TRUE);
212 res = g_strdup_printf ("<%p - JIT trampoline for %s>", ip, mname);
213 g_free (mname);
214 return res;
216 else
217 return NULL;
219 method = mono_method_full_name (ji->method, TRUE);
220 /* FIXME: unused ? */
221 location = mono_debug_lookup_source_location (ji->method, (guint32)((guint8*)ip - (guint8*)ji->code_start), domain);
223 res = g_strdup_printf (" %s + 0x%x (%p %p) [%p - %s]", method, (int)((char*)ip - (char*)ji->code_start), ji->code_start, (char*)ji->code_start + ji->code_size, domain, domain->friendly_name);
225 mono_debug_free_source_location (location);
226 g_free (method);
228 return res;
231 G_GNUC_UNUSED char *
232 mono_pmip (void *ip)
234 return get_method_from_ip (ip);
237 /* debug function */
238 void
239 mono_print_method_from_ip (void *ip)
241 MonoJitInfo *ji;
242 char *method;
243 MonoDebugSourceLocation *source;
244 MonoDomain *domain = mono_domain_get ();
245 FindTrampUserData user_data;
247 ji = mono_jit_info_table_find (domain, ip);
248 if (!ji) {
249 user_data.ip = ip;
250 user_data.method = NULL;
251 mono_domain_lock (domain);
252 g_hash_table_foreach (domain->jit_trampoline_hash, find_tramp, &user_data);
253 mono_domain_unlock (domain);
254 if (user_data.method) {
255 char *mname = mono_method_full_name (user_data.method, TRUE);
256 printf ("IP %p is a JIT trampoline for %s\n", ip, mname);
257 g_free (mname);
259 else
260 g_print ("No method at %p\n", ip);
261 return;
263 method = mono_method_full_name (ji->method, TRUE);
264 source = mono_debug_lookup_source_location (ji->method, (guint32)((guint8*)ip - (guint8*)ji->code_start), domain);
266 g_print ("IP %p at offset 0x%x of method %s (%p %p)[domain %p - %s]\n", ip, (int)((char*)ip - (char*)ji->code_start), method, ji->code_start, (char*)ji->code_start + ji->code_size, domain, domain->friendly_name);
268 if (source)
269 g_print ("%s:%d\n", source->source_file, source->row);
271 mono_debug_free_source_location (source);
272 g_free (method);
276 * mono_method_same_domain:
278 * Determine whenever two compiled methods are in the same domain, thus
279 * the address of the callee can be embedded in the caller.
281 gboolean mono_method_same_domain (MonoJitInfo *caller, MonoJitInfo *callee)
283 if (!caller || !callee)
284 return FALSE;
287 * If the call was made from domain-neutral to domain-specific
288 * code, we can't patch the call site.
290 if (caller->domain_neutral && !callee->domain_neutral)
291 return FALSE;
293 if ((caller->method->klass == mono_defaults.appdomain_class) &&
294 (strstr (caller->method->name, "InvokeInDomain"))) {
295 /* The InvokeInDomain methods change the current appdomain */
296 return FALSE;
299 return TRUE;
303 * mono_global_codeman_reserve:
305 * Allocate code memory from the global code manager.
307 void *mono_global_codeman_reserve (int size)
309 void *ptr;
311 if (!global_codeman) {
312 /* This can happen during startup */
313 global_codeman = mono_code_manager_new ();
314 return mono_code_manager_reserve (global_codeman, size);
316 else {
317 mono_jit_lock ();
318 ptr = mono_code_manager_reserve (global_codeman, size);
319 mono_jit_unlock ();
320 return ptr;
324 MonoJumpInfoToken *
325 mono_jump_info_token_new (MonoMemPool *mp, MonoImage *image, guint32 token)
327 MonoJumpInfoToken *res = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoToken));
328 res->image = image;
329 res->token = token;
331 return res;
334 #define MONO_INIT_VARINFO(vi,id) do { \
335 (vi)->range.first_use.pos.bid = 0xffff; \
336 (vi)->reg = -1; \
337 (vi)->idx = (id); \
338 } while (0)
340 //#define UNVERIFIED do { G_BREAKPOINT (); goto unverified; } while (0)
341 #define UNVERIFIED do { if (debug_options.break_on_unverified) G_BREAKPOINT (); else goto unverified; } while (0)
344 * Basic blocks have two numeric identifiers:
345 * dfn: Depth First Number
346 * block_num: unique ID assigned at bblock creation
348 #define NEW_BBLOCK(cfg) (mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)))
349 #define ADD_BBLOCK(cfg,bbhash,b) do { \
350 g_hash_table_insert (bbhash, (b)->cil_code, (b)); \
351 (b)->block_num = cfg->num_bblocks++; \
352 (b)->real_offset = real_offset; \
353 } while (0)
355 #define GET_BBLOCK(cfg,bbhash,tblock,ip) do { \
356 (tblock) = g_hash_table_lookup (bbhash, (ip)); \
357 if (!(tblock)) { \
358 if ((ip) >= end || (ip) < header->code) UNVERIFIED; \
359 (tblock) = NEW_BBLOCK (cfg); \
360 (tblock)->cil_code = (ip); \
361 ADD_BBLOCK (cfg, (bbhash), (tblock)); \
363 } while (0)
365 #define CHECK_BBLOCK(target,ip,tblock) do { \
366 if ((target) < (ip) && !(tblock)->code) { \
367 bb_recheck = g_list_prepend (bb_recheck, (tblock)); \
368 if (cfg->verbose_level > 2) g_print ("queued block %d for check at IL%04x from IL%04x\n", (tblock)->block_num, (int)((target) - header->code), (int)((ip) - header->code)); \
370 } while (0)
372 #define NEW_ICONST(cfg,dest,val) do { \
373 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
374 (dest)->opcode = OP_ICONST; \
375 (dest)->inst_c0 = (val); \
376 (dest)->type = STACK_I4; \
377 } while (0)
379 #define NEW_PCONST(cfg,dest,val) do { \
380 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
381 (dest)->opcode = OP_PCONST; \
382 (dest)->inst_p0 = (val); \
383 (dest)->type = STACK_PTR; \
384 } while (0)
387 #ifdef MONO_ARCH_NEED_GOT_VAR
389 #define NEW_PATCH_INFO(cfg,dest,el1,el2) do { \
390 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
391 (dest)->opcode = OP_PATCH_INFO; \
392 (dest)->inst_left = (gpointer)(el1); \
393 (dest)->inst_right = (gpointer)(el2); \
394 } while (0)
396 #define NEW_AOTCONST(cfg,dest,patch_type,cons) do { \
397 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
398 (dest)->opcode = cfg->compile_aot ? OP_GOT_ENTRY : OP_PCONST; \
399 if (cfg->compile_aot) { \
400 MonoInst *group, *got_var, *got_loc; \
401 got_loc = mono_get_got_var (cfg); \
402 NEW_TEMPLOAD ((cfg), got_var, got_loc->inst_c0); \
403 NEW_PATCH_INFO ((cfg), group, cons, patch_type); \
404 (dest)->inst_p0 = got_var; \
405 (dest)->inst_p1 = group; \
406 } else { \
407 (dest)->inst_p0 = (cons); \
408 (dest)->inst_i1 = (gpointer)(patch_type); \
410 (dest)->type = STACK_PTR; \
411 } while (0)
413 #define NEW_AOTCONST_TOKEN(cfg,dest,patch_type,image,token,stack_type,stack_class) do { \
414 MonoInst *group, *got_var, *got_loc; \
415 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
416 (dest)->opcode = OP_GOT_ENTRY; \
417 got_loc = mono_get_got_var (cfg); \
418 NEW_TEMPLOAD ((cfg), got_var, got_loc->inst_c0); \
419 NEW_PATCH_INFO ((cfg), group, NULL, patch_type); \
420 group->inst_p0 = mono_jump_info_token_new ((cfg)->mempool, (image), (token)); \
421 (dest)->inst_p0 = got_var; \
422 (dest)->inst_p1 = group; \
423 (dest)->type = (stack_type); \
424 (dest)->klass = (stack_class); \
425 } while (0)
427 #else
429 #define NEW_AOTCONST(cfg,dest,patch_type,cons) do { \
430 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
431 (dest)->opcode = cfg->compile_aot ? OP_AOTCONST : OP_PCONST; \
432 (dest)->inst_p0 = (cons); \
433 (dest)->inst_i1 = (gpointer)(patch_type); \
434 (dest)->type = STACK_PTR; \
435 } while (0)
437 #define NEW_AOTCONST_TOKEN(cfg,dest,patch_type,image,token,stack_type,stack_class) do { \
438 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
439 (dest)->opcode = OP_AOTCONST; \
440 (dest)->inst_p0 = mono_jump_info_token_new ((cfg)->mempool, (image), (token)); \
441 (dest)->inst_p1 = (gpointer)(patch_type); \
442 (dest)->type = (stack_type); \
443 (dest)->klass = (stack_class); \
444 } while (0)
446 #endif
448 #define NEW_CLASSCONST(cfg,dest,val) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_CLASS, (val))
450 #define NEW_IMAGECONST(cfg,dest,val) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_IMAGE, (val))
452 #define NEW_FIELDCONST(cfg,dest,val) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_FIELD, (val))
454 #define NEW_METHODCONST(cfg,dest,val) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_METHODCONST, (val))
456 #define NEW_VTABLECONST(cfg,dest,vtable) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_VTABLE, cfg->compile_aot ? (gpointer)((vtable)->klass) : (vtable))
458 #define NEW_SFLDACONST(cfg,dest,val) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_SFLDA, (val))
460 #define NEW_LDSTRCONST(cfg,dest,image,token) NEW_AOTCONST_TOKEN ((cfg), (dest), MONO_PATCH_INFO_LDSTR, (image), (token), STACK_OBJ, mono_defaults.string_class)
462 #define NEW_TYPE_FROM_HANDLE_CONST(cfg,dest,image,token) NEW_AOTCONST_TOKEN ((cfg), (dest), MONO_PATCH_INFO_TYPE_FROM_HANDLE, (image), (token), STACK_OBJ, mono_defaults.monotype_class)
464 #define NEW_LDTOKENCONST(cfg,dest,image,token) NEW_AOTCONST_TOKEN ((cfg), (dest), MONO_PATCH_INFO_LDTOKEN, (image), (token), STACK_PTR, NULL)
466 #define NEW_DECLSECCONST(cfg,dest,image,entry) do { \
467 if (cfg->compile_aot) { \
468 NEW_AOTCONST_TOKEN (cfg, dest, MONO_PATCH_INFO_DECLSEC, image, (entry).index, STACK_OBJ, NULL); \
469 } else { \
470 NEW_PCONST (cfg, args [0], (entry).blob); \
472 } while (0)
474 #define NEW_DOMAINCONST(cfg,dest) do { \
475 if (cfg->opt & MONO_OPT_SHARED) { \
476 /* avoid depending on undefined C behavior in sequence points */ \
477 MonoInst* __domain_var = mono_get_domainvar (cfg); \
478 NEW_TEMPLOAD (cfg, dest, __domain_var->inst_c0); \
479 } else { \
480 NEW_PCONST (cfg, dest, (cfg)->domain); \
482 } while (0)
484 #define GET_VARINFO_INST(cfg,num) ((cfg)->varinfo [(num)]->inst)
486 #define NEW_ARGLOAD(cfg,dest,num) do { \
487 if (arg_array [(num)]->opcode == OP_ICONST) (dest) = arg_array [(num)]; else { \
488 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
489 (dest)->ssa_op = MONO_SSA_LOAD; \
490 (dest)->inst_i0 = arg_array [(num)]; \
491 (dest)->opcode = mono_type_to_ldind ((dest)->inst_i0->inst_vtype); \
492 type_to_eval_stack_type (param_types [(num)], (dest)); \
493 (dest)->klass = (dest)->inst_i0->klass; \
494 }} while (0)
496 #define NEW_LOCLOAD(cfg,dest,num) do { \
497 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
498 (dest)->ssa_op = MONO_SSA_LOAD; \
499 (dest)->inst_i0 = (cfg)->varinfo [locals_offset + (num)]; \
500 (dest)->opcode = mono_type_to_ldind ((dest)->inst_i0->inst_vtype); \
501 type_to_eval_stack_type (header->locals [(num)], (dest)); \
502 (dest)->klass = (dest)->inst_i0->klass; \
503 } while (0)
505 #define NEW_LOCLOADA(cfg,dest,num) do { \
506 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
507 (dest)->ssa_op = MONO_SSA_ADDRESS_TAKEN; \
508 (dest)->inst_i0 = (cfg)->varinfo [locals_offset + (num)]; \
509 (dest)->inst_i0->flags |= MONO_INST_INDIRECT; \
510 (dest)->opcode = OP_LDADDR; \
511 (dest)->type = STACK_MP; \
512 (dest)->klass = (dest)->inst_i0->klass; \
513 if (!MONO_TYPE_ISSTRUCT (header->locals [(num)])) \
514 (cfg)->disable_ssa = TRUE; \
515 } while (0)
517 #define NEW_RETLOADA(cfg,dest) do { \
518 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
519 (dest)->ssa_op = MONO_SSA_ADDRESS_TAKEN; \
520 (dest)->inst_i0 = (cfg)->ret; \
521 (dest)->inst_i0->flags |= MONO_INST_INDIRECT; \
522 (dest)->opcode = cfg->ret_var_is_local ? OP_LDADDR : CEE_LDIND_I; \
523 (dest)->type = STACK_MP; \
524 (dest)->klass = (dest)->inst_i0->klass; \
525 (cfg)->disable_ssa = TRUE; \
526 } while (0)
528 #define NEW_ARGLOADA(cfg,dest,num) do { \
529 if (arg_array [(num)]->opcode == OP_ICONST) goto inline_failure; \
530 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
531 (dest)->ssa_op = MONO_SSA_ADDRESS_TAKEN; \
532 (dest)->inst_i0 = arg_array [(num)]; \
533 (dest)->inst_i0->flags |= MONO_INST_INDIRECT; \
534 (dest)->opcode = OP_LDADDR; \
535 (dest)->type = STACK_MP; \
536 (dest)->klass = (dest)->inst_i0->klass; \
537 (cfg)->disable_ssa = TRUE; \
538 } while (0)
540 #define NEW_TEMPLOAD(cfg,dest,num) do { \
541 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
542 (dest)->ssa_op = MONO_SSA_LOAD; \
543 (dest)->inst_i0 = (cfg)->varinfo [(num)]; \
544 (dest)->opcode = mono_type_to_ldind ((dest)->inst_i0->inst_vtype); \
545 type_to_eval_stack_type ((dest)->inst_i0->inst_vtype, (dest)); \
546 (dest)->klass = (dest)->inst_i0->klass; \
547 } while (0)
549 #define NEW_TEMPLOADA(cfg,dest,num) do { \
550 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
551 (dest)->ssa_op = MONO_SSA_ADDRESS_TAKEN; \
552 (dest)->inst_i0 = (cfg)->varinfo [(num)]; \
553 (dest)->inst_i0->flags |= MONO_INST_INDIRECT; \
554 (dest)->opcode = OP_LDADDR; \
555 (dest)->type = STACK_MP; \
556 (dest)->klass = (dest)->inst_i0->klass; \
557 if (!MONO_TYPE_ISSTRUCT (cfg->varinfo [(num)]->inst_vtype)) \
558 (cfg)->disable_ssa = TRUE; \
559 } while (0)
562 #define NEW_INDLOAD(cfg,dest,addr,vtype) do { \
563 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
564 (dest)->inst_left = addr; \
565 (dest)->opcode = mono_type_to_ldind (vtype); \
566 type_to_eval_stack_type (vtype, (dest)); \
567 /* FIXME: (dest)->klass = (dest)->inst_i0->klass;*/ \
568 } while (0)
570 #define NEW_INDSTORE(cfg,dest,addr,value,vtype) do { \
571 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
572 (dest)->inst_i0 = addr; \
573 (dest)->opcode = mono_type_to_stind (vtype); \
574 (dest)->inst_i1 = (value); \
575 /* FIXME: (dest)->klass = (dest)->inst_i0->klass;*/ \
576 } while (0)
578 #define NEW_TEMPSTORE(cfg,dest,num,inst) do { \
579 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
580 (dest)->ssa_op = MONO_SSA_STORE; \
581 (dest)->inst_i0 = (cfg)->varinfo [(num)]; \
582 (dest)->opcode = mono_type_to_stind ((dest)->inst_i0->inst_vtype); \
583 (dest)->inst_i1 = (inst); \
584 (dest)->klass = (dest)->inst_i0->klass; \
585 } while (0)
587 #define NEW_LOCSTORE(cfg,dest,num,inst) do { \
588 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
589 (dest)->opcode = mono_type_to_stind (header->locals [(num)]); \
590 (dest)->ssa_op = MONO_SSA_STORE; \
591 (dest)->inst_i0 = (cfg)->varinfo [locals_offset + (num)]; \
592 (dest)->inst_i1 = (inst); \
593 (dest)->klass = (dest)->inst_i0->klass; \
594 } while (0)
596 #define NEW_ARGSTORE(cfg,dest,num,inst) do { \
597 if (arg_array [(num)]->opcode == OP_ICONST) goto inline_failure; \
598 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
599 (dest)->opcode = mono_type_to_stind (param_types [(num)]); \
600 (dest)->ssa_op = MONO_SSA_STORE; \
601 (dest)->inst_i0 = arg_array [(num)]; \
602 (dest)->inst_i1 = (inst); \
603 (dest)->klass = (dest)->inst_i0->klass; \
604 } while (0)
606 #define NEW_DUMMY_USE(cfg,dest,load) do { \
607 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
608 (dest)->opcode = OP_DUMMY_USE; \
609 (dest)->inst_left = (load); \
610 } while (0)
612 #define NEW_DUMMY_STORE(cfg,dest,num) do { \
613 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
614 (dest)->inst_i0 = (cfg)->varinfo [(num)]; \
615 (dest)->opcode = OP_DUMMY_STORE; \
616 (dest)->klass = (dest)->inst_i0->klass; \
617 } while (0)
619 #define ADD_BINOP(op) do { \
620 MONO_INST_NEW (cfg, ins, (op)); \
621 ins->cil_code = ip; \
622 sp -= 2; \
623 ins->inst_i0 = sp [0]; \
624 ins->inst_i1 = sp [1]; \
625 *sp++ = ins; \
626 type_from_op (ins); \
627 CHECK_TYPE (ins); \
628 } while (0)
630 #define ADD_UNOP(op) do { \
631 MONO_INST_NEW (cfg, ins, (op)); \
632 ins->cil_code = ip; \
633 sp--; \
634 ins->inst_i0 = sp [0]; \
635 *sp++ = ins; \
636 type_from_op (ins); \
637 CHECK_TYPE (ins); \
638 } while (0)
640 #define ADD_BINCOND(next_block) do { \
641 MonoInst *cmp; \
642 sp -= 2; \
643 MONO_INST_NEW(cfg, cmp, OP_COMPARE); \
644 cmp->inst_i0 = sp [0]; \
645 cmp->inst_i1 = sp [1]; \
646 cmp->cil_code = ins->cil_code; \
647 type_from_op (cmp); \
648 CHECK_TYPE (cmp); \
649 ins->inst_i0 = cmp; \
650 MONO_ADD_INS (bblock, ins); \
651 ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \
652 GET_BBLOCK (cfg, bbhash, tblock, target); \
653 link_bblock (cfg, bblock, tblock); \
654 ins->inst_true_bb = tblock; \
655 CHECK_BBLOCK (target, ip, tblock); \
656 if ((next_block)) { \
657 link_bblock (cfg, bblock, (next_block)); \
658 ins->inst_false_bb = (next_block); \
659 start_new_bblock = 1; \
660 } else { \
661 GET_BBLOCK (cfg, bbhash, tblock, ip); \
662 link_bblock (cfg, bblock, tblock); \
663 ins->inst_false_bb = tblock; \
664 start_new_bblock = 2; \
666 } while (0)
668 /* FIXME: handle float, long ... */
669 #define ADD_UNCOND(istrue) do { \
670 MonoInst *cmp; \
671 sp--; \
672 MONO_INST_NEW(cfg, cmp, OP_COMPARE); \
673 cmp->inst_i0 = sp [0]; \
674 switch (cmp->inst_i0->type) { \
675 case STACK_I8: \
676 cmp->inst_i1 = zero_int64; break; \
677 case STACK_R8: \
678 cmp->inst_i1 = zero_r8; break; \
679 case STACK_PTR: \
680 case STACK_MP: \
681 cmp->inst_i1 = zero_ptr; break; \
682 case STACK_OBJ: \
683 cmp->inst_i1 = zero_obj; break; \
684 default: \
685 cmp->inst_i1 = zero_int32; \
687 cmp->cil_code = ins->cil_code; \
688 type_from_op (cmp); \
689 CHECK_TYPE (cmp); \
690 ins->inst_i0 = cmp; \
691 ins->opcode = (istrue)? CEE_BNE_UN: CEE_BEQ; \
692 MONO_ADD_INS (bblock, ins); \
693 ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \
694 GET_BBLOCK (cfg, bbhash, tblock, target); \
695 link_bblock (cfg, bblock, tblock); \
696 ins->inst_true_bb = tblock; \
697 CHECK_BBLOCK (target, ip, tblock); \
698 GET_BBLOCK (cfg, bbhash, tblock, ip); \
699 link_bblock (cfg, bblock, tblock); \
700 ins->inst_false_bb = tblock; \
701 start_new_bblock = 2; \
702 } while (0)
704 #define NEW_LDELEMA(cfg,dest,sp,k) do { \
705 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
706 (dest)->opcode = CEE_LDELEMA; \
707 (dest)->inst_left = (sp) [0]; \
708 (dest)->inst_right = (sp) [1]; \
709 (dest)->type = STACK_MP; \
710 (dest)->klass = (k); \
711 (cfg)->flags |= MONO_CFG_HAS_LDELEMA; \
712 } while (0)
714 #define NEW_GROUP(cfg,dest,el1,el2) do { \
715 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
716 (dest)->opcode = OP_GROUP; \
717 (dest)->inst_left = (el1); \
718 (dest)->inst_right = (el2); \
719 } while (0)
721 #if 0
722 static gint
723 compare_bblock (gconstpointer a, gconstpointer b)
725 const MonoBasicBlock *b1 = a;
726 const MonoBasicBlock *b2 = b;
728 return b2->cil_code - b1->cil_code;
730 #endif
732 /* *
733 * link_bblock: Links two basic blocks
735 * links two basic blocks in the control flow graph, the 'from'
736 * argument is the starting block and the 'to' argument is the block
737 * the control flow ends to after 'from'.
739 static void
740 link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
742 MonoBasicBlock **newa;
743 int i, found;
745 #if 0
746 if (from->cil_code) {
747 if (to->cil_code)
748 g_print ("edge from IL%04x to IL_%04x\n", from->cil_code - cfg->cil_code, to->cil_code - cfg->cil_code);
749 else
750 g_print ("edge from IL%04x to exit\n", from->cil_code - cfg->cil_code);
751 } else {
752 if (to->cil_code)
753 g_print ("edge from entry to IL_%04x\n", to->cil_code - cfg->cil_code);
754 else
755 g_print ("edge from entry to exit\n");
757 #endif
758 found = FALSE;
759 for (i = 0; i < from->out_count; ++i) {
760 if (to == from->out_bb [i]) {
761 found = TRUE;
762 break;
765 if (!found) {
766 newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count + 1));
767 for (i = 0; i < from->out_count; ++i) {
768 newa [i] = from->out_bb [i];
770 newa [i] = to;
771 from->out_count++;
772 from->out_bb = newa;
775 found = FALSE;
776 for (i = 0; i < to->in_count; ++i) {
777 if (from == to->in_bb [i]) {
778 found = TRUE;
779 break;
782 if (!found) {
783 newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count + 1));
784 for (i = 0; i < to->in_count; ++i) {
785 newa [i] = to->in_bb [i];
787 newa [i] = from;
788 to->in_count++;
789 to->in_bb = newa;
794 * mono_unlink_bblock:
796 * Unlink two basic blocks.
798 static void
799 mono_unlink_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
801 int i, pos;
802 gboolean found;
804 found = FALSE;
805 for (i = 0; i < from->out_count; ++i) {
806 if (to == from->out_bb [i]) {
807 found = TRUE;
808 break;
811 if (found) {
812 pos = 0;
813 for (i = 0; i < from->out_count; ++i) {
814 if (from->out_bb [i] != to)
815 from->out_bb [pos ++] = from->out_bb [i];
817 g_assert (pos == from->out_count - 1);
818 from->out_count--;
821 found = FALSE;
822 for (i = 0; i < to->in_count; ++i) {
823 if (from == to->in_bb [i]) {
824 found = TRUE;
825 break;
828 if (found) {
829 pos = 0;
830 for (i = 0; i < to->in_count; ++i) {
831 if (to->in_bb [i] != from)
832 to->in_bb [pos ++] = to->in_bb [i];
834 g_assert (pos == to->in_count - 1);
835 to->in_count--;
840 * mono_find_block_region:
842 * We mark each basic block with a region ID. We use that to avoid BB
843 * optimizations when blocks are in different regions.
845 * Returns:
846 * A region token that encodes where this region is, and information
847 * about the clause owner for this block.
849 * The region encodes the try/catch/filter clause that owns this block
850 * as well as the type. -1 is a special value that represents a block
851 * that is in none of try/catch/filter.
853 static int
854 mono_find_block_region (MonoCompile *cfg, int offset)
856 MonoMethod *method = cfg->method;
857 MonoMethodHeader *header = mono_method_get_header (method);
858 MonoExceptionClause *clause;
859 int i;
861 /* first search for handlers and filters */
862 for (i = 0; i < header->num_clauses; ++i) {
863 clause = &header->clauses [i];
864 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) &&
865 (offset < (clause->handler_offset)))
866 return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags;
868 if (MONO_OFFSET_IN_HANDLER (clause, offset)) {
869 if (clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY)
870 return ((i + 1) << 8) | MONO_REGION_FINALLY | clause->flags;
871 else
872 return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags;
876 /* search the try blocks */
877 for (i = 0; i < header->num_clauses; ++i) {
878 clause = &header->clauses [i];
879 if (MONO_OFFSET_IN_CLAUSE (clause, offset))
880 return ((i + 1) << 8) | clause->flags;
883 return -1;
886 static GList*
887 mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type)
889 MonoMethod *method = cfg->method;
890 MonoMethodHeader *header = mono_method_get_header (method);
891 MonoExceptionClause *clause;
892 MonoBasicBlock *handler;
893 int i;
894 GList *res = NULL;
896 for (i = 0; i < header->num_clauses; ++i) {
897 clause = &header->clauses [i];
898 if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) &&
899 (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) {
900 if (clause->flags == type) {
901 handler = g_hash_table_lookup (cfg->bb_hash, header->code + clause->handler_offset);
902 g_assert (handler);
903 res = g_list_append (res, handler);
907 return res;
910 MonoInst *
911 mono_find_spvar_for_region (MonoCompile *cfg, int region)
913 return g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region));
916 static void
917 mono_create_spvar_for_region (MonoCompile *cfg, int region)
919 MonoInst *var;
921 var = g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region));
922 if (var)
923 return;
925 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
926 /* prevent it from being register allocated */
927 var->flags |= MONO_INST_INDIRECT;
929 g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var);
932 static MonoInst *
933 mono_find_exvar_for_offset (MonoCompile *cfg, int offset)
935 return g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
938 static MonoInst*
939 mono_create_exvar_for_offset (MonoCompile *cfg, int offset)
941 MonoInst *var;
943 var = g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
944 if (var)
945 return var;
947 var = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL);
948 /* prevent it from being register allocated */
949 var->flags |= MONO_INST_INDIRECT;
951 g_hash_table_insert (cfg->exvars, GINT_TO_POINTER (offset), var);
953 return var;
956 static void
957 df_visit (MonoBasicBlock *start, int *dfn, MonoBasicBlock **array)
959 int i;
961 array [*dfn] = start;
962 /*g_print ("visit %d at %p (BB%ld)\n", *dfn, start->cil_code, start->block_num);*/
963 for (i = 0; i < start->out_count; ++i) {
964 if (start->out_bb [i]->dfn)
965 continue;
966 (*dfn)++;
967 start->out_bb [i]->dfn = *dfn;
968 start->out_bb [i]->df_parent = start;
969 array [*dfn] = start->out_bb [i];
970 df_visit (start->out_bb [i], dfn, array);
974 typedef struct {
975 const guchar *code;
976 MonoBasicBlock *best;
977 } PrevStruct;
979 static void
980 previous_foreach (gconstpointer key, gpointer val, gpointer data)
982 PrevStruct *p = data;
983 MonoBasicBlock *bb = val;
984 //printf ("FIDPREV %d %p %p %p %p %p %d %d %d\n", bb->block_num, p->code, bb, p->best, bb->cil_code, p->best->cil_code,
985 //bb->method == p->best->method, bb->cil_code < p->code, bb->cil_code > p->best->cil_code);
987 if (bb->cil_code && bb->cil_code < p->code && bb->cil_code > p->best->cil_code)
988 p->best = bb;
991 static MonoBasicBlock*
992 find_previous (GHashTable *bb_hash, MonoBasicBlock *start, const guchar *code) {
993 PrevStruct p;
995 p.code = code;
996 p.best = start;
998 g_hash_table_foreach (bb_hash, (GHFunc)previous_foreach, &p);
999 return p.best;
1002 static void
1003 split_bblock (MonoCompile *cfg, MonoBasicBlock *first, MonoBasicBlock *second) {
1004 int i, j;
1005 MonoInst *inst;
1006 MonoBasicBlock *bb;
1008 if (second->code)
1009 return;
1012 * FIXME: take into account all the details:
1013 * second may have been the target of more than one bblock
1015 second->out_count = first->out_count;
1016 second->out_bb = first->out_bb;
1018 for (i = 0; i < first->out_count; ++i) {
1019 bb = first->out_bb [i];
1020 for (j = 0; j < bb->in_count; ++j) {
1021 if (bb->in_bb [j] == first)
1022 bb->in_bb [j] = second;
1026 first->out_count = 0;
1027 first->out_bb = NULL;
1028 link_bblock (cfg, first, second);
1030 second->last_ins = first->last_ins;
1032 /*g_print ("start search at %p for %p\n", first->cil_code, second->cil_code);*/
1033 for (inst = first->code; inst && inst->next; inst = inst->next) {
1034 /*char *code = mono_disasm_code_one (NULL, cfg->method, inst->next->cil_code, NULL);
1035 g_print ("found %p: %s", inst->next->cil_code, code);
1036 g_free (code);*/
1037 if (inst->cil_code < second->cil_code && inst->next->cil_code >= second->cil_code) {
1038 second->code = inst->next;
1039 inst->next = NULL;
1040 first->last_ins = inst;
1041 second->next_bb = first->next_bb;
1042 first->next_bb = second;
1043 return;
1046 if (!second->code) {
1047 g_warning ("bblock split failed in %s::%s\n", cfg->method->klass->name, cfg->method->name);
1048 //G_BREAKPOINT ();
1052 static guint32
1053 reverse_branch_op (guint32 opcode)
1055 static const int reverse_map [] = {
1056 CEE_BNE_UN, CEE_BLT, CEE_BLE, CEE_BGT, CEE_BGE,
1057 CEE_BEQ, CEE_BLT_UN, CEE_BLE_UN, CEE_BGT_UN, CEE_BGE_UN
1059 static const int reverse_fmap [] = {
1060 OP_FBNE_UN, OP_FBLT, OP_FBLE, OP_FBGT, OP_FBGE,
1061 OP_FBEQ, OP_FBLT_UN, OP_FBLE_UN, OP_FBGT_UN, OP_FBGE_UN
1063 static const int reverse_lmap [] = {
1064 OP_LBNE_UN, OP_LBLT, OP_LBLE, OP_LBGT, OP_LBGE,
1065 OP_LBEQ, OP_LBLT_UN, OP_LBLE_UN, OP_LBGT_UN, OP_LBGE_UN
1067 static const int reverse_imap [] = {
1068 OP_IBNE_UN, OP_IBLT, OP_IBLE, OP_IBGT, OP_IBGE,
1069 OP_IBEQ, OP_IBLT_UN, OP_IBLE_UN, OP_IBGT_UN, OP_IBGE_UN
1072 if (opcode >= CEE_BEQ && opcode <= CEE_BLT_UN) {
1073 opcode = reverse_map [opcode - CEE_BEQ];
1074 } else if (opcode >= OP_FBEQ && opcode <= OP_FBLT_UN) {
1075 opcode = reverse_fmap [opcode - OP_FBEQ];
1076 } else if (opcode >= OP_LBEQ && opcode <= OP_LBLT_UN) {
1077 opcode = reverse_lmap [opcode - OP_LBEQ];
1078 } else if (opcode >= OP_IBEQ && opcode <= OP_IBLT_UN) {
1079 opcode = reverse_imap [opcode - OP_IBEQ];
1080 } else
1081 g_assert_not_reached ();
1083 return opcode;
1086 #ifdef MONO_ARCH_SOFT_FLOAT
1087 static int
1088 condbr_to_fp_br (int opcode)
1090 switch (opcode) {
1091 case CEE_BEQ: return OP_FBEQ;
1092 case CEE_BGE: return OP_FBGE;
1093 case CEE_BGT: return OP_FBGT;
1094 case CEE_BLE: return OP_FBLE;
1095 case CEE_BLT: return OP_FBLT;
1096 case CEE_BNE_UN: return OP_FBNE_UN;
1097 case CEE_BGE_UN: return OP_FBGE_UN;
1098 case CEE_BGT_UN: return OP_FBGT_UN;
1099 case CEE_BLE_UN: return OP_FBLE_UN;
1100 case CEE_BLT_UN: return OP_FBLT_UN;
1102 g_assert_not_reached ();
1103 return 0;
1105 #endif
1108 * Returns the type used in the eval stack when @type is loaded.
1109 * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases.
1111 static void
1112 type_to_eval_stack_type (MonoType *type, MonoInst *inst)
1114 MonoClass *klass;
1116 inst->klass = klass = mono_class_from_mono_type (type);
1117 if (type->byref) {
1118 inst->type = STACK_MP;
1119 return;
1122 handle_enum:
1123 switch (type->type) {
1124 case MONO_TYPE_VOID:
1125 inst->type = STACK_INV;
1126 return;
1127 case MONO_TYPE_I1:
1128 case MONO_TYPE_U1:
1129 case MONO_TYPE_BOOLEAN:
1130 case MONO_TYPE_I2:
1131 case MONO_TYPE_U2:
1132 case MONO_TYPE_CHAR:
1133 case MONO_TYPE_I4:
1134 case MONO_TYPE_U4:
1135 inst->type = STACK_I4;
1136 return;
1137 case MONO_TYPE_I:
1138 case MONO_TYPE_U:
1139 case MONO_TYPE_PTR:
1140 case MONO_TYPE_FNPTR:
1141 inst->type = STACK_PTR;
1142 return;
1143 case MONO_TYPE_CLASS:
1144 case MONO_TYPE_STRING:
1145 case MONO_TYPE_OBJECT:
1146 case MONO_TYPE_SZARRAY:
1147 case MONO_TYPE_ARRAY:
1148 inst->type = STACK_OBJ;
1149 return;
1150 case MONO_TYPE_I8:
1151 case MONO_TYPE_U8:
1152 inst->type = STACK_I8;
1153 return;
1154 case MONO_TYPE_R4:
1155 case MONO_TYPE_R8:
1156 inst->type = STACK_R8;
1157 return;
1158 case MONO_TYPE_VALUETYPE:
1159 if (type->data.klass->enumtype) {
1160 type = type->data.klass->enum_basetype;
1161 goto handle_enum;
1162 } else {
1163 inst->klass = klass;
1164 inst->type = STACK_VTYPE;
1165 return;
1167 case MONO_TYPE_TYPEDBYREF:
1168 inst->klass = mono_defaults.typed_reference_class;
1169 inst->type = STACK_VTYPE;
1170 return;
1171 case MONO_TYPE_GENERICINST:
1172 type = &type->data.generic_class->container_class->byval_arg;
1173 goto handle_enum;
1174 default:
1175 g_error ("unknown type 0x%02x in eval stack type", type->type);
1180 * The following tables are used to quickly validate the IL code in type_from_op ().
1182 static const char
1183 bin_num_table [STACK_MAX] [STACK_MAX] = {
1184 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1185 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
1186 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1187 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
1188 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV},
1189 {STACK_INV, STACK_MP, STACK_INV, STACK_MP, STACK_INV, STACK_PTR, STACK_INV, STACK_INV},
1190 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1191 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
1194 static const char
1195 neg_table [] = {
1196 STACK_INV, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_INV, STACK_INV, STACK_INV
1199 /* reduce the size of this table */
1200 static const char
1201 bin_int_table [STACK_MAX] [STACK_MAX] = {
1202 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1203 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1204 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1205 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1206 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1207 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1208 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1209 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
1212 static const char
1213 bin_comp_table [STACK_MAX] [STACK_MAX] = {
1214 /* Inv i L p F & O vt */
1215 {0},
1216 {0, 1, 0, 1, 0, 0, 0, 0}, /* i, int32 */
1217 {0, 0, 1, 0, 0, 0, 0, 0}, /* L, int64 */
1218 {0, 1, 0, 1, 0, 2, 4, 0}, /* p, ptr */
1219 {0, 0, 0, 0, 1, 0, 0, 0}, /* F, R8 */
1220 {0, 0, 0, 2, 0, 1, 0, 0}, /* &, managed pointer */
1221 {0, 0, 0, 4, 0, 0, 3, 0}, /* O, reference */
1222 {0, 0, 0, 0, 0, 0, 0, 0}, /* vt value type */
1225 /* reduce the size of this table */
1226 static const char
1227 shift_table [STACK_MAX] [STACK_MAX] = {
1228 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1229 {STACK_INV, STACK_I4, STACK_INV, STACK_I4, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1230 {STACK_INV, STACK_I8, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1231 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1232 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1233 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1234 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
1235 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
1239 * Tables to map from the non-specific opcode to the matching
1240 * type-specific opcode.
1242 /* handles from CEE_ADD to CEE_SHR_UN (CEE_REM_UN for floats) */
1243 static const guint16
1244 binops_op_map [STACK_MAX] = {
1245 0, 0, OP_LADD-CEE_ADD, OP_PADD-CEE_ADD, OP_FADD-CEE_ADD, OP_PADD-CEE_ADD
1248 /* handles from CEE_NEG to CEE_CONV_U8 */
1249 static const guint16
1250 unops_op_map [STACK_MAX] = {
1251 0, 0, OP_LNEG-CEE_NEG, OP_PNEG-CEE_NEG, OP_FNEG-CEE_NEG, OP_PNEG-CEE_NEG
1254 /* handles from CEE_CONV_U2 to CEE_SUB_OVF_UN */
1255 static const guint16
1256 ovfops_op_map [STACK_MAX] = {
1257 0, 0, OP_LCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2, OP_FCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2
1260 /* handles from CEE_CONV_OVF_I1_UN to CEE_CONV_OVF_U_UN */
1261 static const guint16
1262 ovf2ops_op_map [STACK_MAX] = {
1263 0, 0, OP_LCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_PCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_FCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_PCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN
1266 /* handles from CEE_CONV_OVF_I1 to CEE_CONV_OVF_U8 */
1267 static const guint16
1268 ovf3ops_op_map [STACK_MAX] = {
1269 0, 0, OP_LCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_PCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_FCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_PCONV_TO_OVF_I1-CEE_CONV_OVF_I1
1272 /* handles from CEE_CEQ to CEE_CLT_UN */
1273 static const guint16
1274 ceqops_op_map [STACK_MAX] = {
1275 0, 0, OP_LCEQ-OP_CEQ, OP_PCEQ-OP_CEQ, OP_FCEQ-OP_CEQ, OP_LCEQ-OP_CEQ
1279 * Sets ins->type (the type on the eval stack) according to the
1280 * type of the opcode and the arguments to it.
1281 * Invalid IL code is marked by setting ins->type to the invalid value STACK_INV.
1283 * FIXME: this function sets ins->type unconditionally in some cases, but
1284 * it should set it to invalid for some types (a conv.x on an object)
1286 static void
1287 type_from_op (MonoInst *ins) {
1288 switch (ins->opcode) {
1289 /* binops */
1290 case CEE_ADD:
1291 case CEE_SUB:
1292 case CEE_MUL:
1293 case CEE_DIV:
1294 case CEE_REM:
1295 /* FIXME: check unverifiable args for STACK_MP */
1296 ins->type = bin_num_table [ins->inst_i0->type] [ins->inst_i1->type];
1297 ins->opcode += binops_op_map [ins->type];
1298 return;
1299 case CEE_DIV_UN:
1300 case CEE_REM_UN:
1301 case CEE_AND:
1302 case CEE_OR:
1303 case CEE_XOR:
1304 ins->type = bin_int_table [ins->inst_i0->type] [ins->inst_i1->type];
1305 ins->opcode += binops_op_map [ins->type];
1306 return;
1307 case CEE_SHL:
1308 case CEE_SHR:
1309 case CEE_SHR_UN:
1310 ins->type = shift_table [ins->inst_i0->type] [ins->inst_i1->type];
1311 ins->opcode += binops_op_map [ins->type];
1312 return;
1313 case OP_COMPARE:
1314 case OP_LCOMPARE:
1315 /* FIXME: handle some specifics with ins->next->type */
1316 ins->type = bin_comp_table [ins->inst_i0->type] [ins->inst_i1->type] ? STACK_I4: STACK_INV;
1317 if ((ins->inst_i0->type == STACK_I8) || ((sizeof (gpointer) == 8) && ((ins->inst_i0->type == STACK_PTR) || (ins->inst_i0->type == STACK_OBJ) || (ins->inst_i0->type == STACK_MP))))
1318 ins->opcode = OP_LCOMPARE;
1319 return;
1320 case OP_CEQ:
1321 case OP_CGT:
1322 case OP_CGT_UN:
1323 case OP_CLT:
1324 case OP_CLT_UN:
1325 ins->type = bin_comp_table [ins->inst_i0->type] [ins->inst_i1->type] ? STACK_I4: STACK_INV;
1326 ins->opcode += ceqops_op_map [ins->inst_i0->type];
1327 return;
1328 /* unops */
1329 case CEE_NEG:
1330 ins->type = neg_table [ins->inst_i0->type];
1331 ins->opcode += unops_op_map [ins->type];
1332 return;
1333 case CEE_NOT:
1334 if (ins->inst_i0->type >= STACK_I4 && ins->inst_i0->type <= STACK_PTR)
1335 ins->type = ins->inst_i0->type;
1336 else
1337 ins->type = STACK_INV;
1338 ins->opcode += unops_op_map [ins->type];
1339 return;
1340 case CEE_CONV_I1:
1341 case CEE_CONV_I2:
1342 case CEE_CONV_I4:
1343 case CEE_CONV_U4:
1344 ins->type = STACK_I4;
1345 ins->opcode += unops_op_map [ins->inst_i0->type];
1346 return;
1347 case CEE_CONV_R_UN:
1348 ins->type = STACK_R8;
1349 switch (ins->inst_i0->type) {
1350 case STACK_I4:
1351 case STACK_PTR:
1352 break;
1353 case STACK_I8:
1354 ins->opcode = OP_LCONV_TO_R_UN;
1355 break;
1357 return;
1358 case CEE_CONV_OVF_I1:
1359 case CEE_CONV_OVF_U1:
1360 case CEE_CONV_OVF_I2:
1361 case CEE_CONV_OVF_U2:
1362 case CEE_CONV_OVF_I4:
1363 case CEE_CONV_OVF_U4:
1364 ins->type = STACK_I4;
1365 ins->opcode += ovf3ops_op_map [ins->inst_i0->type];
1366 return;
1367 case CEE_CONV_OVF_I_UN:
1368 case CEE_CONV_OVF_U_UN:
1369 ins->type = STACK_PTR;
1370 ins->opcode += ovf2ops_op_map [ins->inst_i0->type];
1371 return;
1372 case CEE_CONV_OVF_I1_UN:
1373 case CEE_CONV_OVF_I2_UN:
1374 case CEE_CONV_OVF_I4_UN:
1375 case CEE_CONV_OVF_U1_UN:
1376 case CEE_CONV_OVF_U2_UN:
1377 case CEE_CONV_OVF_U4_UN:
1378 ins->type = STACK_I4;
1379 ins->opcode += ovf2ops_op_map [ins->inst_i0->type];
1380 return;
1381 case CEE_CONV_U:
1382 ins->type = STACK_PTR;
1383 switch (ins->inst_i0->type) {
1384 case STACK_I4:
1385 case STACK_PTR:
1386 case STACK_MP:
1387 break;
1388 case STACK_I8:
1389 ins->opcode = OP_LCONV_TO_U;
1390 break;
1391 case STACK_R8:
1392 ins->opcode = OP_FCONV_TO_U;
1393 break;
1395 return;
1396 case CEE_CONV_I8:
1397 case CEE_CONV_U8:
1398 ins->type = STACK_I8;
1399 ins->opcode += unops_op_map [ins->inst_i0->type];
1400 return;
1401 case CEE_CONV_OVF_I8:
1402 case CEE_CONV_OVF_U8:
1403 ins->type = STACK_I8;
1404 ins->opcode += ovf3ops_op_map [ins->inst_i0->type];
1405 return;
1406 case CEE_CONV_OVF_U8_UN:
1407 case CEE_CONV_OVF_I8_UN:
1408 ins->type = STACK_I8;
1409 ins->opcode += ovf2ops_op_map [ins->inst_i0->type];
1410 return;
1411 case CEE_CONV_R4:
1412 case CEE_CONV_R8:
1413 ins->type = STACK_R8;
1414 ins->opcode += unops_op_map [ins->inst_i0->type];
1415 return;
1416 case CEE_CKFINITE:
1417 ins->type = STACK_R8;
1418 return;
1419 case CEE_CONV_U2:
1420 case CEE_CONV_U1:
1421 ins->type = STACK_I4;
1422 ins->opcode += ovfops_op_map [ins->inst_i0->type];
1423 break;
1424 case CEE_CONV_I:
1425 case CEE_CONV_OVF_I:
1426 case CEE_CONV_OVF_U:
1427 ins->type = STACK_PTR;
1428 ins->opcode += ovfops_op_map [ins->inst_i0->type];
1429 return;
1430 case CEE_ADD_OVF:
1431 case CEE_ADD_OVF_UN:
1432 case CEE_MUL_OVF:
1433 case CEE_MUL_OVF_UN:
1434 case CEE_SUB_OVF:
1435 case CEE_SUB_OVF_UN:
1436 ins->type = bin_num_table [ins->inst_i0->type] [ins->inst_i1->type];
1437 ins->opcode += ovfops_op_map [ins->inst_i0->type];
1438 return;
1439 default:
1440 g_error ("opcode 0x%04x not handled in type from op", ins->opcode);
1441 break;
1445 static const char
1446 ldind_type [] = {
1447 STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_R8, STACK_OBJ
1450 /* map ldelem.x to the matching ldind.x opcode */
1451 static const guchar
1452 ldelem_to_ldind [] = {
1453 CEE_LDIND_I1,
1454 CEE_LDIND_U1,
1455 CEE_LDIND_I2,
1456 CEE_LDIND_U2,
1457 CEE_LDIND_I4,
1458 CEE_LDIND_U4,
1459 CEE_LDIND_I8,
1460 CEE_LDIND_I,
1461 CEE_LDIND_R4,
1462 CEE_LDIND_R8,
1463 CEE_LDIND_REF
1466 /* map stelem.x to the matching stind.x opcode */
1467 static const guchar
1468 stelem_to_stind [] = {
1469 CEE_STIND_I,
1470 CEE_STIND_I1,
1471 CEE_STIND_I2,
1472 CEE_STIND_I4,
1473 CEE_STIND_I8,
1474 CEE_STIND_R4,
1475 CEE_STIND_R8,
1476 CEE_STIND_REF
1479 #if 0
1481 static const char
1482 param_table [STACK_MAX] [STACK_MAX] = {
1483 {0},
1486 static int
1487 check_values_to_signature (MonoInst *args, MonoType *this, MonoMethodSignature *sig) {
1488 int i;
1490 if (sig->hasthis) {
1491 switch (args->type) {
1492 case STACK_I4:
1493 case STACK_I8:
1494 case STACK_R8:
1495 case STACK_VTYPE:
1496 case STACK_INV:
1497 return 0;
1499 args++;
1501 for (i = 0; i < sig->param_count; ++i) {
1502 switch (args [i].type) {
1503 case STACK_INV:
1504 return 0;
1505 case STACK_MP:
1506 if (!sig->params [i]->byref)
1507 return 0;
1508 continue;
1509 case STACK_OBJ:
1510 if (sig->params [i]->byref)
1511 return 0;
1512 switch (sig->params [i]->type) {
1513 case MONO_TYPE_CLASS:
1514 case MONO_TYPE_STRING:
1515 case MONO_TYPE_OBJECT:
1516 case MONO_TYPE_SZARRAY:
1517 case MONO_TYPE_ARRAY:
1518 break;
1519 default:
1520 return 0;
1522 continue;
1523 case STACK_R8:
1524 if (sig->params [i]->byref)
1525 return 0;
1526 if (sig->params [i]->type != MONO_TYPE_R4 && sig->params [i]->type != MONO_TYPE_R8)
1527 return 0;
1528 continue;
1529 case STACK_PTR:
1530 case STACK_I4:
1531 case STACK_I8:
1532 case STACK_VTYPE:
1533 break;
1535 /*if (!param_table [args [i].type] [sig->params [i]->type])
1536 return 0;*/
1538 return 1;
1540 #endif
1543 * When we need a pointer to the current domain many times in a method, we
1544 * call mono_domain_get() once and we store the result in a local variable.
1545 * This function returns the variable that represents the MonoDomain*.
1547 inline static MonoInst *
1548 mono_get_domainvar (MonoCompile *cfg)
1550 if (!cfg->domainvar)
1551 cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1552 return cfg->domainvar;
1556 * The got_var contains the address of the Global Offset Table when AOT
1557 * compiling.
1559 inline static MonoInst *
1560 mono_get_got_var (MonoCompile *cfg)
1562 #ifdef MONO_ARCH_NEED_GOT_VAR
1563 if (!cfg->compile_aot)
1564 return NULL;
1565 if (!cfg->got_var) {
1566 cfg->got_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1568 return cfg->got_var;
1569 #else
1570 return NULL;
1571 #endif
1574 MonoInst*
1575 mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode)
1577 MonoInst *inst;
1578 int num = cfg->num_varinfo;
1580 if ((num + 1) >= cfg->varinfo_count) {
1581 cfg->varinfo_count = (cfg->varinfo_count + 2) * 2;
1582 cfg->varinfo = (MonoInst **)g_realloc (cfg->varinfo, sizeof (MonoInst*) * cfg->varinfo_count);
1583 cfg->vars = (MonoMethodVar **)g_realloc (cfg->vars, sizeof (MonoMethodVar*) * cfg->varinfo_count);
1586 /*g_print ("created temp %d of type 0x%x\n", num, type->type);*/
1587 mono_jit_stats.allocate_var++;
1589 MONO_INST_NEW (cfg, inst, opcode);
1590 inst->inst_c0 = num;
1591 inst->inst_vtype = type;
1592 inst->klass = mono_class_from_mono_type (type);
1593 /* if set to 1 the variable is native */
1594 inst->backend.is_pinvoke = 0;
1596 cfg->varinfo [num] = inst;
1598 cfg->vars [num] = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoMethodVar));
1599 MONO_INIT_VARINFO (cfg->vars [num], num);
1601 cfg->num_varinfo++;
1602 //g_print ("created temp %d of type %s\n", num, mono_type_get_name (type));
1603 return inst;
1607 * Transform a MonoInst into a load from the variable of index var_index.
1609 void
1610 mono_compile_make_var_load (MonoCompile *cfg, MonoInst *dest, gssize var_index) {
1611 memset (dest, 0, sizeof (MonoInst));
1612 dest->ssa_op = MONO_SSA_LOAD;
1613 dest->inst_i0 = cfg->varinfo [var_index];
1614 dest->opcode = mono_type_to_ldind (dest->inst_i0->inst_vtype);
1615 type_to_eval_stack_type (dest->inst_i0->inst_vtype, dest);
1616 dest->klass = dest->inst_i0->klass;
1620 * Create a MonoInst that is a load from the variable of index var_index.
1622 MonoInst*
1623 mono_compile_create_var_load (MonoCompile *cfg, gssize var_index) {
1624 MonoInst *dest;
1625 NEW_TEMPLOAD (cfg,dest,var_index);
1626 return dest;
1630 * Create a MonoInst that is a store of the given value into the variable of index var_index.
1632 MonoInst*
1633 mono_compile_create_var_store (MonoCompile *cfg, gssize var_index, MonoInst *value) {
1634 MonoInst *dest;
1635 NEW_TEMPSTORE (cfg, dest, var_index, value);
1636 return dest;
1639 static MonoType*
1640 type_from_stack_type (MonoInst *ins) {
1641 switch (ins->type) {
1642 case STACK_I4: return &mono_defaults.int32_class->byval_arg;
1643 case STACK_I8: return &mono_defaults.int64_class->byval_arg;
1644 case STACK_PTR: return &mono_defaults.int_class->byval_arg;
1645 case STACK_R8: return &mono_defaults.double_class->byval_arg;
1646 case STACK_MP:
1648 * FIXME: This doesn't work because mono_class_from_mono_type ()
1649 * returns the original klass for a byref type, not a 'byref' class,
1650 * causing the JIT to create variables with the wrong type, for
1651 * example.
1654 if (ins->klass)
1655 return &ins->klass->this_arg;
1656 else
1658 return &mono_defaults.object_class->this_arg;
1659 case STACK_OBJ: return &mono_defaults.object_class->byval_arg;
1660 case STACK_VTYPE: return &ins->klass->byval_arg;
1661 default:
1662 g_error ("stack type %d to montype not handled\n", ins->type);
1664 return NULL;
1667 MonoType*
1668 mono_type_from_stack_type (MonoInst *ins) {
1669 return type_from_stack_type (ins);
1672 static MonoClass*
1673 array_access_to_klass (int opcode)
1675 switch (opcode) {
1676 case CEE_LDELEM_U1:
1677 return mono_defaults.byte_class;
1678 case CEE_LDELEM_U2:
1679 return mono_defaults.uint16_class;
1680 case CEE_LDELEM_I:
1681 case CEE_STELEM_I:
1682 return mono_defaults.int_class;
1683 case CEE_LDELEM_I1:
1684 case CEE_STELEM_I1:
1685 return mono_defaults.sbyte_class;
1686 case CEE_LDELEM_I2:
1687 case CEE_STELEM_I2:
1688 return mono_defaults.int16_class;
1689 case CEE_LDELEM_I4:
1690 case CEE_STELEM_I4:
1691 return mono_defaults.int32_class;
1692 case CEE_LDELEM_U4:
1693 return mono_defaults.uint32_class;
1694 case CEE_LDELEM_I8:
1695 case CEE_STELEM_I8:
1696 return mono_defaults.int64_class;
1697 case CEE_LDELEM_R4:
1698 case CEE_STELEM_R4:
1699 return mono_defaults.single_class;
1700 case CEE_LDELEM_R8:
1701 case CEE_STELEM_R8:
1702 return mono_defaults.double_class;
1703 case CEE_LDELEM_REF:
1704 case CEE_STELEM_REF:
1705 return mono_defaults.object_class;
1706 default:
1707 g_assert_not_reached ();
1709 return NULL;
1712 void
1713 mono_add_ins_to_end (MonoBasicBlock *bb, MonoInst *inst)
1715 MonoInst *prev;
1716 if (!bb->code) {
1717 MONO_ADD_INS (bb, inst);
1718 return;
1720 switch (bb->last_ins->opcode) {
1721 case CEE_BEQ:
1722 case CEE_BGE:
1723 case CEE_BGT:
1724 case CEE_BLE:
1725 case CEE_BLT:
1726 case CEE_BNE_UN:
1727 case CEE_BGE_UN:
1728 case CEE_BGT_UN:
1729 case CEE_BLE_UN:
1730 case CEE_BLT_UN:
1731 case CEE_BR:
1732 case CEE_SWITCH:
1733 prev = bb->code;
1734 while (prev->next && prev->next != bb->last_ins)
1735 prev = prev->next;
1736 if (prev == bb->code) {
1737 if (bb->last_ins == bb->code) {
1738 inst->next = bb->code;
1739 bb->code = inst;
1740 } else {
1741 inst->next = prev->next;
1742 prev->next = inst;
1744 } else {
1745 inst->next = bb->last_ins;
1746 prev->next = inst;
1748 break;
1749 // g_warning ("handle conditional jump in add_ins_to_end ()\n");
1750 default:
1751 MONO_ADD_INS (bb, inst);
1752 break;
1756 void
1757 mono_add_varcopy_to_end (MonoCompile *cfg, MonoBasicBlock *bb, int src, int dest)
1759 MonoInst *inst, *load;
1761 NEW_TEMPLOAD (cfg, load, src);
1763 NEW_TEMPSTORE (cfg, inst, dest, load);
1764 if (inst->opcode == CEE_STOBJ) {
1765 NEW_TEMPLOADA (cfg, inst, dest);
1766 handle_stobj (cfg, bb, inst, load, NULL, inst->klass, TRUE, FALSE, FALSE);
1767 } else {
1768 inst->cil_code = NULL;
1769 mono_add_ins_to_end (bb, inst);
1774 * We try to share variables when possible
1776 static MonoInst *
1777 mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins)
1779 MonoInst *res;
1780 int pos, vnum;
1782 /* inlining can result in deeper stacks */
1783 if (slot >= mono_method_get_header (cfg->method)->max_stack)
1784 return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1786 pos = ins->type - 1 + slot * STACK_MAX;
1788 switch (ins->type) {
1789 case STACK_I4:
1790 case STACK_I8:
1791 case STACK_R8:
1792 case STACK_PTR:
1793 case STACK_MP:
1794 case STACK_OBJ:
1795 if ((vnum = cfg->intvars [pos]))
1796 return cfg->varinfo [vnum];
1797 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1798 cfg->intvars [pos] = res->inst_c0;
1799 break;
1800 default:
1801 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1803 return res;
1807 * merge_stacks:
1809 * Merge stack state between two basic blocks according to Ecma 335, Partition III,
1810 * section 1.8.1.1. Store the resulting stack state into stack_2.
1811 * Returns: TRUE, if verification succeeds, FALSE otherwise.
1812 * FIXME: We should store the stack state in a dedicated structure instead of in
1813 * MonoInst's.
1815 static gboolean
1816 merge_stacks (MonoCompile *cfg, MonoStackSlot *state_1, MonoStackSlot *state_2, guint32 size)
1818 int i;
1820 if (cfg->dont_verify_stack_merge)
1821 return TRUE;
1823 /* FIXME: Implement all checks from the spec */
1825 for (i = 0; i < size; ++i) {
1826 MonoStackSlot *slot1 = &state_1 [i];
1827 MonoStackSlot *slot2 = &state_2 [i];
1829 if (slot1->type != slot2->type)
1830 return FALSE;
1832 switch (slot1->type) {
1833 case STACK_PTR:
1834 /* FIXME: Perform merge ? */
1835 /* klass == NULL means a native int */
1836 if (slot1->klass && slot2->klass) {
1837 if (slot1->klass != slot2->klass)
1838 return FALSE;
1840 break;
1841 case STACK_MP:
1842 /* FIXME: Change this to an assert and fix the JIT to allways fill this */
1843 if (slot1->klass && slot2->klass) {
1844 if (slot1->klass != slot2->klass)
1845 return FALSE;
1847 break;
1848 case STACK_OBJ: {
1849 MonoClass *klass1 = slot1->klass;
1850 MonoClass *klass2 = slot2->klass;
1852 if (!klass1) {
1853 /* slot1 is ldnull */
1854 } else if (!klass2) {
1855 /* slot2 is ldnull */
1856 slot2->klass = slot1->klass;
1858 break;
1863 return TRUE;
1867 * This function is called to handle items that are left on the evaluation stack
1868 * at basic block boundaries. What happens is that we save the values to local variables
1869 * and we reload them later when first entering the target basic block (with the
1870 * handle_loaded_temps () function).
1871 * It is also used to handle items on the stack in store opcodes, since it is
1872 * possible that the variable to be stored into is already on the stack, in
1873 * which case its old value should be used.
1874 * A single joint point will use the same variables (stored in the array bb->out_stack or
1875 * bb->in_stack, if the basic block is before or after the joint point).
1876 * If the stack merge fails at a join point, cfg->unverifiable is set.
1878 static int
1879 handle_stack_args (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **sp, int count) {
1880 int i, bindex;
1881 MonoBasicBlock *outb;
1882 MonoInst *inst, **locals;
1883 MonoStackSlot *stack_state;
1884 gboolean found;
1886 if (!count)
1887 return 0;
1888 if (cfg->verbose_level > 3)
1889 g_print ("%d item(s) on exit from B%d\n", count, bb->block_num);
1891 stack_state = mono_mempool_alloc (cfg->mempool, sizeof (MonoStackSlot) * count);
1892 for (i = 0; i < count; ++i) {
1893 stack_state [i].type = sp [i]->type;
1894 stack_state [i].klass = sp [i]->klass;
1896 /* Check that instructions other than ldnull have ins->klass set */
1897 if (!cfg->dont_verify_stack_merge && (sp [i]->type == STACK_OBJ) && !((sp [i]->opcode == OP_PCONST) && sp [i]->inst_c0 == 0))
1898 g_assert (sp [i]->klass);
1901 /* Perform verification and stack state merge */
1902 for (i = 0; i < bb->out_count; ++i) {
1903 outb = bb->out_bb [i];
1905 /* exception handlers are linked, but they should not be considered for stack args */
1906 if (outb->flags & BB_EXCEPTION_HANDLER)
1907 continue;
1908 if (outb->stack_state) {
1909 gboolean verified;
1911 if (count != outb->in_scount) {
1912 cfg->unverifiable = TRUE;
1913 return 0;
1915 verified = merge_stacks (cfg, stack_state, outb->stack_state, count);
1916 if (!verified) {
1917 cfg->unverifiable = TRUE;
1918 return 0;
1921 if (cfg->verbose_level > 3) {
1922 int j;
1924 for (j = 0; j < count; ++j)
1925 printf ("\tStack state of BB%d, slot %d=%d\n", outb->block_num, j, outb->stack_state [j].type);
1927 } else {
1928 /* Make a copy of the stack state */
1929 outb->stack_state = mono_mempool_alloc (cfg->mempool, sizeof (MonoStackSlot) * count);
1930 memcpy (outb->stack_state, stack_state, sizeof (MonoStackSlot) * count);
1934 if (!bb->out_scount) {
1935 bb->out_scount = count;
1936 //g_print ("bblock %d has out:", bb->block_num);
1937 found = FALSE;
1938 for (i = 0; i < bb->out_count; ++i) {
1939 outb = bb->out_bb [i];
1940 /* exception handlers are linked, but they should not be considered for stack args */
1941 if (outb->flags & BB_EXCEPTION_HANDLER)
1942 continue;
1943 //g_print (" %d", outb->block_num);
1944 if (outb->in_stack) {
1945 found = TRUE;
1946 bb->out_stack = outb->in_stack;
1947 break;
1950 //g_print ("\n");
1951 if (!found) {
1952 bb->out_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count);
1953 for (i = 0; i < count; ++i) {
1955 * try to reuse temps already allocated for this purpouse, if they occupy the same
1956 * stack slot and if they are of the same type.
1957 * This won't cause conflicts since if 'local' is used to
1958 * store one of the values in the in_stack of a bblock, then
1959 * the same variable will be used for the same outgoing stack
1960 * slot as well.
1961 * This doesn't work when inlining methods, since the bblocks
1962 * in the inlined methods do not inherit their in_stack from
1963 * the bblock they are inlined to. See bug #58863 for an
1964 * example.
1966 if (cfg->inlined_method)
1967 bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL);
1968 else
1969 bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]);
1974 for (i = 0; i < bb->out_count; ++i) {
1975 outb = bb->out_bb [i];
1976 /* exception handlers are linked, but they should not be considered for stack args */
1977 if (outb->flags & BB_EXCEPTION_HANDLER)
1978 continue;
1979 if (outb->in_scount) {
1980 if (outb->in_scount != bb->out_scount)
1981 G_BREAKPOINT ();
1982 continue; /* check they are the same locals */
1984 outb->in_scount = count;
1985 outb->in_stack = bb->out_stack;
1988 locals = bb->out_stack;
1989 for (i = 0; i < count; ++i) {
1990 /* add store ops at the end of the bb, before the branch */
1991 NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
1992 if (inst->opcode == CEE_STOBJ) {
1993 NEW_TEMPLOADA (cfg, inst, locals [i]->inst_c0);
1994 handle_stobj (cfg, bb, inst, sp [i], sp [i]->cil_code, inst->klass, TRUE, FALSE, FALSE);
1995 } else {
1996 inst->cil_code = sp [i]->cil_code;
1997 mono_add_ins_to_end (bb, inst);
1999 if (cfg->verbose_level > 3)
2000 g_print ("storing %d to temp %d\n", i, (int)locals [i]->inst_c0);
2004 * It is possible that the out bblocks already have in_stack assigned, and
2005 * the in_stacks differ. In this case, we will store to all the different
2006 * in_stacks.
2009 found = TRUE;
2010 bindex = 0;
2011 while (found) {
2012 /* Find a bblock which has a different in_stack */
2013 found = FALSE;
2014 while (bindex < bb->out_count) {
2015 outb = bb->out_bb [bindex];
2016 /* exception handlers are linked, but they should not be considered for stack args */
2017 if (outb->flags & BB_EXCEPTION_HANDLER) {
2018 bindex++;
2019 continue;
2021 if (outb->in_stack != locals) {
2023 * Instead of storing sp [i] to locals [i], we need to store
2024 * locals [i] to <new locals>[i], since the sp [i] tree can't
2025 * be shared between trees.
2027 for (i = 0; i < count; ++i)
2028 mono_add_varcopy_to_end (cfg, bb, locals [i]->inst_c0, outb->in_stack [i]->inst_c0);
2029 locals = outb->in_stack;
2030 found = TRUE;
2031 break;
2033 bindex ++;
2037 return 0;
2040 static int
2041 ret_type_to_call_opcode (MonoType *type, int calli, int virt)
2043 if (type->byref)
2044 return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL;
2046 handle_enum:
2047 switch (type->type) {
2048 case MONO_TYPE_VOID:
2049 return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALLVIRT: OP_VOIDCALL;
2050 case MONO_TYPE_I1:
2051 case MONO_TYPE_U1:
2052 case MONO_TYPE_BOOLEAN:
2053 case MONO_TYPE_I2:
2054 case MONO_TYPE_U2:
2055 case MONO_TYPE_CHAR:
2056 case MONO_TYPE_I4:
2057 case MONO_TYPE_U4:
2058 return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL;
2059 case MONO_TYPE_I:
2060 case MONO_TYPE_U:
2061 case MONO_TYPE_PTR:
2062 case MONO_TYPE_FNPTR:
2063 return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL;
2064 case MONO_TYPE_CLASS:
2065 case MONO_TYPE_STRING:
2066 case MONO_TYPE_OBJECT:
2067 case MONO_TYPE_SZARRAY:
2068 case MONO_TYPE_ARRAY:
2069 return calli? OP_CALL_REG: virt? CEE_CALLVIRT: CEE_CALL;
2070 case MONO_TYPE_I8:
2071 case MONO_TYPE_U8:
2072 return calli? OP_LCALL_REG: virt? OP_LCALLVIRT: OP_LCALL;
2073 case MONO_TYPE_R4:
2074 case MONO_TYPE_R8:
2075 return calli? OP_FCALL_REG: virt? OP_FCALLVIRT: OP_FCALL;
2076 case MONO_TYPE_VALUETYPE:
2077 if (type->data.klass->enumtype) {
2078 type = type->data.klass->enum_basetype;
2079 goto handle_enum;
2080 } else
2081 return calli? OP_VCALL_REG: virt? OP_VCALLVIRT: OP_VCALL;
2082 case MONO_TYPE_TYPEDBYREF:
2083 return calli? OP_VCALL_REG: virt? OP_VCALLVIRT: OP_VCALL;
2084 case MONO_TYPE_GENERICINST:
2085 type = &type->data.generic_class->container_class->byval_arg;
2086 goto handle_enum;
2087 default:
2088 g_error ("unknown type 0x%02x in ret_type_to_call_opcode", type->type);
2090 return -1;
2093 void
2094 mono_create_jump_table (MonoCompile *cfg, MonoInst *label, MonoBasicBlock **bbs, int num_blocks)
2096 MonoJumpInfo *ji = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfo));
2097 MonoJumpInfoBBTable *table;
2099 table = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfoBBTable));
2100 table->table = bbs;
2101 table->table_size = num_blocks;
2103 ji->ip.label = label;
2104 ji->type = MONO_PATCH_INFO_SWITCH;
2105 ji->data.table = table;
2106 ji->next = cfg->patch_info;
2107 cfg->patch_info = ji;
2111 * When we add a tree of instructions, we need to ensure the instructions currently
2112 * on the stack are executed before (like, if we load a value from a local).
2113 * We ensure this by saving the currently loaded values to temps and rewriting the
2114 * instructions to load the values.
2115 * This is not done for opcodes that terminate a basic block (because it's handled already
2116 * by handle_stack_args ()) and for opcodes that can't change values, like POP.
2118 static void
2119 handle_loaded_temps (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst **stack, MonoInst **sp)
2121 MonoInst *load, *store, *temp, *ins;
2123 while (stack < sp) {
2124 ins = *stack;
2125 /* handle also other constants */
2126 if ((ins->opcode != OP_ICONST) &&
2127 /* temps never get written to again, so we can safely avoid duplicating them */
2128 !(ins->ssa_op == MONO_SSA_LOAD && ins->inst_i0->opcode == OP_LOCAL && ins->inst_i0->flags & MONO_INST_IS_TEMP)) {
2129 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
2130 temp->flags |= MONO_INST_IS_TEMP;
2131 NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
2132 store->cil_code = ins->cil_code;
2133 if (store->opcode == CEE_STOBJ) {
2134 NEW_TEMPLOADA (cfg, store, temp->inst_c0);
2135 handle_stobj (cfg, bblock, store, ins, ins->cil_code, temp->klass, FALSE, FALSE, FALSE);
2136 } else
2137 MONO_ADD_INS (bblock, store);
2138 NEW_TEMPLOAD (cfg, load, temp->inst_c0);
2139 load->cil_code = ins->cil_code;
2140 *stack = load;
2142 stack++;
2147 * target_type_is_incompatible:
2148 * @cfg: MonoCompile context
2150 * Check that the item @arg on the evaluation stack can be stored
2151 * in the target type (can be a local, or field, etc).
2152 * The cfg arg can be used to check if we need verification or just
2153 * validity checks.
2155 * Returns: non-0 value if arg can't be stored on a target.
2157 static int
2158 target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg)
2160 MonoType *simple_type;
2161 MonoClass *klass;
2163 if (target->byref) {
2164 /* FIXME: check that the pointed to types match */
2165 if (arg->type == STACK_MP)
2166 return arg->klass != mono_class_from_mono_type (target);
2167 if (arg->type == STACK_PTR)
2168 return 0;
2169 return 1;
2171 simple_type = mono_type_get_underlying_type (target);
2172 switch (simple_type->type) {
2173 case MONO_TYPE_VOID:
2174 return 1;
2175 case MONO_TYPE_I1:
2176 case MONO_TYPE_U1:
2177 case MONO_TYPE_BOOLEAN:
2178 case MONO_TYPE_I2:
2179 case MONO_TYPE_U2:
2180 case MONO_TYPE_CHAR:
2181 case MONO_TYPE_I4:
2182 case MONO_TYPE_U4:
2183 if (arg->type != STACK_I4 && arg->type != STACK_PTR)
2184 return 1;
2185 return 0;
2186 case MONO_TYPE_PTR:
2187 /* STACK_MP is needed when setting pinned locals */
2188 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2189 return 1;
2190 return 0;
2191 case MONO_TYPE_I:
2192 case MONO_TYPE_U:
2193 case MONO_TYPE_FNPTR:
2194 if (arg->type != STACK_I4 && arg->type != STACK_PTR)
2195 return 1;
2196 return 0;
2197 case MONO_TYPE_CLASS:
2198 case MONO_TYPE_STRING:
2199 case MONO_TYPE_OBJECT:
2200 case MONO_TYPE_SZARRAY:
2201 case MONO_TYPE_ARRAY:
2202 if (arg->type != STACK_OBJ)
2203 return 1;
2204 /* FIXME: check type compatibility */
2205 return 0;
2206 case MONO_TYPE_I8:
2207 case MONO_TYPE_U8:
2208 if (arg->type != STACK_I8)
2209 return 1;
2210 return 0;
2211 case MONO_TYPE_R4:
2212 case MONO_TYPE_R8:
2213 if (arg->type != STACK_R8)
2214 return 1;
2215 return 0;
2216 case MONO_TYPE_VALUETYPE:
2217 if (arg->type != STACK_VTYPE)
2218 return 1;
2219 klass = mono_class_from_mono_type (simple_type);
2220 if (klass != arg->klass)
2221 return 1;
2222 return 0;
2223 case MONO_TYPE_TYPEDBYREF:
2224 if (arg->type != STACK_VTYPE)
2225 return 1;
2226 klass = mono_class_from_mono_type (simple_type);
2227 if (klass != arg->klass)
2228 return 1;
2229 return 0;
2230 case MONO_TYPE_GENERICINST:
2231 if (mono_type_generic_inst_is_valuetype (simple_type)) {
2232 if (arg->type != STACK_VTYPE)
2233 return 1;
2234 klass = mono_class_from_mono_type (simple_type);
2235 if (klass != arg->klass)
2236 return 1;
2237 return 0;
2238 } else {
2239 if (arg->type != STACK_OBJ)
2240 return 1;
2241 /* FIXME: check type compatibility */
2242 return 0;
2244 default:
2245 g_error ("unknown type 0x%02x in target_type_is_incompatible", simple_type->type);
2247 return 1;
2251 * Prepare arguments for passing to a function call.
2252 * Return a non-zero value if the arguments can't be passed to the given
2253 * signature.
2254 * The type checks are not yet complete and some conversions may need
2255 * casts on 32 or 64 bit architectures.
2257 * FIXME: implement this using target_type_is_incompatible ()
2259 static int
2260 check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args)
2262 MonoType *simple_type;
2263 int i;
2265 if (sig->hasthis) {
2266 if (args [0]->type != STACK_OBJ && args [0]->type != STACK_MP && args [0]->type != STACK_PTR)
2267 return 1;
2268 args++;
2270 for (i = 0; i < sig->param_count; ++i) {
2271 if (sig->params [i]->byref) {
2272 if (args [i]->type != STACK_MP && args [i]->type != STACK_PTR)
2273 return 1;
2274 continue;
2276 simple_type = sig->params [i];
2277 handle_enum:
2278 switch (simple_type->type) {
2279 case MONO_TYPE_VOID:
2280 return 1;
2281 continue;
2282 case MONO_TYPE_I1:
2283 case MONO_TYPE_U1:
2284 case MONO_TYPE_BOOLEAN:
2285 case MONO_TYPE_I2:
2286 case MONO_TYPE_U2:
2287 case MONO_TYPE_CHAR:
2288 case MONO_TYPE_I4:
2289 case MONO_TYPE_U4:
2290 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR)
2291 return 1;
2292 continue;
2293 case MONO_TYPE_I:
2294 case MONO_TYPE_U:
2295 case MONO_TYPE_PTR:
2296 case MONO_TYPE_FNPTR:
2297 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR && args [i]->type != STACK_MP && args [i]->type != STACK_OBJ)
2298 return 1;
2299 continue;
2300 case MONO_TYPE_CLASS:
2301 case MONO_TYPE_STRING:
2302 case MONO_TYPE_OBJECT:
2303 case MONO_TYPE_SZARRAY:
2304 case MONO_TYPE_ARRAY:
2305 if (args [i]->type != STACK_OBJ)
2306 return 1;
2307 continue;
2308 case MONO_TYPE_I8:
2309 case MONO_TYPE_U8:
2310 if (args [i]->type != STACK_I8)
2311 return 1;
2312 continue;
2313 case MONO_TYPE_R4:
2314 case MONO_TYPE_R8:
2315 if (args [i]->type != STACK_R8)
2316 return 1;
2317 continue;
2318 case MONO_TYPE_VALUETYPE:
2319 if (simple_type->data.klass->enumtype) {
2320 simple_type = simple_type->data.klass->enum_basetype;
2321 goto handle_enum;
2323 if (args [i]->type != STACK_VTYPE)
2324 return 1;
2325 continue;
2326 case MONO_TYPE_TYPEDBYREF:
2327 if (args [i]->type != STACK_VTYPE)
2328 return 1;
2329 continue;
2330 case MONO_TYPE_GENERICINST:
2331 simple_type = &simple_type->data.generic_class->container_class->byval_arg;
2332 goto handle_enum;
2334 default:
2335 g_error ("unknown type 0x%02x in check_call_signature",
2336 simple_type->type);
2339 return 0;
2342 inline static int
2343 mono_spill_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoCallInst *call, MonoMethodSignature *sig, gboolean ret_object,
2344 const guint8 *ip, gboolean to_end)
2346 MonoInst *temp, *store, *ins = (MonoInst*)call;
2347 MonoType *ret = sig->ret;
2349 if (!MONO_TYPE_IS_VOID (ret) || ret_object) {
2350 if (ret_object) {
2351 call->inst.type = STACK_OBJ;
2352 call->inst.opcode = CEE_CALL;
2353 temp = mono_compile_create_var (cfg, &mono_defaults.string_class->byval_arg, OP_LOCAL);
2354 } else {
2355 type_to_eval_stack_type (ret, ins);
2356 temp = mono_compile_create_var (cfg, ret, OP_LOCAL);
2359 temp->flags |= MONO_INST_IS_TEMP;
2361 if (MONO_TYPE_ISSTRUCT (ret)) {
2362 MonoInst *loada, *dummy_store;
2365 * Emit a dummy store to the local holding the result so the
2366 * liveness info remains correct.
2368 NEW_DUMMY_STORE (cfg, dummy_store, temp->inst_c0);
2369 if (to_end)
2370 mono_add_ins_to_end (bblock, dummy_store);
2371 else
2372 MONO_ADD_INS (bblock, dummy_store);
2374 /* we use this to allocate native sized structs */
2375 temp->backend.is_pinvoke = sig->pinvoke;
2377 NEW_TEMPLOADA (cfg, loada, temp->inst_c0);
2378 if (call->inst.opcode == OP_VCALL)
2379 ins->inst_left = loada;
2380 else
2381 ins->inst_right = loada; /* a virtual or indirect call */
2383 if (to_end)
2384 mono_add_ins_to_end (bblock, ins);
2385 else
2386 MONO_ADD_INS (bblock, ins);
2387 } else {
2388 NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
2389 store->cil_code = ip;
2390 if (to_end)
2391 mono_add_ins_to_end (bblock, store);
2392 else
2393 MONO_ADD_INS (bblock, store);
2395 return temp->inst_c0;
2396 } else {
2397 if (to_end)
2398 mono_add_ins_to_end (bblock, ins);
2399 else
2400 MONO_ADD_INS (bblock, ins);
2401 return -1;
2405 inline static MonoCallInst *
2406 mono_emit_call_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *sig,
2407 MonoInst **args, int calli, int virtual, const guint8 *ip, gboolean to_end)
2409 MonoCallInst *call;
2410 MonoInst *arg;
2412 MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (sig->ret, calli, virtual));
2414 call->inst.cil_code = ip;
2415 call->args = args;
2416 call->signature = sig;
2417 call = mono_arch_call_opcode (cfg, bblock, call, virtual);
2418 type_to_eval_stack_type (sig->ret, &call->inst);
2420 for (arg = call->out_args; arg;) {
2421 MonoInst *narg = arg->next;
2422 arg->next = NULL;
2423 if (!arg->cil_code)
2424 arg->cil_code = ip;
2425 if (to_end)
2426 mono_add_ins_to_end (bblock, arg);
2427 else
2428 MONO_ADD_INS (bblock, arg);
2429 arg = narg;
2431 return call;
2434 inline static int
2435 mono_emit_calli (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *sig,
2436 MonoInst **args, MonoInst *addr, const guint8 *ip)
2438 MonoCallInst *call = mono_emit_call_args (cfg, bblock, sig, args, TRUE, FALSE, ip, FALSE);
2440 call->inst.inst_i0 = addr;
2442 return mono_spill_call (cfg, bblock, call, sig, FALSE, ip, FALSE);
2445 static MonoCallInst*
2446 mono_emit_method_call_full (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method, MonoMethodSignature *sig,
2447 MonoInst **args, const guint8 *ip, MonoInst *this, gboolean to_end)
2449 gboolean virtual = this != NULL;
2450 MonoCallInst *call;
2452 call = mono_emit_call_args (cfg, bblock, sig, args, FALSE, virtual, ip, to_end);
2454 if (this && sig->hasthis &&
2455 (method->klass->marshalbyref || method->klass == mono_defaults.object_class) &&
2456 !(method->flags & METHOD_ATTRIBUTE_VIRTUAL) && !MONO_CHECK_THIS (this)) {
2457 call->method = mono_marshal_get_remoting_invoke_with_check (method);
2458 } else {
2459 call->method = method;
2461 call->inst.flags |= MONO_INST_HAS_METHOD;
2462 call->inst.inst_left = this;
2464 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE)
2465 /* Needed by the code generated in inssel.brg */
2466 mono_get_got_var (cfg);
2468 return call;
2471 static MonoCallInst*
2472 mono_emit_method_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method, MonoMethodSignature *sig,
2473 MonoInst **args, const guint8 *ip, MonoInst *this)
2475 return mono_emit_method_call_full (cfg, bblock, method, sig, args, ip, this, FALSE);
2478 inline static int
2479 mono_emit_method_call_spilled (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method,
2480 MonoMethodSignature *signature, MonoInst **args, const guint8 *ip, MonoInst *this)
2482 MonoCallInst *call = mono_emit_method_call (cfg, bblock, method, signature, args, ip, this);
2484 return mono_spill_call (cfg, bblock, call, signature, method->string_ctor, ip, FALSE);
2487 inline static int
2488 mono_emit_method_call_spilled_full (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method,
2489 MonoMethodSignature *signature, MonoInst **args, const guint8 *ip, MonoInst *this,
2490 gboolean ret_object, gboolean to_end)
2492 MonoCallInst *call = mono_emit_method_call_full (cfg, bblock, method, signature, args, ip, this, to_end);
2494 return mono_spill_call (cfg, bblock, call, signature, ret_object, ip, to_end);
2497 inline static int
2498 mono_emit_native_call (MonoCompile *cfg, MonoBasicBlock *bblock, gconstpointer func, MonoMethodSignature *sig,
2499 MonoInst **args, const guint8 *ip, gboolean ret_object, gboolean to_end)
2501 MonoCallInst *call;
2503 g_assert (sig);
2505 call = mono_emit_call_args (cfg, bblock, sig, args, FALSE, FALSE, ip, to_end);
2506 call->fptr = func;
2508 return mono_spill_call (cfg, bblock, call, sig, ret_object, ip, to_end);
2511 inline static int
2512 mono_emit_jit_icall (MonoCompile *cfg, MonoBasicBlock *bblock, gconstpointer func, MonoInst **args, const guint8 *ip)
2514 MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func);
2516 if (!info) {
2517 g_warning ("unregistered JIT ICall");
2518 g_assert_not_reached ();
2521 return mono_emit_native_call (cfg, bblock, mono_icall_get_wrapper (info), info->sig, args, ip, FALSE, FALSE);
2524 static void
2525 mono_emulate_opcode (MonoCompile *cfg, MonoInst *tree, MonoInst **iargs, MonoJitICallInfo *info)
2527 MonoInst *ins, *temp = NULL, *store, *load, *begin;
2528 MonoInst *last_arg = NULL;
2529 int nargs;
2530 MonoCallInst *call;
2532 //g_print ("emulating: ");
2533 //mono_print_tree_nl (tree);
2534 MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (info->sig->ret, FALSE, FALSE));
2535 ins = (MonoInst*)call;
2537 call->inst.cil_code = tree->cil_code;
2538 call->args = iargs;
2539 call->signature = info->sig;
2541 call = mono_arch_call_opcode (cfg, cfg->cbb, call, FALSE);
2543 if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
2544 temp = mono_compile_create_var (cfg, info->sig->ret, OP_LOCAL);
2545 temp->flags |= MONO_INST_IS_TEMP;
2546 NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
2547 store->cil_code = tree->cil_code;
2548 } else {
2549 store = ins;
2552 nargs = info->sig->param_count + info->sig->hasthis;
2554 for (last_arg = call->out_args; last_arg && last_arg->next; last_arg = last_arg->next) ;
2556 if (nargs)
2557 last_arg->next = store;
2559 if (nargs)
2560 begin = call->out_args;
2561 else
2562 begin = store;
2564 if (cfg->prev_ins) {
2566 * This assumes that that in a tree, emulate_opcode is called for a
2567 * node before it is called for its children. dec_foreach needs to
2568 * take this into account.
2570 store->next = cfg->prev_ins->next;
2571 cfg->prev_ins->next = begin;
2572 } else {
2573 store->next = cfg->cbb->code;
2574 cfg->cbb->code = begin;
2577 call->fptr = mono_icall_get_wrapper (info);
2579 if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
2580 NEW_TEMPLOAD (cfg, load, temp->inst_c0);
2581 *tree = *load;
2585 static MonoMethodSignature *
2586 mono_get_element_address_signature (int arity)
2588 static GHashTable *sighash = NULL;
2589 MonoMethodSignature *res;
2590 int i;
2592 mono_jit_lock ();
2593 if (!sighash) {
2594 sighash = g_hash_table_new (NULL, NULL);
2596 else if ((res = g_hash_table_lookup (sighash, GINT_TO_POINTER (arity)))) {
2597 LeaveCriticalSection (&jit_mutex);
2598 return res;
2601 res = mono_metadata_signature_alloc (mono_defaults.corlib, arity + 1);
2603 res->pinvoke = 1;
2604 #ifdef MONO_ARCH_VARARG_ICALLS
2605 /* Only set this only some archs since not all backends can handle varargs+pinvoke */
2606 res->call_convention = MONO_CALL_VARARG;
2607 #endif
2608 res->params [0] = &mono_defaults.array_class->byval_arg;
2610 #ifdef PLATFORM_WIN32
2612 * The default pinvoke calling convention is STDCALL but we need CDECL.
2614 res->call_convention = MONO_CALL_C;
2615 #endif
2617 for (i = 1; i <= arity; i++)
2618 res->params [i] = &mono_defaults.int_class->byval_arg;
2620 res->ret = &mono_defaults.int_class->byval_arg;
2622 g_hash_table_insert (sighash, GINT_TO_POINTER (arity), res);
2623 mono_jit_unlock ();
2625 return res;
2628 static MonoMethodSignature *
2629 mono_get_array_new_va_signature (int arity)
2631 static GHashTable *sighash = NULL;
2632 MonoMethodSignature *res;
2633 int i;
2635 mono_jit_lock ();
2636 if (!sighash) {
2637 sighash = g_hash_table_new (NULL, NULL);
2639 else if ((res = g_hash_table_lookup (sighash, GINT_TO_POINTER (arity)))) {
2640 mono_jit_unlock ();
2641 return res;
2644 res = mono_metadata_signature_alloc (mono_defaults.corlib, arity + 1);
2646 res->pinvoke = 1;
2647 #ifdef MONO_ARCH_VARARG_ICALLS
2648 /* Only set this only some archs since not all backends can handle varargs+pinvoke */
2649 res->call_convention = MONO_CALL_VARARG;
2650 #endif
2652 #ifdef PLATFORM_WIN32
2653 res->call_convention = MONO_CALL_C;
2654 #endif
2656 res->params [0] = &mono_defaults.int_class->byval_arg;
2657 for (i = 0; i < arity; i++)
2658 res->params [i + 1] = &mono_defaults.int_class->byval_arg;
2660 res->ret = &mono_defaults.int_class->byval_arg;
2662 g_hash_table_insert (sighash, GINT_TO_POINTER (arity), res);
2663 mono_jit_unlock ();
2665 return res;
2668 #ifdef MONO_ARCH_SOFT_FLOAT
2669 static void
2670 handle_store_float (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *ptr, MonoInst *val, const unsigned char *ip)
2672 MonoInst *iargs [2];
2673 iargs [0] = val;
2674 iargs [1] = ptr;
2676 mono_emit_jit_icall (cfg, bblock, mono_fstore_r4, iargs, ip);
2679 static int
2680 handle_load_float (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *ptr, const unsigned char *ip)
2682 MonoInst *iargs [1];
2683 iargs [0] = ptr;
2685 return mono_emit_jit_icall (cfg, bblock, mono_fload_r4, iargs, ip);
2688 #define LDLOC_SOFT_FLOAT(cfg,ins,idx,ip) do {\
2689 if (header->locals [(idx)]->type == MONO_TYPE_R4) { \
2690 int temp; \
2691 NEW_LOCLOADA (cfg, (ins), (idx)); \
2692 temp = handle_load_float (cfg, bblock, (ins), (ip)); \
2693 NEW_TEMPLOAD (cfg, (ins), temp); \
2695 } while (0)
2696 #define STLOC_SOFT_FLOAT(cfg,ins,idx,ip) do {\
2697 if (header->locals [(idx)]->type == MONO_TYPE_R4) { \
2698 int temp; \
2699 NEW_LOCLOADA (cfg, (ins), (idx)); \
2700 handle_store_float (cfg, bblock, (ins), *sp, (ip)); \
2701 MONO_INST_NEW (cfg, (ins), CEE_NOP); \
2703 } while (0)
2704 #define LDARG_SOFT_FLOAT(cfg,ins,idx,ip) do {\
2705 if (param_types [(idx)]->type == MONO_TYPE_R4) { \
2706 int temp; \
2707 NEW_ARGLOADA (cfg, (ins), (idx)); \
2708 temp = handle_load_float (cfg, bblock, (ins), (ip)); \
2709 NEW_TEMPLOAD (cfg, (ins), temp); \
2711 } while (0)
2712 #else
2713 #define LDLOC_SOFT_FLOAT(cfg,ins,idx,ip)
2714 #define STLOC_SOFT_FLOAT(cfg,ins,idx,ip)
2715 #define LDARG_SOFT_FLOAT(cfg,ins,idx,ip)
2716 #endif
2718 static MonoMethod*
2719 get_memcpy_method (void)
2721 static MonoMethod *memcpy_method = NULL;
2722 if (!memcpy_method) {
2723 memcpy_method = mono_class_get_method_from_name (mono_defaults.string_class, "memcpy", 3);
2724 if (!memcpy_method)
2725 g_error ("Old corlib found. Install a new one");
2727 return memcpy_method;
2730 static void
2731 handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src, const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native, gboolean write_barrier) {
2732 MonoInst *iargs [3];
2733 int n;
2734 guint32 align = 0;
2735 MonoMethod *memcpy_method;
2737 g_assert (klass);
2739 * This check breaks with spilled vars... need to handle it during verification anyway.
2740 * g_assert (klass && klass == src->klass && klass == dest->klass);
2743 if (native)
2744 n = mono_class_native_size (klass, &align);
2745 else
2746 n = mono_class_value_size (klass, &align);
2748 #if HAVE_WRITE_BARRIERS
2749 /* if native is true there should be no references in the struct */
2750 if (write_barrier && klass->has_references && !native) {
2751 iargs [0] = dest;
2752 iargs [1] = src;
2753 NEW_PCONST (cfg, iargs [2], klass);
2755 mono_emit_jit_icall (cfg, bblock, mono_value_copy, iargs, ip);
2756 return;
2758 #endif
2760 /* FIXME: add write barrier handling */
2761 if ((cfg->opt & MONO_OPT_INTRINS) && !to_end && n <= sizeof (gpointer) * 5) {
2762 MonoInst *inst;
2763 if (dest->opcode == OP_LDADDR) {
2764 /* Keep liveness info correct */
2765 NEW_DUMMY_STORE (cfg, inst, dest->inst_i0->inst_c0);
2766 MONO_ADD_INS (bblock, inst);
2768 MONO_INST_NEW (cfg, inst, OP_MEMCPY);
2769 inst->inst_left = dest;
2770 inst->inst_right = src;
2771 inst->cil_code = ip;
2772 inst->backend.size = n;
2773 MONO_ADD_INS (bblock, inst);
2774 return;
2776 iargs [0] = dest;
2777 iargs [1] = src;
2778 NEW_ICONST (cfg, iargs [2], n);
2780 memcpy_method = get_memcpy_method ();
2781 mono_emit_method_call_spilled_full (cfg, bblock, memcpy_method, memcpy_method->signature, iargs, ip, NULL, FALSE, to_end);
2784 static MonoMethod*
2785 get_memset_method (void)
2787 static MonoMethod *memset_method = NULL;
2788 if (!memset_method) {
2789 memset_method = mono_class_get_method_from_name (mono_defaults.string_class, "memset", 3);
2790 if (!memset_method)
2791 g_error ("Old corlib found. Install a new one");
2793 return memset_method;
2796 static void
2797 handle_initobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, const guchar *ip, MonoClass *klass, MonoInst **stack_start, MonoInst **sp)
2799 MonoInst *iargs [3];
2800 MonoInst *ins, *zero_int32;
2801 int n;
2802 MonoMethod *memset_method;
2804 NEW_ICONST (cfg, zero_int32, 0);
2806 mono_class_init (klass);
2807 n = mono_class_value_size (klass, NULL);
2808 MONO_INST_NEW (cfg, ins, 0);
2809 ins->cil_code = ip;
2810 ins->inst_left = dest;
2811 ins->inst_right = zero_int32;
2812 switch (n) {
2813 case 1:
2814 ins->opcode = CEE_STIND_I1;
2815 MONO_ADD_INS (bblock, ins);
2816 break;
2817 case 2:
2818 ins->opcode = CEE_STIND_I2;
2819 MONO_ADD_INS (bblock, ins);
2820 break;
2821 case 4:
2822 ins->opcode = CEE_STIND_I4;
2823 MONO_ADD_INS (bblock, ins);
2824 break;
2825 default:
2826 if (n <= sizeof (gpointer) * 5) {
2827 ins->opcode = OP_MEMSET;
2828 ins->inst_imm = 0;
2829 ins->backend.size = n;
2830 MONO_ADD_INS (bblock, ins);
2831 break;
2833 memset_method = get_memset_method ();
2834 handle_loaded_temps (cfg, bblock, stack_start, sp);
2835 iargs [0] = dest;
2836 NEW_ICONST (cfg, iargs [1], 0);
2837 NEW_ICONST (cfg, iargs [2], n);
2838 mono_emit_method_call_spilled (cfg, bblock, memset_method, memset_method->signature, iargs, ip, NULL);
2839 break;
2843 static int
2844 handle_alloc (MonoCompile *cfg, MonoBasicBlock *bblock, MonoClass *klass, gboolean for_box, const guchar *ip)
2846 MonoInst *iargs [2];
2847 void *alloc_ftn;
2849 if (cfg->opt & MONO_OPT_SHARED) {
2850 NEW_DOMAINCONST (cfg, iargs [0]);
2851 NEW_CLASSCONST (cfg, iargs [1], klass);
2853 alloc_ftn = mono_object_new;
2854 } else if (cfg->compile_aot && bblock->out_of_line && klass->type_token && klass->image == mono_defaults.corlib) {
2855 /* This happens often in argument checking code, eg. throw new FooException... */
2856 /* Avoid relocations by calling a helper function specialized to mscorlib */
2857 NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token));
2858 return mono_emit_jit_icall (cfg, bblock, mono_helper_newobj_mscorlib, iargs, ip);
2859 } else {
2860 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
2861 gboolean pass_lw;
2863 alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
2864 if (pass_lw) {
2865 guint32 lw = vtable->klass->instance_size;
2866 lw = ((lw + (sizeof (gpointer) - 1)) & ~(sizeof (gpointer) - 1)) / sizeof (gpointer);
2867 NEW_ICONST (cfg, iargs [0], lw);
2868 NEW_VTABLECONST (cfg, iargs [1], vtable);
2870 else
2871 NEW_VTABLECONST (cfg, iargs [0], vtable);
2874 return mono_emit_jit_icall (cfg, bblock, alloc_ftn, iargs, ip);
2878 * Handles unbox of a Nullable<T>, returning a temp variable
2879 * where the result is stored
2881 static int
2882 handle_unbox_nullable (MonoCompile* cfg, MonoBasicBlock* bblock, MonoInst* val, const guchar *ip, MonoClass* klass)
2884 MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1);
2885 return mono_emit_method_call_spilled (cfg, bblock, method, mono_method_signature (method), &val, ip, NULL);
2891 static MonoInst *
2892 handle_box (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *val, const guchar *ip, MonoClass *klass)
2894 MonoInst *dest, *vtoffset, *add, *vstore;
2895 int temp;
2897 if (mono_class_is_nullable (klass)) {
2898 MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
2899 temp = mono_emit_method_call_spilled (cfg, bblock, method, mono_method_signature (method), &val, ip, NULL);
2900 NEW_TEMPLOAD (cfg, dest, temp);
2901 return dest;
2905 temp = handle_alloc (cfg, bblock, klass, TRUE, ip);
2906 NEW_TEMPLOAD (cfg, dest, temp);
2907 NEW_ICONST (cfg, vtoffset, sizeof (MonoObject));
2908 MONO_INST_NEW (cfg, add, OP_PADD);
2909 add->inst_left = dest;
2910 add->inst_right = vtoffset;
2911 add->cil_code = ip;
2912 add->klass = klass;
2913 MONO_INST_NEW (cfg, vstore, CEE_STIND_I);
2914 vstore->opcode = mono_type_to_stind (&klass->byval_arg);
2915 vstore->cil_code = ip;
2916 vstore->inst_left = add;
2917 vstore->inst_right = val;
2919 if (vstore->opcode == CEE_STOBJ) {
2920 handle_stobj (cfg, bblock, add, val, ip, klass, FALSE, FALSE, TRUE);
2921 } else
2922 MONO_ADD_INS (bblock, vstore);
2924 NEW_TEMPLOAD (cfg, dest, temp);
2925 return dest;
2928 static int
2929 handle_array_new (MonoCompile *cfg, MonoBasicBlock *bblock, int rank, MonoInst **sp, unsigned char *ip)
2931 MonoMethodSignature *esig;
2932 char icall_name [256];
2933 char *name;
2934 MonoJitICallInfo *info;
2936 /* Need to register the icall so it gets an icall wrapper */
2937 sprintf (icall_name, "ves_array_new_va_%d", rank);
2939 mono_jit_lock ();
2940 info = mono_find_jit_icall_by_name (icall_name);
2941 if (info == NULL) {
2942 esig = mono_get_array_new_va_signature (rank);
2943 name = g_strdup (icall_name);
2944 info = mono_register_jit_icall (mono_array_new_va, name, esig, FALSE);
2946 g_hash_table_insert (jit_icall_name_hash, name, name);
2948 mono_jit_unlock ();
2950 cfg->flags |= MONO_CFG_HAS_VARARGS;
2952 /* FIXME: This uses info->sig, but it should use the signature of the wrapper */
2953 return mono_emit_native_call (cfg, bblock, mono_icall_get_wrapper (info), info->sig, sp, ip, TRUE, FALSE);
2956 static void
2957 mono_emit_load_got_addr (MonoCompile *cfg)
2959 MonoInst *load, *store, *dummy_use;
2960 MonoInst *get_got;
2962 if (!cfg->got_var || cfg->got_var_allocated)
2963 return;
2965 MONO_INST_NEW (cfg, get_got, OP_LOAD_GOTADDR);
2966 NEW_TEMPSTORE (cfg, store, cfg->got_var->inst_c0, get_got);
2968 /* Add it to the start of the first bblock */
2969 if (cfg->bb_entry->code) {
2970 store->next = cfg->bb_entry->code;
2971 cfg->bb_entry->code = store;
2973 else
2974 MONO_ADD_INS (cfg->bb_entry, store);
2976 cfg->got_var_allocated = TRUE;
2979 * Add a dummy use to keep the got_var alive, since real uses might
2980 * only be generated in the decompose or instruction selection phases.
2981 * Add it to end_bblock, so the variable's lifetime covers the whole
2982 * method.
2984 NEW_TEMPLOAD (cfg, load, cfg->got_var->inst_c0);
2985 NEW_DUMMY_USE (cfg, dummy_use, load);
2986 MONO_ADD_INS (cfg->bb_exit, dummy_use);
2989 #define CODE_IS_STLOC(ip) (((ip) [0] >= CEE_STLOC_0 && (ip) [0] <= CEE_STLOC_3) || ((ip) [0] == CEE_STLOC_S))
2991 static gboolean
2992 mini_class_is_system_array (MonoClass *klass)
2994 if (klass->parent == mono_defaults.array_class)
2995 return TRUE;
2996 else
2997 return FALSE;
3000 static gboolean
3001 mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
3003 MonoMethodHeader *header = mono_method_get_header (method);
3004 MonoMethodSignature *signature = mono_method_signature (method);
3005 MonoVTable *vtable;
3006 int i;
3008 #ifdef MONO_ARCH_HAVE_LMF_OPS
3009 if (((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
3010 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) &&
3011 !MONO_TYPE_ISSTRUCT (signature->ret) && !mini_class_is_system_array (method->klass))
3012 return TRUE;
3013 #endif
3015 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
3016 (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
3017 (method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
3018 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
3019 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
3020 (method->klass->marshalbyref) ||
3021 !header || header->num_clauses ||
3022 /* fixme: why cant we inline valuetype returns? */
3023 MONO_TYPE_ISSTRUCT (signature->ret))
3024 return FALSE;
3026 /* its not worth to inline methods with valuetype arguments?? */
3027 for (i = 0; i < signature->param_count; i++) {
3028 if (MONO_TYPE_ISSTRUCT (signature->params [i])) {
3029 return FALSE;
3034 * if we can initialize the class of the method right away, we do,
3035 * otherwise we don't allow inlining if the class needs initialization,
3036 * since it would mean inserting a call to mono_runtime_class_init()
3037 * inside the inlined code
3039 if (!(cfg->opt & MONO_OPT_SHARED)) {
3040 vtable = mono_class_vtable (cfg->domain, method->klass);
3041 if (method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
3042 if (cfg->run_cctors) {
3043 /* This makes so that inline cannot trigger */
3044 /* .cctors: too many apps depend on them */
3045 /* running with a specific order... */
3046 if (! vtable->initialized)
3047 return FALSE;
3048 mono_runtime_class_init (vtable);
3051 else if (!vtable->initialized && mono_class_needs_cctor_run (method->klass, NULL))
3052 return FALSE;
3053 } else {
3055 * If we're compiling for shared code
3056 * the cctor will need to be run at aot method load time, for example,
3057 * or at the end of the compilation of the inlining method.
3059 if (mono_class_needs_cctor_run (method->klass, NULL) && !((method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)))
3060 return FALSE;
3062 //if (!MONO_TYPE_IS_VOID (signature->ret)) return FALSE;
3065 * CAS - do not inline methods with declarative security
3066 * Note: this has to be before any possible return TRUE;
3068 if (mono_method_has_declsec (method))
3069 return FALSE;
3071 /* also consider num_locals? */
3072 if (getenv ("MONO_INLINELIMIT")) {
3073 if (header->code_size < atoi (getenv ("MONO_INLINELIMIT"))) {
3074 return TRUE;
3076 } else if (header->code_size < INLINE_LENGTH_LIMIT)
3077 return TRUE;
3079 return FALSE;
3082 static gboolean
3083 mini_field_access_needs_cctor_run (MonoCompile *cfg, MonoMethod *method, MonoVTable *vtable)
3085 if (vtable->initialized && !cfg->compile_aot)
3086 return FALSE;
3088 if (vtable->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)
3089 return FALSE;
3091 if (!mono_class_needs_cctor_run (vtable->klass, method))
3092 return FALSE;
3094 if (! (method->flags & METHOD_ATTRIBUTE_STATIC) && (vtable->klass == method->klass))
3095 /* The initialization is already done before the method is called */
3096 return FALSE;
3098 return TRUE;
3101 static MonoInst*
3102 mini_get_ldelema_ins (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *cmethod, MonoInst **sp, unsigned char *ip, gboolean is_set)
3104 int temp, rank;
3105 MonoInst *addr;
3106 MonoMethodSignature *esig;
3107 char icall_name [256];
3108 char *name;
3109 MonoJitICallInfo *info;
3111 rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0);
3113 if (rank == 1) {
3114 MONO_INST_NEW (cfg, addr, CEE_LDELEMA);
3115 addr->inst_left = sp [0];
3116 addr->inst_right = sp [1];
3117 addr->cil_code = ip;
3118 addr->type = STACK_MP;
3119 addr->klass = cmethod->klass->element_class;
3120 return addr;
3123 if (rank == 2 && (cfg->opt & MONO_OPT_INTRINS)) {
3124 #if defined(MONO_ARCH_EMULATE_MUL_DIV) && !defined(MONO_ARCH_NO_EMULATE_MUL)
3125 /* OP_LDELEMA2D depends on OP_LMUL */
3126 #else
3127 MonoInst *indexes;
3128 NEW_GROUP (cfg, indexes, sp [1], sp [2]);
3129 MONO_INST_NEW (cfg, addr, OP_LDELEMA2D);
3130 addr->inst_left = sp [0];
3131 addr->inst_right = indexes;
3132 addr->cil_code = ip;
3133 addr->type = STACK_MP;
3134 addr->klass = cmethod->klass->element_class;
3135 return addr;
3136 #endif
3139 /* Need to register the icall so it gets an icall wrapper */
3140 sprintf (icall_name, "ves_array_element_address_%d", rank);
3142 mono_jit_lock ();
3143 info = mono_find_jit_icall_by_name (icall_name);
3144 if (info == NULL) {
3145 esig = mono_get_element_address_signature (rank);
3146 name = g_strdup (icall_name);
3147 info = mono_register_jit_icall (ves_array_element_address, name, esig, FALSE);
3149 g_hash_table_insert (jit_icall_name_hash, name, name);
3151 mono_jit_unlock ();
3153 /* FIXME: This uses info->sig, but it should use the signature of the wrapper */
3154 temp = mono_emit_native_call (cfg, bblock, mono_icall_get_wrapper (info), info->sig, sp, ip, FALSE, FALSE);
3155 cfg->flags |= MONO_CFG_HAS_VARARGS;
3157 NEW_TEMPLOAD (cfg, addr, temp);
3158 return addr;
3161 static MonoJitICallInfo **emul_opcode_map = NULL;
3163 MonoJitICallInfo *
3164 mono_find_jit_opcode_emulation (int opcode)
3166 g_assert (opcode >= 0 && opcode <= OP_LAST);
3167 if (emul_opcode_map)
3168 return emul_opcode_map [opcode];
3169 else
3170 return NULL;
3173 static MonoInst*
3174 mini_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
3176 MonoInst *ins = NULL;
3178 static MonoClass *runtime_helpers_class = NULL;
3179 if (! runtime_helpers_class)
3180 runtime_helpers_class = mono_class_from_name (mono_defaults.corlib,
3181 "System.Runtime.CompilerServices", "RuntimeHelpers");
3183 if (cmethod->klass == mono_defaults.string_class) {
3184 if (strcmp (cmethod->name, "get_Chars") == 0) {
3185 MONO_INST_NEW (cfg, ins, OP_GETCHR);
3186 ins->inst_i0 = args [0];
3187 ins->inst_i1 = args [1];
3188 return ins;
3189 } else if (strcmp (cmethod->name, "get_Length") == 0) {
3190 MONO_INST_NEW (cfg, ins, OP_STRLEN);
3191 ins->inst_i0 = args [0];
3192 return ins;
3193 } else if (strcmp (cmethod->name, "InternalSetChar") == 0) {
3194 MonoInst *get_addr;
3195 MONO_INST_NEW (cfg, get_addr, OP_STR_CHAR_ADDR);
3196 get_addr->inst_i0 = args [0];
3197 get_addr->inst_i1 = args [1];
3198 MONO_INST_NEW (cfg, ins, CEE_STIND_I2);
3199 ins->inst_i0 = get_addr;
3200 ins->inst_i1 = args [2];
3201 return ins;
3202 } else
3203 return NULL;
3204 } else if (cmethod->klass == mono_defaults.object_class) {
3205 if (strcmp (cmethod->name, "GetType") == 0) {
3206 MONO_INST_NEW (cfg, ins, OP_GETTYPE);
3207 ins->inst_i0 = args [0];
3208 return ins;
3209 /* The OP_GETHASHCODE rule depends on OP_MUL */
3210 #if !defined(MONO_ARCH_EMULATE_MUL_DIV) && !defined(HAVE_MOVING_COLLECTOR)
3211 } else if (strcmp (cmethod->name, "InternalGetHashCode") == 0) {
3212 MONO_INST_NEW (cfg, ins, OP_GETHASHCODE);
3213 ins->inst_i0 = args [0];
3214 return ins;
3215 #endif
3216 } else if (strcmp (cmethod->name, ".ctor") == 0) {
3217 MONO_INST_NEW (cfg, ins, CEE_NOP);
3218 return ins;
3219 } else
3220 return NULL;
3221 } else if (cmethod->klass == mono_defaults.array_class) {
3222 if (cmethod->name [0] != 'g')
3223 return NULL;
3225 if (strcmp (cmethod->name, "get_Rank") == 0) {
3226 MONO_INST_NEW (cfg, ins, OP_ARRAY_RANK);
3227 ins->inst_i0 = args [0];
3228 return ins;
3229 } else if (strcmp (cmethod->name, "get_Length") == 0) {
3230 MONO_INST_NEW (cfg, ins, CEE_LDLEN);
3231 ins->inst_i0 = args [0];
3232 return ins;
3233 } else
3234 return NULL;
3235 } else if (cmethod->klass == runtime_helpers_class) {
3236 if (strcmp (cmethod->name, "get_OffsetToStringData") == 0) {
3237 NEW_ICONST (cfg, ins, G_STRUCT_OFFSET (MonoString, chars));
3238 return ins;
3239 } else
3240 return NULL;
3241 } else if (cmethod->klass == mono_defaults.thread_class) {
3242 if (strcmp (cmethod->name, "get_CurrentThread") == 0 && (ins = mono_arch_get_thread_intrinsic (cfg)))
3243 return ins;
3244 } else if (mini_class_is_system_array (cmethod->klass) &&
3245 strcmp (cmethod->name, "GetGenericValueImpl") == 0) {
3246 MonoInst *sp [2];
3247 MonoInst *ldelem, *store, *load;
3248 MonoClass *eklass = mono_class_from_mono_type (fsig->params [1]);
3249 int n;
3250 n = mono_type_to_stind (&eklass->byval_arg);
3251 if (n == CEE_STOBJ)
3252 return NULL;
3253 sp [0] = args [0];
3254 sp [1] = args [1];
3255 NEW_LDELEMA (cfg, ldelem, sp, eklass);
3256 ldelem->flags |= MONO_INST_NORANGECHECK;
3257 MONO_INST_NEW (cfg, store, n);
3258 n = mono_type_to_ldind (&eklass->byval_arg);
3259 MONO_INST_NEW (cfg, load, mono_type_to_ldind (&eklass->byval_arg));
3260 type_to_eval_stack_type (&eklass->byval_arg, load);
3261 load->inst_left = ldelem;
3262 store->inst_left = args [2];
3263 store->inst_right = load;
3264 return store;
3267 return mono_arch_get_inst_for_method (cfg, cmethod, fsig, args);
3270 static void
3271 mono_save_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *sig, MonoInst **sp, MonoInst **args)
3273 MonoInst *store, *temp;
3274 int i;
3276 g_assert (!MONO_TYPE_ISSTRUCT (sig->ret));
3278 if (!sig->hasthis && sig->param_count == 0)
3279 return;
3281 if (sig->hasthis) {
3282 if (sp [0]->opcode == OP_ICONST) {
3283 *args++ = sp [0];
3284 } else {
3285 temp = mono_compile_create_var (cfg, type_from_stack_type (*sp), OP_LOCAL);
3286 *args++ = temp;
3287 NEW_TEMPSTORE (cfg, store, temp->inst_c0, *sp);
3288 store->cil_code = sp [0]->cil_code;
3289 MONO_ADD_INS (bblock, store);
3291 sp++;
3294 for (i = 0; i < sig->param_count; ++i) {
3295 if (sp [0]->opcode == OP_ICONST) {
3296 *args++ = sp [0];
3297 } else {
3298 temp = mono_compile_create_var (cfg, sig->params [i], OP_LOCAL);
3299 *args++ = temp;
3300 NEW_TEMPSTORE (cfg, store, temp->inst_c0, *sp);
3301 store->cil_code = sp [0]->cil_code;
3302 if (store->opcode == CEE_STOBJ) {
3303 NEW_TEMPLOADA (cfg, store, temp->inst_c0);
3304 handle_stobj (cfg, bblock, store, *sp, sp [0]->cil_code, temp->klass, FALSE, FALSE, FALSE);
3305 } else {
3306 MONO_ADD_INS (bblock, store);
3309 sp++;
3312 #define MONO_INLINE_CALLED_LIMITED_METHODS 0
3313 #define MONO_INLINE_CALLER_LIMITED_METHODS 0
3315 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
3316 static char*
3317 mono_inline_called_method_name_limit = NULL;
3318 static gboolean check_inline_called_method_name_limit (MonoMethod *called_method) {
3319 char *called_method_name = mono_method_full_name (called_method, TRUE);
3320 int strncmp_result;
3322 if (mono_inline_called_method_name_limit == NULL) {
3323 char *limit_string = getenv ("MONO_INLINE_CALLED_METHOD_NAME_LIMIT");
3324 if (limit_string != NULL) {
3325 mono_inline_called_method_name_limit = limit_string;
3326 } else {
3327 mono_inline_called_method_name_limit = (char *) "";
3331 strncmp_result = strncmp (called_method_name, mono_inline_called_method_name_limit, strlen (mono_inline_called_method_name_limit));
3332 g_free (called_method_name);
3334 //return (strncmp_result <= 0);
3335 return (strncmp_result == 0);
3337 #endif
3339 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
3340 static char*
3341 mono_inline_caller_method_name_limit = NULL;
3342 static gboolean check_inline_caller_method_name_limit (MonoMethod *caller_method) {
3343 char *caller_method_name = mono_method_full_name (caller_method, TRUE);
3344 int strncmp_result;
3346 if (mono_inline_caller_method_name_limit == NULL) {
3347 char *limit_string = getenv ("MONO_INLINE_CALLER_METHOD_NAME_LIMIT");
3348 if (limit_string != NULL) {
3349 mono_inline_caller_method_name_limit = limit_string;
3350 } else {
3351 mono_inline_caller_method_name_limit = (char *) "";
3355 strncmp_result = strncmp (caller_method_name, mono_inline_caller_method_name_limit, strlen (mono_inline_caller_method_name_limit));
3356 g_free (caller_method_name);
3358 //return (strncmp_result <= 0);
3359 return (strncmp_result == 0);
3361 #endif
3363 static int
3364 inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoBasicBlock *bblock, MonoInst **sp,
3365 guchar *ip, guint real_offset, GList *dont_inline, MonoBasicBlock **last_b, gboolean inline_allways)
3367 MonoInst *ins, *rvar = NULL;
3368 MonoMethodHeader *cheader;
3369 MonoBasicBlock *ebblock, *sbblock;
3370 int i, costs, new_locals_offset;
3371 MonoMethod *prev_inlined_method;
3372 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
3373 if ((! inline_allways) && ! check_inline_called_method_name_limit (cmethod))
3374 return 0;
3375 #endif
3376 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
3377 if ((! inline_allways) && ! check_inline_caller_method_name_limit (cfg->method))
3378 return 0;
3379 #endif
3381 if (bblock->out_of_line && !inline_allways)
3382 return 0;
3384 if (cfg->verbose_level > 2)
3385 g_print ("INLINE START %p %s -> %s\n", cmethod, mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
3387 if (!cmethod->inline_info) {
3388 mono_jit_stats.inlineable_methods++;
3389 cmethod->inline_info = 1;
3391 /* allocate space to store the return value */
3392 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
3393 rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
3396 /* allocate local variables */
3397 cheader = mono_method_get_header (cmethod);
3398 new_locals_offset = cfg->num_varinfo;
3399 for (i = 0; i < cheader->num_locals; ++i)
3400 mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL);
3402 /* allocate starte and end blocks */
3403 sbblock = NEW_BBLOCK (cfg);
3404 sbblock->block_num = cfg->num_bblocks++;
3405 sbblock->real_offset = real_offset;
3407 ebblock = NEW_BBLOCK (cfg);
3408 ebblock->block_num = cfg->num_bblocks++;
3409 ebblock->real_offset = real_offset;
3411 prev_inlined_method = cfg->inlined_method;
3412 cfg->inlined_method = cmethod;
3414 costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, new_locals_offset, rvar, dont_inline, sp, real_offset, *ip == CEE_CALLVIRT);
3416 cfg->inlined_method = prev_inlined_method;
3418 if ((costs >= 0 && costs < 60) || inline_allways) {
3419 if (cfg->verbose_level > 2)
3420 g_print ("INLINE END %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
3422 mono_jit_stats.inlined_methods++;
3424 /* always add some code to avoid block split failures */
3425 MONO_INST_NEW (cfg, ins, CEE_NOP);
3426 MONO_ADD_INS (bblock, ins);
3427 ins->cil_code = ip;
3429 bblock->next_bb = sbblock;
3430 link_bblock (cfg, bblock, sbblock);
3432 if (rvar) {
3433 NEW_TEMPLOAD (cfg, ins, rvar->inst_c0);
3434 *sp++ = ins;
3436 *last_b = ebblock;
3437 return costs + 1;
3438 } else {
3439 if (cfg->verbose_level > 2)
3440 g_print ("INLINE ABORTED %s\n", mono_method_full_name (cmethod, TRUE));
3442 return 0;
3446 * Some of these comments may well be out-of-date.
3447 * Design decisions: we do a single pass over the IL code (and we do bblock
3448 * splitting/merging in the few cases when it's required: a back jump to an IL
3449 * address that was not already seen as bblock starting point).
3450 * Code is validated as we go (full verification is still better left to metadata/verify.c).
3451 * Complex operations are decomposed in simpler ones right away. We need to let the
3452 * arch-specific code peek and poke inside this process somehow (except when the
3453 * optimizations can take advantage of the full semantic info of coarse opcodes).
3454 * All the opcodes of the form opcode.s are 'normalized' to opcode.
3455 * MonoInst->opcode initially is the IL opcode or some simplification of that
3456 * (OP_LOAD, OP_STORE). The arch-specific code may rearrange it to an arch-specific
3457 * opcode with value bigger than OP_LAST.
3458 * At this point the IR can be handed over to an interpreter, a dumb code generator
3459 * or to the optimizing code generator that will translate it to SSA form.
3461 * Profiling directed optimizations.
3462 * We may compile by default with few or no optimizations and instrument the code
3463 * or the user may indicate what methods to optimize the most either in a config file
3464 * or through repeated runs where the compiler applies offline the optimizations to
3465 * each method and then decides if it was worth it.
3467 * TODO:
3468 * * consider using an array instead of an hash table (bb_hash)
3471 #define CHECK_TYPE(ins) if (!(ins)->type) UNVERIFIED
3472 #define CHECK_STACK(num) if ((sp - stack_start) < (num)) UNVERIFIED
3473 #define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) UNVERIFIED
3474 #define CHECK_ARG(num) if ((unsigned)(num) >= (unsigned)num_args) UNVERIFIED
3475 #define CHECK_LOCAL(num) if ((unsigned)(num) >= (unsigned)header->num_locals) UNVERIFIED
3476 #define CHECK_OPSIZE(size) if (ip + size > end) UNVERIFIED
3477 #define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
3478 #define CHECK_TYPELOAD(klass) if (!(klass) || (klass)->exception_type) {cfg->exception_ptr = klass; goto load_error;}
3480 /* offset from br.s -> br like opcodes */
3481 #define BIG_BRANCH_OFFSET 13
3483 static gboolean
3484 ip_in_bb (MonoCompile *cfg, MonoBasicBlock *bb, const guint8* ip)
3486 MonoBasicBlock *b = g_hash_table_lookup (cfg->bb_hash, ip);
3488 return b == NULL || b == bb;
3491 static int
3492 get_basic_blocks (MonoCompile *cfg, GHashTable *bbhash, MonoMethodHeader* header, guint real_offset, unsigned char *start, unsigned char *end, unsigned char **pos)
3494 unsigned char *ip = start;
3495 unsigned char *target;
3496 int i;
3497 guint cli_addr;
3498 MonoBasicBlock *bblock;
3499 const MonoOpcode *opcode;
3501 while (ip < end) {
3502 cli_addr = ip - start;
3503 i = mono_opcode_value ((const guint8 **)&ip, end);
3504 if (i < 0)
3505 UNVERIFIED;
3506 opcode = &mono_opcodes [i];
3507 switch (opcode->argument) {
3508 case MonoInlineNone:
3509 ip++;
3510 break;
3511 case MonoInlineString:
3512 case MonoInlineType:
3513 case MonoInlineField:
3514 case MonoInlineMethod:
3515 case MonoInlineTok:
3516 case MonoInlineSig:
3517 case MonoShortInlineR:
3518 case MonoInlineI:
3519 ip += 5;
3520 break;
3521 case MonoInlineVar:
3522 ip += 3;
3523 break;
3524 case MonoShortInlineVar:
3525 case MonoShortInlineI:
3526 ip += 2;
3527 break;
3528 case MonoShortInlineBrTarget:
3529 target = start + cli_addr + 2 + (signed char)ip [1];
3530 GET_BBLOCK (cfg, bbhash, bblock, target);
3531 ip += 2;
3532 if (ip < end)
3533 GET_BBLOCK (cfg, bbhash, bblock, ip);
3534 break;
3535 case MonoInlineBrTarget:
3536 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
3537 GET_BBLOCK (cfg, bbhash, bblock, target);
3538 ip += 5;
3539 if (ip < end)
3540 GET_BBLOCK (cfg, bbhash, bblock, ip);
3541 break;
3542 case MonoInlineSwitch: {
3543 guint32 n = read32 (ip + 1);
3544 guint32 j;
3545 ip += 5;
3546 cli_addr += 5 + 4 * n;
3547 target = start + cli_addr;
3548 GET_BBLOCK (cfg, bbhash, bblock, target);
3550 for (j = 0; j < n; ++j) {
3551 target = start + cli_addr + (gint32)read32 (ip);
3552 GET_BBLOCK (cfg, bbhash, bblock, target);
3553 ip += 4;
3555 break;
3557 case MonoInlineR:
3558 case MonoInlineI8:
3559 ip += 9;
3560 break;
3561 default:
3562 g_assert_not_reached ();
3565 if (i == CEE_THROW) {
3566 unsigned char *bb_start = ip - 1;
3568 /* Find the start of the bblock containing the throw */
3569 bblock = NULL;
3570 while ((bb_start >= start) && !bblock) {
3571 bblock = g_hash_table_lookup (bbhash, (bb_start));
3572 bb_start --;
3574 if (bblock)
3575 bblock->out_of_line = 1;
3578 return 0;
3579 unverified:
3580 *pos = ip;
3581 return 1;
3584 static MonoInst*
3585 emit_tree (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *ins, const guint8* ip_next)
3587 MonoInst *store, *temp, *load;
3589 if (ip_in_bb (cfg, bblock, ip_next) &&
3590 (CODE_IS_STLOC (ip_next) || *ip_next == CEE_RET))
3591 return ins;
3593 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
3594 temp->flags |= MONO_INST_IS_TEMP;
3595 NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
3596 store->cil_code = ins->cil_code;
3597 MONO_ADD_INS (bblock, store);
3598 NEW_TEMPLOAD (cfg, load, temp->inst_c0);
3599 load->cil_code = ins->cil_code;
3600 return load;
3603 static inline MonoMethod *
3604 mini_get_method (MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
3606 MonoMethod *method;
3608 if (m->wrapper_type != MONO_WRAPPER_NONE)
3609 return mono_method_get_wrapper_data (m, token);
3611 method = mono_get_method_full (m->klass->image, token, klass, context);
3613 if (method && method->is_inflated)
3614 method = mono_get_inflated_method (method);
3616 return method;
3619 static inline MonoClass*
3620 mini_get_class (MonoMethod *method, guint32 token, MonoGenericContext *context)
3622 MonoClass *klass;
3624 if (method->wrapper_type != MONO_WRAPPER_NONE)
3625 klass = mono_method_get_wrapper_data (method, token);
3626 else
3627 klass = mono_class_get_full (method->klass->image, token, context);
3628 if (klass)
3629 mono_class_init (klass);
3630 return klass;
3634 * Returns TRUE if the JIT should abort inlining because "callee"
3635 * is influenced by security attributes.
3637 static
3638 gboolean check_linkdemand (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee, MonoBasicBlock *bblock, unsigned char *ip)
3640 guint32 result;
3642 if ((cfg->method != caller) && mono_method_has_declsec (callee)) {
3643 return TRUE;
3646 result = mono_declsec_linkdemand (cfg->domain, caller, callee);
3647 if (result == MONO_JIT_SECURITY_OK)
3648 return FALSE;
3650 if (result == MONO_JIT_LINKDEMAND_ECMA) {
3651 /* Generate code to throw a SecurityException before the actual call/link */
3652 MonoAssembly *assembly = mono_image_get_assembly (caller->klass->image);
3653 MonoReflectionAssembly *refass = (MonoReflectionAssembly*) mono_assembly_get_object (cfg->domain, assembly);
3654 MonoReflectionMethod *refmet = mono_method_get_object (cfg->domain, caller, NULL);
3655 MonoSecurityManager *secman = mono_security_manager_get_methods ();
3656 MonoInst *args [3];
3658 NEW_ICONST (cfg, args [0], 4);
3659 NEW_PCONST (cfg, args [1], refass);
3660 NEW_PCONST (cfg, args [2], refmet);
3661 mono_emit_method_call_spilled (cfg, bblock, secman->linkdemandsecurityexception, mono_method_signature (secman->linkdemandsecurityexception), args, ip, NULL);
3662 } else if (cfg->exception_type == MONO_EXCEPTION_NONE) {
3663 /* don't hide previous results */
3664 cfg->exception_type = MONO_EXCEPTION_SECURITY_LINKDEMAND;
3665 cfg->exception_data = result;
3668 return FALSE;
3671 static gboolean
3672 can_access_internals (MonoAssembly *accessing, MonoAssembly* accessed)
3674 GSList *tmp;
3675 if (accessing == accessed)
3676 return TRUE;
3677 if (!accessed || !accessing)
3678 return FALSE;
3679 for (tmp = accessed->friend_assembly_names; tmp; tmp = tmp->next) {
3680 MonoAssemblyName *friend = tmp->data;
3681 /* Be conservative with checks */
3682 if (!friend->name)
3683 continue;
3684 if (strcmp (accessing->aname.name, friend->name))
3685 continue;
3686 if (friend->public_key_token [0]) {
3687 if (!accessing->aname.public_key_token [0])
3688 continue;
3689 if (strcmp ((char*)friend->public_key_token, (char*)accessing->aname.public_key_token))
3690 continue;
3692 return TRUE;
3694 return FALSE;
3697 /* FIXME: check visibility of type, too */
3698 static gboolean
3699 can_access_member (MonoClass *access_klass, MonoClass *member_klass, int access_level)
3701 /* Partition I 8.5.3.2 */
3702 /* the access level values are the same for fields and methods */
3703 switch (access_level) {
3704 case FIELD_ATTRIBUTE_COMPILER_CONTROLLED:
3705 /* same compilation unit */
3706 return access_klass->image == member_klass->image;
3707 case FIELD_ATTRIBUTE_PRIVATE:
3708 return access_klass == member_klass;
3709 case FIELD_ATTRIBUTE_FAM_AND_ASSEM:
3710 if (mono_class_has_parent (access_klass, member_klass) &&
3711 can_access_internals (access_klass->image->assembly, member_klass->image->assembly))
3712 return TRUE;
3713 return FALSE;
3714 case FIELD_ATTRIBUTE_ASSEMBLY:
3715 return can_access_internals (access_klass->image->assembly, member_klass->image->assembly);
3716 case FIELD_ATTRIBUTE_FAMILY:
3717 if (mono_class_has_parent (access_klass, member_klass))
3718 return TRUE;
3719 return FALSE;
3720 case FIELD_ATTRIBUTE_FAM_OR_ASSEM:
3721 if (mono_class_has_parent (access_klass, member_klass))
3722 return TRUE;
3723 return can_access_internals (access_klass->image->assembly, member_klass->image->assembly);
3724 case FIELD_ATTRIBUTE_PUBLIC:
3725 return TRUE;
3727 return FALSE;
3730 static gboolean
3731 can_access_field (MonoMethod *method, MonoClassField *field)
3733 /* FIXME: check all overlapping fields */
3734 int can = can_access_member (method->klass, field->parent, field->type->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK);
3735 if (!can) {
3736 MonoClass *nested = method->klass->nested_in;
3737 while (nested) {
3738 can = can_access_member (nested, field->parent, field->type->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK);
3739 if (can)
3740 return TRUE;
3741 nested = nested->nested_in;
3744 return can;
3747 static gboolean
3748 can_access_method (MonoMethod *method, MonoMethod *called)
3750 int can = can_access_member (method->klass, called->klass, called->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK);
3751 if (!can) {
3752 MonoClass *nested = method->klass->nested_in;
3753 while (nested) {
3754 can = can_access_member (nested, called->klass, called->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK);
3755 if (can)
3756 return TRUE;
3757 nested = nested->nested_in;
3761 * FIXME:
3762 * with generics calls to explicit interface implementations can be expressed
3763 * directly: the method is private, but we must allow it. This may be opening
3764 * a hole or the generics code should handle this differently.
3765 * Maybe just ensure the interface type is public.
3767 if ((called->flags & METHOD_ATTRIBUTE_VIRTUAL) && (called->flags & METHOD_ATTRIBUTE_FINAL))
3768 return TRUE;
3769 return can;
3773 * mono_method_to_ir: translates IL into basic blocks containing trees
3775 static int
3776 mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
3777 int locals_offset, MonoInst *return_var, GList *dont_inline, MonoInst **inline_args,
3778 guint inline_offset, gboolean is_virtual_call)
3780 MonoInst *zero_int32, *zero_int64, *zero_ptr, *zero_obj, *zero_r8;
3781 MonoInst *ins, **sp, **stack_start;
3782 MonoBasicBlock *bblock, *tblock = NULL, *init_localsbb = NULL;
3783 GHashTable *bbhash;
3784 MonoMethod *cmethod;
3785 MonoInst **arg_array;
3786 MonoMethodHeader *header;
3787 MonoImage *image;
3788 guint32 token, ins_flag;
3789 MonoClass *klass;
3790 MonoClass *constrained_call = NULL;
3791 unsigned char *ip, *end, *target, *err_pos;
3792 static double r8_0 = 0.0;
3793 MonoMethodSignature *sig;
3794 MonoGenericContext *generic_context = NULL;
3795 MonoGenericContainer *generic_container = NULL;
3796 MonoType **param_types;
3797 GList *bb_recheck = NULL, *tmp;
3798 int i, n, start_new_bblock, ialign;
3799 int num_calls = 0, inline_costs = 0;
3800 int breakpoint_id = 0;
3801 guint32 align;
3802 guint real_offset, num_args;
3803 MonoBoolean security, pinvoke;
3804 MonoSecurityManager* secman = NULL;
3805 MonoDeclSecurityActions actions;
3806 GSList *class_inits = NULL;
3807 gboolean dont_verify, dont_verify_stloc;
3809 /* serialization and xdomain stuff may need access to private fields and methods */
3810 dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
3811 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE;
3812 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH;
3813 dont_verify |= method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE; /* bug #77896 */
3814 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP;
3815 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP_INVOKE;
3817 /* still some type unsafety issues in marshal wrappers... (unknown is PtrToStructure) */
3818 dont_verify_stloc = method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE;
3819 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_UNKNOWN;
3820 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED;
3822 /* Not turned on yet */
3823 cfg->dont_verify_stack_merge = TRUE;
3825 image = method->klass->image;
3826 header = mono_method_get_header (method);
3827 generic_container = method->generic_container;
3828 sig = mono_method_signature (method);
3829 num_args = sig->hasthis + sig->param_count;
3830 ip = (unsigned char*)header->code;
3831 end = ip + header->code_size;
3832 mono_jit_stats.cil_code_size += header->code_size;
3834 if (sig->is_inflated)
3835 generic_context = ((MonoMethodInflated *) method)->context;
3836 else if (generic_container)
3837 generic_context = &generic_container->context;
3839 g_assert (!sig->has_type_parameters);
3841 if (cfg->method == method) {
3842 real_offset = 0;
3843 bbhash = cfg->bb_hash;
3844 } else {
3845 real_offset = inline_offset;
3846 bbhash = g_hash_table_new (g_direct_hash, NULL);
3849 if (cfg->verbose_level > 2)
3850 g_print ("method to IR %s\n", mono_method_full_name (method, TRUE));
3852 dont_inline = g_list_prepend (dont_inline, method);
3853 if (cfg->method == method) {
3855 if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
3856 cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
3858 /* ENTRY BLOCK */
3859 cfg->bb_entry = start_bblock = NEW_BBLOCK (cfg);
3860 start_bblock->cil_code = NULL;
3861 start_bblock->cil_length = 0;
3862 start_bblock->block_num = cfg->num_bblocks++;
3864 /* EXIT BLOCK */
3865 cfg->bb_exit = end_bblock = NEW_BBLOCK (cfg);
3866 end_bblock->cil_code = NULL;
3867 end_bblock->cil_length = 0;
3868 end_bblock->block_num = cfg->num_bblocks++;
3869 g_assert (cfg->num_bblocks == 2);
3871 arg_array = alloca (sizeof (MonoInst *) * num_args);
3872 for (i = num_args - 1; i >= 0; i--)
3873 arg_array [i] = cfg->varinfo [i];
3875 if (header->num_clauses) {
3876 cfg->spvars = g_hash_table_new (NULL, NULL);
3877 cfg->exvars = g_hash_table_new (NULL, NULL);
3879 /* handle exception clauses */
3880 for (i = 0; i < header->num_clauses; ++i) {
3881 MonoBasicBlock *try_bb;
3882 MonoExceptionClause *clause = &header->clauses [i];
3883 GET_BBLOCK (cfg, bbhash, try_bb, ip + clause->try_offset);
3884 try_bb->real_offset = clause->try_offset;
3885 GET_BBLOCK (cfg, bbhash, tblock, ip + clause->handler_offset);
3886 tblock->real_offset = clause->handler_offset;
3887 tblock->flags |= BB_EXCEPTION_HANDLER;
3889 link_bblock (cfg, try_bb, tblock);
3891 if (*(ip + clause->handler_offset) == CEE_POP)
3892 tblock->flags |= BB_EXCEPTION_DEAD_OBJ;
3894 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
3895 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
3896 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
3897 MONO_ADD_INS (tblock, ins);
3899 /* todo: is a fault block unsafe to optimize? */
3900 if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
3901 tblock->flags |= BB_EXCEPTION_UNSAFE;
3905 /*g_print ("clause try IL_%04x to IL_%04x handler %d at IL_%04x to IL_%04x\n", clause->try_offset, clause->try_offset + clause->try_len, clause->flags, clause->handler_offset, clause->handler_offset + clause->handler_len);
3906 while (p < end) {
3907 g_print ("%s", mono_disasm_code_one (NULL, method, p, &p));
3909 /* catch and filter blocks get the exception object on the stack */
3910 if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE ||
3911 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
3912 MonoInst *load, *dummy_use;
3914 /* mostly like handle_stack_args (), but just sets the input args */
3915 /* g_print ("handling clause at IL_%04x\n", clause->handler_offset); */
3916 tblock->in_scount = 1;
3917 tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
3918 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
3919 tblock->stack_state = mono_mempool_alloc (cfg->mempool, sizeof (MonoStackSlot));
3920 tblock->stack_state [0].type = STACK_OBJ;
3921 /* FIXME? */
3922 tblock->stack_state [0].klass = mono_defaults.object_class;
3925 * Add a dummy use for the exvar so its liveness info will be
3926 * correct.
3928 NEW_TEMPLOAD (cfg, load, tblock->in_stack [0]->inst_c0);
3929 NEW_DUMMY_USE (cfg, dummy_use, load);
3930 MONO_ADD_INS (tblock, dummy_use);
3932 if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
3933 GET_BBLOCK (cfg, bbhash, tblock, ip + clause->data.filter_offset);
3934 tblock->real_offset = clause->data.filter_offset;
3935 tblock->in_scount = 1;
3936 tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
3937 tblock->stack_state = mono_mempool_alloc (cfg->mempool, sizeof (MonoStackSlot));
3938 tblock->stack_state [0].type = STACK_OBJ;
3939 /* FIXME? */
3940 tblock->stack_state [0].klass = mono_defaults.object_class;
3942 /* The filter block shares the exvar with the handler block */
3943 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
3944 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
3945 MONO_ADD_INS (tblock, ins);
3949 } else {
3950 arg_array = alloca (sizeof (MonoInst *) * num_args);
3951 mono_save_args (cfg, start_bblock, sig, inline_args, arg_array);
3954 /* FIRST CODE BLOCK */
3955 bblock = NEW_BBLOCK (cfg);
3956 bblock->cil_code = ip;
3958 ADD_BBLOCK (cfg, bbhash, bblock);
3960 if (cfg->method == method) {
3961 breakpoint_id = mono_debugger_method_has_breakpoint (method);
3962 if (breakpoint_id && (mono_debug_format != MONO_DEBUG_FORMAT_DEBUGGER)) {
3963 MONO_INST_NEW (cfg, ins, CEE_BREAK);
3964 MONO_ADD_INS (bblock, ins);
3968 if (mono_use_security_manager)
3969 secman = mono_security_manager_get_methods ();
3971 security = (secman && mono_method_has_declsec (method));
3972 /* at this point having security doesn't mean we have any code to generate */
3973 if (security && (cfg->method == method)) {
3974 /* Only Demand, NonCasDemand and DemandChoice requires code generation.
3975 * And we do not want to enter the next section (with allocation) if we
3976 * have nothing to generate */
3977 security = mono_declsec_get_demands (method, &actions);
3980 /* we must Demand SecurityPermission.Unmanaged before P/Invoking */
3981 pinvoke = (secman && (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE));
3982 if (pinvoke) {
3983 MonoMethod *wrapped = mono_marshal_method_from_wrapper (method);
3984 if (wrapped && (wrapped->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
3985 MonoCustomAttrInfo* custom = mono_custom_attrs_from_method (wrapped);
3987 /* unless the method or it's class has the [SuppressUnmanagedCodeSecurity] attribute */
3988 if (custom && mono_custom_attrs_has_attr (custom, secman->suppressunmanagedcodesecurity)) {
3989 pinvoke = FALSE;
3992 if (pinvoke) {
3993 custom = mono_custom_attrs_from_class (wrapped->klass);
3994 if (custom && mono_custom_attrs_has_attr (custom, secman->suppressunmanagedcodesecurity)) {
3995 pinvoke = FALSE;
3998 } else {
3999 /* not a P/Invoke after all */
4000 pinvoke = FALSE;
4004 if ((header->init_locals || (cfg->method == method && (cfg->opt & MONO_OPT_SHARED))) || cfg->compile_aot || security || pinvoke) {
4005 /* we use a separate basic block for the initialization code */
4006 cfg->bb_init = init_localsbb = NEW_BBLOCK (cfg);
4007 init_localsbb->real_offset = real_offset;
4008 start_bblock->next_bb = init_localsbb;
4009 init_localsbb->next_bb = bblock;
4010 link_bblock (cfg, start_bblock, init_localsbb);
4011 link_bblock (cfg, init_localsbb, bblock);
4012 init_localsbb->block_num = cfg->num_bblocks++;
4013 } else {
4014 start_bblock->next_bb = bblock;
4015 link_bblock (cfg, start_bblock, bblock);
4018 /* at this point we know, if security is TRUE, that some code needs to be generated */
4019 if (security && (cfg->method == method)) {
4020 MonoInst *args [2];
4022 mono_jit_stats.cas_demand_generation++;
4024 if (actions.demand.blob) {
4025 /* Add code for SecurityAction.Demand */
4026 NEW_DECLSECCONST (cfg, args[0], image, actions.demand);
4027 NEW_ICONST (cfg, args [1], actions.demand.size);
4028 /* Calls static void SecurityManager.InternalDemand (byte* permissions, int size); */
4029 mono_emit_method_call_spilled (cfg, init_localsbb, secman->demand, mono_method_signature (secman->demand), args, ip, NULL);
4031 if (actions.noncasdemand.blob) {
4032 /* CLR 1.x uses a .noncasdemand (but 2.x doesn't) */
4033 /* For Mono we re-route non-CAS Demand to Demand (as the managed code must deal with it anyway) */
4034 NEW_DECLSECCONST (cfg, args[0], image, actions.noncasdemand);
4035 NEW_ICONST (cfg, args [1], actions.noncasdemand.size);
4036 /* Calls static void SecurityManager.InternalDemand (byte* permissions, int size); */
4037 mono_emit_method_call_spilled (cfg, init_localsbb, secman->demand, mono_method_signature (secman->demand), args, ip, NULL);
4039 if (actions.demandchoice.blob) {
4040 /* New in 2.0, Demand must succeed for one of the permissions (i.e. not all) */
4041 NEW_DECLSECCONST (cfg, args[0], image, actions.demandchoice);
4042 NEW_ICONST (cfg, args [1], actions.demandchoice.size);
4043 /* Calls static void SecurityManager.InternalDemandChoice (byte* permissions, int size); */
4044 mono_emit_method_call_spilled (cfg, init_localsbb, secman->demandchoice, mono_method_signature (secman->demandchoice), args, ip, NULL);
4048 /* we must Demand SecurityPermission.Unmanaged before p/invoking */
4049 if (pinvoke) {
4050 mono_emit_method_call_spilled (cfg, init_localsbb, secman->demandunmanaged, mono_method_signature (secman->demandunmanaged), NULL, ip, NULL);
4053 if (get_basic_blocks (cfg, bbhash, header, real_offset, ip, end, &err_pos)) {
4054 ip = err_pos;
4055 UNVERIFIED;
4058 if (cfg->method == method)
4059 mono_debug_init_method (cfg, bblock, breakpoint_id);
4061 param_types = mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * num_args);
4062 if (sig->hasthis)
4063 param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg;
4064 for (n = 0; n < sig->param_count; ++n)
4065 param_types [n + sig->hasthis] = sig->params [n];
4066 for (n = 0; n < header->num_locals; ++n) {
4067 if (header->locals [n]->type == MONO_TYPE_VOID && !header->locals [n]->byref)
4068 UNVERIFIED;
4070 class_inits = NULL;
4072 /* do this somewhere outside - not here */
4073 NEW_ICONST (cfg, zero_int32, 0);
4074 NEW_ICONST (cfg, zero_int64, 0);
4075 zero_int64->type = STACK_I8;
4076 NEW_PCONST (cfg, zero_ptr, 0);
4077 NEW_PCONST (cfg, zero_obj, 0);
4078 zero_obj->type = STACK_OBJ;
4080 MONO_INST_NEW (cfg, zero_r8, OP_R8CONST);
4081 zero_r8->type = STACK_R8;
4082 zero_r8->inst_p0 = &r8_0;
4084 /* add a check for this != NULL to inlined methods */
4085 if (is_virtual_call) {
4086 MONO_INST_NEW (cfg, ins, OP_CHECK_THIS);
4087 NEW_ARGLOAD (cfg, ins->inst_left, 0);
4088 ins->cil_code = ip;
4089 MONO_ADD_INS (bblock, ins);
4092 /* we use a spare stack slot in SWITCH and NEWOBJ and others */
4093 stack_start = sp = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1));
4095 ins_flag = 0;
4096 start_new_bblock = 0;
4097 while (ip < end) {
4099 if (cfg->method == method)
4100 real_offset = ip - header->code;
4101 else
4102 real_offset = inline_offset;
4104 if (start_new_bblock) {
4105 bblock->cil_length = ip - bblock->cil_code;
4106 if (start_new_bblock == 2) {
4107 g_assert (ip == tblock->cil_code);
4108 } else {
4109 GET_BBLOCK (cfg, bbhash, tblock, ip);
4111 bblock->next_bb = tblock;
4112 bblock = tblock;
4113 start_new_bblock = 0;
4114 for (i = 0; i < bblock->in_scount; ++i) {
4115 if (cfg->verbose_level > 3)
4116 g_print ("loading %d from temp %d\n", i, (int)bblock->in_stack [i]->inst_c0);
4117 NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0);
4118 *sp++ = ins;
4120 g_slist_free (class_inits);
4121 class_inits = NULL;
4122 } else {
4123 if ((tblock = g_hash_table_lookup (bbhash, ip)) && (tblock != bblock)) {
4124 link_bblock (cfg, bblock, tblock);
4125 if (sp != stack_start) {
4126 handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
4127 sp = stack_start;
4128 CHECK_UNVERIFIABLE (cfg);
4130 bblock->next_bb = tblock;
4131 bblock = tblock;
4132 for (i = 0; i < bblock->in_scount; ++i) {
4133 if (cfg->verbose_level > 3)
4134 g_print ("loading %d from temp %d\n", i, (int)bblock->in_stack [i]->inst_c0);
4135 NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0);
4136 *sp++ = ins;
4138 g_slist_free (class_inits);
4139 class_inits = NULL;
4143 bblock->real_offset = real_offset;
4145 if ((cfg->method == method) && cfg->coverage_info) {
4146 MonoInst *store, *one;
4147 guint32 cil_offset = ip - header->code;
4148 cfg->coverage_info->data [cil_offset].cil_code = ip;
4150 /* TODO: Use an increment here */
4151 NEW_ICONST (cfg, one, 1);
4152 one->cil_code = ip;
4154 NEW_PCONST (cfg, ins, &(cfg->coverage_info->data [cil_offset].count));
4155 ins->cil_code = ip;
4157 MONO_INST_NEW (cfg, store, CEE_STIND_I);
4158 store->cil_code = ip;
4159 store->inst_left = ins;
4160 store->inst_right = one;
4162 MONO_ADD_INS (bblock, store);
4165 if (cfg->verbose_level > 3)
4166 g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
4168 switch (*ip) {
4169 case CEE_NOP:
4170 case CEE_BREAK:
4171 MONO_INST_NEW (cfg, ins, *ip);
4172 ins->cil_code = ip++;
4173 MONO_ADD_INS (bblock, ins);
4174 break;
4175 case CEE_LDARG_0:
4176 case CEE_LDARG_1:
4177 case CEE_LDARG_2:
4178 case CEE_LDARG_3:
4179 CHECK_STACK_OVF (1);
4180 n = (*ip)-CEE_LDARG_0;
4181 CHECK_ARG (n);
4182 NEW_ARGLOAD (cfg, ins, n);
4183 LDARG_SOFT_FLOAT (cfg, ins, n, ip);
4184 ins->cil_code = ip++;
4185 *sp++ = ins;
4186 break;
4187 case CEE_LDLOC_0:
4188 case CEE_LDLOC_1:
4189 case CEE_LDLOC_2:
4190 case CEE_LDLOC_3:
4191 CHECK_STACK_OVF (1);
4192 n = (*ip)-CEE_LDLOC_0;
4193 CHECK_LOCAL (n);
4194 NEW_LOCLOAD (cfg, ins, n);
4195 LDLOC_SOFT_FLOAT (cfg, ins, n, ip);
4196 ins->cil_code = ip++;
4197 *sp++ = ins;
4198 break;
4199 case CEE_STLOC_0:
4200 case CEE_STLOC_1:
4201 case CEE_STLOC_2:
4202 case CEE_STLOC_3:
4203 CHECK_STACK (1);
4204 n = (*ip)-CEE_STLOC_0;
4205 CHECK_LOCAL (n);
4206 --sp;
4207 handle_loaded_temps (cfg, bblock, stack_start, sp);
4208 NEW_LOCSTORE (cfg, ins, n, *sp);
4209 ins->cil_code = ip;
4210 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
4211 UNVERIFIED;
4212 STLOC_SOFT_FLOAT (cfg, ins, n, ip);
4213 if (ins->opcode == CEE_STOBJ) {
4214 NEW_LOCLOADA (cfg, ins, n);
4215 handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
4216 } else
4217 MONO_ADD_INS (bblock, ins);
4218 ++ip;
4219 inline_costs += 1;
4220 break;
4221 case CEE_LDARG_S:
4222 CHECK_OPSIZE (2);
4223 CHECK_STACK_OVF (1);
4224 CHECK_ARG (ip [1]);
4225 NEW_ARGLOAD (cfg, ins, ip [1]);
4226 LDARG_SOFT_FLOAT (cfg, ins, ip [1], ip);
4227 ins->cil_code = ip;
4228 *sp++ = ins;
4229 ip += 2;
4230 break;
4231 case CEE_LDARGA_S:
4232 CHECK_OPSIZE (2);
4233 CHECK_STACK_OVF (1);
4234 CHECK_ARG (ip [1]);
4235 NEW_ARGLOADA (cfg, ins, ip [1]);
4236 ins->cil_code = ip;
4237 *sp++ = ins;
4238 ip += 2;
4239 break;
4240 case CEE_STARG_S:
4241 CHECK_OPSIZE (2);
4242 CHECK_STACK (1);
4243 --sp;
4244 CHECK_ARG (ip [1]);
4245 NEW_ARGSTORE (cfg, ins, ip [1], *sp);
4246 handle_loaded_temps (cfg, bblock, stack_start, sp);
4247 ins->cil_code = ip;
4248 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
4249 UNVERIFIED;
4250 if (ins->opcode == CEE_STOBJ) {
4251 NEW_ARGLOADA (cfg, ins, ip [1]);
4252 handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
4253 } else
4254 MONO_ADD_INS (bblock, ins);
4255 ip += 2;
4256 break;
4257 case CEE_LDLOC_S:
4258 CHECK_OPSIZE (2);
4259 CHECK_STACK_OVF (1);
4260 CHECK_LOCAL (ip [1]);
4261 NEW_LOCLOAD (cfg, ins, ip [1]);
4262 LDLOC_SOFT_FLOAT (cfg, ins, ip [1], ip);
4263 ins->cil_code = ip;
4264 *sp++ = ins;
4265 ip += 2;
4266 break;
4267 case CEE_LDLOCA_S:
4268 CHECK_OPSIZE (2);
4269 CHECK_STACK_OVF (1);
4270 CHECK_LOCAL (ip [1]);
4271 NEW_LOCLOADA (cfg, ins, ip [1]);
4272 ins->cil_code = ip;
4273 *sp++ = ins;
4274 ip += 2;
4275 break;
4276 case CEE_STLOC_S:
4277 CHECK_OPSIZE (2);
4278 CHECK_STACK (1);
4279 --sp;
4280 handle_loaded_temps (cfg, bblock, stack_start, sp);
4281 CHECK_LOCAL (ip [1]);
4282 NEW_LOCSTORE (cfg, ins, ip [1], *sp);
4283 ins->cil_code = ip;
4284 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [ip [1]], *sp))
4285 UNVERIFIED;
4286 STLOC_SOFT_FLOAT (cfg, ins, ip [1], ip);
4287 if (ins->opcode == CEE_STOBJ) {
4288 NEW_LOCLOADA (cfg, ins, ip [1]);
4289 handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
4290 } else
4291 MONO_ADD_INS (bblock, ins);
4292 ip += 2;
4293 inline_costs += 1;
4294 break;
4295 case CEE_LDNULL:
4296 CHECK_STACK_OVF (1);
4297 NEW_PCONST (cfg, ins, NULL);
4298 ins->cil_code = ip;
4299 ins->type = STACK_OBJ;
4300 ++ip;
4301 *sp++ = ins;
4302 break;
4303 case CEE_LDC_I4_M1:
4304 CHECK_STACK_OVF (1);
4305 NEW_ICONST (cfg, ins, -1);
4306 ins->cil_code = ip;
4307 ++ip;
4308 *sp++ = ins;
4309 break;
4310 case CEE_LDC_I4_0:
4311 case CEE_LDC_I4_1:
4312 case CEE_LDC_I4_2:
4313 case CEE_LDC_I4_3:
4314 case CEE_LDC_I4_4:
4315 case CEE_LDC_I4_5:
4316 case CEE_LDC_I4_6:
4317 case CEE_LDC_I4_7:
4318 case CEE_LDC_I4_8:
4319 CHECK_STACK_OVF (1);
4320 NEW_ICONST (cfg, ins, (*ip) - CEE_LDC_I4_0);
4321 ins->cil_code = ip;
4322 ++ip;
4323 *sp++ = ins;
4324 break;
4325 case CEE_LDC_I4_S:
4326 CHECK_OPSIZE (2);
4327 CHECK_STACK_OVF (1);
4328 ++ip;
4329 NEW_ICONST (cfg, ins, *((signed char*)ip));
4330 ins->cil_code = ip;
4331 ++ip;
4332 *sp++ = ins;
4333 break;
4334 case CEE_LDC_I4:
4335 CHECK_OPSIZE (5);
4336 CHECK_STACK_OVF (1);
4337 NEW_ICONST (cfg, ins, (gint32)read32 (ip + 1));
4338 ins->cil_code = ip;
4339 ip += 5;
4340 *sp++ = ins;
4341 break;
4342 case CEE_LDC_I8:
4343 CHECK_OPSIZE (9);
4344 CHECK_STACK_OVF (1);
4345 MONO_INST_NEW (cfg, ins, OP_I8CONST);
4346 ins->cil_code = ip;
4347 ins->type = STACK_I8;
4348 ++ip;
4349 ins->inst_l = (gint64)read64 (ip);
4350 ip += 8;
4351 *sp++ = ins;
4352 break;
4353 case CEE_LDC_R4: {
4354 float *f;
4355 /* we should really allocate this only late in the compilation process */
4356 mono_domain_lock (cfg->domain);
4357 f = mono_mempool_alloc (cfg->domain->mp, sizeof (float));
4358 mono_domain_unlock (cfg->domain);
4359 CHECK_OPSIZE (5);
4360 CHECK_STACK_OVF (1);
4361 MONO_INST_NEW (cfg, ins, OP_R4CONST);
4362 ins->type = STACK_R8;
4363 ++ip;
4364 readr4 (ip, f);
4365 ins->inst_p0 = f;
4367 ip += 4;
4368 *sp++ = ins;
4369 break;
4371 case CEE_LDC_R8: {
4372 double *d;
4373 mono_domain_lock (cfg->domain);
4374 d = mono_mempool_alloc (cfg->domain->mp, sizeof (double));
4375 mono_domain_unlock (cfg->domain);
4376 CHECK_OPSIZE (9);
4377 CHECK_STACK_OVF (1);
4378 MONO_INST_NEW (cfg, ins, OP_R8CONST);
4379 ins->type = STACK_R8;
4380 ++ip;
4381 readr8 (ip, d);
4382 ins->inst_p0 = d;
4384 ip += 8;
4385 *sp++ = ins;
4386 break;
4388 case CEE_DUP: {
4389 MonoInst *temp, *store;
4390 CHECK_STACK (1);
4391 CHECK_STACK_OVF (1);
4392 sp--;
4393 ins = *sp;
4396 * small optimization: if the loaded value was from a local already,
4397 * just load it twice.
4399 if (ins->ssa_op == MONO_SSA_LOAD &&
4400 (ins->inst_i0->opcode == OP_LOCAL || ins->inst_i0->opcode == OP_ARG)) {
4401 sp++;
4402 MONO_INST_NEW (cfg, temp, 0);
4403 *temp = *ins;
4404 temp->cil_code = ip;
4405 *sp++ = temp;
4406 } else {
4407 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
4408 temp->flags |= MONO_INST_IS_TEMP;
4409 temp->cil_code = ip;
4410 NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
4411 store->cil_code = ip;
4412 if (store->opcode == CEE_STOBJ) {
4413 NEW_TEMPLOADA (cfg, store, temp->inst_c0);
4414 handle_stobj (cfg, bblock, store, sp [0], sp [0]->cil_code, store->klass, TRUE, FALSE, FALSE);
4415 } else {
4416 MONO_ADD_INS (bblock, store);
4418 NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
4419 *sp++ = ins;
4420 ins->cil_code = ip;
4421 NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
4422 *sp++ = ins;
4423 ins->cil_code = ip;
4425 ++ip;
4426 inline_costs += 2;
4427 break;
4429 case CEE_POP:
4430 CHECK_STACK (1);
4431 MONO_INST_NEW (cfg, ins, CEE_POP);
4432 MONO_ADD_INS (bblock, ins);
4433 ins->cil_code = ip++;
4434 --sp;
4435 ins->inst_i0 = *sp;
4436 break;
4437 case CEE_JMP:
4438 CHECK_OPSIZE (5);
4439 if (stack_start != sp)
4440 UNVERIFIED;
4441 MONO_INST_NEW (cfg, ins, CEE_JMP);
4442 token = read32 (ip + 1);
4443 /* FIXME: check the signature matches */
4444 cmethod = mini_get_method (method, token, NULL, generic_context);
4446 if (!cmethod)
4447 goto load_error;
4449 if (mono_use_security_manager) {
4450 if (check_linkdemand (cfg, method, cmethod, bblock, ip))
4451 INLINE_FAILURE;
4454 ins->inst_p0 = cmethod;
4455 MONO_ADD_INS (bblock, ins);
4456 ip += 5;
4457 start_new_bblock = 1;
4458 break;
4459 case CEE_CALLI:
4460 case CEE_CALL:
4461 case CEE_CALLVIRT: {
4462 MonoInst *addr = NULL;
4463 MonoMethodSignature *fsig = NULL;
4464 int temp, array_rank = 0;
4465 int virtual = *ip == CEE_CALLVIRT;
4467 CHECK_OPSIZE (5);
4468 token = read32 (ip + 1);
4470 if (*ip == CEE_CALLI) {
4471 cmethod = NULL;
4472 CHECK_STACK (1);
4473 --sp;
4474 addr = *sp;
4475 if (method->wrapper_type != MONO_WRAPPER_NONE)
4476 fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
4477 else
4478 fsig = mono_metadata_parse_signature (image, token);
4480 n = fsig->param_count + fsig->hasthis;
4481 } else {
4482 MonoMethod *cil_method;
4484 if (method->wrapper_type != MONO_WRAPPER_NONE) {
4485 cmethod = (MonoMethod *)mono_method_get_wrapper_data (method, token);
4486 cil_method = cmethod;
4487 } else if (constrained_call) {
4488 cmethod = mono_get_method_constrained (image, token, constrained_call, generic_context, &cil_method);
4489 cmethod = mono_get_inflated_method (cmethod);
4490 } else {
4491 cmethod = mini_get_method (method, token, NULL, generic_context);
4492 cil_method = cmethod;
4495 if (!cmethod)
4496 goto load_error;
4497 if (!dont_verify && !cfg->skip_visibility && !can_access_method (method, cil_method))
4498 UNVERIFIED;
4500 if (!virtual && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT))
4501 /* MS.NET seems to silently convert this to a callvirt */
4502 virtual = 1;
4504 if (!cmethod->klass->inited){
4505 if (!mono_class_init (cmethod->klass))
4506 goto load_error;
4509 if (mono_method_signature (cmethod)->pinvoke) {
4510 MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod);
4511 fsig = mono_method_signature (wrapper);
4512 } else if (constrained_call) {
4513 fsig = mono_method_signature (cmethod);
4514 } else {
4515 fsig = mono_method_get_signature_full (cmethod, image, token, generic_context);
4518 n = fsig->param_count + fsig->hasthis;
4520 if (mono_use_security_manager) {
4521 if (check_linkdemand (cfg, method, cmethod, bblock, ip))
4522 INLINE_FAILURE;
4525 if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
4526 mini_class_is_system_array (cmethod->klass)) {
4527 array_rank = cmethod->klass->rank;
4530 if (cmethod->string_ctor)
4531 g_assert_not_reached ();
4535 if (cmethod && cmethod->klass->generic_container)
4536 UNVERIFIED;
4538 CHECK_STACK (n);
4540 //g_assert (!virtual || fsig->hasthis);
4542 sp -= n;
4544 if (constrained_call) {
4546 * We have the `constrained.' prefix opcode.
4548 if (constrained_call->valuetype && !cmethod->klass->valuetype) {
4549 MonoInst *load;
4551 * The type parameter is instantiated as a valuetype,
4552 * but that type doesn't override the method we're
4553 * calling, so we need to box `this'.
4554 * sp [0] is a pointer to the data: we need the value
4555 * in handle_box (), so load it here.
4557 MONO_INST_NEW (cfg, load, mono_type_to_ldind (&constrained_call->byval_arg));
4558 type_to_eval_stack_type (&constrained_call->byval_arg, load);
4559 load->cil_code = ip;
4560 load->inst_left = sp [0];
4561 sp [0] = handle_box (cfg, bblock, load, ip, constrained_call);
4562 } else if (!constrained_call->valuetype) {
4563 MonoInst *ins;
4566 * The type parameter is instantiated as a reference
4567 * type. We have a managed pointer on the stack, so
4568 * we need to dereference it here.
4571 MONO_INST_NEW (cfg, ins, CEE_LDIND_REF);
4572 ins->cil_code = ip;
4573 ins->inst_i0 = sp [0];
4574 ins->type = STACK_OBJ;
4575 ins->klass = mono_class_from_mono_type (&constrained_call->byval_arg);
4576 sp [0] = ins;
4577 } else if (cmethod->klass->valuetype)
4578 virtual = 0;
4579 constrained_call = NULL;
4582 if (*ip != CEE_CALLI && check_call_signature (cfg, fsig, sp))
4583 UNVERIFIED;
4585 if (cmethod && virtual &&
4586 (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) &&
4587 !((cmethod->flags & METHOD_ATTRIBUTE_FINAL) &&
4588 cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) &&
4589 mono_method_signature (cmethod)->generic_param_count) {
4590 MonoInst *this_temp, *this_arg_temp, *store;
4591 MonoInst *iargs [4];
4593 g_assert (mono_method_signature (cmethod)->is_inflated);
4594 /* Prevent inlining of methods that contain indirect calls */
4595 INLINE_FAILURE;
4597 this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
4598 this_temp->cil_code = ip;
4599 NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]);
4601 store->cil_code = ip;
4602 MONO_ADD_INS (bblock, store);
4604 /* FIXME: This should be a managed pointer */
4605 this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
4606 this_arg_temp->cil_code = ip;
4608 NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
4609 NEW_PCONST (cfg, iargs [1], cmethod);
4610 NEW_PCONST (cfg, iargs [2], ((MonoMethodInflated *) cmethod)->context);
4611 NEW_TEMPLOADA (cfg, iargs [3], this_arg_temp->inst_c0);
4612 temp = mono_emit_jit_icall (cfg, bblock, mono_helper_compile_generic_method, iargs, ip);
4614 NEW_TEMPLOAD (cfg, addr, temp);
4615 NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
4617 if ((temp = mono_emit_calli (cfg, bblock, fsig, sp, addr, ip)) != -1) {
4618 NEW_TEMPLOAD (cfg, *sp, temp);
4619 sp++;
4622 ip += 5;
4623 ins_flag = 0;
4624 break;
4627 if ((ins_flag & MONO_INST_TAILCALL) && cmethod && (*ip == CEE_CALL) &&
4628 (mono_metadata_signature_equal (mono_method_signature (method), mono_method_signature (cmethod)))) {
4629 int i;
4631 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
4632 INLINE_FAILURE;
4633 /* FIXME: This assumes the two methods has the same number and type of arguments */
4635 * We implement tail calls by storing the actual arguments into the
4636 * argument variables, then emitting a CEE_JMP. Since the actual arguments
4637 * can refer to the arg variables, we have to spill them.
4639 handle_loaded_temps (cfg, bblock, sp, sp + n);
4640 for (i = 0; i < n; ++i) {
4641 /* Prevent argument from being register allocated */
4642 arg_array [i]->flags |= MONO_INST_VOLATILE;
4644 /* Check if argument is the same */
4646 * FIXME: This loses liveness info, so it can only be done if the
4647 * argument is not register allocated.
4649 NEW_ARGLOAD (cfg, ins, i);
4650 if ((ins->opcode == sp [i]->opcode) && (ins->inst_i0 == sp [i]->inst_i0))
4651 continue;
4653 NEW_ARGSTORE (cfg, ins, i, sp [i]);
4654 ins->cil_code = ip;
4655 if (ins->opcode == CEE_STOBJ) {
4656 NEW_ARGLOADA (cfg, ins, i);
4657 handle_stobj (cfg, bblock, ins, sp [i], sp [i]->cil_code, ins->klass, FALSE, FALSE, FALSE);
4659 else
4660 MONO_ADD_INS (bblock, ins);
4662 MONO_INST_NEW (cfg, ins, CEE_JMP);
4663 ins->cil_code = ip;
4664 ins->inst_p0 = cmethod;
4665 ins->inst_p1 = arg_array [0];
4666 MONO_ADD_INS (bblock, ins);
4667 link_bblock (cfg, bblock, end_bblock);
4668 start_new_bblock = 1;
4669 /* skip CEE_RET as well */
4670 ip += 6;
4671 ins_flag = 0;
4672 break;
4674 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_get_inst_for_method (cfg, cmethod, fsig, sp))) {
4675 ins->cil_code = ip;
4677 if (MONO_TYPE_IS_VOID (fsig->ret)) {
4678 MONO_ADD_INS (bblock, ins);
4679 } else {
4680 type_to_eval_stack_type (fsig->ret, ins);
4681 *sp = ins;
4682 sp++;
4685 ip += 5;
4686 ins_flag = 0;
4687 break;
4690 handle_loaded_temps (cfg, bblock, stack_start, sp);
4692 if ((cfg->opt & MONO_OPT_INLINE) && cmethod &&
4693 (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || (cmethod->flags & METHOD_ATTRIBUTE_FINAL)) &&
4694 mono_method_check_inlining (cfg, cmethod) &&
4695 !g_list_find (dont_inline, cmethod)) {
4696 int costs;
4697 MonoBasicBlock *ebblock;
4698 gboolean allways = FALSE;
4700 if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
4701 (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
4702 /* Prevent inlining of methods that call wrappers */
4703 INLINE_FAILURE;
4704 cmethod = mono_marshal_get_native_wrapper (cmethod);
4705 allways = TRUE;
4708 if ((costs = inline_method (cfg, cmethod, fsig, bblock, sp, ip, real_offset, dont_inline, &ebblock, allways))) {
4709 ip += 5;
4710 real_offset += 5;
4712 GET_BBLOCK (cfg, bbhash, bblock, ip);
4713 ebblock->next_bb = bblock;
4714 link_bblock (cfg, ebblock, bblock);
4716 if (!MONO_TYPE_IS_VOID (fsig->ret))
4717 sp++;
4719 /* indicates start of a new block, and triggers a load of all
4720 stack arguments at bb boundarie */
4721 bblock = ebblock;
4723 inline_costs += costs;
4724 ins_flag = 0;
4725 break;
4729 inline_costs += 10 * num_calls++;
4731 /* tail recursion elimination */
4732 if ((cfg->opt & MONO_OPT_TAILC) && *ip == CEE_CALL && cmethod == method && ip [5] == CEE_RET) {
4733 gboolean has_vtargs = FALSE;
4734 int i;
4736 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
4737 INLINE_FAILURE;
4738 /* keep it simple */
4739 for (i = fsig->param_count - 1; i >= 0; i--) {
4740 if (MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->params [i]))
4741 has_vtargs = TRUE;
4744 if (!has_vtargs) {
4745 for (i = 0; i < n; ++i) {
4746 NEW_ARGSTORE (cfg, ins, i, sp [i]);
4747 ins->cil_code = ip;
4748 MONO_ADD_INS (bblock, ins);
4750 MONO_INST_NEW (cfg, ins, CEE_BR);
4751 ins->cil_code = ip;
4752 MONO_ADD_INS (bblock, ins);
4753 tblock = start_bblock->out_bb [0];
4754 link_bblock (cfg, bblock, tblock);
4755 ins->inst_target_bb = tblock;
4756 start_new_bblock = 1;
4758 /* skip the CEE_RET, too */
4759 if (ip_in_bb (cfg, bblock, ip + 5))
4760 ip += 6;
4761 else
4762 ip += 5;
4763 ins_flag = 0;
4764 break;
4768 if (*ip == CEE_CALLI) {
4769 /* Prevent inlining of methods with indirect calls */
4770 INLINE_FAILURE;
4771 if ((temp = mono_emit_calli (cfg, bblock, fsig, sp, addr, ip)) != -1) {
4772 NEW_TEMPLOAD (cfg, *sp, temp);
4773 sp++;
4775 } else if (array_rank) {
4776 MonoInst *addr;
4778 if (strcmp (cmethod->name, "Set") == 0) { /* array Set */
4779 if (sp [fsig->param_count]->type == STACK_OBJ) {
4780 MonoInst *iargs [2];
4781 MonoInst *array, *to_store, *store;
4783 handle_loaded_temps (cfg, bblock, stack_start, sp);
4785 array = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
4786 NEW_TEMPSTORE (cfg, store, array->inst_c0, sp [0]);
4787 store->cil_code = ip;
4788 MONO_ADD_INS (bblock, store);
4789 NEW_TEMPLOAD (cfg, iargs [0], array->inst_c0);
4791 to_store = mono_compile_create_var (cfg, type_from_stack_type (sp [fsig->param_count]), OP_LOCAL);
4792 NEW_TEMPSTORE (cfg, store, to_store->inst_c0, sp [fsig->param_count]);
4793 store->cil_code = ip;
4794 MONO_ADD_INS (bblock, store);
4795 NEW_TEMPLOAD (cfg, iargs [1], to_store->inst_c0);
4798 * We first save the args for the call so that the args are copied to the stack
4799 * and a new instruction tree for them is created. If we don't do this,
4800 * the same MonoInst is added to two different trees and this is not
4801 * allowed by burg.
4803 mono_emit_jit_icall (cfg, bblock, mono_helper_stelem_ref_check, iargs, ip);
4805 NEW_TEMPLOAD (cfg, sp [0], array->inst_c0);
4806 NEW_TEMPLOAD (cfg, sp [fsig->param_count], to_store->inst_c0);
4809 addr = mini_get_ldelema_ins (cfg, bblock, cmethod, sp, ip, TRUE);
4810 NEW_INDSTORE (cfg, ins, addr, sp [fsig->param_count], fsig->params [fsig->param_count - 1]);
4811 ins->cil_code = ip;
4812 if (ins->opcode == CEE_STOBJ) {
4813 handle_stobj (cfg, bblock, addr, sp [fsig->param_count], ip, mono_class_from_mono_type (fsig->params [fsig->param_count-1]), FALSE, FALSE, TRUE);
4814 } else {
4815 MONO_ADD_INS (bblock, ins);
4818 } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */
4819 addr = mini_get_ldelema_ins (cfg, bblock, cmethod, sp, ip, FALSE);
4820 NEW_INDLOAD (cfg, ins, addr, fsig->ret);
4821 ins->cil_code = ip;
4823 *sp++ = ins;
4824 } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */
4825 addr = mini_get_ldelema_ins (cfg, bblock, cmethod, sp, ip, FALSE);
4826 *sp++ = addr;
4827 } else {
4828 g_assert_not_reached ();
4831 } else {
4832 /* Prevent inlining of methods which call other methods */
4833 INLINE_FAILURE;
4834 if (ip_in_bb (cfg, bblock, ip + 5)
4835 && (!MONO_TYPE_ISSTRUCT (fsig->ret))
4836 && (!MONO_TYPE_IS_VOID (fsig->ret) || cmethod->string_ctor)
4837 && (CODE_IS_STLOC (ip + 5) || ip [5] == CEE_POP || ip [5] == CEE_RET)) {
4838 /* no need to spill */
4839 ins = (MonoInst*)mono_emit_method_call (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL);
4840 *sp++ = ins;
4841 } else {
4842 if ((temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL)) != -1) {
4843 NEW_TEMPLOAD (cfg, *sp, temp);
4844 sp++;
4849 ip += 5;
4850 ins_flag = 0;
4851 break;
4853 case CEE_RET:
4854 if (cfg->method != method) {
4855 /* return from inlined methode */
4856 if (return_var) {
4857 MonoInst *store;
4858 CHECK_STACK (1);
4859 --sp;
4860 //g_assert (returnvar != -1);
4861 NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp);
4862 store->cil_code = sp [0]->cil_code;
4863 if (store->opcode == CEE_STOBJ) {
4864 g_assert_not_reached ();
4865 NEW_TEMPLOADA (cfg, store, return_var->inst_c0);
4866 /* FIXME: it is possible some optimization will pass the a heap pointer for the struct address, so we'll need the write barrier */
4867 handle_stobj (cfg, bblock, store, *sp, sp [0]->cil_code, return_var->klass, FALSE, FALSE, FALSE);
4868 } else
4869 MONO_ADD_INS (bblock, store);
4871 } else {
4872 if (cfg->ret) {
4873 g_assert (!return_var);
4874 CHECK_STACK (1);
4875 --sp;
4876 MONO_INST_NEW (cfg, ins, CEE_NOP);
4877 ins->opcode = mono_type_to_stind (mono_method_signature (method)->ret);
4878 if (ins->opcode == CEE_STOBJ) {
4879 NEW_RETLOADA (cfg, ins);
4880 /* FIXME: it is possible some optimization will pass the a heap pointer for the struct address, so we'll need the write barrier */
4881 handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
4882 } else {
4883 ins->opcode = OP_SETRET;
4884 ins->cil_code = ip;
4885 ins->inst_i0 = *sp;;
4886 ins->inst_i1 = NULL;
4887 MONO_ADD_INS (bblock, ins);
4891 if (sp != stack_start)
4892 UNVERIFIED;
4893 MONO_INST_NEW (cfg, ins, CEE_BR);
4894 ins->cil_code = ip++;
4895 ins->inst_target_bb = end_bblock;
4896 MONO_ADD_INS (bblock, ins);
4897 link_bblock (cfg, bblock, end_bblock);
4898 start_new_bblock = 1;
4899 break;
4900 case CEE_BR_S:
4901 CHECK_OPSIZE (2);
4902 MONO_INST_NEW (cfg, ins, CEE_BR);
4903 ins->cil_code = ip++;
4904 MONO_ADD_INS (bblock, ins);
4905 target = ip + 1 + (signed char)(*ip);
4906 ++ip;
4907 GET_BBLOCK (cfg, bbhash, tblock, target);
4908 link_bblock (cfg, bblock, tblock);
4909 CHECK_BBLOCK (target, ip, tblock);
4910 ins->inst_target_bb = tblock;
4911 if (sp != stack_start) {
4912 handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
4913 sp = stack_start;
4914 CHECK_UNVERIFIABLE (cfg);
4916 start_new_bblock = 1;
4917 inline_costs += BRANCH_COST;
4918 break;
4919 case CEE_BRFALSE_S:
4920 case CEE_BRTRUE_S:
4921 CHECK_OPSIZE (2);
4922 CHECK_STACK (1);
4923 if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
4924 UNVERIFIED;
4925 MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
4926 ins->cil_code = ip++;
4927 target = ip + 1 + *(signed char*)ip;
4928 ip++;
4929 ADD_UNCOND (ins->opcode == CEE_BRTRUE);
4930 if (sp != stack_start) {
4931 handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
4932 sp = stack_start;
4933 CHECK_UNVERIFIABLE (cfg);
4935 inline_costs += BRANCH_COST;
4936 break;
4937 case CEE_BEQ_S:
4938 case CEE_BGE_S:
4939 case CEE_BGT_S:
4940 case CEE_BLE_S:
4941 case CEE_BLT_S:
4942 case CEE_BNE_UN_S:
4943 case CEE_BGE_UN_S:
4944 case CEE_BGT_UN_S:
4945 case CEE_BLE_UN_S:
4946 case CEE_BLT_UN_S:
4947 CHECK_OPSIZE (2);
4948 CHECK_STACK (2);
4949 MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
4950 ins->cil_code = ip++;
4951 target = ip + 1 + *(signed char*)ip;
4952 ip++;
4953 #ifdef MONO_ARCH_SOFT_FLOAT
4954 if (sp [-1]->type == STACK_R8 || sp [-2]->type == STACK_R8) {
4955 ins->opcode = condbr_to_fp_br (ins->opcode);
4956 sp -= 2;
4957 ins->inst_left = sp [0];
4958 ins->inst_right = sp [1];
4959 ins->type = STACK_I4;
4960 *sp++ = emit_tree (cfg, bblock, ins, ins->cil_code);
4961 MONO_INST_NEW (cfg, ins, CEE_BRTRUE);
4962 ADD_UNCOND (TRUE);
4963 } else {
4964 ADD_BINCOND (NULL);
4966 #else
4967 ADD_BINCOND (NULL);
4968 #endif
4969 if (sp != stack_start) {
4970 handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
4971 sp = stack_start;
4972 CHECK_UNVERIFIABLE (cfg);
4974 inline_costs += BRANCH_COST;
4975 break;
4976 case CEE_BR:
4977 CHECK_OPSIZE (5);
4978 MONO_INST_NEW (cfg, ins, CEE_BR);
4979 ins->cil_code = ip++;
4980 MONO_ADD_INS (bblock, ins);
4981 target = ip + 4 + (gint32)read32(ip);
4982 ip += 4;
4983 GET_BBLOCK (cfg, bbhash, tblock, target);
4984 link_bblock (cfg, bblock, tblock);
4985 CHECK_BBLOCK (target, ip, tblock);
4986 ins->inst_target_bb = tblock;
4987 if (sp != stack_start) {
4988 handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
4989 sp = stack_start;
4990 CHECK_UNVERIFIABLE (cfg);
4992 start_new_bblock = 1;
4993 inline_costs += BRANCH_COST;
4994 break;
4995 case CEE_BRFALSE:
4996 case CEE_BRTRUE:
4997 CHECK_OPSIZE (5);
4998 CHECK_STACK (1);
4999 if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
5000 UNVERIFIED;
5001 MONO_INST_NEW (cfg, ins, *ip);
5002 ins->cil_code = ip++;
5003 target = ip + 4 + (gint32)read32(ip);
5004 ip += 4;
5005 ADD_UNCOND(ins->opcode == CEE_BRTRUE);
5006 if (sp != stack_start) {
5007 handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
5008 sp = stack_start;
5009 CHECK_UNVERIFIABLE (cfg);
5011 inline_costs += BRANCH_COST;
5012 break;
5013 case CEE_BEQ:
5014 case CEE_BGE:
5015 case CEE_BGT:
5016 case CEE_BLE:
5017 case CEE_BLT:
5018 case CEE_BNE_UN:
5019 case CEE_BGE_UN:
5020 case CEE_BGT_UN:
5021 case CEE_BLE_UN:
5022 case CEE_BLT_UN:
5023 CHECK_OPSIZE (5);
5024 CHECK_STACK (2);
5025 MONO_INST_NEW (cfg, ins, *ip);
5026 ins->cil_code = ip++;
5027 target = ip + 4 + (gint32)read32(ip);
5028 ip += 4;
5029 #ifdef MONO_ARCH_SOFT_FLOAT
5030 if (sp [-1]->type == STACK_R8 || sp [-2]->type == STACK_R8) {
5031 ins->opcode = condbr_to_fp_br (ins->opcode);
5032 sp -= 2;
5033 ins->inst_left = sp [0];
5034 ins->inst_right = sp [1];
5035 ins->type = STACK_I4;
5036 *sp++ = emit_tree (cfg, bblock, ins, ins->cil_code);
5037 MONO_INST_NEW (cfg, ins, CEE_BRTRUE);
5038 ADD_UNCOND (TRUE);
5039 } else {
5040 ADD_BINCOND (NULL);
5042 #else
5043 ADD_BINCOND (NULL);
5044 #endif
5045 if (sp != stack_start) {
5046 handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
5047 sp = stack_start;
5048 CHECK_UNVERIFIABLE (cfg);
5050 inline_costs += BRANCH_COST;
5051 break;
5052 case CEE_SWITCH:
5053 CHECK_OPSIZE (5);
5054 CHECK_STACK (1);
5055 n = read32 (ip + 1);
5056 MONO_INST_NEW (cfg, ins, *ip);
5057 --sp;
5058 ins->inst_left = *sp;
5059 if ((ins->inst_left->type != STACK_I4) && (ins->inst_left->type != STACK_PTR))
5060 UNVERIFIED;
5061 ins->cil_code = ip;
5062 ip += 5;
5063 CHECK_OPSIZE (n * sizeof (guint32));
5064 target = ip + n * sizeof (guint32);
5065 MONO_ADD_INS (bblock, ins);
5066 GET_BBLOCK (cfg, bbhash, tblock, target);
5067 link_bblock (cfg, bblock, tblock);
5068 ins->klass = GUINT_TO_POINTER (n);
5069 ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (n + 1));
5070 ins->inst_many_bb [n] = tblock;
5072 for (i = 0; i < n; ++i) {
5073 GET_BBLOCK (cfg, bbhash, tblock, target + (gint32)read32(ip));
5074 link_bblock (cfg, bblock, tblock);
5075 ins->inst_many_bb [i] = tblock;
5076 ip += 4;
5078 if (sp != stack_start) {
5079 handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
5080 sp = stack_start;
5081 CHECK_UNVERIFIABLE (cfg);
5083 /* Needed by the code generated in inssel.brg */
5084 mono_get_got_var (cfg);
5085 inline_costs += (BRANCH_COST * 2);
5086 break;
5087 case CEE_LDIND_I1:
5088 case CEE_LDIND_U1:
5089 case CEE_LDIND_I2:
5090 case CEE_LDIND_U2:
5091 case CEE_LDIND_I4:
5092 case CEE_LDIND_U4:
5093 case CEE_LDIND_I8:
5094 case CEE_LDIND_I:
5095 case CEE_LDIND_R4:
5096 case CEE_LDIND_R8:
5097 case CEE_LDIND_REF:
5098 CHECK_STACK (1);
5099 MONO_INST_NEW (cfg, ins, *ip);
5100 ins->cil_code = ip;
5101 --sp;
5102 ins->inst_i0 = *sp;
5103 *sp++ = ins;
5104 ins->type = ldind_type [*ip - CEE_LDIND_I1];
5105 ins->flags |= ins_flag;
5106 ins_flag = 0;
5107 if (ins->type == STACK_OBJ)
5108 ins->klass = mono_defaults.object_class;
5109 #ifdef MONO_ARCH_SOFT_FLOAT
5110 if (*ip == CEE_LDIND_R4) {
5111 int temp;
5112 ++sp;
5113 temp = handle_load_float (cfg, bblock, ins->inst_i0, ip);
5114 NEW_TEMPLOAD (cfg, *sp, temp);
5115 sp++;
5117 #endif
5118 ++ip;
5119 break;
5120 case CEE_STIND_REF:
5121 case CEE_STIND_I1:
5122 case CEE_STIND_I2:
5123 case CEE_STIND_I4:
5124 case CEE_STIND_I8:
5125 case CEE_STIND_R4:
5126 case CEE_STIND_R8:
5127 CHECK_STACK (2);
5128 #ifdef MONO_ARCH_SOFT_FLOAT
5129 if (*ip == CEE_STIND_R4) {
5130 sp -= 2;
5131 handle_store_float (cfg, bblock, sp [0], sp [1], ip);
5132 ip++;
5133 break;
5135 #endif
5136 #if HAVE_WRITE_BARRIERS
5137 if (*ip == CEE_STIND_REF && method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER) {
5138 /* insert call to write barrier */
5139 MonoMethod *write_barrier = mono_marshal_get_write_barrier ();
5140 sp -= 2;
5141 mono_emit_method_call_spilled (cfg, bblock, write_barrier, mono_method_signature (write_barrier), sp, ip, NULL);
5142 ip++;
5143 break;
5145 #endif
5146 MONO_INST_NEW (cfg, ins, *ip);
5147 ins->cil_code = ip++;
5148 sp -= 2;
5149 handle_loaded_temps (cfg, bblock, stack_start, sp);
5150 MONO_ADD_INS (bblock, ins);
5151 ins->inst_i0 = sp [0];
5152 ins->inst_i1 = sp [1];
5153 ins->flags |= ins_flag;
5154 ins_flag = 0;
5155 inline_costs += 1;
5156 break;
5157 case CEE_MUL:
5158 CHECK_STACK (2);
5159 ADD_BINOP (*ip);
5161 #ifdef MONO_ARCH_NO_EMULATE_MUL_IMM
5162 /* FIXME: This breaks with ssapre (mono -O=ssapre loader.exe) */
5163 if ((ins->inst_right->opcode == OP_ICONST) && !(cfg->opt & MONO_OPT_SSAPRE)) {
5164 switch (ins->opcode) {
5165 case CEE_MUL:
5166 ins->opcode = OP_IMUL_IMM;
5167 ins->inst_imm = ins->inst_right->inst_c0;
5168 break;
5169 case OP_LMUL:
5170 ins->opcode = OP_LMUL_IMM;
5171 ins->inst_imm = ins->inst_right->inst_c0;
5172 break;
5173 default:
5174 g_assert_not_reached ();
5177 #endif
5179 if (mono_find_jit_opcode_emulation (ins->opcode)) {
5180 --sp;
5181 *sp++ = emit_tree (cfg, bblock, ins, ip + 1);
5183 ip++;
5184 break;
5185 case CEE_ADD:
5186 case CEE_SUB:
5187 case CEE_DIV:
5188 case CEE_DIV_UN:
5189 case CEE_REM:
5190 case CEE_REM_UN:
5191 case CEE_AND:
5192 case CEE_OR:
5193 case CEE_XOR:
5194 case CEE_SHL:
5195 case CEE_SHR:
5196 case CEE_SHR_UN:
5197 CHECK_STACK (2);
5198 ADD_BINOP (*ip);
5199 /* special case that gives a nice speedup and happens to workaorund a ppc jit but (for the release)
5200 * later apply the speedup to the left shift as well
5201 * See BUG# 57957.
5203 if ((ins->opcode == OP_LSHR_UN) && (ins->type == STACK_I8)
5204 && (ins->inst_right->opcode == OP_ICONST) && (ins->inst_right->inst_c0 == 32)) {
5205 ins->opcode = OP_LONG_SHRUN_32;
5206 /*g_print ("applied long shr speedup to %s\n", cfg->method->name);*/
5207 ip++;
5208 break;
5210 if (mono_find_jit_opcode_emulation (ins->opcode)) {
5211 --sp;
5212 *sp++ = emit_tree (cfg, bblock, ins, ip + 1);
5214 ip++;
5215 break;
5216 case CEE_NEG:
5217 case CEE_NOT:
5218 case CEE_CONV_I1:
5219 case CEE_CONV_I2:
5220 case CEE_CONV_I4:
5221 case CEE_CONV_R4:
5222 case CEE_CONV_R8:
5223 case CEE_CONV_U4:
5224 case CEE_CONV_I8:
5225 case CEE_CONV_U8:
5226 case CEE_CONV_OVF_I8:
5227 case CEE_CONV_OVF_U8:
5228 case CEE_CONV_R_UN:
5229 CHECK_STACK (1);
5230 ADD_UNOP (*ip);
5231 if (mono_find_jit_opcode_emulation (ins->opcode)) {
5232 --sp;
5233 *sp++ = emit_tree (cfg, bblock, ins, ip + 1);
5235 ip++;
5236 break;
5237 case CEE_CONV_OVF_I4:
5238 case CEE_CONV_OVF_I1:
5239 case CEE_CONV_OVF_I2:
5240 case CEE_CONV_OVF_I:
5241 case CEE_CONV_OVF_U:
5242 CHECK_STACK (1);
5244 if (sp [-1]->type == STACK_R8) {
5245 ADD_UNOP (CEE_CONV_OVF_I8);
5246 ADD_UNOP (*ip);
5247 } else {
5248 ADD_UNOP (*ip);
5251 ip++;
5252 break;
5253 case CEE_CONV_OVF_U1:
5254 case CEE_CONV_OVF_U2:
5255 case CEE_CONV_OVF_U4:
5256 CHECK_STACK (1);
5258 if (sp [-1]->type == STACK_R8) {
5259 ADD_UNOP (CEE_CONV_OVF_U8);
5260 ADD_UNOP (*ip);
5261 } else {
5262 ADD_UNOP (*ip);
5265 ip++;
5266 break;
5267 case CEE_CONV_OVF_I1_UN:
5268 case CEE_CONV_OVF_I2_UN:
5269 case CEE_CONV_OVF_I4_UN:
5270 case CEE_CONV_OVF_I8_UN:
5271 case CEE_CONV_OVF_U1_UN:
5272 case CEE_CONV_OVF_U2_UN:
5273 case CEE_CONV_OVF_U4_UN:
5274 case CEE_CONV_OVF_U8_UN:
5275 case CEE_CONV_OVF_I_UN:
5276 case CEE_CONV_OVF_U_UN:
5277 CHECK_STACK (1);
5278 ADD_UNOP (*ip);
5279 ip++;
5280 break;
5281 case CEE_CPOBJ:
5282 CHECK_OPSIZE (5);
5283 CHECK_STACK (2);
5284 token = read32 (ip + 1);
5285 klass = mini_get_class (method, token, generic_context);
5286 CHECK_TYPELOAD (klass);
5287 sp -= 2;
5288 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
5289 MonoInst *store, *load;
5290 MONO_INST_NEW (cfg, load, CEE_LDIND_REF);
5291 load->cil_code = ip;
5292 load->inst_i0 = sp [1];
5293 load->type = STACK_OBJ;
5294 load->klass = klass;
5295 load->flags |= ins_flag;
5296 MONO_INST_NEW (cfg, store, CEE_STIND_REF);
5297 store->cil_code = ip;
5298 handle_loaded_temps (cfg, bblock, stack_start, sp);
5299 MONO_ADD_INS (bblock, store);
5300 store->inst_i0 = sp [0];
5301 store->inst_i1 = load;
5302 store->flags |= ins_flag;
5303 } else {
5304 n = mono_class_value_size (klass, NULL);
5305 if ((cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 5) {
5306 MonoInst *copy;
5307 MONO_INST_NEW (cfg, copy, OP_MEMCPY);
5308 copy->inst_left = sp [0];
5309 copy->inst_right = sp [1];
5310 copy->cil_code = ip;
5311 copy->backend.size = n;
5312 MONO_ADD_INS (bblock, copy);
5313 } else {
5314 MonoMethod *memcpy_method = get_memcpy_method ();
5315 MonoInst *iargs [3];
5316 iargs [0] = sp [0];
5317 iargs [1] = sp [1];
5318 NEW_ICONST (cfg, iargs [2], n);
5319 iargs [2]->cil_code = ip;
5321 mono_emit_method_call_spilled (cfg, bblock, memcpy_method, memcpy_method->signature, iargs, ip, NULL);
5324 ins_flag = 0;
5325 ip += 5;
5326 break;
5327 case CEE_LDOBJ: {
5328 MonoInst *iargs [3];
5329 int loc_index = -1;
5330 int stloc_len = 0;
5331 CHECK_OPSIZE (5);
5332 CHECK_STACK (1);
5333 --sp;
5334 token = read32 (ip + 1);
5335 klass = mini_get_class (method, token, generic_context);
5336 CHECK_TYPELOAD (klass);
5337 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
5338 MONO_INST_NEW (cfg, ins, CEE_LDIND_REF);
5339 ins->cil_code = ip;
5340 ins->inst_i0 = sp [0];
5341 ins->type = STACK_OBJ;
5342 ins->klass = klass;
5343 ins->flags |= ins_flag;
5344 ins_flag = 0;
5345 *sp++ = ins;
5346 ip += 5;
5347 break;
5350 /* Optimize the common ldobj+stloc combination */
5351 switch (ip [5]) {
5352 case CEE_STLOC_S:
5353 loc_index = ip [6];
5354 stloc_len = 2;
5355 break;
5356 case CEE_STLOC_0:
5357 case CEE_STLOC_1:
5358 case CEE_STLOC_2:
5359 case CEE_STLOC_3:
5360 loc_index = ip [5] - CEE_STLOC_0;
5361 stloc_len = 1;
5362 break;
5363 default:
5364 break;
5367 if ((loc_index != -1) && ip_in_bb (cfg, bblock, ip + 5)) {
5368 CHECK_LOCAL (loc_index);
5369 NEW_LOCSTORE (cfg, ins, loc_index, *sp);
5371 if (ins->opcode == CEE_STOBJ) {
5372 handle_loaded_temps (cfg, bblock, stack_start, sp);
5373 ins->cil_code = ip;
5374 g_assert (ins->opcode == CEE_STOBJ);
5375 NEW_LOCLOADA (cfg, ins, loc_index);
5376 handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
5377 ip += 5;
5378 ip += stloc_len;
5379 break;
5383 n = mono_class_value_size (klass, NULL);
5384 ins = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
5385 NEW_TEMPLOADA (cfg, iargs [0], ins->inst_c0);
5386 if ((cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 5) {
5387 MonoInst *copy;
5388 MONO_INST_NEW (cfg, copy, OP_MEMCPY);
5389 copy->inst_left = iargs [0];
5390 copy->inst_right = *sp;
5391 copy->cil_code = ip;
5392 copy->backend.size = n;
5393 MONO_ADD_INS (bblock, copy);
5394 } else {
5395 MonoMethod *memcpy_method = get_memcpy_method ();
5396 iargs [1] = *sp;
5397 NEW_ICONST (cfg, iargs [2], n);
5398 iargs [2]->cil_code = ip;
5400 mono_emit_method_call_spilled (cfg, bblock, memcpy_method, memcpy_method->signature, iargs, ip, NULL);
5402 NEW_TEMPLOAD (cfg, *sp, ins->inst_c0);
5403 ++sp;
5404 ip += 5;
5405 ins_flag = 0;
5406 inline_costs += 1;
5407 break;
5409 case CEE_LDSTR:
5410 CHECK_STACK_OVF (1);
5411 CHECK_OPSIZE (5);
5412 n = read32 (ip + 1);
5414 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
5415 NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, n));
5416 ins->cil_code = ip;
5417 ins->type = STACK_OBJ;
5418 ins->klass = mono_defaults.string_class;
5419 *sp = ins;
5421 else if (method->wrapper_type != MONO_WRAPPER_NONE) {
5422 int temp;
5423 MonoInst *iargs [1];
5425 NEW_PCONST (cfg, iargs [0], mono_method_get_wrapper_data (method, n));
5426 temp = mono_emit_jit_icall (cfg, bblock, mono_string_new_wrapper, iargs, ip);
5427 NEW_TEMPLOAD (cfg, *sp, temp);
5429 } else {
5431 if (cfg->opt & MONO_OPT_SHARED) {
5432 int temp;
5433 MonoInst *iargs [3];
5434 MonoInst* domain_var;
5436 if (cfg->compile_aot) {
5437 cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, GINT_TO_POINTER (n));
5439 /* avoid depending on undefined C behavior in sequence points */
5440 domain_var = mono_get_domainvar (cfg);
5441 NEW_TEMPLOAD (cfg, iargs [0], domain_var->inst_c0);
5442 NEW_IMAGECONST (cfg, iargs [1], image);
5443 NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n));
5444 temp = mono_emit_jit_icall (cfg, bblock, mono_ldstr, iargs, ip);
5445 NEW_TEMPLOAD (cfg, *sp, temp);
5446 mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
5447 } else {
5448 if (bblock->out_of_line) {
5449 MonoInst *iargs [2];
5450 int temp;
5452 if (cfg->compile_aot && cfg->method->klass->image == mono_defaults.corlib) {
5454 * Avoid relocations by using a version of helper_ldstr
5455 * specialized to mscorlib.
5457 NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (n));
5458 temp = mono_emit_jit_icall (cfg, bblock, mono_helper_ldstr_mscorlib, iargs, ip);
5459 } else {
5460 /* Avoid creating the string object */
5461 NEW_IMAGECONST (cfg, iargs [0], image);
5462 NEW_ICONST (cfg, iargs [1], mono_metadata_token_index (n));
5463 temp = mono_emit_jit_icall (cfg, bblock, mono_helper_ldstr, iargs, ip);
5465 NEW_TEMPLOAD (cfg, *sp, temp);
5467 else
5468 if (cfg->compile_aot) {
5469 NEW_LDSTRCONST (cfg, ins, image, n);
5470 *sp = ins;
5472 else {
5473 NEW_PCONST (cfg, ins, NULL);
5474 ins->cil_code = ip;
5475 ins->type = STACK_OBJ;
5476 ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
5477 ins->klass = mono_defaults.string_class;
5478 *sp = ins;
5483 sp++;
5484 ip += 5;
5485 break;
5486 case CEE_NEWOBJ: {
5487 MonoInst *iargs [2];
5488 MonoMethodSignature *fsig;
5489 int temp;
5491 CHECK_OPSIZE (5);
5492 token = read32 (ip + 1);
5493 cmethod = mini_get_method (method, token, NULL, generic_context);
5494 if (!cmethod)
5495 goto load_error;
5496 fsig = mono_method_get_signature (cmethod, image, token);
5498 if (!mono_class_init (cmethod->klass))
5499 goto load_error;
5501 if (mono_use_security_manager) {
5502 if (check_linkdemand (cfg, method, cmethod, bblock, ip))
5503 INLINE_FAILURE;
5506 n = fsig->param_count;
5507 CHECK_STACK (n);
5509 /* move the args to allow room for 'this' in the first position */
5510 while (n--) {
5511 --sp;
5512 sp [1] = sp [0];
5515 handle_loaded_temps (cfg, bblock, stack_start, sp);
5517 if (mini_class_is_system_array (cmethod->klass)) {
5518 NEW_METHODCONST (cfg, *sp, cmethod);
5519 temp = handle_array_new (cfg, bblock, fsig->param_count, sp, ip);
5520 } else if (cmethod->string_ctor) {
5521 /* we simply pass a null pointer */
5522 NEW_PCONST (cfg, *sp, NULL);
5523 /* now call the string ctor */
5524 temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, NULL);
5525 } else {
5526 MonoInst* callvirt_this_arg = NULL;
5528 if (cmethod->klass->valuetype) {
5529 iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL);
5530 temp = iargs [0]->inst_c0;
5532 NEW_TEMPLOADA (cfg, *sp, temp);
5534 handle_initobj (cfg, bblock, *sp, NULL, cmethod->klass, stack_start, sp);
5536 NEW_TEMPLOADA (cfg, *sp, temp);
5539 * The code generated by mini_emit_virtual_call () expects
5540 * iargs [0] to be a boxed instance, but luckily the vcall
5541 * will be transformed into a normal call there.
5543 } else {
5544 temp = handle_alloc (cfg, bblock, cmethod->klass, FALSE, ip);
5545 NEW_TEMPLOAD (cfg, *sp, temp);
5548 /* Avoid virtual calls to ctors if possible */
5549 if (cmethod->klass->marshalbyref)
5550 callvirt_this_arg = sp [0];
5552 if ((cfg->opt & MONO_OPT_INLINE) && cmethod &&
5553 mono_method_check_inlining (cfg, cmethod) &&
5554 !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE) &&
5555 !g_list_find (dont_inline, cmethod)) {
5556 int costs;
5557 MonoBasicBlock *ebblock;
5558 if ((costs = inline_method (cfg, cmethod, fsig, bblock, sp, ip, real_offset, dont_inline, &ebblock, FALSE))) {
5560 ip += 5;
5561 real_offset += 5;
5563 GET_BBLOCK (cfg, bbhash, bblock, ip);
5564 ebblock->next_bb = bblock;
5565 link_bblock (cfg, ebblock, bblock);
5567 NEW_TEMPLOAD (cfg, *sp, temp);
5568 sp++;
5570 /* indicates start of a new block, and triggers a load
5571 of all stack arguments at bb boundarie */
5572 bblock = ebblock;
5574 inline_costs += costs;
5575 break;
5577 } else {
5578 /* Prevent inlining of methods which call other methods */
5579 INLINE_FAILURE;
5580 mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, callvirt_this_arg);
5582 } else {
5583 /* Prevent inlining of methods which call other methods */
5584 INLINE_FAILURE;
5585 /* now call the actual ctor */
5586 mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, callvirt_this_arg);
5590 NEW_TEMPLOAD (cfg, *sp, temp);
5591 sp++;
5593 ip += 5;
5594 inline_costs += 5;
5595 break;
5597 case CEE_ISINST:
5598 CHECK_STACK (1);
5599 --sp;
5600 CHECK_OPSIZE (5);
5601 token = read32 (ip + 1);
5602 klass = mini_get_class (method, token, generic_context);
5603 CHECK_TYPELOAD (klass);
5604 if (sp [0]->type != STACK_OBJ)
5605 UNVERIFIED;
5607 /* Needed by the code generated in inssel.brg */
5608 mono_get_got_var (cfg);
5610 if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
5612 MonoMethod *mono_isinst;
5613 MonoInst *iargs [1];
5614 MonoBasicBlock *ebblock;
5615 int costs;
5616 int temp;
5618 mono_isinst = mono_marshal_get_isinst (klass);
5619 iargs [0] = sp [0];
5621 costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst), bblock,
5622 iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
5624 g_assert (costs > 0);
5626 ip += 5;
5627 real_offset += 5;
5629 GET_BBLOCK (cfg, bbhash, bblock, ip);
5630 ebblock->next_bb = bblock;
5631 link_bblock (cfg, ebblock, bblock);
5633 temp = iargs [0]->inst_i0->inst_c0;
5634 NEW_TEMPLOAD (cfg, *sp, temp);
5636 sp++;
5637 bblock = ebblock;
5638 inline_costs += costs;
5639 } else {
5640 MONO_INST_NEW (cfg, ins, *ip);
5641 ins->type = STACK_OBJ;
5642 ins->inst_left = *sp;
5643 ins->inst_newa_class = klass;
5644 ins->klass = klass;
5645 ins->cil_code = ip;
5646 *sp++ = emit_tree (cfg, bblock, ins, ip + 5);
5647 ip += 5;
5649 break;
5650 case CEE_UNBOX_ANY: {
5651 MonoInst *add, *vtoffset;
5652 MonoInst *iargs [3];
5654 CHECK_STACK (1);
5655 --sp;
5656 CHECK_OPSIZE (5);
5657 token = read32 (ip + 1);
5658 klass = mini_get_class (method, token, generic_context);
5659 CHECK_TYPELOAD (klass);
5661 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
5662 /* CASTCLASS */
5663 if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
5664 MonoMethod *mono_castclass;
5665 MonoInst *iargs [1];
5666 MonoBasicBlock *ebblock;
5667 int costs;
5668 int temp;
5670 mono_castclass = mono_marshal_get_castclass (klass);
5671 iargs [0] = sp [0];
5673 costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass), bblock,
5674 iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
5676 g_assert (costs > 0);
5678 ip += 5;
5679 real_offset += 5;
5681 GET_BBLOCK (cfg, bbhash, bblock, ip);
5682 ebblock->next_bb = bblock;
5683 link_bblock (cfg, ebblock, bblock);
5685 temp = iargs [0]->inst_i0->inst_c0;
5686 NEW_TEMPLOAD (cfg, *sp, temp);
5688 sp++;
5689 bblock = ebblock;
5690 inline_costs += costs;
5691 } else {
5692 MONO_INST_NEW (cfg, ins, CEE_CASTCLASS);
5693 ins->type = STACK_OBJ;
5694 ins->inst_left = *sp;
5695 ins->klass = klass;
5696 ins->inst_newa_class = klass;
5697 ins->cil_code = ip;
5698 *sp++ = ins;
5699 ip += 5;
5701 break;
5704 if (mono_class_is_nullable (klass)) {
5705 int v = handle_unbox_nullable (cfg, bblock, *sp, ip, klass);
5706 NEW_TEMPLOAD (cfg, *sp, v);
5707 sp ++;
5708 ip += 5;
5709 break;
5712 MONO_INST_NEW (cfg, ins, OP_UNBOXCAST);
5713 ins->type = STACK_OBJ;
5714 ins->inst_left = *sp;
5715 ins->klass = klass;
5716 ins->inst_newa_class = klass;
5717 ins->cil_code = ip;
5719 MONO_INST_NEW (cfg, add, OP_PADD);
5720 NEW_ICONST (cfg, vtoffset, sizeof (MonoObject));
5721 add->inst_left = ins;
5722 add->inst_right = vtoffset;
5723 add->type = STACK_MP;
5724 add->klass = mono_defaults.object_class;
5725 *sp = add;
5726 ip += 5;
5727 /* LDOBJ impl */
5728 n = mono_class_value_size (klass, NULL);
5729 ins = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
5730 NEW_TEMPLOADA (cfg, iargs [0], ins->inst_c0);
5731 if ((cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 5) {
5732 MonoInst *copy;
5733 MONO_INST_NEW (cfg, copy, OP_MEMCPY);
5734 copy->inst_left = iargs [0];
5735 copy->inst_right = *sp;
5736 copy->cil_code = ip;
5737 copy->backend.size = n;
5738 MONO_ADD_INS (bblock, copy);
5739 } else {
5740 MonoMethod *memcpy_method = get_memcpy_method ();
5741 iargs [1] = *sp;
5742 NEW_ICONST (cfg, iargs [2], n);
5743 iargs [2]->cil_code = ip;
5745 mono_emit_method_call_spilled (cfg, bblock, memcpy_method, memcpy_method->signature, iargs, ip, NULL);
5747 NEW_TEMPLOAD (cfg, *sp, ins->inst_c0);
5748 ++sp;
5749 inline_costs += 2;
5750 break;
5752 case CEE_UNBOX: {
5753 MonoInst *add, *vtoffset;
5755 CHECK_STACK (1);
5756 --sp;
5757 CHECK_OPSIZE (5);
5758 token = read32 (ip + 1);
5759 klass = mini_get_class (method, token, generic_context);
5760 CHECK_TYPELOAD (klass);
5762 if (mono_class_is_nullable (klass)) {
5763 int v = handle_unbox_nullable (cfg, bblock, *sp, ip, klass);
5764 NEW_TEMPLOAD (cfg, *sp, v);
5765 sp ++;
5766 ip += 5;
5767 break;
5770 /* Needed by the code generated in inssel.brg */
5771 mono_get_got_var (cfg);
5773 MONO_INST_NEW (cfg, ins, OP_UNBOXCAST);
5774 ins->type = STACK_OBJ;
5775 ins->inst_left = *sp;
5776 ins->klass = klass;
5777 ins->inst_newa_class = klass;
5778 ins->cil_code = ip;
5780 MONO_INST_NEW (cfg, add, OP_PADD);
5781 NEW_ICONST (cfg, vtoffset, sizeof (MonoObject));
5782 add->inst_left = ins;
5783 add->inst_right = vtoffset;
5784 add->type = STACK_MP;
5785 add->klass = klass;
5786 *sp++ = add;
5787 ip += 5;
5788 inline_costs += 2;
5789 break;
5791 case CEE_CASTCLASS:
5792 CHECK_STACK (1);
5793 --sp;
5794 CHECK_OPSIZE (5);
5795 token = read32 (ip + 1);
5796 klass = mini_get_class (method, token, generic_context);
5797 CHECK_TYPELOAD (klass);
5798 if (sp [0]->type != STACK_OBJ)
5799 UNVERIFIED;
5801 /* Needed by the code generated in inssel.brg */
5802 mono_get_got_var (cfg);
5804 if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
5806 MonoMethod *mono_castclass;
5807 MonoInst *iargs [1];
5808 MonoBasicBlock *ebblock;
5809 int costs;
5810 int temp;
5812 mono_castclass = mono_marshal_get_castclass (klass);
5813 iargs [0] = sp [0];
5815 costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass), bblock,
5816 iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
5818 g_assert (costs > 0);
5820 ip += 5;
5821 real_offset += 5;
5823 GET_BBLOCK (cfg, bbhash, bblock, ip);
5824 ebblock->next_bb = bblock;
5825 link_bblock (cfg, ebblock, bblock);
5827 temp = iargs [0]->inst_i0->inst_c0;
5828 NEW_TEMPLOAD (cfg, *sp, temp);
5830 sp++;
5831 bblock = ebblock;
5832 inline_costs += costs;
5833 } else {
5834 MONO_INST_NEW (cfg, ins, *ip);
5835 ins->type = STACK_OBJ;
5836 ins->inst_left = *sp;
5837 ins->klass = klass;
5838 ins->inst_newa_class = klass;
5839 ins->cil_code = ip;
5840 *sp++ = emit_tree (cfg, bblock, ins, ip + 5);
5841 ip += 5;
5843 break;
5844 case CEE_THROW:
5845 CHECK_STACK (1);
5846 MONO_INST_NEW (cfg, ins, *ip);
5847 --sp;
5848 ins->inst_left = *sp;
5849 ins->cil_code = ip++;
5850 bblock->out_of_line = TRUE;
5851 MONO_ADD_INS (bblock, ins);
5852 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
5853 ins->cil_code = ip - 1;
5854 MONO_ADD_INS (bblock, ins);
5855 sp = stack_start;
5857 link_bblock (cfg, bblock, end_bblock);
5858 start_new_bblock = 1;
5859 break;
5860 case CEE_LDFLD:
5861 case CEE_LDFLDA:
5862 case CEE_STFLD: {
5863 MonoInst *offset_ins;
5864 MonoClassField *field;
5865 MonoBasicBlock *ebblock;
5866 int costs;
5867 guint foffset;
5869 if (*ip == CEE_STFLD) {
5870 CHECK_STACK (2);
5871 sp -= 2;
5872 } else {
5873 CHECK_STACK (1);
5874 --sp;
5876 if (sp [0]->type == STACK_I4 || sp [0]->type == STACK_I8 || sp [0]->type == STACK_R8)
5877 UNVERIFIED;
5878 if (*ip != CEE_LDFLD && sp [0]->type == STACK_VTYPE)
5879 UNVERIFIED;
5880 CHECK_OPSIZE (5);
5881 token = read32 (ip + 1);
5882 if (method->wrapper_type != MONO_WRAPPER_NONE) {
5883 field = mono_method_get_wrapper_data (method, token);
5884 klass = field->parent;
5885 } else {
5886 field = mono_field_from_token (image, token, &klass, generic_context);
5888 if (!field)
5889 goto load_error;
5890 mono_class_init (klass);
5891 if (!dont_verify && !cfg->skip_visibility && !can_access_field (method, field))
5892 UNVERIFIED;
5894 foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset;
5895 /* FIXME: mark instructions for use in SSA */
5896 if (*ip == CEE_STFLD) {
5897 if (target_type_is_incompatible (cfg, field->type, sp [1]))
5898 UNVERIFIED;
5899 if ((klass->marshalbyref && !MONO_CHECK_THIS (sp [0])) || klass->contextbound || klass == mono_defaults.marshalbyrefobject_class) {
5900 MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type);
5901 MonoInst *iargs [5];
5903 iargs [0] = sp [0];
5904 NEW_CLASSCONST (cfg, iargs [1], klass);
5905 NEW_FIELDCONST (cfg, iargs [2], field);
5906 NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) :
5907 field->offset);
5908 iargs [4] = sp [1];
5910 if (cfg->opt & MONO_OPT_INLINE) {
5911 costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper), bblock,
5912 iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
5913 g_assert (costs > 0);
5915 ip += 5;
5916 real_offset += 5;
5918 GET_BBLOCK (cfg, bbhash, bblock, ip);
5919 ebblock->next_bb = bblock;
5920 link_bblock (cfg, ebblock, bblock);
5922 /* indicates start of a new block, and triggers a load
5923 of all stack arguments at bb boundarie */
5924 bblock = ebblock;
5926 inline_costs += costs;
5927 break;
5928 } else {
5929 mono_emit_method_call_spilled (cfg, bblock, stfld_wrapper, mono_method_signature (stfld_wrapper), iargs, ip, NULL);
5931 #if HAVE_WRITE_BARRIERS
5932 } else if (mono_type_to_stind (field->type) == CEE_STIND_REF) {
5933 /* insert call to write barrier */
5934 MonoMethod *write_barrier = mono_marshal_get_write_barrier ();
5935 MonoInst *iargs [2];
5936 NEW_ICONST (cfg, offset_ins, foffset);
5937 MONO_INST_NEW (cfg, ins, OP_PADD);
5938 ins->cil_code = ip;
5939 ins->inst_left = *sp;
5940 ins->inst_right = offset_ins;
5941 ins->type = STACK_MP;
5942 ins->klass = mono_defaults.object_class;
5943 iargs [0] = ins;
5944 iargs [1] = sp [1];
5945 mono_emit_method_call_spilled (cfg, bblock, write_barrier, mono_method_signature (write_barrier), iargs, ip, NULL);
5946 #endif
5947 #ifdef MONO_ARCH_SOFT_FLOAT
5948 } else if (mono_type_to_stind (field->type) == CEE_STIND_R4) {
5949 NEW_ICONST (cfg, offset_ins, foffset);
5950 MONO_INST_NEW (cfg, ins, OP_PADD);
5951 ins->cil_code = ip;
5952 ins->inst_left = *sp;
5953 ins->inst_right = offset_ins;
5954 ins->type = STACK_MP;
5955 ins->klass = mono_defaults.object_class;
5956 handle_store_float (cfg, bblock, ins, sp [1], ip);
5957 #endif
5958 } else {
5959 MonoInst *store;
5960 NEW_ICONST (cfg, offset_ins, foffset);
5961 MONO_INST_NEW (cfg, ins, OP_PADD);
5962 ins->cil_code = ip;
5963 ins->inst_left = *sp;
5964 ins->inst_right = offset_ins;
5965 ins->type = STACK_MP;
5967 MONO_INST_NEW (cfg, store, mono_type_to_stind (field->type));
5968 store->cil_code = ip;
5969 store->inst_left = ins;
5970 store->inst_right = sp [1];
5971 handle_loaded_temps (cfg, bblock, stack_start, sp);
5972 store->flags |= ins_flag;
5973 ins_flag = 0;
5974 if (store->opcode == CEE_STOBJ) {
5975 handle_stobj (cfg, bblock, ins, sp [1], ip,
5976 mono_class_from_mono_type (field->type), FALSE, FALSE, TRUE);
5977 } else
5978 MONO_ADD_INS (bblock, store);
5980 } else {
5981 if ((klass->marshalbyref && !MONO_CHECK_THIS (sp [0])) || klass->contextbound || klass == mono_defaults.marshalbyrefobject_class) {
5982 MonoMethod *wrapper = (*ip == CEE_LDFLDA) ? mono_marshal_get_ldflda_wrapper (field->type) : mono_marshal_get_ldfld_wrapper (field->type);
5983 MonoInst *iargs [4];
5984 int temp;
5986 iargs [0] = sp [0];
5987 NEW_CLASSCONST (cfg, iargs [1], klass);
5988 NEW_FIELDCONST (cfg, iargs [2], field);
5989 NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
5990 if ((cfg->opt & MONO_OPT_INLINE) && !MONO_TYPE_ISSTRUCT (mono_method_signature (wrapper)->ret)) {
5991 costs = inline_method (cfg, wrapper, mono_method_signature (wrapper), bblock,
5992 iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
5993 g_assert (costs > 0);
5995 ip += 5;
5996 real_offset += 5;
5998 GET_BBLOCK (cfg, bbhash, bblock, ip);
5999 ebblock->next_bb = bblock;
6000 link_bblock (cfg, ebblock, bblock);
6002 temp = iargs [0]->inst_i0->inst_c0;
6004 NEW_TEMPLOAD (cfg, *sp, temp);
6005 sp++;
6007 /* indicates start of a new block, and triggers a load of
6008 all stack arguments at bb boundarie */
6009 bblock = ebblock;
6011 inline_costs += costs;
6012 break;
6013 } else {
6014 temp = mono_emit_method_call_spilled (cfg, bblock, wrapper, mono_method_signature (wrapper), iargs, ip, NULL);
6015 NEW_TEMPLOAD (cfg, *sp, temp);
6016 sp++;
6018 } else {
6019 NEW_ICONST (cfg, offset_ins, foffset);
6020 MONO_INST_NEW (cfg, ins, OP_PADD);
6021 ins->cil_code = ip;
6022 ins->inst_left = *sp;
6023 ins->inst_right = offset_ins;
6024 ins->type = STACK_MP;
6026 if (*ip == CEE_LDFLDA) {
6027 ins->klass = mono_class_from_mono_type (field->type);
6028 *sp++ = ins;
6029 } else {
6030 MonoInst *load;
6031 MONO_INST_NEW (cfg, load, mono_type_to_ldind (field->type));
6032 type_to_eval_stack_type (field->type, load);
6033 load->cil_code = ip;
6034 load->inst_left = ins;
6035 load->flags |= ins_flag;
6036 ins_flag = 0;
6037 #ifdef MONO_ARCH_SOFT_FLOAT
6038 if (mono_type_to_ldind (field->type) == CEE_LDIND_R4) {
6039 int temp;
6040 temp = handle_load_float (cfg, bblock, ins, ip);
6041 NEW_TEMPLOAD (cfg, *sp, temp);
6042 sp++;
6043 } else
6044 #endif
6045 *sp++ = load;
6049 ip += 5;
6050 break;
6052 case CEE_LDSFLD:
6053 case CEE_LDSFLDA:
6054 case CEE_STSFLD: {
6055 MonoClassField *field;
6056 gpointer addr = NULL;
6058 CHECK_OPSIZE (5);
6059 token = read32 (ip + 1);
6060 if (method->wrapper_type != MONO_WRAPPER_NONE) {
6061 field = mono_method_get_wrapper_data (method, token);
6062 klass = field->parent;
6064 else
6065 field = mono_field_from_token (image, token, &klass, generic_context);
6066 if (!field)
6067 goto load_error;
6068 mono_class_init (klass);
6070 g_assert (!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL));
6072 if ((*ip) == CEE_STSFLD)
6073 handle_loaded_temps (cfg, bblock, stack_start, sp);
6075 /* The special_static_fields field is init'd in mono_class_vtable, so it needs
6076 * to be called here.
6078 if (!(cfg->opt & MONO_OPT_SHARED))
6079 mono_class_vtable (cfg->domain, klass);
6080 mono_domain_lock (cfg->domain);
6081 if (cfg->domain->special_static_fields)
6082 addr = g_hash_table_lookup (cfg->domain->special_static_fields, field);
6083 mono_domain_unlock (cfg->domain);
6085 if ((cfg->opt & MONO_OPT_SHARED) || (cfg->compile_aot && addr)) {
6086 int temp;
6087 MonoInst *iargs [2];
6088 MonoInst *domain_var;
6090 g_assert (field->parent);
6091 /* avoid depending on undefined C behavior in sequence points */
6092 domain_var = mono_get_domainvar (cfg);
6093 NEW_TEMPLOAD (cfg, iargs [0], domain_var->inst_c0);
6094 NEW_FIELDCONST (cfg, iargs [1], field);
6095 temp = mono_emit_jit_icall (cfg, bblock, mono_class_static_field_address, iargs, ip);
6096 NEW_TEMPLOAD (cfg, ins, temp);
6097 } else {
6098 MonoVTable *vtable;
6099 vtable = mono_class_vtable (cfg->domain, klass);
6100 if (!addr) {
6101 if (mini_field_access_needs_cctor_run (cfg, method, vtable) && !(g_slist_find (class_inits, vtable))) {
6102 guint8 *tramp = mono_create_class_init_trampoline (vtable);
6103 mono_emit_native_call (cfg, bblock, tramp,
6104 helper_sig_class_init_trampoline,
6105 NULL, ip, FALSE, FALSE);
6106 if (cfg->verbose_level > 2)
6107 g_print ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, field->name);
6108 class_inits = g_slist_prepend (class_inits, vtable);
6109 } else {
6110 if (cfg->run_cctors) {
6111 /* This makes so that inline cannot trigger */
6112 /* .cctors: too many apps depend on them */
6113 /* running with a specific order... */
6114 if (! vtable->initialized)
6115 INLINE_FAILURE;
6116 mono_runtime_class_init (vtable);
6119 addr = (char*)vtable->data + field->offset;
6121 if (cfg->compile_aot)
6122 NEW_SFLDACONST (cfg, ins, field);
6123 else
6124 NEW_PCONST (cfg, ins, addr);
6125 ins->cil_code = ip;
6126 } else {
6128 * insert call to mono_threads_get_static_data (GPOINTER_TO_UINT (addr))
6129 * This could be later optimized to do just a couple of
6130 * memory dereferences with constant offsets.
6132 int temp;
6133 MonoInst *iargs [1];
6134 NEW_ICONST (cfg, iargs [0], GPOINTER_TO_UINT (addr));
6135 temp = mono_emit_jit_icall (cfg, bblock, mono_get_special_static_data, iargs, ip);
6136 NEW_TEMPLOAD (cfg, ins, temp);
6140 /* FIXME: mark instructions for use in SSA */
6141 if (*ip == CEE_LDSFLDA) {
6142 ins->klass = mono_class_from_mono_type (field->type);
6143 *sp++ = ins;
6144 } else if (*ip == CEE_STSFLD) {
6145 MonoInst *store;
6146 CHECK_STACK (1);
6147 sp--;
6148 MONO_INST_NEW (cfg, store, mono_type_to_stind (field->type));
6149 store->cil_code = ip;
6150 store->inst_left = ins;
6151 store->inst_right = sp [0];
6152 store->flags |= ins_flag;
6153 ins_flag = 0;
6155 if (store->opcode == CEE_STOBJ) {
6156 handle_stobj (cfg, bblock, ins, sp [0], ip, mono_class_from_mono_type (field->type), FALSE, FALSE, FALSE);
6157 } else
6158 MONO_ADD_INS (bblock, store);
6159 } else {
6160 gboolean is_const = FALSE;
6161 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
6162 if (!((cfg->opt & MONO_OPT_SHARED) || cfg->compile_aot) &&
6163 vtable->initialized && (field->type->attrs & FIELD_ATTRIBUTE_INIT_ONLY)) {
6164 gpointer addr = (char*)vtable->data + field->offset;
6165 int ro_type = field->type->type;
6166 if (ro_type == MONO_TYPE_VALUETYPE && field->type->data.klass->enumtype) {
6167 ro_type = field->type->data.klass->enum_basetype->type;
6169 /* g_print ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, field->name);*/
6170 is_const = TRUE;
6171 switch (ro_type) {
6172 case MONO_TYPE_BOOLEAN:
6173 case MONO_TYPE_U1:
6174 NEW_ICONST (cfg, *sp, *((guint8 *)addr));
6175 sp++;
6176 break;
6177 case MONO_TYPE_I1:
6178 NEW_ICONST (cfg, *sp, *((gint8 *)addr));
6179 sp++;
6180 break;
6181 case MONO_TYPE_CHAR:
6182 case MONO_TYPE_U2:
6183 NEW_ICONST (cfg, *sp, *((guint16 *)addr));
6184 sp++;
6185 break;
6186 case MONO_TYPE_I2:
6187 NEW_ICONST (cfg, *sp, *((gint16 *)addr));
6188 sp++;
6189 break;
6190 break;
6191 case MONO_TYPE_I4:
6192 NEW_ICONST (cfg, *sp, *((gint32 *)addr));
6193 sp++;
6194 break;
6195 case MONO_TYPE_U4:
6196 NEW_ICONST (cfg, *sp, *((guint32 *)addr));
6197 sp++;
6198 break;
6199 #ifndef HAVE_MOVING_COLLECTOR
6200 case MONO_TYPE_I:
6201 case MONO_TYPE_U:
6202 case MONO_TYPE_STRING:
6203 case MONO_TYPE_OBJECT:
6204 case MONO_TYPE_CLASS:
6205 case MONO_TYPE_SZARRAY:
6206 case MONO_TYPE_PTR:
6207 case MONO_TYPE_FNPTR:
6208 case MONO_TYPE_ARRAY:
6209 NEW_PCONST (cfg, *sp, *((gpointer *)addr));
6210 type_to_eval_stack_type (field->type, *sp);
6211 sp++;
6212 break;
6213 #endif
6214 case MONO_TYPE_I8:
6215 case MONO_TYPE_U8:
6216 MONO_INST_NEW (cfg, *sp, OP_I8CONST);
6217 sp [0]->type = STACK_I8;
6218 sp [0]->inst_l = *((gint64 *)addr);
6219 sp++;
6220 break;
6221 case MONO_TYPE_R4:
6222 case MONO_TYPE_R8:
6223 case MONO_TYPE_VALUETYPE:
6224 default:
6225 is_const = FALSE;
6226 break;
6230 if (!is_const) {
6231 MonoInst *load;
6232 CHECK_STACK_OVF (1);
6233 MONO_INST_NEW (cfg, load, mono_type_to_ldind (field->type));
6234 type_to_eval_stack_type (field->type, load);
6235 load->cil_code = ip;
6236 load->inst_left = ins;
6237 *sp++ = load;
6238 load->flags |= ins_flag;
6239 ins_flag = 0;
6240 /* fixme: dont see the problem why this does not work */
6241 //cfg->disable_aot = TRUE;
6244 ip += 5;
6245 break;
6247 case CEE_STOBJ:
6248 CHECK_STACK (2);
6249 sp -= 2;
6250 CHECK_OPSIZE (5);
6251 token = read32 (ip + 1);
6252 klass = mini_get_class (method, token, generic_context);
6253 CHECK_TYPELOAD (klass);
6254 n = mono_type_to_stind (&klass->byval_arg);
6255 if (n == CEE_STOBJ) {
6256 handle_stobj (cfg, bblock, sp [0], sp [1], ip, klass, FALSE, FALSE, TRUE);
6257 } else {
6258 /* FIXME: should check item at sp [1] is compatible with the type of the store. */
6259 MonoInst *store;
6260 MONO_INST_NEW (cfg, store, n);
6261 store->cil_code = ip;
6262 store->inst_left = sp [0];
6263 store->inst_right = sp [1];
6264 store->flags |= ins_flag;
6265 MONO_ADD_INS (bblock, store);
6267 ins_flag = 0;
6268 ip += 5;
6269 inline_costs += 1;
6270 break;
6271 case CEE_BOX: {
6272 MonoInst *val;
6273 CHECK_STACK (1);
6274 --sp;
6275 val = *sp;
6276 CHECK_OPSIZE (5);
6277 token = read32 (ip + 1);
6278 klass = mini_get_class (method, token, generic_context);
6279 CHECK_TYPELOAD (klass);
6281 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
6282 *sp++ = val;
6283 ip += 5;
6284 break;
6286 if (klass == mono_defaults.void_class)
6287 UNVERIFIED;
6288 if (target_type_is_incompatible (cfg, &klass->byval_arg, *sp))
6289 UNVERIFIED;
6290 /* frequent check in generic code: box (struct), brtrue */
6291 if (!mono_class_is_nullable (klass) &&
6292 ip + 5 < end && ip_in_bb (cfg, bblock, ip + 5) && (ip [5] == CEE_BRTRUE || ip [5] == CEE_BRTRUE_S)) {
6293 /*g_print ("box-brtrue opt at 0x%04x in %s\n", real_offset, method->name);*/
6294 MONO_INST_NEW (cfg, ins, CEE_POP);
6295 MONO_ADD_INS (bblock, ins);
6296 ins->cil_code = ip;
6297 ins->inst_i0 = *sp;
6298 ip += 5;
6299 MONO_INST_NEW (cfg, ins, CEE_BR);
6300 ins->cil_code = ip;
6301 MONO_ADD_INS (bblock, ins);
6302 if (*ip == CEE_BRTRUE_S) {
6303 CHECK_OPSIZE (2);
6304 ip++;
6305 target = ip + 1 + (signed char)(*ip);
6306 ip++;
6307 } else {
6308 CHECK_OPSIZE (5);
6309 ip++;
6310 target = ip + 4 + (gint)(read32 (ip));
6311 ip += 4;
6313 GET_BBLOCK (cfg, bbhash, tblock, target);
6314 link_bblock (cfg, bblock, tblock);
6315 CHECK_BBLOCK (target, ip, tblock);
6316 ins->inst_target_bb = tblock;
6317 if (sp != stack_start) {
6318 handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
6319 sp = stack_start;
6320 CHECK_UNVERIFIABLE (cfg);
6322 start_new_bblock = 1;
6323 break;
6325 *sp++ = handle_box (cfg, bblock, val, ip, klass);
6326 ip += 5;
6327 inline_costs += 1;
6328 break;
6330 case CEE_NEWARR:
6331 CHECK_STACK (1);
6332 MONO_INST_NEW (cfg, ins, *ip);
6333 ins->cil_code = ip;
6334 --sp;
6336 CHECK_OPSIZE (5);
6337 token = read32 (ip + 1);
6339 /* allocate the domainvar - becaus this is used in decompose_foreach */
6340 if (cfg->opt & MONO_OPT_SHARED) {
6341 mono_get_domainvar (cfg);
6342 /* LAME-IR: Mark it as used since otherwise it will be optimized away */
6343 cfg->domainvar->flags |= MONO_INST_VOLATILE;
6346 /* Ditto */
6347 mono_get_got_var (cfg);
6349 klass = mini_get_class (method, token, generic_context);
6350 CHECK_TYPELOAD (klass);
6351 ins->inst_newa_class = klass;
6352 ins->inst_newa_len = *sp;
6353 ins->type = STACK_OBJ;
6354 ins->klass = klass;
6355 ip += 5;
6356 *sp++ = ins;
6358 * we store the object so calls to create the array are not interleaved
6359 * with the arguments of other calls.
6361 if (1) {
6362 MonoInst *store, *temp, *load;
6363 --sp;
6364 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
6365 NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
6366 store->cil_code = ins->cil_code;
6367 MONO_ADD_INS (bblock, store);
6368 NEW_TEMPLOAD (cfg, load, temp->inst_c0);
6369 load->cil_code = ins->cil_code;
6370 *sp++ = load;
6372 inline_costs += 1;
6373 break;
6374 case CEE_LDLEN:
6375 CHECK_STACK (1);
6376 --sp;
6377 if (sp [0]->type != STACK_OBJ)
6378 UNVERIFIED;
6379 MONO_INST_NEW (cfg, ins, *ip);
6380 ins->cil_code = ip++;
6381 ins->inst_left = *sp;
6382 ins->type = STACK_PTR;
6383 *sp++ = ins;
6384 break;
6385 case CEE_LDELEMA:
6386 CHECK_STACK (2);
6387 sp -= 2;
6388 CHECK_OPSIZE (5);
6389 if (sp [0]->type != STACK_OBJ)
6390 UNVERIFIED;
6392 klass = mini_get_class (method, read32 (ip + 1), generic_context);
6393 CHECK_TYPELOAD (klass);
6394 /* we need to make sure that this array is exactly the type it needs
6395 * to be for correctness. the wrappers are lax with their usage
6396 * so we need to ignore them here
6398 if (!klass->valuetype && method->wrapper_type == MONO_WRAPPER_NONE) {
6399 MonoInst* check;
6400 MONO_INST_NEW (cfg, check, OP_CHECK_ARRAY_TYPE);
6401 check->cil_code = ip;
6402 check->klass = klass;
6403 check->inst_left = sp [0];
6404 check->type = STACK_OBJ;
6405 check->klass = klass;
6406 sp [0] = check;
6409 mono_class_init (klass);
6410 NEW_LDELEMA (cfg, ins, sp, klass);
6411 ins->cil_code = ip;
6412 *sp++ = ins;
6413 ip += 5;
6414 break;
6415 case CEE_LDELEM_ANY: {
6416 MonoInst *load;
6417 CHECK_STACK (2);
6418 sp -= 2;
6419 if (sp [0]->type != STACK_OBJ)
6420 UNVERIFIED;
6421 CHECK_OPSIZE (5);
6422 token = read32 (ip + 1);
6423 klass = mono_class_get_full (image, token, generic_context);
6424 CHECK_TYPELOAD (klass);
6425 mono_class_init (klass);
6426 NEW_LDELEMA (cfg, load, sp, klass);
6427 load->cil_code = ip;
6428 MONO_INST_NEW (cfg, ins, mono_type_to_ldind (&klass->byval_arg));
6429 ins->cil_code = ip;
6430 ins->inst_left = load;
6431 *sp++ = ins;
6432 type_to_eval_stack_type (&klass->byval_arg, ins);
6433 ip += 5;
6434 break;
6436 case CEE_LDELEM_I1:
6437 case CEE_LDELEM_U1:
6438 case CEE_LDELEM_I2:
6439 case CEE_LDELEM_U2:
6440 case CEE_LDELEM_I4:
6441 case CEE_LDELEM_U4:
6442 case CEE_LDELEM_I8:
6443 case CEE_LDELEM_I:
6444 case CEE_LDELEM_R4:
6445 case CEE_LDELEM_R8:
6446 case CEE_LDELEM_REF: {
6447 MonoInst *load;
6449 * translate to:
6450 * ldind.x (ldelema (array, index))
6451 * ldelema does the bounds check
6453 CHECK_STACK (2);
6454 sp -= 2;
6455 if (sp [0]->type != STACK_OBJ)
6456 UNVERIFIED;
6457 klass = array_access_to_klass (*ip);
6458 NEW_LDELEMA (cfg, load, sp, klass);
6459 load->cil_code = ip;
6460 #ifdef MONO_ARCH_SOFT_FLOAT
6461 if (*ip == CEE_LDELEM_R4) {
6462 int temp;
6463 temp = handle_load_float (cfg, bblock, load, ip);
6464 NEW_TEMPLOAD (cfg, *sp, temp);
6465 sp++;
6466 ++ip;
6467 break;
6469 #endif
6470 MONO_INST_NEW (cfg, ins, ldelem_to_ldind [*ip - CEE_LDELEM_I1]);
6471 ins->cil_code = ip;
6472 ins->inst_left = load;
6473 *sp++ = ins;
6474 ins->type = ldind_type [ins->opcode - CEE_LDIND_I1];
6475 ins->klass = klass;
6476 ++ip;
6477 break;
6479 case CEE_STELEM_I:
6480 case CEE_STELEM_I1:
6481 case CEE_STELEM_I2:
6482 case CEE_STELEM_I4:
6483 case CEE_STELEM_I8:
6484 case CEE_STELEM_R4:
6485 case CEE_STELEM_R8: {
6486 MonoInst *load;
6488 * translate to:
6489 * stind.x (ldelema (array, index), val)
6490 * ldelema does the bounds check
6492 CHECK_STACK (3);
6493 sp -= 3;
6494 if (sp [0]->type != STACK_OBJ)
6495 UNVERIFIED;
6496 klass = array_access_to_klass (*ip);
6497 NEW_LDELEMA (cfg, load, sp, klass);
6498 load->cil_code = ip;
6499 #ifdef MONO_ARCH_SOFT_FLOAT
6500 if (*ip == CEE_STELEM_R4) {
6501 handle_store_float (cfg, bblock, load, sp [2], ip);
6502 ip++;
6503 break;
6505 #endif
6506 MONO_INST_NEW (cfg, ins, stelem_to_stind [*ip - CEE_STELEM_I]);
6507 ins->cil_code = ip;
6508 ins->inst_left = load;
6509 ins->inst_right = sp [2];
6510 ++ip;
6511 handle_loaded_temps (cfg, bblock, stack_start, sp);
6512 MONO_ADD_INS (bblock, ins);
6513 inline_costs += 1;
6514 break;
6516 case CEE_STELEM_ANY: {
6517 MonoInst *load;
6519 * translate to:
6520 * stind.x (ldelema (array, index), val)
6521 * ldelema does the bounds check
6523 CHECK_STACK (3);
6524 sp -= 3;
6525 if (sp [0]->type != STACK_OBJ)
6526 UNVERIFIED;
6527 CHECK_OPSIZE (5);
6528 token = read32 (ip + 1);
6529 klass = mono_class_get_full (image, token, generic_context);
6530 CHECK_TYPELOAD (klass);
6531 mono_class_init (klass);
6532 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
6533 MonoMethod* helper = mono_marshal_get_stelemref ();
6534 MonoInst *iargs [3];
6535 handle_loaded_temps (cfg, bblock, stack_start, sp);
6537 iargs [2] = sp [2];
6538 iargs [1] = sp [1];
6539 iargs [0] = sp [0];
6541 mono_emit_method_call_spilled (cfg, bblock, helper, mono_method_signature (helper), iargs, ip, NULL);
6542 } else {
6543 NEW_LDELEMA (cfg, load, sp, klass);
6544 load->cil_code = ip;
6546 n = mono_type_to_stind (&klass->byval_arg);
6547 if (n == CEE_STOBJ)
6548 handle_stobj (cfg, bblock, load, sp [2], ip, klass, FALSE, FALSE, TRUE);
6549 else {
6550 MONO_INST_NEW (cfg, ins, n);
6551 ins->cil_code = ip;
6552 ins->inst_left = load;
6553 ins->inst_right = sp [2];
6554 handle_loaded_temps (cfg, bblock, stack_start, sp);
6555 MONO_ADD_INS (bblock, ins);
6558 ip += 5;
6559 inline_costs += 1;
6560 break;
6562 case CEE_STELEM_REF: {
6563 MonoInst *iargs [3];
6564 MonoMethod* helper = mono_marshal_get_stelemref ();
6566 CHECK_STACK (3);
6567 sp -= 3;
6568 if (sp [0]->type != STACK_OBJ)
6569 UNVERIFIED;
6570 if (sp [2]->type != STACK_OBJ)
6571 UNVERIFIED;
6573 handle_loaded_temps (cfg, bblock, stack_start, sp);
6575 iargs [2] = sp [2];
6576 iargs [1] = sp [1];
6577 iargs [0] = sp [0];
6579 mono_emit_method_call_spilled (cfg, bblock, helper, mono_method_signature (helper), iargs, ip, NULL);
6582 MonoInst *group;
6583 NEW_GROUP (cfg, group, sp [0], sp [1]);
6584 MONO_INST_NEW (cfg, ins, CEE_STELEM_REF);
6585 ins->cil_code = ip;
6586 ins->inst_left = group;
6587 ins->inst_right = sp [2];
6588 MONO_ADD_INS (bblock, ins);
6591 ++ip;
6592 inline_costs += 1;
6593 break;
6595 case CEE_CKFINITE: {
6596 MonoInst *store, *temp;
6597 CHECK_STACK (1);
6599 /* this instr. can throw exceptions as side effect,
6600 * so we cant eliminate dead code which contains CKFINITE opdodes.
6601 * Spilling to memory makes sure that we always perform
6602 * this check */
6605 MONO_INST_NEW (cfg, ins, CEE_CKFINITE);
6606 ins->cil_code = ip;
6607 ins->inst_left = sp [-1];
6608 temp = mono_compile_create_var (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL);
6610 NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
6611 store->cil_code = ip;
6612 MONO_ADD_INS (bblock, store);
6614 NEW_TEMPLOAD (cfg, sp [-1], temp->inst_c0);
6616 ++ip;
6617 break;
6619 case CEE_REFANYVAL:
6620 CHECK_STACK (1);
6621 MONO_INST_NEW (cfg, ins, *ip);
6622 --sp;
6623 CHECK_OPSIZE (5);
6624 klass = mono_class_get_full (image, read32 (ip + 1), generic_context);
6625 CHECK_TYPELOAD (klass);
6626 mono_class_init (klass);
6627 ins->type = STACK_MP;
6628 ins->inst_left = *sp;
6629 ins->klass = klass;
6630 ins->inst_newa_class = klass;
6631 ins->cil_code = ip;
6632 ip += 5;
6633 *sp++ = ins;
6634 break;
6635 case CEE_MKREFANY: {
6636 MonoInst *loc, *klassconst;
6638 CHECK_STACK (1);
6639 MONO_INST_NEW (cfg, ins, *ip);
6640 --sp;
6641 CHECK_OPSIZE (5);
6642 klass = mono_class_get_full (image, read32 (ip + 1), generic_context);
6643 CHECK_TYPELOAD (klass);
6644 mono_class_init (klass);
6645 ins->cil_code = ip;
6647 loc = mono_compile_create_var (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL);
6648 NEW_TEMPLOADA (cfg, ins->inst_right, loc->inst_c0);
6650 NEW_PCONST (cfg, klassconst, klass);
6651 NEW_GROUP (cfg, ins->inst_left, *sp, klassconst);
6653 MONO_ADD_INS (bblock, ins);
6655 NEW_TEMPLOAD (cfg, *sp, loc->inst_c0);
6656 ++sp;
6657 ip += 5;
6658 break;
6660 case CEE_LDTOKEN: {
6661 gpointer handle;
6662 MonoClass *handle_class;
6664 CHECK_STACK_OVF (1);
6666 CHECK_OPSIZE (5);
6667 n = read32 (ip + 1);
6669 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
6670 handle = mono_method_get_wrapper_data (method, n);
6671 handle_class = mono_method_get_wrapper_data (method, n + 1);
6672 if (handle_class == mono_defaults.typehandle_class)
6673 handle = &((MonoClass*)handle)->byval_arg;
6675 else {
6676 handle = mono_ldtoken (image, n, &handle_class, generic_context);
6678 if (!handle)
6679 goto load_error;
6680 mono_class_init (handle_class);
6682 if (cfg->opt & MONO_OPT_SHARED) {
6683 int temp;
6684 MonoInst *res, *store, *addr, *vtvar, *iargs [3];
6686 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
6688 NEW_IMAGECONST (cfg, iargs [0], image);
6689 NEW_ICONST (cfg, iargs [1], n);
6690 NEW_PCONST (cfg, iargs [2], generic_context);
6691 temp = mono_emit_jit_icall (cfg, bblock, mono_ldtoken_wrapper, iargs, ip);
6692 NEW_TEMPLOAD (cfg, res, temp);
6693 NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
6694 NEW_INDSTORE (cfg, store, addr, res, &mono_defaults.int_class->byval_arg);
6695 MONO_ADD_INS (bblock, store);
6696 NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
6697 } else {
6698 if ((ip [5] == CEE_CALL) && (cmethod = mini_get_method (method, read32 (ip + 6), NULL, generic_context)) &&
6699 (cmethod->klass == mono_defaults.monotype_class->parent) &&
6700 (strcmp (cmethod->name, "GetTypeFromHandle") == 0) && ip_in_bb (cfg, bblock, ip + 5)) {
6701 MonoClass *tclass = mono_class_from_mono_type (handle);
6702 mono_class_init (tclass);
6703 if (cfg->compile_aot)
6704 NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, image, n);
6705 else
6706 NEW_PCONST (cfg, ins, mono_type_get_object (cfg->domain, handle));
6707 ins->type = STACK_OBJ;
6708 ins->klass = cmethod->klass;
6709 ip += 5;
6710 } else {
6711 MonoInst *store, *addr, *vtvar;
6713 if (cfg->compile_aot)
6714 NEW_LDTOKENCONST (cfg, ins, image, n);
6715 else
6716 NEW_PCONST (cfg, ins, handle);
6717 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
6718 NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
6719 NEW_INDSTORE (cfg, store, addr, ins, &mono_defaults.int_class->byval_arg);
6720 MONO_ADD_INS (bblock, store);
6721 NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
6725 *sp++ = ins;
6726 ip += 5;
6727 break;
6729 case CEE_CONV_U2:
6730 case CEE_CONV_U1:
6731 case CEE_CONV_I:
6732 CHECK_STACK (1);
6733 ADD_UNOP (*ip);
6734 ip++;
6735 break;
6736 case CEE_ADD_OVF:
6737 case CEE_ADD_OVF_UN:
6738 case CEE_MUL_OVF:
6739 case CEE_MUL_OVF_UN:
6740 case CEE_SUB_OVF:
6741 case CEE_SUB_OVF_UN:
6742 CHECK_STACK (2);
6743 ADD_BINOP (*ip);
6744 if (mono_find_jit_opcode_emulation (ins->opcode)) {
6745 --sp;
6746 *sp++ = emit_tree (cfg, bblock, ins, ip + 1);
6748 ip++;
6749 break;
6750 case CEE_ENDFINALLY:
6751 MONO_INST_NEW (cfg, ins, *ip);
6752 MONO_ADD_INS (bblock, ins);
6753 ins->cil_code = ip++;
6754 start_new_bblock = 1;
6757 * Control will leave the method so empty the stack, otherwise
6758 * the next basic block will start with a nonempty stack.
6760 while (sp != stack_start) {
6761 MONO_INST_NEW (cfg, ins, CEE_POP);
6762 ins->cil_code = ip;
6763 sp--;
6764 ins->inst_i0 = *sp;
6765 MONO_ADD_INS (bblock, ins);
6767 break;
6768 case CEE_LEAVE:
6769 case CEE_LEAVE_S: {
6770 GList *handlers;
6772 if (*ip == CEE_LEAVE) {
6773 CHECK_OPSIZE (5);
6774 target = ip + 5 + (gint32)read32(ip + 1);
6775 } else {
6776 CHECK_OPSIZE (2);
6777 target = ip + 2 + (signed char)(ip [1]);
6780 /* empty the stack */
6781 while (sp != stack_start) {
6782 MONO_INST_NEW (cfg, ins, CEE_POP);
6783 ins->cil_code = ip;
6784 sp--;
6785 ins->inst_i0 = *sp;
6786 MONO_ADD_INS (bblock, ins);
6790 * If this leave statement is in a catch block, check for a
6791 * pending exception, and rethrow it if necessary.
6793 for (i = 0; i < header->num_clauses; ++i) {
6794 MonoExceptionClause *clause = &header->clauses [i];
6797 * Use <= in the final comparison to handle clauses with multiple
6798 * leave statements, like in bug #78024.
6799 * The ordering of the exception clauses guarantees that we find the
6800 * innermost clause.
6802 if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) && (ip - header->code + ((*ip == CEE_LEAVE) ? 5 : 2)) <= (clause->handler_offset + clause->handler_len)) {
6803 int temp;
6804 MonoInst *load;
6806 NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, clause->handler_offset)->inst_c0);
6807 load->cil_code = ip;
6809 temp = mono_emit_jit_icall (cfg, bblock, mono_thread_get_pending_exception, NULL, ip);
6810 NEW_TEMPLOAD (cfg, *sp, temp);
6812 MONO_INST_NEW (cfg, ins, OP_THROW_OR_NULL);
6813 ins->inst_left = *sp;
6814 ins->inst_right = load;
6815 ins->cil_code = ip;
6816 MONO_ADD_INS (bblock, ins);
6820 /* fixme: call fault handler ? */
6822 if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
6823 GList *tmp;
6824 for (tmp = handlers; tmp; tmp = tmp->next) {
6825 tblock = tmp->data;
6826 link_bblock (cfg, bblock, tblock);
6827 MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
6828 ins->cil_code = ip;
6829 ins->inst_target_bb = tblock;
6830 MONO_ADD_INS (bblock, ins);
6832 g_list_free (handlers);
6835 MONO_INST_NEW (cfg, ins, CEE_BR);
6836 ins->cil_code = ip;
6837 MONO_ADD_INS (bblock, ins);
6838 GET_BBLOCK (cfg, bbhash, tblock, target);
6839 link_bblock (cfg, bblock, tblock);
6840 CHECK_BBLOCK (target, ip, tblock);
6841 ins->inst_target_bb = tblock;
6842 start_new_bblock = 1;
6844 if (*ip == CEE_LEAVE)
6845 ip += 5;
6846 else
6847 ip += 2;
6849 break;
6851 case CEE_STIND_I:
6852 CHECK_STACK (2);
6853 MONO_INST_NEW (cfg, ins, *ip);
6854 sp -= 2;
6855 handle_loaded_temps (cfg, bblock, stack_start, sp);
6856 MONO_ADD_INS (bblock, ins);
6857 ins->cil_code = ip++;
6858 ins->inst_i0 = sp [0];
6859 ins->inst_i1 = sp [1];
6860 inline_costs += 1;
6861 break;
6862 case CEE_CONV_U:
6863 CHECK_STACK (1);
6864 ADD_UNOP (*ip);
6865 ip++;
6866 break;
6867 /* trampoline mono specific opcodes */
6868 case MONO_CUSTOM_PREFIX: {
6870 g_assert (method->wrapper_type != MONO_WRAPPER_NONE);
6872 CHECK_OPSIZE (2);
6873 switch (ip [1]) {
6875 case CEE_MONO_ICALL: {
6876 int temp;
6877 gpointer func;
6878 MonoJitICallInfo *info;
6880 token = read32 (ip + 2);
6881 func = mono_method_get_wrapper_data (method, token);
6882 info = mono_find_jit_icall_by_addr (func);
6883 g_assert (info);
6885 CHECK_STACK (info->sig->param_count);
6886 sp -= info->sig->param_count;
6888 temp = mono_emit_jit_icall (cfg, bblock, info->func, sp, ip);
6889 if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
6890 NEW_TEMPLOAD (cfg, *sp, temp);
6891 sp++;
6894 ip += 6;
6895 inline_costs += 10 * num_calls++;
6897 break;
6899 case CEE_MONO_LDPTR:
6900 CHECK_STACK_OVF (1);
6901 CHECK_OPSIZE (6);
6902 token = read32 (ip + 2);
6903 NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, token));
6904 ins->cil_code = ip;
6905 *sp++ = ins;
6906 ip += 6;
6907 inline_costs += 10 * num_calls++;
6908 /* Can't embed random pointers into AOT code */
6909 cfg->disable_aot = 1;
6910 break;
6911 case CEE_MONO_VTADDR:
6912 CHECK_STACK (1);
6913 --sp;
6914 MONO_INST_NEW (cfg, ins, OP_VTADDR);
6915 ins->cil_code = ip;
6916 ins->type = STACK_MP;
6917 ins->inst_left = *sp;
6918 *sp++ = ins;
6919 ip += 2;
6920 break;
6921 case CEE_MONO_NEWOBJ: {
6922 MonoInst *iargs [2];
6923 int temp;
6924 CHECK_STACK_OVF (1);
6925 CHECK_OPSIZE (6);
6926 token = read32 (ip + 2);
6927 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
6928 mono_class_init (klass);
6929 NEW_DOMAINCONST (cfg, iargs [0]);
6930 NEW_CLASSCONST (cfg, iargs [1], klass);
6931 temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip);
6932 NEW_TEMPLOAD (cfg, *sp, temp);
6933 sp++;
6934 ip += 6;
6935 inline_costs += 10 * num_calls++;
6936 break;
6938 case CEE_MONO_OBJADDR:
6939 CHECK_STACK (1);
6940 --sp;
6941 MONO_INST_NEW (cfg, ins, OP_OBJADDR);
6942 ins->cil_code = ip;
6943 ins->type = STACK_MP;
6944 ins->inst_left = *sp;
6945 *sp++ = ins;
6946 ip += 2;
6947 break;
6948 case CEE_MONO_LDNATIVEOBJ:
6949 CHECK_STACK (1);
6950 CHECK_OPSIZE (6);
6951 token = read32 (ip + 2);
6952 klass = mono_method_get_wrapper_data (method, token);
6953 g_assert (klass->valuetype);
6954 mono_class_init (klass);
6955 NEW_INDLOAD (cfg, ins, sp [-1], &klass->byval_arg);
6956 sp [-1] = ins;
6957 ip += 6;
6958 break;
6959 case CEE_MONO_RETOBJ:
6960 g_assert (cfg->ret);
6961 g_assert (mono_method_signature (method)->pinvoke);
6962 CHECK_STACK (1);
6963 --sp;
6965 CHECK_OPSIZE (6);
6966 token = read32 (ip + 2);
6967 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
6969 NEW_RETLOADA (cfg, ins);
6970 handle_stobj (cfg, bblock, ins, *sp, ip, klass, FALSE, TRUE, FALSE);
6972 if (sp != stack_start)
6973 UNVERIFIED;
6975 MONO_INST_NEW (cfg, ins, CEE_BR);
6976 ins->cil_code = ip;
6977 ins->inst_target_bb = end_bblock;
6978 MONO_ADD_INS (bblock, ins);
6979 link_bblock (cfg, bblock, end_bblock);
6980 start_new_bblock = 1;
6981 ip += 6;
6982 break;
6983 case CEE_MONO_CISINST:
6984 case CEE_MONO_CCASTCLASS: {
6985 int token;
6986 CHECK_STACK (1);
6987 --sp;
6988 CHECK_OPSIZE (6);
6989 token = read32 (ip + 2);
6990 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
6991 MONO_INST_NEW (cfg, ins, (ip [1] == CEE_MONO_CISINST) ? OP_CISINST : OP_CCASTCLASS);
6992 ins->type = STACK_I4;
6993 ins->inst_left = *sp;
6994 ins->inst_newa_class = klass;
6995 ins->cil_code = ip;
6996 *sp++ = emit_tree (cfg, bblock, ins, ip + 6);
6997 ip += 6;
6998 break;
7000 case CEE_MONO_SAVE_LMF:
7001 case CEE_MONO_RESTORE_LMF:
7002 #ifdef MONO_ARCH_HAVE_LMF_OPS
7003 MONO_INST_NEW (cfg, ins, (ip [1] == CEE_MONO_SAVE_LMF) ? OP_SAVE_LMF : OP_RESTORE_LMF);
7004 MONO_ADD_INS (bblock, ins);
7005 cfg->need_lmf_area = TRUE;
7006 #endif
7007 ip += 2;
7008 break;
7009 case CEE_MONO_CLASSCONST:
7010 CHECK_STACK_OVF (1);
7011 CHECK_OPSIZE (6);
7012 token = read32 (ip + 2);
7013 NEW_CLASSCONST (cfg, ins, mono_method_get_wrapper_data (method, token));
7014 ins->cil_code = ip;
7015 *sp++ = ins;
7016 ip += 6;
7017 inline_costs += 10 * num_calls++;
7018 break;
7019 case CEE_MONO_NOT_TAKEN:
7020 bblock->out_of_line = TRUE;
7021 ip += 2;
7022 break;
7023 default:
7024 g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]);
7025 break;
7027 break;
7029 case CEE_PREFIX1: {
7030 CHECK_OPSIZE (2);
7031 switch (ip [1]) {
7032 case CEE_ARGLIST: {
7033 /* somewhat similar to LDTOKEN */
7034 MonoInst *addr, *vtvar;
7035 CHECK_STACK_OVF (1);
7036 vtvar = mono_compile_create_var (cfg, &mono_defaults.argumenthandle_class->byval_arg, OP_LOCAL);
7038 NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
7039 addr->cil_code = ip;
7040 MONO_INST_NEW (cfg, ins, OP_ARGLIST);
7041 ins->cil_code = ip;
7042 ins->inst_left = addr;
7043 MONO_ADD_INS (bblock, ins);
7044 NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
7045 ins->cil_code = ip;
7046 *sp++ = ins;
7047 ip += 2;
7048 break;
7050 case CEE_CEQ:
7051 case CEE_CGT:
7052 case CEE_CGT_UN:
7053 case CEE_CLT:
7054 case CEE_CLT_UN: {
7055 MonoInst *cmp;
7056 CHECK_STACK (2);
7058 * The following transforms:
7059 * CEE_CEQ into OP_CEQ
7060 * CEE_CGT into OP_CGT
7061 * CEE_CGT_UN into OP_CGT_UN
7062 * CEE_CLT into OP_CLT
7063 * CEE_CLT_UN into OP_CLT_UN
7065 MONO_INST_NEW (cfg, cmp, 256 + ip [1]);
7067 MONO_INST_NEW (cfg, ins, cmp->opcode);
7068 sp -= 2;
7069 cmp->inst_i0 = sp [0];
7070 cmp->inst_i1 = sp [1];
7071 cmp->cil_code = ip;
7072 type_from_op (cmp);
7073 CHECK_TYPE (cmp);
7074 ins->cil_code = ip;
7075 ins->type = STACK_I4;
7076 ins->inst_i0 = cmp;
7077 #if MONO_ARCH_SOFT_FLOAT
7078 if (sp [0]->type == STACK_R8) {
7079 cmp->type = STACK_I4;
7080 *sp++ = emit_tree (cfg, bblock, cmp, ip + 2);
7081 ip += 2;
7082 break;
7084 #endif
7085 if ((sp [0]->type == STACK_I8) || ((sizeof (gpointer) == 8) && ((sp [0]->type == STACK_PTR) || (sp [0]->type == STACK_OBJ) || (sp [0]->type == STACK_MP))))
7086 cmp->opcode = OP_LCOMPARE;
7087 else
7088 cmp->opcode = OP_COMPARE;
7089 *sp++ = ins;
7090 /* spill it to reduce the expression complexity
7091 * and workaround bug 54209
7093 if (cmp->inst_left->type == STACK_I8) {
7094 --sp;
7095 *sp++ = emit_tree (cfg, bblock, ins, ip + 2);
7097 ip += 2;
7098 break;
7100 case CEE_LDFTN: {
7101 MonoInst *argconst;
7102 int temp;
7104 CHECK_STACK_OVF (1);
7105 CHECK_OPSIZE (6);
7106 n = read32 (ip + 2);
7107 cmethod = mini_get_method (method, n, NULL, generic_context);
7108 if (!cmethod)
7109 goto load_error;
7110 mono_class_init (cmethod->klass);
7112 if (mono_use_security_manager) {
7113 if (check_linkdemand (cfg, method, cmethod, bblock, ip))
7114 INLINE_FAILURE;
7117 handle_loaded_temps (cfg, bblock, stack_start, sp);
7119 NEW_METHODCONST (cfg, argconst, cmethod);
7120 if (method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED)
7121 temp = mono_emit_jit_icall (cfg, bblock, mono_ldftn, &argconst, ip);
7122 else
7123 temp = mono_emit_jit_icall (cfg, bblock, mono_ldftn_nosync, &argconst, ip);
7124 NEW_TEMPLOAD (cfg, *sp, temp);
7125 sp ++;
7127 ip += 6;
7128 inline_costs += 10 * num_calls++;
7129 break;
7131 case CEE_LDVIRTFTN: {
7132 MonoInst *args [2];
7133 int temp;
7135 CHECK_STACK (1);
7136 CHECK_OPSIZE (6);
7137 n = read32 (ip + 2);
7138 cmethod = mini_get_method (method, n, NULL, generic_context);
7139 if (!cmethod)
7140 goto load_error;
7141 mono_class_init (cmethod->klass);
7143 if (mono_use_security_manager) {
7144 if (check_linkdemand (cfg, method, cmethod, bblock, ip))
7145 INLINE_FAILURE;
7148 handle_loaded_temps (cfg, bblock, stack_start, sp);
7150 --sp;
7151 args [0] = *sp;
7152 NEW_METHODCONST (cfg, args [1], cmethod);
7153 temp = mono_emit_jit_icall (cfg, bblock, mono_ldvirtfn, args, ip);
7154 NEW_TEMPLOAD (cfg, *sp, temp);
7155 sp ++;
7157 ip += 6;
7158 inline_costs += 10 * num_calls++;
7159 break;
7161 case CEE_LDARG:
7162 CHECK_STACK_OVF (1);
7163 CHECK_OPSIZE (4);
7164 n = read16 (ip + 2);
7165 CHECK_ARG (n);
7166 NEW_ARGLOAD (cfg, ins, n);
7167 LDARG_SOFT_FLOAT (cfg, ins, n, ip);
7168 ins->cil_code = ip;
7169 *sp++ = ins;
7170 ip += 4;
7171 break;
7172 case CEE_LDARGA:
7173 CHECK_STACK_OVF (1);
7174 CHECK_OPSIZE (4);
7175 n = read16 (ip + 2);
7176 CHECK_ARG (n);
7177 NEW_ARGLOADA (cfg, ins, n);
7178 ins->cil_code = ip;
7179 *sp++ = ins;
7180 ip += 4;
7181 break;
7182 case CEE_STARG:
7183 CHECK_STACK (1);
7184 --sp;
7185 handle_loaded_temps (cfg, bblock, stack_start, sp);
7186 CHECK_OPSIZE (4);
7187 n = read16 (ip + 2);
7188 CHECK_ARG (n);
7189 NEW_ARGSTORE (cfg, ins, n, *sp);
7190 ins->cil_code = ip;
7191 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
7192 UNVERIFIED;
7193 if (ins->opcode == CEE_STOBJ) {
7194 NEW_ARGLOADA (cfg, ins, n);
7195 handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
7196 } else
7197 MONO_ADD_INS (bblock, ins);
7198 ip += 4;
7199 break;
7200 case CEE_LDLOC:
7201 CHECK_STACK_OVF (1);
7202 CHECK_OPSIZE (4);
7203 n = read16 (ip + 2);
7204 CHECK_LOCAL (n);
7205 NEW_LOCLOAD (cfg, ins, n);
7206 LDLOC_SOFT_FLOAT (cfg, ins, n, ip);
7207 ins->cil_code = ip;
7208 *sp++ = ins;
7209 ip += 4;
7210 break;
7211 case CEE_LDLOCA:
7212 CHECK_STACK_OVF (1);
7213 CHECK_OPSIZE (4);
7214 n = read16 (ip + 2);
7215 CHECK_LOCAL (n);
7216 NEW_LOCLOADA (cfg, ins, n);
7217 ins->cil_code = ip;
7218 *sp++ = ins;
7219 ip += 4;
7220 break;
7221 case CEE_STLOC:
7222 CHECK_STACK (1);
7223 --sp;
7224 CHECK_OPSIZE (4);
7225 n = read16 (ip + 2);
7226 CHECK_LOCAL (n);
7227 handle_loaded_temps (cfg, bblock, stack_start, sp);
7228 NEW_LOCSTORE (cfg, ins, n, *sp);
7229 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
7230 UNVERIFIED;
7231 ins->cil_code = ip;
7232 STLOC_SOFT_FLOAT (cfg, ins, n, ip);
7233 if (ins->opcode == CEE_STOBJ) {
7234 NEW_LOCLOADA (cfg, ins, n);
7235 handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
7236 } else
7237 MONO_ADD_INS (bblock, ins);
7238 ip += 4;
7239 inline_costs += 1;
7240 break;
7241 case CEE_LOCALLOC:
7242 CHECK_STACK (1);
7243 --sp;
7244 if (sp != stack_start)
7245 UNVERIFIED;
7246 if (cfg->method != method)
7248 * Inlining this into a loop in a parent could lead to
7249 * stack overflows which is different behavior than the
7250 * non-inlined case, thus disable inlining in this case.
7252 goto inline_failure;
7253 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
7254 ins->inst_left = *sp;
7255 ins->cil_code = ip;
7256 ins->type = STACK_PTR;
7258 cfg->flags |= MONO_CFG_HAS_ALLOCA;
7259 if (header->init_locals)
7260 ins->flags |= MONO_INST_INIT;
7262 *sp++ = ins;
7263 ip += 2;
7264 /* FIXME: set init flag if locals init is set in this method */
7265 break;
7266 case CEE_ENDFILTER: {
7267 MonoExceptionClause *clause, *nearest;
7268 int cc, nearest_num;
7270 CHECK_STACK (1);
7271 --sp;
7272 if ((sp != stack_start) || (sp [0]->type != STACK_I4))
7273 UNVERIFIED;
7274 MONO_INST_NEW (cfg, ins, OP_ENDFILTER);
7275 ins->inst_left = *sp;
7276 ins->cil_code = ip;
7277 MONO_ADD_INS (bblock, ins);
7278 start_new_bblock = 1;
7279 ip += 2;
7281 nearest = NULL;
7282 nearest_num = 0;
7283 for (cc = 0; cc < header->num_clauses; ++cc) {
7284 clause = &header->clauses [cc];
7285 if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) &&
7286 ((ip - header->code) > clause->data.filter_offset && (ip - header->code) <= clause->handler_offset) &&
7287 (!nearest || (clause->data.filter_offset < nearest->data.filter_offset))) {
7288 nearest = clause;
7289 nearest_num = cc;
7292 g_assert (nearest);
7293 if ((ip - header->code) != nearest->handler_offset)
7294 UNVERIFIED;
7296 break;
7298 case CEE_UNALIGNED_:
7299 ins_flag |= MONO_INST_UNALIGNED;
7300 /* FIXME: record alignment? we can assume 1 for now */
7301 CHECK_OPSIZE (3);
7302 ip += 3;
7303 break;
7304 case CEE_VOLATILE_:
7305 ins_flag |= MONO_INST_VOLATILE;
7306 ip += 2;
7307 break;
7308 case CEE_TAIL_:
7309 ins_flag |= MONO_INST_TAILCALL;
7310 cfg->flags |= MONO_CFG_HAS_TAIL;
7311 /* Can't inline tail calls at this time */
7312 inline_costs += 100000;
7313 ip += 2;
7314 break;
7315 case CEE_INITOBJ:
7316 CHECK_STACK (1);
7317 --sp;
7318 CHECK_OPSIZE (6);
7319 token = read32 (ip + 2);
7320 klass = mini_get_class (method, token, generic_context);
7321 CHECK_TYPELOAD (klass);
7322 if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) {
7323 MonoInst *store, *load;
7324 NEW_PCONST (cfg, load, NULL);
7325 load->cil_code = ip;
7326 load->type = STACK_OBJ;
7327 load->klass = klass;
7328 MONO_INST_NEW (cfg, store, CEE_STIND_REF);
7329 store->cil_code = ip;
7330 handle_loaded_temps (cfg, bblock, stack_start, sp);
7331 MONO_ADD_INS (bblock, store);
7332 store->inst_i0 = sp [0];
7333 store->inst_i1 = load;
7334 } else {
7335 handle_initobj (cfg, bblock, *sp, NULL, klass, stack_start, sp);
7337 ip += 6;
7338 inline_costs += 1;
7339 break;
7340 case CEE_CONSTRAINED_:
7341 /* FIXME: implement */
7342 CHECK_OPSIZE (6);
7343 token = read32 (ip + 2);
7344 constrained_call = mono_class_get_full (image, token, generic_context);
7345 CHECK_TYPELOAD (constrained_call);
7346 ip += 6;
7347 break;
7348 case CEE_CPBLK:
7349 case CEE_INITBLK: {
7350 MonoInst *iargs [3];
7351 CHECK_STACK (3);
7352 sp -= 3;
7353 if ((cfg->opt & MONO_OPT_INTRINS) && (ip [1] == CEE_CPBLK) && (sp [2]->opcode == OP_ICONST) && ((n = sp [2]->inst_c0) <= sizeof (gpointer) * 5)) {
7354 MonoInst *copy;
7355 MONO_INST_NEW (cfg, copy, OP_MEMCPY);
7356 copy->inst_left = sp [0];
7357 copy->inst_right = sp [1];
7358 copy->cil_code = ip;
7359 copy->backend.size = n;
7360 MONO_ADD_INS (bblock, copy);
7361 ip += 2;
7362 break;
7364 iargs [0] = sp [0];
7365 iargs [1] = sp [1];
7366 iargs [2] = sp [2];
7367 handle_loaded_temps (cfg, bblock, stack_start, sp);
7368 if (ip [1] == CEE_CPBLK) {
7369 MonoMethod *memcpy_method = get_memcpy_method ();
7370 mono_emit_method_call_spilled (cfg, bblock, memcpy_method, memcpy_method->signature, iargs, ip, NULL);
7371 } else {
7372 MonoMethod *memset_method = get_memset_method ();
7373 mono_emit_method_call_spilled (cfg, bblock, memset_method, memset_method->signature, iargs, ip, NULL);
7375 ip += 2;
7376 inline_costs += 1;
7377 break;
7379 case CEE_NO_:
7380 CHECK_OPSIZE (3);
7381 if (ip [2] & 0x1)
7382 ins_flag |= MONO_INST_NOTYPECHECK;
7383 if (ip [2] & 0x2)
7384 ins_flag |= MONO_INST_NORANGECHECK;
7385 /* we ignore the no-nullcheck for now since we
7386 * really do it explicitly only when doing callvirt->call
7388 ip += 3;
7389 break;
7390 case CEE_RETHROW: {
7391 MonoInst *load;
7392 int handler_offset = -1;
7394 for (i = 0; i < header->num_clauses; ++i) {
7395 MonoExceptionClause *clause = &header->clauses [i];
7396 if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && !(clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY))
7397 handler_offset = clause->handler_offset;
7400 bblock->flags |= BB_EXCEPTION_UNSAFE;
7402 g_assert (handler_offset != -1);
7404 NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, handler_offset)->inst_c0);
7405 load->cil_code = ip;
7406 MONO_INST_NEW (cfg, ins, OP_RETHROW);
7407 ins->inst_left = load;
7408 ins->cil_code = ip;
7409 MONO_ADD_INS (bblock, ins);
7410 sp = stack_start;
7411 link_bblock (cfg, bblock, end_bblock);
7412 start_new_bblock = 1;
7413 ip += 2;
7414 break;
7416 case CEE_SIZEOF:
7417 CHECK_STACK_OVF (1);
7418 CHECK_OPSIZE (6);
7419 token = read32 (ip + 2);
7420 /* FIXXME: handle generics. */
7421 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC) {
7422 MonoType *type = mono_type_create_from_typespec (image, token);
7423 token = mono_type_size (type, &ialign);
7424 } else {
7425 MonoClass *klass = mono_class_get_full (image, token, generic_context);
7426 CHECK_TYPELOAD (klass);
7427 mono_class_init (klass);
7428 token = mono_class_value_size (klass, &align);
7430 NEW_ICONST (cfg, ins, token);
7431 ins->cil_code = ip;
7432 *sp++= ins;
7433 ip += 6;
7434 break;
7435 case CEE_REFANYTYPE:
7436 CHECK_STACK (1);
7437 MONO_INST_NEW (cfg, ins, OP_REFANYTYPE);
7438 --sp;
7439 ins->type = STACK_MP;
7440 ins->inst_left = *sp;
7441 ins->type = STACK_VTYPE;
7442 ins->klass = mono_defaults.typehandle_class;
7443 ins->cil_code = ip;
7444 ip += 2;
7445 *sp++ = ins;
7446 break;
7447 case CEE_READONLY_:
7448 ip += 2;
7449 break;
7450 default:
7451 g_error ("opcode 0xfe 0x%02x not handled", ip [1]);
7453 break;
7455 default:
7456 g_error ("opcode 0x%02x not handled", *ip);
7459 if (start_new_bblock != 1)
7460 UNVERIFIED;
7462 bblock->cil_length = ip - bblock->cil_code;
7463 bblock->next_bb = end_bblock;
7465 if (cfg->method == method && cfg->domainvar) {
7466 MonoInst *store;
7467 MonoInst *get_domain;
7469 if (! (get_domain = mono_arch_get_domain_intrinsic (cfg))) {
7470 MonoCallInst *call;
7472 MONO_INST_NEW_CALL (cfg, call, CEE_CALL);
7473 call->signature = helper_sig_domain_get;
7474 call->inst.type = STACK_PTR;
7475 call->fptr = mono_domain_get;
7476 get_domain = (MonoInst*)call;
7479 NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, get_domain);
7480 MONO_ADD_INS (init_localsbb, store);
7483 if (cfg->method == method && cfg->got_var)
7484 mono_emit_load_got_addr (cfg);
7486 if (header->init_locals) {
7487 MonoInst *store;
7488 for (i = 0; i < header->num_locals; ++i) {
7489 MonoType *ptype = header->locals [i];
7490 int t = ptype->type;
7491 if (t == MONO_TYPE_VALUETYPE && ptype->data.klass->enumtype)
7492 t = ptype->data.klass->enum_basetype->type;
7493 if (ptype->byref) {
7494 NEW_PCONST (cfg, ins, NULL);
7495 NEW_LOCSTORE (cfg, store, i, ins);
7496 MONO_ADD_INS (init_localsbb, store);
7497 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
7498 NEW_ICONST (cfg, ins, 0);
7499 NEW_LOCSTORE (cfg, store, i, ins);
7500 MONO_ADD_INS (init_localsbb, store);
7501 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
7502 MONO_INST_NEW (cfg, ins, OP_I8CONST);
7503 ins->type = STACK_I8;
7504 ins->inst_l = 0;
7505 NEW_LOCSTORE (cfg, store, i, ins);
7506 MONO_ADD_INS (init_localsbb, store);
7507 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
7508 #ifdef MONO_ARCH_SOFT_FLOAT
7509 /* FIXME: handle init of R4 */
7510 #else
7511 MONO_INST_NEW (cfg, ins, OP_R8CONST);
7512 ins->type = STACK_R8;
7513 ins->inst_p0 = (void*)&r8_0;
7514 NEW_LOCSTORE (cfg, store, i, ins);
7515 MONO_ADD_INS (init_localsbb, store);
7516 #endif
7517 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
7518 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (ptype))) {
7519 NEW_LOCLOADA (cfg, ins, i);
7520 handle_initobj (cfg, init_localsbb, ins, NULL, mono_class_from_mono_type (ptype), NULL, NULL);
7521 } else {
7522 NEW_PCONST (cfg, ins, NULL);
7523 NEW_LOCSTORE (cfg, store, i, ins);
7524 MONO_ADD_INS (init_localsbb, store);
7529 /* resolve backward branches in the middle of an existing basic block */
7530 for (tmp = bb_recheck; tmp; tmp = tmp->next) {
7531 bblock = tmp->data;
7532 /*g_print ("need recheck in %s at IL_%04x\n", method->name, bblock->cil_code - header->code);*/
7533 tblock = find_previous (bbhash, start_bblock, bblock->cil_code);
7534 if (tblock != start_bblock) {
7535 int l;
7536 split_bblock (cfg, tblock, bblock);
7537 l = bblock->cil_code - header->code;
7538 bblock->cil_length = tblock->cil_length - l;
7539 tblock->cil_length = l;
7540 } else {
7541 g_print ("recheck failed.\n");
7545 if (cfg->method == method) {
7546 MonoBasicBlock *bb;
7547 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
7548 bb->region = mono_find_block_region (cfg, bb->real_offset);
7549 if (cfg->spvars)
7550 mono_create_spvar_for_region (cfg, bb->region);
7551 if (cfg->verbose_level > 2)
7552 g_print ("REGION BB%d IL_%04x ID_%08X\n", bb->block_num, bb->real_offset, bb->region);
7554 } else {
7555 g_hash_table_destroy (bbhash);
7558 g_slist_free (class_inits);
7559 dont_inline = g_list_remove (dont_inline, method);
7561 if (inline_costs < 0) {
7562 char *mname;
7564 /* Method is too large */
7565 mname = mono_method_full_name (method, TRUE);
7566 cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM;
7567 cfg->exception_message = g_strdup_printf ("Method %s is too complex.", mname);
7568 g_free (mname);
7569 return -1;
7572 return inline_costs;
7574 inline_failure:
7575 if (cfg->method != method)
7576 g_hash_table_destroy (bbhash);
7577 g_slist_free (class_inits);
7578 dont_inline = g_list_remove (dont_inline, method);
7579 return -1;
7581 load_error:
7582 if (cfg->method != method)
7583 g_hash_table_destroy (bbhash);
7584 g_slist_free (class_inits);
7585 dont_inline = g_list_remove (dont_inline, method);
7586 cfg->exception_type = MONO_EXCEPTION_TYPE_LOAD;
7587 return -1;
7589 unverified:
7590 if (cfg->method != method)
7591 g_hash_table_destroy (bbhash);
7592 g_slist_free (class_inits);
7593 dont_inline = g_list_remove (dont_inline, method);
7594 cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM;
7595 cfg->exception_message = g_strdup_printf ("Invalid IL code in %s: %s\n",
7596 mono_method_full_name (method, TRUE), mono_disasm_code_one (NULL, method, ip, NULL));
7597 return -1;
7600 void
7601 mono_print_tree (MonoInst *tree) {
7602 int arity;
7604 if (!tree)
7605 return;
7607 arity = mono_burg_arity [tree->opcode];
7609 printf (" %s%s", arity?"(":"", mono_inst_name (tree->opcode));
7611 switch (tree->opcode) {
7612 case OP_ICONST:
7613 printf ("[%d]", (int)tree->inst_c0);
7614 break;
7615 case OP_I8CONST:
7616 printf ("[%lld]", (long long)tree->inst_l);
7617 break;
7618 case OP_R8CONST:
7619 printf ("[%f]", *(double*)tree->inst_p0);
7620 break;
7621 case OP_R4CONST:
7622 printf ("[%f]", *(float*)tree->inst_p0);
7623 break;
7624 case OP_ARG:
7625 case OP_LOCAL:
7626 printf ("[%d]", (int)tree->inst_c0);
7627 break;
7628 case OP_REGOFFSET:
7629 if (tree->inst_offset < 0)
7630 printf ("[-0x%x(%s)]", (int)(-tree->inst_offset), mono_arch_regname (tree->inst_basereg));
7631 else
7632 printf ("[0x%x(%s)]", (int)(tree->inst_offset), mono_arch_regname (tree->inst_basereg));
7633 break;
7634 case OP_REGVAR:
7635 printf ("[%s]", mono_arch_regname (tree->dreg));
7636 break;
7637 case CEE_NEWARR:
7638 printf ("[%s]", tree->inst_newa_class->name);
7639 mono_print_tree (tree->inst_newa_len);
7640 break;
7641 case CEE_CALL:
7642 case CEE_CALLVIRT:
7643 case OP_FCALL:
7644 case OP_FCALLVIRT:
7645 case OP_LCALL:
7646 case OP_LCALLVIRT:
7647 case OP_VCALL:
7648 case OP_VCALLVIRT:
7649 case OP_VOIDCALL:
7650 case OP_VOIDCALLVIRT: {
7651 MonoCallInst *call = (MonoCallInst*)tree;
7652 if (call->method)
7653 printf ("[%s]", call->method->name);
7654 else if (call->fptr) {
7655 MonoJitICallInfo *info = mono_find_jit_icall_by_addr (call->fptr);
7656 if (info)
7657 printf ("[%s]", info->name);
7659 break;
7661 case OP_PHI: {
7662 int i;
7663 printf ("[%d (", (int)tree->inst_c0);
7664 for (i = 0; i < tree->inst_phi_args [0]; i++) {
7665 if (i)
7666 printf (", ");
7667 printf ("%d", tree->inst_phi_args [i + 1]);
7669 printf (")]");
7670 break;
7672 case OP_RENAME:
7673 case OP_RETARG:
7674 case CEE_NOP:
7675 case CEE_JMP:
7676 case CEE_BREAK:
7677 break;
7678 case OP_LOAD_MEMBASE:
7679 case OP_LOADI4_MEMBASE:
7680 case OP_LOADU4_MEMBASE:
7681 case OP_LOADU1_MEMBASE:
7682 case OP_LOADI1_MEMBASE:
7683 case OP_LOADU2_MEMBASE:
7684 case OP_LOADI2_MEMBASE:
7685 printf ("[%s] <- [%s + 0x%x]", mono_arch_regname (tree->dreg), mono_arch_regname (tree->inst_basereg), (int)tree->inst_offset);
7686 break;
7687 case CEE_BR:
7688 case OP_CALL_HANDLER:
7689 printf ("[B%d]", tree->inst_target_bb->block_num);
7690 break;
7691 case CEE_SWITCH:
7692 case CEE_ISINST:
7693 case CEE_CASTCLASS:
7694 case OP_OUTARG:
7695 case OP_CALL_REG:
7696 case OP_FCALL_REG:
7697 case OP_LCALL_REG:
7698 case OP_VCALL_REG:
7699 case OP_VOIDCALL_REG:
7700 mono_print_tree (tree->inst_left);
7701 break;
7702 case CEE_BNE_UN:
7703 case CEE_BEQ:
7704 case CEE_BLT:
7705 case CEE_BLT_UN:
7706 case CEE_BGT:
7707 case CEE_BGT_UN:
7708 case CEE_BGE:
7709 case CEE_BGE_UN:
7710 case CEE_BLE:
7711 case CEE_BLE_UN:
7712 printf ("[B%dB%d]", tree->inst_true_bb->block_num, tree->inst_false_bb->block_num);
7713 mono_print_tree (tree->inst_left);
7714 break;
7715 default:
7716 if (!mono_arch_print_tree(tree, arity)) {
7717 if (arity) {
7718 mono_print_tree (tree->inst_left);
7719 if (arity > 1)
7720 mono_print_tree (tree->inst_right);
7723 break;
7726 if (arity)
7727 printf (")");
7730 void
7731 mono_print_tree_nl (MonoInst *tree)
7733 mono_print_tree (tree);
7734 printf ("\n");
7737 static void
7738 create_helper_signature (void)
7740 helper_sig_domain_get = mono_create_icall_signature ("ptr");
7741 helper_sig_class_init_trampoline = mono_create_icall_signature ("void");
7744 gconstpointer
7745 mono_icall_get_wrapper (MonoJitICallInfo* callinfo)
7747 char *name;
7748 MonoMethod *wrapper;
7749 gconstpointer trampoline;
7750 MonoDomain *domain = mono_get_root_domain ();
7752 if (callinfo->wrapper) {
7753 return callinfo->wrapper;
7756 if (callinfo->trampoline)
7757 return callinfo->trampoline;
7760 * We use the lock on the root domain instead of the JIT lock to protect
7761 * callinfo->trampoline, since we do a lot of stuff inside the critical section.
7763 mono_domain_lock (domain);
7765 if (callinfo->trampoline) {
7766 mono_domain_unlock (domain);
7767 return callinfo->trampoline;
7770 name = g_strdup_printf ("__icall_wrapper_%s", callinfo->name);
7771 wrapper = mono_marshal_get_icall_wrapper (callinfo->sig, name, callinfo->func);
7772 g_free (name);
7774 trampoline = mono_create_ftnptr (domain, mono_create_jit_trampoline_in_domain (domain, wrapper));
7775 mono_register_jit_icall_wrapper (callinfo, trampoline);
7777 callinfo->trampoline = trampoline;
7779 mono_domain_unlock (domain);
7781 return callinfo->trampoline;
7784 static void
7785 mono_init_trampolines (void)
7787 mono_trampoline_code [MONO_TRAMPOLINE_GENERIC] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC);
7788 mono_trampoline_code [MONO_TRAMPOLINE_JUMP] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JUMP);
7789 mono_trampoline_code [MONO_TRAMPOLINE_CLASS_INIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
7790 #ifdef MONO_ARCH_HAVE_PIC_AOT
7791 mono_trampoline_code [MONO_TRAMPOLINE_AOT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_AOT);
7792 mono_trampoline_code [MONO_TRAMPOLINE_AOT_PLT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_AOT_PLT);
7793 #endif
7794 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
7795 mono_trampoline_code [MONO_TRAMPOLINE_DELEGATE] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_DELEGATE);
7796 #endif
7799 static void
7800 mono_init_exceptions (void)
7802 #ifndef CUSTOM_EXCEPTION_HANDLING
7803 mono_arch_get_restore_context ();
7804 mono_arch_get_call_filter ();
7805 mono_arch_get_throw_exception ();
7806 mono_arch_get_rethrow_exception ();
7807 #endif
7810 guint8 *
7811 mono_get_trampoline_code (MonoTrampolineType tramp_type)
7813 return mono_trampoline_code [tramp_type];
7816 gpointer
7817 mono_create_class_init_trampoline (MonoVTable *vtable)
7819 gpointer code, ptr;
7821 /* previously created trampoline code */
7822 mono_domain_lock (vtable->domain);
7823 ptr =
7824 g_hash_table_lookup (vtable->domain->class_init_trampoline_hash,
7825 vtable);
7826 mono_domain_unlock (vtable->domain);
7827 if (ptr)
7828 return ptr;
7830 #ifdef MONO_ARCH_HAVE_CREATE_SPECIFIC_TRAMPOLINE
7831 code = mono_arch_create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT, vtable->domain, NULL);
7832 #else
7833 code = mono_arch_create_class_init_trampoline (vtable);
7834 #endif
7836 ptr = mono_create_ftnptr (vtable->domain, code);
7838 /* store trampoline address */
7839 mono_domain_lock (vtable->domain);
7840 g_hash_table_insert (vtable->domain->class_init_trampoline_hash,
7841 vtable, ptr);
7842 mono_domain_unlock (vtable->domain);
7844 mono_jit_lock ();
7845 if (!class_init_hash_addr)
7846 class_init_hash_addr = g_hash_table_new (NULL, NULL);
7847 g_hash_table_insert (class_init_hash_addr, ptr, vtable);
7848 mono_jit_unlock ();
7850 return ptr;
7853 gpointer
7854 mono_create_jump_trampoline (MonoDomain *domain, MonoMethod *method,
7855 gboolean add_sync_wrapper)
7857 MonoJitInfo *ji;
7858 gpointer code;
7859 #ifdef MONO_ARCH_HAVE_CREATE_SPECIFIC_TRAMPOLINE
7860 guint32 code_size;
7861 #endif
7863 if (add_sync_wrapper && method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
7864 return mono_create_jump_trampoline (domain, mono_marshal_get_synchronized_wrapper (method), FALSE);
7866 code = mono_jit_find_compiled_method (domain, method);
7867 if (code)
7868 return code;
7870 mono_domain_lock (domain);
7871 code = g_hash_table_lookup (domain->jump_trampoline_hash, method);
7872 mono_domain_unlock (domain);
7873 if (code)
7874 return code;
7876 #ifdef MONO_ARCH_HAVE_CREATE_SPECIFIC_TRAMPOLINE
7877 code = mono_arch_create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get (), &code_size);
7879 mono_domain_lock (domain);
7880 ji = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo));
7881 mono_domain_unlock (domain);
7882 ji->code_start = code;
7883 ji->code_size = code_size;
7884 ji->method = method;
7885 #else
7886 ji = mono_arch_create_jump_trampoline (method);
7887 #endif
7890 * mono_delegate_ctor needs to find the method metadata from the
7891 * trampoline address, so we save it here.
7894 mono_jit_info_table_add (domain, ji);
7896 mono_domain_lock (domain);
7897 g_hash_table_insert (domain->jump_trampoline_hash, method, ji->code_start);
7898 mono_domain_unlock (domain);
7900 return ji->code_start;
7903 static gpointer
7904 mono_create_jit_trampoline_in_domain (MonoDomain *domain, MonoMethod *method)
7906 gpointer tramp;
7908 mono_domain_lock (domain);
7909 tramp = g_hash_table_lookup (domain->jit_trampoline_hash, method);
7910 mono_domain_unlock (domain);
7911 if (tramp)
7912 return tramp;
7914 if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
7915 return mono_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method));
7917 #ifdef MONO_ARCH_HAVE_CREATE_SPECIFIC_TRAMPOLINE
7918 tramp = mono_arch_create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC, domain, NULL);
7919 #else
7920 tramp = mono_arch_create_jit_trampoline (method);
7921 #endif
7923 mono_domain_lock (domain);
7924 g_hash_table_insert (domain->jit_trampoline_hash, method, tramp);
7925 mono_domain_unlock (domain);
7927 mono_jit_stats.method_trampolines++;
7929 return tramp;
7932 gpointer
7933 mono_create_jit_trampoline (MonoMethod *method)
7935 return mono_create_jit_trampoline_in_domain (mono_domain_get (), method);
7938 #ifdef MONO_ARCH_HAVE_CREATE_TRAMPOLINE_FROM_TOKEN
7939 gpointer
7940 mono_create_jit_trampoline_from_token (MonoImage *image, guint32 token)
7942 gpointer tramp;
7944 MonoDomain *domain = mono_domain_get ();
7945 guint8 *buf, *start;
7947 mono_domain_lock (domain);
7948 buf = start = mono_code_manager_reserve (domain->code_mp, 2 * sizeof (gpointer));
7949 mono_domain_unlock (domain);
7951 *(gpointer*)(gpointer)buf = image;
7952 buf += sizeof (gpointer);
7953 *(guint32*)(gpointer)buf = token;
7955 tramp = mono_arch_create_specific_trampoline (start, MONO_TRAMPOLINE_AOT, domain, NULL);
7957 mono_jit_stats.method_trampolines++;
7959 return tramp;
7961 #endif
7963 static gpointer
7964 mono_create_delegate_trampoline (MonoMethod *method, gpointer addr)
7966 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
7967 gpointer code, ptr;
7968 guint32 code_size;
7969 MonoDomain *domain = mono_domain_get ();
7971 #ifndef __ia64__
7972 code = mono_jit_find_compiled_method (domain, method);
7973 if (code)
7974 return code;
7975 #else
7977 * FIXME: We should return a function descriptor here but it is not stored
7978 * anywhere so it would be leaked.
7980 #endif
7982 mono_domain_lock (domain);
7983 ptr = g_hash_table_lookup (domain->delegate_trampoline_hash, method);
7984 mono_domain_unlock (domain);
7985 if (ptr)
7986 return ptr;
7988 code = mono_arch_create_specific_trampoline (method, MONO_TRAMPOLINE_DELEGATE, domain, &code_size);
7990 ptr = mono_create_ftnptr (domain, code);
7992 /* store trampoline address */
7993 mono_domain_lock (domain);
7994 g_hash_table_insert (domain->delegate_trampoline_hash,
7995 method, ptr);
7996 mono_domain_unlock (domain);
7998 return ptr;
7999 #else
8000 return addr;
8001 #endif
8004 MonoVTable*
8005 mono_find_class_init_trampoline_by_addr (gconstpointer addr)
8007 MonoVTable *res;
8009 mono_jit_lock ();
8010 if (class_init_hash_addr)
8011 res = g_hash_table_lookup (class_init_hash_addr, addr);
8012 else
8013 res = NULL;
8014 mono_jit_unlock ();
8015 return res;
8018 static void
8019 mono_dynamic_code_hash_insert (MonoDomain *domain, MonoMethod *method, MonoJitDynamicMethodInfo *ji)
8021 if (!domain->dynamic_code_hash)
8022 domain->dynamic_code_hash = g_hash_table_new (NULL, NULL);
8023 g_hash_table_insert (domain->dynamic_code_hash, method, ji);
8026 static MonoJitDynamicMethodInfo*
8027 mono_dynamic_code_hash_lookup (MonoDomain *domain, MonoMethod *method)
8029 MonoJitDynamicMethodInfo *res;
8031 if (domain->dynamic_code_hash)
8032 res = g_hash_table_lookup (domain->dynamic_code_hash, method);
8033 else
8034 res = NULL;
8035 return res;
8038 typedef struct {
8039 MonoClass *vtype;
8040 GList *active;
8041 GList *slots;
8042 } StackSlotInfo;
8045 * mono_allocate_stack_slots_full:
8047 * Allocate stack slots for all non register allocated variables using a
8048 * linear scan algorithm.
8049 * Returns: an array of stack offsets which the caller should free.
8050 * STACK_SIZE is set to the amount of stack space needed.
8051 * STACK_ALIGN is set to the alignment needed by the locals area.
8053 gint32*
8054 mono_allocate_stack_slots_full (MonoCompile *m, gboolean backward, guint32 *stack_size, guint32 *stack_align)
8056 int i, slot, offset, size;
8057 guint32 align;
8058 MonoMethodVar *vmv;
8059 MonoInst *inst;
8060 gint32 *offsets;
8061 GList *vars = NULL, *l;
8062 StackSlotInfo *scalar_stack_slots, *vtype_stack_slots, *slot_info;
8063 MonoType *t;
8064 int nvtypes;
8066 scalar_stack_slots = g_new0 (StackSlotInfo, MONO_TYPE_PINNED);
8067 vtype_stack_slots = g_new0 (StackSlotInfo, 256);
8068 nvtypes = 0;
8070 offsets = g_new (gint32, m->num_varinfo);
8071 for (i = 0; i < m->num_varinfo; ++i)
8072 offsets [i] = -1;
8074 for (i = m->locals_start; i < m->num_varinfo; i++) {
8075 inst = m->varinfo [i];
8076 vmv = MONO_VARINFO (m, i);
8078 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR || inst->opcode == OP_REGOFFSET)
8079 continue;
8081 vars = g_list_prepend (vars, vmv);
8084 vars = mono_varlist_sort (m, vars, 0);
8085 offset = 0;
8086 *stack_align = 0;
8087 for (l = vars; l; l = l->next) {
8088 vmv = l->data;
8089 inst = m->varinfo [vmv->idx];
8091 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
8092 * pinvoke wrappers when they call functions returning structures */
8093 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF)
8094 size = mono_class_native_size (inst->inst_vtype->data.klass, &align);
8095 else {
8096 int ialign;
8098 size = mono_type_size (inst->inst_vtype, &ialign);
8099 align = ialign;
8102 t = mono_type_get_underlying_type (inst->inst_vtype);
8103 if (t->byref) {
8104 slot_info = &scalar_stack_slots [MONO_TYPE_I];
8105 } else {
8106 switch (t->type) {
8107 case MONO_TYPE_GENERICINST:
8108 if (!mono_type_generic_inst_is_valuetype (t)) {
8109 slot_info = &scalar_stack_slots [t->type];
8110 break;
8112 /* Fall through */
8113 case MONO_TYPE_VALUETYPE:
8114 for (i = 0; i < nvtypes; ++i)
8115 if (t->data.klass == vtype_stack_slots [i].vtype)
8116 break;
8117 if (i < nvtypes)
8118 slot_info = &vtype_stack_slots [i];
8119 else {
8120 g_assert (nvtypes < 256);
8121 vtype_stack_slots [nvtypes].vtype = t->data.klass;
8122 slot_info = &vtype_stack_slots [nvtypes];
8123 nvtypes ++;
8125 break;
8126 default:
8127 slot_info = &scalar_stack_slots [t->type];
8131 slot = 0xffffff;
8132 if (m->comp_done & MONO_COMP_LIVENESS) {
8133 //printf ("START %2d %08x %08x\n", vmv->idx, vmv->range.first_use.abs_pos, vmv->range.last_use.abs_pos);
8135 /* expire old intervals in active */
8136 while (slot_info->active) {
8137 MonoMethodVar *amv = (MonoMethodVar *)slot_info->active->data;
8139 if (amv->range.last_use.abs_pos > vmv->range.first_use.abs_pos)
8140 break;
8142 //printf ("EXPIR %2d %08x %08x C%d R%d\n", amv->idx, amv->range.first_use.abs_pos, amv->range.last_use.abs_pos, amv->spill_costs, amv->reg);
8144 slot_info->active = g_list_delete_link (slot_info->active, slot_info->active);
8145 slot_info->slots = g_list_prepend (slot_info->slots, GINT_TO_POINTER (offsets [amv->idx]));
8149 * This also handles the case when the variable is used in an
8150 * exception region, as liveness info is not computed there.
8153 * FIXME: All valuetypes are marked as INDIRECT because of LDADDR
8154 * opcodes.
8156 if (! (inst->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
8157 if (slot_info->slots) {
8158 slot = GPOINTER_TO_INT (slot_info->slots->data);
8160 slot_info->slots = g_list_delete_link (slot_info->slots, slot_info->slots);
8163 slot_info->active = mono_varlist_insert_sorted (m, slot_info->active, vmv, TRUE);
8168 static int count = 0;
8169 count ++;
8172 if (count == atoi (getenv ("COUNT")))
8173 printf ("LAST: %s\n", mono_method_full_name (m->method, TRUE));
8174 if (count > atoi (getenv ("COUNT")))
8175 slot = 0xffffff;
8176 else {
8177 mono_print_tree_nl (inst);
8181 if (slot == 0xffffff) {
8183 * Allways allocate valuetypes to sizeof (gpointer) to allow more
8184 * efficient copying (and to work around the fact that OP_MEMCPY
8185 * and OP_MEMSET ignores alignment).
8187 if (MONO_TYPE_ISSTRUCT (t))
8188 align = sizeof (gpointer);
8190 if (backward) {
8191 offset += size;
8192 offset += align - 1;
8193 offset &= ~(align - 1);
8194 slot = offset;
8196 else {
8197 offset += align - 1;
8198 offset &= ~(align - 1);
8199 slot = offset;
8200 offset += size;
8203 if (*stack_align == 0)
8204 *stack_align = align;
8207 offsets [vmv->idx] = slot;
8209 g_list_free (vars);
8210 for (i = 0; i < MONO_TYPE_PINNED; ++i) {
8211 g_list_free (scalar_stack_slots [i].active);
8212 g_list_free (scalar_stack_slots [i].slots);
8214 for (i = 0; i < nvtypes; ++i) {
8215 g_list_free (vtype_stack_slots [i].active);
8216 g_list_free (vtype_stack_slots [i].slots);
8218 g_free (scalar_stack_slots);
8219 g_free (vtype_stack_slots);
8221 *stack_size = offset;
8222 return offsets;
8225 gint32*
8226 mono_allocate_stack_slots (MonoCompile *m, guint32 *stack_size, guint32 *stack_align)
8228 return mono_allocate_stack_slots_full (m, TRUE, stack_size, stack_align);
8231 void
8232 mono_register_opcode_emulation (int opcode, const char *name, const char *sigstr, gpointer func, gboolean no_throw)
8234 MonoJitICallInfo *info;
8235 MonoMethodSignature *sig = mono_create_icall_signature (sigstr);
8237 if (!emul_opcode_map)
8238 emul_opcode_map = g_new0 (MonoJitICallInfo*, OP_LAST + 1);
8240 g_assert (!sig->hasthis);
8241 g_assert (sig->param_count < 3);
8243 info = mono_register_jit_icall (func, name, sig, no_throw);
8245 emul_opcode_map [opcode] = info;
8248 static void
8249 register_icall (gpointer func, const char *name, const char *sigstr, gboolean save)
8251 MonoMethodSignature *sig;
8253 if (sigstr)
8254 sig = mono_create_icall_signature (sigstr);
8255 else
8256 sig = NULL;
8258 mono_register_jit_icall (func, name, sig, save);
8261 static void
8262 decompose_foreach (MonoInst *tree, gpointer data)
8264 static MonoJitICallInfo *newarr_info = NULL;
8265 static MonoJitICallInfo *newarr_specific_info = NULL;
8266 MonoJitICallInfo *info;
8267 int i;
8269 switch (tree->opcode) {
8270 case CEE_NEWARR: {
8271 MonoCompile *cfg = data;
8272 MonoInst *iargs [3];
8274 if (!newarr_info) {
8275 newarr_info = mono_find_jit_icall_by_addr (mono_array_new);
8276 g_assert (newarr_info);
8277 newarr_specific_info = mono_find_jit_icall_by_addr (mono_array_new_specific);
8278 g_assert (newarr_specific_info);
8281 if (cfg->opt & MONO_OPT_SHARED) {
8282 NEW_DOMAINCONST (cfg, iargs [0]);
8283 NEW_CLASSCONST (cfg, iargs [1], tree->inst_newa_class);
8284 iargs [2] = tree->inst_newa_len;
8286 info = newarr_info;
8288 else {
8289 MonoVTable *vtable = mono_class_vtable (cfg->domain, mono_array_class_get (tree->inst_newa_class, 1));
8291 NEW_VTABLECONST (cfg, iargs [0], vtable);
8292 iargs [1] = tree->inst_newa_len;
8294 info = newarr_specific_info;
8297 mono_emulate_opcode (cfg, tree, iargs, info);
8299 /* Need to decompose arguments after the the opcode is decomposed */
8300 for (i = 0; i < info->sig->param_count; ++i)
8301 dec_foreach (iargs [i], cfg);
8302 break;
8304 #ifdef MONO_ARCH_SOFT_FLOAT
8305 case OP_FBEQ:
8306 case OP_FBGE:
8307 case OP_FBGT:
8308 case OP_FBLE:
8309 case OP_FBLT:
8310 case OP_FBNE_UN:
8311 case OP_FBGE_UN:
8312 case OP_FBGT_UN:
8313 case OP_FBLE_UN:
8314 case OP_FBLT_UN: {
8315 if ((info = mono_find_jit_opcode_emulation (tree->opcode))) {
8316 MonoCompile *cfg = data;
8317 MonoInst *iargs [2];
8319 iargs [0] = tree->inst_i0;
8320 iargs [1] = tree->inst_i1;
8322 mono_emulate_opcode (cfg, tree, iargs, info);
8324 dec_foreach (iargs [0], cfg);
8325 dec_foreach (iargs [1], cfg);
8326 break;
8327 } else {
8328 g_assert_not_reached ();
8330 break;
8332 case OP_FCEQ:
8333 case OP_FCGT:
8334 case OP_FCGT_UN:
8335 case OP_FCLT:
8336 case OP_FCLT_UN: {
8337 if ((info = mono_find_jit_opcode_emulation (tree->opcode))) {
8338 MonoCompile *cfg = data;
8339 MonoInst *iargs [2];
8341 /* the args are in the compare opcode ... */
8342 iargs [0] = tree->inst_i0;
8343 iargs [1] = tree->inst_i1;
8345 mono_emulate_opcode (cfg, tree, iargs, info);
8347 dec_foreach (iargs [0], cfg);
8348 dec_foreach (iargs [1], cfg);
8349 break;
8350 } else {
8351 g_assert_not_reached ();
8353 break;
8355 #endif
8357 default:
8358 break;
8362 void
8363 mono_inst_foreach (MonoInst *tree, MonoInstFunc func, gpointer data) {
8365 switch (mono_burg_arity [tree->opcode]) {
8366 case 0: break;
8367 case 1:
8368 mono_inst_foreach (tree->inst_left, func, data);
8369 break;
8370 case 2:
8371 mono_inst_foreach (tree->inst_left, func, data);
8372 mono_inst_foreach (tree->inst_right, func, data);
8373 break;
8374 default:
8375 g_assert_not_reached ();
8377 func (tree, data);
8380 G_GNUC_UNUSED
8381 static void
8382 mono_print_bb_code (MonoBasicBlock *bb) {
8383 if (bb->code) {
8384 MonoInst *c = bb->code;
8385 while (c) {
8386 mono_print_tree (c);
8387 g_print ("\n");
8388 c = c->next;
8393 static void
8394 print_dfn (MonoCompile *cfg) {
8395 int i, j;
8396 char *code;
8397 MonoBasicBlock *bb;
8399 g_print ("IR code for method %s\n", mono_method_full_name (cfg->method, TRUE));
8401 for (i = 0; i < cfg->num_bblocks; ++i) {
8402 bb = cfg->bblocks [i];
8403 /*if (bb->cil_code) {
8404 char* code1, *code2;
8405 code1 = mono_disasm_code_one (NULL, cfg->method, bb->cil_code, NULL);
8406 if (bb->last_ins->cil_code)
8407 code2 = mono_disasm_code_one (NULL, cfg->method, bb->last_ins->cil_code, NULL);
8408 else
8409 code2 = g_strdup ("");
8411 code1 [strlen (code1) - 1] = 0;
8412 code = g_strdup_printf ("%s -> %s", code1, code2);
8413 g_free (code1);
8414 g_free (code2);
8415 } else*/
8416 code = g_strdup ("\n");
8417 g_print ("\nBB%d DFN%d (len: %d): %s", bb->block_num, i, bb->cil_length, code);
8418 if (bb->code) {
8419 MonoInst *c = bb->code;
8420 while (c) {
8421 mono_print_tree (c);
8422 g_print ("\n");
8423 c = c->next;
8425 } else {
8429 g_print ("\tprev:");
8430 for (j = 0; j < bb->in_count; ++j) {
8431 g_print (" BB%d", bb->in_bb [j]->block_num);
8433 g_print ("\t\tsucc:");
8434 for (j = 0; j < bb->out_count; ++j) {
8435 g_print (" BB%d", bb->out_bb [j]->block_num);
8437 g_print ("\n\tidom: BB%d\n", bb->idom? bb->idom->block_num: -1);
8439 if (bb->idom)
8440 g_assert (mono_bitset_test_fast (bb->dominators, bb->idom->dfn));
8442 if (bb->dominators)
8443 mono_blockset_print (cfg, bb->dominators, "\tdominators", bb->idom? bb->idom->dfn: -1);
8444 if (bb->dfrontier)
8445 mono_blockset_print (cfg, bb->dfrontier, "\tdfrontier", -1);
8446 g_free (code);
8449 g_print ("\n");
8452 void
8453 mono_bblock_add_inst (MonoBasicBlock *bb, MonoInst *inst)
8455 inst->next = NULL;
8456 if (bb->last_ins) {
8457 g_assert (bb->code);
8458 bb->last_ins->next = inst;
8459 bb->last_ins = inst;
8460 } else {
8461 bb->last_ins = bb->code = inst;
8465 void
8466 mono_destroy_compile (MonoCompile *cfg)
8468 //mono_mempool_stats (cfg->mempool);
8469 g_hash_table_destroy (cfg->bb_hash);
8470 mono_free_loop_info (cfg);
8471 if (cfg->rs)
8472 mono_regstate_free (cfg->rs);
8473 if (cfg->spvars)
8474 g_hash_table_destroy (cfg->spvars);
8475 if (cfg->exvars)
8476 g_hash_table_destroy (cfg->exvars);
8477 mono_mempool_destroy (cfg->mempool);
8478 g_list_free (cfg->ldstr_list);
8480 g_free (cfg->varinfo);
8481 g_free (cfg->vars);
8482 g_free (cfg->exception_message);
8483 g_free (cfg);
8486 #ifdef HAVE_KW_THREAD
8487 static __thread gpointer mono_lmf_addr MONO_TLS_FAST;
8488 #ifdef MONO_ARCH_ENABLE_MONO_LMF_VAR
8490 * When this is defined, the current lmf is stored in this tls variable instead of in
8491 * jit_tls->lmf.
8493 static __thread gpointer mono_lmf MONO_TLS_FAST;
8494 #endif
8495 #endif
8497 guint32
8498 mono_get_jit_tls_key (void)
8500 return mono_jit_tls_id;
8503 gint32
8504 mono_get_lmf_tls_offset (void)
8506 #if defined(HAVE_KW_THREAD) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR)
8507 int offset;
8508 MONO_THREAD_VAR_OFFSET(mono_lmf,offset);
8509 return offset;
8510 #else
8511 return -1;
8512 #endif
8515 gint32
8516 mono_get_lmf_addr_tls_offset (void)
8518 int offset;
8519 MONO_THREAD_VAR_OFFSET(mono_lmf_addr,offset);
8520 return offset;
8523 MonoLMF *
8524 mono_get_lmf (void)
8526 #if defined(HAVE_KW_THREAD) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR)
8527 return mono_lmf;
8528 #else
8529 MonoJitTlsData *jit_tls;
8531 if ((jit_tls = TlsGetValue (mono_jit_tls_id)))
8532 return jit_tls->lmf;
8534 g_assert_not_reached ();
8535 return NULL;
8536 #endif
8539 MonoLMF **
8540 mono_get_lmf_addr (void)
8542 #ifdef HAVE_KW_THREAD
8543 return mono_lmf_addr;
8544 #else
8545 MonoJitTlsData *jit_tls;
8547 if ((jit_tls = TlsGetValue (mono_jit_tls_id)))
8548 return &jit_tls->lmf;
8550 g_assert_not_reached ();
8551 return NULL;
8552 #endif
8555 /* Called by native->managed wrappers */
8556 void
8557 mono_jit_thread_attach (MonoDomain *domain)
8559 #ifdef HAVE_KW_THREAD
8560 if (!mono_lmf_addr) {
8561 mono_thread_attach (domain);
8563 #else
8564 if (!TlsGetValue (mono_jit_tls_id))
8565 mono_thread_attach (domain);
8566 #endif
8570 * mono_thread_abort:
8571 * @obj: exception object
8573 * abort the thread, print exception information and stack trace
8575 static void
8576 mono_thread_abort (MonoObject *obj)
8578 /* MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); */
8580 /* handle_remove should be eventually called for this thread, too
8581 g_free (jit_tls);*/
8583 mono_thread_exit ();
8586 static void*
8587 setup_jit_tls_data (gpointer stack_start, gpointer abort_func)
8589 MonoJitTlsData *jit_tls;
8590 MonoLMF *lmf;
8592 jit_tls = TlsGetValue (mono_jit_tls_id);
8593 if (jit_tls)
8594 return jit_tls;
8596 jit_tls = g_new0 (MonoJitTlsData, 1);
8598 TlsSetValue (mono_jit_tls_id, jit_tls);
8600 jit_tls->abort_func = abort_func;
8601 jit_tls->end_of_stack = stack_start;
8603 lmf = g_new0 (MonoLMF, 1);
8604 lmf->ebp = -1;
8606 jit_tls->first_lmf = lmf;
8608 #if defined(HAVE_KW_THREAD) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR)
8609 /* jit_tls->lmf is unused */
8610 mono_lmf = lmf;
8611 mono_lmf_addr = &mono_lmf;
8612 #else
8613 #if defined(HAVE_KW_THREAD)
8614 mono_lmf_addr = &jit_tls->lmf;
8615 #endif
8617 jit_tls->lmf = lmf;
8618 #endif
8620 mono_arch_setup_jit_tls_data (jit_tls);
8622 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
8623 mono_setup_altstack (jit_tls);
8624 #endif
8626 return jit_tls;
8629 static void
8630 mono_thread_start_cb (gsize tid, gpointer stack_start, gpointer func)
8632 MonoThread *thread;
8633 void *jit_tls = setup_jit_tls_data (stack_start, mono_thread_abort);
8634 thread = mono_thread_current ();
8635 if (thread)
8636 thread->jit_data = jit_tls;
8639 void (*mono_thread_attach_aborted_cb ) (MonoObject *obj) = NULL;
8641 static void
8642 mono_thread_abort_dummy (MonoObject *obj)
8644 if (mono_thread_attach_aborted_cb)
8645 mono_thread_attach_aborted_cb (obj);
8646 else
8647 mono_thread_abort (obj);
8650 static void
8651 mono_thread_attach_cb (gsize tid, gpointer stack_start)
8653 MonoThread *thread;
8654 void *jit_tls = setup_jit_tls_data (stack_start, mono_thread_abort_dummy);
8655 thread = mono_thread_current ();
8656 if (thread)
8657 thread->jit_data = jit_tls;
8658 if (mono_profiler_get_events () & MONO_PROFILE_STATISTICAL)
8659 setup_stat_profiler ();
8662 static void
8663 mini_thread_cleanup (MonoThread *thread)
8665 MonoJitTlsData *jit_tls = thread->jit_data;
8667 if (jit_tls) {
8668 mono_arch_free_jit_tls_data (jit_tls);
8670 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
8671 mono_free_altstack (jit_tls);
8672 #endif
8673 g_free (jit_tls->first_lmf);
8674 g_free (jit_tls);
8675 thread->jit_data = NULL;
8676 TlsSetValue (mono_jit_tls_id, NULL);
8680 void
8681 mono_add_patch_info (MonoCompile *cfg, int ip, MonoJumpInfoType type, gconstpointer target)
8683 MonoJumpInfo *ji = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfo));
8685 ji->ip.i = ip;
8686 ji->type = type;
8687 ji->data.target = target;
8688 ji->next = cfg->patch_info;
8690 cfg->patch_info = ji;
8693 void
8694 mono_remove_patch_info (MonoCompile *cfg, int ip)
8696 MonoJumpInfo **ji = &cfg->patch_info;
8698 while (*ji) {
8699 if ((*ji)->ip.i == ip)
8700 *ji = (*ji)->next;
8701 else
8702 ji = &((*ji)->next);
8707 * mono_patch_info_dup_mp:
8709 * Make a copy of PATCH_INFO, allocating memory from the mempool MP.
8711 MonoJumpInfo*
8712 mono_patch_info_dup_mp (MonoMemPool *mp, MonoJumpInfo *patch_info)
8714 MonoJumpInfo *res = mono_mempool_alloc (mp, sizeof (MonoJumpInfo));
8715 memcpy (res, patch_info, sizeof (MonoJumpInfo));
8717 switch (patch_info->type) {
8718 case MONO_PATCH_INFO_LDSTR:
8719 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
8720 case MONO_PATCH_INFO_LDTOKEN:
8721 case MONO_PATCH_INFO_DECLSEC:
8722 res->data.token = mono_mempool_alloc (mp, sizeof (MonoJumpInfoToken));
8723 memcpy (res->data.token, patch_info->data.token, sizeof (MonoJumpInfoToken));
8724 break;
8725 case MONO_PATCH_INFO_SWITCH:
8726 res->data.table = mono_mempool_alloc (mp, sizeof (MonoJumpInfoBBTable));
8727 memcpy (res->data.table, patch_info->data.table, sizeof (MonoJumpInfoBBTable));
8728 break;
8729 default:
8730 break;
8733 return res;
8736 guint
8737 mono_patch_info_hash (gconstpointer data)
8739 const MonoJumpInfo *ji = (MonoJumpInfo*)data;
8741 switch (ji->type) {
8742 case MONO_PATCH_INFO_LDSTR:
8743 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
8744 case MONO_PATCH_INFO_LDTOKEN:
8745 case MONO_PATCH_INFO_DECLSEC:
8746 return (ji->type << 8) | ji->data.token->token;
8747 default:
8748 return (ji->type << 8);
8753 * mono_patch_info_equal:
8755 * This might fail to recognize equivalent patches, i.e. floats, so its only
8756 * usable in those cases where this is not a problem, i.e. sharing GOT slots
8757 * in AOT.
8759 gint
8760 mono_patch_info_equal (gconstpointer ka, gconstpointer kb)
8762 const MonoJumpInfo *ji1 = (MonoJumpInfo*)ka;
8763 const MonoJumpInfo *ji2 = (MonoJumpInfo*)kb;
8765 if (ji1->type != ji2->type)
8766 return 0;
8768 switch (ji1->type) {
8769 case MONO_PATCH_INFO_LDSTR:
8770 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
8771 case MONO_PATCH_INFO_LDTOKEN:
8772 case MONO_PATCH_INFO_DECLSEC:
8773 if ((ji1->data.token->image != ji2->data.token->image) ||
8774 (ji1->data.token->token != ji2->data.token->token))
8775 return 0;
8776 break;
8777 default:
8778 if (ji1->data.name != ji2->data.name)
8779 return 0;
8780 break;
8783 return 1;
8786 gpointer
8787 mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *patch_info, gboolean run_cctors)
8789 unsigned char *ip = patch_info->ip.i + code;
8790 gconstpointer target = NULL;
8792 switch (patch_info->type) {
8793 case MONO_PATCH_INFO_BB:
8794 target = patch_info->data.bb->native_offset + code;
8795 break;
8796 case MONO_PATCH_INFO_ABS:
8797 target = patch_info->data.target;
8798 break;
8799 case MONO_PATCH_INFO_LABEL:
8800 target = patch_info->data.inst->inst_c0 + code;
8801 break;
8802 case MONO_PATCH_INFO_IP:
8803 target = ip;
8804 break;
8805 case MONO_PATCH_INFO_METHOD_REL:
8806 target = code + patch_info->data.offset;
8807 break;
8808 case MONO_PATCH_INFO_INTERNAL_METHOD: {
8809 MonoJitICallInfo *mi = mono_find_jit_icall_by_name (patch_info->data.name);
8810 if (!mi) {
8811 g_warning ("unknown MONO_PATCH_INFO_INTERNAL_METHOD %s", patch_info->data.name);
8812 g_assert_not_reached ();
8814 target = mono_icall_get_wrapper (mi);
8815 break;
8817 case MONO_PATCH_INFO_METHOD_JUMP: {
8818 GSList *list;
8820 /* get the trampoline to the method from the domain */
8821 target = mono_create_jump_trampoline (domain, patch_info->data.method, TRUE);
8822 if (!domain->jump_target_hash)
8823 domain->jump_target_hash = g_hash_table_new (NULL, NULL);
8824 list = g_hash_table_lookup (domain->jump_target_hash, patch_info->data.method);
8825 list = g_slist_prepend (list, ip);
8826 g_hash_table_insert (domain->jump_target_hash, patch_info->data.method, list);
8827 break;
8829 case MONO_PATCH_INFO_METHOD:
8830 if (patch_info->data.method == method) {
8831 target = code;
8832 } else
8833 /* get the trampoline to the method from the domain */
8834 target = mono_create_jit_trampoline (patch_info->data.method);
8835 break;
8836 case MONO_PATCH_INFO_SWITCH: {
8837 gpointer *jump_table;
8838 int i;
8840 if (method && method->dynamic) {
8841 jump_table = mono_code_manager_reserve (mono_dynamic_code_hash_lookup (domain, method)->code_mp, sizeof (gpointer) * patch_info->data.table->table_size);
8842 } else {
8843 mono_domain_lock (domain);
8844 jump_table = mono_code_manager_reserve (domain->code_mp, sizeof (gpointer) * patch_info->data.table->table_size);
8845 mono_domain_unlock (domain);
8848 for (i = 0; i < patch_info->data.table->table_size; i++) {
8849 jump_table [i] = code + GPOINTER_TO_INT (patch_info->data.table->table [i]);
8851 target = jump_table;
8852 break;
8854 case MONO_PATCH_INFO_METHODCONST:
8855 case MONO_PATCH_INFO_CLASS:
8856 case MONO_PATCH_INFO_IMAGE:
8857 case MONO_PATCH_INFO_FIELD:
8858 target = patch_info->data.target;
8859 break;
8860 case MONO_PATCH_INFO_IID:
8861 mono_class_init (patch_info->data.klass);
8862 target = GINT_TO_POINTER ((int)patch_info->data.klass->interface_id);
8863 break;
8864 case MONO_PATCH_INFO_ADJUSTED_IID:
8865 mono_class_init (patch_info->data.klass);
8866 target = GINT_TO_POINTER ((int)(-((patch_info->data.klass->interface_id + 1) * SIZEOF_VOID_P)));
8867 break;
8868 case MONO_PATCH_INFO_VTABLE:
8869 target = mono_class_vtable (domain, patch_info->data.klass);
8870 break;
8871 case MONO_PATCH_INFO_CLASS_INIT:
8872 target = mono_create_class_init_trampoline (mono_class_vtable (domain, patch_info->data.klass));
8873 break;
8874 case MONO_PATCH_INFO_SFLDA: {
8875 MonoVTable *vtable = mono_class_vtable (domain, patch_info->data.field->parent);
8876 if (!vtable->initialized && !(vtable->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) && (method && mono_class_needs_cctor_run (vtable->klass, method)))
8877 /* Done by the generated code */
8879 else {
8880 if (run_cctors)
8881 mono_runtime_class_init (vtable);
8883 target = (char*)vtable->data + patch_info->data.field->offset;
8884 break;
8886 case MONO_PATCH_INFO_R4:
8887 case MONO_PATCH_INFO_R8:
8888 target = patch_info->data.target;
8889 break;
8890 case MONO_PATCH_INFO_EXC_NAME:
8891 target = patch_info->data.name;
8892 break;
8893 case MONO_PATCH_INFO_LDSTR:
8894 target =
8895 mono_ldstr (domain, patch_info->data.token->image,
8896 mono_metadata_token_index (patch_info->data.token->token));
8897 break;
8898 case MONO_PATCH_INFO_TYPE_FROM_HANDLE: {
8899 gpointer handle;
8900 MonoClass *handle_class;
8902 handle = mono_ldtoken (patch_info->data.token->image,
8903 patch_info->data.token->token, &handle_class, NULL);
8904 mono_class_init (handle_class);
8905 mono_class_init (mono_class_from_mono_type (handle));
8907 target =
8908 mono_type_get_object (domain, handle);
8909 break;
8911 case MONO_PATCH_INFO_LDTOKEN: {
8912 gpointer handle;
8913 MonoClass *handle_class;
8915 handle = mono_ldtoken (patch_info->data.token->image,
8916 patch_info->data.token->token, &handle_class, NULL);
8917 mono_class_init (handle_class);
8919 target = handle;
8920 break;
8922 case MONO_PATCH_INFO_DECLSEC:
8923 target = (mono_metadata_blob_heap (patch_info->data.token->image, patch_info->data.token->token) + 2);
8924 break;
8925 case MONO_PATCH_INFO_BB_OVF:
8926 case MONO_PATCH_INFO_EXC_OVF:
8927 case MONO_PATCH_INFO_GOT_OFFSET:
8928 case MONO_PATCH_INFO_NONE:
8929 break;
8930 default:
8931 g_assert_not_reached ();
8934 return (gpointer)target;
8937 static void
8938 dec_foreach (MonoInst *tree, MonoCompile *cfg) {
8939 MonoJitICallInfo *info;
8941 decompose_foreach (tree, cfg);
8943 switch (mono_burg_arity [tree->opcode]) {
8944 case 0: break;
8945 case 1:
8946 dec_foreach (tree->inst_left, cfg);
8948 if ((info = mono_find_jit_opcode_emulation (tree->opcode))) {
8949 MonoInst *iargs [2];
8951 iargs [0] = tree->inst_left;
8953 mono_emulate_opcode (cfg, tree, iargs, info);
8954 return;
8957 break;
8958 case 2:
8959 #ifdef MONO_ARCH_BIGMUL_INTRINS
8960 if (tree->opcode == OP_LMUL
8961 && (cfg->opt & MONO_OPT_INTRINS)
8962 && (tree->inst_left->opcode == CEE_CONV_I8
8963 || tree->inst_left->opcode == CEE_CONV_U8)
8964 && tree->inst_left->inst_left->type == STACK_I4
8965 && (tree->inst_right->opcode == CEE_CONV_I8
8966 || tree->inst_right->opcode == CEE_CONV_U8)
8967 && tree->inst_right->inst_left->type == STACK_I4
8968 && tree->inst_left->opcode == tree->inst_right->opcode) {
8969 tree->opcode = (tree->inst_left->opcode == CEE_CONV_I8 ? OP_BIGMUL: OP_BIGMUL_UN);
8970 tree->inst_left = tree->inst_left->inst_left;
8971 tree->inst_right = tree->inst_right->inst_left;
8972 dec_foreach (tree, cfg);
8973 } else
8974 #endif
8975 if ((info = mono_find_jit_opcode_emulation (tree->opcode))) {
8976 MonoInst *iargs [2];
8978 iargs [0] = tree->inst_i0;
8979 iargs [1] = tree->inst_i1;
8981 mono_emulate_opcode (cfg, tree, iargs, info);
8983 dec_foreach (iargs [0], cfg);
8984 dec_foreach (iargs [1], cfg);
8985 return;
8986 } else {
8987 dec_foreach (tree->inst_left, cfg);
8988 dec_foreach (tree->inst_right, cfg);
8990 break;
8991 default:
8992 g_assert_not_reached ();
8996 static void
8997 decompose_pass (MonoCompile *cfg) {
8998 MonoBasicBlock *bb;
9000 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
9001 MonoInst *tree;
9002 cfg->cbb = bb;
9003 cfg->prev_ins = NULL;
9004 for (tree = cfg->cbb->code; tree; tree = tree->next) {
9005 dec_foreach (tree, cfg);
9006 cfg->prev_ins = tree;
9011 static void
9012 nullify_basic_block (MonoBasicBlock *bb)
9014 bb->in_count = 0;
9015 bb->out_count = 0;
9016 bb->in_bb = NULL;
9017 bb->out_bb = NULL;
9018 bb->next_bb = NULL;
9019 bb->code = bb->last_ins = NULL;
9020 bb->cil_code = NULL;
9023 static void
9024 replace_out_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl)
9026 int i;
9028 for (i = 0; i < bb->out_count; i++) {
9029 MonoBasicBlock *ob = bb->out_bb [i];
9030 if (ob == orig) {
9031 if (!repl) {
9032 if (bb->out_count > 1) {
9033 bb->out_bb [i] = bb->out_bb [bb->out_count - 1];
9035 bb->out_count--;
9036 } else {
9037 bb->out_bb [i] = repl;
9043 static void
9044 replace_in_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl)
9046 int i;
9048 for (i = 0; i < bb->in_count; i++) {
9049 MonoBasicBlock *ib = bb->in_bb [i];
9050 if (ib == orig) {
9051 if (!repl) {
9052 if (bb->in_count > 1) {
9053 bb->in_bb [i] = bb->in_bb [bb->in_count - 1];
9055 bb->in_count--;
9056 } else {
9057 bb->in_bb [i] = repl;
9063 static void
9064 replace_out_block_in_code (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl) {
9065 MonoInst *inst;
9067 for (inst = bb->code; inst != NULL; inst = inst->next) {
9068 if (inst->opcode == OP_CALL_HANDLER) {
9069 if (inst->inst_target_bb == orig) {
9070 inst->inst_target_bb = repl;
9074 if (bb->last_ins != NULL) {
9075 switch (bb->last_ins->opcode) {
9076 case CEE_BR:
9077 if (bb->last_ins->inst_target_bb == orig) {
9078 bb->last_ins->inst_target_bb = repl;
9080 break;
9081 case CEE_SWITCH: {
9082 int i;
9083 int n = GPOINTER_TO_INT (bb->last_ins->klass);
9084 for (i = 0; i < n; i++ ) {
9085 if (bb->last_ins->inst_many_bb [i] == orig) {
9086 bb->last_ins->inst_many_bb [i] = repl;
9089 break;
9091 case CEE_BNE_UN:
9092 case CEE_BEQ:
9093 case CEE_BLT:
9094 case CEE_BLT_UN:
9095 case CEE_BGT:
9096 case CEE_BGT_UN:
9097 case CEE_BGE:
9098 case CEE_BGE_UN:
9099 case CEE_BLE:
9100 case CEE_BLE_UN:
9101 if (bb->last_ins->inst_true_bb == orig) {
9102 bb->last_ins->inst_true_bb = repl;
9104 if (bb->last_ins->inst_false_bb == orig) {
9105 bb->last_ins->inst_false_bb = repl;
9107 break;
9108 default:
9109 break;
9114 static void
9115 replace_basic_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl)
9117 int i, j;
9119 for (i = 0; i < bb->out_count; i++) {
9120 MonoBasicBlock *ob = bb->out_bb [i];
9121 for (j = 0; j < ob->in_count; j++) {
9122 if (ob->in_bb [j] == orig) {
9123 ob->in_bb [j] = repl;
9131 * Check if a bb is useless (is just made of NOPs and ends with an
9132 * unconditional branch, or nothing).
9133 * If it is so, unlink it from the CFG and nullify it, and return TRUE.
9134 * Otherwise, return FALSE;
9136 static gboolean
9137 remove_block_if_useless (MonoCompile *cfg, MonoBasicBlock *bb, MonoBasicBlock *previous_bb) {
9138 MonoBasicBlock *target_bb = NULL;
9139 MonoInst *inst;
9141 /* Do not touch handlers */
9142 if (bb->region != -1) {
9143 bb->not_useless = TRUE;
9144 return FALSE;
9147 for (inst = bb->code; inst != NULL; inst = inst->next) {
9148 switch (inst->opcode) {
9149 case CEE_NOP:
9150 break;
9151 case CEE_BR:
9152 target_bb = inst->inst_target_bb;
9153 break;
9154 default:
9155 bb->not_useless = TRUE;
9156 return FALSE;
9160 if (target_bb == NULL) {
9161 if ((bb->out_count == 1) && (bb->out_bb [0] == bb->next_bb)) {
9162 target_bb = bb->next_bb;
9163 } else {
9164 /* Do not touch empty BBs that do not "fall through" to their next BB (like the exit BB) */
9165 return FALSE;
9169 /* Do not touch BBs following a switch (they are the "default" branch) */
9170 if ((previous_bb->last_ins != NULL) && (previous_bb->last_ins->opcode == CEE_SWITCH)) {
9171 return FALSE;
9174 /* Do not touch BBs following the entry BB and jumping to something that is not */
9175 /* thiry "next" bb (the entry BB cannot contain the branch) */
9176 if ((previous_bb == cfg->bb_entry) && (bb->next_bb != target_bb)) {
9177 return FALSE;
9181 * Do not touch BBs following a try block as the code in
9182 * mini_method_compile needs them to compute the length of the try block.
9184 if (MONO_BBLOCK_IS_IN_REGION (previous_bb, MONO_REGION_TRY))
9185 return FALSE;
9187 /* Check that there is a target BB, and that bb is not an empty loop (Bug 75061) */
9188 if ((target_bb != NULL) && (target_bb != bb)) {
9189 int i;
9191 if (cfg->verbose_level > 1) {
9192 printf ("remove_block_if_useless %s, removed BB%d\n", mono_method_full_name (cfg->method, TRUE), bb->block_num);
9195 /* unlink_bblock () modifies the bb->in_bb array so can't use a for loop here */
9196 while (bb->in_count) {
9197 MonoBasicBlock *in_bb = bb->in_bb [0];
9198 mono_unlink_bblock (cfg, in_bb, bb);
9199 link_bblock (cfg, in_bb, target_bb);
9200 replace_out_block_in_code (in_bb, bb, target_bb);
9203 mono_unlink_bblock (cfg, bb, target_bb);
9205 if ((previous_bb != cfg->bb_entry) &&
9206 (previous_bb->region == bb->region) &&
9207 ((previous_bb->last_ins == NULL) ||
9208 ((previous_bb->last_ins->opcode != CEE_BR) &&
9209 (! (MONO_IS_COND_BRANCH_OP (previous_bb->last_ins))) &&
9210 (previous_bb->last_ins->opcode != CEE_SWITCH)))) {
9211 for (i = 0; i < previous_bb->out_count; i++) {
9212 if (previous_bb->out_bb [i] == target_bb) {
9213 MonoInst *jump;
9214 MONO_INST_NEW (cfg, jump, CEE_BR);
9215 MONO_ADD_INS (previous_bb, jump);
9216 jump->cil_code = previous_bb->cil_code;
9217 jump->inst_target_bb = target_bb;
9218 break;
9223 previous_bb->next_bb = bb->next_bb;
9224 nullify_basic_block (bb);
9226 return TRUE;
9227 } else {
9228 return FALSE;
9232 static void
9233 merge_basic_blocks (MonoBasicBlock *bb, MonoBasicBlock *bbn)
9235 bb->out_count = bbn->out_count;
9236 bb->out_bb = bbn->out_bb;
9238 replace_basic_block (bb, bbn, bb);
9240 /* Nullify branch at the end of bb */
9241 if (bb->last_ins && MONO_IS_BRANCH_OP (bb->last_ins)) {
9242 bb->last_ins->opcode = CEE_NOP;
9245 if (bb->last_ins) {
9246 if (bbn->code) {
9247 bb->last_ins->next = bbn->code;
9248 bb->last_ins = bbn->last_ins;
9250 } else {
9251 bb->code = bbn->code;
9252 bb->last_ins = bbn->last_ins;
9254 bb->next_bb = bbn->next_bb;
9255 nullify_basic_block (bbn);
9258 static void
9259 move_basic_block_to_end (MonoCompile *cfg, MonoBasicBlock *bb)
9261 MonoBasicBlock *bbn, *next;
9263 next = bb->next_bb;
9265 /* Find the previous */
9266 for (bbn = cfg->bb_entry; bbn->next_bb && bbn->next_bb != bb; bbn = bbn->next_bb)
9268 if (bbn->next_bb) {
9269 bbn->next_bb = bb->next_bb;
9272 /* Find the last */
9273 for (bbn = cfg->bb_entry; bbn->next_bb; bbn = bbn->next_bb)
9275 bbn->next_bb = bb;
9276 bb->next_bb = NULL;
9278 /* Add a branch */
9279 if (next && (!bb->last_ins || (bb->last_ins->opcode != OP_NOT_REACHED))) {
9280 MonoInst *ins;
9282 MONO_INST_NEW (cfg, ins, CEE_BR);
9283 MONO_ADD_INS (bb, ins);
9284 link_bblock (cfg, bb, next);
9285 ins->inst_target_bb = next;
9289 /* checks that a and b represent the same instructions, conservatively,
9290 * it can return FALSE also for two trees that are equal.
9291 * FIXME: also make sure there are no side effects.
9293 static int
9294 same_trees (MonoInst *a, MonoInst *b)
9296 int arity;
9297 if (a->opcode != b->opcode)
9298 return FALSE;
9299 arity = mono_burg_arity [a->opcode];
9300 if (arity == 1) {
9301 if (a->ssa_op == b->ssa_op && a->ssa_op == MONO_SSA_LOAD && a->inst_i0 == b->inst_i0)
9302 return TRUE;
9303 return same_trees (a->inst_left, b->inst_left);
9304 } else if (arity == 2) {
9305 return same_trees (a->inst_left, b->inst_left) && same_trees (a->inst_right, b->inst_right);
9306 } else if (arity == 0) {
9307 switch (a->opcode) {
9308 case OP_ICONST:
9309 return a->inst_c0 == b->inst_c0;
9310 default:
9311 return FALSE;
9314 return FALSE;
9317 static int
9318 get_unsigned_condbranch (int opcode)
9320 switch (opcode) {
9321 case CEE_BLE: return CEE_BLE_UN;
9322 case CEE_BLT: return CEE_BLT_UN;
9323 case CEE_BGE: return CEE_BGE_UN;
9324 case CEE_BGT: return CEE_BGT_UN;
9326 g_assert_not_reached ();
9327 return 0;
9330 static int
9331 tree_is_unsigned (MonoInst* ins) {
9332 switch (ins->opcode) {
9333 case OP_ICONST:
9334 return (int)ins->inst_c0 >= 0;
9335 /* array lengths are positive as are string sizes */
9336 case CEE_LDLEN:
9337 case OP_STRLEN:
9338 return TRUE;
9339 case CEE_CONV_U1:
9340 case CEE_CONV_U2:
9341 case CEE_CONV_U4:
9342 case CEE_CONV_OVF_U1:
9343 case CEE_CONV_OVF_U2:
9344 case CEE_CONV_OVF_U4:
9345 return TRUE;
9346 case CEE_LDIND_U1:
9347 case CEE_LDIND_U2:
9348 case CEE_LDIND_U4:
9349 return TRUE;
9350 default:
9351 return FALSE;
9355 /* check if an unsigned compare can be used instead of two signed compares
9356 * for (val < 0 || val > limit) conditionals.
9357 * Returns TRUE if the optimization has been applied.
9358 * Note that this can't be applied if the second arg is not positive...
9360 static int
9361 try_unsigned_compare (MonoCompile *cfg, MonoBasicBlock *bb)
9363 MonoBasicBlock *truet, *falset;
9364 MonoInst *cmp_inst = bb->last_ins->inst_left;
9365 MonoInst *condb;
9366 if (!cmp_inst->inst_right->inst_c0 == 0)
9367 return FALSE;
9368 truet = bb->last_ins->inst_true_bb;
9369 falset = bb->last_ins->inst_false_bb;
9370 if (falset->in_count != 1)
9371 return FALSE;
9372 condb = falset->last_ins;
9373 /* target bb must have one instruction */
9374 if (!condb || (condb != falset->code))
9375 return FALSE;
9376 if ((((condb->opcode == CEE_BLE || condb->opcode == CEE_BLT) && (condb->inst_false_bb == truet))
9377 || ((condb->opcode == CEE_BGE || condb->opcode == CEE_BGT) && (condb->inst_true_bb == truet)))
9378 && same_trees (cmp_inst->inst_left, condb->inst_left->inst_left)) {
9379 if (!tree_is_unsigned (condb->inst_left->inst_right))
9380 return FALSE;
9381 condb->opcode = get_unsigned_condbranch (condb->opcode);
9382 /* change the original condbranch to just point to the new unsigned check */
9383 bb->last_ins->opcode = CEE_BR;
9384 bb->last_ins->inst_target_bb = falset;
9385 replace_out_block (bb, truet, NULL);
9386 replace_in_block (truet, bb, NULL);
9387 return TRUE;
9389 return FALSE;
9393 * Optimizes the branches on the Control Flow Graph
9396 static void
9397 optimize_branches (MonoCompile *cfg)
9399 int i, changed = FALSE;
9400 MonoBasicBlock *bb, *bbn;
9401 guint32 niterations;
9404 * Some crazy loops could cause the code below to go into an infinite
9405 * loop, see bug #53003 for an example. To prevent this, we put an upper
9406 * bound on the number of iterations.
9408 if (cfg->num_bblocks > 1000)
9409 niterations = cfg->num_bblocks * 2;
9410 else
9411 niterations = 1000;
9413 do {
9414 MonoBasicBlock *previous_bb;
9415 changed = FALSE;
9416 niterations --;
9418 /* we skip the entry block (exit is handled specially instead ) */
9419 for (previous_bb = cfg->bb_entry, bb = cfg->bb_entry->next_bb; bb; previous_bb = bb, bb = bb->next_bb) {
9421 /* dont touch code inside exception clauses */
9422 if (bb->region != -1)
9423 continue;
9425 if (!bb->not_useless && remove_block_if_useless (cfg, bb, previous_bb)) {
9426 changed = TRUE;
9427 continue;
9430 if ((bbn = bb->next_bb) && bbn->in_count == 0 && bb->region == bbn->region) {
9431 if (cfg->verbose_level > 2)
9432 g_print ("nullify block triggered %d\n", bbn->block_num);
9434 bb->next_bb = bbn->next_bb;
9436 for (i = 0; i < bbn->out_count; i++)
9437 replace_in_block (bbn->out_bb [i], bbn, NULL);
9439 nullify_basic_block (bbn);
9440 changed = TRUE;
9443 if (bb->out_count == 1) {
9444 bbn = bb->out_bb [0];
9446 /* conditional branches where true and false targets are the same can be also replaced with CEE_BR */
9447 if (bb->last_ins && MONO_IS_COND_BRANCH_OP (bb->last_ins)) {
9448 MonoInst *pop;
9449 MONO_INST_NEW (cfg, pop, CEE_POP);
9450 pop->inst_left = bb->last_ins->inst_left->inst_left;
9451 mono_add_ins_to_end (bb, pop);
9452 MONO_INST_NEW (cfg, pop, CEE_POP);
9453 pop->inst_left = bb->last_ins->inst_left->inst_right;
9454 mono_add_ins_to_end (bb, pop);
9455 bb->last_ins->opcode = CEE_BR;
9456 bb->last_ins->inst_target_bb = bb->last_ins->inst_true_bb;
9457 changed = TRUE;
9458 if (cfg->verbose_level > 2)
9459 g_print ("cond branch removal triggered in %d %d\n", bb->block_num, bb->out_count);
9462 if (bb->region == bbn->region && bb->next_bb == bbn) {
9463 /* the block are in sequence anyway ... */
9465 /* branches to the following block can be removed */
9466 if (bb->last_ins && bb->last_ins->opcode == CEE_BR) {
9467 bb->last_ins->opcode = CEE_NOP;
9468 changed = TRUE;
9469 if (cfg->verbose_level > 2)
9470 g_print ("br removal triggered %d -> %d\n", bb->block_num, bbn->block_num);
9473 if (bbn->in_count == 1) {
9475 if (bbn != cfg->bb_exit) {
9476 if (cfg->verbose_level > 2)
9477 g_print ("block merge triggered %d -> %d\n", bb->block_num, bbn->block_num);
9478 merge_basic_blocks (bb, bbn);
9479 changed = TRUE;
9480 continue;
9483 //mono_print_bb_code (bb);
9487 if ((bbn = bb->next_bb) && bbn->in_count == 0 && bb->region == bbn->region) {
9488 if (cfg->verbose_level > 2) {
9489 g_print ("nullify block triggered %d\n", bbn->block_num);
9491 bb->next_bb = bbn->next_bb;
9493 for (i = 0; i < bbn->out_count; i++)
9494 replace_in_block (bbn->out_bb [i], bbn, NULL);
9496 nullify_basic_block (bbn);
9497 changed = TRUE;
9498 continue;
9501 if (bb->out_count == 1) {
9502 bbn = bb->out_bb [0];
9504 if (bb->last_ins && bb->last_ins->opcode == CEE_BR) {
9505 bbn = bb->last_ins->inst_target_bb;
9506 if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR &&
9507 bbn->code->inst_target_bb->region == bb->region) {
9509 if (cfg->verbose_level > 2)
9510 g_print ("in %s branch to branch triggered %d -> %d -> %d\n", cfg->method->name,
9511 bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num);
9513 replace_in_block (bbn, bb, NULL);
9514 replace_out_block (bb, bbn, bbn->code->inst_target_bb);
9515 link_bblock (cfg, bb, bbn->code->inst_target_bb);
9516 bb->last_ins->inst_target_bb = bbn->code->inst_target_bb;
9517 changed = TRUE;
9518 continue;
9521 } else if (bb->out_count == 2) {
9522 if (bb->last_ins && MONO_IS_COND_BRANCH_NOFP (bb->last_ins)) {
9523 int branch_result = mono_eval_cond_branch (bb->last_ins);
9524 MonoBasicBlock *taken_branch_target = NULL, *untaken_branch_target = NULL;
9525 if (branch_result == BRANCH_TAKEN) {
9526 taken_branch_target = bb->last_ins->inst_true_bb;
9527 untaken_branch_target = bb->last_ins->inst_false_bb;
9528 } else if (branch_result == BRANCH_NOT_TAKEN) {
9529 taken_branch_target = bb->last_ins->inst_false_bb;
9530 untaken_branch_target = bb->last_ins->inst_true_bb;
9532 if (taken_branch_target) {
9533 /* if mono_eval_cond_branch () is ever taken to handle
9534 * non-constant values to compare, issue a pop here.
9536 bb->last_ins->opcode = CEE_BR;
9537 bb->last_ins->inst_target_bb = taken_branch_target;
9538 mono_unlink_bblock (cfg, bb, untaken_branch_target);
9539 changed = TRUE;
9540 continue;
9542 bbn = bb->last_ins->inst_true_bb;
9543 if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR &&
9544 bbn->code->inst_target_bb->region == bb->region) {
9545 if (cfg->verbose_level > 2)
9546 g_print ("cbranch1 to branch triggered %d -> (%d) %d (0x%02x)\n",
9547 bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num,
9548 bbn->code->opcode);
9551 * Unlink, then relink bblocks to avoid various
9552 * tricky situations when the two targets of the branch
9553 * are equal, or will become equal after the change.
9555 mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb);
9556 mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb);
9558 bb->last_ins->inst_true_bb = bbn->code->inst_target_bb;
9560 link_bblock (cfg, bb, bb->last_ins->inst_true_bb);
9561 link_bblock (cfg, bb, bb->last_ins->inst_false_bb);
9563 changed = TRUE;
9564 continue;
9567 bbn = bb->last_ins->inst_false_bb;
9568 if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR &&
9569 bbn->code->inst_target_bb->region == bb->region) {
9570 if (cfg->verbose_level > 2)
9571 g_print ("cbranch2 to branch triggered %d -> (%d) %d (0x%02x)\n",
9572 bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num,
9573 bbn->code->opcode);
9575 mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb);
9576 mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb);
9578 bb->last_ins->inst_false_bb = bbn->code->inst_target_bb;
9580 link_bblock (cfg, bb, bb->last_ins->inst_true_bb);
9581 link_bblock (cfg, bb, bb->last_ins->inst_false_bb);
9583 changed = TRUE;
9584 continue;
9588 /* detect and optimize to unsigned compares checks like: if (v < 0 || v > limit */
9589 if (bb->last_ins && bb->last_ins->opcode == CEE_BLT && bb->last_ins->inst_left->inst_right->opcode == OP_ICONST) {
9590 if (try_unsigned_compare (cfg, bb)) {
9591 /*g_print ("applied in bb %d (->%d) %s\n", bb->block_num, bb->last_ins->inst_target_bb->block_num, mono_method_full_name (cfg->method, TRUE));*/
9592 changed = TRUE;
9593 continue;
9597 if (bb->last_ins && MONO_IS_COND_BRANCH_NOFP (bb->last_ins)) {
9598 if (bb->last_ins->inst_false_bb->out_of_line && (bb->region == bb->last_ins->inst_false_bb->region)) {
9599 /* Reverse the branch */
9600 bb->last_ins->opcode = reverse_branch_op (bb->last_ins->opcode);
9601 bbn = bb->last_ins->inst_false_bb;
9602 bb->last_ins->inst_false_bb = bb->last_ins->inst_true_bb;
9603 bb->last_ins->inst_true_bb = bbn;
9605 move_basic_block_to_end (cfg, bb->last_ins->inst_true_bb);
9606 if (cfg->verbose_level > 2)
9607 g_print ("cbranch to throw block triggered %d.\n",
9608 bb->block_num);
9613 } while (changed && (niterations > 0));
9617 static void
9618 mono_compile_create_vars (MonoCompile *cfg)
9620 MonoMethodSignature *sig;
9621 MonoMethodHeader *header;
9622 int i;
9624 header = mono_method_get_header (cfg->method);
9626 sig = mono_method_signature (cfg->method);
9628 if (!MONO_TYPE_IS_VOID (sig->ret)) {
9629 cfg->ret = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst));
9630 cfg->ret->opcode = OP_RETARG;
9631 cfg->ret->inst_vtype = sig->ret;
9632 cfg->ret->klass = mono_class_from_mono_type (sig->ret);
9634 if (cfg->verbose_level > 2)
9635 g_print ("creating vars\n");
9637 if (sig->hasthis)
9638 mono_compile_create_var (cfg, &cfg->method->klass->this_arg, OP_ARG);
9640 for (i = 0; i < sig->param_count; ++i) {
9641 mono_compile_create_var (cfg, sig->params [i], OP_ARG);
9642 if (sig->params [i]->byref) {
9643 cfg->disable_ssa = TRUE;
9647 cfg->locals_start = cfg->num_varinfo;
9649 if (cfg->verbose_level > 2)
9650 g_print ("creating locals\n");
9651 for (i = 0; i < header->num_locals; ++i)
9652 mono_compile_create_var (cfg, header->locals [i], OP_LOCAL);
9653 if (cfg->verbose_level > 2)
9654 g_print ("locals done\n");
9656 #ifdef MONO_ARCH_HAVE_CREATE_VARS
9657 mono_arch_create_vars (cfg);
9658 #endif
9661 void
9662 mono_print_code (MonoCompile *cfg)
9664 MonoBasicBlock *bb;
9666 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
9667 MonoInst *tree = bb->code;
9669 if (!tree)
9670 continue;
9672 g_print ("CODE BLOCK %d (nesting %d):\n", bb->block_num, bb->nesting);
9674 for (; tree; tree = tree->next) {
9675 mono_print_tree (tree);
9676 g_print ("\n");
9679 if (bb->last_ins)
9680 bb->last_ins->next = NULL;
9684 extern const char * const mono_burg_rule_string [];
9686 static void
9687 emit_state (MonoCompile *cfg, MBState *state, int goal)
9689 MBState *kids [10];
9690 int ern = mono_burg_rule (state, goal);
9691 const guint16 *nts = mono_burg_nts_data + mono_burg_nts [ern];
9693 //g_print ("rule: %s\n", mono_burg_rule_string [ern]);
9694 switch (goal) {
9695 case MB_NTERM_reg:
9696 //if (state->reg2)
9697 // state->reg1 = state->reg2; /* chain rule */
9698 //else
9699 #ifdef MONO_ARCH_ENABLE_EMIT_STATE_OPT
9700 if (!state->reg1)
9701 #endif
9702 state->reg1 = mono_regstate_next_int (cfg->rs);
9703 //g_print ("alloc symbolic R%d (reg2: R%d) in block %d\n", state->reg1, state->reg2, cfg->cbb->block_num);
9704 break;
9705 case MB_NTERM_lreg:
9706 state->reg1 = mono_regstate_next_int (cfg->rs);
9707 state->reg2 = mono_regstate_next_int (cfg->rs);
9708 break;
9709 case MB_NTERM_freg:
9710 #ifdef MONO_ARCH_SOFT_FLOAT
9711 state->reg1 = mono_regstate_next_int (cfg->rs);
9712 state->reg2 = mono_regstate_next_int (cfg->rs);
9713 #else
9714 state->reg1 = mono_regstate_next_float (cfg->rs);
9715 #endif
9716 break;
9717 default:
9718 #ifdef MONO_ARCH_ENABLE_EMIT_STATE_OPT
9720 * Enabling this might cause bugs to surface in the local register
9721 * allocators on some architectures like x86.
9723 if ((state->tree->ssa_op == MONO_SSA_STORE) && (state->left->tree->opcode == OP_REGVAR)) {
9724 /* Do not optimize away reg-reg moves */
9725 if (! ((state->right->tree->ssa_op == MONO_SSA_LOAD) && (state->right->left->tree->opcode == OP_REGVAR))) {
9726 state->right->reg1 = state->left->tree->dreg;
9729 #endif
9731 /* do nothing */
9732 break;
9734 if (nts [0]) {
9735 mono_burg_kids (state, ern, kids);
9737 emit_state (cfg, kids [0], nts [0]);
9738 if (nts [1]) {
9739 emit_state (cfg, kids [1], nts [1]);
9740 if (nts [2]) {
9741 g_assert (!nts [3]);
9742 emit_state (cfg, kids [2], nts [2]);
9747 // g_print ("emit: %s (%p)\n", mono_burg_rule_string [ern], state);
9748 mono_burg_emit (ern, state, state->tree, cfg);
9751 #define DEBUG_SELECTION
9753 static void
9754 mini_select_instructions (MonoCompile *cfg)
9756 MonoBasicBlock *bb;
9758 cfg->state_pool = mono_mempool_new ();
9759 cfg->rs = mono_regstate_new ();
9761 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
9762 if (bb->last_ins && MONO_IS_COND_BRANCH_OP (bb->last_ins) &&
9763 bb->next_bb != bb->last_ins->inst_false_bb) {
9765 /* we are careful when inverting, since bugs like #59580
9766 * could show up when dealing with NaNs.
9768 if (MONO_IS_COND_BRANCH_NOFP(bb->last_ins) && bb->next_bb == bb->last_ins->inst_true_bb) {
9769 MonoBasicBlock *tmp = bb->last_ins->inst_true_bb;
9770 bb->last_ins->inst_true_bb = bb->last_ins->inst_false_bb;
9771 bb->last_ins->inst_false_bb = tmp;
9773 bb->last_ins->opcode = reverse_branch_op (bb->last_ins->opcode);
9774 } else {
9775 MonoInst *inst = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst));
9776 inst->opcode = CEE_BR;
9777 inst->inst_target_bb = bb->last_ins->inst_false_bb;
9778 mono_bblock_add_inst (bb, inst);
9783 #ifdef DEBUG_SELECTION
9784 if (cfg->verbose_level >= 4) {
9785 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
9786 MonoInst *tree = bb->code;
9787 g_print ("DUMP BLOCK %d:\n", bb->block_num);
9788 if (!tree)
9789 continue;
9790 for (; tree; tree = tree->next) {
9791 mono_print_tree (tree);
9792 g_print ("\n");
9796 #endif
9798 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
9799 MonoInst *tree = bb->code, *next;
9800 MBState *mbstate;
9802 if (!tree)
9803 continue;
9804 bb->code = NULL;
9805 bb->last_ins = NULL;
9807 cfg->cbb = bb;
9808 mono_regstate_reset (cfg->rs);
9810 #ifdef DEBUG_SELECTION
9811 if (cfg->verbose_level >= 3)
9812 g_print ("LABEL BLOCK %d:\n", bb->block_num);
9813 #endif
9814 for (; tree; tree = next) {
9815 next = tree->next;
9816 #ifdef DEBUG_SELECTION
9817 if (cfg->verbose_level >= 3) {
9818 mono_print_tree (tree);
9819 g_print ("\n");
9821 #endif
9823 if (!(mbstate = mono_burg_label (tree, cfg))) {
9824 g_warning ("unable to label tree %p", tree);
9825 mono_print_tree (tree);
9826 g_print ("\n");
9827 g_assert_not_reached ();
9829 emit_state (cfg, mbstate, MB_NTERM_stmt);
9831 bb->max_ireg = cfg->rs->next_vireg;
9832 bb->max_freg = cfg->rs->next_vfreg;
9834 if (bb->last_ins)
9835 bb->last_ins->next = NULL;
9837 mono_mempool_empty (cfg->state_pool);
9839 mono_mempool_destroy (cfg->state_pool);
9842 void
9843 mono_codegen (MonoCompile *cfg)
9845 MonoJumpInfo *patch_info;
9846 MonoBasicBlock *bb;
9847 int i, max_epilog_size;
9848 guint8 *code;
9850 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
9851 cfg->spill_count = 0;
9852 /* we reuse dfn here */
9853 /* bb->dfn = bb_count++; */
9854 mono_arch_local_regalloc (cfg, bb);
9857 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
9858 cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, cfg->num_bblocks);
9860 code = mono_arch_emit_prolog (cfg);
9862 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
9863 code = mono_arch_instrument_prolog (cfg, mono_profiler_method_enter, code, FALSE);
9865 cfg->code_len = code - cfg->native_code;
9866 cfg->prolog_end = cfg->code_len;
9868 mono_debug_open_method (cfg);
9870 /* emit code all basic blocks */
9871 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
9872 bb->native_offset = cfg->code_len;
9873 mono_arch_output_basic_block (cfg, bb);
9875 if (bb == cfg->bb_exit) {
9876 cfg->epilog_begin = cfg->code_len;
9878 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) {
9879 code = cfg->native_code + cfg->code_len;
9880 code = mono_arch_instrument_epilog (cfg, mono_profiler_method_leave, code, FALSE);
9881 cfg->code_len = code - cfg->native_code;
9884 mono_arch_emit_epilog (cfg);
9888 mono_arch_emit_exceptions (cfg);
9890 max_epilog_size = 0;
9892 code = cfg->native_code + cfg->code_len;
9894 /* we always allocate code in cfg->domain->code_mp to increase locality */
9895 cfg->code_size = cfg->code_len + max_epilog_size;
9896 /* fixme: align to MONO_ARCH_CODE_ALIGNMENT */
9898 if (cfg->method->dynamic) {
9899 /* Allocate the code into a separate memory pool so it can be freed */
9900 cfg->dynamic_info = g_new0 (MonoJitDynamicMethodInfo, 1);
9901 cfg->dynamic_info->code_mp = mono_code_manager_new_dynamic ();
9902 mono_domain_lock (cfg->domain);
9903 mono_dynamic_code_hash_insert (cfg->domain, cfg->method, cfg->dynamic_info);
9904 mono_domain_unlock (cfg->domain);
9906 code = mono_code_manager_reserve (cfg->dynamic_info->code_mp, cfg->code_size);
9907 } else {
9908 mono_domain_lock (cfg->domain);
9909 code = mono_code_manager_reserve (cfg->domain->code_mp, cfg->code_size);
9910 mono_domain_unlock (cfg->domain);
9913 memcpy (code, cfg->native_code, cfg->code_len);
9914 g_free (cfg->native_code);
9915 cfg->native_code = code;
9916 code = cfg->native_code + cfg->code_len;
9918 /* g_assert (((int)cfg->native_code & (MONO_ARCH_CODE_ALIGNMENT - 1)) == 0); */
9919 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
9920 switch (patch_info->type) {
9921 case MONO_PATCH_INFO_ABS: {
9922 MonoJitICallInfo *info = mono_find_jit_icall_by_addr (patch_info->data.target);
9923 if (info) {
9924 //printf ("TEST %s %p\n", info->name, patch_info->data.target);
9925 if ((cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) &&
9926 strstr (cfg->method->name, info->name))
9928 * This is an icall wrapper, and this is a call to the
9929 * wrapped function.
9932 else {
9933 /* for these array methods we currently register the same function pointer
9934 * since it's a vararg function. But this means that mono_find_jit_icall_by_addr ()
9935 * will return the incorrect one depending on the order they are registered.
9936 * See tests/test-arr.cs
9938 if (strstr (info->name, "ves_array_new_va_") == NULL && strstr (info->name, "ves_array_element_address_") == NULL) {
9939 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
9940 patch_info->data.name = info->name;
9944 else {
9945 MonoVTable *vtable = mono_find_class_init_trampoline_by_addr (patch_info->data.target);
9946 if (vtable) {
9947 patch_info->type = MONO_PATCH_INFO_CLASS_INIT;
9948 patch_info->data.klass = vtable->klass;
9951 break;
9953 case MONO_PATCH_INFO_SWITCH: {
9954 gpointer *table;
9955 if (cfg->method->dynamic) {
9956 table = mono_code_manager_reserve (cfg->dynamic_info->code_mp, sizeof (gpointer) * patch_info->data.table->table_size);
9957 } else {
9958 mono_domain_lock (cfg->domain);
9959 table = mono_code_manager_reserve (cfg->domain->code_mp, sizeof (gpointer) * patch_info->data.table->table_size);
9960 mono_domain_unlock (cfg->domain);
9963 if (!cfg->compile_aot)
9964 /* In the aot case, the patch already points to the correct location */
9965 patch_info->ip.i = patch_info->ip.label->inst_c0;
9966 for (i = 0; i < patch_info->data.table->table_size; i++) {
9967 table [i] = GINT_TO_POINTER (patch_info->data.table->table [i]->native_offset);
9969 patch_info->data.table->table = (MonoBasicBlock**)table;
9970 break;
9972 default:
9973 /* do nothing */
9974 break;
9978 if (cfg->verbose_level > 0) {
9979 char* nm = mono_method_full_name (cfg->method, TRUE);
9980 g_print ("Method %s emitted at %p to %p (code length %d) [%s]\n",
9981 nm,
9982 cfg->native_code, cfg->native_code + cfg->code_len, cfg->code_len, cfg->domain->friendly_name);
9983 g_free (nm);
9986 #ifdef MONO_ARCH_HAVE_SAVE_UNWIND_INFO
9987 mono_arch_save_unwind_info (cfg);
9988 #endif
9990 mono_arch_patch_code (cfg->method, cfg->domain, cfg->native_code, cfg->patch_info, cfg->run_cctors);
9992 if (cfg->method->dynamic) {
9993 mono_code_manager_commit (cfg->dynamic_info->code_mp, cfg->native_code, cfg->code_size, cfg->code_len);
9994 } else {
9995 mono_domain_lock (cfg->domain);
9996 mono_code_manager_commit (cfg->domain->code_mp, cfg->native_code, cfg->code_size, cfg->code_len);
9997 mono_domain_unlock (cfg->domain);
10000 mono_arch_flush_icache (cfg->native_code, cfg->code_len);
10002 mono_debug_close_method (cfg);
10007 static void
10008 remove_critical_edges (MonoCompile *cfg) {
10009 MonoBasicBlock *bb;
10010 MonoBasicBlock *previous_bb;
10012 if (cfg->verbose_level > 3) {
10013 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
10014 int i;
10015 printf ("remove_critical_edges %s, BEFORE BB%d (in:", mono_method_full_name (cfg->method, TRUE), bb->block_num);
10016 for (i = 0; i < bb->in_count; i++) {
10017 printf (" %d", bb->in_bb [i]->block_num);
10019 printf (") (out:");
10020 for (i = 0; i < bb->out_count; i++) {
10021 printf (" %d", bb->out_bb [i]->block_num);
10023 printf (")");
10024 if (bb->last_ins != NULL) {
10025 printf (" ");
10026 mono_print_tree (bb->last_ins);
10028 printf ("\n");
10032 for (previous_bb = cfg->bb_entry, bb = previous_bb->next_bb; bb != NULL; previous_bb = previous_bb->next_bb, bb = bb->next_bb) {
10033 if (bb->in_count > 1) {
10034 int in_bb_index;
10035 for (in_bb_index = 0; in_bb_index < bb->in_count; in_bb_index++) {
10036 MonoBasicBlock *in_bb = bb->in_bb [in_bb_index];
10037 if (in_bb->out_count > 1) {
10038 MonoBasicBlock *new_bb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock));
10039 new_bb->block_num = cfg->num_bblocks++;
10040 // new_bb->real_offset = bb->real_offset;
10041 new_bb->region = bb->region;
10043 /* Do not alter the CFG while altering the BB list */
10044 if (previous_bb->region == bb->region) {
10045 if (previous_bb != cfg->bb_entry) {
10046 /* If previous_bb "followed through" to bb, */
10047 /* keep it linked with a CEE_BR */
10048 if ((previous_bb->last_ins == NULL) ||
10049 ((previous_bb->last_ins->opcode != CEE_BR) &&
10050 (! (MONO_IS_COND_BRANCH_OP (previous_bb->last_ins))) &&
10051 (previous_bb->last_ins->opcode != CEE_SWITCH))) {
10052 int i;
10053 /* Make sure previous_bb really falls through bb */
10054 for (i = 0; i < previous_bb->out_count; i++) {
10055 if (previous_bb->out_bb [i] == bb) {
10056 MonoInst *jump;
10057 MONO_INST_NEW (cfg, jump, CEE_BR);
10058 MONO_ADD_INS (previous_bb, jump);
10059 jump->cil_code = previous_bb->cil_code;
10060 jump->inst_target_bb = bb;
10061 break;
10065 } else {
10066 /* We cannot add any inst to the entry BB, so we must */
10067 /* put a new BB in the middle to hold the CEE_BR */
10068 MonoInst *jump;
10069 MonoBasicBlock *new_bb_after_entry = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock));
10070 new_bb_after_entry->block_num = cfg->num_bblocks++;
10071 // new_bb_after_entry->real_offset = bb->real_offset;
10072 new_bb_after_entry->region = bb->region;
10074 MONO_INST_NEW (cfg, jump, CEE_BR);
10075 MONO_ADD_INS (new_bb_after_entry, jump);
10076 jump->cil_code = bb->cil_code;
10077 jump->inst_target_bb = bb;
10079 previous_bb->next_bb = new_bb_after_entry;
10080 previous_bb = new_bb_after_entry;
10082 if (cfg->verbose_level > 2) {
10083 printf ("remove_critical_edges %s, added helper BB%d jumping to BB%d\n", mono_method_full_name (cfg->method, TRUE), new_bb_after_entry->block_num, bb->block_num);
10088 /* Insert new_bb in the BB list */
10089 previous_bb->next_bb = new_bb;
10090 new_bb->next_bb = bb;
10091 previous_bb = new_bb;
10093 /* Setup in_bb and out_bb */
10094 new_bb->in_bb = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*));
10095 new_bb->in_bb [0] = in_bb;
10096 new_bb->in_count = 1;
10097 new_bb->out_bb = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*));
10098 new_bb->out_bb [0] = bb;
10099 new_bb->out_count = 1;
10101 /* Relink in_bb and bb to (from) new_bb */
10102 replace_out_block (in_bb, bb, new_bb);
10103 replace_out_block_in_code (in_bb, bb, new_bb);
10104 replace_in_block (bb, in_bb, new_bb);
10106 if (cfg->verbose_level > 2) {
10107 printf ("remove_critical_edges %s, removed critical edge from BB%d to BB%d (added BB%d)\n", mono_method_full_name (cfg->method, TRUE), in_bb->block_num, bb->block_num, new_bb->block_num);
10114 if (cfg->verbose_level > 3) {
10115 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
10116 int i;
10117 printf ("remove_critical_edges %s, AFTER BB%d (in:", mono_method_full_name (cfg->method, TRUE), bb->block_num);
10118 for (i = 0; i < bb->in_count; i++) {
10119 printf (" %d", bb->in_bb [i]->block_num);
10121 printf (") (out:");
10122 for (i = 0; i < bb->out_count; i++) {
10123 printf (" %d", bb->out_bb [i]->block_num);
10125 printf (")");
10126 if (bb->last_ins != NULL) {
10127 printf (" ");
10128 mono_print_tree (bb->last_ins);
10130 printf ("\n");
10136 * mini_method_compile:
10137 * @method: the method to compile
10138 * @opts: the optimization flags to use
10139 * @domain: the domain where the method will be compiled in
10140 * @run_cctors: whether we should run type ctors if possible
10141 * @compile_aot: whether this is an AOT compilation
10142 * @parts: debug flag
10144 * Returns: a MonoCompile* pointer. Caller must check the exception_type
10145 * field in the returned struct to see if compilation succeded.
10147 MonoCompile*
10148 mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gboolean run_cctors, gboolean compile_aot, int parts)
10150 MonoMethodHeader *header = mono_method_get_header (method);
10151 guint8 *ip;
10152 MonoCompile *cfg;
10153 MonoJitInfo *jinfo;
10154 int dfn = 0, i, code_size_ratio;
10155 gboolean deadce_has_run = FALSE;
10157 mono_jit_stats.methods_compiled++;
10158 if (mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION)
10159 mono_profiler_method_jit (method);
10161 cfg = g_new0 (MonoCompile, 1);
10162 cfg->method = method;
10163 cfg->mempool = mono_mempool_new ();
10164 cfg->opt = opts;
10165 cfg->prof_options = mono_profiler_get_events ();
10166 cfg->run_cctors = run_cctors;
10167 cfg->bb_hash = g_hash_table_new (NULL, NULL);
10168 cfg->domain = domain;
10169 cfg->verbose_level = mini_verbose;
10170 cfg->compile_aot = compile_aot;
10171 cfg->skip_visibility = method->skip_visibility;
10172 if (!header) {
10173 cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM;
10174 cfg->exception_message = g_strdup_printf ("Missing or incorrect header for method %s", cfg->method->name);
10175 if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
10176 mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_FAILED);
10177 return cfg;
10180 ip = (guint8 *)header->code;
10182 cfg->intvars = mono_mempool_alloc0 (cfg->mempool, sizeof (guint16) * STACK_MAX * header->max_stack);
10183 cfg->aliasing_info = NULL;
10185 if (cfg->verbose_level > 2)
10186 g_print ("converting method %s\n", mono_method_full_name (method, TRUE));
10189 * create MonoInst* which represents arguments and local variables
10191 mono_compile_create_vars (cfg);
10193 if ((i = mono_method_to_ir (cfg, method, NULL, NULL, cfg->locals_start, NULL, NULL, NULL, 0, FALSE)) < 0) {
10194 if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
10195 mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_FAILED);
10196 /* cfg contains the details of the failure, so let the caller cleanup */
10197 return cfg;
10200 mono_jit_stats.basic_blocks += cfg->num_bblocks;
10201 mono_jit_stats.max_basic_blocks = MAX (cfg->num_bblocks, mono_jit_stats.max_basic_blocks);
10203 if ((cfg->num_varinfo > 2000) && !cfg->compile_aot) {
10205 * we disable some optimizations if there are too many variables
10206 * because JIT time may become too expensive. The actual number needs
10207 * to be tweaked and eventually the non-linear algorithms should be fixed.
10209 cfg->opt &= ~ (MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP);
10210 cfg->disable_ssa = TRUE;
10213 /*g_print ("numblocks = %d\n", cfg->num_bblocks);*/
10215 if (cfg->opt & MONO_OPT_BRANCH)
10216 optimize_branches (cfg);
10218 if (cfg->opt & MONO_OPT_SSAPRE) {
10219 remove_critical_edges (cfg);
10222 /* Depth-first ordering on basic blocks */
10223 cfg->bblocks = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (cfg->num_bblocks + 1));
10225 df_visit (cfg->bb_entry, &dfn, cfg->bblocks);
10226 if (cfg->num_bblocks != dfn + 1) {
10227 MonoBasicBlock *bb;
10229 cfg->num_bblocks = dfn + 1;
10231 if (!header->clauses) {
10232 /* remove unreachable code, because the code in them may be
10233 * inconsistent (access to dead variables for example) */
10234 for (bb = cfg->bb_entry; bb;) {
10235 MonoBasicBlock *bbn = bb->next_bb;
10237 if (bbn && bbn->region == -1 && !bbn->dfn) {
10238 if (cfg->verbose_level > 1)
10239 g_print ("found unreachable code in BB%d\n", bbn->block_num);
10240 bb->next_bb = bbn->next_bb;
10241 nullify_basic_block (bbn);
10242 } else {
10243 bb = bb->next_bb;
10249 if (cfg->opt & MONO_OPT_LOOP) {
10250 mono_compile_dominator_info (cfg, MONO_COMP_DOM | MONO_COMP_IDOM);
10251 mono_compute_natural_loops (cfg);
10254 /* after method_to_ir */
10255 if (parts == 1)
10256 return cfg;
10258 //#define DEBUGSSA "logic_run"
10259 #define DEBUGSSA_CLASS "Tests"
10260 #ifdef DEBUGSSA
10262 if (!header->num_clauses && !cfg->disable_ssa) {
10263 mono_local_cprop (cfg);
10264 mono_ssa_compute (cfg);
10266 #else
10268 /* fixme: add all optimizations which requires SSA */
10269 if (cfg->opt & (MONO_OPT_SSA | MONO_OPT_ABCREM | MONO_OPT_SSAPRE)) {
10270 if (!(cfg->comp_done & MONO_COMP_SSA) && !header->num_clauses && !cfg->disable_ssa) {
10271 mono_local_cprop (cfg);
10272 mono_ssa_compute (cfg);
10274 if (cfg->verbose_level >= 2) {
10275 print_dfn (cfg);
10279 #endif
10281 /* after SSA translation */
10282 if (parts == 2)
10283 return cfg;
10285 if ((cfg->opt & MONO_OPT_CONSPROP) || (cfg->opt & MONO_OPT_COPYPROP)) {
10286 if (cfg->comp_done & MONO_COMP_SSA) {
10287 mono_ssa_cprop (cfg);
10288 } else {
10289 mono_local_cprop (cfg);
10293 if (cfg->comp_done & MONO_COMP_SSA) {
10294 //mono_ssa_deadce (cfg);
10296 //mono_ssa_strength_reduction (cfg);
10298 if (cfg->opt & MONO_OPT_SSAPRE) {
10299 mono_perform_ssapre (cfg);
10300 //mono_local_cprop (cfg);
10303 if (cfg->opt & MONO_OPT_DEADCE) {
10304 mono_ssa_deadce (cfg);
10305 deadce_has_run = TRUE;
10308 if ((cfg->flags & MONO_CFG_HAS_LDELEMA) && (cfg->opt & MONO_OPT_ABCREM))
10309 mono_perform_abc_removal (cfg);
10311 mono_ssa_remove (cfg);
10313 if (cfg->opt & MONO_OPT_BRANCH)
10314 optimize_branches (cfg);
10317 /* after SSA removal */
10318 if (parts == 3)
10319 return cfg;
10321 if (cfg->verbose_level > 4) {
10322 printf ("BEFORE DECOMPSE START\n");
10323 mono_print_code (cfg);
10324 printf ("BEFORE DECOMPSE END\n");
10327 decompose_pass (cfg);
10329 if (cfg->got_var) {
10330 GList *regs;
10332 g_assert (cfg->got_var_allocated);
10335 * Allways allocate the GOT var to a register, because keeping it
10336 * in memory will increase the number of live temporaries in some
10337 * code created by inssel.brg, leading to the well known spills+
10338 * branches problem. Testcase: mcs crash in
10339 * System.MonoCustomAttrs:GetCustomAttributes.
10341 regs = mono_arch_get_global_int_regs (cfg);
10342 g_assert (regs);
10343 cfg->got_var->opcode = OP_REGVAR;
10344 cfg->got_var->dreg = GPOINTER_TO_INT (regs->data);
10345 cfg->used_int_regs |= 1LL << cfg->got_var->dreg;
10347 g_list_free (regs);
10350 if (cfg->opt & MONO_OPT_LINEARS) {
10351 GList *vars, *regs;
10353 /* For now, compute aliasing info only if needed for deadce... */
10354 if ((cfg->opt & MONO_OPT_DEADCE) && (! deadce_has_run) && (header->num_clauses == 0)) {
10355 cfg->aliasing_info = mono_build_aliasing_information (cfg);
10358 /* fixme: maybe we can avoid to compute livenesss here if already computed ? */
10359 cfg->comp_done &= ~MONO_COMP_LIVENESS;
10360 if (!(cfg->comp_done & MONO_COMP_LIVENESS))
10361 mono_analyze_liveness (cfg);
10363 if (cfg->aliasing_info != NULL) {
10364 mono_aliasing_deadce (cfg->aliasing_info);
10365 deadce_has_run = TRUE;
10368 if ((vars = mono_arch_get_allocatable_int_vars (cfg))) {
10369 regs = mono_arch_get_global_int_regs (cfg);
10370 if (cfg->got_var)
10371 regs = g_list_delete_link (regs, regs);
10372 mono_linear_scan (cfg, vars, regs, &cfg->used_int_regs);
10375 if (cfg->aliasing_info != NULL) {
10376 mono_destroy_aliasing_information (cfg->aliasing_info);
10377 cfg->aliasing_info = NULL;
10381 //mono_print_code (cfg);
10383 //print_dfn (cfg);
10385 /* variables are allocated after decompose, since decompose could create temps */
10386 mono_arch_allocate_vars (cfg);
10388 if (cfg->opt & MONO_OPT_CFOLD)
10389 mono_constant_fold (cfg);
10391 mini_select_instructions (cfg);
10393 mono_codegen (cfg);
10394 if (cfg->verbose_level >= 2) {
10395 char *id = mono_method_full_name (cfg->method, FALSE);
10396 mono_disassemble_code (cfg, cfg->native_code, cfg->code_len, id + 3);
10397 g_free (id);
10400 if (cfg->method->dynamic) {
10401 jinfo = g_malloc0 (sizeof (MonoJitInfo) + (header->num_clauses * sizeof (MonoJitExceptionInfo)));
10402 } else {
10403 /* we access cfg->domain->mp */
10404 mono_domain_lock (cfg->domain);
10405 jinfo = mono_mempool_alloc0 (cfg->domain->mp, sizeof (MonoJitInfo) + (header->num_clauses * sizeof (MonoJitExceptionInfo)));
10406 mono_domain_unlock (cfg->domain);
10409 jinfo->method = method;
10410 jinfo->code_start = cfg->native_code;
10411 jinfo->code_size = cfg->code_len;
10412 jinfo->used_regs = cfg->used_int_regs;
10413 jinfo->domain_neutral = (cfg->opt & MONO_OPT_SHARED) != 0;
10414 jinfo->cas_inited = FALSE; /* initialization delayed at the first stalk walk using this method */
10416 if (header->num_clauses) {
10417 int i;
10419 jinfo->num_clauses = header->num_clauses;
10421 for (i = 0; i < header->num_clauses; i++) {
10422 MonoExceptionClause *ec = &header->clauses [i];
10423 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
10424 MonoBasicBlock *tblock;
10425 MonoInst *exvar;
10427 ei->flags = ec->flags;
10429 exvar = mono_find_exvar_for_offset (cfg, ec->handler_offset);
10430 ei->exvar_offset = exvar ? exvar->inst_offset : 0;
10432 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
10433 tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->data.filter_offset);
10434 g_assert (tblock);
10435 ei->data.filter = cfg->native_code + tblock->native_offset;
10436 } else {
10437 ei->data.catch_class = ec->data.catch_class;
10440 tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->try_offset);
10441 g_assert (tblock);
10442 ei->try_start = cfg->native_code + tblock->native_offset;
10443 g_assert (tblock->native_offset);
10444 tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->try_offset + ec->try_len);
10445 g_assert (tblock);
10446 ei->try_end = cfg->native_code + tblock->native_offset;
10447 g_assert (tblock->native_offset);
10448 tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->handler_offset);
10449 g_assert (tblock);
10450 ei->handler_start = cfg->native_code + tblock->native_offset;
10454 cfg->jit_info = jinfo;
10455 #if defined(__arm__)
10456 mono_arch_fixup_jinfo (cfg);
10457 #endif
10459 mono_domain_lock (cfg->domain);
10460 mono_jit_info_table_add (cfg->domain, jinfo);
10462 if (cfg->method->dynamic)
10463 mono_dynamic_code_hash_lookup (cfg->domain, cfg->method)->ji = jinfo;
10464 mono_domain_unlock (cfg->domain);
10466 /* collect statistics */
10467 mono_jit_stats.allocated_code_size += cfg->code_len;
10468 code_size_ratio = cfg->code_len;
10469 if (code_size_ratio > mono_jit_stats.biggest_method_size) {
10470 mono_jit_stats.biggest_method_size = code_size_ratio;
10471 mono_jit_stats.biggest_method = method;
10473 code_size_ratio = (code_size_ratio * 100) / mono_method_get_header (method)->code_size;
10474 if (code_size_ratio > mono_jit_stats.max_code_size_ratio) {
10475 mono_jit_stats.max_code_size_ratio = code_size_ratio;
10476 mono_jit_stats.max_ratio_method = method;
10478 mono_jit_stats.native_code_size += cfg->code_len;
10480 if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
10481 mono_profiler_method_end_jit (method, jinfo, MONO_PROFILE_OK);
10483 return cfg;
10486 static gpointer
10487 mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, int opt)
10489 MonoCompile *cfg;
10490 GHashTable *jit_code_hash;
10491 gpointer code = NULL;
10492 MonoJitInfo *info;
10494 jit_code_hash = target_domain->jit_code_hash;
10496 method = mono_get_inflated_method (method);
10498 #ifdef MONO_USE_AOT_COMPILER
10499 if ((opt & MONO_OPT_AOT) && !(mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION)) {
10500 MonoDomain *domain = mono_domain_get ();
10502 mono_class_init (method->klass);
10504 mono_domain_lock (domain);
10505 if ((code = mono_aot_get_method (domain, method))) {
10506 mono_domain_unlock (domain);
10507 mono_runtime_class_init (mono_class_vtable (domain, method->klass));
10508 return code;
10511 mono_domain_unlock (domain);
10513 #endif
10515 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
10516 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
10517 MonoMethod *nm;
10518 MonoMethodPInvoke* piinfo = (MonoMethodPInvoke *) method;
10520 if (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE && !MONO_CLASS_IS_IMPORT(method->klass))
10521 g_error ("Method '%s' in assembly '%s' contains native code and mono can't run it. The assembly was probably created by Managed C++.\n", mono_method_full_name (method, TRUE), method->klass->image->name);
10523 if (!piinfo->addr) {
10524 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
10525 piinfo->addr = mono_lookup_internal_call (method);
10526 else
10527 if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
10528 mono_lookup_pinvoke_call (method, NULL, NULL);
10530 nm = mono_marshal_get_native_wrapper (method);
10531 return mono_get_addr_from_ftnptr (mono_compile_method (nm));
10533 //if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
10534 //mono_debug_add_wrapper (method, nm);
10535 } else if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
10536 const char *name = method->name;
10537 MonoMethod *nm;
10539 if (method->klass->parent == mono_defaults.multicastdelegate_class) {
10540 if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
10541 MonoJitICallInfo *mi = mono_find_jit_icall_by_name ("mono_delegate_ctor");
10542 g_assert (mi);
10543 return mono_get_addr_from_ftnptr ((gpointer)mono_icall_get_wrapper (mi));
10544 } else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
10545 nm = mono_marshal_get_delegate_invoke (method);
10546 return mono_get_addr_from_ftnptr (mono_compile_method (nm));
10547 } else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
10548 nm = mono_marshal_get_delegate_begin_invoke (method);
10549 return mono_get_addr_from_ftnptr (mono_compile_method (nm));
10550 } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
10551 nm = mono_marshal_get_delegate_end_invoke (method);
10552 return mono_get_addr_from_ftnptr (mono_compile_method (nm));
10555 return NULL;
10558 cfg = mini_method_compile (method, opt, target_domain, TRUE, FALSE, 0);
10560 switch (cfg->exception_type) {
10561 case MONO_EXCEPTION_NONE: break;
10562 case MONO_EXCEPTION_TYPE_LOAD:
10563 case MONO_EXCEPTION_MISSING_FIELD:
10564 case MONO_EXCEPTION_MISSING_METHOD: {
10565 /* Throw a type load exception if needed */
10566 MonoLoaderError *error = mono_loader_get_last_error ();
10568 if (error) {
10569 MonoException *ex = mono_loader_error_prepare_exception (error);
10570 mono_destroy_compile (cfg);
10571 mono_raise_exception (ex);
10572 } else {
10573 if (cfg->exception_ptr) {
10574 MonoException *ex = mono_class_get_exception_for_failure (cfg->exception_ptr);
10575 mono_destroy_compile (cfg);
10576 mono_raise_exception (ex);
10578 g_assert_not_reached ();
10581 case MONO_EXCEPTION_INVALID_PROGRAM: {
10582 MonoException *ex = mono_exception_from_name_msg (mono_defaults.corlib, "System", "InvalidProgramException", cfg->exception_message);
10583 mono_destroy_compile (cfg);
10584 mono_raise_exception (ex);
10585 break;
10587 case MONO_EXCEPTION_UNVERIFIABLE_IL: {
10588 MonoException *ex = mono_exception_from_name_msg (mono_defaults.corlib, "System.Security", "VerificationException", cfg->exception_message);
10589 mono_destroy_compile (cfg);
10590 mono_raise_exception (ex);
10591 break;
10593 /* this can only be set if the security manager is active */
10594 case MONO_EXCEPTION_SECURITY_LINKDEMAND: {
10595 MonoAssembly *assembly = mono_image_get_assembly (method->klass->image);
10596 MonoReflectionAssembly *refass = (MonoReflectionAssembly*) mono_assembly_get_object (target_domain, assembly);
10597 MonoReflectionMethod *refmet = mono_method_get_object (target_domain, method, NULL);
10598 MonoSecurityManager* secman = mono_security_manager_get_methods ();
10599 MonoObject *exc = NULL;
10600 gpointer args [3];
10602 args [0] = &cfg->exception_data;
10603 args [1] = refass;
10604 args [2] = refmet;
10605 mono_runtime_invoke (secman->linkdemandsecurityexception, NULL, args, &exc);
10607 mono_destroy_compile (cfg);
10608 cfg = NULL;
10610 mono_raise_exception ((MonoException*)exc);
10612 default:
10613 g_assert_not_reached ();
10616 mono_domain_lock (target_domain);
10618 /* Check if some other thread already did the job. In this case, we can
10619 discard the code this thread generated. */
10621 if ((info = g_hash_table_lookup (target_domain->jit_code_hash, method))) {
10622 /* We can't use a domain specific method in another domain */
10623 if ((target_domain == mono_domain_get ()) || info->domain_neutral) {
10624 code = info->code_start;
10625 // printf("Discarding code for method %s\n", method->name);
10629 if (code == NULL) {
10630 g_hash_table_insert (jit_code_hash, method, cfg->jit_info);
10631 code = cfg->native_code;
10634 mono_destroy_compile (cfg);
10636 if (target_domain->jump_target_hash) {
10637 MonoJumpInfo patch_info;
10638 GSList *list, *tmp;
10639 list = g_hash_table_lookup (target_domain->jump_target_hash, method);
10640 if (list) {
10641 patch_info.next = NULL;
10642 patch_info.ip.i = 0;
10643 patch_info.type = MONO_PATCH_INFO_METHOD_JUMP;
10644 patch_info.data.method = method;
10645 g_hash_table_remove (target_domain->jump_target_hash, method);
10647 for (tmp = list; tmp; tmp = tmp->next)
10648 mono_arch_patch_code (NULL, target_domain, tmp->data, &patch_info, TRUE);
10649 g_slist_free (list);
10652 mono_domain_unlock (target_domain);
10654 mono_runtime_class_init (mono_class_vtable (target_domain, method->klass));
10655 return code;
10658 static gpointer
10659 mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt)
10661 MonoDomain *target_domain, *domain = mono_domain_get ();
10662 MonoJitInfo *info;
10663 gpointer p;
10664 MonoJitICallInfo *callinfo = NULL;
10667 * ICALL wrappers are handled specially, since there is only one copy of them
10668 * shared by all appdomains.
10670 if ((method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) && (strstr (method->name, "__icall_wrapper_") == method->name)) {
10671 const char *icall_name;
10673 icall_name = method->name + strlen ("__icall_wrapper_");
10674 g_assert (icall_name);
10675 callinfo = mono_find_jit_icall_by_name (icall_name);
10676 g_assert (callinfo);
10678 /* Must be domain neutral since there is only one copy */
10679 opt |= MONO_OPT_SHARED;
10682 if (opt & MONO_OPT_SHARED)
10683 target_domain = mono_get_root_domain ();
10684 else
10685 target_domain = domain;
10687 mono_domain_lock (target_domain);
10689 if ((info = g_hash_table_lookup (target_domain->jit_code_hash, method))) {
10690 /* We can't use a domain specific method in another domain */
10691 if (! ((domain != target_domain) && !info->domain_neutral)) {
10692 mono_domain_unlock (target_domain);
10693 mono_jit_stats.methods_lookups++;
10694 mono_runtime_class_init (mono_class_vtable (domain, method->klass));
10695 return mono_create_ftnptr (target_domain, info->code_start);
10699 mono_domain_unlock (target_domain);
10700 p = mono_create_ftnptr (target_domain, mono_jit_compile_method_inner (method, target_domain, opt));
10702 if (callinfo) {
10703 mono_jit_lock ();
10704 if (!callinfo->wrapper) {
10705 callinfo->wrapper = p;
10706 mono_register_jit_icall_wrapper (callinfo, p);
10707 mono_debug_add_icall_wrapper (method, callinfo);
10709 mono_jit_unlock ();
10712 return p;
10715 static gpointer
10716 mono_jit_compile_method (MonoMethod *method)
10718 return mono_jit_compile_method_with_opt (method, default_opt);
10721 static void
10722 invalidated_delegate_trampoline (char *desc)
10724 g_error ("Unmanaged code called delegate of type %s which was already garbage collected.\n"
10725 "See http://www.go-mono.com/delegate.html for an explanation and ways to fix this.",
10726 desc);
10730 * mono_jit_free_method:
10732 * Free all memory allocated by the JIT for METHOD.
10734 static void
10735 mono_jit_free_method (MonoDomain *domain, MonoMethod *method)
10737 MonoJitDynamicMethodInfo *ji;
10738 gboolean destroy = TRUE;
10740 g_assert (method->dynamic);
10742 mono_domain_lock (domain);
10743 ji = mono_dynamic_code_hash_lookup (domain, method);
10744 mono_domain_unlock (domain);
10746 if (!ji)
10747 return;
10748 mono_domain_lock (domain);
10749 g_hash_table_remove (domain->dynamic_code_hash, method);
10750 g_hash_table_remove (domain->jit_code_hash, method);
10751 g_hash_table_remove (domain->jump_trampoline_hash, method);
10752 mono_domain_unlock (domain);
10754 #ifdef MONO_ARCH_HAVE_INVALIDATE_METHOD
10755 if (debug_options.keep_delegates && method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
10757 * Instead of freeing the code, change it to call an error routine
10758 * so people can fix their code.
10760 char *type = mono_type_full_name (&method->klass->byval_arg);
10761 char *type_and_method = g_strdup_printf ("%s.%s", type, method->name);
10763 g_free (type);
10764 mono_arch_invalidate_method (ji->ji, invalidated_delegate_trampoline, type_and_method);
10765 destroy = FALSE;
10767 #endif
10770 * This needs to be done before freeing code_mp, since the code address is the
10771 * key in the table, so if we the code_mp first, another thread can grab the
10772 * same code address and replace our entry in the table.
10774 mono_jit_info_table_remove (domain, ji->ji);
10776 if (destroy)
10777 mono_code_manager_destroy (ji->code_mp);
10778 g_free (ji->ji);
10779 g_free (ji);
10782 static gpointer
10783 mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method)
10785 MonoDomain *target_domain;
10786 MonoJitInfo *info;
10788 if (default_opt & MONO_OPT_SHARED)
10789 target_domain = mono_get_root_domain ();
10790 else
10791 target_domain = domain;
10793 mono_domain_lock (target_domain);
10795 if ((info = g_hash_table_lookup (target_domain->jit_code_hash, method))) {
10796 /* We can't use a domain specific method in another domain */
10797 if (! ((domain != target_domain) && !info->domain_neutral)) {
10798 mono_domain_unlock (target_domain);
10799 mono_jit_stats.methods_lookups++;
10800 return info->code_start;
10804 mono_domain_unlock (target_domain);
10806 return NULL;
10810 * mono_jit_runtime_invoke:
10811 * @method: the method to invoke
10812 * @obj: this pointer
10813 * @params: array of parameter values.
10814 * @exc: used to catch exceptions objects
10816 static MonoObject*
10817 mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
10819 MonoMethod *invoke;
10820 MonoObject *(*runtime_invoke) (MonoObject *this, void **params, MonoObject **exc, void* compiled_method);
10821 void* compiled_method;
10823 if (obj == NULL && !(method->flags & METHOD_ATTRIBUTE_STATIC) && !method->string_ctor && (method->wrapper_type == 0)) {
10824 g_warning ("Ignoring invocation of an instance method on a NULL instance.\n");
10825 return NULL;
10828 method = mono_get_inflated_method (method);
10829 invoke = mono_marshal_get_runtime_invoke (method);
10830 runtime_invoke = mono_jit_compile_method (invoke);
10832 /* We need this here becuase mono_marshal_get_runtime_invoke can be place
10833 * the helper method in System.Object and not the target class
10835 mono_runtime_class_init (mono_class_vtable (mono_domain_get (), method->klass));
10837 compiled_method = mono_jit_compile_method (method);
10838 return runtime_invoke (obj, params, exc, compiled_method);
10841 #ifdef PLATFORM_WIN32
10842 #define GET_CONTEXT \
10843 struct sigcontext *ctx = (struct sigcontext*)_dummy;
10844 #else
10845 #ifdef MONO_ARCH_USE_SIGACTION
10846 #define GET_CONTEXT \
10847 void *ctx = context;
10848 #elif defined(__sparc__)
10849 #define GET_CONTEXT \
10850 void *ctx = sigctx;
10851 #else
10852 #define GET_CONTEXT \
10853 void **_p = (void **)&_dummy; \
10854 struct sigcontext *ctx = (struct sigcontext *)++_p;
10855 #endif
10856 #endif
10858 #ifdef MONO_ARCH_USE_SIGACTION
10859 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, siginfo_t *info, void *context)
10860 #elif defined(__sparc__)
10861 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, void *sigctx)
10862 #else
10863 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy)
10864 #endif
10866 static void
10867 SIG_HANDLER_SIGNATURE (sigfpe_signal_handler)
10869 MonoException *exc = NULL;
10870 #ifndef MONO_ARCH_USE_SIGACTION
10871 void *info = NULL;
10872 #endif
10873 GET_CONTEXT;
10875 #if defined(MONO_ARCH_HAVE_IS_INT_OVERFLOW)
10876 if (mono_arch_is_int_overflow (ctx, info))
10877 exc = mono_get_exception_arithmetic ();
10878 else
10879 exc = mono_get_exception_divide_by_zero ();
10880 #else
10881 exc = mono_get_exception_divide_by_zero ();
10882 #endif
10884 mono_arch_handle_exception (ctx, exc, FALSE);
10887 static void
10888 SIG_HANDLER_SIGNATURE (sigill_signal_handler)
10890 MonoException *exc;
10891 GET_CONTEXT;
10893 exc = mono_get_exception_execution_engine ("SIGILL");
10895 mono_arch_handle_exception (ctx, exc, FALSE);
10898 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
10900 #ifndef MONO_ARCH_USE_SIGACTION
10901 #error "Can't use sigaltstack without sigaction"
10902 #endif
10904 #endif
10906 static void
10907 SIG_HANDLER_SIGNATURE (sigsegv_signal_handler)
10909 MonoException *exc = NULL;
10910 MonoJitInfo *ji;
10912 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
10913 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
10914 #endif
10915 GET_CONTEXT;
10917 #ifdef MONO_ARCH_USE_SIGACTION
10918 if (debug_options.collect_pagefault_stats) {
10919 if (mono_raw_buffer_is_pagefault (info->si_addr)) {
10920 mono_raw_buffer_handle_pagefault (info->si_addr);
10921 return;
10923 if (mono_aot_is_pagefault (info->si_addr)) {
10924 mono_aot_handle_pagefault (info->si_addr);
10925 return;
10928 #endif
10930 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
10931 /* Can't allocate memory using Boehm GC on altstack */
10932 if (jit_tls->stack_size &&
10933 ((guint8*)info->si_addr >= (guint8*)jit_tls->end_of_stack - jit_tls->stack_size) &&
10934 ((guint8*)info->si_addr < (guint8*)jit_tls->end_of_stack))
10935 exc = mono_domain_get ()->stack_overflow_ex;
10936 else
10937 exc = mono_domain_get ()->null_reference_ex;
10938 #endif
10940 ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context(ctx));
10941 if (!ji) {
10942 mono_handle_native_sigsegv (SIGSEGV, ctx);
10945 mono_arch_handle_exception (ctx, exc, FALSE);
10948 static void
10949 SIG_HANDLER_SIGNATURE (sigabrt_signal_handler)
10951 MonoJitInfo *ji;
10952 GET_CONTEXT;
10954 ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context(ctx));
10955 if (!ji) {
10956 mono_handle_native_sigsegv (SIGABRT, ctx);
10960 static void
10961 SIG_HANDLER_SIGNATURE (sigusr1_signal_handler)
10963 gboolean running_managed;
10964 MonoException *exc;
10965 MonoThread *thread = mono_thread_current ();
10966 void *ji;
10968 GET_CONTEXT;
10970 if (thread->thread_dump_requested) {
10971 thread->thread_dump_requested = FALSE;
10973 mono_print_thread_dump (ctx);
10977 * FIXME:
10978 * This is an async signal, so the code below must not call anything which
10979 * is not async safe. That includes the pthread locking functions. If we
10980 * know that we interrupted managed code, then locking is safe.
10982 ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context(ctx));
10983 running_managed = ji != NULL;
10985 exc = mono_thread_request_interruption (running_managed);
10986 if (!exc) return;
10988 mono_arch_handle_exception (ctx, exc, FALSE);
10991 static void
10992 SIG_HANDLER_SIGNATURE (sigprof_signal_handler)
10994 GET_CONTEXT;
10996 mono_profiler_stat_hit (mono_arch_ip_from_context (ctx), ctx);
10999 static void
11000 SIG_HANDLER_SIGNATURE (sigquit_signal_handler)
11002 GET_CONTEXT;
11004 printf ("Full thread dump:\n");
11006 mono_threads_request_thread_dump ();
11009 * print_thread_dump () skips the current thread, since sending a signal
11010 * to it would invoke the signal handler below the sigquit signal handler,
11011 * and signal handlers don't create an lmf, so the stack walk could not
11012 * be performed.
11014 mono_print_thread_dump (ctx);
11017 static void
11018 SIG_HANDLER_SIGNATURE (sigint_signal_handler)
11020 MonoException *exc;
11021 GET_CONTEXT;
11023 exc = mono_get_exception_execution_engine ("Interrupted (SIGINT).");
11025 mono_arch_handle_exception (ctx, exc, FALSE);
11028 static void
11029 SIG_HANDLER_SIGNATURE (sigusr2_signal_handler)
11031 gboolean enabled = mono_trace_is_enabled ();
11033 mono_trace_enable (!enabled);
11036 #ifdef PLATFORM_MACOSX
11039 * This code disables the CrashReporter of MacOS X by installing
11040 * a dummy Mach exception handler.
11044 * http://darwinsource.opendarwin.org/10.4.3/xnu-792.6.22/osfmk/man/exc_server.html
11046 extern
11047 boolean_t
11048 exc_server (mach_msg_header_t *request_msg,
11049 mach_msg_header_t *reply_msg);
11052 * The exception message
11054 typedef struct {
11055 mach_msg_base_t msg; /* common mach message header */
11056 char payload [1024]; /* opaque */
11057 } mach_exception_msg_t;
11059 /* The exception port */
11060 static mach_port_t mach_exception_port = VM_MAP_NULL;
11063 * Implicitly called by exc_server. Must be public.
11065 * http://darwinsource.opendarwin.org/10.4.3/xnu-792.6.22/osfmk/man/catch_exception_raise.html
11067 kern_return_t
11068 catch_exception_raise (
11069 mach_port_t exception_port,
11070 mach_port_t thread,
11071 mach_port_t task,
11072 exception_type_t exception,
11073 exception_data_t code,
11074 mach_msg_type_number_t code_count)
11076 /* consume the exception */
11077 return KERN_FAILURE;
11081 * Exception thread handler.
11083 static
11084 void *
11085 mach_exception_thread (void *arg)
11087 for (;;) {
11088 mach_exception_msg_t request;
11089 mach_exception_msg_t reply;
11090 mach_msg_return_t result;
11092 /* receive from "mach_exception_port" */
11093 result = mach_msg (&request.msg.header,
11094 MACH_RCV_MSG | MACH_RCV_LARGE,
11096 sizeof (request),
11097 mach_exception_port,
11098 MACH_MSG_TIMEOUT_NONE,
11099 MACH_PORT_NULL);
11101 g_assert (result == MACH_MSG_SUCCESS);
11103 /* dispatch to catch_exception_raise () */
11104 exc_server (&request.msg.header, &reply.msg.header);
11106 /* send back to sender */
11107 result = mach_msg (&reply.msg.header,
11108 MACH_SEND_MSG,
11109 reply.msg.header.msgh_size,
11111 MACH_PORT_NULL,
11112 MACH_MSG_TIMEOUT_NONE,
11113 MACH_PORT_NULL);
11115 g_assert (result == MACH_MSG_SUCCESS);
11117 return NULL;
11120 static void
11121 macosx_register_exception_handler ()
11123 mach_port_t task;
11124 pthread_attr_t attr;
11125 pthread_t thread;
11127 if (mach_exception_port != VM_MAP_NULL)
11128 return;
11130 task = mach_task_self ();
11132 /* create the "mach_exception_port" with send & receive rights */
11133 g_assert (mach_port_allocate (task, MACH_PORT_RIGHT_RECEIVE,
11134 &mach_exception_port) == KERN_SUCCESS);
11135 g_assert (mach_port_insert_right (task, mach_exception_port, mach_exception_port,
11136 MACH_MSG_TYPE_MAKE_SEND) == KERN_SUCCESS);
11138 /* create the exception handler thread */
11139 g_assert (!pthread_attr_init (&attr));
11140 g_assert (!pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED));
11141 g_assert (!pthread_create (&thread, &attr, mach_exception_thread, NULL));
11142 pthread_attr_destroy (&attr);
11145 * register "mach_exception_port" as a receiver for the
11146 * EXC_BAD_ACCESS exception
11148 * http://darwinsource.opendarwin.org/10.4.3/xnu-792.6.22/osfmk/man/task_set_exception_ports.html
11150 g_assert (task_set_exception_ports (task, EXC_MASK_BAD_ACCESS,
11151 mach_exception_port,
11152 EXCEPTION_DEFAULT,
11153 MACHINE_THREAD_STATE) == KERN_SUCCESS);
11155 #endif
11157 #ifndef PLATFORM_WIN32
11158 static void
11159 add_signal_handler (int signo, gpointer handler)
11161 struct sigaction sa;
11163 #ifdef MONO_ARCH_USE_SIGACTION
11164 sa.sa_sigaction = handler;
11165 sigemptyset (&sa.sa_mask);
11166 sa.sa_flags = SA_SIGINFO;
11167 #else
11168 sa.sa_handler = handler;
11169 sigemptyset (&sa.sa_mask);
11170 sa.sa_flags = 0;
11171 #endif
11172 g_assert (sigaction (signo, &sa, NULL) != -1);
11175 static void
11176 remove_signal_handler (int signo)
11178 struct sigaction sa;
11180 sa.sa_handler = SIG_DFL;
11181 sigemptyset (&sa.sa_mask);
11182 sa.sa_flags = 0;
11184 g_assert (sigaction (signo, &sa, NULL) != -1);
11186 #endif
11188 static void
11189 mono_runtime_install_handlers (void)
11191 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
11192 struct sigaction sa;
11193 #endif
11195 #ifdef PLATFORM_WIN32
11196 win32_seh_init();
11197 win32_seh_set_handler(SIGFPE, sigfpe_signal_handler);
11198 win32_seh_set_handler(SIGILL, sigill_signal_handler);
11199 win32_seh_set_handler(SIGSEGV, sigsegv_signal_handler);
11200 if (debug_options.handle_sigint)
11201 win32_seh_set_handler(SIGINT, sigint_signal_handler);
11203 #else /* !PLATFORM_WIN32 */
11206 #ifdef PLATFORM_MACOSX
11207 macosx_register_exception_handler ();
11208 #endif
11210 if (debug_options.handle_sigint)
11211 add_signal_handler (SIGINT, sigint_signal_handler);
11213 add_signal_handler (SIGFPE, sigfpe_signal_handler);
11214 add_signal_handler (SIGQUIT, sigquit_signal_handler);
11215 add_signal_handler (SIGILL, sigill_signal_handler);
11216 add_signal_handler (SIGBUS, sigsegv_signal_handler);
11217 if (mono_jit_trace_calls != NULL)
11218 add_signal_handler (SIGUSR2, sigusr2_signal_handler);
11220 add_signal_handler (mono_thread_get_abort_signal (), sigusr1_signal_handler);
11221 signal (SIGPIPE, SIG_IGN);
11223 add_signal_handler (SIGABRT, sigabrt_signal_handler);
11225 /* catch SIGSEGV */
11226 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
11227 sa.sa_sigaction = sigsegv_signal_handler;
11228 sigemptyset (&sa.sa_mask);
11229 sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
11230 g_assert (sigaction (SIGSEGV, &sa, NULL) != -1);
11231 #else
11232 add_signal_handler (SIGSEGV, sigsegv_signal_handler);
11233 #endif
11234 #endif /* PLATFORM_WIN32 */
11237 static void
11238 mono_runtime_cleanup_handlers (void)
11240 #ifdef PLATFORM_WIN32
11241 win32_seh_cleanup();
11242 #else
11243 if (debug_options.handle_sigint)
11244 remove_signal_handler (SIGINT);
11246 remove_signal_handler (SIGFPE);
11247 remove_signal_handler (SIGQUIT);
11248 remove_signal_handler (SIGILL);
11249 remove_signal_handler (SIGBUS);
11250 if (mono_jit_trace_calls != NULL)
11251 remove_signal_handler (SIGUSR2);
11253 remove_signal_handler (mono_thread_get_abort_signal ());
11255 remove_signal_handler (SIGABRT);
11257 remove_signal_handler (SIGSEGV);
11258 #endif /* PLATFORM_WIN32 */
11262 #ifdef HAVE_LINUX_RTC_H
11263 #include <linux/rtc.h>
11264 #include <sys/ioctl.h>
11265 #include <fcntl.h>
11266 static int rtc_fd = -1;
11268 static int
11269 enable_rtc_timer (gboolean enable)
11271 int flags;
11272 flags = fcntl (rtc_fd, F_GETFL);
11273 if (flags < 0) {
11274 perror ("getflags");
11275 return 0;
11277 if (enable)
11278 flags |= FASYNC;
11279 else
11280 flags &= ~FASYNC;
11281 if (fcntl (rtc_fd, F_SETFL, flags) == -1) {
11282 perror ("setflags");
11283 return 0;
11285 return 1;
11287 #endif
11289 static void
11290 setup_stat_profiler (void)
11292 #ifdef ITIMER_PROF
11293 struct itimerval itval;
11294 static int inited = 0;
11295 #ifdef HAVE_LINUX_RTC_H
11296 const char *rtc_freq;
11297 if (!inited && (rtc_freq = g_getenv ("MONO_RTC"))) {
11298 int freq = 0;
11299 inited = 1;
11300 if (*rtc_freq)
11301 freq = atoi (rtc_freq);
11302 if (!freq)
11303 freq = 1024;
11304 rtc_fd = open ("/dev/rtc", O_RDONLY);
11305 if (rtc_fd == -1) {
11306 perror ("open /dev/rtc");
11307 return;
11309 add_signal_handler (SIGPROF, sigprof_signal_handler);
11310 if (ioctl (rtc_fd, RTC_IRQP_SET, freq) == -1) {
11311 perror ("set rtc freq");
11312 return;
11314 if (ioctl (rtc_fd, RTC_PIE_ON, 0) == -1) {
11315 perror ("start rtc");
11316 return;
11318 if (fcntl (rtc_fd, F_SETSIG, SIGPROF) == -1) {
11319 perror ("setsig");
11320 return;
11322 if (fcntl (rtc_fd, F_SETOWN, getpid ()) == -1) {
11323 perror ("setown");
11324 return;
11326 enable_rtc_timer (TRUE);
11327 return;
11329 if (rtc_fd >= 0)
11330 return;
11331 #endif
11333 itval.it_interval.tv_usec = 999;
11334 itval.it_interval.tv_sec = 0;
11335 itval.it_value = itval.it_interval;
11336 setitimer (ITIMER_PROF, &itval, NULL);
11337 if (inited)
11338 return;
11339 inited = 1;
11340 add_signal_handler (SIGPROF, sigprof_signal_handler);
11341 #endif
11344 /* mono_jit_create_remoting_trampoline:
11345 * @method: pointer to the method info
11347 * Creates a trampoline which calls the remoting functions. This
11348 * is used in the vtable of transparent proxies.
11350 * Returns: a pointer to the newly created code
11352 static gpointer
11353 mono_jit_create_remoting_trampoline (MonoMethod *method, MonoRemotingTarget target)
11355 MonoMethod *nm;
11356 guint8 *addr = NULL;
11358 if ((method->flags & METHOD_ATTRIBUTE_ABSTRACT) ||
11359 (mono_method_signature (method)->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class))) {
11360 nm = mono_marshal_get_remoting_invoke_for_target (method, target);
11361 addr = mono_compile_method (nm);
11362 } else {
11363 addr = mono_compile_method (method);
11365 return mono_get_addr_from_ftnptr (addr);
11368 static void
11369 mini_parse_debug_options (void)
11371 char *options = getenv ("MONO_DEBUG");
11372 gchar **args, **ptr;
11374 if (!options)
11375 return;
11377 args = g_strsplit (options, ",", -1);
11379 for (ptr = args; ptr && *ptr; ptr++) {
11380 const char *arg = *ptr;
11382 if (!strcmp (arg, "handle-sigint"))
11383 debug_options.handle_sigint = TRUE;
11384 else if (!strcmp (arg, "keep-delegates"))
11385 debug_options.keep_delegates = TRUE;
11386 else if (!strcmp (arg, "collect-pagefault-stats"))
11387 debug_options.collect_pagefault_stats = TRUE;
11388 else if (!strcmp (arg, "break-on-unverified"))
11389 debug_options.break_on_unverified = TRUE;
11390 else {
11391 fprintf (stderr, "Invalid option for the MONO_DEBUG env variable: %s\n", arg);
11392 fprintf (stderr, "Available options: 'handle-sigint', 'keep-delegates', 'collect-pagefault-stats', 'break-on-unverified'\n");
11393 exit (1);
11398 MonoDomain *
11399 mini_init (const char *filename)
11401 MonoDomain *domain;
11403 #ifdef __linux__
11404 if (access ("/proc/self/maps", F_OK) != 0) {
11405 g_print ("Mono requires /proc to be mounted.\n");
11406 exit (1);
11408 #endif
11410 /* Happens when using the embedding interface */
11411 if (!default_opt_set)
11412 default_opt = mono_parse_default_optimizations (NULL);
11414 InitializeCriticalSection (&jit_mutex);
11416 global_codeman = mono_code_manager_new ();
11417 jit_icall_name_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
11419 mono_arch_cpu_init ();
11421 mono_init_trampolines ();
11423 mono_init_exceptions ();
11425 if (!g_thread_supported ())
11426 g_thread_init (NULL);
11428 if (getenv ("MONO_DEBUG") != NULL)
11429 mini_parse_debug_options ();
11432 * Handle the case when we are called from a thread different from the main thread,
11433 * confusing libgc.
11434 * FIXME: Move this to libgc where it belongs.
11436 * we used to do this only when running on valgrind,
11437 * but it happens also in other setups.
11439 #if defined(HAVE_BOEHM_GC)
11440 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
11442 size_t size;
11443 void *sstart;
11444 pthread_attr_t attr;
11445 pthread_getattr_np (pthread_self (), &attr);
11446 pthread_attr_getstack (&attr, &sstart, &size);
11447 pthread_attr_destroy (&attr);
11448 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
11449 #ifdef __ia64__
11451 * The calculation above doesn't seem to work on ia64, also we need to set
11452 * GC_register_stackbottom as well, but don't know how.
11454 #else
11455 /* apparently with some linuxthreads implementations sstart can be NULL,
11456 * fallback to the more imprecise method (bug# 78096).
11458 if (sstart) {
11459 GC_stackbottom = (char*)sstart + size;
11460 } else {
11461 gsize stack_bottom = (gsize)&domain;
11462 stack_bottom += 4095;
11463 stack_bottom &= ~4095;
11464 GC_stackbottom = (char*)stack_bottom;
11466 #endif
11468 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
11469 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
11470 #else
11472 gsize stack_bottom = (gsize)&domain;
11473 stack_bottom += 4095;
11474 stack_bottom &= ~4095;
11475 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
11476 GC_stackbottom = (char*)stack_bottom;
11478 #endif
11479 #endif
11480 MONO_GC_PRE_INIT ();
11482 mono_jit_tls_id = TlsAlloc ();
11483 setup_jit_tls_data ((gpointer)-1, mono_thread_abort);
11485 mono_burg_init ();
11487 if (default_opt & MONO_OPT_AOT)
11488 mono_aot_init ();
11490 mono_runtime_install_handlers ();
11491 mono_threads_install_cleanup (mini_thread_cleanup);
11493 #define JIT_TRAMPOLINES_WORK
11494 #ifdef JIT_TRAMPOLINES_WORK
11495 mono_install_compile_method (mono_jit_compile_method);
11496 mono_install_free_method (mono_jit_free_method);
11497 mono_install_trampoline (mono_create_jit_trampoline);
11498 mono_install_remoting_trampoline (mono_jit_create_remoting_trampoline);
11499 mono_install_delegate_trampoline (mono_create_delegate_trampoline);
11500 #endif
11501 #define JIT_INVOKE_WORKS
11502 #ifdef JIT_INVOKE_WORKS
11503 mono_install_runtime_invoke (mono_jit_runtime_invoke);
11504 mono_install_handler (mono_arch_get_throw_exception ());
11505 #endif
11506 mono_install_stack_walk (mono_jit_walk_stack);
11507 mono_install_init_vtable (mono_aot_init_vtable);
11508 mono_install_get_cached_class_info (mono_aot_get_cached_class_info);
11509 mono_install_get_class_from_name (mono_aot_get_class_from_name);
11510 mono_install_jit_info_find_in_aot (mono_aot_find_jit_info);
11512 if (debug_options.collect_pagefault_stats) {
11513 mono_raw_buffer_set_make_unreadable (TRUE);
11514 mono_aot_set_make_unreadable (TRUE);
11517 domain = mono_init_from_assembly (filename, filename);
11518 mono_icall_init ();
11520 mono_add_internal_call ("System.Diagnostics.StackFrame::get_frame_info",
11521 ves_icall_get_frame_info);
11522 mono_add_internal_call ("System.Diagnostics.StackTrace::get_trace",
11523 ves_icall_get_trace);
11524 mono_add_internal_call ("System.Exception::get_trace",
11525 ves_icall_System_Exception_get_trace);
11526 mono_add_internal_call ("System.Security.SecurityFrame::_GetSecurityFrame",
11527 ves_icall_System_Security_SecurityFrame_GetSecurityFrame);
11528 mono_add_internal_call ("System.Security.SecurityFrame::_GetSecurityStack",
11529 ves_icall_System_Security_SecurityFrame_GetSecurityStack);
11530 mono_add_internal_call ("Mono.Runtime::mono_runtime_install_handlers",
11531 mono_runtime_install_handlers);
11534 create_helper_signature ();
11536 #define JIT_CALLS_WORK
11537 #ifdef JIT_CALLS_WORK
11538 /* Needs to be called here since register_jit_icall depends on it */
11539 mono_marshal_init ();
11541 mono_arch_register_lowlevel_calls ();
11542 register_icall (mono_profiler_method_enter, "mono_profiler_method_enter", NULL, TRUE);
11543 register_icall (mono_profiler_method_leave, "mono_profiler_method_leave", NULL, TRUE);
11544 register_icall (mono_trace_enter_method, "mono_trace_enter_method", NULL, TRUE);
11545 register_icall (mono_trace_leave_method, "mono_trace_leave_method", NULL, TRUE);
11546 register_icall (mono_get_lmf_addr, "mono_get_lmf_addr", "ptr", TRUE);
11547 register_icall (mono_jit_thread_attach, "mono_jit_thread_attach", "void", TRUE);
11548 register_icall (mono_domain_get, "mono_domain_get", "ptr", TRUE);
11550 register_icall (mono_arch_get_throw_exception (), "mono_arch_throw_exception", "void object", TRUE);
11551 register_icall (mono_arch_get_rethrow_exception (), "mono_arch_rethrow_exception", "void object", TRUE);
11552 register_icall (mono_arch_get_throw_exception_by_name (), "mono_arch_throw_exception_by_name", "void ptr", TRUE);
11553 #if MONO_ARCH_HAVE_THROW_CORLIB_EXCEPTION
11554 register_icall (mono_arch_get_throw_corlib_exception (), "mono_arch_throw_corlib_exception",
11555 "void ptr", TRUE);
11556 #endif
11557 register_icall (mono_thread_get_pending_exception, "mono_thread_get_pending_exception", "object", FALSE);
11558 register_icall (mono_thread_interruption_checkpoint, "mono_thread_interruption_checkpoint", "void", FALSE);
11559 register_icall (mono_thread_force_interruption_checkpoint, "mono_thread_force_interruption_checkpoint", "void", FALSE);
11560 register_icall (mono_load_remote_field_new, "mono_load_remote_field_new", "object object ptr ptr", FALSE);
11561 register_icall (mono_store_remote_field_new, "mono_store_remote_field_new", "void object ptr ptr object", FALSE);
11564 * NOTE, NOTE, NOTE, NOTE:
11565 * when adding emulation for some opcodes, remember to also add a dummy
11566 * rule to the burg files, because we need the arity information to be correct.
11568 #ifndef MONO_ARCH_NO_EMULATE_LONG_MUL_OPTS
11569 mono_register_opcode_emulation (OP_LMUL, "__emul_lmul", "long long long", mono_llmult, TRUE);
11570 mono_register_opcode_emulation (OP_LDIV, "__emul_ldiv", "long long long", mono_lldiv, FALSE);
11571 mono_register_opcode_emulation (OP_LDIV_UN, "__emul_ldiv_un", "long long long", mono_lldiv_un, FALSE);
11572 mono_register_opcode_emulation (OP_LREM, "__emul_lrem", "long long long", mono_llrem, FALSE);
11573 mono_register_opcode_emulation (OP_LREM_UN, "__emul_lrem_un", "long long long", mono_llrem_un, FALSE);
11574 mono_register_opcode_emulation (OP_LMUL_OVF_UN, "__emul_lmul_ovf_un", "long long long", mono_llmult_ovf_un, FALSE);
11575 mono_register_opcode_emulation (OP_LMUL_OVF, "__emul_lmul_ovf", "long long long", mono_llmult_ovf, FALSE);
11576 #endif
11578 #ifndef MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS
11579 mono_register_opcode_emulation (OP_LSHL, "__emul_lshl", "long long int32", mono_lshl, TRUE);
11580 mono_register_opcode_emulation (OP_LSHR, "__emul_lshr", "long long int32", mono_lshr, TRUE);
11581 mono_register_opcode_emulation (OP_LSHR_UN, "__emul_lshr_un", "long long int32", mono_lshr_un, TRUE);
11582 #endif
11584 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
11585 mono_register_opcode_emulation (CEE_DIV, "__emul_idiv", "int32 int32 int32", mono_idiv, FALSE);
11586 mono_register_opcode_emulation (CEE_DIV_UN, "__emul_idiv_un", "int32 int32 int32", mono_idiv_un, FALSE);
11587 mono_register_opcode_emulation (CEE_REM, "__emul_irem", "int32 int32 int32", mono_irem, FALSE);
11588 mono_register_opcode_emulation (CEE_REM_UN, "__emul_irem_un", "int32 int32 int32", mono_irem_un, FALSE);
11589 #endif
11591 #ifdef MONO_ARCH_EMULATE_MUL_DIV
11592 mono_register_opcode_emulation (CEE_MUL_OVF, "__emul_imul_ovf", "int32 int32 int32", mono_imul_ovf, FALSE);
11593 mono_register_opcode_emulation (CEE_MUL_OVF_UN, "__emul_imul_ovf_un", "int32 int32 int32", mono_imul_ovf_un, FALSE);
11594 mono_register_opcode_emulation (CEE_MUL, "__emul_imul", "int32 int32 int32", mono_imul, TRUE);
11595 #endif
11596 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_SOFT_FLOAT)
11597 mono_register_opcode_emulation (OP_FDIV, "__emul_fdiv", "double double double", mono_fdiv, FALSE);
11598 #endif
11600 mono_register_opcode_emulation (OP_FCONV_TO_U8, "__emul_fconv_to_u8", "ulong double", mono_fconv_u8, FALSE);
11601 mono_register_opcode_emulation (OP_FCONV_TO_U4, "__emul_fconv_to_u4", "uint32 double", mono_fconv_u4, FALSE);
11602 mono_register_opcode_emulation (OP_FCONV_TO_OVF_I8, "__emul_fconv_to_ovf_i8", "long double", mono_fconv_ovf_i8, FALSE);
11603 mono_register_opcode_emulation (OP_FCONV_TO_OVF_U8, "__emul_fconv_to_ovf_u8", "ulong double", mono_fconv_ovf_u8, FALSE);
11605 #ifdef MONO_ARCH_EMULATE_FCONV_TO_I8
11606 mono_register_opcode_emulation (OP_FCONV_TO_I8, "__emul_fconv_to_i8", "long double", mono_fconv_i8, FALSE);
11607 #endif
11608 #ifdef MONO_ARCH_EMULATE_CONV_R8_UN
11609 mono_register_opcode_emulation (CEE_CONV_R_UN, "__emul_conv_r_un", "double int32", mono_conv_to_r8_un, FALSE);
11610 #endif
11611 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8
11612 mono_register_opcode_emulation (OP_LCONV_TO_R8, "__emul_lconv_to_r8", "double long", mono_lconv_to_r8, FALSE);
11613 #endif
11614 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R4
11615 mono_register_opcode_emulation (OP_LCONV_TO_R4, "__emul_lconv_to_r4", "float long", mono_lconv_to_r4, FALSE);
11616 #endif
11617 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8_UN
11618 mono_register_opcode_emulation (OP_LCONV_TO_R_UN, "__emul_lconv_to_r8_un", "double long", mono_lconv_to_r8_un, FALSE);
11619 #endif
11620 #ifdef MONO_ARCH_EMULATE_FREM
11621 mono_register_opcode_emulation (OP_FREM, "__emul_frem", "double double double", fmod, FALSE);
11622 #endif
11624 #ifdef MONO_ARCH_SOFT_FLOAT
11625 mono_register_opcode_emulation (OP_FSUB, "__emul_fsub", "double double double", mono_fsub, FALSE);
11626 mono_register_opcode_emulation (OP_FADD, "__emul_fadd", "double double double", mono_fadd, FALSE);
11627 mono_register_opcode_emulation (OP_FMUL, "__emul_fmul", "double double double", mono_fmul, FALSE);
11628 mono_register_opcode_emulation (OP_FNEG, "__emul_fneg", "double double", mono_fneg, FALSE);
11629 mono_register_opcode_emulation (CEE_CONV_R8, "__emul_conv_r8", "double int32", mono_conv_to_r8, FALSE);
11630 mono_register_opcode_emulation (OP_FCONV_TO_R4, "__emul_fconv_to_r4", "double double", mono_fconv_r4, FALSE);
11631 mono_register_opcode_emulation (OP_FCONV_TO_I1, "__emul_fconv_to_i1", "int8 double", mono_fconv_i1, FALSE);
11632 mono_register_opcode_emulation (OP_FCONV_TO_I2, "__emul_fconv_to_i2", "int16 double", mono_fconv_i2, FALSE);
11633 mono_register_opcode_emulation (OP_FCONV_TO_I4, "__emul_fconv_to_i4", "int32 double", mono_fconv_i4, FALSE);
11634 mono_register_opcode_emulation (OP_FCONV_TO_U1, "__emul_fconv_to_u1", "uint8 double", mono_fconv_u1, FALSE);
11635 mono_register_opcode_emulation (OP_FCONV_TO_U2, "__emul_fconv_to_u2", "uint16 double", mono_fconv_u2, FALSE);
11637 mono_register_opcode_emulation (OP_FBEQ, "__emul_fcmp_eq", "uint32 double double", mono_fcmp_eq, FALSE);
11638 mono_register_opcode_emulation (OP_FBLT, "__emul_fcmp_lt", "uint32 double double", mono_fcmp_lt, FALSE);
11639 mono_register_opcode_emulation (OP_FBGT, "__emul_fcmp_gt", "uint32 double double", mono_fcmp_gt, FALSE);
11640 mono_register_opcode_emulation (OP_FBLE, "__emul_fcmp_le", "uint32 double double", mono_fcmp_le, FALSE);
11641 mono_register_opcode_emulation (OP_FBGE, "__emul_fcmp_ge", "uint32 double double", mono_fcmp_ge, FALSE);
11642 mono_register_opcode_emulation (OP_FBNE_UN, "__emul_fcmp_ne_un", "uint32 double double", mono_fcmp_ne_un, FALSE);
11643 mono_register_opcode_emulation (OP_FBLT_UN, "__emul_fcmp_lt_un", "uint32 double double", mono_fcmp_lt_un, FALSE);
11644 mono_register_opcode_emulation (OP_FBGT_UN, "__emul_fcmp_gt_un", "uint32 double double", mono_fcmp_gt_un, FALSE);
11645 mono_register_opcode_emulation (OP_FBLE_UN, "__emul_fcmp_le_un", "uint32 double double", mono_fcmp_le_un, FALSE);
11646 mono_register_opcode_emulation (OP_FBGE_UN, "__emul_fcmp_ge_un", "uint32 double double", mono_fcmp_ge_un, FALSE);
11648 mono_register_opcode_emulation (OP_FCEQ, "__emul_fcmp_ceq", "uint32 double double", mono_fceq, FALSE);
11649 mono_register_opcode_emulation (OP_FCGT, "__emul_fcmp_cgt", "uint32 double double", mono_fcgt, FALSE);
11650 mono_register_opcode_emulation (OP_FCGT_UN, "__emul_fcmp_cgt_un", "uint32 double double", mono_fcgt_un, FALSE);
11651 mono_register_opcode_emulation (OP_FCLT, "__emul_fcmp_clt", "uint32 double double", mono_fclt, FALSE);
11652 mono_register_opcode_emulation (OP_FCLT_UN, "__emul_fcmp_clt_un", "uint32 double double", mono_fclt_un, FALSE);
11654 register_icall (mono_fload_r4, "mono_fload_r4", "double ptr", FALSE);
11655 register_icall (mono_fstore_r4, "mono_fstore_r4", "void double ptr", FALSE);
11656 #endif
11658 #if SIZEOF_VOID_P == 4
11659 mono_register_opcode_emulation (OP_FCONV_TO_U, "__emul_fconv_to_u", "uint32 double", mono_fconv_u4, TRUE);
11660 #endif
11662 /* other jit icalls */
11663 register_icall (mono_delegate_ctor, "mono_delegate_ctor", "void object object ptr", FALSE);
11664 register_icall (mono_class_static_field_address , "mono_class_static_field_address",
11665 "ptr ptr ptr", FALSE);
11666 register_icall (mono_ldtoken_wrapper, "mono_ldtoken_wrapper", "ptr ptr ptr ptr", FALSE);
11667 register_icall (mono_get_special_static_data, "mono_get_special_static_data", "ptr int", FALSE);
11668 register_icall (mono_ldstr, "mono_ldstr", "object ptr ptr int32", FALSE);
11669 register_icall (mono_helper_stelem_ref_check, "helper_stelem_ref_check", "void object object", FALSE);
11670 register_icall (mono_object_new, "mono_object_new", "object ptr ptr", FALSE);
11671 register_icall (mono_object_new_specific, "mono_object_new_specific", "object ptr", FALSE);
11672 register_icall (mono_array_new, "mono_array_new", "object ptr ptr int32", FALSE);
11673 register_icall (mono_array_new_specific, "mono_array_new_specific", "object ptr int32", FALSE);
11674 register_icall (mono_runtime_class_init, "mono_runtime_class_init", "void ptr", FALSE);
11675 register_icall (mono_ldftn, "mono_ldftn", "ptr ptr", FALSE);
11676 register_icall (mono_ldftn_nosync, "mono_ldftn_nosync", "ptr ptr", FALSE);
11677 register_icall (mono_ldvirtfn, "mono_ldvirtfn", "ptr object ptr", FALSE);
11678 register_icall (mono_helper_compile_generic_method, "compile_generic_method", "ptr object ptr ptr ptr", FALSE);
11679 register_icall (mono_helper_ldstr, "helper_ldstr", "object ptr int", FALSE);
11680 register_icall (mono_helper_ldstr_mscorlib, "helper_ldstr_mscorlib", "object int", FALSE);
11681 register_icall (mono_helper_newobj_mscorlib, "helper_newobj_mscorlib", "object int", FALSE);
11682 register_icall (mono_value_copy, "mono_value_copy", "void ptr ptr ptr", FALSE);
11683 #endif
11685 #define JIT_RUNTIME_WORKS
11686 #ifdef JIT_RUNTIME_WORKS
11687 mono_install_runtime_cleanup ((MonoDomainFunc)mini_cleanup);
11688 mono_runtime_init (domain, mono_thread_start_cb, mono_thread_attach_cb);
11689 #endif
11691 mono_thread_attach (domain);
11692 return domain;
11695 MonoJitStats mono_jit_stats = {0};
11697 static void
11698 print_jit_stats (void)
11700 if (mono_jit_stats.enabled) {
11701 g_print ("Mono Jit statistics\n");
11702 g_print ("Compiled methods: %ld\n", mono_jit_stats.methods_compiled);
11703 g_print ("Methods from AOT: %ld\n", mono_jit_stats.methods_aot);
11704 g_print ("Methods cache lookup: %ld\n", mono_jit_stats.methods_lookups);
11705 g_print ("Method trampolines: %ld\n", mono_jit_stats.method_trampolines);
11706 g_print ("Basic blocks: %ld\n", mono_jit_stats.basic_blocks);
11707 g_print ("Max basic blocks: %ld\n", mono_jit_stats.max_basic_blocks);
11708 g_print ("Allocated vars: %ld\n", mono_jit_stats.allocate_var);
11709 g_print ("Analyze stack repeat: %ld\n", mono_jit_stats.analyze_stack_repeat);
11710 g_print ("Compiled CIL code size: %ld\n", mono_jit_stats.cil_code_size);
11711 g_print ("Native code size: %ld\n", mono_jit_stats.native_code_size);
11712 g_print ("Max code size ratio: %.2f (%s::%s)\n", mono_jit_stats.max_code_size_ratio/100.0,
11713 mono_jit_stats.max_ratio_method->klass->name, mono_jit_stats.max_ratio_method->name);
11714 g_print ("Biggest method: %ld (%s::%s)\n", mono_jit_stats.biggest_method_size,
11715 mono_jit_stats.biggest_method->klass->name, mono_jit_stats.biggest_method->name);
11716 g_print ("Code reallocs: %ld\n", mono_jit_stats.code_reallocs);
11717 g_print ("Allocated code size: %ld\n", mono_jit_stats.allocated_code_size);
11718 g_print ("Inlineable methods: %ld\n", mono_jit_stats.inlineable_methods);
11719 g_print ("Inlined methods: %ld\n", mono_jit_stats.inlined_methods);
11721 g_print ("\nCreated object count: %ld\n", mono_stats.new_object_count);
11722 g_print ("Initialized classes: %ld\n", mono_stats.initialized_class_count);
11723 g_print ("Used classes: %ld\n", mono_stats.used_class_count);
11724 g_print ("Generic vtables: %ld\n", mono_stats.generic_vtable_count);
11725 g_print ("Methods: %ld\n", mono_stats.method_count);
11726 g_print ("Static data size: %ld\n", mono_stats.class_static_data_size);
11727 g_print ("VTable data size: %ld\n", mono_stats.class_vtable_size);
11728 g_print ("Mscorlib mempool size: %d\n", mono_mempool_get_allocated (mono_defaults.corlib->mempool));
11730 g_print ("\nGeneric instances: %ld\n", mono_stats.generic_instance_count);
11731 g_print ("Initialized classes: %ld\n", mono_stats.generic_class_count);
11732 g_print ("Inflated methods: %ld / %ld\n", mono_stats.inflated_method_count_2,
11733 mono_stats.inflated_method_count);
11734 g_print ("Inflated types: %ld\n", mono_stats.inflated_type_count);
11735 g_print ("Generics metadata size: %ld\n", mono_stats.generics_metadata_size);
11737 if (mono_use_security_manager) {
11738 g_print ("\nDecl security check : %ld\n", mono_jit_stats.cas_declsec_check);
11739 g_print ("LinkDemand (user) : %ld\n", mono_jit_stats.cas_linkdemand);
11740 g_print ("LinkDemand (icall) : %ld\n", mono_jit_stats.cas_linkdemand_icall);
11741 g_print ("LinkDemand (pinvoke) : %ld\n", mono_jit_stats.cas_linkdemand_pinvoke);
11742 g_print ("LinkDemand (aptc) : %ld\n", mono_jit_stats.cas_linkdemand_aptc);
11743 g_print ("Demand (code gen) : %ld\n", mono_jit_stats.cas_demand_generation);
11745 if (debug_options.collect_pagefault_stats) {
11746 g_print ("Metadata pagefaults : %d\n", mono_raw_buffer_get_n_pagefaults ());
11747 g_print ("AOT pagefaults : %d\n", mono_aot_get_n_pagefaults ());
11752 void
11753 mini_cleanup (MonoDomain *domain)
11755 #ifdef HAVE_LINUX_RTC_H
11756 if (rtc_fd >= 0)
11757 enable_rtc_timer (FALSE);
11758 #endif
11761 * mono_runtime_cleanup() and mono_domain_finalize () need to
11762 * be called early since they need the execution engine still
11763 * fully working (mono_domain_finalize may invoke managed finalizers
11764 * and mono_runtime_cleanup will wait for other threads to finish).
11766 mono_domain_finalize (domain, 2000);
11768 /* This accesses metadata so needs to be called before runtime shutdown */
11769 print_jit_stats ();
11771 mono_runtime_cleanup (domain);
11773 mono_profiler_shutdown ();
11775 mono_debug_cleanup ();
11777 mono_icall_cleanup ();
11779 mono_runtime_cleanup_handlers ();
11781 mono_domain_free (domain, TRUE);
11783 mono_code_manager_destroy (global_codeman);
11784 g_hash_table_destroy (jit_icall_name_hash);
11785 if (class_init_hash_addr)
11786 g_hash_table_destroy (class_init_hash_addr);
11787 g_free (emul_opcode_map);
11789 mono_cleanup ();
11791 mono_trace_cleanup ();
11793 mono_counters_dump (-1, stdout);
11795 TlsFree(mono_jit_tls_id);
11797 DeleteCriticalSection (&jit_mutex);
11799 DeleteCriticalSection (&mono_delegate_section);
11802 void
11803 mono_set_defaults (int verbose_level, guint32 opts)
11805 mini_verbose = verbose_level;
11806 default_opt = opts;
11807 default_opt_set = TRUE;
11810 static void
11811 mono_precompile_assembly (MonoAssembly *ass, void *user_data)
11813 GHashTable *assemblies = (GHashTable*)user_data;
11814 MonoImage *image = mono_assembly_get_image (ass);
11815 MonoMethod *method, *invoke;
11816 int i, count = 0;
11818 if (g_hash_table_lookup (assemblies, ass))
11819 return;
11821 g_hash_table_insert (assemblies, ass, ass);
11823 if (mini_verbose > 0)
11824 printf ("PRECOMPILE: %s.\n", mono_image_get_filename (image));
11826 for (i = 0; i < mono_image_get_table_rows (image, MONO_TABLE_METHOD); ++i) {
11827 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
11828 if (method->flags & METHOD_ATTRIBUTE_ABSTRACT)
11829 continue;
11831 count++;
11832 if (mini_verbose > 1) {
11833 char * desc = mono_method_full_name (method, TRUE);
11834 g_print ("Compiling %d %s\n", count, desc);
11835 g_free (desc);
11837 mono_compile_method (method);
11838 if (strcmp (method->name, "Finalize") == 0) {
11839 invoke = mono_marshal_get_runtime_invoke (method);
11840 mono_compile_method (invoke);
11842 if (method->klass->marshalbyref && mono_method_signature (method)->hasthis) {
11843 invoke = mono_marshal_get_remoting_invoke_with_check (method);
11844 mono_compile_method (invoke);
11848 /* Load and precompile referenced assemblies as well */
11849 for (i = 0; i < mono_image_get_table_rows (image, MONO_TABLE_ASSEMBLYREF); ++i) {
11850 mono_assembly_load_reference (image, i);
11851 if (image->references [i])
11852 mono_precompile_assembly (image->references [i], assemblies);
11856 void mono_precompile_assemblies ()
11858 GHashTable *assemblies = g_hash_table_new (NULL, NULL);
11860 mono_assembly_foreach ((GFunc)mono_precompile_assembly, assemblies);
11862 g_hash_table_destroy (assemblies);