#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "flags.h"
#include "output.h"
#include "insn-config.h"
#include "reload.h"
#include "sbitmap.h"
#include "alloc-pool.h"
#include "fibheap.h"
#include "hashtab.h"
#include "regs.h"
#include "expr.h"
#include "timevar.h"
#include "tree-pass.h"
#include "langhooks.h"
enum micro_operation_type
{
MO_USE,
MO_USE_NO_VAR,
MO_SET,
MO_COPY,
MO_CLOBBER,
MO_CALL,
MO_ADJUST
};
enum emit_note_where
{
EMIT_NOTE_BEFORE_INSN,
EMIT_NOTE_AFTER_INSN
};
typedef struct micro_operation_def
{
enum micro_operation_type type;
union {
rtx loc;
HOST_WIDE_INT adjust;
} u;
rtx insn;
} micro_operation;
typedef struct emit_note_data_def
{
rtx insn;
enum emit_note_where where;
} emit_note_data;
typedef struct attrs_def
{
struct attrs_def *next;
rtx loc;
tree decl;
HOST_WIDE_INT offset;
} *attrs;
typedef struct dataflow_set_def
{
HOST_WIDE_INT stack_adjust;
attrs regs[FIRST_PSEUDO_REGISTER];
htab_t vars;
} dataflow_set;
typedef struct variable_tracking_info_def
{
int n_mos;
micro_operation *mos;
dataflow_set in;
dataflow_set out;
bool visited;
} *variable_tracking_info;
typedef struct location_chain_def
{
struct location_chain_def *next;
rtx loc;
rtx set_src;
enum var_init_status init;
} *location_chain;
typedef struct variable_part_def
{
location_chain loc_chain;
rtx cur_loc;
HOST_WIDE_INT offset;
} variable_part;
#define MAX_VAR_PARTS 16
typedef struct variable_def
{
tree decl;
int refcount;
int n_var_parts;
variable_part var_part[MAX_VAR_PARTS];
} *variable;
#define VARIABLE_HASH_VAL(decl) (DECL_UID (decl))
#define VTI(BB) ((variable_tracking_info) (BB)->aux)
static alloc_pool attrs_pool;
static alloc_pool var_pool;
static alloc_pool loc_chain_pool;
static htab_t changed_variables;
static bool emit_notes;
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 bool vt_stack_adjustments (void);
static rtx adjust_stack_reference (rtx, HOST_WIDE_INT);
static hashval_t variable_htab_hash (const void *);
static int variable_htab_eq (const void *, const void *);
static void variable_htab_free (void *);
static void init_attrs_list_set (attrs *);
static void attrs_list_clear (attrs *);
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_copy (attrs *, attrs);
static void attrs_list_union (attrs *, attrs);
static void vars_clear (htab_t);
static variable unshare_variable (dataflow_set *set, variable var,
enum var_init_status);
static int vars_copy_1 (void **, void *);
static void vars_copy (htab_t, htab_t);
static tree var_debug_decl (tree);
static void var_reg_set (dataflow_set *, rtx, enum var_init_status, rtx);
static void var_reg_delete_and_set (dataflow_set *, rtx, bool,
enum var_init_status, rtx);
static void var_reg_delete (dataflow_set *, rtx, bool);
static void var_regno_delete (dataflow_set *, int);
static void var_mem_set (dataflow_set *, rtx, enum var_init_status, rtx);
static void var_mem_delete_and_set (dataflow_set *, rtx, bool,
enum var_init_status, rtx);
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 void dataflow_set_copy (dataflow_set *, dataflow_set *);
static int variable_union_info_cmp_pos (const void *, const void *);
static int variable_union (void **, void *);
static void dataflow_set_union (dataflow_set *, dataflow_set *);
static bool variable_part_different_p (variable_part *, variable_part *);
static bool variable_different_p (variable, variable, bool);
static int dataflow_set_different_1 (void **, void *);
static int dataflow_set_different_2 (void **, void *);
static bool dataflow_set_different (dataflow_set *, dataflow_set *);
static void dataflow_set_destroy (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 int add_uses (rtx *, void *);
static void add_uses_1 (rtx *, void *);
static void add_stores (rtx, rtx, void *);
static bool compute_bb_dataflow (basic_block);
static void vt_find_locations (void);
static void dump_attrs_list (attrs);
static int dump_variable (void **, void *);
static void dump_vars (htab_t);
static void dump_dataflow_set (dataflow_set *);
static void dump_dataflow_sets (void);
static void variable_was_changed (variable, htab_t);
static void set_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT,
enum var_init_status, rtx);
static void clobber_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT,
rtx);
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);
static int emit_notes_for_differences_1 (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_in_bb (basic_block);
static void vt_emit_notes (void);
static bool vt_get_decl_and_offset (rtx, tree *, HOST_WIDE_INT *);
static void vt_add_function_parameters (void);
static void vt_initialize (void);
static void vt_finalize (void);
static void
stack_adjust_offset_pre_post (rtx pattern, HOST_WIDE_INT *pre,
HOST_WIDE_INT *post)
{
rtx src = SET_SRC (pattern);
rtx dest = SET_DEST (pattern);
enum rtx_code code;
if (dest == stack_pointer_rtx)
{
code = GET_CODE (src);
if (! (code == PLUS || code == MINUS)
|| XEXP (src, 0) != stack_pointer_rtx
|| GET_CODE (XEXP (src, 1)) != CONST_INT)
return;
if (code == MINUS)
*post += INTVAL (XEXP (src, 1));
else
*post -= INTVAL (XEXP (src, 1));
}
else if (MEM_P (dest))
{
src = XEXP (dest, 0);
code = GET_CODE (src);
switch (code)
{
case PRE_MODIFY:
case POST_MODIFY:
if (XEXP (src, 0) == stack_pointer_rtx)
{
rtx val = XEXP (XEXP (src, 1), 1);
gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS &&
GET_CODE (val) == CONST_INT);
if (code == PRE_MODIFY)
*pre -= INTVAL (val);
else
*post -= INTVAL (val);
break;
}
return;
case PRE_DEC:
if (XEXP (src, 0) == stack_pointer_rtx)
{
*pre += GET_MODE_SIZE (GET_MODE (dest));
break;
}
return;
case POST_DEC:
if (XEXP (src, 0) == stack_pointer_rtx)
{
*post += GET_MODE_SIZE (GET_MODE (dest));
break;
}
return;
case PRE_INC:
if (XEXP (src, 0) == stack_pointer_rtx)
{
*pre -= GET_MODE_SIZE (GET_MODE (dest));
break;
}
return;
case POST_INC:
if (XEXP (src, 0) == stack_pointer_rtx)
{
*post -= GET_MODE_SIZE (GET_MODE (dest));
break;
}
return;
default:
return;
}
}
}
static void
insn_stack_adjust_offset_pre_post (rtx insn, HOST_WIDE_INT *pre,
HOST_WIDE_INT *post)
{
*pre = 0;
*post = 0;
if (GET_CODE (PATTERN (insn)) == SET)
stack_adjust_offset_pre_post (PATTERN (insn), pre, post);
else if (GET_CODE (PATTERN (insn)) == PARALLEL
|| GET_CODE (PATTERN (insn)) == SEQUENCE)
{
int i;
for ( i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
stack_adjust_offset_pre_post (XVECEXP (PATTERN (insn), 0, i),
pre, post);
}
}
static void
bb_stack_adjust_offset (basic_block bb)
{
HOST_WIDE_INT offset;
int i;
offset = VTI (bb)->in.stack_adjust;
for (i = 0; i < VTI (bb)->n_mos; i++)
{
if (VTI (bb)->mos[i].type == MO_ADJUST)
offset += VTI (bb)->mos[i].u.adjust;
else if (VTI (bb)->mos[i].type != MO_CALL)
{
if (MEM_P (VTI (bb)->mos[i].u.loc))
{
VTI (bb)->mos[i].u.loc
= adjust_stack_reference (VTI (bb)->mos[i].u.loc, -offset);
}
}
}
VTI (bb)->out.stack_adjust = offset;
}
static bool
vt_stack_adjustments (void)
{
edge_iterator *stack;
int sp;
VTI (ENTRY_BLOCK_PTR)->visited = true;
VTI (ENTRY_BLOCK_PTR)->out.stack_adjust = INCOMING_FRAME_SP_OFFSET;
stack = XNEWVEC (edge_iterator, n_basic_blocks + 1);
sp = 0;
stack[sp++] = ei_start (ENTRY_BLOCK_PTR->succs);
while (sp)
{
edge_iterator ei;
basic_block src;
basic_block dest;
ei = stack[sp - 1];
src = ei_edge (ei)->src;
dest = ei_edge (ei)->dest;
if (!VTI (dest)->visited)
{
VTI (dest)->visited = true;
VTI (dest)->in.stack_adjust = VTI (src)->out.stack_adjust;
bb_stack_adjust_offset (dest);
if (EDGE_COUNT (dest->succs) > 0)
stack[sp++] = ei_start (dest->succs);
}
else
{
if (VTI (dest)->in.stack_adjust != VTI (src)->out.stack_adjust)
{
free (stack);
return false;
}
if (! ei_one_before_end_p (ei))
ei_next (&stack[sp - 1]);
else
sp--;
}
}
free (stack);
return true;
}
static rtx
adjust_stack_reference (rtx mem, HOST_WIDE_INT adjustment)
{
rtx addr, cfa, tmp;
#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
addr = replace_rtx (copy_rtx (XEXP (mem, 0)), stack_pointer_rtx, cfa);
tmp = simplify_rtx (addr);
if (tmp)
addr = tmp;
return replace_equiv_address_nv (mem, addr);
}
static hashval_t
variable_htab_hash (const void *x)
{
const variable v = (const variable) x;
return (VARIABLE_HASH_VAL (v->decl));
}
static int
variable_htab_eq (const void *x, const void *y)
{
const variable v = (const variable) x;
const tree decl = (const tree) y;
return (VARIABLE_HASH_VAL (v->decl) == VARIABLE_HASH_VAL (decl));
}
static void
variable_htab_free (void *elem)
{
int i;
variable var = (variable) elem;
location_chain node, next;
gcc_assert (var->refcount > 0);
var->refcount--;
if (var->refcount > 0)
return;
for (i = 0; i < var->n_var_parts; i++)
{
for (node = var->var_part[i].loc_chain; node; node = next)
{
next = node->next;
pool_free (loc_chain_pool, node);
}
var->var_part[i].loc_chain = NULL;
}
pool_free (var_pool, var);
}
static void
init_attrs_list_set (attrs *set)
{
int i;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
set[i] = NULL;
}
static void
attrs_list_clear (attrs *listp)
{
attrs list, next;
for (list = *listp; list; list = next)
{
next = list->next;
pool_free (attrs_pool, list);
}
*listp = NULL;
}
static attrs
attrs_list_member (attrs list, tree decl, HOST_WIDE_INT offset)
{
for (; list; list = list->next)
if (list->decl == decl && list->offset == offset)
return list;
return NULL;
}
static void
attrs_list_insert (attrs *listp, tree decl, HOST_WIDE_INT offset, rtx loc)
{
attrs list;
list = pool_alloc (attrs_pool);
list->loc = loc;
list->decl = decl;
list->offset = offset;
list->next = *listp;
*listp = list;
}
static void
attrs_list_copy (attrs *dstp, attrs src)
{
attrs n;
attrs_list_clear (dstp);
for (; src; src = src->next)
{
n = pool_alloc (attrs_pool);
n->loc = src->loc;
n->decl = src->decl;
n->offset = src->offset;
n->next = *dstp;
*dstp = n;
}
}
static void
attrs_list_union (attrs *dstp, attrs src)
{
for (; src; src = src->next)
{
if (!attrs_list_member (*dstp, src->decl, src->offset))
attrs_list_insert (dstp, src->decl, src->offset, src->loc);
}
}
static void
vars_clear (htab_t vars)
{
htab_empty (vars);
}
static variable
unshare_variable (dataflow_set *set, variable var,
enum var_init_status initialized)
{
void **slot;
variable new_var;
int i;
new_var = pool_alloc (var_pool);
new_var->decl = var->decl;
new_var->refcount = 1;
var->refcount--;
new_var->n_var_parts = var->n_var_parts;
for (i = 0; i < var->n_var_parts; i++)
{
location_chain node;
location_chain *nextp;
new_var->var_part[i].offset = var->var_part[i].offset;
nextp = &new_var->var_part[i].loc_chain;
for (node = var->var_part[i].loc_chain; node; node = node->next)
{
location_chain new_lc;
new_lc = pool_alloc (loc_chain_pool);
new_lc->next = NULL;
if (node->init > initialized)
new_lc->init = node->init;
else
new_lc->init = initialized;
if (node->set_src && !(MEM_P (node->set_src)))
new_lc->set_src = node->set_src;
else
new_lc->set_src = NULL;
new_lc->loc = node->loc;
*nextp = new_lc;
nextp = &new_lc->next;
}
if (new_var->var_part[i].loc_chain)
new_var->var_part[i].cur_loc = new_var->var_part[i].loc_chain->loc;
else
new_var->var_part[i].cur_loc = NULL;
}
slot = htab_find_slot_with_hash (set->vars, new_var->decl,
VARIABLE_HASH_VAL (new_var->decl),
INSERT);
*slot = new_var;
return new_var;
}
static int
vars_copy_1 (void **slot, void *data)
{
htab_t dst = (htab_t) data;
variable src, *dstp;
src = *(variable *) slot;
src->refcount++;
dstp = (variable *) htab_find_slot_with_hash (dst, src->decl,
VARIABLE_HASH_VAL (src->decl),
INSERT);
*dstp = src;
return 1;
}
static void
vars_copy (htab_t dst, htab_t src)
{
vars_clear (dst);
htab_traverse (src, vars_copy_1, dst);
}
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;
}
static void
var_reg_set (dataflow_set *set, rtx loc, enum var_init_status initialized,
rtx set_src)
{
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, initialized, set_src);
}
static int
get_init_value (dataflow_set *set, rtx loc, tree decl)
{
void **slot;
variable var;
int i;
int ret_val = STATUS_UNKNOWN;
if (! TARGET_DWARF_UNINIT_VARS)
return STATUS_INITIALIZED;
slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
NO_INSERT);
if (slot)
{
var = * (variable *) slot;
for (i = 0; i < var->n_var_parts && ret_val == STATUS_UNKNOWN; i++)
{
location_chain nextp;
for (nextp = var->var_part[i].loc_chain; nextp; nextp = nextp->next)
if (rtx_equal_p (nextp->loc, loc))
{
ret_val = nextp->init;
break;
}
}
}
return ret_val;
}
static void
var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify,
enum var_init_status initialized, rtx set_src)
{
tree decl = REG_EXPR (loc);
HOST_WIDE_INT offset = REG_OFFSET (loc);
attrs node, next;
attrs *nextp;
decl = var_debug_decl (decl);
if (initialized == STATUS_UNKNOWN)
initialized = get_init_value (set, loc, decl);
nextp = &set->regs[REGNO (loc)];
for (node = *nextp; node; node = next)
{
next = node->next;
if (node->decl != decl || node->offset != offset)
{
delete_variable_part (set, node->loc, node->decl, node->offset);
pool_free (attrs_pool, node);
*nextp = next;
}
else
{
node->loc = loc;
nextp = &node->next;
}
}
if (modify)
clobber_variable_part (set, loc, decl, offset, set_src);
var_reg_set (set, loc, initialized, set_src);
}
static void
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, NULL);
}
for (node = *reg; node; node = next)
{
next = node->next;
delete_variable_part (set, node->loc, node->decl, node->offset);
pool_free (attrs_pool, node);
}
*reg = NULL;
}
static void
var_regno_delete (dataflow_set *set, int regno)
{
attrs *reg = &set->regs[regno];
attrs node, next;
for (node = *reg; node; node = next)
{
next = node->next;
delete_variable_part (set, node->loc, node->decl, node->offset);
pool_free (attrs_pool, node);
}
*reg = NULL;
}
static void
var_mem_set (dataflow_set *set, rtx loc, enum var_init_status initialized,
rtx set_src)
{
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, initialized, set_src);
}
static void
var_mem_delete_and_set (dataflow_set *set, rtx loc, bool modify,
enum var_init_status initialized, rtx set_src)
{
tree decl = MEM_EXPR (loc);
HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
decl = var_debug_decl (decl);
if (initialized == STATUS_UNKNOWN)
initialized = get_init_value (set, loc, decl);
if (modify)
clobber_variable_part (set, NULL, decl, offset, set_src);
var_mem_set (set, loc, initialized, set_src);
}
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, NULL);
delete_variable_part (set, loc, decl, offset);
}
static void
dataflow_set_init (dataflow_set *set, int vars_size)
{
init_attrs_list_set (set->regs);
set->vars = htab_create (vars_size, variable_htab_hash, variable_htab_eq,
variable_htab_free);
set->stack_adjust = 0;
}
static void
dataflow_set_clear (dataflow_set *set)
{
int i;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
attrs_list_clear (&set->regs[i]);
vars_clear (set->vars);
}
static void
dataflow_set_copy (dataflow_set *dst, dataflow_set *src)
{
int i;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
attrs_list_copy (&dst->regs[i], src->regs[i]);
vars_copy (dst->vars, src->vars);
dst->stack_adjust = src->stack_adjust;
}
struct variable_union_info
{
location_chain lc;
int pos;
int pos_src;
int pos_dst;
};
static int
variable_union_info_cmp_pos (const void *n1, const void *n2)
{
const struct variable_union_info *i1 = n1;
const struct variable_union_info *i2 = n2;
if (i1->pos != i2->pos)
return i1->pos - i2->pos;
return (i1->pos_dst - i2->pos_dst);
}
static int
variable_union (void **slot, void *data)
{
variable src, dst, *dstp;
dataflow_set *set = (dataflow_set *) data;
int i, j, k;
src = *(variable *) slot;
dstp = (variable *) htab_find_slot_with_hash (set->vars, src->decl,
VARIABLE_HASH_VAL (src->decl),
INSERT);
if (!*dstp)
{
src->refcount++;
for (k = 0; k < src->n_var_parts; k++)
{
gcc_assert (!src->var_part[k].loc_chain
== !src->var_part[k].cur_loc);
if (src->var_part[k].loc_chain)
{
gcc_assert (src->var_part[k].cur_loc);
if (src->var_part[k].cur_loc != src->var_part[k].loc_chain->loc)
break;
}
}
if (k < src->n_var_parts)
{
enum var_init_status status = STATUS_UNKNOWN;
if (! TARGET_DWARF_UNINIT_VARS)
status = STATUS_INITIALIZED;
unshare_variable (set, src, status);
}
else
*dstp = src;
return 1;
}
else
dst = *dstp;
gcc_assert (src->n_var_parts);
for (i = 0, j = 0, k = 0;
i < src->n_var_parts && j < dst->n_var_parts; k++)
{
if (src->var_part[i].offset == dst->var_part[j].offset)
{
i++;
j++;
}
else if (src->var_part[i].offset < dst->var_part[j].offset)
i++;
else
j++;
}
k += src->n_var_parts - i;
k += dst->n_var_parts - j;
gcc_assert (k <= MAX_VAR_PARTS);
if (dst->refcount > 1 && dst->n_var_parts != k)
{
enum var_init_status status = STATUS_UNKNOWN;
if (! TARGET_DWARF_UNINIT_VARS)
status = STATUS_INITIALIZED;
dst = unshare_variable (set, dst, status);
}
i = src->n_var_parts - 1;
j = dst->n_var_parts - 1;
dst->n_var_parts = k;
for (k--; k >= 0; k--)
{
location_chain node, node2;
if (i >= 0 && j >= 0
&& src->var_part[i].offset == dst->var_part[j].offset)
{
int dst_l, src_l;
int ii, jj, n;
struct variable_union_info *vui;
if (dst->refcount > 1)
{
for (node = src->var_part[i].loc_chain,
node2 = dst->var_part[j].loc_chain; node && node2;
node = node->next, node2 = node2->next)
{
if (!((REG_P (node2->loc)
&& REG_P (node->loc)
&& REGNO (node2->loc) == REGNO (node->loc))
|| rtx_equal_p (node2->loc, node->loc)))
if (node2->init < node->init)
node2->init = node->init;
break;
}
if (node || node2)
dst = unshare_variable (set, dst, STATUS_UNKNOWN);
}
src_l = 0;
for (node = src->var_part[i].loc_chain; node; node = node->next)
src_l++;
dst_l = 0;
for (node = dst->var_part[j].loc_chain; node; node = node->next)
dst_l++;
vui = XCNEWVEC (struct variable_union_info, src_l + dst_l);
for (node = dst->var_part[j].loc_chain, jj = 0; node;
node = node->next, jj++)
{
vui[jj].lc = node;
vui[jj].pos_dst = jj;
vui[jj].pos_src = src_l + dst_l;
}
n = dst_l;
for (node = src->var_part[i].loc_chain, ii = 0; node;
node = node->next, ii++)
{
for (jj = 0; jj < dst_l; jj++)
{
if ((REG_P (vui[jj].lc->loc)
&& REG_P (node->loc)
&& REGNO (vui[jj].lc->loc) == REGNO (node->loc))
|| rtx_equal_p (vui[jj].lc->loc, node->loc))
{
vui[jj].pos_src = ii;
break;
}
}
if (jj >= dst_l)
{
location_chain new_node;
new_node = pool_alloc (loc_chain_pool);
new_node->loc = node->loc;
new_node->init = node->init;
if (!node->set_src || MEM_P (node->set_src))
new_node->set_src = NULL;
else
new_node->set_src = node->set_src;
vui[n].lc = new_node;
vui[n].pos_src = ii;
vui[n].pos_dst = src_l + dst_l;
n++;
}
}
for (ii = 0; ii < src_l + dst_l; ii++)
vui[ii].pos = vui[ii].pos_src + vui[ii].pos_dst;
qsort (vui, n, sizeof (struct variable_union_info),
variable_union_info_cmp_pos);
for (ii = 1; ii < n; ii++)
vui[ii - 1].lc->next = vui[ii].lc;
vui[n - 1].lc->next = NULL;
dst->var_part[k].loc_chain = vui[0].lc;
dst->var_part[k].offset = dst->var_part[j].offset;
free (vui);
i--;
j--;
}
else if ((i >= 0 && j >= 0
&& src->var_part[i].offset < dst->var_part[j].offset)
|| i < 0)
{
dst->var_part[k] = dst->var_part[j];
j--;
}
else if ((i >= 0 && j >= 0
&& src->var_part[i].offset > dst->var_part[j].offset)
|| j < 0)
{
location_chain *nextp;
nextp = &dst->var_part[k].loc_chain;
for (node = src->var_part[i].loc_chain; node; node = node->next)
{
location_chain new_lc;
new_lc = pool_alloc (loc_chain_pool);
new_lc->next = NULL;
new_lc->init = node->init;
if (!node->set_src || MEM_P (node->set_src))
new_lc->set_src = NULL;
else
new_lc->set_src = node->set_src;
new_lc->loc = node->loc;
*nextp = new_lc;
nextp = &new_lc->next;
}
dst->var_part[k].offset = src->var_part[i].offset;
i--;
}
if (dst->var_part[k].loc_chain)
dst->var_part[k].cur_loc = dst->var_part[k].loc_chain->loc;
else
dst->var_part[k].cur_loc = NULL;
}
for (i = 0; i < src->n_var_parts && i < dst->n_var_parts; i++)
{
location_chain node, node2;
for (node = src->var_part[i].loc_chain; node; node = node->next)
for (node2 = dst->var_part[i].loc_chain; node2; node2 = node2->next)
if (rtx_equal_p (node->loc, node2->loc))
{
if (node->init > node2->init)
node2->init = node->init;
}
}
return 1;
}
static void
dataflow_set_union (dataflow_set *dst, dataflow_set *src)
{
int i;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
attrs_list_union (&dst->regs[i], src->regs[i]);
htab_traverse (src->vars, variable_union, dst);
}
static bool
dataflow_set_different_value;
static bool
variable_part_different_p (variable_part *vp1, variable_part *vp2)
{
location_chain lc1, lc2;
for (lc1 = vp1->loc_chain; lc1; lc1 = lc1->next)
{
for (lc2 = vp2->loc_chain; lc2; lc2 = lc2->next)
{
if (REG_P (lc1->loc) && REG_P (lc2->loc))
{
if (REGNO (lc1->loc) == REGNO (lc2->loc))
break;
}
if (rtx_equal_p (lc1->loc, lc2->loc))
break;
}
if (!lc2)
return true;
}
return false;
}
static bool
variable_different_p (variable var1, variable var2,
bool compare_current_location)
{
int i;
if (var1 == var2)
return false;
if (var1->n_var_parts != var2->n_var_parts)
return true;
for (i = 0; i < var1->n_var_parts; i++)
{
if (var1->var_part[i].offset != var2->var_part[i].offset)
return true;
if (compare_current_location)
{
if (!((REG_P (var1->var_part[i].cur_loc)
&& REG_P (var2->var_part[i].cur_loc)
&& (REGNO (var1->var_part[i].cur_loc)
== REGNO (var2->var_part[i].cur_loc)))
|| rtx_equal_p (var1->var_part[i].cur_loc,
var2->var_part[i].cur_loc)))
return true;
}
if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i]))
return true;
if (variable_part_different_p (&var2->var_part[i], &var1->var_part[i]))
return true;
}
return false;
}
static int
dataflow_set_different_1 (void **slot, void *data)
{
htab_t htab = (htab_t) data;
variable var1, var2;
var1 = *(variable *) slot;
var2 = htab_find_with_hash (htab, var1->decl,
VARIABLE_HASH_VAL (var1->decl));
if (!var2)
{
dataflow_set_different_value = true;
return 0;
}
if (variable_different_p (var1, var2, false))
{
dataflow_set_different_value = true;
return 0;
}
return 1;
}
static int
dataflow_set_different_2 (void **slot, void *data)
{
htab_t htab = (htab_t) data;
variable var1, var2;
var1 = *(variable *) slot;
var2 = htab_find_with_hash (htab, var1->decl,
VARIABLE_HASH_VAL (var1->decl));
if (!var2)
{
dataflow_set_different_value = true;
return 0;
}
gcc_assert (!variable_different_p (var1, var2, false));
return 1;
}
static bool
dataflow_set_different (dataflow_set *old_set, dataflow_set *new_set)
{
dataflow_set_different_value = false;
htab_traverse (old_set->vars, dataflow_set_different_1, new_set->vars);
if (!dataflow_set_different_value)
{
htab_traverse (new_set->vars, dataflow_set_different_2, old_set->vars);
}
return dataflow_set_different_value;
}
static void
dataflow_set_destroy (dataflow_set *set)
{
int i;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
attrs_list_clear (&set->regs[i]);
htab_delete (set->vars);
set->vars = NULL;
}
static bool
contains_symbol_ref (rtx x)
{
const char *fmt;
RTX_CODE code;
int i;
if (!x)
return false;
code = GET_CODE (x);
if (code == SYMBOL_REF)
return true;
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
if (contains_symbol_ref (XEXP (x, i)))
return true;
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
if (contains_symbol_ref (XVECEXP (x, i, j)))
return true;
}
}
return false;
}
static bool
track_expr_p (tree expr)
{
rtx decl_rtl;
tree realdecl;
if (TREE_CODE (expr) != VAR_DECL && TREE_CODE (expr) != PARM_DECL)
return 0;
if (!DECL_NAME (expr))
return 0;
decl_rtl = DECL_RTL_IF_SET (expr);
if (!decl_rtl)
return 0;
realdecl = expr;
if (DECL_DEBUG_EXPR_IS_FROM (realdecl) && DECL_DEBUG_EXPR (realdecl))
{
realdecl = DECL_DEBUG_EXPR (realdecl);
if (!DECL_P (realdecl))
return 0;
}
if (DECL_IGNORED_P (realdecl))
return 0;
if (TREE_STATIC (realdecl))
return 0;
if (MEM_P (decl_rtl)
&& contains_symbol_ref (XEXP (decl_rtl, 0)))
return 0;
if (MEM_P (decl_rtl))
{
if (GET_MODE (decl_rtl) == BLKmode
|| AGGREGATE_TYPE_P (TREE_TYPE (realdecl)))
return 0;
if (MEM_SIZE (decl_rtl)
&& INTVAL (MEM_SIZE (decl_rtl)) > MAX_VAR_PARTS)
return 0;
}
return 1;
}
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);
}
static int
count_uses (rtx *loc, void *insn)
{
basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
if (REG_P (*loc))
{
gcc_assert (REGNO (*loc) < FIRST_PSEUDO_REGISTER);
VTI (bb)->n_mos++;
}
else if (MEM_P (*loc)
&& MEM_EXPR (*loc)
&& track_expr_p (MEM_EXPR (*loc)))
{
VTI (bb)->n_mos++;
}
return 0;
}
static void
count_uses_1 (rtx *x, void *insn)
{
for_each_rtx (x, count_uses, insn);
}
static void
count_stores (rtx loc, rtx expr ATTRIBUTE_UNUSED, void *insn)
{
count_uses (&loc, insn);
}
static int
add_uses (rtx *loc, void *insn)
{
if (REG_P (*loc))
{
basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
mo->type = ((REG_EXPR (*loc) && track_expr_p (REG_EXPR (*loc)))
? MO_USE : MO_USE_NO_VAR);
mo->u.loc = *loc;
mo->insn = (rtx) insn;
}
else if (MEM_P (*loc)
&& MEM_EXPR (*loc)
&& track_expr_p (MEM_EXPR (*loc)))
{
basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
mo->type = MO_USE;
mo->u.loc = *loc;
mo->insn = (rtx) insn;
}
return 0;
}
static void
add_uses_1 (rtx *x, void *insn)
{
for_each_rtx (x, add_uses, insn);
}
static void
add_stores (rtx loc, rtx expr, void *insn)
{
if (REG_P (loc))
{
basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
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;
}
else if (MEM_P (loc)
&& MEM_EXPR (loc)
&& track_expr_p (MEM_EXPR (loc)))
{
basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
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;
}
}
static enum var_init_status
find_src_status (dataflow_set *in, rtx loc, rtx insn)
{
rtx src = NULL_RTX;
tree decl = NULL_TREE;
enum var_init_status status = STATUS_UNINITIALIZED;
if (! TARGET_DWARF_UNINIT_VARS)
status = STATUS_INITIALIZED;
if (GET_CODE (PATTERN (insn)) == SET)
src = SET_SRC (PATTERN (insn));
else if (GET_CODE (PATTERN (insn)) == PARALLEL
|| GET_CODE (PATTERN (insn)) == SEQUENCE)
{
int i;
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET
&& SET_DEST (XVECEXP (PATTERN (insn), 0, i)) == loc)
src = SET_SRC (XVECEXP (PATTERN (insn), 0, i));
}
else if (GET_CODE (PATTERN (insn)) == COND_EXEC
&& GET_CODE (COND_EXEC_CODE (PATTERN (insn))) == SET)
src = SET_SRC (COND_EXEC_CODE (PATTERN (insn)));
else
gcc_unreachable ();
if (REG_P (src))
decl = var_debug_decl (REG_EXPR (src));
else if (MEM_P (src))
decl = var_debug_decl (MEM_EXPR (src));
if (src && decl)
status = get_init_value (in, src, decl);
return status;
}
static rtx
find_src_set_src (dataflow_set *set, rtx loc, rtx insn)
{
tree decl = NULL_TREE;
rtx src = NULL_RTX;
rtx set_src = NULL_RTX;
void **slot;
variable var;
location_chain nextp;
int i;
bool found;
if (GET_CODE (PATTERN (insn)) == SET)
src = SET_SRC (PATTERN (insn));
else if (GET_CODE (PATTERN (insn)) == PARALLEL
|| GET_CODE (PATTERN (insn)) == SEQUENCE)
{
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET
&& SET_DEST (XVECEXP (PATTERN (insn), 0, i)) == loc)
src = SET_SRC (XVECEXP (PATTERN (insn), 0, i));
}
else if (GET_CODE (PATTERN (insn)) == COND_EXEC
&& GET_CODE (COND_EXEC_CODE (PATTERN (insn))) == SET)
src = SET_SRC (COND_EXEC_CODE (PATTERN (insn)));
else
gcc_unreachable ();
if (REG_P (src))
decl = var_debug_decl (REG_EXPR (src));
else if (MEM_P (src))
decl = var_debug_decl (MEM_EXPR (src));
if (src && decl)
{
slot = htab_find_slot_with_hash (set->vars, decl,
VARIABLE_HASH_VAL (decl), NO_INSERT);
if (slot)
{
var = *(variable *) slot;
found = false;
for (i = 0; i < var->n_var_parts && !found; i++)
for (nextp = var->var_part[i].loc_chain; nextp && !found;
nextp = nextp->next)
if (rtx_equal_p (nextp->loc, src))
{
set_src = nextp->set_src;
found = true;
}
}
}
return set_src;
}
static bool
compute_bb_dataflow (basic_block bb)
{
int i, n, r;
bool changed;
dataflow_set old_out;
dataflow_set *in = &VTI (bb)->in;
dataflow_set *out = &VTI (bb)->out;
dataflow_set_init (&old_out, htab_elements (VTI (bb)->out.vars) + 3);
dataflow_set_copy (&old_out, out);
dataflow_set_copy (out, in);
n = VTI (bb)->n_mos;
for (i = 0; i < n; i++)
{
switch (VTI (bb)->mos[i].type)
{
case MO_CALL:
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
if (TEST_HARD_REG_BIT (call_used_reg_set, r))
var_regno_delete (out, r);
break;
case MO_USE:
{
rtx loc = VTI (bb)->mos[i].u.loc;
enum var_init_status status = STATUS_UNINITIALIZED;
if (! TARGET_DWARF_UNINIT_VARS)
status = STATUS_INITIALIZED;
if (GET_CODE (loc) == REG)
var_reg_set (out, loc, status, NULL);
else if (GET_CODE (loc) == MEM)
var_mem_set (out, loc, status, NULL);
}
break;
case MO_SET:
{
rtx loc = VTI (bb)->mos[i].u.loc;
rtx set_src = NULL;
rtx insn = VTI (bb)->mos[i].insn;
if (GET_CODE (PATTERN (insn)) == SET)
set_src = SET_SRC (PATTERN (insn));
else if (GET_CODE (PATTERN (insn)) == PARALLEL
|| GET_CODE (PATTERN (insn)) == SEQUENCE)
{
int j;
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
&& SET_DEST (XVECEXP (PATTERN (insn), 0, j)) == loc)
set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, j));
}
if (REG_P (loc))
var_reg_delete_and_set (out, loc, true, STATUS_INITIALIZED,
set_src);
else if (MEM_P (loc))
var_mem_delete_and_set (out, loc, true, STATUS_INITIALIZED,
set_src);
}
break;
case MO_COPY:
{
rtx loc = VTI (bb)->mos[i].u.loc;
enum var_init_status src_status;
rtx set_src;
if (! TARGET_DWARF_UNINIT_VARS)
src_status = STATUS_INITIALIZED;
else
src_status = find_src_status (in, loc, VTI (bb)->mos[i].insn);
if (src_status == STATUS_UNKNOWN)
src_status = find_src_status (out, loc, VTI (bb)->mos[i].insn);
set_src = find_src_set_src (in, loc, VTI (bb)->mos[i].insn);
if (REG_P (loc))
var_reg_delete_and_set (out, loc, false, src_status, set_src);
else if (MEM_P (loc))
var_mem_delete_and_set (out, loc, false, src_status, set_src);
}
break;
case MO_USE_NO_VAR:
{
rtx loc = VTI (bb)->mos[i].u.loc;
if (REG_P (loc))
var_reg_delete (out, loc, false);
else if (MEM_P (loc))
var_mem_delete (out, loc, false);
}
break;
case MO_CLOBBER:
{
rtx loc = VTI (bb)->mos[i].u.loc;
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;
}
}
changed = dataflow_set_different (&old_out, out);
dataflow_set_destroy (&old_out);
return changed;
}
static void
vt_find_locations (void)
{
fibheap_t worklist, pending, fibheap_swap;
sbitmap visited, in_worklist, in_pending, sbitmap_swap;
basic_block bb;
edge e;
int *bb_order;
int *rc_order;
int i;
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);
worklist = fibheap_new ();
pending = fibheap_new ();
visited = sbitmap_alloc (last_basic_block);
in_worklist = sbitmap_alloc (last_basic_block);
in_pending = sbitmap_alloc (last_basic_block);
sbitmap_zero (in_worklist);
FOR_EACH_BB (bb)
fibheap_insert (pending, bb_order[bb->index], bb);
sbitmap_ones (in_pending);
while (!fibheap_empty (pending))
{
fibheap_swap = pending;
pending = worklist;
worklist = fibheap_swap;
sbitmap_swap = in_pending;
in_pending = in_worklist;
in_worklist = sbitmap_swap;
sbitmap_zero (visited);
while (!fibheap_empty (worklist))
{
bb = fibheap_extract_min (worklist);
RESET_BIT (in_worklist, bb->index);
if (!TEST_BIT (visited, bb->index))
{
bool changed;
edge_iterator ei;
SET_BIT (visited, bb->index);
dataflow_set_clear (&VTI (bb)->in);
FOR_EACH_EDGE (e, ei, bb->preds)
{
dataflow_set_union (&VTI (bb)->in, &VTI (e->src)->out);
}
changed = compute_bb_dataflow (bb);
if (changed)
{
FOR_EACH_EDGE (e, ei, bb->succs)
{
if (e->dest == EXIT_BLOCK_PTR)
continue;
if (e->dest == bb)
continue;
if (TEST_BIT (visited, e->dest->index))
{
if (!TEST_BIT (in_pending, e->dest->index))
{
SET_BIT (in_pending, e->dest->index);
fibheap_insert (pending,
bb_order[e->dest->index],
e->dest);
}
}
else if (!TEST_BIT (in_worklist, e->dest->index))
{
SET_BIT (in_worklist, e->dest->index);
fibheap_insert (worklist, bb_order[e->dest->index],
e->dest);
}
}
}
}
}
}
free (bb_order);
fibheap_delete (worklist);
fibheap_delete (pending);
sbitmap_free (visited);
sbitmap_free (in_worklist);
sbitmap_free (in_pending);
}
static void
dump_attrs_list (attrs list)
{
for (; list; list = list->next)
{
print_mem_expr (dump_file, list->decl);
fprintf (dump_file, "+" HOST_WIDE_INT_PRINT_DEC, list->offset);
}
fprintf (dump_file, "\n");
}
static int
dump_variable (void **slot, void *data ATTRIBUTE_UNUSED)
{
variable var = *(variable *) slot;
int i;
location_chain node;
fprintf (dump_file, " name: %s\n",
IDENTIFIER_POINTER (DECL_NAME (var->decl)));
for (i = 0; i < var->n_var_parts; i++)
{
fprintf (dump_file, " offset %ld\n",
(long) var->var_part[i].offset);
for (node = var->var_part[i].loc_chain; node; node = node->next)
{
fprintf (dump_file, " ");
if (node->init == STATUS_UNINITIALIZED)
fprintf (dump_file, "[uninit]");
print_rtl_single (dump_file, node->loc);
}
}
return 1;
}
static void
dump_vars (htab_t vars)
{
if (htab_elements (vars) > 0)
{
fprintf (dump_file, "Variables:\n");
htab_traverse (vars, dump_variable, NULL);
}
}
static void
dump_dataflow_set (dataflow_set *set)
{
int 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])
{
fprintf (dump_file, "Reg %d:", i);
dump_attrs_list (set->regs[i]);
}
}
dump_vars (set->vars);
fprintf (dump_file, "\n");
}
static void
dump_dataflow_sets (void)
{
basic_block bb;
FOR_EACH_BB (bb)
{
fprintf (dump_file, "\nBasic block %d:\n", bb->index);
fprintf (dump_file, "IN:\n");
dump_dataflow_set (&VTI (bb)->in);
fprintf (dump_file, "OUT:\n");
dump_dataflow_set (&VTI (bb)->out);
}
}
static const char *_set_name;
static FILE *_f;
static int
dump_dataflow_set_difference_1 (void **slot, void *data)
{
htab_t htab = (htab_t) data;
variable var1, var2;
var1 = *(variable *) slot;
var2 = htab_find_with_hash (htab, var1->decl,
VARIABLE_HASH_VAL (var1->decl));
if (!var2)
fprintf (_f, "%s only in %s set\n", lang_hooks.decl_printable_name (var1->decl, 0), _set_name);
else if (variable_different_p (var1, var2, false))
fprintf (_f, "%s present but not identical in sets\n", lang_hooks.decl_printable_name (var1->decl, 0));
return 1;
}
void debug_dataflow_set_difference (dataflow_set *, dataflow_set *);
void
debug_dataflow_set_difference (dataflow_set *left_set, dataflow_set *right_set)
{
_f = stderr;
_set_name = "left";
htab_traverse (left_set->vars, dump_dataflow_set_difference_1, right_set->vars);
_set_name = "right";
htab_traverse (right_set->vars, dump_dataflow_set_difference_1, left_set->vars);
}
void debug_dataflow_set (dataflow_set *);
void
debug_dataflow_set (dataflow_set *set)
{
FILE *saved_dump_file = dump_file;
dump_file = stderr;
dump_dataflow_set (set);
dump_file = saved_dump_file;
}
void debug_var_tracking (void);
void
debug_var_tracking (void)
{
FILE *saved_dump_file = dump_file;
dump_file = stderr;
dump_dataflow_sets ();
dump_flow_info (stderr, dump_flags);
dump_file = saved_dump_file;
}
static void
variable_was_changed (variable var, htab_t htab)
{
hashval_t hash = VARIABLE_HASH_VAL (var->decl);
if (emit_notes)
{
variable *slot;
slot = (variable *) htab_find_slot_with_hash (changed_variables,
var->decl, hash, INSERT);
if (htab && var->n_var_parts == 0)
{
variable empty_var;
void **old;
empty_var = pool_alloc (var_pool);
empty_var->decl = var->decl;
empty_var->refcount = 1;
empty_var->n_var_parts = 0;
*slot = empty_var;
old = htab_find_slot_with_hash (htab, var->decl, hash,
NO_INSERT);
if (old)
htab_clear_slot (htab, old);
}
else
{
*slot = var;
}
}
else
{
gcc_assert (htab);
if (var->n_var_parts == 0)
{
void **slot = htab_find_slot_with_hash (htab, var->decl, hash,
NO_INSERT);
if (slot)
htab_clear_slot (htab, slot);
}
}
}
static inline int
find_variable_location_part (variable var, HOST_WIDE_INT offset,
int *insertion_point)
{
int pos, low, high;
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;
if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
return pos;
return -1;
}
static void
set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset,
enum var_init_status initialized, rtx set_src)
{
int pos;
location_chain node, next;
location_chain *nextp;
variable var;
void **slot;
slot = htab_find_slot_with_hash (set->vars, decl,
VARIABLE_HASH_VAL (decl), INSERT);
if (!*slot)
{
var = pool_alloc (var_pool);
var->decl = decl;
var->refcount = 1;
var->n_var_parts = 1;
var->var_part[0].offset = offset;
var->var_part[0].loc_chain = NULL;
var->var_part[0].cur_loc = NULL;
*slot = var;
pos = 0;
}
else
{
int inspos = 0;
var = (variable) *slot;
pos = find_variable_location_part (var, offset, &inspos);
if (pos >= 0)
{
node = var->var_part[pos].loc_chain;
if (node
&& ((REG_P (node->loc) && REG_P (loc)
&& REGNO (node->loc) == REGNO (loc))
|| rtx_equal_p (node->loc, loc)))
{
if (node->init < initialized)
node->init = initialized;
if (set_src != NULL)
node->set_src = set_src;
*slot = var;
return;
}
else
{
if (var->refcount > 1)
var = unshare_variable (set, var, initialized);
}
}
else
{
if (var->refcount > 1)
var = unshare_variable (set, var, initialized);
gcc_assert (var->n_var_parts < MAX_VAR_PARTS);
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;
var->var_part[pos].loc_chain = NULL;
var->var_part[pos].cur_loc = NULL;
}
}
nextp = &var->var_part[pos].loc_chain;
for (node = var->var_part[pos].loc_chain; node; node = next)
{
next = node->next;
if ((REG_P (node->loc) && REG_P (loc)
&& REGNO (node->loc) == REGNO (loc))
|| rtx_equal_p (node->loc, loc))
{
if (node->init > initialized)
initialized = node->init;
if (node->set_src != NULL && set_src == NULL)
set_src = node->set_src;
pool_free (loc_chain_pool, node);
*nextp = next;
break;
}
else
nextp = &node->next;
}
node = pool_alloc (loc_chain_pool);
node->loc = loc;
node->init = initialized;
node->set_src = set_src;
node->next = var->var_part[pos].loc_chain;
var->var_part[pos].loc_chain = node;
if (var->var_part[pos].cur_loc == NULL)
{
var->var_part[pos].cur_loc = loc;
variable_was_changed (var, set->vars);
}
}
static void
clobber_variable_part (dataflow_set *set, rtx loc, tree decl,
HOST_WIDE_INT offset, rtx set_src)
{
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;
next = var->var_part[pos].loc_chain;
for (node = next; node; node = next)
{
next = node->next;
if (node->loc != loc
&& (!(TARGET_DWARF_UNINIT_VARS)
|| !set_src
|| MEM_P (set_src)
|| !rtx_equal_p (set_src, node->set_src)))
{
if (REG_P (node->loc))
{
attrs anode, anext;
attrs *anextp;
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;
}
else
anextp = &anode->next;
}
}
delete_variable_part (set, node->loc, decl, offset);
}
}
}
}
}
static void
delete_variable_part (dataflow_set *set, rtx loc, tree decl,
HOST_WIDE_INT offset)
{
void **slot;
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;
location_chain *nextp;
bool changed;
if (var->refcount > 1)
{
for (node = var->var_part[pos].loc_chain; node;
node = node->next)
{
if ((REG_P (node->loc) && REG_P (loc)
&& REGNO (node->loc) == REGNO (loc))
|| rtx_equal_p (node->loc, loc))
{
enum var_init_status status = STATUS_UNKNOWN;
if (! TARGET_DWARF_UNINIT_VARS)
status = STATUS_INITIALIZED;
var = unshare_variable (set, var, status);
break;
}
}
}
nextp = &var->var_part[pos].loc_chain;
for (node = *nextp; node; node = next)
{
next = node->next;
if ((REG_P (node->loc) && REG_P (loc)
&& REGNO (node->loc) == REGNO (loc))
|| rtx_equal_p (node->loc, loc))
{
pool_free (loc_chain_pool, node);
*nextp = next;
break;
}
else
nextp = &node->next;
}
if (var->var_part[pos].cur_loc
&& ((REG_P (loc)
&& REG_P (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)))
{
changed = true;
if (var->var_part[pos].loc_chain)
var->var_part[pos].cur_loc = var->var_part[pos].loc_chain->loc;
}
else
changed = false;
if (var->var_part[pos].loc_chain == NULL)
{
var->n_var_parts--;
while (pos < var->n_var_parts)
{
var->var_part[pos] = var->var_part[pos + 1];
pos++;
}
}
if (changed)
variable_was_changed (var, set->vars);
}
}
}
static int
emit_note_insn_var_location (void **varp, void *data)
{
variable var = *(variable *) varp;
rtx insn = ((emit_note_data *)data)->insn;
enum emit_note_where where = ((emit_note_data *)data)->where;
rtx note;
int i, j, n_var_parts;
bool complete;
enum var_init_status initialized = STATUS_UNINITIALIZED;
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);
if (! TARGET_DWARF_UNINIT_VARS)
initialized = STATUS_INITIALIZED;
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;
}
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]);
initialized = var->var_part[i].loc_chain->init;
last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
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))
complete = false;
if (where == EMIT_NOTE_AFTER_INSN)
note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
else
note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
if (!(TARGET_DWARF_UNINIT_VARS))
initialized = STATUS_INITIALIZED;
if (!complete)
{
NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
NULL_RTX, 0);
}
else if (n_var_parts == 1)
{
rtx expr_list
= gen_rtx_EXPR_LIST (VOIDmode, loc[0], GEN_INT (offsets[0]));
NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
expr_list,
(int) initialized);
}
else if (n_var_parts)
{
rtx parallel;
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 (n_var_parts, loc));
NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
parallel,
(int) initialized);
}
NOTE_VAR_LOCATION_STATUS (note) = (int) initialized;
htab_clear_slot (changed_variables, varp);
if (var->n_var_parts == 0)
{
pool_free (var_pool, var);
}
return 1;
}
static void
emit_notes_for_changes (rtx insn, enum emit_note_where where)
{
emit_note_data data;
data.insn = insn;
data.where = where;
htab_traverse (changed_variables, emit_note_insn_var_location, &data);
}
static int
emit_notes_for_differences_1 (void **slot, void *data)
{
htab_t new_vars = (htab_t) data;
variable old_var, new_var;
old_var = *(variable *) slot;
new_var = htab_find_with_hash (new_vars, old_var->decl,
VARIABLE_HASH_VAL (old_var->decl));
if (!new_var)
{
variable empty_var;
empty_var = pool_alloc (var_pool);
empty_var->decl = old_var->decl;
empty_var->refcount = 1;
empty_var->n_var_parts = 0;
variable_was_changed (empty_var, NULL);
}
else if (variable_different_p (old_var, new_var, true))
{
variable_was_changed (new_var, NULL);
}
return 1;
}
static int
emit_notes_for_differences_2 (void **slot, void *data)
{
htab_t old_vars = (htab_t) data;
variable old_var, new_var;
new_var = *(variable *) slot;
old_var = htab_find_with_hash (old_vars, new_var->decl,
VARIABLE_HASH_VAL (new_var->decl));
if (!old_var)
{
variable_was_changed (new_var, NULL);
}
return 1;
}
static void
emit_notes_for_differences (rtx insn, dataflow_set *old_set,
dataflow_set *new_set)
{
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);
emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
}
static void
emit_notes_in_bb (basic_block bb)
{
int i;
dataflow_set set;
dataflow_set_init (&set, htab_elements (VTI (bb)->in.vars) + 3);
dataflow_set_copy (&set, &VTI (bb)->in);
for (i = 0; i < VTI (bb)->n_mos; i++)
{
rtx insn = VTI (bb)->mos[i].insn;
switch (VTI (bb)->mos[i].type)
{
case MO_CALL:
{
int r;
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
if (TEST_HARD_REG_BIT (call_used_reg_set, r))
{
var_regno_delete (&set, r);
}
emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
}
break;
case MO_USE:
{
rtx loc = VTI (bb)->mos[i].u.loc;
enum var_init_status status = STATUS_UNINITIALIZED;
if (! TARGET_DWARF_UNINIT_VARS)
status = STATUS_INITIALIZED;
if (GET_CODE (loc) == REG)
var_reg_set (&set, loc, status, NULL);
else
var_mem_set (&set, loc, status, NULL);
emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
}
break;
case MO_SET:
{
rtx loc = VTI (bb)->mos[i].u.loc;
rtx set_src = NULL;
if (GET_CODE (PATTERN (insn)) == SET)
set_src = SET_SRC (PATTERN (insn));
else if (GET_CODE (PATTERN (insn)) == PARALLEL
|| GET_CODE (PATTERN (insn)) == SEQUENCE)
{
int j;
for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
&& SET_DEST (XVECEXP (PATTERN (insn), 0, j)) == loc)
set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, j));
}
if (REG_P (loc))
var_reg_delete_and_set (&set, loc, true, STATUS_INITIALIZED,
set_src);
else
var_mem_delete_and_set (&set, loc, true, STATUS_INITIALIZED,
set_src);
emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN);
}
break;
case MO_COPY:
{
rtx loc = VTI (bb)->mos[i].u.loc;
enum var_init_status src_status;
rtx set_src;
src_status = find_src_status (&set, loc, VTI (bb)->mos[i].insn);
set_src = find_src_set_src (&set, loc, VTI (bb)->mos[i].insn);
if (REG_P (loc))
var_reg_delete_and_set (&set, loc, false, src_status, set_src);
else
var_mem_delete_and_set (&set, loc, false, src_status, set_src);
emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN);
}
break;
case MO_USE_NO_VAR:
{
rtx loc = VTI (bb)->mos[i].u.loc;
if (REG_P (loc))
var_reg_delete (&set, loc, false);
else
var_mem_delete (&set, loc, false);
emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
}
break;
case MO_CLOBBER:
{
rtx loc = VTI (bb)->mos[i].u.loc;
if (REG_P (loc))
var_reg_delete (&set, loc, true);
else
var_mem_delete (&set, loc, true);
emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN);
}
break;
case MO_ADJUST:
set.stack_adjust += VTI (bb)->mos[i].u.adjust;
break;
}
}
dataflow_set_destroy (&set);
}
static void
vt_emit_notes (void)
{
basic_block bb;
dataflow_set *last_out;
dataflow_set empty;
gcc_assert (!htab_elements (changed_variables));
emit_notes = true;
dataflow_set_init (&empty, 7);
last_out = ∅
FOR_EACH_BB (bb)
{
emit_notes_for_differences (BB_HEAD (bb), last_out, &VTI (bb)->in);
emit_notes_in_bb (bb);
last_out = &VTI (bb)->out;
}
dataflow_set_destroy (&empty);
emit_notes = false;
}
static bool
vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp)
{
if (REG_P (rtl))
{
if (REG_ATTRS (rtl))
{
*declp = REG_EXPR (rtl);
*offsetp = REG_OFFSET (rtl);
return true;
}
}
else if (MEM_P (rtl))
{
if (MEM_ATTRS (rtl))
{
*declp = MEM_EXPR (rtl);
*offsetp = MEM_OFFSET (rtl) ? INTVAL (MEM_OFFSET (rtl)) : 0;
return true;
}
}
return false;
}
static void
vt_add_function_parameters (void)
{
tree parm;
for (parm = DECL_ARGUMENTS (current_function_decl);
parm; parm = TREE_CHAIN (parm))
{
rtx decl_rtl = DECL_RTL_IF_SET (parm);
rtx incoming = DECL_INCOMING_RTL (parm);
tree decl;
HOST_WIDE_INT offset;
dataflow_set *out;
if (TREE_CODE (parm) != PARM_DECL)
continue;
if (!DECL_NAME (parm))
continue;
if (!decl_rtl || !incoming)
continue;
if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode)
continue;
if (!vt_get_decl_and_offset (incoming, &decl, &offset))
if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
continue;
if (!decl)
continue;
gcc_assert (parm == decl);
out = &VTI (ENTRY_BLOCK_PTR)->out;
if (REG_P (incoming))
{
gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
attrs_list_insert (&out->regs[REGNO (incoming)],
parm, offset, incoming);
set_variable_part (out, incoming, parm, offset, STATUS_INITIALIZED,
NULL);
}
else if (MEM_P (incoming))
set_variable_part (out, incoming, parm, offset, STATUS_INITIALIZED,
NULL);
}
}
static void
vt_initialize (void)
{
basic_block bb;
alloc_aux_for_blocks (sizeof (struct variable_tracking_info_def));
FOR_EACH_BB (bb)
{
rtx insn;
HOST_WIDE_INT pre, post = 0;
VTI (bb)->n_mos = 0;
for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
insn = NEXT_INSN (insn))
{
if (INSN_P (insn))
{
if (!frame_pointer_needed)
{
insn_stack_adjust_offset_pre_post (insn, &pre, &post);
if (pre)
VTI (bb)->n_mos++;
if (post)
VTI (bb)->n_mos++;
}
note_uses (&PATTERN (insn), count_uses_1, insn);
note_stores (PATTERN (insn), count_stores, insn);
if (CALL_P (insn))
VTI (bb)->n_mos++;
}
}
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))
{
if (INSN_P (insn))
{
int n1, n2;
if (!frame_pointer_needed)
{
insn_stack_adjust_offset_pre_post (insn, &pre, &post);
if (pre)
{
micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
mo->type = MO_ADJUST;
mo->u.adjust = pre;
mo->insn = insn;
}
}
n1 = VTI (bb)->n_mos;
note_uses (&PATTERN (insn), add_uses_1, insn);
n2 = VTI (bb)->n_mos - 1;
while (n1 < n2)
{
while (n1 < n2 && VTI (bb)->mos[n1].type == MO_USE)
n1++;
while (n1 < n2 && VTI (bb)->mos[n2].type == MO_USE_NO_VAR)
n2--;
if (n1 < n2)
{
micro_operation sw;
sw = VTI (bb)->mos[n1];
VTI (bb)->mos[n1] = VTI (bb)->mos[n2];
VTI (bb)->mos[n2] = sw;
}
}
if (CALL_P (insn))
{
micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
mo->type = MO_CALL;
mo->insn = insn;
}
n1 = VTI (bb)->n_mos;
note_stores (PATTERN (insn), add_stores, insn);
n2 = VTI (bb)->n_mos - 1;
while (n1 < n2)
{
while (n1 < n2 && VTI (bb)->mos[n1].type == MO_CLOBBER)
n1++;
while (n1 < n2 && (VTI (bb)->mos[n2].type == MO_SET
|| VTI (bb)->mos[n2].type == MO_COPY))
n2--;
if (n1 < n2)
{
micro_operation sw;
sw = VTI (bb)->mos[n1];
VTI (bb)->mos[n1] = VTI (bb)->mos[n2];
VTI (bb)->mos[n2] = sw;
}
}
if (!frame_pointer_needed && post)
{
micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
mo->type = MO_ADJUST;
mo->u.adjust = post;
mo->insn = insn;
}
}
}
}
FOR_ALL_BB (bb)
{
VTI (bb)->visited = false;
dataflow_set_init (&VTI (bb)->in, 7);
dataflow_set_init (&VTI (bb)->out, 7);
}
attrs_pool = create_alloc_pool ("attrs_def pool",
sizeof (struct attrs_def), 1024);
var_pool = create_alloc_pool ("variable_def pool",
sizeof (struct variable_def), 64);
loc_chain_pool = create_alloc_pool ("location_chain_def pool",
sizeof (struct location_chain_def),
1024);
changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq,
NULL);
vt_add_function_parameters ();
}
static void
vt_finalize (void)
{
basic_block bb;
FOR_EACH_BB (bb)
{
free (VTI (bb)->mos);
}
FOR_ALL_BB (bb)
{
dataflow_set_destroy (&VTI (bb)->in);
dataflow_set_destroy (&VTI (bb)->out);
}
free_aux_for_blocks ();
free_alloc_pool (attrs_pool);
free_alloc_pool (var_pool);
free_alloc_pool (loc_chain_pool);
htab_delete (changed_variables);
}
unsigned int
variable_tracking_main (void)
{
if (n_basic_blocks > 500 && n_edges / n_basic_blocks >= 20)
return 0;
mark_dfs_back_edges ();
vt_initialize ();
if (!frame_pointer_needed)
{
if (!vt_stack_adjustments ())
{
vt_finalize ();
return 0;
}
}
vt_find_locations ();
vt_emit_notes ();
if (dump_file && (dump_flags & TDF_DETAILS))
{
dump_dataflow_sets ();
dump_flow_info (dump_file, dump_flags);
}
vt_finalize ();
return 0;
}
static bool
gate_handle_var_tracking (void)
{
return (flag_var_tracking);
}
struct tree_opt_pass pass_variable_tracking =
{
"vartrack",
gate_handle_var_tracking,
variable_tracking_main,
NULL,
NULL,
0,
TV_VAR_TRACKING,
0,
0,
0,
0,
TODO_dump_func,
'V'
};