gimple.h (gimple_can_coalesce_p): Prototype.
* gimple.h (gimple_can_coalesce_p): Prototype. * tree-ssa-coalesce.c (gimple_can_coalesce_p): New function. (create_outofssa_var_map, coalesce_partitions): Use it. * tree-ssa-uncprop.c (uncprop_into_successor_phis): Similarly. * tree-ssa-live.c (var_map_base_init): Use TYPE_CANONICAL if it's available. * gcc.dg/tree-ssa/coalesce-1.c: New test. From-SVN: r200103
This commit is contained in:
parent
51b08adabf
commit
e91d0adbc1
|
@ -1101,6 +1101,9 @@ extern tree tree_ssa_strip_useless_type_conversions (tree);
|
|||
extern bool useless_type_conversion_p (tree, tree);
|
||||
extern bool types_compatible_p (tree, tree);
|
||||
|
||||
/* In tree-ssa-coalesce.c */
|
||||
extern bool gimple_can_coalesce_p (tree, tree);
|
||||
|
||||
/* Return the first node in GIMPLE sequence S. */
|
||||
|
||||
static inline gimple_seq_node
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
/* { dg-do compile } */
|
||||
|
||||
/* { dg-options "-O2 -fdump-rtl-expand-details" } */
|
||||
|
||||
typedef long unsigned int size_t;
|
||||
union tree_node;
|
||||
typedef union tree_node *tree;
|
||||
union gimple_statement_d;
|
||||
typedef union gimple_statement_d *gimple;
|
||||
typedef const union tree_node *const_tree;
|
||||
typedef const union gimple_statement_d *const_gimple;
|
||||
struct gimple_seq_d;
|
||||
typedef struct gimple_seq_d *gimple_seq;
|
||||
struct edge_def;
|
||||
typedef struct edge_def *edge;
|
||||
struct basic_block_def;
|
||||
typedef struct basic_block_def *basic_block;
|
||||
typedef const struct basic_block_def *const_basic_block;
|
||||
struct tree_exp
|
||||
{
|
||||
tree operands[1];
|
||||
};
|
||||
typedef struct ssa_use_operand_d
|
||||
{
|
||||
tree *use;
|
||||
} ssa_use_operand_t;
|
||||
struct phi_arg_d
|
||||
{
|
||||
struct ssa_use_operand_d imm_use;
|
||||
};
|
||||
union tree_node
|
||||
{
|
||||
struct tree_exp exp;
|
||||
};
|
||||
struct function
|
||||
{
|
||||
};
|
||||
extern struct function *cfun;
|
||||
struct edge_def
|
||||
{
|
||||
unsigned int dest_idx;
|
||||
};
|
||||
static __inline__ void
|
||||
VEC_edge_must_be_pointer_type (void)
|
||||
{
|
||||
(void) ((edge) 1 == (void *) 1);
|
||||
} typedef struct VEC_edge_base
|
||||
|
||||
{
|
||||
unsigned num;
|
||||
unsigned alloc;
|
||||
edge vec[1];
|
||||
} VEC_edge_base;
|
||||
typedef struct VEC_edge_none
|
||||
{
|
||||
VEC_edge_base base;
|
||||
} VEC_edge_none;
|
||||
|
||||
static __inline__ edge
|
||||
VEC_edge_base_index (const VEC_edge_base * vec_, unsigned ix_,
|
||||
const char *file_, unsigned line_, const char *function_)
|
||||
{
|
||||
return vec_->vec[ix_];
|
||||
}
|
||||
|
||||
typedef struct VEC_edge_gc
|
||||
{
|
||||
VEC_edge_base base;
|
||||
} VEC_edge_gc;
|
||||
struct basic_block_def
|
||||
{
|
||||
VEC_edge_gc *succs;
|
||||
};
|
||||
static __inline__ edge
|
||||
single_succ_edge (const_basic_block bb)
|
||||
{
|
||||
return (VEC_edge_base_index
|
||||
((((bb)->succs) ? &((bb)->succs)->base : 0), (0),
|
||||
"/home/gcc/virgin-gcc/gcc/basic-block.h", 563, __FUNCTION__));
|
||||
}
|
||||
|
||||
edge find_edge (basic_block, basic_block);
|
||||
typedef tree *def_operand_p;
|
||||
typedef ssa_use_operand_t *use_operand_p;
|
||||
struct gimple_seq_node_d;
|
||||
typedef struct gimple_seq_node_d *gimple_seq_node;
|
||||
struct gimple_seq_node_d
|
||||
{
|
||||
gimple stmt;
|
||||
};
|
||||
typedef struct
|
||||
{
|
||||
gimple_seq_node ptr;
|
||||
gimple_seq seq;
|
||||
basic_block bb;
|
||||
} gimple_stmt_iterator;
|
||||
struct gimple_statement_phi
|
||||
{
|
||||
struct phi_arg_d args[1];
|
||||
};
|
||||
union gimple_statement_d
|
||||
{
|
||||
struct gimple_statement_phi gimple_phi;
|
||||
};
|
||||
extern size_t const gimple_ops_offset_[];
|
||||
static __inline__ tree *
|
||||
gimple_ops (gimple gs)
|
||||
{
|
||||
size_t off;
|
||||
off = gimple_ops_offset_[gimple_statement_structure (gs)];
|
||||
return (tree *) ((char *) gs + off);
|
||||
}
|
||||
|
||||
static __inline__ tree
|
||||
gimple_op (const_gimple gs, unsigned i)
|
||||
{
|
||||
return gimple_ops ((((union
|
||||
{
|
||||
const union gimple_statement_d * _q;
|
||||
union gimple_statement_d * _nq;}) (((gs))))._nq))[i];
|
||||
}
|
||||
|
||||
static __inline__ struct phi_arg_d *
|
||||
gimple_phi_arg (gimple gs, unsigned index)
|
||||
{
|
||||
return &(gs->gimple_phi.args[index]);
|
||||
}
|
||||
|
||||
static __inline__ tree
|
||||
gimple_switch_label (const_gimple gs, unsigned index)
|
||||
{
|
||||
return gimple_op (gs, index + 1);
|
||||
}
|
||||
|
||||
gimple_stmt_iterator gsi_start_phis (basic_block);
|
||||
extern basic_block label_to_block_fn (struct function *, tree);
|
||||
|
||||
static __inline__ tree
|
||||
get_use_from_ptr (use_operand_p use)
|
||||
{
|
||||
return *(use->use);
|
||||
}
|
||||
|
||||
static __inline__ use_operand_p
|
||||
gimple_phi_arg_imm_use_ptr (gimple gs, int i)
|
||||
{
|
||||
return &gimple_phi_arg (gs, i)->imm_use;
|
||||
}
|
||||
|
||||
struct switch_conv_info
|
||||
{
|
||||
basic_block final_bb;
|
||||
basic_block switch_bb;
|
||||
const char *reason;
|
||||
tree *default_values;
|
||||
};
|
||||
static struct switch_conv_info info;
|
||||
|
||||
static void
|
||||
gather_default_values (tree default_case)
|
||||
{
|
||||
gimple_stmt_iterator gsi;
|
||||
basic_block bb =
|
||||
(label_to_block_fn ((cfun + 0), default_case->exp.operands[2]));
|
||||
edge e;
|
||||
int i = 0;
|
||||
if (bb == info.final_bb)
|
||||
e = find_edge (info.switch_bb, bb);
|
||||
else
|
||||
e = single_succ_edge (bb);
|
||||
for (gsi = gsi_start_phis (info.final_bb);
|
||||
gsi_gsi_start_phis (info.final_bb); gsi_next (&gsi))
|
||||
{
|
||||
gimple phi = gsi.ptr->stmt;
|
||||
tree val = get_use_from_ptr (gimple_phi_arg_imm_use_ptr
|
||||
((((phi))), (((e)->dest_idx))));
|
||||
info.default_values[i++] = val;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char
|
||||
process_switch (gimple swtch)
|
||||
{
|
||||
unsigned int i, branch_num = gimple_switch_num_labels (swtch);
|
||||
tree index_type;
|
||||
info.reason = "switch has no labels\n";
|
||||
gather_default_values (gimple_switch_label (swtch, 0));
|
||||
}
|
||||
|
||||
/* Verify that out-of-ssa coalescing did its job by verifying there are not
|
||||
any partition copies inserted. */
|
||||
|
||||
/* { dg-final { scan-rtl-dump-not "partition copy" "expand"} } */
|
||||
/* { dg-final { cleanup-rtl-dump "expand" } } */
|
||||
|
|
@ -943,8 +943,7 @@ create_outofssa_var_map (coalesce_list_p cl, bitmap used_in_copy)
|
|||
continue;
|
||||
|
||||
register_ssa_partition (map, arg);
|
||||
if ((SSA_NAME_VAR (arg) == SSA_NAME_VAR (res)
|
||||
&& TREE_TYPE (arg) == TREE_TYPE (res))
|
||||
if (gimple_can_coalesce_p (arg, res)
|
||||
|| (e->flags & EDGE_ABNORMAL))
|
||||
{
|
||||
saw_copy = true;
|
||||
|
@ -985,8 +984,7 @@ create_outofssa_var_map (coalesce_list_p cl, bitmap used_in_copy)
|
|||
if (gimple_assign_copy_p (stmt)
|
||||
&& TREE_CODE (lhs) == SSA_NAME
|
||||
&& TREE_CODE (rhs1) == SSA_NAME
|
||||
&& SSA_NAME_VAR (lhs) == SSA_NAME_VAR (rhs1)
|
||||
&& TREE_TYPE (lhs) == TREE_TYPE (rhs1))
|
||||
&& gimple_can_coalesce_p (lhs, rhs1))
|
||||
{
|
||||
v1 = SSA_NAME_VERSION (lhs);
|
||||
v2 = SSA_NAME_VERSION (rhs1);
|
||||
|
@ -1037,8 +1035,7 @@ create_outofssa_var_map (coalesce_list_p cl, bitmap used_in_copy)
|
|||
v1 = SSA_NAME_VERSION (outputs[match]);
|
||||
v2 = SSA_NAME_VERSION (input);
|
||||
|
||||
if (SSA_NAME_VAR (outputs[match]) == SSA_NAME_VAR (input)
|
||||
&& TREE_TYPE (outputs[match]) == TREE_TYPE (input))
|
||||
if (gimple_can_coalesce_p (outputs[match], input))
|
||||
{
|
||||
cost = coalesce_cost (REG_BR_PROB_BASE,
|
||||
optimize_bb_for_size_p (bb));
|
||||
|
@ -1072,8 +1069,7 @@ create_outofssa_var_map (coalesce_list_p cl, bitmap used_in_copy)
|
|||
first = var;
|
||||
else
|
||||
{
|
||||
gcc_assert (SSA_NAME_VAR (var) == SSA_NAME_VAR (first)
|
||||
&& TREE_TYPE (var) == TREE_TYPE (first));
|
||||
gcc_assert (gimple_can_coalesce_p (var, first));
|
||||
v1 = SSA_NAME_VERSION (first);
|
||||
v2 = SSA_NAME_VERSION (var);
|
||||
bitmap_set_bit (used_in_copy, v1);
|
||||
|
@ -1210,8 +1206,7 @@ coalesce_partitions (var_map map, ssa_conflicts_p graph, coalesce_list_p cl,
|
|||
var2 = ssa_name (y);
|
||||
|
||||
/* Assert the coalesces have the same base variable. */
|
||||
gcc_assert (SSA_NAME_VAR (var1) == SSA_NAME_VAR (var2)
|
||||
&& TREE_TYPE (var1) == TREE_TYPE (var2));
|
||||
gcc_assert (gimple_can_coalesce_p (var1, var2));
|
||||
|
||||
if (debug)
|
||||
fprintf (debug, "Coalesce list: ");
|
||||
|
@ -1341,3 +1336,38 @@ coalesce_ssa_name (void)
|
|||
|
||||
return map;
|
||||
}
|
||||
|
||||
/* Given SSA_NAMEs NAME1 and NAME2, return true if they are candidates for
|
||||
coalescing together, false otherwise.
|
||||
|
||||
This must stay consistent with var_map_base_init in tree-ssa-live.c. */
|
||||
|
||||
bool
|
||||
gimple_can_coalesce_p (tree name1, tree name2)
|
||||
{
|
||||
/* First check the SSA_NAME's associated DECL. We only want to
|
||||
coalesce if they have the same DECL or both have no associated DECL. */
|
||||
if (SSA_NAME_VAR (name1) != SSA_NAME_VAR (name2))
|
||||
return false;
|
||||
|
||||
/* Now check the types. If the types are the same, then we should
|
||||
try to coalesce V1 and V2. */
|
||||
tree t1 = TREE_TYPE (name1);
|
||||
tree t2 = TREE_TYPE (name2);
|
||||
if (t1 == t2)
|
||||
return true;
|
||||
|
||||
/* If the types are not the same, check for a canonical type match. This
|
||||
(for example) allows coalescing when the types are fundamentally the
|
||||
same, but just have different names.
|
||||
|
||||
Note pointer types with different address spaces may have the same
|
||||
canonical type. Those are rejected for coalescing by the
|
||||
types_compatible_p check. */
|
||||
if (TYPE_CANONICAL (t1)
|
||||
&& TYPE_CANONICAL (t1) == TYPE_CANONICAL (t2)
|
||||
&& types_compatible_p (t1, t2))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -111,8 +111,12 @@ var_map_base_init (var_map map)
|
|||
as it restricts the sets we compute conflicts for.
|
||||
Using TREE_TYPE to generate sets is the easies as
|
||||
type equivalency also holds for SSA names with the same
|
||||
underlying decl. */
|
||||
m->base.from = TREE_TYPE (var);
|
||||
underlying decl.
|
||||
|
||||
Check gimple_can_coalesce_p when changing this code. */
|
||||
m->base.from = (TYPE_CANONICAL (TREE_TYPE (var))
|
||||
? TYPE_CANONICAL (TREE_TYPE (var))
|
||||
: TREE_TYPE (var));
|
||||
/* If base variable hasn't been seen, set it up. */
|
||||
slot = tree_to_index.find_slot (m, INSERT);
|
||||
if (!*slot)
|
||||
|
|
|
@ -474,12 +474,11 @@ uncprop_into_successor_phis (basic_block bb)
|
|||
equiv_hash_elt an_equiv_elt;
|
||||
equiv_hash_elt **slot;
|
||||
|
||||
/* If the argument is not an invariant, and refers to the same
|
||||
underlying variable as the PHI result, then there's no
|
||||
point in un-propagating the argument. */
|
||||
/* If the argument is not an invariant and can be potentially
|
||||
coalesced with the result, then there's no point in
|
||||
un-propagating the argument. */
|
||||
if (!is_gimple_min_invariant (arg)
|
||||
&& (SSA_NAME_VAR (arg) == SSA_NAME_VAR (res)
|
||||
&& TREE_TYPE (arg) == TREE_TYPE (res)))
|
||||
&& gimple_can_coalesce_p (arg, res))
|
||||
continue;
|
||||
|
||||
/* Lookup this argument's value in the hash table. */
|
||||
|
@ -493,7 +492,7 @@ uncprop_into_successor_phis (basic_block bb)
|
|||
int j;
|
||||
|
||||
/* Walk every equivalence with the same value. If we find
|
||||
one with the same underlying variable as the PHI result,
|
||||
one that can potentially coalesce with the PHI rsult,
|
||||
then replace the value in the argument with its equivalent
|
||||
SSA_NAME. Use the most recent equivalence as hopefully
|
||||
that results in shortest lifetimes. */
|
||||
|
@ -501,8 +500,7 @@ uncprop_into_successor_phis (basic_block bb)
|
|||
{
|
||||
tree equiv = elt->equivalences[j];
|
||||
|
||||
if (SSA_NAME_VAR (equiv) == SSA_NAME_VAR (res)
|
||||
&& TREE_TYPE (equiv) == TREE_TYPE (res))
|
||||
if (gimple_can_coalesce_p (equiv, res))
|
||||
{
|
||||
SET_PHI_ARG_DEF (phi, e->dest_idx, equiv);
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue