2008-11-15 Zoltan Varga <vargaz@gmail.com>
authorZoltan Varga <vargaz@gmail.com>
Sat, 15 Nov 2008 20:35:59 +0000 (20:35 -0000)
committerZoltan Varga <vargaz@gmail.com>
Sat, 15 Nov 2008 20:35:59 +0000 (20:35 -0000)
* unwind.h: New file, contains definitions for stack unwinding.

* mini.c (mono_emit_unwind_op): New helper function to append an unwind op
to cfg->unwind_ops.

* aot-compiler.c: Generalize the emitting of unwind information to use the
information in cfg->unwind_ops.

* mini-amd64.c (mono_arch_emit_prolog): Emit unwind info.

svn path=/trunk/mono/; revision=118941

mono/mini/ChangeLog
mono/mini/Makefile.am
mono/mini/aot-compiler.c
mono/mini/mini-amd64.c
mono/mini/unwind.h [new file with mode: 0644]

index 3e34fd358aeeedfe59809cfd5e78915ed53dcc81..d82f52397253fb04de815630061a488fb2539c36 100644 (file)
@@ -1,5 +1,15 @@
 2008-11-15  Zoltan Varga  <vargaz@gmail.com>
 
+       * unwind.h: New file, contains definitions for stack unwinding.
+
+       * mini.c (mono_emit_unwind_op): New helper function to append an unwind op
+       to cfg->unwind_ops.
+       
+       * aot-compiler.c: Generalize the emitting of unwind information to use the
+       information in cfg->unwind_ops.
+
+       * mini-amd64.c (mono_arch_emit_prolog): Emit unwind info.
+
        * aot-compiler.c: Emit dwarf unwind information so gdb can unwind through
        AOT method frames. Enable writing symbols for methods by default.
 
index 45f6ed62406ae9686c96bf294aeec615003ff848..dffde6cee4c2ee6c4197a01a492a8c277c90b6c4 100644 (file)
@@ -261,7 +261,8 @@ common_sources = \
        abcremoval2.c   \
        regalloc2.c     \
        simd-methods.h  \
-       simd-intrinsics.c
+       simd-intrinsics.c       \
+       unwind.h
 
 test_sources =                 \
        basic-calls.cs  \
index 6fbde21416d17ef8b69797d7b9bc9f12a0891a70..5c76cedb76f31058defda73ee8914bd3cb83370c 100644 (file)
@@ -58,6 +58,7 @@
 #include <mono/metadata/gc-internal.h>
 #include <mono/metadata/method-builder.h>
 #include <mono/metadata/monitor.h>
+#include <mono/metadata/mempool-internals.h>
 #include <mono/utils/mono-logger.h>
 #include <mono/utils/mono-compiler.h>
 #include <mono/utils/mono-time.h>
@@ -4056,6 +4057,19 @@ compile_method (MonoAotCompile *acfg, MonoMethod *method)
                }
                cfg->patch_info = patches;
        }
+       /* Make a copy of the unwind info */
+       {
+               GSList *l, *unwind_ops;
+               MonoUnwindOp *op;
+
+               unwind_ops = NULL;
+               for (l = cfg->unwind_ops; l; l = l->next) {
+                       op = mono_mempool_alloc (acfg->mempool, sizeof (MonoUnwindOp));
+                       memcpy (op, l->data, sizeof (MonoUnwindOp));
+                       unwind_ops = g_slist_prepend_mempool (acfg->mempool, unwind_ops, op);
+               }
+               cfg->unwind_ops = g_slist_reverse (unwind_ops);
+       }
 
        /* Free some fields used by cfg to conserve memory */
        mono_mempool_destroy (cfg->mempool);
@@ -4941,37 +4955,6 @@ emit_globals (MonoAotCompile *acfg)
 /*   Emitting DWARF debug information    */
 /*****************************************/
 
-/* The low 6 bits contain additional information */
-#define DW_CFA_advance_loc        0x40
-#define DW_CFA_offset             0x80
-#define DW_CFA_restore            0xc0
-
-#define DW_CFA_nop              0x00
-#define DW_CFA_set_loc          0x01
-#define DW_CFA_advance_loc1     0x02
-#define DW_CFA_advance_loc2     0x03
-#define DW_CFA_advance_loc4     0x04
-#define DW_CFA_offset_extended  0x05
-#define DW_CFA_restore_extended 0x06
-#define DW_CFA_undefined        0x07
-#define DW_CFA_same_value       0x08
-#define DW_CFA_register         0x09
-#define DW_CFA_remember_state   0x0a
-#define DW_CFA_restore_state    0x0b
-#define DW_CFA_def_cfa          0x0c
-#define DW_CFA_def_cfa_register 0x0d
-#define DW_CFA_def_cfa_offset   0x0e
-#define DW_CFA_def_cfa_expression 0x0f
-#define DW_CFA_expression       0x10
-#define DW_CFA_offset_extended_sf 0x11
-#define DW_CFA_def_cfa_sf       0x12
-#define DW_CFA_def_cfa_offset_sf 0x13
-#define DW_CFA_val_offset        0x14
-#define DW_CFA_val_offset_sf     0x15
-#define DW_CFA_val_expression    0x16
-#define DW_CFA_lo_user           0x1c
-#define DW_CFA_hi_user           0x3f
-
 static G_GNUC_UNUSED void
 emit_uleb128 (MonoAotCompile *acfg, guint32 value)
 {
@@ -5012,6 +4995,10 @@ emit_sleb128 (MonoAotCompile *acfg, gint32 value)
        }
 }
 
+#ifdef __x86_64__
+static int hw_reg_to_dwarf_reg [] = { 0, 2, 1, 3, 7, 6, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+#endif
+
 static void
 emit_dwarf_info (MonoAotCompile *acfg)
 {
@@ -5036,8 +5023,7 @@ emit_dwarf_info (MonoAotCompile *acfg)
 
 #ifdef __x86_64__
        emit_byte (acfg, DW_CFA_def_cfa);
-       /* For some reason, 7 is %rsp */
-       emit_uleb128 (acfg, 7); /* reg=%rsp */
+       emit_uleb128 (acfg, hw_reg_to_dwarf_reg [AMD64_RSP]);
        emit_uleb128 (acfg, 8); /* offset=8 */
        emit_byte (acfg, DW_CFA_offset | AMD64_RIP);
        emit_uleb128 (acfg, 1); /* offset=-8 */
@@ -5051,6 +5037,9 @@ emit_dwarf_info (MonoAotCompile *acfg)
        /* DIEs for methods */
        for (i = 0; i < acfg->nmethods; ++i) {
                MonoCompile *cfg = acfg->cfgs [i];
+               GSList *l;
+               MonoUnwindOp *op;
+               int loc;
 
                if (!cfg)
                        continue;
@@ -5064,16 +5053,53 @@ emit_dwarf_info (MonoAotCompile *acfg)
                emit_symbol_diff (acfg, symbol2, symbol, 0); /* address_range */
                emit_int32 (acfg, 0);
 
+               /* Convert the list of MonoUnwindOps to the format used by DWARF */
+               loc = 0;
+               l = cfg->unwind_ops;
 #ifdef __x86_64__
-               // FIXME:
-               if (cfg->arch.omit_fp && cfg->arch.stack_alloc_size < 127) {
-                       emit_byte (acfg, DW_CFA_advance_loc | 4); /* size of alu_reg_imm () */
-                       emit_byte (acfg, DW_CFA_def_cfa_offset);
-                       emit_uleb128 (acfg, cfg->arch.stack_alloc_size + 8);
-               }
+               /* Skip the first two ops which are in the CIE */
+               l = l->next->next;
+#endif
+               for (; l; l = l->next) {
+                       op = l->data;
+
+                       /* Convert the register from the hw encoding to the dwarf encoding */
+#ifdef __x86_64__
+                       op->reg = hw_reg_to_dwarf_reg [op->reg];
 #else
-               g_assert_not_reached ();
+                       g_assert_not_reached ();
 #endif
+                       /* Emit an advance_loc if neccesary */
+                       if (op->when > loc) {
+                               g_assert (op->when - loc < 32);
+                               emit_byte (acfg, DW_CFA_advance_loc | (op->when - loc));
+                       }                       
+
+                       switch (op->op) {
+                       case DW_CFA_def_cfa:
+                               emit_byte (acfg, op->op);
+                               emit_uleb128 (acfg, op->reg);
+                               emit_uleb128 (acfg, op->val);
+                               break;
+                       case DW_CFA_def_cfa_offset:
+                               emit_byte (acfg, op->op);
+                               emit_uleb128 (acfg, op->val);
+                               break;
+                       case DW_CFA_def_cfa_register:
+                               emit_byte (acfg, op->op);
+                               emit_uleb128 (acfg, op->reg);
+                               break;
+                       case DW_CFA_offset:
+                               emit_byte (acfg, DW_CFA_offset | op->reg);
+                               emit_uleb128 (acfg, op->val / - 8);
+                               break;
+                       default:
+                               g_assert_not_reached ();
+                               break;
+                       }
+
+                       loc = op->when;
+               }
 
                emit_alignment (acfg, sizeof (gpointer));
                sprintf (symbol, ".Ldie%d_end", i);
index 7d56de0cf37e71f4f39e99f3b4f1ce2a292feff3..a4a8d3de9afe3e9c2912903a9608246ff4e207b8 100644 (file)
@@ -4850,7 +4850,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        MonoBasicBlock *bb;
        MonoMethodSignature *sig;
        MonoInst *ins;
-       int alloc_size, pos, max_offset, i, quad, max_epilog_size;
+       int alloc_size, pos, max_offset, i, cfa_offset, quad, max_epilog_size;
        guint8 *code;
        CallInfo *cinfo;
        gint32 lmf_offset = cfg->arch.lmf_offset;
@@ -4867,6 +4867,9 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        /* Amount of stack space allocated by register saving code */
        pos = 0;
 
+       /* Offset between RSP and the CFA */
+       cfa_offset = 0;
+
        /* 
         * The prolog consists of the following parts:
         * FP present:
@@ -4882,16 +4885,25 @@ mono_arch_emit_prolog (MonoCompile *cfg)
         * - save callee saved regs using moves
         */
 
+       // CFA = sp + 8
+       cfa_offset = 8;
+       mono_emit_unwind_op_def_cfa (cfg, code, AMD64_RSP, 8);
+       // IP saved at CFA - 8
+       mono_emit_unwind_op_offset (cfg, code, AMD64_RIP, -cfa_offset);
        async_exc_point (code);
 
        if (!cfg->arch.omit_fp) {
                amd64_push_reg (code, AMD64_RBP);
+               cfa_offset += 8;
+               mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+               mono_emit_unwind_op_offset (cfg, code, AMD64_RBP, - cfa_offset);
                async_exc_point (code);
 #ifdef PLATFORM_WIN32
                mono_arch_unwindinfo_add_push_nonvol (&cfg->arch.unwindinfo, cfg->native_code, code, AMD64_RBP);
 #endif
                
                amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof (gpointer));
+               mono_emit_unwind_op_def_cfa_reg (cfg, code, AMD64_RBP);
                async_exc_point (code);
 #ifdef PLATFORM_WIN32
                mono_arch_unwindinfo_add_set_fpreg (&cfg->arch.unwindinfo, cfg->native_code, code, AMD64_RBP);
@@ -4900,10 +4912,14 @@ mono_arch_emit_prolog (MonoCompile *cfg)
 
        /* Save callee saved registers */
        if (!cfg->arch.omit_fp && !method->save_lmf) {
+               int offset = cfa_offset;
+
                for (i = 0; i < AMD64_NREG; ++i)
                        if (AMD64_IS_CALLEE_SAVED_REG (i) && (cfg->used_int_regs & (1 << i))) {
                                amd64_push_reg (code, i);
                                pos += sizeof (gpointer);
+                               offset += 8;
+                               mono_emit_unwind_op_offset (cfg, code, i, - offset);
                                async_exc_point (code);
                        }
        }
@@ -4932,6 +4948,10 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                guint32 remaining_size = alloc_size;
                while (remaining_size >= 0x1000) {
                        amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 0x1000);
+                       if (cfg->arch.omit_fp) {
+                               cfa_offset += 0x1000;
+                               mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+                       }
                        async_exc_point (code);
 #ifdef PLATFORM_WIN32
                        if (cfg->arch.omit_fp) 
@@ -4943,7 +4963,11 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                }
                if (remaining_size) {
                        amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, remaining_size);
-                       async_exc_point (code);
+                       if (cfg->arch.omit_fp) {
+                               cfa_offset += remaining_size;
+                               mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+                               async_exc_point (code);
+                       }
 #ifdef PLATFORM_WIN32
                        if (cfg->arch.omit_fp) 
                                mono_arch_unwindinfo_add_alloc_stack (&cfg->arch.unwindinfo, cfg->native_code, code, remaining_size);
@@ -4951,7 +4975,11 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                }
 #else
                amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, alloc_size);
-               async_exc_point (code);
+               if (cfg->arch.omit_fp) {
+                       cfa_offset += alloc_size;
+                       mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+                       async_exc_point (code);
+               }
 #endif
        }
 
@@ -4992,6 +5020,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                for (i = 0; i < AMD64_NREG; ++i)
                        if (AMD64_IS_CALLEE_SAVED_REG (i) && (cfg->used_int_regs & (1 << i))) {
                                amd64_mov_membase_reg (code, AMD64_RSP, save_area_offset, i, 8);
+                               mono_emit_unwind_op_offset (cfg, code, i, - (cfa_offset - save_area_offset));
                                save_area_offset += 8;
                                async_exc_point (code);
                        }
diff --git a/mono/mini/unwind.h b/mono/mini/unwind.h
new file mode 100644 (file)
index 0000000..55475c3
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * unwind.h: Stack Unwinding Interface
+ *
+ * Authors:
+ *   Zoltan Varga (vargaz@gmail.com)
+ *
+ * (C) 2007 Novell, Inc.
+ */
+
+#ifndef __MONO_UNWIND_H__
+#define __MONO_UNWIND_H__
+
+#include "mini.h"
+
+/*
+ * This is a platform-independent interface for unwinding through stack frames 
+ * based on the Dwarf unwinding interface.
+ * See http://dwarfstd.org/Dwarf3.pdf, section "Call Frame Information".
+ * Currently, this is only used for emitting unwind info in AOT files.
+ */
+
+/* CFA = Canonical Frame Address */
+
+/* Unwind ops */
+
+/* The low 6 bits contain additional information */
+#define DW_CFA_advance_loc        0x40
+#define DW_CFA_offset             0x80
+#define DW_CFA_restore            0xc0
+
+#define DW_CFA_nop              0x00
+#define DW_CFA_set_loc          0x01
+#define DW_CFA_advance_loc1     0x02
+#define DW_CFA_advance_loc2     0x03
+#define DW_CFA_advance_loc4     0x04
+#define DW_CFA_offset_extended  0x05
+#define DW_CFA_restore_extended 0x06
+#define DW_CFA_undefined        0x07
+#define DW_CFA_same_value       0x08
+#define DW_CFA_register         0x09
+#define DW_CFA_remember_state   0x0a
+#define DW_CFA_restore_state    0x0b
+#define DW_CFA_def_cfa          0x0c
+#define DW_CFA_def_cfa_register 0x0d
+#define DW_CFA_def_cfa_offset   0x0e
+#define DW_CFA_def_cfa_expression 0x0f
+#define DW_CFA_expression       0x10
+#define DW_CFA_offset_extended_sf 0x11
+#define DW_CFA_def_cfa_sf       0x12
+#define DW_CFA_def_cfa_offset_sf 0x13
+#define DW_CFA_val_offset        0x14
+#define DW_CFA_val_offset_sf     0x15
+#define DW_CFA_val_expression    0x16
+#define DW_CFA_lo_user           0x1c
+#define DW_CFA_hi_user           0x3f
+
+/* Represents one unwind instruction */
+typedef struct {
+       guint8 op; /* One of DW_CFA_... */
+       guint8 reg; /* register number in the hardware encoding */
+       guint32 val; /* arbitrary value */
+       guint32 when; /* The offset _after_ the cpu instruction this unwind op belongs to */
+} MonoUnwindOp;
+
+/* 
+ * Macros for emitting MonoUnwindOp structures.
+ * These should be called _after_ emitting the cpu instruction the unwind op
+ * belongs to.
+ */
+
+/* Set cfa to reg+offset */
+#define mono_emit_unwind_op_def_cfa(cfg,ip,reg,offset) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_def_cfa, (reg), (offset))
+/* Set cfa to reg+existing offset */
+#define mono_emit_unwind_op_def_cfa_reg(cfg,ip,reg) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_def_cfa_register, (reg), (0))
+/* Set cfa to existing reg+offset */
+#define mono_emit_unwind_op_def_cfa_offset(cfg,ip,offset) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_def_cfa_offset, (0), (offset))
+/* Reg is the same as it was on enter to the function */
+#define mono_emit_unwind_op_same_value(cfg,ip,reg) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_same_value, (reg), 0)
+/* Reg is saved at cfa+offset */
+#define mono_emit_unwind_op_offset(cfg,ip,reg,offset) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_offset, (reg), (offset))
+
+#endif