* mini.h (COMPILE_LLVM): New macro.
* decompose.c (mono_decompose_opcode): Use the COMPILE_LLVM macro.
* ssa.c (mono_ssa_compute): Ditto.
* unwind.c (mono_unwind_get_ops_from_fde): New helper function to extract
the unwind ops from a DWARF FDE.
* mini-llvm.c: Implement generation of unwind info for LLVM compiled
methods by extracting the dwarf unwind ops from the unwind info generated
by LLVM.
svn path=/trunk/mono/; revision=130967
2009-04-03 Zoltan Varga <vargaz@gmail.com>
+ * mini.h (COMPILE_LLVM): New macro.
+
+ * decompose.c (mono_decompose_opcode): Use the COMPILE_LLVM macro.
+
+ * ssa.c (mono_ssa_compute): Ditto.
+
+ * unwind.c (mono_unwind_get_ops_from_fde): New helper function to extract
+ the unwind ops from a DWARF FDE.
+
+ * mini-llvm.c: Implement generation of unwind info for LLVM compiled
+ methods by extracting the dwarf unwind ops from the unwind info generated
+ by LLVM.
+
* mini-llvm.c (mono_llvm_emit_method): Enable support for non-IMT virtual
calls.
/* this doesn't make sense on ppc and other architectures */
#if !defined(MONO_ARCH_NO_IOV_CHECK)
case OP_IADD_OVF:
- if (cfg->compile_llvm)
+ if (COMPILE_LLVM (cfg))
break;
ins->opcode = OP_IADDCC;
MONO_EMIT_NEW_COND_EXC (cfg, IOV, "OverflowException");
break;
case OP_IADD_OVF_UN:
- if (cfg->compile_llvm)
+ if (COMPILE_LLVM (cfg))
break;
ins->opcode = OP_IADDCC;
MONO_EMIT_NEW_COND_EXC (cfg, IC, "OverflowException");
break;
case OP_ISUB_OVF:
- if (cfg->compile_llvm)
+ if (COMPILE_LLVM (cfg))
break;
ins->opcode = OP_ISUBCC;
MONO_EMIT_NEW_COND_EXC (cfg, IOV, "OverflowException");
break;
case OP_ISUB_OVF_UN:
- if (cfg->compile_llvm)
+ if (COMPILE_LLVM (cfg))
break;
ins->opcode = OP_ISUBCC;
MONO_EMIT_NEW_COND_EXC (cfg, IC, "OverflowException");
break;
#else
case OP_LADD_OVF:
- if (cfg->compile_llvm)
+ if (COMPILE_LLVM (cfg))
break;
MONO_EMIT_NEW_BIALU (cfg, OP_ADDCC, ins->dreg, ins->sreg1, ins->sreg2);
MONO_EMIT_NEW_COND_EXC (cfg, OV, "OverflowException");
ins->opcode = OP_NOP;
break;
case OP_LADD_OVF_UN:
- if (cfg->compile_llvm)
+ if (COMPILE_LLVM (cfg))
break;
MONO_EMIT_NEW_BIALU (cfg, OP_ADDCC, ins->dreg, ins->sreg1, ins->sreg2);
MONO_EMIT_NEW_COND_EXC (cfg, C, "OverflowException");
break;
#ifndef __mono_ppc64__
case OP_LSUB_OVF:
- if (cfg->compile_llvm)
+ if (COMPILE_LLVM (cfg))
break;
MONO_EMIT_NEW_BIALU (cfg, OP_SUBCC, ins->dreg, ins->sreg1, ins->sreg2);
MONO_EMIT_NEW_COND_EXC (cfg, OV, "OverflowException");
ins->opcode = OP_NOP;
break;
case OP_LSUB_OVF_UN:
- if (cfg->compile_llvm)
+ if (COMPILE_LLVM (cfg))
break;
MONO_EMIT_NEW_BIALU (cfg, OP_SUBCC, ins->dreg, ins->sreg1, ins->sreg2);
MONO_EMIT_NEW_COND_EXC (cfg, C, "OverflowException");
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/JITMemoryManager.h>
+#include <llvm/Target/TargetOptions.h>
#include "llvm-c/Core.h"
#include "llvm-c/ExecutionEngine.h"
typedef unsigned char * (AllocCodeMemoryCb) (LLVMValueRef function, int size);
typedef void (FunctionEmittedCb) (LLVMValueRef function, void *start, void *end);
+typedef void (ExceptionTableCb) (void *data);
class MonoJITMemoryManager : public JITMemoryManager
{
extern "C" {
LLVMExecutionEngineRef
-mono_llvm_create_ee (LLVMModuleProviderRef MP, AllocCodeMemoryCb *alloc_cb, FunctionEmittedCb *emitted_cb)
+mono_llvm_create_ee (LLVMModuleProviderRef MP, AllocCodeMemoryCb *alloc_cb, FunctionEmittedCb *emitted_cb, ExceptionTableCb *exception_cb)
{
std::string Error;
mono_mm->alloc_cb = alloc_cb;
mono_mm->emitted_cb = emitted_cb;
+ ExceptionHandling = true;
+
ExecutionEngine *EE = ExecutionEngine::createJIT (unwrap (MP), &Error, mono_mm, false);
+ EE->InstallExceptionTableRegister (exception_cb);
return wrap(EE);
}
/* FIXME: Move these to an include file */
typedef unsigned char * (AllocCodeMemoryCb) (LLVMValueRef function, int size);
typedef void (FunctionEmittedCb) (LLVMValueRef function, void *start, void *end);
+typedef void (ExceptionTableCb) (void *data);
LLVMExecutionEngineRef
-mono_llvm_create_ee (LLVMModuleProviderRef MP, AllocCodeMemoryCb *alloc_cb, FunctionEmittedCb *emitted_cb);
+mono_llvm_create_ee (LLVMModuleProviderRef MP, AllocCodeMemoryCb *alloc_cb, FunctionEmittedCb *emitted_cb, ExceptionTableCb *exception_cb);
typedef struct {
MonoMemPool *mempool;
static LLVMModuleRef module;
static LLVMExecutionEngineRef ee;
static GHashTable *llvm_types;
-
-/* A hashtable mapping LLVM methods to MonoCompile's */
-static GHashTable *method_to_cfg;
+static guint32 current_cfg_tls_id;
static void mono_llvm_init (void);
if (!ee)
mono_llvm_init ();
+ /* Used to communicate with the callbacks */
+ TlsSetValue (current_cfg_tls_id, cfg);
+
ctx = g_new0 (EmitContext, 1);
ctx->cfg = cfg;
ctx->mempool = cfg->mempool;
method = LLVMAddFunction (module, method_name, method_type);
ctx->lmethod = method;
- g_hash_table_insert (method_to_cfg, method, cfg);
-
if (cfg->method->save_lmf)
LLVM_FAILURE (ctx, "lmf");
values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), get_tempname (ctx));
break;
case OP_FCONV_TO_U1:
- values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), get_tempname (ctx));
+ values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), get_tempname (ctx));
break;
case OP_FCONV_TO_I2:
values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), get_tempname (ctx));
break;
case OP_FCONV_TO_U2:
- values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), get_tempname (ctx));
+ values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), get_tempname (ctx));
break;
case OP_FCONV_TO_I8:
values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMInt64Type (), dname);
/* Set by emit_cb */
g_assert (cfg->code_len);
- g_hash_table_remove (method_to_cfg, method);
-
goto CLEANUP;
FAILURE:
// FIXME: Cleanup
g_free (ctx);
+ TlsSetValue (current_cfg_tls_id, NULL);
+
mono_loader_unlock ();
}
{
MonoCompile *cfg;
- /* This is called while holding the loader lock */
- cfg = g_hash_table_lookup (method_to_cfg, function);
+ cfg = TlsGetValue (current_cfg_tls_id);
+
if (cfg) {
// FIXME: dynamic
return mono_domain_code_reserve (cfg->domain, size);
{
MonoCompile *cfg;
- /* This is called while holding the loader lock */
- cfg = g_hash_table_lookup (method_to_cfg, function);
+ cfg = TlsGetValue (current_cfg_tls_id);
g_assert (cfg);
cfg->code_len = (guint8*)end - (guint8*)start;
}
+static void
+exception_cb (void *data)
+{
+ MonoCompile *cfg;
+
+ cfg = TlsGetValue (current_cfg_tls_id);
+ g_assert (cfg);
+
+ /*
+ * data points to a DWARF FDE structure, convert it to our unwind format and
+ * save it.
+ * An alternative would be to save it directly, and modify our unwinder to work
+ * with it.
+ */
+ cfg->encoded_unwind_ops = mono_unwind_get_ops_from_fde ((guint8*)data, &cfg->encoded_unwind_ops_len);
+}
+
static void
mono_llvm_init (void)
{
+ current_cfg_tls_id = TlsAlloc ();
+
module = LLVMModuleCreateWithName ("mono");
- ee = mono_llvm_create_ee (LLVMCreateModuleProviderForExistingModule (module), alloc_cb, emitted_cb);
+ ee = mono_llvm_create_ee (LLVMCreateModuleProviderForExistingModule (module), alloc_cb, emitted_cb, exception_cb);
llvm_types = g_hash_table_new (NULL, NULL);
- method_to_cfg = g_hash_table_new (NULL, NULL);
/* Emit declarations of instrinsics */
{
* using it during runtime, hence the define.
*/
#ifdef MONO_ARCH_HAVE_XP_UNWIND
- if (cfg->unwind_ops) {
+ if (cfg->encoded_unwind_ops) {
+ jinfo->used_regs = mono_cache_unwind_info (cfg->encoded_unwind_ops, cfg->encoded_unwind_ops_len);
+ g_free (cfg->encoded_unwind_ops);
+ } else if (cfg->unwind_ops) {
guint32 info_len;
guint8 *unwind_info = mono_unwind_ops_encode (cfg->unwind_ops, &info_len);
#define MINI_DEBUG(level,limit,code) do {if (G_UNLIKELY ((level) >= (limit))) code} while (0)
#endif
+#if ENABLE_LLVM
+#define COMPILE_LLVM(cfg) ((cfg)->compile_llvm)
+#else
+#define COMPILE_LLVM(cfg) (0)
+#endif
+
#define NOT_IMPLEMENTED do { g_assert_not_reached (); } while (0)
#ifndef DISABLE_AOT
char* exception_message;
gpointer exception_ptr;
+ guint8 * encoded_unwind_ops;
+ guint32 encoded_unwind_ops_len;
GSList* unwind_ops;
/* Fields used by the local reg allocator */
/* fixme: create pruned SSA? we would need liveness information for that */
- if (bb == cfg->bb_exit && !cfg->compile_llvm)
+ if (bb == cfg->bb_exit && !COMPILE_LLVM (cfg))
continue;
if ((cfg->comp_done & MONO_COMP_LIVENESS) && !mono_bitset_test_fast (bb->live_in_set, i)) {
return res;
}
+static inline gint32
+decode_sleb128 (guint8 *buf, guint8 **endbuf)
+{
+ guint8 *p = buf;
+ gint32 res = 0;
+ int shift = 0;
+
+ while (TRUE) {
+ guint8 b = *p;
+ p ++;
+
+ res = res | (((int)(b & 0x7f)) << shift);
+ shift += 7;
+ if (!(b & 0x80)) {
+ if (shift < 32 && (b & 0x40))
+ res |= - (1 << shift);
+ break;
+ }
+ }
+
+ *endbuf = p;
+
+ return res;
+}
+
/*
* mono_unwind_ops_encode:
*
case DW_CFA_def_cfa_register:
cfa_reg = mono_dwarf_reg_to_hw_reg (decode_uleb128 (p, &p));
break;
+ case DW_CFA_advance_loc4:
+ pos += *(guint32*)p;
+ p += 4;
+ break;
default:
g_assert_not_reached ();
}
{
return DWARF_PC_REG;
}
+
+static void
+decode_cie_op (guint8 *p, guint8 **endp)
+{
+ int op = *p & 0xc0;
+
+ switch (op) {
+ case DW_CFA_advance_loc:
+ p ++;
+ break;
+ case DW_CFA_offset:
+ p ++;
+ decode_uleb128 (p, &p);
+ break;
+ case 0: {
+ int ext_op = *p;
+ p ++;
+ switch (ext_op) {
+ case DW_CFA_def_cfa:
+ decode_uleb128 (p, &p);
+ decode_uleb128 (p, &p);
+ break;
+ case DW_CFA_def_cfa_offset:
+ decode_uleb128 (p, &p);
+ break;
+ case DW_CFA_def_cfa_register:
+ decode_uleb128 (p, &p);
+ break;
+ case DW_CFA_advance_loc4:
+ p += 4;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ }
+
+ *endp = p;
+}
+
+/*
+ * mono_unwind_get_ops_from_fde:
+ *
+ * Return the unwind opcodes encoded in a DWARF FDE entry.
+ */
+guint8*
+mono_unwind_get_ops_from_fde (guint8 *fde, guint32 *out_len)
+{
+ guint8 *p, *cie, *code, *fde_cfi, *cie_cfi;
+ gint32 fde_len, cie_offset, pc_begin, pc_range, aug_len, fde_data_len;
+ gint32 cie_len, cie_id, cie_version, code_align, data_align, return_reg;
+ gint32 i, cie_aug_len, buf_len;
+ char *cie_aug_str;
+ guint8 *buf;
+
+ /*
+ * http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
+ */
+
+ /* Decode FDE */
+
+ p = fde;
+ // FIXME: Endianess ?
+ fde_len = *(guint32*)p;
+ g_assert (fde_len != 0xffffffff && fde_len != 0);
+ p += 4;
+ cie_offset = *(guint32*)p;
+ cie = p - cie_offset;
+ p += 4;
+ pc_begin = *(gint32*)p;
+ code = p + pc_begin;
+ p += 4;
+ pc_range = *(guint32*)p;
+ p += 4;
+ aug_len = decode_uleb128 (p, &p);
+ g_assert (aug_len == 0);
+ fde_cfi = p;
+ fde_data_len = fde + 4 + fde_len - p;
+
+ /* Decode CIE */
+ p = cie;
+ cie_len = *(guint32*)p;
+ p += 4;
+ cie_id = *(guint32*)p;
+ g_assert (cie_id == 0);
+ p += 4;
+ cie_version = *p;
+ g_assert (cie_version == 1);
+ p += 1;
+ cie_aug_str = (char*)p;
+ p += strlen (cie_aug_str) + 1;
+ code_align = decode_uleb128 (p, &p);
+ data_align = decode_sleb128 (p, &p);
+ return_reg = decode_uleb128 (p, &p);
+ if (strstr (cie_aug_str, "z")) {
+ cie_aug_len = decode_uleb128 (p, &p);
+ p += cie_aug_len;
+ }
+ cie_cfi = p;
+
+ /* Make sure the FDE uses the same constants as we do */
+ g_assert (code_align == 1);
+ g_assert (data_align == DWARF_DATA_ALIGN);
+ g_assert (return_reg == DWARF_PC_REG);
+
+ buf_len = (cie + cie_len + 4 - cie_cfi) + (fde + fde_len + 4 - fde_cfi);
+ buf = g_malloc0 (buf_len);
+
+ i = 0;
+ p = cie_cfi;
+ while (p < cie + cie_len + 4) {
+ if (*p == DW_CFA_nop)
+ break;
+ else
+ decode_cie_op (p, &p);
+ }
+ memcpy (buf + i, cie_cfi, p - cie_cfi);
+ i += p - cie_cfi;
+
+ p = fde_cfi;
+ while (p < fde + fde_len + 4) {
+ if (*p == DW_CFA_nop)
+ break;
+ else
+ decode_cie_op (p, &p);
+ }
+ memcpy (buf + i, fde_cfi, p - fde_cfi);
+ i += p - fde_cfi;
+ g_assert (i <= buf_len);
+
+ *out_len = i;
+
+ return g_realloc (buf, i);
+}
guint8* mono_get_cached_unwind_info (guint32 index, guint32 *unwind_info_len) MONO_INTERNAL;
+guint8* mono_unwind_get_ops_from_fde (guint8 *fde, guint32 *out_len) MONO_INTERNAL;
+
#endif