diff --git a/src/hotspot/share/ci/ciFlatArrayKlass.hpp b/src/hotspot/share/ci/ciFlatArrayKlass.hpp index 2a28743e783..df64fb5457f 100644 --- a/src/hotspot/share/ci/ciFlatArrayKlass.hpp +++ b/src/hotspot/share/ci/ciFlatArrayKlass.hpp @@ -26,6 +26,7 @@ #define SHARE_VM_CI_CIFLATARRAYKLASS_HPP #include "ci/ciArrayKlass.hpp" +#include "oops/flatArrayKlass.hpp" // ciFlatArrayKlass // @@ -42,8 +43,8 @@ class ciFlatArrayKlass : public ciArrayKlass { protected: ciFlatArrayKlass(Klass* h_k); - FlatArrayKlass* get_FlatArrayKlass() { - return (FlatArrayKlass*)get_Klass(); + const FlatArrayKlass* get_FlatArrayKlass() const { + return FlatArrayKlass::cast(get_Klass()); } const char* type_string() { return "ciFlatArrayKlass"; } @@ -52,6 +53,8 @@ class ciFlatArrayKlass : public ciArrayKlass { jobject loader_handle() { return _base_element_klass->loader_handle(); } public: + LayoutKind layout_kind() const { return get_FlatArrayKlass()->layout_kind(); } + // The one-level type of the array elements. ciKlass* element_klass(); diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 6021b885e0c..977a69f22ee 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -32,6 +32,7 @@ #include "gc/shared/tlab_globals.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" +#include "oops/flatArrayKlass.hpp" #include "oops/objArrayKlass.hpp" #include "opto/addnode.hpp" #include "opto/arraycopynode.hpp" @@ -2313,6 +2314,10 @@ const Type* LoadNode::Value(PhaseGVN* phase) const { assert(Opcode() == Op_LoadI, "must load an int from _super_check_offset"); return TypeInt::make(klass->super_check_offset()); } + if (klass->is_flat_array_klass() && tkls->offset() == in_bytes(FlatArrayKlass::layout_kind_offset())) { + assert(Opcode() == Op_LoadI, "must load an int from _layout_kind"); + return TypeInt::make(static_cast(klass->as_flat_array_klass()->layout_kind())); + } if (UseCompactObjectHeaders) { // TODO: Should EnableValhalla also take this path ? if (tkls->offset() == in_bytes(Klass::prototype_header_offset())) { // The field is Klass::_prototype_header. Return its (constant) value. diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java index 75d9b0da47a..a9fdf0ca4bc 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java @@ -3773,4 +3773,19 @@ public double test154() { public void test154_verifier() { Asserts.assertEquals(test154(), rD); } + + // When accessing into an array, we can speculate on the exact type of the array. If the + // speculative assumption holds, we can elide all checks on properties of the array (flatness, + // atomicity, nullability). + @Test + @IR(applyIf = {"UseArrayLoadStoreProfile", "true"}, + failOn = {IRNode.MEMBAR}, counts = {IRNode.IF, "3"}) // null check, class check, range check + static int test155(Test151Value[] a) { + return a[0].b; + } + + @Run(test = "test155") + public void test155_verifier() { + test155((Test151Value[])ValueClass.newNullRestrictedNonAtomicArray(Test151Value.class, 1, Test151Value.DEFAULT)); + } } diff --git a/test/micro/org/openjdk/bench/valhalla/array/read/HoistArrayChecks.java b/test/micro/org/openjdk/bench/valhalla/array/read/HoistArrayChecks.java new file mode 100644 index 00000000000..7e5fa1e7178 --- /dev/null +++ b/test/micro/org/openjdk/bench/valhalla/array/read/HoistArrayChecks.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2025, 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 org.openjdk.bench.valhalla.array.read; + +import java.util.concurrent.TimeUnit; +import jdk.internal.value.ValueClass; +import org.openjdk.jmh.annotations.*; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +public class HoistArrayChecks { + private static final int SIZE = 1_000_000; + + value record Point(byte x, byte y) { + static final Point DEFAULT = new Point((byte) 0, (byte) 0); + } + + Point[] nonAtomicFlatArray = (Point[]) ValueClass.newNullRestrictedNonAtomicArray(Point.class, SIZE, Point.DEFAULT); + Point[] atomicFlatArray = (Point[]) ValueClass.newNullRestrictedAtomicArray(Point.class, SIZE, Point.DEFAULT); + Point[] nullableFlatArray = (Point[]) ValueClass.newNullableAtomicArray(Point.class, SIZE); + + @Setup + public void setup() { + for (int i = 0; i < SIZE; i++) { + nullableFlatArray[i] = Point.DEFAULT; + } + } + + @Benchmark + public int nonAtomicNaive() { + int sum = 0; + for (int i = 0; i < SIZE; i++) { + Point p = nonAtomicFlatArray[i]; + sum += p.x + p.y; + } + return sum; + } + + @Benchmark + public int nonAtomicHoisted() { + Point[] array = nonAtomicFlatArray; + int sum = 0; + for (int i = 0; i < SIZE; i++) { + Point p = array[i]; + sum += p.x + p.y; + } + return sum; + } + + @Benchmark + public int atomicNaive() { + int sum = 0; + for (int i = 0; i < SIZE; i++) { + Point p = atomicFlatArray[i]; + sum += p.x + p.y; + } + return sum; + } + + @Benchmark + public int atomicHoisted() { + Point[] array = atomicFlatArray; + int sum = 0; + for (int i = 0; i < SIZE; i++) { + Point p = array[i]; + sum += p.x + p.y; + } + return sum; + } + + @Benchmark + public int nullableNaive() { + int sum = 0; + for (int i = 0; i < SIZE; i++) { + Point p = nullableFlatArray[i]; + sum += p.x + p.y; + } + return sum; + } + + @Benchmark + public int nullableHoisted() { + Point[] array = nullableFlatArray; + int sum = 0; + for (int i = 0; i < SIZE; i++) { + Point p = array[i]; + sum += p.x + p.y; + } + return sum; + } +}