X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=gcc%2Fipa-icf-gimple.c;h=0713e125898718137b4c9e5a9b2fc436965e8dde;hb=1fcf995f9521c6a958944b5f8bd94d18131d8a0a;hp=fa2c3534d8dc214ea425cfffdef21b297baea386;hpb=e922baf33edbc4fe64062fc34a36b2bd4b5444a0;p=thirdparty%2Fgcc.git diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c index fa2c3534d8dc..0713e1258987 100644 --- a/gcc/ipa-icf-gimple.c +++ b/gcc/ipa-icf-gimple.c @@ -1,5 +1,5 @@ /* Interprocedural Identical Code Folding pass - Copyright (C) 2014 Free Software Foundation, Inc. + Copyright (C) 2014-2019 Free Software Foundation, Inc. Contributed by Jan Hubicka and Martin Liska @@ -22,44 +22,24 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #include "system.h" #include "coretypes.h" +#include "backend.h" +#include "rtl.h" #include "tree.h" -#include "predict.h" -#include "vec.h" -#include "hashtab.h" -#include "hash-set.h" -#include "machmode.h" -#include "tm.h" -#include "hard-reg-set.h" -#include "input.h" -#include "function.h" -#include "basic-block.h" -#include "tree-ssa-alias.h" -#include "internal-fn.h" -#include "gimple-expr.h" -#include "is-a.h" #include "gimple.h" -#include "expr.h" -#include "gimple-iterator.h" -#include "gimple-ssa.h" -#include "tree-cfg.h" -#include "stringpool.h" -#include "tree-dfa.h" #include "tree-pass.h" -#include "gimple-pretty-print.h" -#include "cfgloop.h" -#include "except.h" -#include "hash-map.h" -#include "plugin-api.h" -#include "ipa-ref.h" +#include "ssa.h" #include "cgraph.h" #include "data-streamer.h" +#include "gimple-pretty-print.h" +#include "alias.h" +#include "fold-const.h" +#include "gimple-iterator.h" #include "ipa-utils.h" -#include -#include "tree-ssanames.h" #include "tree-eh.h" +#include "builtins.h" +#include "cfgloop.h" #include "ipa-icf-gimple.h" -#include "ipa-icf.h" namespace ipa_icf_gimple { @@ -110,6 +90,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 +106,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; } @@ -162,8 +159,22 @@ func_checker::compare_decl (tree t1, tree t2) && DECL_BY_REFERENCE (t1) != DECL_BY_REFERENCE (t2)) return return_false_with_msg ("DECL_BY_REFERENCE flags are different"); - if (!compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2), - m_compare_polymorphic)) + if (!compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2))) + return return_false (); + + /* TODO: we are actually too strict here. We only need to compare if + T1 can be used in polymorphic call. */ + if (TREE_ADDRESSABLE (t1) + && m_compare_polymorphic + && !compatible_polymorphic_types_p (TREE_TYPE (t1), TREE_TYPE (t2), + false)) + return return_false (); + + if ((t == VAR_DECL || t == PARM_DECL || t == RESULT_DECL) + && DECL_BY_REFERENCE (t1) + && m_compare_polymorphic + && !compatible_polymorphic_types_p (TREE_TYPE (t1), TREE_TYPE (t2), + true)) return return_false (); bool existed_p; @@ -177,49 +188,209 @@ func_checker::compare_decl (tree t1, tree t2) return true; } +/* Return true if T1 and T2 are same for purposes of ipa-polymorphic-call + analysis. COMPARE_PTR indicates if types of pointers needs to be + considered. */ + +bool +func_checker::compatible_polymorphic_types_p (tree t1, tree t2, + bool compare_ptr) +{ + gcc_assert (TREE_CODE (t1) != FUNCTION_TYPE && TREE_CODE (t1) != METHOD_TYPE); + + /* Pointer types generally give no information. */ + if (POINTER_TYPE_P (t1)) + { + if (!compare_ptr) + return true; + return func_checker::compatible_polymorphic_types_p (TREE_TYPE (t1), + TREE_TYPE (t2), + false); + } + + /* If types contain a polymorphic types, match them. */ + bool c1 = contains_polymorphic_type_p (t1); + bool c2 = contains_polymorphic_type_p (t2); + if (!c1 && !c2) + return true; + if (!c1 || !c2) + return return_false_with_msg ("one type is not polymorphic"); + if (!types_must_be_same_for_odr (t1, t2)) + return return_false_with_msg ("types are not same for ODR"); + return true; +} + /* 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) { if (TREE_CODE (t1) != TREE_CODE (t2)) return return_false_with_msg ("different tree types"); + if (TYPE_RESTRICT (t1) != TYPE_RESTRICT (t2)) + return return_false_with_msg ("restrict flags are different"); + if (!types_compatible_p (t1, t2)) return return_false_with_msg ("types are not compatible"); - if (get_alias_set (t1) != get_alias_set (t2)) + /* We do a lot of unnecesary matching of types that are not being + accessed and thus do not need to be compatible. In longer term we should + remove these checks on all types which are not accessed as memory + locations. + + For time being just avoid calling get_alias_set on types that are not + having alias sets defined at all. */ + if (type_with_alias_set_p (t1) && type_with_alias_set_p (t2) + && get_alias_set (t1) != get_alias_set (t2)) return return_false_with_msg ("alias sets are different"); - /* We call contains_polymorphic_type_p with this pointer type. */ - if (first_argument && TREE_CODE (t1) == POINTER_TYPE) + return true; +} + +/* Function compare for equality given memory operands T1 and T2. */ + +bool +func_checker::compare_memory_operand (tree t1, tree t2) +{ + if (!t1 && !t2) + return true; + else if (!t1 || !t2) + return false; + + ao_ref r1, r2; + ao_ref_init (&r1, t1); + ao_ref_init (&r2, t2); + + tree b1 = ao_ref_base (&r1); + tree b2 = ao_ref_base (&r2); + + bool source_is_memop = DECL_P (b1) || INDIRECT_REF_P (b1) + || TREE_CODE (b1) == MEM_REF + || TREE_CODE (b1) == TARGET_MEM_REF; + + bool target_is_memop = DECL_P (b2) || INDIRECT_REF_P (b2) + || TREE_CODE (b2) == MEM_REF + || TREE_CODE (b2) == TARGET_MEM_REF; + + /* Compare alias sets for memory operands. */ + if (source_is_memop && target_is_memop) { - t1 = TREE_TYPE (t1); - t2 = TREE_TYPE (t2); + if (TREE_THIS_VOLATILE (t1) != TREE_THIS_VOLATILE (t2)) + return return_false_with_msg ("different operand volatility"); + + 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"); + + /* We can't simply use get_object_alignment_1 on the full + reference as for accesses with variable indexes this reports + too conservative alignment. We also can't use the ao_ref_base + base objects as ao_ref_base happily strips MEM_REFs around + decls even though that may carry alignment info. */ + b1 = t1; + while (handled_component_p (b1)) + b1 = TREE_OPERAND (b1, 0); + b2 = t2; + while (handled_component_p (b2)) + b2 = TREE_OPERAND (b2, 0); + unsigned int align1, align2; + unsigned HOST_WIDE_INT tem; + get_object_alignment_1 (b1, &align1, &tem); + get_object_alignment_1 (b2, &align2, &tem); + if (align1 != align2) + return return_false_with_msg ("different access alignment"); + + /* Similarly we have to compare dependence info where equality + tells us we are safe (even some unequal values would be safe + but then we have to maintain a map of bases and cliques). */ + unsigned short clique1 = 0, base1 = 0, clique2 = 0, base2 = 0; + if (TREE_CODE (b1) == MEM_REF) + { + clique1 = MR_DEPENDENCE_CLIQUE (b1); + base1 = MR_DEPENDENCE_BASE (b1); + } + if (TREE_CODE (b2) == MEM_REF) + { + clique2 = MR_DEPENDENCE_CLIQUE (b2); + base2 = MR_DEPENDENCE_BASE (b2); + } + if (clique1 != clique2 || base1 != base2) + return return_false_with_msg ("different dependence info"); } - if (compare_polymorphic) - if (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2)) + 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 INTEGER_CST: + case COMPLEX_CST: + case VECTOR_CST: + case STRING_CST: + case REAL_CST: { - if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2)) - return return_false_with_msg ("one type is not polymorphic"); + 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: + /* All function decls are in the symbol table and known to match + before we start comparing bodies. */ + return true; + case VAR_DECL: + return return_with_debug (compare_variable_decl (t1, t2)); + case FIELD_DECL: + { + tree offset1 = DECL_FIELD_OFFSET (t1); + tree offset2 = DECL_FIELD_OFFSET (t2); - if (!types_must_be_same_for_odr (t1, t2)) - return return_false_with_msg ("types are not same for ODR"); + tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1); + tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2); + + ret = compare_operand (offset1, offset2) + && compare_operand (bit_offset1, bit_offset2); + + return return_with_debug (ret); } + case LABEL_DECL: + { + if (t1 == t2) + return true; - return true; + int *bb1 = m_label_bb_map.get (t1); + int *bb2 = m_label_bb_map.get (t2); + + /* Labels can point to another function (non-local GOTOs). */ + return return_with_debug (bb1 != NULL && bb2 != NULL && *bb1 == *bb2); + } + 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 handled components T1 and 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 func_checker::compare_operand (tree t1, tree t2) { - tree base1, base2, x1, x2, y1, y2, z1, z2; - HOST_WIDE_INT offset1 = 0, offset2 = 0; + tree x1, x2, y1, y2, z1, z2; bool ret; if (!t1 && !t2) @@ -230,24 +401,9 @@ func_checker::compare_operand (tree t1, tree t2) tree tt1 = TREE_TYPE (t1); tree tt2 = TREE_TYPE (t2); - if (TREE_THIS_VOLATILE (t1) != TREE_THIS_VOLATILE (t2)) - return return_false_with_msg ("different operand volatility"); - if (!func_checker::compatible_types_p (tt1, tt2)) return false; - base1 = get_addr_base_and_unit_offset (t1, &offset1); - base2 = get_addr_base_and_unit_offset (t2, &offset2); - - if (base1 && base2) - { - if (offset1 != offset2) - return return_false_with_msg ("base offsets are different"); - - t1 = base1; - t2 = base2; - } - if (TREE_CODE (t1) != TREE_CODE (t2)) return return_false (); @@ -255,8 +411,8 @@ func_checker::compare_operand (tree t1, tree t2) { case CONSTRUCTOR: { - unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1)); - unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2)); + unsigned length1 = CONSTRUCTOR_NELTS (t1); + unsigned length2 = CONSTRUCTOR_NELTS (t2); if (length1 != length2) return return_false (); @@ -270,6 +426,7 @@ func_checker::compare_operand (tree t1, tree t2) } 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); @@ -281,6 +438,7 @@ func_checker::compare_operand (tree t1, tree t2) 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); @@ -305,18 +463,8 @@ func_checker::compare_operand (tree t1, tree t2) 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); + return known_eq (wi::to_poly_offset (y1), wi::to_poly_offset (y2)); } case COMPONENT_REF: { @@ -326,26 +474,33 @@ func_checker::compare_operand (tree t1, tree t2) y2 = TREE_OPERAND (t2, 1); ret = compare_operand (x1, x2) - && compare_operand (y1, y2); + && 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_operand (x1, x2) - && compare_operand (y1, y2) - && compare_operand (z1, z2); + if (!compare_ssa_name (OBJ_TYPE_REF_EXPR (t1), OBJ_TYPE_REF_EXPR (t2))) + return return_false (); + if (opt_for_fn (m_source_func_decl, flag_devirtualize) + && virtual_method_call_p (t1)) + { + if (tree_to_uhwi (OBJ_TYPE_REF_TOKEN (t1)) + != tree_to_uhwi (OBJ_TYPE_REF_TOKEN (t2))) + return return_false_with_msg ("OBJ_TYPE_REF token mismatch"); + if (!types_same_for_odr (obj_type_ref_class (t1), + obj_type_ref_class (t2))) + return return_false_with_msg ("OBJ_TYPE_REF OTR type mismatch"); + if (!compare_operand (OBJ_TYPE_REF_OBJECT (t1), + OBJ_TYPE_REF_OBJECT (t2))) + return return_false_with_msg ("OBJ_TYPE_REF object mismatch"); + } - return return_with_debug (ret); + return return_with_debug (true); } + case IMAGPART_EXPR: + case REALPART_EXPR: case ADDR_EXPR: { x1 = TREE_OPERAND (t1, 0); @@ -354,99 +509,43 @@ func_checker::compare_operand (tree t1, tree t2) ret = compare_operand (x1, x2); return return_with_debug (ret); } - case SSA_NAME: + case BIT_FIELD_REF: { - ret = compare_ssa_name (t1, t2); - - if (!ret) - return return_with_debug (ret); + 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); - 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); + ret = compare_operand (x1, x2) + && compare_cst_or_decl (y1, y2) + && compare_cst_or_decl (z1, z2); 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: - { - ret = operand_equal_p (t1, t2, OEP_ONLY_CONST); - return return_with_debug (ret); - } case FUNCTION_DECL: - { - ret = compare_function_decl (t1, t2); - return return_with_debug (ret); - } case VAR_DECL: - return return_with_debug (compare_variable_decl (t1, t2)); case FIELD_DECL: - { - tree offset1 = DECL_FIELD_OFFSET (t1); - tree offset2 = DECL_FIELD_OFFSET (t2); - - tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1); - tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2); - - ret = compare_operand (offset1, offset2) - && compare_operand (bit_offset1, bit_offset2); - - return return_with_debug (ret); - } case LABEL_DECL: - { - int *bb1 = m_label_bb_map.get (t1); - int *bb2 = m_label_bb_map.get (t2); - - return return_with_debug (*bb1 == *bb2); - } case PARM_DECL: case RESULT_DECL: case CONST_DECL: - case BIT_FIELD_REF: - { - ret = compare_decl (t1, t2); - return return_with_debug (ret); - } + return compare_cst_or_decl (t1, t2); default: return return_false_with_msg ("Unknown TREE code reached"); } } -/* Compares two tree list operands T1 and T2 and returns true if these - two trees are semantically equivalent. */ - bool -func_checker::compare_tree_list_operand (tree t1, tree t2) +func_checker::compare_asm_inputs_outputs (tree t1, tree t2) { gcc_assert (TREE_CODE (t1) == TREE_LIST); gcc_assert (TREE_CODE (t2) == TREE_LIST); @@ -459,6 +558,16 @@ func_checker::compare_tree_list_operand (tree t1, tree t2) if (!compare_operand (TREE_VALUE (t1), TREE_VALUE (t2))) return return_false (); + tree p1 = TREE_PURPOSE (t1); + tree p2 = TREE_PURPOSE (t2); + + gcc_assert (TREE_CODE (p1) == TREE_LIST); + gcc_assert (TREE_CODE (p2) == TREE_LIST); + + if (strcmp (TREE_STRING_POINTER (TREE_VALUE (p1)), + TREE_STRING_POINTER (TREE_VALUE (p2))) != 0) + return return_false (); + t2 = TREE_CHAIN (t2); } @@ -468,69 +577,70 @@ func_checker::compare_tree_list_operand (tree t1, tree t2) return true; } -/* Verifies that trees T1 and T2, representing function declarations - are equivalent from perspective of ICF. */ +/* Verifies that trees T1 and T2 do correspond. */ bool -func_checker::compare_function_decl (tree t1, tree t2) +func_checker::compare_variable_decl (tree t1, tree t2) { bool ret = false; if (t1 == t2) return true; - symtab_node *n1 = symtab_node::get (t1); - symtab_node *n2 = symtab_node::get (t2); + if (DECL_ALIGN (t1) != DECL_ALIGN (t2)) + return return_false_with_msg ("alignments are different"); - if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL) - { - ret = m_ignored_source_nodes->contains (n1) - && m_ignored_target_nodes->contains (n2); + if (DECL_HARD_REGISTER (t1) != DECL_HARD_REGISTER (t2)) + return return_false_with_msg ("DECL_HARD_REGISTER are different"); - if (ret) - return true; - } - - /* If function decl is WEAKREF, we compare targets. */ - cgraph_node *f1 = cgraph_node::get (t1); - cgraph_node *f2 = cgraph_node::get (t2); + if (DECL_HARD_REGISTER (t1) + && DECL_ASSEMBLER_NAME (t1) != DECL_ASSEMBLER_NAME (t2)) + return return_false_with_msg ("HARD REGISTERS are different"); - if(f1 && f2 && f1->weakref && f2->weakref) - ret = f1->alias_target == f2->alias_target; + /* Symbol table variables are known to match before we start comparing + bodies. */ + if (decl_in_symtab_p (t1)) + return decl_in_symtab_p (t2); + ret = compare_decl (t1, t2); - return ret; + return return_with_debug (ret); } -/* Verifies that trees T1 and T2 do correspond. */ +/* Compare loop information for basic blocks BB1 and BB2. */ bool -func_checker::compare_variable_decl (tree t1, tree t2) +func_checker::compare_loops (basic_block bb1, basic_block bb2) { - bool ret = false; + if ((bb1->loop_father == NULL) != (bb2->loop_father == NULL)) + return return_false (); - if (t1 == t2) + struct loop *l1 = bb1->loop_father; + struct loop *l2 = bb2->loop_father; + if (l1 == NULL) return true; - if (TREE_CODE (t1) == VAR_DECL && (DECL_EXTERNAL (t1) || TREE_STATIC (t1))) - { - symtab_node *n1 = symtab_node::get (t1); - symtab_node *n2 = symtab_node::get (t2); - - if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL) - { - ret = m_ignored_source_nodes->contains (n1) - && m_ignored_target_nodes->contains (n2); + if ((bb1 == l1->header) != (bb2 == l2->header)) + return return_false_with_msg ("header"); + if ((bb1 == l1->latch) != (bb2 == l2->latch)) + return return_false_with_msg ("latch"); + if (l1->simdlen != l2->simdlen) + return return_false_with_msg ("simdlen"); + if (l1->safelen != l2->safelen) + return return_false_with_msg ("safelen"); + if (l1->can_be_parallel != l2->can_be_parallel) + return return_false_with_msg ("can_be_parallel"); + if (l1->dont_vectorize != l2->dont_vectorize) + return return_false_with_msg ("dont_vectorize"); + if (l1->force_vectorize != l2->force_vectorize) + return return_false_with_msg ("force_vectorize"); + if (l1->unroll != l2->unroll) + return return_false_with_msg ("unroll"); + if (!compare_variable_decl (l1->simduid, l2->simduid)) + return return_false_with_msg ("simduid"); - if (ret) - return true; - } - } - ret = compare_decl (t1, t2); - - return return_with_debug (ret); + return true; } - /* Function visits all gimple labels and creates corresponding mapping between basic blocks and labels. */ @@ -540,7 +650,7 @@ func_checker::parse_labels (sem_bb *bb) for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p (gsi); gsi_next (&gsi)) { - gimple stmt = gsi_stmt (gsi); + gimple *stmt = gsi_stmt (gsi); if (glabel *label_stmt = dyn_cast (stmt)) { @@ -563,10 +673,10 @@ bool func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2) { gimple_stmt_iterator gsi1, gsi2; - gimple s1, s2; + gimple *s1, *s2; - gsi1 = gsi_start_bb_nondebug (bb1->bb); - gsi2 = gsi_start_bb_nondebug (bb2->bb); + gsi1 = gsi_start_nondebug_bb (bb1->bb); + gsi2 = gsi_start_nondebug_bb (bb2->bb); while (!gsi_end_p (gsi1)) { @@ -608,7 +718,11 @@ func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2) return return_different_stmts (s1, s2, "GIMPLE_SWITCH"); break; case GIMPLE_DEBUG: + break; case GIMPLE_EH_DISPATCH: + if (gimple_eh_dispatch_region (as_a (s1)) + != gimple_eh_dispatch_region (as_a (s2))) + return return_different_stmts (s1, s2, "GIMPLE_EH_DISPATCH"); break; case GIMPLE_RESX: if (!compare_gimple_resx (as_a (s1), @@ -636,7 +750,7 @@ func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2) break; case GIMPLE_PREDICT: case GIMPLE_NOP: - return true; + break; default: return return_false_with_msg ("Unknown GIMPLE code reached"); } @@ -648,6 +762,9 @@ func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2) if (!gsi_end_p (gsi2)) return return_false (); + if (!compare_loops (bb1->bb, bb2->bb)) + return return_false (); + return true; } @@ -675,8 +792,7 @@ func_checker::compare_gimple_call (gcall *s1, gcall *s2) || gimple_call_return_slot_opt_p (s1) != gimple_call_return_slot_opt_p (s2) || gimple_call_from_thunk_p (s1) != gimple_call_from_thunk_p (s2) || gimple_call_va_arg_pack_p (s1) != gimple_call_va_arg_pack_p (s2) - || gimple_call_alloca_for_var_p (s1) != gimple_call_alloca_for_var_p (s2) - || gimple_call_with_bounds_p (s1) != gimple_call_with_bounds_p (s2)) + || gimple_call_alloca_for_var_p (s1) != gimple_call_alloca_for_var_p (s2)) return false; if (gimple_call_internal_p (s1) @@ -703,15 +819,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); } @@ -719,7 +835,7 @@ func_checker::compare_gimple_call (gcall *s1, gcall *s2) assignment statements are semantically equivalent. */ bool -func_checker::compare_gimple_assign (gimple s1, gimple s2) +func_checker::compare_gimple_assign (gimple *s1, gimple *s2) { tree arg1, arg2; tree_code code1, code2; @@ -742,8 +858,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"); } @@ -754,7 +870,7 @@ func_checker::compare_gimple_assign (gimple s1, gimple s2) condition statements are semantically equivalent. */ bool -func_checker::compare_gimple_cond (gimple s1, gimple s2) +func_checker::compare_gimple_cond (gimple *s1, gimple *s2) { tree t1, t2; tree_code code1, code2; @@ -880,7 +996,7 @@ func_checker::compare_gimple_return (const greturn *g1, const greturn *g2) goto statements are semantically equivalent. */ bool -func_checker::compare_gimple_goto (gimple g1, gimple g2) +func_checker::compare_gimple_goto (gimple *g1, gimple *g2) { tree dest1, dest2; @@ -912,6 +1028,12 @@ func_checker::compare_gimple_asm (const gasm *g1, const gasm *g2) if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2)) return false; + if (gimple_asm_input_p (g1) != gimple_asm_input_p (g2)) + return false; + + if (gimple_asm_inline_p (g1) != gimple_asm_inline_p (g2)) + return false; + if (gimple_asm_ninputs (g1) != gimple_asm_ninputs (g2)) return false; @@ -933,7 +1055,7 @@ func_checker::compare_gimple_asm (const gasm *g1, const gasm *g2) tree input1 = gimple_asm_input_op (g1, i); tree input2 = gimple_asm_input_op (g2, i); - if (!compare_tree_list_operand (input1, input2)) + if (!compare_asm_inputs_outputs (input1, input2)) return return_false_with_msg ("ASM input is different"); } @@ -942,7 +1064,7 @@ func_checker::compare_gimple_asm (const gasm *g1, const gasm *g2) tree output1 = gimple_asm_output_op (g1, i); tree output2 = gimple_asm_output_op (g2, i); - if (!compare_tree_list_operand (output1, output2)) + if (!compare_asm_inputs_outputs (output1, output2)) return return_false_with_msg ("ASM output is different"); }