On Mon, Mar 10, 2025 at 03:51:29PM +0100, Konstantinos Eleftheriou wrote:
> Testcases for patterns `((a ^ b) & c) cmp d | a != b -> (0 cmp d | a != b)`
> and `(a ^ b) cmp c | a != b -> (0 cmp c | a != b)` were failing on some
> targets, like PowerPC.
> 
> This patch moves the optimization to reassoc. Doing so, we can now handle
> cases where the related conditions appear in an AND expression too. Also,
> we can optimize cases where we have intermediate expressions between the
> related ones in the AND/OR expression on some targets. This is not handled
> on targets like PowerPC, where each condition of the AND/OR expression
> is placed into a different basic block.

I think this is too late for GCC 15.
Not full review, just some comments.

> Bootstrapped/regtested on x86 and AArch64.
> 
>       PR tree-optimization/116860
> 
> gcc/ChangeLog:
> 
>       * match.pd: Remove the following patterns:
>       ((a ^ b) & c) cmp d | a != b -> (0 cmp d | a != b)
>       (a ^ b) cmp c | a != b -> (0 cmp c | a != b)

I'm not convinced you need to remove this from match.pd, I'd keep it
and add the optimization to tree-ssa-reassoc.cc as well.
In match.pd, it can trigger more often than twice for reassoc.

> @@ -4077,6 +4079,347 @@ optimize_range_tests_var_bound (enum tree_code 
> opcode, int first, int length,
>    return any_changes;
>  }
>  
> +/* Helper function for optimize_cmp_xor_exprs.  Visit EXPR operands
> +   recursively and try to find comparison or XOR expressions that can be
> +   solved using the expressions in CALC_STMTS.  Expressions that can be 
> folded
> +   to 0 are stored in STMTS_TO_FOLD.  IS_OR_EXPR is true for OR expressions
> +   and false for AND expressions.  */
> +
> +tree
> +solve_expr (tree expr, std::set<gimple *> *calc_stmts,
> +         std::set<gimple *> *stmts_to_fold, std::set<tree> *visited,
> +         bool is_or_expr)

Why are you using std::set?  Normally especially for consistency when gcc
has its own containers we use the gcc containers, not stl ones.
So, I'd use hash_set.

> +{
> +  /* Return, if have already visited this expression or the expression is not
> +     an SSA name.  */
> +  if ((visited->find (expr) != visited->end ())
> +      || (TREE_CODE (expr) != SSA_NAME))

Why the redundant ()s around the || operands?

> +  if (terminal_node_num != (op_num - 1))

Again, why the ()s around op_num - 1.

> +      tree_code inverted_code = invert_tree_comparison (stmt_rhs_code,
> +                                             HONOR_NANS (TREE_TYPE (expr)));

This is ugly formatting, just use
      tree_code inverted_code
        = invert_tree_comparison (stmt_rhs_code,
                                  HONOR_NANS (TREE_TYPE (expr)));
or so.

> +       if (is_or_expr
> +           || (gimple_bb (stmt) == gimple_bb (def_stmt))

Why the extra ()s?

> +           || reassoc_stmt_dominates_stmt_p (stmt, def_stmt))

> +      tree op = gimple_op (def_stmt, i);
> +      if (!op) continue;

continue should be on a separate line.

> +  if (!is_or_expr && (opcode != BIT_AND_EXPR))
> +    return false;

Again, don't use () unless it helps readability, avoid warnings
or is needed for formatting.

> +      tree op = *(expr_terms.begin ());

Again.

> +           if (!last_stmt || (gimple_code (last_stmt) != GIMPLE_COND))

Again.

> +               && (EDGE_COUNT (pred->succs) > 1))

Again.

> +     std::set<tree> op_terminal_nodes = get_terminal_nodes (op,
> +                                                     &terminal_nodes, i);

Too ugly formatting, move = on the next line, then this one will most likely
fit.

> +     unsigned int j = i + 1;
> +     for (std::set<tree>::iterator next_it = std::next (it);
> +          next_it != expr_terms.end (); ++next_it, ++j)
> +       {
> +         tree next_op = *next_it;
> +         std::set<tree> next_op_term_nodes = get_terminal_nodes (next_op,
> +                                                     &terminal_nodes, j);
> +         std::set<tree> intersection;
> +         std::set_intersection (op_terminal_nodes.begin (),
> +                                op_terminal_nodes.end (),
> +                                next_op_term_nodes.begin (),
> +                                next_op_term_nodes.end (),
> +                                std::inserter (intersection,
> +                                               intersection.begin ()));
> +         if (intersection.size () > 1)

I think computing whole intersection only to check if there are 2+ entries
in the intersection is expensive.  IMHO just traverse one hash_set and
look up in each case the tree in the other hash_set and stop looking after
finding 2.

> +         tree_code def_stmt_code = gimple_assign_rhs_code (def_stmt);
> +         if ((def_stmt_code == EQ_EXPR || def_stmt_code == NE_EXPR)
> +             && (calc_stmts.find (def_stmt) == calc_stmts.end ())
> +             && (stmts_to_fold.find (def_stmt) == stmts_to_fold.end ()))

2 redundant pairs of ()s.

        Jakub

Reply via email to