#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mempool-internals.h>
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+
#include "llvm-c/Core.h"
#include "llvm-c/ExecutionEngine.h"
+#include "llvm-c/BitWriter.h"
+#include "llvm-c/Analysis.h"
#include "mini-llvm-cpp.h"
+ /*
+ * Information associated by mono with LLVM modules.
+ */
+typedef struct {
+ LLVMModuleRef module;
+ LLVMValueRef throw, throw_corlib_exception;
+ GHashTable *llvm_types;
+ LLVMValueRef got_var;
+ const char *got_symbol;
+ GHashTable *plt_entries;
+} MonoLLVMModule;
+
+/*
+ * Information associated by the backend with mono basic blocks.
+ */
+typedef struct {
+ LLVMBasicBlockRef bblock, end_bblock;
+ LLVMValueRef finally_ind;
+ gboolean added, invoke_target;
+ /*
+ * If this bblock is the start of a finally clause, this is a list of bblocks it
+ * needs to branch to in ENDFINALLY.
+ */
+ GSList *call_handler_return_bbs;
+ LLVMValueRef endfinally_switch;
+ GSList *phi_nodes;
+} BBInfo;
+
/*
* Structure containing emit state
*/
typedef struct {
MonoMemPool *mempool;
- LLVMValueRef got_var;
-
/* Maps method names to the corresponding LLVMValueRef */
GHashTable *emitted_method_decls;
MonoCompile *cfg;
LLVMValueRef lmethod;
- LLVMBasicBlockRef *bblocks, *end_bblocks;
+ MonoLLVMModule *lmodule;
+ LLVMModuleRef module;
+ BBInfo *bblocks;
int sindex, default_index, ex_index;
LLVMBuilderRef builder;
LLVMValueRef *values, *addresses;
+ MonoType **vreg_cli_types;
LLVMCallInfo *linfo;
MonoMethodSignature *sig;
GSList *builders;
+ GHashTable *region_to_handler;
char temp_name [32];
} EmitContext;
typedef struct {
MonoBasicBlock *bb;
MonoInst *phi;
- int index;
+ MonoBasicBlock *in_bb;
+ int sreg;
} PhiNode;
/*
#undef MINI_OP
#undef MINI_OP3
+#if SIZEOF_VOID_P == 4
+#define GET_LONG_IMM(ins) (((guint64)(ins)->inst_ms_word << 32) | (guint64)(guint32)(ins)->inst_ls_word)
+#else
+#define GET_LONG_IMM(ins) ((ins)->inst_imm)
+#endif
+
#define LLVM_INS_INFO(opcode) (&llvm_ins_info [((opcode) - OP_START - 1) * 4])
+#if 0
+#define TRACE_FAILURE(msg) do { printf ("%s\n", msg); } while (0)
+#else
+#define TRACE_FAILURE(msg)
+#endif
+
#define LLVM_FAILURE(ctx, reason) do { \
+ TRACE_FAILURE (reason); \
(ctx)->cfg->exception_message = g_strdup (reason); \
(ctx)->cfg->disable_llvm = TRUE; \
goto FAILURE; \
LLVMRealUGT,
};
-static LLVMModuleRef module;
static LLVMExecutionEngineRef ee;
-static GHashTable *llvm_types;
static guint32 current_cfg_tls_id;
-static LLVMValueRef throw, throw_corlib_exception;
+static MonoLLVMModule jit_module, aot_module;
/*
* IntPtrType:
return size;
}
+/*
+ * simd_class_to_llvm_type:
+ *
+ * Return the LLVM type corresponding to the Mono.SIMD class KLASS
+ */
+static LLVMTypeRef
+simd_class_to_llvm_type (EmitContext *ctx, MonoClass *klass)
+{
+ if (!strcmp (klass->name, "Vector2d")) {
+ return LLVMVectorType (LLVMDoubleType (), 2);
+ } else if (!strcmp (klass->name, "Vector2l")) {
+ return LLVMVectorType (LLVMInt64Type (), 2);
+ } else if (!strcmp (klass->name, "Vector2ul")) {
+ return LLVMVectorType (LLVMInt64Type (), 2);
+ } else if (!strcmp (klass->name, "Vector4i")) {
+ return LLVMVectorType (LLVMInt32Type (), 4);
+ } else if (!strcmp (klass->name, "Vector4ui")) {
+ return LLVMVectorType (LLVMInt32Type (), 4);
+ } else if (!strcmp (klass->name, "Vector4f")) {
+ return LLVMVectorType (LLVMFloatType (), 4);
+ } else if (!strcmp (klass->name, "Vector8s")) {
+ return LLVMVectorType (LLVMInt16Type (), 8);
+ } else if (!strcmp (klass->name, "Vector8us")) {
+ return LLVMVectorType (LLVMInt16Type (), 8);
+ } else if (!strcmp (klass->name, "Vector16sb")) {
+ return LLVMVectorType (LLVMInt8Type (), 16);
+ } else if (!strcmp (klass->name, "Vector16b")) {
+ return LLVMVectorType (LLVMInt8Type (), 16);
+ } else {
+ printf ("%s\n", klass->name);
+ NOT_IMPLEMENTED;
+ return NULL;
+ }
+}
+
/*
* type_to_llvm_type:
*
if (!mono_type_generic_inst_is_valuetype (t))
return IntPtrType ();
/* Fall through */
- case MONO_TYPE_VALUETYPE: {
+ case MONO_TYPE_VALUETYPE:
+ case MONO_TYPE_TYPEDBYREF: {
MonoClass *klass;
LLVMTypeRef ltype;
klass = mono_class_from_mono_type (t);
+ if (MONO_CLASS_IS_SIMD (ctx->cfg, klass))
+ return simd_class_to_llvm_type (ctx, klass);
+
if (klass->enumtype)
- return type_to_llvm_type (ctx, mono_class_enum_basetype (t->data.klass));
- ltype = g_hash_table_lookup (llvm_types, klass);
+ return type_to_llvm_type (ctx, mono_class_enum_basetype (klass));
+ ltype = g_hash_table_lookup (ctx->lmodule->llvm_types, klass);
if (!ltype) {
int i, size;
LLVMTypeRef *eltypes;
eltypes [i] = LLVMInt8Type ();
ltype = LLVMStructType (eltypes, size, FALSE);
- g_hash_table_insert (llvm_types, klass, ltype);
+ g_hash_table_insert (ctx->lmodule->llvm_types, klass, ltype);
g_free (eltypes);
}
return ltype;
}
default:
+ printf ("X: %d\n", t->type);
ctx->cfg->exception_message = g_strdup_printf ("type %s", mono_type_full_name (t));
ctx->cfg->disable_llvm = TRUE;
return NULL;
}
}
+/*
+ * type_is_unsigned:
+ *
+ * Return whenever T is an unsigned int type.
+ */
+static gboolean
+type_is_unsigned (EmitContext *ctx, MonoType *t)
+{
+ if (t->byref)
+ return FALSE;
+ switch (t->type) {
+ case MONO_TYPE_U1:
+ case MONO_TYPE_U2:
+ case MONO_TYPE_U4:
+ case MONO_TYPE_U8:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
/*
* type_to_llvm_arg_type:
*
}
/*
- * conv_to_llvm_type:
+ * op_to_llvm_type:
*
- * Return the LLVM type corresponding to the conversion opcode OPCODE.
+ * Return the LLVM type corresponding to the unary/binary opcode OPCODE.
*/
static LLVMTypeRef
-conv_to_llvm_type (int opcode)
+op_to_llvm_type (int opcode)
{
switch (opcode) {
case OP_ICONV_TO_I1:
case OP_FCONV_TO_I:
case OP_FCONV_TO_U:
return sizeof (gpointer) == 8 ? LLVMInt64Type () : LLVMInt32Type ();
+ case OP_IADD_OVF:
+ case OP_IADD_OVF_UN:
+ case OP_ISUB_OVF:
+ case OP_ISUB_OVF_UN:
+ case OP_IMUL_OVF:
+ case OP_IMUL_OVF_UN:
+ return LLVMInt32Type ();
+ case OP_LADD_OVF:
+ case OP_LSUB_OVF:
+ case OP_LSUB_OVF_UN:
+ case OP_LMUL_OVF:
+ case OP_LMUL_OVF_UN:
+ return LLVMInt64Type ();
default:
printf ("%s\n", mono_inst_name (opcode));
g_assert_not_reached ();
*sext = TRUE;
return LLVMInt8Type ();
case OP_LOADU1_MEMBASE:
+ case OP_LOADU1_MEM:
*size = 1;
*zext = TRUE;
return LLVMInt8Type ();
*sext = TRUE;
return LLVMInt16Type ();
case OP_LOADU2_MEMBASE:
+ case OP_LOADU2_MEM:
*size = 2;
*zext = TRUE;
return LLVMInt16Type ();
}
}
+static const char*
+simd_op_to_intrins (int opcode)
+{
+ switch (opcode) {
+ case OP_MINPD:
+ return "llvm.x86.sse2.min.pd";
+ case OP_MINPS:
+ return "llvm.x86.sse2.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.sse41.pminub";
+ case OP_MAXPD:
+ return "llvm.x86.sse2.max.pd";
+ case OP_MAXPS:
+ return "llvm.x86.sse2.max.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.sse41.pmaxub";
+ default:
+ g_assert_not_reached ();
+ return NULL;
+ }
+}
+
/*
* get_bb:
*
{
char bb_name [128];
- if (ctx->bblocks [bb->block_num] == NULL) {
+ if (ctx->bblocks [bb->block_num].bblock == NULL) {
sprintf (bb_name, "BB%d", bb->block_num);
- ctx->bblocks [bb->block_num] = LLVMAppendBasicBlock (ctx->lmethod, bb_name);
- ctx->end_bblocks [bb->block_num] = ctx->bblocks [bb->block_num];
+ ctx->bblocks [bb->block_num].bblock = LLVMAppendBasicBlock (ctx->lmethod, bb_name);
+ ctx->bblocks [bb->block_num].end_bblock = ctx->bblocks [bb->block_num].bblock;
}
- return ctx->bblocks [bb->block_num];
+ return ctx->bblocks [bb->block_num].bblock;
}
/*
get_end_bb (EmitContext *ctx, MonoBasicBlock *bb)
{
get_bb (ctx, bb);
- return ctx->end_bblocks [bb->block_num];
+ return ctx->bblocks [bb->block_num].end_bblock;
}
-/*
- * get_tempname:
- *
- * Return a tempname required by the LLVM instruction builders.
- */
-static const char*
-get_tempname (EmitContext *ctx)
+static LLVMBasicBlockRef
+gen_bb (EmitContext *ctx, const char *prefix)
{
- // FIXME:
- sprintf (ctx->temp_name, "s%d", ctx->sindex ++);
-
- return ctx->temp_name;
+ char bb_name [128];
+
+ sprintf (bb_name, "%s%d", prefix, ++ ctx->ex_index);
+ return LLVMAppendBasicBlock (ctx->lmethod, bb_name);
}
/*
}
/*
- * convert:
+ * convert_full:
*
* Emit code to convert the LLVM value V to DTYPE.
*/
static LLVMValueRef
-convert (EmitContext *ctx, LLVMValueRef v, LLVMTypeRef dtype)
+convert_full (EmitContext *ctx, LLVMValueRef v, LLVMTypeRef dtype, gboolean is_unsigned)
{
LLVMTypeRef stype = LLVMTypeOf (v);
if (stype != dtype) {
+ gboolean ext = FALSE;
+
/* Extend */
if (dtype == LLVMInt64Type () && (stype == LLVMInt32Type () || stype == LLVMInt16Type () || stype == LLVMInt8Type ()))
- return LLVMBuildSExt (ctx->builder, v, dtype, get_tempname (ctx));
+ ext = TRUE;
else if (dtype == LLVMInt32Type () && (stype == LLVMInt16Type () || stype == LLVMInt8Type ()))
- return LLVMBuildSExt (ctx->builder, v, dtype, get_tempname (ctx));
+ ext = TRUE;
else if (dtype == LLVMInt16Type () && (stype == LLVMInt8Type ()))
- return LLVMBuildSExt (ctx->builder, v, dtype, get_tempname (ctx));
- else if (dtype == LLVMDoubleType () && stype == LLVMFloatType ())
- return LLVMBuildFPExt (ctx->builder, v, dtype, get_tempname (ctx));
+ ext = TRUE;
+
+ if (ext)
+ return is_unsigned ? LLVMBuildZExt (ctx->builder, v, dtype, "") : LLVMBuildSExt (ctx->builder, v, dtype, "");
+
+ if (dtype == LLVMDoubleType () && stype == LLVMFloatType ())
+ return LLVMBuildFPExt (ctx->builder, v, dtype, "");
/* Trunc */
if (stype == LLVMInt64Type () && (dtype == LLVMInt32Type () || dtype == LLVMInt16Type () || dtype == LLVMInt8Type ()))
- return LLVMBuildTrunc (ctx->builder, v, dtype, get_tempname (ctx));
+ return LLVMBuildTrunc (ctx->builder, v, dtype, "");
if (stype == LLVMInt32Type () && (dtype == LLVMInt16Type () || dtype == LLVMInt8Type ()))
- return LLVMBuildTrunc (ctx->builder, v, dtype, get_tempname (ctx));
+ return LLVMBuildTrunc (ctx->builder, v, dtype, "");
if (stype == LLVMDoubleType () && dtype == LLVMFloatType ())
- return LLVMBuildFPTrunc (ctx->builder, v, dtype, get_tempname (ctx));
+ return LLVMBuildFPTrunc (ctx->builder, v, dtype, "");
if (LLVMGetTypeKind (stype) == LLVMPointerTypeKind && LLVMGetTypeKind (dtype) == LLVMPointerTypeKind)
- return LLVMBuildBitCast (ctx->builder, v, dtype, get_tempname (ctx));
+ return LLVMBuildBitCast (ctx->builder, v, dtype, "");
if (LLVMGetTypeKind (dtype) == LLVMPointerTypeKind)
- return LLVMBuildIntToPtr (ctx->builder, v, dtype, get_tempname (ctx));
+ return LLVMBuildIntToPtr (ctx->builder, v, dtype, "");
if (LLVMGetTypeKind (stype) == LLVMPointerTypeKind)
- return LLVMBuildPtrToInt (ctx->builder, v, dtype, get_tempname (ctx));
+ return LLVMBuildPtrToInt (ctx->builder, v, dtype, "");
+
+#ifdef MONO_ARCH_SOFT_FLOAT
+ if (stype == LLVMInt32Type () && dtype == LLVMFloatType ())
+ return LLVMBuildBitCast (ctx->builder, v, dtype, "");
+ if (stype == LLVMInt32Type () && dtype == LLVMDoubleType ())
+ return LLVMBuildBitCast (ctx->builder, LLVMBuildZExt (ctx->builder, v, LLVMInt64Type (), ""), dtype, "");
+#endif
LLVMDumpValue (v);
LLVMDumpValue (LLVMConstNull (dtype));
}
}
+static LLVMValueRef
+convert (EmitContext *ctx, LLVMValueRef v, LLVMTypeRef dtype)
+{
+ return convert_full (ctx, v, dtype, FALSE);
+}
+
+/*
+ * emit_volatile_load:
+ *
+ * If vreg is volatile, emit a load from its address.
+ */
+static LLVMValueRef
+emit_volatile_load (EmitContext *ctx, int vreg)
+{
+ MonoType *t;
+
+ LLVMValueRef v = LLVMBuildLoad (ctx->builder, ctx->addresses [vreg], "");
+ t = ctx->vreg_cli_types [vreg];
+ if (t && !t->byref) {
+ /*
+ * Might have to zero extend since llvm doesn't have
+ * unsigned types.
+ */
+ if (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_U2)
+ v = LLVMBuildZExt (ctx->builder, v, LLVMInt32Type (), "");
+ else if (t->type == MONO_TYPE_U8)
+ v = LLVMBuildZExt (ctx->builder, v, LLVMInt64Type (), "");
+ }
+
+ return v;
+}
+
/*
* emit_volatile_store:
*
MonoInst *var = get_vreg_to_inst (ctx->cfg, vreg);
if (var && var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) {
+ g_assert (ctx->addresses [vreg]);
LLVMBuildStore (ctx->builder, convert (ctx, ctx->values [vreg], type_to_llvm_type (ctx, var->inst_vtype)), ctx->addresses [vreg]);
}
}
return builder;
}
+static LLVMValueRef
+get_plt_entry (EmitContext *ctx, LLVMTypeRef llvm_sig, MonoJumpInfoType type, gconstpointer data)
+{
+ char *callee_name = mono_aot_get_plt_symbol (type, data);
+ LLVMValueRef callee;
+
+ if (!callee_name)
+ return NULL;
+
+ // FIXME: Locking
+ callee = g_hash_table_lookup (ctx->lmodule->plt_entries, callee_name);
+ if (!callee) {
+ callee = LLVMAddFunction (ctx->module, callee_name, llvm_sig);
+
+ g_hash_table_insert (ctx->lmodule->plt_entries, (char*)callee_name, callee);
+ }
+
+ return callee;
+}
+
static void
emit_cond_throw_pos (EmitContext *ctx)
{
}
+/*
+ * emit_call:
+ *
+ * Emit an LLVM call or invoke instruction depending on whenever the call is inside
+ * a try region.
+ */
+static LLVMValueRef
+emit_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, LLVMValueRef callee, LLVMValueRef *args, int pindex)
+{
+ MonoCompile *cfg = ctx->cfg;
+ LLVMValueRef lcall;
+ LLVMBuilderRef builder = *builder_ref;
+
+ // FIXME: Nested clauses
+ if (bb->region && MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY)) {
+ MonoMethodHeader *header = mono_method_get_header (cfg->method);
+ // 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;
+
+ /*
+ * Have to use an invoke instead of a call, branching to the
+ * handler bblock of the clause containing this bblock.
+ */
+
+ g_assert (ec->flags == MONO_EXCEPTION_CLAUSE_NONE || ec->flags == MONO_EXCEPTION_CLAUSE_FINALLY);
+
+ tblock = cfg->cil_offset_to_bb [ec->handler_offset];
+ g_assert (tblock);
+
+ ctx->bblocks [tblock->block_num].invoke_target = TRUE;
+
+ ex_bb = get_bb (ctx, tblock);
+
+ noex_bb = gen_bb (ctx, "NOEX_BB");
+
+ /* Use an invoke */
+ lcall = LLVMBuildInvoke (builder, callee, args, pindex, noex_bb, ex_bb, "");
+
+ builder = ctx->builder = create_builder (ctx);
+ LLVMPositionBuilderAtEnd (ctx->builder, noex_bb);
+
+ ctx->bblocks [bb->block_num].end_bblock = noex_bb;
+ } else {
+ lcall = LLVMBuildCall (builder, callee, args, pindex, "");
+ ctx->builder = builder;
+ }
+
+ *builder_ref = ctx->builder;
+
+ return lcall;
+}
+
/*
* emit_cond_system_exception:
*
static void
emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *exc_type, LLVMValueRef cmp)
{
- char bb_name [128];
LLVMBasicBlockRef ex_bb, noex_bb;
LLVMBuilderRef builder;
MonoClass *exc_class;
LLVMValueRef args [2];
- sprintf (bb_name, "EX_BB%d", ctx->ex_index);
- ex_bb = LLVMAppendBasicBlock (ctx->lmethod, bb_name);
-
- sprintf (bb_name, "NOEX_BB%d", ctx->ex_index);
- noex_bb = LLVMAppendBasicBlock (ctx->lmethod, bb_name);
+ ex_bb = gen_bb (ctx, "EX_BB");
+ noex_bb = gen_bb (ctx, "NOEX_BB");
LLVMBuildCondBr (ctx->builder, cmp, ex_bb, noex_bb);
- ctx->builder = create_builder (ctx);
- LLVMPositionBuilderAtEnd (ctx->builder, noex_bb);
-
- ctx->end_bblocks [bb->block_num] = noex_bb;
-
exc_class = mono_class_from_name (mono_defaults.corlib, "System", exc_type);
g_assert (exc_class);
builder = create_builder (ctx);
LLVMPositionBuilderAtEnd (builder, ex_bb);
- if (!throw_corlib_exception) {
+ 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);
- callee = LLVMAddFunction (module, "throw_corlib_exception", sig_to_llvm_sig (ctx, throw_sig, NULL));
-
- LLVMAddGlobalMapping (ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_corlib_exception"));
+ if (ctx->cfg->compile_aot) {
+ callee = get_plt_entry (ctx, sig, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_corlib_exception");
+ } else {
+ callee = LLVMAddFunction (ctx->module, "throw_corlib_exception", sig_to_llvm_sig (ctx, throw_sig, NULL));
+
+ LLVMAddGlobalMapping (ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_corlib_exception"));
+ }
mono_memory_barrier ();
- throw_corlib_exception = callee;
+ ctx->lmodule->throw_corlib_exception = callee;
}
args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token, FALSE);
* a problem for line numbers in stack traces.
*/
args [1] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
- LLVMBuildCall (builder, throw_corlib_exception, args, 2, "");
+ emit_call (ctx, bb, &builder, ctx->lmodule->throw_corlib_exception, args, 2);
LLVMBuildUnreachable (builder);
+ ctx->builder = create_builder (ctx);
+ LLVMPositionBuilderAtEnd (ctx->builder, noex_bb);
+
+ ctx->bblocks [bb->block_num].end_bblock = noex_bb;
+
ctx->ex_index ++;
}
size = get_vtype_size (t);
+ if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
+ address = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (LLVMInt8Type (), 0), "");
+ }
+
for (j = 0; j < 2; ++j) {
LLVMValueRef index [2], addr;
int part_size = size > sizeof (gpointer) ? sizeof (gpointer) : size;
continue;
part_type = LLVMIntType (part_size * 8);
- index [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
- index [1] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
- addr = LLVMBuildGEP (builder, address, index, 2, get_tempname (ctx));
+ if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
+ index [0] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
+ addr = LLVMBuildGEP (builder, address, index, 1, "");
+ } else {
+ index [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
+ index [1] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
+ addr = LLVMBuildGEP (builder, address, index, 2, "");
+ }
switch (ainfo->pair_storage [j]) {
case LLVMArgInIReg:
- LLVMBuildStore (builder, convert (ctx, regs [j], part_type), LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (part_type, 0), get_tempname (ctx)));
+ LLVMBuildStore (builder, convert (ctx, regs [j], part_type), LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (part_type, 0), ""));
break;
case LLVMArgNone:
break;
size = get_vtype_size (t);
+ if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
+ address = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (LLVMInt8Type (), 0), "");
+ }
+
for (j = 0; j < 2; ++j) {
LLVMValueRef index [2], addr;
int partsize = size > sizeof (gpointer) ? sizeof (gpointer) : size;
if (ainfo->pair_storage [j] == LLVMArgNone)
continue;
- index [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
- index [1] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
- addr = LLVMBuildGEP (builder, address, index, 2, get_tempname (ctx));
+ if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
+ index [0] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
+ addr = LLVMBuildGEP (builder, address, index, 1, "");
+ } else {
+ index [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
+ index [1] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
+ addr = LLVMBuildGEP (builder, address, index, 2, "");
+ }
switch (ainfo->pair_storage [j]) {
case LLVMArgInIReg:
- regs [pindex ++] = convert (ctx, LLVMBuildLoad (builder, LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (LLVMIntType (partsize * 8), 0), get_tempname (ctx)), get_tempname (ctx)), IntPtrType ());
+ regs [pindex ++] = convert (ctx, LLVMBuildLoad (builder, LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (LLVMIntType (partsize * 8), 0), ""), ""), IntPtrType ());
break;
case LLVMArgNone:
break;
*nregs = pindex;
}
+static LLVMValueRef
+build_alloca (EmitContext *ctx, MonoType *t)
+{
+ MonoClass *k = mono_class_from_mono_type (t);
+ int align;
+
+ if (MONO_CLASS_IS_SIMD (ctx->cfg, k))
+ align = 16;
+ else
+ align = mono_class_min_align (k);
+
+ /* Sometimes align is not a power of 2 */
+ while (mono_is_power_of_two (align) == -1)
+ align ++;
+
+ return mono_llvm_build_alloca (ctx->builder, type_to_llvm_type (ctx, t), NULL, align, "");
+}
+
+/*
+ * Put the global into the 'llvm.used' array to prevent it from being optimized away.
+ */
+static void
+mark_as_used (LLVMModuleRef module, LLVMValueRef global)
+{
+ LLVMTypeRef used_type;
+ LLVMValueRef used, used_elem;
+
+ used_type = LLVMArrayType (LLVMPointerType (LLVMInt8Type (), 0), 1);
+ used = LLVMAddGlobal (module, used_type, "llvm.used");
+ used_elem = LLVMConstBitCast (global, LLVMPointerType (LLVMInt8Type (), 0));
+ LLVMSetInitializer (used, LLVMConstArray (LLVMPointerType (LLVMInt8Type (), 0), &used_elem, 1));
+ LLVMSetLinkage (used, LLVMAppendingLinkage);
+ LLVMSetSection (used, "llvm.metadata");
+}
+
/*
* emit_entry_bb:
*
MonoCompile *cfg = ctx->cfg;
MonoMethodSignature *sig = ctx->sig;
LLVMCallInfo *linfo = ctx->linfo;
+ MonoBasicBlock *bb;
/*
* Handle indirect/volatile variables by allocating memory for them
if (var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || MONO_TYPE_ISSTRUCT (var->inst_vtype)) {
vtype = type_to_llvm_type (ctx, var->inst_vtype);
CHECK_FAILURE (ctx);
- ctx->addresses [var->dreg] = LLVMBuildAlloca (builder, vtype, get_tempname (ctx));
+ /* Could be already created by an OP_VPHI */
+ if (!ctx->addresses [var->dreg])
+ ctx->addresses [var->dreg] = build_alloca (ctx, var->inst_vtype);
+ ctx->vreg_cli_types [var->dreg] = var->inst_vtype;
}
}
else
regs [1] = NULL;
- ctx->addresses [reg] = LLVMBuildAlloca (builder, type_to_llvm_type (ctx, &(mono_class_from_mono_type (sig->params [i])->byval_arg)), get_tempname (ctx));
+ ctx->addresses [reg] = build_alloca (ctx, sig->params [i]);
emit_reg_to_vtype (ctx, builder, sig->params [i], ctx->addresses [reg], ainfo, regs);
} else if (ainfo->storage == LLVMArgVtypeByVal) {
}
}
+ if (sig->hasthis)
+ emit_volatile_store (ctx, cfg->args [0]->dreg);
for (i = 0; i < sig->param_count; ++i)
if (!MONO_TYPE_ISSTRUCT (sig->params [i]))
emit_volatile_store (ctx, cfg->args [i + sig->hasthis]->dreg);
+ /*
+ * For finally clauses, create an indicator variable telling OP_ENDFINALLY whenever
+ * it needs to continue normally, or return back to the exception handling system.
+ */
+ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+ 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 (), "");
+ LLVMBuildStore (builder, LLVMConstInt (LLVMInt32Type (), 0, FALSE), val);
+
+ ctx->bblocks [bb->block_num].finally_ind = val;
+ }
+ }
+
FAILURE:
;
}
+
+static void
+mono_personality (void)
+{
+ /* Not used */
+ g_assert_not_reached ();
+}
/*
* mono_llvm_emit_method:
MonoMethodSignature *sig;
MonoBasicBlock *bb;
LLVMTypeRef method_type;
- LLVMValueRef method = NULL;
- char *method_name;
+ LLVMValueRef method = NULL, debug_alias = NULL;
+ char *method_name, *debug_name = NULL;
LLVMValueRef *values, *addresses;
LLVMTypeRef *vreg_types;
- int i, max_block_num, pindex;
+ MonoType **vreg_cli_types;
+ int i, max_block_num, pindex, bb_index;
int *pindexes = NULL;
- GHashTable *phi_nodes;
gboolean last = FALSE;
GPtrArray *phi_values;
LLVMCallInfo *linfo;
GSList *l;
+ LLVMModuleRef module;
+ gboolean *is_dead;
+ gboolean *unreachable;
+ BBInfo *bblocks;
+ GPtrArray *bblock_list;
+ MonoMethodHeader *header;
+ MonoExceptionClause *clause;
/* The code below might acquire the loader lock, so use it for global locking */
mono_loader_lock ();
*/
addresses = g_new0 (LLVMValueRef, cfg->next_vreg);
vreg_types = g_new0 (LLVMTypeRef, cfg->next_vreg);
- phi_nodes = g_hash_table_new (NULL, NULL);
+ 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);
+
+ bblock_list = g_ptr_array_new ();
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 {
+ ctx->lmodule = &jit_module;
+ method_name = mono_method_full_name (cfg->method, TRUE);
+ debug_name = NULL;
+ }
+
+ module = ctx->module = ctx->lmodule->module;
#if 1
{
method_type = sig_to_llvm_sig (ctx, sig, linfo);
CHECK_FAILURE (ctx);
- method_name = mono_method_full_name (cfg->method, TRUE);
method = LLVMAddFunction (module, method_name, method_type);
ctx->lmethod = method;
- g_free (method_name);
+
+ LLVMSetLinkage (method, LLVMPrivateLinkage);
if (cfg->method->save_lmf)
LLVM_FAILURE (ctx, "lmf");
if (sig->pinvoke)
LLVM_FAILURE (ctx, "pinvoke signature");
+ header = mono_method_get_header (cfg->method);
+ for (i = 0; i < header->num_clauses; ++i) {
+ clause = &header->clauses [i];
+ if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY)
+ LLVM_FAILURE (ctx, "non-finally clause.");
+ }
+
/*
* This maps parameter indexes in the original signature to the indexes in
* the LLVM signature.
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 = g_new0 (LLVMBasicBlockRef, max_block_num + 1);
- ctx->end_bblocks = g_new0 (LLVMBasicBlockRef, max_block_num + 1);
+ 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) {
for (ins = bb->code; ins; ins = ins->next) {
switch (ins->opcode) {
case OP_PHI:
- case OP_FPHI: {
+ 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));
CHECK_FAILURE (ctx);
+ if (ins->opcode == OP_VPHI) {
+ /* Treat valuetype PHI nodes as operating on the address itself */
+ g_assert (ins->klass);
+ phi_type = LLVMPointerType (type_to_llvm_type (ctx, &ins->klass->byval_arg), 0);
+ }
+
/*
* Have to precreate these, as they can be referenced by
* earlier instructions.
dname = dname_buf;
values [ins->dreg] = LLVMBuildPhi (builder, phi_type, dname);
+ if (ins->opcode == OP_VPHI)
+ addresses [ins->dreg] = values [ins->dreg];
+
g_ptr_array_add (phi_values, values [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 (bb->region != -1 && !MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY) && !bblocks [bb->block_num].added)
+ g_ptr_array_add (bblock_list, bb);
+ }
+
/*
* Second pass: generate code.
*/
- for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+ for (bb_index = 0; bb_index < bblock_list->len; ++bb_index) {
MonoInst *ins;
LLVMBasicBlockRef cbb;
LLVMBuilderRef builder;
LLVMValueRef v;
LLVMValueRef lhs, rhs;
+ bb = g_ptr_array_index (bblock_list, bb_index);
+
if (!(bb == cfg->bb_entry || bb->in_count > 0))
continue;
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];
+ MonoInst *exvar;
+ static gint32 mapping_inited;
+
+ 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, "llvm.eh.selector");
+
+ personality = LLVMGetNamedFunction (module, "mono_personality");
+ if (InterlockedCompareExchange (&mapping_inited, 1, 0) == 0)
+ LLVMAddGlobalMapping (ee, personality, mono_personality);
+
+ i8ptr = LLVMPointerType (LLVMInt8Type (), 0);
+ args [0] = LLVMConstNull (i8ptr);
+ args [1] = LLVMConstBitCast (personality, i8ptr);
+ args [2] = LLVMConstNull (i8ptr);
+ args [3] = LLVMConstNull (LLVMInt32Type ());
+ LLVMBuildCall (builder, eh_selector, args, 3, "");
+
+ /* Store the exception into the exvar */
+ if (bb->in_scount == 1) {
+ g_assert (bb->in_scount == 1);
+ exvar = bb->in_stack [0];
+
+ eh_exception = LLVMGetNamedFunction (module, "llvm.eh.exception");
+
+ // 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);
+ }
+ }
+
has_terminator = FALSE;
for (ins = bb->code; ins; ins = ins->next) {
const char *spec = LLVM_INS_INFO (ins->opcode);
- char *dname;
+ char *dname = NULL;
char dname_buf [128];
if (has_terminator)
MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1);
if (var && var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) {
- lhs = LLVMBuildLoad (builder, addresses [ins->sreg1], get_tempname (ctx));
+ 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)
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 = LLVMBuildLoad (builder, addresses [ins->sreg2], get_tempname (ctx));
+ rhs = emit_volatile_load (ctx, ins->sreg2);
} else {
if (!values [ins->sreg2])
LLVM_FAILURE (ctx, "sreg2");
values [ins->dreg] = LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE);
break;
case OP_I8CONST:
+#if SIZEOF_VOID_P == 4
+ values [ins->dreg] = LLVMConstInt (LLVMInt64Type (), GET_LONG_IMM (ins), FALSE);
+#else
values [ins->dreg] = LLVMConstInt (LLVMInt64Type (), (gint64)ins->inst_c0, FALSE);
+#endif
break;
case OP_R8CONST:
values [ins->dreg] = LLVMConstReal (LLVMDoubleType (), *(double*)ins->inst_p0);
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), get_tempname (ctx)), get_tempname (ctx)), IntPtrType ());
+ part1 = convert (ctx, LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (LLVMIntType (size * 8), 0), ""), ""), IntPtrType ());
- retval = LLVMBuildInsertValue (builder, LLVMGetUndef (ret_type), part1, 0, get_tempname (ctx));
+ retval = LLVMBuildInsertValue (builder, LLVMGetUndef (ret_type), part1, 0, "");
LLVMBuildRet (builder, retval);
break;
}
- if (!lhs) {
+ 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.
case OP_ICOMPARE_IMM:
case OP_LCOMPARE_IMM:
case OP_COMPARE_IMM:
-#ifdef __x86_64__
+#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;
rel = mono_opcode_to_cond (ins->next->opcode);
/* Used for implementing bound checks */
-#ifdef __x86_64__
+#ifdef TARGET_AMD64
if ((ins->opcode == OP_AMD64_ICOMPARE_MEMBASE_REG) || (ins->opcode == OP_AMD64_ICOMPARE_MEMBASE_IMM)) {
int size = 4;
LLVMValueRef index;
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, get_tempname (ctx)), get_tempname (ctx));
+ 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 = convert (ctx, rhs, LLVMInt32Type ());
#endif
+#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;
+
+ 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_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)
- rhs = LLVMConstInt (LLVMInt64Type (), 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 ());
/* 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 ()), get_tempname (ctx));
+ 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), get_tempname (ctx));
+ 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 ()), get_tempname (ctx));
+ 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, get_tempname (ctx));
+ 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));
rel = mono_opcode_to_cond (ins->opcode);
- cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMDoubleType ()), convert (ctx, rhs, LLVMDoubleType ()), get_tempname (ctx));
+ 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_FPHI:
+ case OP_VPHI:
+ case OP_XPHI: {
int i;
-
- /* Created earlier, insert it now */
- LLVMInsertIntoBuilder (builder, values [ins->dreg]);
+ 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;
}
- // FIXME: If a SWITCH statement branches to the same bblock in more
- // than once case, the PHI should reference the bblock multiple times
- for (i = 0; i < bb->in_count; ++i)
- if (bb->in_bb [i]->last_ins && bb->in_bb [i]->last_ins->opcode == OP_SWITCH) {
- LLVM_FAILURE (ctx, "switch + phi");
- break;
- }
+ 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]);
for (i = 0; i < ins->inst_phi_args [0]; i++) {
int sreg1 = ins->inst_phi_args [i + 1];
- LLVMBasicBlockRef in_bb;
+ int count, j;
- if (sreg1 == -1)
- continue;
-
- /* Add incoming values which are already defined */
- if (FALSE && values [sreg1]) {
- in_bb = get_end_bb (ctx, bb->in_bb [i]);
+ /*
+ * 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;
- g_assert (LLVMTypeOf (values [sreg1]) == LLVMTypeOf (values [ins->dreg]));
- LLVMAddIncoming (values [ins->dreg], &values [sreg1], &in_bb, 1);
+ count = 0;
+ for (j = 0; j < GPOINTER_TO_UINT (switch_ins->klass); ++j) {
+ if (switch_ins->inst_many_bb [j] == bb)
+ count ++;
+ }
} else {
- /* Remember for later */
- //LLVM_FAILURE (ctx, "phi incoming value");
- GSList *node_list = g_hash_table_lookup (phi_nodes, GUINT_TO_POINTER (bb->in_bb [i]));
+ 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->index = i;
- node_list = g_slist_prepend_mempool (ctx->mempool, node_list, node);
- g_hash_table_insert (phi_nodes, GUINT_TO_POINTER (bb->in_bb [i]), node_list);
+ 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_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_ADD_IMM:
case OP_AND_IMM:
case OP_MUL_IMM:
- case OP_SHL_IMM: {
+ case OP_SHL_IMM:
+ case OP_SHR_IMM: {
LLVMValueRef imm;
- if (spec [MONO_INST_SRC1] == 'l')
- imm = LLVMConstInt (LLVMInt64Type (), ins->inst_imm, FALSE);
- else
+ if (spec [MONO_INST_SRC1] == 'l') {
+ imm = LLVMConstInt (LLVMInt64Type (), GET_LONG_IMM (ins), FALSE);
+ } else {
imm = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE);
- if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind)
- lhs = convert (ctx, lhs, IntPtrType ());
- imm = convert (ctx, imm, LLVMTypeOf (lhs));
+ }
+
+#if SIZEOF_VOID_P == 4
+ if (ins->opcode == OP_LSHL_IMM || ins->opcode == OP_LSHR_IMM || ins->opcode == OP_LSHR_UN_IMM)
+ imm = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE);
+#endif
+
+ if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind)
+ lhs = convert (ctx, lhs, IntPtrType ());
+ imm = convert (ctx, imm, LLVMTypeOf (lhs));
switch (ins->opcode) {
case OP_IADD_IMM:
case OP_LADD_IMM:
case OP_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:
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;
values [ins->dreg] = LLVMBuildSub (builder, LLVMConstInt (LLVMInt64Type (), 0, FALSE), lhs, dname);
break;
case OP_FNEG:
+ lhs = convert (ctx, lhs, LLVMDoubleType ());
values [ins->dreg] = LLVMBuildSub (builder, LLVMConstReal (LLVMDoubleType (), 0.0), lhs, dname);
break;
case OP_INOT: {
break;
}
case OP_LNOT: {
- guint64 v = 0xffffffffffffffff;
+ 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), get_tempname (ctx));
- v2 = LLVMBuildAdd (builder, convert (ctx, lhs, IntPtrType ()), v1, get_tempname (ctx));
+ 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:
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, conv_to_llvm_type (ins->opcode), get_tempname (ctx));
+ 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 (), get_tempname (ctx));
+ 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 (), get_tempname (ctx));
+ 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 (), get_tempname (ctx));
+ 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 (), get_tempname (ctx));
+ 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);
case OP_LCONV_TO_R_UN:
values [ins->dreg] = LLVMBuildUIToFP (builder, lhs, LLVMDoubleType (), dname);
break;
+#if SIZEOF_VOID_P == 4
+ case OP_LCONV_TO_U:
+#endif
+ case OP_LCONV_TO_I4:
+ values [ins->dreg] = LLVMBuildTrunc (builder, lhs, LLVMInt32Type (), dname);
+ break;
case OP_ICONV_TO_R4:
case OP_LCONV_TO_R4:
- v = LLVMBuildSIToFP (builder, lhs, LLVMFloatType (), get_tempname (ctx));
+ v = LLVMBuildSIToFP (builder, lhs, LLVMFloatType (), "");
values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
break;
case OP_FCONV_TO_R4:
- v = LLVMBuildFPTrunc (builder, lhs, LLVMFloatType (), get_tempname (ctx));
+ v = LLVMBuildFPTrunc (builder, lhs, LLVMFloatType (), "");
values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
break;
case OP_SEXT_I4:
guint32 size = ins->inst_imm;
size = (size + (MONO_ARCH_FRAME_ALIGNMENT - 1)) & ~ (MONO_ARCH_FRAME_ALIGNMENT - 1);
- v = mono_llvm_build_alloca (builder, LLVMInt8Type (), LLVMConstInt (LLVMInt32Type (), size, FALSE), MONO_ARCH_FRAME_ALIGNMENT, get_tempname (ctx));
+ v = mono_llvm_build_alloca (builder, LLVMInt8Type (), LLVMConstInt (LLVMInt32Type (), size, FALSE), MONO_ARCH_FRAME_ALIGNMENT, "");
if (ins->flags & MONO_INST_INIT) {
LLVMValueRef args [4];
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), "");
+
+ v = mono_llvm_build_alloca (builder, LLVMInt8Type (), size, MONO_ARCH_FRAME_ALIGNMENT, "");
+
+ if (ins->flags & MONO_INST_INIT) {
+ LLVMValueRef args [4];
+
+ args [0] = v;
+ args [1] = LLVMConstInt (LLVMInt8Type (), 0, FALSE);
+ args [2] = size;
+ args [3] = LLVMConstInt (LLVMInt32Type (), MONO_ARCH_FRAME_ALIGNMENT, FALSE);
+ LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.memset.i32"), args, 4, "");
+ }
+ values [ins->dreg] = v;
+ break;
+ }
+
case OP_LOADI1_MEMBASE:
case OP_LOADU1_MEMBASE:
case OP_LOADI2_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: {
t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
if (sext || zext)
- dname = (char*)get_tempname (ctx);
+ dname = (char*)"";
+ /*
+ * We emit volatile loads because otherwise LLVM will
+ * generate invalid code when encountering a load from a
+ * NULL address.
+ * FIXME: Avoid this somehow.
+ */
g_assert (ins->inst_offset % size == 0);
- if ((ins->opcode == OP_LOADI8_MEM) || (ins->opcode == OP_LOAD_MEM) || (ins->opcode == OP_LOADI4_MEM) || (ins->opcode == OP_LOADU4_MEM)) {
- values [ins->dreg] = LLVMBuildLoad (builder, convert (ctx, LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), LLVMPointerType (t, 0)), dname);
+ 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)) {
+ values [ins->dreg] = mono_llvm_build_volatile_load (builder, convert (ctx, LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), LLVMPointerType (t, 0)), dname);
} else if (ins->inst_offset == 0) {
- values [ins->dreg] = LLVMBuildLoad (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), dname);
+ values [ins->dreg] = mono_llvm_build_volatile_load (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), dname);
} else {
index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
- values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), &index, 1, get_tempname (ctx)), dname);
+ values [ins->dreg] = mono_llvm_build_volatile_load (builder, LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), &index, 1, ""), dname);
}
if (sext)
values [ins->dreg] = LLVMBuildSExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
g_assert (ins->inst_offset % size == 0);
index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
- LLVMBuildStore (builder, convert (ctx, values [ins->sreg1], t), LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, get_tempname (ctx)));
+ LLVMBuildStore (builder, convert (ctx, values [ins->sreg1], t), LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, ""));
break;
}
g_assert (ins->inst_offset % size == 0);
index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
- LLVMBuildStore (builder, convert (ctx, LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE), t), LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, get_tempname (ctx)));
+ LLVMBuildStore (builder, convert (ctx, LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE), t), LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, ""));
break;
}
case OP_CHECK_THIS:
- LLVMBuildLoad (builder, convert (ctx, values [ins->sreg1], LLVMPointerType (IntPtrType (), 0)), get_tempname (ctx));
+ mono_llvm_build_volatile_load (builder, convert (ctx, values [ins->sreg1], LLVMPointerType (IntPtrType (), 0)), "");
break;
case OP_OUTARG_VTRETADDR:
break;
case OP_CALL_MEMBASE:
case OP_LCALL_MEMBASE:
case OP_FCALL_MEMBASE:
- case OP_VCALL_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;
LLVMTypeRef llvm_sig;
gpointer target;
int *pindexes;
+ gboolean virtual, calli;
cinfo = call->cinfo;
LLVM_FAILURE (ctx, "rgctx reg");
}
- pindexes = mono_mempool_alloc0 (cfg->mempool, sig->param_count + 2);
+ 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);
+
+ pindexes = mono_mempool_alloc0 (cfg->mempool, (sig->param_count + 2) * sizeof (guint32));
/* FIXME: Avoid creating duplicate methods */
if (ins->flags & MONO_INST_HAS_METHOD) {
- callee = LLVMAddFunction (module, "", llvm_sig);
-
- target =
- mono_create_jit_trampoline_in_domain (mono_domain_get (),
- call->method);
- LLVMAddGlobalMapping (ee, callee, target);
+ 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);
+ }
+ }
+ } else if (calli) {
} else {
MonoJitICallInfo *info = mono_find_jit_icall_by_addr (call->fptr);
- callee = LLVMAddFunction (module, "", llvm_sig);
-
if (info) {
/*
MonoJumpInfo ji;
target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, &ji, FALSE);
*/
- target = (gpointer)mono_icall_get_wrapper (info);
- LLVMAddGlobalMapping (ee, callee, target);
+ 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 {
- target = NULL;
- if (cfg->abs_patches) {
- MonoJumpInfo *abs_ji = g_hash_table_lookup (cfg->abs_patches, call->fptr);
- if (abs_ji) {
- target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, abs_ji, FALSE);
- LLVMAddGlobalMapping (ee, callee, target);
+ 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) {
+ if (abs_ji->type == MONO_PATCH_INFO_MONITOR_ENTER || abs_ji->type == MONO_PATCH_INFO_MONITOR_EXIT)
+ /* FIXME: These have their own calling convention */
+ LLVM_FAILURE (ctx, "monitor enter/exit");
+ 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 (!target)
- LLVMAddGlobalMapping (ee, callee, (gpointer)call->fptr);
}
}
- if (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) {
+ if (virtual) {
int size = sizeof (gpointer);
LLVMValueRef index;
g_assert (ins->inst_offset % size == 0);
index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
- callee = convert (ctx, LLVMBuildLoad (builder, LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (LLVMPointerType (IntPtrType (), 0), 0)), &index, 1, get_tempname (ctx)), get_tempname (ctx)), LLVMPointerType (llvm_sig, 0));
-
// FIXME: mono_arch_get_vcall_slot () can't decode the code
// generated by LLVM
//LLVM_FAILURE (ctx, "virtual call");
- if (call->method && call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE)
+ 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");
+#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) {
}
pindex = 0;
if (vretaddr) {
if (!addresses [call->inst.dreg])
- addresses [call->inst.dreg] = LLVMBuildAlloca (builder, type_to_llvm_type (ctx, &(mono_class_from_mono_type (sig->ret)->byval_arg)), get_tempname (ctx));
- args [pindex ++] = LLVMBuildPtrToInt (builder, addresses [call->inst.dreg], IntPtrType (), get_tempname (ctx));
+ 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) {
/*
* Emit the call
*/
- lcall = LLVMBuildCall (builder, callee, args, pindex, "");
+
+ lcall = emit_call (ctx, bb, &builder, callee, args, pindex);
/* Add byval attributes if needed */
for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
LLVMValueRef regs [2];
if (!addresses [ins->dreg])
- addresses [ins->dreg] = LLVMBuildAlloca (builder, type_to_llvm_type (ctx, &(mono_class_from_mono_type (sig->ret)->byval_arg)), get_tempname (ctx));
+ addresses [ins->dreg] = build_alloca (ctx, sig->ret);
regs [0] = LLVMBuildExtractValue (builder, lcall, 0, "");
if (cinfo->ret.pair_storage [1] != LLVMArgNone)
emit_reg_to_vtype (ctx, builder, sig->ret, addresses [ins->dreg], &cinfo->ret, regs);
} else if (sig->ret->type != MONO_TYPE_VOID && !vretaddr) {
- values [ins->dreg] = convert (ctx, lcall, llvm_type_to_stack_type (type_to_llvm_type (ctx, sig->ret)));
- }
- break;
- }
- case OP_GOT_ENTRY: {
- LLVMValueRef indexes [2];
+ /* If the method returns an unsigned value, need to zext it */
- indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
- indexes [1] = LLVMConstInt (LLVMInt32Type (), (gssize)ins->inst_p0, FALSE);
- values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildGEP (builder, ctx->got_var, indexes, 2, get_tempname (ctx)), dname);
- break;
- }
- case OP_THROW: {
- MonoMethodSignature *throw_sig;
- LLVMValueRef callee, arg;
-
- if (!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;
- callee = LLVMAddFunction (module, "mono_arch_throw_exception", sig_to_llvm_sig (ctx, throw_sig, NULL));
- LLVMAddGlobalMapping (ee, callee, resolve_patch (cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_exception"));
-
- mono_memory_barrier ();
- throw = callee;
+ 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));
}
- arg = convert (ctx, values [ins->sreg1], type_to_llvm_type (ctx, &mono_defaults.object_class->byval_arg));
- LLVMBuildCall (builder, throw, &arg, 1, "");
break;
}
+ case OP_AOTCONST: {
+ guint32 got_offset;
+ LLVMValueRef indexes [2];
+ MonoJumpInfo *ji;
+
+ /*
+ * 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;
+
+ ji = mono_aot_patch_info_dup (ji);
+
+ 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);
+ values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildGEP (builder, ctx->lmodule->got_var, indexes, 2, ""), 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;
case OP_SIN: {
LLVMValueRef args [1];
- args [0] = lhs;
+ 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] = lhs;
+ args [0] = convert (ctx, lhs, LLVMDoubleType ());
values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.cos.f64"), args, 1, dname);
break;
}
case OP_IMIN:
case OP_LMIN: {
- LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntSLE, lhs, rhs, get_tempname (ctx));
+ LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntSLE, lhs, rhs, "");
values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
break;
}
case OP_IMAX:
case OP_LMAX: {
- LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntSGE, lhs, rhs, get_tempname (ctx));
+ LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntSGE, lhs, rhs, "");
values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
break;
}
case OP_IMIN_UN:
case OP_LMIN_UN: {
- LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntULE, lhs, rhs, get_tempname (ctx));
+ LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntULE, lhs, rhs, "");
values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
break;
}
case OP_IMAX_UN:
case OP_LMAX_UN: {
- LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntUGE, lhs, rhs, get_tempname (ctx));
+ LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntUGE, lhs, rhs, "");
values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
break;
}
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, get_tempname (ctx)), args [1], dname);
+ 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: {
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, get_tempname (ctx)), args [1], dname);
+ 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:
LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.memory.barrier"), args, 5, "");
break;
}
+ case OP_RELAXED_NOP: {
+#if defined(TARGET_AMD64) || defined(TARGET_X86)
+ /* No way to get LLVM to emit this */
+ LLVM_FAILURE (ctx, "relaxed_nop");
+#else
+ break;
+#endif
+ }
+ case OP_TLS_GET: {
+#if defined(TARGET_AMD64) || defined(TARGET_X86)
+#ifdef TARGET_AMD64
+ // 257 == FS segment register
+ LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 257);
+#else
+ // 256 == GS segment register
+ LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 256);
+#endif
+
+ // FIXME: XEN
+ values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildIntToPtr (builder, LLVMConstInt (IntPtrType (), ins->inst_offset, TRUE), ptrtype, ""), "");
+#else
+ LLVM_FAILURE (ctx, "opcode tls-get");
+#endif
+
+ break;
+ }
/*
* Overflow opcodes.
*/
- // FIXME: LLVM can't handle mul_ovf_un yet
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_IMUL_OVF_UN:
+#if SIZEOF_VOID_P == 8
case OP_LADD_OVF:
case OP_LSUB_OVF:
case OP_LSUB_OVF_UN:
- case OP_LMUL_OVF: {
- //case OP_LMUL_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] = lhs;
- args [1] = rhs;
+ 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));
g_assert (func);
val = LLVMBuildCall (builder, func, args, 2, "");
values [ins->dreg] = LLVMBuildExtractValue (builder, val, 0, dname);
- ovf = LLVMBuildExtractValue (builder, val, 1, get_tempname (ctx));
+ ovf = LLVMBuildExtractValue (builder, val, 1, "");
emit_cond_system_exception (ctx, bb, "OverflowException", ovf);
builder = ctx->builder;
break;
break;
}
- args [0] = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), get_tempname (ctx));
+ 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
case OP_VMOVE: {
MonoClass *klass = ins->klass;
LLVMValueRef src, dst, args [4];
+ gboolean done = FALSE;
if (!klass) {
// FIXME:
switch (ins->opcode) {
case OP_STOREV_MEMBASE:
- src = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (LLVMInt8Type (), 0), get_tempname (ctx));
- dst = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_destbasereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), get_tempname (ctx)), LLVMPointerType (LLVMInt8Type (), 0));
+ 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] = LLVMBuildAlloca (builder, type_to_llvm_type (ctx, &klass->byval_arg), get_tempname (ctx));
- src = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_basereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), get_tempname (ctx)), LLVMPointerType (LLVMInt8Type (), 0));
- dst = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), get_tempname (ctx));
+ 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] = LLVMBuildAlloca (builder, type_to_llvm_type (ctx, &klass->byval_arg), get_tempname (ctx));
+ addresses [ins->sreg1] = build_alloca (ctx, &klass->byval_arg);
if (!addresses [ins->dreg])
- addresses [ins->dreg] = LLVMBuildAlloca (builder, type_to_llvm_type (ctx, &klass->byval_arg), get_tempname (ctx));
- src = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (LLVMInt8Type (), 0), get_tempname (ctx));
- dst = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), get_tempname (ctx));
+ addresses [ins->dreg] = build_alloca (ctx, &klass->byval_arg);
+ src = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (LLVMInt8Type (), 0), "");
+ dst = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), "");
break;
default:
g_assert_not_reached ();
}
+ if (done)
+ break;
+
args [0] = dst;
args [1] = src;
args [2] = LLVMConstInt (LLVMInt32Type (), mono_class_value_size (klass, NULL), FALSE);
break;
}
case OP_LLVM_OUTARG_VT:
- g_assert (addresses [ins->sreg1]);
+ 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
+ */
+ 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] = LLVMBuildLoad (builder, src, "");
+ 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;
+ 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:
+ 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, lhs, LLVMBuildNot (builder, rhs, ""), "");
+ break;
+ }
+ values [ins->dreg] = LLVMBuildBitCast (builder, v, rt, "");
+ break;
+ }
+ 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);
+ break;
+ }
+ 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;
+
+ 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:
+ case OP_EXTRACT_U2:
+ t = LLVMVectorType (LLVMInt16Type (), 8);
+ break;
+ case OP_EXTRACT_I1:
+ case OP_EXTRACT_U1:
+ t = LLVMVectorType (LLVMInt8Type (), 16);
+ 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), "");
+ break;
+ }
+
+ case OP_DUMMY_USE:
+ break;
+
+ /*
+ * EXCEPTION HANDLING
+ */
+ 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));
+ LLVMAddGlobalMapping (ee, callee, resolve_patch (cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_exception"));
+ }
+
+ 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;
+
+ finally_bb = get_bb (ctx, ins->inst_target_bb);
+
+ bb_list = bblocks [ins->inst_target_bb->block_num].call_handler_return_bbs;
+
+ /*
+ * 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);
+
+ 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);
+
+ 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;
+ GSList *bb_list;
+
+ 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);
+
+ bb_list = bblocks [handler_bb->block_num].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.
+ */
+ 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);
+ */
+
+ builder = ctx->builder = create_builder (ctx);
+ LLVMPositionBuilderAtEnd (ctx->builder, resume_bb);
+
+ LLVMBuildCall (builder, LLVMGetNamedFunction (module, "mono_resume_unwind"), NULL, 0, "");
+ LLVMBuildUnreachable (builder);
+ has_terminator = TRUE;
+ break;
+ }
default: {
char reason [128];
}
/* Convert the value to the type required by phi nodes */
- if (spec [MONO_INST_DEST] != ' ' && !MONO_IS_STORE_MEMBASE (ins) && vreg_types [ins->dreg])
- values [ins->dreg] = convert (ctx, values [ins->dreg], vreg_types [ins->dreg]);
+ 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))
/* Add incoming phi values */
for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
- GSList *ins_list = g_hash_table_lookup (phi_nodes, GUINT_TO_POINTER (bb));
+ GSList *l, *ins_list;
- while (ins_list) {
- PhiNode *node = ins_list->data;
+ ins_list = bblocks [bb->block_num].phi_nodes;
+
+ for (l = ins_list; l; l = l->next) {
+ PhiNode *node = l->data;
MonoInst *phi = node->phi;
- int sreg1 = phi->inst_phi_args [node->index + 1];
+ int sreg1 = node->sreg;
LLVMBasicBlockRef in_bb;
- in_bb = get_end_bb (ctx, node->bb->in_bb [node->index]);
+ if (sreg1 == -1)
+ continue;
+
+ in_bb = get_end_bb (ctx, node->in_bb);
+
+ if (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);
+ }
+ }
- ins_list = ins_list->next;
+ /* 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;
+
+ 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->verbose_level > 1)
mono_llvm_dump_value (method);
- mono_llvm_optimize_method (method);
+ mark_as_used (module, method);
- if (cfg->verbose_level > 1)
- mono_llvm_dump_value (method);
+ if (cfg->compile_aot) {
+ /* Don't generate native code, keep the LLVM IR */
- cfg->native_code = LLVMGetPointerToGlobal (ee, method);
+ /* 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);
+ LLVMSetVisibility (debug_alias, LLVMHiddenVisibility);
+ }
- /* Set by emit_cb */
- g_assert (cfg->code_len);
+ if (cfg->compile_aot && cfg->verbose_level)
+ printf ("%s emitted as %s\n", mono_method_full_name (cfg->method, TRUE), method_name);
- /* FIXME: Free the LLVM IL for the function */
+ //LLVMVerifyFunction(method, 0);
+ } else {
+ mono_llvm_optimize_method (method);
+
+ if (cfg->verbose_level > 1)
+ mono_llvm_dump_value (method);
+
+ cfg->native_code = LLVMGetPointerToGlobal (ee, method);
+
+ /* Set by emit_cb */
+ g_assert (cfg->code_len);
+
+ /* FIXME: Free the LLVM IL for the function */
+ }
goto CLEANUP;
g_free (values);
g_free (addresses);
g_free (vreg_types);
+ g_free (vreg_cli_types);
g_free (pindexes);
- g_hash_table_destroy (phi_nodes);
+ g_free (debug_name);
g_ptr_array_free (phi_values, TRUE);
g_free (ctx->bblocks);
- g_free (ctx->end_bblocks);
+ g_hash_table_destroy (ctx->region_to_handler);
+ g_free (method_name);
+ g_ptr_array_free (bblock_list, TRUE);
for (l = ctx->builders; l; l = l->next) {
LLVMBuilderRef builder = l->data;
call->cinfo = mono_arch_get_llvm_call_info (cfg, sig);
+ if (cfg->disable_llvm)
+ return;
+
if (sig->call_convention == MONO_CALL_VARARG) {
cfg->exception_message = g_strdup ("varargs");
cfg->disable_llvm = TRUE;
ins->klass = mono_class_from_mono_type (sig->params [i - sig->hasthis]);
break;
default:
+ call->cinfo = mono_arch_get_llvm_call_info (cfg, sig);
cfg->exception_message = g_strdup ("ainfo->storage");
cfg->disable_llvm = TRUE;
return;
exception_cb (void *data)
{
MonoCompile *cfg;
+ MonoJitExceptionInfo *ei;
+ guint32 ei_len;
cfg = TlsGetValue (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_get_ops_from_fde ((guint8*)data, &cfg->encoded_unwind_ops_len);
+ cfg->encoded_unwind_ops = mono_unwind_decode_fde ((guint8*)data, &cfg->encoded_unwind_ops_len, NULL, &ei, &ei_len);
+
+ cfg->llvm_ex_info = mono_mempool_alloc0 (cfg->mempool, ei_len * sizeof (MonoJitExceptionInfo));
+ cfg->llvm_ex_info_len = ei_len;
+ memcpy (cfg->llvm_ex_info, ei, ei_len * sizeof (MonoJitExceptionInfo));
+ g_free (ei);
}
-void
-mono_llvm_init (void)
+static void
+add_intrinsics (LLVMModuleRef module)
{
- current_cfg_tls_id = TlsAlloc ();
-
- module = LLVMModuleCreateWithName ("mono");
-
- ee = mono_llvm_create_ee (LLVMCreateModuleProviderForExistingModule (module), alloc_cb, emitted_cb, exception_cb);
-
- llvm_types = g_hash_table_new (NULL, NULL);
-
/* Emit declarations of instrinsics */
{
LLVMTypeRef memset_params [] = { LLVMPointerType (LLVMInt8Type (), 0), LLVMInt8Type (), LLVMInt32Type (), LLVMInt32Type () };
LLVMAddFunction (module, "llvm.smul.with.overflow.i64", LLVMFunctionType (LLVMStructType (ovf_res_i64, 2, FALSE), ovf_params_i64, 2, FALSE));
LLVMAddFunction (module, "llvm.umul.with.overflow.i64", LLVMFunctionType (LLVMStructType (ovf_res_i64, 2, FALSE), ovf_params_i64, 2, FALSE));
}
+
+ /* EH intrinsics */
+ {
+ LLVMTypeRef arg_types [2];
+
+ arg_types [0] = LLVMPointerType (LLVMInt8Type (), 0);
+ arg_types [1] = LLVMPointerType (LLVMInt8Type (), 0);
+ LLVMAddFunction (module, "llvm.eh.selector", LLVMFunctionType (LLVMInt32Type (), 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));
+ }
+
+ /* 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.sse41.pminub", LLVMFunctionType (vector_type, arg_types, 2, FALSE));
+ LLVMAddFunction (module, "llvm.x86.sse41.pmaxub", 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));
+ }
+}
+
+void
+mono_llvm_init (void)
+{
+ current_cfg_tls_id = TlsAlloc ();
+
+ jit_module.module = LLVMModuleCreateWithName ("mono");
+
+ ee = mono_llvm_create_ee (LLVMCreateModuleProviderForExistingModule (jit_module.module), alloc_cb, emitted_cb, exception_cb);
+
+ add_intrinsics (jit_module.module);
+
+ jit_module.llvm_types = g_hash_table_new (NULL, NULL);
+
+ LLVMAddGlobalMapping (ee, LLVMGetNamedFunction (jit_module.module, "mono_resume_unwind"), mono_resume_unwind);
}
void
{
mono_llvm_dispose_ee (ee);
- g_hash_table_destroy (llvm_types);
+ g_hash_table_destroy (jit_module.llvm_types);
+}
+
+void
+mono_llvm_create_aot_module (const char *got_symbol)
+{
+ /* Delete previous module */
+ if (aot_module.plt_entries)
+ g_hash_table_destroy (aot_module.plt_entries);
+
+ memset (&aot_module, 0, sizeof (aot_module));
+
+ aot_module.module = LLVMModuleCreateWithName ("aot");
+ aot_module.got_symbol = got_symbol;
+
+ add_intrinsics (aot_module.module);
+
+ /* Add GOT */
+ /*
+ * We couldn't compute the type of the LLVM global representing the got because
+ * its size is only known after all the methods have been emitted. So create
+ * a dummy variable, and replace all uses it with the real got variable when
+ * its size is known in mono_llvm_emit_aot_module ().
+ */
+ {
+ LLVMTypeRef got_type = LLVMArrayType (IntPtrType (), 0);
+
+ aot_module.got_var = LLVMAddGlobal (aot_module.module, got_type, "mono_dummy_got");
+ LLVMSetInitializer (aot_module.got_var, LLVMConstNull (got_type));
+ }
+
+ /* Add a method to generate the 'methods' symbol needed by the AOT compiler */
+ {
+ LLVMValueRef methods_method = LLVMAddFunction (aot_module.module, "methods", LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE));
+ LLVMBasicBlockRef bb = LLVMAppendBasicBlock (methods_method, "BB_ENTRY");
+ LLVMBuilderRef builder = LLVMCreateBuilder ();
+ LLVMPositionBuilderAtEnd (builder, bb);
+ LLVMBuildRetVoid (builder);
+ }
+
+ aot_module.llvm_types = g_hash_table_new (NULL, NULL);
+ aot_module.plt_entries = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+/*
+ * Emit the aot module into the LLVM bitcode file FILENAME.
+ */
+void
+mono_llvm_emit_aot_module (const char *filename, int got_size)
+{
+ LLVMTypeRef got_type;
+ LLVMValueRef real_got;
+
+ /*
+ * Create the real got variable and replace all uses of the dummy variable with
+ * the real one.
+ */
+ got_type = LLVMArrayType (IntPtrType (), got_size);
+ real_got = LLVMAddGlobal (aot_module.module, got_type, aot_module.got_symbol);
+ LLVMSetInitializer (real_got, LLVMConstNull (got_type));
+ LLVMSetLinkage (real_got, LLVMInternalLinkage);
+
+ mono_llvm_replace_uses_of (aot_module.got_var, real_got);
+
+ mark_as_used (aot_module.module, real_got);
+
+#if 0
+ {
+ char *verifier_err;
+
+ if (LLVMVerifyModule (aot_module.module, LLVMReturnStatusAction, &verifier_err)) {
+ g_assert_not_reached ();
+ }
+ }
+#endif
+
+ LLVMWriteBitcodeToFile (aot_module.module, filename);
}
/*
types are frequently used incorrectly.
*/
+/*
+ AOT SUPPORT:
+ Emit LLVM bytecode into a .bc file, compile it using llc into a .s file, then
+ append the AOT data structures to that file. For methods which cannot be
+ handled by LLVM, the normal JIT compiled versions are used.
+*/
+
/* 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