Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/hotspot/share/opto/compile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4161,10 +4161,10 @@ Node* Compile::conv_I2X_index(PhaseGVN* phase, Node* idx, const TypeInt* sizetyp
}

// Convert integer value to a narrowed long type dependent on ctrl (for example, a range check)
Node* Compile::constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl) {
Node* Compile::constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl, bool carry_dependency) {
if (ctrl != NULL) {
// Express control dependency by a CastII node with a narrow type.
value = new CastIINode(value, itype, false, true /* range check dependency */);
value = new CastIINode(value, itype, carry_dependency, true /* range check dependency */);
// Make the CastII node dependent on the control input to prevent the narrowed ConvI2L
// node from floating above the range check during loop optimizations. Otherwise, the
// ConvI2L node may be eliminated independently of the range check, causing the data path
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/opto/compile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1157,7 +1157,7 @@ class Compile : public Phase {
Node* ctrl = NULL);

// Convert integer value to a narrowed long type dependent on ctrl (for example, a range check)
static Node* constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl);
static Node* constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl, bool carry_dependency = false);

// Auxiliary methods for randomized fuzzing/stressing
int random();
Expand Down
10 changes: 8 additions & 2 deletions src/hotspot/share/opto/parse2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -863,10 +863,16 @@ bool Parse::create_jump_tables(Node* key_val, SwitchRange* lo, SwitchRange* hi)

// Clean the 32-bit int into a real 64-bit offset.
// Otherwise, the jint value 0 might turn into an offset of 0x0800000000.
const TypeInt* ikeytype = TypeInt::make(0, num_cases, Type::WidenMin);
// Make I2L conversion control dependent to prevent it from
// floating above the range check during loop optimizations.
key_val = C->conv_I2X_index(&_gvn, key_val, ikeytype, control());
// Do not use a narrow int type here to prevent the data path from dying
// while the control path is not removed. This can happen if the type of key_val
// is later known to be out of bounds of [0, num_cases] and therefore a narrow cast
// would be replaced by TOP while C2 is not able to fold the corresponding range checks.
// Set _carry_dependency for the cast to avoid being removed by IGVN.
#ifdef _LP64
key_val = C->constrained_convI2L(&_gvn, key_val, TypeInt::INT, control(), true /* carry_dependency */);
#endif

// Shift the value by wordsize so we have an index into the table, rather
// than a switch value
Expand Down
202 changes: 197 additions & 5 deletions test/hotspot/jtreg/compiler/c2/TestJumpTable.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -23,18 +23,24 @@

/**
* @test
* @bug 8229855
* @bug 8229855 8238812
* @summary Test jump table with key value that gets out of bounds after loop unrolling.
* @run main/othervm -XX:CompileCommand=dontinline,compiler.c2.TestJumpTable::test*
* -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:-UseSwitchProfiling
* compiler.c2.TestJumpTable
* @run main/othervm -XX:CompileCommand=dontinline,compiler.c2.TestJumpTable::test*
* -Xbatch -XX:-TieredCompilation -XX:-UseOnStackReplacement
* compiler.c2.TestJumpTable
* @run main/othervm -XX:CompileCommand=dontinline,compiler.c2.TestJumpTable::test*
* -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:+StressIGVN
* compiler.c2.TestJumpTable
*/

package compiler.c2;

public class TestJumpTable {

public static int test() {
public static int test0() {
int res = 0;
for (int i = 10; i < 50; ++i) {
switch (i * 5) {
Expand All @@ -53,9 +59,195 @@ public static int test() {
return res;
}

static int field;

// Original (slightly simplified) fuzzer generated test
public static void test1() {
int i4, i5 = 99, i6, i9 = 89;
for (i4 = 12; i4 < 365; i4++) {
for (i6 = 5; i6 > 1; i6--) {
switch ((i6 * 5) + 11) {
case 13:
case 19:
case 26:
case 31:
case 35:
case 41:
case 43:
case 61:
case 71:
case 83:
case 314:
i9 = i5;
break;
}
}
}
}

// This generates the following subgraph:
/*
// i: -10..4
if ((i+min_jint) u<= max_jint) { <- This is always true but not folded by C2
...
} else {
...
CastII(i-5, 0..45) <- Replaced by TOP because i-5 range is -15..-1 but still considered reachable by C2 although it is dead code
...
}
*/
public static void test2() {
for (int i = 5; i > -10; i--) {
switch (i) {
case 0:
case 4:
case 10:
case 20:
case 30:
case 40:
case 50:
case 100:
field = 42;
break;
}
}
}

// This generates the following subgraph:
/*
// i: -20..0
if (i != 0) {
// i: -20..-1
if (i < 0) { <- This is always true but not folded by C2
// Fall through
} else {
...
CastII(i-1, 0..4) <- Replaced by TOP because i-1 range is -21..-1 but still considered reachable by C2 although it is dead code
...
}
} else {
StoreI <- Due to this additional store on, IfNode::has_shared_region returns false and the fold compares optimization does not kick in
}
*/
public static void test3() {
for (int i = 5; i > -20; i -= 5) {
switch (i) {
case 0:
case 10:
case 20:
case 30:
case 40:
case 50:
case 60:
case 100:
field = 42;
break;
}
}
}

// This generates the following subgraph:
/*
// i: -20..0
if (i != 0) {
// i: -20..-1
if (i u< 101) { <- This is always false but not folded by C2 because CmpU is not handled
CastII(i-1, 0..49) <- Replaced by TOP because i-1 range is -21..-1 but still considered reachable by C2 although it is dead code
} else {
...
}
} else {
...
}
*/
public static void test4() {
int local = 0;
for (int i = 5; i > -20; i -= 5) {
switch (i) {
case 0:
case 10:
case 20:
case 30:
case 40:
case 50:
case 100:
local = 42;
break;
}
}
}

// This generates the following subgraph:
/*
// i: 0..20
if (i != 20) {
// i: 0..19
if ((i-20) u< 281) { <- This is always false but not folded by C2 because the two ifs compare different values
CastII(i-21, 0..49) <- Replaced by TOP because i-21 range is -21..-1 but still considered reachable by C2 although it is dead code
} else {
...
}
} else {
...
}
*/
public static void test5() {
int local;
for (int i = 25; i > 0; i -= 5) {
switch (i) {
case 20:
case 30:
case 40:
case 50:
case 60:
case 70:
case 300:
local = 42;
break;
}
}
}

// This generates the following subgraph:
/*
// i: 0..20
if ((i+10) != 30) {
// i: 0..19
if ((i-20) u< 271) { <- This is always false but not folded by C2 because the two ifs compare different values
CastII(i-21, 0..4) <- Replaced by TOP because i-21 range is -21..-1 but still considered reachable by C2 although it is dead code
} else {
...
}
} else {
...
}
*/
public static void test6() {
int local;
for (int i = 25; i > 0; i -= 5) {
switch (i + 10) {
case 30:
case 40:
case 50:
case 60:
case 70:
case 80:
case 300:
local = 42;
break;
}
}
}

public static void main(String[] args) {
for (int i = 0; i < 20_000; ++i) {
test();
for (int i = 0; i < 50_000; ++i) {
test0();
test1();
test2();
test3();
test4();
test5();
test6();
}
}
}