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:
Martin Liska 2015-01-09 11:45:18 +01:00 committed by Martin Liska
parent ce9401b43f
commit 520b302213
3 changed files with 222 additions and 173 deletions

View file

@ -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

View file

@ -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");
}

View file

@ -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);