2009-04-03 Zoltan Varga <vargaz@gmail.com>
authorZoltan Varga <vargaz@gmail.com>
Fri, 3 Apr 2009 14:06:27 +0000 (14:06 -0000)
committerZoltan Varga <vargaz@gmail.com>
Fri, 3 Apr 2009 14:06:27 +0000 (14:06 -0000)
* 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

mono/mini/ChangeLog
mono/mini/decompose.c
mono/mini/mini-llvm-cpp.cpp
mono/mini/mini-llvm.c
mono/mini/mini.c
mono/mini/mini.h
mono/mini/ssa.c
mono/mini/unwind.c
mono/mini/unwind.h

index 3fe10956825896397b9177272ce582b6f6d8b1dc..271e41634816fd9a5538b0a0334dda702dac00ab 100644 (file)
@@ -1,5 +1,18 @@
 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.
 
index 8196e2a1a2f40c0d3238b41e09f213ffe253d0be..cd47b6789fb5bf13cbe7f505ac036907332741f0 100644 (file)
@@ -41,25 +41,25 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
        /* 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");
@@ -205,14 +205,14 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                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");
@@ -220,14 +220,14 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                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");
index ec061bd667c487d344f4d5324661cf74ae2d739d..9c04d1dbc01dfd0eb9c8826e5567c4e327500fe3 100644 (file)
@@ -21,6 +21,7 @@
 
 #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"
@@ -29,6 +30,7 @@ using namespace llvm;
 
 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
 {
@@ -163,7 +165,7 @@ static MonoJITMemoryManager *mono_mm;
 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;
 
@@ -171,7 +173,10 @@ mono_llvm_create_ee (LLVMModuleProviderRef MP, AllocCodeMemoryCb *alloc_cb, Func
   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);
 }
index 04a055f7df89728768b1101258a76ad3b0be0cec..1374a3221ed63e2306a433f1d0889e0fb26e0b0a 100644 (file)
 /* 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;
@@ -116,9 +117,7 @@ static LLVMRealPredicate fpcond_to_llvm_cond [] = {
 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);
 
@@ -611,6 +610,9 @@ mono_llvm_emit_method (MonoCompile *cfg)
        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;
@@ -647,8 +649,6 @@ mono_llvm_emit_method (MonoCompile *cfg)
        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");
 
@@ -1261,13 +1261,13 @@ mono_llvm_emit_method (MonoCompile *cfg)
                                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);
@@ -1828,8 +1828,6 @@ mono_llvm_emit_method (MonoCompile *cfg)
        /* Set by emit_cb */
        g_assert (cfg->code_len);
 
-       g_hash_table_remove (method_to_cfg, method);
-
        goto CLEANUP;
 
  FAILURE:
@@ -1840,6 +1838,8 @@ mono_llvm_emit_method (MonoCompile *cfg)
        // FIXME: Cleanup
        g_free (ctx);
 
+       TlsSetValue (current_cfg_tls_id, NULL);
+
        mono_loader_unlock ();
 }
 
@@ -1848,8 +1848,8 @@ alloc_cb (LLVMValueRef function, int 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);
+
        if (cfg) {
                // FIXME: dynamic
                return mono_domain_code_reserve (cfg->domain, size);
@@ -1863,21 +1863,38 @@ emitted_cb (LLVMValueRef function, void *start, void *end)
 {
        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 */
        {
index 8f4a5ac51cc45e499b838c920053c9eef2e29a24..2908b78b85398a14e74ed129104ccd1cfce170be 100644 (file)
@@ -3799,7 +3799,10 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
         * 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);
 
index 139cbbdce74fdb9d82103bd2ffcc9b87ed3ff825..a2d044cfdb89295f38f3b23647575045cb29d352 100644 (file)
@@ -54,6 +54,12 @@ typedef gint64 mgreg_t;
 #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
@@ -914,6 +920,8 @@ typedef struct {
        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 */
index 799aa892a34f1328bf169ff83b04f91851e692c7..1d166a9cff7b6039cef0fcdd5856f171ecec1a28 100644 (file)
@@ -407,7 +407,7 @@ mono_ssa_compute (MonoCompile *cfg)
 
                        /* 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)) {
index a54d2ed1473f502abdb2e3c2335b4e888faf3315..217e86fa0bc05a62310d2c08fda0c52de8013bed 100644 (file)
@@ -144,6 +144,31 @@ decode_uleb128 (guint8 *buf, guint8 **endbuf)
        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:
  *
@@ -286,6 +311,10 @@ mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len,
                        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 ();
                        }
@@ -473,3 +502,140 @@ mono_unwind_get_dwarf_pc_reg (void)
 {
        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);
+}
index cb8f8df6dc75bb852294918eb2a04716ea25c990..004898478aba9201380c13929304a6dd82c3063e 100644 (file)
@@ -111,4 +111,6 @@ guint32 mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len) MO
 
 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