Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
11 changes: 3 additions & 8 deletions src/hotspot/share/ci/ciField.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ ciField::ciField(fieldDescriptor *fd) :
static bool trust_final_non_static_fields(ciInstanceKlass* holder) {
if (holder == nullptr)
return false;
// Explicit opt-in from system classes
if (holder->trust_final_fields())
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") ||
Expand All @@ -230,14 +233,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;
}

Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/ci/ciInstanceKlass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/hotspot/share/ci/ciInstanceKlass.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class ciInstanceKlass : public ciKlass {
bool _has_nonstatic_concrete_methods;
bool _is_hidden;
bool _is_record;
bool _trust_final_fields;
bool _has_trusted_loader;

ciFlags _flags;
Expand Down Expand Up @@ -203,6 +204,10 @@ class ciInstanceKlass : public ciKlass {
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);
Expand Down
9 changes: 9 additions & 0 deletions src/hotspot/share/classfile/classFileParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@ class AnnotationCollector : public ResourceObj{
_java_lang_Deprecated_for_removal,
_jdk_internal_vm_annotation_AOTSafeClassInitializer,
_method_AOTRuntimeSetup,
_jdk_internal_vm_annotation_TrustFinalFields,
_annotation_LIMIT
};
const Location _location;
Expand Down Expand Up @@ -1879,6 +1880,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
Expand Down Expand Up @@ -1993,6 +1999,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
Expand Down
5 changes: 1 addition & 4 deletions src/hotspot/share/classfile/vmSymbols.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;") \
Expand Down Expand Up @@ -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;") \
Expand Down
3 changes: 3 additions & 0 deletions src/hotspot/share/oops/instanceKlass.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,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; }
Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/oops/instanceKlassFlags.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/java.base/share/classes/java/util/Optional.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -62,6 +62,7 @@
* @since 1.8
*/
@jdk.internal.ValueBased
@TrustFinalFields
public final class Optional<T> {
/**
* Common instance for {@code empty()}.
Expand All @@ -71,7 +72,6 @@ public final class Optional<T> {
/**
* If non-null, the value; if null, indicates no value is present
*/
@Stable
private final T value;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
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;

/**
Expand Down Expand Up @@ -371,6 +373,7 @@ public final int accumulateAndGet(T obj, int x,
/**
* Standard hotspot implementation using intrinsics.
*/
@TrustFinalFields
private static final class AtomicIntegerFieldUpdaterImpl<T>
extends AtomicIntegerFieldUpdater<T> {
private static final Unsafe U = Unsafe.getUnsafe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
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;

/**
Expand Down Expand Up @@ -368,6 +370,7 @@ public final long accumulateAndGet(T obj, long x,
return next;
}

@TrustFinalFields
private static final class CASUpdater<T> extends AtomicLongFieldUpdater<T> {
private static final Unsafe U = Unsafe.getUnsafe();
private final long offset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
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;

/**
Expand Down Expand Up @@ -312,6 +314,7 @@ public final V accumulateAndGet(T obj, V x,
return next;
}

@TrustFinalFields
private static final class AtomicReferenceFieldUpdaterImpl<T,V>
extends AtomicReferenceFieldUpdater<T,V> {
private static final Unsafe U = Unsafe.getUnsafe();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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 user final field modifications.
///
/// See `constant-folding.md` design document in the same directory as this file
/// for an overview and the best practices around constant folding, including
/// for this annotation.
///
/// 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 {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
Constant Folding
===

Hotspot compiler can fold constant field value access when it constructs its IR
(intermediate representation), unlocking significant performance improvements.
However, it is implemented as a dangerous primitive that can lead to incorrect
programs if used incorrectly.

## What is constant folding?

Constant folding means a read of a variable of a constant value can be replaced
by the read constant value, during the construction of an IR graph. The
related logic resides in `ci/ciField.hpp` (compiler interface field).

For example, if a field `int a` has a constant value `4` and a field `int b` has
constant value `5`, the constant folding will replace the `a + b` in the IR with
a value of `9`.

## How is a value determined to be constant?

Constantness is decided on a per-variable and per-value basis. This includes
the location of the variable so that a variable might have a constant value,
and the value of the variable so the read value is constant.

### Field constants

Whether a field may have a constant value is determined by the
`ciField::is_constant()` method in Hotspot. The value of `_is_constant` is
determined in `ciField::initialize_from`. It is roughly as follows:

1. A field may be constant if it is stable.
2. Before Java 9, `putfield` in `<clinit>` could write to an instance final
field and `putstatic` in `<init>` could write to a static final field. Such
written final fields from pre-53-major classes are considered never constant.
3. Otherwise, if a static final field is not `System.in`, `System.out`, or
`System.err`, it may be constant.
4. If an instance final field comes from a record class, a hidden class, it may
be constant.
5. If an instance final field is declared in a system class that is either:

1. Marked `@TrustFinalFields`
2. In one of the trusted system packages specified in
`trust_final_non_static_fields` in `ciField.cpp`

It may be constant. Note that such a field is not protected from reflective
modification through core reflection `Field.set`.
6. If the field is `CallSite.target`, it may be constant. (This has extra
treatments like nmethod dependency, so is not quite as other constant
fields)

A `ciField` models a field declaration in the JVM, so an inherited field (as
in a field reference, static or instance) in a subclass or subinterface shares
the constantness settings.

After a field is considered to be possibly constant, its value is fetched from
the runtime and examined. If the field is stable, and the value is zero or null
(the default value), this read value is not constant. Only non-stable final
fields can have their zero or null values considered constant.

### Array constants

If an array field is stable, the type system in the compiler of Hotspot marks
the array to be stable up to its declared level. (See the code of the most
generic variant of `Type::make_constant_from_field`) As a result, access to
nested array components _with a constant index_ can be treated as a constant
value, if the read value is not zero or null (the default value).

This means the stable annotation is not as helpful for random access (it only
elides loading the array reference), and null components in an "immutable" array
may cause surprising slowdowns.

## How can I verify constant folding?

Since constant folding makes a huge difference in API performance characteristics,
tests are necessary to guarantee they happen.

The most reliable way to ensure folding is IR tests in the compiler; we can
expect compiler to eliminate known foldable IR structures when its inputs are
eligible. For example, in the initial constant folding example of `a + b`, an
IR test can verify the int addition is eliminated in the resulting IR by
constant folding.

An example test is `compiler.c2.irTests.constantFold.TestOptional`. Note that
IR tests need to be run in a debug (fastdebug) configure profile, which is not
used for most jdk library tests.

JMH benchmarks can be another way to verify, except they are costly to run and
their trend is hard to track. Prefer the compiler tests instead.

## Relation to final mechanisms

### `Field.trustedFinal` property

A `Field` object has a `trustedFinal` field, which when set to `true`, prevents
core reflection or method handles from creating a setter for this field in any
scenario. This is derived from `fieldDescriptor::is_trusted_final()`, which
designates final fields that are static, declared in a record class, or declared
in a hidden class as `trustedFinal`. This rule is different from the "may be
constant variable" rule from above; in particular, it does not protect the
instance final fields in eligible system classes per rule 5 above.

Note that a `Field` object also models a field declaration in the JVM like a
`ciField`, so an inherited field in a subclass or subinterface shares the
`trustedFinal` setting.
Loading