RFR: 8296389: C2: PhaseCFG::convert_NeverBranch_to_Goto must handle both orders of successors

Tobias Hartmann thartmann at openjdk.org
Tue Dec 6 11:39:16 UTC 2022


On Fri, 2 Dec 2022 12:48:31 GMT, Emanuel Peter <epeter at openjdk.org> wrote:

> **Targetted for JDK21**, since this is not a new regression, but rather an old bug. P3 because creates `SIGSEGV` in product build.
> 
> The code in `PhaseCFG::convert_NeverBranch_to_Goto` looks like it is ready to have `idx == 1`, but it is not.
> 
> We would read `succ` from `_succs[1]`.
> https://github.com/openjdk/jdk/blob/8c472e481676ed0ef475c4989477d5714880c59e/src/hotspot/share/opto/block.cpp#L626
> 
> Then overwrite `_succs[0]` with `succ`, and shorten the array.
> https://github.com/openjdk/jdk/blob/8c472e481676ed0ef475c4989477d5714880c59e/src/hotspot/share/opto/block.cpp#L635-L636
> 
> And finally attempt to read `dead` from `_succs[0]`, where the dead block used to be, but was just overwritten.
> https://github.com/openjdk/jdk/blob/8c472e481676ed0ef475c4989477d5714880c59e/src/hotspot/share/opto/block.cpp#L645
> 
> **Solution**
> Read `dead` before overwriting it. I also made it more robust by going via the projections, and not assuming that the projections and successors are ordered equally (though that is probably guaranteed by the matching traversal).
> 
> **Why did we never hit this bug before?**
> Normal case: during matching, "succ" projection is added as output of NeverBranch before the "dead" projection leading to Halt. Thus, the outputs of NeverBranch are normally [[ "succ", "dead" ]], hence `idx == 0`.
> Details: During DFS, usually we go from Halt to NeverBranch. Then via Region/Loop, take backedge, and find the "succ" edge. We already have its inputs (NeverBranch), thus we can now post-visit the live edge, and attach it to the NeverBranch first. Later, once we have processed the whole infinite loop, we post-visit out of NeverBranch to the "dead" projection edge, which we attach second.
> 
> Rare case: "dead" projection is first attached to NeverBranch, and "succ" projection is added second. We have [[ "dead", "succ" ]], hence `idx == 1`.
> We have a peeled infinite loop. The NeverBranch of the peeled iteration is first visited via the "dead" projection from HaltNode. Since the peeled iteration has no backedge, we do not visit the "succ" projection yet, but instead attach "dead" projection to HaltNode already once we are done visiting everything above. Later, we come from the peeled loop's NeverBranch exit, to the "succ" projection of the peeled iteration's NeverBranch, and attach the "succ" projection.
> 
> ![image](https://user-images.githubusercontent.com/32593061/205299027-0e8e1d46-a49c-48c6-81b4-dfe83d8236ec.png)

Changes requested by thartmann (Reviewer).

src/hotspot/share/opto/block.cpp line 626:

> 624:   int end_idx = b->end_idx();
> 625:   int taken_idx = b->get_node(end_idx+1)->as_Proj()->_con;
> 626:   ProjNode* alwaysTaken = b->get_node(end_idx + 1 + taken_idx)->as_Proj();

I find this code rather confusing. Since it's guaranteed that `alwaysTaken->_con == 0`, can't we simply do something like this?

ProjNode* alwaysTaken = b->get_node(end_idx)->as_MultiBranch()->proj_out(0);
Block* succ ==  get_block_for_node(alwaysTaken->unique_ctrl_out_or_null());

test/hotspot/jtreg/compiler/loopopts/TestPhaseCFGNeverBranchToGotoMain.java line 28:

> 26:  * @bug 8296389
> 27:  * @summary Peeling of Irreducible loop can lead to NeverBranch being visited from either side
> 28:  * @run main/othervm -Xcomp -Xbatch -XX:-TieredCompilation -XX:PerMethodTrapLimit=0

Suggestion:

 * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:PerMethodTrapLimit=0


`-Xcomp` implies `-Xbatch`

test/hotspot/jtreg/compiler/loopopts/TestPhaseCFGNeverBranchToGotoMain.java line 38:

> 36:  * @compile TestPhaseCFGNeverBranchToGoto.jasm
> 37:  * @summary Peeling of Irreducible loop can lead to NeverBranch being visited from either side
> 38:  * @run main/othervm -Xcomp -Xbatch -XX:-TieredCompilation -XX:PerMethodTrapLimit=0

Suggestion:

 * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:PerMethodTrapLimit=0

test/hotspot/jtreg/compiler/loopopts/TestPhaseCFGNeverBranchToGotoMain.java line 48:

> 46:         test(false, false);
> 47:     }
> 48:     public static void test(boolean flag1, boolean flag2) {

Suggestion:

    }

    public static void test(boolean flag1, boolean flag2) {

-------------

PR: https://git.openjdk.org/jdk/pull/11481


More information about the hotspot-compiler-dev mailing list