IPA ICF: compare_operand is split to multiple functions.
* ipa-icf-gimple.c (func_checker::compare_ssa_name): Enhance SSA name comparison. (func_checker::compare_memory_operand): New function. (func_checker::compare_operand): Split case to newly added functions. (func_checker::compare_cst_or_decl): New function. (func_checker::compare_gimple_call): Identify memory operands. (func_checker::compare_gimple_assign): Likewise. * ipa-icf-gimple.h: New function. From-SVN: r219379
This commit is contained in:
parent
ce9401b43f
commit
520b302213
3 changed files with 222 additions and 173 deletions
|
@ -1,3 +1,16 @@
|
|||
2015-01-09 Martin Liska <mliska@suse.cz>
|
||||
|
||||
* ipa-icf-gimple.c (func_checker::compare_ssa_name): Enhance SSA
|
||||
name comparison.
|
||||
(func_checker::compare_memory_operand): New function.
|
||||
(func_checker::compare_operand): Split case to newly
|
||||
added functions.
|
||||
(func_checker::compare_cst_or_decl): New function.
|
||||
(func_checker::compare_gimple_call): Identify
|
||||
memory operands.
|
||||
(func_checker::compare_gimple_assign): Likewise.
|
||||
* ipa-icf-gimple.h: New function.
|
||||
|
||||
2015-01-09 Martin Liska <mliska@suse.cz>
|
||||
|
||||
PR ipa/64503
|
||||
|
|
|
@ -110,6 +110,9 @@ func_checker::~func_checker ()
|
|||
bool
|
||||
func_checker::compare_ssa_name (tree t1, tree t2)
|
||||
{
|
||||
gcc_assert (TREE_CODE (t1) == SSA_NAME);
|
||||
gcc_assert (TREE_CODE (t2) == SSA_NAME);
|
||||
|
||||
unsigned i1 = SSA_NAME_VERSION (t1);
|
||||
unsigned i2 = SSA_NAME_VERSION (t2);
|
||||
|
||||
|
@ -123,6 +126,20 @@ func_checker::compare_ssa_name (tree t1, tree t2)
|
|||
else if (m_target_ssa_names[i2] != (int) i1)
|
||||
return false;
|
||||
|
||||
if (SSA_NAME_IS_DEFAULT_DEF (t1))
|
||||
{
|
||||
tree b1 = SSA_NAME_VAR (t1);
|
||||
tree b2 = SSA_NAME_VAR (t2);
|
||||
|
||||
if (b1 == NULL && b2 == NULL)
|
||||
return true;
|
||||
|
||||
if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
|
||||
return return_false ();
|
||||
|
||||
return compare_cst_or_decl (b1, b2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -178,9 +195,10 @@ func_checker::compare_decl (tree t1, tree t2)
|
|||
}
|
||||
|
||||
/* Return true if types are compatible from perspective of ICF. */
|
||||
bool func_checker::compatible_types_p (tree t1, tree t2,
|
||||
bool compare_polymorphic,
|
||||
bool first_argument)
|
||||
bool
|
||||
func_checker::compatible_types_p (tree t1, tree t2,
|
||||
bool compare_polymorphic,
|
||||
bool first_argument)
|
||||
{
|
||||
if (TREE_CODE (t1) != TREE_CODE (t2))
|
||||
return return_false_with_msg ("different tree types");
|
||||
|
@ -214,195 +232,63 @@ bool func_checker::compatible_types_p (tree t1, tree t2,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Function responsible for comparison of handled components T1 and T2.
|
||||
If these components, from functions FUNC1 and FUNC2, are equal, true
|
||||
is returned. */
|
||||
/* Function compare for equality given memory operands T1 and T2. */
|
||||
|
||||
bool
|
||||
func_checker::compare_operand (tree t1, tree t2)
|
||||
func_checker::compare_memory_operand (tree t1, tree t2)
|
||||
{
|
||||
tree base1, base2, x1, x2, y1, y2, z1, z2;
|
||||
HOST_WIDE_INT offset1 = 0, offset2 = 0;
|
||||
bool ret;
|
||||
|
||||
if (!t1 && !t2)
|
||||
return true;
|
||||
else if (!t1 || !t2)
|
||||
return false;
|
||||
|
||||
tree tt1 = TREE_TYPE (t1);
|
||||
tree tt2 = TREE_TYPE (t2);
|
||||
ao_ref r1, r2;
|
||||
ao_ref_init (&r1, t1);
|
||||
ao_ref_init (&r2, t2);
|
||||
|
||||
if (TREE_THIS_VOLATILE (t1) != TREE_THIS_VOLATILE (t2))
|
||||
return return_false_with_msg ("different operand volatility");
|
||||
tree b1 = ao_ref_base (&r1);
|
||||
tree b2 = ao_ref_base (&r2);
|
||||
|
||||
if (!func_checker::compatible_types_p (tt1, tt2))
|
||||
return false;
|
||||
bool source_is_memop = DECL_P (b1) || INDIRECT_REF_P (b1)
|
||||
|| TREE_CODE (b1) == MEM_REF
|
||||
|| TREE_CODE (b1) == TARGET_MEM_REF;
|
||||
|
||||
base1 = get_addr_base_and_unit_offset (t1, &offset1);
|
||||
base2 = get_addr_base_and_unit_offset (t2, &offset2);
|
||||
bool target_is_memop = DECL_P (b2) || INDIRECT_REF_P (b2)
|
||||
|| TREE_CODE (b2) == MEM_REF
|
||||
|| TREE_CODE (b2) == TARGET_MEM_REF;
|
||||
|
||||
if (base1 && base2)
|
||||
/* Compare alias sets for memory operands. */
|
||||
if (source_is_memop && target_is_memop)
|
||||
{
|
||||
if (offset1 != offset2)
|
||||
return return_false_with_msg ("base offsets are different");
|
||||
if (TREE_THIS_VOLATILE (b1) != TREE_THIS_VOLATILE (b2))
|
||||
return return_false_with_msg ("different operand volatility");
|
||||
|
||||
t1 = base1;
|
||||
t2 = base2;
|
||||
if (ao_ref_alias_set (&r1) != ao_ref_alias_set (&r2)
|
||||
|| ao_ref_base_alias_set (&r1) != ao_ref_base_alias_set (&r2))
|
||||
return return_false_with_msg ("ao alias sets are different");
|
||||
}
|
||||
|
||||
if (TREE_CODE (t1) != TREE_CODE (t2))
|
||||
return return_false ();
|
||||
return compare_operand (t1, t2);
|
||||
}
|
||||
|
||||
/* Function compare for equality given trees T1 and T2 which
|
||||
can be either a constant or a declaration type. */
|
||||
|
||||
bool
|
||||
func_checker::compare_cst_or_decl (tree t1, tree t2)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
switch (TREE_CODE (t1))
|
||||
{
|
||||
case CONSTRUCTOR:
|
||||
{
|
||||
unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
|
||||
unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
|
||||
|
||||
if (length1 != length2)
|
||||
return return_false ();
|
||||
|
||||
for (unsigned i = 0; i < length1; i++)
|
||||
if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
|
||||
CONSTRUCTOR_ELT (t2, i)->value))
|
||||
return return_false();
|
||||
|
||||
return true;
|
||||
}
|
||||
case ARRAY_REF:
|
||||
case ARRAY_RANGE_REF:
|
||||
x1 = TREE_OPERAND (t1, 0);
|
||||
x2 = TREE_OPERAND (t2, 0);
|
||||
y1 = TREE_OPERAND (t1, 1);
|
||||
y2 = TREE_OPERAND (t2, 1);
|
||||
|
||||
if (!compare_operand (array_ref_low_bound (t1),
|
||||
array_ref_low_bound (t2)))
|
||||
return return_false_with_msg ("");
|
||||
if (!compare_operand (array_ref_element_size (t1),
|
||||
array_ref_element_size (t2)))
|
||||
return return_false_with_msg ("");
|
||||
if (!compare_operand (x1, x2))
|
||||
return return_false_with_msg ("");
|
||||
return compare_operand (y1, y2);
|
||||
case MEM_REF:
|
||||
{
|
||||
x1 = TREE_OPERAND (t1, 0);
|
||||
x2 = TREE_OPERAND (t2, 0);
|
||||
y1 = TREE_OPERAND (t1, 1);
|
||||
y2 = TREE_OPERAND (t2, 1);
|
||||
|
||||
/* See if operand is an memory access (the test originate from
|
||||
gimple_load_p).
|
||||
|
||||
In this case the alias set of the function being replaced must
|
||||
be subset of the alias set of the other function. At the moment
|
||||
we seek for equivalency classes, so simply require inclussion in
|
||||
both directions. */
|
||||
|
||||
if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
|
||||
return return_false ();
|
||||
|
||||
if (!compare_operand (x1, x2))
|
||||
return return_false_with_msg ("");
|
||||
|
||||
if (get_alias_set (TREE_TYPE (y1)) != get_alias_set (TREE_TYPE (y2)))
|
||||
return return_false_with_msg ("alias set for MEM_REF offsets are different");
|
||||
|
||||
ao_ref r1, r2;
|
||||
ao_ref_init (&r1, t1);
|
||||
ao_ref_init (&r2, t2);
|
||||
if (ao_ref_alias_set (&r1) != ao_ref_alias_set (&r2)
|
||||
|| ao_ref_base_alias_set (&r1) != ao_ref_base_alias_set (&r2))
|
||||
return return_false_with_msg ("ao alias sets are different");
|
||||
|
||||
/* Type of the offset on MEM_REF does not matter. */
|
||||
return wi::to_offset (y1) == wi::to_offset (y2);
|
||||
}
|
||||
case COMPONENT_REF:
|
||||
{
|
||||
x1 = TREE_OPERAND (t1, 0);
|
||||
x2 = TREE_OPERAND (t2, 0);
|
||||
y1 = TREE_OPERAND (t1, 1);
|
||||
y2 = TREE_OPERAND (t2, 1);
|
||||
|
||||
ret = compare_operand (x1, x2)
|
||||
&& compare_operand (y1, y2);
|
||||
|
||||
return return_with_debug (ret);
|
||||
}
|
||||
/* Virtual table call. */
|
||||
case OBJ_TYPE_REF:
|
||||
{
|
||||
x1 = TREE_OPERAND (t1, 0);
|
||||
x2 = TREE_OPERAND (t2, 0);
|
||||
y1 = TREE_OPERAND (t1, 1);
|
||||
y2 = TREE_OPERAND (t2, 1);
|
||||
z1 = TREE_OPERAND (t1, 2);
|
||||
z2 = TREE_OPERAND (t2, 2);
|
||||
|
||||
ret = compare_operand (x1, x2)
|
||||
&& compare_operand (y1, y2)
|
||||
&& compare_operand (z1, z2);
|
||||
|
||||
return return_with_debug (ret);
|
||||
}
|
||||
case ADDR_EXPR:
|
||||
{
|
||||
x1 = TREE_OPERAND (t1, 0);
|
||||
x2 = TREE_OPERAND (t2, 0);
|
||||
|
||||
ret = compare_operand (x1, x2);
|
||||
return return_with_debug (ret);
|
||||
}
|
||||
case SSA_NAME:
|
||||
{
|
||||
ret = compare_ssa_name (t1, t2);
|
||||
|
||||
if (!ret)
|
||||
return return_with_debug (ret);
|
||||
|
||||
if (SSA_NAME_IS_DEFAULT_DEF (t1))
|
||||
{
|
||||
tree b1 = SSA_NAME_VAR (t1);
|
||||
tree b2 = SSA_NAME_VAR (t2);
|
||||
|
||||
if (b1 == NULL && b2 == NULL)
|
||||
return true;
|
||||
|
||||
if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
|
||||
return return_false ();
|
||||
|
||||
switch (TREE_CODE (b1))
|
||||
{
|
||||
case VAR_DECL:
|
||||
return return_with_debug (compare_variable_decl (t1, t2));
|
||||
case PARM_DECL:
|
||||
case RESULT_DECL:
|
||||
ret = compare_decl (b1, b2);
|
||||
return return_with_debug (ret);
|
||||
default:
|
||||
return return_false_with_msg ("Unknown TREE code reached");
|
||||
}
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
case INTEGER_CST:
|
||||
{
|
||||
ret = compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2))
|
||||
&& wi::to_offset (t1) == wi::to_offset (t2);
|
||||
|
||||
return return_with_debug (ret);
|
||||
}
|
||||
case COMPLEX_CST:
|
||||
case VECTOR_CST:
|
||||
case STRING_CST:
|
||||
case REAL_CST:
|
||||
{
|
||||
ret = operand_equal_p (t1, t2, OEP_ONLY_CONST);
|
||||
ret = compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2))
|
||||
&& operand_equal_p (t1, t2, OEP_ONLY_CONST);
|
||||
return return_with_debug (ret);
|
||||
}
|
||||
case FUNCTION_DECL:
|
||||
|
@ -435,11 +321,154 @@ func_checker::compare_operand (tree t1, tree t2)
|
|||
case PARM_DECL:
|
||||
case RESULT_DECL:
|
||||
case CONST_DECL:
|
||||
{
|
||||
ret = compare_decl (t1, t2);
|
||||
return return_with_debug (ret);
|
||||
}
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Function responsible for comparison of various operands T1 and T2.
|
||||
If these components, from functions FUNC1 and FUNC2, are equal, true
|
||||
is returned. */
|
||||
|
||||
bool
|
||||
func_checker::compare_operand (tree t1, tree t2)
|
||||
{
|
||||
tree x1, x2, y1, y2, z1, z2;
|
||||
bool ret;
|
||||
|
||||
if (!t1 && !t2)
|
||||
return true;
|
||||
else if (!t1 || !t2)
|
||||
return false;
|
||||
|
||||
tree tt1 = TREE_TYPE (t1);
|
||||
tree tt2 = TREE_TYPE (t2);
|
||||
|
||||
if (!func_checker::compatible_types_p (tt1, tt2))
|
||||
return false;
|
||||
|
||||
if (TREE_CODE (t1) != TREE_CODE (t2))
|
||||
return return_false ();
|
||||
|
||||
switch (TREE_CODE (t1))
|
||||
{
|
||||
case CONSTRUCTOR:
|
||||
{
|
||||
unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
|
||||
unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
|
||||
|
||||
if (length1 != length2)
|
||||
return return_false ();
|
||||
|
||||
for (unsigned i = 0; i < length1; i++)
|
||||
if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
|
||||
CONSTRUCTOR_ELT (t2, i)->value))
|
||||
return return_false();
|
||||
|
||||
return true;
|
||||
}
|
||||
case ARRAY_REF:
|
||||
case ARRAY_RANGE_REF:
|
||||
/* First argument is the array, second is the index. */
|
||||
x1 = TREE_OPERAND (t1, 0);
|
||||
x2 = TREE_OPERAND (t2, 0);
|
||||
y1 = TREE_OPERAND (t1, 1);
|
||||
y2 = TREE_OPERAND (t2, 1);
|
||||
|
||||
if (!compare_operand (array_ref_low_bound (t1),
|
||||
array_ref_low_bound (t2)))
|
||||
return return_false_with_msg ("");
|
||||
if (!compare_operand (array_ref_element_size (t1),
|
||||
array_ref_element_size (t2)))
|
||||
return return_false_with_msg ("");
|
||||
|
||||
if (!compare_operand (x1, x2))
|
||||
return return_false_with_msg ("");
|
||||
return compare_operand (y1, y2);
|
||||
case MEM_REF:
|
||||
{
|
||||
x1 = TREE_OPERAND (t1, 0);
|
||||
x2 = TREE_OPERAND (t2, 0);
|
||||
y1 = TREE_OPERAND (t1, 1);
|
||||
y2 = TREE_OPERAND (t2, 1);
|
||||
|
||||
/* See if operand is an memory access (the test originate from
|
||||
gimple_load_p).
|
||||
|
||||
In this case the alias set of the function being replaced must
|
||||
be subset of the alias set of the other function. At the moment
|
||||
we seek for equivalency classes, so simply require inclussion in
|
||||
both directions. */
|
||||
|
||||
if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
|
||||
return return_false ();
|
||||
|
||||
if (!compare_operand (x1, x2))
|
||||
return return_false_with_msg ("");
|
||||
|
||||
/* Type of the offset on MEM_REF does not matter. */
|
||||
return wi::to_offset (y1) == wi::to_offset (y2);
|
||||
}
|
||||
case COMPONENT_REF:
|
||||
{
|
||||
x1 = TREE_OPERAND (t1, 0);
|
||||
x2 = TREE_OPERAND (t2, 0);
|
||||
y1 = TREE_OPERAND (t1, 1);
|
||||
y2 = TREE_OPERAND (t2, 1);
|
||||
|
||||
ret = compare_operand (x1, x2)
|
||||
&& compare_cst_or_decl (y1, y2);
|
||||
|
||||
return return_with_debug (ret);
|
||||
}
|
||||
/* Virtual table call. */
|
||||
case OBJ_TYPE_REF:
|
||||
{
|
||||
x1 = TREE_OPERAND (t1, 0);
|
||||
x2 = TREE_OPERAND (t2, 0);
|
||||
y1 = TREE_OPERAND (t1, 1);
|
||||
y2 = TREE_OPERAND (t2, 1);
|
||||
z1 = TREE_OPERAND (t1, 2);
|
||||
z2 = TREE_OPERAND (t2, 2);
|
||||
|
||||
ret = compare_ssa_name (x1, x2)
|
||||
&& compare_ssa_name (y1, y2)
|
||||
&& compare_cst_or_decl (z1, z2);
|
||||
|
||||
return return_with_debug (ret);
|
||||
}
|
||||
case ADDR_EXPR:
|
||||
{
|
||||
x1 = TREE_OPERAND (t1, 0);
|
||||
x2 = TREE_OPERAND (t2, 0);
|
||||
|
||||
ret = compare_operand (x1, x2);
|
||||
return return_with_debug (ret);
|
||||
}
|
||||
case BIT_FIELD_REF:
|
||||
{
|
||||
ret = compare_decl (t1, t2);
|
||||
return return_with_debug (ret);
|
||||
}
|
||||
case SSA_NAME:
|
||||
return compare_ssa_name (t1, t2);
|
||||
case INTEGER_CST:
|
||||
case COMPLEX_CST:
|
||||
case VECTOR_CST:
|
||||
case STRING_CST:
|
||||
case REAL_CST:
|
||||
case FUNCTION_DECL:
|
||||
case VAR_DECL:
|
||||
case FIELD_DECL:
|
||||
case LABEL_DECL:
|
||||
case PARM_DECL:
|
||||
case RESULT_DECL:
|
||||
case CONST_DECL:
|
||||
return compare_cst_or_decl (t1, t2);
|
||||
default:
|
||||
return return_false_with_msg ("Unknown TREE code reached");
|
||||
}
|
||||
|
@ -706,15 +735,15 @@ func_checker::compare_gimple_call (gcall *s1, gcall *s2)
|
|||
t1 = gimple_call_arg (s1, i);
|
||||
t2 = gimple_call_arg (s2, i);
|
||||
|
||||
if (!compare_operand (t1, t2))
|
||||
return false;
|
||||
if (!compare_memory_operand (t1, t2))
|
||||
return return_false_with_msg ("memory operands are different");
|
||||
}
|
||||
|
||||
/* Return value checking. */
|
||||
t1 = gimple_get_lhs (s1);
|
||||
t2 = gimple_get_lhs (s2);
|
||||
|
||||
return compare_operand (t1, t2);
|
||||
return compare_memory_operand (t1, t2);
|
||||
}
|
||||
|
||||
|
||||
|
@ -745,8 +774,8 @@ func_checker::compare_gimple_assign (gimple s1, gimple s2)
|
|||
arg1 = gimple_op (s1, i);
|
||||
arg2 = gimple_op (s2, i);
|
||||
|
||||
if (!compare_operand (arg1, arg2))
|
||||
return false;
|
||||
if (!compare_memory_operand (arg1, arg2))
|
||||
return return_false_with_msg ("memory operands are different");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -203,7 +203,14 @@ public:
|
|||
/* Verifies that tree labels T1 and T2 correspond. */
|
||||
bool compare_tree_ssa_label (tree t1, tree t2);
|
||||
|
||||
/* Function responsible for comparison of handled components T1 and T2.
|
||||
/* Function compare for equality given memory operands T1 and T2. */
|
||||
bool compare_memory_operand (tree t1, tree t2);
|
||||
|
||||
/* Function compare for equality given trees T1 and T2 which
|
||||
can be either a constant or a declaration type. */
|
||||
bool compare_cst_or_decl (tree t1, tree t2);
|
||||
|
||||
/* Function responsible for comparison of various operands T1 and T2.
|
||||
If these components, from functions FUNC1 and FUNC2, are equal, true
|
||||
is returned. */
|
||||
bool compare_operand (tree t1, tree t2);
|
||||
|
|
Loading…
Add table
Reference in a new issue