Skip to content
Draft
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
16 changes: 16 additions & 0 deletions src/hotspot/share/opto/loopnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3155,6 +3155,13 @@ void CountedLoopConverter::TruncatedIncrement::build(Node* expr) {
n1 = t1->in(1);
n1op = n1->Opcode();
trunc_t = TypeInt::CHAR;
// TODO: fix bug
// Problem: we accept mask 0x7fff which produces range [0..32767].
// But we return CHAR, which will check for wrap in [0..65535].
// This leads to wrong results, because we ignore wrap.
// Matching 0x7fff is also a bit silly. I would assume users are
// more likely to want to use "(char)" cast truncation, which
// would lead to a 0xffff mask, with range [0..65535].
} else if (n1op == Op_RShiftI &&
n1->in(1) != nullptr &&
n1->in(1)->Opcode() == Op_LShiftI &&
Expand All @@ -3171,6 +3178,15 @@ void CountedLoopConverter::TruncatedIncrement::build(Node* expr) {
trunc_t = TypeInt::SHORT;
} else if (shift == 8) {
trunc_t = TypeInt::BYTE;
// TODO: maybe fix missing optimization
// shift == 8 means this pattern:
// ((x << 8) >> 8), which only removes the uppermost
// 8 bits, so leaves a 24-bit value. But we probably
// wanted to match ((x << 24) >> 24), which would be
// 8-bit signed truncation. Since we return BYTE, we
// will apply strict signed-bit overflow checks, so
// this just means we get missing optimizations, we
// don't actually manage to allow "(byte)" cast truncation.
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2026, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package compiler.loopopts;

/*
* @test
* @bug 8386591
* @summary Test case for TruncatedIncrement::build /
* CountedLoopConverter::has_truncation_wrap where we got wrong
* results, because we confused "& 0x7fff" as range [0..65535]
* instead of [0..32767].
* @library /test/lib /
* @run main/othervm -Xcomp
* -XX:-TieredCompilation
* -XX:CompileCommand=compileonly,${test.main.class}::test*
* ${test.main.class}
* @run main ${test.main.class}
*/

public class TestTruncationWrapBadCharWrap {
interface TestMethod {
int call();
}

public static void main(String[] args) {
int failures = 0;

failures += run("test1", () -> test1(), 1402);
failures += run("test2", () -> test2(), 2037);
failures += run("test3", () -> test3(), 171761184);

if (failures > 0) {
throw new RuntimeException("failures: " + failures);
}
}

static int run(String name, TestMethod t, int expected) {
for (int i = 0; i < 10_000; i++) {
int result = t.call();
if (result != expected) {
System.out.println(name + " wrong result: " + result + " vs " + expected);
return 1;
}
}
return 0;
}


static int test1() {
int sum = 0;
// The entry value is outside the range [0..32767], but inside [0..65535].
int i = (char)38405;
while (32 < i) {
sum++;
// Ignoring truncation would require values to be in range [0..32767].
// But unfortunately, we classify this as CHAR, and check for [0..65535].
i = (i - 4) & 0x7fff;
}
return sum;
}

static int test2() {
int sum = 0;
// We have 32767 - 32758 = 9 < 48, so the limit is too close to the wrap
// limit, and wrap is possible. But since 0x7fff gets mapped to CHAR,
// we accidentally check 65535 - 32758 < 48, and conclude wrap is not
// possible.
for (int i = 519; i < 32758; i = (i + 48) & 0x7fff) {
sum++;
}
return sum;
}

static int opaqueCounter;

static boolean opaqueCheck() {
return opaqueCounter++ > 10448;
}

static int test3() {
opaqueCounter = 0;
int sum = 0;
int i;
// Similar as with test2:
// We should check 32767 - 32766 = 1 < 50, so wrap possible. But we
// wrongly classify it as CHAR and check 65535 - 32766 < 50, and
// conclude there is no wrap.
for (i = 2046; i <= 32766; i = (i + 50) & 0x7fff) {
sum += i + 1;
if (opaqueCheck()) { break; }
}
return sum + i;
}
}