[RFC] CFG-based generator of unwind info

Josef Zlomek zlomj9am@artax.karlin.mff.cuni.cz
Sun Nov 30 11:47:00 GMT 2003


Hello,

I'm working on a CFG based generator of unwind info, the patch is attached.

Currently it is working on x86 and x86-64 (x86-64 was tested).

It is a dataflow based algorithm which tracks where values which were in
registers at the beginning of function are stored.
So it cares of changes of SP, insns setting CFA and move insns which
copy the value from one place to another.
Because when the debug info is being generated the CFG is already destroyed
this algorithm must run before CFG is destroyed. The notes are used to
pass the unwinding information to the debug generator.

Current limitations:
It is not complete replacement of current implementation yet because:
1. it does not support indirect CFA location ( *(reg+base_offset)+cfa_offset )
and supports only simple CFA location (reg + cfa_offset).
2. It also does not handle indirect sp <-> fp moves (i.e. sp <-> reg <-> fp)
which is needed for some architectures. This could be solved by marking
all relevant insns by RTX_FRAME_RELATED_P and tracking only such insns.
This requires changes to many (all?) machine descriptions to set this
flag. Moreover this flag is not currently set also for function epilogues (at
least on x86 and x86-64).
As soon as these issues are resolved it would be possible to
replace current unwind info generator.

Do you have any idea how to name the pass computing unwind info using CFG?
I named it register parameter tracking (regparam) because its main
purpose for me is tracking values of function parameters to be able to
show the values while unwinding. But I do not like this name.

Josef

2003-11-30  Josef Zlomek  <zlomekj@suse.cz>

	* Makefile.in (regparam.o): New.
	* common.opt (fregparam): New.
	* dwarf2out.c (process_reg_location): New.
	(reg_save): Emit DW_CFA_same_value.
	(dwarf2out_frame_debug): Call process_reg_location.
	* final.c (final_scan_insn): Call dwarf2out_frame_debug for
	NOTE_INSN_REG_LOCATION.
	* flags.h (flag_regparam): New.
	* gengtype.c (adjust_field_rtx_def): Add NOTE_INSN_REG_LOCATION.
	* print-rtl.c (print_rtx): Print NOTE_INSN_REG_LOCATION.
	* regparam.c: New file.
	* rtl.c (note_insn_name): Add NOTE_INSN_REG_LOCATION.
	* rtl.def (reg_location): New.
	* rtl.h (NOTE_REG_LOCATION): New.
	(REG_LOCATION_REGNO): New.
	(REG_LOCATION_LOC): New.
	(insn_note): Add NOTE_INSN_REG_LOCATION.
	(regparam_main): New.
	* timevar.def (TV_REGPARAM): New.
	* toplev.c (rest_of_handle_regparam): New.
	(dump_file_index): Add DFI_regparam.
	(dump_file): Add regparam.
	(flag_regparam): New.
	(lang_independent_options): Add regparam.
	(rest_of_compilation): Call rest_of_handle_regparam.
	(process_options): If fregparam was enabled and target supports it
	set flag_regparam to value which means we will run regparam pass.
	* unwind-dw2.c (_Unwind_Get_Argument_Reg): New.
	(execute_cfa_program: DW_CFA_undefined): Mark REG to be REG_UNSAVED.
	(execute_cfa_program: DW_CFA_same_value): Mark REG to be in REG.
	(uw_frame_state_for): Default for each register except SP is
	that REG is in REG.
	(uw_update_context_1: REG_UNSAVED): Clear the address of value of
	register to mark that the register is undefined.
	* unwind.h (_Unwind_Get_Argument_Reg): New.
	* config/i386/i386.c (override_options): If regparam was enabled set
	flag_regparam to show we support regparam.
	(ix86_emit_restore_regs_using_mov): Set RTX_FRAME_RELATED_P for insn
	if regparam will be run.
	(ix86_expand_epilogue): Likewise.

Index: Makefile.in
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/Makefile.in,v
retrieving revision 1.1202
diff -c -3 -p -r1.1202 Makefile.in
*** Makefile.in	30 Nov 2003 01:07:51 -0000	1.1202
--- Makefile.in	30 Nov 2003 08:02:20 -0000
*************** OBJS-common = \
*** 866,872 ****
   loop.o optabs.o options.o opts.o params.o postreload.o predict.o	   \
   print-rtl.o print-tree.o value-prof.o					   \
   profile.o ra.o ra-build.o ra-colorize.o ra-debug.o ra-rewrite.o	   \
!  real.o recog.o reg-stack.o regclass.o regmove.o regrename.o		   \
   reload.o reload1.o reorg.o resource.o rtl.o rtlanal.o rtl-error.o	   \
   sbitmap.o sched-deps.o sched-ebb.o sched-rgn.o sched-vis.o sdbout.o	   \
   sibcall.o simplify-rtx.o sreal.o stmt.o stor-layout.o stringpool.o 	   \
--- 866,872 ----
   loop.o optabs.o options.o opts.o params.o postreload.o predict.o	   \
   print-rtl.o print-tree.o value-prof.o					   \
   profile.o ra.o ra-build.o ra-colorize.o ra-debug.o ra-rewrite.o	   \
!  real.o recog.o reg-stack.o regclass.o regmove.o regparam.o regrename.o	   \
   reload.o reload1.o reorg.o resource.o rtl.o rtlanal.o rtl-error.o	   \
   sbitmap.o sched-deps.o sched-ebb.o sched-rgn.o sched-vis.o sdbout.o	   \
   sibcall.o simplify-rtx.o sreal.o stmt.o stor-layout.o stringpool.o 	   \
*************** bb-reorder.o : bb-reorder.c $(CONFIG_H) 
*** 1824,1829 ****
--- 1824,1833 ----
  tracer.o : tracer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
     $(BASIC_BLOCK_H) hard-reg-set.h output.h cfglayout.h flags.h \
     $(PARAMS_H) $(COVERAGE_H)
+ regparam.o : regparam.c $(CONFIG_H) $(SYSTEM_H) flags.h coretypes.h $(TM_H) \
+    $(RTL_H) $(TREE_H) hard-reg-set.h insn-config.h reload.h $(BASIC_BLOCK_H) \
+    output.h dwarf2out.h sbitmap.h alloc-pool.h $(FIBHEAP_H) $(HASHTAB_H) \
+    $(TM_P_H) varray.h
  cfglayout.o : cfglayout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
     $(RTL_H) $(TREE_H) insn-config.h $(BASIC_BLOCK_H) hard-reg-set.h output.h \
     function.h cfglayout.h cfgloop.h $(TARGET_H) gt-cfglayout.h $(GGC_H)
Index: common.opt
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/common.opt,v
retrieving revision 1.21
diff -c -3 -p -r1.21 common.opt
*** common.opt	21 Nov 2003 04:05:04 -0000	1.21
--- common.opt	30 Nov 2003 07:55:21 -0000
*************** fregmove
*** 540,545 ****
--- 540,549 ----
  Common
  Enables a register move optimization
  
+ fregparam
+ Common
+ Perform tracking of values of register parameters
+ 
  frename-registers
  Common
  Perform a register renaming optimization pass
Index: dwarf2out.c
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/dwarf2out.c,v
retrieving revision 1.465
diff -c -3 -p -r1.465 dwarf2out.c
*** dwarf2out.c	21 Nov 2003 21:18:45 -0000	1.465
--- dwarf2out.c	30 Nov 2003 07:45:46 -0000
*************** static void queue_reg_save (const char *
*** 360,365 ****
--- 360,366 ----
  static void flush_queued_reg_saves (void);
  static bool clobbers_queued_reg_save (rtx);
  static void dwarf2out_frame_debug_expr (rtx, const char *);
+ static void process_reg_location (rtx, const char *);
  
  /* Support for complex CFA locations.  */
  static void output_cfa_loc (dw_cfi_ref);
*************** reg_save (const char *label, unsigned in
*** 838,845 ****
        cfi->dw_cfi_oprnd2.dw_cfi_offset = offset;
      }
    else if (sreg == reg)
!     /* We could emit a DW_CFA_same_value in this case, but don't bother.  */
!     return;
    else
      {
        cfi->dw_cfi_opc = DW_CFA_register;
--- 839,847 ----
        cfi->dw_cfi_oprnd2.dw_cfi_offset = offset;
      }
    else if (sreg == reg)
!     {
!       cfi->dw_cfi_opc = DW_CFA_same_value;
!     }
    else
      {
        cfi->dw_cfi_opc = DW_CFA_register;
*************** dwarf2out_frame_debug_expr (rtx expr, co
*** 1661,1666 ****
--- 1663,1721 ----
      }
  }
  
+ /* Process a register location note REG_LOC and add CFI onto label LABEL.  */
+ 
+ static void
+ process_reg_location (rtx reg_loc, const char *label)
+ {
+   rtx loc;
+   unsigned int regno;
+   dw_cfi_ref cfi;
+ 
+   regno = (unsigned int) REG_LOCATION_REGNO (reg_loc);
+   loc = REG_LOCATION_LOC (reg_loc);
+ 
+   if (loc)
+     {
+       switch (GET_CODE (loc))
+ 	{
+ 	  case REG:
+ 	    reg_save (label, DWARF_FRAME_REGNUM (regno),
+ 		      DWARF_FRAME_REGNUM (REGNO (loc)), 0);
+ 	    break;
+ 
+ 	  case MEM:
+ 	    if (GET_CODE (XEXP (loc, 0)) == PLUS)
+ 	      reg_save (label, DWARF_FRAME_REGNUM (regno), -1,
+ 			INTVAL (XEXP (XEXP (loc, 0), 1))
+ 			- INCOMING_FRAME_SP_OFFSET);
+ 	    else
+ 	      reg_save (label, DWARF_FRAME_REGNUM (regno), -1, 0);
+ 	    break;
+ 
+ 	  case PLUS:
+ 	    if (!regno == STACK_POINTER_REGNUM)
+ 	      abort ();
+ 	    cfa.reg = REGNO (XEXP (loc, 0));
+ 	    cfa.offset = INTVAL (XEXP (loc, 1)) + INCOMING_FRAME_SP_OFFSET;
+ 	    cfa.base_offset = 0;
+ 	    cfa.indirect = 0;
+ 	    def_cfa_1 (label, &cfa);
+ 	    break;
+ 
+ 	  default:
+ 	    abort ();
+ 	}
+     }
+   else
+     {
+       cfi = new_cfi ();
+       cfi->dw_cfi_opc = DW_CFA_undefined;
+       cfi->dw_cfi_oprnd1.dw_cfi_reg_num = DWARF_FRAME_REGNUM (regno);
+       add_fde_cfi (label, cfi);
+     }
+ }
+ 
  /* Record call frame debugging information for INSN, which either
     sets SP or FP (adjusting how we calculate the frame address) or saves a
     register to the stack.  If INSN is NULL_RTX, initialize our state.  */
*************** dwarf2out_frame_debug (rtx insn)
*** 1685,1690 ****
--- 1740,1764 ----
        cfa_store = cfa;
        cfa_temp.reg = -1;
        cfa_temp.offset = 0;
+       return;
+     }
+ 
+   if (flag_regparam)
+     {
+       if (GET_CODE (insn) == NOTE
+ 	  && NOTE_LINE_NUMBER (insn) == NOTE_INSN_REG_LOCATION)
+ 	{
+ 	  static const char *last_label;
+ 
+ 	  if (PREV_INSN (insn)
+ 	      && GET_CODE (PREV_INSN (insn)) == NOTE
+ 	      && NOTE_LINE_NUMBER (PREV_INSN (insn)) == NOTE_INSN_REG_LOCATION)
+ 	    label = last_label;
+ 	  else
+ 	    last_label = label = dwarf2out_cfi_label ();
+ 
+ 	  process_reg_location (NOTE_REG_LOCATION (insn), label);
+ 	}
        return;
      }
  
Index: final.c
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/final.c,v
retrieving revision 1.296
diff -c -3 -p -r1.296 final.c
*** final.c	27 Nov 2003 18:21:51 -0000	1.296
--- final.c	30 Nov 2003 07:18:34 -0000
*************** final_scan_insn (rtx insn, FILE *file, i
*** 1777,1782 ****
--- 1777,1789 ----
  	  ASM_OUTPUT_DEBUG_LABEL (file, "L", CODE_LABEL_NUMBER (insn));
  	  break;
  
+ 	case NOTE_INSN_REG_LOCATION:
+ #if defined (DWARF2_UNWIND_INFO)
+ 	  if (dwarf2out_do_frame ())
+ 	    dwarf2out_frame_debug (insn);
+ #endif
+ 	  break;
+ 
  	case 0:
  	  break;
  
Index: flags.h
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/flags.h,v
retrieving revision 1.126
diff -c -3 -p -r1.126 flags.h
*** flags.h	20 Oct 2003 21:46:33 -0000	1.126
--- flags.h	30 Nov 2003 07:47:13 -0000
*************** extern int flag_gcse_las;
*** 683,688 ****
--- 683,691 ----
  /* Nonzero if value histograms should be used to optimize code.  */
  extern int flag_value_profile_transformations;
  
+ /* Nonzero if we should track values of register parameters.  */
+ extern int flag_regparam;
+ 
  /* Perform branch target register optimization before prologue / epilogue
     threading.  */
  
Index: gengtype.c
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/gengtype.c,v
retrieving revision 1.41
diff -c -3 -p -r1.41 gengtype.c
*** gengtype.c	21 Nov 2003 04:05:04 -0000	1.41
--- gengtype.c	30 Nov 2003 07:48:34 -0000
*************** adjust_field_rtx_def (type_p t, options_
*** 450,455 ****
--- 450,456 ----
  	    break;
  
  	  case NOTE_INSN_EXPECTED_VALUE:
+ 	  case NOTE_INSN_REG_LOCATION:
  	    note_flds->name = "rtx";
  	    note_flds->type = rtx_tp;
  	    break;
Index: print-rtl.c
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/print-rtl.c,v
retrieving revision 1.103
diff -c -3 -p -r1.103 print-rtl.c
*** print-rtl.c	2 Nov 2003 19:47:56 -0000	1.103
--- print-rtl.c	30 Nov 2003 07:18:34 -0000
*************** print_rtx (rtx in_rtx)
*** 291,296 ****
--- 291,303 ----
  		  fprintf (outfile, " [ ERROR ]");
  		break;
  
+ 	      case NOTE_INSN_REG_LOCATION:
+ 		fprintf (outfile, " (%d ",
+ 			 REG_LOCATION_REGNO (NOTE_REG_LOCATION (in_rtx)));
+ 		print_rtx (REG_LOCATION_LOC (NOTE_REG_LOCATION (in_rtx)));
+ 		fprintf (outfile, ")");
+ 		break;
+ 
  	      default:
  		{
  		  const char * const str = X0STR (in_rtx, i);
Index: regparam.c
===================================================================
RCS file: regparam.c
diff -N regparam.c
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- regparam.c	30 Nov 2003 08:58:01 -0000
***************
*** 0 ****
--- 1,1678 ----
+ /* Functions for tracking values of register parameters.
+    Copyright (C) 2003 Free Software Foundation, Inc.
+ 
+    This file is part of GCC.
+ 
+    GCC 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.
+ 
+    GCC 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 GCC; see the file COPYING.  If not, write to the Free
+    Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+    02111-1307, USA.  */
+ 
+ #include "config.h"
+ #include "system.h"
+ #include "coretypes.h"
+ #include "tm.h"
+ #include "rtl.h"
+ #include "tree.h"
+ #include "tm_p.h"
+ #include "hard-reg-set.h"
+ #include "basic-block.h"
+ #include "flags.h"
+ #include "output.h"
+ #include "dwarf2out.h"
+ #include "insn-config.h"
+ #include "reload.h"
+ #include "sbitmap.h"
+ #include "alloc-pool.h"
+ #include "fibheap.h"
+ #include "hashtab.h"
+ #include "varray.h"
+ 
+ /* Type of operation.  */
+ enum operation_type
+ {
+   OP_REGMEM_TO_REGMEM,	/* Store REG/MEM to REG/MEM.  */
+   OP_UNKNOWN_TO_REGMEM,	/* Store unknown value to REG/MEM.  */
+   OP_CALL,		/* Clobber registers not preserved by
+ 			   a function call.  */
+   OP_ADJUST		/* Adjust stack pointer. */
+ };
+ 
+ /* Structure holding information about operation.  */
+ typedef struct operation_def
+ {
+   /* Type of operation.  */
+   enum operation_type type;
+ 
+   union {
+     /* Operands of the operation.  */
+     struct {
+       rtx dst;
+       rtx src;
+     } s;
+ 
+     /* Stack adjustment.  */
+     HOST_WIDE_INT adjust;
+   } u;
+ 
+   /* The instruction which the operation is in.  */
+   rtx insn;
+ } operation;
+ 
+ /* Structure describing the location of register parameter.  */
+ typedef struct location_def
+ {
+   /* The location itself.  */
+   rtx loc;
+ 
+   /* The register number of the register parameter this location is associated
+      with.  */
+   unsigned int regno;
+ 
+   /* Double-linked list.  */
+   struct location_def *next, *prev;
+ } *location;
+ 
+ /* Structure describing location of Canonical Frame Address.  */
+ typedef struct cfa_location_def
+ {
+   unsigned int regno;
+   HOST_WIDE_INT offset;
+ } cfa_location;
+ 
+ /* Structure holding the IN or OUT set for a basic block.  */
+ typedef struct dataflow_set_def
+ {
+   /* Adjustment of stack offset.  */
+   HOST_WIDE_INT stack_adjust;
+ 
+   /* Array of doubly linked lists with pointers to heads and tails.  */
+   location head[FIRST_PSEUDO_REGISTER];
+   location tail[FIRST_PSEUDO_REGISTER];
+ 
+   /* Locations of the regparams.  */
+   htab_t locs;
+ 
+   /* Location of Canonical Frame Address.  */
+   cfa_location cfa;
+ } dataflow_set;
+ 
+ /* The structure (one for each basic block) containing the information
+    needed for register parameter tracking.  */
+ typedef struct register_parameter_info_def
+ {
+   /* Number of operations stored in the MOS array.  */
+   int n_opers;
+ 
+   /* The array of operations.  */
+   operation *opers;
+ 
+   /* The IN and OUT set for dataflow analysis.  */
+   dataflow_set in;
+   dataflow_set out;
+ 
+   /* Has the block been already visited?  */
+   bool visited;
+ } *register_parameter_info;
+ 
+ /* Pointer to the BB's information specific to variable tracking pass.  */
+ #define RPI(BB) ((register_parameter_info) (BB)->aux)
+ 
+ /* Alloc pool for struct location_def.  */
+ static alloc_pool location_pool;
+ 
+ /* Notes scheduled to be emitted.  */
+ static varray_type scheduled_notes;
+ 
+ /* Which registers should be tracked?  */
+ static bool track_reg_p[FIRST_PSEUDO_REGISTER];
+ 
+ /* RTX for REGNO.  */
+ static rtx reg_rtx[FIRST_PSEUDO_REGISTER];
+ 
+ /* Shall notes be emitted before insn?  */
+ static bool emit_notes_before_insn = false;
+ 
+ /* Special values of stack adjust.  */
+ #define INVALID_STACK_ADJUST						\
+   (((unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)) - 1)
+ #define UNKNOWN_STACK_ADJUST						\
+   (((unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)) - 1)
+ 
+ /* Local function prototypes.  */
+ static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
+ 					  HOST_WIDE_INT *);
+ static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
+ 					       HOST_WIDE_INT *);
+ static int contains_reg (rtx, unsigned int,
+ 						 unsigned int);
+ static rtx adjust_stack_reference (rtx, HOST_WIDE_INT, cfa_location *);
+ static hashval_t regparam_rtx_hash (const rtx);
+ static hashval_t location_htab_hash (const void *);
+ static int location_htab_eq (const void *, const void *);
+ 
+ static location location_lookup (dataflow_set *, rtx);
+ static bool location_insert (dataflow_set *, int, rtx);
+ static bool location_delete (dataflow_set *, rtx);
+ 
+ static void dataflow_set_init (dataflow_set *, int);
+ static void dataflow_set_clear (dataflow_set *);
+ static void dataflow_set_copy (dataflow_set *, dataflow_set *);
+ static void dataflow_set_confluence (dataflow_set *, dataflow_set *);
+ static bool dataflow_set_different (dataflow_set *, dataflow_set *);
+ static void dataflow_set_destroy (dataflow_set *);
+ 
+ static bool track_mem_p (rtx, rtx *);
+ static void count_operations (rtx, rtx, void *);
+ static void add_operations (rtx, rtx, void *);
+ static bool find_locations_in_bb (basic_block);
+ static void find_locations (void);
+ 
+ static void dump_operations (basic_block);
+ static void dump_locs (location);
+ static void dump_dataflow_set (dataflow_set *);
+ static void dump_dataflow_sets (void);
+ 
+ static void schedule_regno_note_to_emit (dataflow_set *, int);
+ static void schedule_note_to_emit (int, rtx);
+ static void emit_notes_for_differences (rtx, dataflow_set *, dataflow_set *);
+ static void emit_notes_for_bb (basic_block);
+ static void emit_scheduled_notes (rtx);
+ static void regparam_emit_notes (void);
+ 
+ static void regparam_initialize (void);
+ static void regparam_finalize (void);
+ 
+ /* Given a SET, calculate the amount of stack adjustment it contains
+    PRE- and POST-modifying stack pointer.
+    This function is similar to stack_adjust_offset.  */
+ 
+ static void
+ stack_adjust_offset_pre_post (rtx pattern, HOST_WIDE_INT *pre,
+ 			      HOST_WIDE_INT *post)
+ {
+   rtx src = SET_SRC (pattern);
+   rtx dest = SET_DEST (pattern);
+   enum rtx_code code;
+ 
+   if (dest == stack_pointer_rtx)
+     {
+       /* (set (reg sp) (plus (reg sp) (const_int))) */
+       code = GET_CODE (src);
+       if (! (code == PLUS || code == MINUS)
+ 	  || XEXP (src, 0) != stack_pointer_rtx
+ 	  || GET_CODE (XEXP (src, 1)) != CONST_INT)
+ 	return;
+ 
+       if (code == MINUS)
+ 	*post += INTVAL (XEXP (src, 1));
+       else
+ 	*post -= INTVAL (XEXP (src, 1));
+     }
+   else if (GET_CODE (dest) == MEM)
+     {
+       /* (set (mem (pre_dec (reg sp))) (foo)) */
+       src = XEXP (dest, 0);
+       code = GET_CODE (src);
+ 
+       switch (code)
+ 	{
+ 	case PRE_MODIFY:
+ 	case POST_MODIFY:
+ 	  if (XEXP (src, 0) == stack_pointer_rtx)
+ 	    {
+ 	      rtx val = XEXP (XEXP (src, 1), 1);
+ 	      /* We handle only adjustments by constant amount.  */
+ 	      if (GET_CODE (XEXP (src, 1)) != PLUS ||
+ 		  GET_CODE (val) != CONST_INT)
+ 		abort ();
+ 	      if (code == PRE_MODIFY)
+ 		*pre -= INTVAL (val);
+ 	      else
+ 		*post -= INTVAL (val);
+ 	      break;
+ 	    }
+ 	  return;
+ 
+ 	case PRE_DEC:
+ 	  if (XEXP (src, 0) == stack_pointer_rtx)
+ 	    {
+ 	      *pre += GET_MODE_SIZE (GET_MODE (dest));
+ 	      break;
+ 	    }
+ 	  return;
+ 
+ 	case POST_DEC:
+ 	  if (XEXP (src, 0) == stack_pointer_rtx)
+ 	    {
+ 	      *post += GET_MODE_SIZE (GET_MODE (dest));
+ 	      break;
+ 	    }
+ 	  return;
+ 
+ 	case PRE_INC:
+ 	  if (XEXP (src, 0) == stack_pointer_rtx)
+ 	    {
+ 	      *pre -= GET_MODE_SIZE (GET_MODE (dest));
+ 	      break;
+ 	    }
+ 	  return;
+ 
+ 	case POST_INC:
+ 	  if (XEXP (src, 0) == stack_pointer_rtx)
+ 	    {
+ 	      *post -= GET_MODE_SIZE (GET_MODE (dest));
+ 	      break;
+ 	    }
+ 	  return;
+ 
+ 	default:
+ 	  return;
+ 	}
+     }
+ }
+ 
+ /* Given an INSN, calculate the amount of stack adjustment it contains
+    PRE- and POST-modifying stack pointer.  */
+ 
+ static void
+ insn_stack_adjust_offset_pre_post (rtx insn, HOST_WIDE_INT *pre,
+ 				   HOST_WIDE_INT *post)
+ {
+   *pre = 0;
+   *post = 0;
+ 
+   if (GET_CODE (PATTERN (insn)) == SET)
+     stack_adjust_offset_pre_post (PATTERN (insn), pre, post);
+   else if (GET_CODE (PATTERN (insn)) == PARALLEL
+ 	   || GET_CODE (PATTERN (insn)) == SEQUENCE)
+     {
+       int i;
+ 
+       /* There may be stack adjustments inside compound insns.  Search
+ 	 for them.  */
+       for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+ 	if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
+ 	  stack_adjust_offset_pre_post (XVECEXP (PATTERN (insn), 0, i),
+ 					pre, post);
+     }
+ }
+ 
+ /* Return 1 if RTL X contains a register number REGNO1,
+    return 2 if RTL X contains a register number REGNO2, return 0 otherwise.  */
+ 
+ static int
+ contains_reg (rtx x, unsigned int regno1, unsigned int regno2)
+ {
+   const char *fmt;
+   RTX_CODE code;
+   int i;
+   int which;
+ 
+   if (!x)
+     return 0;
+ 
+   code = GET_CODE (x);
+   if (code == REG)
+     {
+       if (REGNO (x) == regno1)
+ 	return 1;
+       if (REGNO (x) == regno2)
+ 	return 2;
+       return 0;
+     }
+ 
+   fmt = GET_RTX_FORMAT (code);
+   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+     {
+       if (fmt[i] == 'e')
+ 	{
+ 	  which = contains_reg (XEXP (x, i), regno1, regno2);
+ 	  if (which)
+ 	    return which;
+ 	}
+       else if (fmt[i] == 'E')
+ 	{
+ 	  int j;
+ 	  for (j = 0; j < XVECLEN (x, i); j++)
+ 	    {
+ 	      which = contains_reg (XVECEXP (x, i, j), regno1, regno2);
+ 	      if (which)
+ 		return which;
+ 	    }
+ 	}
+     }
+ 
+   return 0;
+ }
+ 
+ /* Adjust stack reference X.  If X contains STACK_POINTER_RTX adjust it by
+    OFFSET bytes, if X contains CFA->REGNO adjust it by CFA->OFFSET.  */
+ 
+ static rtx
+ adjust_stack_reference (rtx x, HOST_WIDE_INT offset, cfa_location *cfa)
+ {
+   rtx new_x;
+   rtx tmp;
+   int which;
+ 
+   if (GET_CODE (x) != MEM)
+     return x;
+ 
+   which = contains_reg (x, STACK_POINTER_REGNUM, cfa->regno);
+   if (!which)
+     return x;
+ 
+   new_x = copy_rtx (x);
+   switch (which)
+     {
+       case 1:
+ 	XEXP (new_x, 0) = replace_rtx (XEXP (new_x, 0),
+ 				       stack_pointer_rtx,
+ 				       gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+ 						     GEN_INT (offset)));
+ 	break;
+ 
+       case 2:
+ 	XEXP (new_x, 0) = replace_rtx (XEXP (new_x, 0),
+ 				       reg_rtx[cfa->regno],
+ 				       gen_rtx_PLUS (Pmode, reg_rtx[cfa->regno],
+ 						     GEN_INT (-cfa->offset)));
+ 	break;
+     }
+ 
+   tmp = simplify_rtx (XEXP (new_x, 0));
+   if (tmp)
+     XEXP (new_x, 0) = tmp;
+ 
+   return new_x;
+ }
+ 
+ /* Hash function computing the hash value for location_htab from RTL X.  */
+ 
+ static hashval_t
+ regparam_rtx_hash (const rtx x)
+ {
+   if (GET_CODE (x) == REG)
+     return REGNO (x);
+ 
+   if (GET_CODE (x) == MEM)
+     {
+       rtx inside = XEXP (x, 0);
+       if (inside == stack_pointer_rtx
+ 	  || inside == hard_frame_pointer_rtx)
+ 	return 0x1000000;
+ 
+       if (GET_CODE (inside) == PLUS
+ 	  && (XEXP (inside, 0) == stack_pointer_rtx
+ 	      || XEXP (inside, 0) == hard_frame_pointer_rtx)
+ 	  && GET_CODE (XEXP (inside, 1)) == CONST_INT)
+ 	return 0x1000000 + INTVAL (XEXP (inside, 1));
+     }
+ 
+   return (size_t) x;
+ }
+ 
+ /* The hash function for location_htab, computes the hash value
+    from the declaration of variable X.  */
+ 
+ static hashval_t
+ location_htab_hash (const void *x)
+ {
+   const location loc = (const location) x;
+ 
+   return regparam_rtx_hash (loc->loc);
+ }
+ 
+ /* Compare the declaration of variable X with declaration Y.  */
+ 
+ static int
+ location_htab_eq (const void *x, const void *y)
+ {
+   const location loc1 = (const location) x;
+   const rtx loc2 = (const rtx) y;
+ 
+   if (GET_CODE (loc1->loc) != GET_CODE (loc2))
+     return 0;
+ 
+   if (GET_CODE (loc2) == REG)
+     return REGNO (loc1->loc) == REGNO (loc2);
+ 
+   return rtx_equal_p (loc1->loc, loc2);
+ }
+ 
+ /* Lookup location X in the dataflow set SET.  */
+ 
+ static location
+ location_lookup (dataflow_set *set, rtx x)
+ {
+   location loc;
+ 
+   loc = htab_find_with_hash (set->locs, x, regparam_rtx_hash (x));
+ 
+   return loc;
+ }
+ 
+ /* Insert location X to the list of locations for register number REGNO
+    of the dataflow set SET.
+    Return true if the preferred location has changed.  */
+ 
+ static bool
+ location_insert (dataflow_set *set, int regno, rtx x)
+ {
+   void **slot;
+   location loc;
+ 
+   slot = htab_find_slot_with_hash (set->locs, x, regparam_rtx_hash (x),
+ 				   INSERT);
+   if (*slot)
+     return false;
+ 
+   loc = pool_alloc (location_pool);
+   loc->loc = x;
+   loc->regno = regno;
+   *slot = loc;
+ 
+   if (GET_CODE (loc->loc) == REG)
+     {
+       /* Add the register to the tail of the chain.  */
+       if (set->tail[regno])
+ 	set->tail[regno]->next = loc;
+       loc->prev = set->tail[regno];
+       loc->next = NULL;
+       set->tail[regno] = loc;
+       if (!set->head[regno])
+ 	{
+ 	  set->head[regno] = loc;
+ 	  return true;
+ 	}
+     }
+   else
+     {
+       /* Add the memory to the head of the chain.  */
+       if (set->head[regno])
+ 	set->head[regno]->prev = loc;
+       loc->next = set->head[regno];
+       loc->prev = NULL;
+       set->head[regno] = loc;
+       if (!set->tail[regno])
+ 	set->tail[regno] = loc;
+       return true;
+     }
+ 
+   return false;
+ }
+ 
+ /* Delete location X from the list of locations for register number REGNO
+    of the dataflow set SET.
+    Return true if the preferred location has changed.  */
+ 
+ static bool
+ location_delete (dataflow_set *set, rtx x)
+ {
+   void **slot;
+   location loc;
+   int regno;
+   bool r = false;
+ 
+   slot = htab_find_slot_with_hash (set->locs, x, regparam_rtx_hash (x),
+ 				   NO_INSERT);
+   if (!slot)
+     return r;
+ 
+   loc = *slot;
+   regno = loc->regno;
+   if (!loc->prev)
+     {
+       set->head[regno] = loc->next;
+       if (!loc->next)
+ 	set->tail[regno] = NULL;
+       else
+ 	loc->next->prev = NULL;
+       r = true;
+     }
+   else if (!loc->next)
+     {
+       set->tail[regno] = loc->prev;
+       if (!loc->prev)
+ 	{
+ 	  set->head[regno] = NULL;
+ 	  r = true;
+ 	}
+       else
+ 	loc->prev->next = NULL;
+     }
+   else
+     {
+       loc->next->prev = loc->prev;
+       loc->prev->next = loc->next;
+     }
+   htab_clear_slot (set->locs, slot);
+   pool_free (location_pool, loc);
+   return r;
+ }
+ 
+ /* Initialize dataflow set SET to be empty. 
+    VARS_SIZE is the initial size of hash table VARS.  */
+ 
+ static void
+ dataflow_set_init (dataflow_set *set, int locs_size)
+ {
+   int i;
+ 
+   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+     {
+       set->head[i] = NULL;
+       set->tail[i] = NULL;
+     }
+   set->locs = htab_create (locs_size, location_htab_hash, location_htab_eq,
+ 			   NULL);
+   set->stack_adjust = UNKNOWN_STACK_ADJUST;
+   set->cfa.regno = 0;
+   set->cfa.offset = 0;
+ }
+ 
+ /* Delete the contents of dataflow set SET.  */
+ 
+ static void
+ dataflow_set_clear (dataflow_set *set)
+ {
+   int i;
+ 
+   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+     {
+       if (track_reg_p[i])
+ 	{
+ 	  while (set->head[i])
+ 	    location_delete (set, set->head[i]->loc);
+ 	}
+ #ifdef ENABLE_CHECKING
+       else if (set->head[i])
+ 	abort ();
+ #endif
+     }
+ #ifdef ENABLE_CHECKING
+   if (htab_elements (set->locs))
+     abort ();
+ #endif
+ }
+ 
+ /* Copy the contents of dataflow set SRC to DST.  */
+ 
+ static void
+ dataflow_set_copy (dataflow_set *dst, dataflow_set *src)
+ {
+   int i;
+ 
+   dataflow_set_clear (dst);
+   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+     {
+ #ifdef ENABLE_CHECKING
+       if (dst->head[i] || dst->tail[i])
+ 	abort ();
+ #endif
+       if (track_reg_p[i])
+ 	{
+ 	  location loc;
+ 
+ 	  for (loc = src->head[i]; loc; loc = loc->next)
+ 	    location_insert (dst, i, loc->loc);
+ 	}
+ #ifdef ENABLE_CHECKING
+       else if (src->head[i])
+ 	abort ();
+ #endif
+     }
+   dst->stack_adjust = src->stack_adjust;
+   dst->cfa.regno = src->cfa.regno;
+   dst->cfa.offset = src->cfa.offset;
+ }
+ 
+ /* Compute union of dataflow sets SRC and DST and store it to DST.  */
+ 
+ static void
+ dataflow_set_confluence (dataflow_set *dst, dataflow_set *src)
+ {
+   int i;
+   location loc;
+   void **slot;
+ 
+   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+     {
+       if (track_reg_p[i])
+ 	{
+ 	  for (loc = dst->head[i]; loc; loc = loc->next)
+ 	    {
+ 	      slot = htab_find_slot_with_hash (src->locs, loc->loc,
+ 					       regparam_rtx_hash (loc->loc),
+ 					       NO_INSERT);
+ 	      if (!slot)
+ 		{
+ 		  /* LOC is in DST but is not in SRC, delete it from DST.  */
+ 		  location_delete (dst, loc->loc);
+ 		}
+ 	    }
+ 	}
+ #ifdef ENABLE_CHECKING
+       else if (src->head[i])
+ 	abort ();
+ #endif
+     }
+ 
+   if (dst->stack_adjust == UNKNOWN_STACK_ADJUST)
+     dst->stack_adjust = src->stack_adjust;
+   else if (src->stack_adjust != UNKNOWN_STACK_ADJUST
+ 	   && src->stack_adjust != dst->stack_adjust)
+     dst->stack_adjust = INVALID_STACK_ADJUST;
+ 
+   if (!dst->cfa.regno)
+     dst->cfa = src->cfa;
+ #ifdef ENABLE_CHECKING
+   /* If this check fails
+      the code of function being compiled is inconsistent.  */
+   else if (src->cfa.regno
+       && (src->cfa.regno != dst->cfa.regno
+ 	  || src->cfa.offset != dst->cfa.offset))
+       abort ();
+ #endif
+ }
+ 
+ /* Return true if dataflow sets OLD_SET and NEW_SET differ.  */
+ 
+ static bool
+ dataflow_set_different (dataflow_set *old_set, dataflow_set *new_set)
+ {
+   int i;
+ 
+   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+     if (track_reg_p[i])
+       {
+ 	location loc1, loc2;
+ 
+ 	for (loc1 = old_set->head[i], loc2 = new_set->head[i];
+ 	     loc1 && loc2;
+ 	     loc1 = loc1->next, loc2 = loc2->next)
+ 	  ;
+ 	if (loc1 || loc2)
+ 	  {
+ 	    /* The lists do not have the same length.  */
+ 	    return true;
+ 	  }
+ 
+ 	/* Check whether the same locations are in both lists.  */
+ 	for (loc1 = old_set->head[i]; loc1; loc1 = loc1->next)
+ 	  {
+ 	    for (loc2 = new_set->head[i]; loc2; loc2 = loc2->next)
+ 	      {
+ 		if ((GET_CODE (loc1->loc) == REG
+ 		     && GET_CODE (loc2->loc) == REG
+ 		     && REGNO (loc1->loc) == REGNO (loc2->loc))
+ 		    || rtx_equal_p (loc1->loc, loc2->loc))
+ 		  break;
+ 	      }
+ 
+ 	    if (!loc2)
+ 	      {
+ 		/* LOC1 is not in list in OLD_SET.  */
+ 		return true;
+ 	      }
+ 	  }
+       }
+ 
+   return false;
+ }
+ 
+ /* Free the contents of dataflow set SET.  */
+ 
+ static void
+ dataflow_set_destroy (dataflow_set *set)
+ {
+   dataflow_set_clear (set);
+   htab_delete (set->locs);
+   set->locs = NULL;
+ }
+ 
+ /* Shall mem X be tracked?  If so store possibly modified mem to OUTP. */
+ 
+ static bool
+ track_mem_p (rtx x, rtx *outp)
+ {
+   rtx inside;
+ 
+ #ifdef ENABLE_CHECKING
+   if (GET_CODE (x) != MEM)
+     abort ();
+ #endif
+ 
+   *outp = x;
+   inside = XEXP (x, 0);
+   if (inside == stack_pointer_rtx
+       || inside == hard_frame_pointer_rtx)
+     return true;
+ 
+   if ((GET_CODE (inside) == PRE_DEC
+        || GET_CODE (inside) == PRE_INC
+        || GET_CODE (inside) == POST_DEC
+        || GET_CODE (inside) == POST_INC)
+       && XEXP (inside, 0) == stack_pointer_rtx)
+     {
+       *outp = gen_rtx_MEM (GET_MODE (x), XEXP (inside, 0));
+       return true;
+     }
+ 
+   if ((GET_CODE (inside) == PLUS || GET_CODE (inside) == MINUS)
+       && (XEXP (inside, 0) == stack_pointer_rtx
+ 	  || XEXP (inside, 0) == hard_frame_pointer_rtx)
+       && GET_CODE (XEXP (inside, 1)) == CONST_INT)
+     return true;
+ 
+   return false;
+ }
+ 
+ /* Count stores (register and memory references) LOC which will be tracked.
+    INSN is instruction which the LOC is part of.  */
+ 
+ static void
+ count_operations (rtx loc, rtx pattern ATTRIBUTE_UNUSED, void *insn)
+ {
+   basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
+ 
+ #ifdef ENABLE_CHECKING
+   if (GET_CODE (pattern) != SET && GET_CODE (pattern) != CLOBBER)
+     abort ();
+ #endif
+ 
+   if (GET_CODE (loc) == REG)
+     {
+ #ifdef ENABLE_CHECKING
+ 	if (REGNO (loc) >= FIRST_PSEUDO_REGISTER)
+ 	  abort ();
+ #endif
+ 	RPI (bb)->n_opers++;
+     }
+   else if (GET_CODE (loc) == MEM
+ 	   && RTX_FRAME_RELATED_P ((rtx) insn)
+ 	   && track_mem_p (loc, &loc))
+     {
+ 	  RPI (bb)->n_opers++;
+     }
+ }
+ 
+ /* Add stores (register and memory references) LOC which will be tracked
+    to RPI (bb)->opers. PATTERN is the RTL expr containing the store.
+    INSN is instruction which the LOC is part of.  */
+ 
+ static void
+ add_operations (rtx loc, rtx pattern, void *insn)
+ {
+   if (GET_CODE (loc) == REG
+       || (GET_CODE (loc) == MEM
+ 	  && RTX_FRAME_RELATED_P ((rtx) insn)
+ 	  && track_mem_p (loc, &loc)))
+     {
+       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
+ 
+       if ((GET_CODE (loc) != MEM && loc != SET_DEST (pattern))
+ 	  || GET_CODE (pattern) == CLOBBER
+ 	  || (GET_CODE (SET_SRC (pattern)) != REG
+ 	      && GET_CODE (SET_SRC (pattern)) != MEM))
+ 	{
+ 	  operation *oper = RPI (bb)->opers + RPI (bb)->n_opers++;
+ 	  oper->type = OP_UNKNOWN_TO_REGMEM;
+ 	  oper->u.s.dst = loc;
+ 	  if (GET_CODE (pattern) == SET
+ 	      /* Avoid computing stack pointer adjust twice.  */
+ 	      && !(loc == stack_pointer_rtx
+ 		   && contains_reg (SET_SRC (pattern), STACK_POINTER_REGNUM,
+ 				    STACK_POINTER_REGNUM)))
+ 	    oper->u.s.src = SET_SRC (pattern);
+ 	  else
+ 	    oper->u.s.src = NULL;
+ 	  oper->insn = insn;
+ 	}
+       else
+ 	{
+ 	  operation *oper = RPI (bb)->opers + RPI (bb)->n_opers++;
+ 	  oper->type = OP_REGMEM_TO_REGMEM;
+ 	  oper->u.s.dst = loc;
+ 	  oper->u.s.src = SET_SRC (pattern);
+ 	  oper->insn = insn;
+ 	}
+     }
+ }
+ 
+ /* Compute the changes of variable locations in the basic block BB.  */
+ 
+ static bool
+ find_locations_in_bb (basic_block bb)
+ {
+   int i, n, r;
+   bool changed;
+   dataflow_set old_out;
+   dataflow_set *in = &RPI (bb)->in;
+   dataflow_set *out = &RPI (bb)->out;
+   rtx src, dst;
+   location src_loc, dst_loc;
+ 
+   RPI (bb)->visited = true;
+   dataflow_set_init (&old_out, htab_elements (RPI (bb)->out.locs) + 3);
+   dataflow_set_copy (&old_out, out);
+   dataflow_set_copy (out, in);
+ 
+   n = RPI (bb)->n_opers;
+   for (i = 0; i < n; i++)
+     {
+       switch (RPI (bb)->opers[i].type)
+ 	{
+ 	  case OP_REGMEM_TO_REGMEM:
+ 	    src = adjust_stack_reference (RPI (bb)->opers[i].u.s.src,
+ 					  -out->stack_adjust, &out->cfa);
+ 	    dst = adjust_stack_reference (RPI (bb)->opers[i].u.s.dst,
+ 					  -out->stack_adjust, &out->cfa);
+ 	    if (/*RTX_FRAME_RELATED_P (RPI (bb)->opers[i].insn)
+ 		&& */GET_CODE (src) == REG && REGNO (src) == out->cfa.regno
+ 		&& (dst == stack_pointer_rtx || dst == hard_frame_pointer_rtx))
+ 	      {
+ 		if (GET_CODE (dst) != REG)
+ 		  abort ();
+ 
+ 		out->cfa.regno = REGNO (dst);
+ 		if (dst == stack_pointer_rtx)
+ 		  out->stack_adjust = out->cfa.offset;
+ 		else if (src == stack_pointer_rtx)
+ 		  {
+ 		    if (out->stack_adjust == INVALID_STACK_ADJUST)
+ 		      abort ();
+ 		    out->cfa.offset = out->stack_adjust;
+ 		  }
+ 	      }
+ 
+ 	    src_loc = location_lookup (out, src);
+ 	    if (src_loc)
+ 	      {
+ 		dst_loc = location_lookup (out, dst);
+ 		if (dst_loc)
+ 		  {
+ 		    if (src_loc->regno != dst_loc->regno)
+ 		      {
+ 			location_delete (out, dst);
+ 			location_insert (out, src_loc->regno, dst);
+ 		      }
+ 		  }
+ 		else
+ 		  {
+ 		    location_insert (out, src_loc->regno, dst);
+ 		  }
+ 		if (GET_CODE (src) == MEM
+ 		    && RTX_FRAME_RELATED_P (RPI (bb)->opers[i].insn))
+ 		  location_delete (out, src);
+ 	      }
+ 	    else
+ 	      {
+ 		location_delete (out, dst);
+ 	      }
+ 	    break;
+ 
+ 	  case OP_UNKNOWN_TO_REGMEM:
+ 	    dst = adjust_stack_reference (RPI (bb)->opers[i].u.s.dst,
+ 					  -out->stack_adjust, &out->cfa);
+ 	    src = RPI (bb)->opers[i].u.s.src;
+ 	    if (/*RTX_FRAME_RELATED_P (RPI (bb)->opers[i].insn)
+ 		&& */GET_CODE (dst) == REG && src
+ 		&& (dst == stack_pointer_rtx || dst == hard_frame_pointer_rtx)
+ 		&& (GET_CODE (src) == PLUS || GET_CODE (src) == MINUS)
+ 		&& GET_CODE (XEXP (src, 0)) == REG
+ 		&& REGNO (XEXP (src, 0)) == out->cfa.regno
+ 		&& GET_CODE (XEXP (src, 1)) == CONST_INT)
+ 	      {
+ 		HOST_WIDE_INT offset = INTVAL (XEXP (src, 1));
+ 
+ 		if (GET_CODE (XEXP (src, 0)) == MINUS)
+ 		  offset = -offset;
+ 
+ 		out->cfa.regno = REGNO (dst);
+ 		out->cfa.offset -= offset;
+ 		if (dst == stack_pointer_rtx)
+ 		  out->stack_adjust = out->cfa.offset;
+ 	      }
+ 
+ 	    dst_loc = location_lookup (out, dst);
+ 	    if (dst_loc)
+ 	      location_delete (out, dst);
+ 	    break;
+ 
+ 	  case OP_CALL:
+ 	    for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
+ 	      if (TEST_HARD_REG_BIT (call_used_reg_set, r))
+ 		location_delete (out, reg_rtx[r]);
+ 	    break;
+ 
+ 	  case OP_ADJUST:
+ 	    if (out->cfa.regno == STACK_POINTER_REGNUM)
+ 	      {
+ 		if (out->stack_adjust == INVALID_STACK_ADJUST)
+ 		  abort ();
+ 		out->cfa.offset += RPI (bb)->opers[i].u.adjust;
+ 	      }
+ 	    out->stack_adjust += RPI (bb)->opers[i].u.adjust;
+ 	    break;
+ 	}
+     }
+ 
+ #ifdef ENABLE_CHECKING
+     {
+       edge e;
+ 
+       for (e = bb->succ; e; e = e->succ_next)
+ 	{
+ 	  dataflow_set *set = &RPI (e->dest)->in;
+ 
+ 	  if (set->cfa.regno
+ 	      && (set->cfa.regno != out->cfa.regno
+ 		  || set->cfa.offset != out->cfa.offset))
+ 	    abort ();
+ 	}
+     }
+ #endif
+ 
+   changed = dataflow_set_different (&old_out, out);
+   dataflow_set_destroy (&old_out);
+   return changed;
+ }
+ 
+ /* Find the locations of variables in the whole function.  */
+ 
+ static void
+ find_locations (void)
+ {
+   fibheap_t worklist, pending, fibheap_swap;
+   sbitmap visited, in_worklist, in_pending, sbitmap_swap;
+   basic_block bb;
+   edge e;
+   int *bb_order;
+   int *rc_order;
+   int i;
+ 
+   /* Compute reverse completion order of depth first search of the CFG
+      so that the data-flow runs faster.  */
+   rc_order = (int *) xmalloc (n_basic_blocks * sizeof (int));
+   bb_order = (int *) xmalloc (last_basic_block * sizeof (int));
+   flow_depth_first_order_compute (NULL, rc_order);
+   for (i = 0; i < n_basic_blocks; i++)
+     bb_order[rc_order[i]] = i;
+   free (rc_order);
+ 
+   worklist = fibheap_new ();
+   pending = fibheap_new ();
+   visited = sbitmap_alloc (last_basic_block);
+   in_worklist = sbitmap_alloc (last_basic_block);
+   in_pending = sbitmap_alloc (last_basic_block);
+   sbitmap_zero (in_worklist);
+   sbitmap_zero (in_pending);
+ 
+   FOR_EACH_BB (bb)
+     {
+       fibheap_insert (pending, bb_order[bb->index], bb);
+       SET_BIT (in_pending, bb->index);
+     }
+ 
+   mark_dfs_back_edges ();
+   while (!fibheap_empty (pending))
+     {
+       fibheap_swap = pending;
+       pending = worklist;
+       worklist = fibheap_swap;
+       sbitmap_swap = in_pending;
+       in_pending = in_worklist;
+       in_worklist = sbitmap_swap;
+ 
+       sbitmap_zero (visited);
+ 
+       while (!fibheap_empty (worklist))
+ 	{
+ 	  bb = fibheap_extract_min (worklist);
+ 	  RESET_BIT (in_worklist, bb->index);
+ 	  if (!TEST_BIT (visited, bb->index))
+ 	    {
+ 	      bool changed;
+ 
+ 	      SET_BIT (visited, bb->index);
+ 
+ 	      /* Calculate the IN set as union of predecessor OUT sets.  */
+ 	      if ((e = bb->pred) != NULL)
+ 		{
+ 		  bool valid = false;
+ 
+ 		  while (e && e->flags & EDGE_DFS_BACK)
+ 		    e = e->pred_next;
+ #ifdef ENABLE_CHECKING
+ 		  if (!e)
+ 		    abort ();
+ #endif
+ 
+ 		  dataflow_set_copy (&RPI (bb)->in, &RPI (e->src)->out);
+ 		  valid = RPI (e->src)->visited;
+ 		  for (e = e->pred_next; e; e = e->pred_next)
+ 		    if (!(e->flags & EDGE_DFS_BACK))
+ 		      {
+ 			if (!valid)
+ 			  {
+ 			    dataflow_set_copy (&RPI (bb)->in,
+ 					       &RPI (e->src)->out);
+ 			    valid = RPI (e->src)->visited;
+ 			  }
+ 			else if (RPI (e->src)->visited)
+ 			  {
+ 			    dataflow_set_confluence (&RPI (bb)->in,
+ 						     &RPI (e->src)->out);
+ 			    valid = true;
+ 			  }
+ 		      }
+ 		}
+ #ifdef ENABLE_CHECKING
+ 	      if (!RPI (bb)->in.cfa.regno)
+ 		abort ();
+ #endif
+ 
+ 	      changed = find_locations_in_bb (bb);
+ 	      if (changed)
+ 		{
+ 		  for (e = bb->succ; e; e = e->succ_next)
+ 		    {
+ 		      if (e->dest == EXIT_BLOCK_PTR)
+ 			continue;
+ 
+ 		      if (e->dest == bb)
+ 			continue;
+ 
+ 		      if (TEST_BIT (visited, e->dest->index))
+ 			{
+ 			  if (!TEST_BIT (in_pending, e->dest->index))
+ 			    {
+ 			      /* Send E->DEST to next round.  */
+ 			      SET_BIT (in_pending, e->dest->index);
+ 			      fibheap_insert (pending, bb_order[e->dest->index],
+ 					      e->dest);
+ 			    }
+ 			}
+ 		      else if (!TEST_BIT (in_worklist, e->dest->index))
+ 			{
+ 			  /* Add E->DEST to current round.  */
+ 			  SET_BIT (in_worklist, e->dest->index);
+ 			  fibheap_insert (worklist, bb_order[e->dest->index],
+ 					  e->dest);
+ 			}
+ 		    }
+ 		}
+ 	    }
+ 	}
+     }
+ 
+   free (bb_order);
+   fibheap_delete (worklist);
+   fibheap_delete (pending);
+   sbitmap_free (visited);
+   sbitmap_free (in_worklist);
+   sbitmap_free (in_pending);
+ }
+ 
+ /* Dump operations in basic blocks BB.  */
+ 
+ static void
+ dump_operations (basic_block bb)
+ {
+   int i;
+ 
+   for (i = 0; i < RPI (bb)->n_opers; i++)
+     {
+       fprintf (rtl_dump_file, "%d ", INSN_UID (RPI (bb)->opers[i].insn));
+       switch (RPI (bb)->opers[i].type)
+ 	{
+ 	  case OP_REGMEM_TO_REGMEM:
+ 	    fprintf (rtl_dump_file, "REG/MEM <- REG/MEM\n");
+ 	    fprintf (rtl_dump_file, "  ");
+ 	    print_rtl_single (rtl_dump_file, RPI (bb)->opers[i].u.s.dst);
+ 	    fprintf (rtl_dump_file, "  ");
+ 	    print_rtl_single (rtl_dump_file, RPI (bb)->opers[i].u.s.src);
+ 	    break;
+ 
+ 	  case OP_UNKNOWN_TO_REGMEM:
+ 	    fprintf (rtl_dump_file, "REG/MEM <- UNKNOWN\n");
+ 	    fprintf (rtl_dump_file, "  ");
+ 	    print_rtl_single (rtl_dump_file, RPI (bb)->opers[i].u.s.dst);
+ 	    if (RPI (bb)->opers[i].u.s.src)
+ 	      {
+ 		fprintf (rtl_dump_file, "  ");
+ 		print_rtl_single (rtl_dump_file, RPI (bb)->opers[i].u.s.src);
+ 	      }
+ 	    break;
+ 
+ 	  case OP_CALL:
+ 	    fprintf (rtl_dump_file, "CALL\n");
+ 	    break;
+ 
+ 	  case OP_ADJUST:
+ 	    fprintf (rtl_dump_file, "ADJUST ");
+ 	    fprintf (rtl_dump_file, HOST_WIDE_INT_PRINT_DEC,
+ 		     RPI (bb)->opers[i].u.adjust);
+ 	    fprintf (rtl_dump_file, "\n");
+ 	    break;
+ 	}
+     }
+ }
+ 
+ /* Print the information about variables from hash table VARS to dump file.  */
+ static void
+ dump_locs (location loc)
+ {
+   while (loc)
+     {
+       fprintf (rtl_dump_file, "      ");
+       print_rtl_single (rtl_dump_file, loc->loc);
+       loc = loc->next;
+     }
+ }
+ 
+ /* Print the dataflow set SET to dump file.  */
+ 
+ static void
+ dump_dataflow_set (dataflow_set *set)
+ {
+   int i;
+ 
+   fprintf (rtl_dump_file, "Stack adjustment: ");
+   fprintf (rtl_dump_file, HOST_WIDE_INT_PRINT_DEC, set->stack_adjust);
+   fprintf (rtl_dump_file, "\n");
+   fprintf (rtl_dump_file, "CFA: offset ");
+   fprintf (rtl_dump_file, HOST_WIDE_INT_PRINT_DEC, set->cfa.offset);
+   fprintf (rtl_dump_file, " register ");
+   print_rtl_single (rtl_dump_file, reg_rtx[set->cfa.regno]);
+ 
+   for (i = 1; i < FIRST_PSEUDO_REGISTER; i++)
+     if (track_reg_p[i] && set->head[i])
+       {
+ 	fprintf (rtl_dump_file, "  Reg %d ", i);
+ 	print_rtl_single (rtl_dump_file, reg_rtx[i]);
+ 	dump_locs (set->head[i]);
+       }
+   fprintf (rtl_dump_file, "\n");
+ }
+ 
+ /* Print the IN and OUT sets for each basic block to dump file.  */
+ 
+ static void
+ dump_dataflow_sets (void)
+ {
+   basic_block bb;
+ 
+   FOR_EACH_BB (bb)
+     {
+       fprintf (rtl_dump_file, "\nBasic block %d:\n", bb->index);
+       fprintf (rtl_dump_file, "IN:\n");
+       dump_dataflow_set (&RPI (bb)->in);
+       fprintf (rtl_dump_file, "OUT:\n");
+       dump_dataflow_set (&RPI (bb)->out);
+     }
+ }
+ 
+ /* Create REG_LOCATION for register number REGNO in dataflow set SET
+    and schedule it to emit.  */
+ 
+ static void
+ schedule_regno_note_to_emit (dataflow_set *set, int regno)
+ {
+   rtx reg_loc;
+ 
+   if (set->head[regno])
+     reg_loc = gen_rtx_REG_LOCATION (VOIDmode, regno, set->head[regno]->loc);
+   else
+     reg_loc = gen_rtx_REG_LOCATION (VOIDmode, regno, NULL_RTX);
+ 
+   VARRAY_PUSH_RTX (scheduled_notes, reg_loc);
+ }
+ 
+ /* Create REG_LOCATION for register number REGNO in dataflow set SET
+    with location LOC and schedule it to emit.  */
+ 
+ static void
+ schedule_note_to_emit (int regno, rtx loc)
+ {
+   rtx reg_loc;
+ 
+   reg_loc = gen_rtx_REG_LOCATION (VOIDmode, regno, loc);
+ 
+   VARRAY_PUSH_RTX (scheduled_notes, reg_loc);
+ }
+ 
+ /* Emit notes before INSN for differences between dataflow sets OLD_SET and
+    NEW_SET.  */
+ 
+ static void
+ emit_notes_for_differences (rtx insn, dataflow_set *old_set,
+ 			    dataflow_set *new_set)
+ {
+   int i;
+ 
+   if (new_set->cfa.regno != old_set->cfa.regno
+       || new_set->cfa.offset != old_set->cfa.offset)
+     {
+       schedule_note_to_emit (STACK_POINTER_REGNUM,
+ 			     gen_rtx_PLUS (Pmode,
+ 					   reg_rtx[new_set->cfa.regno],
+ 					   GEN_INT (new_set->cfa.offset)));
+     }
+   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+     if (track_reg_p[i])
+       {
+ 	if (old_set->head[i] == NULL && new_set->head[i] == NULL)
+ 	  ;
+ 	else if (old_set->head[i] != NULL && new_set->head[i] != NULL)
+ 	  {
+ 	    if (!location_htab_eq (old_set->head[i], new_set->head[i]->loc))
+ 	      schedule_regno_note_to_emit (new_set, i);
+ 	  }
+ 	else
+ 	  {
+ 	    schedule_regno_note_to_emit (new_set, i);
+ 	  }
+       }
+ 
+   emit_notes_before_insn = true;
+   emit_scheduled_notes (insn);
+   emit_notes_before_insn = false;
+ }
+ 
+ /* Emit the notes for changes of location parts in the basic block BB.  */
+ 
+ static void
+ emit_notes_for_bb (basic_block bb)
+ {
+   int i, n, r;
+   dataflow_set set;
+   rtx src, dst;
+   location src_loc, dst_loc;
+ 
+   dataflow_set_init (&set, htab_elements (RPI (bb)->in.locs) + 3);
+   dataflow_set_copy (&set, &RPI (bb)->in);
+ 
+   n = RPI (bb)->n_opers;
+   for (i = 0; i < n; i++)
+     {
+       rtx insn = RPI (bb)->opers[i].insn;
+ 
+       switch (RPI (bb)->opers[i].type)
+ 	{
+ 	  case OP_REGMEM_TO_REGMEM:
+ 	    src = adjust_stack_reference (RPI (bb)->opers[i].u.s.src,
+ 					  -set.stack_adjust, &set.cfa);
+ 	    dst = adjust_stack_reference (RPI (bb)->opers[i].u.s.dst,
+ 					  -set.stack_adjust, &set.cfa);
+ 	    if (/*RTX_FRAME_RELATED_P (RPI (bb)->opers[i].insn)
+ 		&& */GET_CODE (src) == REG && REGNO (src) == set.cfa.regno
+ 		&& (dst == stack_pointer_rtx || dst == hard_frame_pointer_rtx))
+ 	      {
+ 		set.cfa.regno = REGNO (dst);
+ 		if (dst == stack_pointer_rtx)
+ 		  set.stack_adjust = set.cfa.offset;
+ 		else if (src == stack_pointer_rtx)
+ 		  set.cfa.offset = set.stack_adjust;
+ 		schedule_note_to_emit (STACK_POINTER_REGNUM,
+ 				       gen_rtx_PLUS (Pmode,
+ 						     reg_rtx[set.cfa.regno],
+ 						     GEN_INT (set.cfa.offset)));
+ 	      }
+ 
+ 	    src_loc = location_lookup (&set, src);
+ 	    if (src_loc)
+ 	      {
+ 		dst_loc = location_lookup (&set, dst);
+ 		if (dst_loc)
+ 		  {
+ 		    if (src_loc->regno != dst_loc->regno)
+ 		      {
+ 			int regno = dst_loc->regno;
+ 
+ 			if (location_delete (&set, dst))
+ 			  schedule_regno_note_to_emit (&set, regno);
+ 			if (location_insert (&set, src_loc->regno, dst))
+ 			  schedule_regno_note_to_emit (&set, src_loc->regno);
+ 		      }
+ 		  }
+ 		else
+ 		  {
+ 		    if (location_insert (&set, src_loc->regno, dst))
+ 		      schedule_regno_note_to_emit (&set, src_loc->regno);
+ 		  }
+ 		if (GET_CODE (src) == MEM
+ 		    && RTX_FRAME_RELATED_P (RPI (bb)->opers[i].insn))
+ 		  {
+ 		    int regno = src_loc->regno;
+ 
+ 		    if (location_delete (&set, src))
+ 		      schedule_regno_note_to_emit (&set, regno);
+ 		  }
+ 	      }
+ 	    else
+ 	      {
+ 		dst_loc = location_lookup (&set, dst);
+ 		if (dst_loc)
+ 		  {
+ 		    int regno = dst_loc->regno;
+ 
+ 		    if (location_delete (&set, dst))
+ 		      schedule_regno_note_to_emit (&set, regno);
+ 		  }
+ 	      }
+ 	    break;
+ 
+ 	  case OP_UNKNOWN_TO_REGMEM:
+ 	    dst = adjust_stack_reference (RPI (bb)->opers[i].u.s.dst,
+ 					  -set.stack_adjust, &set.cfa);
+ 	    src = RPI (bb)->opers[i].u.s.src;
+ 	    if (/*RTX_FRAME_RELATED_P (RPI (bb)->opers[i].insn)
+ 		&& */GET_CODE (dst) == REG && src
+ 		&& (dst == stack_pointer_rtx || dst == hard_frame_pointer_rtx)
+ 		&& (GET_CODE (src) == PLUS || GET_CODE (src) == MINUS)
+ 		&& GET_CODE (XEXP (src, 0)) == REG
+ 		&& REGNO (XEXP (src, 0)) == set.cfa.regno
+ 		&& GET_CODE (XEXP (src, 1)) == CONST_INT)
+ 	      {
+ 		HOST_WIDE_INT offset = INTVAL (XEXP (src, 1));
+ 
+ 		if (GET_CODE (XEXP (src, 0)) == MINUS)
+ 		  offset = -offset;
+ 
+ 		set.cfa.regno = REGNO (dst);
+ 		set.cfa.offset -= offset;
+ 		if (dst == stack_pointer_rtx)
+ 		  set.stack_adjust = set.cfa.offset;
+ 		schedule_note_to_emit (STACK_POINTER_REGNUM,
+ 				       gen_rtx_PLUS (Pmode,
+ 						     reg_rtx[set.cfa.regno],
+ 						     GEN_INT (set.cfa.offset)));
+ 	      }
+ 
+ 	    dst_loc = location_lookup (&set, dst);
+ 	    if (dst_loc)
+ 	      {
+ 		int regno = dst_loc->regno;
+ 
+ 		if (location_delete (&set, dst))
+ 		  schedule_regno_note_to_emit (&set, regno);
+ 	      }
+ 	    break;
+ 
+ 	  case OP_CALL:
+ 	    for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
+ 	      if (TEST_HARD_REG_BIT (call_used_reg_set, r))
+ 		{
+ 		  location loc;
+ 		  int regno;
+ 
+ 		  loc = location_lookup (&set, reg_rtx[r]);
+ 		  if (loc)
+ 		    {
+ 		      regno = loc->regno;
+ 		      if (location_delete (&set, reg_rtx[r]))
+ 			schedule_regno_note_to_emit (&set, regno);
+ 		    }
+ 		}
+ 	    break;
+ 
+ 	  case OP_ADJUST:
+ 	    if (set.cfa.regno == STACK_POINTER_REGNUM)
+ 	      {
+ 		set.cfa.offset += RPI (bb)->opers[i].u.adjust;
+ 		schedule_note_to_emit (STACK_POINTER_REGNUM,
+ 				       gen_rtx_PLUS (Pmode,
+ 						     reg_rtx[set.cfa.regno],
+ 						     GEN_INT (set.cfa.offset)));
+ 	      }
+ 	    set.stack_adjust += RPI (bb)->opers[i].u.adjust;
+ 	    break;
+ 	}
+ 
+       if (i + 1 >= n || insn != RPI (bb)->opers[i + 1].insn)
+ 	emit_scheduled_notes (insn);
+     }
+   dataflow_set_destroy (&set);
+ }
+ 
+ /* Emit scheduled notes after insn INSN.  */
+ 
+ static void
+ emit_scheduled_notes (rtx insn)
+ {
+   bool emitted[FIRST_PSEUDO_REGISTER];
+   int i;
+   rtx reg_loc;
+   rtx note;
+ 
+   memset (emitted, 0, sizeof (emitted));
+   if (emit_notes_before_insn)
+     {
+       for (i = VARRAY_ACTIVE_SIZE (scheduled_notes) - 1; i >= 0; i--)
+ 	{
+ 	  reg_loc = VARRAY_RTX (scheduled_notes, i);
+ 	  if (!emitted[REG_LOCATION_REGNO (reg_loc)])
+ 	    {
+ 	      note = emit_note_before (NOTE_INSN_REG_LOCATION, insn);
+ 	      NOTE_REG_LOCATION (note) = reg_loc;
+ 	      emitted[REG_LOCATION_REGNO (reg_loc)] = true;
+ 	      insn = note;
+ 	    }
+ 	}
+     }
+   else
+     {
+       for (i = VARRAY_ACTIVE_SIZE (scheduled_notes) - 1; i >= 0; i--)
+ 	{
+ 	  reg_loc = VARRAY_RTX (scheduled_notes, i);
+ 	  if (!emitted[REG_LOCATION_REGNO (reg_loc)])
+ 	    {
+ 	      note = emit_note_after (NOTE_INSN_REG_LOCATION, insn);
+ 	      NOTE_REG_LOCATION (note) = reg_loc;
+ 	      emitted[REG_LOCATION_REGNO (reg_loc)] = true;
+ 	    }
+ 	}
+     }
+ 
+   VARRAY_CLEAR (scheduled_notes);
+ }
+ 
+ /* Emit notes for the whole function.  */
+ 
+ static void
+ regparam_emit_notes (void)
+ {
+   basic_block bb;
+   dataflow_set *last_out;
+ 
+   last_out = &RPI (ENTRY_BLOCK_PTR)->out;
+ 
+   FOR_EACH_BB (bb)
+     {
+       /* Emit the notes for changes of variable locations between two
+ 	 subsequent basic blocks.  */
+       emit_notes_for_differences (bb->head, last_out, &RPI (bb)->in);
+ 
+       /* Emit the notes for the changes in the basic block itself.  */
+       emit_notes_for_bb (bb);
+ 
+       last_out = &RPI (bb)->out;
+     }
+ }
+ 
+ /* Allocate and initialize the data structures for variable tracking
+    and parse the RTL to get the micro operations.  */
+ 
+ static void
+ regparam_initialize (void)
+ {
+   basic_block bb;
+   unsigned int i;
+ 
+   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+     {
+ #if defined (DWARF_FRAME_REGISTERS) && defined (DBX_REGISTER_NUMBER)
+       track_reg_p[i] = (DBX_REGISTER_NUMBER (i) >= 0
+ 			&& DBX_REGISTER_NUMBER (i) < DWARF_FRAME_REGISTERS
+ 			&& (!TEST_HARD_REG_BIT (call_used_reg_set, i)
+ 			    || FUNCTION_ARG_REGNO_P (i)));
+ #else
+       track_reg_p[i] = (!TEST_HARD_REG_BIT (call_used_reg_set, i)
+ 			|| FUNCTION_ARG_REGNO_P (i));
+ #endif
+       reg_rtx[i] = gen_rtx_REG (Pmode, i);
+     }
+ 
+   alloc_aux_for_blocks (sizeof (struct register_parameter_info_def));
+ 
+   FOR_EACH_BB (bb)
+     {
+       rtx insn;
+       HOST_WIDE_INT pre, post;
+ 
+       /* Count the number of micro operations.  */
+       RPI (bb)->n_opers = 0;
+       for (insn = bb->head; insn != NEXT_INSN (bb->end);
+ 	   insn = NEXT_INSN (insn))
+ 	{
+ 	  if (INSN_P (insn))
+ 	    {
+ 	      insn_stack_adjust_offset_pre_post (insn, &pre, &post);
+ 	      if (pre)
+ 		RPI (bb)->n_opers++;
+ 	      if (post)
+ 		RPI (bb)->n_opers++;
+ 	      note_stores (PATTERN (insn), count_operations, insn);
+ 	      if (GET_CODE (insn) == CALL_INSN)
+ 		RPI (bb)->n_opers++;
+ 	    }
+ 	}
+ 
+       /* Add the nicro-operations to the array.  */
+       RPI (bb)->opers = xmalloc (RPI (bb)->n_opers
+ 				 * sizeof (struct operation_def));
+       RPI (bb)->n_opers = 0;
+       for (insn = bb->head; insn != NEXT_INSN (bb->end);
+ 	   insn = NEXT_INSN (insn))
+ 	{
+ 	  if (INSN_P (insn))
+ 	    {
+ 	      insn_stack_adjust_offset_pre_post (insn, &pre, &post);
+ 	      if (pre)
+ 		{
+ 		  operation *oper = RPI (bb)->opers + RPI (bb)->n_opers++;
+ 
+ 		  oper->type = OP_ADJUST;
+ 		  oper->u.adjust = pre;
+ 		  oper->insn = insn;
+ 		}
+ 
+ 	      if (GET_CODE (insn) == CALL_INSN)
+ 		{
+ 		  operation *oper = RPI (bb)->opers + RPI (bb)->n_opers++;
+ 
+ 		  oper->type = OP_CALL;
+ 		  oper->insn = insn;
+ 		}
+ 
+ 	      note_stores (PATTERN (insn), add_operations, insn);
+ 
+ 	      if (post)
+ 		{
+ 		  operation *oper = RPI (bb)->opers + RPI (bb)->n_opers++;
+ 
+ 		  oper->type = OP_ADJUST;
+ 		  oper->u.adjust = post;
+ 		  oper->insn = insn;
+ 		}
+ 	    }
+ 	}
+     }
+ 
+   /* Init the IN and OUT sets.  */
+   FOR_ALL_BB (bb)
+     {
+       RPI (bb)->visited = false;
+       dataflow_set_init (&RPI (bb)->in, 7);
+       dataflow_set_init (&RPI (bb)->out, 7);
+     }
+ 
+   location_pool = create_alloc_pool ("location_pool",
+ 				     sizeof (struct location_def), 1020);
+   VARRAY_RTX_INIT (scheduled_notes, 10, "scheduled_notes");
+ 
+   /* Initialize OUT set of ENTRY_BLOCK.  */
+   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+     if (track_reg_p[i])
+       location_insert (&RPI (ENTRY_BLOCK_PTR)->out, i, reg_rtx[i]);
+ 
+   RPI (ENTRY_BLOCK_PTR)->visited = true;
+   RPI (ENTRY_BLOCK_PTR)->out.stack_adjust = 0;
+   RPI (ENTRY_BLOCK_PTR)->out.cfa.regno = STACK_POINTER_REGNUM;
+   RPI (ENTRY_BLOCK_PTR)->out.cfa.offset = 0;
+ }
+ 
+ /* Free the data structures needed for variable tracking.  */
+ 
+ static void
+ regparam_finalize (void)
+ {
+   basic_block bb;
+ 
+   FOR_EACH_BB (bb)
+     {
+       free (RPI (bb)->opers);
+     }
+ 
+   FOR_ALL_BB (bb)
+     {
+       dataflow_set_destroy (&RPI (bb)->in);
+       dataflow_set_destroy (&RPI (bb)->out);
+     }
+   free_aux_for_blocks ();
+   free_alloc_pool (location_pool);
+ }
+ 
+ /* The entry point to variable tracking pass.  */
+ 
+ void
+ regparam_main (void)
+ {
+   regparam_initialize ();
+ 
+   if (rtl_dump_file)
+     {
+       basic_block bb;
+ 
+       FOR_EACH_BB (bb)
+ 	{
+ 	  fprintf (rtl_dump_file, "\nBasic block %d:\n", bb->index);
+ 	  dump_operations (bb);
+ 	}
+       fflush (rtl_dump_file);
+     }
+ 
+   find_locations ();
+   regparam_emit_notes ();
+ 
+   if (rtl_dump_file)
+     {
+       dump_dataflow_sets ();
+       dump_flow_info (rtl_dump_file);
+     }
+ 
+   regparam_finalize ();
+ }
Index: rtl.c
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/rtl.c,v
retrieving revision 1.128
diff -c -3 -p -r1.128 rtl.c
*** rtl.c	18 Oct 2003 18:45:15 -0000	1.128
--- rtl.c	30 Nov 2003 07:49:01 -0000
*************** const char * const note_insn_name[NOTE_I
*** 122,128 ****
    "NOTE_INSN_EH_REGION_BEG", "NOTE_INSN_EH_REGION_END",
    "NOTE_INSN_REPEATED_LINE_NUMBER",
    "NOTE_INSN_BASIC_BLOCK", "NOTE_INSN_EXPECTED_VALUE",
!   "NOTE_INSN_PREDICTION"
  };
  
  const char * const reg_note_name[] =
--- 122,128 ----
    "NOTE_INSN_EH_REGION_BEG", "NOTE_INSN_EH_REGION_END",
    "NOTE_INSN_REPEATED_LINE_NUMBER",
    "NOTE_INSN_BASIC_BLOCK", "NOTE_INSN_EXPECTED_VALUE",
!   "NOTE_INSN_PREDICTION", "NOTE_INSN_REG_LOCATION"
  };
  
  const char * const reg_note_name[] =
Index: rtl.def
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/rtl.def,v
retrieving revision 1.75
diff -c -3 -p -r1.75 rtl.def
*** rtl.def	24 Nov 2003 14:14:11 -0000	1.75
--- rtl.def	30 Nov 2003 07:18:34 -0000
*************** DEF_RTL_EXPR(SS_TRUNCATE, "ss_truncate",
*** 1213,1218 ****
--- 1213,1220 ----
  DEF_RTL_EXPR(US_TRUNCATE, "us_truncate", "e", '1')
  
  
+ /* Information about the location of register parameter.  */
+ DEF_RTL_EXPR(REG_LOCATION, "reg_location", "ie", 'x')
  /*
  Local variables:
  mode:c
Index: rtl.h
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/rtl.h,v
retrieving revision 1.439
diff -c -3 -p -r1.439 rtl.h
*** rtl.h	29 Nov 2003 01:13:43 -0000	1.439
--- rtl.h	30 Nov 2003 07:50:33 -0000
*************** extern const char * const reg_note_name[
*** 819,824 ****
--- 819,825 ----
  #define NOTE_EXPECTED_VALUE(INSN) XCEXP (INSN, 4, NOTE)
  #define NOTE_PREDICTION(INSN)   XCINT (INSN, 4, NOTE)
  #define NOTE_PRECONDITIONED(INSN)   XCINT (INSN, 4, NOTE)
+ #define NOTE_REG_LOCATION(INSN)	XCEXP (INSN, 4, NOTE)
  
  /* In a NOTE that is a line number, this is the line number.
     Other kinds of NOTEs are identified by negative numbers here.  */
*************** extern const char * const reg_note_name[
*** 834,839 ****
--- 835,844 ----
  #define NOTE_PREDICTION_FLAGS(INSN) (XCINT(INSN, 4, NOTE)&0xff)
  #define NOTE_PREDICT(ALG,FLAGS)     ((ALG<<8)+(FLAGS))
  
+ /* The register number and its location.  */
+ #define REG_LOCATION_REGNO(RL)	(XCINT (RL, 0, REG_LOCATION))
+ #define REG_LOCATION_LOC(RL)	(XCEXP (RL, 1, REG_LOCATION))
+ 
  /* Codes that appear in the NOTE_LINE_NUMBER field
     for kinds of notes that are not line numbers.
  
*************** enum insn_note
*** 917,922 ****
--- 922,930 ----
    /* Record a prediction.  Uses NOTE_PREDICTION.  */
    NOTE_INSN_PREDICTION,
  
+   /* The location of register parameter.  */
+   NOTE_INSN_REG_LOCATION,
+ 
    NOTE_INSN_MAX
  };
  
*************** extern void invert_br_probabilities (rtx
*** 2324,2328 ****
--- 2332,2339 ----
  extern bool expensive_function_p (int);
  /* In tracer.c */
  extern void tracer (void);
+ 
+ /* In regparam.c */
+ extern void regparam_main		PARAMS ((void));
  
  #endif /* ! GCC_RTL_H */
Index: timevar.def
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/timevar.def,v
retrieving revision 1.22
diff -c -3 -p -r1.22 timevar.def
*** timevar.def	21 Nov 2003 04:05:05 -0000	1.22
--- timevar.def	30 Nov 2003 07:51:23 -0000
*************** DEFTIMEVAR (TV_SHORTEN_BRANCH        , "
*** 94,99 ****
--- 94,100 ----
  DEFTIMEVAR (TV_REG_STACK             , "reg stack")
  DEFTIMEVAR (TV_FINAL                 , "final")
  DEFTIMEVAR (TV_SYMOUT                , "symout")
+ DEFTIMEVAR (TV_REGPARAM              , "regparam")
  
  /* Everything else in rest_of_compilation not included above.  */
  DEFTIMEVAR (TV_REST_OF_COMPILATION   , "rest of compilation")
Index: toplev.c
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/toplev.c,v
retrieving revision 1.845
diff -c -3 -p -r1.845 toplev.c
*** toplev.c	21 Nov 2003 04:05:05 -0000	1.845
--- toplev.c	30 Nov 2003 07:51:35 -0000
*************** static void rest_of_handle_reorder_block
*** 153,158 ****
--- 153,161 ----
  #ifdef STACK_REGS
  static void rest_of_handle_stack_regs (tree, rtx);
  #endif
+ #ifdef DWARF2_DEBUGGING_INFO
+ static void rest_of_handle_regparam (tree, rtx);
+ #endif
  static void rest_of_handle_machine_reorg (tree, rtx);
  #ifdef DELAY_SLOTS
  static void rest_of_handle_delay_slots (tree, rtx);
*************** enum dump_file_index
*** 282,287 ****
--- 285,291 ----
    DFI_branch_target_load,
    DFI_sched2,
    DFI_stack,
+   DFI_regparam,
    DFI_mach,
    DFI_dbr,
    DFI_MAX
*************** static struct dump_file_info dump_file[D
*** 333,338 ****
--- 337,343 ----
    { "btl",	'd', 1, 0, 0 }, /* Yes, duplicate enable switch.  */
    { "sched2",	'R', 1, 0, 0 },
    { "stack",	'k', 1, 0, 0 },
+   { "regparam",	'R', 1, 0, 0 },	/* Yes, duplicate enable switch.  */
    { "mach",	'M', 1, 0, 0 },
    { "dbr",	'd', 0, 0, 0 },
  };
*************** int flag_tracer = 0;
*** 966,971 ****
--- 971,979 ----
  
  int flag_unit_at_a_time = 0;
  
+ /* Nonzero if we should track values of register parameters.  */
+ int flag_regparam = 1;
+ 
  /* Values of the -falign-* flags: how much to align labels in code.
     0 means `use default', 1 means `don't align'.
     For each variable, there is an _log variant which is the power
*************** static const lang_independent_options f_
*** 1144,1149 ****
--- 1152,1158 ----
    {"single-precision-constant", &flag_single_precision_constant, 1 },
    {"time-report", &time_report, 1 },
    {"mem-report", &mem_report, 1 },
+   {"regparam", &flag_regparam, 1},
    { "trapv", &flag_trapv, 1 },
    { "wrapv", &flag_wrapv, 1 },
    { "new-ra", &flag_new_regalloc, 1 }
*************** rest_of_handle_stack_regs (tree decl, rt
*** 2146,2151 ****
--- 2155,2177 ----
  }
  #endif
  
+ #if defined (DWARF2_DEBUGGING_INFO)
+ /* Register parameter tracking - CFG based unwind info generator.  */
+ static void
+ rest_of_handle_regparam (tree decl, rtx insns)
+ {
+   /* Track the values of register paramenters.  */
+   timevar_push (TV_REGPARAM);
+   open_dump_file (DFI_regparam, decl);
+ 
+   regparam_main ();
+ 
+   close_dump_file (DFI_regparam, print_rtl_with_bb, get_insns ());
+   timevar_pop (TV_REGPARAM);
+ 
+   ggc_collect ();
+ }
+ #endif
  
  /* Machine independent reorg pass.  */
  static void
*************** rest_of_compilation (tree decl)
*** 3522,3527 ****
--- 3548,3558 ----
  
    compute_alignments ();
  
+ #ifdef DWARF2_DEBUGGING_INFO
+   if (flag_regparam == 3)
+     rest_of_handle_regparam (decl, insns);
+ #endif
+ 
    /* CFG is no longer maintained up-to-date.  */
    free_bb_for_insn ();
  
*************** process_options (void)
*** 4376,4381 ****
--- 4407,4420 ----
    else
      error ("target system does not support the \"%s\" debug format",
  	   debug_type_names[write_symbols]);
+ 
+   if (flag_regparam == 2)
+     {
+ #if defined (DWARF2_UNWIND_INFO)
+       if (dwarf2out_do_frame ())
+ 	flag_regparam = 3;
+ #endif
+     }
  
    /* If auxiliary info generation is desired, open the output file.
       This goes in the same directory as the source file--unlike
Index: unwind-dw2.c
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/unwind-dw2.c,v
retrieving revision 1.40
diff -c -3 -p -r1.40 unwind-dw2.c
*** unwind-dw2.c	1 Nov 2003 18:31:56 -0000	1.40
--- unwind-dw2.c	30 Nov 2003 07:18:35 -0000
*************** _Unwind_SetIP (struct _Unwind_Context *c
*** 274,279 ****
--- 274,295 ----
    context->ra = (void *) val;
  }
  
+ /* Return 1 if a value of register with index INDEX in unwinding context
+    CONTEXT is known and store the value to VALUE.  */
+ 
+ int
+ _Unwind_Get_Argument_Reg (struct _Unwind_Context *context, int index,
+ 			  _Unwind_Word *value)
+ {
+   if (context->reg[index])
+     {
+       *value = * (_Unwind_Word *) context->reg[index];
+       return 1;
+     }
+ 
+   return 0;
+ }
+ 
  void *
  _Unwind_GetLanguageSpecificData (struct _Unwind_Context *context)
  {
*************** execute_cfa_program (const unsigned char
*** 867,874 ****
--- 883,896 ----
  	  break;
  
  	case DW_CFA_undefined:
+ 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+ 	  fs->regs.reg[reg].how = REG_UNSAVED;
+ 	  break;
+ 
  	case DW_CFA_same_value:
  	  insn_ptr = read_uleb128 (insn_ptr, &reg);
+ 	  fs->regs.reg[reg].how = REG_SAVED_REG;
+ 	  fs->regs.reg[reg].loc.reg = reg;
  	  break;
  
  	case DW_CFA_nop:
*************** uw_frame_state_for (struct _Unwind_Conte
*** 1004,1009 ****
--- 1026,1032 ----
    const struct dwarf_fde *fde;
    const struct dwarf_cie *cie;
    const unsigned char *aug, *insn, *end;
+   int i;
  
    memset (fs, 0, sizeof (*fs));
    context->args_size = 0;
*************** uw_frame_state_for (struct _Unwind_Conte
*** 1029,1034 ****
--- 1052,1064 ----
      }
  
    fs->pc = context->bases.func;
+   for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i)
+     {
+       fs->regs.reg[i].how = REG_SAVED_REG;
+       fs->regs.reg[i].loc.reg = i;
+     }
+   fs->regs.reg[__builtin_dwarf_sp_column ()].how = REG_UNSAVED;
+   fs->regs.reg[__builtin_dwarf_sp_column ()].loc.reg = 0;
  
    cie = get_cie (fde);
    insn = extract_cie_info (cie, context, fs);
*************** uw_update_context_1 (struct _Unwind_Cont
*** 1201,1206 ****
--- 1231,1237 ----
      switch (fs->regs.reg[i].how)
        {
        case REG_UNSAVED:
+ 	context->reg[i] = 0;
  	break;
  
        case REG_SAVED_OFFSET:
Index: unwind.h
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/unwind.h,v
retrieving revision 1.13
diff -c -3 -p -r1.13 unwind.h
*** unwind.h	4 Sep 2003 10:47:45 -0000	1.13
--- unwind.h	30 Nov 2003 07:18:35 -0000
*************** extern void _Unwind_SetGR (struct _Unwin
*** 150,155 ****
--- 150,158 ----
  extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *);
  extern void _Unwind_SetIP (struct _Unwind_Context *, _Unwind_Ptr);
  
+ extern int _Unwind_Get_Argument_Reg (struct _Unwind_Context *, int,
+ 				     _Unwind_Word *);
+ 
  /* @@@ Retrieve the CFA of the given context.  */
  extern _Unwind_Word _Unwind_GetCFA (struct _Unwind_Context *);
  
Index: config/i386/i386.c
===================================================================
RCS file: /cvs/gcc-cvs/gcc/gcc/config/i386/i386.c,v
retrieving revision 1.620
diff -c -3 -p -r1.620 i386.c
*** config/i386/i386.c	22 Nov 2003 16:11:27 -0000	1.620
--- config/i386/i386.c	30 Nov 2003 07:18:35 -0000
*************** override_options (void)
*** 1125,1130 ****
--- 1125,1132 ----
  	flag_asynchronous_unwind_tables = 1;
        if (flag_pcc_struct_return == 2)
  	flag_pcc_struct_return = 0;
+       if (flag_regparam == 1)
+ 	flag_regparam = 2;
      }
    else
      {
*************** override_options (void)
*** 1134,1139 ****
--- 1136,1143 ----
  	flag_asynchronous_unwind_tables = 0;
        if (flag_pcc_struct_return == 2)
  	flag_pcc_struct_return = DEFAULT_PCC_STRUCT_RETURN;
+       if (flag_regparam == 1)
+ 	flag_regparam = 2;
      }
  
  #ifdef SUBTARGET_OVERRIDE_OPTIONS
*************** static void
*** 5235,5247 ****
  ix86_emit_restore_regs_using_mov (rtx pointer, int offset, int maybe_eh_return)
  {
    int regno;
  
    for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
      if (ix86_save_reg (regno, maybe_eh_return))
        {
! 	emit_move_insn (gen_rtx_REG (Pmode, regno),
! 			adjust_address (gen_rtx_MEM (Pmode, pointer),
! 					Pmode, offset));
  	offset += UNITS_PER_WORD;
        }
  }
--- 5239,5254 ----
  ix86_emit_restore_regs_using_mov (rtx pointer, int offset, int maybe_eh_return)
  {
    int regno;
+   rtx insn;
  
    for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
      if (ix86_save_reg (regno, maybe_eh_return))
        {
! 	insn = emit_move_insn (gen_rtx_REG (Pmode, regno),
! 			       adjust_address (gen_rtx_MEM (Pmode, pointer),
! 					       Pmode, offset));
! 	if (flag_regparam == 3)
! 	  RTX_FRAME_RELATED_P (insn) = 1;
  	offset += UNITS_PER_WORD;
        }
  }
*************** ix86_emit_restore_regs_using_mov (rtx po
*** 5251,5256 ****
--- 5258,5264 ----
  void
  ix86_expand_epilogue (int style)
  {
+   rtx insn;
    int regno;
    int sp_valid = !frame_pointer_needed || current_function_sp_is_unchanging;
    struct ix86_frame frame;
*************** ix86_expand_epilogue (int style)
*** 5365,5373 ****
  	if (ix86_save_reg (regno, false))
  	  {
  	    if (TARGET_64BIT)
! 	      emit_insn (gen_popdi1 (gen_rtx_REG (Pmode, regno)));
  	    else
! 	      emit_insn (gen_popsi1 (gen_rtx_REG (Pmode, regno)));
  	  }
        if (frame_pointer_needed)
  	{
--- 5373,5383 ----
  	if (ix86_save_reg (regno, false))
  	  {
  	    if (TARGET_64BIT)
! 	      insn = emit_insn (gen_popdi1 (gen_rtx_REG (Pmode, regno)));
  	    else
! 	      insn = emit_insn (gen_popsi1 (gen_rtx_REG (Pmode, regno)));
! 	    if (flag_regparam == 3)
! 	      RTX_FRAME_RELATED_P (insn) = 1;
  	  }
        if (frame_pointer_needed)
  	{



More information about the Gcc-patches mailing list