IA-64 unwinder patch

Andrew Haley aph@pasanda.cygnus.co.uk
Fri May 12 10:28:00 GMT 2000


This is the rest of the IA-64 unwinder patch, mostly written by Andrew
MacLeod.

Jim Wilson has already commented on this patch, and I intend to make
alterations based on his comments and resubmit.  This posting is to
give others an opportunity to comment.

The biggest problem Jim found was the organization of frame.c and
ia64-frame.c and the name visibility therein.  The issue is that ia64
and dwarf2 unwinders share many functions that we don't want to be
visible outside the unwinder.  To solve this I intend to split out the
DWARF specific functions from frame.c and move them into
frame-dwarf2.c.  I could then #include frame.c in frame-dwarf2.c,
which would allow me to keep all the internal functions static.  I'll
play around with this to see what works best.

We don't need the crtstuff.c patch anymore, as IA-64 does not use this
file.  I'll remove it.

The config/ia64/ia64-frame.c file says it was contributed by Jason
Merrill.  I'll change this to reflect reality more closely.

Jim doesn't like the explicit stop bit in the flushrs pattern.  It
would be better if rtx_needs_barrier took care of this, but this is
something that can be fixed later.  I'll add a ??? comment to ia64.md.

Andrew.

2000-05-05  Andrew MacLeod  <amacleod@cygnus.com>
            Andrew Haley  <aph@cygnus.com>

	* except.c  (func_eh_entry): Add emitted field.
	(new_eh_region_entry): Set emitted field to 0;
	(output_exception_table_entry): Only emit previouslt un-emitted data,
	and send it to the eh_data section.
	(output_exception_table): Break out common parts. Output
	exception table for entire compilation unit to eh_data section.
	(output_exception_table_data): Common parts of output_exception_table.
	Send output to eh_data section.
	(output_function_exception_table): Output exception table data for
	a single function to eh_data section.
	(free_exception_table): New external to free the table.
	* except.h (free_exception_table): Add prototype.
	(output_function_exception_table): Add prototype.
	* final.c (final_end_fuction): Output function exception table
	for IA64_UNWIND_INFO.
	(final_scan_insn): Emit any unwind directives for an insn.

	* config/ia64/ia64-frame.c: New file.
	(gthread_stuff): Make all gthread available with 
	IA64_UNWIND_INFO.
	(dwarf_fde): Define an IA64 struct for dwarf_fde.
	(__register_frame_info, __register_frame): Move to common area of file.
	(__register_frame_info_table, __register_frame_table): Move to common i
	area.
	(__deregister_frame_info, __deregister_frame): Move to common area.
	(__frame_init, find_fde): New versions for IA64_UNWIND_INFO.
	(read_uleb128): New version for ia64.
	(get_unwind_record): Read the next IA-64 unwind record.
	(read_R_record): Read a region header record.
	(process_a_b_reg_code): X record helper.
	(read_X_record): Read an X format record.
	(read_B_record): Read a B format record.
	(P3_record_types): List of record types matching the P3 format.
	(P7_record_types): List of record types matching the P7 format.
	(P8_record_types): List of record types matching the P8 format.
	(read_P_record): Read a P format record.
	(init_ia64_reg_loc): Set default fields for a register.
	(init_ia64_unwind_frame): Set defaults for all register records.
	(execute_one_ia64_descriptor): Execute one descriptor record.
	(rse_address_add): Calculate the position of a local reg in memory.
	(normalize_reg_loc): Turn a location descriptor into a memory address.
	(maybe_normalize_reg_loc): Only normalize a descriptor if it falls 
	within a specified PC offset range.
	(get_real_reg_value): Given a register location, retreive its value.
	(set_real_reg_value): Change the value of a register location.
	(copy_reg_value): Copy reg values, if needed.
	(__copy_saved_reg_state): Copy all registers that need to be copied.
	(process_state_between): Normalize all frame register records that fall 
	within the specifed PC range.
	(frame_translate): Take a processed frame description, and turn 
	everything into addresses.
	(__build_ia64_frame_state ): Find and create frame state record for a PC.
	(ia64_throw_helper): Figure out who to return to and set up the 
	registers.
	(__get_personailty): Get the personality routine for a given frame.
	(__get_except_table): Get the exception table for a given frame.
	(record_name): Unwind record names for debuggin.
	(print_record): Print and unwind record.
	(print_all_records): Print an entire unwind image.
	(__ia64_backtrace): Print a backtrace
	(ia64_backtrace_helper): New function.

	* frame.h (enum unw_record_type): New unwind record types.
	(struct unw_p_record, unw_b_record, unw_x_record) : New unwind records.
	(struct unw_r_record, unwind_record): New unwind record structs.
	(struct unwind_info_ptr): Unwind information layout.
	(IA64_UNW_LOC_TYPE_*): Macros for different types for location 
	descriptors.
	(struct ia64_reg_loc): Register location description.
	(struct ia64_frame_state): Loaction of all registers in a frame.
	libgcc2.c (__ia64_personailty_v1): Personailty routine.
	(__calc_caller_bsp): Calculate the bsp register for the callers frame.
	(__throw): Throw routine.
	(struct object): Add pc_base for IA64_UNWIND_INFO.

	* output.h (assemble_eh_align, assemble_eh_label): New functions to
	functions to generate EH info where we want it.
	(assemble_eh_integer): New function.
	* toplev.c (compile_file): Output module level exception table for
	non-ia64 targets.
	(main): Set exceptions_via_longjump and flag_new_exceptions based 
	on IA64_UNWIND_INFO too.
	* varasm.c (assemble_eh_label): Generate a label via ASM_OUTPUT_EH_LABEL
	if it has been specified.
	* varasm.c (assemble_eh_align): Generate an alignment directive via 
	ASM_OUTPUT_EH_ALIGN if it has been specified.
	* varasm.c (assemble_eh_label): Generate an integer value via
	ASM_OUTPUT_EH_type if they have been specified.
	* config/ia64/ia64.c (ia64_compute_frame_size): PR saves have been
	move earlier, so adjust fr_pad_size appropriately.
	(save_restore_insn): Set RTX_FRAME_RELATED_P appropriately. Save PR regs
	before ar.unat now.
	(ia64_expand_prologue): Set RTX_FRAME_RELATED_P appropriately. Adjust
	for eh_epilogue instruction if specified.
	(ia64_function_prologue): Emit .prologue directive.
	(ia64_init_machine_status): Initialize epilogue communication fields.
	(ia64_mark_machine_status): New, mark for garbage collection.
	(ia64_override_options): Call ia64_init_machine_status.
	(rtx_needs_barrier): Handle 2 new unspecs.
	(spill_offset, spill_offset_emitted, tmp_reg, tmp_saved): New temps.
	(process_set): New function to examine a SET pattern to see
	if any directives should be emitted.
	(process_for_unwind_directive): New function to determine if an
	insn should emit a directive.
	* config/ia64/ia64.h (ASM_OUTPUT_XDATA_type): Emit data in a different 
	section.
	(ASM_OUTPUT_EH_type): Emit EH data in the "IA_64.unwind_info" section.
	(EH_FRAME_ASM_OP): Define to the IA_64.unwind section.
	(IA64_UNWIND_INFO): Define.
	(HANDLER_SECTION): Define directives for handler.
	(IA64_UNWIND_EMIT): Define macro to process insns for unwind directives.
	(struct machine_function): Declare of eh_epilogue communiction.
	(ia64_builtins): Add IA64_BUILTIN_BSP and
	IA64_BUILTIN_FLUSHRS.
	(ia64_function_prologue): Generate a prologue if flag_unwind_tables is true.
	(process_for_unwind_directive): Ditto.
	(ia64_init_builtins): Add __builtin_ia64_bsp
	and __builtin_ia64_flushrs.


	* config/ia64/ia64.md (bsp_value): New insn to get the current BSP.
	(set_bsp): New insn to set the bsp value to a specified register.
	(eh_epilogue): New expansion for communicating the __throw registers.
	(flushrs): New insn to flush the register stack.
	* config/ia64/crtend.asm (__EH_FRAME_END__): End of frame marker.
	* config/ia64/crtbegin.asm (__do_frame_setup): New Function to 
	register frame info. Place in .init section.
	(__EH_FRAME_BEGIN__): Label for start of frame information.
	(frame_object): New static data for frame object.
	(__do_global_dtors_aux): Call __deregister_frame_info for EH frame.
	(frame_object): Increase by 8 bytes.
	(__do_frame_setup): Initialize pc_base field of frame_object.

	* crtstuff.c (__FRAME_END__): Terminate EH frame with -1.

Index: crtstuff.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/crtstuff.c,v
retrieving revision 1.27
diff -c -2 -p -r1.27 crtstuff.c
*** crtstuff.c	2000/04/05 07:34:36	1.27
--- crtstuff.c	2000/05/05 18:49:42
*************** STATIC func_ptr __DTOR_END__[1] __attrib
*** 515,519 ****
--- 515,524 ----
  typedef unsigned int ui32 __attribute__ ((mode (SI)));
  asm (EH_FRAME_SECTION_ASM_OP);
+ #ifdef IA64_UNWIND_INFO
+ STATIC ui32 __FRAME_END__[] __attribute__ ((__unused__)) = { 0xffffffff, 
+ 							     0xffffffff };
+ #else
  STATIC ui32 __FRAME_END__[] __attribute__ ((__unused__)) = { 0 };
+ #endif
  #endif /* EH_FRAME_SECTION */
  
Index: except.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/except.c,v
retrieving revision 1.128
diff -c -2 -p -r1.128 except.c
*** except.c	2000/03/29 08:22:20	1.128
--- except.c	2000/05/05 18:49:43
*************** struct func_eh_entry 
*** 699,702 ****
--- 699,703 ----
    rtx rethrow_label;  /* Label for rethrow.  */
    int rethrow_ref;    /* Is rethrow_label referenced?  */
+   int emitted;        /* 1 if this entry has been emitted in assembly file.  */
    struct handler_info *handlers;
  };
*************** new_eh_region_entry (note_eh_region, ret
*** 740,744 ****
      function_eh_regions[current_func_eh_entry].rethrow_label = rethrow;
    function_eh_regions[current_func_eh_entry].handlers = NULL;
! 
    return current_func_eh_entry++;
  }
--- 741,746 ----
      function_eh_regions[current_func_eh_entry].rethrow_label = rethrow;
    function_eh_regions[current_func_eh_entry].handlers = NULL;
!   function_eh_regions[current_func_eh_entry].emitted = 0;
!  
    return current_func_eh_entry++;
  }
*************** output_exception_table_entry (file, n)
*** 2222,2225 ****
--- 2224,2231 ----
      rethrow = NULL_RTX;
  
+   if (function_eh_regions[index].emitted)
+     return;
+   function_eh_regions[index].emitted  = 1;
+ 
    for ( ; handler != NULL || rethrow != NULL_RTX; handler = handler->next)
      {
*************** output_exception_table_entry (file, n)
*** 2228,2232 ****
          {
            ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", n);
!           assemble_label(buf);
            rethrow = NULL_RTX;
          }
--- 2234,2238 ----
          {
            ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", n);
!           assemble_eh_label(buf);
            rethrow = NULL_RTX;
          }
*************** output_exception_table_entry (file, n)
*** 2234,2250 ****
        ASM_GENERATE_INTERNAL_LABEL (buf, "LEHB", n);
        sym = gen_rtx_SYMBOL_REF (Pmode, buf);
!       assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
  
        ASM_GENERATE_INTERNAL_LABEL (buf, "LEHE", n);
        sym = gen_rtx_SYMBOL_REF (Pmode, buf);
!       assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
        
        if (handler == NULL)
!         assemble_integer (GEN_INT (0), POINTER_SIZE / BITS_PER_UNIT, 1);
        else
          {
            ASM_GENERATE_INTERNAL_LABEL (buf, "L", handler->handler_number);
            sym = gen_rtx_SYMBOL_REF (Pmode, buf);
!           assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
          }
  
--- 2240,2256 ----
        ASM_GENERATE_INTERNAL_LABEL (buf, "LEHB", n);
        sym = gen_rtx_SYMBOL_REF (Pmode, buf);
!       assemble_eh_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
  
        ASM_GENERATE_INTERNAL_LABEL (buf, "LEHE", n);
        sym = gen_rtx_SYMBOL_REF (Pmode, buf);
!       assemble_eh_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
        
        if (handler == NULL)
!         assemble_eh_integer (GEN_INT (0), POINTER_SIZE / BITS_PER_UNIT, 1);
        else
          {
            ASM_GENERATE_INTERNAL_LABEL (buf, "L", handler->handler_number);
            sym = gen_rtx_SYMBOL_REF (Pmode, buf);
!           assemble_eh_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
          }
  
*************** output_exception_table_entry (file, n)
*** 2252,2259 ****
          {
            if (handler == NULL || handler->type_info == NULL)
!             assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
            else
              if (handler->type_info == CATCH_ALL_TYPE)
!               assemble_integer (GEN_INT (CATCH_ALL_TYPE), 
                                               POINTER_SIZE / BITS_PER_UNIT, 1);
              else
--- 2258,2265 ----
          {
            if (handler == NULL || handler->type_info == NULL)
!             assemble_eh_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
            else
              if (handler->type_info == CATCH_ALL_TYPE)
!               assemble_eh_integer (GEN_INT (CATCH_ALL_TYPE), 
                                               POINTER_SIZE / BITS_PER_UNIT, 1);
              else
*************** set_exception_version_code (code)
*** 2288,2315 ****
    version_code = code;
  }
- 
  
  void
! output_exception_table ()
  {
    int i;
    char buf[256];
    extern FILE *asm_out_file;
  
-   if (! doing_eh (0) || ! eh_table)
-     return;
- 
-   exception_section ();
- 
-   /* Beginning marker for table.  */
-   assemble_align (GET_MODE_ALIGNMENT (ptr_mode));
-   assemble_label ("__EXCEPTION_TABLE__");
- 
    if (flag_new_exceptions)
      {
!       assemble_integer (GEN_INT (NEW_EH_RUNTIME), 
                                          POINTER_SIZE / BITS_PER_UNIT, 1);
!       assemble_integer (GEN_INT (language_code), 2 , 1); 
!       assemble_integer (GEN_INT (version_code), 2 , 1);
  
        /* Add enough padding to make sure table aligns on a pointer boundry. */
--- 2294,2320 ----
    version_code = code;
  }
  
+ /* Free the EH table structures.  */
  void
! free_exception_table ()
  {
+   free (eh_table);
+   clear_function_eh_region ();
+ }
+   
+ /* Output the common content of an exception table.  */
+ void
+ output_exception_table_data ()
+ {
    int i;
    char buf[256];
    extern FILE *asm_out_file;
  
    if (flag_new_exceptions)
      {
!       assemble_eh_integer (GEN_INT (NEW_EH_RUNTIME), 
                                          POINTER_SIZE / BITS_PER_UNIT, 1);
!       assemble_eh_integer (GEN_INT (language_code), 2 , 1); 
!       assemble_eh_integer (GEN_INT (version_code), 2 , 1);
  
        /* Add enough padding to make sure table aligns on a pointer boundry. */
*************** output_exception_table ()
*** 2318,2326 ****
          ;
        if (i != 0)
!         assemble_integer (const0_rtx, i , 1);
  
        /* Generate the label for offset calculations on rethrows.  */
        ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", 0);
!       assemble_label(buf);
      }
  
--- 2323,2331 ----
          ;
        if (i != 0)
!         assemble_eh_integer (const0_rtx, i , 1);
  
        /* Generate the label for offset calculations on rethrows.  */
        ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", 0);
!       assemble_eh_label(buf);
      }
  
*************** output_exception_table ()
*** 2328,2347 ****
      output_exception_table_entry (asm_out_file, eh_table[i]);
  
!   free (eh_table);
!   clear_function_eh_region ();
  
    /* Ending marker for table.  */
    /* Generate the label for end of table. */
    ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", CODE_LABEL_NUMBER (final_rethrow));
!   assemble_label(buf);
!   assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
  
    /* For binary compatibility, the old __throw checked the second
       position for a -1, so we should output at least 2 -1's */
    if (! flag_new_exceptions)
!     assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
  
    putc ('\n', asm_out_file);		/* blank line */
  }
  
  /* Emit code to get EH context.
--- 2333,2392 ----
      output_exception_table_entry (asm_out_file, eh_table[i]);
  
! }
! 
! /* Output an exception table for the entire compilation unit.  */
! void
! output_exception_table ()
! {
!   char buf[256];
!   extern FILE *asm_out_file;
! 
!   if (! doing_eh (0) || ! eh_table)
!     return;
! 
!   exception_section ();
  
+   /* Beginning marker for table.  */
+   assemble_eh_align (GET_MODE_ALIGNMENT (ptr_mode));
+   assemble_eh_label ("__EXCEPTION_TABLE__");
+ 
+   output_exception_table_data ();
+ 
    /* Ending marker for table.  */
    /* Generate the label for end of table. */
    ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", CODE_LABEL_NUMBER (final_rethrow));
!   assemble_eh_label(buf);
!   assemble_eh_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
  
    /* For binary compatibility, the old __throw checked the second
       position for a -1, so we should output at least 2 -1's */
    if (! flag_new_exceptions)
!     assemble_eh_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
  
    putc ('\n', asm_out_file);		/* blank line */
  }
+ 
+ /* Used by the ia64 unwind format to output data for an individual 
+    function.  */
+ void
+ output_function_exception_table ()
+ {
+   extern FILE *asm_out_file;
+ 
+   if (! doing_eh (0) || ! eh_table)
+     return;
+ 
+ #ifdef HANDLER_SECTION
+   HANDLER_SECTION;
+ #endif
+ 
+   output_exception_table_data ();
+ 
+   /* Ending marker for table.  */
+   assemble_eh_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+ 
+   putc ('\n', asm_out_file);           /* blank line */
+ }
+ 
  
  /* Emit code to get EH context.
Index: except.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/except.h,v
retrieving revision 1.39
diff -c -2 -p -r1.39 except.h
*** except.h	2000/03/19 18:25:24	1.39
--- except.h	2000/05/05 18:49:43
*************** extern int exception_table_p			PARAMS ((
*** 356,359 ****
--- 356,363 ----
  extern void output_exception_table		PARAMS ((void));
  
+ /* Free the exception table.  */
+ 
+ extern void free_exception_table		PARAMS((void));
+ 
  /* Given a return address in ADDR, determine the address we should use
     to find the corresponding EH region.  */
Index: final.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/final.c,v
retrieving revision 1.124
diff -c -2 -p -r1.124 final.c
*** final.c	2000/05/04 13:49:48	1.124
--- final.c	2000/05/05 18:49:44
*************** final_end_function (first, file, optimiz
*** 1827,1830 ****
--- 1827,1834 ----
    bb_func_label_num = -1;	/* not in function, nuke label # */
  
+ #ifdef IA64_UNWIND_INFO
+   output_function_exception_table ();
+ #endif
+ 
    /* If FUNCTION_EPILOGUE is not defined, then the function body
       itself contains return instructions wherever needed.  */
*************** final_scan_insn (insn, file, optimize, p
*** 2936,2939 ****
--- 2940,2946 ----
  	  break;
  
+ #ifdef IA64_UNWIND_INFO
+ 	IA64_UNWIND_EMIT (asm_out_file, insn);
+ #endif
  	/* Output assembler code from the template.  */
  
Index: frame.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/frame.h,v
retrieving revision 1.11
diff -c -2 -p -r1.11 frame.h
*** frame.h	2000/03/23 12:14:06	1.11
--- frame.h	2000/05/05 18:49:44
*************** typedef struct frame_state
*** 51,54 ****
--- 51,57 ----
  
  struct object {
+ #ifdef IA64_UNWIND_INFO
+   void *pc_base;        /* This field will be set by find_fde. */
+ #endif
    void *pc_begin;
    void *pc_end;
*************** extern void *__deregister_frame_info (vo
*** 88,89 ****
--- 91,273 ----
  
  extern struct frame_state *__frame_state_for (void *, struct frame_state *);
+ 
+ #ifdef IA64_UNWIND_INFO
+ 
+ /* This is the information required for unwind records in an ia64
+    object file. This is required by GAS and the compiler runtime. */
+ 
+ /* These are the starting point masks for the various types of
+    unwind records. To create a record of type R3 for instance, one
+    starts by using the value UNW_R3 and or-ing in any other required values. 
+    These values are also unique (in context), so they can be used to identify 
+    the various record types as well. UNW_Bx and some UNW_Px do have the
+    same value, but Px can only occur in a prologue context, and Bx in
+    a body context.  */
+ 
+ #define UNW_R1	0x00
+ #define UNW_R2	0x40
+ #define UNW_R3	0x60
+ #define UNW_P1	0x80
+ #define UNW_P2	0xA0
+ #define UNW_P3	0xB0
+ #define UNW_P4	0xB8
+ #define UNW_P5	0xB9
+ #define UNW_P6	0xC0
+ #define UNW_P7	0xE0
+ #define UNW_P8	0xF0
+ #define UNW_P9	0xF1
+ #define UNW_P10	0xFF
+ #define UNW_X1	0xF9
+ #define UNW_X2	0xFA
+ #define UNW_X3	0xFB
+ #define UNW_X4	0xFC
+ #define UNW_B1	0x80
+ #define UNW_B2	0xC0
+ #define UNW_B3	0xE0
+ #define UNW_B4	0xF0
+ 
+ /* These are all the various types of unwind records.  */
+ 
+ typedef enum
+ {
+   prologue, prologue_gr, body, mem_stack_f, mem_stack_v, psp_gr, psp_sprel,
+   rp_when, rp_gr, rp_br, rp_psprel, rp_sprel, pfs_when, pfs_gr, pfs_psprel,
+   pfs_sprel, preds_when, preds_gr, preds_psprel, preds_sprel,
+   fr_mem, frgr_mem, gr_gr, gr_mem, br_mem, br_gr, spill_base, spill_mask,
+   unat_when, unat_gr, unat_psprel, unat_sprel, lc_when, lc_gr, lc_psprel,
+   lc_sprel, fpsr_when, fpsr_gr, fpsr_psprel, fpsr_sprel, 
+   priunat_when_gr, priunat_when_mem, priunat_gr, priunat_psprel, 
+   priunat_sprel, bsp_when, bsp_gr, bsp_psprel, bsp_sprel, bspstore_when,
+   bspstore_gr, bspstore_psprel, bspstore_sprel, rnat_when, rnat_gr,
+   rnat_psprel, rnat_sprel, epilogue, label_state, copy_state,
+   spill_psprel, spill_sprel, spill_reg, spill_psprel_p, spill_sprel_p,
+   spill_reg_p
+ } unw_record_type;
+ 
+ 
+ /* These structures declare the fields that can be used in each of the 
+    4 record formats, R, P, B and X.  */
+ 
+ typedef struct unw_r_record
+ {
+   unsigned long rlen;
+   unsigned short mask;
+   unsigned short grsave;
+ } unw_r_record;
+ 
+ typedef struct unw_p_record
+ {
+   void *imask;
+   unsigned long t;
+   unsigned long size;
+   unsigned long spoff;
+   unsigned long br;
+   unsigned long pspoff;
+   unsigned short gr;
+   unsigned short rmask;
+   unsigned short grmask;
+   unsigned long frmask;
+   unsigned short brmask;
+ } unw_p_record;
+ 
+ typedef struct unw_b_record
+ {
+   unsigned long t;
+   unsigned long label;
+   unsigned short ecount;
+ } unw_b_record;
+ 
+ typedef struct unw_x_record
+ {
+   unsigned long t;
+   unsigned long spoff;
+   unsigned long pspoff;
+   unsigned short reg;
+   unsigned short treg;
+   unsigned short qp;
+   unsigned short xy;   /* Value of the XY field..  */
+ } unw_x_record;
+ 
+ /* This structure is used to determine the specific record type and 
+    its fields.  */
+ typedef struct unwind_record
+ {
+   unw_record_type type;
+   union {
+     unw_r_record r;
+     unw_p_record p;
+     unw_b_record b;
+     unw_x_record x;
+   } record;
+ } unwind_record;
+ 
+ #define IA64_UNW_LOC_TYPE_NONE		0
+ #define IA64_UNW_LOC_TYPE_MEM		1
+ #define IA64_UNW_LOC_TYPE_GR		2
+ #define IA64_UNW_LOC_TYPE_FR		3
+ #define IA64_UNW_LOC_TYPE_BR		4
+ #define IA64_UNW_LOC_TYPE_SPOFF		5
+ #define IA64_UNW_LOC_TYPE_PSPOFF	6
+ #define IA64_UNW_LOC_TYPE_OFFSET	7
+ #define IA64_UNW_LOC_TYPE_SPILLBASE	8
+ 
+ typedef struct ia64_reg_loc 
+ {
+   long when;		/* PC relative offset from start of function. */
+   union {		/* In memory or another register?  */
+     void *mem;
+     int regno;
+     int offset;
+   } l;
+   short loc_type;	/* Where to find value.  */
+   short reg_size;
+ } ia64_reg_loc;
+ 
+ /* Frame information record.  */
+ 
+ typedef struct ia64_frame_state
+ {
+   ia64_reg_loc gr[4];	/* gr4 to  gr7.  */
+   ia64_reg_loc fr[20];	/* fr2 to fr5, fr16 to fr31.  */
+   ia64_reg_loc br[5];	/* br1 to  br5.  */
+   ia64_reg_loc rp;
+   ia64_reg_loc fpsr;
+   ia64_reg_loc bsp;
+   ia64_reg_loc bspstore;
+   ia64_reg_loc rnat;
+   ia64_reg_loc pfs;
+   ia64_reg_loc unat;
+   ia64_reg_loc lc;
+   ia64_reg_loc pr;
+   ia64_reg_loc priunat;
+   ia64_reg_loc sp;
+   ia64_reg_loc psp;
+   ia64_reg_loc spill_base;
+   void *my_sp;
+   void *my_bsp;
+ } ia64_frame_state;
+ 
+ /* This structure represents the start of an unwind information pointer.  
+    'unwind_descriptors' is the beginninng of the unwind descriptors, which
+    use up 'length' bytes of storage.  */
+ 
+ typedef struct unwind_info_ptr 
+ {
+   unsigned short version;
+   unsigned short flags;
+   unsigned int length;
+   unsigned char unwind_descriptors[1];
+ } unwind_info_ptr;
+ 
+ 
+ extern unwind_info_ptr *__build_ia64_frame_state (unsigned char *, 
+ 						  ia64_frame_state *, void *,
+ 						  void **);
+ extern void *__get_real_reg_value (ia64_reg_loc *);
+ extern void *__get_personality (unwind_info_ptr *);
+ extern void *__get_except_table (unwind_info_ptr *);
+ extern void __set_real_reg_value (ia64_reg_loc *, void *);
+ void *__calc_caller_bsp (long, unsigned char *);
+ void __copy_saved_reg_state (ia64_frame_state *, ia64_frame_state *);
+ #endif   /* IA64_UNWIND_INFO  */
+ 
Index: libgcc2.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/libgcc2.c,v
retrieving revision 1.91
diff -c -2 -p -r1.91 libgcc2.c
*** libgcc2.c	2000/05/04 23:21:33	1.91
--- libgcc2.c	2000/05/05 18:49:45
*************** label:
*** 3946,3949 ****
--- 3946,4116 ----
  #endif /* DWARF2_UNWIND_INFO */
  
+ #ifdef IA64_UNWIND_INFO
+ #include "frame.h"
+ 
+ /* Return handler to which we want to transfer control, NULL if we don't
+    intend to handle this exception here.  */
+ void *
+ __ia64_personality_v1 (void *pc, old_exception_table *table)
+ {
+   if (table)
+     {
+       int pos;
+       int best = -1;
+ 
+       for (pos = 0; table[pos].start_region != (void *) -1; ++pos)
+         {
+           if (table[pos].start_region <= pc && table[pos].end_region > pc)
+             {
+               /* This can apply.  Make sure it is at least as small as
+                  the previous best.  */
+               if (best == -1 || (table[pos].end_region <= table[best].end_region
+                         && table[pos].start_region >= table[best].start_region))
+                 best = pos;
+             }
+           /* It is sorted by starting PC within a function.  */
+           else if (best >= 0 && table[pos].start_region > pc)
+             break;
+         }
+       if (best != -1)
+         return table[best].exception_handler;
+     }
+   return (void *) 0;
+ }
+ 
+ static void
+ ia64_throw_helper (throw_pc, throw_frame, caller, throw_bsp)
+      void *throw_pc;
+      ia64_frame_state *throw_frame;
+      ia64_frame_state *caller;
+      void *throw_bsp;
+ {
+   unwind_info_ptr *info;
+   void *pc, *handler = NULL;
+   void *pc_base;
+   int frame_count;
+   void *bsp;
+ 
+   __builtin_ia64_flushrs ();      /*  Make the local register stacks available.  */
+ 
+   /* Start at our stack frame, get our state.  */
+   __build_ia64_frame_state (throw_pc, throw_frame, throw_bsp, &pc_base);
+ 
+   /* Now we have to find the proper frame for pc, and see if there
+      is a handler for it. if not, we keep going back frames until
+      we do find one. Otherwise we call uncaught ().  */
+ 
+   frame_count = 0;
+   memcpy (caller, throw_frame, sizeof (*caller));
+   while (!handler)
+     {
+       void *(*personality) ();
+       void *eh_table;
+ 
+       frame_count++;
+       /* We only care about the RP right now, so we dont need to keep
+          any other information about a call frame right now.  */
+       pc = __get_real_reg_value (&caller->rp) - 1;
+       bsp = __calc_caller_bsp ((long)__get_real_reg_value (&caller->pfs), caller->my_bsp);
+       info = __build_ia64_frame_state (pc, caller, bsp, &pc_base);
+ 
+       /* If we couldn't find the next frame, we lose.  */
+       if (! info)
+ 	break;
+ 
+       personality = __get_personality (info); 
+       /* TODO Haven't figured out how to actually load the personality address
+          yet, so just always default to the one we expect for now.  */
+       if (personality != 0)
+ 	personality = __ia64_personality_v1;
+       eh_table = __get_except_table (info);
+       /* If there is no personality routine, we'll keep unwinding.  */
+       if (personality)
+ 	/* Pass a segment relative PC address to the personality routine,
+ 	   because the unwind_info section uses segrel relocs.  */
+ 	handler = personality (pc - pc_base, eh_table);
+     }
+   
+   if (!handler)
+    __terminate ();
+ 
+   /* Handler is a segment relative address, so we must adjust it here.  */
+   handler += (long) pc_base;
+ 
+   /* If we found a handler, we need to unwind the stack to that point.
+      We do this by copying saved values from previous frames into the
+      save slot for the throw_frame saved slots.  when __throw returns,
+      it'll pickup the correct values.  */
+   
+   /* Start with where __throw saved things, and copy each saved register
+      of each previous frame until we get to the one before we're 
+      throwing back to.  */
+   memcpy (caller, throw_frame, sizeof (*caller));
+   for ( ; frame_count > 0; frame_count--)
+     {
+       pc = __get_real_reg_value (&caller->rp) - 1;
+       bsp = __calc_caller_bsp ((long)__get_real_reg_value (&caller->pfs), caller->my_bsp);
+       __build_ia64_frame_state (pc, caller, bsp, &pc_base);
+       /* Any regs that were saved can be put in the throw frame now.  */
+       /* We don't want to copy any saved register from the 
+          target destination, but we do want to load up it's frame.  */
+       if (frame_count > 1)
+ 	__copy_saved_reg_state (throw_frame, caller);
+     }
+ 
+   /* Set return address of the throw frame to the handler. */
+   __set_real_reg_value (&throw_frame->rp, handler);
+ 
+   /* TODO, do we need to do anything to make the values we wrote 'stick'? */
+   /* DO we need to go through the whole loadrs seqeunce?  */
+ 
+ }
+ 
+ void
+ __throw ()
+ {
+   struct eh_context *eh = (*get_eh_context) ();
+   ia64_frame_state my_frame;
+   ia64_frame_state originator;	/* For the context handler is in.  */
+   void *bsp, *tmp_bsp;
+   long offset;
+ 
+   /* This is required for C++ semantics.  We must call terminate if we
+      try and rethrow an exception, when there is no exception currently
+      active.  */
+   if (! eh->info)
+     __terminate ();
+ 
+   __builtin_unwind_init ();
+ label_ia64:
+   /* We have to call another routine to actually process the frame 
+      information, which will force all of __throw's local registers into
+      backing store.  */
+ 
+   /* Get the value of ar.bsp while we're here.  */
+ 
+   bsp = __builtin_ia64_bsp ();
+   ia64_throw_helper (&&label_ia64, &my_frame, &originator, bsp);
+ 
+   /* Now we have to fudge the bsp by the amount in our (__throw)
+      frame marker, since the return is going to adjust it by that much. */
+ 
+   tmp_bsp = __calc_caller_bsp ((long)__get_real_reg_value (&my_frame.pfs), 
+ 			     my_frame.my_bsp);
+   offset = (char *)my_frame.my_bsp - (char *)tmp_bsp;
+   tmp_bsp = (char *)originator.my_bsp + offset;
+ 
+   /* A throw handler is trated like a  non-local goto, which is architeched
+      to set the FP (or PSP) in r7 before branching.  gr[0-3] map to 
+      r4-r7, so we want gr[3].  */
+   __set_real_reg_value (&my_frame.gr[3], __get_real_reg_value (&originator.psp));
+ 
+   __builtin_eh_return (tmp_bsp, offset, originator.my_sp);
+ 
+   /* The return address was already set by throw_helper.  */
+ }
+ 
+ #endif /* IA64_UNWIND_INFO  */
+ 
  #endif /* L_eh */
  
Index: output.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/output.h,v
retrieving revision 1.47
diff -c -2 -p -r1.47 output.h
*** output.h	2000/04/27 05:58:05	1.47
--- output.h	2000/05/05 18:49:45
*************** extern void assemble_zeros		PARAMS ((int
*** 293,296 ****
--- 293,297 ----
  /* Assemble an alignment pseudo op for an ALIGN-bit boundary.  */
  extern void assemble_align		PARAMS ((int));
+ extern void assemble_eh_align		PARAMS ((int));
  
  /* Assemble a string constant with the specified C string as contents.  */
*************** extern void assemble_global		PARAMS ((co
*** 307,310 ****
--- 308,312 ----
  /* Assemble a label named NAME.  */
  extern void assemble_label		PARAMS ((const char *));
+ extern void assemble_eh_label		PARAMS ((const char *));
  
  /* Output to FILE a reference to the assembler name of a C-level name NAME.
*************** extern void assemble_name		PARAMS ((FILE
*** 322,325 ****
--- 324,328 ----
     non-zero, abort if we can't output the constant.  */
  extern int assemble_integer		PARAMS ((rtx, int, int));
+ extern int assemble_eh_integer		PARAMS ((rtx, int, int));
  
  #ifdef EMUSHORT
Index: toplev.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/toplev.c,v
retrieving revision 1.331
diff -c -2 -p -r1.331 toplev.c
*** toplev.c	2000/05/05 16:37:01	1.331
--- toplev.c	2000/05/05 18:49:47
*************** compile_file (name)
*** 2307,2312 ****
         the exception table.  */
  
      output_exception_table ();
! 
      check_global_declarations (vec, len);
  
--- 2307,2315 ----
         the exception table.  */
  
+ #ifndef IA64_UNWIND_INFO
      output_exception_table ();
! #endif
!     free_exception_table ();
!     
      check_global_declarations (vec, len);
  
*************** main (argc, argv)
*** 4558,4564 ****
--- 4561,4579 ----
        exceptions_via_longjmp = ! DWARF2_UNWIND_INFO;
  #else
+ #ifdef IA64_UNWIND_INFO
+       exceptions_via_longjmp = ! IA64_UNWIND_INFO;
+ #else
        exceptions_via_longjmp = 1;
  #endif
+ #endif
      }
+ 
+   /* Since each function gets its own handler data, we can't support the
+      new model currently, since it depend on a specific rethrow label
+      which is declared at the front of the table, and we can only
+      have one such symbol in a file.  */
+ #ifdef IA64_UNWIND_INFO
+   flag_new_exceptions = 0;
+ #endif
  
    /* Set up the align_*_log variables, defaulting them to 1 if they
Index: varasm.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/varasm.c,v
retrieving revision 1.116
diff -c -2 -p -r1.116 varasm.c
*** varasm.c	2000/05/05 16:16:57	1.116
--- varasm.c	2000/05/05 18:49:48
*************** init_varasm_once ()
*** 4813,4814 ****
--- 4813,4885 ----
    ggc_add_string_root (&in_named_name, 1);
  }
+ 
+ /* Extra support for EH values.  */
+ void
+ assemble_eh_label (name)
+      const char *name;
+ {
+ #ifdef ASM_OUTPUT_EH_LABEL
+   ASM_OUTPUT_EH_LABEL (asm_out_file, name);
+ #else
+   assemble_label (name);
+ #endif
+ }
+ 
+ /* Assemble an alignment pseudo op for an ALIGN-bit boundary.  */
+ 
+ void
+ assemble_eh_align (align)
+      int align;
+ {
+ #ifdef ASM_OUTPUT_EH_ALIGN
+   if (align > BITS_PER_UNIT)
+     ASM_OUTPUT_EH_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
+ #else
+   assemble_align (align);
+ #endif
+ }
+ 
+ 
+ /* On some platforms, we may want to specify a special mechansim to
+    output EH data when generating with a function..  */
+ int
+ assemble_eh_integer (x, size, force)
+      rtx x;
+      int size;
+      int force;
+ {
+ 
+   switch (size)
+     {
+ #ifdef ASM_OUTPUT_EH_CHAR
+     case 1:
+       ASM_OUTPUT_EH_CHAR (asm_out_file, x);
+       return 1;
+ #endif
+ 
+ #ifdef ASM_OUTPUT_EH_SHORT
+     case 2:
+       ASM_OUTPUT_EH_SHORT (asm_out_file, x);
+       return 1;
+ #endif
+ 
+ #ifdef ASM_OUTPUT_EH_INT
+     case 4:
+       ASM_OUTPUT_EH_INT (asm_out_file, x);
+       return 1;
+ #endif
+ 
+ #ifdef ASM_OUTPUT_EH_DOUBLE_INT
+     case 8:
+       ASM_OUTPUT_EH_DOUBLE_INT (asm_out_file, x);
+       return 1;
+ #endif
+ 
+     default:
+       break;
+     }
+   return (assemble_integer (x, size, force));
+ }
+ 
+ 
+ 
Index: config/ia64/ia64-frame.c
===================================================================
RCS file: ia64-frame.c
diff -N ia64-frame.c
*** /dev/null	Tue May  5 13:32:27 1998
--- ia64-frame.c	Fri May  5 11:49:49 2000
***************
*** 0 ****
--- 1,1911 ----
+ /* Subroutines needed for unwinding stack frames for exception handling.  */
+ /* Compile this one with gcc.  */
+ /* Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
+    Contributed by Jason Merrill <jason@cygnus.com>.
+ 
+ This file is part of GNU CC.
+ 
+ GNU CC is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+ 
+ GNU CC is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ 
+ You should have received a copy of the GNU General Public License
+ along with GNU CC; see the file COPYING.  If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.  */
+ 
+ /* As a special exception, if you link this library with other files,
+    some of which are compiled with GCC, to produce an executable,
+    this library does not by itself cause the resulting executable
+    to be covered by the GNU General Public License.
+    This exception does not however invalidate any other reasons why
+    the executable file might be covered by the GNU General Public License.  */
+ 
+ /* It is incorrect to include config.h here, because this file is being
+    compiled for the target, and hence definitions concerning only the host
+    do not apply.  */
+ 
+ #include "tconfig.h"
+ 
+ /* We disable this when inhibit_libc, so that gcc can still be built without
+    needing header files first.  */
+ /* ??? This is not a good solution, since prototypes may be required in
+    some cases for correct code.  See also libgcc2.c/crtstuff.c.  */
+ #ifndef inhibit_libc
+ /* fixproto guarantees these system headers exist. */
+ #include <stdlib.h>
+ #include <unistd.h>
+ 
+ #else
+ #include <stddef.h>
+ #ifndef malloc
+ extern void *malloc (size_t);
+ #endif
+ #ifndef free
+ extern void free (void *);
+ #endif
+ #endif
+ 
+ #ifdef IA64_UNWIND_INFO
+ #include "defaults.h"
+ #include "gthr.h"
+ 
+ /* Define a mutex for frame information modification. */
+ #ifdef __GTHREAD_MUTEX_INIT
+ static __gthread_mutex_t object_mutex = __GTHREAD_MUTEX_INIT;
+ #else
+ static __gthread_mutex_t object_mutex;
+ #endif
+ 
+ /* This is undefined below if we need it to be an actual function.  */
+ #define init_object_mutex_once()
+ 
+ /* Some types used by the DWARF 2 spec.  */
+ 
+ typedef          int  sword __attribute__ ((mode (SI)));
+ typedef unsigned int  uword __attribute__ ((mode (SI)));
+ typedef unsigned int  uaddr __attribute__ ((mode (pointer)));
+ typedef          int  saddr __attribute__ ((mode (pointer)));
+ typedef unsigned char ubyte;
+ 
+ static void bad_record (unsigned char*, int) __attribute__ ((__noreturn__));
+ 
+ #if __GTHREADS
+ #ifdef __GTHREAD_MUTEX_INIT_FUNCTION
+ 
+ /* Helper for init_object_mutex_once.  */
+ 
+ static void
+ init_object_mutex (void)
+ {
+   __GTHREAD_MUTEX_INIT_FUNCTION (&object_mutex);
+ }
+ 
+ /* Call this to arrange to initialize the object mutex.  */
+ 
+ #undef init_object_mutex_once
+ static void
+ init_object_mutex_once (void)
+ {
+   static __gthread_once_t once = __GTHREAD_ONCE_INIT;
+   __gthread_once (&once, init_object_mutex);
+ }
+ 
+ #endif /* __GTHREAD_MUTEX_INIT_FUNCTION */
+ #endif /* __GTHREADS */
+ 
+ /* This structure represents a single unwind table entry.  We lie and say
+    its the dwarf_fde structure to use the common object in frame.h */
+ 
+ typedef struct dwarf_fde
+ {
+   long start_offset;
+   long end_offset;
+   long unwind_offset;
+ } unwind_table_entry;
+   
+ /* Defining dwarf_fde allows us to use the common object registration.  */
+ typedef unwind_table_entry dwarf_fde;
+ typedef unwind_table_entry fde;
+ 
+ #include "frame.h" 
+ 
+ static struct object *objects = NULL;
+ 
+ static inline saddr
+ fde_compare (fde *x, fde *y)
+ {
+   return (saddr)x->start_offset - (saddr)y->start_offset;
+ }
+ 
+ 
+ /* Sorting an array of FDEs by address.
+    (Ideally we would have the linker sort the FDEs so we don't have to do
+    it at run time. But the linkers are not yet prepared for this.)  */
+ 
+ /* This is a special mix of insertion sort and heap sort, optimized for
+    the data sets that actually occur. They look like
+    101 102 103 127 128 105 108 110 190 111 115 119 125 160 126 129 130.
+    I.e. a linearly increasing sequence (coming from functions in the text
+    section), with additionally a few unordered elements (coming from functions
+    in gnu_linkonce sections) whose values are higher than the values in the
+    surrounding linear sequence (but not necessarily higher than the values
+    at the end of the linear sequence!).
+    The worst-case total run time is O(N) + O(n log (n)), where N is the
+    total number of FDEs and n is the number of erratic ones.  */
+ 
+ typedef struct fde_vector
+ {
+   fde **array;
+   size_t count;
+ } fde_vector;
+ 
+ typedef struct fde_accumulator
+ {
+   fde_vector linear;
+   fde_vector erratic;
+ } fde_accumulator;
+ 
+ static inline int
+ start_fde_sort (fde_accumulator *accu, size_t count)
+ {
+   accu->linear.array = (fde **) malloc (sizeof (fde *) * count);
+   accu->erratic.array = accu->linear.array ?
+       (fde **) malloc (sizeof (fde *) * count) : NULL;
+   accu->linear.count = 0;
+   accu->erratic.count = 0;
+   
+   return accu->linear.array != NULL;
+ }
+ 
+ static void frame_init (struct object *);
+ 
+ static inline void
+ fde_insert (fde_accumulator *accu, fde *this_fde)
+ {
+   if (accu->linear.array)
+     accu->linear.array[accu->linear.count++] = this_fde;
+ }
+ 
+ /* Split LINEAR into a linear sequence with low values and an erratic
+    sequence with high values, put the linear one (of longest possible
+    length) into LINEAR and the erratic one into ERRATIC. This is O(N).
+    
+    Because the longest linear sequence we are trying to locate within the
+    incoming LINEAR array can be interspersed with (high valued) erratic
+    entries.  We construct a chain indicating the sequenced entries.
+    To avoid having to allocate this chain, we overlay it onto the space of
+    the ERRATIC array during construction.  A final pass iterates over the
+    chain to determine what should be placed in the ERRATIC array, and
+    what is the linear sequence.  This overlay is safe from aliasing.  */
+ static inline void
+ fde_split (fde_vector *linear, fde_vector *erratic)
+ {
+   static fde *marker;
+   size_t count = linear->count;
+   fde **chain_end = ▮
+   size_t i, j, k;
+ 
+   /* This should optimize out, but it is wise to make sure this assumption
+      is correct. Should these have different sizes, we cannot cast between
+      them and the overlaying onto ERRATIC will not work.  */
+   if (sizeof (fde *) != sizeof (fde **))
+     abort ();
+   
+   for (i = 0; i < count; i++)
+     {
+       fde **probe;
+       
+       for (probe = chain_end;
+            probe != &marker && fde_compare (linear->array[i], *probe) < 0;
+            probe = chain_end)
+         {
+           chain_end = (fde **)erratic->array[probe - linear->array];
+           erratic->array[probe - linear->array] = NULL;
+         }
+       erratic->array[i] = (fde *)chain_end;
+       chain_end = &linear->array[i];
+     }
+ 
+   /* Each entry in LINEAR which is part of the linear sequence we have
+      discovered will correspond to a non-NULL entry in the chain we built in
+      the ERRATIC array.  */
+   for (i = j = k = 0; i < count; i++)
+     if (erratic->array[i])
+       linear->array[j++] = linear->array[i];
+     else
+       erratic->array[k++] = linear->array[i];
+   linear->count = j;
+   erratic->count = k;
+ }
+ 
+ /* This is O(n log(n)).  BSD/OS defines heapsort in stdlib.h, so we must
+    use a name that does not conflict.  */
+ static inline void
+ frame_heapsort (fde_vector *erratic)
+ {
+   /* For a description of this algorithm, see:
+      Samuel P. Harbison, Guy L. Steele Jr.: C, a reference manual, 2nd ed.,
+      p. 60-61. */
+   fde ** a = erratic->array;
+   /* A portion of the array is called a "heap" if for all i>=0:
+      If i and 2i+1 are valid indices, then a[i] >= a[2i+1].
+      If i and 2i+2 are valid indices, then a[i] >= a[2i+2]. */
+ #define SWAP(x,y) do { fde * tmp = x; x = y; y = tmp; } while (0)
+   size_t n = erratic->count;
+   size_t m = n;
+   size_t i;
+ 
+   while (m > 0)
+     {
+       /* Invariant: a[m..n-1] is a heap. */
+       m--;
+       for (i = m; 2*i+1 < n; )
+         {
+           if (2*i+2 < n
+               && fde_compare (a[2*i+2], a[2*i+1]) > 0
+               && fde_compare (a[2*i+2], a[i]) > 0)
+             {
+               SWAP (a[i], a[2*i+2]);
+               i = 2*i+2;
+             }
+           else if (fde_compare (a[2*i+1], a[i]) > 0)
+             {
+               SWAP (a[i], a[2*i+1]);
+               i = 2*i+1;
+             }
+           else
+             break;
+         }
+     }
+   while (n > 1)
+     {
+       /* Invariant: a[0..n-1] is a heap. */
+       n--;
+       SWAP (a[0], a[n]);
+       for (i = 0; 2*i+1 < n; )
+         {
+           if (2*i+2 < n
+               && fde_compare (a[2*i+2], a[2*i+1]) > 0
+               && fde_compare (a[2*i+2], a[i]) > 0)
+             {
+               SWAP (a[i], a[2*i+2]);
+               i = 2*i+2;
+             }
+           else if (fde_compare (a[2*i+1], a[i]) > 0)
+             {
+               SWAP (a[i], a[2*i+1]);
+               i = 2*i+1;
+             }
+           else
+             break;
+         }
+     }
+ #undef SWAP
+ }
+ 
+ /* Merge V1 and V2, both sorted, and put the result into V1. */
+ static void
+ fde_merge (fde_vector *v1, const fde_vector *v2)
+ {
+   size_t i1, i2;
+   fde * fde2;
+ 
+   i2 = v2->count;
+   if (i2 > 0)
+     {
+       i1 = v1->count;
+       do {
+         i2--;
+         fde2 = v2->array[i2];
+         while (i1 > 0 && fde_compare (v1->array[i1-1], fde2) > 0)
+           {
+             v1->array[i1+i2] = v1->array[i1-1];
+             i1--;
+           }
+         v1->array[i1+i2] = fde2;
+       } while (i2 > 0);
+       v1->count += v2->count;
+     }
+ }
+ 
+ static fde **
+ end_fde_sort (fde_accumulator *accu, size_t count)
+ {
+   if (accu->linear.array && accu->linear.count != count)
+     abort ();
+   
+   if (accu->erratic.array)
+     {
+       fde_split (&accu->linear, &accu->erratic);
+       if (accu->linear.count + accu->erratic.count != count)
+ 	abort ();
+       frame_heapsort (&accu->erratic);
+       fde_merge (&accu->linear, &accu->erratic);
+       if (accu->erratic.array)
+         free (accu->erratic.array);
+     }
+   else
+     {
+       /* We've not managed to malloc an erratic array, so heap sort in the
+          linear one.  */
+       frame_heapsort (&accu->linear);
+     }
+   return accu->linear.array;
+ }
+ 
+ /* Called from crtbegin.o to register the unwind info for an object.  */
+ 
+ void
+ __register_frame_info (void *begin, struct object *ob)
+ {
+   ob->fde_begin = begin;
+ 
+   ob->pc_begin = ob->pc_end = 0;
+   ob->fde_array = 0;
+   ob->count = 0;
+ 
+   init_object_mutex_once ();
+   __gthread_mutex_lock (&object_mutex);
+ 
+   ob->next = objects;
+   objects = ob;
+ 
+   __gthread_mutex_unlock (&object_mutex);
+ }
+ 
+ void
+ __register_frame (void *begin)
+ {
+   struct object *ob = (struct object *) malloc (sizeof (struct object));
+   __register_frame_info (begin, ob);                       
+ }
+ 
+ /* Similar, but BEGIN is actually a pointer to a table of unwind entries
+    for different translation units.  Called from the file generated by
+    collect2.  */
+ 
+ void
+ __register_frame_info_table (void *begin, struct object *ob)
+ {
+   ob->fde_begin = begin;
+   ob->fde_array = begin;
+ 
+   ob->pc_begin = ob->pc_end = 0;
+   ob->count = 0;
+ 
+   init_object_mutex_once ();
+   __gthread_mutex_lock (&object_mutex);
+ 
+   ob->next = objects;
+   objects = ob;
+ 
+   __gthread_mutex_unlock (&object_mutex);
+ }
+ 
+ void
+ __register_frame_table (void *begin)
+ {
+   struct object *ob = (struct object *) malloc (sizeof (struct object));
+   __register_frame_info_table (begin, ob);
+ }
+ 
+ /* Called from crtbegin.o to deregister the unwind info for an object.  */
+ 
+ void *
+ __deregister_frame_info (void *begin)
+ {
+   struct object **p;
+ 
+   init_object_mutex_once ();
+   __gthread_mutex_lock (&object_mutex);
+ 
+   p = &objects;
+   while (*p)
+     {
+       if ((*p)->fde_begin == begin)
+ 	{
+ 	  struct object *ob = *p;
+ 	  *p = (*p)->next;
+ 
+ 	  /* If we've run init_frame for this object, free the FDE array.  */
+ 	  if (ob->fde_array && ob->fde_array != begin)
+ 	    free (ob->fde_array);
+ 
+ 	  __gthread_mutex_unlock (&object_mutex);
+ 	  return (void *) ob;
+ 	}
+       p = &((*p)->next);
+     }
+ 
+   __gthread_mutex_unlock (&object_mutex);
+   abort ();
+ }
+ 
+ void
+ __deregister_frame (void *begin)
+ {
+   free (__deregister_frame_info (begin));
+ }
+ 
+   
+ static void
+ frame_init (struct object *ob)
+ {
+   int count = 0;  /* reserve one for the dummy last entry.  */
+   fde_accumulator accu;
+   unwind_table_entry *ptr = ob->fde_begin;
+ 
+   if (ptr == 0)
+     return;
+ 
+   /* Count the number of entries objects.  */
+   for ( ; ptr->start_offset != -1; ptr++)
+     count++;
+ 
+   ob->pc_begin = (void *)(uaddr) - 1;
+   ob->pc_end = 0;
+ 
+   start_fde_sort (&accu, count);
+   for (ptr = ob->fde_begin; ptr->start_offset != -1; ptr++)
+   {
+     if (ob->pc_base + ptr->start_offset < ob->pc_begin)
+       ob->pc_begin = ob->pc_base + ptr->start_offset;
+     if (ob->pc_base + ptr->end_offset > ob->pc_end)
+       ob->pc_end = ob->pc_base + ptr->end_offset;
+     fde_insert (&accu, (fde *)ptr);
+   }
+ 
+   ob->fde_array = end_fde_sort (&accu, count);
+   ob->count = count;
+ }
+ 
+ /* Return a pointer to the FDE for the function containing PC.  */
+ 
+ static fde *
+ find_fde (void *pc, void **pc_base)
+ {
+   struct object *ob;
+   size_t lo, hi;
+ 
+   *pc_base = NULL;
+ 
+   init_object_mutex_once ();
+   __gthread_mutex_lock (&object_mutex);
+ 
+   for (ob = objects; ob; ob = ob->next)
+     {
+       if (ob->pc_begin == 0)
+ 	frame_init (ob);
+       if (pc >= ob->pc_begin && pc < ob->pc_end)
+ 	break;
+     }
+ 
+   __gthread_mutex_unlock (&object_mutex);
+ 
+   if (ob == 0)
+     return 0;
+ 
+   *pc_base = ob->pc_base;
+   /* Standard binary search algorithm.  */
+   for (lo = 0, hi = ob->count; lo < hi; )
+     {
+       size_t i = (lo + hi) / 2;
+       fde *f = ob->fde_array[i];
+ 
+       if (pc - ob->pc_base < f->start_offset)
+ 	hi = i;
+       else if (pc - ob->pc_base >= f->end_offset)
+ 	lo = i + 1;
+       else
+ 	return f;
+     }
+ 
+   return 0;
+ }
+ 
+ /* Decode the unsigned LEB128 constant at BUF and return it. The value at
+    MEM is updated to reflect the next position in the buffer.  */
+ 
+ static unsigned long
+ read_uleb128 (unsigned char **mem)
+ {
+   unsigned shift = 0;
+   unsigned long result = 0;
+   unsigned char *buf = *mem;
+ 
+   while (1)
+     {
+       unsigned long byte = *buf++;
+       result |= (byte & 0x7f) << shift;
+       if ((byte & 0x80) == 0)
+         break;
+       shift += 7;
+     }
+   *mem = buf;
+   return result;
+ }
+ 
+ static void
+ bad_record (ptr, offset)
+      unsigned char *ptr;
+      int offset;
+ {
+ #if 0
+   printf ("Bad unwind record format value '%x' at offset %d in record %p\n",
+   	  *(ptr + offset), offset , ptr);
+ #endif  
+   abort ();
+ }
+ 
+ static unsigned char *read_R_record (unwind_record *, unsigned char, unsigned char *);
+ static unsigned char *read_X_record (unwind_record *, unsigned char, unsigned char *);
+ static unsigned char *read_B_record (unwind_record *, unsigned char, unsigned char *);
+ static unsigned char *read_P_record (unwind_record *, unsigned char, unsigned char *);
+ 
+ 
+ /* This routine will determine what type of record the memory pointer
+    is refering to, and fill in the appropriate fields for that record type. 
+    PROLOGUE_FLAG is TRUE if we are currently processing a PROLOGUE
+    body. 
+    DATA is a pointer to an unwind record which will be filled in.
+    PTR is a pointer to the current location in the unwind table where we
+    will read the next record from.  
+    The return value is the start of the next record.  */
+ 
+ extern unsigned char *
+ get_unwind_record (prologue_flag, data, ptr)
+     int prologue_flag;
+     unwind_record *data;
+     unsigned char *ptr;
+ {
+   unsigned char val = *ptr++;
+ 
+   if ((val & 0x80) == 0)
+     {
+       return read_R_record (data, val, ptr);
+     }
+ 
+   if (val == UNW_X1 || val == UNW_X2 || val == UNW_X3 || val == UNW_X4)
+     return read_X_record (data, val, ptr);
+ 
+   if (prologue_flag)
+     return read_P_record (data, val, ptr);
+   else
+     return read_B_record (data, val, ptr);
+ }
+ 
+ 
+ static unsigned char *
+ read_R_record (data, val, ptr)
+      unwind_record *data;
+      unsigned char val;
+      unsigned char *ptr;
+ {
+   if ((val & 0x40) == 0)
+     {
+       /* R1 format.  */
+       if (val & 0x20)
+         data->type = body;
+       else
+         data->type = prologue;
+       data->record.r.rlen = (val & 0x1f);
+       return ptr;
+     }
+ 
+   if ((val & 0xF8) == UNW_R2)
+     {
+       /* R2 format.  */
+       unsigned char mask = (val & 0x07) << 1;
+       if (*ptr & 0x80) 
+         mask = mask | 1;
+       data->type = prologue_gr;
+       data->record.r.mask = mask;
+       data->record.r.grsave = (*ptr++ & 0x7f);
+       data->record.r.rlen = read_uleb128 (&ptr);
+       return ptr;
+     }
+ 
+   if ((val & 0xFC) == UNW_R3)
+     {
+       /* R3 format.  */
+       val = (val & 0x03);
+       if (val == 0)
+         data->type = prologue;
+       else
+         if (val == 1)
+ 	  data->type = body;
+ 	else
+ 	  bad_record (ptr - 1, 0);
+       data->record.r.rlen = read_uleb128 (&ptr);
+       return ptr;
+     }
+   bad_record (ptr - 1, 0);
+ }
+ 
+ static void
+ process_a_b_reg_code(data, val)
+      unwind_record *data;
+      unsigned char val;
+ {
+   int code = (val & 0x60) >> 5;
+   int reg = (val & 0x1f);
+   switch (code) 
+     {
+       case 0:
+ 	data->record.x.reg = GR_REG (reg);
+         break;
+       case 1:
+ 	data->record.x.reg = FR_REG (reg);
+         break;
+       case 2:
+ 	data->record.x.reg = BR_REG (reg);
+         break;
+       case 3:
+         /* TODO. We need to encode the specialty regs here. The table is 
+ 	   on page B-9 of the runtime manual (under the X1 description.) */
+         break;
+     }
+ }
+ 
+ static unsigned char *
+ read_X_record (data, val, ptr)
+      unwind_record *data;
+      unsigned char val;
+      unsigned char *ptr;
+ {
+   unsigned long tmp;
+   int byte1, byte2;
+   switch (val) 
+     {
+       case UNW_X1:
+         byte1 = *ptr++;
+ 	data->record.x.t = read_uleb128 (&ptr);
+ 	tmp = read_uleb128 (&ptr);
+ 	if ((byte1 & 0x80) == 0)
+ 	  {
+ 	    data->type = spill_psprel;
+ 	    data->record.x.pspoff = tmp;
+ 	  }
+ 	else
+ 	  {
+ 	    data->type = spill_sprel;
+ 	    data->record.x.spoff = tmp;
+ 	  }
+ 	process_a_b_reg_code (data, byte1);
+ 	return ptr;
+       case UNW_X4:
+         byte1 = *ptr++;
+ 	data->record.x.qp = PR_REG (byte1 & 0x3f);
+ 	data->type = spill_reg_p;
+       case UNW_X2:
+         {
+ 	  int xy;
+ 	  int treg;
+ 	  /* Only set type if we didn't fall through the UNW_X4 case.  */
+ 	  if (val == UNW_X2)
+ 	    data->type = spill_reg;
+ 	  byte1 = *ptr++;
+ 	  byte2 = *ptr++;
+ 	  process_a_b_reg_code (data, byte1);
+ 	  xy = (((byte1 >> 7) << 1 ) | (byte2 >> 7));
+ 	  treg = (byte2 & 0x7f);
+ 	  switch (xy) 
+ 	    {
+ 	      case 0:
+ 	        data->record.x.treg = GR_REG (treg);
+ 	        break;
+ 	      case 1:
+ 	        data->record.x.treg = FR_REG (treg);
+ 	        break;
+ 	      case 2:
+ 	        data->record.x.treg = BR_REG (treg);
+ 	        break;
+ 	      case 3:
+ 	        bad_record (ptr - 3, 2);
+ 	    }
+ 	  data->record.x.t = read_uleb128 (&ptr);
+         }
+ 	return ptr;
+       case UNW_X3:
+         byte1 = *ptr++;
+ 	byte2 = *ptr++;
+ 	data->record.x.qp = PR_REG (byte1 & 0x3f);
+ 	process_a_b_reg_code (data, byte2);
+ 	data->record.x.t = read_uleb128 (&ptr);
+ 	tmp = read_uleb128 (&ptr);
+ 	if ((byte1 & 0x80) == 0)
+ 	  {
+ 	    data->type = spill_psprel_p;
+ 	    data->record.x.pspoff = tmp;
+ 	  }
+ 	else
+ 	  {
+ 	    data->type = spill_sprel_p;
+ 	    data->record.x.spoff = tmp;
+ 	  }	
+ 	return ptr;
+       default:
+ 	bad_record (ptr - 1, 0);
+     }
+   return NULL;
+ }
+ 
+ static unsigned char *
+ read_B_record (data, val, ptr)
+      unwind_record *data;
+      unsigned char val;
+      unsigned char *ptr;
+ {
+   if ((val & 0xc0) == 0x80)
+     {
+       /* B1 format.  */
+       if ((val & 0x20) == 0)
+         data->type = label_state;
+       else
+         data->type = copy_state;
+       data->record.b.label = (val & 0x1f);
+       return ptr;
+     }
+   
+   if ((val & 0xe0) == 0xc0)
+     {
+       /* B2 format.  */
+       data->type = epilogue;
+       data->record.b.ecount = (val & 0x1f);
+       data->record.b.t = read_uleb128 (&ptr);
+       return ptr;
+     }
+ 
+   if (val == UNW_B3)
+     {
+       /* B3 format.  */
+       data->type = epilogue;
+       data->record.b.t = read_uleb128 (&ptr);
+       data->record.b.ecount = read_uleb128 (&ptr);
+       return ptr;
+     }
+ 
+   if (val == UNW_B4)
+     {
+       /* B4 format, with r == 0.  */
+       data->type = label_state;
+       data->record.b.label = read_uleb128 (&ptr);
+       return ptr;
+     }
+ 
+   if (val == (UNW_B4 | 0x08))
+     {
+       /* B4 format, with r == 1.  */
+       data->type = copy_state;
+       data->record.b.label = read_uleb128 (&ptr);
+       return ptr;
+     }
+ 
+   bad_record (ptr - 1, 0);
+ 
+ }
+ 
+ /* This array is used to set the TYPE field for format P3.  */
+ static unw_record_type P3_record_types[] = {
+   psp_gr, rp_gr, pfs_gr, preds_gr, unat_gr, lc_gr, rp_br, rnat_gr,
+   bsp_gr, bspstore_gr, fpsr_gr, priunat_gr };
+ 
+ /* This array is used to set the TYPE field for format P7.  */
+ static unw_record_type P7_record_types[] = {
+   mem_stack_f, mem_stack_v, spill_base, psp_sprel, rp_when, rp_psprel,
+   pfs_when, pfs_psprel, preds_when, preds_psprel, lc_when, lc_psprel,
+   unat_when, unat_psprel, fpsr_when, fpsr_psprel };
+ 
+ /* These values and the array are used to determine which additional ULEB128
+    fields are required for the P7 format.  */
+ #define P7_T_SIZE	0
+ #define P7_T		1
+ #define P7_PSPOFF       2
+ #define P7_SPOFF	3
+ static unsigned char P7_additional_fields [] = {
+    P7_T_SIZE, P7_T, P7_PSPOFF, P7_SPOFF, P7_T, P7_PSPOFF, 
+    P7_T, P7_PSPOFF, P7_T, P7_PSPOFF, P7_T, P7_PSPOFF, P7_T, P7_PSPOFF };
+ 
+ /* This array is used to set the TYPE field for format P8. 
+    Note that entry 0 is not used in this array, so it is filled with
+    rp_spel for completely arbitrary reasons.  */
+ static unw_record_type P8_record_types[] = {
+   rp_sprel, rp_sprel, pfs_sprel, preds_sprel, lc_sprel, unat_sprel, fpsr_sprel, 
+   bsp_when, bsp_psprel, bsp_sprel, bspstore_when, bspstore_psprel,
+   bspstore_sprel, rnat_when, rnat_psprel, rnat_sprel, priunat_when_gr,
+   priunat_psprel, priunat_sprel, priunat_when_mem };
+ 
+ /* These values and the array are used to determine which additional ULEB128
+    fields are required for the P8 format.  */
+ #define P8_T		0
+ #define P8_PSPOFF       1
+ #define P8_SPOFF	2
+ static unsigned char P8_additional_fields [] = {
+   P8_SPOFF, P8_SPOFF, P8_SPOFF, P8_SPOFF, P8_SPOFF, P8_SPOFF,
+   P8_T, P8_PSPOFF, P8_SPOFF, P8_T, P8_PSPOFF, P8_SPOFF,
+   P8_T, P8_PSPOFF, P8_SPOFF, P8_T, P8_PSPOFF, P8_SPOFF, P8_T };
+ 
+ 
+ static unsigned char *
+ read_P_record (data, val, ptr)
+      unwind_record *data;
+      unsigned char val;
+      unsigned char *ptr;
+ {
+   if ((val & 0xe0) == 0x80)
+     {
+       /* P1 format.  */
+       data->type = br_mem;
+       data->record.p.brmask = (val & 0x1f);
+       return ptr;
+     }
+ 
+   if ((val & 0xf0) == 0xa0)
+     {
+       /* P2 format.  */
+       int byte1;
+       data->type = br_gr;
+       byte1 = *ptr++;
+       data->record.p.brmask = (val & 0x0f) << 1 + (byte1 >> 7);
+       data->record.p.gr = GR_REG (byte1 & 0x7f);
+       return ptr;
+     }
+ 
+   if ((val & 0xf8) == 0xB0)
+     {
+       /* P3 format.  */
+       int byte1 = *ptr++;
+       int r = ((val & 0x07) << 1) + (byte1 >> 7);
+       data->type = P3_record_types[r];
+       if (r == 6)
+         data->record.p.br = BR_REG (byte1 & 0x7f);
+       else
+         data->record.p.gr = GR_REG (byte1 & 0x7f);
+       if (r > 11)
+         bad_record (ptr - 2, 0);
+       return ptr;
+     }
+ 
+   if (val == UNW_P4)
+     {
+       /* P4 format.  Currently unimplemented.  */
+       int len = 0;  /* TODO.. get prologue rlen. */
+       int size = (len * 2 + 7) / 8;
+ 
+       data->type = spill_mask;
+       data->record.p.imask = (unsigned char *) malloc (size);
+       /* memcpy (data->record.p.imask, ptr, size);  */
+       return ptr+size;
+     }
+ 
+   if (val == UNW_P5)
+     {
+       /* P5 format.  */
+       int byte1 = *ptr++;
+       int byte2 = *ptr++;
+       int byte3 = *ptr++;
+       data->type = frgr_mem;
+       data->record.p.grmask = (byte1 >> 4);
+       data->record.p.frmask = ((byte1 & 0x0f) << 16) | (byte2 << 8) | byte3;
+       return ptr;
+     }
+   
+   if ((val & 0xe0) == UNW_P6)
+     {
+       /* P6 format.  */
+       if ((val & 0x10) == 0)
+         data->type = fr_mem;
+       else
+         data->type = gr_mem;
+       data->record.p.rmask = (val & 0x0f);
+       return ptr;
+     }
+   
+   if ((val & 0xf0) == UNW_P7)
+     {
+       /* P7 format.  */
+       int r = (val & 0x0f);
+       data->type = P7_record_types[r];
+       switch (P7_additional_fields[r])
+         {
+ 	  case P7_T_SIZE:
+ 	    data->record.p.t = read_uleb128 (&ptr);
+ 	    data->record.p.size = read_uleb128 (&ptr);
+ 	    break;
+ 	  case P7_T:
+ 	    data->record.p.t = read_uleb128 (&ptr);
+ 	    break;
+ 	  case P7_PSPOFF:
+ 	    data->record.p.pspoff = read_uleb128 (&ptr);
+ 	    break;
+ 	  case P7_SPOFF:
+ 	    data->record.p.spoff = read_uleb128 (&ptr);
+ 	    break;
+ 	}
+       return ptr;
+     }
+  
+   if (val == UNW_P8)
+     {
+       /* P8 format.  */
+       int r = *ptr++;
+       data->type = P8_record_types[r];
+       switch (P8_additional_fields[r])
+         {
+ 	  case P8_T:
+ 	    data->record.p.t = read_uleb128 (&ptr);
+ 	    break;
+ 	  case P8_PSPOFF:
+ 	    data->record.p.pspoff = read_uleb128 (&ptr);
+ 	    break;
+ 	  case P8_SPOFF:
+ 	    data->record.p.spoff = read_uleb128 (&ptr);
+ 	    break;
+ 	}
+       return ptr;
+     }
+   
+   if (val == UNW_P9)
+     {
+       /* P9 format.  */
+       int byte1 = *ptr++;
+       int byte2 = *ptr++;
+       data->type = gr_gr;
+       data->record.p.grmask = (byte1 & 0x0f);
+       data->record.p.gr = GR_REG (byte2 & 0x7f);
+       return ptr;
+     }
+   
+   if (val == UNW_P10)
+     {
+       /* P10 format.  */
+       int abi = *ptr++;
+       int context = *ptr++;
+       /* TODO. something about abi entries.  */
+       return ptr;
+     }
+ 
+   return ptr;
+ }
+ 
+ 
+ /* Frame processing routines.  */
+ 
+ /* Initialize a single register structure.  */
+ static void 
+ init_ia64_reg_loc (reg, size)
+      ia64_reg_loc *reg;
+      short size;
+ {
+   reg->when = -1;
+   reg->loc_type = IA64_UNW_LOC_TYPE_NONE;
+   reg->l.mem = (void *)0;
+   reg->reg_size = size;
+ }
+ 
+ /* Iniitialize an entire frame to the default of nothing.  */
+ static void
+ init_ia64_unwind_frame (frame) 
+      ia64_frame_state *frame ;
+ {
+   int x;
+   
+   for (x = 0; x < 4; x++)
+     init_ia64_reg_loc (&frame->gr[x], 8);
+   for (x = 0; x < 20; x++)
+     init_ia64_reg_loc (&frame->fr[x], 16);
+   for (x = 0; x < 5; x++)
+     init_ia64_reg_loc (&frame->br[x], 8);
+ 
+   init_ia64_reg_loc (&frame->rp, 8);
+   init_ia64_reg_loc (&frame->fpsr, 8);
+   init_ia64_reg_loc (&frame->bsp, 8);
+   init_ia64_reg_loc (&frame->bspstore, 8);
+   init_ia64_reg_loc (&frame->rnat, 8);
+   init_ia64_reg_loc (&frame->pfs, 8);
+   init_ia64_reg_loc (&frame->unat, 8);
+   init_ia64_reg_loc (&frame->lc, 8);
+   init_ia64_reg_loc (&frame->pr, 8);
+   init_ia64_reg_loc (&frame->priunat, 8);
+   init_ia64_reg_loc (&frame->sp, 8);
+   init_ia64_reg_loc (&frame->psp, 8);
+   init_ia64_reg_loc (&frame->spill_base, 8);
+ }
+ 
+ /* This fuction will process a single descriptor.
+    addr is a pointer to the descriptor record to read, 
+    frame is the current frame state structure, which will be
+      modified to reflect this descriptor.
+    len is the length of a prologue region, or -1 if it wasn't one.
+    the return value is a pointer to the start of the next descriptor.  */
+ 
+ static void *
+ execute_one_ia64_descriptor (addr, frame, len)
+      void *addr;
+      ia64_frame_state *frame;
+      long *len;
+ {
+   unwind_record r;
+   ia64_reg_loc *loc_ptr = NULL;
+   int grmask = 0, frmask = 0;
+ 
+   *len = -1;
+   addr = get_unwind_record (1, &r, addr);
+ 
+   /* process it in 2 phases, the first phase will either do the work,
+      or set up a pointer to the records we care about 
+      (ie a special purpose ar perhaps, and the second will actually 
+      fill in the record.  */
+   switch (r.type) 
+     {
+       case prologue:
+       case body:
+ 	*len = r.record.r.rlen;
+ 	break;
+       case prologue_gr:
+         {
+ 	  int val, reg;
+ 
+ 	  *len = r.record.r.rlen;
+ 	  val = r.record.r.mask;
+ 	  reg = r.record.r.grsave;
+ 	  if (val & 0x08)
+ 	    {
+ 	      frame->rp.when = 0;
+ 	      frame->rp.loc_type  = IA64_UNW_LOC_TYPE_GR;
+ 	      frame->rp.l.regno = reg++;
+ 	    }
+ 	  if (val & 0x04)
+ 	    {
+ 	      frame->pfs.when = 0;
+ 	      frame->pfs.loc_type  = IA64_UNW_LOC_TYPE_GR;
+ 	      frame->pfs.l.regno = reg++;
+ 	    }
+ 	  if (val & 0x02)
+ 	    {
+ 	      frame->psp.when = 0;
+ 	      frame->psp.loc_type  = IA64_UNW_LOC_TYPE_GR;
+ 	      frame->psp.l.regno = reg++;
+ 	    }
+ 	  if (val & 0x01)
+ 	    {
+ 	      frame->pr.when = 0;
+ 	      frame->pr.loc_type  = IA64_UNW_LOC_TYPE_GR;
+ 	      frame->pr.l.regno = reg++;
+ 	    }
+ 	  break;
+ 	}
+       case mem_stack_f:
+       case mem_stack_v:
+         frame->sp.when = r.record.p.t; 
+ 	frame->sp.l.offset = r.record.p.size;
+ 	frame->sp.loc_type = IA64_UNW_LOC_TYPE_OFFSET;
+ 	break;
+       case psp_gr:
+       case psp_sprel:
+         loc_ptr = &frame->psp;
+ 	break;
+       case rp_br:
+       case rp_gr:
+       case rp_when:
+       case rp_psprel:
+       case rp_sprel:
+         loc_ptr = &frame->rp;
+ 	break;
+       case pfs_gr:
+       case pfs_when:
+       case pfs_psprel:
+       case pfs_sprel:
+         loc_ptr = &frame->pfs;
+ 	break;
+       case preds_gr:
+       case preds_when:
+       case preds_psprel:
+       case preds_sprel:
+         loc_ptr = &frame->pr;
+ 	break;
+       case unat_gr:
+       case unat_when:
+       case unat_psprel:
+       case unat_sprel:
+         loc_ptr = &frame->unat;
+ 	break;
+       case lc_gr:
+       case lc_when:
+       case lc_psprel:
+       case lc_sprel:
+         loc_ptr = &frame->lc;
+ 	break;
+       case fpsr_gr:
+       case fpsr_when:
+       case fpsr_psprel:
+       case fpsr_sprel:
+         loc_ptr = &frame->fpsr;
+ 	break;
+       case priunat_gr:
+       case priunat_sprel:
+       case priunat_when_gr:
+       case priunat_when_mem:
+       case priunat_psprel:
+         loc_ptr = &frame->priunat;
+ 	break;
+       case bsp_gr:
+       case bsp_sprel:
+       case bsp_when:
+       case bsp_psprel:
+         loc_ptr = &frame->bsp;
+ 	break;
+       case bspstore_gr:
+       case bspstore_sprel:
+       case bspstore_when:
+       case bspstore_psprel:
+         loc_ptr = &frame->bspstore;
+ 	break;
+       case rnat_gr:
+       case rnat_sprel:
+       case rnat_when:
+       case rnat_psprel:
+         loc_ptr = &frame->rnat;
+ 	break;
+       case spill_base:
+         loc_ptr = &frame->spill_base;
+ 	break;
+       case fr_mem:
+         frmask = r.record.p.rmask;
+ 	break;
+       case gr_mem:
+         grmask = r.record.p.rmask;
+ 	break;
+       case frgr_mem:
+         frmask = r.record.p.frmask;
+         grmask = r.record.p.grmask;
+ 	break;
+       case br_mem:
+         {
+ 	  int x, mask = 0x01;
+ 	  int saved = r.record.p.brmask;
+ 	  for (x = 0; x < 5; x++)
+ 	    {
+ 	      if (saved & mask)
+ 		frame->br[x].loc_type = IA64_UNW_LOC_TYPE_SPILLBASE;
+ 	      mask = mask << 1;
+ 	    }
+ 	  break;
+ 	}
+       case br_gr:
+         {
+ 	  int x, mask = 0x01;
+ 	  int reg = r.record.p.gr;
+ 	  int saved = r.record.p.brmask;
+ 	  for (x = 0; x < 5; x++)
+ 	    {
+ 	      if (saved & mask)
+ 	        {
+ 		  frame->br[x].loc_type = IA64_UNW_LOC_TYPE_GR;
+ 		  frame->br[x].l.regno = reg++;
+ 		}
+ 	      mask = mask << 1;
+ 	    }
+ 	  break;
+ 	}
+       case gr_gr:
+         {
+ 	  int x, mask = 0x01;
+ 	  int reg = r.record.p.gr;
+ 	  int saved = r.record.p.grmask;
+ 	  for (x = 0; x < 4; x++)
+ 	    {
+ 	      if (saved & mask)
+ 	        {
+ 		  frame->br[x].loc_type = IA64_UNW_LOC_TYPE_GR;
+ 		  frame->br[x].l.regno = reg++;
+ 		}
+ 	      mask = mask << 1;
+ 	    }
+ 	  break;
+ 	}
+       case spill_mask:
+         /* TODO.  */
+ 	break;
+       case epilogue:
+         /* TODO.  */
+ 	break;
+       case label_state:
+         /* TODO.  */
+ 	break;
+       case copy_state: 
+         /* TODO. */
+ 	break;
+       case spill_psprel:
+       case spill_sprel:
+       case spill_reg:
+       case spill_psprel_p:
+       case spill_sprel_p:
+       case spill_reg_p:
+         /* TODO. */
+ 	break;
+       default:
+ 	abort ();
+ 	break;
+     }
+ 
+   if (frmask)
+     {
+       int x, mask = 0x01, saved;
+       for (x = 0; x < 20; x++)
+ 	{
+ 	  if (frmask & mask)
+ 	    frame->fr[x].loc_type = IA64_UNW_LOC_TYPE_SPILLBASE;
+ 	  mask = mask << 1;
+ 	}
+     }
+ 
+   if (grmask)
+     {
+       int x, mask = 0x01;
+       for (x = 0; x < 4; x++)
+ 	{
+ 	  if (grmask & mask)
+ 	    frame->gr[x].loc_type = IA64_UNW_LOC_TYPE_SPILLBASE;
+ 	  mask = mask << 1;
+ 	}
+     }
+ 
+   /* If there is more to do:  */
+   if (loc_ptr != NULL)
+     switch (r.type) 
+       {
+ 	case psp_gr:
+ 	case rp_gr:
+ 	case pfs_gr:
+ 	case preds_gr:
+ 	case unat_gr:
+ 	case lc_gr:
+ 	case fpsr_gr:
+ 	case priunat_gr:
+ 	case bsp_gr:
+ 	case bspstore_gr:
+ 	case rnat_gr:
+ 	  loc_ptr->loc_type = IA64_UNW_LOC_TYPE_GR;
+ 	  loc_ptr->l.regno = r.record.p.gr;
+ 	  break;
+ 	case rp_br:
+ 	  loc_ptr->loc_type = IA64_UNW_LOC_TYPE_BR;
+ 	  loc_ptr->l.regno = r.record.p.br;
+ 	  break;
+ 	case rp_when:
+ 	case pfs_when:
+ 	case preds_when:
+ 	case unat_when:
+ 	case lc_when:
+ 	case fpsr_when:
+ 	case priunat_when_gr:
+ 	case priunat_when_mem:
+ 	case bsp_when:
+ 	case bspstore_when:
+ 	case rnat_when:
+ 	  loc_ptr->when = r.record.p.t;
+ 	  break;
+ 	case rp_psprel:
+ 	case pfs_psprel:
+ 	case preds_psprel:
+ 	case unat_psprel:
+ 	case lc_psprel:
+ 	case fpsr_psprel:
+ 	case priunat_psprel:
+ 	case bsp_psprel:
+ 	case bspstore_psprel:
+ 	case rnat_psprel:
+ 	case spill_base:
+ 	  loc_ptr->loc_type = IA64_UNW_LOC_TYPE_PSPOFF;
+ 	  loc_ptr->l.offset = r.record.p.pspoff;
+ 	  break;
+ 	case psp_sprel:
+ 	case rp_sprel:
+ 	case pfs_sprel:
+ 	case preds_sprel:
+ 	case unat_sprel:
+ 	case lc_sprel:
+ 	case fpsr_sprel:
+ 	case priunat_sprel:
+ 	case bsp_sprel:
+ 	case bspstore_sprel:
+ 	case rnat_sprel:
+ 	  loc_ptr->loc_type = IA64_UNW_LOC_TYPE_SPOFF;
+ 	  loc_ptr->l.offset = r.record.p.spoff;
+ 	  break;
+ 	default:
+ 	  abort ();
+ 	  break;
+       }
+   return addr;
+ }
+ 
+ 
+ #define IS_NaT_COLLECTION_ADDR(addr) ((((long)(addr) >> 3) & 0x3f) == 0x3f)
+ 
+ /* Returns the address of the slot that's NSLOTS slots away from
+    the address ADDR. NSLOTS may be positive or negative. */
+ static void *
+ rse_address_add(unsigned char *addr, int nslots)
+ {
+   unsigned char *new_addr;
+   int mandatory_nat_slots = nslots / 63;
+   int direction = nslots < 0 ? -1 : 1;
+ 
+   new_addr = addr + 8 * (nslots + mandatory_nat_slots);
+ 
+   if (((long)new_addr >> 9)  != ((long)(addr + 8 * 64 * mandatory_nat_slots) >> 9))
+     new_addr += 8 * direction;
+ 
+   if (IS_NaT_COLLECTION_ADDR(new_addr))
+     new_addr += 8 * direction;
+ 
+   return new_addr;
+ }
+ 
+ 
+ /* Normalize a record to originate in either a register or memory 
+    location.  */
+ static void
+ normalize_reg_loc (frame, reg)
+      ia64_frame_state *frame;
+      ia64_reg_loc *reg;
+ {
+   unsigned char *tmp;
+   switch (reg->loc_type)
+     {
+       case IA64_UNW_LOC_TYPE_MEM:
+         /* Already done.  */
+         break;
+       case IA64_UNW_LOC_TYPE_GR:
+         /* If the register its saved in is a LOCAL register, we know
+ 	   its actually in memory, so we'll pick it up from there.  */
+         if (reg->l.regno >= 32 && frame->my_bsp != 0)
+ 	  {
+ 	   /* Get from backing store.  */
+ 	    tmp = rse_address_add(frame->my_bsp, reg->l.regno - 32);
+ 	    reg->l.mem = tmp;
+ 	    reg->loc_type = IA64_UNW_LOC_TYPE_MEM;
+ 	  }
+         break;
+       case IA64_UNW_LOC_TYPE_FR:
+         /* If the register its saved in is a LOCAL register, we know
+ 	   its actually in memory, so we'll pick it up from there.  */
+         if (reg->l.regno >= 32)
+ 	  {
+ 	   /* TODO. get from backing store.  */
+ 	  }
+         break;
+       case IA64_UNW_LOC_TYPE_BR:
+         break;
+       case IA64_UNW_LOC_TYPE_SPOFF:
+         /* offset from the stack pointer, calculate the memory address
+ 	   now.  */
+ 	tmp = (unsigned char *)frame->my_sp + reg->l.offset * 4;
+ 	reg->l.mem = tmp;
+ 	reg->loc_type = IA64_UNW_LOC_TYPE_MEM;
+         break;
+       case IA64_UNW_LOC_TYPE_PSPOFF:
+         /* Actualy go get the value of the PSP add the offset, and thats 
+ 	   the mem location we can find this value at. */
+ 	tmp = (*(unsigned char **)(frame->psp.l.mem)) + 16 - reg->l.offset * 4;
+ 	reg->l.mem = tmp;
+ 	reg->loc_type = IA64_UNW_LOC_TYPE_MEM;
+         break;
+       case IA64_UNW_LOC_TYPE_SPILLBASE:
+         /* located at the current spill base memory location, and we
+ 	   have to bump it as well. */
+ 	reg->l.mem = frame->spill_base.l.mem;
+ 	reg->loc_type = IA64_UNW_LOC_TYPE_MEM;
+ 	frame->spill_base.l.mem += 8;
+         break;
+     }
+ 
+ }
+ /* this function looks at a reg_loc and determines if its going
+    to be an executed record or not between time start and end.  
+    It is executed if it is exectued at START time. It is NOT
+    executed if it happens at END time. */
+ static void 
+ maybe_normalize_reg_loc (frame, reg, start, end)
+      ia64_frame_state *frame;
+      ia64_reg_loc *reg;
+      int start, end;
+ {
+   if (reg->loc_type != IA64_UNW_LOC_TYPE_NONE 
+       && reg->when >= start && reg->when < end)
+     normalize_reg_loc (frame, reg);
+ }
+ 
+ 
+ /* Only works for 8 byte or less registers.  */
+ void *
+ __get_real_reg_value (reg)
+      ia64_reg_loc *reg;
+ {
+   if (reg->loc_type == IA64_UNW_LOC_TYPE_MEM)
+     return *((void **)(reg->l.mem));
+   
+   /* All registers should be in memory if we've saved them. Local 
+      registers will be in backing store.  */
+   abort ();
+ }
+ 
+ void
+ __set_real_reg_value (reg, val) 
+      ia64_reg_loc *reg;
+      void *val;
+ {
+   if (reg->loc_type == IA64_UNW_LOC_TYPE_MEM)
+     {
+       void **ptr = reg->l.mem;
+       *ptr = val;
+       return;
+     }
+   abort ();
+ }
+ 
+ static void
+ copy_reg_value (src, dest)
+      ia64_reg_loc *src;
+      ia64_reg_loc *dest;
+ {
+   void **p = dest->l.mem;
+   if (src->loc_type == IA64_UNW_LOC_TYPE_NONE)
+     return;
+   
+   if (src->reg_size != dest->reg_size)
+     abort ();
+   if (src->reg_size <= 8)
+     *p = __get_real_reg_value (src);
+   else
+     {
+       void **d;
+       if (src->reg_size> 16)
+         abort ();
+       if (dest->loc_type != IA64_UNW_LOC_TYPE_MEM)
+         abort ();
+       d = (void **)(dest->l.mem);
+       *p++ = *d++;
+       *p = *d;
+     }
+   return;
+ }
+ 
+ /* Copy the values of any relevant saved registers in one frame 
+    to another for unwinding.  */
+ void 
+ __copy_saved_reg_state (dest, src)
+      ia64_frame_state *dest;
+      ia64_frame_state *src;
+ {
+   int x;
+   for (x = 0; x < 4 ; x++)
+     copy_reg_value (&src->gr[x], &dest->gr[x]);
+   for (x = 0; x < 20 ; x++)
+     copy_reg_value (&src->fr[x], &dest->fr[x]);
+   for (x = 0; x < 5 ; x++)
+     copy_reg_value (&src->br[x], &dest->br[x]);
+       
+   copy_reg_value (&src->fpsr, &dest->fpsr);
+   copy_reg_value (&src->rnat, &dest->rnat);
+   copy_reg_value (&src->unat, &dest->unat);
+   copy_reg_value (&src->lc, &dest->lc);
+   copy_reg_value (&src->pr, &dest->pr);
+   copy_reg_value (&src->priunat, &dest->priunat);
+   copy_reg_value (&src->pfs, &dest->pfs);
+ }
+ 
+ 
+ static void 
+ process_state_between (frame, start, end)
+      ia64_frame_state *frame;
+      int start, end;
+ {
+   int x;
+   /* PSP, RP, SP, and PFS are handled seperately from here. */
+ 
+   /* GR's, FR's and BR's are saved at an arbitrary point, so we
+       should handle them at teh very beginning.  */
+   if (start == 0)
+     {
+       for (x = 0; x < 4 ; x++)
+ 	normalize_reg_loc (frame, &frame->gr[x]);
+       for (x = 0; x < 20 ; x++)
+ 	normalize_reg_loc (frame, &frame->fr[x]);
+       for (x = 0; x < 5 ; x++)
+ 	normalize_reg_loc (frame, &frame->br[x]);
+     }
+   
+   maybe_normalize_reg_loc (frame, &frame->fpsr, start, end);
+   maybe_normalize_reg_loc (frame, &frame->bsp, start, end);
+   maybe_normalize_reg_loc (frame, &frame->bspstore, start, end);
+   maybe_normalize_reg_loc (frame, &frame->rnat, start, end);
+   maybe_normalize_reg_loc (frame, &frame->unat, start, end);
+   maybe_normalize_reg_loc (frame, &frame->lc, start, end);
+   maybe_normalize_reg_loc (frame, &frame->pr, start, end);
+   maybe_normalize_reg_loc (frame, &frame->priunat, start, end);
+ }
+ 
+ /* This function will take a frame state, and translate all the location
+    records into actual memory address, or register numbers, based on
+    what the ia64_reg_loc fields require to actually go get the values.  
+    (ie, this translates SPOFF and PSPOFF, etc into MEM types. 
+    frame is the frame to be changed.
+    unwind_time is the insn slot number we are unwinding to.  Anything 
+      that has a WHEN record beyond this time is cleared since it
+      isn't relevant.  */
+ static void
+ frame_translate (frame, unwind_time)
+      ia64_frame_state *frame;
+      long unwind_time;
+ {
+   int sp_offset = 0;
+   /* First, establish values of PFS and PSP and RP, if needed.  */
+ 
+   normalize_reg_loc (frame, &frame->pfs);
+   normalize_reg_loc (frame, &frame->psp);
+   normalize_reg_loc (frame, &frame->rp);
+  
+   if (frame->rp.loc_type == IA64_UNW_LOC_TYPE_NONE)
+     return;
+ 
+   /* The stack pointer at the function start is the PSP value
+      saved away.  */
+   frame->my_sp = __get_real_reg_value (&frame->psp);
+ 
+   if (frame->psp.loc_type != IA64_UNW_LOC_TYPE_MEM)
+     abort ();
+ 
+   /* spill base is set up off the PSP register, which should now 
+      have its value. */
+   normalize_reg_loc (frame, &frame->spill_base);
+ 
+   /* If the SP is adjusted, process records up to where it
+      is adjusted, then adjust it, then process the rest.  */
+   if (frame->sp.when >= 0)
+     {
+       process_state_between (frame, 0, frame->sp.when);
+       if (frame->sp.loc_type != IA64_UNW_LOC_TYPE_OFFSET)
+ 	abort ();
+       frame->my_sp = 
+ 	      (unsigned char *)frame->my_sp - frame->sp.l.offset;
+       process_state_between (frame, frame->sp.when, unwind_time);
+     }
+   else
+     process_state_between (frame, 0, unwind_time);
+ }
+ 
+ /* this function will set a frame_state with all the required fields
+    from a functions unwind descriptors.
+    pc is the location we need info up until (ie, the unwind point)
+    frame is the frame_state structure to be set up.
+    Returns a pointer to the unwind info pointer for the frame.  */
+ unwind_info_ptr *
+ __build_ia64_frame_state (pc, frame, bsp, pc_base_ptr)
+      unsigned char *pc;
+      ia64_frame_state *frame;
+      void *bsp;
+      void **pc_base_ptr;
+ {
+   long len;
+   int region_offset = 0;
+   int last_region_size = 0;
+   void *addr, *end;
+   unwind_table_entry *entry;
+   unsigned char *start_pc;
+   void *pc_base;
+   int pc_offset;
+   struct unwind_info_ptr *unw_info_ptr;
+ 
+   entry = find_fde (pc, &pc_base);
+   if (!entry)
+     return 0;
+ 
+   start_pc = pc_base + entry->start_offset;
+   unw_info_ptr = ((struct unwind_info_ptr *)(pc_base + entry->unwind_offset));
+   addr = unw_info_ptr->unwind_descriptors;
+   end = addr + unw_info_ptr->length * 8;
+   pc_offset = (pc - start_pc) / 16 * 3;
+ 
+   init_ia64_unwind_frame (frame);
+   frame->my_bsp = bsp;
+ 
+   /* stop when we get to the end of the descriptor list, or if we
+      encounter a region whose initial offset is already past the
+      PC we are unwinding too.  */
+ 
+   while (addr < end && pc_offset > region_offset)
+     {
+       /* First one must be a record header.  */
+       addr = execute_one_ia64_descriptor (addr, frame, &len);
+       if (len > 0)
+         {
+ 	  region_offset += last_region_size;
+ 	  last_region_size = len;
+ 	}
+     }
+   /* Now we go get the actual values.  */
+   frame_translate (frame, pc_offset);
+   if (pc_base_ptr)
+     *pc_base_ptr = pc_base;
+   return unw_info_ptr;
+ }
+ 
+ /* Given an unwind info pointer, return the personailty routine.  */
+ void *
+ __get_personality (ptr)
+      unwind_info_ptr *ptr;
+ {
+   void **p;
+   p = (void **) (ptr->unwind_descriptors + ptr->length * 8);
+   return *p;
+ }
+ 
+ void *
+ __get_except_table (ptr)
+      unwind_info_ptr *ptr;
+ {
+   void **p, *table;
+   p = (void **) (ptr->unwind_descriptors + ptr->length * 8);
+   /* If there is no personality, there is no handler data.  */
+   if (*p == 0)
+     return 0;
+   table = (void *) (ptr->unwind_descriptors + ptr->length * 8 + 8);
+   return table;
+ }
+ 
+ /* Given a PFS value, and the current BSp, calculate the BSp of the caller.  */
+ void *
+ __calc_caller_bsp (pfs, bsp)
+      long pfs;
+      unsigned char *bsp;
+ {
+   int size_of_locals;
+   void *new_bsp;
+ 
+   /* The PFS looks like :  xxxx SOL:7 SOF:7. The SOF is bits 0-7 and SOL 
+      is bits 8-15. We only care about SOL.  */
+ 
+   size_of_locals = (pfs >> 7) & 0x7f;
+   return rse_address_add (bsp, -size_of_locals);
+ }
+ 
+ static int 
+ ia64_backtrace_helper (void **array, void *throw_pc, 
+ 		       ia64_frame_state *throw_frame,
+ 		       ia64_frame_state *frame, void *bsp, int size)
+ {
+   void *pc = NULL;
+   int frame_count = 0;
+   unwind_info_ptr *info;
+ 
+   __builtin_ia64_flushrs ();      /*  Make the local register stacks available.  */
+ 
+   /* Start at our stack frame, get our state.  */
+   info = __build_ia64_frame_state (throw_pc, throw_frame, bsp, NULL);
+ 
+   *frame = *throw_frame;
+ 
+   while (info && frame_count < size)
+     {
+       pc = array[frame_count++] = __get_real_reg_value (&frame->rp);
+       --pc;
+       bsp = __calc_caller_bsp 
+ 	((long)__get_real_reg_value (&frame->pfs), frame->my_bsp);
+       info = __build_ia64_frame_state (pc, frame, bsp, NULL);
+       if (frame->rp.loc_type == IA64_UNW_LOC_TYPE_NONE) /* We've finished. */
+ 	break;
+     }
+ 
+   return frame_count;
+ }
+ 
+ /* This is equivalent to glibc's backtrace(). */
+   
+ int
+ __ia64_backtrace (void **array, int size)
+ {
+   ia64_frame_state my_frame;
+   ia64_frame_state originator;	/* For the context handler is in.  */
+   void *bsp;
+  
+   /* Do any necessary initialization to access arbitrary stack frames.
+      This forces gcc to save memory in our stack frame for saved
+      registers. */
+   __builtin_unwind_init ();
+ 
+ label_ia64:
+   bsp = __builtin_ia64_bsp ();
+   
+   return ia64_backtrace_helper (array, &&label_ia64, &my_frame, 
+ 				&originator, bsp, size);
+ }
+ 
+ 
+ 
+ #ifndef inhibit_libc
+ 
+ #if 0
+ #undef NULL;
+ #include <stdio.h>
+ 
+ /* Routines required to generate debug info for the ia64
+    unwind descriptors.  */
+ 
+ static unsigned char *record_name[] = { 
+   "prologue", "prologue_gr", "body", "mem_stack_f", "mem_stack_v", "psp_gr", 
+   "psp_sprel", "rp_when", "rp_gr", "rp_br", "rp_psprel", "rp_sprel", 
+   "pfs_when", "pfs_gr", "pfs_psprel", "pfs_sprel", "preds_when", "preds_gr", 
+   "preds_psprel", "preds_sprel", "fr_mem", "frgr_mem", "gr_gr", "gr_mem", 
+   "br_mem", "br_gr", "spill_base", "spill_mask", "unat_when", "unat_gr", 
+   "unat_psprel", "unat_sprel", "lc_when", "lc_gr", "lc_psprel", "lc_sprel", 
+   "fpsr_when", "fpsr_gr", "fpsr_psprel", "fpsr_sprel", "priunat_when_gr", 
+   "priunat_when_mem", "priunat_gr", "priunat_psprel", "priunat_sprel", 
+   "bsp_when", "bsp_gr", "bsp_psprel", "bsp_sprel", "bspstore_when", 
+   "bspstore_gr", "bspstore_psprel", "bspstore_sprel", "rnat_when", "rnat_gr", 
+   "rnat_psprel", "rnat_sprel", "epilogue", "label_state", "copy_state", 
+   "spill_psprel", "spill_sprel", "spill_reg", "spill_psprel_p", 
+   "spill_sprel_p","spill_reg_p" 
+ };
+ 
+ 
+ 
+ static void
+ print_record (f, ptr)
+      FILE *f;
+      unwind_record *ptr;
+ {
+   fprintf (f, " %s ",record_name[ptr->type]);
+   switch (ptr->type) 
+     {
+       case prologue:
+       case body:
+ 	fprintf (f, "(R1) rlen = %d", ptr->record.r.rlen);
+ 	break;
+       case prologue_gr:
+ 	fprintf (f, "(R2) rlen = %d : ", ptr->record.r.rlen);
+ 	fprintf (f, "grmask = %x, grsave = r%d", ptr->record.r.mask, 
+ 						 ptr->record.r.grsave);
+ 	break;
+       case mem_stack_f:
+       case mem_stack_v:
+ 	fprintf (f, "(P7) t = %d, size = %d", ptr->record.p.t, 
+ 					 ptr->record.p.size);
+ 	break;
+       case psp_gr:
+       case rp_gr:
+       case pfs_gr:
+       case preds_gr:
+       case unat_gr:
+       case lc_gr:
+       case fpsr_gr:
+       case priunat_gr:
+       case bsp_gr:
+       case bspstore_gr:
+       case rnat_gr:
+ 	fprintf (f, "(P3) r%d", ptr->record.p.gr);
+ 	break;
+       case rp_br:
+ 	fprintf (f, "(P3) b%d", ptr->record.p.br);
+ 	break;
+       case psp_sprel:
+ 	fprintf (f, "(P7) spoff = %d", ptr->record.p.spoff);
+ 	break;
+       case rp_when:
+       case pfs_when:
+       case preds_when:
+       case unat_when:
+       case lc_when:
+       case fpsr_when:
+ 	fprintf (f, "(P7) t = %d", ptr->record.p.t);
+ 	break;
+       case rp_psprel:
+       case pfs_psprel:
+       case preds_psprel:
+       case unat_psprel:
+       case lc_psprel:
+       case fpsr_psprel:
+       case spill_base:
+ 	fprintf (f, "(P7) pspoff = %d", ptr->record.p.pspoff, 0);
+ 	break;
+       case rp_sprel:
+       case pfs_sprel:
+       case preds_sprel:
+       case unat_sprel:
+       case lc_sprel:
+       case fpsr_sprel:
+       case priunat_sprel:
+       case bsp_sprel:
+       case bspstore_sprel:
+       case rnat_sprel:
+ 	fprintf (f, "(P8) spoff = %d", ptr->record.p.spoff);
+ 	break;
+       case fr_mem:
+       case gr_mem:
+ 	fprintf (f, "(P6) rmask = %x", ptr->record.p.rmask);
+ 	break;
+       case frgr_mem:
+ 	fprintf (f, "(P5) grmask = %x,  frmask = %x", ptr->record.p.grmask, 
+ 						 ptr->record.p.frmask);
+ 	break;
+       case gr_gr:
+ 	fprintf (f, "(P9) grmask = %x  gr = r%d\n", ptr->record.p.grmask, 
+ 					       ptr->record.p.gr);
+ 	break;
+       case br_mem:
+ 	fprintf (f, "(P1) brmask = %x", ptr->record.p.brmask);
+ 	break;
+       case br_gr:
+ 	fprintf (f, "(P2) brmask = %x,  gr = r%d", ptr->record.p.brmask, 
+ 					      ptr->record.p.gr);
+ 	break;
+       case spill_mask:
+ 	fprintf (f, "spill mask....  unimplemented");
+ 	break;
+       case priunat_when_gr:
+       case priunat_when_mem:
+       case bsp_when:
+       case bspstore_when:
+       case rnat_when:
+ 	fprintf (f, "(P8) t = %d\n", ptr->record.p.t);
+ 	break;
+       case priunat_psprel:
+       case bsp_psprel:
+       case bspstore_psprel:
+       case rnat_psprel:
+ 	fprintf (f, "(P8) pspoff = %d", ptr->record.p.pspoff);
+ 	break;
+       case epilogue:
+ 	fprintf (f, "epilogue record unimplemented.");
+ 	break;
+       case label_state:
+ 	fprintf (f, "label_state record unimplemented.");
+ 	break;
+       case copy_state:
+ 	fprintf (f, "copy_state record unimplemented.");
+ 	break;
+       case spill_psprel:
+       case spill_sprel:
+       case spill_reg:
+       case spill_psprel_p:
+       case spill_sprel_p:
+       case spill_reg_p:
+ 	fprintf (f, "spill_* record unimplemented.");
+ 	break;
+       default:
+ 	fprintf (f, "record_type_not_valid");
+ 	break;
+     }
+   fprintf (f, "\n");
+   
+ }
+ 
+ static void
+ print_all_records (f, mem, size)
+      FILE *f;
+      unsigned char *mem;
+      int size;
+ {
+   unsigned char *end = mem + size;
+   unwind_record r;
+ 
+   fprintf (f, "UNWIND IMAGE:\n");
+   while (mem < end) 
+     {
+       mem = get_unwind_record (1, &r, mem);
+       print_record (f, &r);
+     }
+   fprintf (f, "--end unwind image--\n\n");
+ }
+ #endif /* If 0 */
+ #endif /* inhibit_libc */
+ #endif   /* IA64_UNWIND_INFO  */
Index: config/ia64/ia64.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/ia64/ia64.c,v
retrieving revision 1.11
diff -c -2 -p -r1.11 ia64.c
*** ia64.c	2000/05/03 18:46:05	1.11
--- ia64.c	2000/05/05 18:49:50
*************** rtx_needs_barrier (x, flags, pred)
*** 2519,2524 ****
--- 2519,2527 ----
  	case 20: /* mov = ar.bsp */
            break;
+ 	case 21: /* flushrs */
+           break;
  
  	default:
  	  abort ();
  	}
*************** ia64_init_builtins ()
*** 3202,3205 ****
--- 3205,3214 ----
    def_builtin ("__sync_lock_release_di", void_ftype_pdi, IA64_BUILTIN_LOCK_RELEASE_DI);
  
+   def_builtin ("__builtin_ia64_bsp", build_function_type (ptr_type_node, endlink), IA64_BUILTIN_BSP);
+ 
+   def_builtin ("__builtin_ia64_flushrs", 
+ 	       build_function_type (void_type_node, endlink), 
+ 	       IA64_BUILTIN_FLUSHRS);
+ 
    /* Add all builtins that are operations on two args. */
    for (i=0, d = bdesc_2argsi; i < sizeof(bdesc_2argsi) / sizeof *d; i++, d++)
*************** ia64_expand_builtin (exp, target, subtar
*** 3609,3612 ****
--- 3618,3634 ----
        emit_insn (gen_movdi (op0, GEN_INT(0)));
        return 0;
+ 
+     case IA64_BUILTIN_BSP:
+       {
+ 	rtx reg = gen_reg_rtx (DImode);
+ 	emit_insn (gen_bsp_value (reg));
+ 	return reg;
+       }
+ 
+     case IA64_BUILTIN_FLUSHRS:
+       {
+ 	emit_insn (gen_flushrs ());
+ 	return 0;
+       }
  
      default:
Index: config/ia64/ia64.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/ia64/ia64.h,v
retrieving revision 1.11
diff -c -2 -p -r1.11 ia64.h
*** ia64.h	2000/05/03 18:46:05	1.11
--- ia64.h	2000/05/05 18:49:51
*************** enum ia64_builtins
*** 2889,2893 ****
    IA64_BUILTIN_LOCK_TEST_AND_SET_DI,
  
!   IA64_BUILTIN_LOCK_RELEASE_DI
  };
  
--- 2889,2896 ----
    IA64_BUILTIN_LOCK_TEST_AND_SET_DI,
  
!   IA64_BUILTIN_LOCK_RELEASE_DI,
! 
!   IA64_BUILTIN_BSP,
!   IA64_BUILTIN_FLUSHRS
  };
  
Index: config/ia64/ia64.md
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/ia64/ia64.md,v
retrieving revision 1.8
diff -c -2 -p -r1.8 ia64.md
*** ia64.md	2000/05/03 18:46:05	1.8
--- ia64.md	2000/05/05 18:49:51
***************
*** 3140,3143 ****
--- 3140,3149 ----
     mov ar.rsc=r19\;"
    [(set_attr "type" "I")])
+ 
+ (define_insn "flushrs"
+   [(unspec [(const_int 0)] 21)]
+   ""
+   ";; \; flushrs"
+   [(set_attr "type" "M")])
  
  ;; ::::::::::::::::::::
Index: config/ia64/t-ia64
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/ia64/t-ia64,v
retrieving revision 1.2
diff -c -2 -p -r1.2 t-ia64
*** t-ia64	2000/03/15 19:35:26	1.2
--- t-ia64	2000/05/05 18:49:51
*************** crtendS.o: $(srcdir)/config/ia64/crtend.
*** 40,41 ****
--- 40,43 ----
  
  EXTRA_HEADERS = $(srcdir)/config/ia64/ia64intrin.h
+ LIB2FUNCS_EXTRA = $(srcdir)/config/ia64/ia64-frame.c
+ 



More information about the Gcc-patches mailing list