diff -uprN a/gcc/testsuite/gcc.dg/tree-ssa/pr89430-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr89430-1.c --- a/gcc/testsuite/gcc.dg/tree-ssa/pr89430-1.c 2020-05-26 21:03:43.132721856 +0800 +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr89430-1.c 2020-05-19 20:12:32.655794652 +0800 @@ -9,4 +9,4 @@ unsigned test(unsigned k, unsigned b) { return a[0]+a[1]; } -/* { dg-final { scan-tree-dump "Conditional store replacement" "cselim" { xfail *-*-* } } } */ +/* { dg-final { scan-tree-dump "Conditional store replacement" "cselim" } } */ diff -uprN a/gcc/testsuite/gcc.dg/tree-ssa/pr89430-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr89430-2.c --- a/gcc/testsuite/gcc.dg/tree-ssa/pr89430-2.c 2020-05-26 21:03:43.132721856 +0800 +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr89430-2.c 2020-05-19 20:12:32.667794652 +0800 @@ -11,4 +11,4 @@ unsigned test(unsigned k, unsigned b) { return a[0]+a[1]; } -/* { dg-final { scan-tree-dump "Conditional store replacement" "cselim" { xfail *-*-* } } } */ +/* { dg-final { scan-tree-dump "Conditional store replacement" "cselim" } } */ diff -uprN a/gcc/testsuite/gcc.dg/tree-ssa/pr89430-5.c b/gcc/testsuite/gcc.dg/tree-ssa/pr89430-5.c --- a/gcc/testsuite/gcc.dg/tree-ssa/pr89430-5.c 2020-05-26 21:03:43.132721856 +0800 +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr89430-5.c 2020-05-19 20:12:32.667794652 +0800 @@ -13,4 +13,4 @@ int test(int b, int k) { return a.data[0] + a.data[1]; } -/* { dg-final { scan-tree-dump "Conditional store replacement" "cselim" { xfail *-*-* } } } */ +/* { dg-final { scan-tree-dump "Conditional store replacement" "cselim" } } */ diff -uprN a/gcc/tree-ssa-phiopt.c b/gcc/tree-ssa-phiopt.c --- a/gcc/tree-ssa-phiopt.c 2020-05-26 21:03:43.132721856 +0800 +++ b/gcc/tree-ssa-phiopt.c 2020-05-26 21:02:02.872006469 +0800 @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. #include "params.h" #include "case-cfn-macros.h" #include "tree-eh.h" +#include "inchash.h" static unsigned int tree_ssa_phiopt_worker (bool, bool, bool); static bool two_value_replacement (basic_block, basic_block, edge, gphi *, @@ -1984,6 +1985,18 @@ struct name_to_bb basic_block bb; }; +/* A hash-table of ARRAY_REF with a base of VAR_DECL and an offset of + SSA_NAME, and in which basic block it was seen, which would constitute + a no-trap region for same accessed. */ +struct array_ref_to_bb +{ + unsigned int ssa_name_ver; + unsigned int phase; + HOST_WIDE_INT size; + tree var_decl; + basic_block bb; +}; + /* Hashtable helpers. */ struct ssa_names_hasher : free_ptr_hash @@ -1992,6 +2005,12 @@ struct ssa_names_hasher : free_ptr_hash static inline bool equal (const name_to_bb *, const name_to_bb *); }; +struct array_refs_hasher : free_ptr_hash +{ + static inline hashval_t hash (const array_ref_to_bb *); + static inline bool equal (const array_ref_to_bb *, const array_ref_to_bb *); +}; + /* Used for quick clearing of the hash-table when we see calls. Hash entries with phase < nt_call_phase are invalid. */ static unsigned int nt_call_phase; @@ -2005,6 +2024,16 @@ ssa_names_hasher::hash (const name_to_bb ^ (n->offset << 6) ^ (n->size << 3); } +inline hashval_t +array_refs_hasher::hash (const array_ref_to_bb *n) +{ + inchash::hash hstate (0); + hstate.add_int (n->ssa_name_ver); + hstate.add_hwi (n->size); + hstate.add_ptr (n->var_decl); + return hstate.end (); +} + /* The equality function of *P1 and *P2. */ inline bool @@ -2016,11 +2045,21 @@ ssa_names_hasher::equal (const name_to_b && n1->size == n2->size; } +inline bool +array_refs_hasher::equal (const array_ref_to_bb *n1, const array_ref_to_bb *n2) +{ + return n1->ssa_name_ver == n2->ssa_name_ver + && n1->size == n2->size + && n1->var_decl == n2->var_decl; +} + class nontrapping_dom_walker : public dom_walker { public: nontrapping_dom_walker (cdi_direction direction, hash_set *ps) - : dom_walker (direction), m_nontrapping (ps), m_seen_ssa_names (128) {} + : dom_walker (direction), m_nontrapping (ps), + m_seen_ssa_names (128), m_seen_array_refs (128) + {} virtual edge before_dom_children (basic_block); virtual void after_dom_children (basic_block); @@ -2028,16 +2067,18 @@ public: private: /* We see the expression EXP in basic block BB. If it's an interesting - expression (an MEM_REF through an SSA_NAME) possibly insert the - expression into the set NONTRAP or the hash table of seen expressions. - STORE is true if this expression is on the LHS, otherwise it's on - the RHS. */ + expression (an MEM_REF through an SSA_NAME or an ARRAY_REF with a base + of VAR_DECL and an offset of SSA_NAME) possibly insert the expression + into the set NONTRAP or the hash table of seen expressions. STORE + is true if this expression is on the LHS, otherwise it's on the RHS. */ void add_or_mark_expr (basic_block, tree, bool); + void add_or_mark_array_ref (basic_block, tree); hash_set *m_nontrapping; /* The hash table for remembering what we've seen. */ hash_table m_seen_ssa_names; + hash_table m_seen_array_refs; }; /* Called by walk_dominator_tree, when entering the block BB. */ @@ -2071,7 +2112,9 @@ nontrapping_dom_walker::before_dom_child else if (gimple_assign_single_p (stmt) && !gimple_has_volatile_ops (stmt)) { add_or_mark_expr (bb, gimple_assign_lhs (stmt), true); + add_or_mark_array_ref (bb, gimple_assign_lhs (stmt)); add_or_mark_expr (bb, gimple_assign_rhs1 (stmt), false); + add_or_mark_array_ref (bb, gimple_assign_rhs1 (stmt)); } } return NULL; @@ -2148,6 +2191,74 @@ nontrapping_dom_walker::add_or_mark_expr } } } +} + +/* We see the expression EXP in basic block BB. If it's an interesting + expression (an ARRAY_REF with a base of VAR_DECL and an offset of + SSA_NAME) possibly insert the expression into the set NONTRAP or the + hash table of seen expressions. */ +void +nontrapping_dom_walker::add_or_mark_array_ref (basic_block bb, tree exp) +{ + if (TREE_CODE (exp) == ARRAY_REF + && TREE_CODE (TREE_OPERAND (exp, 1)) == SSA_NAME + && int_size_in_bytes (TREE_TYPE (exp)) > 0) + { + HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (exp)); + tree base = get_base_address (exp); + /* if BASE is a local variable without address-taken, which can't be + read-only, a dominating load can constitute a no-trap region for + a store as well. */ + if (TREE_CODE (base) == VAR_DECL + && auto_var_p (base) && !TREE_ADDRESSABLE (base)) + { + struct array_ref_to_bb array_map; + basic_block found_array_bb = 0; + + /* Try to find the last seen ARRAY_REF with the same base and + offset, which can trap. */ + array_map.ssa_name_ver = SSA_NAME_VERSION (TREE_OPERAND (exp, 1)); + array_map.phase = 0; + array_map.bb = 0; + array_map.size = size; + array_map.var_decl = base; + + array_ref_to_bb **slot + = m_seen_array_refs.find_slot (&array_map, INSERT); + struct array_ref_to_bb *a2bb = *slot; + if (a2bb != NULL && a2bb->phase >= nt_call_phase) + { + found_array_bb = a2bb->bb; + } + + /* If we've found a trapping MEM_REF, _and_ it dominates EXP + (it's in a basic block on the path from us to the dominator root) + then we can't trap. */ + if (found_array_bb && (((size_t)found_array_bb->aux) & 1) == 1) + { + m_nontrapping->add (exp); + } + else + { + /* EXP might trap, so insert it into the hash table. */ + if (a2bb != NULL) + { + a2bb->phase = nt_call_phase; + a2bb->bb = bb; + } + else + { + a2bb = XNEW (struct array_ref_to_bb); + a2bb->ssa_name_ver = SSA_NAME_VERSION (TREE_OPERAND (exp, 1)); + a2bb->phase = nt_call_phase; + a2bb->bb = bb; + a2bb->size = size; + a2bb->var_decl = base; + *slot = a2bb; + } + } + } + } } /* This is the entry point of gathering non trapping memory accesses.