#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mempool-internals.h>
+#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
+#endif
+#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
+#endif
#include "llvm-c/Core.h"
#include "llvm-c/ExecutionEngine.h"
*/
typedef struct {
LLVMModuleRef module;
- LLVMValueRef throw, throw_corlib_exception;
+ 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
*/
LLVMValueRef lmethod;
MonoLLVMModule *lmodule;
LLVMModuleRef module;
- LLVMBasicBlockRef *bblocks, *end_bblocks;
+ BBInfo *bblocks;
int sindex, default_index, ex_index;
LLVMBuilderRef builder;
LLVMValueRef *values, *addresses;
LLVMCallInfo *linfo;
MonoMethodSignature *sig;
GSList *builders;
+ GHashTable *region_to_handler;
+ LLVMBuilderRef alloca_builder;
+ LLVMValueRef last_alloca;
char temp_name [32];
} EmitContext;
static guint32 current_cfg_tls_id;
static MonoLLVMModule jit_module, aot_module;
+static gboolean jit_module_inited;
+
+static void init_jit_module (void);
/*
* IntPtrType:
case OP_IMUL_OVF_UN:
return LLVMInt32Type ();
case OP_LADD_OVF:
+ case OP_LADD_OVF_UN:
case OP_LSUB_OVF:
case OP_LSUB_OVF_UN:
case OP_LMUL_OVF:
simd_op_to_intrins (int opcode)
{
switch (opcode) {
+#if defined(TARGET_X86) || defined(TARGET_AMD64)
case OP_MINPD:
return "llvm.x86.sse2.min.pd";
case OP_MINPS:
return "llvm.x86.sse41.pmaxuw";
case OP_PMAXB_UN:
return "llvm.x86.sse41.pmaxub";
+#endif
default:
g_assert_not_reached ();
return NULL;
{
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;
+}
+
+static LLVMBasicBlockRef
+gen_bb (EmitContext *ctx, const char *prefix)
+{
+ char bb_name [128];
+
+ sprintf (bb_name, "%s%d", prefix, ++ ctx->ex_index);
+ return LLVMAppendBasicBlock (ctx->lmethod, bb_name);
}
/*
}
/*
- * emit_volatile_store_full:
+ * emit_volatile_store:
*
* If VREG is volatile, emit a store from its value to its address.
*/
if (!callee) {
callee = LLVMAddFunction (ctx->module, callee_name, llvm_sig);
+ LLVMSetVisibility (callee, LLVMHiddenVisibility);
+
g_hash_table_insert (ctx->lmodule->plt_entries, (char*)callee_name, callee);
}
{
}
+/*
+ * 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 != -1 && 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);
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));
-
+
+#ifdef TARGET_X86
+ /*
+ * LLVM generated code doesn't push the arguments, so we need another
+ * throw trampoline.
+ */
+ 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
}
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);
- LLVMBuildCall (builder, ctx->lmodule->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 ++;
}
else
align = mono_class_min_align (k);
- return mono_llvm_build_alloca (ctx->builder, type_to_llvm_type (ctx, t), NULL, align, "");
+ /* Sometimes align is not a power of 2 */
+ while (mono_is_power_of_two (align) == -1)
+ align ++;
+
+ /*
+ * Have to place all alloca's at the end of the entry bb, since otherwise they would
+ * get executed every time control reaches them.
+ */
+ LLVMPositionBuilder (ctx->alloca_builder, get_bb (ctx, ctx->cfg->bb_entry), ctx->last_alloca);
+
+ ctx->last_alloca = mono_llvm_build_alloca (ctx->alloca_builder, type_to_llvm_type (ctx, t), NULL, align, "");
+ return ctx->last_alloca;
}
/*
MonoCompile *cfg = ctx->cfg;
MonoMethodSignature *sig = ctx->sig;
LLVMCallInfo *linfo = ctx->linfo;
+ MonoBasicBlock *bb;
+
+ ctx->alloca_builder = create_builder (ctx);
/*
* Handle indirect/volatile variables by allocating memory for them
ctx->addresses [reg] = build_alloca (ctx, sig->params [i]);
emit_reg_to_vtype (ctx, builder, sig->params [i], ctx->addresses [reg], ainfo, regs);
+
+ if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (sig->params [i]))) {
+ /* Treat these as normal values */
+ ctx->values [reg] = LLVMBuildLoad (builder, ctx->addresses [reg], "");
+ }
} else if (ainfo->storage == LLVMArgVtypeByVal) {
ctx->addresses [reg] = LLVMGetParam (ctx->lmethod, pindexes [i]);
} else {
}
}
+ if (cfg->vret_addr)
+ emit_volatile_store (ctx, cfg->vret_addr->dreg);
+ if (sig->hasthis)
+ emit_volatile_store (ctx, cfg->args [0]->dreg);
for (i = 0; i < sig->param_count; ++i)
if (!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:
;
}
+/* Have to export this for AOT */
+void
+mono_personality (void);
+
+void
+mono_personality (void)
+{
+ /* Not used */
+ g_assert_not_reached ();
+}
+
/*
* mono_llvm_emit_method:
*
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;
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);
vreg_cli_types = g_new0 (MonoType*, cfg->next_vreg);
- phi_nodes = g_hash_table_new (NULL, NULL);
phi_values = g_ptr_array_new ();
/*
* This signals whenever the vreg was defined by a phi node with no input vars
/* 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 {
+ init_jit_module ();
ctx->lmodule = &jit_module;
method_name = mono_method_full_name (cfg->method, TRUE);
debug_name = NULL;
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 && clause->flags != MONO_EXCEPTION_CLAUSE_NONE)
+ LLVM_FAILURE (ctx, "non-finally/catch 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) {
}
}
- /*
- * Second pass: generate code.
+ /*
+ * 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) {
+ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+ if (!bblocks [bb->block_num].added)
+ g_ptr_array_add (bblock_list, bb);
+ }
+
+ /*
+ * Second pass: generate code.
+ */
+ for (bb_index = 0; bb_index < bblock_list->len; ++bb_index) {
MonoInst *ins;
LLVMBasicBlockRef cbb;
LLVMBuilderRef builder;
LLVMValueRef v;
LLVMValueRef lhs, rhs;
- bb = cfg->bblocks [bb_index];
+ 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;
+ 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, "llvm.eh.selector");
+
+ 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);
+ }
+
+ i8ptr = LLVMPointerType (LLVMInt8Type (), 0);
+
+ clause_index = (mono_get_block_region_notry (cfg, bb->region) >> 8) - 1;
+
+ /*
+ * Create the type info
+ */
+ sprintf (ti_name, "type_info_%d", ti_generator);
+ ti_generator ++;
+
+ if (cfg->compile_aot) {
+ /* decode_eh_frame () in aot-runtime.c will decode this */
+ type_info = LLVMAddGlobal (module, LLVMInt32Type (), ti_name);
+ LLVMSetInitializer (type_info, LLVMConstInt (LLVMInt32Type (), clause_index, FALSE));
+
+ LLVMSetLinkage (type_info, LLVMPrivateLinkage);
+ LLVMSetVisibility (type_info, LLVMHiddenVisibility);
+
+ /*
+ * FIXME: llc currently generates incorrect data in the LSDA:
+ * .byte 0x9B # @TType format (indirect pcrel sdata4)
+ * and later:
+ * .quad type_info_1 # TypeInfo
+ */
+ LLVM_FAILURE (ctx, "aot+clauses");
+ } else {
+ /* exception_cb will decode this */
+ ti = g_malloc (sizeof (MonoExceptionClause));
+ memcpy (ti, &mono_method_get_header (cfg->method)->clauses [clause_index], sizeof (MonoExceptionClause));
+
+ type_info = LLVMAddGlobal (module, i8ptr, ti_name);
+
+ LLVMAddGlobalMapping (ee, type_info, ti);
+ }
+
+ args [0] = LLVMConstNull (i8ptr);
+ args [1] = LLVMConstBitCast (personality, i8ptr);
+ args [2] = type_info;
+ 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);
break;
}
+ 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
/* Remember for later */
for (j = 0; j < count; ++j) {
- GSList *node_list = g_hash_table_lookup (phi_nodes, GUINT_TO_POINTER (bb->in_bb [i]));
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;
- 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);
+ 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_LOADU4_MEM:
case OP_LOAD_MEM: {
int size = 8;
- LLVMValueRef index;
+ 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);
dname = (char*)"";
/*
- * We emit volatile loads because otherwise LLVM will
- * generate invalid code when encountering a load from a
+ * We emit volatile loads for loads which can fault, 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) || (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);
+ addr = LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE);
} else if (ins->inst_offset == 0) {
- values [ins->dreg] = mono_llvm_build_volatile_load (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), dname);
+ addr = values [ins->inst_basereg];
+ } 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, "");
} else {
index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
- values [ins->dreg] = mono_llvm_build_volatile_load (builder, LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), &index, 1, ""), dname);
+ addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), &index, 1, "");
}
+
+ addr = convert (ctx, addr, LLVMPointerType (t, 0));
+
+ values [ins->dreg] = mono_llvm_build_load (builder, addr, dname, is_volatile);
+
if (sext)
values [ins->dreg] = LLVMBuildSExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
else if (zext)
case OP_STORER8_MEMBASE_REG:
case OP_STORE_MEMBASE_REG: {
int size = 8;
- LLVMValueRef index;
+ LLVMValueRef index, addr;
LLVMTypeRef t;
gboolean sext = FALSE, zext = FALSE;
t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
- 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, ""));
+ 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_STOREI8_MEMBASE_IMM:
case OP_STORE_MEMBASE_IMM: {
int size = 8;
- LLVMValueRef index;
+ LLVMValueRef index, addr;
LLVMTypeRef t;
gboolean sext = FALSE, zext = FALSE;
t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
- 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, ""));
+ 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_CHECK_THIS:
- mono_llvm_build_volatile_load (builder, convert (ctx, values [ins->sreg1], LLVMPointerType (IntPtrType (), 0)), "");
+ mono_llvm_build_load (builder, convert (ctx, values [ins->sreg1], LLVMPointerType (IntPtrType (), 0)), "", TRUE);
break;
case OP_OUTARG_VTRETADDR:
break;
llvm_sig = sig_to_llvm_sig (ctx, sig, cinfo);
CHECK_FAILURE (ctx);
- if (call->rgctx_reg) {
- LLVM_FAILURE (ctx, "rgctx reg");
- }
-
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);
+ pindexes = mono_mempool_alloc0 (cfg->mempool, (sig->param_count + 2) * sizeof (guint32));
/* FIXME: Avoid creating duplicate methods */
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)
- /* FIXME: These have their own calling convention */
LLVM_FAILURE (ctx, "monitor enter/exit");
+#endif
target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, abs_ji, FALSE);
LLVMAddGlobalMapping (ee, callee, target);
}
/*
* 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) {
guint32 got_offset;
LLVMValueRef indexes [2];
MonoJumpInfo *ji;
+ LLVMValueRef got_entry_addr;
/*
* FIXME: Can't allocate from the cfg mempool since that is freed if
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_THROW: {
- MonoMethodSignature *throw_sig;
- LLVMValueRef callee, arg;
+ got_entry_addr = LLVMBuildGEP (builder, ctx->lmodule->got_var, indexes, 2, "");
- 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"));
- }
+ // 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;
- mono_memory_barrier ();
- ctx->lmodule->throw = callee;
+ 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, "");
}
- arg = convert (ctx, values [ins->sreg1], type_to_llvm_type (ctx, &mono_defaults.object_class->byval_arg));
- LLVMBuildCall (builder, ctx->lmodule->throw, &arg, 1, "");
- break;
- }
+#endif
+
+ values [ins->dreg] = LLVMBuildLoad (builder, got_entry_addr, dname);
+ break;
+ }
case OP_NOT_REACHED:
LLVMBuildUnreachable (builder);
has_terminator = TRUE;
}
*/
+ case OP_ABS: {
+ LLVMValueRef args [1];
+
+ args [0] = lhs;
+ values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "fabs"), args, 1, dname);
+ break;
+ }
+
case OP_IMIN:
case OP_LMIN: {
LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntSLE, lhs, rhs, "");
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:
/*
* 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;
values [ins->dreg] = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), "");
break;
}
+#endif
+
+ 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: {
+ 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));
+
+#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"));
+#endif
+ }
+
+ 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];
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));
}
/* Add incoming phi values */
for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
- GSList *l, *ins_list = g_hash_table_lookup (phi_nodes, GUINT_TO_POINTER (bb));
+ GSList *l, *ins_list;
+
+ ins_list = bblocks [bb->block_num].phi_nodes;
for (l = ins_list; l; l = l->next) {
PhiNode *node = l->data;
}
}
+ /* 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);
/* 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);
}
g_free (vreg_cli_types);
g_free (pindexes);
g_free (debug_name);
- g_hash_table_destroy (phi_nodes);
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;
exception_cb (void *data)
{
MonoCompile *cfg;
+ MonoJitExceptionInfo *ei;
+ guint32 ei_len, i;
+ gpointer *type_info;
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, NULL);
+ cfg->encoded_unwind_ops = mono_unwind_decode_fde ((guint8*)data, &cfg->encoded_unwind_ops_len, NULL, &ei, &ei_len, &type_info);
+
+ 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));
+ /* Fill the rest of the information from the type info */
+ for (i = 0; i < ei_len; ++i) {
+ MonoExceptionClause *clause = type_info [i];
+
+ cfg->llvm_ex_info [i].flags = clause->flags;
+ cfg->llvm_ex_info [i].data.catch_class = clause->data.catch_class;
+ }
+
+ g_free (ei);
}
static void
LLVMAddFunction (module, "llvm.sin.f64", LLVMFunctionType (LLVMDoubleType (), params, 1, FALSE));
LLVMAddFunction (module, "llvm.cos.f64", LLVMFunctionType (LLVMDoubleType (), params, 1, FALSE));
LLVMAddFunction (module, "llvm.sqrt.f64", LLVMFunctionType (LLVMDoubleType (), params, 1, FALSE));
+
+ /* This isn't an intrinsic, instead llvm seems to special case it by name */
+ LLVMAddFunction (module, "fabs", LLVMFunctionType (LLVMDoubleType (), params, 1, FALSE));
}
{
LLVMAddFunction (module, "llvm.umul.with.overflow.i64", LLVMFunctionType (LLVMStructType (ovf_res_i64, 2, FALSE), ovf_params_i64, 2, FALSE));
}
+ {
+ LLVMTypeRef struct_ptr = LLVMPointerType (LLVMStructType (NULL, 0, FALSE), 0);
+ LLVMTypeRef invariant_start_params [] = { LLVMInt64Type (), LLVMPointerType (LLVMInt8Type (), 0) };
+ LLVMTypeRef invariant_end_params [] = { struct_ptr, LLVMInt64Type (), LLVMPointerType (LLVMInt8Type (), 0) };
+
+ LLVMAddFunction (module, "llvm.invariant.start", LLVMFunctionType (struct_ptr, invariant_start_params, 2, FALSE));
+
+ LLVMAddFunction (module, "llvm.invariant.end", LLVMFunctionType (LLVMVoidType (), invariant_end_params, 3, 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];
mono_llvm_init (void)
{
current_cfg_tls_id = TlsAlloc ();
+}
+
+static void
+init_jit_module (void)
+{
+ if (jit_module_inited)
+ return;
+
+ mono_loader_lock ();
+
+ if (jit_module_inited) {
+ mono_loader_unlock ();
+ return;
+ }
jit_module.module = LLVMModuleCreateWithName ("mono");
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);
+
+ jit_module_inited = TRUE;
+
+ mono_loader_unlock ();
}
void
mono_llvm_cleanup (void)
{
- mono_llvm_dispose_ee (ee);
+ if (ee)
+ mono_llvm_dispose_ee (ee);
- g_hash_table_destroy (jit_module.llvm_types);
+ if (jit_module.llvm_types)
+ g_hash_table_destroy (jit_module.llvm_types);
}
void
LLVMSetInitializer (aot_module.got_var, LLVMConstNull (got_type));
}
- /* Add a method to generate the 'methods' symbol needed by the AOT compiler */
+ /* Add a dummy personality function */
{
- 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);
+ LLVMBasicBlockRef lbb;
+ LLVMBuilderRef lbuilder;
+ LLVMValueRef personality;
+
+ personality = LLVMAddFunction (aot_module.module, "mono_aot_personality", LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE));
+ LLVMSetLinkage (personality, LLVMPrivateLinkage);
+ lbb = LLVMAppendBasicBlock (personality, "BB0");
+ lbuilder = LLVMCreateBuilder ();
+ LLVMPositionBuilderAtEnd (lbuilder, lbb);
+ LLVMBuildRetVoid (lbuilder);
}
aot_module.llvm_types = g_hash_table_new (NULL, NULL);
mark_as_used (aot_module.module, real_got);
+ /* Delete the dummy got so it doesn't become a global */
+ LLVMDeleteGlobal (aot_module.got_var);
+
#if 0
{
char *verifier_err;