/*
* mini-llvm.c: llvm "Backend" for the mono JIT
*
- * (C) 2009 Novell, Inc.
+ * Copyright 2009-2011 Novell Inc (http://www.novell.com)
+ * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
*/
#include "mini.h"
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mempool-internals.h>
+#include <mono/utils/mono-tls.h>
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
*/
typedef struct {
LLVMModuleRef module;
- LLVMValueRef throw, throw_corlib_exception;
+ LLVMValueRef throw, rethrow, throw_corlib_exception;
GHashTable *llvm_types;
LLVMValueRef got_var;
const char *got_symbol;
* needs to branch to in ENDFINALLY.
*/
GSList *call_handler_return_bbs;
- LLVMValueRef endfinally_switch;
+ /*
+ * 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;
GHashTable *region_to_handler;
LLVMBuilderRef alloca_builder;
LLVMValueRef last_alloca;
+ LLVMValueRef rgctx_arg;
+ LLVMTypeRef *vreg_types;
+ gboolean *is_dead;
+ gboolean *unreachable;
+ int *pindexes;
char temp_name [32];
} EmitContext;
#define TRACE_FAILURE(msg)
#endif
+#ifdef TARGET_X86
+#define IS_TARGET_X86 1
+#else
+#define IS_TARGET_X86 0
+#endif
+
#define LLVM_FAILURE(ctx, reason) do { \
TRACE_FAILURE (reason); \
(ctx)->cfg->exception_message = g_strdup (reason); \
};
static LLVMExecutionEngineRef ee;
-static guint32 current_cfg_tls_id;
+static MonoNativeTlsKey current_cfg_tls_id;
static MonoLLVMModule jit_module, aot_module;
static gboolean jit_module_inited;
}
}
+/* 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;
+ }
+}
+
/*
* type_to_llvm_type:
*
if (!ltype) {
int i, size;
LLVMTypeRef *eltypes;
+ char *name;
size = get_vtype_size (t);
for (i = 0; i < size; ++i)
eltypes [i] = LLVMInt8Type ();
- ltype = LLVMStructType (eltypes, size, FALSE);
+ name = mono_type_full_name (&klass->byval_arg);
+ ltype = LLVMStructCreateNamed (LLVMGetGlobalContext (), name);
+ LLVMStructSetBody (ltype, eltypes, size, FALSE);
g_hash_table_insert (ctx->lmodule->llvm_types, klass, ltype);
g_free (eltypes);
}
case OP_MINPD:
return "llvm.x86.sse2.min.pd";
case OP_MINPS:
- return "llvm.x86.sse2.min.ps";
+ return "llvm.x86.sse.min.ps";
case OP_PMIND_UN:
return "llvm.x86.sse41.pminud";
case OP_PMINW_UN:
return "llvm.x86.sse41.pminuw";
case OP_PMINB_UN:
return "llvm.x86.sse2.pminu.b";
+ case OP_PMINW:
+ return "llvm.x86.sse2.pmins.w";
case OP_MAXPD:
return "llvm.x86.sse2.max.pd";
case OP_MAXPS:
- return "llvm.x86.sse2.max.ps";
+ 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_PMAXD_UN:
return "llvm.x86.sse41.pmaxud";
case OP_PMAXW_UN:
return "llvm.x86.sse41.pmaxuw";
case OP_PMAXB_UN:
return "llvm.x86.sse2.pmaxu.b";
+ 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_PCMPEQB:
+ return "llvm.x86.sse2.pcmpeq.b";
+ case OP_PCMPEQW:
+ return "llvm.x86.sse2.pcmpeq.w";
+ case OP_PCMPEQD:
+ return "llvm.x86.sse2.pcmpeq.d";
+ case OP_PCMPEQQ:
+ return "llvm.x86.sse41.pcmpeqq";
+ case OP_PCMPGTB:
+ return "llvm.x86.sse2.pcmpgt.b";
+ 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_COMPPS:
+ return "llvm.x86.sse.cmp.ps";
+ case OP_COMPPD:
+ return "llvm.x86.sse2.cmp.pd";
+ 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";
#endif
default:
g_assert_not_reached ();
}
}
+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 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, "");
return LLVMBuildBitCast (ctx->builder, LLVMBuildZExt (ctx->builder, v, LLVMInt64Type (), ""), dtype, "");
#endif
+ if (LLVMGetTypeKind (stype) == LLVMVectorTypeKind && LLVMGetTypeKind (dtype) == LLVMVectorTypeKind)
+ return LLVMBuildBitCast (ctx->builder, v, dtype, "");
+
LLVMDumpValue (v);
LLVMDumpValue (LLVMConstNull (dtype));
g_assert_not_reached ();
* Might have to zero extend since llvm doesn't have
* unsigned types.
*/
- if (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_U2)
+ 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_U8)
v = LLVMBuildZExt (ctx->builder, v, LLVMInt64Type (), "");
}
}
+typedef struct {
+ /*
+ * Maps parameter indexes in the original signature to parameter indexes
+ * in the LLVM signature.
+ */
+ int *pindexes;
+ /* The indexes of various special arguments in the LLVM signature */
+ int vret_arg_pindex, this_arg_pindex, rgctx_arg_pindex, imt_arg_pindex;
+} LLVMSigInfo;
+
/*
- * sig_to_llvm_sig:
+ * sig_to_llvm_sig_full:
*
* Return the LLVM signature corresponding to the mono signature SIG using the
- * calling convention information in CINFO.
+ * calling convention information in CINFO. Return parameter mapping information in SINFO.
*/
static LLVMTypeRef
-sig_to_llvm_sig (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *cinfo)
+sig_to_llvm_sig_full (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *cinfo,
+ LLVMSigInfo *sinfo)
{
LLVMTypeRef ret_type;
LLVMTypeRef *param_types = NULL;
LLVMTypeRef res;
- int i, j, pindex;
+ int i, j, pindex, vret_arg_pindex = 0;
+ int *pindexes;
gboolean vretaddr = FALSE;
+ if (sinfo)
+ memset (sinfo, 0, sizeof (LLVMSigInfo));
+
ret_type = type_to_llvm_type (ctx, sig->ret);
CHECK_FAILURE (ctx);
} else if (cinfo && MONO_TYPE_ISSTRUCT (sig->ret)) {
g_assert (cinfo->ret.storage == LLVMArgVtypeRetAddr);
vretaddr = TRUE;
+ ret_type = LLVMVoidType ();
}
- param_types = g_new0 (LLVMTypeRef, (sig->param_count * 2) + 2);
+ pindexes = g_new0 (int, sig->param_count);
+ param_types = g_new0 (LLVMTypeRef, (sig->param_count * 2) + 3);
pindex = 0;
+ if (cinfo && cinfo->rgctx_arg) {
+ if (sinfo)
+ sinfo->rgctx_arg_pindex = pindex;
+ param_types [pindex] = IntPtrType ();
+ pindex ++;
+ }
+ if (cinfo && cinfo->imt_arg && IS_LLVM_MONO_BRANCH) {
+ if (sinfo)
+ sinfo->imt_arg_pindex = pindex;
+ param_types [pindex] = IntPtrType ();
+ pindex ++;
+ }
if (vretaddr) {
- ret_type = LLVMVoidType ();
+ /* 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 ++;
+ }
+ }
+
+ if (sinfo)
+ sinfo->vret_arg_pindex = vret_arg_pindex;
+ }
+
+ if (vretaddr && vret_arg_pindex == pindex)
+ param_types [pindex ++] = IntPtrType ();
+ if (sig->hasthis) {
+ if (sinfo)
+ sinfo->this_arg_pindex = pindex;
param_types [pindex ++] = IntPtrType ();
}
- if (sig->hasthis)
+ if (vretaddr && vret_arg_pindex == pindex)
param_types [pindex ++] = IntPtrType ();
for (i = 0; i < sig->param_count; ++i) {
+ if (vretaddr && vret_arg_pindex == pindex)
+ param_types [pindex ++] = IntPtrType ();
+ pindexes [i] = pindex;
if (cinfo && cinfo->args [i + sig->hasthis].storage == LLVMArgVtypeInReg) {
for (j = 0; j < 2; ++j) {
switch (cinfo->args [i + sig->hasthis].pair_storage [j]) {
param_types [pindex ++] = type_to_llvm_arg_type (ctx, sig->params [i]);
}
}
+ if (vretaddr && vret_arg_pindex == pindex)
+ param_types [pindex ++] = IntPtrType ();
+
CHECK_FAILURE (ctx);
res = LLVMFunctionType (ret_type, param_types, pindex, FALSE);
g_free (param_types);
+ if (sinfo) {
+ sinfo->pindexes = pindexes;
+ } else {
+ g_free (pindexes);
+ }
+
return res;
FAILURE:
return NULL;
}
+static LLVMTypeRef
+sig_to_llvm_sig (EmitContext *ctx, MonoMethodSignature *sig)
+{
+ return sig_to_llvm_sig_full (ctx, sig, NULL, NULL);
+}
+
/*
* LLVMFunctionType1:
*
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 = g_hash_table_lookup (ctx->lmodule->plt_entries, callee_name);
if (!callee) {
return callee;
}
+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 void
-emit_cond_throw_pos (EmitContext *ctx)
+set_metadata_flag (LLVMValueRef v, const char *flag_name)
{
+ LLVMValueRef md_arg;
+ int md_kind;
+
+ if (!IS_LLVM_MONO_BRANCH)
+ return;
+
+ md_kind = LLVMGetMDKindID (flag_name, strlen (flag_name));
+ md_arg = LLVMMDString ("mono", 4);
+ LLVMSetMetadata (v, md_kind, LLVMMDNode (&md_arg, 1));
}
/*
MonoCompile *cfg = ctx->cfg;
LLVMValueRef lcall;
LLVMBuilderRef builder = *builder_ref;
+ int clause_index;
- // FIXME: Nested clauses
- if (bb->region != -1 && MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY)) {
+ clause_index = get_handler_clause (cfg, bb);
+
+ if (clause_index != -1) {
MonoMethodHeader *header = cfg->header;
- // FIXME: Add a macro for this
- int clause_index = (bb->region >> 8) - 1;
MonoExceptionClause *ec = &header->clauses [clause_index];
MonoBasicBlock *tblock;
LLVMBasicBlockRef ex_bb, noex_bb;
return lcall;
}
+static LLVMValueRef
+emit_load (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef addr, const char *name, gboolean is_faulting)
+{
+ const char *intrins_name;
+ LLVMValueRef args [16], res;
+ LLVMTypeRef addr_type;
+
+ if (is_faulting && bb->region != -1 && IS_LLVM_MONO_BRANCH) {
+ /*
+ * 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);
+ res = emit_call (ctx, bb, builder_ref, LLVMGetNamedFunction (ctx->module, intrins_name), args, 3);
+
+ 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.
+ */
+ 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 void
+emit_store (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, gboolean is_faulting)
+{
+ const char *intrins_name;
+ LLVMValueRef args [16];
+
+ if (is_faulting && bb->region != -1 && IS_LLVM_MONO_BRANCH) {
+ 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);
+ emit_call (ctx, bb, builder_ref, LLVMGetNamedFunction (ctx->module, intrins_name), args, 4);
+ } else {
+ LLVMBuildStore (*builder_ref, value, addr);
+ }
+}
+
/*
* 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)
LLVMBuilderRef builder;
MonoClass *exc_class;
LLVMValueRef args [2];
-
+
ex_bb = gen_bb (ctx, "EX_BB");
noex_bb = gen_bb (ctx, "NOEX_BB");
LLVMBuildCondBr (ctx->builder, cmp, ex_bb, noex_bb);
- exc_class = mono_class_from_name (mono_defaults.corlib, "System", exc_type);
+ exc_class = mono_class_from_name (mono_get_corlib (), "System", exc_type);
g_assert (exc_class);
/* Emit exception throwing code */
if (!ctx->lmodule->throw_corlib_exception) {
LLVMValueRef callee;
LLVMTypeRef sig;
-
- MonoMethodSignature *throw_sig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
- throw_sig->ret = &mono_defaults.void_class->byval_arg;
- throw_sig->params [0] = &mono_defaults.int32_class->byval_arg;
- throw_sig->params [1] = &mono_defaults.int32_class->byval_arg;
- sig = sig_to_llvm_sig (ctx, throw_sig, NULL);
+ const char *icall_name;
+
+ MonoMethodSignature *throw_sig = mono_metadata_signature_alloc (mono_get_corlib (), 2);
+ throw_sig->ret = &mono_get_void_class ()->byval_arg;
+ throw_sig->params [0] = &mono_get_int32_class ()->byval_arg;
+ if (IS_LLVM_MONO_BRANCH) {
+ icall_name = "llvm_throw_corlib_exception_abs_trampoline";
+ throw_sig->params [1] = &mono_get_intptr_class ()->byval_arg;
+ } else {
+ icall_name = "llvm_throw_corlib_exception_trampoline";
+ throw_sig->params [1] = &mono_get_int32_class ()->byval_arg;
+ }
+ sig = sig_to_llvm_sig (ctx, throw_sig);
if (ctx->cfg->compile_aot) {
- callee = get_plt_entry (ctx, sig, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_corlib_exception");
+ callee = get_plt_entry (ctx, sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name);
} else {
- callee = LLVMAddFunction (ctx->module, "throw_corlib_exception", sig_to_llvm_sig (ctx, throw_sig, NULL));
+ callee = LLVMAddFunction (ctx->module, "llvm_throw_corlib_exception_trampoline", sig_to_llvm_sig (ctx, throw_sig));
-#ifdef TARGET_X86
- /*
- * LLVM generated code doesn't push the arguments, so we need another
- * throw trampoline.
+ /*
+ * Differences between the LLVM/non-LLVM throw corlib exception trampoline:
+ * - On x86, LLVM generated code doesn't push the arguments
+ * - When using the LLVM mono branch, the trampoline takes the throw address as an
+ * arguments, not a pc offset.
*/
- LLVMAddGlobalMapping (ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_llvm_throw_corlib_exception"));
-#else
- LLVMAddGlobalMapping (ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_corlib_exception"));
-#endif
+ LLVMAddGlobalMapping (ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name));
}
mono_memory_barrier ();
ctx->lmodule->throw_corlib_exception = callee;
}
-#ifdef TARGET_X86
- args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token - MONO_TOKEN_TYPE_DEF, FALSE);
-#else
- args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token, FALSE);
-#endif
- /*
- * FIXME: The offset is 0, this is not a problem for exception handling
- * in general, because we don't llvm compile methods with handlers, its only
- * a problem for line numbers in stack traces.
- */
- args [1] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
- emit_call (ctx, bb, &builder, ctx->lmodule->throw_corlib_exception, args, 2);
+ if (IS_TARGET_X86)
+ args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token - MONO_TOKEN_TYPE_DEF, FALSE);
+ else
+ args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token, FALSE);
+
+ if (IS_LLVM_MONO_BRANCH) {
+ /*
+ * The LLVM mono branch contains changes so a block address can be passed as an
+ * argument to a call.
+ */
+ args [1] = LLVMBuildPtrToInt (builder, LLVMBlockAddress (ctx->lmethod, ex_bb), IntPtrType (), "");
+ emit_call (ctx, bb, &builder, ctx->lmodule->throw_corlib_exception, args, 2);
+ } else {
+ /*
+ * FIXME: The offset is 0, this is only a problem if the code is inside a clause,
+ * otherwise only the line numbers in stack traces are incorrect.
+ */
+ if (bb->region != -1 && !IS_LLVM_MONO_BRANCH)
+ LLVM_FAILURE (ctx, "system-ex-in-region");
+
+ args [1] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
+ emit_call (ctx, bb, &builder, ctx->lmodule->throw_corlib_exception, args, 2);
+ }
LLVMBuildUnreachable (builder);
ctx->bblocks [bb->block_num].end_bblock = noex_bb;
ctx->ex_index ++;
+ return;
+
+ FAILURE:
+ return;
}
/*
* Emit code to load/convert arguments.
*/
static void
-emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder, int *pindexes)
+emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
{
int i, pindex;
MonoCompile *cfg = ctx->cfg;
* Emit code to save the argument from the registers to
* the real argument.
*/
- pindex = pindexes [i];
+ pindex = ctx->pindexes [i];
regs [0] = LLVMGetParam (ctx->lmethod, pindex);
if (ainfo->pair_storage [1] != LLVMArgNone)
regs [1] = LLVMGetParam (ctx->lmethod, pindex + 1);
ctx->values [reg] = LLVMBuildLoad (builder, ctx->addresses [reg], "");
}
} else if (ainfo->storage == LLVMArgVtypeByVal) {
- ctx->addresses [reg] = LLVMGetParam (ctx->lmethod, pindexes [i]);
+ ctx->addresses [reg] = LLVMGetParam (ctx->lmethod, ctx->pindexes [i]);
if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (sig->params [i]))) {
/* Treat these as normal values */
if (!MONO_TYPE_ISSTRUCT (sig->params [i]))
emit_volatile_store (ctx, cfg->args [i + sig->hasthis]->dreg);
+ if (sig->hasthis && !cfg->rgctx_var && cfg->generic_sharing_context) {
+ 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, IntPtrType (), 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);
+
+ 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, ctx->rgctx_arg, rgctx_alloc, TRUE);
+
+ set_metadata_flag (rgctx_alloc, "mono.this");
+ }
+
/*
* For finally clauses, create an indicator variable telling OP_ENDFINALLY whenever
* it needs to continue normally, or return back to the exception handling system.
if (bb->region != -1 && (bb->flags & BB_EXCEPTION_HANDLER))
g_hash_table_insert (ctx->region_to_handler, GUINT_TO_POINTER (mono_get_block_region_notry (cfg, bb->region)), bb);
if (bb->region != -1 && (bb->flags & BB_EXCEPTION_HANDLER) && bb->in_scount == 0) {
- LLVMValueRef val = LLVMBuildAlloca (builder, LLVMInt32Type (), "");
+ char name [128];
+ 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;
+
+ /*
+ * Create a new bblock which CALL_HANDLER can branch to, because branching to the
+ * LLVM bblock containing the call to llvm.eh.selector 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);
}
}
g_assert_not_reached ();
}
-/*
- * 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)
+static void
+process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, MonoInst *ins)
{
- EmitContext *ctx;
- MonoMethodSignature *sig;
- MonoBasicBlock *bb;
- LLVMTypeRef method_type;
- LLVMValueRef method = NULL, debug_alias = NULL;
- char *method_name, *debug_name = NULL;
- LLVMValueRef *values, *addresses;
- LLVMTypeRef *vreg_types;
- MonoType **vreg_cli_types;
- int i, max_block_num, pindex, bb_index;
- int *pindexes = NULL;
- gboolean last = FALSE;
- GPtrArray *phi_values;
- LLVMCallInfo *linfo;
+ MonoCompile *cfg = ctx->cfg;
+ LLVMModuleRef module = ctx->module;
+ LLVMValueRef *values = ctx->values;
+ LLVMValueRef *addresses = ctx->addresses;
+ MonoCallInst *call = (MonoCallInst*)ins;
+ MonoMethodSignature *sig = call->signature;
+ LLVMValueRef callee, lcall;
+ LLVMValueRef *args;
+ LLVMCallInfo *cinfo;
GSList *l;
- LLVMModuleRef module;
- gboolean *is_dead;
- gboolean *unreachable;
- BBInfo *bblocks;
- GPtrArray *bblock_list;
- MonoMethodHeader *header;
- MonoExceptionClause *clause;
+ int i, len;
+ gboolean vretaddr;
+ LLVMTypeRef llvm_sig;
+ gpointer target;
+ gboolean virtual, calli;
+ LLVMBuilderRef builder = *builder_ref;
+ LLVMSigInfo sinfo;
- /* The code below might acquire the loader lock, so use it for global locking */
- mono_loader_lock ();
+ if (call->signature->call_convention != MONO_CALL_DEFAULT)
+ LLVM_FAILURE (ctx, "non-default callconv");
- /* Used to communicate with the callbacks */
- TlsSetValue (current_cfg_tls_id, cfg);
+ if (call->rgctx_arg_reg && !IS_LLVM_MONO_BRANCH)
+ LLVM_FAILURE (ctx, "rgctx reg in call");
- ctx = g_new0 (EmitContext, 1);
- ctx->cfg = cfg;
- ctx->mempool = cfg->mempool;
+ if (call->rgctx_reg && !IS_LLVM_MONO_BRANCH) {
+ /*
+ * It might be possible to support this by creating a static rgctx trampoline, but
+ * common_call_trampoline () would patch callsites to call the trampoline, which
+ * would be incorrect if the rgctx arg is computed dynamically.
+ */
+ LLVM_FAILURE (ctx, "rgctx reg");
+ }
- /*
- * This maps vregs to the LLVM instruction defining them
- */
- values = g_new0 (LLVMValueRef, cfg->next_vreg);
- /*
- * This maps vregs for volatile variables to the LLVM instruction defining their
- * address.
- */
- addresses = g_new0 (LLVMValueRef, cfg->next_vreg);
- vreg_types = g_new0 (LLVMTypeRef, cfg->next_vreg);
- vreg_cli_types = g_new0 (MonoType*, cfg->next_vreg);
- phi_values = g_ptr_array_new ();
- /*
- * 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).
- */
- is_dead = g_new0 (gboolean, cfg->next_vreg);
- /* Whenever the bblock is unreachable */
- unreachable = g_new0 (gboolean, cfg->max_block_num);
+ cinfo = call->cinfo;
+ if (call->rgctx_arg_reg)
+ cinfo->rgctx_arg = TRUE;
+ if (call->imt_arg_reg)
+ cinfo->imt_arg = TRUE;
- bblock_list = g_ptr_array_new ();
+ vretaddr = cinfo && cinfo->ret.storage == LLVMArgVtypeRetAddr;
- ctx->values = values;
- ctx->addresses = addresses;
- ctx->vreg_cli_types = vreg_cli_types;
- ctx->region_to_handler = g_hash_table_new (NULL, NULL);
-
- if (cfg->compile_aot) {
- ctx->lmodule = &aot_module;
- method_name = mono_aot_get_method_name (cfg);
- debug_name = mono_aot_get_method_debug_name (cfg);
- } else {
- init_jit_module ();
- ctx->lmodule = &jit_module;
- method_name = mono_method_full_name (cfg->method, TRUE);
- debug_name = NULL;
- }
-
- module = ctx->module = ctx->lmodule->module;
+ llvm_sig = sig_to_llvm_sig_full (ctx, sig, cinfo, &sinfo);
+ CHECK_FAILURE (ctx);
-#if 1
- {
- static int count = 0;
- count ++;
+ 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);
+ calli = (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);
- if (getenv ("LLVM_COUNT")) {
- if (count == atoi (getenv ("LLVM_COUNT"))) {
- printf ("LAST: %s\n", mono_method_full_name (cfg->method, TRUE));
- last = TRUE;
+ /* FIXME: Avoid creating duplicate methods */
+
+ if (ins->flags & MONO_INST_HAS_METHOD) {
+ if (virtual) {
+ callee = NULL;
+ } else {
+ if (cfg->compile_aot) {
+ callee = get_plt_entry (ctx, llvm_sig, MONO_PATCH_INFO_METHOD, call->method);
+ if (!callee)
+ LLVM_FAILURE (ctx, "can't encode patch");
+ } else {
+ callee = LLVMAddFunction (module, "", llvm_sig);
+
+ target =
+ mono_create_jit_trampoline_in_domain (mono_domain_get (),
+ call->method);
+ LLVMAddGlobalMapping (ee, callee, target);
}
- if (count > atoi (getenv ("LLVM_COUNT")))
- LLVM_FAILURE (ctx, "");
}
- }
-#endif
-
- sig = mono_method_signature (cfg->method);
- ctx->sig = sig;
+ } else if (calli) {
+ } else {
+ MonoJitICallInfo *info = mono_find_jit_icall_by_addr (call->fptr);
- linfo = mono_arch_get_llvm_call_info (cfg, sig);
- ctx->linfo = linfo;
- CHECK_FAILURE (ctx);
+ if (info) {
+ /*
+ MonoJumpInfo ji;
- method_type = sig_to_llvm_sig (ctx, sig, linfo);
- CHECK_FAILURE (ctx);
+ memset (&ji, 0, sizeof (ji));
+ ji.type = MONO_PATCH_INFO_JIT_ICALL_ADDR;
+ ji.data.target = info->name;
- method = LLVMAddFunction (module, method_name, method_type);
- ctx->lmethod = method;
+ target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, &ji, FALSE);
+ */
+ if (cfg->compile_aot) {
+ callee = get_plt_entry (ctx, llvm_sig, MONO_PATCH_INFO_INTERNAL_METHOD, (char*)info->name);
+ if (!callee)
+ LLVM_FAILURE (ctx, "can't encode patch");
+ } else {
+ callee = LLVMAddFunction (module, "", llvm_sig);
+ target = (gpointer)mono_icall_get_wrapper (info);
+ LLVMAddGlobalMapping (ee, callee, target);
+ }
+ } else {
+ if (cfg->compile_aot) {
+ callee = NULL;
+ if (cfg->abs_patches) {
+ MonoJumpInfo *abs_ji = g_hash_table_lookup (cfg->abs_patches, call->fptr);
+ if (abs_ji) {
+ callee = get_plt_entry (ctx, llvm_sig, abs_ji->type, abs_ji->data.target);
+ if (!callee)
+ LLVM_FAILURE (ctx, "can't encode patch");
+ }
+ }
+ if (!callee)
+ LLVM_FAILURE (ctx, "aot");
+ } else {
+ callee = LLVMAddFunction (module, "", llvm_sig);
+ target = NULL;
+ if (cfg->abs_patches) {
+ MonoJumpInfo *abs_ji = g_hash_table_lookup (cfg->abs_patches, call->fptr);
+ if (abs_ji) {
+ /*
+ * FIXME: Some trampolines might have
+ * their own calling convention on some platforms.
+ */
+#ifndef TARGET_AMD64
+ if (abs_ji->type == MONO_PATCH_INFO_MONITOR_ENTER || abs_ji->type == MONO_PATCH_INFO_MONITOR_EXIT || abs_ji->type == MONO_PATCH_INFO_GENERIC_CLASS_INIT)
+ LLVM_FAILURE (ctx, "trampoline with own cconv");
+#endif
+ target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, abs_ji, FALSE);
+ LLVMAddGlobalMapping (ee, callee, target);
+ }
+ }
+ if (!target)
+ LLVMAddGlobalMapping (ee, callee, (gpointer)call->fptr);
+ }
+ }
+ }
- LLVMSetLinkage (method, LLVMPrivateLinkage);
+ if (virtual) {
+ int size = sizeof (gpointer);
+ LLVMValueRef index;
- if (cfg->method->save_lmf)
- LLVM_FAILURE (ctx, "lmf");
+ g_assert (ins->inst_offset % size == 0);
+ index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
- if (sig->pinvoke)
- LLVM_FAILURE (ctx, "pinvoke signature");
+ /*
+ * When using the llvm mono branch, we can support IMT directly, otherwise
+ * we need to call a trampoline.
+ */
+ if (call->method && call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE && !IS_LLVM_MONO_BRANCH) {
+#ifdef MONO_ARCH_HAVE_LLVM_IMT_TRAMPOLINE
+ if (cfg->compile_aot) {
+ MonoJumpInfoImtTramp *imt_tramp = g_new0 (MonoJumpInfoImtTramp, 1);
+ imt_tramp->method = call->method;
+ imt_tramp->vt_offset = call->inst.inst_offset;
- 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_NONE)
- LLVM_FAILURE (ctx, "non-finally/catch clause.");
+ callee = get_plt_entry (ctx, llvm_sig, MONO_PATCH_INFO_LLVM_IMT_TRAMPOLINE, imt_tramp);
+ } else {
+ callee = LLVMAddFunction (module, "", llvm_sig);
+ target = mono_create_llvm_imt_trampoline (cfg->domain, call->method, call->inst.inst_offset);
+ LLVMAddGlobalMapping (ee, callee, target);
+ }
+#else
+ /* No support for passing the IMT argument */
+ LLVM_FAILURE (ctx, "imt");
+#endif
+ } else {
+ 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) {
+ }
}
/*
- * This maps parameter indexes in the original signature to the indexes in
- * the LLVM signature.
+ * Collect and convert arguments
*/
- pindexes = g_new0 (int, sig->param_count);
- pindex = 0;
- if (cfg->vret_addr) {
- values [cfg->vret_addr->dreg] = LLVMGetParam (method, pindex);
- pindex ++;
+ len = sizeof (LLVMValueRef) * ((sig->param_count * 2) + sig->hasthis + vretaddr + call->rgctx_reg);
+ args = alloca (len);
+ memset (args, 0, len);
+ l = call->out_ireg_args;
+
+ if (IS_LLVM_MONO_BRANCH) {
+ if (call->rgctx_arg_reg) {
+ g_assert (values [call->rgctx_arg_reg]);
+ args [sinfo.rgctx_arg_pindex] = values [call->rgctx_arg_reg];
+ }
+ if (call->imt_arg_reg) {
+ g_assert (values [call->imt_arg_reg]);
+ args [sinfo.imt_arg_pindex] = values [call->imt_arg_reg];
+ }
}
- if (sig->hasthis) {
- values [cfg->args [0]->dreg] = LLVMGetParam (method, pindex);
- pindex ++;
+
+ if (vretaddr) {
+ if (!addresses [call->inst.dreg])
+ addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
+ args [sinfo.vret_arg_pindex] = LLVMBuildPtrToInt (builder, addresses [call->inst.dreg], IntPtrType (), "");
}
- for (i = 0; i < sig->param_count; ++i) {
- values [cfg->args [i + sig->hasthis]->dreg] = LLVMGetParam (method, pindex);
- pindexes [i] = pindex;
- if (linfo->args [i + sig->hasthis].storage == LLVMArgVtypeInReg) {
- if (linfo->args [i + sig->hasthis].pair_storage [0] != LLVMArgNone)
- pindex ++;
- if (linfo->args [i + sig->hasthis].pair_storage [1] != LLVMArgNone)
- pindex ++;
- } else if (linfo->args [i + sig->hasthis].storage == LLVMArgVtypeByVal) {
- LLVMAddAttribute (LLVMGetParam (method, pindex), LLVMByValAttribute);
- pindex ++;
+
+ for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
+ guint32 regpair;
+ int reg, pindex;
+ LLVMArgInfo *ainfo = call->cinfo ? &call->cinfo->args [i] : NULL;
+
+ if (sig->hasthis) {
+ if (i == 0)
+ pindex = sinfo.this_arg_pindex;
+ else
+ pindex = sinfo.pindexes [i - 1];
} else {
- pindex ++;
+ pindex = sinfo.pindexes [i];
}
- }
- 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);
+ regpair = (guint32)(gssize)(l->data);
+ reg = regpair & 0xffffff;
+ args [pindex] = values [reg];
+ if (ainfo->storage == LLVMArgVtypeInReg) {
+ int j;
+ LLVMValueRef regs [2];
+ guint32 nregs;
- /* 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 = 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);
+ g_assert (ainfo);
+
+ g_assert (addresses [reg]);
+
+ emit_vtype_to_reg (ctx, builder, sig->params [i - sig->hasthis], addresses [reg], ainfo, regs, &nregs);
+ for (j = 0; j < nregs; ++j)
+ args [pindex ++] = regs [j];
+
+ // FIXME: alignment
+ // FIXME: Get rid of the VMOVE
+ } else if (ainfo->storage == LLVMArgVtypeByVal) {
+ g_assert (addresses [reg]);
+ args [pindex] = addresses [reg];
+ } else {
+ g_assert (args [pindex]);
+ if (i == 0 && sig->hasthis)
+ args [pindex] = convert (ctx, args [pindex], IntPtrType ());
+ else
+ args [pindex] = convert (ctx, args [pindex], type_to_llvm_arg_type (ctx, sig->params [i - sig->hasthis]));
}
+
+ l = l->next;
}
+ // FIXME: Align call sites
+
/*
- * Make a first pass over the code to precreate PHI nodes.
+ * Emit the call
*/
- 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 (type_to_llvm_type (ctx, &ins->klass->byval_arg));
+ lcall = emit_call (ctx, bb, &builder, callee, args, LLVMCountParamTypes (llvm_sig));
- CHECK_FAILURE (ctx);
+#ifdef LLVM_MONO_BRANCH
+ /*
+ * 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)
+ LLVMSetInstructionCallConv (lcall, LLVMMono1CallConv);
+
+ if (call->rgctx_arg_reg)
+ LLVMAddInstrAttribute (lcall, 1 + sinfo.rgctx_arg_pindex, LLVMInRegAttribute);
+ if (call->imt_arg_reg)
+ LLVMAddInstrAttribute (lcall, 1 + sinfo.imt_arg_pindex, LLVMInRegAttribute);
+#endif
- 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);
- }
+ /* Add byval attributes if needed */
+ for (i = 0; i < sig->param_count; ++i) {
+ LLVMArgInfo *ainfo = call->cinfo ? &call->cinfo->args [i + sig->hasthis] : NULL;
- /*
- * 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 (ainfo && ainfo->storage == LLVMArgVtypeByVal) {
+ LLVMAddInstrAttribute (lcall, 1 + sinfo.pindexes [i], LLVMByValAttribute);
+ }
+ }
- if (ins->opcode == OP_VPHI)
- addresses [ins->dreg] = values [ins->dreg];
+ /*
+ * Convert the result
+ */
+ if (cinfo && cinfo->ret.storage == LLVMArgVtypeInReg) {
+ LLVMValueRef regs [2];
- g_ptr_array_add (phi_values, values [ins->dreg]);
+ if (!addresses [ins->dreg])
+ addresses [ins->dreg] = build_alloca (ctx, sig->ret);
- /*
- * 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];
+ regs [0] = LLVMBuildExtractValue (builder, lcall, 0, "");
+ if (cinfo->ret.pair_storage [1] != LLVMArgNone)
+ regs [1] = LLVMBuildExtractValue (builder, lcall, 1, "");
- if (sreg1 != -1)
- vreg_types [sreg1] = phi_type;
- }
- break;
- }
- default:
- break;
- }
- }
- }
+ emit_reg_to_vtype (ctx, builder, sig->ret, addresses [ins->dreg], &cinfo->ret, regs);
+ } else if (sig->ret->type != MONO_TYPE_VOID && !vretaddr) {
+ /* If the method returns an unsigned value, need to zext it */
- /*
- * 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;
- }
+ values [ins->dreg] = convert_full (ctx, lcall, llvm_type_to_stack_type (type_to_llvm_type (ctx, sig->ret)), type_is_unsigned (ctx, sig->ret));
}
- for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
- if (!bblocks [bb->block_num].added)
- g_ptr_array_add (bblock_list, bb);
- }
+ *builder_ref = ctx->builder;
- /*
- * Second pass: generate code.
- */
- for (bb_index = 0; bb_index < bblock_list->len; ++bb_index) {
- MonoInst *ins;
- LLVMBasicBlockRef cbb;
- LLVMBuilderRef builder;
- gboolean has_terminator;
- LLVMValueRef v;
- LLVMValueRef lhs, rhs;
+ g_free (sinfo.pindexes);
+
+ return;
+ FAILURE:
+ return;
+}
- bb = g_ptr_array_index (bblock_list, bb_index);
+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;
+ int i;
+ LLVMCallInfo *linfo = ctx->linfo;
+ LLVMModuleRef module = ctx->module;
+ BBInfo *bblocks = ctx->bblocks;
+ MonoInst *ins;
+ LLVMBasicBlockRef cbb;
+ LLVMBuilderRef builder;
+ gboolean has_terminator;
+ LLVMValueRef v;
+ LLVMValueRef lhs, rhs;
- if (!(bb == cfg->bb_entry || bb->in_count > 0))
- continue;
+ cbb = get_bb (ctx, bb);
+ builder = create_builder (ctx);
+ ctx->builder = builder;
+ LLVMPositionBuilderAtEnd (builder, cbb);
- cbb = get_bb (ctx, bb);
- builder = create_builder (ctx);
- ctx->builder = builder;
- LLVMPositionBuilderAtEnd (builder, cbb);
+ if (bb == cfg->bb_entry)
+ emit_entry_bb (ctx, builder);
+ CHECK_FAILURE (ctx);
- if (bb == cfg->bb_entry)
- emit_entry_bb (ctx, builder, pindexes);
- CHECK_FAILURE (ctx);
+ if (bb->flags & BB_EXCEPTION_HANDLER) {
+ LLVMTypeRef i8ptr;
+ LLVMValueRef eh_selector, eh_exception, personality, args [4];
+ LLVMBasicBlockRef target_bb;
+ MonoInst *exvar;
+ static gint32 mapping_inited;
+ static int ti_generator;
+ char ti_name [128];
+ MonoClass **ti;
+ LLVMValueRef type_info;
+ int clause_index;
+
+ if (!bblocks [bb->block_num].invoke_target) {
+ /*
+ * LLVM asserts if llvm.eh.selector is called from a bblock which
+ * doesn't have an invoke pointing at it.
+ * Update: LLVM no longer asserts, but some tests in exceptions.exe now fail.
+ */
+ LLVM_FAILURE (ctx, "handler without invokes");
+ }
- if (bb->flags & BB_EXCEPTION_HANDLER) {
- LLVMTypeRef i8ptr;
- LLVMValueRef eh_selector, eh_exception, personality, args [4];
- MonoInst *exvar;
- static gint32 mapping_inited;
- static int ti_generator;
- char ti_name [128];
- MonoClass **ti;
- LLVMValueRef type_info;
- int clause_index;
-
- if (!bblocks [bb->block_num].invoke_target) {
- /*
- * LLVM asserts if llvm.eh.selector is called from a bblock which
- * doesn't have an invoke pointing at it.
- */
- LLVM_FAILURE (ctx, "handler without invokes");
- }
+ eh_selector = LLVMGetNamedFunction (module, eh_selector_name);
+
+ if (cfg->compile_aot) {
+ /* Use a dummy personality function */
+ personality = LLVMGetNamedFunction (module, "mono_aot_personality");
+ g_assert (personality);
+ } else {
+ personality = LLVMGetNamedFunction (module, "mono_personality");
+ if (InterlockedCompareExchange (&mapping_inited, 1, 0) == 0)
+ LLVMAddGlobalMapping (ee, personality, mono_personality);
+ }
- eh_selector = LLVMGetNamedFunction (module, eh_selector_name);
+ i8ptr = LLVMPointerType (LLVMInt8Type (), 0);
- if (cfg->compile_aot) {
- /* Use a dummy personality function */
- personality = LLVMGetNamedFunction (module, "mono_aot_personality");
- g_assert (personality);
- } else {
- personality = LLVMGetNamedFunction (module, "mono_personality");
- if (InterlockedCompareExchange (&mapping_inited, 1, 0) == 0)
- LLVMAddGlobalMapping (ee, personality, mono_personality);
- }
+ 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 ++;
- i8ptr = LLVMPointerType (LLVMInt8Type (), 0);
+ if (cfg->compile_aot) {
+ /* decode_eh_frame () in aot-runtime.c will decode this */
+ type_info = LLVMAddGlobal (module, LLVMInt32Type (), ti_name);
+ LLVMSetInitializer (type_info, LLVMConstInt (LLVMInt32Type (), clause_index, FALSE));
- clause_index = (mono_get_block_region_notry (cfg, bb->region) >> 8) - 1;
+ LLVMSetLinkage (type_info, LLVMPrivateLinkage);
+ LLVMSetVisibility (type_info, LLVMHiddenVisibility);
+ /*
+ * Enabling this causes llc to crash:
+ * http://llvm.org/bugs/show_bug.cgi?id=6102
+ */
+ //LLVM_FAILURE (ctx, "aot+clauses");
+ } else {
/*
- * Create the type info
+ * 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.
*/
- sprintf (ti_name, "type_info_%d", ti_generator);
- ti_generator ++;
+ ti = mono_mempool_alloc (cfg->mempool, sizeof (gint32));
+ *(gint32*)ti = clause_index;
- if (cfg->compile_aot) {
- /* decode_eh_frame () in aot-runtime.c will decode this */
- type_info = LLVMAddGlobal (module, LLVMInt32Type (), ti_name);
- LLVMSetInitializer (type_info, LLVMConstInt (LLVMInt32Type (), clause_index, FALSE));
+ type_info = LLVMAddGlobal (module, i8ptr, ti_name);
- LLVMSetLinkage (type_info, LLVMPrivateLinkage);
- LLVMSetVisibility (type_info, LLVMHiddenVisibility);
+ LLVMAddGlobalMapping (ee, type_info, ti);
+ }
- /*
- * Enabling this causes llc to crash:
- * http://llvm.org/bugs/show_bug.cgi?id=6102
- */
- LLVM_FAILURE (ctx, "aot+clauses");
- } else {
- /* exception_cb will decode this */
- ti = g_malloc (sizeof (gint32));
- *(gint32*)ti = clause_index;
+ args [0] = LLVMConstNull (i8ptr);
+ args [1] = LLVMConstBitCast (personality, i8ptr);
+ args [2] = type_info;
+ LLVMBuildCall (builder, eh_selector, args, 3, "");
- type_info = LLVMAddGlobal (module, i8ptr, ti_name);
+ /* Store the exception into the exvar */
+ if (bb->in_scount == 1) {
+ g_assert (bb->in_scount == 1);
+ exvar = bb->in_stack [0];
- LLVMAddGlobalMapping (ee, type_info, ti);
- }
+ eh_exception = LLVMGetNamedFunction (module, "llvm.eh.exception");
- args [0] = LLVMConstNull (i8ptr);
- args [1] = LLVMConstBitCast (personality, i8ptr);
- args [2] = type_info;
- LLVMBuildCall (builder, eh_selector, args, 3, "");
+ // FIXME: This is shared with filter clauses ?
+ g_assert (!values [exvar->dreg]);
+ values [exvar->dreg] = LLVMBuildCall (builder, eh_exception, NULL, 0, "");
+ emit_volatile_store (ctx, exvar->dreg);
+ }
- /* Store the exception into the exvar */
- if (bb->in_scount == 1) {
- g_assert (bb->in_scount == 1);
- exvar = bb->in_stack [0];
+ /* Start a new bblock which CALL_HANDLER can branch to */
+ target_bb = bblocks [bb->block_num].call_handler_target_bb;
+ if (target_bb) {
+ LLVMBuildBr (builder, target_bb);
- eh_exception = LLVMGetNamedFunction (module, "llvm.eh.exception");
+ ctx->builder = builder = create_builder (ctx);
+ LLVMPositionBuilderAtEnd (ctx->builder, target_bb);
- // FIXME: This is shared with filter clauses ?
- g_assert (!values [exvar->dreg]);
- values [exvar->dreg] = LLVMBuildCall (builder, eh_exception, NULL, 0, "");
- emit_volatile_store (ctx, exvar->dreg);
- }
+ ctx->bblocks [bb->block_num].end_bblock = target_bb;
}
+ }
- has_terminator = FALSE;
- for (ins = bb->code; ins; ins = ins->next) {
- const char *spec = LLVM_INS_INFO (ins->opcode);
- char *dname = NULL;
- char dname_buf [128];
+ has_terminator = FALSE;
+ for (ins = bb->code; ins; ins = ins->next) {
+ const char *spec = LLVM_INS_INFO (ins->opcode);
+ char *dname = NULL;
+ char dname_buf [128];
- if (has_terminator)
- /* There could be instructions after a terminator, skip them */
- break;
+ 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_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 (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)) {
- 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)
- LLVM_FAILURE (ctx, "sreg1");
- lhs = values [ins->sreg1];
- }
+ if (var && var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) {
+ lhs = emit_volatile_load (ctx, ins->sreg1);
} else {
- lhs = NULL;
+ /* It is ok for SETRET to have an uninitialized argument */
+ if (!values [ins->sreg1] && ins->opcode != OP_SETRET)
+ LLVM_FAILURE (ctx, "sreg1");
+ 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])
- LLVM_FAILURE (ctx, "sreg2");
- rhs = values [ins->sreg2];
- }
+ 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 {
- rhs = NULL;
+ if (!values [ins->sreg2])
+ LLVM_FAILURE (ctx, "sreg2");
+ 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:
+ //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);
+ values [ins->dreg] = LLVMConstInt (LLVMInt64Type (), GET_LONG_IMM (ins), FALSE);
#else
- values [ins->dreg] = LLVMConstInt (LLVMInt64Type (), (gint64)ins->inst_c0, FALSE);
+ 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:
- values [ins->dreg] = LLVMConstFPExt (LLVMConstReal (LLVMFloatType (), *(float*)ins->inst_p0), LLVMDoubleType ());
- break;
- case OP_BR:
- LLVMBuildBr (builder, get_bb (ctx, ins->inst_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));
- }
+ break;
+ case OP_R8CONST:
+ values [ins->dreg] = LLVMConstReal (LLVMDoubleType (), *(double*)ins->inst_p0);
+ break;
+ case OP_R4CONST:
+ values [ins->dreg] = LLVMConstFPExt (LLVMConstReal (LLVMFloatType (), *(float*)ins->inst_p0), LLVMDoubleType ());
+ break;
+ case OP_BR:
+ LLVMBuildBr (builder, get_bb (ctx, ins->inst_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);
+ new_builder = create_builder (ctx);
+ LLVMPositionBuilderAtEnd (new_builder, new_bb);
+ LLVMBuildUnreachable (new_builder);
- has_terminator = TRUE;
- g_assert (!ins->next);
+ has_terminator = TRUE;
+ g_assert (!ins->next);
- break;
- }
+ break;
+ }
- case OP_SETRET:
- if (linfo->ret.storage == LLVMArgVtypeInReg) {
- LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method)));
- LLVMValueRef part1, retval;
- int size;
+ case OP_SETRET:
+ if (linfo->ret.storage == LLVMArgVtypeInReg) {
+ LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method)));
+ LLVMValueRef part1, retval;
+ int size;
- size = get_vtype_size (sig->ret);
+ size = get_vtype_size (sig->ret);
- g_assert (addresses [ins->sreg1]);
+ g_assert (addresses [ins->sreg1]);
- g_assert (linfo->ret.pair_storage [0] == LLVMArgInIReg);
- g_assert (linfo->ret.pair_storage [1] == LLVMArgNone);
+ g_assert (linfo->ret.pair_storage [0] == LLVMArgInIReg);
+ g_assert (linfo->ret.pair_storage [1] == LLVMArgNone);
- part1 = convert (ctx, LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (LLVMIntType (size * 8), 0), ""), ""), IntPtrType ());
-
- retval = LLVMBuildInsertValue (builder, LLVMGetUndef (ret_type), part1, 0, "");
+ part1 = convert (ctx, LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (LLVMIntType (size * 8), 0), ""), ""), IntPtrType ());
- LLVMBuildRet (builder, retval);
- break;
- }
+ retval = LLVMBuildInsertValue (builder, LLVMGetUndef (ret_type), part1, 0, "");
- if (linfo->ret.storage == LLVMArgVtypeRetAddr) {
- LLVMBuildRetVoid (builder);
- break;
- }
-
- if (!lhs || 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;
+ LLVMBuildRet (builder, retval);
break;
- case OP_ICOMPARE:
- case OP_FCOMPARE:
- case OP_LCOMPARE:
- case OP_COMPARE:
- case OP_ICOMPARE_IMM:
- case OP_LCOMPARE_IMM:
- case OP_COMPARE_IMM:
-#ifdef TARGET_AMD64
- case OP_AMD64_ICOMPARE_MEMBASE_REG:
- case OP_AMD64_ICOMPARE_MEMBASE_IMM:
-#endif
-#ifdef TARGET_X86
- case OP_X86_COMPARE_MEMBASE_REG:
- case OP_X86_COMPARE_MEMBASE_IMM:
-#endif
- {
- CompRelation rel;
- LLVMValueRef cmp;
-
- 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);
-
- /* Used for implementing bound checks */
-#ifdef TARGET_AMD64
- if ((ins->opcode == OP_AMD64_ICOMPARE_MEMBASE_REG) || (ins->opcode == OP_AMD64_ICOMPARE_MEMBASE_IMM)) {
- int size = 4;
- LLVMValueRef index;
- LLVMTypeRef t;
-
- t = LLVMInt32Type ();
-
- g_assert (ins->inst_offset % size == 0);
- index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
+ }
- lhs = LLVMBuildLoad (builder, LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), &index, 1, ""), "");
- }
- if (ins->opcode == OP_AMD64_ICOMPARE_MEMBASE_IMM) {
- lhs = convert (ctx, lhs, LLVMInt32Type ());
- rhs = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE);
- }
- if (ins->opcode == OP_AMD64_ICOMPARE_MEMBASE_REG)
- rhs = convert (ctx, rhs, LLVMInt32Type ());
-#endif
+ if (linfo->ret.storage == LLVMArgVtypeRetAddr) {
+ LLVMBuildRetVoid (builder);
+ break;
+ }
-#ifdef TARGET_X86
- if ((ins->opcode == OP_X86_COMPARE_MEMBASE_REG) || (ins->opcode == OP_X86_COMPARE_MEMBASE_IMM)) {
- int size = 4;
- LLVMValueRef index;
- LLVMTypeRef t;
+ 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;
+ case OP_ICOMPARE:
+ case OP_FCOMPARE:
+ case OP_LCOMPARE:
+ case OP_COMPARE:
+ case OP_ICOMPARE_IMM:
+ case OP_LCOMPARE_IMM:
+ case OP_COMPARE_IMM: {
+ CompRelation rel;
+ LLVMValueRef cmp;
+
+ if (ins->next->opcode == OP_NOP)
+ break;
- t = LLVMInt32Type ();
+ if (ins->next->opcode == OP_BR)
+ /* The comparison result is not needed */
+ continue;
- g_assert (ins->inst_offset % size == 0);
- index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
+ rel = mono_opcode_to_cond (ins->next->opcode);
- lhs = LLVMBuildLoad (builder, LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), &index, 1, ""), "");
- }
- if (ins->opcode == OP_X86_COMPARE_MEMBASE_IMM) {
- lhs = convert (ctx, lhs, LLVMInt32Type ());
- rhs = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE);
- }
- if (ins->opcode == OP_X86_COMPARE_MEMBASE_REG)
- rhs = convert (ctx, rhs, LLVMInt32Type ());
-#endif
+ 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 (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));
+ }
- 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_COMPARE_IMM)
+ 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), "");
}
-
- /* 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_COMPARE_IMM)
- cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], convert (ctx, lhs, IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), "");
- else if (ins->opcode == OP_COMPARE)
- 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 (MONO_IS_COND_BRANCH_OP (ins->next)) {
- 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_throw_pos (ctx);
- emit_cond_system_exception (ctx, bb, ins->next->inst_p1, cmp);
- builder = ctx->builder;
+ }
+ else if (ins->opcode == OP_COMPARE)
+ 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 (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 {
- LLVM_FAILURE (ctx, "next");
+ 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);
- ins = ins->next;
- break;
+ /* 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, ins->next->inst_p1, cmp);
+ CHECK_FAILURE (ctx);
+ builder = ctx->builder;
+ } else {
+ LLVM_FAILURE (ctx, "next");
}
- case OP_FCEQ:
- case OP_FCLT:
- case OP_FCLT_UN:
- case OP_FCGT:
- case OP_FCGT_UN: {
- CompRelation rel;
- LLVMValueRef cmp;
- rel = mono_opcode_to_cond (ins->opcode);
+ ins = ins->next;
+ break;
+ }
+ case OP_FCEQ:
+ case OP_FCLT:
+ case OP_FCLT_UN:
+ case OP_FCGT:
+ case OP_FCGT_UN: {
+ 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_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;
+ }
- cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMDoubleType ()), convert (ctx, rhs, LLVMDoubleType ()), "");
- values [ins->dreg] = LLVMBuildZExt (builder, cmp, LLVMInt32Type (), dname);
+ if (empty) {
+ /* LLVM doesn't like phi instructions with zero operands */
+ ctx->is_dead [ins->dreg] = TRUE;
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 */
- is_dead [ins->dreg] = TRUE;
- break;
- }
+ /* Created earlier, insert it now */
+ LLVMInsertIntoBuilder (builder, values [ins->dreg]);
- /* 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;
- 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 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;
+ 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 = 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);
- }
+ /* Remember for later */
+ for (j = 0; j < count; ++j) {
+ PhiNode *node = 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:
- g_assert (lhs);
- values [ins->dreg] = lhs;
- break;
- case OP_FMOVE: {
- MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
+ break;
+ }
+ case OP_MOVE:
+ case OP_LMOVE:
+ case OP_XMOVE:
+ case OP_SETFRET:
+ g_assert (lhs);
+ values [ins->dreg] = lhs;
+ break;
+ case OP_FMOVE: {
+ MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
- g_assert (lhs);
- values [ins->dreg] = lhs;
+ 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;
+ 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_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]));
+
+ switch (ins->opcode) {
case OP_IADD:
+ case OP_LADD:
+ values [ins->dreg] = LLVMBuildAdd (builder, lhs, rhs, dname);
+ break;
case OP_ISUB:
- case OP_IAND:
+ case OP_LSUB:
+ values [ins->dreg] = LLVMBuildSub (builder, lhs, rhs, dname);
+ break;
case OP_IMUL:
- case OP_IDIV:
- case OP_IDIV_UN:
+ 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_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_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:
- case OP_LREM:
- case OP_LREM_UN:
+ values [ins->dreg] = LLVMBuildUDiv (builder, lhs, rhs, dname);
+ break;
+ case OP_FDIV:
+ 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:
- lhs = convert (ctx, lhs, regtype_to_llvm_type (spec [MONO_INST_DEST]));
- rhs = convert (ctx, rhs, regtype_to_llvm_type (spec [MONO_INST_DEST]));
+ values [ins->dreg] = LLVMBuildLShr (builder, lhs, rhs, dname);
+ break;
- 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:
- 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;
-#if LLVM_MAJOR_VERSION == 2 && LLVM_MINOR_VERSION < 8
- case OP_FADD:
- values [ins->dreg] = LLVMBuildAdd (builder, lhs, rhs, dname);
- break;
- case OP_FSUB:
- values [ins->dreg] = LLVMBuildSub (builder, lhs, rhs, dname);
- break;
- case OP_FMUL:
- values [ins->dreg] = LLVMBuildMul (builder, lhs, rhs, dname);
- break;
-#else
- 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_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_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: {
+ LLVMValueRef imm;
+
+ if (spec [MONO_INST_SRC1] == 'l') {
+ imm = LLVMConstInt (LLVMInt64Type (), GET_LONG_IMM (ins), FALSE);
+ } else {
+ imm = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE);
+ }
+
+#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
- default:
- g_assert_not_reached ();
- }
- break;
+ 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_IREM_IMM:
- case OP_IREM_UN_IMM:
+ case OP_MUL_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_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_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_ADD_IMM:
- case OP_AND_IMM:
- case OP_MUL_IMM:
- case OP_SHL_IMM:
- case OP_SHR_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);
- }
+ 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_INOT: {
+ guint32 v = 0xffffffff;
+ values [ins->dreg] = LLVMBuildXor (builder, LLVMConstInt (LLVMInt32Type (), v, FALSE), lhs, 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;
-#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);
+ 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
- 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:
- 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:
- 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 ());
-#if LLVM_MAJOR_VERSION == 2 && LLVM_MINOR_VERSION < 8
- values [ins->dreg] = LLVMBuildSub (builder, LLVMConstReal (LLVMDoubleType (), 0.0), lhs, dname);
-#else
- values [ins->dreg] = LLVMBuildFSub (builder, LLVMConstReal (LLVMDoubleType (), 0.0), lhs, dname);
-#endif
- break;
- case OP_INOT: {
- guint32 v = 0xffffffff;
- values [ins->dreg] = LLVMBuildXor (builder, LLVMConstInt (LLVMInt32Type (), v, FALSE), lhs, 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;
-
- 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:
- values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMInt32Type (), dname);
- break;
- case OP_FCONV_TO_I1:
- values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), "");
- break;
- case OP_FCONV_TO_U1:
- values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), "");
- break;
- case OP_FCONV_TO_I2:
- values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), "");
- break;
- case OP_FCONV_TO_U2:
- values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), "");
- break;
- case OP_FCONV_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_LCONV_TO_R_UN:
- values [ins->dreg] = LLVMBuildUIToFP (builder, lhs, LLVMDoubleType (), dname);
- break;
+ 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:
+ values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMInt32Type (), dname);
+ break;
+ case OP_FCONV_TO_I1:
+ values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), "");
+ break;
+ case OP_FCONV_TO_U1:
+ values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), "");
+ break;
+ case OP_FCONV_TO_I2:
+ values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), "");
+ break;
+ case OP_FCONV_TO_U2:
+ values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), "");
+ break;
+ case OP_FCONV_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_LCONV_TO_R_UN:
+ values [ins->dreg] = LLVMBuildUIToFP (builder, lhs, LLVMDoubleType (), dname);
+ break;
#if SIZEOF_VOID_P == 4
- case OP_LCONV_TO_U:
+ 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 (), "");
- values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
- break;
- case OP_FCONV_TO_R4:
- v = LLVMBuildFPTrunc (builder, lhs, LLVMFloatType (), "");
- values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
- break;
- case OP_SEXT_I4:
- values [ins->dreg] = LLVMBuildSExt (builder, lhs, LLVMInt64Type (), dname);
- break;
- case OP_ZEXT_I4:
- values [ins->dreg] = LLVMBuildZExt (builder, lhs, 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);
+ 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 (), "");
+ values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
+ break;
+ case OP_FCONV_TO_R4:
+ v = LLVMBuildFPTrunc (builder, lhs, LLVMFloatType (), "");
+ values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
+ break;
+ case OP_SEXT_I4:
+ values [ins->dreg] = LLVMBuildSExt (builder, lhs, LLVMInt64Type (), dname);
+ break;
+ case OP_ZEXT_I4:
+ values [ins->dreg] = LLVMBuildZExt (builder, lhs, LLVMInt64Type (), dname);
+ break;
+ case OP_TRUNC_I4:
+ values [ins->dreg] = LLVMBuildTrunc (builder, lhs, LLVMInt32Type (), dname);
+ break;
+ case OP_LOCALLOC_IMM: {
+ LLVMValueRef v;
- v = mono_llvm_build_alloca (builder, LLVMInt8Type (), LLVMConstInt (LLVMInt32Type (), size, FALSE), MONO_ARCH_FRAME_ALIGNMENT, "");
+ guint32 size = ins->inst_imm;
+ size = (size + (MONO_ARCH_FRAME_ALIGNMENT - 1)) & ~ (MONO_ARCH_FRAME_ALIGNMENT - 1);
- if (ins->flags & MONO_INST_INIT) {
- LLVMValueRef args [5];
+ v = mono_llvm_build_alloca (builder, LLVMInt8Type (), LLVMConstInt (LLVMInt32Type (), size, FALSE), MONO_ARCH_FRAME_ALIGNMENT, "");
- 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, LLVMGetNamedFunction (module, memset_func_name), args, memset_param_count, "");
- }
+ if (ins->flags & MONO_INST_INIT) {
+ LLVMValueRef args [5];
- values [ins->dreg] = v;
- break;
+ 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, LLVMGetNamedFunction (module, memset_func_name), args, memset_param_count, "");
}
- case OP_LOCALLOC: {
- LLVMValueRef v, size;
+
+ values [ins->dreg] = v;
+ break;
+ }
+ case OP_LOCALLOC: {
+ LLVMValueRef v, size;
- size = LLVMBuildAnd (builder, LLVMBuildAdd (builder, lhs, LLVMConstInt (LLVMInt32Type (), MONO_ARCH_FRAME_ALIGNMENT - 1, FALSE), ""), LLVMConstInt (LLVMInt32Type (), ~ (MONO_ARCH_FRAME_ALIGNMENT - 1), FALSE), "");
+ 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, "");
+ v = mono_llvm_build_alloca (builder, LLVMInt8Type (), size, MONO_ARCH_FRAME_ALIGNMENT, "");
- if (ins->flags & MONO_INST_INIT) {
- LLVMValueRef args [5];
+ 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, LLVMGetNamedFunction (module, memset_func_name), args, memset_param_count, "");
- }
- values [ins->dreg] = v;
- break;
+ 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, LLVMGetNamedFunction (module, memset_func_name), args, memset_param_count, "");
}
+ 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 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*)"";
+ 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);
+ } else {
+ /* _MEMBASE */
+ base = lhs;
- /*
- * 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 ((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);
- } else if (ins->inst_offset == 0) {
- addr = values [ins->inst_basereg];
+ 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, values [ins->inst_basereg], LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, "");
+ 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, values [ins->inst_basereg], LLVMPointerType (t, 0)), &index, 1, "");
+ 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));
+ addr = convert (ctx, addr, LLVMPointerType (t, 0));
- values [ins->dreg] = mono_llvm_build_load (builder, addr, dname, is_volatile);
+ values [ins->dreg] = emit_load (ctx, bb, &builder, size, addr, dname, is_volatile);
- 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 (ins->opcode == OP_LOADR4_MEMBASE)
- values [ins->dreg] = LLVMBuildFPExt (builder, values [ins->dreg], LLVMDoubleType (), dname);
- break;
+ if (!is_volatile && (ins->flags & MONO_INST_CONSTANT_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_metadata_flag (values [ins->dreg], "mono.noalias");
+ set_metadata_flag (values [ins->dreg], "mono.nofail.load");
}
+
+ 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 (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;
- LLVMTypeRef t;
- gboolean sext = FALSE, zext = FALSE;
-
- t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
-
- if (ins->inst_offset % size != 0) {
- /* Unaligned store */
- index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE);
- addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, "");
- } else {
- index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
- addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, "");
- }
- LLVMBuildStore (builder, convert (ctx, values [ins->sreg1], t), convert (ctx, addr, LLVMPointerType (t, 0)));
- 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;
+ LLVMTypeRef t;
+ gboolean sext = FALSE, zext = FALSE;
+ gboolean is_volatile = (ins->flags & MONO_INST_FAULT);
+
+ if (!values [ins->inst_destbasereg])
+ LLVM_FAILURE (ctx, "inst_destbasereg");
+
+ t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
+
+ if (ins->inst_offset % size != 0) {
+ /* Unaligned store */
+ index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE);
+ addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, "");
+ } else {
+ index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
+ addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, "");
}
+ emit_store (ctx, bb, &builder, size, convert (ctx, values [ins->sreg1], t), convert (ctx, addr, LLVMPointerType (t, 0)), 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;
- LLVMTypeRef t;
- gboolean sext = FALSE, zext = FALSE;
-
- t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
-
- if (ins->inst_offset % size != 0) {
- /* Unaligned store */
- index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE);
- addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, "");
- } else {
- index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
- addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, "");
- }
- LLVMBuildStore (builder, convert (ctx, LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE), t), addr);
- 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;
+ 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 (ins->inst_offset % size != 0) {
+ /* Unaligned store */
+ index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE);
+ addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, "");
+ } else {
+ index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
+ addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, "");
}
+ emit_store (ctx, bb, &builder, size, convert (ctx, LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), t), addr, is_volatile);
+ break;
+ }
- case OP_CHECK_THIS:
- mono_llvm_build_load (builder, convert (ctx, values [ins->sreg1], LLVMPointerType (IntPtrType (), 0)), "", TRUE);
- break;
- case OP_OUTARG_VTRETADDR:
- break;
- case OP_VOIDCALL:
- case OP_CALL:
- case OP_LCALL:
- case OP_FCALL:
- case OP_VCALL:
- case OP_VOIDCALL_MEMBASE:
- case OP_CALL_MEMBASE:
- case OP_LCALL_MEMBASE:
- case OP_FCALL_MEMBASE:
- case OP_VCALL_MEMBASE:
- case OP_VOIDCALL_REG:
- case OP_CALL_REG:
- case OP_LCALL_REG:
- case OP_FCALL_REG:
- case OP_VCALL_REG: {
- MonoCallInst *call = (MonoCallInst*)ins;
- MonoMethodSignature *sig = call->signature;
- LLVMValueRef callee, lcall;
- LLVMValueRef *args;
- LLVMCallInfo *cinfo;
- GSList *l;
- int i, pindex;
- gboolean vretaddr;
- LLVMTypeRef llvm_sig;
- gpointer target;
- int *pindexes;
- gboolean virtual, calli;
-
- if (call->signature->call_convention != MONO_CALL_DEFAULT)
- LLVM_FAILURE (ctx, "non-default callconv");
-
- cinfo = call->cinfo;
-
- vretaddr = cinfo && cinfo->ret.storage == LLVMArgVtypeRetAddr;
-
- llvm_sig = sig_to_llvm_sig (ctx, sig, cinfo);
- CHECK_FAILURE (ctx);
-
- 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);
- calli = (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);
+ case OP_CHECK_THIS:
+ emit_load (ctx, bb, &builder, sizeof (gpointer), convert (ctx, values [ins->sreg1], LLVMPointerType (IntPtrType (), 0)), "", TRUE);
+ break;
+ case OP_OUTARG_VTRETADDR:
+ break;
+ case OP_VOIDCALL:
+ case OP_CALL:
+ case OP_LCALL:
+ case OP_FCALL:
+ case OP_VCALL:
+ case OP_VOIDCALL_MEMBASE:
+ case OP_CALL_MEMBASE:
+ case OP_LCALL_MEMBASE:
+ case OP_FCALL_MEMBASE:
+ case OP_VCALL_MEMBASE:
+ case OP_VOIDCALL_REG:
+ case OP_CALL_REG:
+ case OP_LCALL_REG:
+ case OP_FCALL_REG:
+ case OP_VCALL_REG: {
+ process_call (ctx, bb, &builder, ins);
+ CHECK_FAILURE (ctx);
+ break;
+ }
+ case OP_AOTCONST: {
+ guint32 got_offset;
+ LLVMValueRef indexes [2];
+ MonoJumpInfo *ji;
+ LLVMValueRef got_entry_addr;
- pindexes = mono_mempool_alloc0 (cfg->mempool, (sig->param_count + 2) * sizeof (guint32));
+ /*
+ * FIXME: Can't allocate from the cfg mempool since that is freed if
+ * the LLVM compile fails.
+ */
+ ji = g_new0 (MonoJumpInfo, 1);
+ ji->type = (MonoJumpInfoType)ins->inst_i1;
+ ji->data.target = ins->inst_p0;
- /* FIXME: Avoid creating duplicate methods */
+ ji = mono_aot_patch_info_dup (ji);
- if (ins->flags & MONO_INST_HAS_METHOD) {
- if (virtual) {
- callee = NULL;
- } else {
- if (cfg->compile_aot) {
- callee = get_plt_entry (ctx, llvm_sig, MONO_PATCH_INFO_METHOD, call->method);
- if (!callee)
- LLVM_FAILURE (ctx, "can't encode patch");
- } else {
- callee = LLVMAddFunction (module, "", llvm_sig);
+ 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);
- target =
- mono_create_jit_trampoline_in_domain (mono_domain_get (),
- call->method);
- LLVMAddGlobalMapping (ee, callee, target);
- }
- }
- } 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_plt_entry (ctx, llvm_sig, MONO_PATCH_INFO_INTERNAL_METHOD, (char*)info->name);
- if (!callee)
- LLVM_FAILURE (ctx, "can't encode patch");
- } else {
- callee = LLVMAddFunction (module, "", llvm_sig);
- target = (gpointer)mono_icall_get_wrapper (info);
- LLVMAddGlobalMapping (ee, callee, target);
- }
- } else {
- if (cfg->compile_aot) {
- callee = NULL;
- if (cfg->abs_patches) {
- MonoJumpInfo *abs_ji = g_hash_table_lookup (cfg->abs_patches, call->fptr);
- if (abs_ji) {
- callee = get_plt_entry (ctx, llvm_sig, abs_ji->type, abs_ji->data.target);
- if (!callee)
- LLVM_FAILURE (ctx, "can't encode patch");
- }
- }
- if (!callee)
- LLVM_FAILURE (ctx, "aot");
- } else {
- callee = LLVMAddFunction (module, "", llvm_sig);
- target = NULL;
- if (cfg->abs_patches) {
- MonoJumpInfo *abs_ji = g_hash_table_lookup (cfg->abs_patches, call->fptr);
- if (abs_ji) {
- /*
- * The monitor entry/exit trampolines might have
- * their own calling convention on some platforms.
- */
-#ifndef TARGET_AMD64
- if (abs_ji->type == MONO_PATCH_INFO_MONITOR_ENTER || abs_ji->type == MONO_PATCH_INFO_MONITOR_EXIT)
- LLVM_FAILURE (ctx, "monitor enter/exit");
-#endif
- target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, abs_ji, FALSE);
- LLVMAddGlobalMapping (ee, callee, target);
- }
- }
- if (!target)
- LLVMAddGlobalMapping (ee, callee, (gpointer)call->fptr);
- }
- }
- }
-
- if (virtual) {
- int size = sizeof (gpointer);
- LLVMValueRef index;
+ indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
+ indexes [1] = LLVMConstInt (LLVMInt32Type (), (gssize)got_offset, FALSE);
+ got_entry_addr = LLVMBuildGEP (builder, ctx->lmodule->got_var, indexes, 2, "");
- g_assert (ins->inst_offset % size == 0);
- index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
+ // FIXME: This doesn't work right now, because it must be
+ // paired with an invariant.end, and even then, its only in effect
+ // inside its basic block
+#if 0
+ {
+ LLVMValueRef args [3];
+ LLVMValueRef ptr, val;
- // FIXME: mono_arch_get_vcall_slot () can't decode the code
- // generated by LLVM
- //LLVM_FAILURE (ctx, "virtual call");
+ ptr = LLVMBuildBitCast (builder, got_entry_addr, LLVMPointerType (LLVMInt8Type (), 0), "ptr");
- if (call->method && call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
-#ifdef MONO_ARCH_HAVE_LLVM_IMT_TRAMPOLINE
- if (cfg->compile_aot) {
- MonoJumpInfoImtTramp *imt_tramp = g_new0 (MonoJumpInfoImtTramp, 1);
- imt_tramp->method = call->method;
- imt_tramp->vt_offset = call->inst.inst_offset;
-
- callee = get_plt_entry (ctx, llvm_sig, MONO_PATCH_INFO_LLVM_IMT_TRAMPOLINE, imt_tramp);
- } else {
- callee = LLVMAddFunction (module, "", llvm_sig);
- target = mono_create_llvm_imt_trampoline (cfg->domain, call->method, call->inst.inst_offset);
- LLVMAddGlobalMapping (ee, callee, target);
- }
-#else
- /* No support for passing the IMT argument */
- LLVM_FAILURE (ctx, "imt");
+ args [0] = LLVMConstInt (LLVMInt64Type (), sizeof (gpointer), FALSE);
+ args [1] = ptr;
+ val = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.invariant.start"), args, 2, "");
+ }
#endif
- } else {
- 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) {
- }
- }
+ values [ins->dreg] = LLVMBuildLoad (builder, got_entry_addr, dname);
+ 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;
/*
- * Collect and convert arguments
- */
-
- args = alloca (sizeof (LLVMValueRef) * ((sig->param_count * 2) + sig->hasthis + vretaddr));
- l = call->out_ireg_args;
- pindex = 0;
- if (vretaddr) {
- if (!addresses [call->inst.dreg])
- addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
- args [pindex ++] = LLVMBuildPtrToInt (builder, addresses [call->inst.dreg], IntPtrType (), "");
- }
-
- for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
- guint32 regpair;
- int reg;
- LLVMArgInfo *ainfo = call->cinfo ? &call->cinfo->args [i] : NULL;
-
- regpair = (guint32)(gssize)(l->data);
- reg = regpair & 0xffffff;
- args [pindex] = values [reg];
- if (ainfo->storage == LLVMArgVtypeInReg) {
- int j;
- LLVMValueRef regs [2];
- guint32 nregs;
-
- g_assert (ainfo);
-
- g_assert (addresses [reg]);
-
- emit_vtype_to_reg (ctx, builder, sig->params [i - sig->hasthis], addresses [reg], ainfo, regs, &nregs);
- for (j = 0; j < nregs; ++j)
- args [pindex ++] = regs [j];
-
- // FIXME: alignment
- // FIXME: Get rid of the VMOVE
- } else if (ainfo->storage == LLVMArgVtypeByVal) {
- g_assert (addresses [reg]);
- args [pindex] = addresses [reg];
- pindexes [i] = pindex;
- pindex ++;
- } else {
- g_assert (args [pindex]);
- if (i == 0 && sig->hasthis)
- args [pindex] = convert (ctx, args [pindex], IntPtrType ());
- else
- args [pindex] = convert (ctx, args [pindex], type_to_llvm_arg_type (ctx, sig->params [i - sig->hasthis]));
- pindex ++;
- }
-
- l = l->next;
- }
-
- // FIXME: Align call sites
-
- /*
- * Emit the call
- */
-
- lcall = emit_call (ctx, bb, &builder, callee, args, pindex);
-
- /* Add byval attributes if needed */
- for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
- LLVMArgInfo *ainfo = call->cinfo ? &call->cinfo->args [i] : NULL;
-
- if (ainfo && ainfo->storage == LLVMArgVtypeByVal) {
- LLVMAddInstrAttribute (lcall, pindexes [i] + 1, LLVMByValAttribute);
- }
- }
-
- /*
- * Convert the result
+ * FIXME: If later code uses the regs defined by these instructions,
+ * compilation will fail.
*/
- if (cinfo && cinfo->ret.storage == LLVMArgVtypeInReg) {
- LLVMValueRef regs [2];
-
- 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_reg_to_vtype (ctx, builder, sig->ret, addresses [ins->dreg], &cinfo->ret, regs);
- } else if (sig->ret->type != MONO_TYPE_VOID && !vretaddr) {
- /* If the method returns an unsigned value, need to zext it */
-
- values [ins->dreg] = convert_full (ctx, lcall, llvm_type_to_stack_type (type_to_llvm_type (ctx, sig->ret)), type_is_unsigned (ctx, sig->ret));
- }
- break;
- }
- case OP_AOTCONST: {
- guint32 got_offset;
- LLVMValueRef indexes [2];
- MonoJumpInfo *ji;
- LLVMValueRef got_entry_addr;
+ MONO_DELETE_INS (bb, next);
+ }
+ break;
+ case OP_LDADDR: {
+ MonoInst *var = ins->inst_p0;
- /*
- * FIXME: Can't allocate from the cfg mempool since that is freed if
- * the LLVM compile fails.
- */
- ji = g_new0 (MonoJumpInfo, 1);
- ji->type = (MonoJumpInfoType)ins->inst_i1;
- ji->data.target = ins->inst_p0;
+ values [ins->dreg] = addresses [var->dreg];
+ break;
+ }
+ case OP_SIN: {
+ LLVMValueRef args [1];
- ji = mono_aot_patch_info_dup (ji);
+ args [0] = convert (ctx, lhs, LLVMDoubleType ());
+ values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.sin.f64"), args, 1, dname);
+ break;
+ }
+ case OP_COS: {
+ LLVMValueRef args [1];
- 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);
-
- indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
- indexes [1] = LLVMConstInt (LLVMInt32Type (), (gssize)got_offset, FALSE);
- got_entry_addr = LLVMBuildGEP (builder, ctx->lmodule->got_var, indexes, 2, "");
+ args [0] = convert (ctx, lhs, LLVMDoubleType ());
+ values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.cos.f64"), args, 1, dname);
+ break;
+ }
+ case OP_SQRT: {
+ LLVMValueRef args [1];
- // FIXME: This doesn't work right now, because it must be
- // paired with an invariant.end, and even then, its only in effect
- // inside its basic block
#if 0
- {
- LLVMValueRef args [3];
- LLVMValueRef ptr, val;
-
- ptr = LLVMBuildBitCast (builder, got_entry_addr, LLVMPointerType (LLVMInt8Type (), 0), "ptr");
-
- args [0] = LLVMConstInt (LLVMInt64Type (), sizeof (gpointer), FALSE);
- args [1] = ptr;
- val = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.invariant.start"), args, 2, "");
- }
+ /* This no longer seems to happen */
+ /*
+ * LLVM optimizes sqrt(nan) into undefined in
+ * lib/Analysis/ConstantFolding.cpp
+ * Also, sqrt(NegativeInfinity) is optimized into 0.
+ */
+ LLVM_FAILURE (ctx, "sqrt");
#endif
+ args [0] = lhs;
+ values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.sqrt.f64"), args, 1, dname);
+ break;
+ }
+ case OP_ABS: {
+ LLVMValueRef args [1];
- values [ins->dreg] = LLVMBuildLoad (builder, got_entry_addr, dname);
- break;
- }
- case OP_NOT_REACHED:
- LLVMBuildUnreachable (builder);
- has_terminator = TRUE;
- g_assert (bb->block_num < cfg->max_block_num);
- unreachable [bb->block_num] = TRUE;
- /* Might have instructions after this */
- while (ins->next) {
- MonoInst *next = ins->next;
- MONO_DELETE_INS (bb, next);
- }
- break;
- case OP_LDADDR: {
- MonoInst *var = ins->inst_p0;
-
- values [ins->dreg] = addresses [var->dreg];
- break;
- }
- case OP_SIN: {
- LLVMValueRef args [1];
-
- args [0] = convert (ctx, lhs, LLVMDoubleType ());
- values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.sin.f64"), args, 1, dname);
- break;
- }
- case OP_COS: {
- LLVMValueRef args [1];
-
- args [0] = convert (ctx, lhs, LLVMDoubleType ());
- values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.cos.f64"), args, 1, dname);
- break;
- }
- /* test_0_sqrt_nan fails with LLVM */
- /*
- case OP_SQRT: {
- LLVMValueRef args [1];
-
- args [0] = lhs;
- values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.sqrt.f64"), args, 1, dname);
- break;
- }
- */
+ args [0] = lhs;
+ values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "fabs"), args, 1, dname);
+ break;
+ }
- case OP_ABS: {
- LLVMValueRef args [1];
+ 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;
- args [0] = lhs;
- values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "fabs"), args, 1, dname);
- break;
- }
+ 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: {
- LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntSLE, lhs, rhs, "");
- values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
+ case OP_LMIN:
+ v = LLVMBuildICmp (builder, LLVMIntSLE, lhs, rhs, "");
break;
- }
case OP_IMAX:
- case OP_LMAX: {
- LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntSGE, lhs, rhs, "");
- values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
+ case OP_LMAX:
+ v = LLVMBuildICmp (builder, LLVMIntSGE, lhs, rhs, "");
break;
- }
case OP_IMIN_UN:
- case OP_LMIN_UN: {
- LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntULE, lhs, rhs, "");
- values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
+ case OP_LMIN_UN:
+ v = LLVMBuildICmp (builder, LLVMIntULE, lhs, rhs, "");
break;
- }
case OP_IMAX_UN:
- case OP_LMAX_UN: {
- LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntUGE, lhs, rhs, "");
- values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
+ case OP_LMAX_UN:
+ v = LLVMBuildICmp (builder, LLVMIntUGE, lhs, rhs, "");
+ break;
+ default:
+ g_assert_not_reached ();
break;
}
- case OP_ATOMIC_EXCHANGE_I4: {
- LLVMValueRef args [2];
+ values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
+ break;
+ }
+ case OP_ATOMIC_EXCHANGE_I4: {
+ LLVMValueRef args [2];
- g_assert (ins->inst_offset == 0);
+ g_assert (ins->inst_offset == 0);
- args [0] = convert (ctx, lhs, LLVMPointerType (LLVMInt32Type (), 0));
- args [1] = rhs;
- values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.atomic.swap.i32.p0i32"), args, 2, dname);
- break;
- }
- case OP_ATOMIC_EXCHANGE_I8: {
- LLVMValueRef args [2];
+ args [0] = convert (ctx, lhs, LLVMPointerType (LLVMInt32Type (), 0));
+ args [1] = rhs;
+ values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.atomic.swap.i32.p0i32"), args, 2, dname);
+ break;
+ }
+ case OP_ATOMIC_EXCHANGE_I8: {
+ LLVMValueRef args [2];
- g_assert (ins->inst_offset == 0);
+ g_assert (ins->inst_offset == 0);
- args [0] = convert (ctx, lhs, LLVMPointerType (LLVMInt64Type (), 0));
- args [1] = rhs;
- values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.atomic.swap.i64.p0i64"), args, 2, dname);
- break;
- }
- case OP_ATOMIC_ADD_NEW_I4: {
- LLVMValueRef args [2];
+ args [0] = convert (ctx, lhs, LLVMPointerType (LLVMInt64Type (), 0));
+ args [1] = convert (ctx, rhs, LLVMInt64Type ());
+ values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.atomic.swap.i64.p0i64"), args, 2, dname);
+ break;
+ }
+ case OP_ATOMIC_ADD_NEW_I4: {
+ LLVMValueRef args [2];
- g_assert (ins->inst_offset == 0);
+ g_assert (ins->inst_offset == 0);
- args [0] = convert (ctx, lhs, LLVMPointerType (LLVMInt32Type (), 0));
- args [1] = rhs;
- values [ins->dreg] = LLVMBuildAdd (builder, LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.atomic.load.add.i32.p0i32"), args, 2, ""), args [1], dname);
- break;
- }
- case OP_ATOMIC_ADD_NEW_I8: {
- LLVMValueRef args [2];
+ args [0] = convert (ctx, lhs, LLVMPointerType (LLVMInt32Type (), 0));
+ args [1] = rhs;
+ values [ins->dreg] = LLVMBuildAdd (builder, LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.atomic.load.add.i32.p0i32"), args, 2, ""), args [1], dname);
+ break;
+ }
+ case OP_ATOMIC_ADD_NEW_I8: {
+ LLVMValueRef args [2];
- g_assert (ins->inst_offset == 0);
+ g_assert (ins->inst_offset == 0);
- args [0] = convert (ctx, lhs, LLVMPointerType (LLVMInt64Type (), 0));
- args [1] = convert (ctx, rhs, LLVMInt64Type ());
- values [ins->dreg] = LLVMBuildAdd (builder, LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.atomic.load.add.i64.p0i64"), args, 2, ""), args [1], dname);
- break;
- }
- case OP_ATOMIC_CAS_I4:
- case OP_ATOMIC_CAS_I8: {
- LLVMValueRef args [3];
- LLVMTypeRef t;
- const char *intrins;
+ args [0] = convert (ctx, lhs, LLVMPointerType (LLVMInt64Type (), 0));
+ args [1] = convert (ctx, rhs, LLVMInt64Type ());
+ values [ins->dreg] = LLVMBuildAdd (builder, LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.atomic.load.add.i64.p0i64"), args, 2, ""), args [1], dname);
+ break;
+ }
+ case OP_ATOMIC_CAS_I4:
+ case OP_ATOMIC_CAS_I8: {
+ LLVMValueRef args [3];
+ LLVMTypeRef t;
+ const char *intrins;
- if (ins->opcode == OP_ATOMIC_CAS_I4) {
- t = LLVMInt32Type ();
- intrins = "llvm.atomic.cmp.swap.i32.p0i32";
- } else {
- t = LLVMInt64Type ();
- intrins = "llvm.atomic.cmp.swap.i64.p0i64";
- }
-
- 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);
- values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, intrins), args, 3, dname);
- break;
+ if (ins->opcode == OP_ATOMIC_CAS_I4) {
+ t = LLVMInt32Type ();
+ intrins = "llvm.atomic.cmp.swap.i32.p0i32";
+ } else {
+ t = LLVMInt64Type ();
+ intrins = "llvm.atomic.cmp.swap.i64.p0i64";
}
- case OP_MEMORY_BARRIER: {
- LLVMValueRef args [5];
- for (i = 0; i < 5; ++i)
- args [i] = LLVMConstInt (LLVMInt1Type (), TRUE, TRUE);
+ 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);
+ values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, intrins), args, 3, dname);
+ break;
+ }
+ case OP_MEMORY_BARRIER: {
+ LLVMValueRef args [5];
- LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.memory.barrier"), args, 5, "");
- break;
- }
- case OP_RELAXED_NOP: {
+#ifdef TARGET_ARM
+ /* Not yet supported by llc on arm */
+ LLVM_FAILURE (ctx, "memory-barrier+arm");
+#endif
+
+ for (i = 0; i < 5; ++i)
+ args [i] = LLVMConstInt (LLVMInt1Type (), TRUE, TRUE);
+
+ LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.memory.barrier"), args, 5, "");
+ break;
+ }
+ case OP_RELAXED_NOP: {
#if defined(TARGET_AMD64) || defined(TARGET_X86)
+ if (IS_LLVM_MONO_BRANCH)
+ emit_call (ctx, bb, &builder, LLVMGetNamedFunction (ctx->module, "llvm.x86.sse2.pause"), NULL, 0);
+ else
/* No way to get LLVM to emit this */
LLVM_FAILURE (ctx, "relaxed_nop");
+ break;
#else
- break;
+ break;
#endif
- }
- case OP_TLS_GET: {
+ }
+ case OP_TLS_GET: {
#if defined(TARGET_AMD64) || defined(TARGET_X86)
#ifdef TARGET_AMD64
- // 257 == FS segment register
- LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 257);
+ // 257 == FS segment register
+ LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 257);
#else
- // 256 == GS segment register
- LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 256);
+ // 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, ""), "");
+ // FIXME: XEN
+ values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildIntToPtr (builder, LLVMConstInt (IntPtrType (), ins->inst_offset, TRUE), ptrtype, ""), "");
#else
- LLVM_FAILURE (ctx, "opcode tls-get");
+ LLVM_FAILURE (ctx, "opcode tls-get");
#endif
- break;
- }
+ 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_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:
#if SIZEOF_VOID_P == 8
- 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:
+ 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:
#endif
{
LLVMValueRef args [2], val, ovf, func;
- emit_cond_throw_pos (ctx);
-
args [0] = convert (ctx, lhs, op_to_llvm_type (ins->opcode));
args [1] = convert (ctx, rhs, op_to_llvm_type (ins->opcode));
func = LLVMGetNamedFunction (module, ovf_op_to_intrins (ins->opcode));
values [ins->dreg] = LLVMBuildExtractValue (builder, val, 0, dname);
ovf = LLVMBuildExtractValue (builder, val, 1, "");
emit_cond_system_exception (ctx, bb, "OverflowException", ovf);
+ CHECK_FAILURE (ctx);
builder = ctx->builder;
break;
}
* 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];
+ case OP_VZERO: {
+ MonoClass *klass = ins->klass;
+ LLVMValueRef args [5];
- if (!klass) {
- // FIXME:
- LLVM_FAILURE (ctx, "!klass");
- break;
- }
+ if (!klass) {
+ // FIXME:
+ LLVM_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, LLVMGetNamedFunction (module, memset_func_name), args, memset_param_count, "");
+ 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, LLVMGetNamedFunction (module, memset_func_name), args, memset_param_count, "");
+ break;
+ }
+
+ case OP_STOREV_MEMBASE:
+ case OP_LOADV_MEMBASE:
+ case OP_VMOVE: {
+ MonoClass *klass = ins->klass;
+ LLVMValueRef src, dst, args [5];
+ gboolean done = FALSE;
+
+ if (!klass) {
+ // FIXME:
+ LLVM_FAILURE (ctx, "!klass");
break;
}
+ switch (ins->opcode) {
case OP_STOREV_MEMBASE:
- case OP_LOADV_MEMBASE:
- case OP_VMOVE: {
- MonoClass *klass = ins->klass;
- LLVMValueRef src, dst, args [5];
- gboolean done = FALSE;
-
- if (!klass) {
- // FIXME:
- LLVM_FAILURE (ctx, "!klass");
+ if (cfg->gen_write_barriers && klass->has_references && ins->inst_destbasereg != cfg->frame_reg) {
+ /* FIXME: Emit write barriers like in mini_emit_stobj () */
+ LLVM_FAILURE (ctx, "storev_membase + write barriers");
break;
}
-
- switch (ins->opcode) {
- case OP_STOREV_MEMBASE:
- 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);
+ 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 = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), "");
- break;
- default:
- g_assert_not_reached ();
+ dst = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_destbasereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (LLVMInt8Type (), 0));
}
-
- 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, LLVMGetNamedFunction (module, memcpy_func_name), args, memcpy_param_count, "");
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 ();
}
- case OP_LLVM_OUTARG_VT:
- if (!addresses [ins->sreg1]) {
- addresses [ins->sreg1] = build_alloca (ctx, &ins->klass->byval_arg);
- g_assert (values [ins->sreg1]);
- LLVMBuildStore (builder, values [ins->sreg1], addresses [ins->sreg1]);
- }
- addresses [ins->dreg] = addresses [ins->sreg1];
+ CHECK_FAILURE (ctx);
+
+ 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, LLVMGetNamedFunction (module, memcpy_func_name), args, memcpy_param_count, "");
+ break;
+ }
+ case OP_LLVM_OUTARG_VT:
+ if (!addresses [ins->sreg1]) {
+ addresses [ins->sreg1] = build_alloca (ctx, &ins->klass->byval_arg);
+ g_assert (values [ins->sreg1]);
+ LLVMBuildStore (builder, values [ins->sreg1], addresses [ins->sreg1]);
+ }
+ addresses [ins->dreg] = addresses [ins->sreg1];
+ 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;
+ 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_ADDPD:
- case OP_ADDPS:
- case OP_PADDB:
- case OP_PADDW:
- case OP_PADDD:
- case OP_PADDQ:
- values [ins->dreg] = LLVMBuildAdd (builder, lhs, rhs, "");
- break;
- case OP_SUBPD:
- case OP_SUBPS:
- case OP_PSUBB:
- case OP_PSUBW:
- case OP_PSUBD:
- case OP_PSUBQ:
- values [ins->dreg] = LLVMBuildSub (builder, lhs, rhs, "");
- break;
- case OP_MULPD:
- case OP_MULPS:
- values [ins->dreg] = LLVMBuildMul (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;
+ 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;
+
+ 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: {
- LLVMTypeRef t, rt;
- LLVMValueRef v;
-
- 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, "");
+ case OP_XORPD:
+ t = LLVMVectorType (LLVMInt64Type (), 2);
+ rt = LLVMVectorType (LLVMDoubleType (), 2);
break;
+ default:
+ t = LLVMInt32Type ();
+ rt = LLVMInt32Type ();
+ g_assert_not_reached ();
}
- case OP_MINPD:
- case OP_MINPS:
- case OP_MAXPD:
- case OP_MAXPS:
- case OP_PMIND_UN:
- case OP_PMINW_UN:
- case OP_PMINB_UN:
- case OP_PMAXD_UN:
- case OP_PMAXW_UN:
- case OP_PMAXB_UN: {
- LLVMValueRef args [2];
-
- args [0] = lhs;
- args [1] = rhs;
-
- values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, simd_op_to_intrins (ins->opcode)), args, 2, dname);
+
+ 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_MINPD:
+ case OP_MINPS:
+ case OP_MAXPD:
+ case OP_MAXPS:
+ case OP_ADDSUBPD:
+ case OP_ADDSUBPS:
+ case OP_PMIND_UN:
+ case OP_PMINW_UN:
+ case OP_PMINB_UN:
+ case OP_PMINW:
+ case OP_PMAXD_UN:
+ case OP_PMAXW_UN:
+ case OP_PMAXB_UN:
+ 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_PCMPEQB:
+ case OP_PCMPEQW:
+ case OP_PCMPEQD:
+ case OP_PCMPEQQ:
+ case OP_PCMPGTB:
+ 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, LLVMGetNamedFunction (module, simd_op_to_intrins (ins->opcode)), args, 2, dname);
+ 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_U2:
case OP_EXTRACT_I1:
- case OP_EXTRACT_U1: {
- LLVMTypeRef t;
- gboolean zext = FALSE;
+ 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;
+
+ 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;
+
+ case OP_CVTDQ2PD:
+ case OP_CVTDQ2PS:
+ case OP_CVTPD2DQ:
+ case OP_CVTPS2DQ:
+ case OP_CVTPD2PS:
+ case OP_CVTPS2PD:
+ case OP_CVTTPD2DQ:
+ case OP_CVTTPS2DQ:
+ 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, LLVMGetNamedFunction (module, simd_op_to_intrins (ins->opcode)), &v, 1, dname);
+ break;
+ }
+
+ case OP_ICONV_TO_R8_RAW:
+ /* Same as OP_ICONV_TO_R8 */
+ values [ins->dreg] = convert (ctx, LLVMBuildBitCast (builder, lhs, LLVMFloatType (), ""), LLVMDoubleType ());
+ break;
+
+ case OP_COMPPS:
+ case OP_COMPPD: {
+ LLVMValueRef args [3];
+
+ args [0] = lhs;
+ args [1] = rhs;
+ args [2] = LLVMConstInt (LLVMInt8Type (), ins->inst_c0, FALSE);
+
+ values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, simd_op_to_intrins (ins->opcode)), args, 3, dname);
+ 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, LLVMGetNamedFunction (module, 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, LLVMGetNamedFunction (module, 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 [4];
+ 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;
+ }
+
+#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)
+ LLVM_FAILURE (ctx, "implicit-exception");
+ break;
+ case OP_THROW:
+ case OP_RETHROW: {
+ MonoMethodSignature *throw_sig;
+ LLVMValueRef callee, arg;
+ gboolean rethrow = (ins->opcode == OP_RETHROW);
+ const char *icall_name;
+
+ callee = rethrow ? ctx->lmodule->rethrow : ctx->lmodule->throw;
+ 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 (cfg->compile_aot) {
+ callee = get_plt_entry (ctx, sig_to_llvm_sig (ctx, throw_sig), MONO_PATCH_INFO_INTERNAL_METHOD, icall_name);
+ } else {
+ callee = LLVMAddFunction (module, icall_name, sig_to_llvm_sig (ctx, throw_sig));
+
+#ifdef TARGET_X86
+ /*
+ * LLVM doesn't push the exception argument, so we need a different
+ * trampoline.
+ */
+ LLVMAddGlobalMapping (ee, callee, resolve_patch (cfg, MONO_PATCH_INFO_INTERNAL_METHOD, rethrow ? "llvm_rethrow_exception_trampoline" : "llvm_throw_exception_trampoline"));
+#else
+ LLVMAddGlobalMapping (ee, callee, resolve_patch (cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name));
+#endif
+ }
+
+ mono_memory_barrier ();
+ if (rethrow)
+ ctx->lmodule->rethrow = callee;
+ else
+ ctx->lmodule->throw = callee;
+ }
+ arg = convert (ctx, values [ins->sreg1], type_to_llvm_type (ctx, &mono_get_object_class ()->byval_arg));
+ emit_call (ctx, bb, &builder, callee, &arg, 1);
+ 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;
+
+ handler_bb = 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->cfg->compile_aot) {
+ callee = get_plt_entry (ctx, LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE), MONO_PATCH_INFO_INTERNAL_METHOD, "llvm_resume_unwind_trampoline");
+ } else {
+ callee = LLVMGetNamedFunction (module, "llvm_resume_unwind_trampoline");
+ }
+ LLVMBuildCall (builder, callee, NULL, 0, "");
+
+ LLVMBuildUnreachable (builder);
+ has_terminator = TRUE;
+ break;
+ }
+ default: {
+ char reason [128];
+
+ sprintf (reason, "opcode %s", mono_inst_name (ins->opcode));
+ LLVM_FAILURE (ctx, reason);
+ 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 (!values [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 (!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)
+ LLVMBuildRetVoid (builder);
+
+ if (bb == cfg->bb_entry)
+ ctx->last_alloca = LLVMGetLastInstruction (get_bb (ctx, cfg->bb_entry));
+
+ return;
+
+ FAILURE:
+ return;
+}
+
+/*
+ * 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)
+{
+ /*
+ MonoMethodHeader *header = cfg->header;
+ MonoExceptionClause *clause;
+ int i;
+ */
+
+ if (cfg->generic_sharing_context && !IS_LLVM_MONO_BRANCH) {
+ /* No way to obtain location info for this/rgctx */
+ cfg->exception_message = g_strdup ("gshared");
+ cfg->disable_llvm = TRUE;
+ }
+
+ if (cfg->method->save_lmf) {
+ cfg->exception_message = g_strdup ("lmf");
+ cfg->disable_llvm = TRUE;
+ }
+
+#if 0
+ for (i = 0; i < header->num_clauses; ++i) {
+ clause = &header->clauses [i];
+
+ if (i > 0 && clause->try_offset <= header->clauses [i - 1].handler_offset + header->clauses [i - 1].handler_len) {
+ /*
+ * FIXME: Some tests still fail with nested clauses.
+ */
+ cfg->exception_message = g_strdup ("nested clauses");
+ cfg->disable_llvm = TRUE;
+ }
+ }
+#endif
+
+ /* FIXME: */
+ if (cfg->method->dynamic) {
+ cfg->exception_message = g_strdup ("dynamic.");
+ cfg->disable_llvm = TRUE;
+ }
+}
+
+/*
+ * 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;
+ MonoMethodSignature *sig;
+ MonoBasicBlock *bb;
+ LLVMTypeRef method_type;
+ LLVMValueRef method = NULL;
+ char *method_name;
+ LLVMValueRef *values;
+ int i, max_block_num, bb_index;
+ gboolean last = FALSE;
+ GPtrArray *phi_values;
+ LLVMCallInfo *linfo;
+ GSList *l;
+ LLVMModuleRef module;
+ BBInfo *bblocks;
+ GPtrArray *bblock_list;
+ MonoMethodHeader *header;
+ MonoExceptionClause *clause;
+ LLVMSigInfo sinfo;
+ char **names;
+
+ /* 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
+ */
+ 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->vreg_cli_types = g_new0 (MonoType*, cfg->next_vreg);
+ phi_values = g_ptr_array_new ();
+ /*
+ * 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);
- switch (ins->opcode) {
- case OP_EXTRACT_R8:
- t = LLVMVectorType (LLVMDoubleType (), 2);
- break;
- case OP_EXTRACT_I8:
- t = LLVMVectorType (LLVMInt64Type (), 2);
- break;
- case OP_EXTRACT_I4:
- t = LLVMVectorType (LLVMInt32Type (), 4);
- break;
- case OP_EXTRACT_I2:
- t = LLVMVectorType (LLVMInt16Type (), 8);
- break;
- case OP_EXTRACT_U2:
- t = LLVMVectorType (LLVMInt16Type (), 8);
- zext = TRUE;
- break;
- case OP_EXTRACT_I1:
- t = LLVMVectorType (LLVMInt8Type (), 16);
- break;
- case OP_EXTRACT_U1:
- t = LLVMVectorType (LLVMInt8Type (), 16);
- zext = TRUE;
- break;
- default:
- t = LLVMInt32Type ();
- g_assert_not_reached ();
- }
+ bblock_list = g_ptr_array_new ();
- 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;
+ ctx->values = values;
+ ctx->region_to_handler = g_hash_table_new (NULL, NULL);
+
+ if (cfg->compile_aot) {
+ ctx->lmodule = &aot_module;
+ method_name = mono_aot_get_method_name (cfg);
+ cfg->llvm_method_name = g_strdup (method_name);
+ } else {
+ init_jit_module ();
+ ctx->lmodule = &jit_module;
+ method_name = mono_method_full_name (cfg->method, TRUE);
+ }
+
+ module = ctx->module = ctx->lmodule->module;
+
+#if 1
+ {
+ static int count = 0;
+ count ++;
+
+ if (getenv ("LLVM_COUNT")) {
+ if (count == atoi (getenv ("LLVM_COUNT"))) {
+ printf ("LAST: %s\n", mono_method_full_name (cfg->method, TRUE));
+ fflush (stdout);
+ last = TRUE;
}
+ if (count > atoi (getenv ("LLVM_COUNT")))
+ LLVM_FAILURE (ctx, "");
+ }
+ }
#endif
- case OP_DUMMY_USE:
- break;
+ sig = mono_method_signature (cfg->method);
+ ctx->sig = sig;
- /*
- * EXCEPTION HANDLING
- */
- case OP_IMPLICIT_EXCEPTION:
- /* This marks a place where an implicit exception can happen */
- if (bb->region != -1)
- LLVM_FAILURE (ctx, "implicit-exception");
- break;
- case OP_THROW: {
- MonoMethodSignature *throw_sig;
- LLVMValueRef callee, arg;
-
- if (!ctx->lmodule->throw) {
- throw_sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
- throw_sig->ret = &mono_defaults.void_class->byval_arg;
- throw_sig->params [0] = &mono_defaults.object_class->byval_arg;
- if (cfg->compile_aot) {
- callee = get_plt_entry (ctx, sig_to_llvm_sig (ctx, throw_sig, NULL), MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_exception");
- } else {
- callee = LLVMAddFunction (module, "mono_arch_throw_exception", sig_to_llvm_sig (ctx, throw_sig, NULL));
+ linfo = mono_arch_get_llvm_call_info (cfg, sig);
+ ctx->linfo = linfo;
+ CHECK_FAILURE (ctx);
-#ifdef TARGET_X86
- /*
- * LLVM doesn't push the exception argument, so we need a different
- * trampoline.
- */
- LLVMAddGlobalMapping (ee, callee, resolve_patch (cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_llvm_throw_exception"));
-#else
- LLVMAddGlobalMapping (ee, callee, resolve_patch (cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_exception"));
+ if (cfg->rgctx_var) {
+ if (IS_LLVM_MONO_BRANCH)
+ linfo->rgctx_arg = TRUE;
+ else
+ LLVM_FAILURE (ctx, "rgctx arg");
+ }
+ method_type = sig_to_llvm_sig_full (ctx, sig, linfo, &sinfo);
+ CHECK_FAILURE (ctx);
+
+ /*
+ * This maps parameter indexes in the original signature to the indexes in
+ * the LLVM signature.
+ */
+ ctx->pindexes = sinfo.pindexes;
+
+ method = LLVMAddFunction (module, method_name, method_type);
+ ctx->lmethod = method;
+
+#ifdef LLVM_MONO_BRANCH
+ LLVMSetFunctionCallConv (method, LLVMMono1CallConv);
#endif
- }
+ LLVMSetLinkage (method, LLVMPrivateLinkage);
- mono_memory_barrier ();
- ctx->lmodule->throw = callee;
- }
- arg = convert (ctx, values [ins->sreg1], type_to_llvm_type (ctx, &mono_defaults.object_class->byval_arg));
- emit_call (ctx, bb, &builder, ctx->lmodule->throw, &arg, 1);
- 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 finally_bb, noex_bb;
- GSList *bb_list;
+ LLVMAddFunctionAttr (method, LLVMUWTableAttribute);
+
+ if (cfg->compile_aot) {
+ LLVMSetLinkage (method, LLVMInternalLinkage);
+ LLVMSetVisibility (method, LLVMHiddenVisibility);
+ } else {
+ LLVMSetLinkage (method, LLVMPrivateLinkage);
+ }
- finally_bb = get_bb (ctx, ins->inst_target_bb);
+ if (cfg->method->save_lmf)
+ LLVM_FAILURE (ctx, "lmf");
- bb_list = bblocks [ins->inst_target_bb->block_num].call_handler_return_bbs;
+ if (sig->pinvoke && cfg->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
+ LLVM_FAILURE (ctx, "pinvoke signature");
- /*
- * Set the indicator variable for the finally clause.
- */
- lhs = bblocks [ins->inst_target_bb->block_num].finally_ind;
- g_assert (lhs);
- LLVMBuildStore (builder, LLVMConstInt (LLVMInt32Type (), g_slist_length (bb_list) + 1, FALSE), lhs);
-
- /* Branch to the finally clause */
- LLVMBuildBr (builder, finally_bb);
+ 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_NONE)
+ LLVM_FAILURE (ctx, "non-finally/catch clause.");
+ }
+
+ if (linfo->rgctx_arg) {
+ ctx->rgctx_arg = LLVMGetParam (method, sinfo.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.
+ */
+ LLVMAddAttribute (ctx->rgctx_arg, LLVMInRegAttribute);
+ LLVMSetValueName (ctx->rgctx_arg, "rgctx");
+ }
+ if (cfg->vret_addr) {
+ values [cfg->vret_addr->dreg] = LLVMGetParam (method, sinfo.vret_arg_pindex);
+ LLVMSetValueName (values [cfg->vret_addr->dreg], "vret");
+ }
+ if (sig->hasthis) {
+ values [cfg->args [0]->dreg] = LLVMGetParam (method, sinfo.this_arg_pindex);
+ LLVMSetValueName (values [cfg->args [0]->dreg], "this");
+ }
- noex_bb = gen_bb (ctx, "CALL_HANDLER_CONT_BB");
- // FIXME: Use a mempool
- bblocks [ins->inst_target_bb->block_num].call_handler_return_bbs = g_slist_append (bblocks [ins->inst_target_bb->block_num].call_handler_return_bbs, noex_bb);
+ names = g_new (char *, sig->param_count);
+ mono_method_get_param_names (cfg->method, (const char **) names);
- builder = ctx->builder = create_builder (ctx);
- LLVMPositionBuilderAtEnd (ctx->builder, noex_bb);
+ for (i = 0; i < sig->param_count; ++i) {
+ char *name;
+
+ values [cfg->args [i + sig->hasthis]->dreg] = LLVMGetParam (method, sinfo.pindexes [i]);
+ 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 (linfo->args [i + sig->hasthis].storage == LLVMArgVtypeByVal)
+ LLVMAddAttribute (LLVMGetParam (method, sinfo.pindexes [i]), LLVMByValAttribute);
+ }
+ g_free (names);
- 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;
- GSList *bb_list;
+ 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 = 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);
+ }
+ }
+
+ /*
+ * The INDIRECT flag added by OP_LDADDR inhibits optimizations, even if the LDADDR
+ * was later optimized away, so clear these flags, and add them back for the still
+ * present OP_LDADDR instructions.
+ */
+ for (i = 0; i < cfg->next_vreg; ++i) {
+ MonoInst *ins;
- handler_bb = g_hash_table_lookup (ctx->region_to_handler, GUINT_TO_POINTER (mono_get_block_region_notry (cfg, bb->region)));
- g_assert (handler_bb);
- lhs = bblocks [handler_bb->block_num].finally_ind;
- g_assert (lhs);
+ ins = get_vreg_to_inst (cfg, i);
+ if (ins && ins != cfg->rgctx_var)
+ ins->flags &= ~MONO_INST_INDIRECT;
+ }
+
+ /*
+ * 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];
- bb_list = bblocks [handler_bb->block_num].call_handler_return_bbs;
+ builder = create_builder (ctx);
- resume_bb = gen_bb (ctx, "ENDFINALLY_RESUME_BB");
+ 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 (type_to_llvm_type (ctx, &ins->klass->byval_arg));
- /* Load the finally variable */
- val = LLVMBuildLoad (builder, lhs, "");
+ CHECK_FAILURE (ctx);
- /* Reset the variable */
- LLVMBuildStore (builder, LLVMConstInt (LLVMInt32Type (), 0, FALSE), lhs);
+ 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);
+ }
- /* 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.
+ * Have to precreate these, as they can be referenced by
+ * earlier instructions.
*/
- bblocks [handler_bb->block_num].endfinally_switch = switch_ins;
- /*
- for (i = 0; i < g_slist_length (bb_list); ++i)
- LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), i + 1, FALSE), g_slist_nth (bb_list, i)->data);
- */
+ sprintf (dname_buf, "t%d", ins->dreg);
+ dname = dname_buf;
+ values [ins->dreg] = LLVMBuildPhi (builder, phi_type, dname);
- builder = ctx->builder = create_builder (ctx);
- LLVMPositionBuilderAtEnd (ctx->builder, resume_bb);
+ if (ins->opcode == OP_VPHI)
+ ctx->addresses [ins->dreg] = values [ins->dreg];
- LLVMBuildCall (builder, LLVMGetNamedFunction (module, "mono_resume_unwind"), NULL, 0, "");
- LLVMBuildUnreachable (builder);
- has_terminator = TRUE;
- break;
- }
- default: {
- char reason [128];
+ g_ptr_array_add (phi_values, values [ins->dreg]);
- sprintf (reason, "opcode %s", mono_inst_name (ins->opcode));
- LLVM_FAILURE (ctx, reason);
+ /*
+ * 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)
+ ctx->vreg_types [sreg1] = phi_type;
+ }
+ break;
+ }
+ case OP_LDADDR:
+ ((MonoInst*)ins->inst_p0)->flags |= MONO_INST_INDIRECT;
+ break;
+ default:
break;
}
- }
-
- /* Convert the value to the type required by phi nodes */
- if (spec [MONO_INST_DEST] != ' ' && !MONO_IS_STORE_MEMBASE (ins) && vreg_types [ins->dreg]) {
- if (!values [ins->dreg])
- /* vtypes */
- values [ins->dreg] = addresses [ins->dreg];
- else
- values [ins->dreg] = convert (ctx, values [ins->dreg], 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);
+ /*
+ * 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);
+ }
- if (!has_terminator && bb->next_bb && (bb == cfg->bb_entry || bb->in_count > 0))
- LLVMBuildBr (builder, get_bb (ctx, bb->next_bb));
+ /*
+ * Second pass: generate code.
+ */
+ for (bb_index = 0; bb_index < bblock_list->len; ++bb_index) {
+ bb = g_ptr_array_index (bblock_list, bb_index);
- if (bb == cfg->bb_exit && sig->ret->type == MONO_TYPE_VOID)
- LLVMBuildRetVoid (builder);
+ if (!(bb == cfg->bb_entry || bb->in_count > 0))
+ continue;
- if (bb == cfg->bb_entry)
- ctx->last_alloca = LLVMGetLastInstruction (get_bb (ctx, cfg->bb_entry));
+ process_bb (ctx, bb);
+ CHECK_FAILURE (ctx);
}
/* Add incoming phi values */
in_bb = get_end_bb (ctx, node->in_bb);
- if (unreachable [node->in_bb->block_num])
+ if (ctx->unreachable [node->in_bb->block_num])
continue;
g_assert (values [sreg1]);
- g_assert (LLVMTypeOf (values [sreg1]) == LLVMTypeOf (values [phi->dreg]));
- LLVMAddIncoming (values [phi->dreg], &values [sreg1], &in_bb, 1);
+ 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 {
+ g_assert (LLVMTypeOf (values [sreg1]) == LLVMTypeOf (values [phi->dreg]));
+ LLVMAddIncoming (values [phi->dreg], &values [sreg1], &in_bb, 1);
+ }
}
}
/* Create the SWITCH statements for ENDFINALLY instructions */
for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
- if (bblocks [bb->block_num].endfinally_switch) {
- LLVMValueRef switch_ins = bblocks [bb->block_num].endfinally_switch;
- GSList *bb_list = bblocks [bb->block_num].call_handler_return_bbs;
+ BBInfo *info = &bblocks [bb->block_num];
+ GSList *l;
+ for (l = info->endfinally_switch_ins_list; l; l = l->next) {
+ LLVMValueRef switch_ins = l->data;
+ GSList *bb_list = info->call_handler_return_bbs;
for (i = 0; i < g_slist_length (bb_list); ++i)
LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), i + 1, FALSE), g_slist_nth (bb_list, i)->data);
if (cfg->compile_aot) {
/* Don't generate native code, keep the LLVM IR */
-
- /* Can't delete the method if it has an alias, so only add it if successful */
- if (debug_name) {
- debug_alias = LLVMAddAlias (module, LLVMTypeOf (method), method, debug_name);
- LLVMSetLinkage (debug_alias, LLVMInternalLinkage);
- LLVMSetVisibility (debug_alias, LLVMHiddenVisibility);
- }
-
if (cfg->compile_aot && cfg->verbose_level)
printf ("%s emitted as %s\n", mono_method_full_name (cfg->method, TRUE), method_name);
CLEANUP:
g_free (values);
- g_free (addresses);
- g_free (vreg_types);
- g_free (vreg_cli_types);
- g_free (pindexes);
- g_free (debug_name);
+ g_free (ctx->addresses);
+ g_free (ctx->vreg_types);
+ g_free (ctx->vreg_cli_types);
+ g_free (ctx->pindexes);
+ g_free (ctx->is_dead);
+ g_free (ctx->unreachable);
g_ptr_array_free (phi_values, TRUE);
g_free (ctx->bblocks);
g_hash_table_destroy (ctx->region_to_handler);
g_free (ctx);
- TlsSetValue (current_cfg_tls_id, NULL);
+ mono_native_tls_set_value (current_cfg_tls_id, NULL);
mono_loader_unlock ();
}
{
MonoCompile *cfg;
- cfg = TlsGetValue (current_cfg_tls_id);
+ cfg = mono_native_tls_get_value (current_cfg_tls_id);
if (cfg) {
// FIXME: dynamic
{
MonoCompile *cfg;
- cfg = TlsGetValue (current_cfg_tls_id);
+ cfg = mono_native_tls_get_value (current_cfg_tls_id);
g_assert (cfg);
cfg->code_len = (guint8*)end - (guint8*)start;
}
{
MonoCompile *cfg;
MonoJitExceptionInfo *ei;
- guint32 ei_len, i;
+ guint32 ei_len, i, j, nested_len, nindex;
gpointer *type_info;
+ int this_reg, this_offset;
- cfg = TlsGetValue (current_cfg_tls_id);
+ cfg = mono_native_tls_get_value (current_cfg_tls_id);
g_assert (cfg);
/*
* 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);
+ 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);
+
+ /* Count nested clauses */
+ nested_len = 0;
+ for (i = 0; i < ei_len; ++i) {
+ for (j = 0; j < ei_len; ++j) {
+ gint32 cindex1 = *(gint32*)type_info [i];
+ MonoExceptionClause *clause1 = &cfg->header->clauses [cindex1];
+ gint32 cindex2 = *(gint32*)type_info [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 = mono_mempool_alloc0 (cfg->mempool, ei_len * sizeof (MonoJitExceptionInfo));
- cfg->llvm_ex_info_len = ei_len;
+ cfg->llvm_ex_info = 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) {
cfg->llvm_ex_info [i].data.catch_class = clause->data.catch_class;
}
+ /*
+ * 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.
+ */
+ /* FIXME: These should be order with the normal clauses */
+ nindex = ei_len;
+ for (i = 0; i < ei_len; ++i) {
+ for (j = 0; j < ei_len; ++j) {
+ gint32 cindex1 = *(gint32*)type_info [i];
+ MonoExceptionClause *clause1 = &cfg->header->clauses [cindex1];
+ gint32 cindex2 = *(gint32*)type_info [j];
+ MonoExceptionClause *clause2 = &cfg->header->clauses [cindex2];
+
+ if (cindex1 != cindex2 && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset) {
+ /*
+ * The try interval comes from the nested clause, everything else from the
+ * nesting clause.
+ */
+ memcpy (&cfg->llvm_ex_info [nindex], &cfg->llvm_ex_info [j], sizeof (MonoJitExceptionInfo));
+ cfg->llvm_ex_info [nindex].try_start = cfg->llvm_ex_info [i].try_start;
+ cfg->llvm_ex_info [nindex].try_end = cfg->llvm_ex_info [i].try_end;
+ nindex ++;
+ }
+ }
+ }
+ 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);
+}
+
+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);
}
static void
add_intrinsics (LLVMModuleRef module)
{
/* 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.
+ */
{
LLVMTypeRef memset_params [] = { LLVMPointerType (LLVMInt8Type (), 0), LLVMInt8Type (), LLVMInt32Type (), LLVMInt32Type (), LLVMInt1Type () };
-#if LLVM_MAJOR_VERSION > 2 || LLVM_MINOR_VERSION >= 8
memset_param_count = 5;
memset_func_name = "llvm.memset.p0i8.i32";
-#else
- memset_param_count = 4;
- memset_func_name = "llvm.memset.i32";
-#endif
+
LLVMAddFunction (module, memset_func_name, LLVMFunctionType (LLVMVoidType (), memset_params, memset_param_count, FALSE));
}
{
LLVMTypeRef memcpy_params [] = { LLVMPointerType (LLVMInt8Type (), 0), LLVMPointerType (LLVMInt8Type (), 0), LLVMInt32Type (), LLVMInt32Type (), LLVMInt1Type () };
-#if LLVM_MAJOR_VERSION > 2 || LLVM_MINOR_VERSION >= 8
memcpy_param_count = 5;
memcpy_func_name = "llvm.memcpy.p0i8.p0i8.i32";
-#else
- memcpy_param_count = 4;
- memcpy_func_name = "llvm.memcpy.i32";
-#endif
LLVMAddFunction (module, memcpy_func_name, LLVMFunctionType (LLVMVoidType (), memcpy_params, memcpy_param_count, FALSE));
}
arg_types [0] = LLVMPointerType (LLVMInt8Type (), 0);
arg_types [1] = LLVMPointerType (LLVMInt8Type (), 0);
-#if LLVM_MAJOR_VERSION > 2 || LLVM_MINOR_VERSION >= 8
eh_selector_name = "llvm.eh.selector";
ret_type = LLVMInt32Type ();
-#else
-#if SIZEOF_VOID_P == 8
- eh_selector_name = "llvm.eh.selector.i64";
- ret_type = LLVMInt64Type ();
-#else
- eh_selector_name = "llvm.eh.selector.i32";
- ret_type = LLVMInt32Type ();
-#endif
-#endif
+
LLVMAddFunction (module, eh_selector_name, LLVMFunctionType (ret_type, arg_types, 2, TRUE));
LLVMAddFunction (module, "llvm.eh.exception", LLVMFunctionType (LLVMPointerType (LLVMInt8Type (), 0), NULL, 0, FALSE));
LLVMAddFunction (module, "mono_personality", LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE));
- LLVMAddFunction (module, "mono_resume_unwind", LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE));
+ LLVMAddFunction (module, "llvm_resume_unwind_trampoline", LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE));
}
/* SSE intrinsics */
{
- LLVMTypeRef vector_type, arg_types [2];
-
- vector_type = LLVMVectorType (LLVMInt32Type (), 4);
- arg_types [0] = vector_type;
- arg_types [1] = vector_type;
- LLVMAddFunction (module, "llvm.x86.sse41.pminud", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
- LLVMAddFunction (module, "llvm.x86.sse41.pmaxud", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
-
- vector_type = LLVMVectorType (LLVMInt16Type (), 8);
- arg_types [0] = vector_type;
- arg_types [1] = vector_type;
- LLVMAddFunction (module, "llvm.x86.sse41.pminuw", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
- LLVMAddFunction (module, "llvm.x86.sse41.pmaxuw", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
-
- vector_type = LLVMVectorType (LLVMInt8Type (), 16);
- arg_types [0] = vector_type;
- arg_types [1] = vector_type;
- LLVMAddFunction (module, "llvm.x86.sse2.pminu.b", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
- LLVMAddFunction (module, "llvm.x86.sse2.pmaxu.b", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
-
- vector_type = LLVMVectorType (LLVMDoubleType (), 2);
- arg_types [0] = vector_type;
- arg_types [1] = vector_type;
- LLVMAddFunction (module, "llvm.x86.sse2.min.pd", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
- LLVMAddFunction (module, "llvm.x86.sse2.max.pd", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
-
- vector_type = LLVMVectorType (LLVMFloatType (), 4);
- arg_types [0] = vector_type;
- arg_types [1] = vector_type;
- LLVMAddFunction (module, "llvm.x86.sse2.min.ps", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
- LLVMAddFunction (module, "llvm.x86.sse2.max.ps", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
+ LLVMTypeRef ret_type, arg_types [2];
+
+ /* Binary ops */
+ ret_type = type_to_simd_type (MONO_TYPE_I4);
+ arg_types [0] = ret_type;
+ arg_types [1] = ret_type;
+ AddFunc (module, "llvm.x86.sse41.pminud", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse41.pmaxud", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pcmpeq.d", ret_type, arg_types, 2);
+
+ ret_type = type_to_simd_type (MONO_TYPE_I2);
+ arg_types [0] = ret_type;
+ arg_types [1] = ret_type;
+ AddFunc (module, "llvm.x86.sse41.pminuw", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pmins.w", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse41.pmaxuw", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pcmpeq.w", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.padds.w", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.psubs.w", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.paddus.w", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.psubus.w", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pavg.w", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pmulh.w", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pmulhu.w", ret_type, arg_types, 2);
+
+ ret_type = type_to_simd_type (MONO_TYPE_I1);
+ arg_types [0] = ret_type;
+ arg_types [1] = ret_type;
+ AddFunc (module, "llvm.x86.sse2.pminu.b", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pmaxu.b", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pcmpeq.b", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pcmpgt.b", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.padds.b", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.psubs.b", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.paddus.b", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.psubus.b", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pavg.b", ret_type, arg_types, 2);
+
+ ret_type = type_to_simd_type (MONO_TYPE_I8);
+ arg_types [0] = ret_type;
+ arg_types [1] = ret_type;
+ AddFunc (module, "llvm.x86.sse41.pcmpeqq", ret_type, arg_types, 2);
+
+ ret_type = type_to_simd_type (MONO_TYPE_R8);
+ arg_types [0] = ret_type;
+ arg_types [1] = ret_type;
+ AddFunc (module, "llvm.x86.sse2.min.pd", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.max.pd", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse3.hadd.pd", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse3.hsub.pd", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse3.addsub.pd", ret_type, arg_types, 2);
+
+ ret_type = type_to_simd_type (MONO_TYPE_R4);
+ arg_types [0] = ret_type;
+ arg_types [1] = ret_type;
+ AddFunc (module, "llvm.x86.sse.min.ps", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse.max.ps", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse3.hadd.ps", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse3.hsub.ps", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse3.addsub.ps", ret_type, arg_types, 2);
+
+ /* 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, "llvm.x86.sse2.packsswb.128", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.packuswb.128", ret_type, arg_types, 2);
+ 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, "llvm.x86.sse2.packssdw.128", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse41.packusdw", ret_type, arg_types, 2);
+
+ /* 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, "llvm.x86.sse2.cmp.pd", ret_type, arg_types, 3);
+ 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, "llvm.x86.sse.cmp.ps", ret_type, arg_types, 3);
+
+ /* Conversion ops */
+ ret_type = type_to_simd_type (MONO_TYPE_R8);
+ arg_types [0] = type_to_simd_type (MONO_TYPE_I4);
+ AddFunc (module, "llvm.x86.sse2.cvtdq2pd", ret_type, arg_types, 1);
+ ret_type = type_to_simd_type (MONO_TYPE_R4);
+ arg_types [0] = type_to_simd_type (MONO_TYPE_I4);
+ AddFunc (module, "llvm.x86.sse2.cvtdq2ps", ret_type, arg_types, 1);
+ ret_type = type_to_simd_type (MONO_TYPE_I4);
+ arg_types [0] = type_to_simd_type (MONO_TYPE_R8);
+ AddFunc (module, "llvm.x86.sse2.cvtpd2dq", ret_type, arg_types, 1);
+ ret_type = type_to_simd_type (MONO_TYPE_I4);
+ arg_types [0] = type_to_simd_type (MONO_TYPE_R4);
+ AddFunc (module, "llvm.x86.sse2.cvtps2dq", ret_type, arg_types, 1);
+ ret_type = type_to_simd_type (MONO_TYPE_R4);
+ arg_types [0] = type_to_simd_type (MONO_TYPE_R8);
+ AddFunc (module, "llvm.x86.sse2.cvtpd2ps", ret_type, arg_types, 1);
+ ret_type = type_to_simd_type (MONO_TYPE_R8);
+ arg_types [0] = type_to_simd_type (MONO_TYPE_R4);
+ AddFunc (module, "llvm.x86.sse2.cvtps2pd", ret_type, arg_types, 1);
+
+ ret_type = type_to_simd_type (MONO_TYPE_I4);
+ arg_types [0] = type_to_simd_type (MONO_TYPE_R8);
+ AddFunc (module, "llvm.x86.sse2.cvttpd2dq", ret_type, arg_types, 1);
+ ret_type = type_to_simd_type (MONO_TYPE_I4);
+ arg_types [0] = type_to_simd_type (MONO_TYPE_R4);
+ AddFunc (module, "llvm.x86.sse2.cvttps2dq", ret_type, arg_types, 1);
+
+ /* Unary ops */
+ ret_type = type_to_simd_type (MONO_TYPE_R8);
+ arg_types [0] = ret_type;
+ AddFunc (module, "llvm.x86.sse2.sqrt.pd", ret_type, arg_types, 1);
+ ret_type = type_to_simd_type (MONO_TYPE_R4);
+ arg_types [0] = ret_type;
+ AddFunc (module, "llvm.x86.sse.sqrt.ps", ret_type, arg_types, 1);
+ ret_type = type_to_simd_type (MONO_TYPE_R4);
+ arg_types [0] = ret_type;
+ AddFunc (module, "llvm.x86.sse.rsqrt.ps", ret_type, arg_types, 1);
+ ret_type = type_to_simd_type (MONO_TYPE_R4);
+ arg_types [0] = ret_type;
+ AddFunc (module, "llvm.x86.sse.rcp.ps", ret_type, arg_types, 1);
+
+ /* shifts */
+ ret_type = type_to_simd_type (MONO_TYPE_I2);
+ arg_types [0] = ret_type;
+ arg_types [1] = LLVMInt32Type ();
+ AddFunc (module, "llvm.x86.sse2.psrli.w", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.psrai.w", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pslli.w", ret_type, arg_types, 2);
+ ret_type = type_to_simd_type (MONO_TYPE_I4);
+ arg_types [0] = ret_type;
+ arg_types [1] = LLVMInt32Type ();
+ AddFunc (module, "llvm.x86.sse2.psrli.d", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.psrai.d", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pslli.d", ret_type, arg_types, 2);
+ ret_type = type_to_simd_type (MONO_TYPE_I8);
+ arg_types [0] = ret_type;
+ arg_types [1] = LLVMInt32Type ();
+ AddFunc (module, "llvm.x86.sse2.psrli.q", ret_type, arg_types, 2);
+ AddFunc (module, "llvm.x86.sse2.pslli.q", ret_type, arg_types, 2);
+
+ /* pmovmskb */
+ ret_type = LLVMInt32Type ();
+ arg_types [0] = type_to_simd_type (MONO_TYPE_I1);
+ AddFunc (module, "llvm.x86.sse2.pmovmskb.128", ret_type, arg_types, 1);
+ }
+
+ if (IS_LLVM_MONO_BRANCH) {
+ AddFunc (module, "llvm.x86.sse2.pause", LLVMVoidType (), NULL, 0);
+ }
+
+ /* Load/Store intrinsics */
+ if (IS_LLVM_MONO_BRANCH) {
+ 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 ();
+ sprintf (name, "llvm.mono.load.i%d.p0i%d", i * 8, i * 8);
+ LLVMAddFunction (module, name, LLVMFunctionType (LLVMIntType (i * 8), arg_types, 3, FALSE));
+
+ arg_types [0] = LLVMIntType (i * 8);
+ arg_types [1] = LLVMPointerType (LLVMIntType (i * 8), 0);
+ arg_types [2] = LLVMInt32Type ();
+ arg_types [3] = LLVMInt1Type ();
+ sprintf (name, "llvm.mono.store.i%d.p0i%d", i * 8, i * 8);
+ LLVMAddFunction (module, name, LLVMFunctionType (LLVMVoidType (), arg_types, 4, FALSE));
+ }
}
}
void
mono_llvm_init (void)
{
- current_cfg_tls_id = TlsAlloc ();
+ mono_native_tls_alloc (¤t_cfg_tls_id, NULL);
}
static void
init_jit_module (void)
{
+ MonoJitICallInfo *info;
+
if (jit_module_inited)
return;
jit_module.llvm_types = g_hash_table_new (NULL, NULL);
- LLVMAddGlobalMapping (ee, LLVMGetNamedFunction (jit_module.module, "mono_resume_unwind"), mono_resume_unwind);
+ info = mono_find_jit_icall_by_name ("llvm_resume_unwind_trampoline");
+ g_assert (info);
+ LLVMAddGlobalMapping (ee, LLVMGetNamedFunction (jit_module.module, "llvm_resume_unwind_trampoline"), (void*)info->func);
jit_module_inited = TRUE;
if (jit_module.llvm_types)
g_hash_table_destroy (jit_module.llvm_types);
+
+ if (aot_module.module)
+ LLVMDisposeModule (aot_module.module);
+
+ LLVMContextDispose (LLVMGetGlobalContext ());
}
void
/* Delete previous module */
if (aot_module.plt_entries)
g_hash_table_destroy (aot_module.plt_entries);
+ if (aot_module.module)
+ LLVMDisposeModule (aot_module.module);
memset (&aot_module, 0, sizeof (aot_module));
- 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.
- FIXME:
- - llvm's PrettyStackTrace class seems to register a signal handler which screws
- up our GC. Also, it calls sigaction () a _lot_ of times instead of just once.
*/
/*
/* 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
- * - merge some changes back to HEAD, to reduce the differences.
* - avoid some transformations in the JIT which make it harder for us to generate
* code.
- * - fix memory leaks.
* - use pointer types to help optimizations.
*/