This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

patch to handle i386 stack alignment using the stack pointer (ver. 2)


[ The only change is that this version applies against the current source
  tree and gdb-4.17-PatchJLW01 has both gdb patches consolidated togeither. ]

This patch causes gcc to align the i386 stack when necessary.  It works
by using reload to eliminate the frame pointer from local variables in favor
of the stack pointer and by aligning the stack pointer in the prologue.

Notes:

  1) The definition for PREFERRED_STACK_BOUNDARY should be removed from
     i386.h in order for this patch to be useful.

  2) The BRL-CAD benchmark has a total of 443 functions which desire the
     stack to be aligned on a 64 bit boundary.  When compiling with a frame
     pointer 160 functions were successfully aligned.  When compiling without
     a frame pointer 89 functions were successfully aligned.

     The crafty benchmark has a total of 2 functions which desire the
     stack to be aligned on a 64 bit boundary.  When compiling with a frame
     1 function was successfully aligned.  When compiling without a frame
     0 functions were successfully aligned.

     A typical reason that compiling with a frame pointer failed to align
     the stack is due to the EXIT_IGNORE_STACK optimization preventing
     reload from replacing the frame pointer with the stack pointer.  A
     typical reason that compiling without a frame pointer failed to align
     the stack is due to the frame pointer register being already used
     (I don't force it to be spilled).

  3) Currently aligning the stack is disabled if flag_exceptions is set
     since it potentally interacts with the ability to unwind the stack.
     In general the interactions between this patch and exception handling
     needs to be examined.

  4) This version of gdb-4.17-PatchJLW01 has been submitted to the
     gdb maintainers.  It is included here only as a convenience.

Performance (233MHz Pentium II):

  BRL-CAD RayTracing benchmark
  (five different images ... larger numbers are better):

    egcs-current 19990122 aout

      24893.22	11650.86	10510.76	9600.45	12123.02

    egcs-current 19990122 aout omit-frame-pointer

      27179.11	11546.11	11094.55	9918.96	12786.46

    egcs-current 19990122 aout align stack pointer

      27077.31	11529.60	11044.90	9873.43	12733.91

    egcs-current 19990122 aout omit-frame-pointer align stack pointer

      26580.99	11330.90	10935.80	9880.35	12785.87

  Crafty Chess benchmark
  (Three runs each ... larger numbers are better):

    egcs-current 19990122 aout

      32869602	91304
      32772167	91033
      32864610	91290

    egcs-current 19990122 aout align stack pointer

      32789444	91081
      32791781	91088
      32792908	91091

    egcs-current 19990122 aout omit-frame-pointer

      33064805	91846
      33070808	91863
      33073712	91871

    egcs-current 19990122 aout omit-frame-pointer align stack pointer

      33256417	92378
      33192359	92201
      33269693	92415

Sizes:

  BRL-CAD RayTracing software:

    egcs-current 19990122 aout

      text	data	bss	dec	hex
      40960	4096	17328	62384	f3b0	../libfb/libfb.so.11
      45056	4096	0	49152	c000	../libnurb/libnurb.so.11
      16384	4096	0	20480	5000	../libpkg/libpkg.so.11
      45056	4096	0	49152	c000	../libplot3/libplot3.so.11
      1204224	20480	23088	1247792	130a30	../librt/librt.so.11
      4096	4096	0	8192	2000	../libsysv/libsysv.so.11
      16384	4096	0	20480	5000	../libwdb/libwdb.so.11
      180224	45056	1984	227264	377c0	../rt/rt

    egcs-current 19990122 aout align stack pointer

      text	data	bss	dec	hex
      40960	4096	17328	62384	f3b0	../libfb/libfb.so.11
      45056	4096	0	49152	c000	../libnurb/libnurb.so.11
      16384	4096	0	20480	5000	../libpkg/libpkg.so.11
      45056	4096	0	49152	c000	../libplot3/libplot3.so.11
      1208320	20480	23088	1251888	131a30	../librt/librt.so.11
      4096	4096	0	8192	2000	../libsysv/libsysv.so.11
      16384	4096	0	20480	5000	../libwdb/libwdb.so.11
      180224	45056	1984	227264	377c0	../rt/rt

    egcs-current 19990122 aout omit-frame-pointer

      text	data	bss	dec	hex
      40960	4096	17328	62384	f3b0	../libfb/libfb.so.11
      45056	4096	0	49152	c000	../libnurb/libnurb.so.11
      16384	4096	0	20480	5000	../libpkg/libpkg.so.11
      45056	4096	0	49152	c000	../libplot3/libplot3.so.11
      1220608	20480	23088	1264176	134a30	../librt/librt.so.11
      4096	4096	0	8192	2000	../libsysv/libsysv.so.11
      20480	4096	0	24576	6000	../libwdb/libwdb.so.11
      180224	45056	1984	227264	377c0	../rt/rt

    egcs-current 19990122 aout omit-frame-pointer align stack pointer

      text	data	bss	dec	hex
      40960	4096	17328	62384	f3b0	../libfb/libfb.so.11
      45056	4096	0	49152	c000	../libnurb/libnurb.so.11
      16384	4096	0	20480	5000	../libpkg/libpkg.so.11
      45056	4096	0	49152	c000	../libplot3/libplot3.so.11
      1224704	20480	23088	1268272	135a30	../librt/librt.so.11
      4096	4096	0	8192	2000	../libsysv/libsysv.so.11
      20480	4096	0	24576	6000	../libwdb/libwdb.so.11
      180224	45056	1984	227264	377c0	../rt/rt

  Crafty:

    egcs-current 19990122 aout

      text	data	bss	dec	hex
      364544	32768	318668	715980	aeccc	crafty

    egcs-current 19990122 aout align stack pointer

      text	data	bss	dec	hex
      364544	32768	318668	715980	aeccc	crafty

    egcs-current 19990122 aout omit-frame-pointer

      text	data	bss	dec	hex
      364544	32768	318668	715980	aeccc	crafty

    egcs-current 19990122 aout omit-frame-pointer align stack pointer

      text	data	bss	dec	hex
      364544	32768	318668	715980	aeccc	crafty

ChangeLog:

Tue Mar 30 01:28:30 EST 1999  John Wehle  (john@feith.com)

	* rtl.h (desired_stack_local_alignment): Prototype.
	* function.c (desired_stack_local_alignment): New function.
	(current_function_desired_stack_alignment): Define.
	(init_function_start): Initialize it.
	* output.h Declare it.
	* reload1.c (reload, alter_reg): Set it.
	(reload): Assign stack slots to pseudos that lack hard regs or
	equivalents before calling init_elim_table.
	(init_elim_table): Disable frame (or argument when
	-fomit-frame-pointer) pointer elimination unless stack alignment is
	desired and the hard frame pointer is available.
	(reload): Enable the proper frame (or argument when
	-fomit-frame-pointer) pointer elimination once it's known if
	stack alignment is desired and if the hard frame pointer is available.
	(update_eliminables): Don't clear frame_pointer_needed if there is
	an elimination which depends on it.

	* i386.h (HARD_FRAME_POINTER_REGNUM): Define.
	(FRAME_POINTER_REGNUM): Change to 17.
	(ELIMINABLE_REGS): Add frame pointer eliminations.
	(FIRST_PSEUDO_REGISTER, FIXED_REGISTERS, CALL_USED_REGISTERS,
	REG_ALLOC_ORDER, REG_CLASS_CONTENTS, INITIAL_ELIMINATION_OFFSET,
	REGNO_OK_FOR_BASE_P, REGNO_OK_FOR_BASE_NONSTRICT_P, HI_REGISTER_NAMES,
	PRINT_REG, DEBUG_PRINT_REG): Match HARD_FRAME_POINTER_REGNUM and
	FRAME_POINTER_REGNUM changes.
	* i386.c (AT_BP, regclass_map, ix86_prologue,
	ix86_can_use_return_insn_p, ix86_epilogue): Likewise.
	* i386.md (epilogue_set_stack_ptr): Likewise.

	* i386.c (ix86_prologue, ix86_epilogue): Align the stack.
	(ix86_compute_frame_size): Account for the stack being aligned.
	* i386.h (INITIAL_ELIMINATION_OFFSET): Likewise.

	* gdb-4.17-PatchJLW01: New file containing gdb support for
	debugging i386 code where the frame has been aligned.

Enjoy!

-- John Wehle
------------------8<------------------------8<------------------------
*** gcc/rtl.h.ORIGINAL	Thu Mar 25 07:04:20 1999
--- gcc/rtl.h	Mon Mar 29 22:47:11 1999
*************** extern rtx assign_stack_temp		PROTO((enu
*** 930,935 ****
--- 930,936 ----
  					       HOST_WIDE_INT, int));
  extern rtx assign_temp			PROTO((union tree_node *,
  					       int, int, int));
+ extern int desired_stack_local_alignment PROTO((rtx, enum machine_mode));
  extern rtx protect_from_queue		PROTO((rtx, int));
  extern void emit_queue			PROTO((void));
  extern rtx emit_move_insn		PROTO((rtx, rtx));
*** gcc/function.c.ORIGINAL	Tue Mar 23 02:51:01 1999
--- gcc/function.c	Mon Mar 29 22:48:30 1999
*************** int current_function_sp_is_unchanging;
*** 150,155 ****
--- 150,160 ----
  
  int current_function_has_computed_jump;
  
+ /* Nonzero if function being compiled desires a stricter stack alignment
+    than that specified by STACK_BOUNDARY.  This is only valid after reload. */
+ 
+ int current_function_desired_stack_alignment;
+ 
  /* Nonzero if the current function is a thunk (a lightweight function that
     just adjusts one of its arguments and forwards to another function), so
     we should try to cut corners where we can.  */
*************** find_temp_slot_from_address (x)
*** 1241,1246 ****
--- 1246,1344 ----
    return 0;
  }
        
+ /* If X refers to a stack slot then return the desired alignment.  */
+ 
+ int
+ desired_stack_local_alignment (x, mode)
+      rtx x;
+      enum machine_mode mode;
+ {
+   int align;
+   int max_align;
+   register int i;
+   RTX_CODE code = GET_CODE (x);
+   register char *fmt;
+ 
+   if (code == MEM)
+     return desired_stack_local_alignment (XEXP (x, 0), GET_MODE (x));
+   else
+     if (x == frame_pointer_rtx
+ 	|| (code == PLUS
+ 	    && XEXP (x, 0) == frame_pointer_rtx
+ 	    && GET_CODE (XEXP (x, 1)) == CONST_INT))
+       {
+ 	struct temp_slot *p;
+ 	rtx next;
+ 
+ 	for (p = temp_slots; p; p = p->next)
+ 	  if (XEXP (p->slot, 0) == x
+ 	      || p->address == x
+ 	      || (GET_CODE (x) == PLUS
+ 		  && XEXP (x, 0) == frame_pointer_rtx
+ 		  && GET_CODE (XEXP (x, 1)) == CONST_INT
+ 		  && INTVAL (XEXP (x, 1))
+ 		      - STARTING_FRAME_OFFSET >= p->base_offset
+ 		  && INTVAL (XEXP (x, 1))
+ 		      - STARTING_FRAME_OFFSET < p->base_offset + p->full_size))
+ 	    break;
+ 
+ 	else if (p->address != 0 && GET_CODE (p->address) == EXPR_LIST)
+ 	  {
+ 	    for (next = p->address; next; next = XEXP (next, 1))
+ 	      if (XEXP (next, 0) == x)
+ 	        break;
+ 	    if (next)
+ 	      break;
+ 	  }
+ 
+ 	if (p)
+ 	  return p->align;
+ 
+ 	align = GET_MODE_ALIGNMENT (mode);
+ 	if (mode == BLKmode)
+ 	  align = BIGGEST_ALIGNMENT;
+ #ifdef LOCAL_ALIGNMENT
+ 	{
+ 	  tree type;
+ 
+ 	  type = type_for_mode (mode, 0);
+ 	  if (type)
+ 	    align = LOCAL_ALIGNMENT (type, align);
+         }
+ #endif
+ 
+         return align;
+       }
+ 
+   /* Nothing special about this RTX; check its operands.  */
+ 
+   max_align = 0;
+ 
+   fmt = GET_RTX_FORMAT (code);
+   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+     {
+       if (fmt[i] == 'e')
+ 	{
+ 	  align = desired_stack_local_alignment (XEXP (x, i), mode);
+ 	  if (align > max_align)
+ 	    max_align = align;
+ 	}
+       if (fmt[i] == 'E')
+ 	{
+ 	  register int j;
+ 	  for (j = 0; j < XVECLEN (x, i); j++)
+ 	    {
+ 	      align = desired_stack_local_alignment (XVECEXP (x, i, j),
+ 						     VOIDmode);
+ 	      if (align > max_align)
+ 		max_align = align;
+ 	    }
+ 	}
+     }
+ 
+   return max_align;
+ }
+ 
  /* Indicate that NEW is an alternate way of referring to the temp slot
     that previously was known by OLD.  */
  
*************** init_function_start (subr, filename, lin
*** 5898,5903 ****
--- 5996,6002 ----
    current_function_has_nonlocal_goto = 0;
    current_function_contains_functions = 0;
    current_function_sp_is_unchanging = 0;
+   current_function_desired_stack_alignment = 0;
    current_function_has_computed_jump = 0;
    current_function_is_thunk = 0;
  
*** gcc/output.h.ORIGINAL	Mon Feb  8 16:39:30 1999
--- gcc/output.h	Mon Mar 29 22:47:11 1999
*************** extern int current_function_sp_is_unchan
*** 393,398 ****
--- 393,403 ----
  
  extern int current_function_has_computed_jump;
  
+ /* Nonzero if function being compiled desires a stricter stack alignment
+    than that specified by STACK_BOUNDARY.  This is only valid after reload. */
+ 
+ extern int current_function_desired_stack_alignment;
+ 
  /* Nonzero if the current function returns a pointer type */
  
  extern int current_function_returns_pointer;
*** gcc/reload1.c.ORIGINAL	Wed Mar 24 08:44:58 1999
--- gcc/reload1.c	Mon Mar 29 22:47:11 1999
*************** reload (first, global, dumpfile)
*** 598,603 ****
--- 598,605 ----
    register int i;
    register rtx insn;
    register struct elim_table *ep;
+   int alignment_desirability_is_known;
+   int initial_frame_pointer_needed;
  
    /* The two pointers used to track the true location of the memory used
       for label offsets.  */
*************** reload (first, global, dumpfile)
*** 769,779 ****
  			       reg_equiv_init[REGNO (SET_SRC (set))]);
  
        if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
! 	scan_paradoxical_subregs (PATTERN (insn));
      }
  
    init_elim_table ();
  
    num_labels = max_label_num () - get_first_label_num ();
  
    /* Allocate the tables used to store offset information at labels.  */
--- 771,813 ----
  			       reg_equiv_init[REGNO (SET_SRC (set))]);
  
        if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
! 	{
! 	  scan_paradoxical_subregs (PATTERN (insn));
! 
! 	  /* Update the desired stack alignment.  */
! #ifdef LOCAL_ALIGNMENT
! 	  if (! current_function_calls_alloca && ! flag_exceptions)
! 	    {
! 	      int align = desired_stack_local_alignment (PATTERN (insn),
! 							 VOIDmode);
! 	      if (align > current_function_desired_stack_alignment
! #if defined(PREFERRED_STACK_BOUNDARY)
! 		  && align > PREFERRED_STACK_BOUNDARY
! #elif defined(STACK_BOUNDARY)
! 		  && align > STACK_BOUNDARY
! #endif
! 	         )
! 	        current_function_desired_stack_alignment = align;
! 	    }
! #endif
! 	}
      }
  
+   /* Alter each pseudo-reg rtx to contain its hard reg number.
+      Assign stack slots to the pseudos that lack hard regs or equivalents
+      and determine if the pseudos desire special stack alignment.
+      Do not touch virtual registers.  */
+ 
+   for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
+     alter_reg (i, -1);
+ 
    init_elim_table ();
  
+   alignment_desirability_is_known
+     = current_function_desired_stack_alignment && frame_pointer_needed;
+ 
+   initial_frame_pointer_needed = frame_pointer_needed;
+ 
    num_labels = max_label_num () - get_first_label_num ();
  
    /* Allocate the tables used to store offset information at labels.  */
*************** reload (first, global, dumpfile)
*** 789,801 ****
    offsets_at
      = (int (*)[NUM_ELIMINABLE_REGS]) (real_at_ptr - get_first_label_num ());
  
-   /* Alter each pseudo-reg rtx to contain its hard reg number.
-      Assign stack slots to the pseudos that lack hard regs or equivalents.
-      Do not touch virtual registers.  */
- 
-   for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
-     alter_reg (i, -1);
- 
    /* If we have some registers we think can be eliminated, scan all insns to
       see if there is an insn that sets one of these registers to something
       other than itself plus a constant.  If so, the register cannot be
--- 823,828 ----
*************** reload (first, global, dumpfile)
*** 871,876 ****
--- 898,904 ----
        struct insn_chain *chain;
  
        HOST_WIDE_INT starting_frame_size;
+       int starting_desired_stack_alignment;
  
        /* Round size of stack frame to BIGGEST_ALIGNMENT.  This must be done
  	 here because the stack size may be a part of the offset computation
*************** reload (first, global, dumpfile)
*** 879,884 ****
--- 907,914 ----
        assign_stack_local (BLKmode, 0, 0);
  
        starting_frame_size = get_frame_size ();
+       starting_desired_stack_alignment
+ 	= current_function_desired_stack_alignment;
  
        set_initial_elim_offsets ();
        set_initial_label_offsets ();
*************** reload (first, global, dumpfile)
*** 1000,1006 ****
  	something_changed |= finish_spills (global, dumpfile);
  
        if (! something_changed)
! 	break;
  
        if (caller_save_needed)
  	delete_caller_save_insns ();
--- 1030,1113 ----
  	something_changed |= finish_spills (global, dumpfile);
  
        if (! something_changed)
! 	{
! #ifdef LOCAL_ALIGNMENT
! 	  if (! alignment_desirability_is_known)
!  	    {
! 	      int align_stack;
! 
! 	      alignment_desirability_is_known = 1;
! 
! 	      align_stack = current_function_desired_stack_alignment
! 		&& (frame_pointer_needed
! 		    || ! regs_ever_live[HARD_FRAME_POINTER_REGNUM]);
! 	      frame_pointer_needed |= align_stack;
! 	      regs_ever_live[HARD_FRAME_POINTER_REGNUM] |= align_stack;
! 
! 	      /* Enable the appropriate argument and frame pointer
! 		 eliminations now that it has been determined if
! 		 stack alignment is desired.  */
! 
! 	      for (ep = reg_eliminate;
! 		ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
! 		if (initial_frame_pointer_needed
! 		    && ep->from == FRAME_POINTER_REGNUM)
! 		  {
! 		    ep->can_eliminate
! 		      = CAN_ELIMINATE (ep->from, ep->to)
! 			&& ! (ep->to == STACK_POINTER_REGNUM
! 			        && ! align_stack);
! 
! 		    if (ep->can_eliminate)
! 		      num_eliminable++;
! 		  }
! 		else if (! initial_frame_pointer_needed
! 			 && ep->from == ARG_POINTER_REGNUM)
! 		  {
! 		    ep->can_eliminate
! 		      = CAN_ELIMINATE (ep->from, ep->to)
! 			&& ! (ep->to == STACK_POINTER_REGNUM
! 			        && align_stack);
! 
! 		    if (ep->can_eliminate)
! 		      num_eliminable++;
! 		  }
! 	    }
! 	  else
! 	    {
! 	      /* See if the stack can still be aligned.  */
! 
! 	      if (current_function_desired_stack_alignment)
! 		for (ep = reg_eliminate;
! 		  ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
! 		  if (ep->from == FRAME_POINTER_REGNUM && ep->can_eliminate)
! 		    {
! 		      if (ep->to != STACK_POINTER_REGNUM)
! 			current_function_desired_stack_alignment = 0;
! 		      break;
! 		    }
! 
! 	      if (current_function_desired_stack_alignment)
! 		for (ep = reg_eliminate;
! 		  ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
! 		  if (ep->from == ARG_POINTER_REGNUM && ep->can_eliminate)
! 		    {
! 		      if (ep->to == STACK_POINTER_REGNUM)
! 			current_function_desired_stack_alignment = 0;
! 		      break;
! 		    }
! 
! 	      /* If the desired stack alignment has changed then make
! 		 another pass since it might have changed elimination
! 		 offsets.  */
! 	      if (current_function_desired_stack_alignment
! 		  == starting_desired_stack_alignment)
! 		break;
! 	    }
! #else
! 	    break;
! #endif
! 	}
  
        if (caller_save_needed)
  	delete_caller_save_insns ();
*************** alter_reg (i, from_reg)
*** 2509,2514 ****
--- 2616,2637 ----
  
        /* Save the stack slot for later.   */
        reg_equiv_memory_loc[i] = x;
+ 
+       /* Update the desired stack alignment.  */
+ #ifdef LOCAL_ALIGNMENT
+       if (! current_function_calls_alloca && ! flag_exceptions)
+ 	{
+ 	  int align = desired_stack_local_alignment (x, VOIDmode);
+ 	  if (align > current_function_desired_stack_alignment
+ #if defined(PREFERRED_STACK_BOUNDARY)
+ 	      && align > PREFERRED_STACK_BOUNDARY
+ #elif defined(STACK_BOUNDARY)
+ 	      && align > STACK_BOUNDARY
+ #endif
+ 	     )
+ 	    current_function_desired_stack_alignment = align;
+ 	}
+ #endif
      }
  }
  
*************** update_eliminables (pset)
*** 3726,3731 ****
--- 3849,3877 ----
  	}
      }
  
+   for (ep = reg_eliminate; ! frame_pointer_needed
+ 			   && ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+     {
+       int elimination_requires_frame_pointer;
+       struct elim_table *op;
+ 
+       elimination_requires_frame_pointer = 0;
+       for (op = reg_eliminate; op < &reg_eliminate[NUM_ELIMINABLE_REGS]
+ 			       && !frame_pointer_needed; op++)
+ 	if (op->can_eliminate && op->from == ep->from)
+ 	  {
+ 	    if (op->to != HARD_FRAME_POINTER_REGNUM)
+ 	      {
+ 		elimination_requires_frame_pointer = 0;
+ 		break;
+ 	      }
+ 	    elimination_requires_frame_pointer = 1;
+ 	  }
+ 
+       if (elimination_requires_frame_pointer)
+ 	frame_pointer_needed = 1;
+     }
+ 
  #if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
    /* If we didn't need a frame pointer last time, but we do now, spill
       the hard frame pointer.  */
*************** init_elim_table ()
*** 3774,3781 ****
        ep->from = ep1->from;
        ep->to = ep1->to;
        ep->can_eliminate = ep->can_eliminate_previous
! 	= (CAN_ELIMINATE (ep->from, ep->to)
! 	   && ! (ep->to == STACK_POINTER_REGNUM && frame_pointer_needed));
      }
  #else
    reg_eliminate[0].from = reg_eliminate_1[0].from;
--- 3920,3957 ----
        ep->from = ep1->from;
        ep->to = ep1->to;
        ep->can_eliminate = ep->can_eliminate_previous
! 	= CAN_ELIMINATE (ep->from, ep->to)
! #ifdef LOCAL_ALIGNMENT
! 
! 	  /* If the frame pointer is needed
!            *   then
!            *     No registers should be eliminated by the stack pointer
!            *     with the possible exception of the frame pointer (if
!            *     stack alignment is desired).
!            *
!            *     Disable any frame pointer eliminations unless stack alignment
!            *     is desired.  These eliminations will be re-enabled by reload
!            *     once it's determined whether stack alignment is desired (if
!            *     it's not already known).
!            *
!            *   else
!            *     Disable any argument pointer eliminations.  These eliminations
!            *     will be re-enabled by reload once it's determined whether
!            *     stack alignment is desired and whether the frame pointer is
!            *     available for use.
!            */
! 
! 	  && ! (ep->from != FRAME_POINTER_REGNUM
! 		&& ep->to == STACK_POINTER_REGNUM
! 		&& frame_pointer_needed)
! 	  && ! (ep->from == FRAME_POINTER_REGNUM
! 		&& frame_pointer_needed
! 		&& ! current_function_desired_stack_alignment)
! 	  && ! (ep->from == ARG_POINTER_REGNUM
! 		&& ! frame_pointer_needed);
! #else
! 	  && ! (ep->to == STACK_POINTER_REGNUM && frame_pointer_needed);
! #endif
      }
  #else
    reg_eliminate[0].from = reg_eliminate_1[0].from;
*** gcc/config/i386/i386.h.ORIGINAL	Thu Mar 25 05:06:56 1999
--- gcc/config/i386/i386.h	Mon Mar 29 23:04:35 1999
*************** extern int ix86_arch;
*** 413,419 ****
  /* We want to keep the stack aligned to 128 bits when possible, for the
     benefit of doubles and SSE __m128.  But the compiler can not rely on
     the stack having this alignment.*/
! #define PREFERRED_STACK_BOUNDARY 128
  
  /* Allocation boundary (in *bits*) for the code of a function.
     For i486, we get better performance by aligning to a cache
--- 413,419 ----
  /* We want to keep the stack aligned to 128 bits when possible, for the
     benefit of doubles and SSE __m128.  But the compiler can not rely on
     the stack having this alignment.*/
! /* #define PREFERRED_STACK_BOUNDARY 128 */
  
  /* Allocation boundary (in *bits*) for the code of a function.
     For i486, we get better performance by aligning to a cache
*************** extern int ix86_arch;
*** 591,607 ****
  
     Reg 16 does not correspond to any hardware register, but instead
     appears in the RTL as an argument pointer prior to reload, and is
!    eliminated during reloading in favor of either the stack or frame
!    pointer. */
  
! #define FIRST_PSEUDO_REGISTER 17
  
  /* 1 for registers that have pervasive standard uses
     and are not available for the register allocator.
     On the 80386, the stack pointer is such, as is the arg pointer. */
  #define FIXED_REGISTERS \
! /*ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg*/       \
! {  0, 0, 0, 0, 0, 0, 0, 1, 0,  0,  0,  0,  0,  0,  0,  0,  1 }
  
  /* 1 for registers not available across function calls.
     These must include the FIXED_REGISTERS and also any
--- 591,612 ----
  
     Reg 16 does not correspond to any hardware register, but instead
     appears in the RTL as an argument pointer prior to reload, and is
!    eliminated during reloading in favor of either the stack or hard
!    frame pointer.
  
!    Reg 17 does not correspond to any hardware register, but instead
!    appears in the RTL as a frame pointer prior to reload, and is
!    eliminated during reloading in favor of either the stack or hard
!    frame pointer. */
! 
! #define FIRST_PSEUDO_REGISTER 18
  
  /* 1 for registers that have pervasive standard uses
     and are not available for the register allocator.
     On the 80386, the stack pointer is such, as is the arg pointer. */
  #define FIXED_REGISTERS \
! /*ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg,fp*/       \
! {  0, 0, 0, 0, 0, 0, 0, 1, 0,  0,  0,  0,  0,  0,  0,  0,  1, 1 }
  
  /* 1 for registers not available across function calls.
     These must include the FIXED_REGISTERS and also any
*************** extern int ix86_arch;
*** 611,618 ****
     Aside from that, you can include as many other registers as you like.  */
  
  #define CALL_USED_REGISTERS \
! /*ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg*/ \
! {  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
  
  /* Order in which to allocate registers.  Each register must be
     listed once, even those in FIXED_REGISTERS.  List frame pointer
--- 616,623 ----
     Aside from that, you can include as many other registers as you like.  */
  
  #define CALL_USED_REGISTERS \
! /*ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg,fp*/ \
! {  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1, 1 }
  
  /* Order in which to allocate registers.  Each register must be
     listed once, even those in FIXED_REGISTERS.  List frame pointer
*************** extern int ix86_arch;
*** 634,641 ****
     generated by allocating edx first, so restore the 'natural' order of things. */
  
  #define REG_ALLOC_ORDER \
! /*ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg*/ \
! {  0, 1, 2, 3, 4, 5, 6, 7, 8,  9, 10, 11, 12, 13, 14, 15, 16 }
  
  /* A C statement (sans semicolon) to choose the order in which to
     allocate hard registers for pseudo-registers local to a basic
--- 639,646 ----
     generated by allocating edx first, so restore the 'natural' order of things. */
  
  #define REG_ALLOC_ORDER \
! /*ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg,fp*/ \
! {  0, 1, 2, 3, 4, 5, 6, 7, 8,  9, 10, 11, 12, 13, 14, 15, 16,17 }
  
  /* A C statement (sans semicolon) to choose the order in which to
     allocate hard registers for pseudo-registers local to a basic
*************** extern int ix86_arch;
*** 723,729 ****
  #define STACK_POINTER_REGNUM 7
  
  /* Base register for access to local variables of the function.  */
! #define FRAME_POINTER_REGNUM 6
  
  /* First floating point reg */
  #define FIRST_FLOAT_REG 8
--- 728,735 ----
  #define STACK_POINTER_REGNUM 7
  
  /* Base register for access to local variables of the function.  */
! #define FRAME_POINTER_REGNUM 17
! #define HARD_FRAME_POINTER_REGNUM 6
  
  /* First floating point reg */
  #define FIRST_FLOAT_REG 8
*************** enum reg_class
*** 845,854 ****
       {0xf},			/* Q_REGS */			\
      {0x10},   {0x20},		/* SIREG, DIREG */		\
   {0x7f},				/* INDEX_REGS */		\
!  {0x100ff},			/* GENERAL_REGS */		\
    {0x0100}, {0x0200},		/* FP_TOP_REG, FP_SECOND_REG */	\
    {0xff00},			/* FLOAT_REGS */		\
!  {0x1ffff}}
  
  /* The same information, inverted:
     Return the class number of the smallest class containing
--- 851,860 ----
       {0xf},			/* Q_REGS */			\
      {0x10},   {0x20},		/* SIREG, DIREG */		\
   {0x7f},				/* INDEX_REGS */		\
!  {0x300ff},			/* GENERAL_REGS */		\
    {0x0100}, {0x0200},		/* FP_TOP_REG, FP_SECOND_REG */	\
    {0xff00},			/* FLOAT_REGS */		\
!  {0x3ffff}}
  
  /* The same information, inverted:
     Return the class number of the smallest class containing
*************** do {						\
*** 1605,1614 ****
     pointer register.  Secondly, the argument pointer register can always be
     eliminated; it is replaced with either the stack or frame pointer. */
  
! #define ELIMINABLE_REGS				\
! {{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM},	\
!  { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM},   \
!  { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
  
  /* Given FROM and TO register numbers, say whether this elimination is allowed.
     Frame pointer elimination is automatically handled.
--- 1611,1621 ----
     pointer register.  Secondly, the argument pointer register can always be
     eliminated; it is replaced with either the stack or frame pointer. */
  
! #define ELIMINABLE_REGS					\
! {{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM},		\
!  { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM},	\
!  { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM},		\
!  { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}}
  
  /* Given FROM and TO register numbers, say whether this elimination is allowed.
     Frame pointer elimination is automatically handled.
*************** do {						\
*** 1628,1635 ****
  
  #define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)			\
  {									\
!   if ((FROM) == ARG_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM)	\
!     (OFFSET) = 8;	/* Skip saved PC and previous frame pointer */	\
    else									\
      {									\
        int nregs;							\
--- 1635,1645 ----
  
  #define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)			\
  {									\
!   if ((TO) == HARD_FRAME_POINTER_REGNUM)				\
!     if ((FROM) == ARG_POINTER_REGNUM)					\
!       (OFFSET) = 8;	/* Skip saved PC and previous frame pointer */	\
!     else								\
!       (OFFSET) = 0;							\
    else									\
      {									\
        int nregs;							\
*************** do {						\
*** 1647,1654 ****
        if ((FROM) == ARG_POINTER_REGNUM)					\
  	(OFFSET) += offset;						\
        else								\
! 	(OFFSET) -= ((offset + preferred_alignment - 1)			\
! 		     & -preferred_alignment) - offset;			\
      }									\
  }
  
--- 1657,1665 ----
        if ((FROM) == ARG_POINTER_REGNUM)					\
  	(OFFSET) += offset;						\
        else								\
! 	if (! current_function_desired_stack_alignment)			\
! 	  (OFFSET) -= ((offset + preferred_alignment - 1)		\
! 		       & -preferred_alignment) - offset;		\
      }									\
  }
  
*************** do {						\
*** 1675,1680 ****
--- 1686,1692 ----
  #define REGNO_OK_FOR_BASE_P(REGNO) \
    ((REGNO) <= STACK_POINTER_REGNUM \
     || (REGNO) == ARG_POINTER_REGNUM \
+    || (REGNO) == FRAME_POINTER_REGNUM \
     || (unsigned) reg_renumber[REGNO] <= STACK_POINTER_REGNUM)
  
  #define REGNO_OK_FOR_SIREG_P(REGNO) ((REGNO) == 4 || reg_renumber[REGNO] == 4)
*************** do {						\
*** 1702,1707 ****
--- 1714,1720 ----
  #define REG_OK_FOR_BASE_NONSTRICT_P(X)					\
    (REGNO (X) <= STACK_POINTER_REGNUM					\
     || REGNO (X) == ARG_POINTER_REGNUM					\
+    || REGNO (X) == FRAME_POINTER_REGNUM					\
     || REGNO (X) >= FIRST_PSEUDO_REGISTER)
  
  #define REG_OK_FOR_STRREG_NONSTRICT_P(X)				\
*************** extern struct rtx_def *(*i386_compare_ge
*** 2381,2387 ****
  
  #define HI_REGISTER_NAMES \
  {"ax","dx","cx","bx","si","di","bp","sp",          \
!  "st","st(1)","st(2)","st(3)","st(4)","st(5)","st(6)","st(7)","" }
  
  #define REGISTER_NAMES HI_REGISTER_NAMES
  
--- 2394,2400 ----
  
  #define HI_REGISTER_NAMES \
  {"ax","dx","cx","bx","si","di","bp","sp",          \
!  "st","st(1)","st(2)","st(3)","st(4)","st(5)","st(6)","st(7)","","" }
  
  #define REGISTER_NAMES HI_REGISTER_NAMES
  
*************** extern char *qi_reg_name[];
*** 2592,2598 ****
  extern char *qi_high_reg_name[];
  
  #define PRINT_REG(X, CODE, FILE) \
!   do { if (REGNO (X) == ARG_POINTER_REGNUM)		\
  	 abort ();					\
         fprintf (FILE, "%s", RP);			\
         switch ((CODE == 'w' ? 2 			\
--- 2605,2612 ----
  extern char *qi_high_reg_name[];
  
  #define PRINT_REG(X, CODE, FILE) \
!   do { if (REGNO (X) == ARG_POINTER_REGNUM		\
! 	   || REGNO (X) == FRAME_POINTER_REGNUM)	\
  	 abort ();					\
         fprintf (FILE, "%s", RP);			\
         switch ((CODE == 'w' ? 2 			\
*************** extern char *qi_high_reg_name[];
*** 2641,2646 ****
--- 2655,2662 ----
         fprintf (FILE, "%d %s", REGNO (X), RP);	\
         if (REGNO (X) == ARG_POINTER_REGNUM)		\
  	 { fputs ("argp", FILE); break; }		\
+        if (REGNO (X) == FRAME_POINTER_REGNUM)		\
+ 	 { fputs ("fp", FILE); break; }			\
         if (STACK_TOP_P (X))				\
  	 { fputs ("st(0)", FILE); break; }		\
         if (FP_REG_P (X))				\
*** gcc/config/i386/i386.c.ORIGINAL	Sat Mar 27 23:36:47 1999
--- gcc/config/i386/i386.c	Mon Mar 29 22:47:11 1999
*************** const int x86_use_any_reg = m_486;
*** 131,137 ****
  const int x86_cmove = m_PPRO;
  const int x86_deep_branch = m_PPRO| m_K6;
  
! #define AT_BP(mode) (gen_rtx_MEM ((mode), frame_pointer_rtx))
  
  extern FILE *asm_out_file;
  extern char *strcat ();
--- 131,137 ----
  const int x86_cmove = m_PPRO;
  const int x86_deep_branch = m_PPRO| m_K6;
  
! #define AT_BP(mode) (gen_rtx_MEM ((mode), hard_frame_pointer_rtx))
  
  extern FILE *asm_out_file;
  extern char *strcat ();
*************** enum reg_class regclass_map[FIRST_PSEUDO
*** 160,166 ****
    FP_TOP_REG, FP_SECOND_REG, FLOAT_REGS, FLOAT_REGS,
    FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS,
    /* arg pointer */
!   INDEX_REGS
  };
  
  /* Test and compare insns in i386.md store the information needed to
--- 160,168 ----
    FP_TOP_REG, FP_SECOND_REG, FLOAT_REGS, FLOAT_REGS,
    FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS,
    /* arg pointer */
!   GENERAL_REGS,
!   /* frame pointer */
!   GENERAL_REGS
  };
  
  /* Test and compare insns in i386.md store the information needed to
*************** ix86_compute_frame_size (size, nregs_on_
*** 2086,2091 ****
--- 2088,2094 ----
       HOST_WIDE_INT size;
       int *nregs_on_stack;
  {
+   int alignment = current_function_desired_stack_alignment / BITS_PER_UNIT;
    int limit;
    int nregs;
    int regno;
*************** ix86_compute_frame_size (size, nregs_on_
*** 2095,2101 ****
    HOST_WIDE_INT total_size;
  
    limit = frame_pointer_needed
! 	  ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM;
  
    nregs = 0;
  
--- 2098,2104 ----
    HOST_WIDE_INT total_size;
  
    limit = frame_pointer_needed
! 	  ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM;
  
    nregs = 0;
  
*************** ix86_compute_frame_size (size, nregs_on_
*** 2107,2130 ****
    padding = 0;
    total_size = size + (nregs * UNITS_PER_WORD);
  
  #ifdef PREFERRED_STACK_BOUNDARY
!   {
!     int offset;
!     int preferred_alignment = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT;
! 
!     offset = 4;
!     if (frame_pointer_needed)
!       offset += UNITS_PER_WORD;
  
!     total_size += offset;
      
!     padding = ((total_size + preferred_alignment - 1)
  	       & -preferred_alignment) - total_size;
  
!     if (padding < (((offset + preferred_alignment - 1)
! 		    & -preferred_alignment) - offset))
!       padding += preferred_alignment;
!   }
  #endif
  
    if (nregs_on_stack)
--- 2110,2136 ----
    padding = 0;
    total_size = size + (nregs * UNITS_PER_WORD);
  
+   if (alignment)
+     padding = ((total_size + alignment - 1) & -alignment) - total_size;
  #ifdef PREFERRED_STACK_BOUNDARY
!   else
!     {
!       int offset;
!       int preferred_alignment = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT;
  
!       offset = 4;
!       if (frame_pointer_needed)
! 	offset += UNITS_PER_WORD;
! 
!       total_size += offset;
      
!       padding = ((total_size + preferred_alignment - 1)
  	       & -preferred_alignment) - total_size;
  
!       if (padding < (((offset + preferred_alignment - 1)
! 		      & -preferred_alignment) - offset))
! 	padding += preferred_alignment;
!     }
  #endif
  
    if (nregs_on_stack)
*************** ix86_prologue (do_rtl)
*** 2140,2145 ****
--- 2146,2152 ----
    register int regno;
    int limit;
    rtx xops[4];
+   int alignment = current_function_desired_stack_alignment / BITS_PER_UNIT;
    int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
  				  || current_function_uses_const_pool);
    HOST_WIDE_INT tsize = ix86_compute_frame_size (get_frame_size (), (int *)0);
*************** ix86_prologue (do_rtl)
*** 2147,2154 ****
    int cfa_offset = INCOMING_FRAME_SP_OFFSET, cfa_store_offset = cfa_offset;
  
    xops[0] = stack_pointer_rtx;
!   xops[1] = frame_pointer_rtx;
    xops[2] = GEN_INT (tsize);
  
    if (frame_pointer_needed)
      {
--- 2154,2162 ----
    int cfa_offset = INCOMING_FRAME_SP_OFFSET, cfa_store_offset = cfa_offset;
  
    xops[0] = stack_pointer_rtx;
!   xops[1] = hard_frame_pointer_rtx;
    xops[2] = GEN_INT (tsize);
+   xops[3] = GEN_INT (-alignment);
  
    if (frame_pointer_needed)
      {
*************** ix86_prologue (do_rtl)
*** 2158,2168 ****
  				     gen_rtx_MEM (SImode,
  					      gen_rtx (PRE_DEC, SImode,
  						       stack_pointer_rtx)),
! 				     frame_pointer_rtx));
  
  	  RTX_FRAME_RELATED_P (insn) = 1;
  	  insn = emit_move_insn (xops[1], xops[0]);
  	  RTX_FRAME_RELATED_P (insn) = 1;
  	}
  
        else
--- 2166,2183 ----
  				     gen_rtx_MEM (SImode,
  					      gen_rtx (PRE_DEC, SImode,
  						       stack_pointer_rtx)),
! 				     hard_frame_pointer_rtx));
  
  	  RTX_FRAME_RELATED_P (insn) = 1;
  	  insn = emit_move_insn (xops[1], xops[0]);
  	  RTX_FRAME_RELATED_P (insn) = 1;
+ 	  if (alignment)
+ 	    {
+ 	      insn = emit_insn (gen_rtx_SET (VOIDmode, xops[0],
+ 					     gen_rtx_AND (SImode,
+ 							  xops[0], xops[3])));
+ 	      RTX_FRAME_RELATED_P (insn) = 1;
+ 	    }
  	}
  
        else
*************** ix86_prologue (do_rtl)
*** 2185,2190 ****
--- 2200,2207 ----
   	  if (dwarf2out_do_frame ())
   	    dwarf2out_def_cfa ("", FRAME_POINTER_REGNUM, cfa_offset);
  #endif
+ 	  if (alignment)
+ 	    output_asm_insn (AS2 (and%L0,%3,%0), xops);
  	}
      }
  
*************** ix86_prologue (do_rtl)
*** 2240,2246 ****
       }
       */
  
!   limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
    for (regno = limit - 1; regno >= 0; regno--)
      if ((regs_ever_live[regno] && ! call_used_regs[regno])
  	|| (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
--- 2257,2263 ----
       }
       */
  
!   limit = (frame_pointer_needed ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
    for (regno = limit - 1; regno >= 0; regno--)
      if ((regs_ever_live[regno] && ! call_used_regs[regno])
  	|| (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
*************** ix86_can_use_return_insn_p ()
*** 2310,2316 ****
    int regno;
    int nregs = 0;
    int reglimit = (frame_pointer_needed
! 		  ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
    int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
  				  || current_function_uses_const_pool);
  
--- 2327,2333 ----
    int regno;
    int nregs = 0;
    int reglimit = (frame_pointer_needed
! 		  ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
    int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
  				  || current_function_uses_const_pool);
  
*************** ix86_epilogue (do_rtl)
*** 2357,2363 ****
    register int regno;
    register int limit;
    int nregs;
!   rtx xops[3];
    int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
  				  || current_function_uses_const_pool);
    int sp_valid = !frame_pointer_needed || current_function_sp_is_unchanging;
--- 2374,2381 ----
    register int regno;
    register int limit;
    int nregs;
!   rtx xops[4];
!   int alignment = current_function_desired_stack_alignment / BITS_PER_UNIT;
    int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
  				  || current_function_uses_const_pool);
    int sp_valid = !frame_pointer_needed || current_function_sp_is_unchanging;
*************** ix86_epilogue (do_rtl)
*** 2369,2374 ****
--- 2387,2393 ----
    offset = -(tsize + nregs * UNITS_PER_WORD);
  
    xops[2] = stack_pointer_rtx;
+   xops[3] = GEN_INT (-alignment);
  
    /* When -fpic, we must emit a scheduling barrier, so that the instruction
       that restores %ebx (which is PIC_OFFSET_TABLE_REGNUM), does not get
*************** ix86_epilogue (do_rtl)
*** 2381,2403 ****
    if (flag_pic || profile_flag || profile_block_flag)
      emit_insn (gen_blockage ());
  
!   /* If we're only restoring one register and sp is not valid then
!      using a move instruction to restore the register since it's
!      less work than reloading sp and popping the register.  Otherwise,
!      restore sp (if necessary) and pop the registers. */
  
    limit = frame_pointer_needed
! 	  ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM;
  
!   if (nregs > 1 || sp_valid)
      {
!       if ( !sp_valid )
  	{
  	  xops[0] = adj_offsettable_operand (AT_BP (QImode), offset);
  	  if (do_rtl)
! 	    emit_insn (gen_movsi_lea (xops[2], XEXP (xops[0], 0)));
  	  else
! 	    output_asm_insn (AS2 (lea%L2,%0,%2), xops);
  	}
  
        for (regno = 0; regno < limit; regno++)
--- 2400,2433 ----
    if (flag_pic || profile_flag || profile_block_flag)
      emit_insn (gen_blockage ());
  
!   /* If we're only restoring one register and sp is not valid and
!      the stack has not been aligned by the prologue then use a move
!      instruction to restore the register since it's less work than
!      reloading sp and popping the register.  Otherwise, restore sp
!      (if necessary) and pop the registers. */
  
    limit = frame_pointer_needed
! 	  ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM;
  
!   if (nregs > 1 || sp_valid || alignment)
      {
!       if (! sp_valid && nregs)
  	{
  	  xops[0] = adj_offsettable_operand (AT_BP (QImode), offset);
  	  if (do_rtl)
! 	    {
! 	      emit_insn (gen_movsi_lea (xops[2], XEXP (xops[0], 0)));
! 	      if (alignment)
! 		emit_insn (gen_rtx_SET (VOIDmode, xops[2],
! 					gen_rtx_AND (SImode,
! 						     xops[2], xops[3])));
! 	    }
  	  else
! 	    {
! 	      output_asm_insn (AS2 (lea%L2,%0,%2), xops);
! 	      if (alignment)
! 		output_asm_insn (AS2 (and%L2,%3,%2), xops);
! 	    }
  	}
  
        for (regno = 0; regno < limit; regno++)
*************** ix86_epilogue (do_rtl)
*** 2442,2448 ****
  	}
        else
  	{
! 	  xops[0] = frame_pointer_rtx;
  	  xops[1] = stack_pointer_rtx;
  
  	  if (do_rtl)
--- 2472,2478 ----
  	}
        else
  	{
! 	  xops[0] = hard_frame_pointer_rtx;
  	  xops[1] = stack_pointer_rtx;
  
  	  if (do_rtl)
*** gcc/config/i386/i386.md.ORIGINAL	Sun Mar 28 00:22:17 1999
--- gcc/config/i386/i386.md	Mon Mar 29 22:47:12 1999
*************** byte_xor_operation:
*** 6691,6697 ****
  {
    rtx xops [2];
  
!     xops[0] = frame_pointer_rtx;
      xops[1] = stack_pointer_rtx;
      output_asm_insn (AS2 (mov%L0,%0,%1), xops);
      RET;
--- 6691,6697 ----
  {
    rtx xops [2];
  
!     xops[0] = hard_frame_pointer_rtx;
      xops[1] = stack_pointer_rtx;
      output_asm_insn (AS2 (mov%L0,%0,%1), xops);
      RET;
*** /dev/null	Tue Mar 30 00:53:30 1999
--- contrib/gdb-4.17-PatchJLW01	Tue Mar 30 01:45:34 1999
***************
*** 0 ****
--- 1,1190 ----
+ To: shebs@cygnus.com
+ Cc: gdb-patches@cygnus.com
+ Subject: Re: GDB 4.17 Patch for stack aligned i386 code
+ 
+ > Actually, the last I looked at this, I was unclear about what/which of
+ > several patches were needed, and in which order.  If you had a single
+ > integrated set and could resend, that would be helpful...
+ 
+ Here's both patches consolidated into one.
+ 
+ This patch adds support for debugging functions where the
+ frame pointer has been eliminated and the stack pointer
+ is unchanging.  It also adds support for debugging functions
+ where the stack has been aligned.
+ 
+ There are no regressions evident from running the gdb 4.17
+ testsuite on Solaris 2.5.1 with gcc 2.7.2.1, egcs (from cvs on
+ 1999-02-25), and egcs -momit-leaf-frame-pointer with this patch
+ installed.  There were no regressions evident from running the
+ gdb 4.17 testsuite on FreeBSD 3.0 and Solaris 2.5.1 with gcc
+ 2.7.2.1, egcs (from cvs on 1999-01-14), and egcs
+ -momit-leaf-frame-pointer with the original two separate
+ patches installed.
+ 
+ Notes:
+ 
+   1) The SVR4 changes to support sigtramp backtraces are untested.
+ 
+   2) Platforms other than those tested may require some work in order
+      to properly support sigtramp backtraces.  Start by looking at
+      the BSD and Solaris definitions of:
+ 
+        i386_sigcontext_addr
+        SIGCONTEXT_FP_OFFSET
+        SIGCONTEXT_SP_OFFSET
+ 
+ ChangeLog:
+ 
+ Wed Jan 20 22:14:03 EST 1999  John Wehle  (john@feith.com)
+ 
+ 	* tm-i386.h (FRAME_CHAIN): Call i386_frame_chain for
+ 	signal handler callers.
+ 	(EXTRA_FRAME_INFO): Define stack_mask.
+ 	(FRAME_ARGS_ADDRESS, FRAME_LOCALS_ADDRESS): Use it.
+ 	* i386-tdep.c (i386_get_frame_setup): Set it.
+ 	(i386_frame_find_saved_regs): Use it.
+ 	(i386_init_extra_frame_info): Initialize it.
+ 	(i386_frame_find_saved_regs): Don't accidently zero
+ 	fi->fsr.regs[FP_REGNUM].
+ 	(i386_sigcontext_addr): New function.
+ 	(i386v4_sigtramp_saved_pc): Use it.
+ 	(i386_analyze_prologue): Use it.
+ 	(i386_analyze_prologue): Handle dummy frames.
+ 	(i386_analyze_prologue): If the start of a function can't
+ 	be located than assume the frame is in the frame pointer.
+ 	(i386_frame_chain): If the caller's prologue can't be
+ 	analyze then return the current frame as the frame.
+ 	(i386_frame_chain): Check the current frame for FP_REGNUM.
+ 	(i386_frame_chain): Handle chaining back from a signal
+ 	handler caller to a function which uses the stack pointer
+ 	for the frame pointer.
+ 	(i386_frame_chain): Include the stack space occupied by
+ 	FP_REGNUM when caculating the caller's frame.
+ 	(i386_pop_frame): Don't bother explicitly restoring PC_REGNUM,
+ 	it is restored by the for loop.
+ 	* tm-i386bsd.h (SIGCONTEXT_FP_OFFSET,
+ 	SIGCONTEXT_SP_OFFSET): Define.
+ 	* tm-i386sol2.h: Likewise.
+ 	* tm-i386v4.h: Likewise.
+ 
+ Wed Nov  4 22:29:19 EST 1998  John Wehle  (john@feith.com)
+ 
+ 	* i386-tdep.c (codestream_fill): Use read_memory_nobpt.
+ 	(i386_analyze_prologue, i386_frame_chain,
+ 	i386_init_extra_frame_info): New functions.
+ 	(i386_get_frame_setup, i386_frame_find_saved_regs,
+ 	i386_skip_prologue, i386_pop_frame): Support prologues
+ 	which don't setup a frame pointer.
+ 	* tm-i386.h (EXTRA_FRAME_INFO, INIT_EXTRA_FRAME_INFO,
+ 	INIT_FRAME_PC): Define.
+ 	(FRAME_CHAIN): Call i386_frame_chain.
+ 	(FRAME_FIND_SAVED_REGS, FRAME_SAVED_PC): Use fsr to
+ 	locate the saved registers.
+ 	(FRAMELESS_FUNCTION_INVOCATION): Don't define.
+ 	(i386_init_extra_frame_info, i386_frame_chain): Add
+ 	prototypes.
+ 
+ -- John Wehle
+ ------------------8<------------------------8<------------------------
+ *** gdb/i386-tdep.c.ORIGINAL	Sat Apr 11 01:39:37 1998
+ --- gdb/i386-tdep.c	Thu Feb 25 22:24:11 1999
+ *************** Foundation, Inc., 59 Temple Place - Suit
+ *** 28,37 ****
+   #include "symtab.h"
+   #include "gdbcmd.h"
+   
+ - static long i386_get_frame_setup PARAMS ((CORE_ADDR));
+ - 
+ - static void i386_follow_jump PARAMS ((void));
+ - 
+   static void codestream_read PARAMS ((unsigned char *, int));
+   
+   static void codestream_seek PARAMS ((CORE_ADDR));
+ --- 28,33 ----
+ *************** static unsigned char 
+ *** 72,82 ****
+   codestream_fill (peek_flag)
+       int peek_flag;
+   {
+     codestream_addr = codestream_next_addr;
+     codestream_next_addr += CODESTREAM_BUFSIZ;
+     codestream_off = 0;
+     codestream_cnt = CODESTREAM_BUFSIZ;
+ !   read_memory (codestream_addr, (char *) codestream_buf, CODESTREAM_BUFSIZ);
+     
+     if (peek_flag)
+       return (codestream_peek());
+ --- 68,83 ----
+   codestream_fill (peek_flag)
+       int peek_flag;
+   {
+ +   int status;
+ + 
+     codestream_addr = codestream_next_addr;
+     codestream_next_addr += CODESTREAM_BUFSIZ;
+     codestream_off = 0;
+     codestream_cnt = CODESTREAM_BUFSIZ;
+ !   status = read_memory_nobpt (codestream_addr,
+ ! 			      (char *) codestream_buf, CODESTREAM_BUFSIZ);
+ !   if (status != 0)
+ !     memory_error (status, codestream_addr);
+     
+     if (peek_flag)
+       return (codestream_peek());
+ *************** i386_follow_jump ()
+ *** 161,185 ****
+   }
+   
+   /*
+ !  * find & return amound a local space allocated, and advance codestream to
+ !  * first register push (if any)
+    *
+ !  * if entry sequence doesn't make sense, return -1, and leave 
+ !  * codestream pointer random
+    */
+   
+ ! static long
+ ! i386_get_frame_setup (pc)
+ !      CORE_ADDR pc;
+   {
+     unsigned char op;
+   
+ -   codestream_seek (pc);
+ - 
+ -   i386_follow_jump ();
+ - 
+     op = codestream_get ();
+ - 
+     if (op == 0x58)		/* popl %eax */
+       {
+         /*
+ --- 162,186 ----
+   }
+   
+   /*
+ !  * Determine the amount of local space allocated, and advance codestream to
+ !  * first register push (if any).
+    *
+ !  * If the entry sequence doesn't make sense, then assume it is a frameless
+ !  * function with no local space.
+    */
+   
+ ! #define MY_FRAME_IN_SP   0x1
+ ! #define MY_FRAME_IN_FP   0x2
+ ! #define NO_MORE_FRAMES   0x4
+ ! #define INCOMPLETE_FRAME 0x8
+ ! 
+ ! static void
+ ! i386_get_frame_setup (fi)
+ !      struct frame_info *fi;
+   {
+     unsigned char op;
+   
+     op = codestream_get ();
+     if (op == 0x58)		/* popl %eax */
+       {
+         /*
+ *************** i386_get_frame_setup (pc)
+ *** 214,285 ****
+     if (op == 0x55)		/* pushl %ebp */
+       {			
+         /* check for movl %esp, %ebp - can be written two ways */
+ !       switch (codestream_get ())
+ ! 	{
+ ! 	case 0x8b:
+ ! 	  if (codestream_get () != 0xec)
+ ! 	    return (-1);
+ ! 	  break;
+ ! 	case 0x89:
+ ! 	  if (codestream_get () != 0xe5)
+ ! 	    return (-1);
+ ! 	  break;
+ ! 	default:
+ ! 	  return (-1);
+   	}
+ !       /* check for stack adjustment 
+ !        *
+ !        *  subl $XXX, %esp
+ !        *
+ !        * note: you can't subtract a 16 bit immediate
+ !        * from a 32 bit reg, so we don't have to worry
+ !        * about a data16 prefix 
+ !        */
+ !       op = codestream_peek ();
+ !       if (op == 0x83)
+   	{
+ ! 	  /* subl with 8 bit immed */
+ ! 	  codestream_get ();
+ ! 	  if (codestream_get () != 0xec)
+ ! 	    /* Some instruction starting with 0x83 other than subl.  */
+ ! 	    {
+ ! 	      codestream_seek (codestream_tell () - 2);
+ ! 	      return 0;
+ ! 	    }
+ ! 	  /* subl with signed byte immediate 
+ ! 	   * (though it wouldn't make sense to be negative)
+ ! 	   */
+ ! 	  return (codestream_get());
+   	}
+ !       else if (op == 0x81)
+   	{
+ ! 	  char buf[4];
+ ! 	  /* Maybe it is subl with 32 bit immedediate.  */
+ ! 	  codestream_get();
+ ! 	  if (codestream_get () != 0xec)
+ ! 	    /* Some instruction starting with 0x81 other than subl.  */
+ ! 	    {
+ ! 	      codestream_seek (codestream_tell () - 2);
+ ! 	      return 0;
+ ! 	    }
+ ! 	  /* It is subl with 32 bit immediate.  */
+ ! 	  codestream_read ((unsigned char *)buf, 4);
+ ! 	  return extract_signed_integer (buf, 4);
+   	}
+ !       else
+   	{
+ ! 	  return (0);
+   	}
+       }
+ !   else if (op == 0xc8)
+       {
+ !       char buf[2];
+ !       /* enter instruction: arg is 16 bit unsigned immed */
+ !       codestream_read ((unsigned char *)buf, 2);
+ !       codestream_get (); /* flush final byte of enter instruction */
+ !       return extract_unsigned_integer (buf, 2);
+       }
+ !   return (-1);
+   }
+   
+   /* Return number of args passed to a frame.
+ --- 215,328 ----
+     if (op == 0x55)		/* pushl %ebp */
+       {			
+         /* check for movl %esp, %ebp - can be written two ways */
+ !       unsigned char buf[2];
+ !       static unsigned char proto1[2] = { 0x8b,0xec };
+ !       static unsigned char proto2[2] = { 0x89,0xe5 };
+ !       codestream_read (buf, sizeof(buf));
+ !       if (memcmp (buf, proto1, 2) != 0
+ ! 	  && memcmp (buf, proto2, 2) != 0)
+ ! 	{
+ ! 	  /* frameless function with no local space */
+ ! 	  codestream_seek (codestream_tell () - 3);
+ ! 	  return;
+   	}
+ !       fi->status = MY_FRAME_IN_FP;
+ !     }
+ !   else if (op == 0xc8)
+ !     {
+ !       char buf[2];
+ !       /* enter instruction: arg is 16 bit unsigned immed */
+ !       codestream_read ((unsigned char *)buf, 2);
+ !       codestream_get (); /* flush final byte of enter instruction */
+ !       fi->status = MY_FRAME_IN_FP;
+ !       fi->stack_size = extract_unsigned_integer (buf, 2);
+ !       return;
+ !     }
+ !   else
+ !     codestream_seek (codestream_tell () - 1);
+ ! 
+ !   if ( (fi->status & MY_FRAME_IN_SP) )
+ !     fi->stack_mask = -1;
+ ! 
+ !   /* check for stack alignment 
+ !    *
+ !    *  andl $XXX, %esp
+ !    *
+ !    * note: you can't and a 16 bit immediate
+ !    * with a 32 bit reg, so we don't have to worry
+ !    * about a data16 prefix 
+ !    */
+ !   op = codestream_peek ();
+ !   if (op == 0x83)
+ !     {
+ !       /* andl with 8 bit immed */
+ !       codestream_get ();
+ !       if (codestream_get () != 0xe4)
+ ! 	/* Some instruction starting with 0x83 other than andl.  */
+ ! 	codestream_seek (codestream_tell () - 2);
+ !       else
+   	{
+ !           /* andl with signed byte immediate  */
+ !           fi->stack_mask = (char)codestream_get();
+   	}
+ !     }
+ !   else if (op == 0x81)
+ !     {
+ !       char buf[4];
+ !       /* Maybe it is andl with 32 bit immediate.  */
+ !       codestream_get();
+ !       if (codestream_get () != 0xe4)
+ ! 	/* Some instruction starting with 0x81 other than andl.  */
+ ! 	codestream_seek (codestream_tell () - 2);
+ !       else
+   	{
+ !           /* It is andl with 32 bit immediate.  */
+ !           codestream_read ((unsigned char *)buf, 4);
+ !           fi->stack_mask = extract_signed_integer (buf, 4);
+   	}
+ !     }
+ ! 
+ !   /* check for stack adjustment 
+ !    *
+ !    *  subl $XXX, %esp
+ !    *
+ !    * note: you can't subtract a 16 bit immediate
+ !    * from a 32 bit reg, so we don't have to worry
+ !    * about a data16 prefix 
+ !    */
+ !   op = codestream_peek ();
+ !   if (op == 0x83)
+ !     {
+ !       /* subl with 8 bit immed */
+ !       codestream_get ();
+ !       if (codestream_get () != 0xec)
+ ! 	/* Some instruction starting with 0x83 other than subl.  */
+   	{
+ ! 	  codestream_seek (codestream_tell () - 2);
+ ! 	  return;
+   	}
+ +       /* subl with signed byte immediate 
+ +        * (though it wouldn't make sense to be negative)
+ +        */
+ +       fi->stack_size = codestream_get();
+       }
+ !   else if (op == 0x81)
+       {
+ !       char buf[4];
+ !       /* Maybe it is subl with 32 bit immedediate.  */
+ !       codestream_get();
+ !       if (codestream_get () != 0xec)
+ ! 	/* Some instruction starting with 0x81 other than subl.  */
+ ! 	{
+ ! 	  codestream_seek (codestream_tell () - 2);
+ ! 	  return;
+ ! 	}
+ !       /* It is subl with 32 bit immediate.  */
+ !       codestream_read ((unsigned char *)buf, 4);
+ !       fi->stack_size = extract_signed_integer (buf, 4);
+       }
+ ! 
+ !   return;
+   }
+   
+   /* Return number of args passed to a frame.
+ *************** i386_frame_num_args (fi)
+ *** 358,372 ****
+   #endif
+   }
+   
+   /*
+ !  * parse the first few instructions of the function to see
+ !  * what registers were stored.
+    *
+    * We handle these cases:
+    *
+    * The startup sequence can be at the start of the function,
+    * or the function can start with a branch to startup code at the end.
+    *
+    * %ebp can be set up with either the 'enter' instruction, or 
+    * 'pushl %ebp, movl %esp, %ebp' (enter is too slow to be useful,
+    * but was once used in the sys5 compiler)
+ --- 401,562 ----
+   #endif
+   }
+   
+ + #if defined(I386V4_SIGTRAMP_SAVED_PC)
+ + /*
+ +  * Determine the location of the registers saved in the signal context
+ +  * for all three variants of SVR4 sigtramps.
+ +  */
+ + 
+ + static CORE_ADDR
+ + i386_sigcontext_addr (fi)
+ +      struct frame_info *fi;
+ + {
+ +   int offset = 0;
+ +   char *name = NULL;
+ + 
+ +   find_pc_partial_function (fi->pc, &name, NULL, NULL);
+ +   if (name)
+ +     {
+ +       if (STREQ (name, "_sigreturn"))
+ + 	offset = 132;
+ +       else if (STREQ (name, "_sigacthandler"))
+ + 	offset = 80;
+ +       else if (STREQ (name, "sigvechandler"))
+ + 	offset = 120;
+ +     }
+ + 
+ +   if (offset == 0)
+ +     return 0;
+ + 
+ +   if (fi->next)
+ +     return fi->next->frame + offset;
+ + 
+ +   return read_register (SP_REGNUM) + offset;
+ + }
+ + 
+ + /* Get saved user PC for sigtramp from the pushed ucontext on the stack.  */
+ + 
+ + CORE_ADDR
+ + i386v4_sigtramp_saved_pc (fi)
+ +      struct frame_info *frame;
+ + {
+ + 
+ +   return read_memory_integer (i386_sigcontext_addr (fi) + 14 * 4, 4);
+ + }
+ + 
+ + #elif defined(SIGCONTEXT_FP_OFFSET)
+ + /*
+ +  * Determine the location of the signal context.
+ +  */
+ + 
+ + static CORE_ADDR
+ + i386_sigcontext_addr (fi)
+ +      struct frame_info *fi;
+ + {
+ +   CORE_ADDR sigcontext_addr;
+ +   int ptrbytes = TARGET_PTR_BIT / TARGET_CHAR_BIT;
+ +   int sigcontext_offs = (2 * TARGET_INT_BIT) / TARGET_CHAR_BIT;
+ + 
+ +   /* Get sigcontext address, it is the third parameter on the stack.  */
+ +   if (fi->next)
+ +     sigcontext_addr = read_memory_integer (FRAME_ARGS_ADDRESS (fi->next)
+ + 					   + FRAME_ARGS_SKIP
+ + 					   + sigcontext_offs,
+ + 					   ptrbytes);
+ +   else
+ +     sigcontext_addr = read_memory_integer (read_register (SP_REGNUM)
+ + 					    + sigcontext_offs,
+ + 					   ptrbytes);
+ + 
+ +   return sigcontext_addr;
+ + }
+ + #endif
+ + 
+   /*
+ !  * Determine where the registers were stored.
+ !  */
+ ! 
+ ! static void
+ ! i386_frame_find_saved_regs (fi)
+ !      struct frame_info *fi;
+ ! {
+ !   unsigned char op;
+ !   CORE_ADDR adr;
+ !   int i;
+ !   int offset;
+ !   
+ !   /*
+ !    * First determine the offset of each register that has
+ !    * been placed on the stack.
+ !    */
+ ! 
+ !   offset = fi->stack_size;
+ ! 
+ !   for (i = 0; i < 8; i++) 
+ !     {
+ !       op = codestream_peek ();
+ !       if (op < 0x50 || op > 0x57)
+ ! 	break;
+ !       codestream_get ();
+ !       offset += 4;
+ ! #ifdef I386_REGNO_TO_SYMMETRY
+ !       /* Dynix uses different internal numbering.  Ick.  */
+ !       fi->fsr.regs[I386_REGNO_TO_SYMMETRY(op - 0x50)] = offset;
+ ! #else
+ !       fi->fsr.regs[op - 0x50] = offset;
+ ! #endif
+ !     }
+ ! 
+ !   /*
+ !    * Then determine the actual location of each register
+ !    * that has been placed on the stack.
+ !    */
+ ! 
+ !   if (fi->status == MY_FRAME_IN_SP)
+ !     {
+ !       /* Fix fi->frame if it's bogus at this point.  */
+ !       if (fi->next == NULL)
+ ! 	fi->frame = read_sp() + offset;
+ !     }
+ ! 
+ !   adr = fi->stack_mask ? (fi->frame & fi->stack_mask) : fi->frame;
+ ! 
+ !   for (i = 0; i < 8; i++) 
+ !     {
+ ! #ifdef I386_REGNO_TO_SYMMETRY
+ !       /* Dynix uses different internal numbering.  Ick.  */
+ !       if (fi->fsr.regs[I386_REGNO_TO_SYMMETRY(i)])
+ ! 	fi->fsr.regs[I386_REGNO_TO_SYMMETRY(i)] =
+ ! 	  adr - fi->fsr.regs[I386_REGNO_TO_SYMMETRY(i)];
+ ! #else
+ !       if (fi->fsr.regs[i])
+ ! 	fi->fsr.regs[i] = adr - fi->fsr.regs[i];
+ ! #endif
+ !     }
+ ! 
+ !   /*
+ !    * Finally determine where some important registers are located
+ !    */
+ ! 
+ !   if (fi->status == MY_FRAME_IN_FP)
+ !     {
+ !       fi->fsr.regs[FP_REGNUM] = fi->frame;
+ !       fi->fsr.regs[PC_REGNUM] = fi->frame + 4;
+ !     }
+ !   else if (fi->status == MY_FRAME_IN_SP)
+ !     fi->fsr.regs[PC_REGNUM] = fi->frame;
+ ! }
+ ! 
+ ! /*
+ !  * Parse the prologue.
+    *
+    * We handle these cases:
+    *
+    * The startup sequence can be at the start of the function,
+    * or the function can start with a branch to startup code at the end.
+    *
+ +  * The function can be frameless.
+ +  *
+    * %ebp can be set up with either the 'enter' instruction, or 
+    * 'pushl %ebp, movl %esp, %ebp' (enter is too slow to be useful,
+    * but was once used in the sys5 compiler)
+ *************** i386_frame_num_args (fi)
+ *** 374,448 ****
+    * Local space is allocated just below the saved %ebp by either the
+    * 'enter' instruction, or by 'subl $<size>, %esp'.  'enter' has
+    * a 16 bit unsigned argument for space to allocate, and the
+ !  * 'addl' instruction could have either a signed byte, or
+    * 32 bit immediate.
+    *
+    * Next, the registers used by this function are pushed.  In
+    * the sys5 compiler they will always be in the order: %edi, %esi, %ebx
+    * (and sometimes a harmless bug causes it to also save but not restore %eax);
+ !  * however, the code below is willing to see the pushes in any order,
+    * and will handle up to 8 of them.
+    *
+    * If the setup sequence is at the end of the function, then the
+    * next instruction will be a branch back to the start.
+    */
+   
+ ! void
+ ! i386_frame_find_saved_regs (fip, fsrp)
+ !      struct frame_info *fip;
+ !      struct frame_saved_regs *fsrp;
+   {
+ -   long locals = -1;
+ -   unsigned char op;
+ -   CORE_ADDR dummy_bottom;
+     CORE_ADDR adr;
+ !   CORE_ADDR pc;
+     int i;
+ !   
+ !   memset (fsrp, 0, sizeof *fsrp);
+ !   
+     /* if frame is the end of a dummy, compute where the
+      * beginning would be
+      */
+ !   dummy_bottom = fip->frame - 4 - REGISTER_BYTES - CALL_DUMMY_LENGTH;
+     
+     /* check if the PC is in the stack, in a dummy frame */
+ !   if (dummy_bottom <= fip->pc && fip->pc <= fip->frame) 
+       {
+ !       /* all regs were saved by push_call_dummy () */
+ !       adr = fip->frame;
+         for (i = 0; i < NUM_REGS; i++) 
+   	{
+   	  adr -= REGISTER_RAW_SIZE (i);
+ ! 	  fsrp->regs[i] = adr;
+   	}
+         return;
+       }
+     
+ !   pc = get_pc_function_start (fip->pc);
+ !   if (pc != 0)
+ !     locals = i386_get_frame_setup (pc);
+ !   
+ !   if (locals >= 0) 
+       {
+ !       adr = fip->frame - 4 - locals;
+ !       for (i = 0; i < 8; i++) 
+ ! 	{
+ ! 	  op = codestream_get ();
+ ! 	  if (op < 0x50 || op > 0x57)
+ ! 	    break;
+   #ifdef I386_REGNO_TO_SYMMETRY
+ ! 	  /* Dynix uses different internal numbering.  Ick.  */
+ ! 	  fsrp->regs[I386_REGNO_TO_SYMMETRY(op - 0x50)] = adr;
+   #else
+ ! 	  fsrp->regs[op - 0x50] = adr;
+   #endif
+ ! 	  adr -= 4;
+ ! 	}
+       }
+ -   
+ -   fsrp->regs[PC_REGNUM] = fip->frame + 4;
+ -   fsrp->regs[FP_REGNUM] = fip->frame;
+   }
+   
+   /* return pc of first real instruction */
+ --- 564,771 ----
+    * Local space is allocated just below the saved %ebp by either the
+    * 'enter' instruction, or by 'subl $<size>, %esp'.  'enter' has
+    * a 16 bit unsigned argument for space to allocate, and the
+ !  * 'subl' instruction could have either a signed byte, or
+    * 32 bit immediate.
+    *
+    * Next, the registers used by this function are pushed.  In
+    * the sys5 compiler they will always be in the order: %edi, %esi, %ebx
+    * (and sometimes a harmless bug causes it to also save but not restore %eax);
+ !  * however, the code is willing to see the pushes in any order,
+    * and will handle up to 8 of them.
+    *
+    * If the setup sequence is at the end of the function, then the
+    * next instruction will be a branch back to the start.
+    */
+   
+ ! static void
+ ! i386_analyze_prologue (fi, skip_prologue)
+ !     struct frame_info *fi;
+ !     int skip_prologue;
+   {
+     CORE_ADDR adr;
+ !   CORE_ADDR dummy_bottom;
+ !   CORE_ADDR func_addr, func_end;
+ !   char *name;
+ !   unsigned char op;
+     int i;
+ !   int status;
+ ! 
+ !   /* Find the start of this function.  */
+ !   status = find_pc_partial_function (fi->pc, &name, &func_addr, &func_end);
+ ! 
+ !   /* If we're in a sigtramp which has a sigcontext then record the location
+ !      of the frame and the stack pointer so that i386_frame_chain will work.
+ !      At some point it may be worth while to also record the location of the
+ !      other registers.  */
+ ! #if defined(SIGCONTEXT_FP_OFFSET)
+ !   if (IN_SIGTRAMP (fi->pc, name))
+ !     {
+ !       fi->fsr.regs[FP_REGNUM]
+ ! 	= i386_sigcontext_addr (fi) + SIGCONTEXT_FP_OFFSET;
+ !       fi->fsr.regs[SP_REGNUM]
+ ! 	= i386_sigcontext_addr (fi) + SIGCONTEXT_SP_OFFSET;
+ !       return;
+ !     }
+ ! #endif
+ ! 
+     /* if frame is the end of a dummy, compute where the
+      * beginning would be
+      */
+ !   dummy_bottom = fi->frame - 4 - REGISTER_BYTES - CALL_DUMMY_LENGTH;
+     
+     /* check if the PC is in the stack, in a dummy frame */
+ !   if (dummy_bottom <= fi->pc && fi->pc <= fi->frame) 
+       {
+ !       /* all regs were saved by i386_push_dummy_frame */
+ !       fi->status = MY_FRAME_IN_FP;
+ !       adr = fi->frame;
+         for (i = 0; i < NUM_REGS; i++) 
+   	{
+   	  adr -= REGISTER_RAW_SIZE (i);
+ ! 	  fi->fsr.regs[i] = adr;
+   	}
+ +       fi->fsr.regs[FP_REGNUM] = fi->frame;
+ +       fi->fsr.regs[PC_REGNUM] = fi->frame + 4;
+         return;
+       }
+ + 
+ +   /* If we couldn't find the start of this function then assume that
+ +      it's frame is in the frame pointer so that we can chain back to
+ +      a dummy frame.  */
+ +   if (status == 0)
+ +     {
+ +       fi->status = MY_FRAME_IN_FP;
+ +       return;
+ +     }
+ + 
+ +   /* If we're in start, then give up.  */
+ +   if (strcmp (name, "start") == 0)
+ +     {
+ +       fi->status = NO_MORE_FRAMES;
+ +       return;
+ +     }
+ + 
+ +   /* At the start of a function our frame is in the stack pointer.  */
+ +   fi->status = MY_FRAME_IN_SP;
+ + 
+ +   /* If we're physically on the first insn of a prologue, then our frame
+ +      hasn't been allocated yet.  And if we're physically on an "return"
+ +      instruction, then our frame has already been deallocated.  */
+ +   if (!skip_prologue)
+ +     {
+ +       codestream_seek (fi->pc);
+ +       op = codestream_peek();
+ +       if (fi->pc == func_addr
+ + 	  || op == 0xc2 || op == 0xc3 || op == 0xca || op == 0xcb)
+ + 	{
+ + 	  /* Fix fi->frame if it's bogus at this point.  */
+ + 	  if (fi->next == NULL)
+ + 	    fi->frame = read_sp();
+ + 	  fi->fsr.regs[PC_REGNUM] = fi->frame;
+ + 	  fi->status = INCOMPLETE_FRAME;
+ +           return;
+ + 	}
+ +     }
+ + 
+ +   /* Start scanning on the first instruction of this function.  */
+ +   codestream_seek (func_addr);
+ + 
+ +   i386_follow_jump ();
+ + 
+ +   i386_get_frame_setup (fi);
+     
+ !   i386_frame_find_saved_regs (fi);
+ ! }
+ ! 
+ ! /* Function: frame_chain
+ !    Figure out and return the caller's frame pointer given current
+ !    frame_info struct.
+ ! 
+ !    We don't handle dummy frames yet but we would probably just return the
+ !    stack pointer that was in use at the time the function call was made?  */
+ ! 
+ ! CORE_ADDR
+ ! i386_frame_chain (fi)
+ !      struct frame_info *fi;
+ ! {
+ !   struct frame_info dummy_frame;
+ ! 
+ !   /* Walk through the prologue to determine the stack size,
+ !      location of saved registers, end of the prologue, etc.  */
+ !   if (fi->status == 0)
+ !     i386_init_extra_frame_info (fi);
+ ! 
+ !   /* Quit now if i386_analyze_prologue set NO_MORE_FRAMES.  */
+ !   if (fi->status & NO_MORE_FRAMES)
+ !     return 0;
+ ! 
+ !   /* Now that we've analyzed our prologue, determine the frame
+ !      pointer for our caller.
+ ! 
+ !      If our caller has a frame pointer, then we need to
+ !      find the entry value of %ebp to our function.
+ ! 
+ !        If fsr.regs[FP_REGNUM] is nonzero, then it's at the memory
+ !        location pointed to by fsr.regs[FP_REGNUM].
+ ! 
+ !        Else it's still in %ebp.
+ ! 
+ !      If our caller does not have a frame pointer, then his
+ !      frame base is fi->frame + <pc save space> +
+ !      <caller's register save space> + <caller's stack size>
+ !      assuming that the caller's stack pointer was unchanging.
+ !        
+ !      The easiest way to get that info is to analyze our caller's frame.
+ !      So we set up a dummy frame and call i386_init_extra_frame_info to
+ !      find stuff for us.  BTW, the actual value of dummy_frame.frame
+ !      doesn't matter so long as it isn't zero.  */
+ ! 
+ !   dummy_frame.next = fi;
+ !   dummy_frame.frame = fi->frame;
+ !   i386_init_extra_frame_info (&dummy_frame);
+ ! 
+ !   if (! dummy_frame.status)
+ !     return fi->frame;
+ ! 
+ !   if (dummy_frame.status & MY_FRAME_IN_FP)
+ !     {
+ !       char buf[MAX_REGISTER_RAW_SIZE];
+ ! 
+ !       /* Our caller has a frame pointer.  So find the frame in %ebp or
+ !          in the stack.  */
+ !       if (fi->fsr.regs[FP_REGNUM])
+ ! 	return read_memory_integer (fi->fsr.regs[FP_REGNUM], 4);
+ !       get_saved_register(buf, NULL, NULL, fi, FP_REGNUM, NULL);
+ !       return extract_address (buf, REGISTER_RAW_SIZE (FP_REGNUM));
+ !     }
+ !   else
+       {
+ !       int offset;
+ !       int i;
+ ! 
+ !       offset = dummy_frame.stack_size;
+ ! 
+ !       for (i = 0; i < 8; i++)
+   #ifdef I386_REGNO_TO_SYMMETRY
+ ! 	/* Dynix uses different internal numbering.  Ick.  */
+ ! 	if (dummy_frame.fsr.regs[I386_REGNO_TO_SYMMETRY(i)])
+ ! 	  offset += 4;
+   #else
+ ! 	if (dummy_frame.fsr.regs[i])
+ ! 	  offset += 4;
+   #endif
+ ! 
+ !       if (fi->signal_handler_caller && fi->fsr.regs[SP_REGNUM])
+ ! 	return read_memory_integer (fi->fsr.regs[SP_REGNUM], 4) + offset;
+ ! 
+ !       offset += (fi->status & MY_FRAME_IN_FP) ? 8 : 4;
+ ! 
+ !       /* Our caller does not have a frame pointer.  Assume his stack
+ ! 	 pointer was constant which means that his frame starts at
+ ! 	 the base of our frame (fi->frame) + pc save space +
+ ! 	 <his register save space> + <his size>. */
+ !       return fi->frame + offset;
+       }
+   }
+   
+   /* return pc of first real instruction */
+ *************** i386_skip_prologue (pc)
+ *** 457,479 ****
+   				      0x5b,             /* popl   %ebx */
+   				    };
+     CORE_ADDR pos;
+     
+ !   if (i386_get_frame_setup (pc) < 0)
+ !     return (pc);
+ !   
+ !   /* found valid frame setup - codestream now points to 
+ !    * start of push instructions for saving registers
+ !    */
+ !   
+ !   /* skip over register saves */
+ !   for (i = 0; i < 8; i++)
+ !     {
+ !       op = codestream_peek ();
+ !       /* break if not pushl inst */
+ !       if (op < 0x50 || op > 0x57) 
+ ! 	break;
+ !       codestream_get ();
+ !     }
+   
+     /* The native cc on SVR4 in -K PIC mode inserts the following code to get
+        the address of the global offset table (GOT) into register %ebx.
+ --- 780,791 ----
+   				      0x5b,             /* popl   %ebx */
+   				    };
+     CORE_ADDR pos;
+ +   struct frame_info fi;
+     
+ !   fi.frame = 0;
+ !   fi.pc = pc;
+ ! 
+ !   i386_analyze_prologue (&fi, 1);
+   
+     /* The native cc on SVR4 in -K PIC mode inserts the following code to get
+        the address of the global offset table (GOT) into register %ebx.
+ *************** i386_skip_prologue (pc)
+ *** 525,531 ****
+     
+     i386_follow_jump ();
+     
+ !   return (codestream_tell ());
+   }
+   
+   void
+ --- 837,843 ----
+     
+     i386_follow_jump ();
+     
+ !   return codestream_tell ();
+   }
+   
+   void
+ *************** void
+ *** 550,562 ****
+   i386_pop_frame ()
+   {
+     struct frame_info *frame = get_current_frame ();
+ -   CORE_ADDR fp;
+     int regnum;
+     struct frame_saved_regs fsr;
+     char regbuf[MAX_REGISTER_RAW_SIZE];
+     
+ -   fp = FRAME_FP (frame);
+     get_frame_saved_regs (frame, &fsr);
+     for (regnum = 0; regnum < NUM_REGS; regnum++) 
+       {
+         CORE_ADDR adr;
+ --- 862,873 ----
+   i386_pop_frame ()
+   {
+     struct frame_info *frame = get_current_frame ();
+     int regnum;
+     struct frame_saved_regs fsr;
+     char regbuf[MAX_REGISTER_RAW_SIZE];
+     
+     get_frame_saved_regs (frame, &fsr);
+ + 
+     for (regnum = 0; regnum < NUM_REGS; regnum++) 
+       {
+         CORE_ADDR adr;
+ *************** i386_pop_frame ()
+ *** 568,576 ****
+   				REGISTER_RAW_SIZE (regnum));
+   	}
+       }
+ !   write_register (FP_REGNUM, read_memory_integer (fp, 4));
+ !   write_register (PC_REGNUM, read_memory_integer (fp + 4, 4));
+ !   write_register (SP_REGNUM, fp + 8);
+     flush_cached_frames ();
+   }
+   
+ --- 879,887 ----
+   				REGISTER_RAW_SIZE (regnum));
+   	}
+       }
+ ! 
+ !   write_register (SP_REGNUM, fsr.regs[PC_REGNUM] + 4);
+ ! 
+     flush_cached_frames ();
+   }
+   
+ *************** i386_extract_return_value(type, regbuf, 
+ *** 632,665 ****
+       }
+   }
+   
+ - #ifdef I386V4_SIGTRAMP_SAVED_PC
+ - /* Get saved user PC for sigtramp from the pushed ucontext on the stack
+ -    for all three variants of SVR4 sigtramps.  */
+ - 
+ - CORE_ADDR
+ - i386v4_sigtramp_saved_pc (frame)
+ -      struct frame_info *frame;
+ - {
+ -   CORE_ADDR saved_pc_offset = 4;
+ -   char *name = NULL;
+ - 
+ -   find_pc_partial_function (frame->pc, &name, NULL, NULL);
+ -   if (name)
+ -     {
+ -       if (STREQ (name, "_sigreturn"))
+ - 	saved_pc_offset = 132 + 14 * 4;
+ -       else if (STREQ (name, "_sigacthandler"))
+ - 	saved_pc_offset = 80 + 14 * 4;
+ -       else if (STREQ (name, "sigvechandler"))
+ - 	saved_pc_offset = 120 + 14 * 4;
+ -     }
+ - 
+ -   if (frame->next)
+ -     return read_memory_integer (frame->next->frame + saved_pc_offset, 4);
+ -   return read_memory_integer (read_register (SP_REGNUM) + saved_pc_offset, 4);
+ - }
+ - #endif /* I386V4_SIGTRAMP_SAVED_PC */
+ - 
+   #ifdef STATIC_TRANSFORM_NAME
+   /* SunPRO encodes the static variables.  This is not related to C++ mangling,
+      it is done for C too.  */
+ --- 943,948 ----
+ *************** skip_trampoline_code (pc, name)
+ *** 712,717 ****
+ --- 995,1029 ----
+     return 0;			/* not a trampoline */
+   }
+   
+ + /* Function: init_extra_frame_info
+ +    Setup the frame's frame pointer, pc, and frame addresses for saved
+ +    registers.  Most of the work is done in i386_analyze_prologue().
+ + 
+ +    Note that when we are called for the last frame (currently active frame),
+ +    that fi->pc and fi->frame will already be setup.  However, fi->frame will
+ +    be valid only if this routine uses FP.  For previous frames, fi->frame will
+ +    always be correct.  i386_analyze_prologue will fix fi->frame if
+ +    it's not valid.
+ + 
+ +    We can be called with the PC in the call dummy under two circumstances.
+ +    First, during normal backtracing, second, while figuring out the frame
+ +    pointer just prior to calling the target function (see run_stack_dummy).  */
+ + 
+ + void
+ + i386_init_extra_frame_info (fi)
+ +      struct frame_info *fi;
+ + {
+ + 
+ +   if (fi->next)
+ +     fi->pc = FRAME_SAVED_PC (fi->next);
+ + 
+ +   memset (fi->fsr.regs, '\000', sizeof fi->fsr.regs);
+ +   fi->status = 0;
+ +   fi->stack_mask = 0;
+ +   fi->stack_size = 0;
+ + 
+ +   i386_analyze_prologue (fi, 0);
+ + }
+   
+   void
+   _initialize_i386_tdep ()
+ *** gdb/config/i386/tm-i386.h.ORIGINAL	Thu Jan  4 02:23:24 1996
+ --- gdb/config/i386/tm-i386.h	Thu Feb 25 22:09:21 1999
+ *************** extern void i386_extract_return_value PA
+ *** 188,193 ****
+ --- 188,204 ----
+   
+   #define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) (*(int *)(REGBUF))
+   
+ + #define EXTRA_FRAME_INFO \
+ +   int status; \
+ +   int stack_mask; \
+ +   int stack_size; \
+ +   struct frame_saved_regs fsr;
+ + 
+ + #define INIT_EXTRA_FRAME_INFO(fromleaf, fi) i386_init_extra_frame_info (fi)
+ + #define INIT_FRAME_PC		/* Not necessary */
+ + 
+ + extern void i386_init_extra_frame_info PARAMS ((struct frame_info *));
+ + 
+   /* The following redefines make backtracing through sigtramp work.
+      They manufacture a fake sigtramp frame and obtain the saved pc in sigtramp
+      from the sigcontext structure which is pushed by the kernel on the
+ *************** extern void i386_extract_return_value PA
+ *** 200,235 ****
+   
+   #define FRAME_CHAIN(thisframe)  \
+     ((thisframe)->signal_handler_caller \
+ !    ? (thisframe)->frame \
+      : (!inside_entry_file ((thisframe)->pc) \
+ !       ? read_memory_integer ((thisframe)->frame, 4) \
+         : 0))
+   
+ ! /* A macro that tells us whether the function invocation represented
+ !    by FI does not have a frame on the stack associated with it.  If it
+ !    does not, FRAMELESS is set to 1, else 0.  */
+ ! 
+ ! #define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \
+ !   do { \
+ !     if ((FI)->signal_handler_caller) \
+ !       (FRAMELESS) = 0; \
+ !     else \
+ !       (FRAMELESS) = frameless_look_for_prologue(FI); \
+ !   } while (0)
+   
+   /* Saved Pc.  Get it from sigcontext if within sigtramp.  */
+   
+   #define FRAME_SAVED_PC(FRAME) \
+     (((FRAME)->signal_handler_caller \
+       ? sigtramp_saved_pc (FRAME) \
+ !     : read_memory_integer ((FRAME)->frame + 4, 4)) \
+      )
+   
+   extern CORE_ADDR sigtramp_saved_pc PARAMS ((struct frame_info *));
+   
+ ! #define FRAME_ARGS_ADDRESS(fi) ((fi)->frame)
+   
+ ! #define FRAME_LOCALS_ADDRESS(fi) ((fi)->frame)
+   
+   /* Return number of args passed to a frame.  Can return -1, meaning no way
+      to tell, which is typical now that the C compiler delays popping them.  */
+ --- 211,251 ----
+   
+   #define FRAME_CHAIN(thisframe)  \
+     ((thisframe)->signal_handler_caller \
+ !    ? i386_frame_chain (thisframe) \
+      : (!inside_entry_file ((thisframe)->pc) \
+ !       ? i386_frame_chain (thisframe) \
+         : 0))
+   
+ ! extern CORE_ADDR i386_frame_chain PARAMS ((struct frame_info *));
+   
+   /* Saved Pc.  Get it from sigcontext if within sigtramp.  */
+   
+   #define FRAME_SAVED_PC(FRAME) \
+     (((FRAME)->signal_handler_caller \
+       ? sigtramp_saved_pc (FRAME) \
+ !     : read_memory_integer ((FRAME)->fsr.regs[PC_REGNUM], 4)) \
+      )
+   
+   extern CORE_ADDR sigtramp_saved_pc PARAMS ((struct frame_info *));
+   
+ ! /* There are several meanings associated with stack_mask:
+ ! 
+ !      stack_mask == 0      Both the args and locals use %ebp.
+ ! 
+ !      stack_mask == -1     Both the args and locals use %esp.
+   
+ !      stack_mask != 0      The args use %ebp and the locals use %esp.
+ !      && stack_mask != -1  */
+ ! 
+ ! #define FRAME_ARGS_ADDRESS(fi) \
+ !   (((fi)->stack_mask == -1) \
+ !    ? ((fi)->frame - (fi)->stack_size) \
+ !    : (fi)->frame)
+ ! 
+ ! #define FRAME_LOCALS_ADDRESS(fi) \
+ !   ((fi)->stack_mask \
+ !    ? (((fi)->frame & (fi)->stack_mask) - (fi)->stack_size) \
+ !    : (fi)->frame)
+   
+   /* Return number of args passed to a frame.  Can return -1, meaning no way
+      to tell, which is typical now that the C compiler delays popping them.  */
+ *************** extern int i386_frame_num_args PARAMS ((
+ *** 248,259 ****
+      ways in the stack frame.  sp is even more special:
+      the address we return for it IS the sp for the next frame.  */
+   
+ ! #define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \
+ ! { i386_frame_find_saved_regs ((frame_info), &(frame_saved_regs)); }
+ ! 
+ ! extern void i386_frame_find_saved_regs PARAMS ((struct frame_info *,
+ ! 						struct frame_saved_regs *));
+ ! 
+   
+   /* Things needed for making the inferior call functions.  */
+   
+ --- 264,270 ----
+      ways in the stack frame.  sp is even more special:
+      the address we return for it IS the sp for the next frame.  */
+   
+ ! #define FRAME_FIND_SAVED_REGS(fi, regaddr) regaddr = fi->fsr
+   
+   /* Things needed for making the inferior call functions.  */
+   
+ *** gdb/config/i386/tm-i386bsd.h.ORIGINAL	Sun May 26 17:41:33 1996
+ --- gdb/config/i386/tm-i386bsd.h	Thu Feb 25 22:09:21 1999
+ *************** Foundation, Inc., 59 Temple Place - Suit
+ *** 39,43 ****
+ --- 39,45 ----
+   
+   /* Offset to saved PC in sigcontext, from <sys/signal.h>.  */
+   #define SIGCONTEXT_PC_OFFSET 20
+ + #define SIGCONTEXT_FP_OFFSET 12
+ + #define SIGCONTEXT_SP_OFFSET 8
+   
+   #endif  /* ifndef TM_I386BSD_H */
+ *** gdb/config/i386/tm-i386sol2.h.ORIGINAL	Sat Apr 11 01:39:47 1998
+ --- gdb/config/i386/tm-i386sol2.h	Thu Feb 25 22:23:14 1999
+ *************** Foundation, Inc., 59 Temple Place - Suit
+ *** 27,33 ****
+ --- 27,37 ----
+      a pointer to an ucontext.  */
+   #undef sigtramp_saved_pc
+   #undef I386V4_SIGTRAMP_SAVED_PC
+ + #undef SIGCONTEXT_FP_OFFSET
+ + #undef SIGCONTEXT_SP_OFFSET
+   #define SIGCONTEXT_PC_OFFSET (36 + 14 * 4)
+ + #define SIGCONTEXT_FP_OFFSET (36 + 6 * 4)
+ + #define SIGCONTEXT_SP_OFFSET (36 + 17 * 4)
+   #undef IN_SIGTRAMP
+   #define IN_SIGTRAMP(pc, name) (pc == 0xFFFFFFFF)
+   
+ *** gdb/config/i386/tm-i386v4.h.ORIGINAL	Thu Nov  2 10:20:44 1995
+ --- gdb/config/i386/tm-i386v4.h	Thu Feb 25 22:09:21 1999
+ *************** get_longjmp_target PARAMS ((CORE_ADDR *)
+ *** 65,70 ****
+ --- 65,72 ----
+      user stack. Unfortunately there are three variants of sigtramp handlers.  */
+   
+   #define I386V4_SIGTRAMP_SAVED_PC
+ + #define SIGCONTEXT_FP_OFFSET (6 * 4)
+ + #define SIGCONTEXT_SP_OFFSET (17 * 4)
+   #define IN_SIGTRAMP(pc, name) ((name)					\
+   			       && (STREQ ("_sigreturn", name)		\
+   				   || STREQ ("_sigacthandler", name)	\
+ -------------------------------------------------------------------------
+ |   Feith Systems  |   Voice: 1-215-646-8000  |  Email: john@feith.com  |
+ |    John Wehle    |     Fax: 1-215-540-5495  |                         |
+ -------------------------------------------------------------------------
-------------------------------------------------------------------------
|   Feith Systems  |   Voice: 1-215-646-8000  |  Email: john@feith.com  |
|    John Wehle    |     Fax: 1-215-540-5495  |                         |
-------------------------------------------------------------------------



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]