#include "builtins.h"
#include "cfgloop.h"
#include "attribs.h"
+#include "gimple-walk.h"
#include "ipa-icf-gimple.h"
tree b1 = SSA_NAME_VAR (t1);
tree b2 = SSA_NAME_VAR (t2);
- return compare_operand (b1, b2);
+ return compare_operand (b1, b2, OP_NORMAL);
}
return true;
return true;
}
-/* Function compare for equality given trees T1 and T2 which
- can be either a constant or a declaration type. */
+/* Add hash of ARG to HSTATE. FLAGS have same meaning
+ as for operand_equal_p. Works only if operand acces type is OP_NORMAL. */
void
func_checker::hash_operand (const_tree arg, inchash::hash &hstate,
return operand_compare::hash_operand (arg, hstate, flags);
}
+/* Add hash of ARG accesses according to ACCESS to HSTATE.
+ FLAGS have same meaning as for operand_equal_p. */
+
+void
+func_checker::hash_operand (const_tree arg, inchash::hash &hstate,
+ unsigned int flags, operand_access_type)
+{
+ return hash_operand (arg, hstate, flags);
+}
+
bool
func_checker::operand_equal_p (const_tree t1, const_tree t2,
unsigned int flags)
return operand_compare::operand_equal_p (t1, t2, flags);
}
-/* Function responsible for comparison of various operands T1 and T2.
+/* Function responsible for comparison of various operands T1 and T2
+ which are accessed as ACCESS.
If these components, from functions FUNC1 and FUNC2, are equal, true
is returned. */
bool
-func_checker::compare_operand (tree t1, tree t2)
+func_checker::compare_operand (tree t1, tree t2, operand_access_type access)
{
if (!t1 && !t2)
return true;
return false;
if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS))
return true;
- return return_false_with_msg ("operand_equal_p failed");
+ switch (access)
+ {
+ case OP_MEMORY:
+ return return_false_with_msg
+ ("operand_equal_p failed (access == memory)");
+ case OP_NORMAL:
+ return return_false_with_msg
+ ("operand_equal_p failed (access == normal)");
+ }
+ gcc_unreachable ();
}
bool
-func_checker::compare_asm_inputs_outputs (tree t1, tree t2)
+func_checker::compare_asm_inputs_outputs (tree t1, tree t2,
+ operand_access_type_map *map)
{
gcc_assert (TREE_CODE (t1) == TREE_LIST);
gcc_assert (TREE_CODE (t2) == TREE_LIST);
if (!t2)
return false;
- if (!compare_operand (TREE_VALUE (t1), TREE_VALUE (t2)))
+ if (!compare_operand (TREE_VALUE (t1), TREE_VALUE (t2),
+ get_operand_access_type (map, t1)))
return return_false ();
tree p1 = TREE_PURPOSE (t1);
if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
return false;
+ operand_access_type_map map (5);
+ classify_operands (s1, &map);
+
t1 = gimple_call_fn (s1);
t2 = gimple_call_fn (s2);
- if (!compare_operand (t1, t2))
+ if (!compare_operand (t1, t2, get_operand_access_type (&map, t1)))
return return_false ();
/* Compare flags. */
tree chain2 = gimple_call_chain (s2);
if ((chain1 && !chain2)
|| (!chain1 && chain2)
- || !compare_operand (chain1, chain2))
+ || !compare_operand (chain1, chain2,
+ get_operand_access_type (&map, chain1)))
return return_false_with_msg ("static call chains are different");
/* Checking of argument. */
t1 = gimple_call_arg (s1, i);
t2 = gimple_call_arg (s2, i);
- if (!compare_operand (t1, t2))
+ if (!compare_operand (t1, t2, get_operand_access_type (&map, t1)))
return return_false_with_msg ("GIMPLE call operands are different");
}
t1 = gimple_get_lhs (s1);
t2 = gimple_get_lhs (s2);
- return compare_operand (t1, t2);
+ return compare_operand (t1, t2, get_operand_access_type (&map, t1));
}
if (code1 != code2)
return false;
+ operand_access_type_map map (5);
+ classify_operands (s1, &map);
+
for (i = 0; i < gimple_num_ops (s1); i++)
{
arg1 = gimple_op (s1, i);
return return_false_with_msg ("GIMPLE NOP LHS type mismatch");
}
- if (!compare_operand (arg1, arg2))
+ if (!compare_operand (arg1, arg2, get_operand_access_type (&map, arg1)))
return return_false_with_msg ("GIMPLE assignment operands "
"are different");
}
t1 = gimple_cond_lhs (s1);
t2 = gimple_cond_lhs (s2);
- if (!compare_operand (t1, t2))
+ if (!compare_operand (t1, t2, OP_NORMAL))
return false;
t1 = gimple_cond_rhs (s1);
t2 = gimple_cond_rhs (s2);
- return compare_operand (t1, t2);
+ return compare_operand (t1, t2, OP_NORMAL);
}
/* Verifies for given GIMPLE_LABEL stmts S1 and S2 that
tree t1 = gimple_switch_index (g1);
tree t2 = gimple_switch_index (g2);
- if (!compare_operand (t1, t2))
+ if (!compare_operand (t1, t2, OP_NORMAL))
return false;
for (i = 0; i < lsize1; i++)
label1 = CASE_LABEL (label1);
label2 = CASE_LABEL (label2);
- if (!compare_operand (label1, label2))
+ if (!compare_operand (label1, label2, OP_NORMAL))
return return_false_with_msg ("switch label_exprs are different");
}
else if (!tree_int_cst_equal (label1, label2))
if (t1 == NULL && t2 == NULL)
return true;
else
- return compare_operand (t1, t2);
+ {
+ operand_access_type_map map (3);
+ return compare_operand (t1, t2, get_operand_access_type (&map, t1));
+ }
}
/* Verifies for given GIMPLEs S1 and S2 that
if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
return false;
- return compare_operand (dest1, dest2);
+ return compare_operand (dest1, dest2, OP_NORMAL);
}
/* Verifies for given GIMPLE_RESX stmts S1 and S2 that
if (strcmp (gimple_asm_string (g1), gimple_asm_string (g2)) != 0)
return return_false_with_msg ("ASM strings are different");
+ operand_access_type_map map (5);
+ classify_operands (g1, &map);
+
for (unsigned i = 0; i < gimple_asm_ninputs (g1); i++)
{
tree input1 = gimple_asm_input_op (g1, i);
tree input2 = gimple_asm_input_op (g2, i);
- if (!compare_asm_inputs_outputs (input1, input2))
+ if (!compare_asm_inputs_outputs (input1, input2, &map))
return return_false_with_msg ("ASM input is different");
}
tree output1 = gimple_asm_output_op (g1, i);
tree output2 = gimple_asm_output_op (g2, i);
- if (!compare_asm_inputs_outputs (output1, output2))
+ if (!compare_asm_inputs_outputs (output1, output2, &map))
return return_false_with_msg ("ASM output is different");
}
return true;
}
+/* Helper for func_checker::classify_operands. Record that T is a load. */
+
+static bool
+visit_load_store (gimple *, tree, tree t, void *data)
+{
+ func_checker::operand_access_type_map *map =
+ (func_checker::operand_access_type_map *) data;
+ map->add (t);
+ return false;
+}
+
+/* Compute hash map determining access types of operands. */
+
+void
+func_checker::classify_operands (const gimple *stmt,
+ operand_access_type_map *map)
+{
+ walk_stmt_load_store_ops (const_cast <gimple *> (stmt),
+ (void *)map, visit_load_store, visit_load_store);
+}
+
+/* Return access type of a given operand. */
+
+func_checker::operand_access_type
+func_checker::get_operand_access_type (operand_access_type_map *map, tree t)
+{
+ if (map->contains (t))
+ return OP_MEMORY;
+ return OP_NORMAL;
+}
+
} // ipa_icf_gimple namespace
/* Verification function for declaration trees T1 and T2. */
bool compare_decl (const_tree t1, const_tree t2);
+ /* Compute hash map MAP that determines loads and stores of STMT. */
+ enum operand_access_type {OP_MEMORY, OP_NORMAL};
+ typedef hash_set<tree> operand_access_type_map;
+
/* 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);
+ bool compare_operand (tree t1, tree t2, operand_access_type type);
/* Compares GIMPLE ASM inputs (or outputs) where we iterate tree chain
and compare both TREE_PURPOSEs and TREE_VALUEs. */
- bool compare_asm_inputs_outputs (tree t1, tree t2);
+ bool compare_asm_inputs_outputs (tree t1, tree t2,
+ operand_access_type_map *map);
/* Verifies that trees T1 and T2, representing function declarations
are equivalent from perspective of ICF. */
first parameter of a function. */
static bool compatible_types_p (tree t1, tree t2);
+ /* Compute hash map determining access types of operands. */
+ static void classify_operands (const gimple *stmt,
+ operand_access_type_map *map);
+ /* Return access type of a given operand. */
+ static operand_access_type get_operand_access_type
+ (operand_access_type_map *map, tree);
private:
/* Vector mapping source SSA names to target ones. */
vec <int> m_source_ssa_names;
/* Generate a hash value for an expression. This can be used iteratively
by passing a previous result as the HSTATE argument. */
virtual void hash_operand (const_tree, inchash::hash &, unsigned flags);
+ void hash_operand (const_tree, inchash::hash &, unsigned flags,
+ operand_access_type access);
};
} // ipa_icf_gimple namespace
{
case GIMPLE_SWITCH:
m_checker->hash_operand (gimple_switch_index (as_a <gswitch *> (stmt)),
- hstate, 0);
+ hstate, 0, func_checker::OP_NORMAL);
break;
case GIMPLE_ASSIGN:
hstate.add_int (gimple_assign_rhs_code (stmt));
- if (commutative_tree_code (gimple_assign_rhs_code (stmt))
- || commutative_ternary_tree_code (gimple_assign_rhs_code (stmt)))
- {
- m_checker->hash_operand (gimple_assign_rhs1 (stmt), hstate, 0);
- m_checker->hash_operand (gimple_assign_rhs2 (stmt), hstate, 0);
- if (commutative_ternary_tree_code (gimple_assign_rhs_code (stmt)))
- m_checker->hash_operand (gimple_assign_rhs3 (stmt), hstate, 0);
- m_checker->hash_operand (gimple_assign_lhs (stmt), hstate, 0);
- }
/* fall through */
case GIMPLE_CALL:
case GIMPLE_ASM:
case GIMPLE_COND:
case GIMPLE_GOTO:
case GIMPLE_RETURN:
- /* All these statements are equivalent if their operands are. */
- for (unsigned i = 0; i < gimple_num_ops (stmt); ++i)
- m_checker->hash_operand (gimple_op (stmt, i), hstate, 0);
- /* Consider nocf_check attribute in hash as it affects code
- generation. */
- if (code == GIMPLE_CALL
- && flag_cf_protection & CF_BRANCH)
- hstate.add_flag (gimple_call_nocf_check_p (as_a <gcall *> (stmt)));
+ {
+ func_checker::operand_access_type_map map (5);
+ func_checker::classify_operands (stmt, &map);
+
+ /* All these statements are equivalent if their operands are. */
+ for (unsigned i = 0; i < gimple_num_ops (stmt); ++i)
+ m_checker->hash_operand (gimple_op (stmt, i), hstate, 0,
+ func_checker::get_operand_access_type
+ (&map, gimple_op (stmt, i)));
+ /* Consider nocf_check attribute in hash as it affects code
+ generation. */
+ if (code == GIMPLE_CALL
+ && flag_cf_protection & CF_BRANCH)
+ hstate.add_flag (gimple_call_nocf_check_p (as_a <gcall *> (stmt)));
+ }
+ break;
default:
break;
}
tree phi_result1 = gimple_phi_result (phi1);
tree phi_result2 = gimple_phi_result (phi2);
- if (!m_checker->compare_operand (phi_result1, phi_result2))
+ if (!m_checker->compare_operand (phi_result1, phi_result2,
+ func_checker::OP_NORMAL))
return return_false_with_msg ("PHI results are different");
size1 = gimple_phi_num_args (phi1);
t1 = gimple_phi_arg (phi1, i)->def;
t2 = gimple_phi_arg (phi2, i)->def;
- if (!m_checker->compare_operand (t1, t2))
+ if (!m_checker->compare_operand (t1, t2, func_checker::OP_NORMAL))
return return_false ();
e1 = gimple_phi_arg_edge (phi1, i);