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