diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp index 19e05784f4d..e0c818f02fc 100644 --- a/src/hotspot/share/ci/ciField.cpp +++ b/src/hotspot/share/ci/ciField.cpp @@ -216,6 +216,10 @@ ciField::ciField(fieldDescriptor *fd) : static bool trust_final_non_static_fields(ciInstanceKlass* holder) { if (holder == nullptr) return false; + if (holder->trust_final_fields()) { + // Explicit opt-in from system classes + return true; + } // Even if general trusting is disabled, trust system-built closures in these packages. if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke") || holder->is_in_package("java/lang/reflect") || holder->is_in_package("jdk/internal/reflect") || @@ -230,14 +234,6 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) { // Trust final fields in records if (holder->is_record()) return true; - // Trust Atomic*FieldUpdaters: they are very important for performance, and make up one - // more reason not to use Unsafe, if their final fields are trusted. See more in JDK-8140483. - if (holder->name() == ciSymbols::java_util_concurrent_atomic_AtomicIntegerFieldUpdater_Impl() || - holder->name() == ciSymbols::java_util_concurrent_atomic_AtomicLongFieldUpdater_CASUpdater() || - holder->name() == ciSymbols::java_util_concurrent_atomic_AtomicLongFieldUpdater_LockedUpdater() || - holder->name() == ciSymbols::java_util_concurrent_atomic_AtomicReferenceFieldUpdater_Impl()) { - return true; - } return TrustFinalNonStaticFields; } diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp index 64b9acf9146..33bcabc4566 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.cpp +++ b/src/hotspot/share/ci/ciInstanceKlass.cpp @@ -65,6 +65,7 @@ ciInstanceKlass::ciInstanceKlass(Klass* k) : _has_nonstatic_concrete_methods = ik->has_nonstatic_concrete_methods(); _is_hidden = ik->is_hidden(); _is_record = ik->is_record(); + _trust_final_fields = ik->trust_final_fields(); _nonstatic_fields = nullptr; // initialized lazily by compute_nonstatic_fields: _has_injected_fields = -1; _implementor = nullptr; // we will fill these lazily diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp index a1b2d8dd12d..8ccf1fadfb7 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.hpp +++ b/src/hotspot/share/ci/ciInstanceKlass.hpp @@ -59,6 +59,7 @@ private: bool _has_nonstatic_concrete_methods; bool _is_hidden; bool _is_record; + bool _trust_final_fields; bool _has_trusted_loader; ciFlags _flags; @@ -207,6 +208,10 @@ public: return _is_record; } + bool trust_final_fields() const { + return _trust_final_fields; + } + ciInstanceKlass* get_canonical_holder(int offset); ciField* get_field_by_offset(int field_offset, bool is_static); ciField* get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static); diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index c9d9d3632b5..817d0c64d11 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -943,6 +943,7 @@ public: _java_lang_Deprecated_for_removal, _jdk_internal_vm_annotation_AOTSafeClassInitializer, _method_AOTRuntimeSetup, + _jdk_internal_vm_annotation_TrustFinalFields, _annotation_LIMIT }; const Location _location; @@ -1878,6 +1879,11 @@ AnnotationCollector::annotation_index(const ClassLoaderData* loader_data, if (!privileged) break; // only allow in privileged code return _field_Stable; } + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_TrustFinalFields_signature): { + if (_location != _in_class) break; // only allow for classes + if (!privileged) break; // only allow in privileged code + return _jdk_internal_vm_annotation_TrustFinalFields; + } case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Contended_signature): { if (_location != _in_field && _location != _in_class) { break; // only allow for fields and classes @@ -1992,6 +1998,9 @@ void ClassFileParser::ClassAnnotationCollector::apply_to(InstanceKlass* ik) { if (has_annotation(_jdk_internal_vm_annotation_AOTSafeClassInitializer)) { ik->set_has_aot_safe_initializer(); } + if (has_annotation(_jdk_internal_vm_annotation_TrustFinalFields)) { + ik->set_trust_final_fields(true); + } } #define MAX_ARGS_SIZE 255 diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 8388b98faae..79646f24d0e 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -245,10 +245,6 @@ class SerializeClosure; \ /* Concurrency support */ \ template(java_util_concurrent_locks_AbstractOwnableSynchronizer, "java/util/concurrent/locks/AbstractOwnableSynchronizer") \ - template(java_util_concurrent_atomic_AtomicIntegerFieldUpdater_Impl, "java/util/concurrent/atomic/AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl") \ - template(java_util_concurrent_atomic_AtomicLongFieldUpdater_CASUpdater, "java/util/concurrent/atomic/AtomicLongFieldUpdater$CASUpdater") \ - template(java_util_concurrent_atomic_AtomicLongFieldUpdater_LockedUpdater, "java/util/concurrent/atomic/AtomicLongFieldUpdater$LockedUpdater") \ - template(java_util_concurrent_atomic_AtomicReferenceFieldUpdater_Impl, "java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl") \ template(jdk_internal_vm_annotation_Contended_signature, "Ljdk/internal/vm/annotation/Contended;") \ template(jdk_internal_vm_annotation_ReservedStackAccess_signature, "Ljdk/internal/vm/annotation/ReservedStackAccess;") \ template(jdk_internal_ValueBased_signature, "Ljdk/internal/ValueBased;") \ @@ -302,6 +298,7 @@ class SerializeClosure; template(jdk_internal_misc_Scoped_signature, "Ljdk/internal/misc/ScopedMemoryAccess$Scoped;") \ template(jdk_internal_vm_annotation_IntrinsicCandidate_signature, "Ljdk/internal/vm/annotation/IntrinsicCandidate;") \ template(jdk_internal_vm_annotation_Stable_signature, "Ljdk/internal/vm/annotation/Stable;") \ + template(jdk_internal_vm_annotation_TrustFinalFields_signature, "Ljdk/internal/vm/annotation/TrustFinalFields;") \ \ template(jdk_internal_vm_annotation_ChangesCurrentThread_signature, "Ljdk/internal/vm/annotation/ChangesCurrentThread;") \ template(jdk_internal_vm_annotation_JvmtiHideEvents_signature, "Ljdk/internal/vm/annotation/JvmtiHideEvents;") \ diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 23a59d26093..e370a3b7a7c 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -353,6 +353,9 @@ class InstanceKlass: public Klass { int static_oop_field_count() const { return (int)_static_oop_field_count; } void set_static_oop_field_count(u2 size) { _static_oop_field_count = size; } + bool trust_final_fields() { return _misc_flags.trust_final_fields(); } + void set_trust_final_fields(bool value) { _misc_flags.set_trust_final_fields(value); } + // Java itable int itable_length() const { return _itable_len; } void set_itable_length(int len) { _itable_len = len; } diff --git a/src/hotspot/share/oops/instanceKlassFlags.hpp b/src/hotspot/share/oops/instanceKlassFlags.hpp index 5eebaab27d1..d576805b30f 100644 --- a/src/hotspot/share/oops/instanceKlassFlags.hpp +++ b/src/hotspot/share/oops/instanceKlassFlags.hpp @@ -54,6 +54,7 @@ class InstanceKlassFlags { flag(has_localvariable_table , 1 << 11) /* has localvariable information */ \ flag(has_miranda_methods , 1 << 12) /* True if this class has miranda methods in it's vtable */ \ flag(has_final_method , 1 << 13) /* True if klass has final method */ \ + flag(trust_final_fields , 1 << 14) /* All instance final fields in this class should be trusted */ \ /* end of list */ #define IK_FLAGS_ENUM_NAME(name, value) _misc_##name = value, diff --git a/src/java.base/share/classes/java/util/Optional.java b/src/java.base/share/classes/java/util/Optional.java index 3e577bd379c..3d0375c4354 100644 --- a/src/java.base/share/classes/java/util/Optional.java +++ b/src/java.base/share/classes/java/util/Optional.java @@ -25,7 +25,7 @@ package java.util; -import jdk.internal.vm.annotation.Stable; +import jdk.internal.vm.annotation.TrustFinalFields; import java.util.function.Consumer; import java.util.function.Function; @@ -62,6 +62,7 @@ import java.util.stream.Stream; * @since 1.8 */ @jdk.internal.ValueBased +@TrustFinalFields public final class Optional { /** * Common instance for {@code empty()}. @@ -71,7 +72,6 @@ public final class Optional { /** * If non-null, the value; if null, indicates no value is present */ - @Stable private final T value; /** diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java index 2250009e8f5..70acb8a0889 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -42,6 +42,8 @@ import java.util.function.IntUnaryOperator; import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.TrustFinalFields; + import java.lang.invoke.VarHandle; /** @@ -371,6 +373,7 @@ public abstract class AtomicIntegerFieldUpdater { /** * Standard hotspot implementation using intrinsics. */ + @TrustFinalFields private static final class AtomicIntegerFieldUpdaterImpl extends AtomicIntegerFieldUpdater { private static final Unsafe U = Unsafe.getUnsafe(); diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java index 5f0a666cb04..d3a3fe63d0f 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -42,6 +42,8 @@ import java.util.function.LongUnaryOperator; import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.TrustFinalFields; + import java.lang.invoke.VarHandle; /** @@ -368,6 +370,7 @@ public abstract class AtomicLongFieldUpdater { return next; } + @TrustFinalFields private static final class CASUpdater extends AtomicLongFieldUpdater { private static final Unsafe U = Unsafe.getUnsafe(); private final long offset; diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java index 4a758f77a47..3d47e8e323a 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -42,6 +42,8 @@ import java.util.function.UnaryOperator; import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.TrustFinalFields; + import java.lang.invoke.VarHandle; /** @@ -312,6 +314,7 @@ public abstract class AtomicReferenceFieldUpdater { return next; } + @TrustFinalFields private static final class AtomicReferenceFieldUpdaterImpl extends AtomicReferenceFieldUpdater { private static final Unsafe U = Unsafe.getUnsafe(); diff --git a/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java b/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java new file mode 100644 index 00000000000..a94f58159a2 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java @@ -0,0 +1,57 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 jdk.internal.vm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/// Indicates all instance final fields declared in the annotated class should +/// be trusted as constants by compilers in `ciField::is_constant`. +/// +/// The compiler already treats static final fields and instance final fields in +/// record classes and hidden classes as constant. All classes in select +/// packages (Defined in `trust_final_non_static_fields` in `ciField.cpp`) in +/// the boot class loader also have their instance final fields trusted. This +/// annotation is not necessary in these cases. +/// +/// The [Stable] annotation treats fields as constants once they are not the +/// zero or null value. In comparison, a non-stable final instance field +/// trusted by this annotation can treat zero and null values as constants. +/// +/// This annotation is suitable when constant treatment of final fields is +/// performance sensitive, yet package-wide final field constant treatment may +/// be at risk from final field modifications such as serialization. +/// +/// This annotation is only recognized on classes from the boot and platform +/// class loaders and is ignored elsewhere. +/// +/// @since 26 +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface TrustFinalFields { +} diff --git a/test/hotspot/jtreg/compiler/corelibs/OptionalFold.java b/test/hotspot/jtreg/compiler/corelibs/OptionalFold.java new file mode 100644 index 00000000000..0661a2e37e6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/corelibs/OptionalFold.java @@ -0,0 +1,63 @@ +/* + * 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 compiler.corelibs; + +import java.util.Optional; + +import compiler.lib.ir_framework.Check; +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.IRNode; +import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.TestFramework; + +/* + * @test + * @bug 8372696 + * @summary Verify constant folding for Optional, both present and absent + * @library /test/lib / + * @run driver ${test.main.class} + */ +public class OptionalFold { + + public static void main(String[] args) { + TestFramework.run(); + } + + // Ensure both present and empty values can fold + static final Optional ONE = Optional.of(5), TWO = Optional.empty(); + + @Test + @IR(failOn = {IRNode.ADD_I}) + public int testSum() { + return ONE.orElse(7) + TWO.orElse(12); + } + + @Check(test = "testSum") + public void checkTestSum(int res) { + if (res != 5 + 12) { + throw new RuntimeException("incorrect result: " + res); + } + } + +}