On 8/14/19 3:56 PM, Richard Biener wrote: > On Wed, Aug 14, 2019 at 3:19 PM Martin Liška <mli...@suse.cz> wrote: >> >> On 8/14/19 3:04 PM, Richard Biener wrote: >>> On Mon, Aug 12, 2019 at 3:56 PM Martin Liška <mli...@suse.cz> wrote: >>>> >>>> On 8/12/19 2:43 PM, Richard Biener wrote: >>>>> On Mon, Aug 12, 2019 at 1:49 PM Martin Liška <mli...@suse.cz> wrote: >>>>>> >>>>>> On 8/12/19 1:40 PM, Richard Biener wrote: >>>>>>> On Mon, Aug 12, 2019 at 1:19 PM Martin Liška <mli...@suse.cz> wrote: >>>>>>>> >>>>>>>> On 8/8/19 5:55 PM, Michael Matz wrote: >>>>>>>>> Hi, >>>>>>>>> >>>>>>>>> On Mon, 10 Jun 2019, Martin Liska wrote: >>>>>>>>> >>>>>>>>>> 2019-07-24 Martin Liska <mli...@suse.cz> >>>>>>>>>> >>>>>>>>>> * fold-const.c (operand_equal_p): Rename to ... >>>>>>>>>> (operand_compare::operand_equal_p): ... this. >>>>>>>>>> (add_expr): Rename to ... >>>>>>>>>> (operand_compare::hash_operand): ... this. >>>>>>>>>> (operand_compare::operand_equal_valueize): Likewise. >>>>>>>>>> (operand_compare::hash_operand_valueize): Likewise. >>>>>>>>>> * fold-const.h (operand_equal_p): Set default >>>>>>>>>> value for last argument. >>>>>>>>>> (class operand_compare): New. >>>>>>>>> >>>>>>>>> Hmpf. A class without any data? That doesn't sound like a good >>>>>>>>> design. >>>>>>>> >>>>>>>> Yes, the base class (current operand_equal_p) does not have a data. >>>>>>>> But the ICF derive class has a data and e.g. >>>>>>>> func_checker::operand_equal_valueize >>>>>>>> will use m_label_bb_map.get (t1). Which are member data of class >>>>>>>> func_checker. >>>>>>>> >>>>>>>>> You seem to need it only to have the possibility of virtual functions, >>>>>>>>> i.e. fancy callbacks. AFAICS you only have one derived class, i.e. a >>>>>>>>> simple distinction of two cases. What do you think about encoding the >>>>>>>>> additional new (ICF) case in the (existing) 'flags' argument to >>>>>>>>> operand_equal_p (and in case the ICF flag is set simply call the >>>>>>>>> "callback" directly)? >>>>>>>> >>>>>>>> That's possible. I can add two more callbacks to the operand_equal_p >>>>>>>> function >>>>>>>> (hash_operand_valueize and operand_equal_valueize). >>>>>>>> >>>>>>>> Is Richi also supporting this approach? >>>>>>> >>>>>>> I still see no value in the abstraction since you invoke none of the >>>>>>> (virtual) methods from the base class operand_equal_p. >>>>>> >>>>>> I call operand_equal_valueize (and hash_operand) from operand_equal_p. >>>>>> These are then used in IPA ICF (patch 6/9). >>>>> >>>>> Ugh. I see you call that after >>>>> >>>>> if (TREE_CODE (arg0) != TREE_CODE (arg1)) >>>>> { >>>>> ... >>>>> } >>>>> else >>>>> return false; >>>>> } >>>>> >>>>> and also after >>>>> >>>>> /* Check equality of integer constants before bailing out due to >>>>> precision differences. */ >>>>> if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) >>>>> >>>>> which means for arg0 == SSA_NAME and arg1 == INTEGER_CST you return false >>>>> instead of valueizing arg0 to the possibly same or same "lose" value >>>>> and returning true. >>>> >>>> Yes. ICF does not allow to have anything where TREE_CODEs do not match. >>>> >>>>> >>>>> Also >>>>> >>>>> + int val = operand_equal_valueize (arg0, arg1, flags); >>>>> + if (val == 1) >>>>> + return 1; >>>>> + if (val == 0) >>>>> + return 0; >>>>> >>>>> suggests that you pass in arbirtrary trees for "valueization" but it >>>>> isn't actually >>>>> valueization that is performed but instead it should do an alternate >>>>> comparison >>>>> of arg0 and arg1 with valueization. Why's this done this way instead of >>>>> sth like >>>>> >>>>> if (TREE_CODE (arg0) == SSA_NAME) >>>>> arg0 = operand_equal_valueize (arg0, flags); >>>>> if (TREE_CODE (arg1) == SSA_NAME) >>>>> arg1 = operand_equal_valueize (arg1, flags); >>>> >>>> Because I want to be given a pair of trees about which the function >>>> operand_equal_valueize returns match/no-match/dunno. >>>> >>>>> >>>>> and why's this done with virtual functions rather than a callback that we >>>>> can >>>>> cheaply check for NULLness in the default implementation? >>>> >>>> I can transform it into a hook. But as mentioned I'll need two hooks. >>>> >>>>> >>>>> So - what does ICF want to make "equal" that isn't equal normally and >>>>> how's >>>>> that "valueization"? >>>> >>>> E.g. for a FUNCTION_DECL, ICF always return true because it can only calls >>>> the operand_equal_p after callgraph is compared. Similarly for LABEL_DECLs, >>>> we have a map (m_label_bb_map). Please take a look at patch 6/9 in this >>>> series. >>> >>> Hmm, ok, so you basically replace recursive calls to operand_equal_p with > > _recursive calls_ > >>> >>> operand_equal_valueize (t1, t2, 0) >>> || operand_equal_p (t1, t2, 0) >>> >>> no? >> >> This is not going to work .. > > I wonder if > > class base > { > virtual operand_equal_p (tree a, tree b, int f); > }; > > base::operand_equal_p (tree a, tree b, int f) > { > as-is now, recursing to virtual operand_equal_p > } > > class deriv : public base > { > vritual operand_equal_p (tree a, tree b, int f); > }; > > deriv::operand_equal_p (tree a, tree b, int f) > { > // just example > if (TREE_CODE (a) == TREE_CODE (b) > && TREE_CODE (a) == FUNCTION_DECL) > return true; > > return base::operand_equal_p (tree a, tree b, int f); > } > > would work? ICF would call deriv::operand_equal_p and > base::operand_equal_p would recurse via the derived implementation. > > That at least is cleaner from the "looks".
LGTM, I'm sending updated for to address that. > >>> But the same could be achieved by actually making t1 and t2 equal >>> according to operand_equal_p rules via the valueization hook? So replace >>> FUNCTION_DECLs with their prevailing ones, LABEL_DECLs with theirs, etc. >>> >>> As given your abstraction is quite awkward to use, say, from value-numbering >>> which knows how to "valueize" a single tree but doesn't compare things. >>> >>> To make it work for your case you'd valueize not only SSA names but also >>> all DECL_P I guess. After all your operand_equal_valueize only does >>> something for "leafs" but is called for all intermediate expressions as >>> well. >> >> ... because I need to be called for all intermediate expression. One simple >> example can be a ADDR_EXPR of a DECL. The first call will recursively call >> operand_equal_p for the DECL and the DECL can be compared with >> operand_equal_valueize >> in ICF. >> >> Note that current ICF code is more complex than only selection of a canonical >> form of a tree. >> >> I'm not saying the suggested API change is beautiful. But having a more >> specific >> equal hook seams to me a reasonable extension to current operand_equal_p. >> Moreover, we'll be able to kill all the ICF duplicate comparison machinery. > > I wonder if all FUNCTION_DECL are really equal. If you just compare > the callgraph > you don't notice differences in the following (with disabled DSE/FRE > to retain both > stores to *dest) > > void fna(); > void fnb(); > > void foo (void *dest) > { > *dest = (void *)fna; > *dest = (void *)fnb; > } > > void bar (void *dest) > { > *dest = (void *)fnb; > *dest = (void *)fna; > } > > and if you compare IPA refs you'd need to identify the ref stmts as the same? This is all handled in IPA reference and IPA edge comparison in IPA ICF. Don't worry ;) Martin > > >> Martin >> >>> >>> Richard. >>> >>>> Thanks, >>>> Martin >>>> >>>>> >>>>> Thanks, >>>>> Richard. >>>>> >>>>>> Martin >>>>>> >>>>>>> >>>>>>> Richard. >>>>>>> >>>>>>>> Thanks, >>>>>>>> Martin >>>>>>>> >>>>>>>>> IMHO that would also make the logic within >>>>>>>>> operand_equal_p clearer, because you don't have to think about all the >>>>>>>>> potential callback functions that might be called. >>>>>>>>> >>>>>>>>> >>>>>>>>> Ciao, >>>>>>>>> Michael. >>>>>>>>> >>>>>>>> >>>>>> >>>> >>
>From 45c80314110c7227c697748c63394dce11b50966 Mon Sep 17 00:00:00 2001 From: Martin Liska <mli...@suse.cz> Date: Thu, 15 Aug 2019 10:35:14 +0200 Subject: [PATCH 5/9] Come up with an abstraction. gcc/ChangeLog: 2019-08-15 Martin Liska <mli...@suse.cz> * fold-const.c (operand_equal_p): Move to ... (operand_compare::operand_equal_p): ... here. (operand_compare::verify_hash_value): New. (add_expr): Move to ... (operand_compare::hash_operand): ... here. * fold-const.h (operand_equal_p): Move to the class. (class operand_compare): New. * tree.c (add_expr): Remove. --- gcc/fold-const.c | 366 ++++++++++++++++++++++++++++++++++++++++++++--- gcc/fold-const.h | 25 +++- gcc/tree.c | 286 ------------------------------------ 3 files changed, 368 insertions(+), 309 deletions(-) diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 6b588d8e3ce..520a14041da 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -2940,29 +2940,12 @@ combine_comparisons (location_t loc, even if var is volatile. */ bool -operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags) +operand_compare::operand_equal_p (const_tree arg0, const_tree arg1, + unsigned int flags) { - /* When checking, verify at the outermost operand_equal_p call that - if operand_equal_p returns non-zero then ARG0 and ARG1 has the same - hash value. */ - if (flag_checking && !(flags & OEP_NO_HASH_CHECK)) - { - if (operand_equal_p (arg0, arg1, flags | OEP_NO_HASH_CHECK)) - { - if (arg0 != arg1) - { - inchash::hash hstate0 (0), hstate1 (0); - inchash::add_expr (arg0, hstate0, flags | OEP_HASH_CHECK); - inchash::add_expr (arg1, hstate1, flags | OEP_HASH_CHECK); - hashval_t h0 = hstate0.end (); - hashval_t h1 = hstate1.end (); - gcc_assert (h0 == h1); - } - return true; - } - else - return false; - } + bool r; + if (verify_hash_value (arg0, arg1, flags, &r)) + return r; STRIP_ANY_LOCATION_WRAPPER (arg0); STRIP_ANY_LOCATION_WRAPPER (arg1); @@ -3606,6 +3589,345 @@ operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags) #undef OP_SAME #undef OP_SAME_WITH_NULL +} + +/* Generate a hash value for an expression. This can be used iteratively + by passing a previous result as the HSTATE argument. */ + +void +operand_compare::hash_operand (const_tree t, inchash::hash &hstate, + unsigned int flags) +{ + int i; + enum tree_code code; + enum tree_code_class tclass; + + if (t == NULL_TREE || t == error_mark_node) + { + hstate.merge_hash (0); + return; + } + + STRIP_ANY_LOCATION_WRAPPER (t); + + if (!(flags & OEP_ADDRESS_OF)) + STRIP_NOPS (t); + + code = TREE_CODE (t); + + switch (code) + { + /* Alas, constants aren't shared, so we can't rely on pointer + identity. */ + case VOID_CST: + hstate.merge_hash (0); + return; + case INTEGER_CST: + gcc_checking_assert (!(flags & OEP_ADDRESS_OF)); + for (i = 0; i < TREE_INT_CST_EXT_NUNITS (t); i++) + hstate.add_hwi (TREE_INT_CST_ELT (t, i)); + return; + case REAL_CST: + { + unsigned int val2; + if (!HONOR_SIGNED_ZEROS (t) && real_zerop (t)) + val2 = rvc_zero; + else + val2 = real_hash (TREE_REAL_CST_PTR (t)); + hstate.merge_hash (val2); + return; + } + case FIXED_CST: + { + unsigned int val2 = fixed_hash (TREE_FIXED_CST_PTR (t)); + hstate.merge_hash (val2); + return; + } + case STRING_CST: + hstate.add ((const void *) TREE_STRING_POINTER (t), + TREE_STRING_LENGTH (t)); + return; + case COMPLEX_CST: + hash_operand (TREE_REALPART (t), hstate, flags); + hash_operand (TREE_IMAGPART (t), hstate, flags); + return; + case VECTOR_CST: + { + hstate.add_int (VECTOR_CST_NPATTERNS (t)); + hstate.add_int (VECTOR_CST_NELTS_PER_PATTERN (t)); + unsigned int count = vector_cst_encoded_nelts (t); + for (unsigned int i = 0; i < count; ++i) + hash_operand (VECTOR_CST_ENCODED_ELT (t, i), hstate, flags); + return; + } + case SSA_NAME: + /* We can just compare by pointer. */ + hstate.add_hwi (SSA_NAME_VERSION (t)); + return; + case PLACEHOLDER_EXPR: + /* The node itself doesn't matter. */ + return; + case BLOCK: + case OMP_CLAUSE: + /* Ignore. */ + return; + case TREE_LIST: + /* A list of expressions, for a CALL_EXPR or as the elements of a + VECTOR_CST. */ + for (; t; t = TREE_CHAIN (t)) + hash_operand (TREE_VALUE (t), hstate, flags); + return; + case CONSTRUCTOR: + { + unsigned HOST_WIDE_INT idx; + tree field, value; + flags &= ~OEP_ADDRESS_OF; + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (t), idx, field, value) + { + hash_operand (field, hstate, flags); + hash_operand (value, hstate, flags); + } + return; + } + case STATEMENT_LIST: + { + tree_stmt_iterator i; + for (i = tsi_start (CONST_CAST_TREE (t)); + !tsi_end_p (i); tsi_next (&i)) + hash_operand (tsi_stmt (i), hstate, flags); + return; + } + case TREE_VEC: + for (i = 0; i < TREE_VEC_LENGTH (t); ++i) + hash_operand (TREE_VEC_ELT (t, i), hstate, flags); + return; + case IDENTIFIER_NODE: + hstate.add_object (IDENTIFIER_HASH_VALUE (t)); + return; + case FIELD_DECL: + inchash::add_expr (DECL_FIELD_OFFSET (t), hstate, flags); + inchash::add_expr (DECL_FIELD_BIT_OFFSET (t), hstate, flags); + return; + case FUNCTION_DECL: + /* When referring to a built-in FUNCTION_DECL, use the __builtin__ form. + Otherwise nodes that compare equal according to operand_equal_p might + get different hash codes. However, don't do this for machine specific + or front end builtins, since the function code is overloaded in those + cases. */ + if (DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL + && builtin_decl_explicit_p (DECL_FUNCTION_CODE (t))) + { + t = builtin_decl_explicit (DECL_FUNCTION_CODE (t)); + code = TREE_CODE (t); + } + /* FALL THROUGH */ + default: + if (POLY_INT_CST_P (t)) + { + for (unsigned int i = 0; i < NUM_POLY_INT_COEFFS; ++i) + hstate.add_wide_int (wi::to_wide (POLY_INT_CST_COEFF (t, i))); + return; + } + tclass = TREE_CODE_CLASS (code); + + if (tclass == tcc_declaration) + { + /* DECL's have a unique ID */ + hstate.add_hwi (DECL_UID (t)); + } + else if (tclass == tcc_comparison && !commutative_tree_code (code)) + { + /* For comparisons that can be swapped, use the lower + tree code. */ + enum tree_code ccode = swap_tree_comparison (code); + if (code < ccode) + ccode = code; + hstate.add_object (ccode); + hash_operand (TREE_OPERAND (t, ccode != code), hstate, flags); + hash_operand (TREE_OPERAND (t, ccode == code), hstate, flags); + } + else if (CONVERT_EXPR_CODE_P (code)) + { + /* NOP_EXPR and CONVERT_EXPR are considered equal by + operand_equal_p. */ + enum tree_code ccode = NOP_EXPR; + hstate.add_object (ccode); + + /* Don't hash the type, that can lead to having nodes which + compare equal according to operand_equal_p, but which + have different hash codes. Make sure to include signedness + in the hash computation. */ + hstate.add_int (TYPE_UNSIGNED (TREE_TYPE (t))); + hash_operand (TREE_OPERAND (t, 0), hstate, flags); + } + /* For OEP_ADDRESS_OF, hash MEM_EXPR[&decl, 0] the same as decl. */ + else if (code == MEM_REF + && (flags & OEP_ADDRESS_OF) != 0 + && TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR + && DECL_P (TREE_OPERAND (TREE_OPERAND (t, 0), 0)) + && integer_zerop (TREE_OPERAND (t, 1))) + hash_operand (TREE_OPERAND (TREE_OPERAND (t, 0), 0), + hstate, flags); + /* Don't ICE on FE specific trees, or their arguments etc. + during operand_equal_p hash verification. */ + else if (!IS_EXPR_CODE_CLASS (tclass)) + gcc_assert (flags & OEP_HASH_CHECK); + else + { + unsigned int sflags = flags; + + hstate.add_object (code); + + switch (code) + { + case ADDR_EXPR: + gcc_checking_assert (!(flags & OEP_ADDRESS_OF)); + flags |= OEP_ADDRESS_OF; + sflags = flags; + break; + + case INDIRECT_REF: + case MEM_REF: + case TARGET_MEM_REF: + flags &= ~OEP_ADDRESS_OF; + sflags = flags; + break; + + case ARRAY_REF: + case ARRAY_RANGE_REF: + case COMPONENT_REF: + case BIT_FIELD_REF: + sflags &= ~OEP_ADDRESS_OF; + break; + + case COND_EXPR: + flags &= ~OEP_ADDRESS_OF; + break; + + case WIDEN_MULT_PLUS_EXPR: + case WIDEN_MULT_MINUS_EXPR: + { + /* The multiplication operands are commutative. */ + inchash::hash one, two; + hash_operand (TREE_OPERAND (t, 0), one, flags); + hash_operand (TREE_OPERAND (t, 1), two, flags); + hstate.add_commutative (one, two); + hash_operand (TREE_OPERAND (t, 2), two, flags); + return; + } + + case CALL_EXPR: + if (CALL_EXPR_FN (t) == NULL_TREE) + hstate.add_int (CALL_EXPR_IFN (t)); + break; + + case TARGET_EXPR: + /* For TARGET_EXPR, just hash on the TARGET_EXPR_SLOT. + Usually different TARGET_EXPRs just should use + different temporaries in their slots. */ + hash_operand (TARGET_EXPR_SLOT (t), hstate, flags); + return; + + /* Virtual table call. */ + case OBJ_TYPE_REF: + inchash::add_expr (OBJ_TYPE_REF_EXPR (t), hstate, flags); + if (virtual_method_call_p (t)) + { + inchash::add_expr (OBJ_TYPE_REF_TOKEN (t), hstate, flags); + inchash::add_expr (OBJ_TYPE_REF_OBJECT (t), hstate, flags); + } + return; + default: + break; + } + + /* Don't hash the type, that can lead to having nodes which + compare equal according to operand_equal_p, but which + have different hash codes. */ + if (code == NON_LVALUE_EXPR) + { + /* Make sure to include signness in the hash computation. */ + hstate.add_int (TYPE_UNSIGNED (TREE_TYPE (t))); + hash_operand (TREE_OPERAND (t, 0), hstate, flags); + } + + else if (commutative_tree_code (code)) + { + /* It's a commutative expression. We want to hash it the same + however it appears. We do this by first hashing both operands + and then rehashing based on the order of their independent + hashes. */ + inchash::hash one, two; + hash_operand (TREE_OPERAND (t, 0), one, flags); + hash_operand (TREE_OPERAND (t, 1), two, flags); + hstate.add_commutative (one, two); + } + else + for (i = TREE_OPERAND_LENGTH (t) - 1; i >= 0; --i) + hash_operand (TREE_OPERAND (t, i), hstate, + i == 0 ? flags : sflags); + } + return; + } +} + +bool +operand_compare::verify_hash_value (const_tree arg0, const_tree arg1, + unsigned int flags, bool *ret) +{ + /* When checking, verify at the outermost operand_equal_p call that + if operand_equal_p returns non-zero then ARG0 and ARG1 has the same + hash value. */ + if (flag_checking && !(flags & OEP_NO_HASH_CHECK)) + { + if (operand_equal_p (arg0, arg1, flags | OEP_NO_HASH_CHECK)) + { + if (arg0 != arg1) + { + inchash::hash hstate0 (0), hstate1 (0); + hash_operand (arg0, hstate0, flags | OEP_HASH_CHECK); + hash_operand (arg1, hstate1, flags | OEP_HASH_CHECK); + hashval_t h0 = hstate0.end (); + hashval_t h1 = hstate1.end (); + gcc_assert (h0 == h1); + } + *ret = true; + } + else + *ret = false; + + return true; + } + + return false; +} + + +static operand_compare default_compare_instance; + +/* Conveinece wrapper around operand_compare class because usually we do + not need to play with the valueizer. */ + +bool +operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags) +{ + return default_compare_instance.operand_equal_p (arg0, arg1, flags); +} + +namespace inchash +{ + +/* Generate a hash value for an expression. This can be used iteratively + by passing a previous result as the HSTATE argument. + + This function is intended to produce the same hash for expressions which + would compare equal using operand_equal_p. */ +void +add_expr (const_tree t, inchash::hash &hstate, unsigned int flags) +{ + default_compare_instance.hash_operand (t, hstate, flags); +} + } /* Similar to operand_equal_p, but see if ARG0 might be a variant of ARG1 diff --git a/gcc/fold-const.h b/gcc/fold-const.h index 54c850a3ee1..c9c5cbdae36 100644 --- a/gcc/fold-const.h +++ b/gcc/fold-const.h @@ -84,7 +84,7 @@ extern bool fold_deferring_overflow_warnings_p (void); extern void fold_overflow_warning (const char*, enum warn_strict_overflow_code); extern enum tree_code fold_div_compare (enum tree_code, tree, tree, tree *, tree *, bool *); -extern bool operand_equal_p (const_tree, const_tree, unsigned int); +extern bool operand_equal_p (const_tree, const_tree, unsigned int flags = 0); extern int multiple_of_p (tree, const_tree, const_tree); #define omit_one_operand(T1,T2,T3)\ omit_one_operand_loc (UNKNOWN_LOCATION, T1, T2, T3) @@ -212,4 +212,27 @@ extern tree fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE #define fold_build_pointer_plus_hwi(p,o) \ fold_build_pointer_plus_hwi_loc (UNKNOWN_LOCATION, p, o) + + +/* Class used to compare gimple operands. */ + +class operand_compare +{ +public: + /* Return true if two operands are equal. The flags fields can be used + to specify OEP flags described above. */ + virtual bool operand_equal_p (const_tree, const_tree, unsigned int flags); + + /* 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); + +protected: + /* Verify that when arguments (ARG0 and ARG1) are equal, then they have + an equal hash value. When the function knowns comparison return, + true is returned. Then RET is set to corresponding comparsion result. */ + bool verify_hash_value (const_tree arg0, const_tree arg1, unsigned int flags, + bool *ret); +}; + #endif // GCC_FOLD_CONST_H diff --git a/gcc/tree.c b/gcc/tree.c index 3a0851fdcf8..35c0a2c6fb7 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -7790,292 +7790,6 @@ operation_no_trapping_overflow (tree type, enum tree_code code) } } -namespace inchash -{ - -/* Generate a hash value for an expression. This can be used iteratively - by passing a previous result as the HSTATE argument. - - This function is intended to produce the same hash for expressions which - would compare equal using operand_equal_p. */ -void -add_expr (const_tree t, inchash::hash &hstate, unsigned int flags) -{ - int i; - enum tree_code code; - enum tree_code_class tclass; - - if (t == NULL_TREE || t == error_mark_node) - { - hstate.merge_hash (0); - return; - } - - STRIP_ANY_LOCATION_WRAPPER (t); - - if (!(flags & OEP_ADDRESS_OF)) - STRIP_NOPS (t); - - code = TREE_CODE (t); - - switch (code) - { - /* Alas, constants aren't shared, so we can't rely on pointer - identity. */ - case VOID_CST: - hstate.merge_hash (0); - return; - case INTEGER_CST: - gcc_checking_assert (!(flags & OEP_ADDRESS_OF)); - for (i = 0; i < TREE_INT_CST_EXT_NUNITS (t); i++) - hstate.add_hwi (TREE_INT_CST_ELT (t, i)); - return; - case REAL_CST: - { - unsigned int val2; - if (!HONOR_SIGNED_ZEROS (t) && real_zerop (t)) - val2 = rvc_zero; - else - val2 = real_hash (TREE_REAL_CST_PTR (t)); - hstate.merge_hash (val2); - return; - } - case FIXED_CST: - { - unsigned int val2 = fixed_hash (TREE_FIXED_CST_PTR (t)); - hstate.merge_hash (val2); - return; - } - case STRING_CST: - hstate.add ((const void *) TREE_STRING_POINTER (t), - TREE_STRING_LENGTH (t)); - return; - case COMPLEX_CST: - inchash::add_expr (TREE_REALPART (t), hstate, flags); - inchash::add_expr (TREE_IMAGPART (t), hstate, flags); - return; - case VECTOR_CST: - { - hstate.add_int (VECTOR_CST_NPATTERNS (t)); - hstate.add_int (VECTOR_CST_NELTS_PER_PATTERN (t)); - unsigned int count = vector_cst_encoded_nelts (t); - for (unsigned int i = 0; i < count; ++i) - inchash::add_expr (VECTOR_CST_ENCODED_ELT (t, i), hstate, flags); - return; - } - case SSA_NAME: - /* We can just compare by pointer. */ - hstate.add_hwi (SSA_NAME_VERSION (t)); - return; - case PLACEHOLDER_EXPR: - /* The node itself doesn't matter. */ - return; - case BLOCK: - case OMP_CLAUSE: - /* Ignore. */ - return; - case TREE_LIST: - /* A list of expressions, for a CALL_EXPR or as the elements of a - VECTOR_CST. */ - for (; t; t = TREE_CHAIN (t)) - inchash::add_expr (TREE_VALUE (t), hstate, flags); - return; - case CONSTRUCTOR: - { - unsigned HOST_WIDE_INT idx; - tree field, value; - flags &= ~OEP_ADDRESS_OF; - FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (t), idx, field, value) - { - inchash::add_expr (field, hstate, flags); - inchash::add_expr (value, hstate, flags); - } - return; - } - case STATEMENT_LIST: - { - tree_stmt_iterator i; - for (i = tsi_start (CONST_CAST_TREE (t)); - !tsi_end_p (i); tsi_next (&i)) - inchash::add_expr (tsi_stmt (i), hstate, flags); - return; - } - case TREE_VEC: - for (i = 0; i < TREE_VEC_LENGTH (t); ++i) - inchash::add_expr (TREE_VEC_ELT (t, i), hstate, flags); - return; - case IDENTIFIER_NODE: - hstate.add_object (IDENTIFIER_HASH_VALUE (t)); - return; - case FIELD_DECL: - inchash::add_expr (DECL_FIELD_OFFSET (t), hstate, flags); - inchash::add_expr (DECL_FIELD_BIT_OFFSET (t), hstate, flags); - return; - case FUNCTION_DECL: - /* When referring to a built-in FUNCTION_DECL, use the __builtin__ form. - Otherwise nodes that compare equal according to operand_equal_p might - get different hash codes. However, don't do this for machine specific - or front end builtins, since the function code is overloaded in those - cases. */ - if (DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL - && builtin_decl_explicit_p (DECL_FUNCTION_CODE (t))) - { - t = builtin_decl_explicit (DECL_FUNCTION_CODE (t)); - code = TREE_CODE (t); - } - /* FALL THROUGH */ - default: - if (POLY_INT_CST_P (t)) - { - for (unsigned int i = 0; i < NUM_POLY_INT_COEFFS; ++i) - hstate.add_wide_int (wi::to_wide (POLY_INT_CST_COEFF (t, i))); - return; - } - tclass = TREE_CODE_CLASS (code); - - if (tclass == tcc_declaration) - { - /* DECL's have a unique ID */ - hstate.add_hwi (DECL_UID (t)); - } - else if (tclass == tcc_comparison && !commutative_tree_code (code)) - { - /* For comparisons that can be swapped, use the lower - tree code. */ - enum tree_code ccode = swap_tree_comparison (code); - if (code < ccode) - ccode = code; - hstate.add_object (ccode); - inchash::add_expr (TREE_OPERAND (t, ccode != code), hstate, flags); - inchash::add_expr (TREE_OPERAND (t, ccode == code), hstate, flags); - } - else if (CONVERT_EXPR_CODE_P (code)) - { - /* NOP_EXPR and CONVERT_EXPR are considered equal by - operand_equal_p. */ - enum tree_code ccode = NOP_EXPR; - hstate.add_object (ccode); - - /* Don't hash the type, that can lead to having nodes which - compare equal according to operand_equal_p, but which - have different hash codes. Make sure to include signedness - in the hash computation. */ - hstate.add_int (TYPE_UNSIGNED (TREE_TYPE (t))); - inchash::add_expr (TREE_OPERAND (t, 0), hstate, flags); - } - /* For OEP_ADDRESS_OF, hash MEM_EXPR[&decl, 0] the same as decl. */ - else if (code == MEM_REF - && (flags & OEP_ADDRESS_OF) != 0 - && TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR - && DECL_P (TREE_OPERAND (TREE_OPERAND (t, 0), 0)) - && integer_zerop (TREE_OPERAND (t, 1))) - inchash::add_expr (TREE_OPERAND (TREE_OPERAND (t, 0), 0), - hstate, flags); - /* Don't ICE on FE specific trees, or their arguments etc. - during operand_equal_p hash verification. */ - else if (!IS_EXPR_CODE_CLASS (tclass)) - gcc_assert (flags & OEP_HASH_CHECK); - else - { - unsigned int sflags = flags; - - hstate.add_object (code); - - switch (code) - { - case ADDR_EXPR: - gcc_checking_assert (!(flags & OEP_ADDRESS_OF)); - flags |= OEP_ADDRESS_OF; - sflags = flags; - break; - - case INDIRECT_REF: - case MEM_REF: - case TARGET_MEM_REF: - flags &= ~OEP_ADDRESS_OF; - sflags = flags; - break; - - case ARRAY_REF: - case ARRAY_RANGE_REF: - case COMPONENT_REF: - case BIT_FIELD_REF: - sflags &= ~OEP_ADDRESS_OF; - break; - - case COND_EXPR: - flags &= ~OEP_ADDRESS_OF; - break; - - case WIDEN_MULT_PLUS_EXPR: - case WIDEN_MULT_MINUS_EXPR: - { - /* The multiplication operands are commutative. */ - inchash::hash one, two; - inchash::add_expr (TREE_OPERAND (t, 0), one, flags); - inchash::add_expr (TREE_OPERAND (t, 1), two, flags); - hstate.add_commutative (one, two); - inchash::add_expr (TREE_OPERAND (t, 2), two, flags); - return; - } - - case CALL_EXPR: - if (CALL_EXPR_FN (t) == NULL_TREE) - hstate.add_int (CALL_EXPR_IFN (t)); - break; - - case TARGET_EXPR: - /* For TARGET_EXPR, just hash on the TARGET_EXPR_SLOT. - Usually different TARGET_EXPRs just should use - different temporaries in their slots. */ - inchash::add_expr (TARGET_EXPR_SLOT (t), hstate, flags); - return; - - /* Virtual table call. */ - case OBJ_TYPE_REF: - inchash::add_expr (OBJ_TYPE_REF_EXPR (t), hstate, flags); - if (virtual_method_call_p (t)) - { - inchash::add_expr (OBJ_TYPE_REF_TOKEN (t), hstate, flags); - inchash::add_expr (OBJ_TYPE_REF_OBJECT (t), hstate, flags); - } - return; - default: - break; - } - - /* Don't hash the type, that can lead to having nodes which - compare equal according to operand_equal_p, but which - have different hash codes. */ - if (code == NON_LVALUE_EXPR) - { - /* Make sure to include signness in the hash computation. */ - hstate.add_int (TYPE_UNSIGNED (TREE_TYPE (t))); - inchash::add_expr (TREE_OPERAND (t, 0), hstate, flags); - } - - else if (commutative_tree_code (code)) - { - /* It's a commutative expression. We want to hash it the same - however it appears. We do this by first hashing both operands - and then rehashing based on the order of their independent - hashes. */ - inchash::hash one, two; - inchash::add_expr (TREE_OPERAND (t, 0), one, flags); - inchash::add_expr (TREE_OPERAND (t, 1), two, flags); - hstate.add_commutative (one, two); - } - else - for (i = TREE_OPERAND_LENGTH (t) - 1; i >= 0; --i) - inchash::add_expr (TREE_OPERAND (t, i), hstate, - i == 0 ? flags : sflags); - } - return; - } -} - -} - /* Constructors for pointer, array and function types. (RECORD_TYPE, UNION_TYPE and ENUMERAL_TYPE nodes are constructed by language-dependent code, not here.) */ -- 2.22.0