Skip to content

Commit efe46bb

Browse files
committed
8275610: C2: Object field load floats above its null check resulting in a segfault
Reviewed-by: phh Backport-of: 7c6f57fcb1f1fcecf26f7b8046a5a41ca6d9c315
1 parent 716ceb7 commit efe46bb

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

src/hotspot/share/opto/loopopts.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,16 @@ bool PhaseIdealLoop::identical_backtoback_ifs(Node *n) {
10951095
if (!n->in(0)->is_Region()) {
10961096
return false;
10971097
}
1098+
1099+
IfNode* n_if = n->as_If();
1100+
if (n_if->proj_out(0)->outcnt() > 1 || n_if->proj_out(1)->outcnt() > 1) {
1101+
// Removing the dominated If node by using the split-if optimization does not work if there are data dependencies.
1102+
// Some data dependencies depend on its immediate dominator If node and should not be separated from it (e.g. null
1103+
// checks, division by zero checks etc.). Bail out for now until data dependencies are correctly handled when
1104+
// optimizing back-to-back ifs.
1105+
return false;
1106+
}
1107+
10981108
Node* region = n->in(0);
10991109
Node* dom = idom(region);
11001110
if (!dom->is_If() || dom->in(1) != n->in(1)) {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
/*
26+
* @test
27+
* @key stress
28+
* @bug 8275610
29+
* @summary Null check for field access of object floats above null check resulting in a segfault.
30+
* @requires vm.compiler2.enabled
31+
* @run main/othervm -Xbatch -XX:CompileCommand=compileonly,compiler.loopopts.TestEliminateNullCheckWithSplitIf::test
32+
* -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM compiler.loopopts.TestEliminateNullCheckWithSplitIf
33+
*/
34+
35+
package compiler.loopopts;
36+
37+
public class TestEliminateNullCheckWithSplitIf {
38+
public static int[] iArrFld = new int[20];
39+
public static int[] iArrFld2 = new int[20];
40+
public static int iFld = 10;
41+
public static MyClass obj;
42+
43+
public static void main(String[] strArr) {
44+
for (int i = 0; i < 10000; i++) {
45+
obj = (i % 100 == 0 ? null : new MyClass());
46+
test();
47+
}
48+
}
49+
50+
// The field access obj.iFld requires a null check NC3 and adds a not-null CastPP node on the succeeded projection.
51+
// In the first IGVN after parsing, the null check NC3 can be subsumed by the explicit null check NC2.
52+
// (done in IfNode::simple_subsuming()). The Bool node of NC2 is also shared with the same null check NC1 earlier.
53+
// However, C2 cannot remove the null check NC2, yet, because the IR in between the two checks are too complex
54+
// (IfNode::search_identical() fails).
55+
// Now, loopopts are applied:
56+
// (1) First, the split if optimization is done. It recognizes that NC1 and NC2 are back to back null checks and removes
57+
// the null check NC2 by splitting it through the region R which is removed afterwards. In this process, control dependent
58+
// data nodes on the out projections of NC2 end up at the new regions R1/R2 created for each projection for R. They get
59+
// the last nodes of the if and else block as input. For this example, R1 is a control input to the CastPP node which
60+
// will merge both true projections.
61+
// (2) Later in loop opts, the loop L is transformed into normal code and y will become a constant 1.
62+
// After loopopts, another round of IGVN is done:
63+
// (These steps also depend on the order in which they are applied in order to trigger the bug)
64+
// (1) The region R is removed because one path is dead (a result of the split if optimization).
65+
// (2) The new If node added by the above split if optimization is also folded. This rewires the CastPP node to
66+
// the last control node in the If block which is the true projection of range check RC2. Up until now, the CastPP
67+
// is still after the null check NC1.
68+
// (3) The range check RC2 is removed because the range check RC1 already covers this range (see RangeCheck::Ideal()).
69+
// All data nodes which are control dependent on RC2 will be rewired to the dominating range check RC1, including
70+
// the non-null CastPP node - which now has a control input above the null check NC1. This also means that the field
71+
// load obj.iFld now has the same early control as the CastPP (CastPP -> AddP -> LoadI). Using StressGCM can
72+
// now schedule the obj.iFld load before the null check NC1 because the early control allows it which leads to a
73+
// segmentation fault if obj is null.
74+
public static void test() {
75+
int x = iArrFld[17]; // Emits range check RC1
76+
if (obj != null) { // Null check NC1
77+
int y = 0;
78+
for (int i = 0; i < 1; i++) { // Loop L
79+
y++;
80+
}
81+
// Use additional loop to keep the rangecheck for iArrFld[y] in before loopopts.
82+
// y will become constant 1 but only once the loop above is removed in loopopts.
83+
x = iArrFld[y]; // Emits range check RC2
84+
} else {
85+
x = iArrFld2[18];
86+
}
87+
// Region R merging the if and else paths above.
88+
if (obj != null) { // Null check NC2
89+
x = iArrFld2[obj.iFld]; // Emits Null check NC3 for obj.iFld
90+
}
91+
}
92+
}
93+
94+
class MyClass {
95+
int iFld;
96+
}
97+
98+
99+
100+

0 commit comments

Comments
 (0)