You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
- Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA. */
+ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
/* This file contains the variable tracking pass. It computes where
variables are located (which registers or where in memory) at each position
#include "alloc-pool.h"
#include "fibheap.h"
#include "hashtab.h"
+#include "regs.h"
+#include "expr.h"
+#include "timevar.h"
+#include "tree-pass.h"
/* Type of micro operation. */
enum micro_operation_type
MO_USE_NO_VAR,/* Use location which is not associated with a variable
or the variable is not trackable. */
MO_SET, /* Set location. */
+ MO_COPY, /* Copy the same portion of a variable from one
+ location to another. */
MO_CLOBBER, /* Clobber location. */
MO_CALL, /* Call insn. */
MO_ADJUST /* Adjust stack pointer. */
HOST_WIDE_INT adjust;
} u;
- /* The instruction which the micro operation is in. */
+ /* The instruction which the micro operation is in, for MO_USE,
+ MO_USE_NO_VAR, MO_CALL and MO_ADJUST, or the subsequent
+ instruction or note in the original flow (before any var-tracking
+ notes are inserted, to simplify emission of notes), for MO_SET
+ and MO_CLOBBER. */
rtx insn;
} micro_operation;
/* Shall notes be emitted? */
static bool emit_notes;
-/* Fake variable for stack pointer. */
-tree frame_base_decl;
-
-/* Stack adjust caused by function prologue. */
-static HOST_WIDE_INT frame_stack_adjust;
-
/* Local function prototypes. */
static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
HOST_WIDE_INT *);
static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
HOST_WIDE_INT *);
static void bb_stack_adjust_offset (basic_block);
-static HOST_WIDE_INT prologue_stack_adjust (void);
static bool vt_stack_adjustments (void);
static rtx adjust_stack_reference (rtx, HOST_WIDE_INT);
static hashval_t variable_htab_hash (const void *);
static variable unshare_variable (dataflow_set *set, variable var);
static int vars_copy_1 (void **, void *);
static void vars_copy (htab_t, htab_t);
-static void var_reg_delete_and_set (dataflow_set *, rtx);
-static void var_reg_delete (dataflow_set *, rtx);
+static tree var_debug_decl (tree);
+static void var_reg_set (dataflow_set *, rtx);
+static void var_reg_delete_and_set (dataflow_set *, rtx, bool);
+static void var_reg_delete (dataflow_set *, rtx, bool);
static void var_regno_delete (dataflow_set *, int);
-static void var_mem_delete_and_set (dataflow_set *, rtx);
-static void var_mem_delete (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 (dataflow_set *, rtx, bool);
static void dataflow_set_init (dataflow_set *, int);
static void dataflow_set_clear (dataflow_set *);
static bool contains_symbol_ref (rtx);
static bool track_expr_p (tree);
+static bool same_variable_part_p (rtx, tree, HOST_WIDE_INT);
static int count_uses (rtx *, void *);
static void count_uses_1 (rtx *, void *);
static void count_stores (rtx, rtx, void *);
static void dump_dataflow_sets (void);
static void variable_was_changed (variable, htab_t);
-static void set_frame_base_location (dataflow_set *, rtx);
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 delete_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
static int emit_note_insn_var_location (void **, void *);
static void emit_notes_for_changes (rtx, enum emit_note_where);
VTI (bb)->out.stack_adjust = offset;
}
-/* Compute stack adjustment caused by function prologue. */
-
-static HOST_WIDE_INT
-prologue_stack_adjust (void)
-{
- HOST_WIDE_INT offset = 0;
- basic_block bb = ENTRY_BLOCK_PTR->next_bb;
- rtx insn;
- rtx end;
-
- if (!BB_END (bb))
- return 0;
-
- end = NEXT_INSN (BB_END (bb));
- for (insn = BB_HEAD (bb); insn != end; insn = NEXT_INSN (insn))
- {
- if (NOTE_P (insn)
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END)
- break;
-
- if (INSN_P (insn))
- {
- HOST_WIDE_INT tmp;
-
- insn_stack_adjust_offset_pre_post (insn, &tmp, &tmp);
- offset += tmp;
- }
- }
-
- return offset;
-}
-
/* Compute stack adjustments for all blocks by traversing DFS tree.
Return true when the adjustments on all incoming edges are consistent.
- Heavily borrowed from flow_depth_first_order_compute. */
+ Heavily borrowed from pre_and_rev_post_order_compute. */
static bool
vt_stack_adjustments (void)
/* Initialize entry block. */
VTI (ENTRY_BLOCK_PTR)->visited = true;
- VTI (ENTRY_BLOCK_PTR)->out.stack_adjust = frame_stack_adjust;
+ VTI (ENTRY_BLOCK_PTR)->out.stack_adjust = INCOMING_FRAME_SP_OFFSET;
/* Allocate stack for back-tracking up CFG. */
- stack = xmalloc ((n_basic_blocks + 1) * sizeof (edge_iterator));
+ stack = XNEWVEC (edge_iterator, n_basic_blocks + 1);
sp = 0;
/* Push the first edge on to the stack. */
return true;
}
-/* Adjust stack reference MEM by ADJUSTMENT bytes and return the new rtx. */
+/* Adjust stack reference MEM by ADJUSTMENT bytes and make it relative
+ to the argument pointer. Return the new rtx. */
static rtx
adjust_stack_reference (rtx mem, HOST_WIDE_INT adjustment)
{
- rtx adjusted_mem;
- rtx tmp;
+ rtx addr, cfa, tmp;
- if (adjustment == 0)
- return mem;
+#ifdef FRAME_POINTER_CFA_OFFSET
+ adjustment -= FRAME_POINTER_CFA_OFFSET (current_function_decl);
+ cfa = plus_constant (frame_pointer_rtx, adjustment);
+#else
+ adjustment -= ARG_POINTER_CFA_OFFSET (current_function_decl);
+ cfa = plus_constant (arg_pointer_rtx, adjustment);
+#endif
- adjusted_mem = copy_rtx (mem);
- XEXP (adjusted_mem, 0) = replace_rtx (XEXP (adjusted_mem, 0),
- stack_pointer_rtx,
- gen_rtx_PLUS (Pmode, stack_pointer_rtx,
- GEN_INT (adjustment)));
- tmp = simplify_rtx (XEXP (adjusted_mem, 0));
+ addr = replace_rtx (copy_rtx (XEXP (mem, 0)), stack_pointer_rtx, cfa);
+ tmp = simplify_rtx (addr);
if (tmp)
- XEXP (adjusted_mem, 0) = tmp;
+ addr = tmp;
- return adjusted_mem;
+ return replace_equiv_address_nv (mem, addr);
}
/* The hash function for variable_htab, computes the hash value
htab_traverse (src, vars_copy_1, dst);
}
-/* Delete current content of register LOC in dataflow set SET
- and set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC). */
+/* Map a decl to its main debug decl. */
+
+static inline tree
+var_debug_decl (tree decl)
+{
+ if (decl && DECL_P (decl)
+ && DECL_DEBUG_EXPR_IS_FROM (decl) && DECL_DEBUG_EXPR (decl)
+ && DECL_P (DECL_DEBUG_EXPR (decl)))
+ decl = DECL_DEBUG_EXPR (decl);
+
+ return decl;
+}
+
+/* Set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC). */
static void
-var_reg_delete_and_set (dataflow_set *set, rtx loc)
+var_reg_set (dataflow_set *set, rtx loc)
+{
+ tree decl = REG_EXPR (loc);
+ HOST_WIDE_INT offset = REG_OFFSET (loc);
+ attrs node;
+
+ decl = var_debug_decl (decl);
+
+ for (node = set->regs[REGNO (loc)]; node; node = node->next)
+ if (node->decl == decl && node->offset == offset)
+ break;
+ if (!node)
+ attrs_list_insert (&set->regs[REGNO (loc)], decl, offset, loc);
+ set_variable_part (set, loc, decl, offset);
+}
+
+/* Delete current content of register LOC in dataflow set SET and set
+ the register to contain REG_EXPR (LOC), REG_OFFSET (LOC). If
+ MODIFY is true, any other live copies of the same variable part are
+ also deleted from the dataflow set, otherwise the variable part is
+ assumed to be copied from another location holding the same
+ part. */
+
+static void
+var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify)
{
tree decl = REG_EXPR (loc);
HOST_WIDE_INT offset = REG_OFFSET (loc);
attrs node, next;
attrs *nextp;
+ decl = var_debug_decl (decl);
+
nextp = &set->regs[REGNO (loc)];
for (node = *nextp; node; node = next)
{
nextp = &node->next;
}
}
- if (set->regs[REGNO (loc)] == NULL)
- attrs_list_insert (&set->regs[REGNO (loc)], decl, offset, loc);
- set_variable_part (set, loc, decl, offset);
+ if (modify)
+ clobber_variable_part (set, loc, decl, offset);
+ var_reg_set (set, loc);
}
-/* Delete current content of register LOC in dataflow set SET. */
+/* Delete current content of register LOC in dataflow set SET. If
+ CLOBBER is true, also delete any other live copies of the same
+ variable part. */
static void
-var_reg_delete (dataflow_set *set, rtx loc)
+var_reg_delete (dataflow_set *set, rtx loc, bool clobber)
{
attrs *reg = &set->regs[REGNO (loc)];
attrs node, next;
+ if (clobber)
+ {
+ tree decl = REG_EXPR (loc);
+ HOST_WIDE_INT offset = REG_OFFSET (loc);
+
+ decl = var_debug_decl (decl);
+
+ clobber_variable_part (set, NULL, decl, offset);
+ }
+
for (node = *reg; node; node = next)
{
next = node->next;
*reg = NULL;
}
-/* Delete and set the location part of variable MEM_EXPR (LOC)
- in dataflow set SET to LOC.
+/* Set the location part of variable MEM_EXPR (LOC) in dataflow set
+ SET to LOC.
Adjust the address first if it is stack pointer based. */
static void
-var_mem_delete_and_set (dataflow_set *set, rtx loc)
+var_mem_set (dataflow_set *set, rtx loc)
{
tree decl = MEM_EXPR (loc);
HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
+ decl = var_debug_decl (decl);
+
set_variable_part (set, loc, decl, offset);
}
-/* Delete the location part LOC from dataflow set SET.
+/* 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
+ of the same variable part are also deleted from the dataflow set,
+ otherwise the variable part is assumed to be copied from another
+ location holding the same part.
Adjust the address first if it is stack pointer based. */
static void
-var_mem_delete (dataflow_set *set, rtx loc)
+var_mem_delete_and_set (dataflow_set *set, rtx loc, bool modify)
{
tree decl = MEM_EXPR (loc);
HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
+ decl = var_debug_decl (decl);
+
+ if (modify)
+ clobber_variable_part (set, NULL, decl, offset);
+ var_mem_set (set, loc);
+}
+
+/* Delete the location part LOC from dataflow set SET. If CLOBBER is
+ true, also delete any other live copies of the same variable part.
+ Adjust the address first if it is stack pointer based. */
+
+static void
+var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
+{
+ tree decl = MEM_EXPR (loc);
+ HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
+
+ decl = var_debug_decl (decl);
+ if (clobber)
+ clobber_variable_part (set, NULL, decl, offset);
delete_variable_part (set, loc, decl, offset);
}
dst_l = 0;
for (node = dst->var_part[j].loc_chain; node; node = node->next)
dst_l++;
- vui = xcalloc (src_l + dst_l, sizeof (struct variable_union_info));
+ vui = XCNEWVEC (struct variable_union_info, src_l + dst_l);
/* Fill in the locations from DST. */
for (node = dst->var_part[j].loc_chain, jj = 0; node;
don't need to track this expression if the ultimate declaration is
ignored. */
realdecl = expr;
- if (DECL_DEBUG_EXPR (realdecl)
- && DECL_DEBUG_EXPR_IS_FROM (realdecl))
+ if (DECL_DEBUG_EXPR_IS_FROM (realdecl) && DECL_DEBUG_EXPR (realdecl))
{
realdecl = DECL_DEBUG_EXPR (realdecl);
/* ??? We don't yet know how to emit DW_OP_piece for variable
return 1;
}
+/* Determine whether a given LOC refers to the same variable part as
+ EXPR+OFFSET. */
+
+static bool
+same_variable_part_p (rtx loc, tree expr, HOST_WIDE_INT offset)
+{
+ tree expr2;
+ HOST_WIDE_INT offset2;
+
+ if (! DECL_P (expr))
+ return false;
+
+ if (REG_P (loc))
+ {
+ expr2 = REG_EXPR (loc);
+ offset2 = REG_OFFSET (loc);
+ }
+ else if (MEM_P (loc))
+ {
+ expr2 = MEM_EXPR (loc);
+ offset2 = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
+ }
+ else
+ return false;
+
+ if (! expr2 || ! DECL_P (expr2))
+ return false;
+
+ expr = var_debug_decl (expr);
+ expr2 = var_debug_decl (expr2);
+
+ return (expr == expr2 && offset == offset2);
+}
+
+
/* Count uses (register and memory references) LOC which will be tracked.
INSN is instruction which the LOC is part of. */
basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
- mo->type = ((GET_CODE (expr) != CLOBBER && REG_EXPR (loc)
- && track_expr_p (REG_EXPR (loc)))
- ? MO_SET : MO_CLOBBER);
+ if (GET_CODE (expr) == CLOBBER
+ || ! REG_EXPR (loc)
+ || ! track_expr_p (REG_EXPR (loc)))
+ mo->type = MO_CLOBBER;
+ else if (GET_CODE (expr) == SET
+ && SET_DEST (expr) == loc
+ && same_variable_part_p (SET_SRC (expr),
+ REG_EXPR (loc),
+ REG_OFFSET (loc)))
+ mo->type = MO_COPY;
+ else
+ mo->type = MO_SET;
mo->u.loc = loc;
- mo->insn = (rtx) insn;
+ mo->insn = NEXT_INSN ((rtx) insn);
}
else if (MEM_P (loc)
&& MEM_EXPR (loc)
basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
- mo->type = GET_CODE (expr) == CLOBBER ? MO_CLOBBER : MO_SET;
+ if (GET_CODE (expr) == CLOBBER)
+ mo->type = MO_CLOBBER;
+ else if (GET_CODE (expr) == SET
+ && SET_DEST (expr) == loc
+ && same_variable_part_p (SET_SRC (expr),
+ MEM_EXPR (loc),
+ MEM_OFFSET (loc)
+ ? INTVAL (MEM_OFFSET (loc)) : 0))
+ mo->type = MO_COPY;
+ else
+ mo->type = MO_SET;
mo->u.loc = loc;
- mo->insn = (rtx) insn;
+ mo->insn = NEXT_INSN ((rtx) insn);
}
}
break;
case MO_USE:
+ {
+ rtx loc = VTI (bb)->mos[i].u.loc;
+
+ if (GET_CODE (loc) == REG)
+ var_reg_set (out, loc);
+ else if (GET_CODE (loc) == MEM)
+ var_mem_set (out, loc);
+ }
+ break;
+
case MO_SET:
{
rtx loc = VTI (bb)->mos[i].u.loc;
if (REG_P (loc))
- var_reg_delete_and_set (out, loc);
+ var_reg_delete_and_set (out, loc, true);
+ else if (MEM_P (loc))
+ var_mem_delete_and_set (out, loc, true);
+ }
+ break;
+
+ case MO_COPY:
+ {
+ rtx loc = VTI (bb)->mos[i].u.loc;
+
+ if (REG_P (loc))
+ var_reg_delete_and_set (out, loc, false);
else if (MEM_P (loc))
- var_mem_delete_and_set (out, loc);
+ var_mem_delete_and_set (out, loc, false);
}
break;
case MO_USE_NO_VAR:
- case MO_CLOBBER:
{
rtx loc = VTI (bb)->mos[i].u.loc;
if (REG_P (loc))
- var_reg_delete (out, loc);
+ var_reg_delete (out, loc, false);
else if (MEM_P (loc))
- var_mem_delete (out, loc);
+ var_mem_delete (out, loc, false);
}
break;
- case MO_ADJUST:
+ case MO_CLOBBER:
{
- rtx base;
+ rtx loc = VTI (bb)->mos[i].u.loc;
- out->stack_adjust += VTI (bb)->mos[i].u.adjust;
- base = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx,
- out->stack_adjust));
- set_frame_base_location (out, base);
+ if (REG_P (loc))
+ var_reg_delete (out, loc, true);
+ else if (MEM_P (loc))
+ var_mem_delete (out, loc, true);
}
break;
+
+ case MO_ADJUST:
+ out->stack_adjust += VTI (bb)->mos[i].u.adjust;
+ break;
}
}
/* Compute reverse completion order of depth first search of the CFG
so that the data-flow runs faster. */
- rc_order = xmalloc (n_basic_blocks * sizeof (int));
- bb_order = xmalloc (last_basic_block * sizeof (int));
- flow_depth_first_order_compute (NULL, rc_order);
- for (i = 0; i < n_basic_blocks; i++)
+ rc_order = XNEWVEC (int, n_basic_blocks - NUM_FIXED_BLOCKS);
+ bb_order = XNEWVEC (int, last_basic_block);
+ pre_and_rev_post_order_compute (NULL, rc_order, false);
+ for (i = 0; i < n_basic_blocks - NUM_FIXED_BLOCKS; i++)
bb_order[rc_order[i]] = i;
free (rc_order);
for (; list; list = list->next)
{
print_mem_expr (dump_file, list->decl);
- fprintf (dump_file, "+");
- fprintf (dump_file, HOST_WIDE_INT_PRINT_DEC, list->offset);
+ fprintf (dump_file, "+" HOST_WIDE_INT_PRINT_DEC, list->offset);
}
fprintf (dump_file, "\n");
}
{
int i;
- fprintf (dump_file, "Stack adjustment: ");
- fprintf (dump_file, HOST_WIDE_INT_PRINT_DEC, set->stack_adjust);
- fprintf (dump_file, "\n");
- for (i = 1; i < FIRST_PSEUDO_REGISTER; i++)
+ fprintf (dump_file, "Stack adjustment: " HOST_WIDE_INT_PRINT_DEC "\n",
+ set->stack_adjust);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
if (set->regs[i])
{
}
}
-/* Set the location of frame_base_decl to LOC in dataflow set SET. This
- function expects that frame_base_decl has already one location for offset 0
- in the variable table. */
+/* Look for the index in VAR->var_part corresponding to OFFSET.
+ 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
+ have, if it should be inserted. */
-static void
-set_frame_base_location (dataflow_set *set, rtx loc)
+static inline int
+find_variable_location_part (variable var, HOST_WIDE_INT offset,
+ int *insertion_point)
{
- variable var;
-
- var = htab_find_with_hash (set->vars, frame_base_decl,
- VARIABLE_HASH_VAL (frame_base_decl));
- gcc_assert (var);
- gcc_assert (var->n_var_parts == 1);
- gcc_assert (!var->var_part[0].offset);
- gcc_assert (var->var_part[0].loc_chain);
+ int pos, low, high;
- /* If frame_base_decl is shared unshare it first. */
- if (var->refcount > 1)
- var = unshare_variable (set, var);
+ /* Find the location part. */
+ low = 0;
+ high = var->n_var_parts;
+ while (low != high)
+ {
+ pos = (low + high) / 2;
+ if (var->var_part[pos].offset < offset)
+ low = pos + 1;
+ else
+ high = pos;
+ }
+ pos = low;
+
+ if (insertion_point)
+ *insertion_point = pos;
- var->var_part[0].loc_chain->loc = loc;
- var->var_part[0].cur_loc = loc;
- variable_was_changed (var, set->vars);
+ if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+ return pos;
+
+ return -1;
}
/* Set the part of variable's location in the dataflow set SET. The variable
static void
set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset)
{
- int pos, low, high;
+ int pos;
location_chain node, next;
location_chain *nextp;
variable var;
}
else
{
+ int inspos = 0;
+
var = (variable) *slot;
- /* Find the location part. */
- low = 0;
- high = var->n_var_parts;
- while (low != high)
- {
- pos = (low + high) / 2;
- if (var->var_part[pos].offset < offset)
- low = pos + 1;
- else
- high = pos;
- }
- pos = low;
+ pos = find_variable_location_part (var, offset, &inspos);
- if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+ if (pos >= 0)
{
node = var->var_part[pos].loc_chain;
thus there are at most MAX_VAR_PARTS different offsets. */
gcc_assert (var->n_var_parts < MAX_VAR_PARTS);
- /* We have to move the elements of array starting at index low to the
- next position. */
- for (high = var->n_var_parts; high > low; high--)
- var->var_part[high] = var->var_part[high - 1];
+ /* We have to move the elements of array starting at index
+ inspos to the next position. */
+ for (pos = var->n_var_parts; pos > inspos; pos--)
+ var->var_part[pos] = var->var_part[pos - 1];
var->n_var_parts++;
var->var_part[pos].offset = offset;
}
}
+/* Remove all recorded register locations for the given variable part
+ from dataflow set SET, except for those that are identical to loc.
+ The variable part is specified by variable's declaration DECL and
+ offset OFFSET. */
+
+static void
+clobber_variable_part (dataflow_set *set, rtx loc, tree decl,
+ HOST_WIDE_INT offset)
+{
+ void **slot;
+
+ if (! decl || ! DECL_P (decl))
+ return;
+
+ slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
+ NO_INSERT);
+ if (slot)
+ {
+ variable var = (variable) *slot;
+ int pos = find_variable_location_part (var, offset, NULL);
+
+ if (pos >= 0)
+ {
+ location_chain node, next;
+
+ /* Remove the register locations from the dataflow set. */
+ next = var->var_part[pos].loc_chain;
+ for (node = next; node; node = next)
+ {
+ next = node->next;
+ if (node->loc != loc)
+ {
+ if (REG_P (node->loc))
+ {
+ attrs anode, anext;
+ attrs *anextp;
+
+ /* Remove the variable part from the register's
+ list, but preserve any other variable parts
+ that might be regarded as live in that same
+ register. */
+ anextp = &set->regs[REGNO (node->loc)];
+ for (anode = *anextp; anode; anode = anext)
+ {
+ anext = anode->next;
+ if (anode->decl == decl
+ && anode->offset == offset)
+ {
+ pool_free (attrs_pool, anode);
+ *anextp = anext;
+ }
+ }
+ }
+
+ delete_variable_part (set, node->loc, decl, offset);
+ }
+ }
+ }
+ }
+}
+
/* 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's location by LOC. */
delete_variable_part (dataflow_set *set, rtx loc, tree decl,
HOST_WIDE_INT offset)
{
- int pos, low, high;
void **slot;
slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
if (slot)
{
variable var = (variable) *slot;
+ int pos = find_variable_location_part (var, offset, NULL);
- /* Find the location part. */
- low = 0;
- high = var->n_var_parts;
- while (low != high)
- {
- pos = (low + high) / 2;
- if (var->var_part[pos].offset < offset)
- low = pos + 1;
- else
- high = pos;
- }
- pos = low;
-
- if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+ if (pos >= 0)
{
location_chain node, next;
location_chain *nextp;
}
}
if (changed)
- variable_was_changed (var, set->vars);
+ variable_was_changed (var, set->vars);
}
}
}
rtx insn = ((emit_note_data *)data)->insn;
enum emit_note_where where = ((emit_note_data *)data)->where;
rtx note;
- int i;
+ int i, j, n_var_parts;
bool complete;
HOST_WIDE_INT last_limit;
tree type_size_unit;
+ HOST_WIDE_INT offsets[MAX_VAR_PARTS];
+ rtx loc[MAX_VAR_PARTS];
gcc_assert (var->decl);
complete = true;
last_limit = 0;
+ n_var_parts = 0;
for (i = 0; i < var->n_var_parts; i++)
{
+ enum machine_mode mode, wider_mode;
+
if (last_limit < var->var_part[i].offset)
{
complete = false;
break;
}
- last_limit
- = (var->var_part[i].offset
- + GET_MODE_SIZE (GET_MODE (var->var_part[i].loc_chain->loc)));
+ else if (last_limit > var->var_part[i].offset)
+ continue;
+ offsets[n_var_parts] = var->var_part[i].offset;
+ loc[n_var_parts] = var->var_part[i].loc_chain->loc;
+ mode = GET_MODE (loc[n_var_parts]);
+ last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
+
+ /* Attempt to merge adjacent registers or memory. */
+ wider_mode = GET_MODE_WIDER_MODE (mode);
+ for (j = i + 1; j < var->n_var_parts; j++)
+ if (last_limit <= var->var_part[j].offset)
+ break;
+ if (j < var->n_var_parts
+ && wider_mode != VOIDmode
+ && GET_CODE (loc[n_var_parts])
+ == GET_CODE (var->var_part[j].loc_chain->loc)
+ && mode == GET_MODE (var->var_part[j].loc_chain->loc)
+ && last_limit == var->var_part[j].offset)
+ {
+ rtx new_loc = NULL;
+ rtx loc2 = var->var_part[j].loc_chain->loc;
+
+ 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])][wider_mode]
+ && REGNO (loc[n_var_parts])
+ + hard_regno_nregs[REGNO (loc[n_var_parts])][mode]
+ == REGNO (loc2))
+ {
+ if (! WORDS_BIG_ENDIAN && ! BYTES_BIG_ENDIAN)
+ new_loc = simplify_subreg (wider_mode, loc[n_var_parts],
+ mode, 0);
+ else if (WORDS_BIG_ENDIAN && BYTES_BIG_ENDIAN)
+ new_loc = simplify_subreg (wider_mode, loc2, mode, 0);
+ if (new_loc)
+ {
+ if (!REG_P (new_loc)
+ || REGNO (new_loc) != REGNO (loc[n_var_parts]))
+ new_loc = NULL;
+ else
+ REG_ATTRS (new_loc) = REG_ATTRS (loc[n_var_parts]);
+ }
+ }
+ else if (MEM_P (loc[n_var_parts])
+ && GET_CODE (XEXP (loc2, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (loc2, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (loc2, 0), 1)) == CONST_INT)
+ {
+ if ((GET_CODE (XEXP (loc[n_var_parts], 0)) == REG
+ && rtx_equal_p (XEXP (loc[n_var_parts], 0),
+ XEXP (XEXP (loc2, 0), 0))
+ && INTVAL (XEXP (XEXP (loc2, 0), 1))
+ == GET_MODE_SIZE (mode))
+ || (GET_CODE (XEXP (loc[n_var_parts], 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (loc[n_var_parts], 0), 1))
+ == CONST_INT
+ && rtx_equal_p (XEXP (XEXP (loc[n_var_parts], 0), 0),
+ XEXP (XEXP (loc2, 0), 0))
+ && INTVAL (XEXP (XEXP (loc[n_var_parts], 0), 1))
+ + GET_MODE_SIZE (mode)
+ == INTVAL (XEXP (XEXP (loc2, 0), 1))))
+ new_loc = adjust_address_nv (loc[n_var_parts],
+ wider_mode, 0);
+ }
+
+ if (new_loc)
+ {
+ loc[n_var_parts] = new_loc;
+ mode = wider_mode;
+ last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
+ i = j;
+ }
+ }
+ ++n_var_parts;
}
type_size_unit = TYPE_SIZE_UNIT (TREE_TYPE (var->decl));
if ((unsigned HOST_WIDE_INT) last_limit < TREE_INT_CST_LOW (type_size_unit))
NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
NULL_RTX);
}
- else if (var->n_var_parts == 1)
+ else if (n_var_parts == 1)
{
rtx expr_list
- = gen_rtx_EXPR_LIST (VOIDmode,
- var->var_part[0].loc_chain->loc,
- GEN_INT (var->var_part[0].offset));
+ = gen_rtx_EXPR_LIST (VOIDmode, loc[0], GEN_INT (offsets[0]));
NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
expr_list);
}
- else if (var->n_var_parts)
+ else if (n_var_parts)
{
- rtx argp[MAX_VAR_PARTS];
rtx parallel;
- for (i = 0; i < var->n_var_parts; i++)
- argp[i] = gen_rtx_EXPR_LIST (VOIDmode, var->var_part[i].loc_chain->loc,
- GEN_INT (var->var_part[i].offset));
+ for (i = 0; i < n_var_parts; i++)
+ loc[i]
+ = gen_rtx_EXPR_LIST (VOIDmode, loc[i], GEN_INT (offsets[i]));
+
parallel = gen_rtx_PARALLEL (VOIDmode,
- gen_rtvec_v (var->n_var_parts, argp));
+ gen_rtvec_v (n_var_parts, loc));
NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
parallel);
}
break;
case MO_USE:
+ {
+ rtx loc = VTI (bb)->mos[i].u.loc;
+
+ if (GET_CODE (loc) == REG)
+ var_reg_set (&set, loc);
+ else
+ var_mem_set (&set, loc);
+
+ emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+ }
+ break;
+
case MO_SET:
{
rtx loc = VTI (bb)->mos[i].u.loc;
if (REG_P (loc))
- var_reg_delete_and_set (&set, loc);
+ var_reg_delete_and_set (&set, loc, true);
else
- var_mem_delete_and_set (&set, loc);
+ var_mem_delete_and_set (&set, loc, true);
+
+ emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
+ }
+ break;
+
+ case MO_COPY:
+ {
+ rtx loc = VTI (bb)->mos[i].u.loc;
- if (VTI (bb)->mos[i].type == MO_USE)
- emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
+ if (REG_P (loc))
+ var_reg_delete_and_set (&set, loc, false);
else
- emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+ var_mem_delete_and_set (&set, loc, false);
+
+ emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
}
break;
case MO_USE_NO_VAR:
- case MO_CLOBBER:
{
rtx loc = VTI (bb)->mos[i].u.loc;
if (REG_P (loc))
- var_reg_delete (&set, loc);
+ var_reg_delete (&set, loc, false);
else
- var_mem_delete (&set, loc);
+ var_mem_delete (&set, loc, false);
- if (VTI (bb)->mos[i].type == MO_USE_NO_VAR)
- emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
- else
- emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+ emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
}
break;
- case MO_ADJUST:
+ case MO_CLOBBER:
{
- rtx base;
+ rtx loc = VTI (bb)->mos[i].u.loc;
- set.stack_adjust += VTI (bb)->mos[i].u.adjust;
- base = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx,
- set.stack_adjust));
- set_frame_base_location (&set, base);
- emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+ if (REG_P (loc))
+ var_reg_delete (&set, loc, true);
+ else
+ var_mem_delete (&set, loc, true);
+
+ emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
}
break;
+
+ case MO_ADJUST:
+ set.stack_adjust += VTI (bb)->mos[i].u.adjust;
+ break;
}
}
dataflow_set_destroy (&set);
gcc_assert (parm == decl);
- incoming = eliminate_regs (incoming, 0, NULL_RTX);
out = &VTI (ENTRY_BLOCK_PTR)->out;
if (REG_P (incoming))
set_variable_part (out, incoming, parm, offset);
}
else if (MEM_P (incoming))
- {
- set_variable_part (out, incoming, parm, offset);
- }
+ set_variable_part (out, incoming, parm, offset);
}
}
FOR_EACH_BB (bb)
{
rtx insn;
- HOST_WIDE_INT pre, post;
+ HOST_WIDE_INT pre, post = 0;
/* Count the number of micro operations. */
VTI (bb)->n_mos = 0;
}
/* Add the micro-operations to the array. */
- VTI (bb)->mos = xmalloc (VTI (bb)->n_mos
- * sizeof (struct micro_operation_def));
+ VTI (bb)->mos = XNEWVEC (micro_operation, VTI (bb)->n_mos);
VTI (bb)->n_mos = 0;
for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
insn = NEXT_INSN (insn))
}
n1 = VTI (bb)->n_mos;
+ /* This will record NEXT_INSN (insn), such that we can
+ insert notes before it without worrying about any
+ notes that MO_USEs might emit after the insn. */
note_stores (PATTERN (insn), add_stores, insn);
n2 = VTI (bb)->n_mos - 1;
- /* Order the MO_SETs to be before MO_CLOBBERs. */
+ /* Order the MO_CLOBBERs to be before MO_SETs. */
while (n1 < n2)
{
- while (n1 < n2 && VTI (bb)->mos[n1].type == MO_SET)
+ while (n1 < n2 && VTI (bb)->mos[n1].type == MO_CLOBBER)
n1++;
- while (n1 < n2 && VTI (bb)->mos[n2].type == MO_CLOBBER)
+ while (n1 < n2 && (VTI (bb)->mos[n2].type == MO_SET
+ || VTI (bb)->mos[n2].type == MO_COPY))
n2--;
if (n1 < n2)
{
changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq,
NULL);
vt_add_function_parameters ();
-
- if (!frame_pointer_needed)
- {
- rtx base;
-
- /* Create fake variable for tracking stack pointer changes. */
- frame_base_decl = make_node (VAR_DECL);
- DECL_NAME (frame_base_decl) = get_identifier ("___frame_base_decl");
- TREE_TYPE (frame_base_decl) = char_type_node;
- DECL_ARTIFICIAL (frame_base_decl) = 1;
- DECL_IGNORED_P (frame_base_decl) = 1;
-
- /* Set its initial "location". */
- frame_stack_adjust = -prologue_stack_adjust ();
- base = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx,
- frame_stack_adjust));
- set_variable_part (&VTI (ENTRY_BLOCK_PTR)->out, base, frame_base_decl, 0);
- }
- else
- {
- frame_base_decl = NULL;
- }
}
/* Free the data structures needed for variable tracking. */
/* The entry point to variable tracking pass. */
-void
+unsigned int
variable_tracking_main (void)
{
if (n_basic_blocks > 500 && n_edges / n_basic_blocks >= 20)
- return;
+ return 0;
mark_dfs_back_edges ();
vt_initialize ();
if (!vt_stack_adjustments ())
{
vt_finalize ();
- return;
+ return 0;
}
}
vt_find_locations ();
vt_emit_notes ();
- if (dump_file)
+ if (dump_file && (dump_flags & TDF_DETAILS))
{
dump_dataflow_sets ();
- dump_flow_info (dump_file);
+ dump_flow_info (dump_file, dump_flags);
}
vt_finalize ();
+ return 0;
+}
+\f
+static bool
+gate_handle_var_tracking (void)
+{
+ return (flag_var_tracking);
}
+
+
+
+struct tree_opt_pass pass_variable_tracking =
+{
+ "vartrack", /* name */
+ gate_handle_var_tracking, /* gate */
+ variable_tracking_main, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_VAR_TRACKING, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func, /* todo_flags_finish */
+ 'V' /* letter */
+};
+