/** * \file * llvm "Backend" for the mono JIT * * Copyright 2009-2011 Novell Inc (http://www.novell.com) * Copyright 2011 Xamarin Inc (http://www.xamarin.com) * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #include "llvm-c/BitWriter.h" #include "llvm-c/Analysis.h" #include "mini-llvm-cpp.h" #include "llvm-jit.h" #include "aot-compiler.h" #include "mini-llvm.h" #ifndef DISABLE_JIT #ifdef __MINGW32__ #include extern void *memset(void *, int, size_t); void bzero (void *to, size_t count) { memset (to, 0, count); } #endif #if LLVM_API_VERSION < 4 #error "The version of the mono llvm repository is too old." #endif #define ALIGN_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1))) /* * Information associated by mono with LLVM modules. */ typedef struct { LLVMModuleRef lmodule; LLVMValueRef throw_icall, rethrow, match_exc, throw_corlib_exception, resume_eh; GHashTable *llvm_types; LLVMValueRef got_var; const char *got_symbol; const char *get_method_symbol; const char *get_unbox_tramp_symbol; GHashTable *plt_entries; GHashTable *plt_entries_ji; GHashTable *method_to_lmethod; GHashTable *direct_callables; char **bb_names; int bb_names_len; GPtrArray *used; LLVMTypeRef ptr_type; GPtrArray *subprogram_mds; MonoEERef *mono_ee; LLVMExecutionEngineRef ee; gboolean external_symbols; gboolean emit_dwarf; int max_got_offset; LLVMValueRef personality; /* For AOT */ MonoAssembly *assembly; char *global_prefix; MonoAotFileInfo aot_info; const char *jit_got_symbol; const char *eh_frame_symbol; LLVMValueRef get_method, get_unbox_tramp; LLVMValueRef init_method, init_method_gshared_mrgctx, init_method_gshared_this, init_method_gshared_vtable; LLVMValueRef code_start, code_end; LLVMValueRef inited_var; int max_inited_idx, max_method_idx; gboolean has_jitted_code; gboolean static_link; gboolean llvm_only; GHashTable *idx_to_lmethod; GHashTable *idx_to_unbox_tramp; /* Maps a MonoMethod to LLVM instructions representing it */ GHashTable *method_to_callers; LLVMContextRef context; LLVMValueRef sentinel_exception; void *di_builder, *cu; GHashTable *objc_selector_to_var; } MonoLLVMModule; /* * Information associated by the backend with mono basic blocks. */ typedef struct { LLVMBasicBlockRef bblock, end_bblock; LLVMValueRef finally_ind; gboolean added, invoke_target; /* * If this bblock is the start of a finally clause, this is a list of bblocks it * needs to branch to in ENDFINALLY. */ GSList *call_handler_return_bbs; /* * If this bblock is the start of a finally clause, this is the bblock that * CALL_HANDLER needs to branch to. */ LLVMBasicBlockRef call_handler_target_bb; /* The list of switch statements generated by ENDFINALLY instructions */ GSList *endfinally_switch_ins_list; GSList *phi_nodes; } BBInfo; /* * Structure containing emit state */ typedef struct { MonoMemPool *mempool; /* Maps method names to the corresponding LLVMValueRef */ GHashTable *emitted_method_decls; MonoCompile *cfg; LLVMValueRef lmethod; MonoLLVMModule *module; LLVMModuleRef lmodule; BBInfo *bblocks; int sindex, default_index, ex_index; LLVMBuilderRef builder; LLVMValueRef *values, *addresses; MonoType **vreg_cli_types; LLVMCallInfo *linfo; MonoMethodSignature *sig; GSList *builders; GHashTable *region_to_handler; GHashTable *clause_to_handler; LLVMBuilderRef alloca_builder; LLVMValueRef last_alloca; LLVMValueRef rgctx_arg; LLVMValueRef this_arg; LLVMTypeRef *vreg_types; gboolean *is_vphi; LLVMTypeRef method_type; LLVMBasicBlockRef init_bb, inited_bb; gboolean *is_dead; gboolean *unreachable; gboolean llvm_only; gboolean has_got_access; gboolean is_linkonce; int this_arg_pindex, rgctx_arg_pindex; LLVMValueRef imt_rgctx_loc; GHashTable *llvm_types; LLVMValueRef dbg_md; MonoDebugMethodInfo *minfo; char temp_name [32]; /* For every clause, the clauses it is nested in */ GSList **nested_in; LLVMValueRef ex_var; GHashTable *exc_meta; GHashTable *method_to_callers; GPtrArray *phi_values; GPtrArray *bblock_list; char *method_name; GHashTable *jit_callees; LLVMValueRef long_bb_break_var; } EmitContext; typedef struct { MonoBasicBlock *bb; MonoInst *phi; MonoBasicBlock *in_bb; int sreg; } PhiNode; /* * Instruction metadata * This is the same as ins_info, but LREG != IREG. */ #ifdef MINI_OP #undef MINI_OP #endif #ifdef MINI_OP3 #undef MINI_OP3 #endif #define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ', #define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3, #define NONE ' ' #define IREG 'i' #define FREG 'f' #define VREG 'v' #define XREG 'x' #define LREG 'l' /* keep in sync with the enum in mini.h */ const char llvm_ins_info[] = { #include "mini-ops.h" }; #undef MINI_OP #undef MINI_OP3 #if SIZEOF_VOID_P == 4 #define GET_LONG_IMM(ins) (((guint64)(ins)->inst_ms_word << 32) | (guint64)(guint32)(ins)->inst_ls_word) #else #define GET_LONG_IMM(ins) ((ins)->inst_imm) #endif #define LLVM_INS_INFO(opcode) (&llvm_ins_info [((opcode) - OP_START - 1) * 4]) #if 0 #define TRACE_FAILURE(msg) do { printf ("%s\n", msg); } while (0) #else #define TRACE_FAILURE(msg) #endif #ifdef TARGET_X86 #define IS_TARGET_X86 1 #else #define IS_TARGET_X86 0 #endif #ifdef TARGET_AMD64 #define IS_TARGET_AMD64 1 #else #define IS_TARGET_AMD64 0 #endif #define ctx_ok(ctx) (!(ctx)->cfg->disable_llvm) static LLVMIntPredicate cond_to_llvm_cond [] = { LLVMIntEQ, LLVMIntNE, LLVMIntSLE, LLVMIntSGE, LLVMIntSLT, LLVMIntSGT, LLVMIntULE, LLVMIntUGE, LLVMIntULT, LLVMIntUGT, }; static LLVMRealPredicate fpcond_to_llvm_cond [] = { LLVMRealOEQ, LLVMRealUNE, LLVMRealOLE, LLVMRealOGE, LLVMRealOLT, LLVMRealOGT, LLVMRealULE, LLVMRealUGE, LLVMRealULT, LLVMRealUGT, }; static MonoNativeTlsKey current_cfg_tls_id; static MonoLLVMModule aot_module; static GHashTable *intrins_id_to_name; static GHashTable *intrins_name_to_id; static void init_jit_module (MonoDomain *domain); static void emit_dbg_loc (EmitContext *ctx, LLVMBuilderRef builder, const unsigned char *cil_code); static LLVMValueRef emit_dbg_subprogram (EmitContext *ctx, MonoCompile *cfg, LLVMValueRef method, const char *name); static void emit_dbg_info (MonoLLVMModule *module, const char *filename, const char *cu_name); static void emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *exc_type, LLVMValueRef cmp); static LLVMValueRef get_intrinsic (EmitContext *ctx, const char *name); static void decode_llvm_eh_info (EmitContext *ctx, gpointer eh_frame); static inline void set_failure (EmitContext *ctx, const char *message) { TRACE_FAILURE (reason); ctx->cfg->exception_message = g_strdup (message); ctx->cfg->disable_llvm = TRUE; } /* * IntPtrType: * * The LLVM type with width == sizeof (gpointer) */ static LLVMTypeRef IntPtrType (void) { return sizeof (gpointer) == 8 ? LLVMInt64Type () : LLVMInt32Type (); } static LLVMTypeRef ObjRefType (void) { return sizeof (gpointer) == 8 ? LLVMPointerType (LLVMInt64Type (), 0) : LLVMPointerType (LLVMInt32Type (), 0); } static LLVMTypeRef ThisType (void) { return sizeof (gpointer) == 8 ? LLVMPointerType (LLVMInt64Type (), 0) : LLVMPointerType (LLVMInt32Type (), 0); } /* * get_vtype_size: * * Return the size of the LLVM representation of the vtype T. */ static guint32 get_vtype_size (MonoType *t) { int size; size = mono_class_value_size (mono_class_from_mono_type (t), NULL); /* LLVMArgAsIArgs depends on this since it stores whole words */ while (size < 2 * sizeof (gpointer) && mono_is_power_of_two (size) == -1) size ++; return size; } /* * simd_class_to_llvm_type: * * Return the LLVM type corresponding to the Mono.SIMD class KLASS */ static LLVMTypeRef simd_class_to_llvm_type (EmitContext *ctx, MonoClass *klass) { if (!strcmp (klass->name, "Vector2d")) { return LLVMVectorType (LLVMDoubleType (), 2); } else if (!strcmp (klass->name, "Vector2l")) { return LLVMVectorType (LLVMInt64Type (), 2); } else if (!strcmp (klass->name, "Vector2ul")) { return LLVMVectorType (LLVMInt64Type (), 2); } else if (!strcmp (klass->name, "Vector4i")) { return LLVMVectorType (LLVMInt32Type (), 4); } else if (!strcmp (klass->name, "Vector4ui")) { return LLVMVectorType (LLVMInt32Type (), 4); } else if (!strcmp (klass->name, "Vector4f")) { return LLVMVectorType (LLVMFloatType (), 4); } else if (!strcmp (klass->name, "Vector8s")) { return LLVMVectorType (LLVMInt16Type (), 8); } else if (!strcmp (klass->name, "Vector8us")) { return LLVMVectorType (LLVMInt16Type (), 8); } else if (!strcmp (klass->name, "Vector16sb")) { return LLVMVectorType (LLVMInt8Type (), 16); } else if (!strcmp (klass->name, "Vector16b")) { return LLVMVectorType (LLVMInt8Type (), 16); } else if (!strcmp (klass->name, "Vector2")) { /* System.Numerics */ return LLVMVectorType (LLVMFloatType (), 4); } else if (!strcmp (klass->name, "Vector3")) { return LLVMVectorType (LLVMFloatType (), 4); } else if (!strcmp (klass->name, "Vector4")) { return LLVMVectorType (LLVMFloatType (), 4); } else if (!strcmp (klass->name, "Vector`1")) { MonoType *etype = mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]; switch (etype->type) { case MONO_TYPE_I1: case MONO_TYPE_U1: return LLVMVectorType (LLVMInt8Type (), 16); case MONO_TYPE_I2: case MONO_TYPE_U2: return LLVMVectorType (LLVMInt16Type (), 8); case MONO_TYPE_I4: case MONO_TYPE_U4: return LLVMVectorType (LLVMInt32Type (), 4); case MONO_TYPE_I8: case MONO_TYPE_U8: return LLVMVectorType (LLVMInt64Type (), 2); case MONO_TYPE_R4: return LLVMVectorType (LLVMFloatType (), 4); case MONO_TYPE_R8: return LLVMVectorType (LLVMDoubleType (), 2); default: g_assert_not_reached (); return NULL; } } else { printf ("%s\n", klass->name); NOT_IMPLEMENTED; return NULL; } } /* Return the 128 bit SIMD type corresponding to the mono type TYPE */ static inline G_GNUC_UNUSED LLVMTypeRef type_to_simd_type (int type) { switch (type) { case MONO_TYPE_I1: return LLVMVectorType (LLVMInt8Type (), 16); case MONO_TYPE_I2: return LLVMVectorType (LLVMInt16Type (), 8); case MONO_TYPE_I4: return LLVMVectorType (LLVMInt32Type (), 4); case MONO_TYPE_I8: return LLVMVectorType (LLVMInt64Type (), 2); case MONO_TYPE_R8: return LLVMVectorType (LLVMDoubleType (), 2); case MONO_TYPE_R4: return LLVMVectorType (LLVMFloatType (), 4); default: g_assert_not_reached (); return NULL; } } static LLVMTypeRef create_llvm_type_for_type (MonoLLVMModule *module, MonoClass *klass) { int i, size, nfields, esize; LLVMTypeRef *eltypes; char *name; MonoType *t; LLVMTypeRef ltype; t = &klass->byval_arg; if (mini_type_is_hfa (t, &nfields, &esize)) { /* * This is needed on arm64 where HFAs are returned in * registers. */ /* SIMD types have size 16 in mono_class_value_size () */ if (klass->simd_type) nfields = 16/ esize; size = nfields; eltypes = g_new (LLVMTypeRef, size); for (i = 0; i < size; ++i) eltypes [i] = esize == 4 ? LLVMFloatType () : LLVMDoubleType (); } else { size = get_vtype_size (t); eltypes = g_new (LLVMTypeRef, size); for (i = 0; i < size; ++i) eltypes [i] = LLVMInt8Type (); } name = mono_type_full_name (&klass->byval_arg); ltype = LLVMStructCreateNamed (module->context, name); LLVMStructSetBody (ltype, eltypes, size, FALSE); g_free (eltypes); g_free (name); return ltype; } /* * type_to_llvm_type: * * Return the LLVM type corresponding to T. */ static LLVMTypeRef type_to_llvm_type (EmitContext *ctx, MonoType *t) { if (t->byref) return ThisType (); t = mini_get_underlying_type (t); switch (t->type) { case MONO_TYPE_VOID: return LLVMVoidType (); case MONO_TYPE_I1: return LLVMInt8Type (); case MONO_TYPE_I2: return LLVMInt16Type (); case MONO_TYPE_I4: return LLVMInt32Type (); case MONO_TYPE_U1: return LLVMInt8Type (); case MONO_TYPE_U2: return LLVMInt16Type (); case MONO_TYPE_U4: return LLVMInt32Type (); case MONO_TYPE_I8: case MONO_TYPE_U8: return LLVMInt64Type (); case MONO_TYPE_R4: return LLVMFloatType (); case MONO_TYPE_R8: return LLVMDoubleType (); case MONO_TYPE_I: case MONO_TYPE_U: return IntPtrType (); case MONO_TYPE_OBJECT: case MONO_TYPE_PTR: return ObjRefType (); case MONO_TYPE_VAR: case MONO_TYPE_MVAR: /* Because of generic sharing */ return ObjRefType (); case MONO_TYPE_GENERICINST: if (!mono_type_generic_inst_is_valuetype (t)) return ObjRefType (); /* Fall through */ case MONO_TYPE_VALUETYPE: case MONO_TYPE_TYPEDBYREF: { MonoClass *klass; LLVMTypeRef ltype; klass = mono_class_from_mono_type (t); if (MONO_CLASS_IS_SIMD (ctx->cfg, klass)) return simd_class_to_llvm_type (ctx, klass); if (klass->enumtype) return type_to_llvm_type (ctx, mono_class_enum_basetype (klass)); ltype = (LLVMTypeRef)g_hash_table_lookup (ctx->module->llvm_types, klass); if (!ltype) { ltype = create_llvm_type_for_type (ctx->module, klass); g_hash_table_insert (ctx->module->llvm_types, klass, ltype); } return ltype; } default: printf ("X: %d\n", t->type); ctx->cfg->exception_message = g_strdup_printf ("type %s", mono_type_full_name (t)); ctx->cfg->disable_llvm = TRUE; return NULL; } } /* * type_is_unsigned: * * Return whenever T is an unsigned int type. */ static gboolean type_is_unsigned (EmitContext *ctx, MonoType *t) { t = mini_get_underlying_type (t); if (t->byref) return FALSE; switch (t->type) { case MONO_TYPE_U1: case MONO_TYPE_U2: case MONO_TYPE_CHAR: case MONO_TYPE_U4: case MONO_TYPE_U8: return TRUE; default: return FALSE; } } /* * type_to_llvm_arg_type: * * Same as type_to_llvm_type, but treat i8/i16 as i32. */ static LLVMTypeRef type_to_llvm_arg_type (EmitContext *ctx, MonoType *t) { LLVMTypeRef ptype = type_to_llvm_type (ctx, t); if (ctx->cfg->llvm_only) return ptype; /* * This works on all abis except arm64/ios which passes multiple * arguments in one stack slot. */ #ifndef TARGET_ARM64 if (ptype == LLVMInt8Type () || ptype == LLVMInt16Type ()) { /* * LLVM generates code which only sets the lower bits, while JITted * code expects all the bits to be set. */ ptype = LLVMInt32Type (); } #endif return ptype; } /* * llvm_type_to_stack_type: * * Return the LLVM type which needs to be used when a value of type TYPE is pushed * on the IL stack. */ static G_GNUC_UNUSED LLVMTypeRef llvm_type_to_stack_type (MonoCompile *cfg, LLVMTypeRef type) { if (type == NULL) return NULL; if (type == LLVMInt8Type ()) return LLVMInt32Type (); else if (type == LLVMInt16Type ()) return LLVMInt32Type (); else if (!cfg->r4fp && type == LLVMFloatType ()) return LLVMDoubleType (); else return type; } /* * regtype_to_llvm_type: * * Return the LLVM type corresponding to the regtype C used in instruction * descriptions. */ static LLVMTypeRef regtype_to_llvm_type (char c) { switch (c) { case 'i': return LLVMInt32Type (); case 'l': return LLVMInt64Type (); case 'f': return LLVMDoubleType (); default: return NULL; } } /* * op_to_llvm_type: * * Return the LLVM type corresponding to the unary/binary opcode OPCODE. */ static LLVMTypeRef op_to_llvm_type (int opcode) { switch (opcode) { case OP_ICONV_TO_I1: case OP_LCONV_TO_I1: return LLVMInt8Type (); case OP_ICONV_TO_U1: case OP_LCONV_TO_U1: return LLVMInt8Type (); case OP_ICONV_TO_I2: case OP_LCONV_TO_I2: return LLVMInt16Type (); case OP_ICONV_TO_U2: case OP_LCONV_TO_U2: return LLVMInt16Type (); case OP_ICONV_TO_I4: case OP_LCONV_TO_I4: return LLVMInt32Type (); case OP_ICONV_TO_U4: case OP_LCONV_TO_U4: return LLVMInt32Type (); case OP_ICONV_TO_I8: return LLVMInt64Type (); case OP_ICONV_TO_R4: return LLVMFloatType (); case OP_ICONV_TO_R8: return LLVMDoubleType (); case OP_ICONV_TO_U8: return LLVMInt64Type (); case OP_FCONV_TO_I4: return LLVMInt32Type (); case OP_FCONV_TO_I8: return LLVMInt64Type (); case OP_FCONV_TO_I1: case OP_FCONV_TO_U1: case OP_RCONV_TO_I1: case OP_RCONV_TO_U1: return LLVMInt8Type (); case OP_FCONV_TO_I2: case OP_FCONV_TO_U2: case OP_RCONV_TO_I2: case OP_RCONV_TO_U2: return LLVMInt16Type (); case OP_RCONV_TO_U4: return LLVMInt32Type (); case OP_FCONV_TO_I: case OP_FCONV_TO_U: return sizeof (gpointer) == 8 ? LLVMInt64Type () : LLVMInt32Type (); case OP_IADD_OVF: case OP_IADD_OVF_UN: case OP_ISUB_OVF: case OP_ISUB_OVF_UN: case OP_IMUL_OVF: case OP_IMUL_OVF_UN: return LLVMInt32Type (); case OP_LADD_OVF: case OP_LADD_OVF_UN: case OP_LSUB_OVF: case OP_LSUB_OVF_UN: case OP_LMUL_OVF: case OP_LMUL_OVF_UN: return LLVMInt64Type (); default: printf ("%s\n", mono_inst_name (opcode)); g_assert_not_reached (); return NULL; } } #define CLAUSE_START(clause) ((clause)->try_offset) #define CLAUSE_END(clause) (((clause))->try_offset + ((clause))->try_len) /* * load_store_to_llvm_type: * * Return the size/sign/zero extension corresponding to the load/store opcode * OPCODE. */ static LLVMTypeRef load_store_to_llvm_type (int opcode, int *size, gboolean *sext, gboolean *zext) { *sext = FALSE; *zext = FALSE; switch (opcode) { case OP_LOADI1_MEMBASE: case OP_STOREI1_MEMBASE_REG: case OP_STOREI1_MEMBASE_IMM: case OP_ATOMIC_LOAD_I1: case OP_ATOMIC_STORE_I1: *size = 1; *sext = TRUE; return LLVMInt8Type (); case OP_LOADU1_MEMBASE: case OP_LOADU1_MEM: case OP_ATOMIC_LOAD_U1: case OP_ATOMIC_STORE_U1: *size = 1; *zext = TRUE; return LLVMInt8Type (); case OP_LOADI2_MEMBASE: case OP_STOREI2_MEMBASE_REG: case OP_STOREI2_MEMBASE_IMM: case OP_ATOMIC_LOAD_I2: case OP_ATOMIC_STORE_I2: *size = 2; *sext = TRUE; return LLVMInt16Type (); case OP_LOADU2_MEMBASE: case OP_LOADU2_MEM: case OP_ATOMIC_LOAD_U2: case OP_ATOMIC_STORE_U2: *size = 2; *zext = TRUE; return LLVMInt16Type (); case OP_LOADI4_MEMBASE: case OP_LOADU4_MEMBASE: case OP_LOADI4_MEM: case OP_LOADU4_MEM: case OP_STOREI4_MEMBASE_REG: case OP_STOREI4_MEMBASE_IMM: case OP_ATOMIC_LOAD_I4: case OP_ATOMIC_STORE_I4: case OP_ATOMIC_LOAD_U4: case OP_ATOMIC_STORE_U4: *size = 4; return LLVMInt32Type (); case OP_LOADI8_MEMBASE: case OP_LOADI8_MEM: case OP_STOREI8_MEMBASE_REG: case OP_STOREI8_MEMBASE_IMM: case OP_ATOMIC_LOAD_I8: case OP_ATOMIC_STORE_I8: case OP_ATOMIC_LOAD_U8: case OP_ATOMIC_STORE_U8: *size = 8; return LLVMInt64Type (); case OP_LOADR4_MEMBASE: case OP_STORER4_MEMBASE_REG: case OP_ATOMIC_LOAD_R4: case OP_ATOMIC_STORE_R4: *size = 4; return LLVMFloatType (); case OP_LOADR8_MEMBASE: case OP_STORER8_MEMBASE_REG: case OP_ATOMIC_LOAD_R8: case OP_ATOMIC_STORE_R8: *size = 8; return LLVMDoubleType (); case OP_LOAD_MEMBASE: case OP_LOAD_MEM: case OP_STORE_MEMBASE_REG: case OP_STORE_MEMBASE_IMM: *size = sizeof (gpointer); return IntPtrType (); default: g_assert_not_reached (); return NULL; } } /* * ovf_op_to_intrins: * * Return the LLVM intrinsics corresponding to the overflow opcode OPCODE. */ static const char* ovf_op_to_intrins (int opcode) { switch (opcode) { case OP_IADD_OVF: return "llvm.sadd.with.overflow.i32"; case OP_IADD_OVF_UN: return "llvm.uadd.with.overflow.i32"; case OP_ISUB_OVF: return "llvm.ssub.with.overflow.i32"; case OP_ISUB_OVF_UN: return "llvm.usub.with.overflow.i32"; case OP_IMUL_OVF: return "llvm.smul.with.overflow.i32"; case OP_IMUL_OVF_UN: return "llvm.umul.with.overflow.i32"; case OP_LADD_OVF: return "llvm.sadd.with.overflow.i64"; case OP_LADD_OVF_UN: return "llvm.uadd.with.overflow.i64"; case OP_LSUB_OVF: return "llvm.ssub.with.overflow.i64"; case OP_LSUB_OVF_UN: return "llvm.usub.with.overflow.i64"; case OP_LMUL_OVF: return "llvm.smul.with.overflow.i64"; case OP_LMUL_OVF_UN: return "llvm.umul.with.overflow.i64"; default: g_assert_not_reached (); return NULL; } } static const char* simd_op_to_intrins (int opcode) { switch (opcode) { #if defined(TARGET_X86) || defined(TARGET_AMD64) case OP_MINPD: return "llvm.x86.sse2.min.pd"; case OP_MINPS: return "llvm.x86.sse.min.ps"; case OP_MAXPD: return "llvm.x86.sse2.max.pd"; case OP_MAXPS: return "llvm.x86.sse.max.ps"; case OP_HADDPD: return "llvm.x86.sse3.hadd.pd"; case OP_HADDPS: return "llvm.x86.sse3.hadd.ps"; case OP_HSUBPD: return "llvm.x86.sse3.hsub.pd"; case OP_HSUBPS: return "llvm.x86.sse3.hsub.ps"; case OP_ADDSUBPS: return "llvm.x86.sse3.addsub.ps"; case OP_ADDSUBPD: return "llvm.x86.sse3.addsub.pd"; case OP_EXTRACT_MASK: return "llvm.x86.sse2.pmovmskb.128"; case OP_PSHRW: case OP_PSHRW_REG: return "llvm.x86.sse2.psrli.w"; case OP_PSHRD: case OP_PSHRD_REG: return "llvm.x86.sse2.psrli.d"; case OP_PSHRQ: case OP_PSHRQ_REG: return "llvm.x86.sse2.psrli.q"; case OP_PSHLW: case OP_PSHLW_REG: return "llvm.x86.sse2.pslli.w"; case OP_PSHLD: case OP_PSHLD_REG: return "llvm.x86.sse2.pslli.d"; case OP_PSHLQ: case OP_PSHLQ_REG: return "llvm.x86.sse2.pslli.q"; case OP_PSARW: case OP_PSARW_REG: return "llvm.x86.sse2.psrai.w"; case OP_PSARD: case OP_PSARD_REG: return "llvm.x86.sse2.psrai.d"; case OP_PADDB_SAT: return "llvm.x86.sse2.padds.b"; case OP_PADDW_SAT: return "llvm.x86.sse2.padds.w"; case OP_PSUBB_SAT: return "llvm.x86.sse2.psubs.b"; case OP_PSUBW_SAT: return "llvm.x86.sse2.psubs.w"; case OP_PADDB_SAT_UN: return "llvm.x86.sse2.paddus.b"; case OP_PADDW_SAT_UN: return "llvm.x86.sse2.paddus.w"; case OP_PSUBB_SAT_UN: return "llvm.x86.sse2.psubus.b"; case OP_PSUBW_SAT_UN: return "llvm.x86.sse2.psubus.w"; case OP_PAVGB_UN: return "llvm.x86.sse2.pavg.b"; case OP_PAVGW_UN: return "llvm.x86.sse2.pavg.w"; case OP_SQRTPS: return "llvm.x86.sse.sqrt.ps"; case OP_SQRTPD: return "llvm.x86.sse2.sqrt.pd"; case OP_RSQRTPS: return "llvm.x86.sse.rsqrt.ps"; case OP_RCPPS: return "llvm.x86.sse.rcp.ps"; case OP_CVTDQ2PD: return "llvm.x86.sse2.cvtdq2pd"; case OP_CVTDQ2PS: return "llvm.x86.sse2.cvtdq2ps"; case OP_CVTPD2DQ: return "llvm.x86.sse2.cvtpd2dq"; case OP_CVTPS2DQ: return "llvm.x86.sse2.cvtps2dq"; case OP_CVTPD2PS: return "llvm.x86.sse2.cvtpd2ps"; case OP_CVTPS2PD: return "llvm.x86.sse2.cvtps2pd"; case OP_CVTTPD2DQ: return "llvm.x86.sse2.cvttpd2dq"; case OP_CVTTPS2DQ: return "llvm.x86.sse2.cvttps2dq"; case OP_PACKW: return "llvm.x86.sse2.packsswb.128"; case OP_PACKD: return "llvm.x86.sse2.packssdw.128"; case OP_PACKW_UN: return "llvm.x86.sse2.packuswb.128"; case OP_PACKD_UN: return "llvm.x86.sse41.packusdw"; case OP_PMULW_HIGH: return "llvm.x86.sse2.pmulh.w"; case OP_PMULW_HIGH_UN: return "llvm.x86.sse2.pmulhu.w"; case OP_DPPS: return "llvm.x86.sse41.dpps"; #endif default: g_assert_not_reached (); return NULL; } } static LLVMTypeRef simd_op_to_llvm_type (int opcode) { #if defined(TARGET_X86) || defined(TARGET_AMD64) switch (opcode) { case OP_EXTRACT_R8: case OP_EXPAND_R8: return type_to_simd_type (MONO_TYPE_R8); case OP_EXTRACT_I8: case OP_EXPAND_I8: return type_to_simd_type (MONO_TYPE_I8); case OP_EXTRACT_I4: case OP_EXPAND_I4: return type_to_simd_type (MONO_TYPE_I4); case OP_EXTRACT_I2: case OP_EXTRACT_U2: case OP_EXTRACTX_U2: case OP_EXPAND_I2: return type_to_simd_type (MONO_TYPE_I2); case OP_EXTRACT_I1: case OP_EXTRACT_U1: case OP_EXPAND_I1: return type_to_simd_type (MONO_TYPE_I1); case OP_EXPAND_R4: return type_to_simd_type (MONO_TYPE_R4); case OP_CVTDQ2PD: case OP_CVTDQ2PS: return type_to_simd_type (MONO_TYPE_I4); case OP_CVTPD2DQ: case OP_CVTPD2PS: case OP_CVTTPD2DQ: return type_to_simd_type (MONO_TYPE_R8); case OP_CVTPS2DQ: case OP_CVTPS2PD: case OP_CVTTPS2DQ: return type_to_simd_type (MONO_TYPE_R4); case OP_EXTRACT_MASK: return type_to_simd_type (MONO_TYPE_I1); case OP_SQRTPS: case OP_RSQRTPS: case OP_RCPPS: case OP_DUPPS_LOW: case OP_DUPPS_HIGH: return type_to_simd_type (MONO_TYPE_R4); case OP_SQRTPD: case OP_DUPPD: return type_to_simd_type (MONO_TYPE_R8); default: g_assert_not_reached (); return NULL; } #else return NULL; #endif } /* * get_bb: * * Return the LLVM basic block corresponding to BB. */ static LLVMBasicBlockRef get_bb (EmitContext *ctx, MonoBasicBlock *bb) { char bb_name_buf [128]; char *bb_name; if (ctx->bblocks [bb->block_num].bblock == NULL) { if (bb->flags & BB_EXCEPTION_HANDLER) { int clause_index = (mono_get_block_region_notry (ctx->cfg, bb->region) >> 8) - 1; sprintf (bb_name_buf, "EH_CLAUSE%d_BB%d", clause_index, bb->block_num); bb_name = bb_name_buf; } else if (bb->block_num < 256) { if (!ctx->module->bb_names) { ctx->module->bb_names_len = 256; ctx->module->bb_names = g_new0 (char*, ctx->module->bb_names_len); } if (!ctx->module->bb_names [bb->block_num]) { char *n; n = g_strdup_printf ("BB%d", bb->block_num); mono_memory_barrier (); ctx->module->bb_names [bb->block_num] = n; } bb_name = ctx->module->bb_names [bb->block_num]; } else { sprintf (bb_name_buf, "BB%d", bb->block_num); bb_name = bb_name_buf; } ctx->bblocks [bb->block_num].bblock = LLVMAppendBasicBlock (ctx->lmethod, bb_name); ctx->bblocks [bb->block_num].end_bblock = ctx->bblocks [bb->block_num].bblock; } return ctx->bblocks [bb->block_num].bblock; } /* * get_end_bb: * * Return the last LLVM bblock corresponding to BB. * This might not be equal to the bb returned by get_bb () since we need to generate * multiple LLVM bblocks for a mono bblock to handle throwing exceptions. */ static LLVMBasicBlockRef get_end_bb (EmitContext *ctx, MonoBasicBlock *bb) { get_bb (ctx, bb); return ctx->bblocks [bb->block_num].end_bblock; } static LLVMBasicBlockRef gen_bb (EmitContext *ctx, const char *prefix) { char bb_name [128]; sprintf (bb_name, "%s%d", prefix, ++ ctx->ex_index); return LLVMAppendBasicBlock (ctx->lmethod, bb_name); } /* * resolve_patch: * * Return the target of the patch identified by TYPE and TARGET. */ static gpointer resolve_patch (MonoCompile *cfg, MonoJumpInfoType type, gconstpointer target) { MonoJumpInfo ji; MonoError error; gpointer res; memset (&ji, 0, sizeof (ji)); ji.type = type; ji.data.target = target; res = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, &ji, FALSE, &error); mono_error_assert_ok (&error); return res; } /* * convert_full: * * Emit code to convert the LLVM value V to DTYPE. */ static LLVMValueRef convert_full (EmitContext *ctx, LLVMValueRef v, LLVMTypeRef dtype, gboolean is_unsigned) { LLVMTypeRef stype = LLVMTypeOf (v); if (stype != dtype) { gboolean ext = FALSE; /* Extend */ if (dtype == LLVMInt64Type () && (stype == LLVMInt32Type () || stype == LLVMInt16Type () || stype == LLVMInt8Type ())) ext = TRUE; else if (dtype == LLVMInt32Type () && (stype == LLVMInt16Type () || stype == LLVMInt8Type ())) ext = TRUE; else if (dtype == LLVMInt16Type () && (stype == LLVMInt8Type ())) ext = TRUE; if (ext) return is_unsigned ? LLVMBuildZExt (ctx->builder, v, dtype, "") : LLVMBuildSExt (ctx->builder, v, dtype, ""); if (dtype == LLVMDoubleType () && stype == LLVMFloatType ()) return LLVMBuildFPExt (ctx->builder, v, dtype, ""); /* Trunc */ if (stype == LLVMInt64Type () && (dtype == LLVMInt32Type () || dtype == LLVMInt16Type () || dtype == LLVMInt8Type ())) return LLVMBuildTrunc (ctx->builder, v, dtype, ""); if (stype == LLVMInt32Type () && (dtype == LLVMInt16Type () || dtype == LLVMInt8Type ())) return LLVMBuildTrunc (ctx->builder, v, dtype, ""); if (stype == LLVMInt16Type () && dtype == LLVMInt8Type ()) return LLVMBuildTrunc (ctx->builder, v, dtype, ""); if (stype == LLVMDoubleType () && dtype == LLVMFloatType ()) return LLVMBuildFPTrunc (ctx->builder, v, dtype, ""); if (LLVMGetTypeKind (stype) == LLVMPointerTypeKind && LLVMGetTypeKind (dtype) == LLVMPointerTypeKind) return LLVMBuildBitCast (ctx->builder, v, dtype, ""); if (LLVMGetTypeKind (dtype) == LLVMPointerTypeKind) return LLVMBuildIntToPtr (ctx->builder, v, dtype, ""); if (LLVMGetTypeKind (stype) == LLVMPointerTypeKind) return LLVMBuildPtrToInt (ctx->builder, v, dtype, ""); if (mono_arch_is_soft_float ()) { if (stype == LLVMInt32Type () && dtype == LLVMFloatType ()) return LLVMBuildBitCast (ctx->builder, v, dtype, ""); if (stype == LLVMInt32Type () && dtype == LLVMDoubleType ()) return LLVMBuildBitCast (ctx->builder, LLVMBuildZExt (ctx->builder, v, LLVMInt64Type (), ""), dtype, ""); } if (LLVMGetTypeKind (stype) == LLVMVectorTypeKind && LLVMGetTypeKind (dtype) == LLVMVectorTypeKind) return LLVMBuildBitCast (ctx->builder, v, dtype, ""); LLVMDumpValue (v); LLVMDumpValue (LLVMConstNull (dtype)); g_assert_not_reached (); return NULL; } else { return v; } } static LLVMValueRef convert (EmitContext *ctx, LLVMValueRef v, LLVMTypeRef dtype) { return convert_full (ctx, v, dtype, FALSE); } /* * emit_volatile_load: * * If vreg is volatile, emit a load from its address. */ static LLVMValueRef emit_volatile_load (EmitContext *ctx, int vreg) { MonoType *t; LLVMValueRef v; #ifdef TARGET_ARM64 // FIXME: This hack is required because we pass the rgctx in a callee saved // register on arm64 (x15), and llvm might keep the value in that register // even through the register is marked as 'reserved' inside llvm. if (ctx->cfg->rgctx_var && ctx->cfg->rgctx_var->dreg == vreg) v = mono_llvm_build_load (ctx->builder, ctx->addresses [vreg], "", TRUE); else v = LLVMBuildLoad (ctx->builder, ctx->addresses [vreg], ""); #else v = LLVMBuildLoad (ctx->builder, ctx->addresses [vreg], ""); #endif t = ctx->vreg_cli_types [vreg]; if (t && !t->byref) { /* * Might have to zero extend since llvm doesn't have * unsigned types. */ if (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_U2 || t->type == MONO_TYPE_CHAR || t->type == MONO_TYPE_BOOLEAN) v = LLVMBuildZExt (ctx->builder, v, LLVMInt32Type (), ""); else if (t->type == MONO_TYPE_I1 || t->type == MONO_TYPE_I2) v = LLVMBuildSExt (ctx->builder, v, LLVMInt32Type (), ""); else if (t->type == MONO_TYPE_U8) v = LLVMBuildZExt (ctx->builder, v, LLVMInt64Type (), ""); } return v; } /* * emit_volatile_store: * * If VREG is volatile, emit a store from its value to its address. */ static void emit_volatile_store (EmitContext *ctx, int vreg) { MonoInst *var = get_vreg_to_inst (ctx->cfg, vreg); if (var && var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) { g_assert (ctx->addresses [vreg]); LLVMBuildStore (ctx->builder, convert (ctx, ctx->values [vreg], type_to_llvm_type (ctx, var->inst_vtype)), ctx->addresses [vreg]); } } static LLVMTypeRef sig_to_llvm_sig_no_cinfo (EmitContext *ctx, MonoMethodSignature *sig) { LLVMTypeRef ret_type; LLVMTypeRef *param_types = NULL; LLVMTypeRef res; int i, pindex; MonoType *rtype; ret_type = type_to_llvm_type (ctx, sig->ret); if (!ctx_ok (ctx)) return NULL; rtype = mini_get_underlying_type (sig->ret); param_types = g_new0 (LLVMTypeRef, (sig->param_count * 8) + 3); pindex = 0; if (sig->hasthis) param_types [pindex ++] = ThisType (); for (i = 0; i < sig->param_count; ++i) param_types [pindex ++] = type_to_llvm_arg_type (ctx, sig->params [i]); if (!ctx_ok (ctx)) { g_free (param_types); return NULL; } res = LLVMFunctionType (ret_type, param_types, pindex, FALSE); g_free (param_types); return res; } /* * sig_to_llvm_sig_full: * * Return the LLVM signature corresponding to the mono signature SIG using the * calling convention information in CINFO. Fill out the parameter mapping information in CINFO. */ static LLVMTypeRef sig_to_llvm_sig_full (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *cinfo) { LLVMTypeRef ret_type; LLVMTypeRef *param_types = NULL; LLVMTypeRef res; int i, j, pindex, vret_arg_pindex = 0; gboolean vretaddr = FALSE; MonoType *rtype; if (!cinfo) return sig_to_llvm_sig_no_cinfo (ctx, sig); ret_type = type_to_llvm_type (ctx, sig->ret); if (!ctx_ok (ctx)) return NULL; rtype = mini_get_underlying_type (sig->ret); switch (cinfo->ret.storage) { case LLVMArgVtypeInReg: /* LLVM models this by returning an aggregate value */ if (cinfo->ret.pair_storage [0] == LLVMArgInIReg && cinfo->ret.pair_storage [1] == LLVMArgNone) { LLVMTypeRef members [2]; members [0] = IntPtrType (); ret_type = LLVMStructType (members, 1, FALSE); } else if (cinfo->ret.pair_storage [0] == LLVMArgNone && cinfo->ret.pair_storage [1] == LLVMArgNone) { /* Empty struct */ ret_type = LLVMVoidType (); } else if (cinfo->ret.pair_storage [0] == LLVMArgInIReg && cinfo->ret.pair_storage [1] == LLVMArgInIReg) { LLVMTypeRef members [2]; members [0] = IntPtrType (); members [1] = IntPtrType (); ret_type = LLVMStructType (members, 2, FALSE); } else { g_assert_not_reached (); } break; case LLVMArgVtypeByVal: /* Vtype returned normally by val */ break; case LLVMArgVtypeAsScalar: { int size = mono_class_value_size (mono_class_from_mono_type (rtype), NULL); /* LLVM models this by returning an int */ if (size < SIZEOF_VOID_P) { g_assert (cinfo->ret.nslots == 1); ret_type = LLVMIntType (size * 8); } else { g_assert (cinfo->ret.nslots == 1 || cinfo->ret.nslots == 2); ret_type = LLVMIntType (cinfo->ret.nslots * sizeof (mgreg_t) * 8); } break; } case LLVMArgAsIArgs: ret_type = LLVMArrayType (IntPtrType (), cinfo->ret.nslots); break; case LLVMArgFpStruct: { /* Vtype returned as a fp struct */ LLVMTypeRef members [16]; /* Have to create our own structure since we don't map fp structures to LLVM fp structures yet */ for (i = 0; i < cinfo->ret.nslots; ++i) members [i] = cinfo->ret.esize == 8 ? LLVMDoubleType () : LLVMFloatType (); ret_type = LLVMStructType (members, cinfo->ret.nslots, FALSE); break; } case LLVMArgVtypeByRef: /* Vtype returned using a hidden argument */ ret_type = LLVMVoidType (); break; case LLVMArgVtypeRetAddr: case LLVMArgGsharedvtFixed: case LLVMArgGsharedvtFixedVtype: case LLVMArgGsharedvtVariable: vretaddr = TRUE; ret_type = LLVMVoidType (); break; default: break; } param_types = g_new0 (LLVMTypeRef, (sig->param_count * 8) + 3); pindex = 0; if (cinfo->ret.storage == LLVMArgVtypeByRef) { /* * Has to be the first argument because of the sret argument attribute * FIXME: This might conflict with passing 'this' as the first argument, but * this is only used on arm64 which has a dedicated struct return register. */ cinfo->vret_arg_pindex = pindex; param_types [pindex] = type_to_llvm_arg_type (ctx, sig->ret); if (!ctx_ok (ctx)) { g_free (param_types); return NULL; } param_types [pindex] = LLVMPointerType (param_types [pindex], 0); pindex ++; } if (!ctx->llvm_only && cinfo->rgctx_arg) { cinfo->rgctx_arg_pindex = pindex; param_types [pindex] = ctx->module->ptr_type; pindex ++; } if (cinfo->imt_arg) { cinfo->imt_arg_pindex = pindex; param_types [pindex] = ctx->module->ptr_type; pindex ++; } if (vretaddr) { /* Compute the index in the LLVM signature where the vret arg needs to be passed */ vret_arg_pindex = pindex; if (cinfo->vret_arg_index == 1) { /* Add the slots consumed by the first argument */ LLVMArgInfo *ainfo = &cinfo->args [0]; switch (ainfo->storage) { case LLVMArgVtypeInReg: for (j = 0; j < 2; ++j) { if (ainfo->pair_storage [j] == LLVMArgInIReg) vret_arg_pindex ++; } break; default: vret_arg_pindex ++; } } cinfo->vret_arg_pindex = vret_arg_pindex; } if (vretaddr && vret_arg_pindex == pindex) param_types [pindex ++] = IntPtrType (); if (sig->hasthis) { cinfo->this_arg_pindex = pindex; param_types [pindex ++] = ThisType (); cinfo->args [0].pindex = cinfo->this_arg_pindex; } if (vretaddr && vret_arg_pindex == pindex) param_types [pindex ++] = IntPtrType (); for (i = 0; i < sig->param_count; ++i) { LLVMArgInfo *ainfo = &cinfo->args [i + sig->hasthis]; if (vretaddr && vret_arg_pindex == pindex) param_types [pindex ++] = IntPtrType (); ainfo->pindex = pindex; switch (ainfo->storage) { case LLVMArgVtypeInReg: for (j = 0; j < 2; ++j) { switch (ainfo->pair_storage [j]) { case LLVMArgInIReg: param_types [pindex ++] = LLVMIntType (sizeof (gpointer) * 8); break; case LLVMArgNone: break; default: g_assert_not_reached (); } } break; case LLVMArgVtypeByVal: param_types [pindex] = type_to_llvm_arg_type (ctx, ainfo->type); if (!ctx_ok (ctx)) break; param_types [pindex] = LLVMPointerType (param_types [pindex], 0); pindex ++; break; case LLVMArgAsIArgs: if (ainfo->esize == 8) param_types [pindex] = LLVMArrayType (LLVMInt64Type (), ainfo->nslots); else param_types [pindex] = LLVMArrayType (IntPtrType (), ainfo->nslots); pindex ++; break; case LLVMArgVtypeByRef: param_types [pindex] = type_to_llvm_arg_type (ctx, ainfo->type); if (!ctx_ok (ctx)) break; param_types [pindex] = LLVMPointerType (param_types [pindex], 0); pindex ++; break; case LLVMArgAsFpArgs: { int j; /* Emit dummy fp arguments if needed so the rest is passed on the stack */ for (j = 0; j < ainfo->ndummy_fpargs; ++j) param_types [pindex ++] = LLVMDoubleType (); for (j = 0; j < ainfo->nslots; ++j) param_types [pindex ++] = ainfo->esize == 8 ? LLVMDoubleType () : LLVMFloatType (); break; } case LLVMArgVtypeAsScalar: g_assert_not_reached (); break; case LLVMArgGsharedvtFixed: case LLVMArgGsharedvtFixedVtype: param_types [pindex ++] = LLVMPointerType (type_to_llvm_arg_type (ctx, ainfo->type), 0); break; case LLVMArgGsharedvtVariable: param_types [pindex ++] = LLVMPointerType (IntPtrType (), 0); break; default: param_types [pindex ++] = type_to_llvm_arg_type (ctx, ainfo->type); break; } } if (!ctx_ok (ctx)) { g_free (param_types); return NULL; } if (vretaddr && vret_arg_pindex == pindex) param_types [pindex ++] = IntPtrType (); if (ctx->llvm_only && cinfo->rgctx_arg) { /* Pass the rgctx as the last argument */ cinfo->rgctx_arg_pindex = pindex; param_types [pindex] = ctx->module->ptr_type; pindex ++; } res = LLVMFunctionType (ret_type, param_types, pindex, FALSE); g_free (param_types); return res; } static LLVMTypeRef sig_to_llvm_sig (EmitContext *ctx, MonoMethodSignature *sig) { return sig_to_llvm_sig_full (ctx, sig, NULL); } /* * LLVMFunctionType1: * * Create an LLVM function type from the arguments. */ static G_GNUC_UNUSED LLVMTypeRef LLVMFunctionType0 (LLVMTypeRef ReturnType, int IsVarArg) { return LLVMFunctionType (ReturnType, NULL, 0, IsVarArg); } /* * LLVMFunctionType1: * * Create an LLVM function type from the arguments. */ static G_GNUC_UNUSED LLVMTypeRef LLVMFunctionType1 (LLVMTypeRef ReturnType, LLVMTypeRef ParamType1, int IsVarArg) { LLVMTypeRef param_types [1]; param_types [0] = ParamType1; return LLVMFunctionType (ReturnType, param_types, 1, IsVarArg); } /* * LLVMFunctionType2: * * Create an LLVM function type from the arguments. */ static G_GNUC_UNUSED LLVMTypeRef LLVMFunctionType2 (LLVMTypeRef ReturnType, LLVMTypeRef ParamType1, LLVMTypeRef ParamType2, int IsVarArg) { LLVMTypeRef param_types [2]; param_types [0] = ParamType1; param_types [1] = ParamType2; return LLVMFunctionType (ReturnType, param_types, 2, IsVarArg); } /* * LLVMFunctionType3: * * Create an LLVM function type from the arguments. */ static G_GNUC_UNUSED LLVMTypeRef LLVMFunctionType3 (LLVMTypeRef ReturnType, LLVMTypeRef ParamType1, LLVMTypeRef ParamType2, LLVMTypeRef ParamType3, int IsVarArg) { LLVMTypeRef param_types [3]; param_types [0] = ParamType1; param_types [1] = ParamType2; param_types [2] = ParamType3; return LLVMFunctionType (ReturnType, param_types, 3, IsVarArg); } static G_GNUC_UNUSED LLVMTypeRef LLVMFunctionType5 (LLVMTypeRef ReturnType, LLVMTypeRef ParamType1, LLVMTypeRef ParamType2, LLVMTypeRef ParamType3, LLVMTypeRef ParamType4, LLVMTypeRef ParamType5, int IsVarArg) { LLVMTypeRef param_types [5]; param_types [0] = ParamType1; param_types [1] = ParamType2; param_types [2] = ParamType3; param_types [3] = ParamType4; param_types [4] = ParamType5; return LLVMFunctionType (ReturnType, param_types, 5, IsVarArg); } /* * create_builder: * * Create an LLVM builder and remember it so it can be freed later. */ static LLVMBuilderRef create_builder (EmitContext *ctx) { LLVMBuilderRef builder = LLVMCreateBuilder (); ctx->builders = g_slist_prepend_mempool (ctx->cfg->mempool, ctx->builders, builder); return builder; } static char* get_aotconst_name (MonoJumpInfoType type, gconstpointer data, int got_offset) { char *name; switch (type) { case MONO_PATCH_INFO_INTERNAL_METHOD: name = g_strdup_printf ("jit_icall_%s", data); break; case MONO_PATCH_INFO_RGCTX_SLOT_INDEX: { MonoJumpInfoRgctxEntry *entry = (MonoJumpInfoRgctxEntry*)data; name = g_strdup_printf ("RGCTX_SLOT_INDEX_%s", mono_rgctx_info_type_to_str (entry->info_type)); break; } default: name = g_strdup_printf ("%s_%d", mono_ji_type_to_string (type), got_offset); break; } return name; } static LLVMValueRef get_aotconst_typed (EmitContext *ctx, MonoJumpInfoType type, gconstpointer data, LLVMTypeRef llvm_type) { MonoCompile *cfg; guint32 got_offset; LLVMValueRef indexes [2]; LLVMValueRef got_entry_addr, load; LLVMBuilderRef builder = ctx->builder; char *name = NULL; cfg = ctx->cfg; MonoJumpInfo tmp_ji; tmp_ji.type = type; tmp_ji.data.target = data; MonoJumpInfo *ji = mono_aot_patch_info_dup (&tmp_ji); ji->next = cfg->patch_info; cfg->patch_info = ji; got_offset = mono_aot_get_got_offset (cfg->patch_info); ctx->module->max_got_offset = MAX (ctx->module->max_got_offset, got_offset); /* * If the got slot is shared, it means its initialized when the aot image is loaded, so we don't need to * explicitly initialize it. */ if (!mono_aot_is_shared_got_offset (got_offset)) { //mono_print_ji (ji); //printf ("\n"); ctx->has_got_access = TRUE; } indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); indexes [1] = LLVMConstInt (LLVMInt32Type (), (gssize)got_offset, FALSE); got_entry_addr = LLVMBuildGEP (builder, ctx->module->got_var, indexes, 2, ""); name = get_aotconst_name (type, data, got_offset); if (llvm_type) { load = LLVMBuildLoad (builder, got_entry_addr, ""); load = convert (ctx, load, llvm_type); LLVMSetValueName (load, name ? name : ""); } else { load = LLVMBuildLoad (builder, got_entry_addr, name ? name : ""); } g_free (name); //set_invariant_load_flag (load); return load; } static LLVMValueRef get_aotconst (EmitContext *ctx, MonoJumpInfoType type, gconstpointer data) { return get_aotconst_typed (ctx, type, data, NULL); } static LLVMValueRef get_callee (EmitContext *ctx, LLVMTypeRef llvm_sig, MonoJumpInfoType type, gconstpointer data) { LLVMValueRef callee; char *callee_name; if (ctx->llvm_only) { callee_name = mono_aot_get_direct_call_symbol (type, data); if (callee_name) { /* Directly callable */ // FIXME: Locking callee = (LLVMValueRef)g_hash_table_lookup (ctx->module->direct_callables, callee_name); if (!callee) { callee = LLVMAddFunction (ctx->lmodule, callee_name, llvm_sig); LLVMSetVisibility (callee, LLVMHiddenVisibility); g_hash_table_insert (ctx->module->direct_callables, (char*)callee_name, callee); } else { /* LLVMTypeRef's are uniqued */ if (LLVMGetElementType (LLVMTypeOf (callee)) != llvm_sig) return LLVMConstBitCast (callee, LLVMPointerType (llvm_sig, 0)); g_free (callee_name); } return callee; } /* * Calls are made through the GOT. */ return get_aotconst_typed (ctx, type, data, LLVMPointerType (llvm_sig, 0)); } else { MonoJumpInfo *ji = NULL; callee_name = mono_aot_get_plt_symbol (type, data); if (!callee_name) return NULL; if (ctx->cfg->compile_aot) /* Add a patch so referenced wrappers can be compiled in full aot mode */ mono_add_patch_info (ctx->cfg, 0, type, data); // FIXME: Locking callee = (LLVMValueRef)g_hash_table_lookup (ctx->module->plt_entries, callee_name); if (!callee) { callee = LLVMAddFunction (ctx->lmodule, callee_name, llvm_sig); LLVMSetVisibility (callee, LLVMHiddenVisibility); g_hash_table_insert (ctx->module->plt_entries, (char*)callee_name, callee); } if (ctx->cfg->compile_aot) { ji = g_new0 (MonoJumpInfo, 1); ji->type = type; ji->data.target = data; g_hash_table_insert (ctx->module->plt_entries_ji, ji, callee); } return callee; } } static LLVMValueRef emit_jit_callee (EmitContext *ctx, const char *name, LLVMTypeRef llvm_sig, gpointer target) { #if LLVM_API_VERSION > 100 LLVMValueRef tramp_var = LLVMAddGlobal (ctx->lmodule, LLVMPointerType (llvm_sig, 0), name); LLVMSetInitializer (tramp_var, LLVMConstIntToPtr (LLVMConstInt (LLVMInt64Type (), (guint64)(size_t)target, FALSE), LLVMPointerType (llvm_sig, 0))); LLVMSetLinkage (tramp_var, LLVMExternalLinkage); LLVMValueRef callee = LLVMBuildLoad (ctx->builder, tramp_var, ""); return callee; #else LLVMValueRef callee = LLVMAddFunction (ctx->lmodule, "", llvm_sig); LLVMAddGlobalMapping (ctx->module->ee, callee, target); return callee; #endif } static int get_handler_clause (MonoCompile *cfg, MonoBasicBlock *bb) { MonoMethodHeader *header = cfg->header; MonoExceptionClause *clause; int i; /* Directly */ if (bb->region != -1 && MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY)) return (bb->region >> 8) - 1; /* Indirectly */ for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if (MONO_OFFSET_IN_CLAUSE (clause, bb->real_offset) && clause->flags == MONO_EXCEPTION_CLAUSE_NONE) return i; } return -1; } static MonoExceptionClause * get_most_deep_clause (MonoCompile *cfg, EmitContext *ctx, MonoBasicBlock *bb) { if (bb == cfg->bb_init) return NULL; // Since they're sorted by nesting we just need // the first one that the bb is a member of for (int i = 0; i < cfg->header->num_clauses; i++) { MonoExceptionClause *curr = &cfg->header->clauses [i]; if (MONO_OFFSET_IN_CLAUSE (curr, bb->real_offset)) return curr; } return NULL; } static void set_metadata_flag (LLVMValueRef v, const char *flag_name) { LLVMValueRef md_arg; int md_kind; md_kind = LLVMGetMDKindID (flag_name, strlen (flag_name)); md_arg = LLVMMDString ("mono", 4); LLVMSetMetadata (v, md_kind, LLVMMDNode (&md_arg, 1)); } static void set_invariant_load_flag (LLVMValueRef v) { LLVMValueRef md_arg; int md_kind; const char *flag_name; // FIXME: Cache this flag_name = "invariant.load"; md_kind = LLVMGetMDKindID (flag_name, strlen (flag_name)); md_arg = LLVMMDString ("", strlen ("")); LLVMSetMetadata (v, md_kind, LLVMMDNode (&md_arg, 1)); } /* * emit_call: * * Emit an LLVM call or invoke instruction depending on whenever the call is inside * a try region. */ static LLVMValueRef emit_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, LLVMValueRef callee, LLVMValueRef *args, int pindex) { MonoCompile *cfg = ctx->cfg; LLVMValueRef lcall = NULL; LLVMBuilderRef builder = *builder_ref; MonoExceptionClause *clause; if (ctx->llvm_only) { clause = get_most_deep_clause (cfg, ctx, bb); if (clause) { g_assert (clause->flags == MONO_EXCEPTION_CLAUSE_NONE || clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY || clause->flags == MONO_EXCEPTION_CLAUSE_FAULT); /* * Have to use an invoke instead of a call, branching to the * handler bblock of the clause containing this bblock. */ intptr_t key = CLAUSE_END(clause); LLVMBasicBlockRef lpad_bb = (LLVMBasicBlockRef)g_hash_table_lookup (ctx->exc_meta, (gconstpointer)key); // FIXME: Find the one that has the lowest end bound for the right start address // FIXME: Finally + nesting if (lpad_bb) { LLVMBasicBlockRef noex_bb = gen_bb (ctx, "CALL_NOEX_BB"); /* Use an invoke */ lcall = LLVMBuildInvoke (builder, callee, args, pindex, noex_bb, lpad_bb, ""); builder = ctx->builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); ctx->bblocks [bb->block_num].end_bblock = noex_bb; } } } else { int clause_index = get_handler_clause (cfg, bb); if (clause_index != -1) { MonoMethodHeader *header = cfg->header; MonoExceptionClause *ec = &header->clauses [clause_index]; MonoBasicBlock *tblock; LLVMBasicBlockRef ex_bb, noex_bb; /* * Have to use an invoke instead of a call, branching to the * handler bblock of the clause containing this bblock. */ g_assert (ec->flags == MONO_EXCEPTION_CLAUSE_NONE || ec->flags == MONO_EXCEPTION_CLAUSE_FINALLY || ec->flags == MONO_EXCEPTION_CLAUSE_FAULT); tblock = cfg->cil_offset_to_bb [ec->handler_offset]; g_assert (tblock); ctx->bblocks [tblock->block_num].invoke_target = TRUE; ex_bb = get_bb (ctx, tblock); noex_bb = gen_bb (ctx, "NOEX_BB"); /* Use an invoke */ lcall = LLVMBuildInvoke (builder, callee, args, pindex, noex_bb, ex_bb, ""); builder = ctx->builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); ctx->bblocks [bb->block_num].end_bblock = noex_bb; } } if (!lcall) { lcall = LLVMBuildCall (builder, callee, args, pindex, ""); ctx->builder = builder; } if (builder_ref) *builder_ref = ctx->builder; return lcall; } static LLVMValueRef emit_load_general (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef addr, LLVMValueRef base, const char *name, gboolean is_faulting, BarrierKind barrier) { const char *intrins_name; LLVMValueRef args [16], res; LLVMTypeRef addr_type; gboolean use_intrinsics = TRUE; #if LLVM_API_VERSION > 100 if (is_faulting && bb->region != -1 && !ctx->cfg->llvm_only) { /* The llvm.mono.load/store intrinsics are not supported by this llvm version, emit an explicit null check instead */ LLVMValueRef cmp; cmp = LLVMBuildICmp (*builder_ref, LLVMIntEQ, base, LLVMConstNull (LLVMTypeOf (base)), ""); emit_cond_system_exception (ctx, bb, "NullReferenceException", cmp); *builder_ref = ctx->builder; use_intrinsics = FALSE; } #endif if (is_faulting && bb->region != -1 && !ctx->cfg->llvm_only && use_intrinsics) { LLVMAtomicOrdering ordering; switch (barrier) { case LLVM_BARRIER_NONE: ordering = LLVMAtomicOrderingNotAtomic; break; case LLVM_BARRIER_ACQ: ordering = LLVMAtomicOrderingAcquire; break; case LLVM_BARRIER_SEQ: ordering = LLVMAtomicOrderingSequentiallyConsistent; break; default: g_assert_not_reached (); break; } /* * We handle loads which can fault by calling a mono specific intrinsic * using an invoke, so they are handled properly inside try blocks. * We can't use this outside clauses, since LLVM optimizes intrinsics which * are marked with IntrReadArgMem. */ switch (size) { case 1: intrins_name = "llvm.mono.load.i8.p0i8"; break; case 2: intrins_name = "llvm.mono.load.i16.p0i16"; break; case 4: intrins_name = "llvm.mono.load.i32.p0i32"; break; case 8: intrins_name = "llvm.mono.load.i64.p0i64"; break; default: g_assert_not_reached (); } addr_type = LLVMTypeOf (addr); if (addr_type == LLVMPointerType (LLVMDoubleType (), 0) || addr_type == LLVMPointerType (LLVMFloatType (), 0)) addr = LLVMBuildBitCast (*builder_ref, addr, LLVMPointerType (LLVMIntType (size * 8), 0), ""); args [0] = addr; args [1] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); args [2] = LLVMConstInt (LLVMInt1Type (), TRUE, FALSE); args [3] = LLVMConstInt (LLVMInt32Type (), ordering, FALSE); res = emit_call (ctx, bb, builder_ref, get_intrinsic (ctx, intrins_name), args, 4); if (addr_type == LLVMPointerType (LLVMDoubleType (), 0)) res = LLVMBuildBitCast (*builder_ref, res, LLVMDoubleType (), ""); else if (addr_type == LLVMPointerType (LLVMFloatType (), 0)) res = LLVMBuildBitCast (*builder_ref, res, LLVMFloatType (), ""); return res; } else { LLVMValueRef res; /* * We emit volatile loads for loads which can fault, because otherwise * LLVM will generate invalid code when encountering a load from a * NULL address. */ if (barrier != LLVM_BARRIER_NONE) res = mono_llvm_build_atomic_load (*builder_ref, addr, name, is_faulting, size, barrier); else res = mono_llvm_build_load (*builder_ref, addr, name, is_faulting); /* Mark it with a custom metadata */ /* if (is_faulting) set_metadata_flag (res, "mono.faulting.load"); */ return res; } } static LLVMValueRef emit_load (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef addr, const char *name, gboolean is_faulting) { return emit_load_general (ctx, bb, builder_ref, size, addr, addr, name, is_faulting, LLVM_BARRIER_NONE); } static void emit_store_general (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, LLVMValueRef base, gboolean is_faulting, BarrierKind barrier) { const char *intrins_name; LLVMValueRef args [16]; gboolean use_intrinsics = TRUE; #if LLVM_API_VERSION > 100 if (is_faulting && bb->region != -1 && !ctx->cfg->llvm_only) { /* The llvm.mono.load/store intrinsics are not supported by this llvm version, emit an explicit null check instead */ LLVMValueRef cmp = LLVMBuildICmp (*builder_ref, LLVMIntEQ, base, LLVMConstNull (LLVMTypeOf (base)), ""); emit_cond_system_exception (ctx, bb, "NullReferenceException", cmp); *builder_ref = ctx->builder; use_intrinsics = FALSE; } #endif if (is_faulting && bb->region != -1 && !ctx->cfg->llvm_only && use_intrinsics) { LLVMAtomicOrdering ordering; switch (barrier) { case LLVM_BARRIER_NONE: ordering = LLVMAtomicOrderingNotAtomic; break; case LLVM_BARRIER_REL: ordering = LLVMAtomicOrderingRelease; break; case LLVM_BARRIER_SEQ: ordering = LLVMAtomicOrderingSequentiallyConsistent; break; default: g_assert_not_reached (); break; } switch (size) { case 1: intrins_name = "llvm.mono.store.i8.p0i8"; break; case 2: intrins_name = "llvm.mono.store.i16.p0i16"; break; case 4: intrins_name = "llvm.mono.store.i32.p0i32"; break; case 8: intrins_name = "llvm.mono.store.i64.p0i64"; break; default: g_assert_not_reached (); } if (LLVMTypeOf (value) == LLVMDoubleType () || LLVMTypeOf (value) == LLVMFloatType ()) { value = LLVMBuildBitCast (*builder_ref, value, LLVMIntType (size * 8), ""); addr = LLVMBuildBitCast (*builder_ref, addr, LLVMPointerType (LLVMIntType (size * 8), 0), ""); } args [0] = value; args [1] = addr; args [2] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); args [3] = LLVMConstInt (LLVMInt1Type (), TRUE, FALSE); args [4] = LLVMConstInt (LLVMInt32Type (), ordering, FALSE); emit_call (ctx, bb, builder_ref, get_intrinsic (ctx, intrins_name), args, 5); } else { if (barrier != LLVM_BARRIER_NONE) mono_llvm_build_aligned_store (*builder_ref, value, addr, barrier, size); else mono_llvm_build_store (*builder_ref, value, addr, is_faulting, barrier); } } static void emit_store (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, LLVMValueRef base, gboolean is_faulting) { emit_store_general (ctx, bb, builder_ref, size, value, addr, base, is_faulting, LLVM_BARRIER_NONE); } /* * emit_cond_system_exception: * * Emit code to throw the exception EXC_TYPE if the condition CMP is false. * Might set the ctx exception. */ static void emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *exc_type, LLVMValueRef cmp) { LLVMBasicBlockRef ex_bb, ex2_bb = NULL, noex_bb; LLVMBuilderRef builder; MonoClass *exc_class; LLVMValueRef args [2]; LLVMValueRef callee; gboolean no_pc = FALSE; if (IS_TARGET_AMD64) /* Some platforms don't require the pc argument */ no_pc = TRUE; ex_bb = gen_bb (ctx, "EX_BB"); if (ctx->llvm_only) ex2_bb = gen_bb (ctx, "EX2_BB"); noex_bb = gen_bb (ctx, "NOEX_BB"); LLVMBuildCondBr (ctx->builder, cmp, ex_bb, noex_bb); exc_class = mono_class_load_from_name (mono_get_corlib (), "System", exc_type); /* Emit exception throwing code */ ctx->builder = builder = create_builder (ctx); LLVMPositionBuilderAtEnd (builder, ex_bb); if (ctx->cfg->llvm_only) { static LLVMTypeRef sig; if (!sig) sig = LLVMFunctionType1 (LLVMVoidType (), LLVMInt32Type (), FALSE); callee = get_callee (ctx, sig, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_llvm_throw_corlib_exception"); LLVMBuildBr (builder, ex2_bb); ctx->builder = builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, ex2_bb); args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token - MONO_TOKEN_TYPE_DEF, FALSE); emit_call (ctx, bb, &builder, callee, args, 1); LLVMBuildUnreachable (builder); ctx->builder = builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); ctx->bblocks [bb->block_num].end_bblock = noex_bb; ctx->ex_index ++; return; } callee = ctx->module->throw_corlib_exception; if (!callee) { LLVMTypeRef sig; const char *icall_name; if (no_pc) sig = LLVMFunctionType1 (LLVMVoidType (), LLVMInt32Type (), FALSE); else sig = LLVMFunctionType2 (LLVMVoidType (), LLVMInt32Type (), LLVMPointerType (LLVMInt8Type (), 0), FALSE); icall_name = "llvm_throw_corlib_exception_abs_trampoline"; if (ctx->cfg->compile_aot) { callee = get_callee (ctx, sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name); } else { /* * Differences between the LLVM/non-LLVM throw corlib exception trampoline: * - On x86, LLVM generated code doesn't push the arguments * - The trampoline takes the throw address as an arguments, not a pc offset. */ gpointer target = resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name); callee = emit_jit_callee (ctx, "llvm_throw_corlib_exception_trampoline", sig, target); #if LLVM_API_VERSION > 100 /* * Make sure that ex_bb starts with the invoke, so the block address points to it, and not to the load * added by emit_jit_callee (). */ ex2_bb = gen_bb (ctx, "EX2_BB"); LLVMBuildBr (builder, ex2_bb); ex_bb = ex2_bb; ctx->builder = builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, ex2_bb); #else mono_memory_barrier (); ctx->module->throw_corlib_exception = callee; #endif } } args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token - MONO_TOKEN_TYPE_DEF, FALSE); /* * The LLVM mono branch contains changes so a block address can be passed as an * argument to a call. */ if (no_pc) { emit_call (ctx, bb, &builder, callee, args, 1); } else { args [1] = LLVMBlockAddress (ctx->lmethod, ex_bb); emit_call (ctx, bb, &builder, callee, args, 2); } LLVMBuildUnreachable (builder); ctx->builder = builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); ctx->bblocks [bb->block_num].end_bblock = noex_bb; ctx->ex_index ++; return; } /* * emit_args_to_vtype: * * Emit code to store the vtype in the arguments args to the address ADDRESS. */ static void emit_args_to_vtype (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMValueRef address, LLVMArgInfo *ainfo, LLVMValueRef *args) { int j, size, nslots; size = mono_class_value_size (mono_class_from_mono_type (t), NULL); if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) { address = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (LLVMInt8Type (), 0), ""); } if (ainfo->storage == LLVMArgAsFpArgs) nslots = ainfo->nslots; else nslots = 2; for (j = 0; j < nslots; ++j) { LLVMValueRef index [2], addr, daddr; int part_size = size > sizeof (gpointer) ? sizeof (gpointer) : size; LLVMTypeRef part_type; while (part_size != 1 && part_size != 2 && part_size != 4 && part_size < 8) part_size ++; if (ainfo->pair_storage [j] == LLVMArgNone) continue; switch (ainfo->pair_storage [j]) { case LLVMArgInIReg: { part_type = LLVMIntType (part_size * 8); if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) { index [0] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE); addr = LLVMBuildGEP (builder, address, index, 1, ""); } else { daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (IntPtrType (), 0), ""); index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE); addr = LLVMBuildGEP (builder, daddr, index, 1, ""); } LLVMBuildStore (builder, convert (ctx, args [j], part_type), LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (part_type, 0), "")); break; } case LLVMArgInFPReg: { LLVMTypeRef arg_type; if (ainfo->esize == 8) arg_type = LLVMDoubleType (); else arg_type = LLVMFloatType (); index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE); daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (arg_type, 0), ""); addr = LLVMBuildGEP (builder, daddr, index, 1, ""); LLVMBuildStore (builder, args [j], addr); break; } case LLVMArgNone: break; default: g_assert_not_reached (); } size -= sizeof (gpointer); } } /* * emit_vtype_to_args: * * Emit code to load a vtype at address ADDRESS into scalar arguments. Store the arguments * into ARGS, and the number of arguments into NARGS. */ static void emit_vtype_to_args (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMValueRef address, LLVMArgInfo *ainfo, LLVMValueRef *args, guint32 *nargs) { int pindex = 0; int j, size, nslots; LLVMTypeRef arg_type; size = get_vtype_size (t); if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) address = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (LLVMInt8Type (), 0), ""); if (ainfo->storage == LLVMArgAsFpArgs) nslots = ainfo->nslots; else nslots = 2; for (j = 0; j < nslots; ++j) { LLVMValueRef index [2], addr, daddr; int partsize = size > sizeof (gpointer) ? sizeof (gpointer) : size; if (ainfo->pair_storage [j] == LLVMArgNone) continue; switch (ainfo->pair_storage [j]) { case LLVMArgInIReg: if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) { index [0] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE); addr = LLVMBuildGEP (builder, address, index, 1, ""); } else { daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (IntPtrType (), 0), ""); index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE); addr = LLVMBuildGEP (builder, daddr, index, 1, ""); } args [pindex ++] = convert (ctx, LLVMBuildLoad (builder, LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (LLVMIntType (partsize * 8), 0), ""), ""), IntPtrType ()); break; case LLVMArgInFPReg: if (ainfo->esize == 8) arg_type = LLVMDoubleType (); else arg_type = LLVMFloatType (); daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (arg_type, 0), ""); index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE); addr = LLVMBuildGEP (builder, daddr, index, 1, ""); args [pindex ++] = LLVMBuildLoad (builder, addr, ""); break; case LLVMArgNone: break; default: g_assert_not_reached (); } size -= sizeof (gpointer); } *nargs = pindex; } static LLVMValueRef build_alloca_llvm_type_name (EmitContext *ctx, LLVMTypeRef t, int align, const char *name) { /* * Have to place all alloca's at the end of the entry bb, since otherwise they would * get executed every time control reaches them. */ LLVMPositionBuilder (ctx->alloca_builder, get_bb (ctx, ctx->cfg->bb_entry), ctx->last_alloca); ctx->last_alloca = mono_llvm_build_alloca (ctx->alloca_builder, t, NULL, align, name); return ctx->last_alloca; } static LLVMValueRef build_alloca_llvm_type (EmitContext *ctx, LLVMTypeRef t, int align) { return build_alloca_llvm_type_name (ctx, t, align, ""); } static LLVMValueRef build_alloca (EmitContext *ctx, MonoType *t) { MonoClass *k = mono_class_from_mono_type (t); int align; g_assert (!mini_is_gsharedvt_variable_type (t)); if (MONO_CLASS_IS_SIMD (ctx->cfg, k)) align = 16; else align = mono_class_min_align (k); /* Sometimes align is not a power of 2 */ while (mono_is_power_of_two (align) == -1) align ++; return build_alloca_llvm_type (ctx, type_to_llvm_type (ctx, t), align); } static LLVMValueRef emit_gsharedvt_ldaddr (EmitContext *ctx, int vreg) { /* * gsharedvt local. * Compute the address of the local as gsharedvt_locals_var + gsharedvt_info_var->locals_offsets [idx]. */ MonoCompile *cfg = ctx->cfg; LLVMBuilderRef builder = ctx->builder; LLVMValueRef offset, offset_var; LLVMValueRef info_var = ctx->values [cfg->gsharedvt_info_var->dreg]; LLVMValueRef locals_var = ctx->values [cfg->gsharedvt_locals_var->dreg]; LLVMValueRef ptr; char *name; g_assert (info_var); g_assert (locals_var); int idx = cfg->gsharedvt_vreg_to_idx [vreg] - 1; offset = LLVMConstInt (LLVMInt32Type (), MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)), FALSE); ptr = LLVMBuildAdd (builder, convert (ctx, info_var, IntPtrType ()), convert (ctx, offset, IntPtrType ()), ""); name = g_strdup_printf ("gsharedvt_local_%d_offset", vreg); offset_var = LLVMBuildLoad (builder, convert (ctx, ptr, LLVMPointerType (LLVMInt32Type (), 0)), name); return LLVMBuildAdd (builder, convert (ctx, locals_var, IntPtrType ()), convert (ctx, offset_var, IntPtrType ()), ""); } /* * Put the global into the 'llvm.used' array to prevent it from being optimized away. */ static void mark_as_used (MonoLLVMModule *module, LLVMValueRef global) { if (!module->used) module->used = g_ptr_array_sized_new (16); g_ptr_array_add (module->used, global); } static void emit_llvm_used (MonoLLVMModule *module) { LLVMModuleRef lmodule = module->lmodule; LLVMTypeRef used_type; LLVMValueRef used, *used_elem; int i; if (!module->used) return; used_type = LLVMArrayType (LLVMPointerType (LLVMInt8Type (), 0), module->used->len); used = LLVMAddGlobal (lmodule, used_type, "llvm.used"); used_elem = g_new0 (LLVMValueRef, module->used->len); for (i = 0; i < module->used->len; ++i) used_elem [i] = LLVMConstBitCast ((LLVMValueRef)g_ptr_array_index (module->used, i), LLVMPointerType (LLVMInt8Type (), 0)); LLVMSetInitializer (used, LLVMConstArray (LLVMPointerType (LLVMInt8Type (), 0), used_elem, module->used->len)); LLVMSetLinkage (used, LLVMAppendingLinkage); LLVMSetSection (used, "llvm.metadata"); } /* * emit_get_method: * * Emit a function mapping method indexes to their code */ static void emit_get_method (MonoLLVMModule *module) { LLVMModuleRef lmodule = module->lmodule; LLVMValueRef func, switch_ins, m; LLVMBasicBlockRef entry_bb, fail_bb, bb, code_start_bb, code_end_bb; LLVMBasicBlockRef *bbs; LLVMTypeRef rtype; LLVMBuilderRef builder = LLVMCreateBuilder (); char *name; int i; /* * Emit a switch statement. Emitting a table of function addresses is smaller/faster, * but generating code seems safer. */ rtype = LLVMPointerType (LLVMInt8Type (), 0); func = LLVMAddFunction (lmodule, module->get_method_symbol, LLVMFunctionType1 (rtype, LLVMInt32Type (), FALSE)); LLVMSetLinkage (func, LLVMExternalLinkage); LLVMSetVisibility (func, LLVMHiddenVisibility); mono_llvm_add_func_attr (func, LLVM_ATTR_NO_UNWIND); module->get_method = func; entry_bb = LLVMAppendBasicBlock (func, "ENTRY"); /* * Return llvm_code_start/llvm_code_end when called with -1/-2. * Hopefully, the toolchain doesn't reorder these functions. If it does, * then we will have to find another solution. */ name = g_strdup_printf ("BB_CODE_START"); code_start_bb = LLVMAppendBasicBlock (func, name); g_free (name); LLVMPositionBuilderAtEnd (builder, code_start_bb); LLVMBuildRet (builder, LLVMBuildBitCast (builder, module->code_start, rtype, "")); name = g_strdup_printf ("BB_CODE_END"); code_end_bb = LLVMAppendBasicBlock (func, name); g_free (name); LLVMPositionBuilderAtEnd (builder, code_end_bb); LLVMBuildRet (builder, LLVMBuildBitCast (builder, module->code_end, rtype, "")); bbs = g_new0 (LLVMBasicBlockRef, module->max_method_idx + 1); for (i = 0; i < module->max_method_idx + 1; ++i) { name = g_strdup_printf ("BB_%d", i); bb = LLVMAppendBasicBlock (func, name); g_free (name); bbs [i] = bb; LLVMPositionBuilderAtEnd (builder, bb); m = (LLVMValueRef)g_hash_table_lookup (module->idx_to_lmethod, GINT_TO_POINTER (i)); if (m) LLVMBuildRet (builder, LLVMBuildBitCast (builder, m, rtype, "")); else LLVMBuildRet (builder, LLVMConstNull (rtype)); } fail_bb = LLVMAppendBasicBlock (func, "FAIL"); LLVMPositionBuilderAtEnd (builder, fail_bb); LLVMBuildRet (builder, LLVMConstNull (rtype)); LLVMPositionBuilderAtEnd (builder, entry_bb); switch_ins = LLVMBuildSwitch (builder, LLVMGetParam (func, 0), fail_bb, 0); LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), -1, FALSE), code_start_bb); LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), -2, FALSE), code_end_bb); for (i = 0; i < module->max_method_idx + 1; ++i) { LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), i, FALSE), bbs [i]); } mark_as_used (module, func); LLVMDisposeBuilder (builder); } /* * emit_get_unbox_tramp: * * Emit a function mapping method indexes to their unbox trampoline */ static void emit_get_unbox_tramp (MonoLLVMModule *module) { LLVMModuleRef lmodule = module->lmodule; LLVMValueRef func, switch_ins, m; LLVMBasicBlockRef entry_bb, fail_bb, bb; LLVMBasicBlockRef *bbs; LLVMTypeRef rtype; LLVMBuilderRef builder = LLVMCreateBuilder (); char *name; int i; /* Similar to emit_get_method () */ rtype = LLVMPointerType (LLVMInt8Type (), 0); func = LLVMAddFunction (lmodule, module->get_unbox_tramp_symbol, LLVMFunctionType1 (rtype, LLVMInt32Type (), FALSE)); LLVMSetLinkage (func, LLVMExternalLinkage); LLVMSetVisibility (func, LLVMHiddenVisibility); mono_llvm_add_func_attr (func, LLVM_ATTR_NO_UNWIND); module->get_unbox_tramp = func; entry_bb = LLVMAppendBasicBlock (func, "ENTRY"); bbs = g_new0 (LLVMBasicBlockRef, module->max_method_idx + 1); for (i = 0; i < module->max_method_idx + 1; ++i) { m = (LLVMValueRef)g_hash_table_lookup (module->idx_to_unbox_tramp, GINT_TO_POINTER (i)); if (!m) continue; name = g_strdup_printf ("BB_%d", i); bb = LLVMAppendBasicBlock (func, name); g_free (name); bbs [i] = bb; LLVMPositionBuilderAtEnd (builder, bb); LLVMBuildRet (builder, LLVMBuildBitCast (builder, m, rtype, "")); } fail_bb = LLVMAppendBasicBlock (func, "FAIL"); LLVMPositionBuilderAtEnd (builder, fail_bb); LLVMBuildRet (builder, LLVMConstNull (rtype)); LLVMPositionBuilderAtEnd (builder, entry_bb); switch_ins = LLVMBuildSwitch (builder, LLVMGetParam (func, 0), fail_bb, 0); for (i = 0; i < module->max_method_idx + 1; ++i) { m = (LLVMValueRef)g_hash_table_lookup (module->idx_to_unbox_tramp, GINT_TO_POINTER (i)); if (!m) continue; LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), i, FALSE), bbs [i]); } mark_as_used (module, func); LLVMDisposeBuilder (builder); } /* Add a function to mark the beginning of LLVM code */ static void emit_llvm_code_start (MonoLLVMModule *module) { LLVMModuleRef lmodule = module->lmodule; LLVMValueRef func; LLVMBasicBlockRef entry_bb; LLVMBuilderRef builder; func = LLVMAddFunction (lmodule, "llvm_code_start", LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE)); LLVMSetLinkage (func, LLVMInternalLinkage); mono_llvm_add_func_attr (func, LLVM_ATTR_NO_UNWIND); module->code_start = func; entry_bb = LLVMAppendBasicBlock (func, "ENTRY"); builder = LLVMCreateBuilder (); LLVMPositionBuilderAtEnd (builder, entry_bb); LLVMBuildRetVoid (builder); LLVMDisposeBuilder (builder); } static LLVMValueRef emit_init_icall_wrapper (MonoLLVMModule *module, const char *name, const char *icall_name, int subtype) { LLVMModuleRef lmodule = module->lmodule; LLVMValueRef func, indexes [2], got_entry_addr, args [16], callee; LLVMBasicBlockRef entry_bb; LLVMBuilderRef builder; LLVMTypeRef sig; MonoJumpInfo *ji; int got_offset; switch (subtype) { case 0: func = LLVMAddFunction (lmodule, name, LLVMFunctionType1 (LLVMVoidType (), LLVMInt32Type (), FALSE)); sig = LLVMFunctionType2 (LLVMVoidType (), IntPtrType (), LLVMInt32Type (), FALSE); break; case 1: case 3: /* mrgctx/vtable */ func = LLVMAddFunction (lmodule, name, LLVMFunctionType2 (LLVMVoidType (), LLVMInt32Type (), IntPtrType (), FALSE)); sig = LLVMFunctionType3 (LLVMVoidType (), IntPtrType (), LLVMInt32Type (), IntPtrType (), FALSE); break; case 2: func = LLVMAddFunction (lmodule, name, LLVMFunctionType2 (LLVMVoidType (), LLVMInt32Type (), ObjRefType (), FALSE)); sig = LLVMFunctionType3 (LLVMVoidType (), IntPtrType (), LLVMInt32Type (), ObjRefType (), FALSE); break; default: g_assert_not_reached (); } LLVMSetLinkage (func, LLVMInternalLinkage); mono_llvm_add_func_attr (func, LLVM_ATTR_NO_INLINE); mono_llvm_set_preserveall_cc (func); entry_bb = LLVMAppendBasicBlock (func, "ENTRY"); builder = LLVMCreateBuilder (); LLVMPositionBuilderAtEnd (builder, entry_bb); /* get_aotconst */ ji = g_new0 (MonoJumpInfo, 1); ji->type = MONO_PATCH_INFO_AOT_MODULE; ji = mono_aot_patch_info_dup (ji); got_offset = mono_aot_get_got_offset (ji); module->max_got_offset = MAX (module->max_got_offset, got_offset); indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); indexes [1] = LLVMConstInt (LLVMInt32Type (), got_offset, FALSE); got_entry_addr = LLVMBuildGEP (builder, module->got_var, indexes, 2, ""); args [0] = LLVMBuildPtrToInt (builder, LLVMBuildLoad (builder, got_entry_addr, ""), IntPtrType (), ""); args [1] = LLVMGetParam (func, 0); if (subtype) args [2] = LLVMGetParam (func, 1); ji = g_new0 (MonoJumpInfo, 1); ji->type = MONO_PATCH_INFO_INTERNAL_METHOD; ji->data.name = icall_name; ji = mono_aot_patch_info_dup (ji); got_offset = mono_aot_get_got_offset (ji); module->max_got_offset = MAX (module->max_got_offset, got_offset); indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); indexes [1] = LLVMConstInt (LLVMInt32Type (), got_offset, FALSE); got_entry_addr = LLVMBuildGEP (builder, module->got_var, indexes, 2, ""); callee = LLVMBuildLoad (builder, got_entry_addr, ""); callee = LLVMBuildBitCast (builder, callee, LLVMPointerType (sig, 0), ""); LLVMBuildCall (builder, callee, args, LLVMCountParamTypes (sig), ""); // Set the inited flag indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); indexes [1] = LLVMGetParam (func, 0); LLVMBuildStore (builder, LLVMConstInt (LLVMInt8Type (), 1, FALSE), LLVMBuildGEP (builder, module->inited_var, indexes, 2, "")); LLVMBuildRetVoid (builder); LLVMVerifyFunction(func, LLVMAbortProcessAction); LLVMDisposeBuilder (builder); return func; } /* * Emit wrappers around the C icalls used to initialize llvm methods, to * make the calling code smaller and to enable usage of the llvm * PreserveAll calling convention. */ static void emit_init_icall_wrappers (MonoLLVMModule *module) { module->init_method = emit_init_icall_wrapper (module, "init_method", "mono_aot_init_llvm_method", 0); module->init_method_gshared_mrgctx = emit_init_icall_wrapper (module, "init_method_gshared_mrgctx", "mono_aot_init_gshared_method_mrgctx", 1); module->init_method_gshared_this = emit_init_icall_wrapper (module, "init_method_gshared_this", "mono_aot_init_gshared_method_this", 2); module->init_method_gshared_vtable = emit_init_icall_wrapper (module, "init_method_gshared_vtable", "mono_aot_init_gshared_method_vtable", 3); } static void emit_llvm_code_end (MonoLLVMModule *module) { LLVMModuleRef lmodule = module->lmodule; LLVMValueRef func; LLVMBasicBlockRef entry_bb; LLVMBuilderRef builder; func = LLVMAddFunction (lmodule, "llvm_code_end", LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE)); LLVMSetLinkage (func, LLVMInternalLinkage); mono_llvm_add_func_attr (func, LLVM_ATTR_NO_UNWIND); module->code_end = func; entry_bb = LLVMAppendBasicBlock (func, "ENTRY"); builder = LLVMCreateBuilder (); LLVMPositionBuilderAtEnd (builder, entry_bb); LLVMBuildRetVoid (builder); LLVMDisposeBuilder (builder); } static void emit_div_check (EmitContext *ctx, LLVMBuilderRef builder, MonoBasicBlock *bb, MonoInst *ins, LLVMValueRef lhs, LLVMValueRef rhs) { gboolean need_div_check = ctx->cfg->backend->need_div_check; if (bb->region) /* LLVM doesn't know that these can throw an exception since they are not called through an intrinsic */ need_div_check = TRUE; if (!need_div_check) return; switch (ins->opcode) { case OP_IDIV: case OP_LDIV: case OP_IREM: case OP_LREM: case OP_IDIV_UN: case OP_LDIV_UN: case OP_IREM_UN: case OP_LREM_UN: case OP_IDIV_IMM: case OP_LDIV_IMM: case OP_IREM_IMM: case OP_LREM_IMM: case OP_IDIV_UN_IMM: case OP_LDIV_UN_IMM: case OP_IREM_UN_IMM: case OP_LREM_UN_IMM: { LLVMValueRef cmp; gboolean is_signed = (ins->opcode == OP_IDIV || ins->opcode == OP_LDIV || ins->opcode == OP_IREM || ins->opcode == OP_LREM || ins->opcode == OP_IDIV_IMM || ins->opcode == OP_LDIV_IMM || ins->opcode == OP_IREM_IMM || ins->opcode == OP_LREM_IMM); cmp = LLVMBuildICmp (builder, LLVMIntEQ, rhs, LLVMConstInt (LLVMTypeOf (rhs), 0, FALSE), ""); emit_cond_system_exception (ctx, bb, "DivideByZeroException", cmp); if (!ctx_ok (ctx)) break; builder = ctx->builder; /* b == -1 && a == 0x80000000 */ if (is_signed) { LLVMValueRef c = (LLVMTypeOf (lhs) == LLVMInt32Type ()) ? LLVMConstInt (LLVMTypeOf (lhs), 0x80000000, FALSE) : LLVMConstInt (LLVMTypeOf (lhs), 0x8000000000000000LL, FALSE); LLVMValueRef cond1 = LLVMBuildICmp (builder, LLVMIntEQ, rhs, LLVMConstInt (LLVMTypeOf (rhs), -1, FALSE), ""); LLVMValueRef cond2 = LLVMBuildICmp (builder, LLVMIntEQ, lhs, c, ""); cmp = LLVMBuildICmp (builder, LLVMIntEQ, LLVMBuildAnd (builder, cond1, cond2, ""), LLVMConstInt (LLVMInt1Type (), 1, FALSE), ""); emit_cond_system_exception (ctx, bb, "OverflowException", cmp); if (!ctx_ok (ctx)) break; builder = ctx->builder; } break; } default: break; } } /* * emit_init_method: * * Emit code to initialize the GOT slots used by the method. */ static void emit_init_method (EmitContext *ctx) { LLVMValueRef indexes [16], args [16], callee; LLVMValueRef inited_var, cmp, call; LLVMBasicBlockRef inited_bb, notinited_bb; LLVMBuilderRef builder = ctx->builder; MonoCompile *cfg = ctx->cfg; ctx->module->max_inited_idx = MAX (ctx->module->max_inited_idx, cfg->method_index); indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); indexes [1] = LLVMConstInt (LLVMInt32Type (), cfg->method_index, FALSE); inited_var = LLVMBuildLoad (builder, LLVMBuildGEP (builder, ctx->module->inited_var, indexes, 2, ""), "is_inited"); args [0] = inited_var; args [1] = LLVMConstInt (LLVMInt8Type (), 1, FALSE); inited_var = LLVMBuildCall (ctx->builder, get_intrinsic (ctx, "llvm.expect.i8"), args, 2, ""); cmp = LLVMBuildICmp (builder, LLVMIntEQ, inited_var, LLVMConstInt (LLVMTypeOf (inited_var), 0, FALSE), ""); inited_bb = ctx->inited_bb; notinited_bb = gen_bb (ctx, "NOTINITED_BB"); LLVMBuildCondBr (ctx->builder, cmp, notinited_bb, inited_bb); builder = ctx->builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, notinited_bb); // FIXME: Cache if (ctx->rgctx_arg && cfg->method->is_inflated && mono_method_get_context (cfg->method)->method_inst) { args [0] = LLVMConstInt (LLVMInt32Type (), cfg->method_index, 0); args [1] = convert (ctx, ctx->rgctx_arg, IntPtrType ()); callee = ctx->module->init_method_gshared_mrgctx; call = LLVMBuildCall (builder, callee, args, 2, ""); } else if (ctx->rgctx_arg) { /* A vtable is passed as the rgctx argument */ args [0] = LLVMConstInt (LLVMInt32Type (), cfg->method_index, 0); args [1] = convert (ctx, ctx->rgctx_arg, IntPtrType ()); callee = ctx->module->init_method_gshared_vtable; call = LLVMBuildCall (builder, callee, args, 2, ""); } else if (cfg->gshared) { args [0] = LLVMConstInt (LLVMInt32Type (), cfg->method_index, 0); args [1] = convert (ctx, ctx->this_arg, ObjRefType ()); callee = ctx->module->init_method_gshared_this; call = LLVMBuildCall (builder, callee, args, 2, ""); } else { args [0] = LLVMConstInt (LLVMInt32Type (), cfg->method_index, 0); callee = ctx->module->init_method; call = LLVMBuildCall (builder, callee, args, 1, ""); } /* * This enables llvm to keep arguments in their original registers/ * scratch registers, since the call will not clobber them. */ mono_llvm_set_call_preserveall_cc (call); LLVMBuildBr (builder, inited_bb); ctx->bblocks [cfg->bb_entry->block_num].end_bblock = inited_bb; builder = ctx->builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, inited_bb); } static void emit_unbox_tramp (EmitContext *ctx, const char *method_name, LLVMTypeRef method_type, LLVMValueRef method, int method_index) { /* * Emit unbox trampoline using a tail call */ LLVMValueRef tramp, call, *args; LLVMBuilderRef builder; LLVMBasicBlockRef lbb; LLVMCallInfo *linfo; char *tramp_name; int i, nargs; tramp_name = g_strdup_printf ("ut_%s", method_name); tramp = LLVMAddFunction (ctx->module->lmodule, tramp_name, method_type); LLVMSetLinkage (tramp, LLVMInternalLinkage); mono_llvm_add_func_attr (tramp, LLVM_ATTR_OPTIMIZE_FOR_SIZE); //mono_llvm_add_func_attr (tramp, LLVM_ATTR_NO_UNWIND); linfo = ctx->linfo; // FIXME: Reduce code duplication with mono_llvm_compile_method () etc. if (!ctx->llvm_only && ctx->rgctx_arg_pindex != -1) mono_llvm_add_param_attr (LLVMGetParam (tramp, ctx->rgctx_arg_pindex), LLVM_ATTR_IN_REG); if (ctx->cfg->vret_addr) { LLVMSetValueName (LLVMGetParam (tramp, linfo->vret_arg_pindex), "vret"); if (linfo->ret.storage == LLVMArgVtypeByRef) { mono_llvm_add_param_attr (LLVMGetParam (tramp, linfo->vret_arg_pindex), LLVM_ATTR_STRUCT_RET); mono_llvm_add_param_attr (LLVMGetParam (tramp, linfo->vret_arg_pindex), LLVM_ATTR_NO_ALIAS); } } lbb = LLVMAppendBasicBlock (tramp, ""); builder = LLVMCreateBuilder (); LLVMPositionBuilderAtEnd (builder, lbb); nargs = LLVMCountParamTypes (method_type); args = g_new0 (LLVMValueRef, nargs); for (i = 0; i < nargs; ++i) { args [i] = LLVMGetParam (tramp, i); if (i == ctx->this_arg_pindex) { LLVMTypeRef arg_type = LLVMTypeOf (args [i]); args [i] = LLVMBuildPtrToInt (builder, args [i], IntPtrType (), ""); args [i] = LLVMBuildAdd (builder, args [i], LLVMConstInt (IntPtrType (), sizeof (MonoObject), FALSE), ""); args [i] = LLVMBuildIntToPtr (builder, args [i], arg_type, ""); } } call = LLVMBuildCall (builder, method, args, nargs, ""); if (!ctx->llvm_only && ctx->rgctx_arg_pindex != -1) mono_llvm_add_instr_attr (call, 1 + ctx->rgctx_arg_pindex, LLVM_ATTR_IN_REG); if (linfo->ret.storage == LLVMArgVtypeByRef) mono_llvm_add_instr_attr (call, 1 + linfo->vret_arg_pindex, LLVM_ATTR_STRUCT_RET); // FIXME: This causes assertions in clang //mono_llvm_set_must_tail (call); if (LLVMGetReturnType (method_type) == LLVMVoidType ()) LLVMBuildRetVoid (builder); else LLVMBuildRet (builder, call); g_hash_table_insert (ctx->module->idx_to_unbox_tramp, GINT_TO_POINTER (method_index), tramp); LLVMDisposeBuilder (builder); } /* * emit_entry_bb: * * Emit code to load/convert arguments. */ static void emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder) { int i, j, pindex; MonoCompile *cfg = ctx->cfg; MonoMethodSignature *sig = ctx->sig; LLVMCallInfo *linfo = ctx->linfo; MonoBasicBlock *bb; char **names; LLVMBuilderRef old_builder = ctx->builder; ctx->builder = builder; ctx->alloca_builder = create_builder (ctx); /* * Handle indirect/volatile variables by allocating memory for them * using 'alloca', and storing their address in a temporary. */ for (i = 0; i < cfg->num_varinfo; ++i) { MonoInst *var = cfg->varinfo [i]; LLVMTypeRef vtype; if (var->opcode == OP_GSHAREDVT_LOCAL || var->opcode == OP_GSHAREDVT_ARG_REGOFFSET) { } else if (var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (mini_type_is_vtype (var->inst_vtype) && !MONO_CLASS_IS_SIMD (ctx->cfg, var->klass))) { vtype = type_to_llvm_type (ctx, var->inst_vtype); if (!ctx_ok (ctx)) return; /* Could be already created by an OP_VPHI */ if (!ctx->addresses [var->dreg]) { ctx->addresses [var->dreg] = build_alloca (ctx, var->inst_vtype); //LLVMSetValueName (ctx->addresses [var->dreg], g_strdup_printf ("vreg_loc_%d", var->dreg)); } ctx->vreg_cli_types [var->dreg] = var->inst_vtype; } } names = g_new (char *, sig->param_count); mono_method_get_param_names (cfg->method, (const char **) names); for (i = 0; i < sig->param_count; ++i) { LLVMArgInfo *ainfo = &linfo->args [i + sig->hasthis]; int reg = cfg->args [i + sig->hasthis]->dreg; char *name; pindex = ainfo->pindex; switch (ainfo->storage) { case LLVMArgVtypeInReg: case LLVMArgAsFpArgs: { LLVMValueRef args [8]; int j; pindex += ainfo->ndummy_fpargs; /* The argument is received as a set of int/fp arguments, store them into the real argument */ memset (args, 0, sizeof (args)); if (ainfo->storage == LLVMArgVtypeInReg) { args [0] = LLVMGetParam (ctx->lmethod, pindex); if (ainfo->pair_storage [1] != LLVMArgNone) args [1] = LLVMGetParam (ctx->lmethod, pindex + 1); } else { g_assert (ainfo->nslots <= 8); for (j = 0; j < ainfo->nslots; ++j) args [j] = LLVMGetParam (ctx->lmethod, pindex + j); } ctx->addresses [reg] = build_alloca (ctx, ainfo->type); emit_args_to_vtype (ctx, builder, ainfo->type, ctx->addresses [reg], ainfo, args); if (ainfo->storage == LLVMArgVtypeInReg && MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (ainfo->type))) { /* Treat these as normal values */ ctx->values [reg] = LLVMBuildLoad (builder, ctx->addresses [reg], ""); } break; } case LLVMArgVtypeByVal: { ctx->addresses [reg] = LLVMGetParam (ctx->lmethod, pindex); if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (ainfo->type))) { /* Treat these as normal values */ ctx->values [reg] = LLVMBuildLoad (builder, ctx->addresses [reg], ""); } break; } case LLVMArgVtypeByRef: { /* The argument is passed by ref */ ctx->addresses [reg] = LLVMGetParam (ctx->lmethod, pindex); break; } case LLVMArgAsIArgs: { LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex); int size; /* The argument is received as an array of ints, store it into the real argument */ ctx->addresses [reg] = build_alloca (ctx, ainfo->type); size = mono_class_value_size (mono_class_from_mono_type (ainfo->type), NULL); if (size < SIZEOF_VOID_P) { /* The upper bits of the registers might not be valid */ LLVMValueRef val = LLVMBuildExtractValue (builder, arg, 0, ""); LLVMValueRef dest = convert (ctx, ctx->addresses [reg], LLVMPointerType (LLVMIntType (size * 8), 0)); LLVMBuildStore (ctx->builder, LLVMBuildTrunc (builder, val, LLVMIntType (size * 8), ""), dest); } else { LLVMBuildStore (ctx->builder, arg, convert (ctx, ctx->addresses [reg], LLVMPointerType (LLVMTypeOf (arg), 0))); } break; } case LLVMArgVtypeAsScalar: g_assert_not_reached (); break; case LLVMArgGsharedvtFixed: { /* These are non-gsharedvt arguments passed by ref, the rest of the IR treats them as scalars */ LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex); if (names [i]) name = g_strdup_printf ("arg_%s", names [i]); else name = g_strdup_printf ("arg_%d", i); ctx->values [reg] = LLVMBuildLoad (builder, convert (ctx, arg, LLVMPointerType (type_to_llvm_type (ctx, ainfo->type), 0)), name); break; } case LLVMArgGsharedvtFixedVtype: { LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex); if (names [i]) name = g_strdup_printf ("vtype_arg_%s", names [i]); else name = g_strdup_printf ("vtype_arg_%d", i); /* Non-gsharedvt vtype argument passed by ref, the rest of the IR treats it as a vtype */ g_assert (ctx->addresses [reg]); LLVMSetValueName (ctx->addresses [reg], name); LLVMBuildStore (builder, LLVMBuildLoad (builder, convert (ctx, arg, LLVMPointerType (type_to_llvm_type (ctx, ainfo->type), 0)), ""), ctx->addresses [reg]); break; } case LLVMArgGsharedvtVariable: /* The IR treats these as variables with addresses */ ctx->addresses [reg] = LLVMGetParam (ctx->lmethod, pindex); break; default: ctx->values [reg] = convert_full (ctx, ctx->values [reg], llvm_type_to_stack_type (cfg, type_to_llvm_type (ctx, ainfo->type)), type_is_unsigned (ctx, ainfo->type)); break; } } g_free (names); if (cfg->vret_addr) emit_volatile_store (ctx, cfg->vret_addr->dreg); if (sig->hasthis) emit_volatile_store (ctx, cfg->args [0]->dreg); for (i = 0; i < sig->param_count; ++i) if (!mini_type_is_vtype (sig->params [i])) emit_volatile_store (ctx, cfg->args [i + sig->hasthis]->dreg); if (sig->hasthis && !cfg->rgctx_var && cfg->gshared) { LLVMValueRef this_alloc; /* * The exception handling code needs the location where the this argument was * stored for gshared methods. We create a separate alloca to hold it, and mark it * with the "mono.this" custom metadata to tell llvm that it needs to save its * location into the LSDA. */ this_alloc = mono_llvm_build_alloca (builder, ThisType (), LLVMConstInt (LLVMInt32Type (), 1, FALSE), 0, ""); /* This volatile store will keep the alloca alive */ mono_llvm_build_store (builder, ctx->values [cfg->args [0]->dreg], this_alloc, TRUE, LLVM_BARRIER_NONE); set_metadata_flag (this_alloc, "mono.this"); } if (cfg->rgctx_var) { LLVMValueRef rgctx_alloc, store; /* * We handle the rgctx arg similarly to the this pointer. */ g_assert (ctx->addresses [cfg->rgctx_var->dreg]); rgctx_alloc = ctx->addresses [cfg->rgctx_var->dreg]; /* This volatile store will keep the alloca alive */ store = mono_llvm_build_store (builder, convert (ctx, ctx->rgctx_arg, IntPtrType ()), rgctx_alloc, TRUE, LLVM_BARRIER_NONE); set_metadata_flag (rgctx_alloc, "mono.this"); } /* Initialize the method if needed */ if (cfg->compile_aot && ctx->llvm_only) { /* Emit a location for the initialization code */ ctx->init_bb = gen_bb (ctx, "INIT_BB"); ctx->inited_bb = gen_bb (ctx, "INITED_BB"); LLVMBuildBr (ctx->builder, ctx->init_bb); builder = ctx->builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, ctx->inited_bb); ctx->bblocks [cfg->bb_entry->block_num].end_bblock = ctx->inited_bb; } /* Compute nesting between clauses */ ctx->nested_in = (GSList**)mono_mempool_alloc0 (cfg->mempool, sizeof (GSList*) * cfg->header->num_clauses); for (i = 0; i < cfg->header->num_clauses; ++i) { for (j = 0; j < cfg->header->num_clauses; ++j) { MonoExceptionClause *clause1 = &cfg->header->clauses [i]; MonoExceptionClause *clause2 = &cfg->header->clauses [j]; if (i != j && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset) ctx->nested_in [i] = g_slist_prepend_mempool (cfg->mempool, ctx->nested_in [i], GINT_TO_POINTER (j)); } } /* * For finally clauses, create an indicator variable telling OP_ENDFINALLY whenever * it needs to continue normally, or return back to the exception handling system. */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { int clause_index; char name [128]; if (!(bb->region != -1 && (bb->flags & BB_EXCEPTION_HANDLER))) continue; clause_index = MONO_REGION_CLAUSE_INDEX (bb->region); g_hash_table_insert (ctx->region_to_handler, GUINT_TO_POINTER (mono_get_block_region_notry (cfg, bb->region)), bb); g_hash_table_insert (ctx->clause_to_handler, GINT_TO_POINTER (clause_index), bb); if (bb->in_scount == 0) { LLVMValueRef val; sprintf (name, "finally_ind_bb%d", bb->block_num); val = LLVMBuildAlloca (builder, LLVMInt32Type (), name); LLVMBuildStore (builder, LLVMConstInt (LLVMInt32Type (), 0, FALSE), val); ctx->bblocks [bb->block_num].finally_ind = val; } else { /* Create a variable to hold the exception var */ if (!ctx->ex_var) ctx->ex_var = LLVMBuildAlloca (builder, ObjRefType (), "exvar"); } /* * Create a new bblock which CALL_HANDLER/landing pads can branch to, because branching to the * LLVM bblock containing a landing pad causes problems for the * LLVM optimizer passes. */ sprintf (name, "BB%d_CALL_HANDLER_TARGET", bb->block_num); ctx->bblocks [bb->block_num].call_handler_target_bb = LLVMAppendBasicBlock (ctx->lmethod, name); } ctx->builder = old_builder; } static void process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, MonoInst *ins) { MonoCompile *cfg = ctx->cfg; LLVMValueRef *values = ctx->values; LLVMValueRef *addresses = ctx->addresses; MonoCallInst *call = (MonoCallInst*)ins; MonoMethodSignature *sig = call->signature; LLVMValueRef callee = NULL, lcall; LLVMValueRef *args; LLVMCallInfo *cinfo; GSList *l; int i, len, nargs; gboolean vretaddr; LLVMTypeRef llvm_sig; gpointer target; gboolean is_virtual, calli, preserveall; LLVMBuilderRef builder = *builder_ref; if ((call->signature->call_convention != MONO_CALL_DEFAULT) && !((call->signature->call_convention == MONO_CALL_C) && ctx->llvm_only)) { set_failure (ctx, "non-default callconv"); return; } cinfo = call->cinfo; g_assert (cinfo); if (call->rgctx_arg_reg) cinfo->rgctx_arg = TRUE; if (call->imt_arg_reg) cinfo->imt_arg = TRUE; vretaddr = (cinfo->ret.storage == LLVMArgVtypeRetAddr || cinfo->ret.storage == LLVMArgVtypeByRef || cinfo->ret.storage == LLVMArgGsharedvtFixed || cinfo->ret.storage == LLVMArgGsharedvtVariable || cinfo->ret.storage == LLVMArgGsharedvtFixedVtype); llvm_sig = sig_to_llvm_sig_full (ctx, sig, cinfo); if (!ctx_ok (ctx)) return; is_virtual = (ins->opcode == OP_VOIDCALL_MEMBASE || ins->opcode == OP_CALL_MEMBASE || ins->opcode == OP_VCALL_MEMBASE || ins->opcode == OP_LCALL_MEMBASE || ins->opcode == OP_FCALL_MEMBASE || ins->opcode == OP_RCALL_MEMBASE); calli = !call->fptr_is_patch && (ins->opcode == OP_VOIDCALL_REG || ins->opcode == OP_CALL_REG || ins->opcode == OP_VCALL_REG || ins->opcode == OP_LCALL_REG || ins->opcode == OP_FCALL_REG || ins->opcode == OP_RCALL_REG); /* Unused */ preserveall = FALSE; /* FIXME: Avoid creating duplicate methods */ if (ins->flags & MONO_INST_HAS_METHOD) { if (is_virtual) { callee = NULL; } else { if (cfg->compile_aot) { callee = get_callee (ctx, llvm_sig, MONO_PATCH_INFO_METHOD, call->method); if (!callee) { set_failure (ctx, "can't encode patch"); return; } if (cfg->llvm_only && call->method->klass->image->assembly == ctx->module->assembly) { /* * Collect instructions representing the callee into a hash so they can be replaced * by the llvm method for the callee if the callee turns out to be direct * callable. Currently this only requires it to not fail llvm compilation. */ GSList *l = (GSList*)g_hash_table_lookup (ctx->method_to_callers, call->method); l = g_slist_prepend (l, callee); g_hash_table_insert (ctx->method_to_callers, call->method, l); } } else { MonoError error; static int tramp_index; char *name; name = g_strdup_printf ("tramp_%d", tramp_index); tramp_index ++; #if LLVM_API_VERSION > 100 /* * Use our trampoline infrastructure for lazy compilation instead of llvm's. * Make all calls through a global. The address of the global will be saved in * MonoJitDomainInfo.llvm_jit_callees and updated when the method it refers to is * compiled. */ LLVMValueRef tramp_var = g_hash_table_lookup (ctx->jit_callees, call->method); if (!tramp_var) { target = mono_create_jit_trampoline (mono_domain_get (), call->method, &error); if (!is_ok (&error)) { set_failure (ctx, mono_error_get_message (&error)); mono_error_cleanup (&error); return; } tramp_var = LLVMAddGlobal (ctx->lmodule, LLVMPointerType (llvm_sig, 0), name); LLVMSetInitializer (tramp_var, LLVMConstIntToPtr (LLVMConstInt (LLVMInt64Type (), (guint64)(size_t)target, FALSE), LLVMPointerType (llvm_sig, 0))); LLVMSetLinkage (tramp_var, LLVMExternalLinkage); g_hash_table_insert (ctx->jit_callees, call->method, tramp_var); } callee = LLVMBuildLoad (builder, tramp_var, ""); #else target = mono_create_jit_trampoline (mono_domain_get (), call->method, &error); if (!is_ok (&error)) { g_free (name); set_failure (ctx, mono_error_get_message (&error)); mono_error_cleanup (&error); return; } callee = LLVMAddFunction (ctx->lmodule, name, llvm_sig); g_free (name); LLVMAddGlobalMapping (ctx->module->ee, callee, target); #endif } } if (!cfg->llvm_only && call->method && strstr (call->method->klass->name, "AsyncVoidMethodBuilder")) { /* LLVM miscompiles async methods */ set_failure (ctx, "#13734"); return; } } else if (calli) { } else { MonoJitICallInfo *info = mono_find_jit_icall_by_addr (call->fptr); if (info) { /* MonoJumpInfo ji; memset (&ji, 0, sizeof (ji)); ji.type = MONO_PATCH_INFO_JIT_ICALL_ADDR; ji.data.target = info->name; target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, &ji, FALSE); */ if (cfg->compile_aot) { callee = get_callee (ctx, llvm_sig, MONO_PATCH_INFO_INTERNAL_METHOD, (char*)info->name); if (!callee) { set_failure (ctx, "can't encode patch"); return; } } else { target = (gpointer)mono_icall_get_wrapper (info); callee = emit_jit_callee (ctx, "", llvm_sig, target); } } else { if (cfg->compile_aot) { callee = NULL; if (cfg->abs_patches) { MonoJumpInfo *abs_ji = (MonoJumpInfo*)g_hash_table_lookup (cfg->abs_patches, call->fptr); if (abs_ji) { callee = get_callee (ctx, llvm_sig, abs_ji->type, abs_ji->data.target); if (!callee) { set_failure (ctx, "can't encode patch"); return; } } } if (!callee) { set_failure (ctx, "aot"); return; } } else { #if LLVM_API_VERSION > 100 if (cfg->abs_patches) { MonoJumpInfo *abs_ji = (MonoJumpInfo*)g_hash_table_lookup (cfg->abs_patches, call->fptr); if (abs_ji) { MonoError error; target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, abs_ji, FALSE, &error); mono_error_assert_ok (&error); callee = emit_jit_callee (ctx, "", llvm_sig, target); } else { g_assert_not_reached (); } } else { g_assert_not_reached (); } #else callee = LLVMAddFunction (ctx->lmodule, "", llvm_sig); target = NULL; if (cfg->abs_patches) { MonoJumpInfo *abs_ji = (MonoJumpInfo*)g_hash_table_lookup (cfg->abs_patches, call->fptr); if (abs_ji) { MonoError error; /* * FIXME: Some trampolines might have * their own calling convention on some platforms. */ target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, abs_ji, FALSE, &error); mono_error_assert_ok (&error); LLVMAddGlobalMapping (ctx->module->ee, callee, target); } } if (!target) LLVMAddGlobalMapping (ctx->module->ee, callee, (gpointer)call->fptr); #endif } } } if (is_virtual) { int size = sizeof (gpointer); LLVMValueRef index; g_assert (ins->inst_offset % size == 0); index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE); callee = convert (ctx, LLVMBuildLoad (builder, LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (LLVMPointerType (IntPtrType (), 0), 0)), &index, 1, ""), ""), LLVMPointerType (llvm_sig, 0)); } else if (calli) { callee = convert (ctx, values [ins->sreg1], LLVMPointerType (llvm_sig, 0)); } else { if (ins->flags & MONO_INST_HAS_METHOD) { } } /* * Collect and convert arguments */ nargs = (sig->param_count * 16) + sig->hasthis + vretaddr + call->rgctx_reg + call->imt_arg_reg; len = sizeof (LLVMValueRef) * nargs; args = (LLVMValueRef*)alloca (len); memset (args, 0, len); l = call->out_ireg_args; if (call->rgctx_arg_reg) { g_assert (values [call->rgctx_arg_reg]); g_assert (cinfo->rgctx_arg_pindex < nargs); /* * On ARM, the imt/rgctx argument is passed in a caller save register, but some of our trampolines etc. clobber it, leading to * problems is LLVM moves the arg assignment earlier. To work around this, save the argument into a stack slot and load * it using a volatile load. */ #ifdef TARGET_ARM if (!ctx->imt_rgctx_loc) ctx->imt_rgctx_loc = build_alloca_llvm_type (ctx, ctx->module->ptr_type, sizeof (gpointer)); LLVMBuildStore (builder, convert (ctx, ctx->values [call->rgctx_arg_reg], ctx->module->ptr_type), ctx->imt_rgctx_loc); args [cinfo->rgctx_arg_pindex] = mono_llvm_build_load (builder, ctx->imt_rgctx_loc, "", TRUE); #else args [cinfo->rgctx_arg_pindex] = convert (ctx, values [call->rgctx_arg_reg], ctx->module->ptr_type); #endif } if (call->imt_arg_reg) { g_assert (!ctx->llvm_only); g_assert (values [call->imt_arg_reg]); g_assert (cinfo->imt_arg_pindex < nargs); #ifdef TARGET_ARM if (!ctx->imt_rgctx_loc) ctx->imt_rgctx_loc = build_alloca_llvm_type (ctx, ctx->module->ptr_type, sizeof (gpointer)); LLVMBuildStore (builder, convert (ctx, ctx->values [call->imt_arg_reg], ctx->module->ptr_type), ctx->imt_rgctx_loc); args [cinfo->imt_arg_pindex] = mono_llvm_build_load (builder, ctx->imt_rgctx_loc, "", TRUE); #else args [cinfo->imt_arg_pindex] = convert (ctx, values [call->imt_arg_reg], ctx->module->ptr_type); #endif } switch (cinfo->ret.storage) { case LLVMArgGsharedvtVariable: { MonoInst *var = get_vreg_to_inst (cfg, call->inst.dreg); if (var && var->opcode == OP_GSHAREDVT_LOCAL) { args [cinfo->vret_arg_pindex] = convert (ctx, emit_gsharedvt_ldaddr (ctx, var->dreg), IntPtrType ()); } else { g_assert (addresses [call->inst.dreg]); args [cinfo->vret_arg_pindex] = addresses [call->inst.dreg]; } break; } default: if (vretaddr) { if (!addresses [call->inst.dreg]) addresses [call->inst.dreg] = build_alloca (ctx, sig->ret); g_assert (cinfo->vret_arg_pindex < nargs); if (cinfo->ret.storage == LLVMArgVtypeByRef) args [cinfo->vret_arg_pindex] = addresses [call->inst.dreg]; else args [cinfo->vret_arg_pindex] = LLVMBuildPtrToInt (builder, addresses [call->inst.dreg], IntPtrType (), ""); } break; } /* * Sometimes the same method is called with two different signatures (i.e. with and without 'this'), so * use the real callee for argument type conversion. */ LLVMTypeRef callee_type = LLVMGetElementType (LLVMTypeOf (callee)); LLVMTypeRef *param_types = (LLVMTypeRef*)g_alloca (sizeof (LLVMTypeRef) * LLVMCountParamTypes (callee_type)); LLVMGetParamTypes (callee_type, param_types); for (i = 0; i < sig->param_count + sig->hasthis; ++i) { guint32 regpair; int reg, pindex; LLVMArgInfo *ainfo = &call->cinfo->args [i]; pindex = ainfo->pindex; regpair = (guint32)(gssize)(l->data); reg = regpair & 0xffffff; args [pindex] = values [reg]; switch (ainfo->storage) { case LLVMArgVtypeInReg: case LLVMArgAsFpArgs: { guint32 nargs; int j; for (j = 0; j < ainfo->ndummy_fpargs; ++j) args [pindex + j] = LLVMConstNull (LLVMDoubleType ()); pindex += ainfo->ndummy_fpargs; g_assert (addresses [reg]); emit_vtype_to_args (ctx, builder, ainfo->type, addresses [reg], ainfo, args + pindex, &nargs); pindex += nargs; // FIXME: alignment // FIXME: Get rid of the VMOVE break; } case LLVMArgVtypeByVal: g_assert (addresses [reg]); args [pindex] = addresses [reg]; break; case LLVMArgVtypeByRef: { g_assert (addresses [reg]); args [pindex] = convert (ctx, addresses [reg], LLVMPointerType (type_to_llvm_arg_type (ctx, ainfo->type), 0)); break; } case LLVMArgAsIArgs: g_assert (addresses [reg]); if (ainfo->esize == 8) args [pindex] = LLVMBuildLoad (ctx->builder, convert (ctx, addresses [reg], LLVMPointerType (LLVMArrayType (LLVMInt64Type (), ainfo->nslots), 0)), ""); else args [pindex] = LLVMBuildLoad (ctx->builder, convert (ctx, addresses [reg], LLVMPointerType (LLVMArrayType (IntPtrType (), ainfo->nslots), 0)), ""); break; case LLVMArgVtypeAsScalar: g_assert_not_reached (); break; case LLVMArgGsharedvtFixed: case LLVMArgGsharedvtFixedVtype: g_assert (addresses [reg]); args [pindex] = convert (ctx, addresses [reg], LLVMPointerType (type_to_llvm_arg_type (ctx, ainfo->type), 0)); break; case LLVMArgGsharedvtVariable: g_assert (addresses [reg]); args [pindex] = convert (ctx, addresses [reg], LLVMPointerType (IntPtrType (), 0)); break; default: g_assert (args [pindex]); if (i == 0 && sig->hasthis) args [pindex] = convert (ctx, args [pindex], param_types [pindex]); else args [pindex] = convert (ctx, args [pindex], type_to_llvm_arg_type (ctx, ainfo->type)); break; } g_assert (pindex <= nargs); l = l->next; } // FIXME: Align call sites /* * Emit the call */ lcall = emit_call (ctx, bb, &builder, callee, args, LLVMCountParamTypes (llvm_sig)); if (ins->opcode != OP_TAILCALL && LLVMGetInstructionOpcode (lcall) == LLVMCall) mono_llvm_set_call_notail (lcall); /* * Modify cconv and parameter attributes to pass rgctx/imt correctly. */ #if defined(MONO_ARCH_IMT_REG) && defined(MONO_ARCH_RGCTX_REG) g_assert (MONO_ARCH_IMT_REG == MONO_ARCH_RGCTX_REG); #endif /* The two can't be used together, so use only one LLVM calling conv to pass them */ g_assert (!(call->rgctx_arg_reg && call->imt_arg_reg)); if (!sig->pinvoke && !cfg->llvm_only) LLVMSetInstructionCallConv (lcall, LLVMMono1CallConv); if (preserveall) mono_llvm_set_call_preserveall_cc (lcall); if (cinfo->ret.storage == LLVMArgVtypeByRef) mono_llvm_add_instr_attr (lcall, 1 + cinfo->vret_arg_pindex, LLVM_ATTR_STRUCT_RET); if (!ctx->llvm_only && call->rgctx_arg_reg) mono_llvm_add_instr_attr (lcall, 1 + cinfo->rgctx_arg_pindex, LLVM_ATTR_IN_REG); if (call->imt_arg_reg) mono_llvm_add_instr_attr (lcall, 1 + cinfo->imt_arg_pindex, LLVM_ATTR_IN_REG); /* Add byval attributes if needed */ for (i = 0; i < sig->param_count; ++i) { LLVMArgInfo *ainfo = &call->cinfo->args [i + sig->hasthis]; if (ainfo && ainfo->storage == LLVMArgVtypeByVal) mono_llvm_add_instr_attr (lcall, 1 + ainfo->pindex, LLVM_ATTR_BY_VAL); } /* * Convert the result */ switch (cinfo->ret.storage) { case LLVMArgVtypeInReg: { LLVMValueRef regs [2]; if (LLVMTypeOf (lcall) == LLVMVoidType ()) /* Empty struct */ break; if (!addresses [ins->dreg]) addresses [ins->dreg] = build_alloca (ctx, sig->ret); regs [0] = LLVMBuildExtractValue (builder, lcall, 0, ""); if (cinfo->ret.pair_storage [1] != LLVMArgNone) regs [1] = LLVMBuildExtractValue (builder, lcall, 1, ""); emit_args_to_vtype (ctx, builder, sig->ret, addresses [ins->dreg], &cinfo->ret, regs); break; } case LLVMArgVtypeByVal: if (!addresses [call->inst.dreg]) addresses [call->inst.dreg] = build_alloca (ctx, sig->ret); LLVMBuildStore (builder, lcall, addresses [call->inst.dreg]); break; case LLVMArgAsIArgs: case LLVMArgFpStruct: if (!addresses [call->inst.dreg]) addresses [call->inst.dreg] = build_alloca (ctx, sig->ret); LLVMBuildStore (builder, lcall, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (LLVMTypeOf (lcall), 0), FALSE)); break; case LLVMArgVtypeAsScalar: if (!addresses [call->inst.dreg]) addresses [call->inst.dreg] = build_alloca (ctx, sig->ret); LLVMBuildStore (builder, lcall, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (LLVMTypeOf (lcall), 0), FALSE)); break; case LLVMArgVtypeRetAddr: case LLVMArgVtypeByRef: if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (sig->ret))) { /* Some opcodes like STOREX_MEMBASE access these by value */ g_assert (addresses [call->inst.dreg]); values [ins->dreg] = LLVMBuildLoad (builder, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (type_to_llvm_type (ctx, sig->ret), 0), FALSE), ""); } break; case LLVMArgGsharedvtVariable: break; case LLVMArgGsharedvtFixed: case LLVMArgGsharedvtFixedVtype: values [ins->dreg] = LLVMBuildLoad (builder, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (type_to_llvm_type (ctx, sig->ret), 0), FALSE), ""); break; default: if (sig->ret->type != MONO_TYPE_VOID) /* If the method returns an unsigned value, need to zext it */ values [ins->dreg] = convert_full (ctx, lcall, llvm_type_to_stack_type (cfg, type_to_llvm_type (ctx, sig->ret)), type_is_unsigned (ctx, sig->ret)); break; } *builder_ref = ctx->builder; } static void emit_llvmonly_throw (EmitContext *ctx, MonoBasicBlock *bb, gboolean rethrow, LLVMValueRef exc) { const char *icall_name = rethrow ? "mono_llvm_rethrow_exception" : "mono_llvm_throw_exception"; LLVMValueRef callee = rethrow ? ctx->module->rethrow : ctx->module->throw_icall; LLVMTypeRef exc_type = type_to_llvm_type (ctx, &mono_get_exception_class ()->byval_arg); if (!callee) { LLVMTypeRef fun_sig = LLVMFunctionType1 (LLVMVoidType (), exc_type, FALSE); if (ctx->cfg->compile_aot) { callee = get_callee (ctx, fun_sig, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name); } else { callee = LLVMAddFunction (ctx->lmodule, icall_name, fun_sig); LLVMAddGlobalMapping (ctx->module->ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name)); mono_memory_barrier (); if (rethrow) ctx->module->rethrow = callee; else ctx->module->throw_icall = callee; } } LLVMValueRef args [2]; args [0] = convert (ctx, exc, exc_type); emit_call (ctx, bb, &ctx->builder, callee, args, 1); LLVMBuildUnreachable (ctx->builder); ctx->builder = create_builder (ctx); } static void emit_throw (EmitContext *ctx, MonoBasicBlock *bb, gboolean rethrow, LLVMValueRef exc) { MonoMethodSignature *throw_sig; LLVMValueRef callee, arg; const char *icall_name; callee = rethrow ? ctx->module->rethrow : ctx->module->throw_icall; icall_name = rethrow ? "mono_arch_rethrow_exception" : "mono_arch_throw_exception"; if (!callee) { throw_sig = mono_metadata_signature_alloc (mono_get_corlib (), 1); throw_sig->ret = &mono_get_void_class ()->byval_arg; throw_sig->params [0] = &mono_get_object_class ()->byval_arg; if (ctx->cfg->compile_aot) { callee = get_callee (ctx, sig_to_llvm_sig (ctx, throw_sig), MONO_PATCH_INFO_INTERNAL_METHOD, icall_name); } else { gpointer target; #ifdef TARGET_X86 /* * LLVM doesn't push the exception argument, so we need a different * trampoline. */ target = resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, rethrow ? "llvm_rethrow_exception_trampoline" : "llvm_throw_exception_trampoline"); #else target = resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name); #endif callee = emit_jit_callee (ctx, icall_name, sig_to_llvm_sig (ctx, throw_sig), target); } mono_memory_barrier (); #if LLVM_API_VERSION < 100 if (rethrow) ctx->module->rethrow = callee; else ctx->module->throw_icall = callee; #endif } arg = convert (ctx, exc, type_to_llvm_type (ctx, &mono_get_object_class ()->byval_arg)); emit_call (ctx, bb, &ctx->builder, callee, &arg, 1); } static void emit_resume_eh (EmitContext *ctx, MonoBasicBlock *bb) { const char *icall_name = "mono_llvm_resume_exception"; LLVMValueRef callee = ctx->module->resume_eh; LLVMTypeRef fun_sig = LLVMFunctionType0 (LLVMVoidType (), FALSE); if (!callee) { if (ctx->cfg->compile_aot) { callee = get_callee (ctx, fun_sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name); } else { callee = LLVMAddFunction (ctx->lmodule, icall_name, fun_sig); LLVMAddGlobalMapping (ctx->module->ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name)); mono_memory_barrier (); ctx->module->resume_eh = callee; } } emit_call (ctx, bb, &ctx->builder, callee, NULL, 0); LLVMBuildUnreachable (ctx->builder); ctx->builder = create_builder (ctx); } static LLVMValueRef mono_llvm_emit_clear_exception_call (EmitContext *ctx, LLVMBuilderRef builder) { const char *icall_name = "mono_llvm_clear_exception"; LLVMTypeRef call_sig = LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE); LLVMValueRef callee = NULL; if (!callee) { if (ctx->cfg->compile_aot) { callee = get_callee (ctx, call_sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name); } else { // FIXME: This is broken. callee = LLVMAddFunction (ctx->lmodule, icall_name, call_sig); } } g_assert (builder && callee); return LLVMBuildCall (builder, callee, NULL, 0, ""); } static LLVMValueRef mono_llvm_emit_load_exception_call (EmitContext *ctx, LLVMBuilderRef builder) { const char *icall_name = "mono_llvm_load_exception"; LLVMTypeRef call_sig = LLVMFunctionType (ObjRefType (), NULL, 0, FALSE); LLVMValueRef callee = NULL; if (!callee) { if (ctx->cfg->compile_aot) { callee = get_callee (ctx, call_sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name); } else { // FIXME: This is broken. callee = LLVMAddFunction (ctx->lmodule, icall_name, call_sig); } } g_assert (builder && callee); return LLVMBuildCall (builder, callee, NULL, 0, icall_name); } static LLVMValueRef mono_llvm_emit_match_exception_call (EmitContext *ctx, LLVMBuilderRef builder, gint32 region_start, gint32 region_end) { const char *icall_name = "mono_llvm_match_exception"; ctx->builder = builder; const int num_args = 5; LLVMValueRef args [num_args]; args [0] = convert (ctx, get_aotconst (ctx, MONO_PATCH_INFO_AOT_JIT_INFO, GINT_TO_POINTER (ctx->cfg->method_index)), IntPtrType ()); args [1] = LLVMConstInt (LLVMInt32Type (), region_start, 0); args [2] = LLVMConstInt (LLVMInt32Type (), region_end, 0); if (ctx->cfg->rgctx_var) { LLVMValueRef rgctx_alloc = ctx->addresses [ctx->cfg->rgctx_var->dreg]; g_assert (rgctx_alloc); args [3] = LLVMBuildLoad (builder, convert (ctx, rgctx_alloc, LLVMPointerType (IntPtrType (), 0)), ""); } else { args [3] = LLVMConstInt (IntPtrType (), 0, 0); } if (ctx->this_arg) args [4] = convert (ctx, ctx->this_arg, IntPtrType ()); else args [4] = LLVMConstInt (IntPtrType (), 0, 0); LLVMTypeRef match_sig = LLVMFunctionType5 (LLVMInt32Type (), IntPtrType (), LLVMInt32Type (), LLVMInt32Type (), IntPtrType (), IntPtrType (), FALSE); LLVMValueRef callee = ctx->module->match_exc; if (!callee) { if (ctx->cfg->compile_aot) { ctx->builder = builder; // get_callee expects ctx->builder to be the emitting builder callee = get_callee (ctx, match_sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name); } else { callee = ctx->module->match_exc = LLVMAddFunction (ctx->lmodule, icall_name, match_sig); LLVMAddGlobalMapping (ctx->module->ee, ctx->module->match_exc, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name)); ctx->module->match_exc = callee; mono_memory_barrier (); } } g_assert (builder && callee); g_assert (ctx->ex_var); return LLVMBuildCall (builder, callee, args, num_args, icall_name); } // FIXME: This won't work because the code-finding makes this // not a constant. /*#define MONO_PERSONALITY_DEBUG*/ #ifdef MONO_PERSONALITY_DEBUG static const gboolean use_debug_personality = TRUE; static const char *default_personality_name = "mono_debug_personality"; #else static const gboolean use_debug_personality = FALSE; static const char *default_personality_name = "__gxx_personality_v0"; #endif static LLVMTypeRef default_cpp_lpad_exc_signature (void) { static gboolean inited = FALSE; static LLVMTypeRef sig; if (!sig) { LLVMTypeRef signature [2]; signature [0] = LLVMPointerType (LLVMInt8Type (), 0); signature [1] = LLVMInt32Type (); sig = LLVMStructType (signature, 2, FALSE); inited = TRUE; } return sig; } static LLVMValueRef get_mono_personality (EmitContext *ctx) { LLVMValueRef personality = NULL; static gint32 mapping_inited = FALSE; LLVMTypeRef personality_type = LLVMFunctionType (LLVMInt32Type (), NULL, 0, TRUE); if (!use_debug_personality) { if (ctx->cfg->compile_aot) { personality = get_intrinsic (ctx, default_personality_name); } else if (InterlockedCompareExchange (&mapping_inited, 1, 0) == 0) { personality = LLVMAddFunction (ctx->lmodule, default_personality_name, personality_type); LLVMAddGlobalMapping (ctx->module->ee, personality, personality); } } else { if (ctx->cfg->compile_aot) { personality = get_callee (ctx, personality_type, MONO_PATCH_INFO_INTERNAL_METHOD, default_personality_name); } else { personality = LLVMAddFunction (ctx->lmodule, default_personality_name, personality_type); LLVMAddGlobalMapping (ctx->module->ee, personality, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, default_personality_name)); mono_memory_barrier (); } } g_assert (personality); return personality; } static LLVMBasicBlockRef emit_landing_pad (EmitContext *ctx, int group_index, int group_size) { MonoCompile *cfg = ctx->cfg; LLVMBuilderRef old_builder = ctx->builder; MonoExceptionClause *group_start = cfg->header->clauses + group_index; LLVMBuilderRef lpadBuilder = create_builder (ctx); ctx->builder = lpadBuilder; MonoBasicBlock *handler_bb = cfg->cil_offset_to_bb [CLAUSE_START (group_start)]; g_assert (handler_bb); // = landingpad personality + LLVMValueRef personality = get_mono_personality (ctx); g_assert (personality); char *bb_name = g_strdup_printf ("LPAD%d_BB", group_index); LLVMBasicBlockRef lpad_bb = gen_bb (ctx, bb_name); g_free (bb_name); LLVMPositionBuilderAtEnd (lpadBuilder, lpad_bb); LLVMValueRef landing_pad = LLVMBuildLandingPad (lpadBuilder, default_cpp_lpad_exc_signature (), personality, 0, ""); g_assert (landing_pad); LLVMValueRef cast = LLVMBuildBitCast (lpadBuilder, ctx->module->sentinel_exception, LLVMPointerType (LLVMInt8Type (), 0), "int8TypeInfo"); LLVMAddClause (landing_pad, cast); LLVMBasicBlockRef resume_bb = gen_bb (ctx, "RESUME_BB"); LLVMBuilderRef resume_builder = create_builder (ctx); ctx->builder = resume_builder; LLVMPositionBuilderAtEnd (resume_builder, resume_bb); emit_resume_eh (ctx, handler_bb); // Build match ctx->builder = lpadBuilder; LLVMPositionBuilderAtEnd (lpadBuilder, lpad_bb); gboolean finally_only = TRUE; MonoExceptionClause *group_cursor = group_start; for (int i = 0; i < group_size; i ++) { if (!(group_cursor->flags & MONO_EXCEPTION_CLAUSE_FINALLY || group_cursor->flags & MONO_EXCEPTION_CLAUSE_FAULT)) finally_only = FALSE; group_cursor++; } // FIXME: // Handle landing pad inlining if (!finally_only) { // So at each level of the exception stack we will match the exception again. // During that match, we need to compare against the handler types for the current // protected region. We send the try start and end so that we can only check against // handlers for this lexical protected region. LLVMValueRef match = mono_llvm_emit_match_exception_call (ctx, lpadBuilder, group_start->try_offset, group_start->try_offset + group_start->try_len); // if returns -1, resume LLVMValueRef switch_ins = LLVMBuildSwitch (lpadBuilder, match, resume_bb, group_size); // else move to that target bb for (int i = 0; i < group_size; i++) { MonoExceptionClause *clause = group_start + i; int clause_index = clause - cfg->header->clauses; MonoBasicBlock *handler_bb = (MonoBasicBlock*)g_hash_table_lookup (ctx->clause_to_handler, GINT_TO_POINTER (clause_index)); g_assert (handler_bb); g_assert (ctx->bblocks [handler_bb->block_num].call_handler_target_bb); LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), clause_index, FALSE), ctx->bblocks [handler_bb->block_num].call_handler_target_bb); } } else { int clause_index = group_start - cfg->header->clauses; MonoBasicBlock *finally_bb = (MonoBasicBlock*)g_hash_table_lookup (ctx->clause_to_handler, GINT_TO_POINTER (clause_index)); g_assert (finally_bb); LLVMBuildBr (ctx->builder, ctx->bblocks [finally_bb->block_num].call_handler_target_bb); } ctx->builder = old_builder; return lpad_bb; } static void emit_llvmonly_handler_start (EmitContext *ctx, MonoBasicBlock *bb, LLVMBasicBlockRef cbb) { int clause_index = MONO_REGION_CLAUSE_INDEX (bb->region); MonoExceptionClause *clause = &ctx->cfg->header->clauses [clause_index]; // Make exception available to catch blocks if (!(clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY || clause->flags & MONO_EXCEPTION_CLAUSE_FAULT)) { LLVMValueRef mono_exc = mono_llvm_emit_load_exception_call (ctx, ctx->builder); g_assert (ctx->ex_var); LLVMBuildStore (ctx->builder, LLVMBuildBitCast (ctx->builder, mono_exc, ObjRefType (), ""), ctx->ex_var); if (bb->in_scount == 1) { MonoInst *exvar = bb->in_stack [0]; g_assert (!ctx->values [exvar->dreg]); g_assert (ctx->ex_var); ctx->values [exvar->dreg] = LLVMBuildLoad (ctx->builder, ctx->ex_var, "save_exception"); emit_volatile_store (ctx, exvar->dreg); } mono_llvm_emit_clear_exception_call (ctx, ctx->builder); } LLVMBuilderRef handler_builder = create_builder (ctx); LLVMBasicBlockRef target_bb = ctx->bblocks [bb->block_num].call_handler_target_bb; LLVMPositionBuilderAtEnd (handler_builder, target_bb); // Make the handler code end with a jump to cbb LLVMBuildBr (handler_builder, cbb); } static void emit_handler_start (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef builder) { MonoCompile *cfg = ctx->cfg; LLVMValueRef *values = ctx->values; LLVMModuleRef lmodule = ctx->lmodule; BBInfo *bblocks = ctx->bblocks; LLVMTypeRef i8ptr; LLVMValueRef personality; LLVMValueRef landing_pad; LLVMBasicBlockRef target_bb; MonoInst *exvar; static int ti_generator; char ti_name [128]; LLVMValueRef type_info; int clause_index; GSList *l; // = landingpad personality + if (cfg->compile_aot) { /* Use a dummy personality function */ personality = LLVMGetNamedFunction (lmodule, "mono_personality"); g_assert (personality); } else { #if LLVM_API_VERSION > 100 /* Can't cache this as each method is in its own llvm module */ LLVMTypeRef personality_type = LLVMFunctionType (LLVMInt32Type (), NULL, 0, TRUE); personality = LLVMAddFunction (ctx->lmodule, "mono_personality", personality_type); mono_llvm_add_func_attr (personality, LLVM_ATTR_NO_UNWIND); LLVMBasicBlockRef entry_bb = LLVMAppendBasicBlock (personality, "ENTRY"); LLVMBuilderRef builder2 = LLVMCreateBuilder (); LLVMPositionBuilderAtEnd (builder2, entry_bb); LLVMBuildRet (builder2, LLVMConstInt (LLVMInt32Type (), 0, FALSE)); LLVMDisposeBuilder (builder2); #else static gint32 mapping_inited; personality = LLVMGetNamedFunction (lmodule, "mono_personality"); if (InterlockedCompareExchange (&mapping_inited, 1, 0) == 0) LLVMAddGlobalMapping (ctx->module->ee, personality, (gpointer)mono_personality); #endif } i8ptr = LLVMPointerType (LLVMInt8Type (), 0); clause_index = (mono_get_block_region_notry (cfg, bb->region) >> 8) - 1; /* * Create the type info */ sprintf (ti_name, "type_info_%d", ti_generator); ti_generator ++; if (cfg->compile_aot) { /* decode_eh_frame () in aot-runtime.c will decode this */ type_info = LLVMAddGlobal (lmodule, LLVMInt32Type (), ti_name); LLVMSetInitializer (type_info, LLVMConstInt (LLVMInt32Type (), clause_index, FALSE)); /* * These symbols are not really used, the clause_index is embedded into the EH tables generated by DwarfMonoException in LLVM. */ LLVMSetLinkage (type_info, LLVMInternalLinkage); } else { #if LLVM_API_VERSION > 100 type_info = LLVMAddGlobal (lmodule, LLVMInt32Type (), ti_name); LLVMSetInitializer (type_info, LLVMConstInt (LLVMInt32Type (), clause_index, FALSE)); #else gint32 *ti; /* * After the cfg mempool is freed, the type info will point to stale memory, * but this is not a problem, since we decode it once in exception_cb during * compilation. */ ti = (gint32*)mono_mempool_alloc (cfg->mempool, sizeof (gint32)); *(gint32*)ti = clause_index; type_info = LLVMAddGlobal (lmodule, i8ptr, ti_name); LLVMAddGlobalMapping (ctx->module->ee, type_info, ti); #endif } { LLVMTypeRef members [2], ret_type; members [0] = i8ptr; members [1] = LLVMInt32Type (); ret_type = LLVMStructType (members, 2, FALSE); landing_pad = LLVMBuildLandingPad (builder, ret_type, personality, 1, ""); LLVMAddClause (landing_pad, type_info); /* Store the exception into the exvar */ if (ctx->ex_var) LLVMBuildStore (builder, convert (ctx, LLVMBuildExtractValue (builder, landing_pad, 0, "ex_obj"), ObjRefType ()), ctx->ex_var); } /* * LLVM throw sites are associated with a one landing pad, and LLVM generated * code expects control to be transferred to this landing pad even in the * presence of nested clauses. The landing pad needs to branch to the landing * pads belonging to nested clauses based on the selector value returned by * the landing pad instruction, which is passed to the landing pad in a * register by the EH code. */ target_bb = bblocks [bb->block_num].call_handler_target_bb; g_assert (target_bb); /* * Branch to the correct landing pad */ LLVMValueRef ex_selector = LLVMBuildExtractValue (builder, landing_pad, 1, "ex_selector"); LLVMValueRef switch_ins = LLVMBuildSwitch (builder, ex_selector, target_bb, 0); for (l = ctx->nested_in [clause_index]; l; l = l->next) { int nesting_clause_index = GPOINTER_TO_INT (l->data); MonoBasicBlock *handler_bb; handler_bb = (MonoBasicBlock*)g_hash_table_lookup (ctx->clause_to_handler, GINT_TO_POINTER (nesting_clause_index)); g_assert (handler_bb); g_assert (ctx->bblocks [handler_bb->block_num].call_handler_target_bb); LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), nesting_clause_index, FALSE), ctx->bblocks [handler_bb->block_num].call_handler_target_bb); } /* Start a new bblock which CALL_HANDLER can branch to */ ctx->builder = builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, target_bb); ctx->bblocks [bb->block_num].end_bblock = target_bb; /* Store the exception into the IL level exvar */ if (bb->in_scount == 1) { g_assert (bb->in_scount == 1); exvar = bb->in_stack [0]; // FIXME: This is shared with filter clauses ? g_assert (!values [exvar->dreg]); g_assert (ctx->ex_var); values [exvar->dreg] = LLVMBuildLoad (builder, ctx->ex_var, ""); emit_volatile_store (ctx, exvar->dreg); } /* Make normal branches to the start of the clause branch to the new bblock */ bblocks [bb->block_num].bblock = target_bb; } static void process_bb (EmitContext *ctx, MonoBasicBlock *bb) { MonoCompile *cfg = ctx->cfg; MonoMethodSignature *sig = ctx->sig; LLVMValueRef method = ctx->lmethod; LLVMValueRef *values = ctx->values; LLVMValueRef *addresses = ctx->addresses; LLVMCallInfo *linfo = ctx->linfo; BBInfo *bblocks = ctx->bblocks; MonoInst *ins; LLVMBasicBlockRef cbb; LLVMBuilderRef builder, starting_builder; gboolean has_terminator; LLVMValueRef v; LLVMValueRef lhs, rhs; int nins = 0; cbb = get_end_bb (ctx, bb); builder = create_builder (ctx); ctx->builder = builder; LLVMPositionBuilderAtEnd (builder, cbb); if (!ctx_ok (ctx)) return; if (bb->flags & BB_EXCEPTION_HANDLER) { if (!ctx->llvm_only && !bblocks [bb->block_num].invoke_target) { set_failure (ctx, "handler without invokes"); return; } if (ctx->llvm_only) emit_llvmonly_handler_start (ctx, bb, cbb); else emit_handler_start (ctx, bb, builder); if (!ctx_ok (ctx)) return; builder = ctx->builder; } has_terminator = FALSE; starting_builder = builder; for (ins = bb->code; ins; ins = ins->next) { const char *spec = LLVM_INS_INFO (ins->opcode); char *dname = NULL; char dname_buf [128]; emit_dbg_loc (ctx, builder, ins->cil_code); nins ++; if (nins > 1000) { /* * Some steps in llc are non-linear in the size of basic blocks, see #5714. * Start a new bblock. * Prevent the bblocks to be merged by doing a volatile load + cond branch * from localloc-ed memory. */ if (!cfg->llvm_only) ;//set_failure (ctx, "basic block too long"); if (!ctx->long_bb_break_var) { ctx->long_bb_break_var = build_alloca_llvm_type_name (ctx, LLVMInt32Type (), 0, "long_bb_break"); mono_llvm_build_store (ctx->alloca_builder, LLVMConstInt (LLVMInt32Type (), 0, FALSE), ctx->long_bb_break_var, TRUE, LLVM_BARRIER_NONE); } cbb = gen_bb (ctx, "CONT_LONG_BB"); LLVMBasicBlockRef dummy_bb = gen_bb (ctx, "CONT_LONG_BB_DUMMY"); LLVMValueRef load = mono_llvm_build_load (builder, ctx->long_bb_break_var, "", TRUE); /* * The long_bb_break_var is initialized to 0 in the prolog, so this branch will always go to 'cbb' * but llvm doesn't know that, so the branch is not going to be eliminated. */ LLVMValueRef cmp = LLVMBuildICmp (builder, LLVMIntEQ, load, LLVMConstInt (LLVMInt32Type (), 0, FALSE), ""); LLVMBuildCondBr (builder, cmp, cbb, dummy_bb); /* Emit a dummy false bblock which does nothing but contains a volatile store so it cannot be eliminated */ ctx->builder = builder = create_builder (ctx); LLVMPositionBuilderAtEnd (builder, dummy_bb); mono_llvm_build_store (builder, LLVMConstInt (LLVMInt32Type (), 1, FALSE), ctx->long_bb_break_var, TRUE, LLVM_BARRIER_NONE); LLVMBuildBr (builder, cbb); ctx->builder = builder = create_builder (ctx); LLVMPositionBuilderAtEnd (builder, cbb); ctx->bblocks [bb->block_num].end_bblock = cbb; nins = 0; } if (has_terminator) /* There could be instructions after a terminator, skip them */ break; if (spec [MONO_INST_DEST] != ' ' && !MONO_IS_STORE_MEMBASE (ins)) { sprintf (dname_buf, "t%d", ins->dreg); dname = dname_buf; } if (spec [MONO_INST_SRC1] != ' ' && spec [MONO_INST_SRC1] != 'v') { MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1); if (var && var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) && var->opcode != OP_GSHAREDVT_ARG_REGOFFSET) { lhs = emit_volatile_load (ctx, ins->sreg1); } else { /* It is ok for SETRET to have an uninitialized argument */ if (!values [ins->sreg1] && ins->opcode != OP_SETRET) { set_failure (ctx, "sreg1"); return; } lhs = values [ins->sreg1]; } } else { lhs = NULL; } if (spec [MONO_INST_SRC2] != ' ' && spec [MONO_INST_SRC2] != ' ') { MonoInst *var = get_vreg_to_inst (cfg, ins->sreg2); if (var && var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) { rhs = emit_volatile_load (ctx, ins->sreg2); } else { if (!values [ins->sreg2]) { set_failure (ctx, "sreg2"); return; } rhs = values [ins->sreg2]; } } else { rhs = NULL; } //mono_print_ins (ins); switch (ins->opcode) { case OP_NOP: case OP_NOT_NULL: case OP_LIVERANGE_START: case OP_LIVERANGE_END: break; case OP_ICONST: values [ins->dreg] = LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE); break; case OP_I8CONST: #if SIZEOF_VOID_P == 4 values [ins->dreg] = LLVMConstInt (LLVMInt64Type (), GET_LONG_IMM (ins), FALSE); #else values [ins->dreg] = LLVMConstInt (LLVMInt64Type (), (gint64)ins->inst_c0, FALSE); #endif break; case OP_R8CONST: values [ins->dreg] = LLVMConstReal (LLVMDoubleType (), *(double*)ins->inst_p0); break; case OP_R4CONST: if (cfg->r4fp) values [ins->dreg] = LLVMConstReal (LLVMFloatType (), *(float*)ins->inst_p0); else values [ins->dreg] = LLVMConstFPExt (LLVMConstReal (LLVMFloatType (), *(float*)ins->inst_p0), LLVMDoubleType ()); break; case OP_DUMMY_ICONST: values [ins->dreg] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); break; case OP_DUMMY_I8CONST: values [ins->dreg] = LLVMConstInt (LLVMInt64Type (), 0, FALSE); break; case OP_DUMMY_R8CONST: values [ins->dreg] = LLVMConstReal (LLVMDoubleType (), 0.0f); break; case OP_BR: { LLVMBasicBlockRef target_bb = get_bb (ctx, ins->inst_target_bb); LLVMBuildBr (builder, target_bb); has_terminator = TRUE; break; } case OP_SWITCH: { int i; LLVMValueRef v; char bb_name [128]; LLVMBasicBlockRef new_bb; LLVMBuilderRef new_builder; // The default branch is already handled // FIXME: Handle it here /* Start new bblock */ sprintf (bb_name, "SWITCH_DEFAULT_BB%d", ctx->default_index ++); new_bb = LLVMAppendBasicBlock (ctx->lmethod, bb_name); lhs = convert (ctx, lhs, LLVMInt32Type ()); v = LLVMBuildSwitch (builder, lhs, new_bb, GPOINTER_TO_UINT (ins->klass)); for (i = 0; i < GPOINTER_TO_UINT (ins->klass); ++i) { MonoBasicBlock *target_bb = ins->inst_many_bb [i]; LLVMAddCase (v, LLVMConstInt (LLVMInt32Type (), i, FALSE), get_bb (ctx, target_bb)); } new_builder = create_builder (ctx); LLVMPositionBuilderAtEnd (new_builder, new_bb); LLVMBuildUnreachable (new_builder); has_terminator = TRUE; g_assert (!ins->next); break; } case OP_SETRET: switch (linfo->ret.storage) { case LLVMArgVtypeInReg: { LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method))); LLVMValueRef val, addr, retval; int i; retval = LLVMGetUndef (ret_type); if (!addresses [ins->sreg1]) { /* * The return type is an LLVM vector type, have to convert between it and the * real return type which is a struct type. */ g_assert (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (sig->ret))); /* Convert to 2xi64 first */ val = LLVMBuildBitCast (builder, values [ins->sreg1], LLVMVectorType (IntPtrType (), 2), ""); for (i = 0; i < 2; ++i) { if (linfo->ret.pair_storage [i] == LLVMArgInIReg) { retval = LLVMBuildInsertValue (builder, retval, LLVMBuildExtractElement (builder, val, LLVMConstInt (LLVMInt32Type (), i, FALSE), ""), i, ""); } else { g_assert (linfo->ret.pair_storage [i] == LLVMArgNone); } } } else { addr = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""); for (i = 0; i < 2; ++i) { if (linfo->ret.pair_storage [i] == LLVMArgInIReg) { LLVMValueRef indexes [2], part_addr; indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); indexes [1] = LLVMConstInt (LLVMInt32Type (), i, FALSE); part_addr = LLVMBuildGEP (builder, addr, indexes, 2, ""); retval = LLVMBuildInsertValue (builder, retval, LLVMBuildLoad (builder, part_addr, ""), i, ""); } else { g_assert (linfo->ret.pair_storage [i] == LLVMArgNone); } } } LLVMBuildRet (builder, retval); break; } case LLVMArgVtypeAsScalar: { LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method))); LLVMValueRef retval; g_assert (addresses [ins->sreg1]); retval = LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""), ""); LLVMBuildRet (builder, retval); break; } case LLVMArgVtypeByVal: { LLVMValueRef retval; g_assert (addresses [ins->sreg1]); retval = LLVMBuildLoad (builder, addresses [ins->sreg1], ""); LLVMBuildRet (builder, retval); break; } case LLVMArgVtypeByRef: { LLVMBuildRetVoid (builder); break; } case LLVMArgGsharedvtFixed: { LLVMTypeRef ret_type = type_to_llvm_type (ctx, sig->ret); /* The return value is in lhs, need to store to the vret argument */ /* sreg1 might not be set */ if (lhs) { g_assert (cfg->vret_addr); g_assert (values [cfg->vret_addr->dreg]); LLVMBuildStore (builder, convert (ctx, lhs, ret_type), convert (ctx, values [cfg->vret_addr->dreg], LLVMPointerType (ret_type, 0))); } LLVMBuildRetVoid (builder); break; } case LLVMArgGsharedvtFixedVtype: { /* Already set */ LLVMBuildRetVoid (builder); break; } case LLVMArgGsharedvtVariable: { /* Already set */ LLVMBuildRetVoid (builder); break; } case LLVMArgVtypeRetAddr: { LLVMBuildRetVoid (builder); break; } case LLVMArgAsIArgs: case LLVMArgFpStruct: { LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method))); LLVMValueRef retval; g_assert (addresses [ins->sreg1]); retval = LLVMBuildLoad (builder, convert (ctx, addresses [ins->sreg1], LLVMPointerType (ret_type, 0)), ""); LLVMBuildRet (builder, retval); break; } case LLVMArgNone: case LLVMArgNormal: { if (!lhs || ctx->is_dead [ins->sreg1]) { /* * The method did not set its return value, probably because it * ends with a throw. */ if (cfg->vret_addr) LLVMBuildRetVoid (builder); else LLVMBuildRet (builder, LLVMConstNull (type_to_llvm_type (ctx, sig->ret))); } else { LLVMBuildRet (builder, convert (ctx, lhs, type_to_llvm_type (ctx, sig->ret))); } has_terminator = TRUE; break; } default: g_assert_not_reached (); break; } break; case OP_ICOMPARE: case OP_FCOMPARE: case OP_RCOMPARE: case OP_LCOMPARE: case OP_COMPARE: case OP_ICOMPARE_IMM: case OP_LCOMPARE_IMM: case OP_COMPARE_IMM: { CompRelation rel; LLVMValueRef cmp, args [16]; gboolean likely = (ins->flags & MONO_INST_LIKELY) != 0; if (ins->next->opcode == OP_NOP) break; if (ins->next->opcode == OP_BR) /* The comparison result is not needed */ continue; rel = mono_opcode_to_cond (ins->next->opcode); if (ins->opcode == OP_ICOMPARE_IMM) { lhs = convert (ctx, lhs, LLVMInt32Type ()); rhs = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE); } if (ins->opcode == OP_LCOMPARE_IMM) { lhs = convert (ctx, lhs, LLVMInt64Type ()); rhs = LLVMConstInt (LLVMInt64Type (), GET_LONG_IMM (ins), FALSE); } if (ins->opcode == OP_LCOMPARE) { lhs = convert (ctx, lhs, LLVMInt64Type ()); rhs = convert (ctx, rhs, LLVMInt64Type ()); } if (ins->opcode == OP_ICOMPARE) { lhs = convert (ctx, lhs, LLVMInt32Type ()); rhs = convert (ctx, rhs, LLVMInt32Type ()); } if (lhs && rhs) { if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind) rhs = convert (ctx, rhs, LLVMTypeOf (lhs)); else if (LLVMGetTypeKind (LLVMTypeOf (rhs)) == LLVMPointerTypeKind) lhs = convert (ctx, lhs, LLVMTypeOf (rhs)); } /* We use COMPARE+SETcc/Bcc, llvm uses SETcc+br cond */ if (ins->opcode == OP_FCOMPARE) { cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMDoubleType ()), convert (ctx, rhs, LLVMDoubleType ()), ""); } else if (ins->opcode == OP_RCOMPARE) { cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMFloatType ()), convert (ctx, rhs, LLVMFloatType ()), ""); } else if (ins->opcode == OP_COMPARE_IMM) { if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind && ins->inst_imm == 0) cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], lhs, LLVMConstNull (LLVMTypeOf (lhs)), ""); else cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], convert (ctx, lhs, IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), ""); } else if (ins->opcode == OP_LCOMPARE_IMM) { if (SIZEOF_REGISTER == 4 && COMPILE_LLVM (cfg)) { /* The immediate is encoded in two fields */ guint64 l = ((guint64)(guint32)ins->inst_offset << 32) | ((guint32)ins->inst_imm); cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], convert (ctx, lhs, LLVMInt64Type ()), LLVMConstInt (LLVMInt64Type (), l, FALSE), ""); } else { cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], convert (ctx, lhs, LLVMInt64Type ()), LLVMConstInt (LLVMInt64Type (), ins->inst_imm, FALSE), ""); } } else if (ins->opcode == OP_COMPARE) { if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind && LLVMTypeOf (lhs) == LLVMTypeOf (rhs)) cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], lhs, rhs, ""); else cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], convert (ctx, lhs, IntPtrType ()), convert (ctx, rhs, IntPtrType ()), ""); } else cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], lhs, rhs, ""); if (likely) { args [0] = cmp; args [1] = LLVMConstInt (LLVMInt1Type (), 1, FALSE); cmp = LLVMBuildCall (ctx->builder, get_intrinsic (ctx, "llvm.expect.i1"), args, 2, ""); } if (MONO_IS_COND_BRANCH_OP (ins->next)) { if (ins->next->inst_true_bb == ins->next->inst_false_bb) { /* * If the target bb contains PHI instructions, LLVM requires * two PHI entries for this bblock, while we only generate one. * So convert this to an unconditional bblock. (bxc #171). */ LLVMBuildBr (builder, get_bb (ctx, ins->next->inst_true_bb)); } else { LLVMBuildCondBr (builder, cmp, get_bb (ctx, ins->next->inst_true_bb), get_bb (ctx, ins->next->inst_false_bb)); } has_terminator = TRUE; } else if (MONO_IS_SETCC (ins->next)) { sprintf (dname_buf, "t%d", ins->next->dreg); dname = dname_buf; values [ins->next->dreg] = LLVMBuildZExt (builder, cmp, LLVMInt32Type (), dname); /* Add stores for volatile variables */ emit_volatile_store (ctx, ins->next->dreg); } else if (MONO_IS_COND_EXC (ins->next)) { emit_cond_system_exception (ctx, bb, (const char*)ins->next->inst_p1, cmp); if (!ctx_ok (ctx)) break; builder = ctx->builder; } else { set_failure (ctx, "next"); break; } ins = ins->next; break; } case OP_FCEQ: case OP_FCNEQ: case OP_FCLT: case OP_FCLT_UN: case OP_FCGT: case OP_FCGT_UN: case OP_FCGE: case OP_FCLE: { CompRelation rel; LLVMValueRef cmp; rel = mono_opcode_to_cond (ins->opcode); cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMDoubleType ()), convert (ctx, rhs, LLVMDoubleType ()), ""); values [ins->dreg] = LLVMBuildZExt (builder, cmp, LLVMInt32Type (), dname); break; } case OP_RCEQ: case OP_RCLT: case OP_RCLT_UN: case OP_RCGT: case OP_RCGT_UN: { CompRelation rel; LLVMValueRef cmp; rel = mono_opcode_to_cond (ins->opcode); cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMFloatType ()), convert (ctx, rhs, LLVMFloatType ()), ""); values [ins->dreg] = LLVMBuildZExt (builder, cmp, LLVMInt32Type (), dname); break; } case OP_PHI: case OP_FPHI: case OP_VPHI: case OP_XPHI: { int i; gboolean empty = TRUE; /* Check that all input bblocks really branch to us */ for (i = 0; i < bb->in_count; ++i) { if (bb->in_bb [i]->last_ins && bb->in_bb [i]->last_ins->opcode == OP_NOT_REACHED) ins->inst_phi_args [i + 1] = -1; else empty = FALSE; } if (empty) { /* LLVM doesn't like phi instructions with zero operands */ ctx->is_dead [ins->dreg] = TRUE; break; } /* Created earlier, insert it now */ LLVMInsertIntoBuilder (builder, values [ins->dreg]); for (i = 0; i < ins->inst_phi_args [0]; i++) { int sreg1 = ins->inst_phi_args [i + 1]; int count, j; /* * Count the number of times the incoming bblock branches to us, * since llvm requires a separate entry for each. */ if (bb->in_bb [i]->last_ins && bb->in_bb [i]->last_ins->opcode == OP_SWITCH) { MonoInst *switch_ins = bb->in_bb [i]->last_ins; count = 0; for (j = 0; j < GPOINTER_TO_UINT (switch_ins->klass); ++j) { if (switch_ins->inst_many_bb [j] == bb) count ++; } } else { count = 1; } /* Remember for later */ for (j = 0; j < count; ++j) { PhiNode *node = (PhiNode*)mono_mempool_alloc0 (ctx->mempool, sizeof (PhiNode)); node->bb = bb; node->phi = ins; node->in_bb = bb->in_bb [i]; node->sreg = sreg1; bblocks [bb->in_bb [i]->block_num].phi_nodes = g_slist_prepend_mempool (ctx->mempool, bblocks [bb->in_bb [i]->block_num].phi_nodes, node); } } break; } case OP_MOVE: case OP_LMOVE: case OP_XMOVE: case OP_SETFRET: g_assert (lhs); values [ins->dreg] = lhs; break; case OP_FMOVE: case OP_RMOVE: { MonoInst *var = get_vreg_to_inst (cfg, ins->dreg); g_assert (lhs); values [ins->dreg] = lhs; if (var && var->klass->byval_arg.type == MONO_TYPE_R4) { /* * This is added by the spilling pass in case of the JIT, * but we have to do it ourselves. */ values [ins->dreg] = convert (ctx, values [ins->dreg], LLVMFloatType ()); } break; } case OP_MOVE_F_TO_I4: { values [ins->dreg] = LLVMBuildBitCast (builder, LLVMBuildFPTrunc (builder, lhs, LLVMFloatType (), ""), LLVMInt32Type (), ""); break; } case OP_MOVE_I4_TO_F: { values [ins->dreg] = LLVMBuildFPExt (builder, LLVMBuildBitCast (builder, lhs, LLVMFloatType (), ""), LLVMDoubleType (), ""); break; } case OP_MOVE_F_TO_I8: { values [ins->dreg] = LLVMBuildBitCast (builder, lhs, LLVMInt64Type (), ""); break; } case OP_MOVE_I8_TO_F: { values [ins->dreg] = LLVMBuildBitCast (builder, lhs, LLVMDoubleType (), ""); break; } case OP_IADD: case OP_ISUB: case OP_IAND: case OP_IMUL: case OP_IDIV: case OP_IDIV_UN: case OP_IREM: case OP_IREM_UN: case OP_IOR: case OP_IXOR: case OP_ISHL: case OP_ISHR: case OP_ISHR_UN: case OP_FADD: case OP_FSUB: case OP_FMUL: case OP_FDIV: case OP_LADD: case OP_LSUB: case OP_LMUL: case OP_LDIV: case OP_LDIV_UN: case OP_LREM: case OP_LREM_UN: case OP_LAND: case OP_LOR: case OP_LXOR: case OP_LSHL: case OP_LSHR: case OP_LSHR_UN: lhs = convert (ctx, lhs, regtype_to_llvm_type (spec [MONO_INST_DEST])); rhs = convert (ctx, rhs, regtype_to_llvm_type (spec [MONO_INST_DEST])); emit_div_check (ctx, builder, bb, ins, lhs, rhs); if (!ctx_ok (ctx)) break; builder = ctx->builder; switch (ins->opcode) { case OP_IADD: case OP_LADD: values [ins->dreg] = LLVMBuildAdd (builder, lhs, rhs, dname); break; case OP_ISUB: case OP_LSUB: values [ins->dreg] = LLVMBuildSub (builder, lhs, rhs, dname); break; case OP_IMUL: case OP_LMUL: values [ins->dreg] = LLVMBuildMul (builder, lhs, rhs, dname); break; case OP_IREM: case OP_LREM: values [ins->dreg] = LLVMBuildSRem (builder, lhs, rhs, dname); break; case OP_IREM_UN: case OP_LREM_UN: values [ins->dreg] = LLVMBuildURem (builder, lhs, rhs, dname); break; case OP_IDIV: case OP_LDIV: values [ins->dreg] = LLVMBuildSDiv (builder, lhs, rhs, dname); break; case OP_IDIV_UN: case OP_LDIV_UN: values [ins->dreg] = LLVMBuildUDiv (builder, lhs, rhs, dname); break; case OP_FDIV: case OP_RDIV: values [ins->dreg] = LLVMBuildFDiv (builder, lhs, rhs, dname); break; case OP_IAND: case OP_LAND: values [ins->dreg] = LLVMBuildAnd (builder, lhs, rhs, dname); break; case OP_IOR: case OP_LOR: values [ins->dreg] = LLVMBuildOr (builder, lhs, rhs, dname); break; case OP_IXOR: case OP_LXOR: values [ins->dreg] = LLVMBuildXor (builder, lhs, rhs, dname); break; case OP_ISHL: case OP_LSHL: values [ins->dreg] = LLVMBuildShl (builder, lhs, rhs, dname); break; case OP_ISHR: case OP_LSHR: values [ins->dreg] = LLVMBuildAShr (builder, lhs, rhs, dname); break; case OP_ISHR_UN: case OP_LSHR_UN: values [ins->dreg] = LLVMBuildLShr (builder, lhs, rhs, dname); break; case OP_FADD: values [ins->dreg] = LLVMBuildFAdd (builder, lhs, rhs, dname); break; case OP_FSUB: values [ins->dreg] = LLVMBuildFSub (builder, lhs, rhs, dname); break; case OP_FMUL: values [ins->dreg] = LLVMBuildFMul (builder, lhs, rhs, dname); break; default: g_assert_not_reached (); } break; case OP_RADD: case OP_RSUB: case OP_RMUL: case OP_RDIV: { lhs = convert (ctx, lhs, LLVMFloatType ()); rhs = convert (ctx, rhs, LLVMFloatType ()); switch (ins->opcode) { case OP_RADD: values [ins->dreg] = LLVMBuildFAdd (builder, lhs, rhs, dname); break; case OP_RSUB: values [ins->dreg] = LLVMBuildFSub (builder, lhs, rhs, dname); break; case OP_RMUL: values [ins->dreg] = LLVMBuildFMul (builder, lhs, rhs, dname); break; case OP_RDIV: values [ins->dreg] = LLVMBuildFDiv (builder, lhs, rhs, dname); break; default: g_assert_not_reached (); break; } break; } case OP_IADD_IMM: case OP_ISUB_IMM: case OP_IMUL_IMM: case OP_IREM_IMM: case OP_IREM_UN_IMM: case OP_IDIV_IMM: case OP_IDIV_UN_IMM: case OP_IAND_IMM: case OP_IOR_IMM: case OP_IXOR_IMM: case OP_ISHL_IMM: case OP_ISHR_IMM: case OP_ISHR_UN_IMM: case OP_LADD_IMM: case OP_LSUB_IMM: case OP_LMUL_IMM: case OP_LREM_IMM: case OP_LAND_IMM: case OP_LOR_IMM: case OP_LXOR_IMM: case OP_LSHL_IMM: case OP_LSHR_IMM: case OP_LSHR_UN_IMM: case OP_ADD_IMM: case OP_AND_IMM: case OP_MUL_IMM: case OP_SHL_IMM: case OP_SHR_IMM: case OP_SHR_UN_IMM: { LLVMValueRef imm; if (spec [MONO_INST_SRC1] == 'l') { imm = LLVMConstInt (LLVMInt64Type (), GET_LONG_IMM (ins), FALSE); } else { imm = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE); } emit_div_check (ctx, builder, bb, ins, lhs, imm); if (!ctx_ok (ctx)) break; builder = ctx->builder; #if SIZEOF_VOID_P == 4 if (ins->opcode == OP_LSHL_IMM || ins->opcode == OP_LSHR_IMM || ins->opcode == OP_LSHR_UN_IMM) imm = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE); #endif if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind) lhs = convert (ctx, lhs, IntPtrType ()); imm = convert (ctx, imm, LLVMTypeOf (lhs)); switch (ins->opcode) { case OP_IADD_IMM: case OP_LADD_IMM: case OP_ADD_IMM: values [ins->dreg] = LLVMBuildAdd (builder, lhs, imm, dname); break; case OP_ISUB_IMM: case OP_LSUB_IMM: values [ins->dreg] = LLVMBuildSub (builder, lhs, imm, dname); break; case OP_IMUL_IMM: case OP_MUL_IMM: case OP_LMUL_IMM: values [ins->dreg] = LLVMBuildMul (builder, lhs, imm, dname); break; case OP_IDIV_IMM: case OP_LDIV_IMM: values [ins->dreg] = LLVMBuildSDiv (builder, lhs, imm, dname); break; case OP_IDIV_UN_IMM: case OP_LDIV_UN_IMM: values [ins->dreg] = LLVMBuildUDiv (builder, lhs, imm, dname); break; case OP_IREM_IMM: case OP_LREM_IMM: values [ins->dreg] = LLVMBuildSRem (builder, lhs, imm, dname); break; case OP_IREM_UN_IMM: values [ins->dreg] = LLVMBuildURem (builder, lhs, imm, dname); break; case OP_IAND_IMM: case OP_LAND_IMM: case OP_AND_IMM: values [ins->dreg] = LLVMBuildAnd (builder, lhs, imm, dname); break; case OP_IOR_IMM: case OP_LOR_IMM: values [ins->dreg] = LLVMBuildOr (builder, lhs, imm, dname); break; case OP_IXOR_IMM: case OP_LXOR_IMM: values [ins->dreg] = LLVMBuildXor (builder, lhs, imm, dname); break; case OP_ISHL_IMM: case OP_LSHL_IMM: case OP_SHL_IMM: values [ins->dreg] = LLVMBuildShl (builder, lhs, imm, dname); break; case OP_ISHR_IMM: case OP_LSHR_IMM: case OP_SHR_IMM: values [ins->dreg] = LLVMBuildAShr (builder, lhs, imm, dname); break; case OP_ISHR_UN_IMM: /* This is used to implement conv.u4, so the lhs could be an i8 */ lhs = convert (ctx, lhs, LLVMInt32Type ()); imm = convert (ctx, imm, LLVMInt32Type ()); values [ins->dreg] = LLVMBuildLShr (builder, lhs, imm, dname); break; case OP_LSHR_UN_IMM: case OP_SHR_UN_IMM: values [ins->dreg] = LLVMBuildLShr (builder, lhs, imm, dname); break; default: g_assert_not_reached (); } break; } case OP_INEG: values [ins->dreg] = LLVMBuildSub (builder, LLVMConstInt (LLVMInt32Type (), 0, FALSE), convert (ctx, lhs, LLVMInt32Type ()), dname); break; case OP_LNEG: values [ins->dreg] = LLVMBuildSub (builder, LLVMConstInt (LLVMInt64Type (), 0, FALSE), lhs, dname); break; case OP_FNEG: lhs = convert (ctx, lhs, LLVMDoubleType ()); values [ins->dreg] = LLVMBuildFSub (builder, LLVMConstReal (LLVMDoubleType (), 0.0), lhs, dname); break; case OP_RNEG: lhs = convert (ctx, lhs, LLVMFloatType ()); values [ins->dreg] = LLVMBuildFSub (builder, LLVMConstReal (LLVMFloatType (), 0.0), lhs, dname); break; case OP_INOT: { guint32 v = 0xffffffff; values [ins->dreg] = LLVMBuildXor (builder, LLVMConstInt (LLVMInt32Type (), v, FALSE), convert (ctx, lhs, LLVMInt32Type ()), dname); break; } case OP_LNOT: { guint64 v = 0xffffffffffffffffLL; values [ins->dreg] = LLVMBuildXor (builder, LLVMConstInt (LLVMInt64Type (), v, FALSE), lhs, dname); break; } #if defined(TARGET_X86) || defined(TARGET_AMD64) case OP_X86_LEA: { LLVMValueRef v1, v2; rhs = LLVMBuildSExt (builder, convert (ctx, rhs, LLVMInt32Type ()), LLVMInt64Type (), ""); v1 = LLVMBuildMul (builder, convert (ctx, rhs, IntPtrType ()), LLVMConstInt (IntPtrType (), (1 << ins->backend.shift_amount), FALSE), ""); v2 = LLVMBuildAdd (builder, convert (ctx, lhs, IntPtrType ()), v1, ""); values [ins->dreg] = LLVMBuildAdd (builder, v2, LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), dname); break; } #endif case OP_ICONV_TO_I1: case OP_ICONV_TO_I2: case OP_ICONV_TO_I4: case OP_ICONV_TO_U1: case OP_ICONV_TO_U2: case OP_ICONV_TO_U4: case OP_LCONV_TO_I1: case OP_LCONV_TO_I2: case OP_LCONV_TO_U1: case OP_LCONV_TO_U2: case OP_LCONV_TO_U4: { gboolean sign; sign = (ins->opcode == OP_ICONV_TO_I1) || (ins->opcode == OP_ICONV_TO_I2) || (ins->opcode == OP_ICONV_TO_I4) || (ins->opcode == OP_LCONV_TO_I1) || (ins->opcode == OP_LCONV_TO_I2); /* Have to do two casts since our vregs have type int */ v = LLVMBuildTrunc (builder, lhs, op_to_llvm_type (ins->opcode), ""); if (sign) values [ins->dreg] = LLVMBuildSExt (builder, v, LLVMInt32Type (), dname); else values [ins->dreg] = LLVMBuildZExt (builder, v, LLVMInt32Type (), dname); break; } case OP_ICONV_TO_I8: values [ins->dreg] = LLVMBuildSExt (builder, lhs, LLVMInt64Type (), dname); break; case OP_ICONV_TO_U8: values [ins->dreg] = LLVMBuildZExt (builder, lhs, LLVMInt64Type (), dname); break; case OP_FCONV_TO_I4: case OP_RCONV_TO_I4: values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMInt32Type (), dname); break; case OP_FCONV_TO_I1: case OP_RCONV_TO_I1: values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), ""); break; case OP_FCONV_TO_U1: case OP_RCONV_TO_U1: values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildTrunc (builder, LLVMBuildFPToUI (builder, lhs, IntPtrType (), dname), LLVMInt8Type (), ""), LLVMInt32Type (), ""); break; case OP_FCONV_TO_I2: case OP_RCONV_TO_I2: values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), ""); break; case OP_FCONV_TO_U2: case OP_RCONV_TO_U2: values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), ""); break; case OP_RCONV_TO_U4: values [ins->dreg] = LLVMBuildFPToUI (builder, lhs, LLVMInt32Type (), dname); break; case OP_FCONV_TO_I8: case OP_RCONV_TO_I8: values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMInt64Type (), dname); break; case OP_FCONV_TO_I: values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, IntPtrType (), dname); break; case OP_ICONV_TO_R8: case OP_LCONV_TO_R8: values [ins->dreg] = LLVMBuildSIToFP (builder, lhs, LLVMDoubleType (), dname); break; case OP_ICONV_TO_R_UN: case OP_LCONV_TO_R_UN: values [ins->dreg] = LLVMBuildUIToFP (builder, lhs, LLVMDoubleType (), dname); break; #if SIZEOF_VOID_P == 4 case OP_LCONV_TO_U: #endif case OP_LCONV_TO_I4: values [ins->dreg] = LLVMBuildTrunc (builder, lhs, LLVMInt32Type (), dname); break; case OP_ICONV_TO_R4: case OP_LCONV_TO_R4: v = LLVMBuildSIToFP (builder, lhs, LLVMFloatType (), ""); if (cfg->r4fp) values [ins->dreg] = v; else values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname); break; case OP_FCONV_TO_R4: v = LLVMBuildFPTrunc (builder, lhs, LLVMFloatType (), ""); if (cfg->r4fp) values [ins->dreg] = v; else values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname); break; case OP_RCONV_TO_R8: values [ins->dreg] = LLVMBuildFPExt (builder, lhs, LLVMDoubleType (), dname); break; case OP_RCONV_TO_R4: values [ins->dreg] = lhs; break; case OP_SEXT_I4: values [ins->dreg] = LLVMBuildSExt (builder, convert (ctx, lhs, LLVMInt32Type ()), LLVMInt64Type (), dname); break; case OP_ZEXT_I4: values [ins->dreg] = LLVMBuildZExt (builder, convert (ctx, lhs, LLVMInt32Type ()), LLVMInt64Type (), dname); break; case OP_TRUNC_I4: values [ins->dreg] = LLVMBuildTrunc (builder, lhs, LLVMInt32Type (), dname); break; case OP_LOCALLOC_IMM: { LLVMValueRef v; guint32 size = ins->inst_imm; size = (size + (MONO_ARCH_FRAME_ALIGNMENT - 1)) & ~ (MONO_ARCH_FRAME_ALIGNMENT - 1); v = mono_llvm_build_alloca (builder, LLVMInt8Type (), LLVMConstInt (LLVMInt32Type (), size, FALSE), MONO_ARCH_FRAME_ALIGNMENT, ""); if (ins->flags & MONO_INST_INIT) { LLVMValueRef args [5]; args [0] = v; args [1] = LLVMConstInt (LLVMInt8Type (), 0, FALSE); args [2] = LLVMConstInt (LLVMInt32Type (), size, FALSE); args [3] = LLVMConstInt (LLVMInt32Type (), MONO_ARCH_FRAME_ALIGNMENT, FALSE); args [4] = LLVMConstInt (LLVMInt1Type (), 0, FALSE); LLVMBuildCall (builder, get_intrinsic (ctx, "llvm.memset.p0i8.i32"), args, 5, ""); } values [ins->dreg] = v; break; } case OP_LOCALLOC: { LLVMValueRef v, size; size = LLVMBuildAnd (builder, LLVMBuildAdd (builder, convert (ctx, lhs, LLVMInt32Type ()), LLVMConstInt (LLVMInt32Type (), MONO_ARCH_FRAME_ALIGNMENT - 1, FALSE), ""), LLVMConstInt (LLVMInt32Type (), ~ (MONO_ARCH_FRAME_ALIGNMENT - 1), FALSE), ""); v = mono_llvm_build_alloca (builder, LLVMInt8Type (), size, MONO_ARCH_FRAME_ALIGNMENT, ""); if (ins->flags & MONO_INST_INIT) { LLVMValueRef args [5]; args [0] = v; args [1] = LLVMConstInt (LLVMInt8Type (), 0, FALSE); args [2] = size; args [3] = LLVMConstInt (LLVMInt32Type (), MONO_ARCH_FRAME_ALIGNMENT, FALSE); args [4] = LLVMConstInt (LLVMInt1Type (), 0, FALSE); LLVMBuildCall (builder, get_intrinsic (ctx, "llvm.memset.p0i8.i32"), args, 5, ""); } values [ins->dreg] = v; break; } case OP_LOADI1_MEMBASE: case OP_LOADU1_MEMBASE: case OP_LOADI2_MEMBASE: case OP_LOADU2_MEMBASE: case OP_LOADI4_MEMBASE: case OP_LOADU4_MEMBASE: case OP_LOADI8_MEMBASE: case OP_LOADR4_MEMBASE: case OP_LOADR8_MEMBASE: case OP_LOAD_MEMBASE: case OP_LOADI8_MEM: case OP_LOADU1_MEM: case OP_LOADU2_MEM: case OP_LOADI4_MEM: case OP_LOADU4_MEM: case OP_LOAD_MEM: { int size = 8; LLVMValueRef base, index, addr; LLVMTypeRef t; gboolean sext = FALSE, zext = FALSE; gboolean is_volatile = (ins->flags & MONO_INST_FAULT); t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext); if (sext || zext) dname = (char*)""; if ((ins->opcode == OP_LOADI8_MEM) || (ins->opcode == OP_LOAD_MEM) || (ins->opcode == OP_LOADI4_MEM) || (ins->opcode == OP_LOADU4_MEM) || (ins->opcode == OP_LOADU1_MEM) || (ins->opcode == OP_LOADU2_MEM)) { addr = LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE); base = addr; } else { /* _MEMBASE */ base = lhs; if (ins->inst_offset == 0) { addr = base; } else if (ins->inst_offset % size != 0) { /* Unaligned load */ index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE); addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, ""); } else { index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE); addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (t, 0)), &index, 1, ""); } } addr = convert (ctx, addr, LLVMPointerType (t, 0)); values [ins->dreg] = emit_load_general (ctx, bb, &builder, size, addr, base, dname, is_volatile, LLVM_BARRIER_NONE); if (!is_volatile && (ins->flags & MONO_INST_INVARIANT_LOAD)) { /* * These will signal LLVM that these loads do not alias any stores, and * they can't fail, allowing them to be hoisted out of loops. */ set_invariant_load_flag (values [ins->dreg]); #if LLVM_API_VERSION < 100 set_metadata_flag (values [ins->dreg], "mono.nofail.load"); #endif } if (sext) values [ins->dreg] = LLVMBuildSExt (builder, values [ins->dreg], LLVMInt32Type (), dname); else if (zext) values [ins->dreg] = LLVMBuildZExt (builder, values [ins->dreg], LLVMInt32Type (), dname); else if (!cfg->r4fp && ins->opcode == OP_LOADR4_MEMBASE) values [ins->dreg] = LLVMBuildFPExt (builder, values [ins->dreg], LLVMDoubleType (), dname); break; } case OP_STOREI1_MEMBASE_REG: case OP_STOREI2_MEMBASE_REG: case OP_STOREI4_MEMBASE_REG: case OP_STOREI8_MEMBASE_REG: case OP_STORER4_MEMBASE_REG: case OP_STORER8_MEMBASE_REG: case OP_STORE_MEMBASE_REG: { int size = 8; LLVMValueRef index, addr, base; LLVMTypeRef t; gboolean sext = FALSE, zext = FALSE; gboolean is_volatile = (ins->flags & MONO_INST_FAULT); if (!values [ins->inst_destbasereg]) { set_failure (ctx, "inst_destbasereg"); break; } t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext); base = values [ins->inst_destbasereg]; if (ins->inst_offset % size != 0) { /* Unaligned store */ index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE); addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, ""); } else { index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE); addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (t, 0)), &index, 1, ""); } emit_store (ctx, bb, &builder, size, convert (ctx, values [ins->sreg1], t), convert (ctx, addr, LLVMPointerType (t, 0)), base, is_volatile); break; } case OP_STOREI1_MEMBASE_IMM: case OP_STOREI2_MEMBASE_IMM: case OP_STOREI4_MEMBASE_IMM: case OP_STOREI8_MEMBASE_IMM: case OP_STORE_MEMBASE_IMM: { int size = 8; LLVMValueRef index, addr, base; LLVMTypeRef t; gboolean sext = FALSE, zext = FALSE; gboolean is_volatile = (ins->flags & MONO_INST_FAULT); t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext); base = values [ins->inst_destbasereg]; if (ins->inst_offset % size != 0) { /* Unaligned store */ index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE); addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, ""); } else { index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE); addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (t, 0)), &index, 1, ""); } emit_store (ctx, bb, &builder, size, convert (ctx, LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), t), convert (ctx, addr, LLVMPointerType (t, 0)), base, is_volatile); break; } case OP_CHECK_THIS: emit_load_general (ctx, bb, &builder, sizeof (gpointer), convert (ctx, lhs, LLVMPointerType (IntPtrType (), 0)), lhs, "", TRUE, LLVM_BARRIER_NONE); break; case OP_OUTARG_VTRETADDR: break; case OP_VOIDCALL: case OP_CALL: case OP_LCALL: case OP_FCALL: case OP_RCALL: case OP_VCALL: case OP_VOIDCALL_MEMBASE: case OP_CALL_MEMBASE: case OP_LCALL_MEMBASE: case OP_FCALL_MEMBASE: case OP_RCALL_MEMBASE: case OP_VCALL_MEMBASE: case OP_VOIDCALL_REG: case OP_CALL_REG: case OP_LCALL_REG: case OP_FCALL_REG: case OP_RCALL_REG: case OP_VCALL_REG: { process_call (ctx, bb, &builder, ins); break; } case OP_AOTCONST: { guint32 got_offset; LLVMValueRef indexes [2]; MonoJumpInfo *tmp_ji, *ji; LLVMValueRef got_entry_addr; char *name; /* * FIXME: Can't allocate from the cfg mempool since that is freed if * the LLVM compile fails. */ tmp_ji = g_new0 (MonoJumpInfo, 1); tmp_ji->type = (MonoJumpInfoType)ins->inst_c1; tmp_ji->data.target = ins->inst_p0; ji = mono_aot_patch_info_dup (tmp_ji); g_free (tmp_ji); if (ji->type == MONO_PATCH_INFO_ICALL_ADDR) { char *symbol = mono_aot_get_direct_call_symbol (MONO_PATCH_INFO_ICALL_ADDR_CALL, ji->data.target); if (symbol) { /* * Avoid emitting a got entry for these since the method is directly called, and it might not be * resolvable at runtime using dlsym (). */ g_free (symbol); values [ins->dreg] = LLVMConstInt (IntPtrType (), 0, FALSE); break; } } ji->next = cfg->patch_info; cfg->patch_info = ji; //mono_add_patch_info (cfg, 0, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0); got_offset = mono_aot_get_got_offset (cfg->patch_info); ctx->module->max_got_offset = MAX (ctx->module->max_got_offset, got_offset); if (!mono_aot_is_shared_got_offset (got_offset)) { //mono_print_ji (ji); //printf ("\n"); ctx->has_got_access = TRUE; } indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); indexes [1] = LLVMConstInt (LLVMInt32Type (), (gssize)got_offset, FALSE); got_entry_addr = LLVMBuildGEP (builder, ctx->module->got_var, indexes, 2, ""); name = get_aotconst_name (ji->type, ji->data.target, got_offset); values [ins->dreg] = LLVMBuildLoad (builder, got_entry_addr, name); g_free (name); /* Can't use this in llvmonly mode since the got slots are initialized by the methods themselves */ if (!cfg->llvm_only) set_invariant_load_flag (values [ins->dreg]); break; } case OP_NOT_REACHED: LLVMBuildUnreachable (builder); has_terminator = TRUE; g_assert (bb->block_num < cfg->max_block_num); ctx->unreachable [bb->block_num] = TRUE; /* Might have instructions after this */ while (ins->next) { MonoInst *next = ins->next; /* * FIXME: If later code uses the regs defined by these instructions, * compilation will fail. */ MONO_DELETE_INS (bb, next); } break; case OP_LDADDR: { MonoInst *var = ins->inst_i0; if (var->opcode == OP_VTARG_ADDR) { /* The variable contains the vtype address */ values [ins->dreg] = values [var->dreg]; } else if (var->opcode == OP_GSHAREDVT_LOCAL) { values [ins->dreg] = emit_gsharedvt_ldaddr (ctx, var->dreg); } else { values [ins->dreg] = addresses [var->dreg]; } break; } case OP_SIN: { LLVMValueRef args [1]; args [0] = convert (ctx, lhs, LLVMDoubleType ()); values [ins->dreg] = LLVMBuildCall (builder, get_intrinsic (ctx, "llvm.sin.f64"), args, 1, dname); break; } case OP_COS: { LLVMValueRef args [1]; args [0] = convert (ctx, lhs, LLVMDoubleType ()); values [ins->dreg] = LLVMBuildCall (builder, get_intrinsic (ctx, "llvm.cos.f64"), args, 1, dname); break; } case OP_SQRT: { LLVMValueRef args [1]; args [0] = convert (ctx, lhs, LLVMDoubleType ()); values [ins->dreg] = LLVMBuildCall (builder, get_intrinsic (ctx, "llvm.sqrt.f64"), args, 1, dname); break; } case OP_ABS: { LLVMValueRef args [1]; args [0] = convert (ctx, lhs, LLVMDoubleType ()); values [ins->dreg] = LLVMBuildCall (builder, get_intrinsic (ctx, "fabs"), args, 1, dname); break; } case OP_IMIN: case OP_LMIN: case OP_IMAX: case OP_LMAX: case OP_IMIN_UN: case OP_LMIN_UN: case OP_IMAX_UN: case OP_LMAX_UN: { LLVMValueRef v; lhs = convert (ctx, lhs, regtype_to_llvm_type (spec [MONO_INST_DEST])); rhs = convert (ctx, rhs, regtype_to_llvm_type (spec [MONO_INST_DEST])); switch (ins->opcode) { case OP_IMIN: case OP_LMIN: v = LLVMBuildICmp (builder, LLVMIntSLE, lhs, rhs, ""); break; case OP_IMAX: case OP_LMAX: v = LLVMBuildICmp (builder, LLVMIntSGE, lhs, rhs, ""); break; case OP_IMIN_UN: case OP_LMIN_UN: v = LLVMBuildICmp (builder, LLVMIntULE, lhs, rhs, ""); break; case OP_IMAX_UN: case OP_LMAX_UN: v = LLVMBuildICmp (builder, LLVMIntUGE, lhs, rhs, ""); break; default: g_assert_not_reached (); break; } values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname); break; } /* * See the ARM64 comment in mono/utils/atomic.h for an explanation of why this * hack is necessary (for now). */ #ifdef TARGET_ARM64 #define ARM64_ATOMIC_FENCE_FIX mono_llvm_build_fence (builder, LLVM_BARRIER_SEQ) #else #define ARM64_ATOMIC_FENCE_FIX #endif case OP_ATOMIC_EXCHANGE_I4: case OP_ATOMIC_EXCHANGE_I8: { LLVMValueRef args [2]; LLVMTypeRef t; if (ins->opcode == OP_ATOMIC_EXCHANGE_I4) t = LLVMInt32Type (); else t = LLVMInt64Type (); g_assert (ins->inst_offset == 0); args [0] = convert (ctx, lhs, LLVMPointerType (t, 0)); args [1] = convert (ctx, rhs, t); ARM64_ATOMIC_FENCE_FIX; values [ins->dreg] = mono_llvm_build_atomic_rmw (builder, LLVM_ATOMICRMW_OP_XCHG, args [0], args [1]); ARM64_ATOMIC_FENCE_FIX; break; } case OP_ATOMIC_ADD_I4: case OP_ATOMIC_ADD_I8: { LLVMValueRef args [2]; LLVMTypeRef t; if (ins->opcode == OP_ATOMIC_ADD_I4) t = LLVMInt32Type (); else t = LLVMInt64Type (); g_assert (ins->inst_offset == 0); args [0] = convert (ctx, lhs, LLVMPointerType (t, 0)); args [1] = convert (ctx, rhs, t); ARM64_ATOMIC_FENCE_FIX; values [ins->dreg] = LLVMBuildAdd (builder, mono_llvm_build_atomic_rmw (builder, LLVM_ATOMICRMW_OP_ADD, args [0], args [1]), args [1], dname); ARM64_ATOMIC_FENCE_FIX; break; } case OP_ATOMIC_CAS_I4: case OP_ATOMIC_CAS_I8: { LLVMValueRef args [3], val; LLVMTypeRef t; if (ins->opcode == OP_ATOMIC_CAS_I4) t = LLVMInt32Type (); else t = LLVMInt64Type (); args [0] = convert (ctx, lhs, LLVMPointerType (t, 0)); /* comparand */ args [1] = convert (ctx, values [ins->sreg3], t); /* new value */ args [2] = convert (ctx, values [ins->sreg2], t); ARM64_ATOMIC_FENCE_FIX; val = mono_llvm_build_cmpxchg (builder, args [0], args [1], args [2]); ARM64_ATOMIC_FENCE_FIX; /* cmpxchg returns a pair */ values [ins->dreg] = LLVMBuildExtractValue (builder, val, 0, ""); break; } case OP_MEMORY_BARRIER: { mono_llvm_build_fence (builder, (BarrierKind) ins->backend.memory_barrier_kind); break; } case OP_ATOMIC_LOAD_I1: case OP_ATOMIC_LOAD_I2: case OP_ATOMIC_LOAD_I4: case OP_ATOMIC_LOAD_I8: case OP_ATOMIC_LOAD_U1: case OP_ATOMIC_LOAD_U2: case OP_ATOMIC_LOAD_U4: case OP_ATOMIC_LOAD_U8: case OP_ATOMIC_LOAD_R4: case OP_ATOMIC_LOAD_R8: { #if LLVM_API_VERSION > 100 int size; gboolean sext, zext; LLVMTypeRef t; gboolean is_volatile = (ins->flags & MONO_INST_FAULT); BarrierKind barrier = (BarrierKind) ins->backend.memory_barrier_kind; LLVMValueRef index, addr; t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext); if (sext || zext) dname = (char *)""; if (ins->inst_offset != 0) { index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE); addr = LLVMBuildGEP (builder, convert (ctx, lhs, LLVMPointerType (t, 0)), &index, 1, ""); } else { addr = lhs; } addr = convert (ctx, addr, LLVMPointerType (t, 0)); ARM64_ATOMIC_FENCE_FIX; values [ins->dreg] = emit_load_general (ctx, bb, &builder, size, addr, lhs, dname, is_volatile, barrier); ARM64_ATOMIC_FENCE_FIX; if (sext) values [ins->dreg] = LLVMBuildSExt (builder, values [ins->dreg], LLVMInt32Type (), dname); else if (zext) values [ins->dreg] = LLVMBuildZExt (builder, values [ins->dreg], LLVMInt32Type (), dname); break; #else set_failure (ctx, "atomic mono.load intrinsic"); break; #endif } case OP_ATOMIC_STORE_I1: case OP_ATOMIC_STORE_I2: case OP_ATOMIC_STORE_I4: case OP_ATOMIC_STORE_I8: case OP_ATOMIC_STORE_U1: case OP_ATOMIC_STORE_U2: case OP_ATOMIC_STORE_U4: case OP_ATOMIC_STORE_U8: case OP_ATOMIC_STORE_R4: case OP_ATOMIC_STORE_R8: { int size; gboolean sext, zext; LLVMTypeRef t; gboolean is_volatile = (ins->flags & MONO_INST_FAULT); BarrierKind barrier = (BarrierKind) ins->backend.memory_barrier_kind; LLVMValueRef index, addr, value, base; #if LLVM_API_VERSION < 100 if (!cfg->llvm_only) { set_failure (ctx, "atomic mono.store intrinsic"); break; } #endif if (!values [ins->inst_destbasereg]) { set_failure (ctx, "inst_destbasereg"); break; } t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext); base = values [ins->inst_destbasereg]; index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE); addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (t, 0)), &index, 1, ""); value = convert (ctx, values [ins->sreg1], t); ARM64_ATOMIC_FENCE_FIX; emit_store_general (ctx, bb, &builder, size, value, addr, base, is_volatile, barrier); ARM64_ATOMIC_FENCE_FIX; break; } case OP_RELAXED_NOP: { #if defined(TARGET_AMD64) || defined(TARGET_X86) emit_call (ctx, bb, &builder, get_intrinsic (ctx, "llvm.x86.sse2.pause"), NULL, 0); break; #else break; #endif } case OP_TLS_GET: { #if (defined(TARGET_AMD64) || defined(TARGET_X86)) && defined(__linux__) #ifdef TARGET_AMD64 // 257 == FS segment register LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 257); #else // 256 == GS segment register LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 256); #endif // FIXME: XEN values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildIntToPtr (builder, LLVMConstInt (IntPtrType (), ins->inst_offset, TRUE), ptrtype, ""), ""); #elif defined(TARGET_AMD64) && defined(TARGET_OSX) /* See mono_amd64_emit_tls_get () */ int offset = mono_amd64_get_tls_gs_offset () + (ins->inst_offset * 8); // 256 == GS segment register LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 256); values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildIntToPtr (builder, LLVMConstInt (IntPtrType (), offset, TRUE), ptrtype, ""), ""); #else set_failure (ctx, "opcode tls-get"); break; #endif break; } case OP_GC_SAFE_POINT: { LLVMValueRef val, cmp, callee; LLVMBasicBlockRef poll_bb, cont_bb; static LLVMTypeRef sig; const char *icall_name = "mono_threads_state_poll"; if (!sig) sig = LLVMFunctionType0 (LLVMVoidType (), FALSE); /* * if (!*sreg1) * mono_threads_state_poll (); * FIXME: Use a preserveall wrapper */ val = mono_llvm_build_load (builder, convert (ctx, lhs, LLVMPointerType (IntPtrType (), 0)), "", TRUE); cmp = LLVMBuildICmp (builder, LLVMIntEQ, val, LLVMConstNull (LLVMTypeOf (val)), ""); poll_bb = gen_bb (ctx, "POLL_BB"); cont_bb = gen_bb (ctx, "CONT_BB"); LLVMBuildCondBr (builder, cmp, cont_bb, poll_bb); ctx->builder = builder = create_builder (ctx); LLVMPositionBuilderAtEnd (builder, poll_bb); if (ctx->cfg->compile_aot) { callee = get_callee (ctx, sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name); } else { gpointer target = resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name); callee = emit_jit_callee (ctx, icall_name, sig, target); } LLVMBuildCall (builder, callee, NULL, 0, ""); LLVMBuildBr (builder, cont_bb); ctx->builder = builder = create_builder (ctx); LLVMPositionBuilderAtEnd (builder, cont_bb); ctx->bblocks [bb->block_num].end_bblock = cont_bb; break; } /* * Overflow opcodes. */ case OP_IADD_OVF: case OP_IADD_OVF_UN: case OP_ISUB_OVF: case OP_ISUB_OVF_UN: case OP_IMUL_OVF: case OP_IMUL_OVF_UN: case OP_LADD_OVF: case OP_LADD_OVF_UN: case OP_LSUB_OVF: case OP_LSUB_OVF_UN: case OP_LMUL_OVF: case OP_LMUL_OVF_UN: { LLVMValueRef args [2], val, ovf, func; args [0] = convert (ctx, lhs, op_to_llvm_type (ins->opcode)); args [1] = convert (ctx, rhs, op_to_llvm_type (ins->opcode)); func = get_intrinsic (ctx, ovf_op_to_intrins (ins->opcode)); g_assert (func); val = LLVMBuildCall (builder, func, args, 2, ""); values [ins->dreg] = LLVMBuildExtractValue (builder, val, 0, dname); ovf = LLVMBuildExtractValue (builder, val, 1, ""); emit_cond_system_exception (ctx, bb, "OverflowException", ovf); if (!ctx_ok (ctx)) break; builder = ctx->builder; break; } /* * Valuetypes. * We currently model them using arrays. Promotion to local vregs is * disabled for them in mono_handle_global_vregs () in the LLVM case, * so we always have an entry in cfg->varinfo for them. * FIXME: Is this needed ? */ case OP_VZERO: { MonoClass *klass = ins->klass; LLVMValueRef args [5]; if (!klass) { // FIXME: set_failure (ctx, "!klass"); break; } if (!addresses [ins->dreg]) addresses [ins->dreg] = build_alloca (ctx, &klass->byval_arg); args [0] = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), ""); args [1] = LLVMConstInt (LLVMInt8Type (), 0, FALSE); args [2] = LLVMConstInt (LLVMInt32Type (), mono_class_value_size (klass, NULL), FALSE); // FIXME: Alignment args [3] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); args [4] = LLVMConstInt (LLVMInt1Type (), 0, FALSE); LLVMBuildCall (builder, get_intrinsic (ctx, "llvm.memset.p0i8.i32"), args, 5, ""); break; } case OP_DUMMY_VZERO: break; case OP_STOREV_MEMBASE: case OP_LOADV_MEMBASE: case OP_VMOVE: { MonoClass *klass = ins->klass; LLVMValueRef src = NULL, dst, args [5]; gboolean done = FALSE; if (!klass) { // FIXME: set_failure (ctx, "!klass"); break; } if (mini_is_gsharedvt_klass (klass)) { // FIXME: set_failure (ctx, "gsharedvt"); break; } switch (ins->opcode) { case OP_STOREV_MEMBASE: if (cfg->gen_write_barriers && klass->has_references && ins->inst_destbasereg != cfg->frame_reg && LLVMGetInstructionOpcode (values [ins->inst_destbasereg]) != LLVMAlloca) { /* Decomposed earlier */ g_assert_not_reached (); break; } if (!addresses [ins->sreg1]) { /* SIMD */ g_assert (values [ins->sreg1]); dst = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_destbasereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (type_to_llvm_type (ctx, &klass->byval_arg), 0)); LLVMBuildStore (builder, values [ins->sreg1], dst); done = TRUE; } else { src = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (LLVMInt8Type (), 0), ""); dst = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_destbasereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (LLVMInt8Type (), 0)); } break; case OP_LOADV_MEMBASE: if (!addresses [ins->dreg]) addresses [ins->dreg] = build_alloca (ctx, &klass->byval_arg); src = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_basereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (LLVMInt8Type (), 0)); dst = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), ""); break; case OP_VMOVE: if (!addresses [ins->sreg1]) addresses [ins->sreg1] = build_alloca (ctx, &klass->byval_arg); if (!addresses [ins->dreg]) addresses [ins->dreg] = build_alloca (ctx, &klass->byval_arg); src = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (LLVMInt8Type (), 0), ""); dst = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), ""); break; default: g_assert_not_reached (); } if (!ctx_ok (ctx)) break; if (done) break; args [0] = dst; args [1] = src; args [2] = LLVMConstInt (LLVMInt32Type (), mono_class_value_size (klass, NULL), FALSE); args [3] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); // FIXME: Alignment args [3] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); args [4] = LLVMConstInt (LLVMInt1Type (), 0, FALSE); LLVMBuildCall (builder, get_intrinsic (ctx, "llvm.memcpy.p0i8.p0i8.i32"), args, 5, ""); break; } case OP_LLVM_OUTARG_VT: { LLVMArgInfo *ainfo = (LLVMArgInfo*)ins->inst_p0; MonoType *t = mini_get_underlying_type (ins->inst_vtype); if (ainfo->storage == LLVMArgGsharedvtVariable) { MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1); if (var && var->opcode == OP_GSHAREDVT_LOCAL) { addresses [ins->dreg] = convert (ctx, emit_gsharedvt_ldaddr (ctx, var->dreg), LLVMPointerType (IntPtrType (), 0)); } else { g_assert (addresses [ins->sreg1]); addresses [ins->dreg] = addresses [ins->sreg1]; } } else if (ainfo->storage == LLVMArgGsharedvtFixed) { if (!addresses [ins->sreg1]) { addresses [ins->sreg1] = build_alloca (ctx, t); g_assert (values [ins->sreg1]); } LLVMBuildStore (builder, convert (ctx, values [ins->sreg1], LLVMGetElementType (LLVMTypeOf (addresses [ins->sreg1]))), addresses [ins->sreg1]); addresses [ins->dreg] = addresses [ins->sreg1]; } else { if (!addresses [ins->sreg1]) { addresses [ins->sreg1] = build_alloca (ctx, t); g_assert (values [ins->sreg1]); LLVMBuildStore (builder, convert (ctx, values [ins->sreg1], type_to_llvm_type (ctx, t)), addresses [ins->sreg1]); } addresses [ins->dreg] = addresses [ins->sreg1]; } break; } case OP_OBJC_GET_SELECTOR: { const char *name = (const char*)ins->inst_p0; LLVMValueRef var; if (!ctx->module->objc_selector_to_var) { ctx->module->objc_selector_to_var = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); LLVMValueRef info_var = LLVMAddGlobal (ctx->lmodule, LLVMArrayType (LLVMInt8Type (), 8), "@OBJC_IMAGE_INFO"); int32_t objc_imageinfo [] = { 0, 16 }; LLVMSetInitializer (info_var, mono_llvm_create_constant_data_array ((uint8_t *) &objc_imageinfo, 8)); LLVMSetLinkage (info_var, LLVMPrivateLinkage); LLVMSetExternallyInitialized (info_var, TRUE); LLVMSetSection (info_var, "__DATA, __objc_imageinfo,regular,no_dead_strip"); LLVMSetAlignment (info_var, sizeof (mgreg_t)); mark_as_used (ctx->module, info_var); } var = g_hash_table_lookup (ctx->module->objc_selector_to_var, name); if (!var) { LLVMValueRef indexes [16]; LLVMValueRef name_var = LLVMAddGlobal (ctx->lmodule, LLVMArrayType (LLVMInt8Type (), strlen (name) + 1), "@OBJC_METH_VAR_NAME_"); LLVMSetInitializer (name_var, mono_llvm_create_constant_data_array ((const uint8_t*)name, strlen (name) + 1)); LLVMSetLinkage (name_var, LLVMPrivateLinkage); LLVMSetSection (name_var, "__TEXT,__objc_methname,cstring_literals"); mark_as_used (ctx->module, name_var); LLVMValueRef ref_var = LLVMAddGlobal (ctx->lmodule, LLVMPointerType (LLVMInt8Type (), 0), "@OBJC_SELECTOR_REFERENCES_"); indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, 0); indexes [1] = LLVMConstInt (LLVMInt32Type (), 0, 0); LLVMSetInitializer (ref_var, LLVMConstGEP (name_var, indexes, 2)); LLVMSetLinkage (ref_var, LLVMPrivateLinkage); LLVMSetExternallyInitialized (ref_var, TRUE); LLVMSetSection (ref_var, "__DATA, __objc_selrefs, literal_pointers, no_dead_strip"); LLVMSetAlignment (ref_var, sizeof (mgreg_t)); mark_as_used (ctx->module, ref_var); g_hash_table_insert (ctx->module->objc_selector_to_var, g_strdup (name), ref_var); var = ref_var; } values [ins->dreg] = LLVMBuildLoad (builder, var, ""); break; } /* * SIMD */ #if defined(TARGET_X86) || defined(TARGET_AMD64) case OP_XZERO: { values [ins->dreg] = LLVMConstNull (type_to_llvm_type (ctx, &ins->klass->byval_arg)); break; } case OP_LOADX_MEMBASE: { LLVMTypeRef t = type_to_llvm_type (ctx, &ins->klass->byval_arg); LLVMValueRef src; src = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_basereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (t, 0)); values [ins->dreg] = mono_llvm_build_aligned_load (builder, src, "", FALSE, 1); break; } case OP_STOREX_MEMBASE: { LLVMTypeRef t = LLVMTypeOf (values [ins->sreg1]); LLVMValueRef dest; dest = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_destbasereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (t, 0)); mono_llvm_build_aligned_store (builder, values [ins->sreg1], dest, FALSE, 1); break; } case OP_PADDB: case OP_PADDW: case OP_PADDD: case OP_PADDQ: values [ins->dreg] = LLVMBuildAdd (builder, lhs, rhs, ""); break; case OP_ADDPD: case OP_ADDPS: values [ins->dreg] = LLVMBuildFAdd (builder, lhs, rhs, ""); break; case OP_PSUBB: case OP_PSUBW: case OP_PSUBD: case OP_PSUBQ: values [ins->dreg] = LLVMBuildSub (builder, lhs, rhs, ""); break; case OP_SUBPD: case OP_SUBPS: values [ins->dreg] = LLVMBuildFSub (builder, lhs, rhs, ""); break; case OP_MULPD: case OP_MULPS: values [ins->dreg] = LLVMBuildFMul (builder, lhs, rhs, ""); break; case OP_DIVPD: case OP_DIVPS: values [ins->dreg] = LLVMBuildFDiv (builder, lhs, rhs, ""); break; case OP_PAND: values [ins->dreg] = LLVMBuildAnd (builder, lhs, rhs, ""); break; case OP_POR: values [ins->dreg] = LLVMBuildOr (builder, lhs, rhs, ""); break; case OP_PXOR: values [ins->dreg] = LLVMBuildXor (builder, lhs, rhs, ""); break; case OP_PMULW: case OP_PMULD: values [ins->dreg] = LLVMBuildMul (builder, lhs, rhs, ""); break; case OP_ANDPS: case OP_ANDNPS: case OP_ORPS: case OP_XORPS: case OP_ANDPD: case OP_ANDNPD: case OP_ORPD: case OP_XORPD: { LLVMTypeRef t, rt; LLVMValueRef v = NULL; switch (ins->opcode) { case OP_ANDPS: case OP_ANDNPS: case OP_ORPS: case OP_XORPS: t = LLVMVectorType (LLVMInt32Type (), 4); rt = LLVMVectorType (LLVMFloatType (), 4); break; case OP_ANDPD: case OP_ANDNPD: case OP_ORPD: case OP_XORPD: t = LLVMVectorType (LLVMInt64Type (), 2); rt = LLVMVectorType (LLVMDoubleType (), 2); break; default: t = LLVMInt32Type (); rt = LLVMInt32Type (); g_assert_not_reached (); } lhs = LLVMBuildBitCast (builder, lhs, t, ""); rhs = LLVMBuildBitCast (builder, rhs, t, ""); switch (ins->opcode) { case OP_ANDPS: case OP_ANDPD: v = LLVMBuildAnd (builder, lhs, rhs, ""); break; case OP_ORPS: case OP_ORPD: v = LLVMBuildOr (builder, lhs, rhs, ""); break; case OP_XORPS: case OP_XORPD: v = LLVMBuildXor (builder, lhs, rhs, ""); break; case OP_ANDNPS: case OP_ANDNPD: v = LLVMBuildAnd (builder, rhs, LLVMBuildNot (builder, lhs, ""), ""); break; } values [ins->dreg] = LLVMBuildBitCast (builder, v, rt, ""); break; } case OP_PMIND_UN: case OP_PMINW_UN: case OP_PMINB_UN: { LLVMValueRef cmp = LLVMBuildICmp (builder, LLVMIntULT, lhs, rhs, ""); values [ins->dreg] = LLVMBuildSelect (builder, cmp, lhs, rhs, ""); break; } case OP_PMAXD_UN: case OP_PMAXW_UN: case OP_PMAXB_UN: { LLVMValueRef cmp = LLVMBuildICmp (builder, LLVMIntUGT, lhs, rhs, ""); values [ins->dreg] = LLVMBuildSelect (builder, cmp, lhs, rhs, ""); break; } case OP_PMINW: { LLVMValueRef cmp = LLVMBuildICmp (builder, LLVMIntSLT, lhs, rhs, ""); values [ins->dreg] = LLVMBuildSelect (builder, cmp, lhs, rhs, ""); break; } case OP_MINPD: case OP_MINPS: case OP_MAXPD: case OP_MAXPS: case OP_ADDSUBPD: case OP_ADDSUBPS: case OP_HADDPD: case OP_HADDPS: case OP_HSUBPD: case OP_HSUBPS: case OP_PADDB_SAT: case OP_PADDW_SAT: case OP_PSUBB_SAT: case OP_PSUBW_SAT: case OP_PADDB_SAT_UN: case OP_PADDW_SAT_UN: case OP_PSUBB_SAT_UN: case OP_PSUBW_SAT_UN: case OP_PAVGB_UN: case OP_PAVGW_UN: case OP_PACKW: case OP_PACKD: case OP_PACKW_UN: case OP_PACKD_UN: case OP_PMULW_HIGH: case OP_PMULW_HIGH_UN: { LLVMValueRef args [2]; args [0] = lhs; args [1] = rhs; values [ins->dreg] = LLVMBuildCall (builder, get_intrinsic (ctx, simd_op_to_intrins (ins->opcode)), args, 2, dname); break; } case OP_PCMPEQB: case OP_PCMPEQW: case OP_PCMPEQD: case OP_PCMPEQQ: { values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildICmp (builder, LLVMIntEQ, lhs, rhs, ""), LLVMTypeOf (lhs), ""); break; } case OP_PCMPGTB: { values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildICmp (builder, LLVMIntSGT, lhs, rhs, ""), LLVMTypeOf (lhs), ""); break; } case OP_EXTRACT_R8: case OP_EXTRACT_I8: case OP_EXTRACT_I4: case OP_EXTRACT_I2: case OP_EXTRACT_U2: case OP_EXTRACTX_U2: case OP_EXTRACT_I1: case OP_EXTRACT_U1: { LLVMTypeRef t; gboolean zext = FALSE; t = simd_op_to_llvm_type (ins->opcode); switch (ins->opcode) { case OP_EXTRACT_R8: case OP_EXTRACT_I8: case OP_EXTRACT_I4: case OP_EXTRACT_I2: case OP_EXTRACT_I1: break; case OP_EXTRACT_U2: case OP_EXTRACTX_U2: case OP_EXTRACT_U1: zext = TRUE; break; default: t = LLVMInt32Type (); g_assert_not_reached (); } lhs = LLVMBuildBitCast (builder, lhs, t, ""); values [ins->dreg] = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), ""); if (zext) values [ins->dreg] = LLVMBuildZExt (builder, values [ins->dreg], LLVMInt32Type (), ""); break; } case OP_EXPAND_I1: case OP_EXPAND_I2: case OP_EXPAND_I4: case OP_EXPAND_I8: case OP_EXPAND_R4: case OP_EXPAND_R8: { LLVMTypeRef t = simd_op_to_llvm_type (ins->opcode); LLVMValueRef mask [16], v; int i; for (i = 0; i < 16; ++i) mask [i] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); v = convert (ctx, values [ins->sreg1], LLVMGetElementType (t)); values [ins->dreg] = LLVMBuildInsertElement (builder, LLVMConstNull (t), v, LLVMConstInt (LLVMInt32Type (), 0, FALSE), ""); values [ins->dreg] = LLVMBuildShuffleVector (builder, values [ins->dreg], LLVMGetUndef (t), LLVMConstVector (mask, LLVMGetVectorSize (t)), ""); break; } case OP_INSERT_I1: values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMInt8Type ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname); break; case OP_INSERT_I2: values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMInt16Type ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname); break; case OP_INSERT_I4: values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMInt32Type ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname); break; case OP_INSERT_I8: values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMInt64Type ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname); break; case OP_INSERT_R4: values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMFloatType ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname); break; case OP_INSERT_R8: values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMDoubleType ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname); break; #if LLVM_API_VERSION > 100 case OP_CVTDQ2PD: { LLVMValueRef indexes [16]; indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); indexes [1] = LLVMConstInt (LLVMInt32Type (), 1, FALSE); LLVMValueRef mask = LLVMConstVector (indexes, 2); LLVMValueRef shuffle = LLVMBuildShuffleVector (builder, lhs, LLVMConstNull (LLVMTypeOf (lhs)), mask, ""); values [ins->dreg] = LLVMBuildSIToFP (builder, shuffle, LLVMVectorType (LLVMDoubleType (), 2), dname); break; } case OP_CVTPS2PD: { LLVMValueRef indexes [16]; indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); indexes [1] = LLVMConstInt (LLVMInt32Type (), 1, FALSE); LLVMValueRef mask = LLVMConstVector (indexes, 2); LLVMValueRef shuffle = LLVMBuildShuffleVector (builder, lhs, LLVMConstNull (LLVMTypeOf (lhs)), mask, ""); values [ins->dreg] = LLVMBuildFPExt (builder, shuffle, LLVMVectorType (LLVMDoubleType (), 2), dname); break; } case OP_CVTTPS2DQ: values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMVectorType (LLVMInt32Type (), 4), dname); break; #endif #if LLVM_API_VERSION <= 100 case OP_CVTDQ2PD: case OP_CVTPS2PD: case OP_CVTTPS2DQ: #endif case OP_CVTDQ2PS: case OP_CVTPD2DQ: case OP_CVTPS2DQ: case OP_CVTPD2PS: case OP_CVTTPD2DQ: case OP_EXTRACT_MASK: case OP_SQRTPS: case OP_SQRTPD: case OP_RSQRTPS: case OP_RCPPS: { LLVMValueRef v; v = convert (ctx, values [ins->sreg1], simd_op_to_llvm_type (ins->opcode)); values [ins->dreg] = LLVMBuildCall (builder, get_intrinsic (ctx, simd_op_to_intrins (ins->opcode)), &v, 1, dname); break; } case OP_COMPPS: case OP_COMPPD: { LLVMRealPredicate op; switch (ins->inst_c0) { case SIMD_COMP_EQ: op = LLVMRealOEQ; break; case SIMD_COMP_LT: op = LLVMRealOLT; break; case SIMD_COMP_LE: op = LLVMRealOLE; break; case SIMD_COMP_UNORD: op = LLVMRealUNO; break; case SIMD_COMP_NEQ: op = LLVMRealUNE; break; case SIMD_COMP_NLT: op = LLVMRealUGE; break; case SIMD_COMP_NLE: op = LLVMRealUGT; break; case SIMD_COMP_ORD: op = LLVMRealORD; break; default: g_assert_not_reached (); } LLVMValueRef cmp = LLVMBuildFCmp (builder, op, lhs, rhs, ""); if (ins->opcode == OP_COMPPD) values [ins->dreg] = LLVMBuildBitCast (builder, LLVMBuildSExt (builder, cmp, LLVMVectorType (LLVMInt64Type (), 2), ""), LLVMTypeOf (lhs), ""); else values [ins->dreg] = LLVMBuildBitCast (builder, LLVMBuildSExt (builder, cmp, LLVMVectorType (LLVMInt32Type (), 4), ""), LLVMTypeOf (lhs), ""); break; } case OP_ICONV_TO_X: /* This is only used for implementing shifts by non-immediate */ values [ins->dreg] = lhs; break; case OP_PSHRW: case OP_PSHRD: case OP_PSHRQ: case OP_PSARW: case OP_PSARD: case OP_PSHLW: case OP_PSHLD: case OP_PSHLQ: { LLVMValueRef args [3]; args [0] = lhs; args [1] = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE); values [ins->dreg] = LLVMBuildCall (builder, get_intrinsic (ctx, simd_op_to_intrins (ins->opcode)), args, 2, dname); break; } case OP_PSHRW_REG: case OP_PSHRD_REG: case OP_PSHRQ_REG: case OP_PSARW_REG: case OP_PSARD_REG: case OP_PSHLW_REG: case OP_PSHLD_REG: case OP_PSHLQ_REG: { LLVMValueRef args [3]; args [0] = lhs; args [1] = values [ins->sreg2]; values [ins->dreg] = LLVMBuildCall (builder, get_intrinsic (ctx, simd_op_to_intrins (ins->opcode)), args, 2, dname); break; } case OP_SHUFPS: case OP_SHUFPD: case OP_PSHUFLED: case OP_PSHUFLEW_LOW: case OP_PSHUFLEW_HIGH: { int mask [16]; LLVMValueRef v1 = NULL, v2 = NULL, mask_values [16]; int i, mask_size = 0; int imask = ins->inst_c0; /* Convert the x86 shuffle mask to LLVM's */ switch (ins->opcode) { case OP_SHUFPS: mask_size = 4; mask [0] = ((imask >> 0) & 3); mask [1] = ((imask >> 2) & 3); mask [2] = ((imask >> 4) & 3) + 4; mask [3] = ((imask >> 6) & 3) + 4; v1 = values [ins->sreg1]; v2 = values [ins->sreg2]; break; case OP_SHUFPD: mask_size = 2; mask [0] = ((imask >> 0) & 1); mask [1] = ((imask >> 1) & 1) + 2; v1 = values [ins->sreg1]; v2 = values [ins->sreg2]; break; case OP_PSHUFLEW_LOW: mask_size = 8; mask [0] = ((imask >> 0) & 3); mask [1] = ((imask >> 2) & 3); mask [2] = ((imask >> 4) & 3); mask [3] = ((imask >> 6) & 3); mask [4] = 4 + 0; mask [5] = 4 + 1; mask [6] = 4 + 2; mask [7] = 4 + 3; v1 = values [ins->sreg1]; v2 = LLVMGetUndef (LLVMTypeOf (v1)); break; case OP_PSHUFLEW_HIGH: mask_size = 8; mask [0] = 0; mask [1] = 1; mask [2] = 2; mask [3] = 3; mask [4] = 4 + ((imask >> 0) & 3); mask [5] = 4 + ((imask >> 2) & 3); mask [6] = 4 + ((imask >> 4) & 3); mask [7] = 4 + ((imask >> 6) & 3); v1 = values [ins->sreg1]; v2 = LLVMGetUndef (LLVMTypeOf (v1)); break; case OP_PSHUFLED: mask_size = 4; mask [0] = ((imask >> 0) & 3); mask [1] = ((imask >> 2) & 3); mask [2] = ((imask >> 4) & 3); mask [3] = ((imask >> 6) & 3); v1 = values [ins->sreg1]; v2 = LLVMGetUndef (LLVMTypeOf (v1)); break; default: g_assert_not_reached (); } for (i = 0; i < mask_size; ++i) mask_values [i] = LLVMConstInt (LLVMInt32Type (), mask [i], FALSE); values [ins->dreg] = LLVMBuildShuffleVector (builder, v1, v2, LLVMConstVector (mask_values, mask_size), dname); break; } case OP_UNPACK_LOWB: case OP_UNPACK_LOWW: case OP_UNPACK_LOWD: case OP_UNPACK_LOWQ: case OP_UNPACK_LOWPS: case OP_UNPACK_LOWPD: case OP_UNPACK_HIGHB: case OP_UNPACK_HIGHW: case OP_UNPACK_HIGHD: case OP_UNPACK_HIGHQ: case OP_UNPACK_HIGHPS: case OP_UNPACK_HIGHPD: { int mask [16]; LLVMValueRef mask_values [16]; int i, mask_size = 0; gboolean low = FALSE; switch (ins->opcode) { case OP_UNPACK_LOWB: mask_size = 16; low = TRUE; break; case OP_UNPACK_LOWW: mask_size = 8; low = TRUE; break; case OP_UNPACK_LOWD: case OP_UNPACK_LOWPS: mask_size = 4; low = TRUE; break; case OP_UNPACK_LOWQ: case OP_UNPACK_LOWPD: mask_size = 2; low = TRUE; break; case OP_UNPACK_HIGHB: mask_size = 16; break; case OP_UNPACK_HIGHW: mask_size = 8; break; case OP_UNPACK_HIGHD: case OP_UNPACK_HIGHPS: mask_size = 4; break; case OP_UNPACK_HIGHQ: case OP_UNPACK_HIGHPD: mask_size = 2; break; default: g_assert_not_reached (); } if (low) { for (i = 0; i < (mask_size / 2); ++i) { mask [(i * 2)] = i; mask [(i * 2) + 1] = mask_size + i; } } else { for (i = 0; i < (mask_size / 2); ++i) { mask [(i * 2)] = (mask_size / 2) + i; mask [(i * 2) + 1] = mask_size + (mask_size / 2) + i; } } for (i = 0; i < mask_size; ++i) mask_values [i] = LLVMConstInt (LLVMInt32Type (), mask [i], FALSE); values [ins->dreg] = LLVMBuildShuffleVector (builder, values [ins->sreg1], values [ins->sreg2], LLVMConstVector (mask_values, mask_size), dname); break; } case OP_DUPPD: { LLVMTypeRef t = simd_op_to_llvm_type (ins->opcode); LLVMValueRef v, val; v = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), 0, FALSE), ""); val = LLVMConstNull (t); val = LLVMBuildInsertElement (builder, val, v, LLVMConstInt (LLVMInt32Type (), 0, FALSE), ""); val = LLVMBuildInsertElement (builder, val, v, LLVMConstInt (LLVMInt32Type (), 1, FALSE), dname); values [ins->dreg] = val; break; } case OP_DUPPS_LOW: case OP_DUPPS_HIGH: { LLVMTypeRef t = simd_op_to_llvm_type (ins->opcode); LLVMValueRef v1, v2, val; if (ins->opcode == OP_DUPPS_LOW) { v1 = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), 0, FALSE), ""); v2 = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), 2, FALSE), ""); } else { v1 = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), 1, FALSE), ""); v2 = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), 3, FALSE), ""); } val = LLVMConstNull (t); val = LLVMBuildInsertElement (builder, val, v1, LLVMConstInt (LLVMInt32Type (), 0, FALSE), ""); val = LLVMBuildInsertElement (builder, val, v1, LLVMConstInt (LLVMInt32Type (), 1, FALSE), ""); val = LLVMBuildInsertElement (builder, val, v2, LLVMConstInt (LLVMInt32Type (), 2, FALSE), ""); val = LLVMBuildInsertElement (builder, val, v2, LLVMConstInt (LLVMInt32Type (), 3, FALSE), ""); values [ins->dreg] = val; break; } case OP_DPPS: { LLVMValueRef args [3]; args [0] = lhs; args [1] = rhs; /* 0xf1 == multiply all 4 elements, add them together, and store the result to the lowest element */ #if LLVM_API_VERSION >= 500 args [2] = LLVMConstInt (LLVMInt8Type (), 0xf1, FALSE); #else args [2] = LLVMConstInt (LLVMInt32Type (), 0xf1, FALSE); #endif values [ins->dreg] = LLVMBuildCall (builder, get_intrinsic (ctx, simd_op_to_intrins (ins->opcode)), args, 3, dname); break; } #endif /* SIMD */ case OP_DUMMY_USE: break; /* * EXCEPTION HANDLING */ case OP_IMPLICIT_EXCEPTION: /* This marks a place where an implicit exception can happen */ if (bb->region != -1) set_failure (ctx, "implicit-exception"); break; case OP_THROW: case OP_RETHROW: { gboolean rethrow = (ins->opcode == OP_RETHROW); if (ctx->llvm_only) { emit_llvmonly_throw (ctx, bb, rethrow, lhs); has_terminator = TRUE; ctx->unreachable [bb->block_num] = TRUE; } else { emit_throw (ctx, bb, rethrow, lhs); builder = ctx->builder; } break; } case OP_CALL_HANDLER: { /* * We don't 'call' handlers, but instead simply branch to them. * The code generated by ENDFINALLY will branch back to us. */ LLVMBasicBlockRef noex_bb; GSList *bb_list; BBInfo *info = &bblocks [ins->inst_target_bb->block_num]; bb_list = info->call_handler_return_bbs; /* * Set the indicator variable for the finally clause. */ lhs = info->finally_ind; g_assert (lhs); LLVMBuildStore (builder, LLVMConstInt (LLVMInt32Type (), g_slist_length (bb_list) + 1, FALSE), lhs); /* Branch to the finally clause */ LLVMBuildBr (builder, info->call_handler_target_bb); noex_bb = gen_bb (ctx, "CALL_HANDLER_CONT_BB"); info->call_handler_return_bbs = g_slist_append_mempool (cfg->mempool, info->call_handler_return_bbs, noex_bb); builder = ctx->builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); bblocks [bb->block_num].end_bblock = noex_bb; break; } case OP_START_HANDLER: { break; } case OP_ENDFINALLY: { LLVMBasicBlockRef resume_bb; MonoBasicBlock *handler_bb; LLVMValueRef val, switch_ins, callee; GSList *bb_list; BBInfo *info; gboolean is_fault = MONO_REGION_FLAGS (bb->region) == MONO_EXCEPTION_CLAUSE_FAULT; /* * Fault clauses are like finally clauses, but they are only called if an exception is thrown. */ if (!is_fault) { handler_bb = (MonoBasicBlock*)g_hash_table_lookup (ctx->region_to_handler, GUINT_TO_POINTER (mono_get_block_region_notry (cfg, bb->region))); g_assert (handler_bb); info = &bblocks [handler_bb->block_num]; lhs = info->finally_ind; g_assert (lhs); bb_list = info->call_handler_return_bbs; resume_bb = gen_bb (ctx, "ENDFINALLY_RESUME_BB"); /* Load the finally variable */ val = LLVMBuildLoad (builder, lhs, ""); /* Reset the variable */ LLVMBuildStore (builder, LLVMConstInt (LLVMInt32Type (), 0, FALSE), lhs); /* Branch to either resume_bb, or to the bblocks in bb_list */ switch_ins = LLVMBuildSwitch (builder, val, resume_bb, g_slist_length (bb_list)); /* * The other targets are added at the end to handle OP_CALL_HANDLER * opcodes processed later. */ info->endfinally_switch_ins_list = g_slist_append_mempool (cfg->mempool, info->endfinally_switch_ins_list, switch_ins); builder = ctx->builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, resume_bb); } if (ctx->llvm_only) { emit_resume_eh (ctx, bb); } else { if (ctx->cfg->compile_aot) { callee = get_callee (ctx, LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE), MONO_PATCH_INFO_INTERNAL_METHOD, "llvm_resume_unwind_trampoline"); } else { #if LLVM_API_VERSION > 100 MonoJitICallInfo *info; info = mono_find_jit_icall_by_name ("llvm_resume_unwind_trampoline"); g_assert (info); gpointer target = (void*)info->func; LLVMTypeRef icall_sig = LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE); callee = emit_jit_callee (ctx, "llvm_resume_unwind_trampoline", icall_sig, target); #else callee = LLVMGetNamedFunction (ctx->lmodule, "llvm_resume_unwind_trampoline"); #endif } LLVMBuildCall (builder, callee, NULL, 0, ""); LLVMBuildUnreachable (builder); } has_terminator = TRUE; break; } case OP_IL_SEQ_POINT: break; default: { char reason [128]; sprintf (reason, "opcode %s", mono_inst_name (ins->opcode)); set_failure (ctx, reason); break; } } if (!ctx_ok (ctx)) break; /* Convert the value to the type required by phi nodes */ if (spec [MONO_INST_DEST] != ' ' && !MONO_IS_STORE_MEMBASE (ins) && ctx->vreg_types [ins->dreg]) { if (ctx->is_vphi [ins->dreg]) /* vtypes */ values [ins->dreg] = addresses [ins->dreg]; else values [ins->dreg] = convert (ctx, values [ins->dreg], ctx->vreg_types [ins->dreg]); } /* Add stores for volatile variables */ if (spec [MONO_INST_DEST] != ' ' && spec [MONO_INST_DEST] != 'v' && !MONO_IS_STORE_MEMBASE (ins)) emit_volatile_store (ctx, ins->dreg); } if (!ctx_ok (ctx)) return; if (!has_terminator && bb->next_bb && (bb == cfg->bb_entry || bb->in_count > 0)) { LLVMBuildBr (builder, get_bb (ctx, bb->next_bb)); } if (bb == cfg->bb_exit && sig->ret->type == MONO_TYPE_VOID) { emit_dbg_loc (ctx, builder, cfg->header->code + cfg->header->code_size - 1); LLVMBuildRetVoid (builder); } if (bb == cfg->bb_entry) ctx->last_alloca = LLVMGetLastInstruction (get_bb (ctx, cfg->bb_entry)); } /* * mono_llvm_check_method_supported: * * Do some quick checks to decide whenever cfg->method can be compiled by LLVM, to avoid * compiling a method twice. */ void mono_llvm_check_method_supported (MonoCompile *cfg) { int i, j; if (cfg->llvm_only) return; if (cfg->method->save_lmf) { cfg->exception_message = g_strdup ("lmf"); cfg->disable_llvm = TRUE; } if (cfg->disable_llvm) return; /* * Nested clauses where one of the clauses is a finally clause is * not supported, because LLVM can't figure out the control flow, * probably because we resume exception handling by calling our * own function instead of using the 'resume' llvm instruction. */ for (i = 0; i < cfg->header->num_clauses; ++i) { for (j = 0; j < cfg->header->num_clauses; ++j) { MonoExceptionClause *clause1 = &cfg->header->clauses [i]; MonoExceptionClause *clause2 = &cfg->header->clauses [j]; // FIXME: Nested try clauses fail in some cases too, i.e. #37273 if (i != j && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset) { //(clause1->flags == MONO_EXCEPTION_CLAUSE_FINALLY || clause2->flags == MONO_EXCEPTION_CLAUSE_FINALLY)) { cfg->exception_message = g_strdup ("nested clauses"); cfg->disable_llvm = TRUE; break; } } } if (cfg->disable_llvm) return; /* FIXME: */ if (cfg->method->dynamic) { cfg->exception_message = g_strdup ("dynamic."); cfg->disable_llvm = TRUE; } if (cfg->disable_llvm) return; } static LLVMCallInfo* get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig) { LLVMCallInfo *linfo; int i; if (cfg->gsharedvt && cfg->llvm_only && mini_is_gsharedvt_variable_signature (sig)) { int i, n, pindex; /* * Gsharedvt methods have the following calling convention: * - all arguments are passed by ref, even non generic ones * - the return value is returned by ref too, using a vret * argument passed after 'this'. */ n = sig->param_count + sig->hasthis; linfo = (LLVMCallInfo*)mono_mempool_alloc0 (cfg->mempool, sizeof (LLVMCallInfo) + (sizeof (LLVMArgInfo) * n)); pindex = 0; if (sig->hasthis) linfo->args [pindex ++].storage = LLVMArgNormal; if (sig->ret->type != MONO_TYPE_VOID) { if (mini_is_gsharedvt_variable_type (sig->ret)) linfo->ret.storage = LLVMArgGsharedvtVariable; else if (mini_type_is_vtype (sig->ret)) linfo->ret.storage = LLVMArgGsharedvtFixedVtype; else linfo->ret.storage = LLVMArgGsharedvtFixed; linfo->vret_arg_index = pindex; } else { linfo->ret.storage = LLVMArgNone; } for (i = 0; i < sig->param_count; ++i) { if (sig->params [i]->byref) linfo->args [pindex].storage = LLVMArgNormal; else if (mini_is_gsharedvt_variable_type (sig->params [i])) linfo->args [pindex].storage = LLVMArgGsharedvtVariable; else if (mini_type_is_vtype (sig->params [i])) linfo->args [pindex].storage = LLVMArgGsharedvtFixedVtype; else linfo->args [pindex].storage = LLVMArgGsharedvtFixed; linfo->args [pindex].type = sig->params [i]; pindex ++; } return linfo; } linfo = mono_arch_get_llvm_call_info (cfg, sig); for (i = 0; i < sig->param_count; ++i) linfo->args [i + sig->hasthis].type = sig->params [i]; return linfo; } static void emit_method_inner (EmitContext *ctx); static void free_ctx (EmitContext *ctx) { GSList *l; g_free (ctx->values); g_free (ctx->addresses); g_free (ctx->vreg_types); g_free (ctx->is_vphi); g_free (ctx->vreg_cli_types); g_free (ctx->is_dead); g_free (ctx->unreachable); g_ptr_array_free (ctx->phi_values, TRUE); g_free (ctx->bblocks); g_hash_table_destroy (ctx->region_to_handler); g_hash_table_destroy (ctx->clause_to_handler); g_hash_table_destroy (ctx->jit_callees); GHashTableIter iter; g_hash_table_iter_init (&iter, ctx->method_to_callers); while (g_hash_table_iter_next (&iter, NULL, (gpointer)&l)) g_slist_free (l); g_hash_table_destroy (ctx->method_to_callers); g_free (ctx->method_name); g_ptr_array_free (ctx->bblock_list, TRUE); for (l = ctx->builders; l; l = l->next) { LLVMBuilderRef builder = (LLVMBuilderRef)l->data; LLVMDisposeBuilder (builder); } g_free (ctx); } /* * mono_llvm_emit_method: * * Emit LLVM IL from the mono IL, and compile it to native code using LLVM. */ void mono_llvm_emit_method (MonoCompile *cfg) { EmitContext *ctx; char *method_name; gboolean is_linkonce = FALSE; int i; /* The code below might acquire the loader lock, so use it for global locking */ mono_loader_lock (); /* Used to communicate with the callbacks */ mono_native_tls_set_value (current_cfg_tls_id, cfg); ctx = g_new0 (EmitContext, 1); ctx->cfg = cfg; ctx->mempool = cfg->mempool; /* * This maps vregs to the LLVM instruction defining them */ ctx->values = g_new0 (LLVMValueRef, cfg->next_vreg); /* * This maps vregs for volatile variables to the LLVM instruction defining their * address. */ ctx->addresses = g_new0 (LLVMValueRef, cfg->next_vreg); ctx->vreg_types = g_new0 (LLVMTypeRef, cfg->next_vreg); ctx->is_vphi = g_new0 (gboolean, cfg->next_vreg); ctx->vreg_cli_types = g_new0 (MonoType*, cfg->next_vreg); ctx->phi_values = g_ptr_array_sized_new (256); /* * This signals whenever the vreg was defined by a phi node with no input vars * (i.e. all its input bblocks end with NOT_REACHABLE). */ ctx->is_dead = g_new0 (gboolean, cfg->next_vreg); /* Whenever the bblock is unreachable */ ctx->unreachable = g_new0 (gboolean, cfg->max_block_num); ctx->bblock_list = g_ptr_array_sized_new (256); ctx->region_to_handler = g_hash_table_new (NULL, NULL); ctx->clause_to_handler = g_hash_table_new (NULL, NULL); ctx->method_to_callers = g_hash_table_new (NULL, NULL); ctx->jit_callees = g_hash_table_new (NULL, NULL); if (cfg->compile_aot) { ctx->module = &aot_module; method_name = NULL; /* * Allow the linker to discard duplicate copies of wrappers, generic instances etc. by using the 'linkonce' * linkage for them. This requires the following: * - the method needs to have a unique mangled name * - llvmonly mode, since the code in aot-runtime.c would initialize got slots in the wrong aot image etc. */ is_linkonce = ctx->module->llvm_only && ctx->module->static_link && mono_aot_is_linkonce_method (cfg->method); if (is_linkonce) { method_name = mono_aot_get_mangled_method_name (cfg->method); if (!method_name) is_linkonce = FALSE; /* if (method_name) printf ("%s %s\n", mono_method_full_name (cfg->method, 1), method_name); else printf ("%s\n", mono_method_full_name (cfg->method, 1)); */ } if (!method_name) method_name = mono_aot_get_method_name (cfg); cfg->llvm_method_name = g_strdup (method_name); } else { init_jit_module (cfg->domain); ctx->module = (MonoLLVMModule*)domain_jit_info (cfg->domain)->llvm_module; method_name = mono_method_full_name (cfg->method, TRUE); } ctx->method_name = method_name; ctx->is_linkonce = is_linkonce; #if LLVM_API_VERSION > 100 if (cfg->compile_aot) ctx->lmodule = ctx->module->lmodule; else ctx->lmodule = LLVMModuleCreateWithName (g_strdup_printf ("jit-module-%s", cfg->method->name)); #else ctx->lmodule = ctx->module->lmodule; #endif ctx->llvm_only = ctx->module->llvm_only; emit_method_inner (ctx); if (!ctx_ok (ctx)) { if (ctx->lmethod) { /* Need to add unused phi nodes as they can be referenced by other values */ LLVMBasicBlockRef phi_bb = LLVMAppendBasicBlock (ctx->lmethod, "PHI_BB"); LLVMBuilderRef builder; builder = create_builder (ctx); LLVMPositionBuilderAtEnd (builder, phi_bb); for (i = 0; i < ctx->phi_values->len; ++i) { LLVMValueRef v = (LLVMValueRef)g_ptr_array_index (ctx->phi_values, i); if (LLVMGetInstructionParent (v) == NULL) LLVMInsertIntoBuilder (builder, v); } LLVMDeleteFunction (ctx->lmethod); } } free_ctx (ctx); mono_native_tls_set_value (current_cfg_tls_id, NULL); mono_loader_unlock (); } static void emit_method_inner (EmitContext *ctx) { MonoCompile *cfg = ctx->cfg; MonoMethodSignature *sig; MonoBasicBlock *bb; LLVMTypeRef method_type; LLVMValueRef method = NULL; LLVMValueRef *values = ctx->values; int i, max_block_num, bb_index; gboolean last = FALSE; LLVMCallInfo *linfo; LLVMModuleRef lmodule = ctx->lmodule; BBInfo *bblocks; GPtrArray *bblock_list = ctx->bblock_list; MonoMethodHeader *header; MonoExceptionClause *clause; char **names; if (cfg->gsharedvt && !cfg->llvm_only) { set_failure (ctx, "gsharedvt"); return; } #if 1 { static int count = 0; count ++; char *llvm_count_str = g_getenv ("LLVM_COUNT"); if (llvm_count_str) { int lcount = atoi (llvm_count_str); g_free (llvm_count_str); if (count == lcount) { printf ("LAST: %s\n", mono_method_full_name (cfg->method, TRUE)); fflush (stdout); last = TRUE; } if (count > lcount) { set_failure (ctx, "count"); return; } } } #endif sig = mono_method_signature (cfg->method); ctx->sig = sig; linfo = get_llvm_call_info (cfg, sig); ctx->linfo = linfo; if (!ctx_ok (ctx)) return; if (cfg->rgctx_var) linfo->rgctx_arg = TRUE; ctx->method_type = method_type = sig_to_llvm_sig_full (ctx, sig, linfo); if (!ctx_ok (ctx)) return; method = LLVMAddFunction (lmodule, ctx->method_name, method_type); ctx->lmethod = method; if (!cfg->llvm_only) LLVMSetFunctionCallConv (method, LLVMMono1CallConv); LLVMSetLinkage (method, LLVMPrivateLinkage); mono_llvm_add_func_attr (method, LLVM_ATTR_UW_TABLE); if (cfg->compile_aot) { LLVMSetLinkage (method, LLVMInternalLinkage); if (ctx->module->external_symbols) { LLVMSetLinkage (method, LLVMExternalLinkage); LLVMSetVisibility (method, LLVMHiddenVisibility); } if (ctx->is_linkonce) { LLVMSetLinkage (method, LLVMLinkOnceAnyLinkage); LLVMSetVisibility (method, LLVMDefaultVisibility); } } else { #if LLVM_API_VERSION > 100 LLVMSetLinkage (method, LLVMExternalLinkage); #else LLVMSetLinkage (method, LLVMPrivateLinkage); #endif } if (cfg->method->save_lmf && !cfg->llvm_only) { set_failure (ctx, "lmf"); return; } if (sig->pinvoke && cfg->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && !cfg->llvm_only) { set_failure (ctx, "pinvoke signature"); return; } header = cfg->header; for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY && clause->flags != MONO_EXCEPTION_CLAUSE_FAULT && clause->flags != MONO_EXCEPTION_CLAUSE_NONE) { set_failure (ctx, "non-finally/catch/fault clause."); return; } } if (header->num_clauses || (cfg->method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) || cfg->no_inline) /* We can't handle inlined methods with clauses */ mono_llvm_add_func_attr (method, LLVM_ATTR_NO_INLINE); if (linfo->rgctx_arg) { ctx->rgctx_arg = LLVMGetParam (method, linfo->rgctx_arg_pindex); ctx->rgctx_arg_pindex = linfo->rgctx_arg_pindex; /* * We mark the rgctx parameter with the inreg attribute, which is mapped to * MONO_ARCH_RGCTX_REG in the Mono calling convention in llvm, i.e. * CC_X86_64_Mono in X86CallingConv.td. */ if (!ctx->llvm_only) mono_llvm_add_param_attr (ctx->rgctx_arg, LLVM_ATTR_IN_REG); LLVMSetValueName (ctx->rgctx_arg, "rgctx"); } else { ctx->rgctx_arg_pindex = -1; } if (cfg->vret_addr) { values [cfg->vret_addr->dreg] = LLVMGetParam (method, linfo->vret_arg_pindex); LLVMSetValueName (values [cfg->vret_addr->dreg], "vret"); if (linfo->ret.storage == LLVMArgVtypeByRef) { mono_llvm_add_param_attr (LLVMGetParam (method, linfo->vret_arg_pindex), LLVM_ATTR_STRUCT_RET); mono_llvm_add_param_attr (LLVMGetParam (method, linfo->vret_arg_pindex), LLVM_ATTR_NO_ALIAS); } } if (sig->hasthis) { ctx->this_arg_pindex = linfo->this_arg_pindex; ctx->this_arg = LLVMGetParam (method, linfo->this_arg_pindex); values [cfg->args [0]->dreg] = ctx->this_arg; LLVMSetValueName (values [cfg->args [0]->dreg], "this"); } names = g_new (char *, sig->param_count); mono_method_get_param_names (cfg->method, (const char **) names); /* Set parameter names/attributes */ for (i = 0; i < sig->param_count; ++i) { LLVMArgInfo *ainfo = &linfo->args [i + sig->hasthis]; char *name; int pindex = ainfo->pindex + ainfo->ndummy_fpargs; int j; for (j = 0; j < ainfo->ndummy_fpargs; ++j) { name = g_strdup_printf ("dummy_%d_%d", i, j); LLVMSetValueName (LLVMGetParam (method, ainfo->pindex + j), name); g_free (name); } if (ainfo->storage == LLVMArgVtypeInReg && ainfo->pair_storage [0] == LLVMArgNone && ainfo->pair_storage [1] == LLVMArgNone) continue; values [cfg->args [i + sig->hasthis]->dreg] = LLVMGetParam (method, pindex); if (ainfo->storage == LLVMArgGsharedvtFixed || ainfo->storage == LLVMArgGsharedvtFixedVtype) { if (names [i] && names [i][0] != '\0') name = g_strdup_printf ("p_arg_%s", names [i]); else name = g_strdup_printf ("p_arg_%d", i); } else { if (names [i] && names [i][0] != '\0') name = g_strdup_printf ("arg_%s", names [i]); else name = g_strdup_printf ("arg_%d", i); } LLVMSetValueName (values [cfg->args [i + sig->hasthis]->dreg], name); g_free (name); if (ainfo->storage == LLVMArgVtypeByVal) mono_llvm_add_param_attr (LLVMGetParam (method, pindex), LLVM_ATTR_BY_VAL); if (ainfo->storage == LLVMArgVtypeByRef) { /* For OP_LDADDR */ cfg->args [i + sig->hasthis]->opcode = OP_VTARG_ADDR; } } g_free (names); if (ctx->module->emit_dwarf && cfg->compile_aot && mono_debug_enabled ()) { ctx->minfo = mono_debug_lookup_method (cfg->method); ctx->dbg_md = emit_dbg_subprogram (ctx, cfg, method, ctx->method_name); } max_block_num = 0; for (bb = cfg->bb_entry; bb; bb = bb->next_bb) max_block_num = MAX (max_block_num, bb->block_num); ctx->bblocks = bblocks = g_new0 (BBInfo, max_block_num + 1); /* Add branches between non-consecutive bblocks */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { if (bb->last_ins && MONO_IS_COND_BRANCH_OP (bb->last_ins) && bb->next_bb != bb->last_ins->inst_false_bb) { MonoInst *inst = (MonoInst*)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst)); inst->opcode = OP_BR; inst->inst_target_bb = bb->last_ins->inst_false_bb; mono_bblock_add_inst (bb, inst); } } /* * Make a first pass over the code to precreate PHI nodes/set INDIRECT flags. */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { MonoInst *ins; LLVMBuilderRef builder; char *dname; char dname_buf[128]; builder = create_builder (ctx); for (ins = bb->code; ins; ins = ins->next) { switch (ins->opcode) { case OP_PHI: case OP_FPHI: case OP_VPHI: case OP_XPHI: { LLVMTypeRef phi_type = llvm_type_to_stack_type (cfg, type_to_llvm_type (ctx, &ins->klass->byval_arg)); if (!ctx_ok (ctx)) return; if (ins->opcode == OP_VPHI) { /* Treat valuetype PHI nodes as operating on the address itself */ g_assert (ins->klass); phi_type = LLVMPointerType (type_to_llvm_type (ctx, &ins->klass->byval_arg), 0); } /* * Have to precreate these, as they can be referenced by * earlier instructions. */ sprintf (dname_buf, "t%d", ins->dreg); dname = dname_buf; values [ins->dreg] = LLVMBuildPhi (builder, phi_type, dname); if (ins->opcode == OP_VPHI) ctx->addresses [ins->dreg] = values [ins->dreg]; g_ptr_array_add (ctx->phi_values, values [ins->dreg]); /* * Set the expected type of the incoming arguments since these have * to have the same type. */ for (i = 0; i < ins->inst_phi_args [0]; i++) { int sreg1 = ins->inst_phi_args [i + 1]; if (sreg1 != -1) { if (ins->opcode == OP_VPHI) ctx->is_vphi [sreg1] = TRUE; ctx->vreg_types [sreg1] = phi_type; } } break; } case OP_LDADDR: ((MonoInst*)ins->inst_p0)->flags |= MONO_INST_INDIRECT; break; default: break; } } } /* * Create an ordering for bblocks, use the depth first order first, then * put the exception handling bblocks last. */ for (bb_index = 0; bb_index < cfg->num_bblocks; ++bb_index) { bb = cfg->bblocks [bb_index]; if (!(bb->region != -1 && !MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY))) { g_ptr_array_add (bblock_list, bb); bblocks [bb->block_num].added = TRUE; } } for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { if (!bblocks [bb->block_num].added) g_ptr_array_add (bblock_list, bb); } /* * Second pass: generate code. */ // Emit entry point LLVMBuilderRef entry_builder = create_builder (ctx); LLVMBasicBlockRef entry_bb = get_bb (ctx, cfg->bb_entry); LLVMPositionBuilderAtEnd (entry_builder, entry_bb); emit_entry_bb (ctx, entry_builder); // Make landing pads first ctx->exc_meta = g_hash_table_new_full (NULL, NULL, NULL, NULL); if (ctx->llvm_only) { size_t group_index = 0; while (group_index < cfg->header->num_clauses) { int count = 0; size_t cursor = group_index; while (cursor < cfg->header->num_clauses && CLAUSE_START (&cfg->header->clauses [cursor]) == CLAUSE_START (&cfg->header->clauses [group_index]) && CLAUSE_END (&cfg->header->clauses [cursor]) == CLAUSE_END (&cfg->header->clauses [group_index])) { count++; cursor++; } LLVMBasicBlockRef lpad_bb = emit_landing_pad (ctx, group_index, count); intptr_t key = CLAUSE_END (&cfg->header->clauses [group_index]); g_hash_table_insert (ctx->exc_meta, (gpointer)key, lpad_bb); group_index = cursor; } } for (bb_index = 0; bb_index < bblock_list->len; ++bb_index) { bb = (MonoBasicBlock*)g_ptr_array_index (bblock_list, bb_index); // Prune unreachable mono BBs. if (!(bb == cfg->bb_entry || bb->in_count > 0)) continue; process_bb (ctx, bb); if (!ctx_ok (ctx)) return; } g_hash_table_destroy (ctx->exc_meta); mono_memory_barrier (); /* Add incoming phi values */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { GSList *l, *ins_list; ins_list = bblocks [bb->block_num].phi_nodes; for (l = ins_list; l; l = l->next) { PhiNode *node = (PhiNode*)l->data; MonoInst *phi = node->phi; int sreg1 = node->sreg; LLVMBasicBlockRef in_bb; if (sreg1 == -1) continue; in_bb = get_end_bb (ctx, node->in_bb); if (ctx->unreachable [node->in_bb->block_num]) continue; if (!values [sreg1]) { /* Can happen with values in EH clauses */ set_failure (ctx, "incoming phi sreg1"); return; } if (phi->opcode == OP_VPHI) { g_assert (LLVMTypeOf (ctx->addresses [sreg1]) == LLVMTypeOf (values [phi->dreg])); LLVMAddIncoming (values [phi->dreg], &ctx->addresses [sreg1], &in_bb, 1); } else { if (LLVMTypeOf (values [sreg1]) != LLVMTypeOf (values [phi->dreg])) { set_failure (ctx, "incoming phi arg type mismatch"); return; } g_assert (LLVMTypeOf (values [sreg1]) == LLVMTypeOf (values [phi->dreg])); LLVMAddIncoming (values [phi->dreg], &values [sreg1], &in_bb, 1); } } } /* Nullify empty phi instructions */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { GSList *l, *ins_list; ins_list = bblocks [bb->block_num].phi_nodes; for (l = ins_list; l; l = l->next) { PhiNode *node = (PhiNode*)l->data; MonoInst *phi = node->phi; LLVMValueRef phi_ins = values [phi->dreg]; if (!phi_ins) /* Already removed */ continue; if (LLVMCountIncoming (phi_ins) == 0) { mono_llvm_replace_uses_of (phi_ins, LLVMConstNull (LLVMTypeOf (phi_ins))); LLVMInstructionEraseFromParent (phi_ins); values [phi->dreg] = NULL; } } } /* Create the SWITCH statements for ENDFINALLY instructions */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { BBInfo *info = &bblocks [bb->block_num]; GSList *l; for (l = info->endfinally_switch_ins_list; l; l = l->next) { LLVMValueRef switch_ins = (LLVMValueRef)l->data; GSList *bb_list = info->call_handler_return_bbs; GSList *bb_list_iter; i = 0; for (bb_list_iter = bb_list; bb_list_iter; bb_list_iter = g_slist_next (bb_list_iter)) { LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), i + 1, FALSE), (LLVMBasicBlockRef)bb_list_iter->data); i ++; } } } /* Initialize the method if needed */ if (cfg->compile_aot && ctx->llvm_only) { // FIXME: Add more shared got entries ctx->builder = create_builder (ctx); LLVMPositionBuilderAtEnd (ctx->builder, ctx->init_bb); ctx->module->max_method_idx = MAX (ctx->module->max_method_idx, cfg->method_index); // FIXME: beforefieldinit /* * NATIVE_TO_MANAGED methods might be called on a thread not attached to the runtime, so they are initialized when loaded * in load_method (). */ if ((ctx->has_got_access || mono_class_get_cctor (cfg->method->klass)) && !(cfg->method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)) { /* * linkonce methods shouldn't have initialization, * because they might belong to assemblies which * haven't been loaded yet. */ g_assert (!ctx->is_linkonce); emit_init_method (ctx); } else { LLVMBuildBr (ctx->builder, ctx->inited_bb); } } if (cfg->llvm_only) { GHashTableIter iter; MonoMethod *method; GSList *callers, *l, *l2; /* * Add the contents of ctx->method_to_callers to module->method_to_callers. * We can't do this earlier, as it contains llvm instructions which can be * freed if compilation fails. * FIXME: Get rid of this when all methods can be llvm compiled. */ g_hash_table_iter_init (&iter, ctx->method_to_callers); while (g_hash_table_iter_next (&iter, (void**)&method, (void**)&callers)) { for (l = callers; l; l = l->next) { l2 = (GSList*)g_hash_table_lookup (ctx->module->method_to_callers, method); l2 = g_slist_prepend (l2, l->data); g_hash_table_insert (ctx->module->method_to_callers, method, l2); } } } if (cfg->verbose_level > 1) mono_llvm_dump_value (method); if (cfg->compile_aot && !cfg->llvm_only) mark_as_used (ctx->module, method); if (!cfg->llvm_only) { LLVMValueRef md_args [16]; LLVMValueRef md_node; int method_index; if (cfg->compile_aot) method_index = mono_aot_get_method_index (cfg->orig_method); else method_index = 1; md_args [0] = LLVMMDString (ctx->method_name, strlen (ctx->method_name)); md_args [1] = LLVMConstInt (LLVMInt32Type (), method_index, FALSE); md_node = LLVMMDNode (md_args, 2); LLVMAddNamedMetadataOperand (lmodule, "mono.function_indexes", md_node); //LLVMSetMetadata (method, md_kind, LLVMMDNode (&md_arg, 1)); } if (cfg->compile_aot) { /* Don't generate native code, keep the LLVM IR */ if (cfg->verbose_level) printf ("%s emitted as %s\n", mono_method_full_name (cfg->method, TRUE), ctx->method_name); #if LLVM_API_VERSION < 100 /* VerifyFunction can't handle some of the debug info created by DIBuilder in llvm 3.9 */ int err = LLVMVerifyFunction(ctx->lmethod, LLVMPrintMessageAction); g_assert (err == 0); #endif } else { //LLVMVerifyFunction(method, 0); #if LLVM_API_VERSION > 100 MonoDomain *domain = mono_domain_get (); MonoJitDomainInfo *domain_info; int nvars = g_hash_table_size (ctx->jit_callees); LLVMValueRef *callee_vars = g_new0 (LLVMValueRef, nvars); gpointer *callee_addrs = g_new0 (gpointer, nvars); GHashTableIter iter; LLVMValueRef var; MonoMethod *callee; gpointer eh_frame; /* * Compute the addresses of the LLVM globals pointing to the * methods called by the current method. Pass it to the trampoline * code so it can update them after their corresponding method was * compiled. */ g_hash_table_iter_init (&iter, ctx->jit_callees); i = 0; while (g_hash_table_iter_next (&iter, NULL, (void**)&var)) callee_vars [i ++] = var; cfg->native_code = mono_llvm_compile_method (ctx->module->mono_ee, ctx->lmethod, nvars, callee_vars, callee_addrs, &eh_frame); decode_llvm_eh_info (ctx, eh_frame); mono_domain_lock (domain); domain_info = domain_jit_info (domain); if (!domain_info->llvm_jit_callees) domain_info->llvm_jit_callees = g_hash_table_new (NULL, NULL); g_hash_table_iter_init (&iter, ctx->jit_callees); i = 0; while (g_hash_table_iter_next (&iter, (void**)&callee, (void**)&var)) { GSList *addrs = g_hash_table_lookup (domain_info->llvm_jit_callees, callee); addrs = g_slist_prepend (addrs, callee_addrs [i]); g_hash_table_insert (domain_info->llvm_jit_callees, callee, addrs); i ++; } mono_domain_unlock (domain); #else mono_llvm_optimize_method (ctx->module->mono_ee, ctx->lmethod); if (cfg->verbose_level > 1) mono_llvm_dump_value (ctx->lmethod); cfg->native_code = (unsigned char*)LLVMGetPointerToGlobal (ctx->module->ee, ctx->lmethod); /* Set by emit_cb */ g_assert (cfg->code_len); #endif } if (ctx->module->method_to_lmethod) g_hash_table_insert (ctx->module->method_to_lmethod, cfg->method, ctx->lmethod); if (ctx->module->idx_to_lmethod) g_hash_table_insert (ctx->module->idx_to_lmethod, GINT_TO_POINTER (cfg->method_index), ctx->lmethod); if (ctx->llvm_only && cfg->orig_method->klass->valuetype && !(cfg->orig_method->flags & METHOD_ATTRIBUTE_STATIC)) emit_unbox_tramp (ctx, ctx->method_name, ctx->method_type, ctx->lmethod, cfg->method_index); } /* * mono_llvm_create_vars: * * Same as mono_arch_create_vars () for LLVM. */ void mono_llvm_create_vars (MonoCompile *cfg) { MonoMethodSignature *sig; sig = mono_method_signature (cfg->method); if (cfg->gsharedvt && cfg->llvm_only) { if (mini_is_gsharedvt_variable_signature (sig) && sig->ret->type != MONO_TYPE_VOID) { cfg->vret_addr = mono_compile_create_var (cfg, &mono_get_intptr_class ()->byval_arg, OP_ARG); if (G_UNLIKELY (cfg->verbose_level > 1)) { printf ("vret_addr = "); mono_print_ins (cfg->vret_addr); } } } else { mono_arch_create_vars (cfg); } } /* * mono_llvm_emit_call: * * Same as mono_arch_emit_call () for LLVM. */ void mono_llvm_emit_call (MonoCompile *cfg, MonoCallInst *call) { MonoInst *in; MonoMethodSignature *sig; int i, n, stack_size; LLVMArgInfo *ainfo; stack_size = 0; sig = call->signature; n = sig->param_count + sig->hasthis; call->cinfo = get_llvm_call_info (cfg, sig); if (cfg->disable_llvm) return; if (sig->call_convention == MONO_CALL_VARARG) { cfg->exception_message = g_strdup ("varargs"); cfg->disable_llvm = TRUE; } for (i = 0; i < n; ++i) { MonoInst *ins; ainfo = call->cinfo->args + i; in = call->args [i]; /* Simply remember the arguments */ switch (ainfo->storage) { case LLVMArgNormal: { MonoType *t = (sig->hasthis && i == 0) ? &mono_get_intptr_class ()->byval_arg : ainfo->type; int opcode; opcode = mono_type_to_regmove (cfg, t); if (opcode == OP_FMOVE) { MONO_INST_NEW (cfg, ins, OP_FMOVE); ins->dreg = mono_alloc_freg (cfg); } else if (opcode == OP_LMOVE) { MONO_INST_NEW (cfg, ins, OP_LMOVE); ins->dreg = mono_alloc_lreg (cfg); } else if (opcode == OP_RMOVE) { MONO_INST_NEW (cfg, ins, OP_RMOVE); ins->dreg = mono_alloc_freg (cfg); } else { MONO_INST_NEW (cfg, ins, OP_MOVE); ins->dreg = mono_alloc_ireg (cfg); } ins->sreg1 = in->dreg; break; } case LLVMArgVtypeByVal: case LLVMArgVtypeByRef: case LLVMArgVtypeInReg: case LLVMArgVtypeAsScalar: case LLVMArgAsIArgs: case LLVMArgAsFpArgs: case LLVMArgGsharedvtVariable: case LLVMArgGsharedvtFixed: case LLVMArgGsharedvtFixedVtype: MONO_INST_NEW (cfg, ins, OP_LLVM_OUTARG_VT); ins->dreg = mono_alloc_ireg (cfg); ins->sreg1 = in->dreg; ins->inst_p0 = mono_mempool_alloc0 (cfg->mempool, sizeof (LLVMArgInfo)); memcpy (ins->inst_p0, ainfo, sizeof (LLVMArgInfo)); ins->inst_vtype = ainfo->type; ins->klass = mono_class_from_mono_type (ainfo->type); break; default: cfg->exception_message = g_strdup ("ainfo->storage"); cfg->disable_llvm = TRUE; return; } if (!cfg->disable_llvm) { MONO_ADD_INS (cfg->cbb, ins); mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, 0, FALSE); } } } static unsigned char* alloc_cb (LLVMValueRef function, int size) { MonoCompile *cfg; cfg = (MonoCompile*)mono_native_tls_get_value (current_cfg_tls_id); if (cfg) { // FIXME: dynamic return (unsigned char*)mono_domain_code_reserve (cfg->domain, size); } else { return (unsigned char*)mono_domain_code_reserve (mono_domain_get (), size); } } static void emitted_cb (LLVMValueRef function, void *start, void *end) { MonoCompile *cfg; cfg = (MonoCompile*)mono_native_tls_get_value (current_cfg_tls_id); g_assert (cfg); cfg->code_len = (guint8*)end - (guint8*)start; } static void exception_cb (void *data) { MonoCompile *cfg; MonoJitExceptionInfo *ei; guint32 ei_len, i, j, nested_len, nindex; gpointer *type_info; int this_reg, this_offset; cfg = (MonoCompile*)mono_native_tls_get_value (current_cfg_tls_id); g_assert (cfg); /* * data points to a DWARF FDE structure, convert it to our unwind format and * save it. * An alternative would be to save it directly, and modify our unwinder to work * with it. */ cfg->encoded_unwind_ops = mono_unwind_decode_fde ((guint8*)data, &cfg->encoded_unwind_ops_len, NULL, &ei, &ei_len, &type_info, &this_reg, &this_offset); if (cfg->verbose_level > 1) mono_print_unwind_info (cfg->encoded_unwind_ops, cfg->encoded_unwind_ops_len); /* Count nested clauses */ nested_len = 0; for (i = 0; i < ei_len; ++i) { gint32 cindex1 = *(gint32*)type_info [i]; MonoExceptionClause *clause1 = &cfg->header->clauses [cindex1]; for (j = 0; j < cfg->header->num_clauses; ++j) { int cindex2 = j; MonoExceptionClause *clause2 = &cfg->header->clauses [cindex2]; if (cindex1 != cindex2 && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset) { nested_len ++; } } } cfg->llvm_ex_info = (MonoJitExceptionInfo*)mono_mempool_alloc0 (cfg->mempool, (ei_len + nested_len) * sizeof (MonoJitExceptionInfo)); cfg->llvm_ex_info_len = ei_len + nested_len; memcpy (cfg->llvm_ex_info, ei, ei_len * sizeof (MonoJitExceptionInfo)); /* Fill the rest of the information from the type info */ for (i = 0; i < ei_len; ++i) { gint32 clause_index = *(gint32*)type_info [i]; MonoExceptionClause *clause = &cfg->header->clauses [clause_index]; cfg->llvm_ex_info [i].flags = clause->flags; cfg->llvm_ex_info [i].data.catch_class = clause->data.catch_class; cfg->llvm_ex_info [i].clause_index = clause_index; } /* * For nested clauses, the LLVM produced exception info associates the try interval with * the innermost handler, while mono expects it to be associated with all nesting clauses. * So add new clauses which use the IL info (catch class etc.) from the nesting clause, * and everything else from the nested clause. */ nindex = ei_len; for (i = 0; i < ei_len; ++i) { gint32 cindex1 = *(gint32*)type_info [i]; MonoExceptionClause *clause1 = &cfg->header->clauses [cindex1]; for (j = 0; j < cfg->header->num_clauses; ++j) { int cindex2 = j; MonoExceptionClause *clause2 = &cfg->header->clauses [cindex2]; MonoJitExceptionInfo *nesting_ei, *nested_ei; if (cindex1 != cindex2 && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset) { /* clause1 is the nested clause */ nested_ei = &cfg->llvm_ex_info [i]; nesting_ei = &cfg->llvm_ex_info [nindex]; nindex ++; memcpy (nesting_ei, nested_ei, sizeof (MonoJitExceptionInfo)); nesting_ei->flags = clause2->flags; nesting_ei->data.catch_class = clause2->data.catch_class; nesting_ei->clause_index = cindex2; } } } g_assert (nindex == ei_len + nested_len); cfg->llvm_this_reg = this_reg; cfg->llvm_this_offset = this_offset; /* type_info [i] is cfg mempool allocated, no need to free it */ g_free (ei); g_free (type_info); } #if LLVM_API_VERSION > 100 /* * decode_llvm_eh_info: * * Decode the EH table emitted by llvm in jit mode, and store * the result into cfg. */ static void decode_llvm_eh_info (EmitContext *ctx, gpointer eh_frame) { MonoCompile *cfg = ctx->cfg; guint8 *cie, *fde; int fde_len; MonoLLVMFDEInfo info; MonoJitExceptionInfo *ei; guint8 *p = eh_frame; int version, fde_count, fde_offset; guint32 ei_len, i, nested_len; gpointer *type_info; gint32 *table; guint8 *unw_info; /* * Decode the one element EH table emitted by the MonoException class * in llvm. */ /* Similar to decode_llvm_mono_eh_frame () in aot-runtime.c */ version = *p; g_assert (version == 3); p ++; p ++; p = (guint8 *)ALIGN_PTR_TO (p, 4); fde_count = *(guint32*)p; p += 4; table = (gint32*)p; g_assert (fde_count <= 2); /* The first entry is the real method */ g_assert (table [0] == 1); fde_offset = table [1]; table += fde_count * 2; /* Extra entry */ cfg->code_len = table [0]; fde_len = table [1] - fde_offset; table += 2; fde = (guint8*)eh_frame + fde_offset; cie = (guint8*)table; /* Compute lengths */ mono_unwind_decode_llvm_mono_fde (fde, fde_len, cie, cfg->native_code, &info, NULL, NULL, NULL); ei = (MonoJitExceptionInfo *)g_malloc0 (info.ex_info_len * sizeof (MonoJitExceptionInfo)); type_info = (gpointer *)g_malloc0 (info.ex_info_len * sizeof (gpointer)); unw_info = (guint8*)g_malloc0 (info.unw_info_len); mono_unwind_decode_llvm_mono_fde (fde, fde_len, cie, cfg->native_code, &info, ei, type_info, unw_info); cfg->encoded_unwind_ops = unw_info; cfg->encoded_unwind_ops_len = info.unw_info_len; if (cfg->verbose_level > 1) mono_print_unwind_info (cfg->encoded_unwind_ops, cfg->encoded_unwind_ops_len); if (info.this_reg != -1) { cfg->llvm_this_reg = info.this_reg; cfg->llvm_this_offset = info.this_offset; } ei_len = info.ex_info_len; // Nested clauses are currently disabled nested_len = 0; cfg->llvm_ex_info = (MonoJitExceptionInfo*)mono_mempool_alloc0 (cfg->mempool, (ei_len + nested_len) * sizeof (MonoJitExceptionInfo)); cfg->llvm_ex_info_len = ei_len + nested_len; memcpy (cfg->llvm_ex_info, ei, ei_len * sizeof (MonoJitExceptionInfo)); /* Fill the rest of the information from the type info */ for (i = 0; i < ei_len; ++i) { gint32 clause_index = *(gint32*)type_info [i]; MonoExceptionClause *clause = &cfg->header->clauses [clause_index]; cfg->llvm_ex_info [i].flags = clause->flags; cfg->llvm_ex_info [i].data.catch_class = clause->data.catch_class; cfg->llvm_ex_info [i].clause_index = clause_index; } } #endif static char* dlsym_cb (const char *name, void **symbol) { MonoDl *current; char *err; err = NULL; if (!strcmp (name, "__bzero")) { *symbol = (void*)bzero; } else { current = mono_dl_open (NULL, 0, NULL); g_assert (current); err = mono_dl_symbol (current, name, symbol); mono_dl_close (current); } #ifdef MONO_ARCH_HAVE_CREATE_LLVM_NATIVE_THUNK *symbol = (char*)mono_arch_create_llvm_native_thunk (mono_domain_get (), (guint8*)(*symbol)); #endif return err; } static inline void AddFunc (LLVMModuleRef module, const char *name, LLVMTypeRef ret_type, LLVMTypeRef *param_types, int nparams) { LLVMAddFunction (module, name, LLVMFunctionType (ret_type, param_types, nparams, FALSE)); } static inline void AddFunc2 (LLVMModuleRef module, const char *name, LLVMTypeRef ret_type, LLVMTypeRef param_type1, LLVMTypeRef param_type2) { LLVMTypeRef param_types [4]; param_types [0] = param_type1; param_types [1] = param_type2; AddFunc (module, name, ret_type, param_types, 2); } typedef enum { INTRINS_MEMSET, INTRINS_MEMCPY, INTRINS_SADD_OVF_I32, INTRINS_UADD_OVF_I32, INTRINS_SSUB_OVF_I32, INTRINS_USUB_OVF_I32, INTRINS_SMUL_OVF_I32, INTRINS_UMUL_OVF_I32, INTRINS_SADD_OVF_I64, INTRINS_UADD_OVF_I64, INTRINS_SSUB_OVF_I64, INTRINS_USUB_OVF_I64, INTRINS_SMUL_OVF_I64, INTRINS_UMUL_OVF_I64, INTRINS_SIN, INTRINS_COS, INTRINS_SQRT, INTRINS_FABS, INTRINS_EXPECT_I8, INTRINS_EXPECT_I1, #if defined(TARGET_AMD64) || defined(TARGET_X86) INTRINS_SSE_PMOVMSKB, INTRINS_SSE_PSRLI_W, INTRINS_SSE_PSRAI_W, INTRINS_SSE_PSLLI_W, INTRINS_SSE_PSRLI_D, INTRINS_SSE_PSRAI_D, INTRINS_SSE_PSLLI_D, INTRINS_SSE_PSRLI_Q, INTRINS_SSE_PSLLI_Q, INTRINS_SSE_SQRT_PD, INTRINS_SSE_SQRT_PS, INTRINS_SSE_RSQRT_PS, INTRINS_SSE_RCP_PS, INTRINS_SSE_CVTTPD2DQ, INTRINS_SSE_CVTTPS2DQ, INTRINS_SSE_CVTDQ2PD, INTRINS_SSE_CVTDQ2PS, INTRINS_SSE_CVTPD2DQ, INTRINS_SSE_CVTPS2DQ, INTRINS_SSE_CVTPD2PS, INTRINS_SSE_CVTPS2PD, INTRINS_SSE_CMPPD, INTRINS_SSE_CMPPS, INTRINS_SSE_PACKSSWB, INTRINS_SSE_PACKUSWB, INTRINS_SSE_PACKSSDW, INTRINS_SSE_PACKUSDW, INTRINS_SSE_MINPS, INTRINS_SSE_MAXPS, INTRINS_SSE_HADDPS, INTRINS_SSE_HSUBPS, INTRINS_SSE_ADDSUBPS, INTRINS_SSE_MINPD, INTRINS_SSE_MAXPD, INTRINS_SSE_HADDPD, INTRINS_SSE_HSUBPD, INTRINS_SSE_ADDSUBPD, INTRINS_SSE_PADDSW, INTRINS_SSE_PSUBSW, INTRINS_SSE_PADDUSW, INTRINS_SSE_PSUBUSW, INTRINS_SSE_PAVGW, INTRINS_SSE_PMULHW, INTRINS_SSE_PMULHU, INTRINS_SE_PADDSB, INTRINS_SSE_PSUBSB, INTRINS_SSE_PADDUSB, INTRINS_SSE_PSUBUSB, INTRINS_SSE_PAVGB, INTRINS_SSE_PAUSE, INTRINS_SSE_DPPS, #endif INTRINS_NUM } IntrinsicId; typedef struct { IntrinsicId id; const char *name; } IntrinsicDesc; static IntrinsicDesc intrinsics[] = { {INTRINS_MEMSET, "llvm.memset.p0i8.i32"}, {INTRINS_MEMCPY, "llvm.memcpy.p0i8.p0i8.i32"}, {INTRINS_SADD_OVF_I32, "llvm.sadd.with.overflow.i32"}, {INTRINS_UADD_OVF_I32, "llvm.uadd.with.overflow.i32"}, {INTRINS_SSUB_OVF_I32, "llvm.ssub.with.overflow.i32"}, {INTRINS_USUB_OVF_I32, "llvm.usub.with.overflow.i32"}, {INTRINS_SMUL_OVF_I32, "llvm.smul.with.overflow.i32"}, {INTRINS_UMUL_OVF_I32, "llvm.umul.with.overflow.i32"}, {INTRINS_SADD_OVF_I64, "llvm.sadd.with.overflow.i64"}, {INTRINS_UADD_OVF_I64, "llvm.uadd.with.overflow.i64"}, {INTRINS_SSUB_OVF_I64, "llvm.ssub.with.overflow.i64"}, {INTRINS_USUB_OVF_I64, "llvm.usub.with.overflow.i64"}, {INTRINS_SMUL_OVF_I64, "llvm.smul.with.overflow.i64"}, {INTRINS_UMUL_OVF_I64, "llvm.umul.with.overflow.i64"}, {INTRINS_SIN, "llvm.sin.f64"}, {INTRINS_COS, "llvm.cos.f64"}, {INTRINS_SQRT, "llvm.sqrt.f64"}, /* This isn't an intrinsic, instead llvm seems to special case it by name */ {INTRINS_FABS, "fabs"}, {INTRINS_EXPECT_I8, "llvm.expect.i8"}, {INTRINS_EXPECT_I1, "llvm.expect.i1"}, #if defined(TARGET_AMD64) || defined(TARGET_X86) {INTRINS_SSE_PMOVMSKB, "llvm.x86.sse2.pmovmskb.128"}, {INTRINS_SSE_PSRLI_W, "llvm.x86.sse2.psrli.w"}, {INTRINS_SSE_PSRAI_W, "llvm.x86.sse2.psrai.w"}, {INTRINS_SSE_PSLLI_W, "llvm.x86.sse2.pslli.w"}, {INTRINS_SSE_PSRLI_D, "llvm.x86.sse2.psrli.d"}, {INTRINS_SSE_PSRAI_D, "llvm.x86.sse2.psrai.d"}, {INTRINS_SSE_PSLLI_D, "llvm.x86.sse2.pslli.d"}, {INTRINS_SSE_PSRLI_Q, "llvm.x86.sse2.psrli.q"}, {INTRINS_SSE_PSLLI_Q, "llvm.x86.sse2.pslli.q"}, {INTRINS_SSE_SQRT_PD, "llvm.x86.sse2.sqrt.pd"}, {INTRINS_SSE_SQRT_PS, "llvm.x86.sse.sqrt.ps"}, {INTRINS_SSE_RSQRT_PS, "llvm.x86.sse.rsqrt.ps"}, {INTRINS_SSE_RCP_PS, "llvm.x86.sse.rcp.ps"}, {INTRINS_SSE_CVTTPD2DQ, "llvm.x86.sse2.cvttpd2dq"}, {INTRINS_SSE_CVTTPS2DQ, "llvm.x86.sse2.cvttps2dq"}, {INTRINS_SSE_CVTDQ2PD, "llvm.x86.sse2.cvtdq2pd"}, {INTRINS_SSE_CVTDQ2PS, "llvm.x86.sse2.cvtdq2ps"}, {INTRINS_SSE_CVTPD2DQ, "llvm.x86.sse2.cvtpd2dq"}, {INTRINS_SSE_CVTPS2DQ, "llvm.x86.sse2.cvtps2dq"}, {INTRINS_SSE_CVTPD2PS, "llvm.x86.sse2.cvtpd2ps"}, {INTRINS_SSE_CVTPS2PD, "llvm.x86.sse2.cvtps2pd"}, {INTRINS_SSE_CMPPD, "llvm.x86.sse2.cmp.pd"}, {INTRINS_SSE_CMPPS, "llvm.x86.sse.cmp.ps"}, {INTRINS_SSE_PACKSSWB, "llvm.x86.sse2.packsswb.128"}, {INTRINS_SSE_PACKUSWB, "llvm.x86.sse2.packuswb.128"}, {INTRINS_SSE_PACKSSDW, "llvm.x86.sse2.packssdw.128"}, {INTRINS_SSE_PACKUSDW, "llvm.x86.sse41.packusdw"}, {INTRINS_SSE_MINPS, "llvm.x86.sse.min.ps"}, {INTRINS_SSE_MAXPS, "llvm.x86.sse.max.ps"}, {INTRINS_SSE_HADDPS, "llvm.x86.sse3.hadd.ps"}, {INTRINS_SSE_HSUBPS, "llvm.x86.sse3.hsub.ps"}, {INTRINS_SSE_ADDSUBPS, "llvm.x86.sse3.addsub.ps"}, {INTRINS_SSE_MINPD, "llvm.x86.sse2.min.pd"}, {INTRINS_SSE_MAXPD, "llvm.x86.sse2.max.pd"}, {INTRINS_SSE_HADDPD, "llvm.x86.sse3.hadd.pd"}, {INTRINS_SSE_HSUBPD, "llvm.x86.sse3.hsub.pd"}, {INTRINS_SSE_ADDSUBPD, "llvm.x86.sse3.addsub.pd"}, {INTRINS_SSE_PADDSW, "llvm.x86.sse2.padds.w"}, {INTRINS_SSE_PSUBSW, "llvm.x86.sse2.psubs.w"}, {INTRINS_SSE_PADDUSW, "llvm.x86.sse2.paddus.w"}, {INTRINS_SSE_PSUBUSW, "llvm.x86.sse2.psubus.w"}, {INTRINS_SSE_PAVGW, "llvm.x86.sse2.pavg.w"}, {INTRINS_SSE_PMULHW, "llvm.x86.sse2.pmulh.w"}, {INTRINS_SSE_PMULHU, "llvm.x86.sse2.pmulhu.w"}, {INTRINS_SE_PADDSB, "llvm.x86.sse2.padds.b"}, {INTRINS_SSE_PSUBSB, "llvm.x86.sse2.psubs.b"}, {INTRINS_SSE_PADDUSB, "llvm.x86.sse2.paddus.b"}, {INTRINS_SSE_PSUBUSB, "llvm.x86.sse2.psubus.b"}, {INTRINS_SSE_PAVGB, "llvm.x86.sse2.pavg.b"}, {INTRINS_SSE_PAUSE, "llvm.x86.sse2.pause"}, {INTRINS_SSE_DPPS, "llvm.x86.sse41.dpps"} #endif }; static void add_sse_binary (LLVMModuleRef module, const char *name, int type) { LLVMTypeRef ret_type = type_to_simd_type (type); AddFunc2 (module, name, ret_type, ret_type, ret_type); } static void add_intrinsic (LLVMModuleRef module, int id) { const char *name; #if defined(TARGET_AMD64) || defined(TARGET_X86) LLVMTypeRef ret_type, arg_types [16]; #endif name = g_hash_table_lookup (intrins_id_to_name, GINT_TO_POINTER (id)); g_assert (name); switch (id) { case INTRINS_MEMSET: { LLVMTypeRef params [] = { LLVMPointerType (LLVMInt8Type (), 0), LLVMInt8Type (), LLVMInt32Type (), LLVMInt32Type (), LLVMInt1Type () }; AddFunc (module, name, LLVMVoidType (), params, 5); break; } case INTRINS_MEMCPY: { LLVMTypeRef params [] = { LLVMPointerType (LLVMInt8Type (), 0), LLVMPointerType (LLVMInt8Type (), 0), LLVMInt32Type (), LLVMInt32Type (), LLVMInt1Type () }; AddFunc (module, name, LLVMVoidType (), params, 5); break; } case INTRINS_SADD_OVF_I32: case INTRINS_UADD_OVF_I32: case INTRINS_SSUB_OVF_I32: case INTRINS_USUB_OVF_I32: case INTRINS_SMUL_OVF_I32: case INTRINS_UMUL_OVF_I32: { LLVMTypeRef ovf_res_i32 [] = { LLVMInt32Type (), LLVMInt1Type () }; LLVMTypeRef params [] = { LLVMInt32Type (), LLVMInt32Type () }; LLVMTypeRef ret_type = LLVMStructType (ovf_res_i32, 2, FALSE); AddFunc (module, name, ret_type, params, 2); break; } case INTRINS_SADD_OVF_I64: case INTRINS_UADD_OVF_I64: case INTRINS_SSUB_OVF_I64: case INTRINS_USUB_OVF_I64: case INTRINS_SMUL_OVF_I64: case INTRINS_UMUL_OVF_I64: { LLVMTypeRef ovf_res_i64 [] = { LLVMInt64Type (), LLVMInt1Type () }; LLVMTypeRef params [] = { LLVMInt64Type (), LLVMInt64Type () }; LLVMTypeRef ret_type = LLVMStructType (ovf_res_i64, 2, FALSE); AddFunc (module, name, ret_type, params, 2); break; } case INTRINS_SIN: case INTRINS_COS: case INTRINS_SQRT: case INTRINS_FABS: { LLVMTypeRef params [] = { LLVMDoubleType () }; AddFunc (module, name, LLVMDoubleType (), params, 1); break; } case INTRINS_EXPECT_I8: AddFunc2 (module, name, LLVMInt8Type (), LLVMInt8Type (), LLVMInt8Type ()); break; case INTRINS_EXPECT_I1: AddFunc2 (module, name, LLVMInt1Type (), LLVMInt1Type (), LLVMInt1Type ()); break; #if defined(TARGET_AMD64) || defined(TARGET_X86) case INTRINS_SSE_PMOVMSKB: /* pmovmskb */ ret_type = LLVMInt32Type (); arg_types [0] = type_to_simd_type (MONO_TYPE_I1); AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_PSRLI_W: case INTRINS_SSE_PSRAI_W: case INTRINS_SSE_PSLLI_W: /* shifts */ ret_type = type_to_simd_type (MONO_TYPE_I2); arg_types [0] = ret_type; arg_types [1] = LLVMInt32Type (); AddFunc (module, name, ret_type, arg_types, 2); break; case INTRINS_SSE_PSRLI_D: case INTRINS_SSE_PSRAI_D: case INTRINS_SSE_PSLLI_D: ret_type = type_to_simd_type (MONO_TYPE_I4); arg_types [0] = ret_type; arg_types [1] = LLVMInt32Type (); AddFunc (module, name, ret_type, arg_types, 2); break; case INTRINS_SSE_PSRLI_Q: case INTRINS_SSE_PSLLI_Q: ret_type = type_to_simd_type (MONO_TYPE_I8); arg_types [0] = ret_type; arg_types [1] = LLVMInt32Type (); AddFunc (module, name, ret_type, arg_types, 2); break; case INTRINS_SSE_SQRT_PD: /* Unary ops */ ret_type = type_to_simd_type (MONO_TYPE_R8); arg_types [0] = ret_type; AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_SQRT_PS: ret_type = type_to_simd_type (MONO_TYPE_R4); arg_types [0] = ret_type; AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_RSQRT_PS: ret_type = type_to_simd_type (MONO_TYPE_R4); arg_types [0] = ret_type; AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_RCP_PS: ret_type = type_to_simd_type (MONO_TYPE_R4); arg_types [0] = ret_type; AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_CVTTPD2DQ: ret_type = type_to_simd_type (MONO_TYPE_I4); arg_types [0] = type_to_simd_type (MONO_TYPE_R8); AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_CVTTPS2DQ: ret_type = type_to_simd_type (MONO_TYPE_I4); arg_types [0] = type_to_simd_type (MONO_TYPE_R4); AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_CVTDQ2PD: /* Conversion ops */ ret_type = type_to_simd_type (MONO_TYPE_R8); arg_types [0] = type_to_simd_type (MONO_TYPE_I4); AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_CVTDQ2PS: ret_type = type_to_simd_type (MONO_TYPE_R4); arg_types [0] = type_to_simd_type (MONO_TYPE_I4); AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_CVTPD2DQ: ret_type = type_to_simd_type (MONO_TYPE_I4); arg_types [0] = type_to_simd_type (MONO_TYPE_R8); AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_CVTPS2DQ: ret_type = type_to_simd_type (MONO_TYPE_I4); arg_types [0] = type_to_simd_type (MONO_TYPE_R4); AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_CVTPD2PS: ret_type = type_to_simd_type (MONO_TYPE_R4); arg_types [0] = type_to_simd_type (MONO_TYPE_R8); AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_CVTPS2PD: ret_type = type_to_simd_type (MONO_TYPE_R8); arg_types [0] = type_to_simd_type (MONO_TYPE_R4); AddFunc (module, name, ret_type, arg_types, 1); break; case INTRINS_SSE_CMPPD: /* cmp pd/ps */ ret_type = type_to_simd_type (MONO_TYPE_R8); arg_types [0] = ret_type; arg_types [1] = ret_type; arg_types [2] = LLVMInt8Type (); AddFunc (module, name, ret_type, arg_types, 3); break; case INTRINS_SSE_CMPPS: ret_type = type_to_simd_type (MONO_TYPE_R4); arg_types [0] = ret_type; arg_types [1] = ret_type; arg_types [2] = LLVMInt8Type (); AddFunc (module, name, ret_type, arg_types, 3); break; case INTRINS_SSE_PACKSSWB: case INTRINS_SSE_PACKUSWB: case INTRINS_SSE_PACKSSDW: /* pack */ ret_type = type_to_simd_type (MONO_TYPE_I1); arg_types [0] = type_to_simd_type (MONO_TYPE_I2); arg_types [1] = type_to_simd_type (MONO_TYPE_I2); AddFunc (module, name, ret_type, arg_types, 2); break; case INTRINS_SSE_PACKUSDW: ret_type = type_to_simd_type (MONO_TYPE_I2); arg_types [0] = type_to_simd_type (MONO_TYPE_I4); arg_types [1] = type_to_simd_type (MONO_TYPE_I4); AddFunc (module, name, ret_type, arg_types, 2); break; /* SSE Binary ops */ case INTRINS_SSE_PADDSW: case INTRINS_SSE_PSUBSW: case INTRINS_SSE_PADDUSW: case INTRINS_SSE_PSUBUSW: case INTRINS_SSE_PAVGW: case INTRINS_SSE_PMULHW: case INTRINS_SSE_PMULHU: add_sse_binary (module, name, MONO_TYPE_I2); break; case INTRINS_SSE_MINPS: case INTRINS_SSE_MAXPS: case INTRINS_SSE_HADDPS: case INTRINS_SSE_HSUBPS: case INTRINS_SSE_ADDSUBPS: add_sse_binary (module, name, MONO_TYPE_R4); break; case INTRINS_SSE_MINPD: case INTRINS_SSE_MAXPD: case INTRINS_SSE_HADDPD: case INTRINS_SSE_HSUBPD: case INTRINS_SSE_ADDSUBPD: add_sse_binary (module, name, MONO_TYPE_R8); break; case INTRINS_SE_PADDSB: case INTRINS_SSE_PSUBSB: case INTRINS_SSE_PADDUSB: case INTRINS_SSE_PSUBUSB: case INTRINS_SSE_PAVGB: add_sse_binary (module, name, MONO_TYPE_I1); break; case INTRINS_SSE_PAUSE: AddFunc (module, "llvm.x86.sse2.pause", LLVMVoidType (), NULL, 0); break; case INTRINS_SSE_DPPS: ret_type = type_to_simd_type (MONO_TYPE_R4); arg_types [0] = type_to_simd_type (MONO_TYPE_R4); arg_types [1] = type_to_simd_type (MONO_TYPE_R4); #if LLVM_API_VERSION >= 500 arg_types [2] = LLVMInt8Type (); #else arg_types [2] = LLVMInt32Type (); #endif AddFunc (module, name, ret_type, arg_types, 3); break; #endif default: g_assert_not_reached (); break; } } static LLVMValueRef get_intrinsic (EmitContext *ctx, const char *name) { #if LLVM_API_VERSION > 100 LLVMValueRef res; /* * Every method is emitted into its own module so * we can add intrinsics on demand. */ res = LLVMGetNamedFunction (ctx->lmodule, name); if (!res) { int id = -1; /* No locking needed */ id = GPOINTER_TO_INT (g_hash_table_lookup (intrins_name_to_id, name)); id --; if (id == -1) printf ("%s\n", name); g_assert (id != -1); add_intrinsic (ctx->lmodule, id); res = LLVMGetNamedFunction (ctx->lmodule, name); g_assert (res); } return res; #else LLVMValueRef res; res = LLVMGetNamedFunction (ctx->lmodule, name); g_assert (res); return res; #endif } static void add_intrinsics (LLVMModuleRef module) { int i; /* Emit declarations of instrinsics */ /* * It would be nicer to emit only the intrinsics actually used, but LLVM's Module * type doesn't seem to do any locking. */ for (i = 0; i < INTRINS_NUM; ++i) add_intrinsic (module, i); /* EH intrinsics */ { AddFunc (module, "mono_personality", LLVMVoidType (), NULL, 0); AddFunc (module, "llvm_resume_unwind_trampoline", LLVMVoidType (), NULL, 0); } /* Load/Store intrinsics */ { LLVMTypeRef arg_types [5]; int i; char name [128]; for (i = 1; i <= 8; i *= 2) { arg_types [0] = LLVMPointerType (LLVMIntType (i * 8), 0); arg_types [1] = LLVMInt32Type (); arg_types [2] = LLVMInt1Type (); arg_types [3] = LLVMInt32Type (); sprintf (name, "llvm.mono.load.i%d.p0i%d", i * 8, i * 8); AddFunc (module, name, LLVMIntType (i * 8), arg_types, 4); arg_types [0] = LLVMIntType (i * 8); arg_types [1] = LLVMPointerType (LLVMIntType (i * 8), 0); arg_types [2] = LLVMInt32Type (); arg_types [3] = LLVMInt1Type (); arg_types [4] = LLVMInt32Type (); sprintf (name, "llvm.mono.store.i%d.p0i%d", i * 8, i * 8); AddFunc (module, name, LLVMVoidType (), arg_types, 5); } } } static void add_types (MonoLLVMModule *module) { module->ptr_type = LLVMPointerType (sizeof (gpointer) == 8 ? LLVMInt64Type () : LLVMInt32Type (), 0); } void mono_llvm_init (void) { GHashTable *h; int i; mono_native_tls_alloc (¤t_cfg_tls_id, NULL); h = g_hash_table_new (NULL, NULL); for (i = 0; i < INTRINS_NUM; ++i) g_hash_table_insert (h, GINT_TO_POINTER (intrinsics [i].id), (gpointer)intrinsics [i].name); intrins_id_to_name = h; h = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; i < INTRINS_NUM; ++i) g_hash_table_insert (h, (gpointer)intrinsics [i].name, GINT_TO_POINTER (intrinsics [i].id + 1)); intrins_name_to_id = h; } static void init_jit_module (MonoDomain *domain) { MonoJitDomainInfo *dinfo; MonoLLVMModule *module; char *name; dinfo = domain_jit_info (domain); if (dinfo->llvm_module) return; mono_loader_lock (); if (dinfo->llvm_module) { mono_loader_unlock (); return; } module = g_new0 (MonoLLVMModule, 1); name = g_strdup_printf ("mono-%s", domain->friendly_name); module->lmodule = LLVMModuleCreateWithName (name); module->context = LLVMGetGlobalContext (); module->mono_ee = (MonoEERef*)mono_llvm_create_ee (LLVMCreateModuleProviderForExistingModule (module->lmodule), alloc_cb, emitted_cb, exception_cb, dlsym_cb, &module->ee); add_intrinsics (module->lmodule); add_types (module); module->llvm_types = g_hash_table_new (NULL, NULL); #if LLVM_API_VERSION < 100 MonoJitICallInfo *info; info = mono_find_jit_icall_by_name ("llvm_resume_unwind_trampoline"); g_assert (info); LLVMAddGlobalMapping (module->ee, LLVMGetNamedFunction (module->lmodule, "llvm_resume_unwind_trampoline"), (void*)info->func); #endif mono_memory_barrier (); dinfo->llvm_module = module; mono_loader_unlock (); } void mono_llvm_cleanup (void) { MonoLLVMModule *module = &aot_module; if (module->lmodule) LLVMDisposeModule (module->lmodule); if (module->context) LLVMContextDispose (module->context); } void mono_llvm_free_domain_info (MonoDomain *domain) { MonoJitDomainInfo *info = domain_jit_info (domain); MonoLLVMModule *module = (MonoLLVMModule*)info->llvm_module; int i; if (!module) return; if (module->llvm_types) g_hash_table_destroy (module->llvm_types); mono_llvm_dispose_ee (module->mono_ee); if (module->bb_names) { for (i = 0; i < module->bb_names_len; ++i) g_free (module->bb_names [i]); g_free (module->bb_names); } //LLVMDisposeModule (module->module); g_free (module); info->llvm_module = NULL; } void mono_llvm_create_aot_module (MonoAssembly *assembly, const char *global_prefix, int initial_got_size, gboolean emit_dwarf, gboolean static_link, gboolean llvm_only) { MonoLLVMModule *module = &aot_module; /* Delete previous module */ if (module->plt_entries) g_hash_table_destroy (module->plt_entries); if (module->lmodule) LLVMDisposeModule (module->lmodule); memset (module, 0, sizeof (aot_module)); module->lmodule = LLVMModuleCreateWithName ("aot"); module->assembly = assembly; module->global_prefix = g_strdup (global_prefix); module->got_symbol = g_strdup_printf ("%s_llvm_got", global_prefix); module->eh_frame_symbol = g_strdup_printf ("%s_eh_frame", global_prefix); module->get_method_symbol = g_strdup_printf ("%s_get_method", global_prefix); module->get_unbox_tramp_symbol = g_strdup_printf ("%s_get_unbox_tramp", global_prefix); module->external_symbols = TRUE; module->emit_dwarf = emit_dwarf; module->static_link = static_link; module->llvm_only = llvm_only; /* The first few entries are reserved */ module->max_got_offset = initial_got_size; module->context = LLVMGetGlobalContext (); if (llvm_only) /* clang ignores our debug info because it has an invalid version */ module->emit_dwarf = FALSE; add_intrinsics (module->lmodule); add_types (module); #if LLVM_API_VERSION > 100 if (module->emit_dwarf) { char *dir, *build_info, *s, *cu_name; module->di_builder = mono_llvm_create_di_builder (module->lmodule); // FIXME: dir = g_strdup ("."); build_info = mono_get_runtime_build_info (); s = g_strdup_printf ("Mono AOT Compiler %s (LLVM)", build_info); cu_name = g_path_get_basename (assembly->image->name); module->cu = mono_llvm_di_create_compile_unit (module->di_builder, cu_name, dir, s); g_free (dir); g_free (build_info); g_free (s); } #endif /* Add GOT */ /* * We couldn't compute the type of the LLVM global representing the got because * its size is only known after all the methods have been emitted. So create * a dummy variable, and replace all uses it with the real got variable when * its size is known in mono_llvm_emit_aot_module (). */ { LLVMTypeRef got_type = LLVMArrayType (module->ptr_type, 0); module->got_var = LLVMAddGlobal (module->lmodule, got_type, "mono_dummy_got"); LLVMSetInitializer (module->got_var, LLVMConstNull (got_type)); } /* Add initialization array */ if (llvm_only) { LLVMTypeRef inited_type = LLVMArrayType (LLVMInt8Type (), 0); module->inited_var = LLVMAddGlobal (aot_module.lmodule, inited_type, "mono_inited_tmp"); LLVMSetInitializer (module->inited_var, LLVMConstNull (inited_type)); } if (llvm_only) emit_init_icall_wrappers (module); emit_llvm_code_start (module); /* Add a dummy personality function */ if (!use_debug_personality) { LLVMValueRef personality = LLVMAddFunction (module->lmodule, default_personality_name, LLVMFunctionType (LLVMInt32Type (), NULL, 0, TRUE)); LLVMSetLinkage (personality, LLVMExternalLinkage); mark_as_used (module, personality); } /* Add a reference to the c++ exception we throw/catch */ { LLVMTypeRef exc = LLVMPointerType (LLVMInt8Type (), 0); module->sentinel_exception = LLVMAddGlobal (module->lmodule, exc, "_ZTIPi"); LLVMSetLinkage (module->sentinel_exception, LLVMExternalLinkage); mono_llvm_set_is_constant (module->sentinel_exception); } module->llvm_types = g_hash_table_new (NULL, NULL); module->plt_entries = g_hash_table_new (g_str_hash, g_str_equal); module->plt_entries_ji = g_hash_table_new (NULL, NULL); module->direct_callables = g_hash_table_new (g_str_hash, g_str_equal); module->method_to_lmethod = g_hash_table_new (NULL, NULL); module->idx_to_lmethod = g_hash_table_new (NULL, NULL); module->idx_to_unbox_tramp = g_hash_table_new (NULL, NULL); module->method_to_callers = g_hash_table_new (NULL, NULL); } static LLVMValueRef llvm_array_from_uints (LLVMTypeRef el_type, guint32 *values, int nvalues) { int i; LLVMValueRef res, *vals; vals = g_new0 (LLVMValueRef, nvalues); for (i = 0; i < nvalues; ++i) vals [i] = LLVMConstInt (LLVMInt32Type (), values [i], FALSE); res = LLVMConstArray (LLVMInt32Type (), vals, nvalues); g_free (vals); return res; } static LLVMValueRef llvm_array_from_bytes (guint8 *values, int nvalues) { int i; LLVMValueRef res, *vals; vals = g_new0 (LLVMValueRef, nvalues); for (i = 0; i < nvalues; ++i) vals [i] = LLVMConstInt (LLVMInt8Type (), values [i], FALSE); res = LLVMConstArray (LLVMInt8Type (), vals, nvalues); g_free (vals); return res; } /* * mono_llvm_emit_aot_file_info: * * Emit the MonoAotFileInfo structure. * Same as emit_aot_file_info () in aot-compiler.c. */ void mono_llvm_emit_aot_file_info (MonoAotFileInfo *info, gboolean has_jitted_code) { MonoLLVMModule *module = &aot_module; /* Save these for later */ memcpy (&module->aot_info, info, sizeof (MonoAotFileInfo)); module->has_jitted_code = has_jitted_code; } /* * mono_llvm_emit_aot_data: * * Emit the binary data DATA pointed to by symbol SYMBOL. */ void mono_llvm_emit_aot_data (const char *symbol, guint8 *data, int data_len) { MonoLLVMModule *module = &aot_module; LLVMTypeRef type; LLVMValueRef d; type = LLVMArrayType (LLVMInt8Type (), data_len); d = LLVMAddGlobal (module->lmodule, type, symbol); LLVMSetVisibility (d, LLVMHiddenVisibility); LLVMSetLinkage (d, LLVMInternalLinkage); LLVMSetInitializer (d, mono_llvm_create_constant_data_array (data, data_len)); LLVMSetAlignment (d, 8); mono_llvm_set_is_constant (d); } /* Add a reference to a global defined in JITted code */ static LLVMValueRef AddJitGlobal (MonoLLVMModule *module, LLVMTypeRef type, const char *name) { char *s; LLVMValueRef v; s = g_strdup_printf ("%s%s", module->global_prefix, name); v = LLVMAddGlobal (module->lmodule, LLVMInt8Type (), s); g_free (s); return v; } static void emit_aot_file_info (MonoLLVMModule *module) { LLVMTypeRef file_info_type; LLVMTypeRef *eltypes, eltype; LLVMValueRef info_var; LLVMValueRef *fields; int i, nfields, tindex; MonoAotFileInfo *info; LLVMModuleRef lmodule = module->lmodule; info = &module->aot_info; /* Create an LLVM type to represent MonoAotFileInfo */ nfields = 2 + MONO_AOT_FILE_INFO_NUM_SYMBOLS + 16 + 5; eltypes = g_new (LLVMTypeRef, nfields); tindex = 0; eltypes [tindex ++] = LLVMInt32Type (); eltypes [tindex ++] = LLVMInt32Type (); /* Symbols */ for (i = 0; i < MONO_AOT_FILE_INFO_NUM_SYMBOLS; ++i) eltypes [tindex ++] = LLVMPointerType (LLVMInt8Type (), 0); /* Scalars */ for (i = 0; i < 15; ++i) eltypes [tindex ++] = LLVMInt32Type (); /* Arrays */ eltypes [tindex ++] = LLVMArrayType (LLVMInt32Type (), MONO_AOT_TABLE_NUM); for (i = 0; i < 4; ++i) eltypes [tindex ++] = LLVMArrayType (LLVMInt32Type (), MONO_AOT_TRAMP_NUM); eltypes [tindex ++] = LLVMArrayType (LLVMInt8Type (), 16); g_assert (tindex == nfields); file_info_type = LLVMStructCreateNamed (module->context, "MonoAotFileInfo"); LLVMStructSetBody (file_info_type, eltypes, nfields, FALSE); info_var = LLVMAddGlobal (lmodule, file_info_type, "mono_aot_file_info"); if (module->static_link) { LLVMSetVisibility (info_var, LLVMHiddenVisibility); LLVMSetLinkage (info_var, LLVMInternalLinkage); } fields = g_new (LLVMValueRef, nfields); tindex = 0; fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->version, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->dummy, FALSE); /* Symbols */ /* * We use LLVMGetNamedGlobal () for symbol which are defined in LLVM code, and LLVMAddGlobal () * for symbols defined in the .s file emitted by the aot compiler. */ eltype = eltypes [tindex]; if (module->llvm_only) fields [tindex ++] = LLVMConstNull (eltype); else fields [tindex ++] = AddJitGlobal (module, eltype, "jit_got"); fields [tindex ++] = module->got_var; /* llc defines this directly */ if (!module->llvm_only) { fields [tindex ++] = LLVMAddGlobal (lmodule, eltype, module->eh_frame_symbol); fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMConstNull (eltype); } else { fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = module->get_method; fields [tindex ++] = module->get_unbox_tramp; } if (module->has_jitted_code) { fields [tindex ++] = AddJitGlobal (module, eltype, "jit_code_start"); fields [tindex ++] = AddJitGlobal (module, eltype, "jit_code_end"); } else { fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMConstNull (eltype); } if (!module->llvm_only) fields [tindex ++] = AddJitGlobal (module, eltype, "method_addresses"); else fields [tindex ++] = LLVMConstNull (eltype); if (info->flags & MONO_AOT_FILE_FLAG_SEPARATE_DATA) { for (i = 0; i < MONO_AOT_TABLE_NUM; ++i) fields [tindex ++] = LLVMConstNull (eltype); } else { fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "blob"); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "class_name_table"); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "class_info_offsets"); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "method_info_offsets"); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "ex_info_offsets"); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "extra_method_info_offsets"); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "extra_method_table"); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "got_info_offsets"); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "llvm_got_info_offsets"); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "image_table"); } /* Not needed (mem_end) */ fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "assembly_guid"); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "runtime_version"); if (info->trampoline_size [0]) { fields [tindex ++] = AddJitGlobal (module, eltype, "specific_trampolines"); fields [tindex ++] = AddJitGlobal (module, eltype, "static_rgctx_trampolines"); fields [tindex ++] = AddJitGlobal (module, eltype, "imt_trampolines"); fields [tindex ++] = AddJitGlobal (module, eltype, "gsharedvt_arg_trampolines"); } else { fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMConstNull (eltype); } if (module->static_link && !module->llvm_only) fields [tindex ++] = AddJitGlobal (module, eltype, "globals"); else fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "assembly_name"); if (!module->llvm_only) { fields [tindex ++] = AddJitGlobal (module, eltype, "plt"); fields [tindex ++] = AddJitGlobal (module, eltype, "plt_end"); fields [tindex ++] = AddJitGlobal (module, eltype, "unwind_info"); fields [tindex ++] = AddJitGlobal (module, eltype, "unbox_trampolines"); fields [tindex ++] = AddJitGlobal (module, eltype, "unbox_trampolines_end"); fields [tindex ++] = AddJitGlobal (module, eltype, "unbox_trampoline_addresses"); } else { fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMConstNull (eltype); fields [tindex ++] = LLVMConstNull (eltype); } for (i = 0; i < MONO_AOT_FILE_INFO_NUM_SYMBOLS; ++i) fields [2 + i] = LLVMConstBitCast (fields [2 + i], eltype); /* Scalars */ fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->plt_got_offset_base, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->got_size, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->plt_size, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->nmethods, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->flags, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->opts, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->simd_opts, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->gc_name_index, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->num_rgctx_fetch_trampolines, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->double_align, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->long_align, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->generic_tramp_num, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->tramp_page_size, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->nshared_got_entries, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->datafile_size, FALSE); /* Arrays */ fields [tindex ++] = llvm_array_from_uints (LLVMInt32Type (), info->table_offsets, MONO_AOT_TABLE_NUM); fields [tindex ++] = llvm_array_from_uints (LLVMInt32Type (), info->num_trampolines, MONO_AOT_TRAMP_NUM); fields [tindex ++] = llvm_array_from_uints (LLVMInt32Type (), info->trampoline_got_offset_base, MONO_AOT_TRAMP_NUM); fields [tindex ++] = llvm_array_from_uints (LLVMInt32Type (), info->trampoline_size, MONO_AOT_TRAMP_NUM); fields [tindex ++] = llvm_array_from_uints (LLVMInt32Type (), info->tramp_page_code_offsets, MONO_AOT_TRAMP_NUM); fields [tindex ++] = llvm_array_from_bytes (info->aotid, 16); g_assert (tindex == nfields); LLVMSetInitializer (info_var, LLVMConstNamedStruct (file_info_type, fields, nfields)); if (module->static_link) { char *s, *p; LLVMValueRef var; s = g_strdup_printf ("mono_aot_module_%s_info", module->assembly->aname.name); /* Get rid of characters which cannot occur in symbols */ p = s; for (p = s; *p; ++p) { if (!(isalnum (*p) || *p == '_')) *p = '_'; } var = LLVMAddGlobal (module->lmodule, LLVMPointerType (LLVMInt8Type (), 0), s); g_free (s); LLVMSetInitializer (var, LLVMConstBitCast (LLVMGetNamedGlobal (module->lmodule, "mono_aot_file_info"), LLVMPointerType (LLVMInt8Type (), 0))); LLVMSetLinkage (var, LLVMExternalLinkage); } } /* * Emit the aot module into the LLVM bitcode file FILENAME. */ void mono_llvm_emit_aot_module (const char *filename, const char *cu_name) { LLVMTypeRef got_type, inited_type; LLVMValueRef real_got, real_inited; MonoLLVMModule *module = &aot_module; emit_llvm_code_end (module); /* * Create the real got variable and replace all uses of the dummy variable with * the real one. */ got_type = LLVMArrayType (module->ptr_type, module->max_got_offset + 1); real_got = LLVMAddGlobal (module->lmodule, got_type, module->got_symbol); LLVMSetInitializer (real_got, LLVMConstNull (got_type)); if (module->external_symbols) { LLVMSetLinkage (real_got, LLVMExternalLinkage); LLVMSetVisibility (real_got, LLVMHiddenVisibility); } else { LLVMSetLinkage (real_got, LLVMInternalLinkage); } mono_llvm_replace_uses_of (module->got_var, real_got); mark_as_used (&aot_module, real_got); /* Delete the dummy got so it doesn't become a global */ LLVMDeleteGlobal (module->got_var); module->got_var = real_got; /* * Same for the init_var */ if (module->llvm_only) { inited_type = LLVMArrayType (LLVMInt8Type (), module->max_inited_idx + 1); real_inited = LLVMAddGlobal (module->lmodule, inited_type, "mono_inited"); LLVMSetInitializer (real_inited, LLVMConstNull (inited_type)); LLVMSetLinkage (real_inited, LLVMInternalLinkage); mono_llvm_replace_uses_of (module->inited_var, real_inited); LLVMDeleteGlobal (module->inited_var); } if (module->llvm_only) { emit_get_method (&aot_module); emit_get_unbox_tramp (&aot_module); } emit_llvm_used (&aot_module); emit_dbg_info (&aot_module, filename, cu_name); emit_aot_file_info (&aot_module); /* * Replace GOT entries for directly callable methods with the methods themselves. * It would be easier to implement this by predefining all methods before compiling * their bodies, but that couldn't handle the case when a method fails to compile * with llvm. */ if (module->llvm_only) { GHashTableIter iter; MonoMethod *method; GSList *callers, *l; g_hash_table_iter_init (&iter, module->method_to_callers); while (g_hash_table_iter_next (&iter, (void**)&method, (void**)&callers)) { LLVMValueRef lmethod; if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) continue; lmethod = (LLVMValueRef)g_hash_table_lookup (module->method_to_lmethod, method); if (lmethod) { for (l = callers; l; l = l->next) { LLVMValueRef caller = (LLVMValueRef)l->data; mono_llvm_replace_uses_of (caller, lmethod); } } } } /* Replace PLT entries for directly callable methods with the methods themselves */ { GHashTableIter iter; MonoJumpInfo *ji; LLVMValueRef callee; g_hash_table_iter_init (&iter, module->plt_entries_ji); while (g_hash_table_iter_next (&iter, (void**)&ji, (void**)&callee)) { if (mono_aot_is_direct_callable (ji)) { LLVMValueRef lmethod; lmethod = (LLVMValueRef)g_hash_table_lookup (module->method_to_lmethod, ji->data.method); /* The types might not match because the caller might pass an rgctx */ if (lmethod && LLVMTypeOf (callee) == LLVMTypeOf (lmethod)) { mono_llvm_replace_uses_of (callee, lmethod); mono_aot_mark_unused_llvm_plt_entry (ji); } } } } #if 1 { char *verifier_err; if (LLVMVerifyModule (module->lmodule, LLVMReturnStatusAction, &verifier_err)) { printf ("%s\n", verifier_err); g_assert_not_reached (); } } #endif LLVMWriteBitcodeToFile (module->lmodule, filename); } static LLVMValueRef md_string (const char *s) { return LLVMMDString (s, strlen (s)); } /* Debugging support */ static void emit_dbg_info (MonoLLVMModule *module, const char *filename, const char *cu_name) { LLVMModuleRef lmodule = module->lmodule; LLVMValueRef args [16], ver; /* * This can only be enabled when LLVM code is emitted into a separate object * file, since the AOT compiler also emits dwarf info, * and the abbrev indexes will not be correct since llvm has added its own * abbrevs. */ if (!module->emit_dwarf) return; #if LLVM_API_VERSION > 100 mono_llvm_di_builder_finalize (module->di_builder); #else LLVMValueRef cu_args [16], cu; int n_cuargs; char *build_info, *s, *dir; /* * Emit dwarf info in the form of LLVM metadata. There is some * out-of-date documentation at: * http://llvm.org/docs/SourceLevelDebugging.html * but most of this was gathered from the llvm and * clang sources. */ n_cuargs = 0; cu_args [n_cuargs ++] = LLVMConstInt (LLVMInt32Type (), DW_TAG_compile_unit, FALSE); /* CU name/compilation dir */ dir = g_path_get_dirname (filename); args [0] = LLVMMDString (cu_name, strlen (cu_name)); args [1] = LLVMMDString (dir, strlen (dir)); cu_args [n_cuargs ++] = LLVMMDNode (args, 2); g_free (dir); /* Language */ cu_args [n_cuargs ++] = LLVMConstInt (LLVMInt32Type (), DW_LANG_C99, FALSE); /* Producer */ build_info = mono_get_runtime_build_info (); s = g_strdup_printf ("Mono AOT Compiler %s (LLVM)", build_info); cu_args [n_cuargs ++] = LLVMMDString (s, strlen (s)); g_free (build_info); /* Optimized */ cu_args [n_cuargs ++] = LLVMConstInt (LLVMInt32Type (), 1, FALSE); /* Flags */ cu_args [n_cuargs ++] = LLVMMDString ("", strlen ("")); /* Runtime version */ cu_args [n_cuargs ++] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); /* Enums */ cu_args [n_cuargs ++] = LLVMMDNode (args, 0); cu_args [n_cuargs ++] = LLVMMDNode (args, 0); /* Subprograms */ if (module->subprogram_mds) { LLVMValueRef *mds; int i; mds = g_new0 (LLVMValueRef, module->subprogram_mds->len); for (i = 0; i < module->subprogram_mds->len; ++i) mds [i] = (LLVMValueRef)g_ptr_array_index (module->subprogram_mds, i); cu_args [n_cuargs ++] = LLVMMDNode (mds, module->subprogram_mds->len); } else { cu_args [n_cuargs ++] = LLVMMDNode (args, 0); } /* GVs */ cu_args [n_cuargs ++] = LLVMMDNode (args, 0); /* Imported modules */ cu_args [n_cuargs ++] = LLVMMDNode (args, 0); /* SplitName */ cu_args [n_cuargs ++] = LLVMMDString ("", strlen ("")); /* DebugEmissionKind = FullDebug */ cu_args [n_cuargs ++] = LLVMConstInt (LLVMInt32Type (), 1, FALSE); cu = LLVMMDNode (cu_args, n_cuargs); LLVMAddNamedMetadataOperand (lmodule, "llvm.dbg.cu", cu); #endif #if LLVM_API_VERSION > 100 args [0] = LLVMConstInt (LLVMInt32Type (), 2, FALSE); args [1] = LLVMMDString ("Dwarf Version", strlen ("Dwarf Version")); args [2] = LLVMConstInt (LLVMInt32Type (), 2, FALSE); ver = LLVMMDNode (args, 3); LLVMAddNamedMetadataOperand (lmodule, "llvm.module.flags", ver); args [0] = LLVMConstInt (LLVMInt32Type (), 2, FALSE); args [1] = LLVMMDString ("Debug Info Version", strlen ("Debug Info Version")); args [2] = LLVMConstInt (LLVMInt64Type (), 3, FALSE); ver = LLVMMDNode (args, 3); LLVMAddNamedMetadataOperand (lmodule, "llvm.module.flags", ver); #else args [0] = LLVMConstInt (LLVMInt32Type (), 1, FALSE); args [1] = LLVMMDString ("Dwarf Version", strlen ("Dwarf Version")); args [2] = LLVMConstInt (LLVMInt32Type (), 2, FALSE); ver = LLVMMDNode (args, 3); LLVMAddNamedMetadataOperand (lmodule, "llvm.module.flags", ver); args [0] = LLVMConstInt (LLVMInt32Type (), 1, FALSE); args [1] = LLVMMDString ("Debug Info Version", strlen ("Debug Info Version")); args [2] = LLVMConstInt (LLVMInt32Type (), 1, FALSE); ver = LLVMMDNode (args, 3); LLVMAddNamedMetadataOperand (lmodule, "llvm.module.flags", ver); #endif } static LLVMValueRef emit_dbg_subprogram (EmitContext *ctx, MonoCompile *cfg, LLVMValueRef method, const char *name) { MonoLLVMModule *module = ctx->module; MonoDebugMethodInfo *minfo = ctx->minfo; char *source_file, *dir, *filename; LLVMValueRef md, args [16], ctx_args [16], md_args [64], type_args [16], ctx_md, type_md; MonoSymSeqPoint *sym_seq_points; int n_seq_points; if (!minfo) return NULL; mono_debug_get_seq_points (minfo, &source_file, NULL, NULL, &sym_seq_points, &n_seq_points); if (!source_file) source_file = g_strdup (""); dir = g_path_get_dirname (source_file); filename = g_path_get_basename (source_file); #if LLVM_API_VERSION > 100 return mono_llvm_di_create_function (module->di_builder, module->cu, method, cfg->method->name, name, dir, filename, n_seq_points ? sym_seq_points [0].line : 1); #endif ctx_args [0] = LLVMConstInt (LLVMInt32Type (), 0x29, FALSE); args [0] = md_string (filename); args [1] = md_string (dir); ctx_args [1] = LLVMMDNode (args, 2); ctx_md = LLVMMDNode (ctx_args, 2); type_args [0] = LLVMConstInt (LLVMInt32Type (), DW_TAG_subroutine_type, FALSE); type_args [1] = NULL; type_args [2] = NULL; type_args [3] = LLVMMDString ("", 0); type_args [4] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); type_args [5] = LLVMConstInt (LLVMInt64Type (), 0, FALSE); type_args [6] = LLVMConstInt (LLVMInt64Type (), 0, FALSE); type_args [7] = LLVMConstInt (LLVMInt64Type (), 0, FALSE); type_args [8] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); type_args [9] = NULL; type_args [10] = NULL; type_args [11] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); type_args [12] = NULL; type_args [13] = NULL; type_args [14] = NULL; type_md = LLVMMDNode (type_args, 14); /* http://llvm.org/docs/SourceLevelDebugging.html#subprogram-descriptors */ md_args [0] = LLVMConstInt (LLVMInt32Type (), DW_TAG_subprogram, FALSE); /* Source directory + file pair */ args [0] = md_string (filename); args [1] = md_string (dir); md_args [1] = LLVMMDNode (args ,2); md_args [2] = ctx_md; md_args [3] = md_string (cfg->method->name); md_args [4] = md_string (name); md_args [5] = md_string (name); /* Line number */ if (n_seq_points) md_args [6] = LLVMConstInt (LLVMInt32Type (), sym_seq_points [0].line, FALSE); else md_args [6] = LLVMConstInt (LLVMInt32Type (), 1, FALSE); /* Type */ md_args [7] = type_md; /* static */ md_args [8] = LLVMConstInt (LLVMInt1Type (), 0, FALSE); /* not extern */ md_args [9] = LLVMConstInt (LLVMInt1Type (), 1, FALSE); /* Virtuality */ md_args [10] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); /* Index into a virtual function */ md_args [11] = NULL; md_args [12] = NULL; /* Flags */ md_args [13] = LLVMConstInt (LLVMInt1Type (), 0, FALSE); /* isOptimized */ md_args [14] = LLVMConstInt (LLVMInt1Type (), 1, FALSE); /* Pointer to LLVM function */ md_args [15] = method; /* Function template parameter */ md_args [16] = NULL; /* Function declaration descriptor */ md_args [17] = NULL; /* List of function variables */ md_args [18] = LLVMMDNode (args, 0); /* Line number */ md_args [19] = LLVMConstInt (LLVMInt32Type (), 1, FALSE); md = LLVMMDNode (md_args, 20); if (!module->subprogram_mds) module->subprogram_mds = g_ptr_array_new (); g_ptr_array_add (module->subprogram_mds, md); g_free (dir); g_free (filename); g_free (source_file); g_free (sym_seq_points); return md; } static void emit_dbg_loc (EmitContext *ctx, LLVMBuilderRef builder, const unsigned char *cil_code) { MonoCompile *cfg = ctx->cfg; if (ctx->minfo && cil_code && cil_code >= cfg->header->code && cil_code < cfg->header->code + cfg->header->code_size) { MonoDebugSourceLocation *loc; LLVMValueRef loc_md; loc = mono_debug_method_lookup_location (ctx->minfo, cil_code - cfg->header->code); if (loc) { #if LLVM_API_VERSION > 100 loc_md = mono_llvm_di_create_location (ctx->module->di_builder, ctx->dbg_md, loc->row, loc->column); mono_llvm_di_set_location (builder, loc_md); #else LLVMValueRef md_args [16]; int nmd_args; nmd_args = 0; md_args [nmd_args ++] = LLVMConstInt (LLVMInt32Type (), loc->row, FALSE); md_args [nmd_args ++] = LLVMConstInt (LLVMInt32Type (), loc->column, FALSE); md_args [nmd_args ++] = ctx->dbg_md; md_args [nmd_args ++] = NULL; loc_md = LLVMMDNode (md_args, nmd_args); LLVMSetCurrentDebugLocation (builder, loc_md); #endif mono_debug_free_source_location (loc); } } } void default_mono_llvm_unhandled_exception (void) { MonoJitTlsData *jit_tls = mono_get_jit_tls (); MonoObject *target = mono_gchandle_get_target (jit_tls->thrown_exc); mono_unhandled_exception (target); mono_invoke_unhandled_exception_hook (target); g_assert_not_reached (); } /* DESIGN: - Emit LLVM IR from the mono IR using the LLVM C API. - The original arch specific code remains, so we can fall back to it if we run into something we can't handle. */ /* A partial list of issues: - Handling of opcodes which can throw exceptions. In the mono JIT, these are implemented using code like this: method: throw_pos: b ex_label ex_label: push throw_pos - method call The problematic part is push throw_pos - method, which cannot be represented in the LLVM IR, since it does not support label values. -> this can be implemented in AOT mode using inline asm + labels, but cannot be implemented in JIT mode ? -> a possible but slower implementation would use the normal exception throwing code but it would need to control the placement of the throw code (it needs to be exactly after the compare+branch). -> perhaps add a PC offset intrinsics ? - efficient implementation of .ovf opcodes. These are currently implemented as: b ex_label Some overflow opcodes are now supported by LLVM SVN. - exception handling, unwinding. - SSA is disabled for methods with exception handlers - How to obtain unwind info for LLVM compiled methods ? -> this is now solved by converting the unwind info generated by LLVM into our format. - LLVM uses the c++ exception handling framework, while we use our home grown code, and couldn't use the c++ one: - its not supported under VC++, other exotic platforms. - it might be impossible to support filter clauses with it. - trampolines. The trampolines need a predictable call sequence, since they need to disasm the calling code to obtain register numbers / offsets. LLVM currently generates this code in non-JIT mode: mov -0x98(%rax),%eax callq *%rax Here, the vtable pointer is lost. -> solution: use one vtable trampoline per class. - passing/receiving the IMT pointer/RGCTX. -> solution: pass them as normal arguments ? - argument passing. LLVM does not allow the specification of argument registers etc. This means that all calls are made according to the platform ABI. - passing/receiving vtypes. Vtypes passed/received in registers are handled by the front end by using a signature with scalar arguments, and loading the parts of the vtype into those arguments. Vtypes passed on the stack are handled using the 'byval' attribute. - ldaddr. Supported though alloca, we need to emit the load/store code. - types. The mono JIT uses pointer sized iregs/double fregs, while LLVM uses precisely typed registers, so we have to keep track of the precise LLVM type of each vreg. This is made easier because the IR is already in SSA form. An additional problem is that our IR is not consistent with types, i.e. i32/i64 types are frequently used incorrectly. */ /* AOT SUPPORT: Emit LLVM bytecode into a .bc file, compile it using llc into a .s file, then link it with the file containing the methods emitted by the JIT and the AOT data structures. */ /* FIXME: Normalize some aspects of the mono IR to allow easier translation, like: * - each bblock should end with a branch * - setting the return value, making cfg->ret non-volatile * - avoid some transformations in the JIT which make it harder for us to generate * code. * - use pointer types to help optimizations. */ #else /* DISABLE_JIT */ void mono_llvm_cleanup (void) { } void mono_llvm_free_domain_info (MonoDomain *domain) { } void mono_llvm_init (void) { } void default_mono_llvm_unhandled_exception (void) { } #endif /* DISABLE_JIT */