OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [gcc-4.2.2/] [gcc/] [var-tracking.c] - Diff between revs 154 and 816

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 154 Rev 816
/* Variable tracking routines for the GNU compiler.
/* Variable tracking routines for the GNU compiler.
   Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
   Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
 
 
   This file is part of GCC.
   This file is part of GCC.
 
 
   GCC is free software; you can redistribute it and/or modify it
   GCC is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   the Free Software Foundation; either version 3, or (at your option)
   any later version.
   any later version.
 
 
   GCC is distributed in the hope that it will be useful, but WITHOUT
   GCC is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
   License for more details.
   License for more details.
 
 
   You should have received a copy of the GNU General Public License
   You should have received a copy of the GNU General Public License
   along with GCC; see the file COPYING3.  If not see
   along with GCC; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */
   <http://www.gnu.org/licenses/>.  */
 
 
/* This file contains the variable tracking pass.  It computes where
/* This file contains the variable tracking pass.  It computes where
   variables are located (which registers or where in memory) at each position
   variables are located (which registers or where in memory) at each position
   in instruction stream and emits notes describing the locations.
   in instruction stream and emits notes describing the locations.
   Debug information (DWARF2 location lists) is finally generated from
   Debug information (DWARF2 location lists) is finally generated from
   these notes.
   these notes.
   With this debug information, it is possible to show variables
   With this debug information, it is possible to show variables
   even when debugging optimized code.
   even when debugging optimized code.
 
 
   How does the variable tracking pass work?
   How does the variable tracking pass work?
 
 
   First, it scans RTL code for uses, stores and clobbers (register/memory
   First, it scans RTL code for uses, stores and clobbers (register/memory
   references in instructions), for call insns and for stack adjustments
   references in instructions), for call insns and for stack adjustments
   separately for each basic block and saves them to an array of micro
   separately for each basic block and saves them to an array of micro
   operations.
   operations.
   The micro operations of one instruction are ordered so that
   The micro operations of one instruction are ordered so that
   pre-modifying stack adjustment < use < use with no var < call insn <
   pre-modifying stack adjustment < use < use with no var < call insn <
     < set < clobber < post-modifying stack adjustment
     < set < clobber < post-modifying stack adjustment
 
 
   Then, a forward dataflow analysis is performed to find out how locations
   Then, a forward dataflow analysis is performed to find out how locations
   of variables change through code and to propagate the variable locations
   of variables change through code and to propagate the variable locations
   along control flow graph.
   along control flow graph.
   The IN set for basic block BB is computed as a union of OUT sets of BB's
   The IN set for basic block BB is computed as a union of OUT sets of BB's
   predecessors, the OUT set for BB is copied from the IN set for BB and
   predecessors, the OUT set for BB is copied from the IN set for BB and
   is changed according to micro operations in BB.
   is changed according to micro operations in BB.
 
 
   The IN and OUT sets for basic blocks consist of a current stack adjustment
   The IN and OUT sets for basic blocks consist of a current stack adjustment
   (used for adjusting offset of variables addressed using stack pointer),
   (used for adjusting offset of variables addressed using stack pointer),
   the table of structures describing the locations of parts of a variable
   the table of structures describing the locations of parts of a variable
   and for each physical register a linked list for each physical register.
   and for each physical register a linked list for each physical register.
   The linked list is a list of variable parts stored in the register,
   The linked list is a list of variable parts stored in the register,
   i.e. it is a list of triplets (reg, decl, offset) where decl is
   i.e. it is a list of triplets (reg, decl, offset) where decl is
   REG_EXPR (reg) and offset is REG_OFFSET (reg).  The linked list is used for
   REG_EXPR (reg) and offset is REG_OFFSET (reg).  The linked list is used for
   effective deleting appropriate variable parts when we set or clobber the
   effective deleting appropriate variable parts when we set or clobber the
   register.
   register.
 
 
   There may be more than one variable part in a register.  The linked lists
   There may be more than one variable part in a register.  The linked lists
   should be pretty short so it is a good data structure here.
   should be pretty short so it is a good data structure here.
   For example in the following code, register allocator may assign same
   For example in the following code, register allocator may assign same
   register to variables A and B, and both of them are stored in the same
   register to variables A and B, and both of them are stored in the same
   register in CODE:
   register in CODE:
 
 
     if (cond)
     if (cond)
       set A;
       set A;
     else
     else
       set B;
       set B;
     CODE;
     CODE;
     if (cond)
     if (cond)
       use A;
       use A;
     else
     else
       use B;
       use B;
 
 
   Finally, the NOTE_INSN_VAR_LOCATION notes describing the variable locations
   Finally, the NOTE_INSN_VAR_LOCATION notes describing the variable locations
   are emitted to appropriate positions in RTL code.  Each such a note describes
   are emitted to appropriate positions in RTL code.  Each such a note describes
   the location of one variable at the point in instruction stream where the
   the location of one variable at the point in instruction stream where the
   note is.  There is no need to emit a note for each variable before each
   note is.  There is no need to emit a note for each variable before each
   instruction, we only emit these notes where the location of variable changes
   instruction, we only emit these notes where the location of variable changes
   (this means that we also emit notes for changes between the OUT set of the
   (this means that we also emit notes for changes between the OUT set of the
   previous block and the IN set of the current block).
   previous block and the IN set of the current block).
 
 
   The notes consist of two parts:
   The notes consist of two parts:
   1. the declaration (from REG_EXPR or MEM_EXPR)
   1. the declaration (from REG_EXPR or MEM_EXPR)
   2. the location of a variable - it is either a simple register/memory
   2. the location of a variable - it is either a simple register/memory
      reference (for simple variables, for example int),
      reference (for simple variables, for example int),
      or a parallel of register/memory references (for a large variables
      or a parallel of register/memory references (for a large variables
      which consist of several parts, for example long long).
      which consist of several parts, for example long long).
 
 
*/
*/
 
 
#include "config.h"
#include "config.h"
#include "system.h"
#include "system.h"
#include "coretypes.h"
#include "coretypes.h"
#include "tm.h"
#include "tm.h"
#include "rtl.h"
#include "rtl.h"
#include "tree.h"
#include "tree.h"
#include "hard-reg-set.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "basic-block.h"
#include "flags.h"
#include "flags.h"
#include "output.h"
#include "output.h"
#include "insn-config.h"
#include "insn-config.h"
#include "reload.h"
#include "reload.h"
#include "sbitmap.h"
#include "sbitmap.h"
#include "alloc-pool.h"
#include "alloc-pool.h"
#include "fibheap.h"
#include "fibheap.h"
#include "hashtab.h"
#include "hashtab.h"
#include "regs.h"
#include "regs.h"
#include "expr.h"
#include "expr.h"
#include "timevar.h"
#include "timevar.h"
#include "tree-pass.h"
#include "tree-pass.h"
 
 
/* Type of micro operation.  */
/* Type of micro operation.  */
enum micro_operation_type
enum micro_operation_type
{
{
  MO_USE,       /* Use location (REG or MEM).  */
  MO_USE,       /* Use location (REG or MEM).  */
  MO_USE_NO_VAR,/* Use location which is not associated with a variable
  MO_USE_NO_VAR,/* Use location which is not associated with a variable
                   or the variable is not trackable.  */
                   or the variable is not trackable.  */
  MO_SET,       /* Set location.  */
  MO_SET,       /* Set location.  */
  MO_COPY,      /* Copy the same portion of a variable from one
  MO_COPY,      /* Copy the same portion of a variable from one
                   location to another.  */
                   location to another.  */
  MO_CLOBBER,   /* Clobber location.  */
  MO_CLOBBER,   /* Clobber location.  */
  MO_CALL,      /* Call insn.  */
  MO_CALL,      /* Call insn.  */
  MO_ADJUST     /* Adjust stack pointer.  */
  MO_ADJUST     /* Adjust stack pointer.  */
};
};
 
 
/* Where shall the note be emitted?  BEFORE or AFTER the instruction.  */
/* Where shall the note be emitted?  BEFORE or AFTER the instruction.  */
enum emit_note_where
enum emit_note_where
{
{
  EMIT_NOTE_BEFORE_INSN,
  EMIT_NOTE_BEFORE_INSN,
  EMIT_NOTE_AFTER_INSN
  EMIT_NOTE_AFTER_INSN
};
};
 
 
/* Structure holding information about micro operation.  */
/* Structure holding information about micro operation.  */
typedef struct micro_operation_def
typedef struct micro_operation_def
{
{
  /* Type of micro operation.  */
  /* Type of micro operation.  */
  enum micro_operation_type type;
  enum micro_operation_type type;
 
 
  union {
  union {
    /* Location.  */
    /* Location.  */
    rtx loc;
    rtx loc;
 
 
    /* Stack adjustment.  */
    /* Stack adjustment.  */
    HOST_WIDE_INT adjust;
    HOST_WIDE_INT adjust;
  } u;
  } u;
 
 
  /* The instruction which the micro operation is in, for MO_USE,
  /* The instruction which the micro operation is in, for MO_USE,
     MO_USE_NO_VAR, MO_CALL and MO_ADJUST, or the subsequent
     MO_USE_NO_VAR, MO_CALL and MO_ADJUST, or the subsequent
     instruction or note in the original flow (before any var-tracking
     instruction or note in the original flow (before any var-tracking
     notes are inserted, to simplify emission of notes), for MO_SET
     notes are inserted, to simplify emission of notes), for MO_SET
     and MO_CLOBBER.  */
     and MO_CLOBBER.  */
  rtx insn;
  rtx insn;
} micro_operation;
} micro_operation;
 
 
/* Structure for passing some other parameters to function
/* Structure for passing some other parameters to function
   emit_note_insn_var_location.  */
   emit_note_insn_var_location.  */
typedef struct emit_note_data_def
typedef struct emit_note_data_def
{
{
  /* The instruction which the note will be emitted before/after.  */
  /* The instruction which the note will be emitted before/after.  */
  rtx insn;
  rtx insn;
 
 
  /* Where the note will be emitted (before/after insn)?  */
  /* Where the note will be emitted (before/after insn)?  */
  enum emit_note_where where;
  enum emit_note_where where;
} emit_note_data;
} emit_note_data;
 
 
/* Description of location of a part of a variable.  The content of a physical
/* Description of location of a part of a variable.  The content of a physical
   register is described by a chain of these structures.
   register is described by a chain of these structures.
   The chains are pretty short (usually 1 or 2 elements) and thus
   The chains are pretty short (usually 1 or 2 elements) and thus
   chain is the best data structure.  */
   chain is the best data structure.  */
typedef struct attrs_def
typedef struct attrs_def
{
{
  /* Pointer to next member of the list.  */
  /* Pointer to next member of the list.  */
  struct attrs_def *next;
  struct attrs_def *next;
 
 
  /* The rtx of register.  */
  /* The rtx of register.  */
  rtx loc;
  rtx loc;
 
 
  /* The declaration corresponding to LOC.  */
  /* The declaration corresponding to LOC.  */
  tree decl;
  tree decl;
 
 
  /* Offset from start of DECL.  */
  /* Offset from start of DECL.  */
  HOST_WIDE_INT offset;
  HOST_WIDE_INT offset;
} *attrs;
} *attrs;
 
 
/* Structure holding the IN or OUT set for a basic block.  */
/* Structure holding the IN or OUT set for a basic block.  */
typedef struct dataflow_set_def
typedef struct dataflow_set_def
{
{
  /* Adjustment of stack offset.  */
  /* Adjustment of stack offset.  */
  HOST_WIDE_INT stack_adjust;
  HOST_WIDE_INT stack_adjust;
 
 
  /* Attributes for registers (lists of attrs).  */
  /* Attributes for registers (lists of attrs).  */
  attrs regs[FIRST_PSEUDO_REGISTER];
  attrs regs[FIRST_PSEUDO_REGISTER];
 
 
  /* Variable locations.  */
  /* Variable locations.  */
  htab_t vars;
  htab_t vars;
} dataflow_set;
} dataflow_set;
 
 
/* The structure (one for each basic block) containing the information
/* The structure (one for each basic block) containing the information
   needed for variable tracking.  */
   needed for variable tracking.  */
typedef struct variable_tracking_info_def
typedef struct variable_tracking_info_def
{
{
  /* Number of micro operations stored in the MOS array.  */
  /* Number of micro operations stored in the MOS array.  */
  int n_mos;
  int n_mos;
 
 
  /* The array of micro operations.  */
  /* The array of micro operations.  */
  micro_operation *mos;
  micro_operation *mos;
 
 
  /* The IN and OUT set for dataflow analysis.  */
  /* The IN and OUT set for dataflow analysis.  */
  dataflow_set in;
  dataflow_set in;
  dataflow_set out;
  dataflow_set out;
 
 
  /* Has the block been visited in DFS?  */
  /* Has the block been visited in DFS?  */
  bool visited;
  bool visited;
} *variable_tracking_info;
} *variable_tracking_info;
 
 
/* Structure for chaining the locations.  */
/* Structure for chaining the locations.  */
typedef struct location_chain_def
typedef struct location_chain_def
{
{
  /* Next element in the chain.  */
  /* Next element in the chain.  */
  struct location_chain_def *next;
  struct location_chain_def *next;
 
 
  /* The location (REG or MEM).  */
  /* The location (REG or MEM).  */
  rtx loc;
  rtx loc;
} *location_chain;
} *location_chain;
 
 
/* Structure describing one part of variable.  */
/* Structure describing one part of variable.  */
typedef struct variable_part_def
typedef struct variable_part_def
{
{
  /* Chain of locations of the part.  */
  /* Chain of locations of the part.  */
  location_chain loc_chain;
  location_chain loc_chain;
 
 
  /* Location which was last emitted to location list.  */
  /* Location which was last emitted to location list.  */
  rtx cur_loc;
  rtx cur_loc;
 
 
  /* The offset in the variable.  */
  /* The offset in the variable.  */
  HOST_WIDE_INT offset;
  HOST_WIDE_INT offset;
} variable_part;
} variable_part;
 
 
/* Maximum number of location parts.  */
/* Maximum number of location parts.  */
#define MAX_VAR_PARTS 16
#define MAX_VAR_PARTS 16
 
 
/* Structure describing where the variable is located.  */
/* Structure describing where the variable is located.  */
typedef struct variable_def
typedef struct variable_def
{
{
  /* The declaration of the variable.  */
  /* The declaration of the variable.  */
  tree decl;
  tree decl;
 
 
  /* Reference count.  */
  /* Reference count.  */
  int refcount;
  int refcount;
 
 
  /* Number of variable parts.  */
  /* Number of variable parts.  */
  int n_var_parts;
  int n_var_parts;
 
 
  /* The variable parts.  */
  /* The variable parts.  */
  variable_part var_part[MAX_VAR_PARTS];
  variable_part var_part[MAX_VAR_PARTS];
} *variable;
} *variable;
 
 
/* Hash function for DECL for VARIABLE_HTAB.  */
/* Hash function for DECL for VARIABLE_HTAB.  */
#define VARIABLE_HASH_VAL(decl) (DECL_UID (decl))
#define VARIABLE_HASH_VAL(decl) (DECL_UID (decl))
 
 
/* Pointer to the BB's information specific to variable tracking pass.  */
/* Pointer to the BB's information specific to variable tracking pass.  */
#define VTI(BB) ((variable_tracking_info) (BB)->aux)
#define VTI(BB) ((variable_tracking_info) (BB)->aux)
 
 
/* Alloc pool for struct attrs_def.  */
/* Alloc pool for struct attrs_def.  */
static alloc_pool attrs_pool;
static alloc_pool attrs_pool;
 
 
/* Alloc pool for struct variable_def.  */
/* Alloc pool for struct variable_def.  */
static alloc_pool var_pool;
static alloc_pool var_pool;
 
 
/* Alloc pool for struct location_chain_def.  */
/* Alloc pool for struct location_chain_def.  */
static alloc_pool loc_chain_pool;
static alloc_pool loc_chain_pool;
 
 
/* Changed variables, notes will be emitted for them.  */
/* Changed variables, notes will be emitted for them.  */
static htab_t changed_variables;
static htab_t changed_variables;
 
 
/* Shall notes be emitted?  */
/* Shall notes be emitted?  */
static bool emit_notes;
static bool emit_notes;
 
 
/* Local function prototypes.  */
/* Local function prototypes.  */
static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
                                          HOST_WIDE_INT *);
                                          HOST_WIDE_INT *);
static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
                                               HOST_WIDE_INT *);
                                               HOST_WIDE_INT *);
static void bb_stack_adjust_offset (basic_block);
static void bb_stack_adjust_offset (basic_block);
static bool vt_stack_adjustments (void);
static bool vt_stack_adjustments (void);
static rtx adjust_stack_reference (rtx, HOST_WIDE_INT);
static rtx adjust_stack_reference (rtx, HOST_WIDE_INT);
static hashval_t variable_htab_hash (const void *);
static hashval_t variable_htab_hash (const void *);
static int variable_htab_eq (const void *, const void *);
static int variable_htab_eq (const void *, const void *);
static void variable_htab_free (void *);
static void variable_htab_free (void *);
 
 
static void init_attrs_list_set (attrs *);
static void init_attrs_list_set (attrs *);
static void attrs_list_clear (attrs *);
static void attrs_list_clear (attrs *);
static attrs attrs_list_member (attrs, tree, HOST_WIDE_INT);
static attrs attrs_list_member (attrs, tree, HOST_WIDE_INT);
static void attrs_list_insert (attrs *, tree, HOST_WIDE_INT, rtx);
static void attrs_list_insert (attrs *, tree, HOST_WIDE_INT, rtx);
static void attrs_list_copy (attrs *, attrs);
static void attrs_list_copy (attrs *, attrs);
static void attrs_list_union (attrs *, attrs);
static void attrs_list_union (attrs *, attrs);
 
 
static void vars_clear (htab_t);
static void vars_clear (htab_t);
static variable unshare_variable (dataflow_set *set, variable var);
static variable unshare_variable (dataflow_set *set, variable var);
static int vars_copy_1 (void **, void *);
static int vars_copy_1 (void **, void *);
static void vars_copy (htab_t, htab_t);
static void vars_copy (htab_t, htab_t);
static tree var_debug_decl (tree);
static tree var_debug_decl (tree);
static void var_reg_set (dataflow_set *, rtx);
static void var_reg_set (dataflow_set *, rtx);
static void var_reg_delete_and_set (dataflow_set *, rtx, bool);
static void var_reg_delete_and_set (dataflow_set *, rtx, bool);
static void var_reg_delete (dataflow_set *, rtx, bool);
static void var_reg_delete (dataflow_set *, rtx, bool);
static void var_regno_delete (dataflow_set *, int);
static void var_regno_delete (dataflow_set *, int);
static void var_mem_set (dataflow_set *, rtx);
static void var_mem_set (dataflow_set *, rtx);
static void var_mem_delete_and_set (dataflow_set *, rtx, bool);
static void var_mem_delete_and_set (dataflow_set *, rtx, bool);
static void var_mem_delete (dataflow_set *, rtx, bool);
static void var_mem_delete (dataflow_set *, rtx, bool);
 
 
static void dataflow_set_init (dataflow_set *, int);
static void dataflow_set_init (dataflow_set *, int);
static void dataflow_set_clear (dataflow_set *);
static void dataflow_set_clear (dataflow_set *);
static void dataflow_set_copy (dataflow_set *, dataflow_set *);
static void dataflow_set_copy (dataflow_set *, dataflow_set *);
static int variable_union_info_cmp_pos (const void *, const void *);
static int variable_union_info_cmp_pos (const void *, const void *);
static int variable_union (void **, void *);
static int variable_union (void **, void *);
static void dataflow_set_union (dataflow_set *, dataflow_set *);
static void dataflow_set_union (dataflow_set *, dataflow_set *);
static bool variable_part_different_p (variable_part *, variable_part *);
static bool variable_part_different_p (variable_part *, variable_part *);
static bool variable_different_p (variable, variable, bool);
static bool variable_different_p (variable, variable, bool);
static int dataflow_set_different_1 (void **, void *);
static int dataflow_set_different_1 (void **, void *);
static int dataflow_set_different_2 (void **, void *);
static int dataflow_set_different_2 (void **, void *);
static bool dataflow_set_different (dataflow_set *, dataflow_set *);
static bool dataflow_set_different (dataflow_set *, dataflow_set *);
static void dataflow_set_destroy (dataflow_set *);
static void dataflow_set_destroy (dataflow_set *);
 
 
static bool contains_symbol_ref (rtx);
static bool contains_symbol_ref (rtx);
static bool track_expr_p (tree);
static bool track_expr_p (tree);
static bool same_variable_part_p (rtx, tree, HOST_WIDE_INT);
static bool same_variable_part_p (rtx, tree, HOST_WIDE_INT);
static int count_uses (rtx *, void *);
static int count_uses (rtx *, void *);
static void count_uses_1 (rtx *, void *);
static void count_uses_1 (rtx *, void *);
static void count_stores (rtx, rtx, void *);
static void count_stores (rtx, rtx, void *);
static int add_uses (rtx *, void *);
static int add_uses (rtx *, void *);
static void add_uses_1 (rtx *, void *);
static void add_uses_1 (rtx *, void *);
static void add_stores (rtx, rtx, void *);
static void add_stores (rtx, rtx, void *);
static bool compute_bb_dataflow (basic_block);
static bool compute_bb_dataflow (basic_block);
static void vt_find_locations (void);
static void vt_find_locations (void);
 
 
static void dump_attrs_list (attrs);
static void dump_attrs_list (attrs);
static int dump_variable (void **, void *);
static int dump_variable (void **, void *);
static void dump_vars (htab_t);
static void dump_vars (htab_t);
static void dump_dataflow_set (dataflow_set *);
static void dump_dataflow_set (dataflow_set *);
static void dump_dataflow_sets (void);
static void dump_dataflow_sets (void);
 
 
static void variable_was_changed (variable, htab_t);
static void variable_was_changed (variable, htab_t);
static void set_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
static void set_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
static void clobber_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
static void clobber_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
static void delete_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
static void delete_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
static int emit_note_insn_var_location (void **, void *);
static int emit_note_insn_var_location (void **, void *);
static void emit_notes_for_changes (rtx, enum emit_note_where);
static void emit_notes_for_changes (rtx, enum emit_note_where);
static int emit_notes_for_differences_1 (void **, void *);
static int emit_notes_for_differences_1 (void **, void *);
static int emit_notes_for_differences_2 (void **, void *);
static int emit_notes_for_differences_2 (void **, void *);
static void emit_notes_for_differences (rtx, dataflow_set *, dataflow_set *);
static void emit_notes_for_differences (rtx, dataflow_set *, dataflow_set *);
static void emit_notes_in_bb (basic_block);
static void emit_notes_in_bb (basic_block);
static void vt_emit_notes (void);
static void vt_emit_notes (void);
 
 
static bool vt_get_decl_and_offset (rtx, tree *, HOST_WIDE_INT *);
static bool vt_get_decl_and_offset (rtx, tree *, HOST_WIDE_INT *);
static void vt_add_function_parameters (void);
static void vt_add_function_parameters (void);
static void vt_initialize (void);
static void vt_initialize (void);
static void vt_finalize (void);
static void vt_finalize (void);
 
 
/* Given a SET, calculate the amount of stack adjustment it contains
/* Given a SET, calculate the amount of stack adjustment it contains
   PRE- and POST-modifying stack pointer.
   PRE- and POST-modifying stack pointer.
   This function is similar to stack_adjust_offset.  */
   This function is similar to stack_adjust_offset.  */
 
 
static void
static void
stack_adjust_offset_pre_post (rtx pattern, HOST_WIDE_INT *pre,
stack_adjust_offset_pre_post (rtx pattern, HOST_WIDE_INT *pre,
                              HOST_WIDE_INT *post)
                              HOST_WIDE_INT *post)
{
{
  rtx src = SET_SRC (pattern);
  rtx src = SET_SRC (pattern);
  rtx dest = SET_DEST (pattern);
  rtx dest = SET_DEST (pattern);
  enum rtx_code code;
  enum rtx_code code;
 
 
  if (dest == stack_pointer_rtx)
  if (dest == stack_pointer_rtx)
    {
    {
      /* (set (reg sp) (plus (reg sp) (const_int))) */
      /* (set (reg sp) (plus (reg sp) (const_int))) */
      code = GET_CODE (src);
      code = GET_CODE (src);
      if (! (code == PLUS || code == MINUS)
      if (! (code == PLUS || code == MINUS)
          || XEXP (src, 0) != stack_pointer_rtx
          || XEXP (src, 0) != stack_pointer_rtx
          || GET_CODE (XEXP (src, 1)) != CONST_INT)
          || GET_CODE (XEXP (src, 1)) != CONST_INT)
        return;
        return;
 
 
      if (code == MINUS)
      if (code == MINUS)
        *post += INTVAL (XEXP (src, 1));
        *post += INTVAL (XEXP (src, 1));
      else
      else
        *post -= INTVAL (XEXP (src, 1));
        *post -= INTVAL (XEXP (src, 1));
    }
    }
  else if (MEM_P (dest))
  else if (MEM_P (dest))
    {
    {
      /* (set (mem (pre_dec (reg sp))) (foo)) */
      /* (set (mem (pre_dec (reg sp))) (foo)) */
      src = XEXP (dest, 0);
      src = XEXP (dest, 0);
      code = GET_CODE (src);
      code = GET_CODE (src);
 
 
      switch (code)
      switch (code)
        {
        {
        case PRE_MODIFY:
        case PRE_MODIFY:
        case POST_MODIFY:
        case POST_MODIFY:
          if (XEXP (src, 0) == stack_pointer_rtx)
          if (XEXP (src, 0) == stack_pointer_rtx)
            {
            {
              rtx val = XEXP (XEXP (src, 1), 1);
              rtx val = XEXP (XEXP (src, 1), 1);
              /* We handle only adjustments by constant amount.  */
              /* We handle only adjustments by constant amount.  */
              gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS &&
              gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS &&
                          GET_CODE (val) == CONST_INT);
                          GET_CODE (val) == CONST_INT);
 
 
              if (code == PRE_MODIFY)
              if (code == PRE_MODIFY)
                *pre -= INTVAL (val);
                *pre -= INTVAL (val);
              else
              else
                *post -= INTVAL (val);
                *post -= INTVAL (val);
              break;
              break;
            }
            }
          return;
          return;
 
 
        case PRE_DEC:
        case PRE_DEC:
          if (XEXP (src, 0) == stack_pointer_rtx)
          if (XEXP (src, 0) == stack_pointer_rtx)
            {
            {
              *pre += GET_MODE_SIZE (GET_MODE (dest));
              *pre += GET_MODE_SIZE (GET_MODE (dest));
              break;
              break;
            }
            }
          return;
          return;
 
 
        case POST_DEC:
        case POST_DEC:
          if (XEXP (src, 0) == stack_pointer_rtx)
          if (XEXP (src, 0) == stack_pointer_rtx)
            {
            {
              *post += GET_MODE_SIZE (GET_MODE (dest));
              *post += GET_MODE_SIZE (GET_MODE (dest));
              break;
              break;
            }
            }
          return;
          return;
 
 
        case PRE_INC:
        case PRE_INC:
          if (XEXP (src, 0) == stack_pointer_rtx)
          if (XEXP (src, 0) == stack_pointer_rtx)
            {
            {
              *pre -= GET_MODE_SIZE (GET_MODE (dest));
              *pre -= GET_MODE_SIZE (GET_MODE (dest));
              break;
              break;
            }
            }
          return;
          return;
 
 
        case POST_INC:
        case POST_INC:
          if (XEXP (src, 0) == stack_pointer_rtx)
          if (XEXP (src, 0) == stack_pointer_rtx)
            {
            {
              *post -= GET_MODE_SIZE (GET_MODE (dest));
              *post -= GET_MODE_SIZE (GET_MODE (dest));
              break;
              break;
            }
            }
          return;
          return;
 
 
        default:
        default:
          return;
          return;
        }
        }
    }
    }
}
}
 
 
/* Given an INSN, calculate the amount of stack adjustment it contains
/* Given an INSN, calculate the amount of stack adjustment it contains
   PRE- and POST-modifying stack pointer.  */
   PRE- and POST-modifying stack pointer.  */
 
 
static void
static void
insn_stack_adjust_offset_pre_post (rtx insn, HOST_WIDE_INT *pre,
insn_stack_adjust_offset_pre_post (rtx insn, HOST_WIDE_INT *pre,
                                   HOST_WIDE_INT *post)
                                   HOST_WIDE_INT *post)
{
{
  *pre = 0;
  *pre = 0;
  *post = 0;
  *post = 0;
 
 
  if (GET_CODE (PATTERN (insn)) == SET)
  if (GET_CODE (PATTERN (insn)) == SET)
    stack_adjust_offset_pre_post (PATTERN (insn), pre, post);
    stack_adjust_offset_pre_post (PATTERN (insn), pre, post);
  else if (GET_CODE (PATTERN (insn)) == PARALLEL
  else if (GET_CODE (PATTERN (insn)) == PARALLEL
           || GET_CODE (PATTERN (insn)) == SEQUENCE)
           || GET_CODE (PATTERN (insn)) == SEQUENCE)
    {
    {
      int i;
      int i;
 
 
      /* There may be stack adjustments inside compound insns.  Search
      /* There may be stack adjustments inside compound insns.  Search
         for them.  */
         for them.  */
      for ( i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
      for ( i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
        if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
        if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
          stack_adjust_offset_pre_post (XVECEXP (PATTERN (insn), 0, i),
          stack_adjust_offset_pre_post (XVECEXP (PATTERN (insn), 0, i),
                                        pre, post);
                                        pre, post);
    }
    }
}
}
 
 
/* Compute stack adjustment in basic block BB.  */
/* Compute stack adjustment in basic block BB.  */
 
 
static void
static void
bb_stack_adjust_offset (basic_block bb)
bb_stack_adjust_offset (basic_block bb)
{
{
  HOST_WIDE_INT offset;
  HOST_WIDE_INT offset;
  int i;
  int i;
 
 
  offset = VTI (bb)->in.stack_adjust;
  offset = VTI (bb)->in.stack_adjust;
  for (i = 0; i < VTI (bb)->n_mos; i++)
  for (i = 0; i < VTI (bb)->n_mos; i++)
    {
    {
      if (VTI (bb)->mos[i].type == MO_ADJUST)
      if (VTI (bb)->mos[i].type == MO_ADJUST)
        offset += VTI (bb)->mos[i].u.adjust;
        offset += VTI (bb)->mos[i].u.adjust;
      else if (VTI (bb)->mos[i].type != MO_CALL)
      else if (VTI (bb)->mos[i].type != MO_CALL)
        {
        {
          if (MEM_P (VTI (bb)->mos[i].u.loc))
          if (MEM_P (VTI (bb)->mos[i].u.loc))
            {
            {
              VTI (bb)->mos[i].u.loc
              VTI (bb)->mos[i].u.loc
                = adjust_stack_reference (VTI (bb)->mos[i].u.loc, -offset);
                = adjust_stack_reference (VTI (bb)->mos[i].u.loc, -offset);
            }
            }
        }
        }
    }
    }
  VTI (bb)->out.stack_adjust = offset;
  VTI (bb)->out.stack_adjust = offset;
}
}
 
 
/* Compute stack adjustments for all blocks by traversing DFS tree.
/* Compute stack adjustments for all blocks by traversing DFS tree.
   Return true when the adjustments on all incoming edges are consistent.
   Return true when the adjustments on all incoming edges are consistent.
   Heavily borrowed from pre_and_rev_post_order_compute.  */
   Heavily borrowed from pre_and_rev_post_order_compute.  */
 
 
static bool
static bool
vt_stack_adjustments (void)
vt_stack_adjustments (void)
{
{
  edge_iterator *stack;
  edge_iterator *stack;
  int sp;
  int sp;
 
 
  /* Initialize entry block.  */
  /* Initialize entry block.  */
  VTI (ENTRY_BLOCK_PTR)->visited = true;
  VTI (ENTRY_BLOCK_PTR)->visited = true;
  VTI (ENTRY_BLOCK_PTR)->out.stack_adjust = INCOMING_FRAME_SP_OFFSET;
  VTI (ENTRY_BLOCK_PTR)->out.stack_adjust = INCOMING_FRAME_SP_OFFSET;
 
 
  /* Allocate stack for back-tracking up CFG.  */
  /* Allocate stack for back-tracking up CFG.  */
  stack = XNEWVEC (edge_iterator, n_basic_blocks + 1);
  stack = XNEWVEC (edge_iterator, n_basic_blocks + 1);
  sp = 0;
  sp = 0;
 
 
  /* Push the first edge on to the stack.  */
  /* Push the first edge on to the stack.  */
  stack[sp++] = ei_start (ENTRY_BLOCK_PTR->succs);
  stack[sp++] = ei_start (ENTRY_BLOCK_PTR->succs);
 
 
  while (sp)
  while (sp)
    {
    {
      edge_iterator ei;
      edge_iterator ei;
      basic_block src;
      basic_block src;
      basic_block dest;
      basic_block dest;
 
 
      /* Look at the edge on the top of the stack.  */
      /* Look at the edge on the top of the stack.  */
      ei = stack[sp - 1];
      ei = stack[sp - 1];
      src = ei_edge (ei)->src;
      src = ei_edge (ei)->src;
      dest = ei_edge (ei)->dest;
      dest = ei_edge (ei)->dest;
 
 
      /* Check if the edge destination has been visited yet.  */
      /* Check if the edge destination has been visited yet.  */
      if (!VTI (dest)->visited)
      if (!VTI (dest)->visited)
        {
        {
          VTI (dest)->visited = true;
          VTI (dest)->visited = true;
          VTI (dest)->in.stack_adjust = VTI (src)->out.stack_adjust;
          VTI (dest)->in.stack_adjust = VTI (src)->out.stack_adjust;
          bb_stack_adjust_offset (dest);
          bb_stack_adjust_offset (dest);
 
 
          if (EDGE_COUNT (dest->succs) > 0)
          if (EDGE_COUNT (dest->succs) > 0)
            /* Since the DEST node has been visited for the first
            /* Since the DEST node has been visited for the first
               time, check its successors.  */
               time, check its successors.  */
            stack[sp++] = ei_start (dest->succs);
            stack[sp++] = ei_start (dest->succs);
        }
        }
      else
      else
        {
        {
          /* Check whether the adjustments on the edges are the same.  */
          /* Check whether the adjustments on the edges are the same.  */
          if (VTI (dest)->in.stack_adjust != VTI (src)->out.stack_adjust)
          if (VTI (dest)->in.stack_adjust != VTI (src)->out.stack_adjust)
            {
            {
              free (stack);
              free (stack);
              return false;
              return false;
            }
            }
 
 
          if (! ei_one_before_end_p (ei))
          if (! ei_one_before_end_p (ei))
            /* Go to the next edge.  */
            /* Go to the next edge.  */
            ei_next (&stack[sp - 1]);
            ei_next (&stack[sp - 1]);
          else
          else
            /* Return to previous level if there are no more edges.  */
            /* Return to previous level if there are no more edges.  */
            sp--;
            sp--;
        }
        }
    }
    }
 
 
  free (stack);
  free (stack);
  return true;
  return true;
}
}
 
 
/* Adjust stack reference MEM by ADJUSTMENT bytes and make it relative
/* Adjust stack reference MEM by ADJUSTMENT bytes and make it relative
   to the argument pointer.  Return the new rtx.  */
   to the argument pointer.  Return the new rtx.  */
 
 
static rtx
static rtx
adjust_stack_reference (rtx mem, HOST_WIDE_INT adjustment)
adjust_stack_reference (rtx mem, HOST_WIDE_INT adjustment)
{
{
  rtx addr, cfa, tmp;
  rtx addr, cfa, tmp;
 
 
#ifdef FRAME_POINTER_CFA_OFFSET
#ifdef FRAME_POINTER_CFA_OFFSET
  adjustment -= FRAME_POINTER_CFA_OFFSET (current_function_decl);
  adjustment -= FRAME_POINTER_CFA_OFFSET (current_function_decl);
  cfa = plus_constant (frame_pointer_rtx, adjustment);
  cfa = plus_constant (frame_pointer_rtx, adjustment);
#else
#else
  adjustment -= ARG_POINTER_CFA_OFFSET (current_function_decl);
  adjustment -= ARG_POINTER_CFA_OFFSET (current_function_decl);
  cfa = plus_constant (arg_pointer_rtx, adjustment);
  cfa = plus_constant (arg_pointer_rtx, adjustment);
#endif
#endif
 
 
  addr = replace_rtx (copy_rtx (XEXP (mem, 0)), stack_pointer_rtx, cfa);
  addr = replace_rtx (copy_rtx (XEXP (mem, 0)), stack_pointer_rtx, cfa);
  tmp = simplify_rtx (addr);
  tmp = simplify_rtx (addr);
  if (tmp)
  if (tmp)
    addr = tmp;
    addr = tmp;
 
 
  return replace_equiv_address_nv (mem, addr);
  return replace_equiv_address_nv (mem, addr);
}
}
 
 
/* The hash function for variable_htab, computes the hash value
/* The hash function for variable_htab, computes the hash value
   from the declaration of variable X.  */
   from the declaration of variable X.  */
 
 
static hashval_t
static hashval_t
variable_htab_hash (const void *x)
variable_htab_hash (const void *x)
{
{
  const variable v = (const variable) x;
  const variable v = (const variable) x;
 
 
  return (VARIABLE_HASH_VAL (v->decl));
  return (VARIABLE_HASH_VAL (v->decl));
}
}
 
 
/* Compare the declaration of variable X with declaration Y.  */
/* Compare the declaration of variable X with declaration Y.  */
 
 
static int
static int
variable_htab_eq (const void *x, const void *y)
variable_htab_eq (const void *x, const void *y)
{
{
  const variable v = (const variable) x;
  const variable v = (const variable) x;
  const tree decl = (const tree) y;
  const tree decl = (const tree) y;
 
 
  return (VARIABLE_HASH_VAL (v->decl) == VARIABLE_HASH_VAL (decl));
  return (VARIABLE_HASH_VAL (v->decl) == VARIABLE_HASH_VAL (decl));
}
}
 
 
/* Free the element of VARIABLE_HTAB (its type is struct variable_def).  */
/* Free the element of VARIABLE_HTAB (its type is struct variable_def).  */
 
 
static void
static void
variable_htab_free (void *elem)
variable_htab_free (void *elem)
{
{
  int i;
  int i;
  variable var = (variable) elem;
  variable var = (variable) elem;
  location_chain node, next;
  location_chain node, next;
 
 
  gcc_assert (var->refcount > 0);
  gcc_assert (var->refcount > 0);
 
 
  var->refcount--;
  var->refcount--;
  if (var->refcount > 0)
  if (var->refcount > 0)
    return;
    return;
 
 
  for (i = 0; i < var->n_var_parts; i++)
  for (i = 0; i < var->n_var_parts; i++)
    {
    {
      for (node = var->var_part[i].loc_chain; node; node = next)
      for (node = var->var_part[i].loc_chain; node; node = next)
        {
        {
          next = node->next;
          next = node->next;
          pool_free (loc_chain_pool, node);
          pool_free (loc_chain_pool, node);
        }
        }
      var->var_part[i].loc_chain = NULL;
      var->var_part[i].loc_chain = NULL;
    }
    }
  pool_free (var_pool, var);
  pool_free (var_pool, var);
}
}
 
 
/* Initialize the set (array) SET of attrs to empty lists.  */
/* Initialize the set (array) SET of attrs to empty lists.  */
 
 
static void
static void
init_attrs_list_set (attrs *set)
init_attrs_list_set (attrs *set)
{
{
  int i;
  int i;
 
 
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
    set[i] = NULL;
    set[i] = NULL;
}
}
 
 
/* Make the list *LISTP empty.  */
/* Make the list *LISTP empty.  */
 
 
static void
static void
attrs_list_clear (attrs *listp)
attrs_list_clear (attrs *listp)
{
{
  attrs list, next;
  attrs list, next;
 
 
  for (list = *listp; list; list = next)
  for (list = *listp; list; list = next)
    {
    {
      next = list->next;
      next = list->next;
      pool_free (attrs_pool, list);
      pool_free (attrs_pool, list);
    }
    }
  *listp = NULL;
  *listp = NULL;
}
}
 
 
/* Return true if the pair of DECL and OFFSET is the member of the LIST.  */
/* Return true if the pair of DECL and OFFSET is the member of the LIST.  */
 
 
static attrs
static attrs
attrs_list_member (attrs list, tree decl, HOST_WIDE_INT offset)
attrs_list_member (attrs list, tree decl, HOST_WIDE_INT offset)
{
{
  for (; list; list = list->next)
  for (; list; list = list->next)
    if (list->decl == decl && list->offset == offset)
    if (list->decl == decl && list->offset == offset)
      return list;
      return list;
  return NULL;
  return NULL;
}
}
 
 
/* Insert the triplet DECL, OFFSET, LOC to the list *LISTP.  */
/* Insert the triplet DECL, OFFSET, LOC to the list *LISTP.  */
 
 
static void
static void
attrs_list_insert (attrs *listp, tree decl, HOST_WIDE_INT offset, rtx loc)
attrs_list_insert (attrs *listp, tree decl, HOST_WIDE_INT offset, rtx loc)
{
{
  attrs list;
  attrs list;
 
 
  list = pool_alloc (attrs_pool);
  list = pool_alloc (attrs_pool);
  list->loc = loc;
  list->loc = loc;
  list->decl = decl;
  list->decl = decl;
  list->offset = offset;
  list->offset = offset;
  list->next = *listp;
  list->next = *listp;
  *listp = list;
  *listp = list;
}
}
 
 
/* Copy all nodes from SRC and create a list *DSTP of the copies.  */
/* Copy all nodes from SRC and create a list *DSTP of the copies.  */
 
 
static void
static void
attrs_list_copy (attrs *dstp, attrs src)
attrs_list_copy (attrs *dstp, attrs src)
{
{
  attrs n;
  attrs n;
 
 
  attrs_list_clear (dstp);
  attrs_list_clear (dstp);
  for (; src; src = src->next)
  for (; src; src = src->next)
    {
    {
      n = pool_alloc (attrs_pool);
      n = pool_alloc (attrs_pool);
      n->loc = src->loc;
      n->loc = src->loc;
      n->decl = src->decl;
      n->decl = src->decl;
      n->offset = src->offset;
      n->offset = src->offset;
      n->next = *dstp;
      n->next = *dstp;
      *dstp = n;
      *dstp = n;
    }
    }
}
}
 
 
/* Add all nodes from SRC which are not in *DSTP to *DSTP.  */
/* Add all nodes from SRC which are not in *DSTP to *DSTP.  */
 
 
static void
static void
attrs_list_union (attrs *dstp, attrs src)
attrs_list_union (attrs *dstp, attrs src)
{
{
  for (; src; src = src->next)
  for (; src; src = src->next)
    {
    {
      if (!attrs_list_member (*dstp, src->decl, src->offset))
      if (!attrs_list_member (*dstp, src->decl, src->offset))
        attrs_list_insert (dstp, src->decl, src->offset, src->loc);
        attrs_list_insert (dstp, src->decl, src->offset, src->loc);
    }
    }
}
}
 
 
/* Delete all variables from hash table VARS.  */
/* Delete all variables from hash table VARS.  */
 
 
static void
static void
vars_clear (htab_t vars)
vars_clear (htab_t vars)
{
{
  htab_empty (vars);
  htab_empty (vars);
}
}
 
 
/* Return a copy of a variable VAR and insert it to dataflow set SET.  */
/* Return a copy of a variable VAR and insert it to dataflow set SET.  */
 
 
static variable
static variable
unshare_variable (dataflow_set *set, variable var)
unshare_variable (dataflow_set *set, variable var)
{
{
  void **slot;
  void **slot;
  variable new_var;
  variable new_var;
  int i;
  int i;
 
 
  new_var = pool_alloc (var_pool);
  new_var = pool_alloc (var_pool);
  new_var->decl = var->decl;
  new_var->decl = var->decl;
  new_var->refcount = 1;
  new_var->refcount = 1;
  var->refcount--;
  var->refcount--;
  new_var->n_var_parts = var->n_var_parts;
  new_var->n_var_parts = var->n_var_parts;
 
 
  for (i = 0; i < var->n_var_parts; i++)
  for (i = 0; i < var->n_var_parts; i++)
    {
    {
      location_chain node;
      location_chain node;
      location_chain *nextp;
      location_chain *nextp;
 
 
      new_var->var_part[i].offset = var->var_part[i].offset;
      new_var->var_part[i].offset = var->var_part[i].offset;
      nextp = &new_var->var_part[i].loc_chain;
      nextp = &new_var->var_part[i].loc_chain;
      for (node = var->var_part[i].loc_chain; node; node = node->next)
      for (node = var->var_part[i].loc_chain; node; node = node->next)
        {
        {
          location_chain new_lc;
          location_chain new_lc;
 
 
          new_lc = pool_alloc (loc_chain_pool);
          new_lc = pool_alloc (loc_chain_pool);
          new_lc->next = NULL;
          new_lc->next = NULL;
          new_lc->loc = node->loc;
          new_lc->loc = node->loc;
 
 
          *nextp = new_lc;
          *nextp = new_lc;
          nextp = &new_lc->next;
          nextp = &new_lc->next;
        }
        }
 
 
      /* We are at the basic block boundary when copying variable description
      /* We are at the basic block boundary when copying variable description
         so set the CUR_LOC to be the first element of the chain.  */
         so set the CUR_LOC to be the first element of the chain.  */
      if (new_var->var_part[i].loc_chain)
      if (new_var->var_part[i].loc_chain)
        new_var->var_part[i].cur_loc = new_var->var_part[i].loc_chain->loc;
        new_var->var_part[i].cur_loc = new_var->var_part[i].loc_chain->loc;
      else
      else
        new_var->var_part[i].cur_loc = NULL;
        new_var->var_part[i].cur_loc = NULL;
    }
    }
 
 
  slot = htab_find_slot_with_hash (set->vars, new_var->decl,
  slot = htab_find_slot_with_hash (set->vars, new_var->decl,
                                   VARIABLE_HASH_VAL (new_var->decl),
                                   VARIABLE_HASH_VAL (new_var->decl),
                                   INSERT);
                                   INSERT);
  *slot = new_var;
  *slot = new_var;
  return new_var;
  return new_var;
}
}
 
 
/* Add a variable from *SLOT to hash table DATA and increase its reference
/* Add a variable from *SLOT to hash table DATA and increase its reference
   count.  */
   count.  */
 
 
static int
static int
vars_copy_1 (void **slot, void *data)
vars_copy_1 (void **slot, void *data)
{
{
  htab_t dst = (htab_t) data;
  htab_t dst = (htab_t) data;
  variable src, *dstp;
  variable src, *dstp;
 
 
  src = *(variable *) slot;
  src = *(variable *) slot;
  src->refcount++;
  src->refcount++;
 
 
  dstp = (variable *) htab_find_slot_with_hash (dst, src->decl,
  dstp = (variable *) htab_find_slot_with_hash (dst, src->decl,
                                                VARIABLE_HASH_VAL (src->decl),
                                                VARIABLE_HASH_VAL (src->decl),
                                                INSERT);
                                                INSERT);
  *dstp = src;
  *dstp = src;
 
 
  /* Continue traversing the hash table.  */
  /* Continue traversing the hash table.  */
  return 1;
  return 1;
}
}
 
 
/* Copy all variables from hash table SRC to hash table DST.  */
/* Copy all variables from hash table SRC to hash table DST.  */
 
 
static void
static void
vars_copy (htab_t dst, htab_t src)
vars_copy (htab_t dst, htab_t src)
{
{
  vars_clear (dst);
  vars_clear (dst);
  htab_traverse (src, vars_copy_1, dst);
  htab_traverse (src, vars_copy_1, dst);
}
}
 
 
/* Map a decl to its main debug decl.  */
/* Map a decl to its main debug decl.  */
 
 
static inline tree
static inline tree
var_debug_decl (tree decl)
var_debug_decl (tree decl)
{
{
  if (decl && DECL_P (decl)
  if (decl && DECL_P (decl)
      && DECL_DEBUG_EXPR_IS_FROM (decl) && DECL_DEBUG_EXPR (decl)
      && DECL_DEBUG_EXPR_IS_FROM (decl) && DECL_DEBUG_EXPR (decl)
      && DECL_P (DECL_DEBUG_EXPR (decl)))
      && DECL_P (DECL_DEBUG_EXPR (decl)))
    decl = DECL_DEBUG_EXPR (decl);
    decl = DECL_DEBUG_EXPR (decl);
 
 
  return decl;
  return decl;
}
}
 
 
/* Set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  */
/* Set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  */
 
 
static void
static void
var_reg_set (dataflow_set *set, rtx loc)
var_reg_set (dataflow_set *set, rtx loc)
{
{
  tree decl = REG_EXPR (loc);
  tree decl = REG_EXPR (loc);
  HOST_WIDE_INT offset = REG_OFFSET (loc);
  HOST_WIDE_INT offset = REG_OFFSET (loc);
  attrs node;
  attrs node;
 
 
  decl = var_debug_decl (decl);
  decl = var_debug_decl (decl);
 
 
  for (node = set->regs[REGNO (loc)]; node; node = node->next)
  for (node = set->regs[REGNO (loc)]; node; node = node->next)
    if (node->decl == decl && node->offset == offset)
    if (node->decl == decl && node->offset == offset)
      break;
      break;
  if (!node)
  if (!node)
    attrs_list_insert (&set->regs[REGNO (loc)], decl, offset, loc);
    attrs_list_insert (&set->regs[REGNO (loc)], decl, offset, loc);
  set_variable_part (set, loc, decl, offset);
  set_variable_part (set, loc, decl, offset);
}
}
 
 
/* Delete current content of register LOC in dataflow set SET and set
/* Delete current content of register LOC in dataflow set SET and set
   the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  If
   the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  If
   MODIFY is true, any other live copies of the same variable part are
   MODIFY is true, any other live copies of the same variable part are
   also deleted from the dataflow set, otherwise the variable part is
   also deleted from the dataflow set, otherwise the variable part is
   assumed to be copied from another location holding the same
   assumed to be copied from another location holding the same
   part.  */
   part.  */
 
 
static void
static void
var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify)
var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify)
{
{
  tree decl = REG_EXPR (loc);
  tree decl = REG_EXPR (loc);
  HOST_WIDE_INT offset = REG_OFFSET (loc);
  HOST_WIDE_INT offset = REG_OFFSET (loc);
  attrs node, next;
  attrs node, next;
  attrs *nextp;
  attrs *nextp;
 
 
  decl = var_debug_decl (decl);
  decl = var_debug_decl (decl);
 
 
  nextp = &set->regs[REGNO (loc)];
  nextp = &set->regs[REGNO (loc)];
  for (node = *nextp; node; node = next)
  for (node = *nextp; node; node = next)
    {
    {
      next = node->next;
      next = node->next;
      if (node->decl != decl || node->offset != offset)
      if (node->decl != decl || node->offset != offset)
        {
        {
          delete_variable_part (set, node->loc, node->decl, node->offset);
          delete_variable_part (set, node->loc, node->decl, node->offset);
          pool_free (attrs_pool, node);
          pool_free (attrs_pool, node);
          *nextp = next;
          *nextp = next;
        }
        }
      else
      else
        {
        {
          node->loc = loc;
          node->loc = loc;
          nextp = &node->next;
          nextp = &node->next;
        }
        }
    }
    }
  if (modify)
  if (modify)
    clobber_variable_part (set, loc, decl, offset);
    clobber_variable_part (set, loc, decl, offset);
  var_reg_set (set, loc);
  var_reg_set (set, loc);
}
}
 
 
/* Delete current content of register LOC in dataflow set SET.  If
/* Delete current content of register LOC in dataflow set SET.  If
   CLOBBER is true, also delete any other live copies of the same
   CLOBBER is true, also delete any other live copies of the same
   variable part.  */
   variable part.  */
 
 
static void
static void
var_reg_delete (dataflow_set *set, rtx loc, bool clobber)
var_reg_delete (dataflow_set *set, rtx loc, bool clobber)
{
{
  attrs *reg = &set->regs[REGNO (loc)];
  attrs *reg = &set->regs[REGNO (loc)];
  attrs node, next;
  attrs node, next;
 
 
  if (clobber)
  if (clobber)
    {
    {
      tree decl = REG_EXPR (loc);
      tree decl = REG_EXPR (loc);
      HOST_WIDE_INT offset = REG_OFFSET (loc);
      HOST_WIDE_INT offset = REG_OFFSET (loc);
 
 
      decl = var_debug_decl (decl);
      decl = var_debug_decl (decl);
 
 
      clobber_variable_part (set, NULL, decl, offset);
      clobber_variable_part (set, NULL, decl, offset);
    }
    }
 
 
  for (node = *reg; node; node = next)
  for (node = *reg; node; node = next)
    {
    {
      next = node->next;
      next = node->next;
      delete_variable_part (set, node->loc, node->decl, node->offset);
      delete_variable_part (set, node->loc, node->decl, node->offset);
      pool_free (attrs_pool, node);
      pool_free (attrs_pool, node);
    }
    }
  *reg = NULL;
  *reg = NULL;
}
}
 
 
/* Delete content of register with number REGNO in dataflow set SET.  */
/* Delete content of register with number REGNO in dataflow set SET.  */
 
 
static void
static void
var_regno_delete (dataflow_set *set, int regno)
var_regno_delete (dataflow_set *set, int regno)
{
{
  attrs *reg = &set->regs[regno];
  attrs *reg = &set->regs[regno];
  attrs node, next;
  attrs node, next;
 
 
  for (node = *reg; node; node = next)
  for (node = *reg; node; node = next)
    {
    {
      next = node->next;
      next = node->next;
      delete_variable_part (set, node->loc, node->decl, node->offset);
      delete_variable_part (set, node->loc, node->decl, node->offset);
      pool_free (attrs_pool, node);
      pool_free (attrs_pool, node);
    }
    }
  *reg = NULL;
  *reg = NULL;
}
}
 
 
/* Set the location part of variable MEM_EXPR (LOC) in dataflow set
/* Set the location part of variable MEM_EXPR (LOC) in dataflow set
   SET to LOC.
   SET to LOC.
   Adjust the address first if it is stack pointer based.  */
   Adjust the address first if it is stack pointer based.  */
 
 
static void
static void
var_mem_set (dataflow_set *set, rtx loc)
var_mem_set (dataflow_set *set, rtx loc)
{
{
  tree decl = MEM_EXPR (loc);
  tree decl = MEM_EXPR (loc);
  HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
  HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
 
 
  decl = var_debug_decl (decl);
  decl = var_debug_decl (decl);
 
 
  set_variable_part (set, loc, decl, offset);
  set_variable_part (set, loc, decl, offset);
}
}
 
 
/* Delete and set the location part of variable MEM_EXPR (LOC) in
/* Delete and set the location part of variable MEM_EXPR (LOC) in
   dataflow set SET to LOC.  If MODIFY is true, any other live copies
   dataflow set SET to LOC.  If MODIFY is true, any other live copies
   of the same variable part are also deleted from the dataflow set,
   of the same variable part are also deleted from the dataflow set,
   otherwise the variable part is assumed to be copied from another
   otherwise the variable part is assumed to be copied from another
   location holding the same part.
   location holding the same part.
   Adjust the address first if it is stack pointer based.  */
   Adjust the address first if it is stack pointer based.  */
 
 
static void
static void
var_mem_delete_and_set (dataflow_set *set, rtx loc, bool modify)
var_mem_delete_and_set (dataflow_set *set, rtx loc, bool modify)
{
{
  tree decl = MEM_EXPR (loc);
  tree decl = MEM_EXPR (loc);
  HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
  HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
 
 
  decl = var_debug_decl (decl);
  decl = var_debug_decl (decl);
 
 
  if (modify)
  if (modify)
    clobber_variable_part (set, NULL, decl, offset);
    clobber_variable_part (set, NULL, decl, offset);
  var_mem_set (set, loc);
  var_mem_set (set, loc);
}
}
 
 
/* Delete the location part LOC from dataflow set SET.  If CLOBBER is
/* Delete the location part LOC from dataflow set SET.  If CLOBBER is
   true, also delete any other live copies of the same variable part.
   true, also delete any other live copies of the same variable part.
   Adjust the address first if it is stack pointer based.  */
   Adjust the address first if it is stack pointer based.  */
 
 
static void
static void
var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
{
{
  tree decl = MEM_EXPR (loc);
  tree decl = MEM_EXPR (loc);
  HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
  HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
 
 
  decl = var_debug_decl (decl);
  decl = var_debug_decl (decl);
  if (clobber)
  if (clobber)
    clobber_variable_part (set, NULL, decl, offset);
    clobber_variable_part (set, NULL, decl, offset);
  delete_variable_part (set, loc, decl, offset);
  delete_variable_part (set, loc, decl, offset);
}
}
 
 
/* Initialize dataflow set SET to be empty.
/* Initialize dataflow set SET to be empty.
   VARS_SIZE is the initial size of hash table VARS.  */
   VARS_SIZE is the initial size of hash table VARS.  */
 
 
static void
static void
dataflow_set_init (dataflow_set *set, int vars_size)
dataflow_set_init (dataflow_set *set, int vars_size)
{
{
  init_attrs_list_set (set->regs);
  init_attrs_list_set (set->regs);
  set->vars = htab_create (vars_size, variable_htab_hash, variable_htab_eq,
  set->vars = htab_create (vars_size, variable_htab_hash, variable_htab_eq,
                           variable_htab_free);
                           variable_htab_free);
  set->stack_adjust = 0;
  set->stack_adjust = 0;
}
}
 
 
/* Delete the contents of dataflow set SET.  */
/* Delete the contents of dataflow set SET.  */
 
 
static void
static void
dataflow_set_clear (dataflow_set *set)
dataflow_set_clear (dataflow_set *set)
{
{
  int i;
  int i;
 
 
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
    attrs_list_clear (&set->regs[i]);
    attrs_list_clear (&set->regs[i]);
 
 
  vars_clear (set->vars);
  vars_clear (set->vars);
}
}
 
 
/* Copy the contents of dataflow set SRC to DST.  */
/* Copy the contents of dataflow set SRC to DST.  */
 
 
static void
static void
dataflow_set_copy (dataflow_set *dst, dataflow_set *src)
dataflow_set_copy (dataflow_set *dst, dataflow_set *src)
{
{
  int i;
  int i;
 
 
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
    attrs_list_copy (&dst->regs[i], src->regs[i]);
    attrs_list_copy (&dst->regs[i], src->regs[i]);
 
 
  vars_copy (dst->vars, src->vars);
  vars_copy (dst->vars, src->vars);
  dst->stack_adjust = src->stack_adjust;
  dst->stack_adjust = src->stack_adjust;
}
}
 
 
/* Information for merging lists of locations for a given offset of variable.
/* Information for merging lists of locations for a given offset of variable.
 */
 */
struct variable_union_info
struct variable_union_info
{
{
  /* Node of the location chain.  */
  /* Node of the location chain.  */
  location_chain lc;
  location_chain lc;
 
 
  /* The sum of positions in the input chains.  */
  /* The sum of positions in the input chains.  */
  int pos;
  int pos;
 
 
  /* The position in the chains of SRC and DST dataflow sets.  */
  /* The position in the chains of SRC and DST dataflow sets.  */
  int pos_src;
  int pos_src;
  int pos_dst;
  int pos_dst;
};
};
 
 
/* Compare function for qsort, order the structures by POS element.  */
/* Compare function for qsort, order the structures by POS element.  */
 
 
static int
static int
variable_union_info_cmp_pos (const void *n1, const void *n2)
variable_union_info_cmp_pos (const void *n1, const void *n2)
{
{
  const struct variable_union_info *i1 = n1;
  const struct variable_union_info *i1 = n1;
  const struct variable_union_info *i2 = n2;
  const struct variable_union_info *i2 = n2;
 
 
  if (i1->pos != i2->pos)
  if (i1->pos != i2->pos)
    return i1->pos - i2->pos;
    return i1->pos - i2->pos;
 
 
  return (i1->pos_dst - i2->pos_dst);
  return (i1->pos_dst - i2->pos_dst);
}
}
 
 
/* Compute union of location parts of variable *SLOT and the same variable
/* Compute union of location parts of variable *SLOT and the same variable
   from hash table DATA.  Compute "sorted" union of the location chains
   from hash table DATA.  Compute "sorted" union of the location chains
   for common offsets, i.e. the locations of a variable part are sorted by
   for common offsets, i.e. the locations of a variable part are sorted by
   a priority where the priority is the sum of the positions in the 2 chains
   a priority where the priority is the sum of the positions in the 2 chains
   (if a location is only in one list the position in the second list is
   (if a location is only in one list the position in the second list is
   defined to be larger than the length of the chains).
   defined to be larger than the length of the chains).
   When we are updating the location parts the newest location is in the
   When we are updating the location parts the newest location is in the
   beginning of the chain, so when we do the described "sorted" union
   beginning of the chain, so when we do the described "sorted" union
   we keep the newest locations in the beginning.  */
   we keep the newest locations in the beginning.  */
 
 
static int
static int
variable_union (void **slot, void *data)
variable_union (void **slot, void *data)
{
{
  variable src, dst, *dstp;
  variable src, dst, *dstp;
  dataflow_set *set = (dataflow_set *) data;
  dataflow_set *set = (dataflow_set *) data;
  int i, j, k;
  int i, j, k;
 
 
  src = *(variable *) slot;
  src = *(variable *) slot;
  dstp = (variable *) htab_find_slot_with_hash (set->vars, src->decl,
  dstp = (variable *) htab_find_slot_with_hash (set->vars, src->decl,
                                                VARIABLE_HASH_VAL (src->decl),
                                                VARIABLE_HASH_VAL (src->decl),
                                                INSERT);
                                                INSERT);
  if (!*dstp)
  if (!*dstp)
    {
    {
      src->refcount++;
      src->refcount++;
 
 
      /* If CUR_LOC of some variable part is not the first element of
      /* If CUR_LOC of some variable part is not the first element of
         the location chain we are going to change it so we have to make
         the location chain we are going to change it so we have to make
         a copy of the variable.  */
         a copy of the variable.  */
      for (k = 0; k < src->n_var_parts; k++)
      for (k = 0; k < src->n_var_parts; k++)
        {
        {
          gcc_assert (!src->var_part[k].loc_chain
          gcc_assert (!src->var_part[k].loc_chain
                      == !src->var_part[k].cur_loc);
                      == !src->var_part[k].cur_loc);
          if (src->var_part[k].loc_chain)
          if (src->var_part[k].loc_chain)
            {
            {
              gcc_assert (src->var_part[k].cur_loc);
              gcc_assert (src->var_part[k].cur_loc);
              if (src->var_part[k].cur_loc != src->var_part[k].loc_chain->loc)
              if (src->var_part[k].cur_loc != src->var_part[k].loc_chain->loc)
                break;
                break;
            }
            }
        }
        }
      if (k < src->n_var_parts)
      if (k < src->n_var_parts)
        unshare_variable (set, src);
        unshare_variable (set, src);
      else
      else
        *dstp = src;
        *dstp = src;
 
 
      /* Continue traversing the hash table.  */
      /* Continue traversing the hash table.  */
      return 1;
      return 1;
    }
    }
  else
  else
    dst = *dstp;
    dst = *dstp;
 
 
  gcc_assert (src->n_var_parts);
  gcc_assert (src->n_var_parts);
 
 
  /* Count the number of location parts, result is K.  */
  /* Count the number of location parts, result is K.  */
  for (i = 0, j = 0, k = 0;
  for (i = 0, j = 0, k = 0;
       i < src->n_var_parts && j < dst->n_var_parts; k++)
       i < src->n_var_parts && j < dst->n_var_parts; k++)
    {
    {
      if (src->var_part[i].offset == dst->var_part[j].offset)
      if (src->var_part[i].offset == dst->var_part[j].offset)
        {
        {
          i++;
          i++;
          j++;
          j++;
        }
        }
      else if (src->var_part[i].offset < dst->var_part[j].offset)
      else if (src->var_part[i].offset < dst->var_part[j].offset)
        i++;
        i++;
      else
      else
        j++;
        j++;
    }
    }
  k += src->n_var_parts - i;
  k += src->n_var_parts - i;
  k += dst->n_var_parts - j;
  k += dst->n_var_parts - j;
 
 
  /* We track only variables whose size is <= MAX_VAR_PARTS bytes
  /* We track only variables whose size is <= MAX_VAR_PARTS bytes
     thus there are at most MAX_VAR_PARTS different offsets.  */
     thus there are at most MAX_VAR_PARTS different offsets.  */
  gcc_assert (k <= MAX_VAR_PARTS);
  gcc_assert (k <= MAX_VAR_PARTS);
 
 
  if (dst->refcount > 1 && dst->n_var_parts != k)
  if (dst->refcount > 1 && dst->n_var_parts != k)
    dst = unshare_variable (set, dst);
    dst = unshare_variable (set, dst);
 
 
  i = src->n_var_parts - 1;
  i = src->n_var_parts - 1;
  j = dst->n_var_parts - 1;
  j = dst->n_var_parts - 1;
  dst->n_var_parts = k;
  dst->n_var_parts = k;
 
 
  for (k--; k >= 0; k--)
  for (k--; k >= 0; k--)
    {
    {
      location_chain node, node2;
      location_chain node, node2;
 
 
      if (i >= 0 && j >= 0
      if (i >= 0 && j >= 0
          && src->var_part[i].offset == dst->var_part[j].offset)
          && src->var_part[i].offset == dst->var_part[j].offset)
        {
        {
          /* Compute the "sorted" union of the chains, i.e. the locations which
          /* Compute the "sorted" union of the chains, i.e. the locations which
             are in both chains go first, they are sorted by the sum of
             are in both chains go first, they are sorted by the sum of
             positions in the chains.  */
             positions in the chains.  */
          int dst_l, src_l;
          int dst_l, src_l;
          int ii, jj, n;
          int ii, jj, n;
          struct variable_union_info *vui;
          struct variable_union_info *vui;
 
 
          /* If DST is shared compare the location chains.
          /* If DST is shared compare the location chains.
             If they are different we will modify the chain in DST with
             If they are different we will modify the chain in DST with
             high probability so make a copy of DST.  */
             high probability so make a copy of DST.  */
          if (dst->refcount > 1)
          if (dst->refcount > 1)
            {
            {
              for (node = src->var_part[i].loc_chain,
              for (node = src->var_part[i].loc_chain,
                   node2 = dst->var_part[j].loc_chain; node && node2;
                   node2 = dst->var_part[j].loc_chain; node && node2;
                   node = node->next, node2 = node2->next)
                   node = node->next, node2 = node2->next)
                {
                {
                  if (!((REG_P (node2->loc)
                  if (!((REG_P (node2->loc)
                         && REG_P (node->loc)
                         && REG_P (node->loc)
                         && REGNO (node2->loc) == REGNO (node->loc))
                         && REGNO (node2->loc) == REGNO (node->loc))
                        || rtx_equal_p (node2->loc, node->loc)))
                        || rtx_equal_p (node2->loc, node->loc)))
                    break;
                    break;
                }
                }
              if (node || node2)
              if (node || node2)
                dst = unshare_variable (set, dst);
                dst = unshare_variable (set, dst);
            }
            }
 
 
          src_l = 0;
          src_l = 0;
          for (node = src->var_part[i].loc_chain; node; node = node->next)
          for (node = src->var_part[i].loc_chain; node; node = node->next)
            src_l++;
            src_l++;
          dst_l = 0;
          dst_l = 0;
          for (node = dst->var_part[j].loc_chain; node; node = node->next)
          for (node = dst->var_part[j].loc_chain; node; node = node->next)
            dst_l++;
            dst_l++;
          vui = XCNEWVEC (struct variable_union_info, src_l + dst_l);
          vui = XCNEWVEC (struct variable_union_info, src_l + dst_l);
 
 
          /* Fill in the locations from DST.  */
          /* Fill in the locations from DST.  */
          for (node = dst->var_part[j].loc_chain, jj = 0; node;
          for (node = dst->var_part[j].loc_chain, jj = 0; node;
               node = node->next, jj++)
               node = node->next, jj++)
            {
            {
              vui[jj].lc = node;
              vui[jj].lc = node;
              vui[jj].pos_dst = jj;
              vui[jj].pos_dst = jj;
 
 
              /* Value larger than a sum of 2 valid positions.  */
              /* Value larger than a sum of 2 valid positions.  */
              vui[jj].pos_src = src_l + dst_l;
              vui[jj].pos_src = src_l + dst_l;
            }
            }
 
 
          /* Fill in the locations from SRC.  */
          /* Fill in the locations from SRC.  */
          n = dst_l;
          n = dst_l;
          for (node = src->var_part[i].loc_chain, ii = 0; node;
          for (node = src->var_part[i].loc_chain, ii = 0; node;
               node = node->next, ii++)
               node = node->next, ii++)
            {
            {
              /* Find location from NODE.  */
              /* Find location from NODE.  */
              for (jj = 0; jj < dst_l; jj++)
              for (jj = 0; jj < dst_l; jj++)
                {
                {
                  if ((REG_P (vui[jj].lc->loc)
                  if ((REG_P (vui[jj].lc->loc)
                       && REG_P (node->loc)
                       && REG_P (node->loc)
                       && REGNO (vui[jj].lc->loc) == REGNO (node->loc))
                       && REGNO (vui[jj].lc->loc) == REGNO (node->loc))
                      || rtx_equal_p (vui[jj].lc->loc, node->loc))
                      || rtx_equal_p (vui[jj].lc->loc, node->loc))
                    {
                    {
                      vui[jj].pos_src = ii;
                      vui[jj].pos_src = ii;
                      break;
                      break;
                    }
                    }
                }
                }
              if (jj >= dst_l)  /* The location has not been found.  */
              if (jj >= dst_l)  /* The location has not been found.  */
                {
                {
                  location_chain new_node;
                  location_chain new_node;
 
 
                  /* Copy the location from SRC.  */
                  /* Copy the location from SRC.  */
                  new_node = pool_alloc (loc_chain_pool);
                  new_node = pool_alloc (loc_chain_pool);
                  new_node->loc = node->loc;
                  new_node->loc = node->loc;
                  vui[n].lc = new_node;
                  vui[n].lc = new_node;
                  vui[n].pos_src = ii;
                  vui[n].pos_src = ii;
                  vui[n].pos_dst = src_l + dst_l;
                  vui[n].pos_dst = src_l + dst_l;
                  n++;
                  n++;
                }
                }
            }
            }
 
 
          for (ii = 0; ii < src_l + dst_l; ii++)
          for (ii = 0; ii < src_l + dst_l; ii++)
            vui[ii].pos = vui[ii].pos_src + vui[ii].pos_dst;
            vui[ii].pos = vui[ii].pos_src + vui[ii].pos_dst;
 
 
          qsort (vui, n, sizeof (struct variable_union_info),
          qsort (vui, n, sizeof (struct variable_union_info),
                 variable_union_info_cmp_pos);
                 variable_union_info_cmp_pos);
 
 
          /* Reconnect the nodes in sorted order.  */
          /* Reconnect the nodes in sorted order.  */
          for (ii = 1; ii < n; ii++)
          for (ii = 1; ii < n; ii++)
            vui[ii - 1].lc->next = vui[ii].lc;
            vui[ii - 1].lc->next = vui[ii].lc;
          vui[n - 1].lc->next = NULL;
          vui[n - 1].lc->next = NULL;
 
 
          dst->var_part[k].loc_chain = vui[0].lc;
          dst->var_part[k].loc_chain = vui[0].lc;
          dst->var_part[k].offset = dst->var_part[j].offset;
          dst->var_part[k].offset = dst->var_part[j].offset;
 
 
          free (vui);
          free (vui);
          i--;
          i--;
          j--;
          j--;
        }
        }
      else if ((i >= 0 && j >= 0
      else if ((i >= 0 && j >= 0
                && src->var_part[i].offset < dst->var_part[j].offset)
                && src->var_part[i].offset < dst->var_part[j].offset)
               || i < 0)
               || i < 0)
        {
        {
          dst->var_part[k] = dst->var_part[j];
          dst->var_part[k] = dst->var_part[j];
          j--;
          j--;
        }
        }
      else if ((i >= 0 && j >= 0
      else if ((i >= 0 && j >= 0
                && src->var_part[i].offset > dst->var_part[j].offset)
                && src->var_part[i].offset > dst->var_part[j].offset)
               || j < 0)
               || j < 0)
        {
        {
          location_chain *nextp;
          location_chain *nextp;
 
 
          /* Copy the chain from SRC.  */
          /* Copy the chain from SRC.  */
          nextp = &dst->var_part[k].loc_chain;
          nextp = &dst->var_part[k].loc_chain;
          for (node = src->var_part[i].loc_chain; node; node = node->next)
          for (node = src->var_part[i].loc_chain; node; node = node->next)
            {
            {
              location_chain new_lc;
              location_chain new_lc;
 
 
              new_lc = pool_alloc (loc_chain_pool);
              new_lc = pool_alloc (loc_chain_pool);
              new_lc->next = NULL;
              new_lc->next = NULL;
              new_lc->loc = node->loc;
              new_lc->loc = node->loc;
 
 
              *nextp = new_lc;
              *nextp = new_lc;
              nextp = &new_lc->next;
              nextp = &new_lc->next;
            }
            }
 
 
          dst->var_part[k].offset = src->var_part[i].offset;
          dst->var_part[k].offset = src->var_part[i].offset;
          i--;
          i--;
        }
        }
 
 
      /* We are at the basic block boundary when computing union
      /* We are at the basic block boundary when computing union
         so set the CUR_LOC to be the first element of the chain.  */
         so set the CUR_LOC to be the first element of the chain.  */
      if (dst->var_part[k].loc_chain)
      if (dst->var_part[k].loc_chain)
        dst->var_part[k].cur_loc = dst->var_part[k].loc_chain->loc;
        dst->var_part[k].cur_loc = dst->var_part[k].loc_chain->loc;
      else
      else
        dst->var_part[k].cur_loc = NULL;
        dst->var_part[k].cur_loc = NULL;
    }
    }
 
 
  /* Continue traversing the hash table.  */
  /* Continue traversing the hash table.  */
  return 1;
  return 1;
}
}
 
 
/* Compute union of dataflow sets SRC and DST and store it to DST.  */
/* Compute union of dataflow sets SRC and DST and store it to DST.  */
 
 
static void
static void
dataflow_set_union (dataflow_set *dst, dataflow_set *src)
dataflow_set_union (dataflow_set *dst, dataflow_set *src)
{
{
  int i;
  int i;
 
 
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
    attrs_list_union (&dst->regs[i], src->regs[i]);
    attrs_list_union (&dst->regs[i], src->regs[i]);
 
 
  htab_traverse (src->vars, variable_union, dst);
  htab_traverse (src->vars, variable_union, dst);
}
}
 
 
/* Flag whether two dataflow sets being compared contain different data.  */
/* Flag whether two dataflow sets being compared contain different data.  */
static bool
static bool
dataflow_set_different_value;
dataflow_set_different_value;
 
 
static bool
static bool
variable_part_different_p (variable_part *vp1, variable_part *vp2)
variable_part_different_p (variable_part *vp1, variable_part *vp2)
{
{
  location_chain lc1, lc2;
  location_chain lc1, lc2;
 
 
  for (lc1 = vp1->loc_chain; lc1; lc1 = lc1->next)
  for (lc1 = vp1->loc_chain; lc1; lc1 = lc1->next)
    {
    {
      for (lc2 = vp2->loc_chain; lc2; lc2 = lc2->next)
      for (lc2 = vp2->loc_chain; lc2; lc2 = lc2->next)
        {
        {
          if (REG_P (lc1->loc) && REG_P (lc2->loc))
          if (REG_P (lc1->loc) && REG_P (lc2->loc))
            {
            {
              if (REGNO (lc1->loc) == REGNO (lc2->loc))
              if (REGNO (lc1->loc) == REGNO (lc2->loc))
                break;
                break;
            }
            }
          if (rtx_equal_p (lc1->loc, lc2->loc))
          if (rtx_equal_p (lc1->loc, lc2->loc))
            break;
            break;
        }
        }
      if (!lc2)
      if (!lc2)
        return true;
        return true;
    }
    }
  return false;
  return false;
}
}
 
 
/* Return true if variables VAR1 and VAR2 are different.
/* Return true if variables VAR1 and VAR2 are different.
   If COMPARE_CURRENT_LOCATION is true compare also the cur_loc of each
   If COMPARE_CURRENT_LOCATION is true compare also the cur_loc of each
   variable part.  */
   variable part.  */
 
 
static bool
static bool
variable_different_p (variable var1, variable var2,
variable_different_p (variable var1, variable var2,
                      bool compare_current_location)
                      bool compare_current_location)
{
{
  int i;
  int i;
 
 
  if (var1 == var2)
  if (var1 == var2)
    return false;
    return false;
 
 
  if (var1->n_var_parts != var2->n_var_parts)
  if (var1->n_var_parts != var2->n_var_parts)
    return true;
    return true;
 
 
  for (i = 0; i < var1->n_var_parts; i++)
  for (i = 0; i < var1->n_var_parts; i++)
    {
    {
      if (var1->var_part[i].offset != var2->var_part[i].offset)
      if (var1->var_part[i].offset != var2->var_part[i].offset)
        return true;
        return true;
      if (compare_current_location)
      if (compare_current_location)
        {
        {
          if (!((REG_P (var1->var_part[i].cur_loc)
          if (!((REG_P (var1->var_part[i].cur_loc)
                 && REG_P (var2->var_part[i].cur_loc)
                 && REG_P (var2->var_part[i].cur_loc)
                 && (REGNO (var1->var_part[i].cur_loc)
                 && (REGNO (var1->var_part[i].cur_loc)
                     == REGNO (var2->var_part[i].cur_loc)))
                     == REGNO (var2->var_part[i].cur_loc)))
                || rtx_equal_p (var1->var_part[i].cur_loc,
                || rtx_equal_p (var1->var_part[i].cur_loc,
                                var2->var_part[i].cur_loc)))
                                var2->var_part[i].cur_loc)))
            return true;
            return true;
        }
        }
      if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i]))
      if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i]))
        return true;
        return true;
      if (variable_part_different_p (&var2->var_part[i], &var1->var_part[i]))
      if (variable_part_different_p (&var2->var_part[i], &var1->var_part[i]))
        return true;
        return true;
    }
    }
  return false;
  return false;
}
}
 
 
/* Compare variable *SLOT with the same variable in hash table DATA
/* Compare variable *SLOT with the same variable in hash table DATA
   and set DATAFLOW_SET_DIFFERENT_VALUE if they are different.  */
   and set DATAFLOW_SET_DIFFERENT_VALUE if they are different.  */
 
 
static int
static int
dataflow_set_different_1 (void **slot, void *data)
dataflow_set_different_1 (void **slot, void *data)
{
{
  htab_t htab = (htab_t) data;
  htab_t htab = (htab_t) data;
  variable var1, var2;
  variable var1, var2;
 
 
  var1 = *(variable *) slot;
  var1 = *(variable *) slot;
  var2 = htab_find_with_hash (htab, var1->decl,
  var2 = htab_find_with_hash (htab, var1->decl,
                              VARIABLE_HASH_VAL (var1->decl));
                              VARIABLE_HASH_VAL (var1->decl));
  if (!var2)
  if (!var2)
    {
    {
      dataflow_set_different_value = true;
      dataflow_set_different_value = true;
 
 
      /* Stop traversing the hash table.  */
      /* Stop traversing the hash table.  */
      return 0;
      return 0;
    }
    }
 
 
  if (variable_different_p (var1, var2, false))
  if (variable_different_p (var1, var2, false))
    {
    {
      dataflow_set_different_value = true;
      dataflow_set_different_value = true;
 
 
      /* Stop traversing the hash table.  */
      /* Stop traversing the hash table.  */
      return 0;
      return 0;
    }
    }
 
 
  /* Continue traversing the hash table.  */
  /* Continue traversing the hash table.  */
  return 1;
  return 1;
}
}
 
 
/* Compare variable *SLOT with the same variable in hash table DATA
/* Compare variable *SLOT with the same variable in hash table DATA
   and set DATAFLOW_SET_DIFFERENT_VALUE if they are different.  */
   and set DATAFLOW_SET_DIFFERENT_VALUE if they are different.  */
 
 
static int
static int
dataflow_set_different_2 (void **slot, void *data)
dataflow_set_different_2 (void **slot, void *data)
{
{
  htab_t htab = (htab_t) data;
  htab_t htab = (htab_t) data;
  variable var1, var2;
  variable var1, var2;
 
 
  var1 = *(variable *) slot;
  var1 = *(variable *) slot;
  var2 = htab_find_with_hash (htab, var1->decl,
  var2 = htab_find_with_hash (htab, var1->decl,
                              VARIABLE_HASH_VAL (var1->decl));
                              VARIABLE_HASH_VAL (var1->decl));
  if (!var2)
  if (!var2)
    {
    {
      dataflow_set_different_value = true;
      dataflow_set_different_value = true;
 
 
      /* Stop traversing the hash table.  */
      /* Stop traversing the hash table.  */
      return 0;
      return 0;
    }
    }
 
 
  /* If both variables are defined they have been already checked for
  /* If both variables are defined they have been already checked for
     equivalence.  */
     equivalence.  */
  gcc_assert (!variable_different_p (var1, var2, false));
  gcc_assert (!variable_different_p (var1, var2, false));
 
 
  /* Continue traversing the hash table.  */
  /* Continue traversing the hash table.  */
  return 1;
  return 1;
}
}
 
 
/* Return true if dataflow sets OLD_SET and NEW_SET differ.  */
/* Return true if dataflow sets OLD_SET and NEW_SET differ.  */
 
 
static bool
static bool
dataflow_set_different (dataflow_set *old_set, dataflow_set *new_set)
dataflow_set_different (dataflow_set *old_set, dataflow_set *new_set)
{
{
  dataflow_set_different_value = false;
  dataflow_set_different_value = false;
 
 
  htab_traverse (old_set->vars, dataflow_set_different_1, new_set->vars);
  htab_traverse (old_set->vars, dataflow_set_different_1, new_set->vars);
  if (!dataflow_set_different_value)
  if (!dataflow_set_different_value)
    {
    {
      /* We have compared the variables which are in both hash tables
      /* We have compared the variables which are in both hash tables
         so now only check whether there are some variables in NEW_SET->VARS
         so now only check whether there are some variables in NEW_SET->VARS
         which are not in OLD_SET->VARS.  */
         which are not in OLD_SET->VARS.  */
      htab_traverse (new_set->vars, dataflow_set_different_2, old_set->vars);
      htab_traverse (new_set->vars, dataflow_set_different_2, old_set->vars);
    }
    }
  return dataflow_set_different_value;
  return dataflow_set_different_value;
}
}
 
 
/* Free the contents of dataflow set SET.  */
/* Free the contents of dataflow set SET.  */
 
 
static void
static void
dataflow_set_destroy (dataflow_set *set)
dataflow_set_destroy (dataflow_set *set)
{
{
  int i;
  int i;
 
 
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
    attrs_list_clear (&set->regs[i]);
    attrs_list_clear (&set->regs[i]);
 
 
  htab_delete (set->vars);
  htab_delete (set->vars);
  set->vars = NULL;
  set->vars = NULL;
}
}
 
 
/* Return true if RTL X contains a SYMBOL_REF.  */
/* Return true if RTL X contains a SYMBOL_REF.  */
 
 
static bool
static bool
contains_symbol_ref (rtx x)
contains_symbol_ref (rtx x)
{
{
  const char *fmt;
  const char *fmt;
  RTX_CODE code;
  RTX_CODE code;
  int i;
  int i;
 
 
  if (!x)
  if (!x)
    return false;
    return false;
 
 
  code = GET_CODE (x);
  code = GET_CODE (x);
  if (code == SYMBOL_REF)
  if (code == SYMBOL_REF)
    return true;
    return true;
 
 
  fmt = GET_RTX_FORMAT (code);
  fmt = GET_RTX_FORMAT (code);
  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
    {
    {
      if (fmt[i] == 'e')
      if (fmt[i] == 'e')
        {
        {
          if (contains_symbol_ref (XEXP (x, i)))
          if (contains_symbol_ref (XEXP (x, i)))
            return true;
            return true;
        }
        }
      else if (fmt[i] == 'E')
      else if (fmt[i] == 'E')
        {
        {
          int j;
          int j;
          for (j = 0; j < XVECLEN (x, i); j++)
          for (j = 0; j < XVECLEN (x, i); j++)
            if (contains_symbol_ref (XVECEXP (x, i, j)))
            if (contains_symbol_ref (XVECEXP (x, i, j)))
              return true;
              return true;
        }
        }
    }
    }
 
 
  return false;
  return false;
}
}
 
 
/* Shall EXPR be tracked?  */
/* Shall EXPR be tracked?  */
 
 
static bool
static bool
track_expr_p (tree expr)
track_expr_p (tree expr)
{
{
  rtx decl_rtl;
  rtx decl_rtl;
  tree realdecl;
  tree realdecl;
 
 
  /* If EXPR is not a parameter or a variable do not track it.  */
  /* If EXPR is not a parameter or a variable do not track it.  */
  if (TREE_CODE (expr) != VAR_DECL && TREE_CODE (expr) != PARM_DECL)
  if (TREE_CODE (expr) != VAR_DECL && TREE_CODE (expr) != PARM_DECL)
    return 0;
    return 0;
 
 
  /* It also must have a name...  */
  /* It also must have a name...  */
  if (!DECL_NAME (expr))
  if (!DECL_NAME (expr))
    return 0;
    return 0;
 
 
  /* ... and a RTL assigned to it.  */
  /* ... and a RTL assigned to it.  */
  decl_rtl = DECL_RTL_IF_SET (expr);
  decl_rtl = DECL_RTL_IF_SET (expr);
  if (!decl_rtl)
  if (!decl_rtl)
    return 0;
    return 0;
 
 
  /* If this expression is really a debug alias of some other declaration, we
  /* If this expression is really a debug alias of some other declaration, we
     don't need to track this expression if the ultimate declaration is
     don't need to track this expression if the ultimate declaration is
     ignored.  */
     ignored.  */
  realdecl = expr;
  realdecl = expr;
  if (DECL_DEBUG_EXPR_IS_FROM (realdecl) && DECL_DEBUG_EXPR (realdecl))
  if (DECL_DEBUG_EXPR_IS_FROM (realdecl) && DECL_DEBUG_EXPR (realdecl))
    {
    {
      realdecl = DECL_DEBUG_EXPR (realdecl);
      realdecl = DECL_DEBUG_EXPR (realdecl);
      /* ??? We don't yet know how to emit DW_OP_piece for variable
      /* ??? We don't yet know how to emit DW_OP_piece for variable
         that has been SRA'ed.  */
         that has been SRA'ed.  */
      if (!DECL_P (realdecl))
      if (!DECL_P (realdecl))
        return 0;
        return 0;
    }
    }
 
 
  /* Do not track EXPR if REALDECL it should be ignored for debugging
  /* Do not track EXPR if REALDECL it should be ignored for debugging
     purposes.  */
     purposes.  */
  if (DECL_IGNORED_P (realdecl))
  if (DECL_IGNORED_P (realdecl))
    return 0;
    return 0;
 
 
  /* Do not track global variables until we are able to emit correct location
  /* Do not track global variables until we are able to emit correct location
     list for them.  */
     list for them.  */
  if (TREE_STATIC (realdecl))
  if (TREE_STATIC (realdecl))
    return 0;
    return 0;
 
 
  /* When the EXPR is a DECL for alias of some variable (see example)
  /* When the EXPR is a DECL for alias of some variable (see example)
     the TREE_STATIC flag is not used.  Disable tracking all DECLs whose
     the TREE_STATIC flag is not used.  Disable tracking all DECLs whose
     DECL_RTL contains SYMBOL_REF.
     DECL_RTL contains SYMBOL_REF.
 
 
     Example:
     Example:
     extern char **_dl_argv_internal __attribute__ ((alias ("_dl_argv")));
     extern char **_dl_argv_internal __attribute__ ((alias ("_dl_argv")));
     char **_dl_argv;
     char **_dl_argv;
  */
  */
  if (MEM_P (decl_rtl)
  if (MEM_P (decl_rtl)
      && contains_symbol_ref (XEXP (decl_rtl, 0)))
      && contains_symbol_ref (XEXP (decl_rtl, 0)))
    return 0;
    return 0;
 
 
  /* If RTX is a memory it should not be very large (because it would be
  /* If RTX is a memory it should not be very large (because it would be
     an array or struct).  */
     an array or struct).  */
  if (MEM_P (decl_rtl))
  if (MEM_P (decl_rtl))
    {
    {
      /* Do not track structures and arrays.  */
      /* Do not track structures and arrays.  */
      if (GET_MODE (decl_rtl) == BLKmode
      if (GET_MODE (decl_rtl) == BLKmode
          || AGGREGATE_TYPE_P (TREE_TYPE (realdecl)))
          || AGGREGATE_TYPE_P (TREE_TYPE (realdecl)))
        return 0;
        return 0;
      if (MEM_SIZE (decl_rtl)
      if (MEM_SIZE (decl_rtl)
          && INTVAL (MEM_SIZE (decl_rtl)) > MAX_VAR_PARTS)
          && INTVAL (MEM_SIZE (decl_rtl)) > MAX_VAR_PARTS)
        return 0;
        return 0;
    }
    }
 
 
  return 1;
  return 1;
}
}
 
 
/* Determine whether a given LOC refers to the same variable part as
/* Determine whether a given LOC refers to the same variable part as
   EXPR+OFFSET.  */
   EXPR+OFFSET.  */
 
 
static bool
static bool
same_variable_part_p (rtx loc, tree expr, HOST_WIDE_INT offset)
same_variable_part_p (rtx loc, tree expr, HOST_WIDE_INT offset)
{
{
  tree expr2;
  tree expr2;
  HOST_WIDE_INT offset2;
  HOST_WIDE_INT offset2;
 
 
  if (! DECL_P (expr))
  if (! DECL_P (expr))
    return false;
    return false;
 
 
  if (REG_P (loc))
  if (REG_P (loc))
    {
    {
      expr2 = REG_EXPR (loc);
      expr2 = REG_EXPR (loc);
      offset2 = REG_OFFSET (loc);
      offset2 = REG_OFFSET (loc);
    }
    }
  else if (MEM_P (loc))
  else if (MEM_P (loc))
    {
    {
      expr2 = MEM_EXPR (loc);
      expr2 = MEM_EXPR (loc);
      offset2 = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
      offset2 = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
    }
    }
  else
  else
    return false;
    return false;
 
 
  if (! expr2 || ! DECL_P (expr2))
  if (! expr2 || ! DECL_P (expr2))
    return false;
    return false;
 
 
  expr = var_debug_decl (expr);
  expr = var_debug_decl (expr);
  expr2 = var_debug_decl (expr2);
  expr2 = var_debug_decl (expr2);
 
 
  return (expr == expr2 && offset == offset2);
  return (expr == expr2 && offset == offset2);
}
}
 
 
 
 
/* Count uses (register and memory references) LOC which will be tracked.
/* Count uses (register and memory references) LOC which will be tracked.
   INSN is instruction which the LOC is part of.  */
   INSN is instruction which the LOC is part of.  */
 
 
static int
static int
count_uses (rtx *loc, void *insn)
count_uses (rtx *loc, void *insn)
{
{
  basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
  basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
 
 
  if (REG_P (*loc))
  if (REG_P (*loc))
    {
    {
      gcc_assert (REGNO (*loc) < FIRST_PSEUDO_REGISTER);
      gcc_assert (REGNO (*loc) < FIRST_PSEUDO_REGISTER);
      VTI (bb)->n_mos++;
      VTI (bb)->n_mos++;
    }
    }
  else if (MEM_P (*loc)
  else if (MEM_P (*loc)
           && MEM_EXPR (*loc)
           && MEM_EXPR (*loc)
           && track_expr_p (MEM_EXPR (*loc)))
           && track_expr_p (MEM_EXPR (*loc)))
    {
    {
      VTI (bb)->n_mos++;
      VTI (bb)->n_mos++;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* Helper function for finding all uses of REG/MEM in X in insn INSN.  */
/* Helper function for finding all uses of REG/MEM in X in insn INSN.  */
 
 
static void
static void
count_uses_1 (rtx *x, void *insn)
count_uses_1 (rtx *x, void *insn)
{
{
  for_each_rtx (x, count_uses, insn);
  for_each_rtx (x, count_uses, insn);
}
}
 
 
/* Count stores (register and memory references) LOC which will be tracked.
/* Count stores (register and memory references) LOC which will be tracked.
   INSN is instruction which the LOC is part of.  */
   INSN is instruction which the LOC is part of.  */
 
 
static void
static void
count_stores (rtx loc, rtx expr ATTRIBUTE_UNUSED, void *insn)
count_stores (rtx loc, rtx expr ATTRIBUTE_UNUSED, void *insn)
{
{
  count_uses (&loc, insn);
  count_uses (&loc, insn);
}
}
 
 
/* Add uses (register and memory references) LOC which will be tracked
/* Add uses (register and memory references) LOC which will be tracked
   to VTI (bb)->mos.  INSN is instruction which the LOC is part of.  */
   to VTI (bb)->mos.  INSN is instruction which the LOC is part of.  */
 
 
static int
static int
add_uses (rtx *loc, void *insn)
add_uses (rtx *loc, void *insn)
{
{
  if (REG_P (*loc))
  if (REG_P (*loc))
    {
    {
      basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
      basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
 
      mo->type = ((REG_EXPR (*loc) && track_expr_p (REG_EXPR (*loc)))
      mo->type = ((REG_EXPR (*loc) && track_expr_p (REG_EXPR (*loc)))
                  ? MO_USE : MO_USE_NO_VAR);
                  ? MO_USE : MO_USE_NO_VAR);
      mo->u.loc = *loc;
      mo->u.loc = *loc;
      mo->insn = (rtx) insn;
      mo->insn = (rtx) insn;
    }
    }
  else if (MEM_P (*loc)
  else if (MEM_P (*loc)
           && MEM_EXPR (*loc)
           && MEM_EXPR (*loc)
           && track_expr_p (MEM_EXPR (*loc)))
           && track_expr_p (MEM_EXPR (*loc)))
    {
    {
      basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
      basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
 
      mo->type = MO_USE;
      mo->type = MO_USE;
      mo->u.loc = *loc;
      mo->u.loc = *loc;
      mo->insn = (rtx) insn;
      mo->insn = (rtx) insn;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* Helper function for finding all uses of REG/MEM in X in insn INSN.  */
/* Helper function for finding all uses of REG/MEM in X in insn INSN.  */
 
 
static void
static void
add_uses_1 (rtx *x, void *insn)
add_uses_1 (rtx *x, void *insn)
{
{
  for_each_rtx (x, add_uses, insn);
  for_each_rtx (x, add_uses, insn);
}
}
 
 
/* Add stores (register and memory references) LOC which will be tracked
/* Add stores (register and memory references) LOC which will be tracked
   to VTI (bb)->mos. EXPR is the RTL expression containing the store.
   to VTI (bb)->mos. EXPR is the RTL expression containing the store.
   INSN is instruction which the LOC is part of.  */
   INSN is instruction which the LOC is part of.  */
 
 
static void
static void
add_stores (rtx loc, rtx expr, void *insn)
add_stores (rtx loc, rtx expr, void *insn)
{
{
  if (REG_P (loc))
  if (REG_P (loc))
    {
    {
      basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
      basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
 
      if (GET_CODE (expr) == CLOBBER
      if (GET_CODE (expr) == CLOBBER
          || ! REG_EXPR (loc)
          || ! REG_EXPR (loc)
          || ! track_expr_p (REG_EXPR (loc)))
          || ! track_expr_p (REG_EXPR (loc)))
        mo->type = MO_CLOBBER;
        mo->type = MO_CLOBBER;
      else if (GET_CODE (expr) == SET
      else if (GET_CODE (expr) == SET
               && SET_DEST (expr) == loc
               && SET_DEST (expr) == loc
               && same_variable_part_p (SET_SRC (expr),
               && same_variable_part_p (SET_SRC (expr),
                                        REG_EXPR (loc),
                                        REG_EXPR (loc),
                                        REG_OFFSET (loc)))
                                        REG_OFFSET (loc)))
        mo->type = MO_COPY;
        mo->type = MO_COPY;
      else
      else
        mo->type = MO_SET;
        mo->type = MO_SET;
      mo->u.loc = loc;
      mo->u.loc = loc;
      mo->insn = NEXT_INSN ((rtx) insn);
      mo->insn = NEXT_INSN ((rtx) insn);
    }
    }
  else if (MEM_P (loc)
  else if (MEM_P (loc)
           && MEM_EXPR (loc)
           && MEM_EXPR (loc)
           && track_expr_p (MEM_EXPR (loc)))
           && track_expr_p (MEM_EXPR (loc)))
    {
    {
      basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
      basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
 
      if (GET_CODE (expr) == CLOBBER)
      if (GET_CODE (expr) == CLOBBER)
        mo->type = MO_CLOBBER;
        mo->type = MO_CLOBBER;
      else if (GET_CODE (expr) == SET
      else if (GET_CODE (expr) == SET
               && SET_DEST (expr) == loc
               && SET_DEST (expr) == loc
               && same_variable_part_p (SET_SRC (expr),
               && same_variable_part_p (SET_SRC (expr),
                                        MEM_EXPR (loc),
                                        MEM_EXPR (loc),
                                        MEM_OFFSET (loc)
                                        MEM_OFFSET (loc)
                                        ? INTVAL (MEM_OFFSET (loc)) : 0))
                                        ? INTVAL (MEM_OFFSET (loc)) : 0))
        mo->type = MO_COPY;
        mo->type = MO_COPY;
      else
      else
        mo->type = MO_SET;
        mo->type = MO_SET;
      mo->u.loc = loc;
      mo->u.loc = loc;
      mo->insn = NEXT_INSN ((rtx) insn);
      mo->insn = NEXT_INSN ((rtx) insn);
    }
    }
}
}
 
 
/* Compute the changes of variable locations in the basic block BB.  */
/* Compute the changes of variable locations in the basic block BB.  */
 
 
static bool
static bool
compute_bb_dataflow (basic_block bb)
compute_bb_dataflow (basic_block bb)
{
{
  int i, n, r;
  int i, n, r;
  bool changed;
  bool changed;
  dataflow_set old_out;
  dataflow_set old_out;
  dataflow_set *in = &VTI (bb)->in;
  dataflow_set *in = &VTI (bb)->in;
  dataflow_set *out = &VTI (bb)->out;
  dataflow_set *out = &VTI (bb)->out;
 
 
  dataflow_set_init (&old_out, htab_elements (VTI (bb)->out.vars) + 3);
  dataflow_set_init (&old_out, htab_elements (VTI (bb)->out.vars) + 3);
  dataflow_set_copy (&old_out, out);
  dataflow_set_copy (&old_out, out);
  dataflow_set_copy (out, in);
  dataflow_set_copy (out, in);
 
 
  n = VTI (bb)->n_mos;
  n = VTI (bb)->n_mos;
  for (i = 0; i < n; i++)
  for (i = 0; i < n; i++)
    {
    {
      switch (VTI (bb)->mos[i].type)
      switch (VTI (bb)->mos[i].type)
        {
        {
          case MO_CALL:
          case MO_CALL:
            for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
            for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
              if (TEST_HARD_REG_BIT (call_used_reg_set, r))
              if (TEST_HARD_REG_BIT (call_used_reg_set, r))
                var_regno_delete (out, r);
                var_regno_delete (out, r);
            break;
            break;
 
 
          case MO_USE:
          case MO_USE:
            {
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
              rtx loc = VTI (bb)->mos[i].u.loc;
 
 
              if (GET_CODE (loc) == REG)
              if (GET_CODE (loc) == REG)
                var_reg_set (out, loc);
                var_reg_set (out, loc);
              else if (GET_CODE (loc) == MEM)
              else if (GET_CODE (loc) == MEM)
                var_mem_set (out, loc);
                var_mem_set (out, loc);
            }
            }
            break;
            break;
 
 
          case MO_SET:
          case MO_SET:
            {
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
              rtx loc = VTI (bb)->mos[i].u.loc;
 
 
              if (REG_P (loc))
              if (REG_P (loc))
                var_reg_delete_and_set (out, loc, true);
                var_reg_delete_and_set (out, loc, true);
              else if (MEM_P (loc))
              else if (MEM_P (loc))
                var_mem_delete_and_set (out, loc, true);
                var_mem_delete_and_set (out, loc, true);
            }
            }
            break;
            break;
 
 
          case MO_COPY:
          case MO_COPY:
            {
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
              rtx loc = VTI (bb)->mos[i].u.loc;
 
 
              if (REG_P (loc))
              if (REG_P (loc))
                var_reg_delete_and_set (out, loc, false);
                var_reg_delete_and_set (out, loc, false);
              else if (MEM_P (loc))
              else if (MEM_P (loc))
                var_mem_delete_and_set (out, loc, false);
                var_mem_delete_and_set (out, loc, false);
            }
            }
            break;
            break;
 
 
          case MO_USE_NO_VAR:
          case MO_USE_NO_VAR:
            {
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
              rtx loc = VTI (bb)->mos[i].u.loc;
 
 
              if (REG_P (loc))
              if (REG_P (loc))
                var_reg_delete (out, loc, false);
                var_reg_delete (out, loc, false);
              else if (MEM_P (loc))
              else if (MEM_P (loc))
                var_mem_delete (out, loc, false);
                var_mem_delete (out, loc, false);
            }
            }
            break;
            break;
 
 
          case MO_CLOBBER:
          case MO_CLOBBER:
            {
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
              rtx loc = VTI (bb)->mos[i].u.loc;
 
 
              if (REG_P (loc))
              if (REG_P (loc))
                var_reg_delete (out, loc, true);
                var_reg_delete (out, loc, true);
              else if (MEM_P (loc))
              else if (MEM_P (loc))
                var_mem_delete (out, loc, true);
                var_mem_delete (out, loc, true);
            }
            }
            break;
            break;
 
 
          case MO_ADJUST:
          case MO_ADJUST:
            out->stack_adjust += VTI (bb)->mos[i].u.adjust;
            out->stack_adjust += VTI (bb)->mos[i].u.adjust;
            break;
            break;
        }
        }
    }
    }
 
 
  changed = dataflow_set_different (&old_out, out);
  changed = dataflow_set_different (&old_out, out);
  dataflow_set_destroy (&old_out);
  dataflow_set_destroy (&old_out);
  return changed;
  return changed;
}
}
 
 
/* Find the locations of variables in the whole function.  */
/* Find the locations of variables in the whole function.  */
 
 
static void
static void
vt_find_locations (void)
vt_find_locations (void)
{
{
  fibheap_t worklist, pending, fibheap_swap;
  fibheap_t worklist, pending, fibheap_swap;
  sbitmap visited, in_worklist, in_pending, sbitmap_swap;
  sbitmap visited, in_worklist, in_pending, sbitmap_swap;
  basic_block bb;
  basic_block bb;
  edge e;
  edge e;
  int *bb_order;
  int *bb_order;
  int *rc_order;
  int *rc_order;
  int i;
  int i;
 
 
  /* Compute reverse completion order of depth first search of the CFG
  /* Compute reverse completion order of depth first search of the CFG
     so that the data-flow runs faster.  */
     so that the data-flow runs faster.  */
  rc_order = XNEWVEC (int, n_basic_blocks - NUM_FIXED_BLOCKS);
  rc_order = XNEWVEC (int, n_basic_blocks - NUM_FIXED_BLOCKS);
  bb_order = XNEWVEC (int, last_basic_block);
  bb_order = XNEWVEC (int, last_basic_block);
  pre_and_rev_post_order_compute (NULL, rc_order, false);
  pre_and_rev_post_order_compute (NULL, rc_order, false);
  for (i = 0; i < n_basic_blocks - NUM_FIXED_BLOCKS; i++)
  for (i = 0; i < n_basic_blocks - NUM_FIXED_BLOCKS; i++)
    bb_order[rc_order[i]] = i;
    bb_order[rc_order[i]] = i;
  free (rc_order);
  free (rc_order);
 
 
  worklist = fibheap_new ();
  worklist = fibheap_new ();
  pending = fibheap_new ();
  pending = fibheap_new ();
  visited = sbitmap_alloc (last_basic_block);
  visited = sbitmap_alloc (last_basic_block);
  in_worklist = sbitmap_alloc (last_basic_block);
  in_worklist = sbitmap_alloc (last_basic_block);
  in_pending = sbitmap_alloc (last_basic_block);
  in_pending = sbitmap_alloc (last_basic_block);
  sbitmap_zero (in_worklist);
  sbitmap_zero (in_worklist);
 
 
  FOR_EACH_BB (bb)
  FOR_EACH_BB (bb)
    fibheap_insert (pending, bb_order[bb->index], bb);
    fibheap_insert (pending, bb_order[bb->index], bb);
  sbitmap_ones (in_pending);
  sbitmap_ones (in_pending);
 
 
  while (!fibheap_empty (pending))
  while (!fibheap_empty (pending))
    {
    {
      fibheap_swap = pending;
      fibheap_swap = pending;
      pending = worklist;
      pending = worklist;
      worklist = fibheap_swap;
      worklist = fibheap_swap;
      sbitmap_swap = in_pending;
      sbitmap_swap = in_pending;
      in_pending = in_worklist;
      in_pending = in_worklist;
      in_worklist = sbitmap_swap;
      in_worklist = sbitmap_swap;
 
 
      sbitmap_zero (visited);
      sbitmap_zero (visited);
 
 
      while (!fibheap_empty (worklist))
      while (!fibheap_empty (worklist))
        {
        {
          bb = fibheap_extract_min (worklist);
          bb = fibheap_extract_min (worklist);
          RESET_BIT (in_worklist, bb->index);
          RESET_BIT (in_worklist, bb->index);
          if (!TEST_BIT (visited, bb->index))
          if (!TEST_BIT (visited, bb->index))
            {
            {
              bool changed;
              bool changed;
              edge_iterator ei;
              edge_iterator ei;
 
 
              SET_BIT (visited, bb->index);
              SET_BIT (visited, bb->index);
 
 
              /* Calculate the IN set as union of predecessor OUT sets.  */
              /* Calculate the IN set as union of predecessor OUT sets.  */
              dataflow_set_clear (&VTI (bb)->in);
              dataflow_set_clear (&VTI (bb)->in);
              FOR_EACH_EDGE (e, ei, bb->preds)
              FOR_EACH_EDGE (e, ei, bb->preds)
                {
                {
                  dataflow_set_union (&VTI (bb)->in, &VTI (e->src)->out);
                  dataflow_set_union (&VTI (bb)->in, &VTI (e->src)->out);
                }
                }
 
 
              changed = compute_bb_dataflow (bb);
              changed = compute_bb_dataflow (bb);
              if (changed)
              if (changed)
                {
                {
                  FOR_EACH_EDGE (e, ei, bb->succs)
                  FOR_EACH_EDGE (e, ei, bb->succs)
                    {
                    {
                      if (e->dest == EXIT_BLOCK_PTR)
                      if (e->dest == EXIT_BLOCK_PTR)
                        continue;
                        continue;
 
 
                      if (e->dest == bb)
                      if (e->dest == bb)
                        continue;
                        continue;
 
 
                      if (TEST_BIT (visited, e->dest->index))
                      if (TEST_BIT (visited, e->dest->index))
                        {
                        {
                          if (!TEST_BIT (in_pending, e->dest->index))
                          if (!TEST_BIT (in_pending, e->dest->index))
                            {
                            {
                              /* Send E->DEST to next round.  */
                              /* Send E->DEST to next round.  */
                              SET_BIT (in_pending, e->dest->index);
                              SET_BIT (in_pending, e->dest->index);
                              fibheap_insert (pending,
                              fibheap_insert (pending,
                                              bb_order[e->dest->index],
                                              bb_order[e->dest->index],
                                              e->dest);
                                              e->dest);
                            }
                            }
                        }
                        }
                      else if (!TEST_BIT (in_worklist, e->dest->index))
                      else if (!TEST_BIT (in_worklist, e->dest->index))
                        {
                        {
                          /* Add E->DEST to current round.  */
                          /* Add E->DEST to current round.  */
                          SET_BIT (in_worklist, e->dest->index);
                          SET_BIT (in_worklist, e->dest->index);
                          fibheap_insert (worklist, bb_order[e->dest->index],
                          fibheap_insert (worklist, bb_order[e->dest->index],
                                          e->dest);
                                          e->dest);
                        }
                        }
                    }
                    }
                }
                }
            }
            }
        }
        }
    }
    }
 
 
  free (bb_order);
  free (bb_order);
  fibheap_delete (worklist);
  fibheap_delete (worklist);
  fibheap_delete (pending);
  fibheap_delete (pending);
  sbitmap_free (visited);
  sbitmap_free (visited);
  sbitmap_free (in_worklist);
  sbitmap_free (in_worklist);
  sbitmap_free (in_pending);
  sbitmap_free (in_pending);
}
}
 
 
/* Print the content of the LIST to dump file.  */
/* Print the content of the LIST to dump file.  */
 
 
static void
static void
dump_attrs_list (attrs list)
dump_attrs_list (attrs list)
{
{
  for (; list; list = list->next)
  for (; list; list = list->next)
    {
    {
      print_mem_expr (dump_file, list->decl);
      print_mem_expr (dump_file, list->decl);
      fprintf (dump_file, "+" HOST_WIDE_INT_PRINT_DEC, list->offset);
      fprintf (dump_file, "+" HOST_WIDE_INT_PRINT_DEC, list->offset);
    }
    }
  fprintf (dump_file, "\n");
  fprintf (dump_file, "\n");
}
}
 
 
/* Print the information about variable *SLOT to dump file.  */
/* Print the information about variable *SLOT to dump file.  */
 
 
static int
static int
dump_variable (void **slot, void *data ATTRIBUTE_UNUSED)
dump_variable (void **slot, void *data ATTRIBUTE_UNUSED)
{
{
  variable var = *(variable *) slot;
  variable var = *(variable *) slot;
  int i;
  int i;
  location_chain node;
  location_chain node;
 
 
  fprintf (dump_file, "  name: %s\n",
  fprintf (dump_file, "  name: %s\n",
           IDENTIFIER_POINTER (DECL_NAME (var->decl)));
           IDENTIFIER_POINTER (DECL_NAME (var->decl)));
  for (i = 0; i < var->n_var_parts; i++)
  for (i = 0; i < var->n_var_parts; i++)
    {
    {
      fprintf (dump_file, "    offset %ld\n",
      fprintf (dump_file, "    offset %ld\n",
               (long) var->var_part[i].offset);
               (long) var->var_part[i].offset);
      for (node = var->var_part[i].loc_chain; node; node = node->next)
      for (node = var->var_part[i].loc_chain; node; node = node->next)
        {
        {
          fprintf (dump_file, "      ");
          fprintf (dump_file, "      ");
          print_rtl_single (dump_file, node->loc);
          print_rtl_single (dump_file, node->loc);
        }
        }
    }
    }
 
 
  /* Continue traversing the hash table.  */
  /* Continue traversing the hash table.  */
  return 1;
  return 1;
}
}
 
 
/* Print the information about variables from hash table VARS to dump file.  */
/* Print the information about variables from hash table VARS to dump file.  */
 
 
static void
static void
dump_vars (htab_t vars)
dump_vars (htab_t vars)
{
{
  if (htab_elements (vars) > 0)
  if (htab_elements (vars) > 0)
    {
    {
      fprintf (dump_file, "Variables:\n");
      fprintf (dump_file, "Variables:\n");
      htab_traverse (vars, dump_variable, NULL);
      htab_traverse (vars, dump_variable, NULL);
    }
    }
}
}
 
 
/* Print the dataflow set SET to dump file.  */
/* Print the dataflow set SET to dump file.  */
 
 
static void
static void
dump_dataflow_set (dataflow_set *set)
dump_dataflow_set (dataflow_set *set)
{
{
  int i;
  int i;
 
 
  fprintf (dump_file, "Stack adjustment: " HOST_WIDE_INT_PRINT_DEC "\n",
  fprintf (dump_file, "Stack adjustment: " HOST_WIDE_INT_PRINT_DEC "\n",
           set->stack_adjust);
           set->stack_adjust);
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
    {
    {
      if (set->regs[i])
      if (set->regs[i])
        {
        {
          fprintf (dump_file, "Reg %d:", i);
          fprintf (dump_file, "Reg %d:", i);
          dump_attrs_list (set->regs[i]);
          dump_attrs_list (set->regs[i]);
        }
        }
    }
    }
  dump_vars (set->vars);
  dump_vars (set->vars);
  fprintf (dump_file, "\n");
  fprintf (dump_file, "\n");
}
}
 
 
/* Print the IN and OUT sets for each basic block to dump file.  */
/* Print the IN and OUT sets for each basic block to dump file.  */
 
 
static void
static void
dump_dataflow_sets (void)
dump_dataflow_sets (void)
{
{
  basic_block bb;
  basic_block bb;
 
 
  FOR_EACH_BB (bb)
  FOR_EACH_BB (bb)
    {
    {
      fprintf (dump_file, "\nBasic block %d:\n", bb->index);
      fprintf (dump_file, "\nBasic block %d:\n", bb->index);
      fprintf (dump_file, "IN:\n");
      fprintf (dump_file, "IN:\n");
      dump_dataflow_set (&VTI (bb)->in);
      dump_dataflow_set (&VTI (bb)->in);
      fprintf (dump_file, "OUT:\n");
      fprintf (dump_file, "OUT:\n");
      dump_dataflow_set (&VTI (bb)->out);
      dump_dataflow_set (&VTI (bb)->out);
    }
    }
}
}
 
 
/* Add variable VAR to the hash table of changed variables and
/* Add variable VAR to the hash table of changed variables and
   if it has no locations delete it from hash table HTAB.  */
   if it has no locations delete it from hash table HTAB.  */
 
 
static void
static void
variable_was_changed (variable var, htab_t htab)
variable_was_changed (variable var, htab_t htab)
{
{
  hashval_t hash = VARIABLE_HASH_VAL (var->decl);
  hashval_t hash = VARIABLE_HASH_VAL (var->decl);
 
 
  if (emit_notes)
  if (emit_notes)
    {
    {
      variable *slot;
      variable *slot;
 
 
      slot = (variable *) htab_find_slot_with_hash (changed_variables,
      slot = (variable *) htab_find_slot_with_hash (changed_variables,
                                                    var->decl, hash, INSERT);
                                                    var->decl, hash, INSERT);
 
 
      if (htab && var->n_var_parts == 0)
      if (htab && var->n_var_parts == 0)
        {
        {
          variable empty_var;
          variable empty_var;
          void **old;
          void **old;
 
 
          empty_var = pool_alloc (var_pool);
          empty_var = pool_alloc (var_pool);
          empty_var->decl = var->decl;
          empty_var->decl = var->decl;
          empty_var->refcount = 1;
          empty_var->refcount = 1;
          empty_var->n_var_parts = 0;
          empty_var->n_var_parts = 0;
          *slot = empty_var;
          *slot = empty_var;
 
 
          old = htab_find_slot_with_hash (htab, var->decl, hash,
          old = htab_find_slot_with_hash (htab, var->decl, hash,
                                          NO_INSERT);
                                          NO_INSERT);
          if (old)
          if (old)
            htab_clear_slot (htab, old);
            htab_clear_slot (htab, old);
        }
        }
      else
      else
        {
        {
          *slot = var;
          *slot = var;
        }
        }
    }
    }
  else
  else
    {
    {
      gcc_assert (htab);
      gcc_assert (htab);
      if (var->n_var_parts == 0)
      if (var->n_var_parts == 0)
        {
        {
          void **slot = htab_find_slot_with_hash (htab, var->decl, hash,
          void **slot = htab_find_slot_with_hash (htab, var->decl, hash,
                                                  NO_INSERT);
                                                  NO_INSERT);
          if (slot)
          if (slot)
            htab_clear_slot (htab, slot);
            htab_clear_slot (htab, slot);
        }
        }
    }
    }
}
}
 
 
/* Look for the index in VAR->var_part corresponding to OFFSET.
/* Look for the index in VAR->var_part corresponding to OFFSET.
   Return -1 if not found.  If INSERTION_POINT is non-NULL, the
   Return -1 if not found.  If INSERTION_POINT is non-NULL, the
   referenced int will be set to the index that the part has or should
   referenced int will be set to the index that the part has or should
   have, if it should be inserted.  */
   have, if it should be inserted.  */
 
 
static inline int
static inline int
find_variable_location_part (variable var, HOST_WIDE_INT offset,
find_variable_location_part (variable var, HOST_WIDE_INT offset,
                             int *insertion_point)
                             int *insertion_point)
{
{
  int pos, low, high;
  int pos, low, high;
 
 
  /* Find the location part.  */
  /* Find the location part.  */
  low = 0;
  low = 0;
  high = var->n_var_parts;
  high = var->n_var_parts;
  while (low != high)
  while (low != high)
    {
    {
      pos = (low + high) / 2;
      pos = (low + high) / 2;
      if (var->var_part[pos].offset < offset)
      if (var->var_part[pos].offset < offset)
        low = pos + 1;
        low = pos + 1;
      else
      else
        high = pos;
        high = pos;
    }
    }
  pos = low;
  pos = low;
 
 
  if (insertion_point)
  if (insertion_point)
    *insertion_point = pos;
    *insertion_point = pos;
 
 
  if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
  if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
    return pos;
    return pos;
 
 
  return -1;
  return -1;
}
}
 
 
/* Set the part of variable's location in the dataflow set SET.  The variable
/* Set the part of variable's location in the dataflow set SET.  The variable
   part is specified by variable's declaration DECL and offset OFFSET and the
   part is specified by variable's declaration DECL and offset OFFSET and the
   part's location by LOC.  */
   part's location by LOC.  */
 
 
static void
static void
set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset)
set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset)
{
{
  int pos;
  int pos;
  location_chain node, next;
  location_chain node, next;
  location_chain *nextp;
  location_chain *nextp;
  variable var;
  variable var;
  void **slot;
  void **slot;
 
 
  slot = htab_find_slot_with_hash (set->vars, decl,
  slot = htab_find_slot_with_hash (set->vars, decl,
                                   VARIABLE_HASH_VAL (decl), INSERT);
                                   VARIABLE_HASH_VAL (decl), INSERT);
  if (!*slot)
  if (!*slot)
    {
    {
      /* Create new variable information.  */
      /* Create new variable information.  */
      var = pool_alloc (var_pool);
      var = pool_alloc (var_pool);
      var->decl = decl;
      var->decl = decl;
      var->refcount = 1;
      var->refcount = 1;
      var->n_var_parts = 1;
      var->n_var_parts = 1;
      var->var_part[0].offset = offset;
      var->var_part[0].offset = offset;
      var->var_part[0].loc_chain = NULL;
      var->var_part[0].loc_chain = NULL;
      var->var_part[0].cur_loc = NULL;
      var->var_part[0].cur_loc = NULL;
      *slot = var;
      *slot = var;
      pos = 0;
      pos = 0;
    }
    }
  else
  else
    {
    {
      int inspos = 0;
      int inspos = 0;
 
 
      var = (variable) *slot;
      var = (variable) *slot;
 
 
      pos = find_variable_location_part (var, offset, &inspos);
      pos = find_variable_location_part (var, offset, &inspos);
 
 
      if (pos >= 0)
      if (pos >= 0)
        {
        {
          node = var->var_part[pos].loc_chain;
          node = var->var_part[pos].loc_chain;
 
 
          if (node
          if (node
              && ((REG_P (node->loc) && REG_P (loc)
              && ((REG_P (node->loc) && REG_P (loc)
                   && REGNO (node->loc) == REGNO (loc))
                   && REGNO (node->loc) == REGNO (loc))
                  || rtx_equal_p (node->loc, loc)))
                  || rtx_equal_p (node->loc, loc)))
            {
            {
              /* LOC is in the beginning of the chain so we have nothing
              /* LOC is in the beginning of the chain so we have nothing
                 to do.  */
                 to do.  */
              return;
              return;
            }
            }
          else
          else
            {
            {
              /* We have to make a copy of a shared variable.  */
              /* We have to make a copy of a shared variable.  */
              if (var->refcount > 1)
              if (var->refcount > 1)
                var = unshare_variable (set, var);
                var = unshare_variable (set, var);
            }
            }
        }
        }
      else
      else
        {
        {
          /* We have not found the location part, new one will be created.  */
          /* We have not found the location part, new one will be created.  */
 
 
          /* We have to make a copy of the shared variable.  */
          /* We have to make a copy of the shared variable.  */
          if (var->refcount > 1)
          if (var->refcount > 1)
            var = unshare_variable (set, var);
            var = unshare_variable (set, var);
 
 
          /* We track only variables whose size is <= MAX_VAR_PARTS bytes
          /* We track only variables whose size is <= MAX_VAR_PARTS bytes
             thus there are at most MAX_VAR_PARTS different offsets.  */
             thus there are at most MAX_VAR_PARTS different offsets.  */
          gcc_assert (var->n_var_parts < MAX_VAR_PARTS);
          gcc_assert (var->n_var_parts < MAX_VAR_PARTS);
 
 
          /* We have to move the elements of array starting at index
          /* We have to move the elements of array starting at index
             inspos to the next position.  */
             inspos to the next position.  */
          for (pos = var->n_var_parts; pos > inspos; pos--)
          for (pos = var->n_var_parts; pos > inspos; pos--)
            var->var_part[pos] = var->var_part[pos - 1];
            var->var_part[pos] = var->var_part[pos - 1];
 
 
          var->n_var_parts++;
          var->n_var_parts++;
          var->var_part[pos].offset = offset;
          var->var_part[pos].offset = offset;
          var->var_part[pos].loc_chain = NULL;
          var->var_part[pos].loc_chain = NULL;
          var->var_part[pos].cur_loc = NULL;
          var->var_part[pos].cur_loc = NULL;
        }
        }
    }
    }
 
 
  /* Delete the location from the list.  */
  /* Delete the location from the list.  */
  nextp = &var->var_part[pos].loc_chain;
  nextp = &var->var_part[pos].loc_chain;
  for (node = var->var_part[pos].loc_chain; node; node = next)
  for (node = var->var_part[pos].loc_chain; node; node = next)
    {
    {
      next = node->next;
      next = node->next;
      if ((REG_P (node->loc) && REG_P (loc)
      if ((REG_P (node->loc) && REG_P (loc)
           && REGNO (node->loc) == REGNO (loc))
           && REGNO (node->loc) == REGNO (loc))
          || rtx_equal_p (node->loc, loc))
          || rtx_equal_p (node->loc, loc))
        {
        {
          pool_free (loc_chain_pool, node);
          pool_free (loc_chain_pool, node);
          *nextp = next;
          *nextp = next;
          break;
          break;
        }
        }
      else
      else
        nextp = &node->next;
        nextp = &node->next;
    }
    }
 
 
  /* Add the location to the beginning.  */
  /* Add the location to the beginning.  */
  node = pool_alloc (loc_chain_pool);
  node = pool_alloc (loc_chain_pool);
  node->loc = loc;
  node->loc = loc;
  node->next = var->var_part[pos].loc_chain;
  node->next = var->var_part[pos].loc_chain;
  var->var_part[pos].loc_chain = node;
  var->var_part[pos].loc_chain = node;
 
 
  /* If no location was emitted do so.  */
  /* If no location was emitted do so.  */
  if (var->var_part[pos].cur_loc == NULL)
  if (var->var_part[pos].cur_loc == NULL)
    {
    {
      var->var_part[pos].cur_loc = loc;
      var->var_part[pos].cur_loc = loc;
      variable_was_changed (var, set->vars);
      variable_was_changed (var, set->vars);
    }
    }
}
}
 
 
/* Remove all recorded register locations for the given variable part
/* Remove all recorded register locations for the given variable part
   from dataflow set SET, except for those that are identical to loc.
   from dataflow set SET, except for those that are identical to loc.
   The variable part is specified by variable's declaration DECL and
   The variable part is specified by variable's declaration DECL and
   offset OFFSET.  */
   offset OFFSET.  */
 
 
static void
static void
clobber_variable_part (dataflow_set *set, rtx loc, tree decl,
clobber_variable_part (dataflow_set *set, rtx loc, tree decl,
                      HOST_WIDE_INT offset)
                      HOST_WIDE_INT offset)
{
{
  void **slot;
  void **slot;
 
 
  if (! decl || ! DECL_P (decl))
  if (! decl || ! DECL_P (decl))
    return;
    return;
 
 
  slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
  slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
                                   NO_INSERT);
                                   NO_INSERT);
  if (slot)
  if (slot)
    {
    {
      variable var = (variable) *slot;
      variable var = (variable) *slot;
      int pos = find_variable_location_part (var, offset, NULL);
      int pos = find_variable_location_part (var, offset, NULL);
 
 
      if (pos >= 0)
      if (pos >= 0)
        {
        {
          location_chain node, next;
          location_chain node, next;
 
 
          /* Remove the register locations from the dataflow set.  */
          /* Remove the register locations from the dataflow set.  */
          next = var->var_part[pos].loc_chain;
          next = var->var_part[pos].loc_chain;
          for (node = next; node; node = next)
          for (node = next; node; node = next)
            {
            {
              next = node->next;
              next = node->next;
              if (node->loc != loc)
              if (node->loc != loc)
                {
                {
                  if (REG_P (node->loc))
                  if (REG_P (node->loc))
                    {
                    {
                      attrs anode, anext;
                      attrs anode, anext;
                      attrs *anextp;
                      attrs *anextp;
 
 
                      /* Remove the variable part from the register's
                      /* Remove the variable part from the register's
                         list, but preserve any other variable parts
                         list, but preserve any other variable parts
                         that might be regarded as live in that same
                         that might be regarded as live in that same
                         register.  */
                         register.  */
                      anextp = &set->regs[REGNO (node->loc)];
                      anextp = &set->regs[REGNO (node->loc)];
                      for (anode = *anextp; anode; anode = anext)
                      for (anode = *anextp; anode; anode = anext)
                        {
                        {
                          anext = anode->next;
                          anext = anode->next;
                          if (anode->decl == decl
                          if (anode->decl == decl
                              && anode->offset == offset)
                              && anode->offset == offset)
                            {
                            {
                              pool_free (attrs_pool, anode);
                              pool_free (attrs_pool, anode);
                              *anextp = anext;
                              *anextp = anext;
                            }
                            }
                        }
                        }
                    }
                    }
 
 
                  delete_variable_part (set, node->loc, decl, offset);
                  delete_variable_part (set, node->loc, decl, offset);
                }
                }
            }
            }
        }
        }
    }
    }
}
}
 
 
/* Delete the part of variable's location from dataflow set SET.  The variable
/* Delete the part of variable's location from dataflow set SET.  The variable
   part is specified by variable's declaration DECL and offset OFFSET and the
   part is specified by variable's declaration DECL and offset OFFSET and the
   part's location by LOC.  */
   part's location by LOC.  */
 
 
static void
static void
delete_variable_part (dataflow_set *set, rtx loc, tree decl,
delete_variable_part (dataflow_set *set, rtx loc, tree decl,
                      HOST_WIDE_INT offset)
                      HOST_WIDE_INT offset)
{
{
  void **slot;
  void **slot;
 
 
  slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
  slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
                                   NO_INSERT);
                                   NO_INSERT);
  if (slot)
  if (slot)
    {
    {
      variable var = (variable) *slot;
      variable var = (variable) *slot;
      int pos = find_variable_location_part (var, offset, NULL);
      int pos = find_variable_location_part (var, offset, NULL);
 
 
      if (pos >= 0)
      if (pos >= 0)
        {
        {
          location_chain node, next;
          location_chain node, next;
          location_chain *nextp;
          location_chain *nextp;
          bool changed;
          bool changed;
 
 
          if (var->refcount > 1)
          if (var->refcount > 1)
            {
            {
              /* If the variable contains the location part we have to
              /* If the variable contains the location part we have to
                 make a copy of the variable.  */
                 make a copy of the variable.  */
              for (node = var->var_part[pos].loc_chain; node;
              for (node = var->var_part[pos].loc_chain; node;
                   node = node->next)
                   node = node->next)
                {
                {
                  if ((REG_P (node->loc) && REG_P (loc)
                  if ((REG_P (node->loc) && REG_P (loc)
                       && REGNO (node->loc) == REGNO (loc))
                       && REGNO (node->loc) == REGNO (loc))
                      || rtx_equal_p (node->loc, loc))
                      || rtx_equal_p (node->loc, loc))
                    {
                    {
                      var = unshare_variable (set, var);
                      var = unshare_variable (set, var);
                      break;
                      break;
                    }
                    }
                }
                }
            }
            }
 
 
          /* Delete the location part.  */
          /* Delete the location part.  */
          nextp = &var->var_part[pos].loc_chain;
          nextp = &var->var_part[pos].loc_chain;
          for (node = *nextp; node; node = next)
          for (node = *nextp; node; node = next)
            {
            {
              next = node->next;
              next = node->next;
              if ((REG_P (node->loc) && REG_P (loc)
              if ((REG_P (node->loc) && REG_P (loc)
                   && REGNO (node->loc) == REGNO (loc))
                   && REGNO (node->loc) == REGNO (loc))
                  || rtx_equal_p (node->loc, loc))
                  || rtx_equal_p (node->loc, loc))
                {
                {
                  pool_free (loc_chain_pool, node);
                  pool_free (loc_chain_pool, node);
                  *nextp = next;
                  *nextp = next;
                  break;
                  break;
                }
                }
              else
              else
                nextp = &node->next;
                nextp = &node->next;
            }
            }
 
 
          /* If we have deleted the location which was last emitted
          /* If we have deleted the location which was last emitted
             we have to emit new location so add the variable to set
             we have to emit new location so add the variable to set
             of changed variables.  */
             of changed variables.  */
          if (var->var_part[pos].cur_loc
          if (var->var_part[pos].cur_loc
              && ((REG_P (loc)
              && ((REG_P (loc)
                   && REG_P (var->var_part[pos].cur_loc)
                   && REG_P (var->var_part[pos].cur_loc)
                   && REGNO (loc) == REGNO (var->var_part[pos].cur_loc))
                   && REGNO (loc) == REGNO (var->var_part[pos].cur_loc))
                  || rtx_equal_p (loc, var->var_part[pos].cur_loc)))
                  || rtx_equal_p (loc, var->var_part[pos].cur_loc)))
            {
            {
              changed = true;
              changed = true;
              if (var->var_part[pos].loc_chain)
              if (var->var_part[pos].loc_chain)
                var->var_part[pos].cur_loc = var->var_part[pos].loc_chain->loc;
                var->var_part[pos].cur_loc = var->var_part[pos].loc_chain->loc;
            }
            }
          else
          else
            changed = false;
            changed = false;
 
 
          if (var->var_part[pos].loc_chain == NULL)
          if (var->var_part[pos].loc_chain == NULL)
            {
            {
              var->n_var_parts--;
              var->n_var_parts--;
              while (pos < var->n_var_parts)
              while (pos < var->n_var_parts)
                {
                {
                  var->var_part[pos] = var->var_part[pos + 1];
                  var->var_part[pos] = var->var_part[pos + 1];
                  pos++;
                  pos++;
                }
                }
            }
            }
          if (changed)
          if (changed)
            variable_was_changed (var, set->vars);
            variable_was_changed (var, set->vars);
        }
        }
    }
    }
}
}
 
 
/* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP.  DATA contains
/* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP.  DATA contains
   additional parameters: WHERE specifies whether the note shall be emitted
   additional parameters: WHERE specifies whether the note shall be emitted
   before of after instruction INSN.  */
   before of after instruction INSN.  */
 
 
static int
static int
emit_note_insn_var_location (void **varp, void *data)
emit_note_insn_var_location (void **varp, void *data)
{
{
  variable var = *(variable *) varp;
  variable var = *(variable *) varp;
  rtx insn = ((emit_note_data *)data)->insn;
  rtx insn = ((emit_note_data *)data)->insn;
  enum emit_note_where where = ((emit_note_data *)data)->where;
  enum emit_note_where where = ((emit_note_data *)data)->where;
  rtx note;
  rtx note;
  int i, j, n_var_parts;
  int i, j, n_var_parts;
  bool complete;
  bool complete;
  HOST_WIDE_INT last_limit;
  HOST_WIDE_INT last_limit;
  tree type_size_unit;
  tree type_size_unit;
  HOST_WIDE_INT offsets[MAX_VAR_PARTS];
  HOST_WIDE_INT offsets[MAX_VAR_PARTS];
  rtx loc[MAX_VAR_PARTS];
  rtx loc[MAX_VAR_PARTS];
 
 
  gcc_assert (var->decl);
  gcc_assert (var->decl);
 
 
  complete = true;
  complete = true;
  last_limit = 0;
  last_limit = 0;
  n_var_parts = 0;
  n_var_parts = 0;
  for (i = 0; i < var->n_var_parts; i++)
  for (i = 0; i < var->n_var_parts; i++)
    {
    {
      enum machine_mode mode, wider_mode;
      enum machine_mode mode, wider_mode;
 
 
      if (last_limit < var->var_part[i].offset)
      if (last_limit < var->var_part[i].offset)
        {
        {
          complete = false;
          complete = false;
          break;
          break;
        }
        }
      else if (last_limit > var->var_part[i].offset)
      else if (last_limit > var->var_part[i].offset)
        continue;
        continue;
      offsets[n_var_parts] = var->var_part[i].offset;
      offsets[n_var_parts] = var->var_part[i].offset;
      loc[n_var_parts] = var->var_part[i].loc_chain->loc;
      loc[n_var_parts] = var->var_part[i].loc_chain->loc;
      mode = GET_MODE (loc[n_var_parts]);
      mode = GET_MODE (loc[n_var_parts]);
      last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
      last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
 
 
      /* Attempt to merge adjacent registers or memory.  */
      /* Attempt to merge adjacent registers or memory.  */
      wider_mode = GET_MODE_WIDER_MODE (mode);
      wider_mode = GET_MODE_WIDER_MODE (mode);
      for (j = i + 1; j < var->n_var_parts; j++)
      for (j = i + 1; j < var->n_var_parts; j++)
        if (last_limit <= var->var_part[j].offset)
        if (last_limit <= var->var_part[j].offset)
          break;
          break;
      if (j < var->n_var_parts
      if (j < var->n_var_parts
          && wider_mode != VOIDmode
          && wider_mode != VOIDmode
          && GET_CODE (loc[n_var_parts])
          && GET_CODE (loc[n_var_parts])
             == GET_CODE (var->var_part[j].loc_chain->loc)
             == GET_CODE (var->var_part[j].loc_chain->loc)
          && mode == GET_MODE (var->var_part[j].loc_chain->loc)
          && mode == GET_MODE (var->var_part[j].loc_chain->loc)
          && last_limit == var->var_part[j].offset)
          && last_limit == var->var_part[j].offset)
        {
        {
          rtx new_loc = NULL;
          rtx new_loc = NULL;
          rtx loc2 = var->var_part[j].loc_chain->loc;
          rtx loc2 = var->var_part[j].loc_chain->loc;
 
 
          if (REG_P (loc[n_var_parts])
          if (REG_P (loc[n_var_parts])
              && hard_regno_nregs[REGNO (loc[n_var_parts])][mode] * 2
              && hard_regno_nregs[REGNO (loc[n_var_parts])][mode] * 2
                 == hard_regno_nregs[REGNO (loc[n_var_parts])][wider_mode]
                 == hard_regno_nregs[REGNO (loc[n_var_parts])][wider_mode]
              && REGNO (loc[n_var_parts])
              && REGNO (loc[n_var_parts])
                 + hard_regno_nregs[REGNO (loc[n_var_parts])][mode]
                 + hard_regno_nregs[REGNO (loc[n_var_parts])][mode]
                 == REGNO (loc2))
                 == REGNO (loc2))
            {
            {
              if (! WORDS_BIG_ENDIAN && ! BYTES_BIG_ENDIAN)
              if (! WORDS_BIG_ENDIAN && ! BYTES_BIG_ENDIAN)
                new_loc = simplify_subreg (wider_mode, loc[n_var_parts],
                new_loc = simplify_subreg (wider_mode, loc[n_var_parts],
                                           mode, 0);
                                           mode, 0);
              else if (WORDS_BIG_ENDIAN && BYTES_BIG_ENDIAN)
              else if (WORDS_BIG_ENDIAN && BYTES_BIG_ENDIAN)
                new_loc = simplify_subreg (wider_mode, loc2, mode, 0);
                new_loc = simplify_subreg (wider_mode, loc2, mode, 0);
              if (new_loc)
              if (new_loc)
                {
                {
                  if (!REG_P (new_loc)
                  if (!REG_P (new_loc)
                      || REGNO (new_loc) != REGNO (loc[n_var_parts]))
                      || REGNO (new_loc) != REGNO (loc[n_var_parts]))
                    new_loc = NULL;
                    new_loc = NULL;
                  else
                  else
                    REG_ATTRS (new_loc) = REG_ATTRS (loc[n_var_parts]);
                    REG_ATTRS (new_loc) = REG_ATTRS (loc[n_var_parts]);
                }
                }
            }
            }
          else if (MEM_P (loc[n_var_parts])
          else if (MEM_P (loc[n_var_parts])
                   && GET_CODE (XEXP (loc2, 0)) == PLUS
                   && GET_CODE (XEXP (loc2, 0)) == PLUS
                   && GET_CODE (XEXP (XEXP (loc2, 0), 0)) == REG
                   && GET_CODE (XEXP (XEXP (loc2, 0), 0)) == REG
                   && GET_CODE (XEXP (XEXP (loc2, 0), 1)) == CONST_INT)
                   && GET_CODE (XEXP (XEXP (loc2, 0), 1)) == CONST_INT)
            {
            {
              if ((GET_CODE (XEXP (loc[n_var_parts], 0)) == REG
              if ((GET_CODE (XEXP (loc[n_var_parts], 0)) == REG
                   && rtx_equal_p (XEXP (loc[n_var_parts], 0),
                   && rtx_equal_p (XEXP (loc[n_var_parts], 0),
                                   XEXP (XEXP (loc2, 0), 0))
                                   XEXP (XEXP (loc2, 0), 0))
                   && INTVAL (XEXP (XEXP (loc2, 0), 1))
                   && INTVAL (XEXP (XEXP (loc2, 0), 1))
                      == GET_MODE_SIZE (mode))
                      == GET_MODE_SIZE (mode))
                  || (GET_CODE (XEXP (loc[n_var_parts], 0)) == PLUS
                  || (GET_CODE (XEXP (loc[n_var_parts], 0)) == PLUS
                      && GET_CODE (XEXP (XEXP (loc[n_var_parts], 0), 1))
                      && GET_CODE (XEXP (XEXP (loc[n_var_parts], 0), 1))
                         == CONST_INT
                         == CONST_INT
                      && rtx_equal_p (XEXP (XEXP (loc[n_var_parts], 0), 0),
                      && rtx_equal_p (XEXP (XEXP (loc[n_var_parts], 0), 0),
                                      XEXP (XEXP (loc2, 0), 0))
                                      XEXP (XEXP (loc2, 0), 0))
                      && INTVAL (XEXP (XEXP (loc[n_var_parts], 0), 1))
                      && INTVAL (XEXP (XEXP (loc[n_var_parts], 0), 1))
                         + GET_MODE_SIZE (mode)
                         + GET_MODE_SIZE (mode)
                         == INTVAL (XEXP (XEXP (loc2, 0), 1))))
                         == INTVAL (XEXP (XEXP (loc2, 0), 1))))
                new_loc = adjust_address_nv (loc[n_var_parts],
                new_loc = adjust_address_nv (loc[n_var_parts],
                                             wider_mode, 0);
                                             wider_mode, 0);
            }
            }
 
 
          if (new_loc)
          if (new_loc)
            {
            {
              loc[n_var_parts] = new_loc;
              loc[n_var_parts] = new_loc;
              mode = wider_mode;
              mode = wider_mode;
              last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
              last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
              i = j;
              i = j;
            }
            }
        }
        }
      ++n_var_parts;
      ++n_var_parts;
    }
    }
  type_size_unit = TYPE_SIZE_UNIT (TREE_TYPE (var->decl));
  type_size_unit = TYPE_SIZE_UNIT (TREE_TYPE (var->decl));
  if ((unsigned HOST_WIDE_INT) last_limit < TREE_INT_CST_LOW (type_size_unit))
  if ((unsigned HOST_WIDE_INT) last_limit < TREE_INT_CST_LOW (type_size_unit))
    complete = false;
    complete = false;
 
 
  if (where == EMIT_NOTE_AFTER_INSN)
  if (where == EMIT_NOTE_AFTER_INSN)
    note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
    note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
  else
  else
    note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
    note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
 
 
  if (!complete)
  if (!complete)
    {
    {
      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
                                                       NULL_RTX);
                                                       NULL_RTX);
    }
    }
  else if (n_var_parts == 1)
  else if (n_var_parts == 1)
    {
    {
      rtx expr_list
      rtx expr_list
        = gen_rtx_EXPR_LIST (VOIDmode, loc[0], GEN_INT (offsets[0]));
        = gen_rtx_EXPR_LIST (VOIDmode, loc[0], GEN_INT (offsets[0]));
 
 
      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
                                                       expr_list);
                                                       expr_list);
    }
    }
  else if (n_var_parts)
  else if (n_var_parts)
    {
    {
      rtx parallel;
      rtx parallel;
 
 
      for (i = 0; i < n_var_parts; i++)
      for (i = 0; i < n_var_parts; i++)
        loc[i]
        loc[i]
          = gen_rtx_EXPR_LIST (VOIDmode, loc[i], GEN_INT (offsets[i]));
          = gen_rtx_EXPR_LIST (VOIDmode, loc[i], GEN_INT (offsets[i]));
 
 
      parallel = gen_rtx_PARALLEL (VOIDmode,
      parallel = gen_rtx_PARALLEL (VOIDmode,
                                   gen_rtvec_v (n_var_parts, loc));
                                   gen_rtvec_v (n_var_parts, loc));
      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
                                                       parallel);
                                                       parallel);
    }
    }
 
 
  htab_clear_slot (changed_variables, varp);
  htab_clear_slot (changed_variables, varp);
 
 
  /* When there are no location parts the variable has been already
  /* When there are no location parts the variable has been already
     removed from hash table and a new empty variable was created.
     removed from hash table and a new empty variable was created.
     Free the empty variable.  */
     Free the empty variable.  */
  if (var->n_var_parts == 0)
  if (var->n_var_parts == 0)
    {
    {
      pool_free (var_pool, var);
      pool_free (var_pool, var);
    }
    }
 
 
  /* Continue traversing the hash table.  */
  /* Continue traversing the hash table.  */
  return 1;
  return 1;
}
}
 
 
/* Emit NOTE_INSN_VAR_LOCATION note for each variable from a chain
/* Emit NOTE_INSN_VAR_LOCATION note for each variable from a chain
   CHANGED_VARIABLES and delete this chain.  WHERE specifies whether the notes
   CHANGED_VARIABLES and delete this chain.  WHERE specifies whether the notes
   shall be emitted before of after instruction INSN.  */
   shall be emitted before of after instruction INSN.  */
 
 
static void
static void
emit_notes_for_changes (rtx insn, enum emit_note_where where)
emit_notes_for_changes (rtx insn, enum emit_note_where where)
{
{
  emit_note_data data;
  emit_note_data data;
 
 
  data.insn = insn;
  data.insn = insn;
  data.where = where;
  data.where = where;
  htab_traverse (changed_variables, emit_note_insn_var_location, &data);
  htab_traverse (changed_variables, emit_note_insn_var_location, &data);
}
}
 
 
/* Add variable *SLOT to the chain CHANGED_VARIABLES if it differs from the
/* Add variable *SLOT to the chain CHANGED_VARIABLES if it differs from the
   same variable in hash table DATA or is not there at all.  */
   same variable in hash table DATA or is not there at all.  */
 
 
static int
static int
emit_notes_for_differences_1 (void **slot, void *data)
emit_notes_for_differences_1 (void **slot, void *data)
{
{
  htab_t new_vars = (htab_t) data;
  htab_t new_vars = (htab_t) data;
  variable old_var, new_var;
  variable old_var, new_var;
 
 
  old_var = *(variable *) slot;
  old_var = *(variable *) slot;
  new_var = htab_find_with_hash (new_vars, old_var->decl,
  new_var = htab_find_with_hash (new_vars, old_var->decl,
                                 VARIABLE_HASH_VAL (old_var->decl));
                                 VARIABLE_HASH_VAL (old_var->decl));
 
 
  if (!new_var)
  if (!new_var)
    {
    {
      /* Variable has disappeared.  */
      /* Variable has disappeared.  */
      variable empty_var;
      variable empty_var;
 
 
      empty_var = pool_alloc (var_pool);
      empty_var = pool_alloc (var_pool);
      empty_var->decl = old_var->decl;
      empty_var->decl = old_var->decl;
      empty_var->refcount = 1;
      empty_var->refcount = 1;
      empty_var->n_var_parts = 0;
      empty_var->n_var_parts = 0;
      variable_was_changed (empty_var, NULL);
      variable_was_changed (empty_var, NULL);
    }
    }
  else if (variable_different_p (old_var, new_var, true))
  else if (variable_different_p (old_var, new_var, true))
    {
    {
      variable_was_changed (new_var, NULL);
      variable_was_changed (new_var, NULL);
    }
    }
 
 
  /* Continue traversing the hash table.  */
  /* Continue traversing the hash table.  */
  return 1;
  return 1;
}
}
 
 
/* Add variable *SLOT to the chain CHANGED_VARIABLES if it is not in hash
/* Add variable *SLOT to the chain CHANGED_VARIABLES if it is not in hash
   table DATA.  */
   table DATA.  */
 
 
static int
static int
emit_notes_for_differences_2 (void **slot, void *data)
emit_notes_for_differences_2 (void **slot, void *data)
{
{
  htab_t old_vars = (htab_t) data;
  htab_t old_vars = (htab_t) data;
  variable old_var, new_var;
  variable old_var, new_var;
 
 
  new_var = *(variable *) slot;
  new_var = *(variable *) slot;
  old_var = htab_find_with_hash (old_vars, new_var->decl,
  old_var = htab_find_with_hash (old_vars, new_var->decl,
                                 VARIABLE_HASH_VAL (new_var->decl));
                                 VARIABLE_HASH_VAL (new_var->decl));
  if (!old_var)
  if (!old_var)
    {
    {
      /* Variable has appeared.  */
      /* Variable has appeared.  */
      variable_was_changed (new_var, NULL);
      variable_was_changed (new_var, NULL);
    }
    }
 
 
  /* Continue traversing the hash table.  */
  /* Continue traversing the hash table.  */
  return 1;
  return 1;
}
}
 
 
/* Emit notes before INSN for differences between dataflow sets OLD_SET and
/* Emit notes before INSN for differences between dataflow sets OLD_SET and
   NEW_SET.  */
   NEW_SET.  */
 
 
static void
static void
emit_notes_for_differences (rtx insn, dataflow_set *old_set,
emit_notes_for_differences (rtx insn, dataflow_set *old_set,
                            dataflow_set *new_set)
                            dataflow_set *new_set)
{
{
  htab_traverse (old_set->vars, emit_notes_for_differences_1, new_set->vars);
  htab_traverse (old_set->vars, emit_notes_for_differences_1, new_set->vars);
  htab_traverse (new_set->vars, emit_notes_for_differences_2, old_set->vars);
  htab_traverse (new_set->vars, emit_notes_for_differences_2, old_set->vars);
  emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
  emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
}
}
 
 
/* Emit the notes for changes of location parts in the basic block BB.  */
/* Emit the notes for changes of location parts in the basic block BB.  */
 
 
static void
static void
emit_notes_in_bb (basic_block bb)
emit_notes_in_bb (basic_block bb)
{
{
  int i;
  int i;
  dataflow_set set;
  dataflow_set set;
 
 
  dataflow_set_init (&set, htab_elements (VTI (bb)->in.vars) + 3);
  dataflow_set_init (&set, htab_elements (VTI (bb)->in.vars) + 3);
  dataflow_set_copy (&set, &VTI (bb)->in);
  dataflow_set_copy (&set, &VTI (bb)->in);
 
 
  for (i = 0; i < VTI (bb)->n_mos; i++)
  for (i = 0; i < VTI (bb)->n_mos; i++)
    {
    {
      rtx insn = VTI (bb)->mos[i].insn;
      rtx insn = VTI (bb)->mos[i].insn;
 
 
      switch (VTI (bb)->mos[i].type)
      switch (VTI (bb)->mos[i].type)
        {
        {
          case MO_CALL:
          case MO_CALL:
            {
            {
              int r;
              int r;
 
 
              for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
              for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
                if (TEST_HARD_REG_BIT (call_used_reg_set, r))
                if (TEST_HARD_REG_BIT (call_used_reg_set, r))
                  {
                  {
                    var_regno_delete (&set, r);
                    var_regno_delete (&set, r);
                  }
                  }
              emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
              emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
            }
            }
            break;
            break;
 
 
          case MO_USE:
          case MO_USE:
            {
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
              rtx loc = VTI (bb)->mos[i].u.loc;
 
 
              if (GET_CODE (loc) == REG)
              if (GET_CODE (loc) == REG)
                var_reg_set (&set, loc);
                var_reg_set (&set, loc);
              else
              else
                var_mem_set (&set, loc);
                var_mem_set (&set, loc);
 
 
              emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
              emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
            }
            }
            break;
            break;
 
 
          case MO_SET:
          case MO_SET:
            {
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
              rtx loc = VTI (bb)->mos[i].u.loc;
 
 
              if (REG_P (loc))
              if (REG_P (loc))
                var_reg_delete_and_set (&set, loc, true);
                var_reg_delete_and_set (&set, loc, true);
              else
              else
                var_mem_delete_and_set (&set, loc, true);
                var_mem_delete_and_set (&set, loc, true);
 
 
              emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
              emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
            }
            }
            break;
            break;
 
 
          case MO_COPY:
          case MO_COPY:
            {
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
              rtx loc = VTI (bb)->mos[i].u.loc;
 
 
              if (REG_P (loc))
              if (REG_P (loc))
                var_reg_delete_and_set (&set, loc, false);
                var_reg_delete_and_set (&set, loc, false);
              else
              else
                var_mem_delete_and_set (&set, loc, false);
                var_mem_delete_and_set (&set, loc, false);
 
 
              emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
              emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
            }
            }
            break;
            break;
 
 
          case MO_USE_NO_VAR:
          case MO_USE_NO_VAR:
            {
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
              rtx loc = VTI (bb)->mos[i].u.loc;
 
 
              if (REG_P (loc))
              if (REG_P (loc))
                var_reg_delete (&set, loc, false);
                var_reg_delete (&set, loc, false);
              else
              else
                var_mem_delete (&set, loc, false);
                var_mem_delete (&set, loc, false);
 
 
              emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
              emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
            }
            }
            break;
            break;
 
 
          case MO_CLOBBER:
          case MO_CLOBBER:
            {
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
              rtx loc = VTI (bb)->mos[i].u.loc;
 
 
              if (REG_P (loc))
              if (REG_P (loc))
                var_reg_delete (&set, loc, true);
                var_reg_delete (&set, loc, true);
              else
              else
                var_mem_delete (&set, loc, true);
                var_mem_delete (&set, loc, true);
 
 
              emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
              emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
            }
            }
            break;
            break;
 
 
          case MO_ADJUST:
          case MO_ADJUST:
            set.stack_adjust += VTI (bb)->mos[i].u.adjust;
            set.stack_adjust += VTI (bb)->mos[i].u.adjust;
            break;
            break;
        }
        }
    }
    }
  dataflow_set_destroy (&set);
  dataflow_set_destroy (&set);
}
}
 
 
/* Emit notes for the whole function.  */
/* Emit notes for the whole function.  */
 
 
static void
static void
vt_emit_notes (void)
vt_emit_notes (void)
{
{
  basic_block bb;
  basic_block bb;
  dataflow_set *last_out;
  dataflow_set *last_out;
  dataflow_set empty;
  dataflow_set empty;
 
 
  gcc_assert (!htab_elements (changed_variables));
  gcc_assert (!htab_elements (changed_variables));
 
 
  /* Enable emitting notes by functions (mainly by set_variable_part and
  /* Enable emitting notes by functions (mainly by set_variable_part and
     delete_variable_part).  */
     delete_variable_part).  */
  emit_notes = true;
  emit_notes = true;
 
 
  dataflow_set_init (&empty, 7);
  dataflow_set_init (&empty, 7);
  last_out = &empty;
  last_out = &empty;
 
 
  FOR_EACH_BB (bb)
  FOR_EACH_BB (bb)
    {
    {
      /* Emit the notes for changes of variable locations between two
      /* Emit the notes for changes of variable locations between two
         subsequent basic blocks.  */
         subsequent basic blocks.  */
      emit_notes_for_differences (BB_HEAD (bb), last_out, &VTI (bb)->in);
      emit_notes_for_differences (BB_HEAD (bb), last_out, &VTI (bb)->in);
 
 
      /* Emit the notes for the changes in the basic block itself.  */
      /* Emit the notes for the changes in the basic block itself.  */
      emit_notes_in_bb (bb);
      emit_notes_in_bb (bb);
 
 
      last_out = &VTI (bb)->out;
      last_out = &VTI (bb)->out;
    }
    }
  dataflow_set_destroy (&empty);
  dataflow_set_destroy (&empty);
  emit_notes = false;
  emit_notes = false;
}
}
 
 
/* If there is a declaration and offset associated with register/memory RTL
/* If there is a declaration and offset associated with register/memory RTL
   assign declaration to *DECLP and offset to *OFFSETP, and return true.  */
   assign declaration to *DECLP and offset to *OFFSETP, and return true.  */
 
 
static bool
static bool
vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp)
vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp)
{
{
  if (REG_P (rtl))
  if (REG_P (rtl))
    {
    {
      if (REG_ATTRS (rtl))
      if (REG_ATTRS (rtl))
        {
        {
          *declp = REG_EXPR (rtl);
          *declp = REG_EXPR (rtl);
          *offsetp = REG_OFFSET (rtl);
          *offsetp = REG_OFFSET (rtl);
          return true;
          return true;
        }
        }
    }
    }
  else if (MEM_P (rtl))
  else if (MEM_P (rtl))
    {
    {
      if (MEM_ATTRS (rtl))
      if (MEM_ATTRS (rtl))
        {
        {
          *declp = MEM_EXPR (rtl);
          *declp = MEM_EXPR (rtl);
          *offsetp = MEM_OFFSET (rtl) ? INTVAL (MEM_OFFSET (rtl)) : 0;
          *offsetp = MEM_OFFSET (rtl) ? INTVAL (MEM_OFFSET (rtl)) : 0;
          return true;
          return true;
        }
        }
    }
    }
  return false;
  return false;
}
}
 
 
/* Insert function parameters to IN and OUT sets of ENTRY_BLOCK.  */
/* Insert function parameters to IN and OUT sets of ENTRY_BLOCK.  */
 
 
static void
static void
vt_add_function_parameters (void)
vt_add_function_parameters (void)
{
{
  tree parm;
  tree parm;
 
 
  for (parm = DECL_ARGUMENTS (current_function_decl);
  for (parm = DECL_ARGUMENTS (current_function_decl);
       parm; parm = TREE_CHAIN (parm))
       parm; parm = TREE_CHAIN (parm))
    {
    {
      rtx decl_rtl = DECL_RTL_IF_SET (parm);
      rtx decl_rtl = DECL_RTL_IF_SET (parm);
      rtx incoming = DECL_INCOMING_RTL (parm);
      rtx incoming = DECL_INCOMING_RTL (parm);
      tree decl;
      tree decl;
      HOST_WIDE_INT offset;
      HOST_WIDE_INT offset;
      dataflow_set *out;
      dataflow_set *out;
 
 
      if (TREE_CODE (parm) != PARM_DECL)
      if (TREE_CODE (parm) != PARM_DECL)
        continue;
        continue;
 
 
      if (!DECL_NAME (parm))
      if (!DECL_NAME (parm))
        continue;
        continue;
 
 
      if (!decl_rtl || !incoming)
      if (!decl_rtl || !incoming)
        continue;
        continue;
 
 
      if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode)
      if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode)
        continue;
        continue;
 
 
      if (!vt_get_decl_and_offset (incoming, &decl, &offset))
      if (!vt_get_decl_and_offset (incoming, &decl, &offset))
        if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
        if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
          continue;
          continue;
 
 
      if (!decl)
      if (!decl)
        continue;
        continue;
 
 
      gcc_assert (parm == decl);
      gcc_assert (parm == decl);
 
 
      out = &VTI (ENTRY_BLOCK_PTR)->out;
      out = &VTI (ENTRY_BLOCK_PTR)->out;
 
 
      if (REG_P (incoming))
      if (REG_P (incoming))
        {
        {
          gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
          gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
          attrs_list_insert (&out->regs[REGNO (incoming)],
          attrs_list_insert (&out->regs[REGNO (incoming)],
                             parm, offset, incoming);
                             parm, offset, incoming);
          set_variable_part (out, incoming, parm, offset);
          set_variable_part (out, incoming, parm, offset);
        }
        }
      else if (MEM_P (incoming))
      else if (MEM_P (incoming))
        set_variable_part (out, incoming, parm, offset);
        set_variable_part (out, incoming, parm, offset);
    }
    }
}
}
 
 
/* Allocate and initialize the data structures for variable tracking
/* Allocate and initialize the data structures for variable tracking
   and parse the RTL to get the micro operations.  */
   and parse the RTL to get the micro operations.  */
 
 
static void
static void
vt_initialize (void)
vt_initialize (void)
{
{
  basic_block bb;
  basic_block bb;
 
 
  alloc_aux_for_blocks (sizeof (struct variable_tracking_info_def));
  alloc_aux_for_blocks (sizeof (struct variable_tracking_info_def));
 
 
  FOR_EACH_BB (bb)
  FOR_EACH_BB (bb)
    {
    {
      rtx insn;
      rtx insn;
      HOST_WIDE_INT pre, post = 0;
      HOST_WIDE_INT pre, post = 0;
 
 
      /* Count the number of micro operations.  */
      /* Count the number of micro operations.  */
      VTI (bb)->n_mos = 0;
      VTI (bb)->n_mos = 0;
      for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
      for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
           insn = NEXT_INSN (insn))
           insn = NEXT_INSN (insn))
        {
        {
          if (INSN_P (insn))
          if (INSN_P (insn))
            {
            {
              if (!frame_pointer_needed)
              if (!frame_pointer_needed)
                {
                {
                  insn_stack_adjust_offset_pre_post (insn, &pre, &post);
                  insn_stack_adjust_offset_pre_post (insn, &pre, &post);
                  if (pre)
                  if (pre)
                    VTI (bb)->n_mos++;
                    VTI (bb)->n_mos++;
                  if (post)
                  if (post)
                    VTI (bb)->n_mos++;
                    VTI (bb)->n_mos++;
                }
                }
              note_uses (&PATTERN (insn), count_uses_1, insn);
              note_uses (&PATTERN (insn), count_uses_1, insn);
              note_stores (PATTERN (insn), count_stores, insn);
              note_stores (PATTERN (insn), count_stores, insn);
              if (CALL_P (insn))
              if (CALL_P (insn))
                VTI (bb)->n_mos++;
                VTI (bb)->n_mos++;
            }
            }
        }
        }
 
 
      /* Add the micro-operations to the array.  */
      /* Add the micro-operations to the array.  */
      VTI (bb)->mos = XNEWVEC (micro_operation, VTI (bb)->n_mos);
      VTI (bb)->mos = XNEWVEC (micro_operation, VTI (bb)->n_mos);
      VTI (bb)->n_mos = 0;
      VTI (bb)->n_mos = 0;
      for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
      for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
           insn = NEXT_INSN (insn))
           insn = NEXT_INSN (insn))
        {
        {
          if (INSN_P (insn))
          if (INSN_P (insn))
            {
            {
              int n1, n2;
              int n1, n2;
 
 
              if (!frame_pointer_needed)
              if (!frame_pointer_needed)
                {
                {
                  insn_stack_adjust_offset_pre_post (insn, &pre, &post);
                  insn_stack_adjust_offset_pre_post (insn, &pre, &post);
                  if (pre)
                  if (pre)
                    {
                    {
                      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
                      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
 
                      mo->type = MO_ADJUST;
                      mo->type = MO_ADJUST;
                      mo->u.adjust = pre;
                      mo->u.adjust = pre;
                      mo->insn = insn;
                      mo->insn = insn;
                    }
                    }
                }
                }
 
 
              n1 = VTI (bb)->n_mos;
              n1 = VTI (bb)->n_mos;
              note_uses (&PATTERN (insn), add_uses_1, insn);
              note_uses (&PATTERN (insn), add_uses_1, insn);
              n2 = VTI (bb)->n_mos - 1;
              n2 = VTI (bb)->n_mos - 1;
 
 
              /* Order the MO_USEs to be before MO_USE_NO_VARs.  */
              /* Order the MO_USEs to be before MO_USE_NO_VARs.  */
              while (n1 < n2)
              while (n1 < n2)
                {
                {
                  while (n1 < n2 && VTI (bb)->mos[n1].type == MO_USE)
                  while (n1 < n2 && VTI (bb)->mos[n1].type == MO_USE)
                    n1++;
                    n1++;
                  while (n1 < n2 && VTI (bb)->mos[n2].type == MO_USE_NO_VAR)
                  while (n1 < n2 && VTI (bb)->mos[n2].type == MO_USE_NO_VAR)
                    n2--;
                    n2--;
                  if (n1 < n2)
                  if (n1 < n2)
                    {
                    {
                      micro_operation sw;
                      micro_operation sw;
 
 
                      sw = VTI (bb)->mos[n1];
                      sw = VTI (bb)->mos[n1];
                      VTI (bb)->mos[n1] = VTI (bb)->mos[n2];
                      VTI (bb)->mos[n1] = VTI (bb)->mos[n2];
                      VTI (bb)->mos[n2] = sw;
                      VTI (bb)->mos[n2] = sw;
                    }
                    }
                }
                }
 
 
              if (CALL_P (insn))
              if (CALL_P (insn))
                {
                {
                  micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
                  micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
 
                  mo->type = MO_CALL;
                  mo->type = MO_CALL;
                  mo->insn = insn;
                  mo->insn = insn;
                }
                }
 
 
              n1 = VTI (bb)->n_mos;
              n1 = VTI (bb)->n_mos;
              /* This will record NEXT_INSN (insn), such that we can
              /* This will record NEXT_INSN (insn), such that we can
                 insert notes before it without worrying about any
                 insert notes before it without worrying about any
                 notes that MO_USEs might emit after the insn.  */
                 notes that MO_USEs might emit after the insn.  */
              note_stores (PATTERN (insn), add_stores, insn);
              note_stores (PATTERN (insn), add_stores, insn);
              n2 = VTI (bb)->n_mos - 1;
              n2 = VTI (bb)->n_mos - 1;
 
 
              /* Order the MO_CLOBBERs to be before MO_SETs.  */
              /* Order the MO_CLOBBERs to be before MO_SETs.  */
              while (n1 < n2)
              while (n1 < n2)
                {
                {
                  while (n1 < n2 && VTI (bb)->mos[n1].type == MO_CLOBBER)
                  while (n1 < n2 && VTI (bb)->mos[n1].type == MO_CLOBBER)
                    n1++;
                    n1++;
                  while (n1 < n2 && (VTI (bb)->mos[n2].type == MO_SET
                  while (n1 < n2 && (VTI (bb)->mos[n2].type == MO_SET
                                     || VTI (bb)->mos[n2].type == MO_COPY))
                                     || VTI (bb)->mos[n2].type == MO_COPY))
                    n2--;
                    n2--;
                  if (n1 < n2)
                  if (n1 < n2)
                    {
                    {
                      micro_operation sw;
                      micro_operation sw;
 
 
                      sw = VTI (bb)->mos[n1];
                      sw = VTI (bb)->mos[n1];
                      VTI (bb)->mos[n1] = VTI (bb)->mos[n2];
                      VTI (bb)->mos[n1] = VTI (bb)->mos[n2];
                      VTI (bb)->mos[n2] = sw;
                      VTI (bb)->mos[n2] = sw;
                    }
                    }
                }
                }
 
 
              if (!frame_pointer_needed && post)
              if (!frame_pointer_needed && post)
                {
                {
                  micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
                  micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
 
                  mo->type = MO_ADJUST;
                  mo->type = MO_ADJUST;
                  mo->u.adjust = post;
                  mo->u.adjust = post;
                  mo->insn = insn;
                  mo->insn = insn;
                }
                }
            }
            }
        }
        }
    }
    }
 
 
  /* Init the IN and OUT sets.  */
  /* Init the IN and OUT sets.  */
  FOR_ALL_BB (bb)
  FOR_ALL_BB (bb)
    {
    {
      VTI (bb)->visited = false;
      VTI (bb)->visited = false;
      dataflow_set_init (&VTI (bb)->in, 7);
      dataflow_set_init (&VTI (bb)->in, 7);
      dataflow_set_init (&VTI (bb)->out, 7);
      dataflow_set_init (&VTI (bb)->out, 7);
    }
    }
 
 
  attrs_pool = create_alloc_pool ("attrs_def pool",
  attrs_pool = create_alloc_pool ("attrs_def pool",
                                  sizeof (struct attrs_def), 1024);
                                  sizeof (struct attrs_def), 1024);
  var_pool = create_alloc_pool ("variable_def pool",
  var_pool = create_alloc_pool ("variable_def pool",
                                sizeof (struct variable_def), 64);
                                sizeof (struct variable_def), 64);
  loc_chain_pool = create_alloc_pool ("location_chain_def pool",
  loc_chain_pool = create_alloc_pool ("location_chain_def pool",
                                      sizeof (struct location_chain_def),
                                      sizeof (struct location_chain_def),
                                      1024);
                                      1024);
  changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq,
  changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq,
                                   NULL);
                                   NULL);
  vt_add_function_parameters ();
  vt_add_function_parameters ();
}
}
 
 
/* Free the data structures needed for variable tracking.  */
/* Free the data structures needed for variable tracking.  */
 
 
static void
static void
vt_finalize (void)
vt_finalize (void)
{
{
  basic_block bb;
  basic_block bb;
 
 
  FOR_EACH_BB (bb)
  FOR_EACH_BB (bb)
    {
    {
      free (VTI (bb)->mos);
      free (VTI (bb)->mos);
    }
    }
 
 
  FOR_ALL_BB (bb)
  FOR_ALL_BB (bb)
    {
    {
      dataflow_set_destroy (&VTI (bb)->in);
      dataflow_set_destroy (&VTI (bb)->in);
      dataflow_set_destroy (&VTI (bb)->out);
      dataflow_set_destroy (&VTI (bb)->out);
    }
    }
  free_aux_for_blocks ();
  free_aux_for_blocks ();
  free_alloc_pool (attrs_pool);
  free_alloc_pool (attrs_pool);
  free_alloc_pool (var_pool);
  free_alloc_pool (var_pool);
  free_alloc_pool (loc_chain_pool);
  free_alloc_pool (loc_chain_pool);
  htab_delete (changed_variables);
  htab_delete (changed_variables);
}
}
 
 
/* The entry point to variable tracking pass.  */
/* The entry point to variable tracking pass.  */
 
 
unsigned int
unsigned int
variable_tracking_main (void)
variable_tracking_main (void)
{
{
  if (n_basic_blocks > 500 && n_edges / n_basic_blocks >= 20)
  if (n_basic_blocks > 500 && n_edges / n_basic_blocks >= 20)
    return 0;
    return 0;
 
 
  mark_dfs_back_edges ();
  mark_dfs_back_edges ();
  vt_initialize ();
  vt_initialize ();
  if (!frame_pointer_needed)
  if (!frame_pointer_needed)
    {
    {
      if (!vt_stack_adjustments ())
      if (!vt_stack_adjustments ())
        {
        {
          vt_finalize ();
          vt_finalize ();
          return 0;
          return 0;
        }
        }
    }
    }
 
 
  vt_find_locations ();
  vt_find_locations ();
  vt_emit_notes ();
  vt_emit_notes ();
 
 
  if (dump_file && (dump_flags & TDF_DETAILS))
  if (dump_file && (dump_flags & TDF_DETAILS))
    {
    {
      dump_dataflow_sets ();
      dump_dataflow_sets ();
      dump_flow_info (dump_file, dump_flags);
      dump_flow_info (dump_file, dump_flags);
    }
    }
 
 
  vt_finalize ();
  vt_finalize ();
  return 0;
  return 0;
}
}


static bool
static bool
gate_handle_var_tracking (void)
gate_handle_var_tracking (void)
{
{
  return (flag_var_tracking);
  return (flag_var_tracking);
}
}
 
 
 
 
 
 
struct tree_opt_pass pass_variable_tracking =
struct tree_opt_pass pass_variable_tracking =
{
{
  "vartrack",                           /* name */
  "vartrack",                           /* name */
  gate_handle_var_tracking,             /* gate */
  gate_handle_var_tracking,             /* gate */
  variable_tracking_main,               /* execute */
  variable_tracking_main,               /* execute */
  NULL,                                 /* sub */
  NULL,                                 /* sub */
  NULL,                                 /* next */
  NULL,                                 /* next */
  0,                                    /* static_pass_number */
  0,                                    /* static_pass_number */
  TV_VAR_TRACKING,                      /* tv_id */
  TV_VAR_TRACKING,                      /* tv_id */
  0,                                    /* properties_required */
  0,                                    /* properties_required */
  0,                                    /* properties_provided */
  0,                                    /* properties_provided */
  0,                                    /* properties_destroyed */
  0,                                    /* properties_destroyed */
  0,                                    /* todo_flags_start */
  0,                                    /* todo_flags_start */
  TODO_dump_func,                       /* todo_flags_finish */
  TODO_dump_func,                       /* todo_flags_finish */
  'V'                                   /* letter */
  'V'                                   /* letter */
};
};
 
 
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.