aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/ia64/unwind-ia64.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/ia64/unwind-ia64.c')
-rw-r--r--gcc/config/ia64/unwind-ia64.c298
1 files changed, 250 insertions, 48 deletions
diff --git a/gcc/config/ia64/unwind-ia64.c b/gcc/config/ia64/unwind-ia64.c
index 99923aa9b87..ca915397f5f 100644
--- a/gcc/config/ia64/unwind-ia64.c
+++ b/gcc/config/ia64/unwind-ia64.c
@@ -35,6 +35,10 @@
#include "tsystem.h"
#include "unwind.h"
#include "unwind-ia64.h"
+#include "ia64intrin.h"
+
+/* This isn't thread safe, but nice for occasional tests. */
+#undef ENABLE_MALLOC_CHECKING
#ifndef __USING_SJLJ_EXCEPTIONS__
#define UNW_VER(x) ((x) >> 48)
@@ -121,13 +125,24 @@ struct unw_reg_info
int when; /* when the register gets saved */
};
+struct unw_reg_state {
+ struct unw_reg_state *next; /* next (outer) element on state stack */
+ struct unw_reg_info reg[UNW_NUM_REGS]; /* register save locations */
+};
+
+struct unw_labeled_state {
+ struct unw_labeled_state *next; /* next labeled state (or NULL) */
+ unsigned long label; /* label for this state */
+ struct unw_reg_state saved_state;
+};
+
typedef struct unw_state_record
{
unsigned int first_region : 1; /* is this the first region? */
unsigned int done : 1; /* are we done scanning descriptors? */
unsigned int any_spills : 1; /* got any register spills? */
unsigned int in_body : 1; /* are we inside a body? */
-
+ unsigned int no_reg_stack_frame : 1; /* Don't adjust bsp for i&l regs */
unsigned char *imask; /* imask of of spill_mask record or NULL */
unsigned long pr_val; /* predicate values */
unsigned long pr_mask; /* predicate mask */
@@ -141,11 +156,8 @@ typedef struct unw_state_record
unsigned char gr_save_loc; /* next general register to use for saving */
unsigned char return_link_reg; /* branch register for return link */
- struct unw_reg_state {
- struct unw_reg_state *next;
- unsigned long label; /* label of this state record */
- struct unw_reg_info reg[UNW_NUM_REGS];
- } curr, *stack, *reg_state_list;
+ struct unw_labeled_state *labeled_states; /* list of all labeled states */
+ struct unw_reg_state curr; /* current state */
_Unwind_Personality_Fn personality;
@@ -184,9 +196,12 @@ struct _Unwind_Context
void *lsda; /* language specific data area */
/* Preserved state. */
- unsigned long *bsp_loc; /* previous bsp save location */
+ unsigned long *bsp_loc; /* previous bsp save location
+ Appears to be write-only? */
unsigned long *bspstore_loc;
- unsigned long *pfs_loc;
+ unsigned long *pfs_loc; /* Save location for pfs in current
+ (corr. to sp) frame. Target
+ contains cfm for caller. */
unsigned long *pri_unat_loc;
unsigned long *unat_loc;
unsigned long *lc_loc;
@@ -226,28 +241,196 @@ static unsigned char const save_order[] =
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
-/* Unwind decoder routines */
+/* MASK is a bitmap describing the allocation state of emergency buffers,
+ with bit set indicating free. Return >= 0 if allocation is successful;
+ < 0 if failure. */
-static void
-push (struct unw_state_record *sr)
+static inline int
+atomic_alloc (unsigned int *mask)
+{
+ unsigned int old = *mask, ret, new;
+
+ while (1)
+ {
+ if (old == 0)
+ return -1;
+ ret = old & -old;
+ new = old & ~ret;
+ new = __sync_val_compare_and_swap (mask, old, new);
+ if (old == new)
+ break;
+ old = new;
+ }
+
+ return __builtin_ffs (ret) - 1;
+}
+
+/* Similarly, free an emergency buffer. */
+
+static inline void
+atomic_free (unsigned int *mask, int bit)
+{
+ __sync_xor_and_fetch (mask, 1 << bit);
+}
+
+
+#define SIZE(X) (sizeof(X) / sizeof(*(X)))
+#define MASK_FOR(X) ((2U << (SIZE (X) - 1)) - 1)
+#define PTR_IN(X, P) ((P) >= (X) && (P) < (X) + SIZE (X))
+
+static struct unw_reg_state emergency_reg_state[32];
+static int emergency_reg_state_free = MASK_FOR (emergency_reg_state);
+
+static struct unw_labeled_state emergency_labeled_state[8];
+static int emergency_labeled_state_free = MASK_FOR (emergency_labeled_state);
+
+#ifdef ENABLE_MALLOC_CHECKING
+static int reg_state_alloced;
+static int labeled_state_alloced;
+#endif
+
+/* Allocation and deallocation of structures. */
+
+static struct unw_reg_state *
+alloc_reg_state (void)
{
struct unw_reg_state *rs;
+#ifdef ENABLE_MALLOC_CHECKING
+ reg_state_alloced++;
+#endif
+
rs = malloc (sizeof (struct unw_reg_state));
+ if (!rs)
+ {
+ int n = atomic_alloc (&emergency_reg_state_free);
+ if (n >= 0)
+ rs = &emergency_reg_state[n];
+ }
+
+ return rs;
+}
+
+static void
+free_reg_state (struct unw_reg_state *rs)
+{
+#ifdef ENABLE_MALLOC_CHECKING
+ reg_state_alloced--;
+#endif
+
+ if (PTR_IN (emergency_reg_state, rs))
+ atomic_free (&emergency_reg_state_free, rs - emergency_reg_state);
+ else
+ free (rs);
+}
+
+static struct unw_labeled_state *
+alloc_label_state (void)
+{
+ struct unw_labeled_state *ls;
+
+#ifdef ENABLE_MALLOC_CHECKING
+ labeled_state_alloced++;
+#endif
+
+ ls = malloc(sizeof(struct unw_labeled_state));
+ if (!ls)
+ {
+ int n = atomic_alloc (&emergency_labeled_state_free);
+ if (n >= 0)
+ ls = &emergency_labeled_state[n];
+ }
+
+ return ls;
+}
+
+static void
+free_label_state (struct unw_labeled_state *ls)
+{
+#ifdef ENABLE_MALLOC_CHECKING
+ labeled_state_alloced--;
+#endif
+
+ if (PTR_IN (emergency_labeled_state, ls))
+ atomic_free (&emergency_labeled_state_free, emergency_labeled_state - ls);
+ else
+ free (ls);
+}
+
+/* Routines to manipulate the state stack. */
+
+static void
+push (struct unw_state_record *sr)
+{
+ struct unw_reg_state *rs = alloc_reg_state ();
memcpy (rs, &sr->curr, sizeof (*rs));
- rs->next = sr->stack;
- sr->stack = rs;
+ sr->curr.next = rs;
}
static void
pop (struct unw_state_record *sr)
{
- struct unw_reg_state *rs;
+ struct unw_reg_state *rs = sr->curr.next;
+
+ if (!rs)
+ abort ();
+ memcpy (&sr->curr, rs, sizeof(*rs));
+ free_reg_state (rs);
+}
+
+/* Make a copy of the state stack. Non-recursive to avoid stack overflows. */
+
+static struct unw_reg_state *
+dup_state_stack (struct unw_reg_state *rs)
+{
+ struct unw_reg_state *copy, *prev = NULL, *first = NULL;
+
+ while (rs)
+ {
+ copy = alloc_reg_state ();
+ memcpy (copy, rs, sizeof(*copy));
+ if (first)
+ prev->next = copy;
+ else
+ first = copy;
+ rs = rs->next;
+ prev = copy;
+ }
+
+ return first;
+}
+
+/* Free all stacked register states (but not RS itself). */
+static void
+free_state_stack (struct unw_reg_state *rs)
+{
+ struct unw_reg_state *p, *next;
- rs = sr->stack;
- sr->stack = rs->next;
- free (rs);
+ for (p = rs->next; p != NULL; p = next)
+ {
+ next = p->next;
+ free_reg_state (p);
+ }
+ rs->next = NULL;
+}
+
+/* Free all labeled states. */
+
+static void
+free_label_states (struct unw_labeled_state *ls)
+{
+ struct unw_labeled_state *next;
+
+ for (; ls ; ls = next)
+ {
+ next = ls->next;
+
+ free_state_stack (&ls->saved_state);
+ free_label_state (ls);
+ }
}
+
+/* Unwind decoder routines */
static enum unw_register_index __attribute__((const))
decode_abreg (unsigned char abreg, int memory)
@@ -295,8 +478,8 @@ alloc_spill_area (unsigned long *offp, unsigned long regsize,
if (reg->where == UNW_WHERE_SPILL_HOME)
{
reg->where = UNW_WHERE_PSPREL;
- reg->val = 0x10 - *offp;
- *offp += regsize;
+ *offp -= regsize;
+ reg->val = *offp;
}
}
}
@@ -330,7 +513,7 @@ finish_prologue (struct unw_state_record *sr)
/* First, resolve implicit register save locations
(see Section "11.4.2.3 Rules for Using Unwind Descriptors", rule 3). */
- for (i = 0; i < (int) sizeof(save_order); ++i)
+ for (i = 0; i < (int) sizeof (save_order); ++i)
{
reg = sr->curr.reg + save_order[i];
if (reg->where == UNW_WHERE_GR_SAVE)
@@ -363,8 +546,8 @@ finish_prologue (struct unw_state_record *sr)
mask = *cp++;
kind = (mask >> 2*(3-(t & 3))) & 3;
if (kind > 0)
- spill_next_when(&regs[kind - 1], sr->curr.reg + limit[kind - 1],
- sr->region_start + t);
+ spill_next_when (&regs[kind - 1], sr->curr.reg + limit[kind - 1],
+ sr->region_start + t);
}
}
@@ -372,12 +555,12 @@ finish_prologue (struct unw_state_record *sr)
if (sr->any_spills)
{
off = sr->spill_offset;
- alloc_spill_area(&off, 16, sr->curr.reg + UNW_REG_F2,
- sr->curr.reg + UNW_REG_F31);
- alloc_spill_area(&off, 8, sr->curr.reg + UNW_REG_B1,
- sr->curr.reg + UNW_REG_B5);
- alloc_spill_area(&off, 8, sr->curr.reg + UNW_REG_R4,
- sr->curr.reg + UNW_REG_R7);
+ alloc_spill_area (&off, 16, sr->curr.reg + UNW_REG_F2,
+ sr->curr.reg + UNW_REG_F31);
+ alloc_spill_area (&off, 8, sr->curr.reg + UNW_REG_B1,
+ sr->curr.reg + UNW_REG_B5);
+ alloc_spill_area (&off, 8, sr->curr.reg + UNW_REG_R4,
+ sr->curr.reg + UNW_REG_R7);
}
}
@@ -392,23 +575,24 @@ desc_prologue (int body, unw_word rlen, unsigned char mask,
int i;
if (!(sr->in_body || sr->first_region))
- finish_prologue(sr);
+ finish_prologue (sr);
sr->first_region = 0;
/* Check if we're done. */
- if (body && sr->when_target < sr->region_start + sr->region_len)
+ if (sr->when_target < sr->region_start + sr->region_len)
{
sr->done = 1;
return;
}
for (i = 0; i < sr->epilogue_count; ++i)
- pop(sr);
+ pop (sr);
+
sr->epilogue_count = 0;
sr->epilogue_start = UNW_WHEN_NEVER;
if (!body)
- push(sr);
+ push (sr);
sr->region_start += sr->region_len;
sr->region_len = rlen;
@@ -494,7 +678,8 @@ desc_frgr_mem (unsigned char grmask, unw_word frmask,
{
if ((frmask & 1) != 0)
{
- set_reg (sr->curr.reg + UNW_REG_F2 + i, UNW_WHERE_SPILL_HOME,
+ enum unw_register_index base = i < 4 ? UNW_REG_F2 : UNW_REG_F16 - 4;
+ set_reg (sr->curr.reg + base + i, UNW_WHERE_SPILL_HOME,
sr->region_start + sr->region_len - 1, 0);
sr->any_spills = 1;
}
@@ -631,13 +816,15 @@ desc_epilogue (unw_word t, unw_word ecount, struct unw_state_record *sr)
static inline void
desc_copy_state (unw_word label, struct unw_state_record *sr)
{
- struct unw_reg_state *rs;
+ struct unw_labeled_state *ls;
- for (rs = sr->reg_state_list; rs; rs = rs->next)
+ for (ls = sr->labeled_states; ls; ls = ls->next)
{
- if (rs->label == label)
- {
- memcpy (&sr->curr, rs, sizeof(sr->curr));
+ if (ls->label == label)
+ {
+ free_state_stack (&sr->curr);
+ memcpy (&sr->curr, &ls->saved_state, sizeof (sr->curr));
+ sr->curr.next = dup_state_stack (ls->saved_state.next);
return;
}
}
@@ -647,13 +834,15 @@ desc_copy_state (unw_word label, struct unw_state_record *sr)
static inline void
desc_label_state (unw_word label, struct unw_state_record *sr)
{
- struct unw_reg_state *rs;
+ struct unw_labeled_state *ls = alloc_label_state ();
- rs = malloc (sizeof (struct unw_reg_state));
- memcpy (rs, &sr->curr, sizeof (*rs));
- rs->label = label;
- rs->next = sr->reg_state_list;
- sr->reg_state_list = rs;
+ ls->label = label;
+ memcpy (&ls->saved_state, &sr->curr, sizeof (ls->saved_state));
+ ls->saved_state.next = dup_state_stack (sr->curr.next);
+
+ /* Insert into list of labeled states. */
+ ls->next = sr->labeled_states;
+ sr->labeled_states = ls;
}
/*
@@ -1461,8 +1650,11 @@ uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)
unsigned long *unw, header, length;
unsigned char *insn, *insn_end;
unsigned long segment_base;
+ struct unw_reg_info *r;
memset (fs, 0, sizeof (*fs));
+ for (r = fs->curr.reg; r < fs->curr.reg + UNW_NUM_REGS; ++r)
+ r->when = UNW_WHEN_NEVER;
context->lsda = 0;
ent = _Unwind_FindTableEntry ((void *) context->rp,
@@ -1518,6 +1710,14 @@ uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)
while (!fs->done && insn < insn_end)
insn = unw_decode (insn, fs->in_body, fs);
+ free_label_states (fs->labeled_states);
+ free_state_stack (&fs->curr);
+
+#ifdef ENABLE_MALLOC_CHECKING
+ if (reg_state_alloced || labeled_state_alloced)
+ abort ();
+#endif
+
/* If we're in the epilogue, sp has been restored and all values
on the memory stack below psp also have been restored. */
if (fs->when_target > fs->epilogue_start)
@@ -1578,7 +1778,7 @@ uw_update_reg_address (struct _Unwind_Context *context,
/* Note that while RVAL can only be 1-5 from normal descriptors,
we can want to look at B0 due to having manually unwound a
signal frame. */
- if (rval >= 0 && rval <= 5)
+ if (rval <= 5)
addr = context->br_loc[rval];
else
abort ();
@@ -1677,8 +1877,7 @@ uw_update_reg_address (struct _Unwind_Context *context,
context->psp = *(unsigned long *)addr;
break;
- case UNW_REG_RNAT:
- case UNW_NUM_REGS:
+ default:
abort ();
}
}
@@ -1720,7 +1919,10 @@ uw_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
/* Unwind BSP for the local registers allocated this frame. */
/* ??? What to do with stored BSP or BSPSTORE registers. */
- if (fs->when_target > fs->curr.reg[UNW_REG_PFS].when)
+ /* We assert that we are either at a call site, or we have
+ just unwound through a signal frame. In either case
+ pfs_loc is valid. */
+ if (!(fs -> no_reg_stack_frame))
{
unsigned long pfs = *context->pfs_loc;
unsigned long sol = (pfs >> 7) & 0x7f;