Rework ifcombine to support merging conditions from noncontiguous blocks. This depends on earlier preparation changes.
The function that attempted to ifcombine a block with its immediate predecessor, tree_ssa_ifcombine_bb, now loops over dominating blocks eligible for ifcombine, attempting to combine with them. The function that actually drives the combination of a pair of blocks, tree_ssa_ifcombine_bb_1, now takes an additional parameter: the successor of outer that leads to inner. The function that recognizes if_then_else patterns is modified to enable testing without distinguishing between then and else, or to require nondegenerate conditions, that aren't worth combining with. for gcc/ChangeLog * tree-ssa-ifcombine.cc (recognize_if_then_else): Support relaxed then/else testing; require nondegenerate condition otherwise. (tree_ssa_ifcombine_bb_1): Add outer_succ_bb parm, use it instead of inner_cond_bb. Adjust callers. (tree_ssa_ifcombine_bb): Loop over dominating outer blocks eligible for ifcombine. (pass_tree_ifcombine::execute): Noted potential need for changes to the post-combine logic. --- gcc/tree-ssa-ifcombine.cc | 152 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 123 insertions(+), 29 deletions(-) diff --git a/gcc/tree-ssa-ifcombine.cc b/gcc/tree-ssa-ifcombine.cc index 71c7c9074e94a..817c95b20252e 100644 --- a/gcc/tree-ssa-ifcombine.cc +++ b/gcc/tree-ssa-ifcombine.cc @@ -85,25 +85,34 @@ known_succ_p (basic_block cond_bb) is left to CFG cleanup and DCE. */ -/* Recognize a if-then-else CFG pattern starting to match with the - COND_BB basic-block containing the COND_EXPR. The recognized - then end else blocks are stored to *THEN_BB and *ELSE_BB. If - *THEN_BB and/or *ELSE_BB are already set, they are required to - match the then and else basic-blocks to make the pattern match. - Returns true if the pattern matched, false otherwise. */ +/* Recognize a if-then-else CFG pattern starting to match with the COND_BB + basic-block containing the COND_EXPR. If !SUCCS_ANY, the condition must not + resolve to a constant for a match. Returns true if the pattern matched, + false otherwise. In case of a !SUCCS_ANY match, the recognized then end + else blocks are stored to *THEN_BB and *ELSE_BB. If *THEN_BB and/or + *ELSE_BB are already set, they are required to match the then and else + basic-blocks to make the pattern match. If SUCCS_ANY, *THEN_BB and *ELSE_BB + will not be filled in, and they will be found to match even if reversed. */ static bool recognize_if_then_else (basic_block cond_bb, - basic_block *then_bb, basic_block *else_bb) + basic_block *then_bb, basic_block *else_bb, + bool succs_any = false) { edge t, e; - if (EDGE_COUNT (cond_bb->succs) != 2) + if (EDGE_COUNT (cond_bb->succs) != 2 + || (!succs_any && known_succ_p (cond_bb))) return false; /* Find the then/else edges. */ t = EDGE_SUCC (cond_bb, 0); e = EDGE_SUCC (cond_bb, 1); + + if (succs_any) + return ((t->dest == *then_bb && e->dest == *else_bb) + || (t->dest == *else_bb && e->dest == *then_bb)); + if (!(t->flags & EDGE_TRUE_VALUE)) std::swap (t, e); if (!(t->flags & EDGE_TRUE_VALUE) @@ -886,19 +895,21 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool inner_inv, /* Helper function for tree_ssa_ifcombine_bb. Recognize a CFG pattern and dispatch to the appropriate if-conversion helper for a particular set of INNER_COND_BB, OUTER_COND_BB, THEN_BB and ELSE_BB. - PHI_PRED_BB should be one of INNER_COND_BB, THEN_BB or ELSE_BB. */ + PHI_PRED_BB should be one of INNER_COND_BB, THEN_BB or ELSE_BB. + OUTER_SUCC_BB is the successor of OUTER_COND_BB on the path towards + INNER_COND_BB. */ static bool tree_ssa_ifcombine_bb_1 (basic_block inner_cond_bb, basic_block outer_cond_bb, basic_block then_bb, basic_block else_bb, - basic_block phi_pred_bb) + basic_block phi_pred_bb, basic_block outer_succ_bb) { /* The && form is characterized by a common else_bb with the two edges leading to it mergable. The latter is guaranteed by matching PHI arguments in the else_bb and the inner cond_bb having no side-effects. */ if (phi_pred_bb != else_bb - && recognize_if_then_else (outer_cond_bb, &inner_cond_bb, &else_bb) + && recognize_if_then_else (outer_cond_bb, &outer_succ_bb, &else_bb) && same_phi_args_p (outer_cond_bb, phi_pred_bb, else_bb)) { /* We have @@ -915,7 +926,7 @@ tree_ssa_ifcombine_bb_1 (basic_block inner_cond_bb, basic_block outer_cond_bb, /* And a version where the outer condition is negated. */ if (phi_pred_bb != else_bb - && recognize_if_then_else (outer_cond_bb, &else_bb, &inner_cond_bb) + && recognize_if_then_else (outer_cond_bb, &else_bb, &outer_succ_bb) && same_phi_args_p (outer_cond_bb, phi_pred_bb, else_bb)) { /* We have @@ -935,7 +946,7 @@ tree_ssa_ifcombine_bb_1 (basic_block inner_cond_bb, basic_block outer_cond_bb, by matching PHI arguments in the then_bb and the inner cond_bb having no side-effects. */ if (phi_pred_bb != then_bb - && recognize_if_then_else (outer_cond_bb, &then_bb, &inner_cond_bb) + && recognize_if_then_else (outer_cond_bb, &then_bb, &outer_succ_bb) && same_phi_args_p (outer_cond_bb, phi_pred_bb, then_bb)) { /* We have @@ -951,7 +962,7 @@ tree_ssa_ifcombine_bb_1 (basic_block inner_cond_bb, basic_block outer_cond_bb, /* And a version where the outer condition is negated. */ if (phi_pred_bb != then_bb - && recognize_if_then_else (outer_cond_bb, &inner_cond_bb, &then_bb) + && recognize_if_then_else (outer_cond_bb, &outer_succ_bb, &then_bb) && same_phi_args_p (outer_cond_bb, phi_pred_bb, then_bb)) { /* We have @@ -975,10 +986,11 @@ tree_ssa_ifcombine_bb_1 (basic_block inner_cond_bb, basic_block outer_cond_bb, static bool tree_ssa_ifcombine_bb (basic_block inner_cond_bb) { + bool ret = false; basic_block then_bb = NULL, else_bb = NULL; if (!recognize_if_then_else (inner_cond_bb, &then_bb, &else_bb)) - return false; + return ret; /* Recognize && and || of two conditions with a common then/else block which entry edges we can merge. That is: @@ -987,17 +999,62 @@ tree_ssa_ifcombine_bb (basic_block inner_cond_bb) and if (a && b) ; - This requires a single predecessor of the inner cond_bb. */ - if (single_pred_p (inner_cond_bb) - && bb_no_side_effects_p (inner_cond_bb)) + This requires a single predecessor of the inner cond_bb. + + Look for an OUTER_COND_BBs to combine with INNER_COND_BB. They need not + be contiguous, as long as inner and intervening blocks have no side + effects, and are either single-entry-single-exit or conditionals choosing + between the same EXIT_BB with the same PHI args, and the path leading to + INNER_COND_BB. ??? We could potentially handle multi-block + single-entry-single-exit regions, but the loop below only deals with + single-entry-single-exit individual intervening blocks. Larger regions + without side effects are presumably rare, so it's probably not worth the + effort. */ + for (basic_block bb = inner_cond_bb, outer_cond_bb, exit_bb = NULL; + single_pred_p (bb) && bb_no_side_effects_p (bb); + bb = outer_cond_bb) { - basic_block outer_cond_bb = single_pred (inner_cond_bb); + bool changed = false; - if (tree_ssa_ifcombine_bb_1 (inner_cond_bb, outer_cond_bb, - then_bb, else_bb, inner_cond_bb)) - return true; + outer_cond_bb = single_pred (bb); - if (forwarder_block_to (else_bb, then_bb)) + /* Skip blocks without conditions. */ + if (single_succ_p (outer_cond_bb)) + continue; + + /* When considering noncontiguous conditions, make sure that all + non-final conditions lead to the same successor of the final + condition, when not taking the path to inner_bb, so that we can + combine C into A, both in A && (B && C), and in A || (B || C), but + neither in A && (B || C), nor A || (B && C). Say, if C goes to + THEN_BB or ELSE_BB, then B must go to either of these, say X, besides + C (whether C is then or else), and A must go to X and B (whether then + or else). + + We test for this, while allowing intervening nonconditional blocks, by + first taking note of which of the successors of the inner conditional + block is the exit path taken by the first considered outer conditional + block. + + Having identified and saved the exit block in EXIT_BB at the end of + the loop, here we test that subsequent conditional blocks under + consideration also use the exit block as a successor, besides the + block that leads to inner_cond_bb, and that the edges to exit share + the same phi values. */ + if (exit_bb + && !recognize_if_then_else (outer_cond_bb, &bb, &exit_bb, true)) + break; + + /* After checking dests and phi args, we can also skip blocks whose + conditions have been optimized down to a constant, without trying to + combine them, but we must not skip the computation of EXIT_BB and the + checking of same phi args. */ + if (known_succ_p (outer_cond_bb)) + changed = false; + else if (tree_ssa_ifcombine_bb_1 (inner_cond_bb, outer_cond_bb, + then_bb, else_bb, inner_cond_bb, bb)) + changed = true; + else if (forwarder_block_to (else_bb, then_bb)) { /* Other possibilities for the && form, if else_bb is empty forwarder block to then_bb. Compared to the above simpler @@ -1006,8 +1063,8 @@ tree_ssa_ifcombine_bb (basic_block inner_cond_bb) For same_phi_args_p we look at equality of arguments between edge from outer_cond_bb and the forwarder block. */ if (tree_ssa_ifcombine_bb_1 (inner_cond_bb, outer_cond_bb, else_bb, - then_bb, else_bb)) - return true; + then_bb, else_bb, bb)) + changed = true; } else if (forwarder_block_to (then_bb, else_bb)) { @@ -1018,12 +1075,46 @@ tree_ssa_ifcombine_bb (basic_block inner_cond_bb) For same_phi_args_p we look at equality of arguments between edge from outer_cond_bb and the forwarder block. */ if (tree_ssa_ifcombine_bb_1 (inner_cond_bb, outer_cond_bb, else_bb, - then_bb, then_bb)) - return true; + then_bb, then_bb, bb)) + changed = true; } + + if (changed) + ret = changed; + + /* If the inner condition is gone, there's no point in attempting to + combine it any further. */ + if (changed && known_succ_p (inner_cond_bb)) + break; + + /* Record the exit path taken by the outer condition. */ + if (!exit_bb) + { + if (recognize_if_then_else (outer_cond_bb, &then_bb, &bb, true)) + exit_bb = then_bb; + else if (recognize_if_then_else (outer_cond_bb, &bb, &else_bb, true)) + exit_bb = else_bb; + else + break; + } + + /* Before trying an earlier block, make sure INNER_COND_BB and the + current OUTER_COND_BB share the same PHI args at EXIT_BB. We don't + need to check if the latest attempt at combining succeeded, because + that means we'll have already checked. But we can't only check outer + and inner, we have to check that all intervening blocks also get to + exit with the same result, otherwise the transformation may change the + final result. Consider (a ? 0 : b ? 1 : c ? 0 : -1). If we combine + (a | c), yielding ((a | c) ? 0 : b ? 1 : [0 ? 0 :] -1), we'd get 0 + rather than 1 when (!a&&b). And if we were to replace inner instead + of outer, we'd get ([1 ? 0 :] b ? 1 : (a | c) ? 0 : -1), which would + yield 1 rather than 0 when (a). */ + if (!changed + && !same_phi_args_p (outer_cond_bb, inner_cond_bb, exit_bb)) + break; } - return false; + return ret; } /* Main entry for the tree if-conversion pass. */ @@ -1082,7 +1173,10 @@ pass_tree_ifcombine::execute (function *fun) if (tree_ssa_ifcombine_bb (bb)) { /* Clear range info from all stmts in BB which is now executed - conditional on a always true/false condition. */ + conditional on a always true/false condition. ??? When we + combine noncontiguous blocks, do we need to adjust the + intervening blocks as well? If we leave outer conditions + nonconstant, do we still need to modify them? */ reset_flow_sensitive_info_in_bb (bb); for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) -- Alexandre Oliva, happy hacker https://FSFLA.org/blogs/lxo/ Free Software Activist GNU Toolchain Engineer More tolerance and less prejudice are key for inclusion and diversity Excluding neuro-others for not behaving ""normal"" is *not* inclusive