diff options
Diffstat (limited to 'gcc/config/ia64/unwind-ia64.c')
-rw-r--r-- | gcc/config/ia64/unwind-ia64.c | 298 |
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(®s[kind - 1], sr->curr.reg + limit[kind - 1], - sr->region_start + t); + spill_next_when (®s[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; |