Skip to content
Open
11 changes: 10 additions & 1 deletion src/hotspot/share/opto/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5162,6 +5162,8 @@ const TypeAryPtr* TypeAryPtr::cast_to_null_free(bool null_free) const {
if (res->speculative() == res->remove_speculative()) {
return res->remove_speculative();
}
assert(res->speculative() == nullptr || res->speculative()->with_inline_depth(res->inline_depth())->higher_equal(res->remove_speculative()),
"speculative type must not be narrower than non-speculative type");
return res;
}

Expand All @@ -5172,13 +5174,20 @@ const TypeAryPtr* TypeAryPtr::cast_to_not_null_free(bool not_null_free) const {
}
assert(!not_null_free || !is_null_free(), "inconsistency");
const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), is_flat(), is_not_flat(), not_null_free, is_atomic());
const TypePtr* new_spec = _speculative;
if (new_spec != nullptr) {
// Could be 'null free' from profiling, which would contradict the cast.
new_spec = new_spec->is_aryptr()->cast_to_null_free(false)->cast_to_not_null_free();
}
const TypeAryPtr* res = make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset,
_instance_id, _speculative, _inline_depth, _is_autobox_cache);
_instance_id, new_spec, _inline_depth, _is_autobox_cache);
// We keep the speculative part if it contains information about flat-/nullability.
// Make sure it's removed if it's not better than the non-speculative type anymore.
if (res->speculative() == res->remove_speculative()) {
return res->remove_speculative();
}
assert(res->speculative() == nullptr || res->speculative()->with_inline_depth(res->inline_depth())->higher_equal(res->remove_speculative()),
"speculative type must not be narrower than non-speculative type");
return res;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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.valhalla.inlinetypes;

import test.java.lang.invoke.lib.InstructionHelper;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

import jdk.internal.value.ValueClass;

import static compiler.valhalla.inlinetypes.InlineTypes.*;

/*
* @test
* @bug 8367624
* @summary Writing a null value to an inline type array casts the array to
* 'not null free'. If there is a speculative type before the cast,
* we have to make sure to cast it as well, otherwise we get a
* missed value optimization.
* @library /test/lib /test/jdk/java/lang/invoke/common /
* @enablePreview
* @modules java.base/jdk.internal.value
* java.base/jdk.internal.vm.annotation
* @build test.java.lang.invoke.lib.InstructionHelper
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+IgnoreUnrecognizedVMOptions
* -Xbatch -XX:PerMethodSpecTrapLimit=0 -XX:PerMethodTrapLimit=0
* -XX:VerifyIterativeGVN=1110 -XX:CompileCommand=compileonly,${test.main.class}::test
* ${test.main.class}
* @run main ${test.main.class}
*/

public class TestMissingOptCastSpeculative {
public static void main(String[] args) {
TestMissingOptCastSpeculative t = new TestMissingOptCastSpeculative();
MyValue1[] testValue1Array = (MyValue1[])ValueClass.newNullRestrictedNonAtomicArray(MyValue1.class, 3, MyValue1.DEFAULT);
for (int i = 0; i < 10000; i++) {
try {
t.test(testValue1Array, 0);
} catch (Throwable e) {}
}
}

// stores null at the specified index in the array
private static final MethodHandle setArrayElementNull = InstructionHelper.buildMethodHandle(MethodHandles.lookup(),
"setArrayElementNull",
MethodType.methodType(void.class, TestMissingOptCastSpeculative.class, MyValue1[].class, int.class),
CODE -> {
CODE.
aload(1).
iload(2).
aconst_null().
aastore().
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand well, it's doing va[index] = null and it's expected to throw? Maybe worth adding a little comment for people who don't enjoy assemblies as much as I do?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if we do va[index] = null in test the generated IR is a little different and we don't hit the assert. This is all extracted (and reduced) from TestLWorld.java. I have added a comment

return_();
});

private void test(MyValue1[] va, int index) throws Throwable {
setArrayElementNull.invoke(this, va, index);
}
}