From fyang at openjdk.org Wed Oct 1 00:22:33 2025 From: fyang at openjdk.org (Fei Yang) Date: Wed, 1 Oct 2025 00:22:33 GMT Subject: RFR: 8367601: Remove held_monitor_count In-Reply-To: References: Message-ID: On Tue, 30 Sep 2025 09:43:51 GMT, Fredrik Bredberg wrote: > Since we have removed all other locking modes than lightweight locking (see: [JDK-8344261](https://bugs.openjdk.org/browse/JDK-8344261)), we no longer need: > - `_held_monitor_count` > - `_parent_held_monitor_count` > - `_jni_monitor_count` > > This PR removes them from shared code as well as from `X86`, `AArch64`, `PowerPC` and `RISC-V`. > They are not present in other platforms. > > Tested tier1-7 (on supported platforms) without seeing any problems that can be traced to this code change. > `PowerPC` and `RISC-V` has been sanity checked using QEMU. RISC-V part of the change seems fine to me. My local hs:tier1-hs:tier3 passed with fastdebug build. ------------- Marked as reviewed by fyang (Reviewer). PR Review: https://git.openjdk.org/jdk/pull/27570#pullrequestreview-3287111055 From fbredberg at openjdk.org Wed Oct 1 13:35:52 2025 From: fbredberg at openjdk.org (Fredrik Bredberg) Date: Wed, 1 Oct 2025 13:35:52 GMT Subject: RFR: 8367601: Remove held_monitor_count [v2] In-Reply-To: References: Message-ID: > Since we have removed all other locking modes than lightweight locking (see: [JDK-8344261](https://bugs.openjdk.org/browse/JDK-8344261)), we no longer need: > - `_held_monitor_count` > - `_parent_held_monitor_count` > - `_jni_monitor_count` > > This PR removes them from shared code as well as from `X86`, `AArch64`, `PowerPC` and `RISC-V`. > They are not present in other platforms. > > Tested tier1-7 (on supported platforms) without seeing any problems that can be traced to this code change. > `PowerPC` and `RISC-V` has been sanity checked using QEMU. Fredrik Bredberg has updated the pull request incrementally with one additional commit since the last revision: Update after review ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27570/files - new: https://git.openjdk.org/jdk/pull/27570/files/f05981b1..a3f09e85 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27570&range=01 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27570&range=00-01 Stats: 16 lines in 4 files changed: 0 ins; 6 del; 10 mod Patch: https://git.openjdk.org/jdk/pull/27570.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27570/head:pull/27570 PR: https://git.openjdk.org/jdk/pull/27570 From fbredberg at openjdk.org Wed Oct 1 13:39:56 2025 From: fbredberg at openjdk.org (Fredrik Bredberg) Date: Wed, 1 Oct 2025 13:39:56 GMT Subject: RFR: 8367601: Remove held_monitor_count In-Reply-To: References: Message-ID: On Tue, 30 Sep 2025 13:32:24 GMT, Martin Doerr wrote: >> Since we have removed all other locking modes than lightweight locking (see: [JDK-8344261](https://bugs.openjdk.org/browse/JDK-8344261)), we no longer need: >> - `_held_monitor_count` >> - `_parent_held_monitor_count` >> - `_jni_monitor_count` >> >> This PR removes them from shared code as well as from `X86`, `AArch64`, `PowerPC` and `RISC-V`. >> They are not present in other platforms. >> >> Tested tier1-7 (on supported platforms) without seeing any problems that can be traced to this code change. >> `PowerPC` and `RISC-V` has been sanity checked using QEMU. > > Looks correct (PPC64 and shared code changes) and tier1 has passed. Would be nice to clean up unused temp registers > > diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp > index 0bcc24a23bf..9fe7e1f22ff 100644 > --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp > +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp > @@ -1639,7 +1639,6 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, > assert_different_registers(reg_cont_obj, reg_flags); > Register zero = R8_ARG6; > Register tmp2 = R9_ARG7; > - Register tmp3 = R10_ARG8; > > DEBUG_ONLY(__ block_comment("fill {")); > #ifdef ASSERT > @@ -1678,7 +1677,6 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, > static void continuation_enter_cleanup(MacroAssembler* masm) { > Register tmp1 = R8_ARG6; > Register tmp2 = R9_ARG7; > - Register tmp3 = R10_ARG8; > > #ifdef ASSERT > __ block_comment("clean {"); > @@ -1689,8 +1687,8 @@ static void continuation_enter_cleanup(MacroAssembler* masm) { > > __ ld_ptr(tmp1, ContinuationEntry::parent_cont_fastpath_offset(), R1_SP); > __ st_ptr(tmp1, JavaThread::cont_fastpath_offset(), R16_thread); > - __ ld_ptr(tmp3, ContinuationEntry::parent_offset(), R1_SP); > - __ st_ptr(tmp3, JavaThread::cont_entry_offset(), R16_thread); > + __ ld_ptr(tmp2, ContinuationEntry::parent_offset(), R1_SP); > + __ st_ptr(tmp2, JavaThread::cont_entry_offset(), R16_thread); > DEBUG_ONLY(__ block_comment("} clean")); > } > > > Thanks for doing it for all platforms! @TheRealMDoerr > Would be nice to clean up unused temp registers Fixed those. > Thanks for doing it for all platforms! The same ways as eating different types of food enriches your life, so does programming for different CPUs. :) ------------- PR Comment: https://git.openjdk.org/jdk/pull/27570#issuecomment-3356372605 From fbredberg at openjdk.org Wed Oct 1 13:40:00 2025 From: fbredberg at openjdk.org (Fredrik Bredberg) Date: Wed, 1 Oct 2025 13:40:00 GMT Subject: RFR: 8367601: Remove held_monitor_count [v2] In-Reply-To: References: Message-ID: On Tue, 30 Sep 2025 15:51:14 GMT, Patricio Chilano Mateo wrote: >> Fredrik Bredberg has updated the pull request incrementally with one additional commit since the last revision: >> >> Update after review > > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1742: > >> 1740: log_develop_debug(continuations)("PINNED due to critical section"); >> 1741: verify_continuation(cont.continuation()); >> 1742: freeze_result res = entry->is_pinned() ? freeze_pinned_cs : freeze_pinned_monitor; > > We can remove this and always return freeze_pinned_cs. We should remove freeze_pinned_monitor (there is a matching definition in Continuation.java). Fixed ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27570#discussion_r2394622676 From fbredberg at openjdk.org Wed Oct 1 14:13:35 2025 From: fbredberg at openjdk.org (Fredrik Bredberg) Date: Wed, 1 Oct 2025 14:13:35 GMT Subject: RFR: 8367601: Remove held_monitor_count [v2] In-Reply-To: References: Message-ID: On Wed, 1 Oct 2025 14:01:57 GMT, Martin Doerr wrote: >> Fredrik Bredberg has updated the pull request incrementally with one additional commit since the last revision: >> >> Update after review > > I guess you want to update the Copyright headers eventually. @TheRealMDoerr > I guess you want to update the Copyright headers eventually. Think I updated all the years to 2025. `grep -i 'copyright' $(git diff --name-only master) | grep -i oracle` Did you see something I missed? ------------- PR Comment: https://git.openjdk.org/jdk/pull/27570#issuecomment-3356537891 From mdoerr at openjdk.org Wed Oct 1 14:20:13 2025 From: mdoerr at openjdk.org (Martin Doerr) Date: Wed, 1 Oct 2025 14:20:13 GMT Subject: RFR: 8367601: Remove held_monitor_count [v2] In-Reply-To: References: Message-ID: <2HZuTvrbG5DED7c-r-MBu-itrFiIzJiy4B2WCjXxdiw=.cb10434a-3390-4c48-b27f-ad0ac7e6092f@github.com> On Wed, 1 Oct 2025 14:01:57 GMT, Martin Doerr wrote: >> Fredrik Bredberg has updated the pull request incrementally with one additional commit since the last revision: >> >> Update after review > > I guess you want to update the Copyright headers eventually. > @TheRealMDoerr > > > I guess you want to update the Copyright headers eventually. > > Think I updated all the years to 2025. `grep -i 'copyright' $(git diff --name-only master) | grep -i oracle` > > Did you see something I missed? I thought I had seen 2024 somewhere. But, I can't find it again. I guess I had looked at the wrong file. Your Copyright updates look fine. Sorry. ------------- PR Comment: https://git.openjdk.org/jdk/pull/27570#issuecomment-3356572661 From mdoerr at openjdk.org Wed Oct 1 14:04:52 2025 From: mdoerr at openjdk.org (Martin Doerr) Date: Wed, 1 Oct 2025 14:04:52 GMT Subject: RFR: 8367601: Remove held_monitor_count [v2] In-Reply-To: References: Message-ID: On Wed, 1 Oct 2025 13:35:52 GMT, Fredrik Bredberg wrote: >> Since we have removed all other locking modes than lightweight locking (see: [JDK-8344261](https://bugs.openjdk.org/browse/JDK-8344261)), we no longer need: >> - `_held_monitor_count` >> - `_parent_held_monitor_count` >> - `_jni_monitor_count` >> >> This PR removes them from shared code as well as from `X86`, `AArch64`, `PowerPC` and `RISC-V`. >> They are not present in other platforms. >> >> Tested tier1-7 (on supported platforms) without seeing any problems that can be traced to this code change. >> `PowerPC` and `RISC-V` has been sanity checked using QEMU. > > Fredrik Bredberg has updated the pull request incrementally with one additional commit since the last revision: > > Update after review PPC64 and shared code changes look good. Thanks! I guess you want to update the Copyright headers eventually. ------------- Marked as reviewed by mdoerr (Reviewer). PR Review: https://git.openjdk.org/jdk/pull/27570#pullrequestreview-3289324176 PR Comment: https://git.openjdk.org/jdk/pull/27570#issuecomment-3356501843 From pchilanomate at openjdk.org Wed Oct 1 16:10:30 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Wed, 1 Oct 2025 16:10:30 GMT Subject: RFR: 8367601: Remove held_monitor_count [v2] In-Reply-To: References: Message-ID: On Wed, 1 Oct 2025 13:35:52 GMT, Fredrik Bredberg wrote: >> Since we have removed all other locking modes than lightweight locking (see: [JDK-8344261](https://bugs.openjdk.org/browse/JDK-8344261)), we no longer need: >> - `_held_monitor_count` >> - `_parent_held_monitor_count` >> - `_jni_monitor_count` >> >> This PR removes them from shared code as well as from `X86`, `AArch64`, `PowerPC` and `RISC-V`. >> They are not present in other platforms. >> >> Tested tier1-7 (on supported platforms) without seeing any problems that can be traced to this code change. >> `PowerPC` and `RISC-V` has been sanity checked using QEMU. > > Fredrik Bredberg has updated the pull request incrementally with one additional commit since the last revision: > > Update after review Looks good to me, thanks! ------------- Marked as reviewed by pchilanomate (Reviewer). PR Review: https://git.openjdk.org/jdk/pull/27570#pullrequestreview-3289970713 From dlong at openjdk.org Thu Oct 2 00:54:54 2025 From: dlong at openjdk.org (Dean Long) Date: Thu, 2 Oct 2025 00:54:54 GMT Subject: RFR: 8366461: Remove obsolete method handle invoke logic [v4] In-Reply-To: References: <_pqvEs0LIlAc7RjFUwg-bpxS3D2v5U7c6In2sG8XLhQ=.57e3aead-6ac4-4a42-89d2-385d7e6ecedf@github.com> Message-ID: On Tue, 2 Sep 2025 21:09:27 GMT, Vladimir Ivanov wrote: >> Dean Long has updated the pull request with a new target base due to a merge or a rebase. The incremental webrev excludes the unrelated changes brought in by the merge/rebase. The pull request contains seven additional commits since the last revision: >> >> - Merge branch 'openjdk:master' into 8366461-mh-invoke >> - revert whitespace change >> - undo debug changes >> - cleanup >> - arm32 build >> - Merge branch 'openjdk:master' into 8366461-mh-invoke >> - first pass > > Nice cleanup! Looks good. @iwanowww , I think I need you to re-review the final version. ------------- PR Comment: https://git.openjdk.org/jdk/pull/27059#issuecomment-3358642300 From roland at openjdk.org Thu Oct 2 09:08:08 2025 From: roland at openjdk.org (Roland Westrelin) Date: Thu, 2 Oct 2025 09:08:08 GMT Subject: RFR: 8354282: C2: more crashes in compiled code because of dependency on removed range check CastIIs [v3] In-Reply-To: <7Y1VflHDgnEChBAv9bwWH5ayU-K9ngRa3BfPjgzzHP0=.61111d18-a22e-4cb5-9492-e50f5524ac08@github.com> References: <7Y1VflHDgnEChBAv9bwWH5ayU-K9ngRa3BfPjgzzHP0=.61111d18-a22e-4cb5-9492-e50f5524ac08@github.com> Message-ID: On Wed, 23 Apr 2025 10:56:51 GMT, Emanuel Peter wrote: >> Roland Westrelin has updated the pull request incrementally with three additional commits since the last revision: >> >> - review >> - infinite loop in gvn fix >> - renaming > > @rwestrel thanks for looking into this one! > > I have not yet deeply studied the PR, but am feeling some confusion about the naming. > > I think the `DependencyType` is really a good step into the right direction, it helps clean things up. > > I'm wondering if we should pick either `depends_only_on_test` or `pinned`, and use it everywhere consistently. Having both around as near synonymes (antonymes?) is a bit confusing for me. > > I'll look into the code more later. I pushed an update with the renaming suggested by @eme64 and an extra comment with example use cases ------------- PR Comment: https://git.openjdk.org/jdk/pull/24575#issuecomment-3360011105 From roland at openjdk.org Thu Oct 2 09:08:06 2025 From: roland at openjdk.org (Roland Westrelin) Date: Thu, 2 Oct 2025 09:08:06 GMT Subject: RFR: 8354282: C2: more crashes in compiled code because of dependency on removed range check CastIIs [v3] In-Reply-To: References: Message-ID: > This is a variant of 8332827. In 8332827, an array access becomes > dependent on a range check `CastII` for another array access. When, > after loop opts are over, that RC `CastII` was removed, the array > access could float and an out of bound access happened. With the fix > for 8332827, RC `CastII`s are no longer removed. > > With this one what happens is that some transformations applied after > loop opts are over widen the type of the RC `CastII`. As a result, the > type of the RC `CastII` is no longer narrower than that of its input, > the `CastII` is removed and the dependency is lost. > > There are 2 transformations that cause this to happen: > > - after loop opts are over, the type of the `CastII` nodes are widen > so nodes that have the same inputs but a slightly different type can > common. > > - When pushing a `CastII` through an `Add`, if of the type both inputs > of the `Add`s are non constant, then we end up widening the type > (the resulting `Add` has a type that's wider than that of the > initial `CastII`). > > There are already 3 types of `Cast` nodes depending on the > optimizations that are allowed. Either the `Cast` is floating > (`depends_only_test()` returns `true`) or pinned. Either the `Cast` > can be removed if it no longer narrows the type of its input or > not. We already have variants of the `CastII`: > > - if the Cast can float and be removed when it doesn't narrow the type > of its input. > > - if the Cast is pinned and be removed when it doesn't narrow the type > of its input. > > - if the Cast is pinned and can't be removed when it doesn't narrow > the type of its input. > > What we need here, I think, is the 4th combination: > > - if the Cast can float and can't be removed when it doesn't narrow > the type of its input. > > Anyway, things are becoming confusing with all these different > variants named in ways that don't always help figure out what > constraints one of them operate under. So I refactored this and that's > the biggest part of this change. The fix consists in marking `Cast` > nodes when their type is widen in a way that prevents them from being > optimized out. > > Tobias ran performance testing with a slightly different version of > this change and there was no regression. Roland Westrelin has updated the pull request incrementally with three additional commits since the last revision: - review - infinite loop in gvn fix - renaming ------------- Changes: - all: https://git.openjdk.org/jdk/pull/24575/files - new: https://git.openjdk.org/jdk/pull/24575/files/c509ef56..aff5894b Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=24575&range=02 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=24575&range=01-02 Stats: 61 lines in 10 files changed: 11 ins; 0 del; 50 mod Patch: https://git.openjdk.org/jdk/pull/24575.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/24575/head:pull/24575 PR: https://git.openjdk.org/jdk/pull/24575 From vlivanov at openjdk.org Thu Oct 2 20:52:48 2025 From: vlivanov at openjdk.org (Vladimir Ivanov) Date: Thu, 2 Oct 2025 20:52:48 GMT Subject: RFR: 8366461: Remove obsolete method handle invoke logic [v9] In-Reply-To: References: Message-ID: On Fri, 26 Sep 2025 20:07:36 GMT, Dean Long wrote: >> At one time, JSR292 support needed special logic to save and restore SP across method handle instrinsic calls, but that is no longer the case. The only platform that still does the save/restore is arm32, which is no longer necessary. The save/restore can be removed along with related APIs and logic. Note that the arm32 port is largely based on the x86 port, which stopped doing the save/restore in jdk9 ([JDK-8068945](https://bugs.openjdk.org/browse/JDK-8068945)). > > Dean Long has updated the pull request incrementally with one additional commit since the last revision: > > Update src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java > > Co-authored-by: Manuel H?ssig Still looks good. ------------- Marked as reviewed by vlivanov (Reviewer). PR Review: https://git.openjdk.org/jdk/pull/27059#pullrequestreview-3296355729 From dlong at openjdk.org Thu Oct 2 22:24:02 2025 From: dlong at openjdk.org (Dean Long) Date: Thu, 2 Oct 2025 22:24:02 GMT Subject: RFR: 8366461: Remove obsolete method handle invoke logic [v9] In-Reply-To: References: Message-ID: <9wbYkH5F7FQqsuagYmsja5H-_YOZSel32FHZA0Ofet0=.e3faece6-0131-43e1-ae22-e9f50d81588f@github.com> On Fri, 26 Sep 2025 20:07:36 GMT, Dean Long wrote: >> At one time, JSR292 support needed special logic to save and restore SP across method handle instrinsic calls, but that is no longer the case. The only platform that still does the save/restore is arm32, which is no longer necessary. The save/restore can be removed along with related APIs and logic. Note that the arm32 port is largely based on the x86 port, which stopped doing the save/restore in jdk9 ([JDK-8068945](https://bugs.openjdk.org/browse/JDK-8068945)). > > Dean Long has updated the pull request incrementally with one additional commit since the last revision: > > Update src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java > > Co-authored-by: Manuel H?ssig Thanks Vladimir for the re-review. ------------- PR Comment: https://git.openjdk.org/jdk/pull/27059#issuecomment-3363412878 From dlong at openjdk.org Thu Oct 2 22:24:03 2025 From: dlong at openjdk.org (Dean Long) Date: Thu, 2 Oct 2025 22:24:03 GMT Subject: Integrated: 8366461: Remove obsolete method handle invoke logic In-Reply-To: References: Message-ID: On Tue, 2 Sep 2025 19:21:48 GMT, Dean Long wrote: > At one time, JSR292 support needed special logic to save and restore SP across method handle instrinsic calls, but that is no longer the case. The only platform that still does the save/restore is arm32, which is no longer necessary. The save/restore can be removed along with related APIs and logic. Note that the arm32 port is largely based on the x86 port, which stopped doing the save/restore in jdk9 ([JDK-8068945](https://bugs.openjdk.org/browse/JDK-8068945)). This pull request has now been integrated. Changeset: da7121af Author: Dean Long URL: https://git.openjdk.org/jdk/commit/da7121aff9eccb046b82a75093034f1cdbd9b9e4 Stats: 899 lines in 76 files changed: 22 ins; 825 del; 52 mod 8366461: Remove obsolete method handle invoke logic Reviewed-by: vlivanov, mhaessig ------------- PR: https://git.openjdk.org/jdk/pull/27059 From sherman at openjdk.org Fri Oct 3 19:10:20 2025 From: sherman at openjdk.org (Xueming Shen) Date: Fri, 3 Oct 2025 19:10:20 GMT Subject: RFR: 8365675: Add String Unicode Case-Folding Support Message-ID: ### Summary Case folding is a key operation for case-insensitive matching (e.g., string equality, regex matching), where the goal is to eliminate case distinctions without applying locale or language specific conversions. Currently, the JDK does not expose a direct API for Unicode-compliant case folding. Developers now rely on methods such as: **String.equalsIgnoreCase(String)** - Unicode-aware, locale-independent. - Implementation uses Character.toLowerCase(Character.toUpperCase(int)) per code point. - Limited: does not support 1:M mapping defined in Unicode case folding. **Character.toLowerCase(int) / Character.toUpperCase(int)** - Locale-independent, single code point only. - No support for 1:M mappings. **String.toLowerCase(Locale.ROOT) / String.toUpperCase(Locale.ROOT)** - Based on Unicode SpecialCasing.txt, supports 1:M mappings. - Intended primarily for presentation/display, not structural case-insensitive matching. - Requires full string conversion before comparison, which is less efficient and not intended for structural matching. **1:M mapping example, U+00DF (?)** - String.toUpperCase(Locale.ROOT, "?") ? "SS" - Case folding produces "ss", matching Unicode caseless comparison rules. jshell> "\u00df".equalsIgnoreCase("ss") $22 ==> false jshell> "\u00df".toUpperCase(Locale.ROOT).toLowerCase(Locale.ROOT).equals("ss") $24 ==> true ### Motivation & Direction Add Unicode standard-compliant case-less comparison methods to the String class, enabling & improving reliable and efficient Unicode-aware/compliant case-insensitive matching. - Unicode-compliant **full** case folding. - Simpler, stable and more efficient case-less matching without workarounds. - Brings Java's string comparison handling in line with other programming languages/libraries. This PR proposes to introduce the following comparison methods in `String` class - boolean equalsFoldCase(String anotherString) - int compareToFoldCase(String anotherString) - Comparator UNICODE_CASEFOLD_ORDER These methods are intended to be the preferred choice when Unicode-compliant case-less matching is required. *Note: An early draft also proposed a String.toCaseFold() method returning a new case-folded string. However, during review this was considered error-prone, as the resulting string could easily be mistaken for a general transformation like toLowerCase() and then passed into APIs where case-folding semantics are not appropriate. ### The New API /** * Compares this {@code String} to another {@code String} for equality, * using Unicode case folding. Two strings are considered equal * by this method if their case-folded forms are identical. *

* Case folding is defined by the Unicode Standard in * CaseFolding.txt, * including 1:M mappings. For example, {@code "Ma?e".equalsFoldCase("MASSE")} * returns {@code true}, since the character {@code U+00DF} (sharp s) folds * to {@code "ss"}. *

* Case folding is locale-independent and language-neutral, unlike * locale-sensitive transformations such as {@link #toLowerCase()} or * {@link #toUpperCase()}. It is intended for caseless matching, * searching, and indexing. * * @apiNote * This method is the Unicode-compliant alternative to * {@link #equalsIgnoreCase(String)}. It implements full case folding as * defined by the Unicode Standard, which may differ from the simpler * per-character mapping performed by {@code equalsIgnoreCase}. * For example: *

{@snippet lang=java :
     * String a = "Ma?e";
     * String b = "MASSE";
     * boolean equalsFoldCase = a.equalsFoldCase(b);       // returns true
     * boolean equalsIgnoreCase = a.equalsIgnoreCase(b);   // returns false
     * }
* * @param anotherString * The {@code String} to compare this {@code String} against * * @return {@code true} if the given object is not {@code null} and represents * the same sequence of characters as this string under Unicode case * folding; {@code false} otherwise. * * @see #compareToFoldCase(String) * @see #equalsIgnoreCase(String) * @since 26 */ public boolean equalsFoldCase(String anotherString) /** * Compares two strings lexicographically using Unicode case folding. * This method returns an integer whose sign is that of calling {@code compareTo} * on the Unicode case folded version of the strings. Unicode Case folding * eliminates differences in case according to the Unicode Standard, using the * mappings defined in * CaseFolding.txt, * including 1:M mappings, such as {@code"?"} ? {@code }"ss"}. *

* Case folding is a locale-independent, language-neutral form of case mapping, * primarily intended for caseless matching. Unlike {@link #compareToIgnoreCase(String)}, * which applies a simpler locale-insensitive uppercase mapping. This method * follows the Unicode full case folding, providing stable and * consistent results across all environments. *

* Note that this method does not take locale into account, and may * produce results that differ from locale-sensitive ordering. Use * {@link java.text.Collator} for locale-sensitive comparison. * * @apiNote * This method is the Unicode-compliant alternative to * {@link #compareToIgnoreCase(String)}. It implements the full case folding * as defined by the Unicode Standard, which may differ from the simpler * per-character mapping performed by {@code compareToIgnoreCase}. * For example: *

{@snippet lang=java :
     * String a = "Ma?e";
     * String b = "MASSE";
     * int cmpFoldCase = a.compareToFoldCase(b);     // returns 0
     * int cmpIgnoreCase = a.compareToIgnoreCase(b); // returns > 0
     * }
* * @param str the {@code String} to be compared. * @return a negative integer, zero, or a positive integer as the specified * String is greater than, equal to, or less than this String, * ignoring case considerations by case folding. * @see #equalsFoldCase(String) * @see #compareToIgnoreCase(String) * @see java.text.Collator * @since 26 */ public int compareToFoldCase(String str) /** * A Comparator that orders {@code String} objects as by * {@link #compareToFoldCase(String) compareToFoldCase()}. * * @see #compareToFoldCase(String) * @since 26 */ public static final Comparator UNICODE_CASEFOLD_ORDER; ### Usage Examples Sharp s (U+00DF) case-folds to "ss" "stra?e".equalsIgnoreCase("strasse"); // false "stra?e".compareToIgnoreCase("strasse"); // != 0 "stra?e".equalsFoldCase("strasse"); // true ### Performance The JMH microbenchmark StringCompareToIgnoreCase has been updated to compare performance of compareToFoldCase with the existing compareToIgnoreCase(). Benchmark Mode Cnt Score Error Units StringCompareToIgnoreCase.asciiGreekLower avgt 15 20.195 ? 0.300 ns/op StringCompareToIgnoreCase.asciiGreekLowerCF avgt 15 11.051 ? 0.254 ns/op StringCompareToIgnoreCase.asciiGreekUpperLower avgt 15 6.035 ? 0.047 ns/op StringCompareToIgnoreCase.asciiGreekUpperLowerCF avgt 15 14.786 ? 0.382 ns/op StringCompareToIgnoreCase.asciiLower avgt 15 17.688 ? 1.396 ns/op StringCompareToIgnoreCase.asciiLowerCF avgt 15 44.552 ? 0.155 ns/op StringCompareToIgnoreCase.asciiUpperLower avgt 15 13.069 ? 0.487 ns/op StringCompareToIgnoreCase.asciiUpperLowerCF avgt 15 58.684 ? 0.274 ns/op StringCompareToIgnoreCase.greekLower avgt 15 20.642 ? 0.082 ns/op StringCompareToIgnoreCase.greekLowerCF avgt 15 7.255 ? 0.271 ns/op StringCompareToIgnoreCase.greekUpperLower avgt 15 5.737 ? 0.013 ns/op StringCompareToIgnoreCase.greekUpperLowerCF avgt 15 11.100 ? 1.147 ns/op StringCompareToIgnoreCase.lower avgt 15 20.192 ? 0.044 ns/op StringCompareToIgnoreCase.lowerrCF avgt 15 11.257 ? 0.259 ns/op StringCompareToIgnoreCase.supLower avgt 15 54.801 ? 0.415 ns/op StringCompareToIgnoreCase.supLowerCF avgt 15 15.207 ? 0.418 ns/op StringCompareToIgnoreCase.supUpperLower avgt 15 14.431 ? 0.188 ns/op StringCompareToIgnoreCase.supUpperLowerCF avgt 15 19.149 ? 0.985 ns/op StringCompareToIgnoreCase.upperLower avgt 15 5.650 ? 0.051 ns/op StringCompareToIgnoreCase.upperLowerCF avgt 15 14.338 ? 0.352 ns/op StringCompareToIgnoreCase.utf16SubLower avgt 15 14.774 ? 0.200 ns/op StringCompareToIgnoreCase.utf16SubLowerCF avgt 15 2.669 ? 0.041 ns/op StringCompareToIgnoreCase.utf16SupUpperLower avgt 15 16.250 ? 0.099 ns/op StringCompareToIgnoreCase.utf16SupUpperLowerCF avgt 15 11.524 ? 0.327 ns/op ### Refs [Unicode Standard 5.18.4 Caseless Matching](https://www.unicode.org/versions/latest/core-spec/chapter-5/#G21790) [Unicode? Standard Annex #44: 5.6 Case and Case Mapping](https://www.unicode.org/reports/tr44/#Casemapping) [Unicode Technical Standard #18: Unicode Regular Expressions RL1.5: Simple Loose Matches](https://www.unicode.org/reports/tr18/#Simple_Loose_Matches) [Unicode SpecialCasing.txt](https://www.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt) [Unicode CaseFolding.txt](https://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt) ### Other Languages **Python string.casefold()** The str.casefold() method in Python returns a casefolded version of a string. Casefolding is a more aggressive form of lowercasing, designed to remove all case distinctions in a string, particularly for the purpose of caseless string comparisons. **Perl?s fc()** Returns the casefolded version of EXPR. This is the internal function implementing the \F escape in double-quoted strings. Casefolding is the process of mapping strings to a form where case differences are erased; comparing two strings in their casefolded form is effectively a way of asking if two strings are equal, regardless of case. Perl only implements the full form of casefolding, but you can access the simple folds using "casefold()" in Unicode::UCD] ad "prop_invmap()" in Unicode::UCD]. **ICU4J UCharacter.foldCase (Java)** Purpose: Provides extensions to the standard Java Character class, including support for more Unicode properties and handling of supplementary characters (code points beyond U+FFFF). Method Signature (String based): public static String foldCase(String str, int options) Method Signature (CharSequence & Appendable based): public static A foldCase(CharSequence src, A dest, int options, Edits edits) Key Features: Case Folding: Converts a string to its case-folded equivalent. Locale Independent: Case folding in UCharacter.foldCase is generally not dependent on locale settings. Context Insensitive: The mapping of a character is not affected by surrounding characters. Turkic Option: An option exists to include or exclude special mappings for Turkish/Azerbaijani text. Result Length: The resulting string can be longer or shorter than the original. Edits Recording: Allows for recording of edits for index mapping, styled text, and getting only changes. **u_strFoldCase (C/C++)** A lower-level C API function for case folding a string. Case Folding Options: Similar options as UCharacter.foldCase for controlling case folding behavior. Availability: Found in the ustring.h and unistr.h headers in the ICU4C library. ------------- Commit messages: - 8365675: Add String Unicode Case-Folding Support Changes: https://git.openjdk.org/jdk/pull/26892/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=26892&range=00 Issue: https://bugs.openjdk.org/browse/JDK-8365675 Stats: 1279 lines in 12 files changed: 1116 ins; 137 del; 26 mod Patch: https://git.openjdk.org/jdk/pull/26892.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/26892/head:pull/26892 PR: https://git.openjdk.org/jdk/pull/26892 From fbredberg at openjdk.org Mon Oct 6 08:13:03 2025 From: fbredberg at openjdk.org (Fredrik Bredberg) Date: Mon, 6 Oct 2025 08:13:03 GMT Subject: RFR: 8367601: Remove held_monitor_count [v2] In-Reply-To: References: Message-ID: On Wed, 1 Oct 2025 13:35:52 GMT, Fredrik Bredberg wrote: >> Since we have removed all other locking modes than lightweight locking (see: [JDK-8344261](https://bugs.openjdk.org/browse/JDK-8344261)), we no longer need: >> - `_held_monitor_count` >> - `_parent_held_monitor_count` >> - `_jni_monitor_count` >> >> This PR removes them from shared code as well as from `X86`, `AArch64`, `PowerPC` and `RISC-V`. >> They are not present in other platforms. >> >> Tested tier1-7 (on supported platforms) without seeing any problems that can be traced to this code change. >> `PowerPC` and `RISC-V` has been sanity checked using QEMU. > > Fredrik Bredberg has updated the pull request incrementally with one additional commit since the last revision: > > Update after review Thank you all for the reviews. Now let's... ------------- PR Comment: https://git.openjdk.org/jdk/pull/27570#issuecomment-3370402267 From fbredberg at openjdk.org Mon Oct 6 08:13:04 2025 From: fbredberg at openjdk.org (Fredrik Bredberg) Date: Mon, 6 Oct 2025 08:13:04 GMT Subject: Integrated: 8367601: Remove held_monitor_count In-Reply-To: References: Message-ID: On Tue, 30 Sep 2025 09:43:51 GMT, Fredrik Bredberg wrote: > Since we have removed all other locking modes than lightweight locking (see: [JDK-8344261](https://bugs.openjdk.org/browse/JDK-8344261)), we no longer need: > - `_held_monitor_count` > - `_parent_held_monitor_count` > - `_jni_monitor_count` > > This PR removes them from shared code as well as from `X86`, `AArch64`, `PowerPC` and `RISC-V`. > They are not present in other platforms. > > Tested tier1-7 (on supported platforms) without seeing any problems that can be traced to this code change. > `PowerPC` and `RISC-V` has been sanity checked using QEMU. This pull request has now been integrated. Changeset: e6781fd9 Author: Fredrik Bredberg URL: https://git.openjdk.org/jdk/commit/e6781fd9497723a7baab38d6bfb958ba1b1c24ff Stats: 401 lines in 27 files changed: 0 ins; 384 del; 17 mod 8367601: Remove held_monitor_count Reviewed-by: mdoerr, pchilanomate, fyang ------------- PR: https://git.openjdk.org/jdk/pull/27570 From shade at openjdk.org Mon Oct 6 09:04:01 2025 From: shade at openjdk.org (Aleksey Shipilev) Date: Mon, 6 Oct 2025 09:04:01 GMT Subject: RFR: 8367601: Remove held_monitor_count [v2] In-Reply-To: References: Message-ID: On Wed, 1 Oct 2025 13:35:52 GMT, Fredrik Bredberg wrote: >> Since we have removed all other locking modes than lightweight locking (see: [JDK-8344261](https://bugs.openjdk.org/browse/JDK-8344261)), we no longer need: >> - `_held_monitor_count` >> - `_parent_held_monitor_count` >> - `_jni_monitor_count` >> >> This PR removes them from shared code as well as from `X86`, `AArch64`, `PowerPC` and `RISC-V`. >> They are not present in other platforms. >> >> Tested tier1-7 (on supported platforms) without seeing any problems that can be traced to this code change. >> `PowerPC` and `RISC-V` has been sanity checked using QEMU. > > Fredrik Bredberg has updated the pull request incrementally with one additional commit since the last revision: > > Update after review Good cleanup, thanks! ------------- Marked as reviewed by shade (Reviewer). PR Review: https://git.openjdk.org/jdk/pull/27570#pullrequestreview-3303476645 From duke at openjdk.org Tue Oct 7 05:49:48 2025 From: duke at openjdk.org (erifan) Date: Tue, 7 Oct 2025 05:49:48 GMT Subject: RFR: 8303762: Optimize vector slice operation with constant index using VPALIGNR instruction [v8] In-Reply-To: References: Message-ID: <_6err-oI7jnkN1zwTDpqBR4Gurfez_OdLJtveJYvORc=.53394d2b-2214-47e1-a87a-1590a356aaab@github.com> On Wed, 20 Aug 2025 10:11:47 GMT, Jatin Bhateja wrote: >> Patch optimizes Vector. slice operation with constant index using x86 ALIGNR instruction. >> It also adds a new hybrid call generator to facilitate lazy intrinsification or else perform procedural inlining to prevent call overhead and boxing penalties in case the fallback implementation expects to operate over vectors. The existing vector API-based slice implementation is now the fallback code that gets inlined in case intrinsification fails. >> >> Idea here is to add infrastructure support to enable intrinsification of fast path for selected vector APIs, else enable inlining of fall-back implementation if it's based on vector APIs. Existing call generators like PredictedCallGenerator, used to handle bi-morphic inlining, already make use of multiple call generators to handle hit/miss scenarios for a particular receiver type. The newly added hybrid call generator is lazy and called during incremental inlining optimization. It also relieves the inline expander to handle slow paths, which can easily be implemented library side (Java). >> >> Vector API jtreg tests pass at AVX level 2, remaining validation in progress. >> >> Performance numbers: >> >> >> System : 13th Gen Intel(R) Core(TM) i3-1315U >> >> Baseline: >> Benchmark (size) Mode Cnt Score Error Units >> VectorSliceBenchmark.byteVectorSliceWithConstantIndex1 1024 thrpt 2 9444.444 ops/ms >> VectorSliceBenchmark.byteVectorSliceWithConstantIndex2 1024 thrpt 2 10009.319 ops/ms >> VectorSliceBenchmark.byteVectorSliceWithVariableIndex 1024 thrpt 2 9081.926 ops/ms >> VectorSliceBenchmark.intVectorSliceWithConstantIndex1 1024 thrpt 2 6085.825 ops/ms >> VectorSliceBenchmark.intVectorSliceWithConstantIndex2 1024 thrpt 2 6505.378 ops/ms >> VectorSliceBenchmark.intVectorSliceWithVariableIndex 1024 thrpt 2 6204.489 ops/ms >> VectorSliceBenchmark.longVectorSliceWithConstantIndex1 1024 thrpt 2 1651.334 ops/ms >> VectorSliceBenchmark.longVectorSliceWithConstantIndex2 1024 thrpt 2 1642.784 ops/ms >> VectorSliceBenchmark.longVectorSliceWithVariableIndex 1024 thrpt 2 1474.808 ops/ms >> VectorSliceBenchmark.shortVectorSliceWithConstantIndex1 1024 thrpt 2 10399.394 ops/ms >> VectorSliceBenchmark.shortVectorSliceWithConstantIndex2 1024 thrpt 2 10502.894 ops/ms >> VectorSliceB... > > Jatin Bhateja has updated the pull request incrementally with one additional commit since the last revision: > > Update callGenerator.hpp copyright year @jatin-bhateja I have no further comments, great work. After this PR is merged, I will complete the backend optimization of the aarch64 part based on it. Thanks! src/hotspot/cpu/x86/x86.ad line 10770: > 10768: %} > 10769: > 10770: instruct vector_slice_const_origin_LT16B_reg(vec dst, vec src1, vec src2, immI origin) Suggestion: instruct vector_slice_const_origin_EQ16B_reg(vec dst, vec src1, vec src2, immI origin) Or Suggestion: instruct vector_slice_const_origin_16B_reg(vec dst, vec src1, vec src2, immI origin) ------------- PR Review: https://git.openjdk.org/jdk/pull/24104#pullrequestreview-3308445233 PR Review Comment: https://git.openjdk.org/jdk/pull/24104#discussion_r2409418070 From duke at openjdk.org Tue Oct 7 05:49:50 2025 From: duke at openjdk.org (erifan) Date: Tue, 7 Oct 2025 05:49:50 GMT Subject: RFR: 8303762: Optimize vector slice operation with constant index using VPALIGNR instruction [v8] In-Reply-To: References: Message-ID: <30EG_sC2od4Xwsibk4Uv1XW_XROt9OtbYSaEDwFmycY=.c2c59c3b-dbfe-4d52-a353-08b7f41bab1d@github.com> On Thu, 25 Sep 2025 08:52:09 GMT, erifan wrote: >> Jatin Bhateja has updated the pull request incrementally with one additional commit since the last revision: >> >> Update callGenerator.hpp copyright year > > test/hotspot/jtreg/compiler/vectorapi/TestSliceOptValueTransforms.java line 45: > >> 43: public static final VectorSpecies SSP = ShortVector.SPECIES_PREFERRED; >> 44: public static final VectorSpecies ISP = IntVector.SPECIES_PREFERRED; >> 45: public static final VectorSpecies LSP = LongVector.SPECIES_PREFERRED; > > The implementation supports floating point types, but why doesn't the test include fp types? It might be better to consider **partial cases**. I looked at the aarch64 situation and found that different implementations are needed for partial and non-partial cases. The test indices in `test/jdk/jdk/incubator/vector/` are randomly generated, so it might be better to test different vector species here. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/24104#discussion_r2409431981 From pchilanomate at openjdk.org Tue Oct 14 17:18:17 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Tue, 14 Oct 2025 17:18:17 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths Message-ID: If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. ### Summary of implementation The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). ### Notes `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `InterpreterMacroAssembler::call_VM_preemptable_helper` and generalized for calls that take more than one argument, and that can return oops and throw exceptions. Method `InterpreterMacroAssembler::call_VM_preemptable` is now a wrapper that calls the helper, following the pattern of `MacroAssembler::call_VM` and `MacroAssembler::call_VM_helper` methods. As with platform threads, a virtual thread preempted at `wait_uninterruptibly` that is interrupted will not throw IE, and will preserve the interrupted status. Member `_interruptible` was added to `ObjectWaiter` to differentiate this case against `Object.wait`. Also field `interruptableWait` was added to VirtualThread class, mainly to avoid an interrupted virtual thread in `wait_uninterruptibly` to keep looping and submitting the continuation to the scheduler queue until the class is waiting for is initialized. Currently (and still with this change), when the thread responsible for initializing a class finishes executing the class initializer, it will set the initialization lock to null so the object can be GC'ed. For platform threads blocked waiting on the initialization lock, the `Handle` in `InstanceKlass::initialize_impl` will still protect the object from being collected until the last thread exits the monitor. For preempted virtual threads though, that `Handle` would have already been destroyed. In order to protect the init_lock from being collected while there are still virtual threads using the associated `ObjectMonitor`, the first preempted virtual thread will put the oop in an `OopHandle` in the `ObjectMonitor` (see `ObjectMonitor::set_object_strong()`), which will be released later when the monitor is deflated. Preempting at `invokestatic` means the top frame in the `stackChunk` can now have the callee?s arguments at the top of the expression stack, which during gc, will need to be processed as part of that frame (no callee yet). Class `SmallRegisterMap` was therefore modified so that we now have two static instances, one where `include_argument_oops()` returns true and is used when processing the top frame on this case, and the regular one where it return false and it?s used everywhere else. Also, because `InterpretedArgumentOopFinder` calculates the address of oops as offsets from the top of the expression stack, we need to correct possible added alignment after the top frame is thawed, since we can safepoint while redoing the VM call. Class `AnchorMark` was added to deal with this. ### Testing The changes have been running in the Loom pipeline for several months now. They include new test `KlassInit.java` which exercises preemption on different class initialization cases. Also, the current patch has been run through mach5 tiers 1-8. I'll keep running tests periodically until integration time. ------------- Commit messages: - RISC-V support - Fix whitespaces - v1 Changes: https://git.openjdk.org/jdk/pull/27802/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=00 Issue: https://bugs.openjdk.org/browse/JDK-8369238 Stats: 1979 lines in 94 files changed: 1628 ins; 86 del; 265 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From mdoerr at openjdk.org Tue Oct 14 19:30:05 2025 From: mdoerr at openjdk.org (Martin Doerr) Date: Tue, 14 Oct 2025 19:30:05 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths In-Reply-To: References: Message-ID: On Tue, 14 Oct 2025 13:42:18 GMT, Patricio Chilano Mateo wrote: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... https://github.com/openjdk/jdk/pull/27676 has been integrated which requires resolution. ------------- PR Comment: https://git.openjdk.org/jdk/pull/27802#issuecomment-3403294086 From duke at openjdk.org Tue Oct 14 19:40:37 2025 From: duke at openjdk.org (Shawn M Emery) Date: Tue, 14 Oct 2025 19:40:37 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 Message-ID: General: ----------- i) This work is to replace the existing AES cipher under the Cryptix license. ii) The lookup tables are employed for performance, but also for operating in constant time. iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. Correctness: ----------------- The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass -intrinsics mode for: ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures iv) jdk_security_infra: passed, with 48 known failures v) tier1 and tier2: all 110257 tests pass Security: ----------- In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. Performance: ------------------ All AES related benchmarks have been executed against the new and original Cryptix code: micro:org.openjdk.bench.javax.crypto.AES micro:org.openjdk.bench.javax.crypto.full.AESBench micro:org.openjdk.bench.javax.crypto.full.AESExtraBench micro:org.openjdk.bench.javax.crypto.full.AESGCMBench micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: i) Default (no JVM options, non-intrinsics) mode: a) Encryption: the new code performed better for both architectures tested (x86: +7.6%, arm64: +3.5%) Analysis: the new code makes some optimizations in the last cipher round with the T2 lookup table that Cryptix may not, hence the better performance in non-intrinsics. b) Decryption: the new code performed mixed between architectures tested (x86: +8.3%, arm64: -1.1%) Analysis: the new code performs predominately better, except for decryption on arm64, which we believe is negligible and acceptable to have noticeably better performance with x86 decryption. ii) Default (no JVM options, intrinsics) mode: a) Encryption and Decryption: as expected, both the new code and Cryptix code performed similarly (within the error margins) Analysis: this is from the fact that the intrinsics related code has not changed with the new changes. iii) Interpreted-only (-Xint) mode: a) Encryption: the new code performed better than the Cryptix code for both architectures (x86: +0.6%, arm64: +6.0%). b) Decryption: the new code performed slightly worse than the Cryptix code for both architectures (x86: -3.3%, arm64: -2.4%). Analysis: the design of the new code was focused on instruction efficiency; eliminating unnecessary index variables, rolling out the rounds loop, and using no objects for round and inverse round transforms. This is especially noticeable in arm64 encryption, but we believe that decryption's slight drop in performance is negligible. iv) JIT compiler (-Xcomp) mode: a) Encryption: in this mode, performance is mixed performant between the two architectures tested (x86: +11.7%, arm64: +1.5%). b) Decryption: performance is decreases for both of the architectures tested (x86: -4.9%, arm64: -3.2%). Analysis: As with the no options results, we believe that the increase in performance for both architectures in encryption is most likely from the T2 gadgetry that we've implemented. We believe that the slight performance drop in decryption is negligible. In any case, the -Xcomp option is primarily used for debugging purposes, ergo we are not as concerned about this slight drop. Resource utilization: ---------------------------- The new AES code uses similar resources to that of the existing Cryptix code. Memory allocation has the following characteristics: i) Peak allocation for both Cryptix and the new code is only a fraction of a percentage point different for both the 1 cipher object and 10 cipher objects test. Analysis: We believe that this is negligible given the difference in the 20ms to 50ms window of peak allocation. ii) Total GC pause for Cryptix and the new code only differs by less than 5% for both the 1 object and 10 objects test. Analysis: This is acceptable behavior given that the benchmark performance for the new code is better overall. iii) Peak pre-GC allocation for Cryptix and the new code is only a fraction of a percent more for the new code in the 1 object case and is only 2% more for the 10 objects case. Analysis: These differences indicate ~500 bytes per object discrepancy between the Cryptix and new code, which is also negligible. ------------- Commit messages: - Implement the rest of the class name changes in intrinsics - 8326609: New AES implementation with updates specified in FIPS 197 Changes: https://git.openjdk.org/jdk/pull/27807/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=00 Issue: https://bugs.openjdk.org/browse/JDK-8326609 Stats: 2989 lines in 5 files changed: 1506 ins; 1473 del; 10 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Tue Oct 14 19:48:47 2025 From: duke at openjdk.org (Shawn M Emery) Date: Tue, 14 Oct 2025 19:48:47 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v2] In-Reply-To: References: Message-ID: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Add vmIntrinsics.hpp updates ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/1f742c1f..af0f9c4c Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=01 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=00-01 Stats: 2 lines in 1 file changed: 0 ins; 0 del; 2 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From vlivanov at openjdk.org Tue Oct 14 20:07:13 2025 From: vlivanov at openjdk.org (Vladimir Ivanov) Date: Tue, 14 Oct 2025 20:07:13 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v2] In-Reply-To: References: Message-ID: On Tue, 14 Oct 2025 19:48:47 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Add vmIntrinsics.hpp updates src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 43: > 41: * https://www.internationaljournalcorner.com/index.php/ijird_ojs/article/view/134688 > 42: */ > 43: public final class AESCrypt extends SymmetricCipher { Should the class be named `AES_Crypt` instead? src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1408: > 1406: */ > 1407: public void encryptBlock(byte[] plain, int pOff, byte[] cipher, int cOff) { > 1408: implEncryptBlock(plain, pOff, cipher, cOff); There are no bounds checks around intrinsic methods. Previous implementation has a comment stating that the checks are placed in caller code (for performance reasons) and declared the methods package-private. It makes sense either to introduce bounds checks here or keep the wrappers package-private. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2430292341 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2430306371 From pchilanomate at openjdk.org Tue Oct 14 20:23:33 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Tue, 14 Oct 2025 20:23:33 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v2] In-Reply-To: References: Message-ID: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains four commits: - Merge branch 'master' into JDK-8369238 - RISC-V support - Fix whitespaces - v1 ------------- Changes: https://git.openjdk.org/jdk/pull/27802/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=01 Stats: 1979 lines in 94 files changed: 1628 ins; 86 del; 265 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Tue Oct 14 20:23:34 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Tue, 14 Oct 2025 20:23:34 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths In-Reply-To: References: Message-ID: On Tue, 14 Oct 2025 19:27:40 GMT, Martin Doerr wrote: > #27676 has been integrated which requires resolution. > Thanks, merged with latest master. ------------- PR Comment: https://git.openjdk.org/jdk/pull/27802#issuecomment-3403481837 From duke at openjdk.org Wed Oct 15 05:28:31 2025 From: duke at openjdk.org (Shawn M Emery) Date: Wed, 15 Oct 2025 05:28:31 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v3] In-Reply-To: References: Message-ID: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with two additional commits since the last revision: - encryptBlock/decryptBlock methods set to package-private - Revert AESCrypt to AES_Crypt ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/af0f9c4c..07003719 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=02 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=01-02 Stats: 3 lines in 1 file changed: 0 ins; 0 del; 3 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Wed Oct 15 05:28:34 2025 From: duke at openjdk.org (Shawn M Emery) Date: Wed, 15 Oct 2025 05:28:34 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v2] In-Reply-To: References: Message-ID: On Tue, 14 Oct 2025 19:59:04 GMT, Vladimir Ivanov wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Add vmIntrinsics.hpp updates > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 43: > >> 41: * https://www.internationaljournalcorner.com/index.php/ijird_ojs/article/view/134688 >> 42: */ >> 43: public final class AESCrypt extends SymmetricCipher { > > Should the class be named `AES_Crypt` instead? Yes, you're right. I'm not sure how it reverted back to AESCrypt. Fixed. > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1408: > >> 1406: */ >> 1407: public void encryptBlock(byte[] plain, int pOff, byte[] cipher, int cOff) { >> 1408: implEncryptBlock(plain, pOff, cipher, cOff); > > There are no bounds checks around intrinsic methods. Previous implementation has a comment stating that the checks are placed in caller code (for performance reasons) and declared the methods package-private. It makes sense either to introduce bounds checks here or keep the wrappers package-private. Good catch, I will leave it as package-private then. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2431157744 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2431158083 From duke at openjdk.org Wed Oct 15 05:51:40 2025 From: duke at openjdk.org (Shawn M Emery) Date: Wed, 15 Oct 2025 05:51:40 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v4] In-Reply-To: References: Message-ID: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Add remaining files to be staged ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/07003719..3fc25aef Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=03 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=02-03 Stats: 30 lines in 12 files changed: 0 ins; 0 del; 30 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From rrich at openjdk.org Wed Oct 15 17:06:10 2025 From: rrich at openjdk.org (Richard Reingruber) Date: Wed, 15 Oct 2025 17:06:10 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v2] In-Reply-To: References: Message-ID: On Tue, 14 Oct 2025 20:23:33 GMT, Patricio Chilano Mateo wrote: >> If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. >> >> As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. >> >> This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. >> >> ### Summary of implementation >> >> The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. >> >> If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). >> >> ### Notes >> >> `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::mon... > > Patricio Chilano Mateo has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains four commits: > > - Merge branch 'master' into JDK-8369238 > - RISC-V support > - Fix whitespaces > - v1 Great enhancement indeed @pchilano! The ppc part of it is almost finished. Unfortunately I'm stuck with a problem in verification code already in initial testing. Please see my comment on `verify_frame_kind`. src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1751: > 1749: RegisterMap::ProcessFrames::skip, > 1750: RegisterMap::WalkContinuation::skip); > 1751: frame fr = top.sender(®_map); I think there's a problem here. I get an assertion on ppc if `top` is a heap frame (calling from `log_preempt_after_freeze`) because in `frame::sender_raw()` we don't take the path we normally would for a frame on heap. Instead `sender_for_compiled_frame()` is called which uses a constructor that asserts alignment of `sp` (see [here](https://github.com/openjdk/jdk/blob/1bd814c3b24eb7ef5633ee34bb418e0981ca1708/src/hotspot/cpu/ppc/frame_ppc.inline.hpp#L81-L86)). The assertion fails because `_on_heap` is false but should be `true`. I think in `sender_raw` `map->in_cont()` should return true if this frame is on heap. It's not quite easy to fix though since `top` can also be on stack. ------------- PR Review: https://git.openjdk.org/jdk/pull/27802#pullrequestreview-3341440114 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2433336330 From valeriep at openjdk.org Wed Oct 15 17:58:38 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Wed, 15 Oct 2025 17:58:38 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v4] In-Reply-To: References: Message-ID: On Wed, 15 Oct 2025 05:51:40 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Add remaining files to be staged src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 43: > 41: * https://www.internationaljournalcorner.com/index.php/ijird_ojs/article/view/134688 > 42: */ > 43: public final class AES_Crypt extends SymmetricCipher { This internal class does not need to be public? I'd assume it's only used within the same package? test/micro/org/openjdk/bench/javax/crypto/AESDecrypt.java line 53: > 51: public void setup() throws Exception { > 52: SecretKeySpec keySpec = new SecretKeySpec(new byte[]{-80, -103, -1, 68, -29, -94, 61, -52, 93, -59, -128, 105, 110, 88, 44, 105}, "AES"); > 53: IvParameterSpec iv = new IvParameterSpec(new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); Is the all-0s IV intentional? test/micro/org/openjdk/bench/javax/crypto/AESDecrypt.java line 82: > 80: public byte[] testUseAesIntrinsics() throws Exception { > 81: return cipher.doFinal(ct); > 82: } These 3 methods look same to me except for the method names? ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2433490853 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2433487319 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2433483607 From valeriep at openjdk.org Wed Oct 15 18:48:29 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Wed, 15 Oct 2025 18:48:29 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v4] In-Reply-To: References: Message-ID: <-QG-DjJMOsPrJDY4lSgr0jOrcwEH_40FmvRgt4CQW90=.df69e912-a5e1-4574-a70d-b30e855c1dcc@github.com> On Wed, 15 Oct 2025 05:51:40 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Add remaining files to be staged src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 55: > 53: > 54: private static final int AES_256_ROUNDS = 14; > 55: private static final int AES_256_NKEYS = 32; The `AES_XXX_NKEYS` constants (valued 16, 24, 32) are also defined in `AESConstants` class, maybe we can just refer to that class instead of duplicate the definition here? ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2433608974 From valeriep at openjdk.org Wed Oct 15 18:53:55 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Wed, 15 Oct 2025 18:53:55 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v4] In-Reply-To: <-QG-DjJMOsPrJDY4lSgr0jOrcwEH_40FmvRgt4CQW90=.df69e912-a5e1-4574-a70d-b30e855c1dcc@github.com> References: <-QG-DjJMOsPrJDY4lSgr0jOrcwEH_40FmvRgt4CQW90=.df69e912-a5e1-4574-a70d-b30e855c1dcc@github.com> Message-ID: <8Hb0rpkClUOY9-wId7h9oVsyZx6Q1BfCKKEshQ8u6PA=.c3a9339b-0567-47bb-a2f2-92836e1af493@github.com> On Wed, 15 Oct 2025 18:45:24 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Add remaining files to be staged > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 55: > >> 53: >> 54: private static final int AES_256_ROUNDS = 14; >> 55: private static final int AES_256_NKEYS = 32; > > The `AES_XXX_NKEYS` constants (valued 16, 24, 32) are also defined in `AESConstants` class, maybe we can just refer to that class instead of duplicate the definition here? Or, merge the values defined in `AESConstants` into this class. Either way is fine with me as long as no duplicated values. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2433622686 From pchilanomate at openjdk.org Wed Oct 15 19:35:50 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Wed, 15 Oct 2025 19:35:50 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v2] In-Reply-To: References: Message-ID: On Wed, 15 Oct 2025 16:54:42 GMT, Richard Reingruber wrote: >> Patricio Chilano Mateo has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains four commits: >> >> - Merge branch 'master' into JDK-8369238 >> - RISC-V support >> - Fix whitespaces >> - v1 > > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1751: > >> 1749: RegisterMap::ProcessFrames::skip, >> 1750: RegisterMap::WalkContinuation::skip); >> 1751: frame fr = top.sender(®_map); > > I think there's a problem here. I get an assertion on ppc if `top` is a heap frame (calling from `log_preempt_after_freeze`) because in `frame::sender_raw()` we don't take the path we normally would for a frame on heap. Instead `sender_for_compiled_frame()` is called which uses a constructor that asserts alignment of `sp` (see [here](https://github.com/openjdk/jdk/blob/1bd814c3b24eb7ef5633ee34bb418e0981ca1708/src/hotspot/cpu/ppc/frame_ppc.inline.hpp#L81-L86)). The assertion fails because `_on_heap` is false but should be `true`. > > I think in `sender_raw` `map->in_cont()` should return true if this frame is on heap. > > It's not quite easy to fix though since `top` can also be on stack. Right, it should be walked as a heap frame. Could you verify if this patch fixes the issue? diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 86c56fe583f..fb1f66c60f4 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -196,7 +196,7 @@ static bool do_verify_after_thaw(JavaThread* thread, stackChunkOop chunk, output static void log_frames(JavaThread* thread, bool dolog = true); static void log_frames_after_thaw(JavaThread* thread, ContinuationWrapper& cont, intptr_t* sp); static void print_frame_layout(const frame& f, bool callee_complete, outputStream* st = tty); -static void verify_frame_kind(const frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr = nullptr, const char** code_name_ptr = nullptr, int* bci_ptr = nullptr); +static void verify_frame_kind(frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr = nullptr, const char** code_name_ptr = nullptr, int* bci_ptr = nullptr, stackChunkOop chunk = nullptr); #define assert_pfl(p, ...) \ do { \ @@ -1723,7 +1723,7 @@ bool FreezeBase::check_valid_fast_path() { return true; } -static void verify_frame_kind(const frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr, const char** code_name_ptr, int* bci_ptr) { +static void verify_frame_kind(frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr, const char** code_name_ptr, int* bci_ptr, stackChunkOop chunk) { Method* m; const char* code_name; int bci; @@ -1747,7 +1747,13 @@ static void verify_frame_kind(const frame& top, Continuation::preempt_kind preem RegisterMap reg_map(current, RegisterMap::UpdateMap::skip, RegisterMap::ProcessFrames::skip, - RegisterMap::WalkContinuation::skip); + RegisterMap::WalkContinuation::include); + if (top.is_heap_frame()) { + assert(chunk != nullptr, ""); + reg_map.set_stack_chunk(chunk); + top = chunk->relativize(top); + top.set_frame_index(0); + } frame fr = top.sender(®_map); vframe* vf = vframe::new_vframe(&fr, ®_map, current); compiledVFrame* cvf = compiledVFrame::cast(vf); @@ -1803,7 +1809,7 @@ static void log_preempt_after_freeze(ContinuationWrapper& cont) { Method* m = nullptr; const char* code_name = nullptr; int bci = InvalidFrameStateBci; - verify_frame_kind(top_frame, pk, &m, &code_name, &bci); + verify_frame_kind(top_frame, pk, &m, &code_name, &bci, cont.tail()); assert(m != nullptr && code_name != nullptr && bci != InvalidFrameStateBci, "should be set"); ResourceMark rm(current); ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2433721003 From valeriep at openjdk.org Wed Oct 15 20:48:45 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Wed, 15 Oct 2025 20:48:45 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v4] In-Reply-To: References: Message-ID: On Wed, 15 Oct 2025 05:51:40 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Add remaining files to be staged src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 920: > 918: if (prevKey != null) { > 919: Arrays.fill(prevKey, (byte) 0); > 920: } Can be moved down to be right before `prevKey = key.clone()` call? This way, `sessionK` assignments are together and not separated by this call ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2433898660 From duke at openjdk.org Wed Oct 15 21:31:23 2025 From: duke at openjdk.org (Shawn M Emery) Date: Wed, 15 Oct 2025 21:31:23 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v4] In-Reply-To: References: Message-ID: <7faVZ8RHcws5sOoA1rfBrE3f7ON__bKvBCra2R3rLNU=.dfd5d4f3-7506-4d99-9ba5-ccb0b4ca0184@github.com> On Wed, 15 Oct 2025 17:54:37 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Add remaining files to be staged > > test/micro/org/openjdk/bench/javax/crypto/AESDecrypt.java line 53: > >> 51: public void setup() throws Exception { >> 52: SecretKeySpec keySpec = new SecretKeySpec(new byte[]{-80, -103, -1, 68, -29, -94, 61, -52, 93, -59, -128, 105, 110, 88, 44, 105}, "AES"); >> 53: IvParameterSpec iv = new IvParameterSpec(new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); > > Is the all-0s IV intentional? Yes, it's in keeping with the other benchmarks (e.g., test/micro/org/openjdk/bench/javax/crypto/AES.java). > test/micro/org/openjdk/bench/javax/crypto/AESDecrypt.java line 82: > >> 80: public byte[] testUseAesIntrinsics() throws Exception { >> 81: return cipher.doFinal(ct); >> 82: } > > These 3 methods look same to me except for the method names? The forked arguments will test different levels of optimizations: testBaseline: no optimizations testUseAes: AES optimizations testUseAesIntrinsics: AES machine instructions ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434006387 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434001817 From duke at openjdk.org Wed Oct 15 23:04:10 2025 From: duke at openjdk.org (Shawn M Emery) Date: Wed, 15 Oct 2025 23:04:10 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v5] In-Reply-To: References: Message-ID: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Updates for code review comments from @valeriepeng ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/3fc25aef..f48160cf Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=04 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=03-04 Stats: 31 lines in 1 file changed: 6 ins; 10 del; 15 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Wed Oct 15 23:04:11 2025 From: duke at openjdk.org (Shawn M Emery) Date: Wed, 15 Oct 2025 23:04:11 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v4] In-Reply-To: References: Message-ID: On Wed, 15 Oct 2025 17:55:58 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Add remaining files to be staged > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 43: > >> 41: * https://www.internationaljournalcorner.com/index.php/ijird_ojs/article/view/134688 >> 42: */ >> 43: public final class AES_Crypt extends SymmetricCipher { > > This internal class does not need to be public? I'd assume it's only used within the same package? You're right, it doesn't appear to be used externally. Fixed. > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 920: > >> 918: if (prevKey != null) { >> 919: Arrays.fill(prevKey, (byte) 0); >> 920: } > > Can be moved down to be right before `prevKey = key.clone()` call? This way, `sessionK` assignments are together and not separated by this call It can be and I agree. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434164829 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434165068 From duke at openjdk.org Wed Oct 15 23:04:13 2025 From: duke at openjdk.org (Shawn M Emery) Date: Wed, 15 Oct 2025 23:04:13 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v4] In-Reply-To: <8Hb0rpkClUOY9-wId7h9oVsyZx6Q1BfCKKEshQ8u6PA=.c3a9339b-0567-47bb-a2f2-92836e1af493@github.com> References: <-QG-DjJMOsPrJDY4lSgr0jOrcwEH_40FmvRgt4CQW90=.df69e912-a5e1-4574-a70d-b30e855c1dcc@github.com> <8Hb0rpkClUOY9-wId7h9oVsyZx6Q1BfCKKEshQ8u6PA=.c3a9339b-0567-47bb-a2f2-92836e1af493@github.com> Message-ID: On Wed, 15 Oct 2025 18:51:29 GMT, Valerie Peng wrote: >> src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 55: >> >>> 53: >>> 54: private static final int AES_256_ROUNDS = 14; >>> 55: private static final int AES_256_NKEYS = 32; >> >> The `AES_XXX_NKEYS` constants (valued 16, 24, 32) are also defined in `AESConstants` class, maybe we can just refer to that class instead of duplicate the definition here? > > Or, merge the values defined in `AESConstants` into this class. Either way is fine with me as long as no duplicated values. I've made the update that references the AESConstants to avoid duplication. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434164972 From valeriep at openjdk.org Wed Oct 15 23:31:03 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Wed, 15 Oct 2025 23:31:03 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v4] In-Reply-To: <7faVZ8RHcws5sOoA1rfBrE3f7ON__bKvBCra2R3rLNU=.dfd5d4f3-7506-4d99-9ba5-ccb0b4ca0184@github.com> References: <7faVZ8RHcws5sOoA1rfBrE3f7ON__bKvBCra2R3rLNU=.dfd5d4f3-7506-4d99-9ba5-ccb0b4ca0184@github.com> Message-ID: <-4wxYGqcPRXJYZEXd91_MPCBd491IFjd7TOnMP_HdxE=.060bb090-1084-4a3b-8315-2116e73e69df@github.com> On Wed, 15 Oct 2025 21:26:33 GMT, Shawn M Emery wrote: >> test/micro/org/openjdk/bench/javax/crypto/AESDecrypt.java line 82: >> >>> 80: public byte[] testUseAesIntrinsics() throws Exception { >>> 81: return cipher.doFinal(ct); >>> 82: } >> >> These 3 methods look same to me except for the method names? > > The forked arguments will test different levels of optimizations: > testBaseline: no optimizations > testUseAes: AES optimizations > testUseAesIntrinsics: AES machine instructions Ah, I see, some has "+" vs some uses "-". My eye sights are getting worse. (sigh) ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434206251 From valeriep at openjdk.org Thu Oct 16 00:21:12 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Thu, 16 Oct 2025 00:21:12 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v5] In-Reply-To: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> References: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> Message-ID: <5aAa7y5hs9zHT05sehzUaDmSXY31QDQPj_ioD9V9nK8=.78641e57-e2df-4f8b-9c0b-35a56fbea7db@github.com> On Wed, 15 Oct 2025 23:04:10 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 896: > 894: if (key.length == AESConstants.AES_KEYSIZES[0]) { > 895: rounds = AES_128_ROUNDS; > 896: nk = AESConstants.AES_KEYSIZES[0]/WB; Looks like we can get rid of `nk` as the `genRKeys(byte[])` method can calculate/derive it based on the specified `key` argument, i.e. `key.length >> 2` or `key.length / WB` ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434256434 From valeriep at openjdk.org Thu Oct 16 00:42:05 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Thu, 16 Oct 2025 00:42:05 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v5] In-Reply-To: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> References: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> Message-ID: On Wed, 15 Oct 2025 23:04:10 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 954: > 952: } > 953: w[i] = w[i - nk] ^ tmp; > 954: } Looks like most of these local variables can be removed? Since you are not changing the value of `len`, you can just use `WB`. `rW` is only used inside the if-block from line 944-948, so it can be declared on line 945. Line 946-948 can be merged on one line, e.g. `tmp = subByte(rW, SBOX) ^ RCON[(i / nk) - 1];` and no need for `subWord` and `g`. Same goes for line 950 and 951. Also, the value of `WB * (rounds + 1)` is used twice, this can be stored in a local variable say `wLen`, so it's only calculated once. Same goes for the `i * WB` value from line 937-940 ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434276514 From valeriep at openjdk.org Thu Oct 16 00:45:03 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Thu, 16 Oct 2025 00:45:03 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v5] In-Reply-To: References: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> Message-ID: <2Qva3E5Ux0iHeg_Xx_yDV06gkjVmtIQL0J1aL2oSkmE=.28ead96c-2f65-4501-b44d-97898d032d6a@github.com> On Thu, 16 Oct 2025 00:38:09 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Updates for code review comments from @valeriepeng > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 954: > >> 952: } >> 953: w[i] = w[i - nk] ^ tmp; >> 954: } > > Looks like most of these local variables can be removed? Since you are not changing the value of `len`, you can just use `WB`. `rW` is only used inside the if-block from line 944-948, so it can be declared on line 945. Line 946-948 can be merged on one line, e.g. `tmp = subByte(rW, SBOX) ^ RCON[(i / nk) - 1];` and no need for `subWord` and `g`. Same goes for line 950 and 951. > Also, the value of `WB * (rounds + 1)` is used twice, this can be stored in a local variable say `wLen`, so it's only calculated once. > Same goes for the `i * WB` value from line 937-940 On the second thought, instead of calculating `i * WB` value, You can use another local variable to store this index and increment it by 4 for each iteration. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434280527 From valeriep at openjdk.org Thu Oct 16 00:51:02 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Thu, 16 Oct 2025 00:51:02 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v5] In-Reply-To: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> References: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> Message-ID: On Wed, 15 Oct 2025 23:04:10 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 932: > 930: * @return w the cipher round keys. > 931: */ > 932: private int[] genRKeys(byte[] key, int nk) { nit: The spec names this "keyExpansion" and documents it under section 5.2. Could we include this in the method javadoc? Also, how about "genRoundKeys" or "doKeyExpansion" as method name? `nk` can be derived from `key` and maybe no need for extra argument? ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434286447 From valeriep at openjdk.org Thu Oct 16 00:54:10 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Thu, 16 Oct 2025 00:54:10 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v5] In-Reply-To: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> References: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> Message-ID: On Wed, 15 Oct 2025 23:04:10 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 932: > 930: * @return w the cipher round keys. > 931: */ > 932: private int[] genRKeys(byte[] key, int nk) { This method can be static? ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434289681 From duke at openjdk.org Thu Oct 16 04:04:23 2025 From: duke at openjdk.org (Shawn M Emery) Date: Thu, 16 Oct 2025 04:04:23 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v6] In-Reply-To: References: Message-ID: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Updates for code review comments from @valeriepeng ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/f48160cf..fbf2117f Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=05 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=04-05 Stats: 28 lines in 1 file changed: 1 ins; 10 del; 17 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Thu Oct 16 04:04:25 2025 From: duke at openjdk.org (Shawn M Emery) Date: Thu, 16 Oct 2025 04:04:25 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v5] In-Reply-To: <5aAa7y5hs9zHT05sehzUaDmSXY31QDQPj_ioD9V9nK8=.78641e57-e2df-4f8b-9c0b-35a56fbea7db@github.com> References: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> <5aAa7y5hs9zHT05sehzUaDmSXY31QDQPj_ioD9V9nK8=.78641e57-e2df-4f8b-9c0b-35a56fbea7db@github.com> Message-ID: On Thu, 16 Oct 2025 00:18:08 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Updates for code review comments from @valeriepeng > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 896: > >> 894: if (key.length == AESConstants.AES_KEYSIZES[0]) { >> 895: rounds = AES_128_ROUNDS; >> 896: nk = AESConstants.AES_KEYSIZES[0]/WB; > > Looks like we can get rid of `nk` as the `genRKeys(byte[])` method can calculate/derive it based on the specified `key` argument, i.e. `key.length >> 2` or `key.length / WB` Sounds reasonable. Fixed. > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 932: > >> 930: * @return w the cipher round keys. >> 931: */ >> 932: private int[] genRKeys(byte[] key, int nk) { > > nit: The spec names this "keyExpansion" and documents it under section 5.2. Could we include this in the method javadoc? Also, how about "genRoundKeys" or "doKeyExpansion" as method name? `nk` can be derived from `key` and maybe no need for extra argument? Actually I used Stallings' cryptography book specifically for the round key concepts, hence the variable name mismatch at least for 128 bit keys. But after your suggestions is does look more like FIPS 192-upd 1 so I will reference the section ;) Fixed. > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 932: > >> 930: * @return w the cipher round keys. >> 931: */ >> 932: private int[] genRKeys(byte[] key, int nk) { > > This method can be static if you pass the `rounds` value as a method argument. Yes and subWord() would also need to be static for this change. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434497086 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434497821 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434498001 From duke at openjdk.org Thu Oct 16 04:04:27 2025 From: duke at openjdk.org (Shawn M Emery) Date: Thu, 16 Oct 2025 04:04:27 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v5] In-Reply-To: <2Qva3E5Ux0iHeg_Xx_yDV06gkjVmtIQL0J1aL2oSkmE=.28ead96c-2f65-4501-b44d-97898d032d6a@github.com> References: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> <2Qva3E5Ux0iHeg_Xx_yDV06gkjVmtIQL0J1aL2oSkmE=.28ead96c-2f65-4501-b44d-97898d032d6a@github.com> Message-ID: On Thu, 16 Oct 2025 00:42:20 GMT, Valerie Peng wrote: >> src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 954: >> >>> 952: } >>> 953: w[i] = w[i - nk] ^ tmp; >>> 954: } >> >> Looks like most of these local variables can be removed? Since you are not changing the value of `len`, you can just use `WB`. `rW` is only used inside the if-block from line 944-948, so it can be declared on line 945. Line 946-948 can be merged on one line, e.g. `tmp = subByte(rW, SBOX) ^ RCON[(i / nk) - 1];` and no need for `subWord` and `g`. Same goes for line 950 and 951. >> Also, the value of `WB * (rounds + 1)` is used twice, this can be stored in a local variable say `wLen`, so it's only calculated once. >> Same goes for the `i * WB` value from line 937-940 > > On the second thought, instead of calculating `i * WB` value, You can use another local variable to store this index and increment it by 4 for each iteration. I've made these changes and used the 2nd approach for indexing key. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434497439 From valeriep at openjdk.org Thu Oct 16 04:52:04 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Thu, 16 Oct 2025 04:52:04 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v5] In-Reply-To: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> References: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> Message-ID: On Wed, 15 Oct 2025 23:04:10 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1032: > 1030: * @return the substituted word. > 1031: */ > 1032: private int subByte(int state, byte[][] sub) { Given the input and output are both `int` type, i.e. word, maybe it's better named as `subWord` ? This also matches the pseudocode routine name used in the spec. This method also can be made static. It seems that `sub` is always the static `SBOX`, maybe we don't have to use an argument to pass it? nit: the variable name `state` is a bit misleading as we are only using part of it. A state is consisting of 4 words and the input here is only 1 word. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434562796 From duke at openjdk.org Thu Oct 16 05:14:44 2025 From: duke at openjdk.org (Shawn M Emery) Date: Thu, 16 Oct 2025 05:14:44 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v7] In-Reply-To: References: Message-ID: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Updates for code review comments from @valeriepeng ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/fbf2117f..9f00c355 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=06 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=05-06 Stats: 12 lines in 1 file changed: 0 ins; 0 del; 12 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Thu Oct 16 05:14:44 2025 From: duke at openjdk.org (Shawn M Emery) Date: Thu, 16 Oct 2025 05:14:44 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v5] In-Reply-To: References: <-4R3yVX3ufogOLHqu2y6c2EGOPKmiy0rTuVwQoCk_SE=.942b296d-4540-4f57-ac5c-ff214d2985bc@github.com> Message-ID: On Thu, 16 Oct 2025 04:49:11 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Updates for code review comments from @valeriepeng > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1032: > >> 1030: * @return the substituted word. >> 1031: */ >> 1032: private int subByte(int state, byte[][] sub) { > > Given the input and output are both `int` type, i.e. word, maybe it's better named as `subWord` ? This also matches the pseudocode routine name used in the spec. > This method also can be made static. It seems that `sub` is always the static `SBOX`, maybe we don't have to use an argument to pass it? > nit: the variable name `state` is a bit misleading as we are only using part of it. A state is consisting of 4 words and the input here is only 1 word. Good, it was a byte operation, but evolved to a word. Last commit made it a static. Yes, before I switched over to a LUT for the inverse mix column transform of the inverse key expansion it needed both, but doesn't anymore. I'll switch from state to word then. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2434594049 From rrich at openjdk.org Thu Oct 16 07:18:11 2025 From: rrich at openjdk.org (Richard Reingruber) Date: Thu, 16 Oct 2025 07:18:11 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v2] In-Reply-To: References: Message-ID: On Wed, 15 Oct 2025 19:33:26 GMT, Patricio Chilano Mateo wrote: >> src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1751: >> >>> 1749: RegisterMap::ProcessFrames::skip, >>> 1750: RegisterMap::WalkContinuation::skip); >>> 1751: frame fr = top.sender(®_map); >> >> I think there's a problem here. I get an assertion on ppc if `top` is a heap frame (calling from `log_preempt_after_freeze`) because in `frame::sender_raw()` we don't take the path we normally would for a frame on heap. Instead `sender_for_compiled_frame()` is called which uses a constructor that asserts alignment of `sp` (see [here](https://github.com/openjdk/jdk/blob/1bd814c3b24eb7ef5633ee34bb418e0981ca1708/src/hotspot/cpu/ppc/frame_ppc.inline.hpp#L81-L86)). The assertion fails because `_on_heap` is false but should be `true`. >> >> I think in `sender_raw` `map->in_cont()` should return true if this frame is on heap. >> >> It's not quite easy to fix though since `top` can also be on stack. > > Right, it should be walked as a heap frame. Could you verify if this patch fixes the issue? > > > diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp > index 86c56fe583f..fb1f66c60f4 100644 > --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp > +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp > @@ -196,7 +196,7 @@ static bool do_verify_after_thaw(JavaThread* thread, stackChunkOop chunk, output > static void log_frames(JavaThread* thread, bool dolog = true); > static void log_frames_after_thaw(JavaThread* thread, ContinuationWrapper& cont, intptr_t* sp); > static void print_frame_layout(const frame& f, bool callee_complete, outputStream* st = tty); > -static void verify_frame_kind(const frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr = nullptr, const char** code_name_ptr = nullptr, int* bci_ptr = nullptr); > +static void verify_frame_kind(frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr = nullptr, const char** code_name_ptr = nullptr, int* bci_ptr = nullptr, stackChunkOop chunk = nullptr); > > #define assert_pfl(p, ...) \ > do { \ > @@ -1723,7 +1723,7 @@ bool FreezeBase::check_valid_fast_path() { > return true; > } > > -static void verify_frame_kind(const frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr, const char** code_name_ptr, int* bci_ptr) { > +static void verify_frame_kind(frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr, const char** code_name_ptr, int* bci_ptr, stackChunkOop chunk) { > Method* m; > const char* code_name; > int bci; > @@ -1747,7 +1747,13 @@ static void verify_frame_kind(const frame& top, Continuation::preempt_kind preem > RegisterMap reg_map(current, > RegisterMap::UpdateMap::skip, > RegisterMap::ProcessFrames::skip, > - RegisterMap::WalkContinuation::skip); > + RegisterMap::WalkContinuation::include); > + if (top.is_heap_frame()) { > + assert(chunk != nullptr, ""); > + reg_map.set_stack_chunk(chunk); > + top = chunk->relativize(top); > + top.set_frame_index(0); > + } > frame fr = top.sender(®_map); > vframe* vf = vframe::new_vframe(&fr, ®_map, current); > compiledVFrame* cvf = compiledVFrame::cast(vf); > @@ -1803,7 +1809,7 @@ static void log_preempt_after_freeze(ContinuationWrapper& cont) { > Method* m = nullptr; > const char* code_name = nu... Your patch fixes the issue. Thanks! ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2434828305 From aph at openjdk.org Thu Oct 16 09:08:13 2025 From: aph at openjdk.org (Andrew Haley) Date: Thu, 16 Oct 2025 09:08:13 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v2] In-Reply-To: References: Message-ID: On Tue, 14 Oct 2025 20:23:33 GMT, Patricio Chilano Mateo wrote: >> If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. >> >> As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. >> >> This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. >> >> ### Summary of implementation >> >> The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. >> >> If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). >> >> ### Notes >> >> `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::mon... > > Patricio Chilano Mateo has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains four commits: > > - Merge branch 'master' into JDK-8369238 > - RISC-V support > - Fix whitespaces > - v1 src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp line 209: > 207: // the last_sp saved in the frame (remove possible alignment added while > 208: // thawing, see ThawBase::finish_thaw()). We also need to clear the last_sp > 209: // saved in the frame as it is not expected to be set in case we preempt again. A bit stronger? Suggestion: // saved in the frame because it must be clear if we freeze again. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2435147223 From pchilanomate at openjdk.org Thu Oct 16 16:13:43 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Thu, 16 Oct 2025 16:13:43 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v2] In-Reply-To: References: Message-ID: On Thu, 16 Oct 2025 07:15:33 GMT, Richard Reingruber wrote: >> Right, it should be walked as a heap frame. Could you verify if this patch fixes the issue? >> >> >> diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp >> index 86c56fe583f..fb1f66c60f4 100644 >> --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp >> +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp >> @@ -196,7 +196,7 @@ static bool do_verify_after_thaw(JavaThread* thread, stackChunkOop chunk, output >> static void log_frames(JavaThread* thread, bool dolog = true); >> static void log_frames_after_thaw(JavaThread* thread, ContinuationWrapper& cont, intptr_t* sp); >> static void print_frame_layout(const frame& f, bool callee_complete, outputStream* st = tty); >> -static void verify_frame_kind(const frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr = nullptr, const char** code_name_ptr = nullptr, int* bci_ptr = nullptr); >> +static void verify_frame_kind(frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr = nullptr, const char** code_name_ptr = nullptr, int* bci_ptr = nullptr, stackChunkOop chunk = nullptr); >> >> #define assert_pfl(p, ...) \ >> do { \ >> @@ -1723,7 +1723,7 @@ bool FreezeBase::check_valid_fast_path() { >> return true; >> } >> >> -static void verify_frame_kind(const frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr, const char** code_name_ptr, int* bci_ptr) { >> +static void verify_frame_kind(frame& top, Continuation::preempt_kind preempt_kind, Method** m_ptr, const char** code_name_ptr, int* bci_ptr, stackChunkOop chunk) { >> Method* m; >> const char* code_name; >> int bci; >> @@ -1747,7 +1747,13 @@ static void verify_frame_kind(const frame& top, Continuation::preempt_kind preem >> RegisterMap reg_map(current, >> RegisterMap::UpdateMap::skip, >> RegisterMap::ProcessFrames::skip, >> - RegisterMap::WalkContinuation::skip); >> + RegisterMap::WalkContinuation::include); >> + if (top.is_heap_frame()) { >> + assert(chunk != nullptr, ""); >> + reg_map.set_stack_chunk(chunk); >> + top = chunk->relativize(top); >> + top.set_frame_index(0); >> + } >> frame fr = top.sender(®_map); >> vframe* vf = vframe::new_vframe(&fr, ®_map, current); >> compiledVFrame* cvf = compiledVFrame::cast(vf); >> @@ -1803,7 +1809,7 @@ static void log_preempt_after_freeze(... > > Your patch fixes the issue. Thanks! Great, pushed fix. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2436600849 From pchilanomate at openjdk.org Thu Oct 16 16:13:42 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Thu, 16 Oct 2025 16:13:42 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: References: Message-ID: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: fix verify_frame_kind ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27802/files - new: https://git.openjdk.org/jdk/pull/27802/files/79fce80f..ac557152 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=02 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=01-02 Stats: 10 lines in 1 file changed: 6 ins; 0 del; 4 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Thu Oct 16 16:26:09 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Thu, 16 Oct 2025 16:26:09 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v2] In-Reply-To: References: Message-ID: On Thu, 16 Oct 2025 09:05:24 GMT, Andrew Haley wrote: >> Patricio Chilano Mateo has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains four commits: >> >> - Merge branch 'master' into JDK-8369238 >> - RISC-V support >> - Fix whitespaces >> - v1 > > src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp line 209: > >> 207: // the last_sp saved in the frame (remove possible alignment added while >> 208: // thawing, see ThawBase::finish_thaw()). We also need to clear the last_sp >> 209: // saved in the frame as it is not expected to be set in case we preempt again. > > A bit stronger? > Suggestion: > > // saved in the frame because it must be clear if we freeze again. Just to add more context, not clearing last_sp will make this assert [1] fire if we freeze again. That assert is mostly a verification check, because we know the interpreter doesn?t set last_sp for the top frame when calling into the VM. But I don?t see a fundamental reason why it must be cleared (removing the assert and not clearing last_sp works). I don?t see any other code that checks last_sp needs to be cleared for the top frame (other than in the interpreter before calling into the VM). How about changing that last sentence with: `We also clear last_sp to match the behavior when calling the VM from the interpreter (we check for this in FreezeBase::prepare_freeze_interpreted_top_frame).` [1] https://github.com/openjdk/jdk/blob/87092ef1d97e00ddb6674b0e309f2f904d307604/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp#L136 ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2436641958 From valeriep at openjdk.org Thu Oct 16 20:04:35 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Thu, 16 Oct 2025 20:04:35 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v7] In-Reply-To: References: Message-ID: On Thu, 16 Oct 2025 05:14:44 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1040: > 1038: * @param p [in] the plaintext to be encrypted. > 1039: * @param po [in] the plaintext offset in the array of bytes. > 1040: * @param c [out] the encrypted ciphertext output. nit: ciphertext already implied to be encrypted. Maybe no need for the "encrypted" adj. src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1157: > 1155: ti3 = T0[a3 >>> 24] ^ T1[(a0 >> 16) & 0xFF] > 1156: ^ T2[(a1 >> 8) & 0xFF] ^ T3[a2 & 0xFF] ^ K[w + 7]; > 1157: w += 8; No need for w, since you already checked the `rounds` value, you can directly reference K inside this block, i.e. K[40] - K[47]. Same goes for the next block for AES-256, i.e. directly reference K[48]-K[55]. src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1195: > 1193: ^ T3[(ti0 >> 16) & 0xFF] & 0xFF0000 > 1194: ^ T0[(ti1 >> 8) & 0xFF] & 0xFF00 > 1195: ^ T1[ti2 & 0xFF] & 0xFF ^ K[w+3]; Here you always use the last 4 elements of `K`, so you can just use `w = K.length - 4` and no need to keep tracking it in the earlier 2 blocks. src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1220: > 1218: * @param c [in] the ciphertext to be decrypted. > 1219: * @param co [in] the ciphertext offset in the array of bytes. > 1220: * @param p [out] the decrypted plaintext output. nit: same comment for removing "decrypted" adj. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2437316942 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2437308268 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2437313179 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2437325831 From duke at openjdk.org Thu Oct 16 20:04:37 2025 From: duke at openjdk.org (Shawn M Emery) Date: Thu, 16 Oct 2025 20:04:37 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v7] In-Reply-To: References: Message-ID: On Thu, 16 Oct 2025 19:55:12 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Updates for code review comments from @valeriepeng > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1157: > >> 1155: ti3 = T0[a3 >>> 24] ^ T1[(a0 >> 16) & 0xFF] >> 1156: ^ T2[(a1 >> 8) & 0xFF] ^ T3[a2 & 0xFF] ^ K[w + 7]; >> 1157: w += 8; > > No need for w, since you already checked the `rounds` value, you can directly reference K inside this block, i.e. K[40] - K[47]. Same goes for the next block for AES-256, i.e. directly reference K[48]-K[55]. I would still need w for lines 1180 - 1195 though. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2437324027 From duke at openjdk.org Thu Oct 16 20:22:20 2025 From: duke at openjdk.org (Shawn M Emery) Date: Thu, 16 Oct 2025 20:22:20 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: References: Message-ID: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Updates for code review comments from @valeriepeng ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/9f00c355..a5991a2f Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=07 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=06-07 Stats: 42 lines in 1 file changed: 0 ins; 4 del; 38 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Thu Oct 16 20:22:22 2025 From: duke at openjdk.org (Shawn M Emery) Date: Thu, 16 Oct 2025 20:22:22 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v7] In-Reply-To: References: Message-ID: On Thu, 16 Oct 2025 19:58:40 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Updates for code review comments from @valeriepeng > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1040: > >> 1038: * @param p [in] the plaintext to be encrypted. >> 1039: * @param po [in] the plaintext offset in the array of bytes. >> 1040: * @param c [out] the encrypted ciphertext output. > > nit: ciphertext already implied to be encrypted. Maybe no need for the "encrypted" adj. Agreed. Fixed. > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1195: > >> 1193: ^ T3[(ti0 >> 16) & 0xFF] & 0xFF0000 >> 1194: ^ T0[(ti1 >> 8) & 0xFF] & 0xFF00 >> 1195: ^ T1[ti2 & 0xFF] & 0xFF ^ K[w+3]; > > Here you always use the last 4 elements of `K`, so you can just use `w = K.length - 4` and no need to keep tracking it in the earlier 2 blocks. Agreed. I've changed decryption as well. Fixed. > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1220: > >> 1218: * @param c [in] the ciphertext to be decrypted. >> 1219: * @param co [in] the ciphertext offset in the array of bytes. >> 1220: * @param p [out] the decrypted plaintext output. > > nit: same comment for removing "decrypted" adj. Agreed. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2437361415 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2437361126 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2437362009 From valeriep at openjdk.org Fri Oct 17 05:44:04 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Fri, 17 Oct 2025 05:44:04 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: References: Message-ID: <2_eqasQo7DtbnrxwxuFYvl_yhVh7P6wzlxwPEl_DB-Q=.ff81a933-2c29-4d69-826b-d1ccf04d2e1c@github.com> On Thu, 16 Oct 2025 20:22:20 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 911: > 909: } > 910: sessionK[0] = genRoundKeys(key, rounds); > 911: sessionK[1] = invGenRoundKeys(); Given the decryption round keys are somewhat based on the encryption round keys, we could combine these two methods into one, e.g. private static int[][] genRoundKeys(byte[] key, int rounds) { int[][] ks = new int[2][]; // key schedule int wLen = (rounds + 1) * WB; int nk = key.length / WB; // generate the round keys for encryption int[] w = new int[wLen]; for (int i = 0, j = 0; i < nk; i++, j+=4) { w[i] = ((key[j] & 0xFF) << 24) | ((key[j + 1] & 0xFF) << 16) | ((key[j + 2] & 0xFF) << 8) | (key[j + 3] & 0xFF); } for (int i = nk; i < wLen; i++) { int tmp = w[i - 1]; if (i % nk == 0) { int rW = (tmp << 8) & 0xFFFFFF00 | (tmp >>> 24); tmp = subWord(rW) ^ RCON[(i / nk) - 1]; } else if ((nk > 6) && ((i % nk) == WB)) { tmp = subWord(tmp); } w[i] = w[i - nk] ^ tmp; } ks[0] = w; // generate the decryption round keys based on encryption ones int[] dw = new int[wLen]; int[] temp = new int[WB]; // Intrinsics requires the inverse key expansion to be reverse order // except for the first and last round key as the first two round keys // are without a mix column transform. for (int i = 1; i < rounds; i++) { System.arraycopy(w, i * WB, temp, 0, WB); invMixRKey(temp); System.arraycopy(temp, 0, dw, wLen - (i * WB), WB); } // dw[0...3] <- w[0...3] AND dw[4...7] <- w[(wLen - 4)...(wLen -1)] System.arraycopy(w, 0, dw, 0, WB); System.arraycopy(w, wLen - WB, dw, WB, WB); ks[1] = dw; Arrays.fill(temp, 0); return ks; } ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2438441223 From valeriep at openjdk.org Fri Oct 17 06:17:06 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Fri, 17 Oct 2025 06:17:06 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: References: Message-ID: On Thu, 16 Oct 2025 20:22:20 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 958: > 956: * @return the processed round key row. > 957: */ > 958: private static int invMix(int[] state, int idx) { It seems that we can just use an `int` argument and make the callers do the array dereferencing. This way we can get rid of the temporary buffer inside `invMixRKey(int[])` as passing an integer to `invMix(int)` method will not affect the array, e.g. private static void invMixRKey(int[] state) { state[0] = invMix(state[0]); state[1] = invMix(state[1]); state[2] = invMix(state[2]); state[3] = invMix(state[3]); } ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2438501716 From valeriep at openjdk.org Fri Oct 17 06:30:02 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Fri, 17 Oct 2025 06:30:02 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: References: Message-ID: <56BA-l6ZrdSY0IRWxXme_AM_CWW_vtBSrzYqIA4oZaE=.b9011356-c376-455a-8964-4534f9db6035@github.com> On Thu, 16 Oct 2025 20:22:20 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 976: > 974: * @param state [in, out] the round key for inverse mix column processing. > 975: */ > 976: private static void invMixRKey(int[] state) { nit: name the method "invMixColumns(int[])". This name matches the spec psuedo code and goes better with the "state" argument name. Or use "invMixRoundKey(int[] roundKey)"? ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2438537870 From dholmes at openjdk.org Fri Oct 17 06:53:20 2025 From: dholmes at openjdk.org (David Holmes) Date: Fri, 17 Oct 2025 06:53:20 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: References: Message-ID: On Thu, 16 Oct 2025 16:13:42 GMT, Patricio Chilano Mateo wrote: >> If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. >> >> As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. >> >> This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. >> >> ### Summary of implementation >> >> The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. >> >> If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). >> >> ### Notes >> >> `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::mon... > > Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: > > fix verify_frame_kind Great piece of work @pchilano ! I've taken an initial pass through the main class/thread/objectMonitor pieces. src/hotspot/share/interpreter/linkResolver.cpp line 1122: > 1120: resolved_klass->initialize(CHECK); > 1121: } else if (static_mode == StaticMode::initialize_klass_preemptable) { > 1122: resolved_klass->initialize_preemptable(CHECK); Why is this not CHECK_AND_CLEAR_PREEMPTED? src/hotspot/share/interpreter/linkResolver.hpp line 192: > 190: }; > 191: > 192: enum class StaticMode : uint8_t { Need to think of a better name for this ... `ClassInitMode`? src/hotspot/share/interpreter/linkResolver.hpp line 195: > 193: dont_initialize_klass, > 194: initialize_klass, > 195: initialize_klass_preemptable And maybe more concisely: Suggestion: dont_init init init_preemptable ? src/hotspot/share/oops/instanceKlass.cpp line 1284: > 1282: // Block preemption once we are the initializer thread. Unmounting now > 1283: // would complicate the reentrant case (identity is platform thread). > 1284: NoPreemptMark npm(THREAD); How does this affect unrelated "preemption" that may occur in the Java code called as part of ``? src/hotspot/share/oops/klass.hpp line 583: > 581: // initializes the klass > 582: virtual void initialize(TRAPS); > 583: virtual void initialize_preemptable(TRAPS); Can we not define these on `instanceKlass` instead of `klass`? Seems really odd to declare virtual methods for which the base class version should never be called (they should be pure virtuals in that case I would think?). src/hotspot/share/runtime/continuationEntry.cpp line 117: > 115: assert(thread->has_last_Java_frame(), "Wrong place to use this assertion"); > 116: > 117: if (preempted) return true; I don't see the point of adding a new parameter just so the method can check it and return immediately. Shouldn't the caller be checking this? src/hotspot/share/runtime/continuationFreezeThaw.cpp line 196: > 194: static void do_deopt_after_thaw(JavaThread* thread); > 195: static bool do_verify_after_thaw(JavaThread* thread, stackChunkOop chunk, outputStream* st); > 196: static void log_frames(JavaThread* thread, bool dolog = true); Suggestion: static void log_frames(JavaThread* thread, bool do_log = true); src/hotspot/share/runtime/javaThread.hpp line 492: > 490: // throw IE at the end of thawing before returning to Java. > 491: bool _pending_interrupted_exception; > 492: // We allow preemption on some klass initializion calls. Suggestion: // We allow preemption on some klass initialization calls. src/hotspot/share/runtime/javaThread.hpp line 507: > 505: > 506: bool at_preemptable_init() { return _at_preemptable_init; } > 507: void set_at_preemptable_init(bool b) { _at_preemptable_init = b; } If you are going align 503 and 504 then you may as well align these two lines as well src/hotspot/share/runtime/javaThread.hpp line 522: > 520: JavaThread* _thread; > 521: public: > 522: AtRedoVMCall(JavaThread *t) : _thread(t) { Suggestion: AtRedoVMCall(JavaThread* t) : _thread(t) { src/hotspot/share/runtime/javaThread.hpp line 524: > 522: AtRedoVMCall(JavaThread *t) : _thread(t) { > 523: _thread->_interp_at_preemptable_vmcall_cnt++; > 524: assert(_thread->_interp_at_preemptable_vmcall_cnt > 0, ""); Suggestion: assert(_thread->_interp_at_preemptable_vmcall_cnt > 0, "Unexpected count: %d", _thread->_interp_at_preemptable_vmcall_cnt); src/hotspot/share/runtime/javaThread.hpp line 528: > 526: ~AtRedoVMCall() { > 527: _thread->_interp_at_preemptable_vmcall_cnt--; > 528: assert(_thread->_interp_at_preemptable_vmcall_cnt >= 0, ""); Suggestion: assert(_thread->_interp_at_preemptable_vmcall_cnt > 0, "Unexpected count: %d", _thread->_interp_at_preemptable_vmcall_cnt); src/hotspot/share/runtime/objectMonitor.cpp line 315: > 313: } > 314: > 315: // Keep object protected during ObjectLocker preemption. I don't understand why this is necessary. src/hotspot/share/runtime/synchronizer.cpp line 489: > 487: if (_thread->preempting()) { > 488: // If preemption was cancelled we acquired the monitor after freezing > 489: // the frames. Redoing the vm call later?in thaw will require us to I don't quite follow the control flow here - at what point have we frozen any frames? Was that deep inside `enter`? src/hotspot/share/runtime/synchronizer.hpp line 230: > 228: bool _skip_exit; > 229: public: > 230: ObjectLocker(Handle obj, TRAPS); I wonder if we should declare `PREEMPTABLE_TRAPS` as an indicator that the only exception expected to come out of a call is the preempted-exception? src/java.base/share/classes/java/lang/VirtualThread.java line 172: > 170: > 171: // true when waiting in Object.wait, false for VM internal uninterruptible Object.wait > 172: private volatile boolean interruptableWait; Suggestion: private volatile boolean interruptibleWait; src/java.base/share/classes/java/lang/VirtualThread.java line 605: > 603: if (s == WAITING || s == TIMED_WAITING) { > 604: int newState; > 605: boolean interruptable = interruptableWait; Suggestion: boolean interruptible = interruptibleWait; src/java.base/share/classes/jdk/internal/vm/PreemptedException.java line 31: > 29: * Internal exception used only by the VM. > 30: */ > 31: public class PreemptedException extends Exception { Should probably extend `RuntimeException` so that it is not considered a checked-exception ------------- PR Review: https://git.openjdk.org/jdk/pull/27802#pullrequestreview-3348320781 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438580798 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438444689 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438447733 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438469910 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438473899 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438480176 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438482435 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438506706 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438516134 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438518828 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438523339 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438524180 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438533049 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438542039 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438549455 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438568359 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438568876 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2438571309 From duke at openjdk.org Fri Oct 17 06:56:43 2025 From: duke at openjdk.org (Shawn M Emery) Date: Fri, 17 Oct 2025 06:56:43 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v9] In-Reply-To: References: Message-ID: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Updates for code review comments from @valeriepeng ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/a5991a2f..2ce35b97 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=08 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=07-08 Stats: 47 lines in 1 file changed: 7 ins; 39 del; 1 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Fri Oct 17 06:56:46 2025 From: duke at openjdk.org (Shawn M Emery) Date: Fri, 17 Oct 2025 06:56:46 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: <2_eqasQo7DtbnrxwxuFYvl_yhVh7P6wzlxwPEl_DB-Q=.ff81a933-2c29-4d69-826b-d1ccf04d2e1c@github.com> References: <2_eqasQo7DtbnrxwxuFYvl_yhVh7P6wzlxwPEl_DB-Q=.ff81a933-2c29-4d69-826b-d1ccf04d2e1c@github.com> Message-ID: <5SGZrrpgKtrf7IKtfEDPFb4LnggKNoeaZpFVGuHu-p4=.1ec8dd26-e125-4c8a-ba22-9006c7da4024@github.com> On Fri, 17 Oct 2025 05:41:25 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Updates for code review comments from @valeriepeng > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 911: > >> 909: } >> 910: sessionK[0] = genRoundKeys(key, rounds); >> 911: sessionK[1] = invGenRoundKeys(); > > Given the decryption round keys are somewhat based on the encryption round keys, we could combine these two methods into one, e.g. > > private static int[][] genRoundKeys(byte[] key, int rounds) { > int[][] ks = new int[2][]; // key schedule > > int wLen = (rounds + 1) * WB; > int nk = key.length / WB; > > // generate the round keys for encryption > int[] w = new int[wLen]; > for (int i = 0, j = 0; i < nk; i++, j+=4) { > w[i] = ((key[j] & 0xFF) << 24) > | ((key[j + 1] & 0xFF) << 16) > | ((key[j + 2] & 0xFF) << 8) > | (key[j + 3] & 0xFF); > } > for (int i = nk; i < wLen; i++) { > int tmp = w[i - 1]; > if (i % nk == 0) { > int rW = (tmp << 8) & 0xFFFFFF00 | (tmp >>> 24); > tmp = subWord(rW) ^ RCON[(i / nk) - 1]; > } else if ((nk > 6) && ((i % nk) == WB)) { > tmp = subWord(tmp); > } > w[i] = w[i - nk] ^ tmp; > } > ks[0] = w; > > // generate the decryption round keys based on encryption ones > int[] dw = new int[wLen]; > int[] temp = new int[WB]; > > // Intrinsics requires the inverse key expansion to be reverse order > // except for the first and last round key as the first two round keys > // are without a mix column transform. > for (int i = 1; i < rounds; i++) { > System.arraycopy(w, i * WB, temp, 0, WB); > invMixRKey(temp); > System.arraycopy(temp, 0, dw, wLen - (i * WB), WB); > } > // dw[0...3] <- w[0...3] AND dw[4...7] <- w[(wLen - 4)...(wLen -1)] > System.arraycopy(w, 0, dw, 0, WB); > System.arraycopy(w, wLen - WB, dw, WB, WB); > ks[1] = dw; > Arrays.fill(temp, 0); > > return ks; > } These two methods were only the few that I was able to make that were compact and singular in purpose (gen round key, gen inverse round key) code as the coding style guidelines espouse. The rest of the methods' construction were dictated by performance improvements, where compactness came at the cost of interpreter speed. > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 958: > >> 956: * @return the processed round key row. >> 957: */ >> 958: private static int invMix(int[] state, int idx) { > > It seems that we can just use an `int` argument and make the callers do the array dereferencing. This way we can get rid of the temporary buffer inside `invMixRKey(int[])` as passing an integer to `invMix(int)` method will not affect the array, e.g. > > private static void invMixRKey(int[] state) { > state[0] = invMix(state[0]); > state[1] = invMix(state[1]); > state[2] = invMix(state[2]); > state[3] = invMix(state[3]); > } I've removed this method and inlined this logic in the invGenRoundKeys method. > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 976: > >> 974: * @param state [in, out] the round key for inverse mix column processing. >> 975: */ >> 976: private static void invMixRKey(int[] state) { > > nit: name the method "invMixColumns(int[])". This name matches the spec psuedo code and goes better with the "state" argument name. Or use "invMixRoundKey(int[] roundKey)"? I've removed this method and inlined this logic in the invGenRoundKeys method. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2438587221 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2438587085 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2438586207 From valeriep at openjdk.org Fri Oct 17 07:07:11 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Fri, 17 Oct 2025 07:07:11 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: <5SGZrrpgKtrf7IKtfEDPFb4LnggKNoeaZpFVGuHu-p4=.1ec8dd26-e125-4c8a-ba22-9006c7da4024@github.com> References: <2_eqasQo7DtbnrxwxuFYvl_yhVh7P6wzlxwPEl_DB-Q=.ff81a933-2c29-4d69-826b-d1ccf04d2e1c@github.com> <5SGZrrpgKtrf7IKtfEDPFb4LnggKNoeaZpFVGuHu-p4=.1ec8dd26-e125-4c8a-ba22-9006c7da4024@github.com> Message-ID: <3IhmbTDiDNPdMTe_K1OZx6sC67UGjObzOXwX8Ekp7pA=.0e742e44-4dba-4680-8f24-7321f8516071@github.com> On Fri, 17 Oct 2025 06:52:39 GMT, Shawn M Emery wrote: >> src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 958: >> >>> 956: * @return the processed round key row. >>> 957: */ >>> 958: private static int invMix(int[] state, int idx) { >> >> It seems that we can just use an `int` argument and make the callers do the array dereferencing. This way we can get rid of the temporary buffer inside `invMixRKey(int[])` as passing an integer to `invMix(int)` method will not affect the array, e.g. >> >> private static void invMixRKey(int[] state) { >> state[0] = invMix(state[0]); >> state[1] = invMix(state[1]); >> state[2] = invMix(state[2]); >> state[3] = invMix(state[3]); >> } > > I've removed this method and inlined this logic in the invGenRoundKeys method. Sure, this works as well. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2438612714 From epeter at openjdk.org Fri Oct 17 13:02:04 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Fri, 17 Oct 2025 13:02:04 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v9] In-Reply-To: References: Message-ID: On Fri, 17 Oct 2025 06:56:43 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng test/micro/org/openjdk/bench/javax/crypto/AESDecrypt.java line 44: > 42: > 43: @Param("10000000") > 44: private int count; Drive-by comment / question: Did you do all benchmarking with this single (quite large) size? How are the results for much smaller sizes? It may be worth it to just get a nice plot that goes over a range of sizes, to see if it behaves as expected. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2439948821 From duke at openjdk.org Fri Oct 17 17:22:06 2025 From: duke at openjdk.org (Shawn M Emery) Date: Fri, 17 Oct 2025 17:22:06 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v9] In-Reply-To: References: Message-ID: On Fri, 17 Oct 2025 12:59:42 GMT, Emanuel Peter wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Updates for code review comments from @valeriepeng > > test/micro/org/openjdk/bench/javax/crypto/AESDecrypt.java line 44: > >> 42: >> 43: @Param("10000000") >> 44: private int count; > > Drive-by comment / question: > Did you do all benchmarking with this single (quite large) size? How are the results for much smaller sizes? It may be worth it to just get a nice plot that goes over a range of sizes, to see if it behaves as expected. The benchmarks listed in the PR description execute tests for data sizes ranging from 16 to 10_000_000 bytes for decryption and encryption. The difference in performance between the old and new code were within SE. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2440660231 From duke at openjdk.org Fri Oct 17 20:07:05 2025 From: duke at openjdk.org (Shawn M Emery) Date: Fri, 17 Oct 2025 20:07:05 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v10] In-Reply-To: References: Message-ID: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Updates for code review comments from @valeriepeng ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/2ce35b97..1102c609 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=09 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=08-09 Stats: 17 lines in 1 file changed: 0 ins; 1 del; 16 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Fri Oct 17 20:07:06 2025 From: duke at openjdk.org (Shawn M Emery) Date: Fri, 17 Oct 2025 20:07:06 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: <5SGZrrpgKtrf7IKtfEDPFb4LnggKNoeaZpFVGuHu-p4=.1ec8dd26-e125-4c8a-ba22-9006c7da4024@github.com> References: <2_eqasQo7DtbnrxwxuFYvl_yhVh7P6wzlxwPEl_DB-Q=.ff81a933-2c29-4d69-826b-d1ccf04d2e1c@github.com> <5SGZrrpgKtrf7IKtfEDPFb4LnggKNoeaZpFVGuHu-p4=.1ec8dd26-e125-4c8a-ba22-9006c7da4024@github.com> Message-ID: On Fri, 17 Oct 2025 06:52:44 GMT, Shawn M Emery wrote: >> src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 911: >> >>> 909: } >>> 910: sessionK[0] = genRoundKeys(key, rounds); >>> 911: sessionK[1] = invGenRoundKeys(); >> >> Given the decryption round keys are somewhat based on the encryption round keys, we could combine these two methods into one, e.g. >> >> private static int[][] genRoundKeys(byte[] key, int rounds) { >> int[][] ks = new int[2][]; // key schedule >> >> int wLen = (rounds + 1) * WB; >> int nk = key.length / WB; >> >> // generate the round keys for encryption >> int[] w = new int[wLen]; >> for (int i = 0, j = 0; i < nk; i++, j+=4) { >> w[i] = ((key[j] & 0xFF) << 24) >> | ((key[j + 1] & 0xFF) << 16) >> | ((key[j + 2] & 0xFF) << 8) >> | (key[j + 3] & 0xFF); >> } >> for (int i = nk; i < wLen; i++) { >> int tmp = w[i - 1]; >> if (i % nk == 0) { >> int rW = (tmp << 8) & 0xFFFFFF00 | (tmp >>> 24); >> tmp = subWord(rW) ^ RCON[(i / nk) - 1]; >> } else if ((nk > 6) && ((i % nk) == WB)) { >> tmp = subWord(tmp); >> } >> w[i] = w[i - nk] ^ tmp; >> } >> ks[0] = w; >> >> // generate the decryption round keys based on encryption ones >> int[] dw = new int[wLen]; >> int[] temp = new int[WB]; >> >> // Intrinsics requires the inverse key expansion to be reverse order >> // except for the first and last round key as the first two round keys >> // are without a mix column transform. >> for (int i = 1; i < rounds; i++) { >> System.arraycopy(w, i * WB, temp, 0, WB); >> invMixRKey(temp); >> System.arraycopy(temp, 0, dw, wLen - (i * WB), WB); >> } >> // dw[0...3] <- w[0...3] AND dw[4...7] <- w[(wLen - 4)...(wLen -1)] >> System.arraycopy(w, 0, dw, 0, WB); >> System.arraycopy(w, wLen - WB, dw, WB, WB); >> ks[1] = dw; >> Arrays.fill(temp, 0); >> >> return ks; >> } > > These two methods were only the few that I was able to make that were compact and singular in purpose (gen round key, gen inverse round key) code as the coding style guidelines espouse. The rest of the methods' construction were dictated by performance improvements, where compactness came at the cost of interpreter speed. I did make changes based on your code to eliminate len and updates to variable names. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2441096947 From valeriep at openjdk.org Fri Oct 17 20:21:02 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Fri, 17 Oct 2025 20:21:02 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: References: <2_eqasQo7DtbnrxwxuFYvl_yhVh7P6wzlxwPEl_DB-Q=.ff81a933-2c29-4d69-826b-d1ccf04d2e1c@github.com> <5SGZrrpgKtrf7IKtfEDPFb4LnggKNoeaZpFVGuHu-p4=.1ec8dd26-e125-4c8a-ba22-9006c7da4024@github.com> Message-ID: On Fri, 17 Oct 2025 20:01:21 GMT, Shawn M Emery wrote: >> These two methods were only the few that I was able to make that were compact and singular in purpose (gen round key, gen inverse round key) code as the coding style guidelines espouse. The rest of the methods' construction were dictated by performance improvements, where compactness came at the cost of interpreter speed. > > I did make changes based on your code to eliminate len and updates to variable names. Yes, I take a second look and maybe a smaller adjustments would work as well. E.g, 1) nit: method name `invGenRoundKeys` -> `genInvRoundKeys` 2) make this method static by passing `sessionKey[0]` and `rounds` as arguments, 3) no need for `len` since it's always `WB` 4) for the intermediate buffer of 4 words, can we not use `w` as this name is used in both the spec and genRoundKeys method as "Word array for the key schedule". It'd help people understand the code better if we adopt the same naming convention in "Algorithm 5 Pseudocode for KEYEXPANSIONEIC()", e.g. `temp` for the intermediate buffer and `dw` for the final result. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2441139964 From valeriep at openjdk.org Fri Oct 17 21:15:04 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Fri, 17 Oct 2025 21:15:04 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v10] In-Reply-To: References: Message-ID: On Fri, 17 Oct 2025 20:07:05 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 45: > 43: final class AES_Crypt extends SymmetricCipher { > 44: > 45: // Number of words in a block nit: from the usage, e.g. `int nk = key.length / WB`;, it seems WB means "number of bytes in a word". ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2441263128 From valeriep at openjdk.org Fri Oct 17 21:39:05 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Fri, 17 Oct 2025 21:39:05 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v10] In-Reply-To: References: Message-ID: On Fri, 17 Oct 2025 20:07:05 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 134: > 132: }; > 133: > 134: private static final int[] T0 = { nit: add comment for all these precomputed lookup tables and their usage. Are these tables publicly available somewhere? I checked both spec in the class header and they don't have these included. I wonder if they are made available somewhere which corresponds with the current impl code better. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2441320498 From duke at openjdk.org Fri Oct 17 22:19:26 2025 From: duke at openjdk.org (Shawn M Emery) Date: Fri, 17 Oct 2025 22:19:26 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v11] In-Reply-To: References: Message-ID: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Updates for code review comments from @valeriepeng ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/1102c609..e0741b17 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=10 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=09-10 Stats: 22 lines in 1 file changed: 20 ins; 0 del; 2 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Fri Oct 17 22:19:28 2025 From: duke at openjdk.org (Shawn M Emery) Date: Fri, 17 Oct 2025 22:19:28 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v10] In-Reply-To: References: Message-ID: On Fri, 17 Oct 2025 21:12:21 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Updates for code review comments from @valeriepeng > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 45: > >> 43: final class AES_Crypt extends SymmetricCipher { >> 44: >> 45: // Number of words in a block > > nit: from the usage, e.g. `int nk = key.length / WB`;, it seems WB means "number of bytes in a word". I agree, it should be bytes per word for number of keys (nk) calculation, so BW? I want to preserved words per block (WB) for maintainability (e.g., if we decide to implement Rijndael-256, where WB = 8). Fixed. > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 134: > >> 132: }; >> 133: >> 134: private static final int[] T0 = { > > nit: add comment for all these precomputed lookup tables and their usage. > > Are these tables publicly available somewhere? I checked both spec in the class header and they don't have these included. I wonder if they are made available somewhere which corresponds with the current impl code better. I generated the tables separately, but their usage is referenced in the original specification cited in section 5.2.1. If made comments indicating of such. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2441377285 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2441377328 From duke at openjdk.org Sun Oct 19 02:18:43 2025 From: duke at openjdk.org (Shawn M Emery) Date: Sun, 19 Oct 2025 02:18:43 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v12] In-Reply-To: References: Message-ID: <3nnB5pkNnYqYi6OAH3u83PNmiO607_xwHXCmCeIE7gA=.371aa791-952e-4143-9012-1728dbf31ae9@github.com> > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Updates for code review comments from @valeriepeng ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/e0741b17..5ea6933b Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=11 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=10-11 Stats: 19 lines in 1 file changed: 0 ins; 0 del; 19 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Sun Oct 19 02:18:44 2025 From: duke at openjdk.org (Shawn M Emery) Date: Sun, 19 Oct 2025 02:18:44 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: References: <2_eqasQo7DtbnrxwxuFYvl_yhVh7P6wzlxwPEl_DB-Q=.ff81a933-2c29-4d69-826b-d1ccf04d2e1c@github.com> <5SGZrrpgKtrf7IKtfEDPFb4LnggKNoeaZpFVGuHu-p4=.1ec8dd26-e125-4c8a-ba22-9006c7da4024@github.com> Message-ID: <7kU60fP_Aw4oiwfwMN48YfFwC70wVObcHQpSXqZvyC4=.5094fe4c-de7e-4a96-8000-121a9db66ae5@github.com> On Fri, 17 Oct 2025 20:18:24 GMT, Valerie Peng wrote: >> I did make changes based on your code to eliminate len and updates to variable names. > > Yes, I take a second look and maybe a smaller adjustments would work as well. E.g, > 1) nit: method name `invGenRoundKeys` -> `genInvRoundKeys` > 2) make this method static by passing `sessionKey[0]` and `rounds` as arguments, > 3) no need for `len` since it's always `WB` > 4) for the intermediate buffer of 4 words, can we not use `w` as this name is used in both the spec and genRoundKeys method as "Word array for the key schedule". It'd help people understand the code better if we adopt the same naming convention in "Algorithm 5 Pseudocode for KEYEXPANSIONEIC()", e.g. `temp` for the intermediate buffer and `dw` for the final result. Sorry, missed this comment in the melee. Re: 1) method name, agreed; 2) to static, agreed; 3) remove len, prior commit; 4) variable name alignment, agreed. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2442722654 From rrich at openjdk.org Mon Oct 20 06:33:02 2025 From: rrich at openjdk.org (Richard Reingruber) Date: Mon, 20 Oct 2025 06:33:02 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths In-Reply-To: References: Message-ID: On Tue, 14 Oct 2025 20:20:55 GMT, Patricio Chilano Mateo wrote: >> https://github.com/openjdk/jdk/pull/27676 has been integrated which requires resolution. > >> #27676 has been integrated which requires resolution. >> > Thanks, merged with latest master. @pchilano I've finished the ppc part: https://github.com/reinrich/jdk/commit/99b96528539dd7239711d9c70aebb1a4c1a7fdfe ------------- PR Comment: https://git.openjdk.org/jdk/pull/27802#issuecomment-3420740434 From pchilanomate at openjdk.org Mon Oct 20 19:01:58 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 20 Oct 2025 19:01:58 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v4] In-Reply-To: References: Message-ID: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request incrementally with two additional commits since the last revision: - Extra comments from David - Use ClassInitMode and shorter enum names ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27802/files - new: https://git.openjdk.org/jdk/pull/27802/files/ac557152..30bdf498 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=03 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=02-03 Stats: 65 lines in 16 files changed: 2 ins; 4 del; 59 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Mon Oct 20 19:12:24 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 20 Oct 2025 19:12:24 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v5] In-Reply-To: References: Message-ID: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: Remove extra debugging param ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27802/files - new: https://git.openjdk.org/jdk/pull/27802/files/30bdf498..6882db04 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=04 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=03-04 Stats: 3 lines in 1 file changed: 0 ins; 0 del; 3 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Mon Oct 20 19:12:29 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 20 Oct 2025 19:12:29 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: References: Message-ID: <8SlbTQ-LN3PIBw1M6hERwi8KyA02Avg5KrYrZISR_4o=.5ef0f9a9-48ef-4a36-a2ef-ffb2b77863ec@github.com> On Fri, 17 Oct 2025 05:43:49 GMT, David Holmes wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> fix verify_frame_kind > > src/hotspot/share/interpreter/linkResolver.hpp line 192: > >> 190: }; >> 191: >> 192: enum class StaticMode : uint8_t { > > Need to think of a better name for this ... `ClassInitMode`? Yes, much better. Changed. > src/hotspot/share/interpreter/linkResolver.hpp line 195: > >> 193: dont_initialize_klass, >> 194: initialize_klass, >> 195: initialize_klass_preemptable > > And maybe more concisely: > Suggestion: > > dont_init > init > init_preemptable > > ? Yes, changed. > src/hotspot/share/oops/instanceKlass.cpp line 1284: > >> 1282: // Block preemption once we are the initializer thread. Unmounting now >> 1283: // would complicate the reentrant case (identity is platform thread). >> 1284: NoPreemptMark npm(THREAD); > > How does this affect unrelated "preemption" that may occur in the Java code called as part of ``? While executing the `` method the vthread is pinned because of the call_stub in the stack (freeze will fail if we try to unmount), regardless of this `NoPreemptMark`. So this is for the other places where it is preemptable, mainly when initializing the super klass below. > src/hotspot/share/oops/klass.hpp line 583: > >> 581: // initializes the klass >> 582: virtual void initialize(TRAPS); >> 583: virtual void initialize_preemptable(TRAPS); > > Can we not define these on `instanceKlass` instead of `klass`? Seems really odd to declare virtual methods for which the base class version should never be called (they should be pure virtuals in that case I would think?). Ok. Just to double check, casting to `InstanceKlass*` where we call `initialize_preemptable` for the `invokestatic` and `getstatic/putstatic` cases should be always safe right? I don?t see how we could get there for an `ArrayKlass`. > src/hotspot/share/runtime/continuationEntry.cpp line 117: > >> 115: assert(thread->has_last_Java_frame(), "Wrong place to use this assertion"); >> 116: >> 117: if (preempted) return true; > > I don't see the point of adding a new parameter just so the method can check it and return immediately. Shouldn't the caller be checking this? Right, fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445862480 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445862813 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445864285 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445868222 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445869014 From pchilanomate at openjdk.org Mon Oct 20 19:23:16 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 20 Oct 2025 19:23:16 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: References: Message-ID: <4RnCaK-UiAcxujqvgB6zbTe70SMXBkJDJieP9U0tzg4=.3fbda558-c9b3-4d5a-a0e0-e009005d9a50@github.com> On Fri, 17 Oct 2025 06:49:39 GMT, David Holmes wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> fix verify_frame_kind > > src/hotspot/share/interpreter/linkResolver.cpp line 1122: > >> 1120: resolved_klass->initialize(CHECK); >> 1121: } else if (static_mode == StaticMode::initialize_klass_preemptable) { >> 1122: resolved_klass->initialize_preemptable(CHECK); > > Why is this not CHECK_AND_CLEAR_PREEMPTED? We need to let the exception propagate all the way back to the VM entry point, and only then we can clear it. > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 196: > >> 194: static void do_deopt_after_thaw(JavaThread* thread); >> 195: static bool do_verify_after_thaw(JavaThread* thread, stackChunkOop chunk, outputStream* st); >> 196: static void log_frames(JavaThread* thread, bool dolog = true); > > Suggestion: > > static void log_frames(JavaThread* thread, bool do_log = true); I removed it altogether. I used it during debugging when I wanted to disable all frame logging except from certain places. But it's not really needed. > src/hotspot/share/runtime/javaThread.hpp line 492: > >> 490: // throw IE at the end of thawing before returning to Java. >> 491: bool _pending_interrupted_exception; >> 492: // We allow preemption on some klass initializion calls. > > Suggestion: > > // We allow preemption on some klass initialization calls. Fixed. > src/hotspot/share/runtime/javaThread.hpp line 507: > >> 505: >> 506: bool at_preemptable_init() { return _at_preemptable_init; } >> 507: void set_at_preemptable_init(bool b) { _at_preemptable_init = b; } > > If you are going align 503 and 504 then you may as well align these two lines as well Fixed. > src/hotspot/share/runtime/javaThread.hpp line 522: > >> 520: JavaThread* _thread; >> 521: public: >> 522: AtRedoVMCall(JavaThread *t) : _thread(t) { > > Suggestion: > > AtRedoVMCall(JavaThread* t) : _thread(t) { Fixed. > src/hotspot/share/runtime/javaThread.hpp line 524: > >> 522: AtRedoVMCall(JavaThread *t) : _thread(t) { >> 523: _thread->_interp_at_preemptable_vmcall_cnt++; >> 524: assert(_thread->_interp_at_preemptable_vmcall_cnt > 0, ""); > > Suggestion: > > assert(_thread->_interp_at_preemptable_vmcall_cnt > 0, "Unexpected count: %d", _thread->_interp_at_preemptable_vmcall_cnt); Added. > src/hotspot/share/runtime/javaThread.hpp line 528: > >> 526: ~AtRedoVMCall() { >> 527: _thread->_interp_at_preemptable_vmcall_cnt--; >> 528: assert(_thread->_interp_at_preemptable_vmcall_cnt >= 0, ""); > > Suggestion: > > assert(_thread->_interp_at_preemptable_vmcall_cnt > 0, "Unexpected count: %d", _thread->_interp_at_preemptable_vmcall_cnt); Added. > src/hotspot/share/runtime/objectMonitor.cpp line 315: > >> 313: } >> 314: >> 315: // Keep object protected during ObjectLocker preemption. > > I don't understand why this is necessary. Once the thread initializing the class finishes the initialization and calls `InstanceKlass::fence_and_clear_init_lock()`, the init_lock oop can be GC?ed, unless there is some other `Handle` protecting it (`ObjectMonitor::_object` is only a `WeakHandle`). But we could still have preempted virtual threads using the ObjectMonitor, so we need to protect the oop. In practice, I don?t see paths in `ObjectMonitor::resume_operation` where we try to get the oop out of the monitor, but I?m not sure if we want to break the invariant where the object needs to be alive while using its ObjectMonitor. > src/hotspot/share/runtime/synchronizer.cpp line 489: > >> 487: if (_thread->preempting()) { >> 488: // If preemption was cancelled we acquired the monitor after freezing >> 489: // the frames. Redoing the vm call later?in thaw will require us to > > I don't quite follow the control flow here - at what point have we frozen any frames? Was that deep inside `enter`? Yes, in `ObjectMonitor::enter_with_contention_mark` we first freezed the frames, but then the thread was able to acquire the monitor in `ObjectMonitor::vthread_monitor_enter` while trying to add itself to the `_entry_list`, so preemption was cancelled. For this `ObjectLocker` case, we will have to redo the VM call again after thawing so we need to release the monitor. > src/java.base/share/classes/java/lang/VirtualThread.java line 172: > >> 170: >> 171: // true when waiting in Object.wait, false for VM internal uninterruptible Object.wait >> 172: private volatile boolean interruptableWait; > > Suggestion: > > private volatile boolean interruptibleWait; Fixed. > src/java.base/share/classes/java/lang/VirtualThread.java line 605: > >> 603: if (s == WAITING || s == TIMED_WAITING) { >> 604: int newState; >> 605: boolean interruptable = interruptableWait; > > Suggestion: > > boolean interruptible = interruptibleWait; Fixed. > src/java.base/share/classes/jdk/internal/vm/PreemptedException.java line 31: > >> 29: * Internal exception used only by the VM. >> 30: */ >> 31: public class PreemptedException extends Exception { > > Should probably extend `RuntimeException` so that it is not considered a checked-exception Changed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445892049 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445884886 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445886314 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445886550 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445886740 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445886915 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445887072 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445889274 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445890688 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445890950 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445891154 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2445891449 From pchilanomate at openjdk.org Mon Oct 20 20:26:44 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 20 Oct 2025 20:26:44 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v6] In-Reply-To: References: Message-ID: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: PPC support ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27802/files - new: https://git.openjdk.org/jdk/pull/27802/files/6882db04..6000edb6 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=05 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=04-05 Stats: 167 lines in 17 files changed: 107 ins; 20 del; 40 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Mon Oct 20 20:26:45 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 20 Oct 2025 20:26:45 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths In-Reply-To: References: Message-ID: On Tue, 14 Oct 2025 20:20:55 GMT, Patricio Chilano Mateo wrote: >> https://github.com/openjdk/jdk/pull/27676 has been integrated which requires resolution. > >> #27676 has been integrated which requires resolution. >> > Thanks, merged with latest master. > @pchilano I've finished the ppc part: [reinrich at 99b9652](https://github.com/reinrich/jdk/commit/99b96528539dd7239711d9c70aebb1a4c1a7fdfe) > Great, thanks Richard! Pushed the changes. ------------- PR Comment: https://git.openjdk.org/jdk/pull/27802#issuecomment-3423592607 From valeriep at openjdk.org Mon Oct 20 23:17:03 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Mon, 20 Oct 2025 23:17:03 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v12] In-Reply-To: <3nnB5pkNnYqYi6OAH3u83PNmiO607_xwHXCmCeIE7gA=.371aa791-952e-4143-9012-1728dbf31ae9@github.com> References: <3nnB5pkNnYqYi6OAH3u83PNmiO607_xwHXCmCeIE7gA=.371aa791-952e-4143-9012-1728dbf31ae9@github.com> Message-ID: <2uIJVPehvByBLLKXHTHvkysLZIyHXsUvviNj1mbelxE=.379ce84d-7532-4a61-a63a-f4f54791b4e7@github.com> On Sun, 19 Oct 2025 02:18:43 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1034: > 1032: int ti0, ti1, ti2, ti3; > 1033: int a0, a1, a2, a3; > 1034: int w = K.length - 4; nit: 4 could be WB? ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2446328176 From valeriep at openjdk.org Mon Oct 20 23:51:06 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Mon, 20 Oct 2025 23:51:06 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v12] In-Reply-To: <3nnB5pkNnYqYi6OAH3u83PNmiO607_xwHXCmCeIE7gA=.371aa791-952e-4143-9012-1728dbf31ae9@github.com> References: <3nnB5pkNnYqYi6OAH3u83PNmiO607_xwHXCmCeIE7gA=.371aa791-952e-4143-9012-1728dbf31ae9@github.com> Message-ID: On Sun, 19 Oct 2025 02:18:43 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng Just have some minor questions and nit. src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 589: > 587: > 588: // Lookup table for inverse substitution transform of last round as > 589: // described in the international journal article referenced. Is there a link that I can look it up also? src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1180: > 1178: ^ T3[(ti0 >> 16) & 0xFF] & 0xFF0000 > 1179: ^ T0[(ti1 >> 8) & 0xFF] & 0xFF00 > 1180: ^ T1[ti2 & 0xFF] & 0xFF ^ K[w + 3]; Is this last round processing also based on spec or some journal? ------------- PR Review: https://git.openjdk.org/jdk/pull/27807#pullrequestreview-3358325489 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2446391365 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2446389723 From duke at openjdk.org Tue Oct 21 00:05:31 2025 From: duke at openjdk.org (Shawn M Emery) Date: Tue, 21 Oct 2025 00:05:31 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v13] In-Reply-To: References: Message-ID: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: Updates for code review comments from @valeriepeng ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27807/files - new: https://git.openjdk.org/jdk/pull/27807/files/5ea6933b..fdfd3892 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=12 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27807&range=11-12 Stats: 1 line in 1 file changed: 0 ins; 0 del; 1 mod Patch: https://git.openjdk.org/jdk/pull/27807.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27807/head:pull/27807 PR: https://git.openjdk.org/jdk/pull/27807 From duke at openjdk.org Tue Oct 21 00:05:34 2025 From: duke at openjdk.org (Shawn M Emery) Date: Tue, 21 Oct 2025 00:05:34 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v12] In-Reply-To: References: <3nnB5pkNnYqYi6OAH3u83PNmiO607_xwHXCmCeIE7gA=.371aa791-952e-4143-9012-1728dbf31ae9@github.com> Message-ID: On Mon, 20 Oct 2025 23:47:51 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Updates for code review comments from @valeriepeng > > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 589: > >> 587: >> 588: // Lookup table for inverse substitution transform of last round as >> 589: // described in the international journal article referenced. > > Is there a link that I can look it up also? Yes, it's the 3rd document cited for this class: https://www.internationaljournalcorner.com/index.php/ijird_ojs/article/view/134688 > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1034: > >> 1032: int ti0, ti1, ti2, ti3; >> 1033: int a0, a1, a2, a3; >> 1034: int w = K.length - 4; > > nit: 4 could be WB? Yes, I think that logic is acceptable. Fixed. > src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 1180: > >> 1178: ^ T3[(ti0 >> 16) & 0xFF] & 0xFF0000 >> 1179: ^ T0[(ti1 >> 8) & 0xFF] & 0xFF00 >> 1180: ^ T1[ti2 & 0xFF] & 0xFF ^ K[w + 3]; > > Is this last round processing also based on spec or some journal? Yes, it's an optimization based on the 3rd document cited for this class. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2446411532 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2446411617 PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2446411563 From duke at openjdk.org Tue Oct 21 00:21:05 2025 From: duke at openjdk.org (Shawn M Emery) Date: Tue, 21 Oct 2025 00:21:05 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: <3IhmbTDiDNPdMTe_K1OZx6sC67UGjObzOXwX8Ekp7pA=.0e742e44-4dba-4680-8f24-7321f8516071@github.com> References: <2_eqasQo7DtbnrxwxuFYvl_yhVh7P6wzlxwPEl_DB-Q=.ff81a933-2c29-4d69-826b-d1ccf04d2e1c@github.com> <5SGZrrpgKtrf7IKtfEDPFb4LnggKNoeaZpFVGuHu-p4=.1ec8dd26-e125-4c8a-ba22-9006c7da4024@github.com> <3IhmbTDiDNPdMTe_K1OZx6sC67UGjObzOXwX8Ekp7pA=.0e742e44-4dba-4680-8f24-7321f8516071@github.com> Message-ID: <56yK_DbZSYd0QHrNIo3kAy5wxesDss6VTTB0Ii6q_JU=.12a00665-fd8d-4e44-a169-5fec560fc0b2@github.com> On Fri, 17 Oct 2025 07:04:47 GMT, Valerie Peng wrote: >> I've removed this method and inlined this logic in the invGenRoundKeys method. > > Sure, this works as well. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2446428127 From duke at openjdk.org Tue Oct 21 00:21:07 2025 From: duke at openjdk.org (Shawn M Emery) Date: Tue, 21 Oct 2025 00:21:07 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v8] In-Reply-To: <5SGZrrpgKtrf7IKtfEDPFb4LnggKNoeaZpFVGuHu-p4=.1ec8dd26-e125-4c8a-ba22-9006c7da4024@github.com> References: <2_eqasQo7DtbnrxwxuFYvl_yhVh7P6wzlxwPEl_DB-Q=.ff81a933-2c29-4d69-826b-d1ccf04d2e1c@github.com> <5SGZrrpgKtrf7IKtfEDPFb4LnggKNoeaZpFVGuHu-p4=.1ec8dd26-e125-4c8a-ba22-9006c7da4024@github.com> Message-ID: On Fri, 17 Oct 2025 06:52:16 GMT, Shawn M Emery wrote: >> src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java line 976: >> >>> 974: * @param state [in, out] the round key for inverse mix column processing. >>> 975: */ >>> 976: private static void invMixRKey(int[] state) { >> >> nit: name the method "invMixColumns(int[])". This name matches the spec psuedo code and goes better with the "state" argument name. Or use "invMixRoundKey(int[] roundKey)"? > > I've removed this method and inlined this logic in the invGenRoundKeys method. Fixed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2446428552 From epeter at openjdk.org Tue Oct 21 05:50:02 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Tue, 21 Oct 2025 05:50:02 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v9] In-Reply-To: References: Message-ID: On Fri, 17 Oct 2025 17:19:09 GMT, Shawn M Emery wrote: >> test/micro/org/openjdk/bench/javax/crypto/AESDecrypt.java line 44: >> >>> 42: >>> 43: @Param("10000000") >>> 44: private int count; >> >> Drive-by comment / question: >> Did you do all benchmarking with this single (quite large) size? How are the results for much smaller sizes? It may be worth it to just get a nice plot that goes over a range of sizes, to see if it behaves as expected. > > The benchmarks listed in the PR description execute tests for data sizes ranging from 16 to 10_000_000 bytes for decryption and encryption. The difference in performance between the old and new code were within SE. Ok, sure. Why not list all the relevant sizes in the benchmark itself then? But totally up to you. This was just a hint/drive-by comment, feel free to mark it as resolved :) ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27807#discussion_r2446783134 From rcastanedalo at openjdk.org Tue Oct 21 13:51:52 2025 From: rcastanedalo at openjdk.org (Roberto =?UTF-8?B?Q2FzdGHDsWVkYQ==?= Lozano) Date: Tue, 21 Oct 2025 13:51:52 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v8] In-Reply-To: <4UN1z9fhxeUqUGagnZVEIFOyDb_mP8WaWUBwWO2HjFA=.93b7c9ad-443c-4fff-810d-7fe805ccbfaa@github.com> References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> <1gdeBnZ7YuIf9CgQW2bCXkDDBWPjUgRnickHts-fvzE=.e6e901ba-3e9f-41a2-9c68-167a879e9655@github.com> <2m1_XtiSsW_LaBRrkX4qv7AKtLOjNgnl4mUp3zisasE=.dda62164-7aa0-4c1a-b83f-fa40ba7902e5@github.com> <4374L3lkQK90wLxxOA7POBmIKNX2DFK-4pO4vj1bkuQ=.5b8d7825-a7f1-497f-ab66-02a85a266659@github.com> <4UN1z9fhxeUqUGagnZVEIFOyDb_mP8WaWUBwWO2HjFA=.93b7c9ad-443c-4fff-810d-7fe805ccbfaa@github.com> Message-ID: On Mon, 29 Sep 2025 08:40:01 GMT, Roland Westrelin wrote: >>> That sounds good to me, thank you for enforcing this Roland! I will re-run testing and have a new look at the changeset within the next days. >> >> Test results of b701d03ed335286587c4d2539dde715b091d30bd on top of jdk-26+14 look good. Will have a look at the code within the next days. > > @robcasloz Thanks for the patches. I added them. Hi @rwestrel, could you please have a look at the merge conflicts of this PR so that we can progress further with the review of this work? > Hi @rwestrel, could you please have a look at the merge conflicts of this PR so that we can progress further with the review of this work? The conflict is caused by the integration of [JDK-8360031](https://bugs.openjdk.org/browse/JDK-8360031), which relaxes the assertion in https://github.com/openjdk/jdk/blob/430041d366ddf450c2480c81608dde980dfa6d41/src/hotspot/share/opto/memnode.cpp#L4232 which is also touched by this changeset. Is the current assertion in mainline (after JDK-8360031) still valid in the context of this changeset? ------------- PR Comment: https://git.openjdk.org/jdk/pull/24570#issuecomment-3423349747 PR Comment: https://git.openjdk.org/jdk/pull/24570#issuecomment-3425213782 From rcastanedalo at openjdk.org Tue Oct 21 13:51:56 2025 From: rcastanedalo at openjdk.org (Roberto =?UTF-8?B?Q2FzdGHDsWVkYQ==?= Lozano) Date: Tue, 21 Oct 2025 13:51:56 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v8] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> <1gdeBnZ7YuIf9CgQW2bCXkDDBWPjUgRnickHts-fvzE=.e6e901ba-3e9f-41a2-9c68-167a879e9655@github.com> <2m1_XtiSsW_LaBRrkX4qv7AKtLOjNgnl4mUp3zisasE=.dda62164-7aa0-4c1a-b83f-fa40ba7902e5@github.com> <4374L3lkQK90wLxxOA7POBmIKNX2DFK-4pO4vj1bkuQ=.5b8d7825-a7f1-497f-ab66-02a85a266659@github.com> <4UN1z9fhxeUqUGagnZVEIFOyDb_mP8WaWUBwWO2HjFA=.93b7c9ad-443c-4fff-810d-7fe805ccbfaa@github.com> Message-ID: On Tue, 21 Oct 2025 07:41:37 GMT, Roberto Casta?eda Lozano wrote: > Is the current assertion in mainline (after JDK-8360031) still valid in the context of this changeset? I did a bit of testing and updating the asserted invariant to `(outcnt() > 0 && outcnt() <= 2) || Opcode() == Op_Initialize` seems to work. ------------- PR Comment: https://git.openjdk.org/jdk/pull/24570#issuecomment-3426782209 From valeriep at openjdk.org Tue Oct 21 18:22:20 2025 From: valeriep at openjdk.org (Valerie Peng) Date: Tue, 21 Oct 2025 18:22:20 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v13] In-Reply-To: References: Message-ID: <45hkD8RQ3TH-4dvjl_bN9dG0B4BSE3d1wXZAPMxeDSA=.84597563-35d7-43c4-bd7c-cad7da7e9277@github.com> On Tue, 21 Oct 2025 00:05:31 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng Changes look fine, thanks~ ------------- Marked as reviewed by valeriep (Reviewer). PR Review: https://git.openjdk.org/jdk/pull/27807#pullrequestreview-3362117409 From dholmes at openjdk.org Wed Oct 22 06:11:05 2025 From: dholmes at openjdk.org (David Holmes) Date: Wed, 22 Oct 2025 06:11:05 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: <8SlbTQ-LN3PIBw1M6hERwi8KyA02Avg5KrYrZISR_4o=.5ef0f9a9-48ef-4a36-a2ef-ffb2b77863ec@github.com> References: <8SlbTQ-LN3PIBw1M6hERwi8KyA02Avg5KrYrZISR_4o=.5ef0f9a9-48ef-4a36-a2ef-ffb2b77863ec@github.com> Message-ID: On Mon, 20 Oct 2025 19:07:10 GMT, Patricio Chilano Mateo wrote: >> src/hotspot/share/oops/klass.hpp line 583: >> >>> 581: // initializes the klass >>> 582: virtual void initialize(TRAPS); >>> 583: virtual void initialize_preemptable(TRAPS); >> >> Can we not define these on `instanceKlass` instead of `klass`? Seems really odd to declare virtual methods for which the base class version should never be called (they should be pure virtuals in that case I would think?). > > Ok. Just to double check, casting to `InstanceKlass*` where we call `initialize_preemptable` for the `invokestatic` and `getstatic/putstatic` cases should be always safe right? I don?t see how we could get there for an `ArrayKlass`. Hmmm seems there is an `objArrayKlass::initialize` (and an empty `typeArrayKlass::initialize`) but I don't know when such classes would be initialized. I would not expect them to be the target of invokestatic/getstatic/putstatic, nor for "new" but a "new array" would have to do the initialization of the bottom class (at least that is what `objArrayKlass::initialize` does) - and I don't think the current changes address that case. ?? Anyway leave the placement as-is. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2450537248 From dholmes at openjdk.org Wed Oct 22 06:26:03 2025 From: dholmes at openjdk.org (David Holmes) Date: Wed, 22 Oct 2025 06:26:03 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: <4RnCaK-UiAcxujqvgB6zbTe70SMXBkJDJieP9U0tzg4=.3fbda558-c9b3-4d5a-a0e0-e009005d9a50@github.com> References: <4RnCaK-UiAcxujqvgB6zbTe70SMXBkJDJieP9U0tzg4=.3fbda558-c9b3-4d5a-a0e0-e009005d9a50@github.com> Message-ID: <6OgwhFiJQKmo3c-NsbctFTk7kbYSI1_wNYZRZisq-tU=.e26dcbc5-3334-4d43-8525-dca8b204ff8e@github.com> On Mon, 20 Oct 2025 19:19:05 GMT, Patricio Chilano Mateo wrote: >> src/hotspot/share/runtime/objectMonitor.cpp line 315: >> >>> 313: } >>> 314: >>> 315: // Keep object protected during ObjectLocker preemption. >> >> I don't understand why this is necessary. > > Once the thread initializing the class finishes the initialization and calls `InstanceKlass::fence_and_clear_init_lock()`, the init_lock oop can be GC?ed, unless there is some other `Handle` protecting it (`ObjectMonitor::_object` is only a `WeakHandle`). But we could still have preempted virtual threads using the ObjectMonitor, so we need to protect the oop. In practice, I don?t see paths in `ObjectMonitor::resume_operation` where we try to get the oop out of the monitor, but I?m not sure if we want to break the invariant where the object needs to be alive while using its ObjectMonitor. I see - platforms threads have a Handle to the oop whilst they wait, but vthreads have left that code, so we need the OM to maintain the strong reference. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2450563031 From dholmes at openjdk.org Wed Oct 22 06:32:08 2025 From: dholmes at openjdk.org (David Holmes) Date: Wed, 22 Oct 2025 06:32:08 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: <4RnCaK-UiAcxujqvgB6zbTe70SMXBkJDJieP9U0tzg4=.3fbda558-c9b3-4d5a-a0e0-e009005d9a50@github.com> References: <4RnCaK-UiAcxujqvgB6zbTe70SMXBkJDJieP9U0tzg4=.3fbda558-c9b3-4d5a-a0e0-e009005d9a50@github.com> Message-ID: On Mon, 20 Oct 2025 19:20:31 GMT, Patricio Chilano Mateo wrote: >> src/hotspot/share/interpreter/linkResolver.cpp line 1122: >> >>> 1120: resolved_klass->initialize(CHECK); >>> 1121: } else if (static_mode == StaticMode::initialize_klass_preemptable) { >>> 1122: resolved_klass->initialize_preemptable(CHECK); >> >> Why is this not CHECK_AND_CLEAR_PREEMPTED? > > We need to let the exception propagate all the way back to the VM entry point, and only then we can clear it. Right - sorry - I though this was the entry point, but it is up in IRT. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2450573813 From duke at openjdk.org Thu Oct 23 03:24:08 2025 From: duke at openjdk.org (Shawn M Emery) Date: Thu, 23 Oct 2025 03:24:08 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v13] In-Reply-To: <45hkD8RQ3TH-4dvjl_bN9dG0B4BSE3d1wXZAPMxeDSA=.84597563-35d7-43c4-bd7c-cad7da7e9277@github.com> References: <45hkD8RQ3TH-4dvjl_bN9dG0B4BSE3d1wXZAPMxeDSA=.84597563-35d7-43c4-bd7c-cad7da7e9277@github.com> Message-ID: On Tue, 21 Oct 2025 18:19:18 GMT, Valerie Peng wrote: >> Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: >> >> Updates for code review comments from @valeriepeng > > Changes look fine, thanks~ Thank you @valeriepeng, @iwanowww, and @eme64 for your comments. After code review comment updates, all recommended regression tests have been executed and have passed (with all known failures). Benchmarks have been reran in each of the modes supported by intrinsics and these results have matched to those of pre-code review update. ------------- PR Comment: https://git.openjdk.org/jdk/pull/27807#issuecomment-3434921841 From duke at openjdk.org Thu Oct 23 03:24:09 2025 From: duke at openjdk.org (duke) Date: Thu, 23 Oct 2025 03:24:09 GMT Subject: RFR: 8326609: New AES implementation with updates specified in FIPS 197 [v13] In-Reply-To: References: Message-ID: On Tue, 21 Oct 2025 00:05:31 GMT, Shawn M Emery wrote: >> General: >> ----------- >> i) This work is to replace the existing AES cipher under the Cryptix license. >> >> ii) The lookup tables are employed for performance, but also for operating in constant time. >> >> iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. >> >> iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. >> >> Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. >> >> Correctness: >> ----------------- >> The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: >> >> i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass >> >> -intrinsics mode for: >> >> ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass >> >> iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures >> >> iv) jdk_security_infra: passed, with 48 known failures >> >> v) tier1 and tier2: all 110257 tests pass >> >> Security: >> ----------- >> In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. >> >> Performance: >> ------------------ >> All AES related benchmarks have been executed against the new and original Cryptix code: >> >> micro:org.openjdk.bench.javax.crypto.AES >> >> micro:org.openjdk.bench.javax.crypto.full.AESBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESExtraBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMBench >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream >> >> micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. >> >> micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) >> >> The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption re... > > Shawn M Emery has updated the pull request incrementally with one additional commit since the last revision: > > Updates for code review comments from @valeriepeng @smemery Your change (at version fdfd38929d3c7b725cf44312eba5d2f0d7da7b0a) is now ready to be sponsored by a Committer. ------------- PR Comment: https://git.openjdk.org/jdk/pull/27807#issuecomment-3434923911 From coleenp at openjdk.org Thu Oct 23 13:54:26 2025 From: coleenp at openjdk.org (Coleen Phillimore) Date: Thu, 23 Oct 2025 13:54:26 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v6] In-Reply-To: References: Message-ID: On Mon, 20 Oct 2025 20:26:44 GMT, Patricio Chilano Mateo wrote: >> If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. >> >> As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. >> >> This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. >> >> ### Summary of implementation >> >> The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. >> >> If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). >> >> ### Notes >> >> `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::mon... > > Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: > > PPC support Nice work! This is a first pass with comments. I haven't looked at the platform specific code or tests yet. src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp line 34: > 32: > 33: // Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap > 34: template Can we have a follow-on RFE to make SmallRegisterMap and it's new template in shared code. And only have the platform specific functions here? src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp line 73: > 71: bool update_map() const { return false; } > 72: bool walk_cont() const { return false; } > 73: bool include_argument_oops() const { return IncludeArgs; } You made this a template rather than having an _include_argument_oops property for performance? src/hotspot/share/interpreter/interpreterRuntime.cpp line 837: > 835: note_trap(current, Deoptimization::Reason_null_check); > 836: } > 837: CLEAR_PENDING_PREEMPTED_EXCEPTION; You clear this because we want the preempted exception to cause a return to here, but not return an exception to the interpreter to rethrow? Can you add a comment why, especially if I've got this wrong. src/hotspot/share/interpreter/linkResolver.hpp line 192: > 190: }; > 191: > 192: enum class ClassInitMode : uint8_t { If this is just a parameter, does this need to be uint8_t ? src/hotspot/share/oops/instanceKlass.cpp line 810: > 808: void InstanceKlass::initialize_preemptable(TRAPS) { > 809: if (this->should_be_initialized()) { > 810: PREEMPT_ON_INIT_SUPPORTED_ONLY(PreemptableInitCall pic(THREAD, this);) Are there any other uses of this macro? src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1123: > 1121: assert(!call.is_valid() || call.is_invokestatic(), "only invokestatic allowed"); > 1122: if (call.is_invokestatic() && call.size_of_parameters() > 0) { > 1123: assert(top_frame.interpreter_frame_expression_stack_size() > 0, "should have parameters in exp stack"); The "this" pointer will be on the top of the interpreter frame expression stack right? Are size of parameters ever 0 here then? src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1653: > 1651: } > 1652: inline intptr_t* anchor_mark_set_pd(); > 1653: inline void anchor_mark_clear_pd(); The code is really confused whether "pd" is the beginning of the method name or the end. Both are used but mostly in the beginning of the method. The freeze/thaw code uses it at the end so this is okay. src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1731: > 1729: int bci; > 1730: if (preempt_kind == Continuation::monitorenter) { > 1731: assert(top.is_interpreted_frame() || top.is_runtime_frame(), ""); So could you use precond() here since it's a precondition and you have no assert message? src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2061: > 2059: > 2060: // Only used for preemption on ObjectLocker > 2061: ObjectMonitor* _monitor; Rather than calling it monitor, you could call it _init_lock so that it makes more sense in the following code. The other reason to give it the same name as in initialization is so in the future we'll see the connection easily. src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2686: > 2684: > 2685: { > 2686: HandleMarkCleaner hmc(current); // Cleanup so._conth Handle Why doesn't a plain HandleMark do this? I think you chose HandleMarkCleaner because this is going to back to the interpreter code, so to be like JRT_ENTRY code. So add something like before going back to the interpreted Java code. src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2696: > 2694: > 2695: // These InterpreterRuntime entry points use JRT_ENTRY which uses a HandleMarkCleaner. > 2696: // Create a HandeMark to avoid destroying so._conth. I see. The comment is helpful. src/hotspot/share/runtime/javaThread.hpp line 540: > 538: } > 539: }; > 540: #endif Nit: add // ASSERT to the endif. I always wonder if these big blocks of code added to thread.hpp and javaThread.hpp should be in some new file, and just referenced from this. Not for this change. Just a general comment. src/hotspot/share/runtime/javaThread.hpp line 1372: > 1370: }; > 1371: > 1372: class PreemptableInitCall { If this is only used in instanceKlass.cpp, I think the definition should be there and not in javaThread.hpp. It's not using private methds in javaThread.hpp as far as I can tell. src/hotspot/share/runtime/javaThread.hpp line 1421: > 1419: }; > 1420: > 1421: class ThreadWaitingForClassInit : public StackObj { I think this should be in instanceKlass.cpp also near the single caller (unless there's another caller that I don't see). src/hotspot/share/runtime/objectMonitor.cpp line 319: > 317: check_object_context(); > 318: if (_object_strong.is_empty()) { > 319: if (Thread::TrySpinAcquire(&_object_strong_lock)) { @toxaart Here's a new use of this SpinAcquire/SpinRelease as a TrySpinAcquire. ------------- PR Review: https://git.openjdk.org/jdk/pull/27802#pullrequestreview-3367219722 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2454882893 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2454888028 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2453118945 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2453122999 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2454901203 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2455074172 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2455083202 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2455091984 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2455104045 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2455123338 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2455138736 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2455164468 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2454898223 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2454906094 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2455185797 From coleenp at openjdk.org Thu Oct 23 13:54:28 2025 From: coleenp at openjdk.org (Coleen Phillimore) Date: Thu, 23 Oct 2025 13:54:28 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: References: <8SlbTQ-LN3PIBw1M6hERwi8KyA02Avg5KrYrZISR_4o=.5ef0f9a9-48ef-4a36-a2ef-ffb2b77863ec@github.com> Message-ID: On Wed, 22 Oct 2025 06:08:43 GMT, David Holmes wrote: >> Ok. Just to double check, casting to `InstanceKlass*` where we call `initialize_preemptable` for the `invokestatic` and `getstatic/putstatic` cases should be always safe right? I don?t see how we could get there for an `ArrayKlass`. > > Hmmm seems there is an `objArrayKlass::initialize` (and an empty `typeArrayKlass::initialize`) but I don't know when such classes would be initialized. I would not expect them to be the target of invokestatic/getstatic/putstatic, nor for "new" but a "new array" would have to do the initialization of the bottom class (at least that is what `objArrayKlass::initialize` does) - and I don't think the current changes address that case. ?? > > Anyway leave the placement as-is. Would an initialize_preemptable() = 0 be better? then you can see if it's called by anything other than InstanceKlass. Klass is always abstract. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2454914030 From duke at openjdk.org Thu Oct 23 19:40:38 2025 From: duke at openjdk.org (Shawn M Emery) Date: Thu, 23 Oct 2025 19:40:38 GMT Subject: Integrated: 8326609: New AES implementation with updates specified in FIPS 197 In-Reply-To: References: Message-ID: On Tue, 14 Oct 2025 19:33:41 GMT, Shawn M Emery wrote: > General: > ----------- > i) This work is to replace the existing AES cipher under the Cryptix license. > > ii) The lookup tables are employed for performance, but also for operating in constant time. > > iii) Several loops have been unrolled for optimization purposes, but are harder to read and don't meet coding style guidelines. > > iv) None of the AES related intrinsics has been modified in this PR, but the new code has been updated to use the intrinsics related hooks for the AES block and key table arguments. > > Note: I've purposefully not seen the original Cryptix code, so when making a code review comment please don't quote the code in the AESCrypt.java file. > > Correctness: > ----------------- > The following AES-specific regression tests have passed in intrinsics (default) and non-intrinsic (-Xint) modes: > > i) test/jdk/com/sun/crypto/provider/Cipher/AES: all 27 tests pass > > -intrinsics mode for: > > ii) test/hotspot/jtreg/compiler/codegen/aes: all 4 tests pass > > iii) jck:api/java_security, jck:api/javax_crypto, jck:api/javax_net, jck:api/javax_security, jck:api/org_ietf, and jck:api/javax_xml/crypto: passed, with 10 known failures > > iv) jdk_security_infra: passed, with 48 known failures > > v) tier1 and tier2: all 110257 tests pass > > Security: > ----------- > In order to prevent side-channel (timing and differential power analysis) attacks the code has been constructed to operate in constant time and does not use conditionals based on the key or key expansion table. This is accomplished by using lookup tables in both the cipher and inverse cipher of AES. > > Performance: > ------------------ > All AES related benchmarks have been executed against the new and original Cryptix code: > > micro:org.openjdk.bench.javax.crypto.AES > > micro:org.openjdk.bench.javax.crypto.full.AESBench > > micro:org.openjdk.bench.javax.crypto.full.AESExtraBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMBench > > micro:org.openjdk.bench.javax.crypto.full.AESGCMByteBuffer > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherInputStream > > micro:org.openjdk.bench.javax.crypto.full.AESGCMCipherOutputStream > > micro:org.openjdk.bench.javax.crypto.full.AESKeyWrapBench. > > micro:org.openjdk.bench.java.security.CipherSuiteBench (AES) > > The benchmarks were executed in different compiler modes (default (no compiler options), -Xint, and -Xcomp) and on two different architectures (x86 and arm64) with the following encryption results: > > i) Default (no JVM options, non-intrinsics) mode: > > a) Encryption: the new code performed better for b... This pull request has now been integrated. Changeset: 62f11cd4 Author: Shawn M Emery Committer: Valerie Peng URL: https://git.openjdk.org/jdk/commit/62f11cd4070f21ad82eebbb5319bdbbf4e13f9cf Stats: 2991 lines in 18 files changed: 1476 ins; 1473 del; 42 mod 8326609: New AES implementation with updates specified in FIPS 197 Reviewed-by: valeriep ------------- PR: https://git.openjdk.org/jdk/pull/27807 From pchilanomate at openjdk.org Thu Oct 23 22:25:53 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Thu, 23 Oct 2025 22:25:53 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v7] In-Reply-To: References: Message-ID: <9IlDXdLcZSa3JCBBn9J8JUYWvlfKukDOlP1oqeTJRAQ=.6642ab0e-67ad-4776-b46c-acbee0136138@github.com> > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request incrementally with three additional commits since the last revision: - rename _monitor to _init_lock - Extra comments from Coleen - define methods in resolve_from_cache with TRAPS and use CHECK_AND_CLEAR_PREEMPTED ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27802/files - new: https://git.openjdk.org/jdk/pull/27802/files/6000edb6..2786b1d8 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=06 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=05-06 Stats: 95 lines in 7 files changed: 34 ins; 38 del; 23 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Thu Oct 23 22:34:14 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Thu, 23 Oct 2025 22:34:14 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v6] In-Reply-To: References: Message-ID: On Thu, 23 Oct 2025 11:59:02 GMT, Coleen Phillimore wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> PPC support > > src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp line 34: > >> 32: >> 33: // Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap >> 34: template > > Can we have a follow-on RFE to make SmallRegisterMap and it's new template in shared code. And only have the platform specific functions here? Yes, good idea. > src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp line 73: > >> 71: bool update_map() const { return false; } >> 72: bool walk_cont() const { return false; } >> 73: bool include_argument_oops() const { return IncludeArgs; } > > You made this a template rather than having an _include_argument_oops property for performance? Not really, using a bool member would work too. > src/hotspot/share/interpreter/interpreterRuntime.cpp line 837: > >> 835: note_trap(current, Deoptimization::Reason_null_check); >> 836: } >> 837: CLEAR_PENDING_PREEMPTED_EXCEPTION; > > You clear this because we want the preempted exception to cause a return to here, but not return an exception to the interpreter to rethrow? Can you add a comment why, especially if I've got this wrong. You got it right. I realized we can remove this macro which looks odd here, and use `CHECK_AND_CLEAR_PREEMPTED` at the actual VM entry point as we have for the `new` case. We only needed to declare `resolve_invoke` with the `TRAPS` parameter (as it should have been already). I did the same?with `resolve_get_put`, and also fixed the other methods in `resolve_from_cache` for consistency. Let me know if you still want a comment about not wanting to throw this exception to Java (not sure where to place it). > src/hotspot/share/interpreter/linkResolver.hpp line 192: > >> 190: }; >> 191: >> 192: enum class ClassInitMode : uint8_t { > > If this is just a parameter, does this need to be uint8_t ? Right, removed. > src/hotspot/share/oops/instanceKlass.cpp line 810: > >> 808: void InstanceKlass::initialize_preemptable(TRAPS) { >> 809: if (this->should_be_initialized()) { >> 810: PREEMPT_ON_INIT_SUPPORTED_ONLY(PreemptableInitCall pic(THREAD, this);) > > Are there any other uses of this macro? I missed to remove this when ppc was added. Removed now. > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1123: > >> 1121: assert(!call.is_valid() || call.is_invokestatic(), "only invokestatic allowed"); >> 1122: if (call.is_invokestatic() && call.size_of_parameters() > 0) { >> 1123: assert(top_frame.interpreter_frame_expression_stack_size() > 0, "should have parameters in exp stack"); > > The "this" pointer will be on the top of the interpreter frame expression stack right? Are size of parameters ever 0 here then? This is only for `invokestatic` so no this pointer. > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1653: > >> 1651: } >> 1652: inline intptr_t* anchor_mark_set_pd(); >> 1653: inline void anchor_mark_clear_pd(); > > The code is really confused whether "pd" is the beginning of the method name or the end. Both are used but mostly in the beginning of the method. The freeze/thaw code uses it at the end so this is okay. I added `patch_pd_unused` in 8359222 which should have been `patch_unused_pd`. :) > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1731: > >> 1729: int bci; >> 1730: if (preempt_kind == Continuation::monitorenter) { >> 1731: assert(top.is_interpreted_frame() || top.is_runtime_frame(), ""); > > So could you use precond() here since it's a precondition and you have no assert message? Yes, but don?t really see the benefit. It?s replacing a null string for `precond` in a crash. > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2061: > >> 2059: >> 2060: // Only used for preemption on ObjectLocker >> 2061: ObjectMonitor* _monitor; > > Rather than calling it monitor, you could call it _init_lock so that it makes more sense in the following code. The other reason to give it the same name as in initialization is so in the future we'll see the connection easily. Done. > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2686: > >> 2684: >> 2685: { >> 2686: HandleMarkCleaner hmc(current); // Cleanup so._conth Handle > > Why doesn't a plain HandleMark do this? > I think you chose HandleMarkCleaner because this is going to back to the interpreter code, so to be like JRT_ENTRY code. > So add something like before going back to the interpreted Java code. A full `HandleMark` works too. It?s just that there is already a `HandleMark` in the callstack (from the original upcall to Java from the carrier thread), so we can use a `HandleMarkCleaner` here. > src/hotspot/share/runtime/javaThread.hpp line 540: > >> 538: } >> 539: }; >> 540: #endif > > Nit: add // ASSERT to the endif. > I always wonder if these big blocks of code added to thread.hpp and javaThread.hpp should be in some new file, and just referenced from this. Not for this change. Just a general comment. Done. > src/hotspot/share/runtime/javaThread.hpp line 1372: > >> 1370: }; >> 1371: >> 1372: class PreemptableInitCall { > > If this is only used in instanceKlass.cpp, I think the definition should be there and not in javaThread.hpp. It's not using private methds in javaThread.hpp as far as I can tell. Moved, and also `ThreadWaitingForClassInit`. Should I move pre-existent `ThreadInClassInitializer` too? > src/hotspot/share/runtime/javaThread.hpp line 1421: > >> 1419: }; >> 1420: >> 1421: class ThreadWaitingForClassInit : public StackObj { > > I think this should be in instanceKlass.cpp also near the single caller (unless there's another caller that I don't see). Done. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457645698 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457646549 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457643620 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457645117 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457648892 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457658418 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457660576 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457662495 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457664724 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457669384 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457670686 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457647838 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457649861 From pchilanomate at openjdk.org Thu Oct 23 22:34:15 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Thu, 23 Oct 2025 22:34:15 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: References: <8SlbTQ-LN3PIBw1M6hERwi8KyA02Avg5KrYrZISR_4o=.5ef0f9a9-48ef-4a36-a2ef-ffb2b77863ec@github.com> Message-ID: On Thu, 23 Oct 2025 12:11:08 GMT, Coleen Phillimore wrote: >> Hmmm seems there is an `objArrayKlass::initialize` (and an empty `typeArrayKlass::initialize`) but I don't know when such classes would be initialized. I would not expect them to be the target of invokestatic/getstatic/putstatic, nor for "new" but a "new array" would have to do the initialization of the bottom class (at least that is what `objArrayKlass::initialize` does) - and I don't think the current changes address that case. ?? >> >> Anyway leave the placement as-is. > > Would an initialize_preemptable() = 0 be better? then you can see if it's called by anything other than InstanceKlass. Klass is always abstract. We can but we would have to implement it for `ArrayKlass` too which is kind of the same of what we have now. We have a `ShouldNotReachHere()` in `Klass::initialize_preemptable` (following the same pattern as `Klass::initialize`), so we will catch anything other than `InstanceKlass`. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2457655990 From epeter at openjdk.org Fri Oct 24 09:56:25 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Fri, 24 Oct 2025 09:56:25 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v15] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: On Fri, 24 Oct 2025 09:40:45 GMT, Emanuel Peter wrote: >> src/hotspot/share/opto/memnode.hpp line 1424: >> >>> 1422: template ProjNode* apply_to_narrow_mem_projs(DUIterator& i, Callback callback) const { >>> 1423: return apply_to_narrow_mem_projs_any_iterator(UsesIterator(i, this), callback); >>> 1424: } >> >> Do we really need to have a `public` API where we can pass in an iterator? What if someone uses it with an iterator for the wrong node? >> >> The only use for it seems to be [ConnectionGraph::split_unique_types](https://github.com/openjdk/jdk/pull/24570/files#diff-03f7ae3cf79ff61be6e4f0590b7809a87825b073341fdbfcf36143b99c304474R4467) with `DUIterator`. >> >> Is there a reason you want to pass the iterator explicitly? > > Also: why does it return anything? What kind of callback is exected here? I think the public API deserves a bit of documentation. The use in [ConnectionGraph::split_unique_types](https://github.com/openjdk/jdk/pull/24570/files#diff-03f7ae3cf79ff61be6e4f0590b7809a87825b073341fdbfcf36143b99c304474R4467) really does not need a return value, and also could work fine with a `callback` that return `void`. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459562042 From epeter at openjdk.org Fri Oct 24 09:56:23 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Fri, 24 Oct 2025 09:56:23 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v15] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: On Mon, 29 Sep 2025 08:44:51 GMT, Roland Westrelin wrote: >> An `Initialize` node for an `Allocate` node is created with a memory >> `Proj` of adr type raw memory. In order for stores to be captured, the >> memory state out of the allocation is a `MergeMem` with slices for the >> various object fields/array element set to the raw memory `Proj` of >> the `Initialize` node. If `Phi`s need to be created during later >> transformations from this memory state, The `Phi` for a particular >> slice gets its adr type from the type of the `Proj` which is raw >> memory. If during macro expansion, the `Allocate` is found to have no >> use and so can be removed, the `Proj` out of the `Initialize` is >> replaced by the memory state on input to the `Allocate`. A `Phi` for >> some slice for a field of an object will end up with the raw memory >> state on input to the `Allocate` node. As a result, memory state at >> the `Phi` is incorrect and incorrect execution can happen. >> >> The fix I propose is, rather than have a single `Proj` for the memory >> state out of the `Initialize` with adr type raw memory, to use one >> `Proj` per slice added to the memory state after the `Initalize`. Each >> of the `Proj` should return the right adr type for its slice. For that >> I propose having a new type of `Proj`: `NarrowMemProj` that captures >> the right adr type. >> >> Logic for the construction of the `Allocate`/`Initialize` subgraph is >> tweaked so the right adr type captured in is own `NarrowMemProj` is >> added to the memory sugraph. Code that removes an allocation or moves >> it also has to be changed so it correctly takes the multiple memory >> projections out of the `Initialize` node into account. >> >> One tricky issue is that when EA split types for a scalar replaceable >> `Allocate` node: >> >> 1- the adr type captured in the `NarrowMemProj` becomes out of sync >> with the type of the slices for the allocation >> >> 2- before EA, the memory state for one particular field out of the >> `Initialize` node can be used for a `Store` to the just allocated >> object or some other. So we can have a chain of `Store`s, some to >> the newly allocated object, some to some other objects, all of them >> using the state of `NarrowMemProj` out of the `Initialize`. After >> split unique types, the `NarrowMemProj` is for the slice of a >> particular allocation. So `Store`s to some other objects shouldn't >> use that memory state but the memory state before the `Allocate`. >> >> For that, I added logic to update the adr type of `NarrowMemProj` >> during split uni... > > Roland Westrelin has updated the pull request incrementally with two additional commits since the last revision: > > - review > - Roberto's patches @rwestrel Thanks for your continued work on this, and you patience with the slow reviews ? I stumbled a bit over the many overloads of `apply_to_narrow_mem_projs`, and left some comments for that below. There are really only one use of `already_has_narrow_mem_proj_with_adr_type` and one of `apply_to_narrow_mem_projs`, and you have a whole lot of methods that implement a lot of abstractions that confuse me. Is there a plan behind this, or just an artefact of many refactorings? Could we not just implement those 2 methods directly using `apply_to_projs_any_iterator`? I'll continue reviewing other parts now. src/hotspot/share/opto/escape.cpp line 4457: > 4455: const TypePtr* new_adr_type = tinst->add_offset(adr_type->offset()); > 4456: bool already_has_narrow_mem_proj_with_adr_type = init->already_has_narrow_mem_proj_with_adr_type(new_adr_type); > 4457: if (adr_type != new_adr_type && !already_has_narrow_mem_proj_with_adr_type) { Nit: we could avoid the iteration inside `already_has_narrow_mem_proj_with_adr_type` if we did the `adr_type != new_adr_type` first. Feel free to ignore if you think this is a micro-optimization ;) src/hotspot/share/opto/escape.cpp line 4467: > 4465: return MultiNode::CONTINUE; > 4466: }; > 4467: init->apply_to_narrow_mem_projs(i, process_narrow_proj); Is there a reason why this is not a `init->for_each_narrow_mem_proj(callback)`, that has an internal iterator? Because with this API, I'm wondering: What would happen if I feed `apply_to_narrow_mem_projs` an iterator that does not belong to the `init`? src/hotspot/share/opto/memnode.cpp line 5491: > 5489: }; > 5490: return apply_to_narrow_mem_projs(find_proj, adr_type) != nullptr; > 5491: } Am I seeing this right: `already_has_narrow_mem_proj_with_adr_type` calls `apply_to_narrow_mem_projs` with `callback = find_proj`. `find_proj` returns `BREAK_AND_RETURN_CURRENT_PROJ`, which is an element from `ApplyToProjs`. That would mean that when we call the `callback`, we get an enum element, and not a boolean, right? If that is the case, you should probably not do an implicit comparison on this line: ` if (proj->adr_type() == adr_type && callback(proj->as_NarrowMemProj())) {` Hotspot style guide does not like implicit conversion. You should use an explicit comparison. I think it would also be more clear what is happening. Currently, I'm a bit confused. All the overloadings of `apply_to_narrow_mem_projs` make it a bit hard to see what goes where :/ I wonder if we really need all that complexity. src/hotspot/share/opto/memnode.hpp line 1408: > 1406: template ProjNode* apply_to_narrow_mem_projs_any_iterator(Iterator i, Callback callback) const { > 1407: auto filter = [&](ProjNode* proj) { > 1408: if (proj->is_NarrowMemProj() && callback(proj->as_NarrowMemProj())) { What does the `callback` return here? Are we sure this is not an implicit zero/null check, that the hotspot style guide would not be happy with? src/hotspot/share/opto/memnode.hpp line 1424: > 1422: template ProjNode* apply_to_narrow_mem_projs(DUIterator& i, Callback callback) const { > 1423: return apply_to_narrow_mem_projs_any_iterator(UsesIterator(i, this), callback); > 1424: } Do we really need to have a `public` API where we can pass in an iterator? What if someone uses it with an iterator for the wrong node? The only use for it seems to be [ConnectionGraph::split_unique_types](https://github.com/openjdk/jdk/pull/24570/files#diff-03f7ae3cf79ff61be6e4f0590b7809a87825b073341fdbfcf36143b99c304474R4467) with `DUIterator`. Is there a reason you want to pass the iterator explicitly? src/hotspot/share/opto/memnode.hpp line 1428: > 1426: template ProjNode* apply_to_narrow_mem_projs(DUIterator_Fast& imax, DUIterator_Fast& i, Callback callback) const { > 1427: return apply_to_narrow_mem_projs_any_iterator(UsesIteratorFast(imax, i, this), callback); > 1428: } Is there any use for this method? I could not find any. ------------- PR Review: https://git.openjdk.org/jdk/pull/24570#pullrequestreview-3375302849 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459494172 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459451788 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459537600 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459569501 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459482037 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459456185 From epeter at openjdk.org Fri Oct 24 09:56:24 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Fri, 24 Oct 2025 09:56:24 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v15] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: On Fri, 24 Oct 2025 09:12:43 GMT, Emanuel Peter wrote: >> Roland Westrelin has updated the pull request incrementally with two additional commits since the last revision: >> >> - review >> - Roberto's patches > > src/hotspot/share/opto/escape.cpp line 4467: > >> 4465: return MultiNode::CONTINUE; >> 4466: }; >> 4467: init->apply_to_narrow_mem_projs(i, process_narrow_proj); > > Is there a reason why this is not a `init->for_each_narrow_mem_proj(callback)`, that has an internal iterator? > > Because with this API, I'm wondering: What would happen if I feed `apply_to_narrow_mem_projs` an iterator that does not belong to the `init`? If it was a plain `for_each_narrow_mem_proj`, your `callback` would also not have to return anything, you could remove this line: `return MultiNode::CONTINUE;`. > src/hotspot/share/opto/memnode.hpp line 1424: > >> 1422: template ProjNode* apply_to_narrow_mem_projs(DUIterator& i, Callback callback) const { >> 1423: return apply_to_narrow_mem_projs_any_iterator(UsesIterator(i, this), callback); >> 1424: } > > Do we really need to have a `public` API where we can pass in an iterator? What if someone uses it with an iterator for the wrong node? > > The only use for it seems to be [ConnectionGraph::split_unique_types](https://github.com/openjdk/jdk/pull/24570/files#diff-03f7ae3cf79ff61be6e4f0590b7809a87825b073341fdbfcf36143b99c304474R4467) with `DUIterator`. > > Is there a reason you want to pass the iterator explicitly? Also: why does it return anything? What kind of callback is exected here? I think the public API deserves a bit of documentation. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459559137 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459554017 From epeter at openjdk.org Fri Oct 24 10:41:16 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Fri, 24 Oct 2025 10:41:16 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v15] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: <_qiLczbSCNh63QdZCymWBBv0nh82yEO5euGDw9RyMH8=.8142c826-00bd-4aab-9574-cc3004b04dd3@github.com> On Mon, 29 Sep 2025 08:44:51 GMT, Roland Westrelin wrote: >> An `Initialize` node for an `Allocate` node is created with a memory >> `Proj` of adr type raw memory. In order for stores to be captured, the >> memory state out of the allocation is a `MergeMem` with slices for the >> various object fields/array element set to the raw memory `Proj` of >> the `Initialize` node. If `Phi`s need to be created during later >> transformations from this memory state, The `Phi` for a particular >> slice gets its adr type from the type of the `Proj` which is raw >> memory. If during macro expansion, the `Allocate` is found to have no >> use and so can be removed, the `Proj` out of the `Initialize` is >> replaced by the memory state on input to the `Allocate`. A `Phi` for >> some slice for a field of an object will end up with the raw memory >> state on input to the `Allocate` node. As a result, memory state at >> the `Phi` is incorrect and incorrect execution can happen. >> >> The fix I propose is, rather than have a single `Proj` for the memory >> state out of the `Initialize` with adr type raw memory, to use one >> `Proj` per slice added to the memory state after the `Initalize`. Each >> of the `Proj` should return the right adr type for its slice. For that >> I propose having a new type of `Proj`: `NarrowMemProj` that captures >> the right adr type. >> >> Logic for the construction of the `Allocate`/`Initialize` subgraph is >> tweaked so the right adr type captured in is own `NarrowMemProj` is >> added to the memory sugraph. Code that removes an allocation or moves >> it also has to be changed so it correctly takes the multiple memory >> projections out of the `Initialize` node into account. >> >> One tricky issue is that when EA split types for a scalar replaceable >> `Allocate` node: >> >> 1- the adr type captured in the `NarrowMemProj` becomes out of sync >> with the type of the slices for the allocation >> >> 2- before EA, the memory state for one particular field out of the >> `Initialize` node can be used for a `Store` to the just allocated >> object or some other. So we can have a chain of `Store`s, some to >> the newly allocated object, some to some other objects, all of them >> using the state of `NarrowMemProj` out of the `Initialize`. After >> split unique types, the `NarrowMemProj` is for the slice of a >> particular allocation. So `Store`s to some other objects shouldn't >> use that memory state but the memory state before the `Allocate`. >> >> For that, I added logic to update the adr type of `NarrowMemProj` >> during split uni... > > Roland Westrelin has updated the pull request incrementally with two additional commits since the last revision: > > - review > - Roberto's patches Second batch of comments. Will continue later at `class NarrowMemProjNode`. src/hotspot/share/opto/library_call.cpp line 5612: > 5610: return MultiNode::CONTINUE; > 5611: }; > 5612: init->apply_to_projs(move_proj, TypeFunc::Memory); A "for each" using callback with `void` return would create a little less noise. src/hotspot/share/opto/macro.cpp line 1609: > 1607: // null, this allocation does have an InitializeNode but this logic can't locate it (see comment in > 1608: // PhaseMacroExpand::initialize_object()). > 1609: MemBarNode* mb = MemBarNode::make(C, Op_MemBarStoreStore, Compile::AliasIdxRaw); Would it not be nicer to have the explanation here than below, and refer from below to here? Would help the reading flow ;) src/hotspot/share/opto/macro.cpp line 1638: > 1636: Node* ctrl = new ProjNode(init, TypeFunc::Control); > 1637: transform_later(ctrl); > 1638: Node* existing_raw_mem_proj = nullptr; Tiny suggestion: `existing_raw_mem_proj` -> `old_raw_mem_proj`, to emphasize that it is old, and will be replaced. src/hotspot/share/opto/macro.cpp line 1646: > 1644: return MultiNode::CONTINUE; > 1645: }; > 1646: init->apply_to_projs(find_raw_mem, TypeFunc::Memory); A "for each" with `void` return callback would reduce noise. src/hotspot/share/opto/memnode.cpp line 5468: > 5466: }; > 5467: apply_to_projs(imax, i, replace_proj, TypeFunc::Memory); > 5468: } Ouff, it's a little sad that we modify the iterator both outside and inside the method call. But I don't have a better solution now either. It may just be what we have to do. src/hotspot/share/opto/multnode.cpp line 63: > 61: }; > 62: return apply_to_projs(find_proj, which_proj, is_io_use); > 63: } A `find_first` API with a boolean predicate could reduce some noise here. src/hotspot/share/opto/multnode.cpp line 67: > 65: template ProjNode* MultiNode::apply_to_projs(Callback callback, uint which_proj, bool is_io_use) const { > 66: auto filter = [&](ProjNode* proj) { > 67: if (proj->_is_io_use == is_io_use && callback(proj)) { implicit zero check? src/hotspot/share/opto/multnode.cpp line 81: > 79: return CONTINUE; > 80: }; > 81: apply_to_projs(count_projs, which_proj); for each pattern would have been nice here. ------------- PR Review: https://git.openjdk.org/jdk/pull/24570#pullrequestreview-3375556080 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459657397 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459675950 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459698388 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459680660 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459717105 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459729036 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459730207 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459733750 From epeter at openjdk.org Fri Oct 24 10:41:17 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Fri, 24 Oct 2025 10:41:17 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v15] In-Reply-To: <_qiLczbSCNh63QdZCymWBBv0nh82yEO5euGDw9RyMH8=.8142c826-00bd-4aab-9574-cc3004b04dd3@github.com> References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> <_qiLczbSCNh63QdZCymWBBv0nh82yEO5euGDw9RyMH8=.8142c826-00bd-4aab-9574-cc3004b04dd3@github.com> Message-ID: On Fri, 24 Oct 2025 10:18:04 GMT, Emanuel Peter wrote: >> Roland Westrelin has updated the pull request incrementally with two additional commits since the last revision: >> >> - review >> - Roberto's patches > > src/hotspot/share/opto/macro.cpp line 1646: > >> 1644: return MultiNode::CONTINUE; >> 1645: }; >> 1646: init->apply_to_projs(find_raw_mem, TypeFunc::Memory); > > A "for each" with `void` return callback would reduce noise. Alternatives: - `init->unique_raw_mem_proj()` - `init->unique_out(raw_mem_proj_predicate)` with boolean predicate. Both of those would assert if we find multiple or none. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2459689999 From epeter at openjdk.org Fri Oct 24 13:42:27 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Fri, 24 Oct 2025 13:42:27 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v15] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: On Mon, 29 Sep 2025 08:44:51 GMT, Roland Westrelin wrote: >> An `Initialize` node for an `Allocate` node is created with a memory >> `Proj` of adr type raw memory. In order for stores to be captured, the >> memory state out of the allocation is a `MergeMem` with slices for the >> various object fields/array element set to the raw memory `Proj` of >> the `Initialize` node. If `Phi`s need to be created during later >> transformations from this memory state, The `Phi` for a particular >> slice gets its adr type from the type of the `Proj` which is raw >> memory. If during macro expansion, the `Allocate` is found to have no >> use and so can be removed, the `Proj` out of the `Initialize` is >> replaced by the memory state on input to the `Allocate`. A `Phi` for >> some slice for a field of an object will end up with the raw memory >> state on input to the `Allocate` node. As a result, memory state at >> the `Phi` is incorrect and incorrect execution can happen. >> >> The fix I propose is, rather than have a single `Proj` for the memory >> state out of the `Initialize` with adr type raw memory, to use one >> `Proj` per slice added to the memory state after the `Initalize`. Each >> of the `Proj` should return the right adr type for its slice. For that >> I propose having a new type of `Proj`: `NarrowMemProj` that captures >> the right adr type. >> >> Logic for the construction of the `Allocate`/`Initialize` subgraph is >> tweaked so the right adr type captured in is own `NarrowMemProj` is >> added to the memory sugraph. Code that removes an allocation or moves >> it also has to be changed so it correctly takes the multiple memory >> projections out of the `Initialize` node into account. >> >> One tricky issue is that when EA split types for a scalar replaceable >> `Allocate` node: >> >> 1- the adr type captured in the `NarrowMemProj` becomes out of sync >> with the type of the slices for the allocation >> >> 2- before EA, the memory state for one particular field out of the >> `Initialize` node can be used for a `Store` to the just allocated >> object or some other. So we can have a chain of `Store`s, some to >> the newly allocated object, some to some other objects, all of them >> using the state of `NarrowMemProj` out of the `Initialize`. After >> split unique types, the `NarrowMemProj` is for the slice of a >> particular allocation. So `Store`s to some other objects shouldn't >> use that memory state but the memory state before the `Allocate`. >> >> For that, I added logic to update the adr type of `NarrowMemProj` >> during split uni... > > Roland Westrelin has updated the pull request incrementally with two additional commits since the last revision: > > - review > - Roberto's patches Last batch of comments for this round. I'm quite happy with the solution @rwestrel , thanks for coming up with this ? I think really all my suggestions / requests are about the complexity / implementation around the `apply_...` methods. It seems to me that it makes sense to internally implement it with the `apply_...` functionality that take callbacks which return a `ApplyToProjs`. But I think many of the `public` API methods could have a simpler semantic, for example many could just be `for_each_...` calls that just take a callback with void return / no return. src/hotspot/share/opto/memnode.cpp line 5476: > 5474: > 5475: > 5476: template ProjNode* InitializeNode::apply_to_narrow_mem_projs(Callback callback, const TypePtr* adr_type) const { Another nit: we will only ever return a `NarrowMemProj`, so you might as well make the return value more precise ;) src/hotspot/share/opto/multnode.cpp line 273: > 271: ProjNode::dump_compact_spec(st); > 272: MemNode::dump_adr_type(_adr_type, st); > 273: } Can you show us an example out put of `dump`? I'm just wondering if there maybe needs to be a space between the two, and if it is immediately readable :) src/hotspot/share/opto/multnode.hpp line 215: > 213: } > 214: public: > 215: NarrowMemProjNode(Node* src, const TypePtr* adr_type) Can you feed it any other `src` than a `InitializeNode*`? Suggestion: NarrowMemProjNode(InitializeNode* src, const TypePtr* adr_type) src/hotspot/share/opto/multnode.hpp line 232: > 230: }; > 231: > 232: template ProjNode* MultiNode::apply_to_projs(DUIterator_Fast& imax, DUIterator_Fast& i, Callback callback, uint which_proj) const { Does this not belong right after the `MultiNode`? Or even in `multnode.cpp`? src/hotspot/share/opto/multnode.hpp line 234: > 232: template ProjNode* MultiNode::apply_to_projs(DUIterator_Fast& imax, DUIterator_Fast& i, Callback callback, uint which_proj) const { > 233: auto filter = [&](ProjNode* proj) { > 234: if (proj->_con == which_proj && callback(proj)) { Implicit zero check? src/hotspot/share/opto/phaseX.cpp line 2598: > 2596: add_users_to_worklist0(proj, worklist); > 2597: return MultiNode::CONTINUE; > 2598: }; `for_each` call below would mean we would not need to return anything. ------------- PR Review: https://git.openjdk.org/jdk/pull/24570#pullrequestreview-3376663539 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460499158 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460487363 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460422959 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460434657 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460443335 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2460448870 From coleenp at openjdk.org Fri Oct 24 14:26:39 2025 From: coleenp at openjdk.org (Coleen Phillimore) Date: Fri, 24 Oct 2025 14:26:39 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v7] In-Reply-To: <9IlDXdLcZSa3JCBBn9J8JUYWvlfKukDOlP1oqeTJRAQ=.6642ab0e-67ad-4776-b46c-acbee0136138@github.com> References: <9IlDXdLcZSa3JCBBn9J8JUYWvlfKukDOlP1oqeTJRAQ=.6642ab0e-67ad-4776-b46c-acbee0136138@github.com> Message-ID: On Thu, 23 Oct 2025 22:25:53 GMT, Patricio Chilano Mateo wrote: >> If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. >> >> As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. >> >> This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. >> >> ### Summary of implementation >> >> The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. >> >> If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). >> >> ### Notes >> >> `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::mon... > > Patricio Chilano Mateo has updated the pull request incrementally with three additional commits since the last revision: > > - rename _monitor to _init_lock > - Extra comments from Coleen > - define methods in resolve_from_cache with TRAPS and use CHECK_AND_CLEAR_PREEMPTED Almost done. Thank you for making suggested changes. src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp line 347: > 345: intptr_t* sp = enterSpecial.sp(); > 346: > 347: sp[-1] = (intptr_t)StubRoutines::cont_preempt_stub(); push_cleanup_continuation sets sp[-2]. This doesn't have to set that? src/hotspot/cpu/aarch64/stackChunkFrameStream_aarch64.inline.hpp line 116: > 114: InterpreterOopMap mask; > 115: frame f = to_frame(); > 116: f.interpreted_frame_oop_map(&mask); There are two uses of this function left in continuationHelper.inline.hpp and continuationFreezeThaw.cpp under verification code. Maybe they can be removed? Do the places that call this in verification code still valid for preempted class initialization? Do they need to count arguments now? src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp line 220: > 218: intptr_t* sp = _top_frame.sp(); > 219: if (sp != _last_sp_from_frame) { > 220: sp[-1] = (intptr_t)_top_frame.pc(); Same coment as aarch64, does this need to set sp[-2] to fp like above? Or should it preserve the value? Can you add a comment for each telling why? src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp line 334: > 332: intptr_t* sp = enterSpecial.sp(); > 333: > 334: sp[-1] = (intptr_t)StubRoutines::cont_preempt_stub(); Same here sp-2 ? src/hotspot/share/runtime/frame.cpp line 951: > 949: // code in the interpreter calls a blocking runtime > 950: // routine which can cause this code to be executed). > 951: // (was bug gri 7/27/98) I like the refactoring of this condition. It may be finally time to remove this line from 1998. I, for one, will miss it but it doesn't really help anyone with anything. test/jdk/java/lang/Thread/virtual/KlassInit.java line 63: > 61: * @library /test/lib > 62: * @requires vm.debug == true & vm.continuations > 63: * @run junit/othervm/timeout=480 -XX:+UnlockDiagnosticVMOptions -XX:+FullGCALot -XX:FullGCALotInterval=1000 -XX:CompileCommand=exclude,KlassInit::lambda$testReleaseAtKlassInit* -XX:CompileCommand=exclude,KlassInit$$Lambda*::run -XX:CompileCommand=exclude,KlassInit$1Driver::foo KlassInit I think you can use multiple lines for the run command and not have to wrap like this. ------------- PR Review: https://git.openjdk.org/jdk/pull/27802#pullrequestreview-3376098254 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460039130 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460624836 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460575662 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460577616 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460697549 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460721081 From coleenp at openjdk.org Fri Oct 24 14:26:45 2025 From: coleenp at openjdk.org (Coleen Phillimore) Date: Fri, 24 Oct 2025 14:26:45 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v6] In-Reply-To: References: Message-ID: On Thu, 23 Oct 2025 22:26:57 GMT, Patricio Chilano Mateo wrote: >> src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp line 73: >> >>> 71: bool update_map() const { return false; } >>> 72: bool walk_cont() const { return false; } >>> 73: bool include_argument_oops() const { return IncludeArgs; } >> >> You made this a template rather than having an _include_argument_oops property for performance? > > Not really, using a bool member would work too. ok, good to know. We could or might not change it in the follow-up issue to move SmallRegisterMap to common code. >> src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1123: >> >>> 1121: assert(!call.is_valid() || call.is_invokestatic(), "only invokestatic allowed"); >>> 1122: if (call.is_invokestatic() && call.size_of_parameters() > 0) { >>> 1123: assert(top_frame.interpreter_frame_expression_stack_size() > 0, "should have parameters in exp stack"); >> >> The "this" pointer will be on the top of the interpreter frame expression stack right? Are size of parameters ever 0 here then? > > This is only for `invokestatic` so no this pointer. I see, this just verifies if size_of_parameters > 0, there must be something on the expression stack. >> src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1653: >> >>> 1651: } >>> 1652: inline intptr_t* anchor_mark_set_pd(); >>> 1653: inline void anchor_mark_clear_pd(); >> >> The code is really confused whether "pd" is the beginning of the method name or the end. Both are used but mostly in the beginning of the method. The freeze/thaw code uses it at the end so this is okay. > > I added `patch_pd_unused` in 8359222 which should have been `patch_unused_pd`. :) I like AnchorMark. There are other places that clear_anchor in this code. Can they use AnchorMark? >> src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1731: >> >>> 1729: int bci; >>> 1730: if (preempt_kind == Continuation::monitorenter) { >>> 1731: assert(top.is_interpreted_frame() || top.is_runtime_frame(), ""); >> >> So could you use precond() here since it's a precondition and you have no assert message? > > Yes, but don?t really see the benefit. It?s replacing a null string for `precond` in a crash. These null strings make me wish we had an assert with no strings if one isn't provided. I suppose the "precond" string isn't much better. I don't like null strings - it seems like you want to say why you're asserting this condition or what it means, ie take the opportunity to provide a bit more documentation. Like here you could say that monitorenter is only preempted when the top frame is interpreted or runtime (which is coming from the compiler right?), which I suppose is redundant with the condition. I suppose nothing is better than "sanity" or "should be". I retract my suggestion to use precond though. Others might believe it's better but I'm agnostic. >> src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2686: >> >>> 2684: >>> 2685: { >>> 2686: HandleMarkCleaner hmc(current); // Cleanup so._conth Handle >> >> Why doesn't a plain HandleMark do this? >> I think you chose HandleMarkCleaner because this is going to back to the interpreter code, so to be like JRT_ENTRY code. >> So add something like before going back to the interpreted Java code. > > A full `HandleMark` works too. It?s just that there is already a `HandleMark` in the callstack (from the original upcall to Java from the carrier thread), so we can use a `HandleMarkCleaner` here. The main place we have HandleMarkCleaners are in the JRT_ENTRY. Can you change the comment to: // Returning to java so cleanup all handles including so._conth Handle or something like that. >> src/hotspot/share/runtime/javaThread.hpp line 1372: >> >>> 1370: }; >>> 1371: >>> 1372: class PreemptableInitCall { >> >> If this is only used in instanceKlass.cpp, I think the definition should be there and not in javaThread.hpp. It's not using private methds in javaThread.hpp as far as I can tell. > > Moved, and also `ThreadWaitingForClassInit`. Should I move pre-existent `ThreadInClassInitializer` too? It's preexisting so I don't think so. Leave it for a trivial change. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460563120 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460632055 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460637514 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460668197 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460687828 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460751847 From coleenp at openjdk.org Fri Oct 24 14:26:46 2025 From: coleenp at openjdk.org (Coleen Phillimore) Date: Fri, 24 Oct 2025 14:26:46 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: References: <8SlbTQ-LN3PIBw1M6hERwi8KyA02Avg5KrYrZISR_4o=.5ef0f9a9-48ef-4a36-a2ef-ffb2b77863ec@github.com> Message-ID: On Thu, 23 Oct 2025 22:29:04 GMT, Patricio Chilano Mateo wrote: >> Would an initialize_preemptable() = 0 be better? then you can see if it's called by anything other than InstanceKlass. Klass is always abstract. > > We can but we would have to implement it for `ArrayKlass` too which is kind of the same of what we have now. We have a `ShouldNotReachHere()` in `Klass::initialize_preemptable` (following the same pattern as `Klass::initialize`), so we will catch anything other than `InstanceKlass`. I guess following the pattern is better. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460745900 From coleenp at openjdk.org Fri Oct 24 14:26:49 2025 From: coleenp at openjdk.org (Coleen Phillimore) Date: Fri, 24 Oct 2025 14:26:49 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: References: Message-ID: On Fri, 17 Oct 2025 06:33:23 GMT, David Holmes wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> fix verify_frame_kind > > src/hotspot/share/runtime/synchronizer.hpp line 230: > >> 228: bool _skip_exit; >> 229: public: >> 230: ObjectLocker(Handle obj, TRAPS); > > I wonder if we should declare `PREEMPTABLE_TRAPS` as an indicator that the only exception expected to come out of a call is the preempted-exception? Not sure if I like that idea because then we might have to change other callers along the way for this new convention and everybody's already confused by TRAPS so then they'd be confused by a new TRAPS too. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2460734436 From dlong at openjdk.org Sat Oct 25 02:43:05 2025 From: dlong at openjdk.org (Dean Long) Date: Sat, 25 Oct 2025 02:43:05 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v2] In-Reply-To: References: Message-ID: On Thu, 16 Oct 2025 16:23:05 GMT, Patricio Chilano Mateo wrote: >> src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp line 209: >> >>> 207: // the last_sp saved in the frame (remove possible alignment added while >>> 208: // thawing, see ThawBase::finish_thaw()). We also need to clear the last_sp >>> 209: // saved in the frame as it is not expected to be set in case we preempt again. >> >> A bit stronger? >> Suggestion: >> >> // saved in the frame because it must be clear if we freeze again. > > Just to add more context, not clearing last_sp will make this assert [1] fire if we freeze again. That assert is mostly a verification check, because we know the interpreter doesn?t set last_sp for the top frame when calling into the VM. But I don?t see a fundamental reason why it must be cleared (removing the assert and not clearing last_sp works). I don?t see any other code that checks last_sp needs to be cleared for the top frame (other than in the interpreter before calling into the VM). > How about changing that last sentence with: `We also clear last_sp to match the behavior when calling the VM from the interpreter (we check for this in FreezeBase::prepare_freeze_interpreted_top_frame).` > > [1] https://github.com/openjdk/jdk/blob/87092ef1d97e00ddb6674b0e309f2f904d307604/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp#L136 FWIW, interpreter_frame_tos_address() behaves differently depending on if last_sp() is cleared or not. I know deoptimization sets last_sp temporarily but makes sure to clear it before giving control back to the interpreter. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2462366413 From duke at openjdk.org Sat Oct 25 02:53:01 2025 From: duke at openjdk.org (Josiah Noel) Date: Sat, 25 Oct 2025 02:53:01 GMT Subject: RFR: 8368695: Support 101 switching protocol in jdk.httpserver [v13] In-Reply-To: References: Message-ID: > - adds a flag to ExchangeImpl to signal whether the current request is an Upgrade request > - Adds a new `UpgradeInputStream` to ensure that the server keeps track of when the upgraded request is closed > - on 101 response codes, `sendResponseHeaders` will not immediately close the output stream > - on 101 response codes, `sendResponseHeaders` will use an `UndefLengthOutputStream` Josiah Noel has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 18 commits: - Update ExchangeImpl.java - Merge remote-tracking branch 'upstream/master' into JDK-8368695 - reduce diff - Merge remote-tracking branch 'upstream/master' into JDK-8368695 - Update SwitchingProtocolTest.java - Update SwitchingProtocolTest.java - Update SwitchingProtocolTest.java - add exception test - Create UpgradeOutputStream.java - close raw streams - ... and 8 more: https://git.openjdk.org/jdk/compare/32697bf6...ae2b1184 ------------- Changes: https://git.openjdk.org/jdk/pull/27751/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=27751&range=12 Stats: 25304 lines in 632 files changed: 6529 ins; 14641 del; 4134 mod Patch: https://git.openjdk.org/jdk/pull/27751.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27751/head:pull/27751 PR: https://git.openjdk.org/jdk/pull/27751 From duke at openjdk.org Sat Oct 25 02:53:02 2025 From: duke at openjdk.org (Josiah Noel) Date: Sat, 25 Oct 2025 02:53:02 GMT Subject: Withdrawn: 8368695: Support 101 switching protocol in jdk.httpserver In-Reply-To: References: Message-ID: On Fri, 10 Oct 2025 19:45:57 GMT, Josiah Noel wrote: > - adds a flag to ExchangeImpl to signal whether the current request is an Upgrade request > - Adds a new `UpgradeInputStream` to ensure that the server keeps track of when the upgraded request is closed > - on 101 response codes, `sendResponseHeaders` will not immediately close the output stream > - on 101 response codes, `sendResponseHeaders` will use an `UndefLengthOutputStream` This pull request has been closed without being integrated. ------------- PR: https://git.openjdk.org/jdk/pull/27751 From pchilanomate at openjdk.org Mon Oct 27 19:28:57 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 27 Oct 2025 19:28:57 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v8] In-Reply-To: References: Message-ID: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: More comments from Coleen ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27802/files - new: https://git.openjdk.org/jdk/pull/27802/files/2786b1d8..bea5620a Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=07 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=06-07 Stats: 53 lines in 6 files changed: 22 ins; 19 del; 12 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Mon Oct 27 19:29:03 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 27 Oct 2025 19:29:03 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v7] In-Reply-To: References: <9IlDXdLcZSa3JCBBn9J8JUYWvlfKukDOlP1oqeTJRAQ=.6642ab0e-67ad-4776-b46c-acbee0136138@github.com> Message-ID: <4SKua96Afdg1kugXFHLdQVgSjHfCOCmt4S2Q-EN-EXc=.819a7f52-8bb2-469d-a5de-3478f041f97c@github.com> On Fri, 24 Oct 2025 11:55:28 GMT, Coleen Phillimore wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with three additional commits since the last revision: >> >> - rename _monitor to _init_lock >> - Extra comments from Coleen >> - define methods in resolve_from_cache with TRAPS and use CHECK_AND_CLEAR_PREEMPTED > > src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp line 347: > >> 345: intptr_t* sp = enterSpecial.sp(); >> 346: >> 347: sp[-1] = (intptr_t)StubRoutines::cont_preempt_stub(); > > push_cleanup_continuation sets sp[-2]. This doesn't have to set that? `push_cleanup_continuation()` doesn?t need it. I removed it there and added a comment on both methods. > src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp line 220: > >> 218: intptr_t* sp = _top_frame.sp(); >> 219: if (sp != _last_sp_from_frame) { >> 220: sp[-1] = (intptr_t)_top_frame.pc(); > > Same coment as aarch64, does this need to set sp[-2] to fp like above? Or should it preserve the value? Can you add a comment for each telling why? Here we need to set it because it?s the value that will be set as `_last_Java_fp` in the `_anchor`. Added a comment. > src/hotspot/share/runtime/frame.cpp line 951: > >> 949: // code in the interpreter calls a blocking runtime >> 950: // routine which can cause this code to be executed). >> 951: // (was bug gri 7/27/98) > > I like the refactoring of this condition. It may be finally time to remove this line from 1998. I, for one, will miss it but it doesn't really help anyone with anything. Ok, I removed that part of the comment. > test/jdk/java/lang/Thread/virtual/KlassInit.java line 63: > >> 61: * @library /test/lib >> 62: * @requires vm.debug == true & vm.continuations >> 63: * @run junit/othervm/timeout=480 -XX:+UnlockDiagnosticVMOptions -XX:+FullGCALot -XX:FullGCALotInterval=1000 -XX:CompileCommand=exclude,KlassInit::lambda$testReleaseAtKlassInit* -XX:CompileCommand=exclude,KlassInit$$Lambda*::run -XX:CompileCommand=exclude,KlassInit$1Driver::foo KlassInit > > I think you can use multiple lines for the run command and not have to wrap like this. Done. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2466822550 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2466825853 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2466828401 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2466828651 From pchilanomate at openjdk.org Mon Oct 27 19:29:05 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 27 Oct 2025 19:29:05 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v8] In-Reply-To: References: <9IlDXdLcZSa3JCBBn9J8JUYWvlfKukDOlP1oqeTJRAQ=.6642ab0e-67ad-4776-b46c-acbee0136138@github.com> Message-ID: On Fri, 24 Oct 2025 13:58:07 GMT, Coleen Phillimore wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> More comments from Coleen > > src/hotspot/cpu/aarch64/stackChunkFrameStream_aarch64.inline.hpp line 116: > >> 114: InterpreterOopMap mask; >> 115: frame f = to_frame(); >> 116: f.interpreted_frame_oop_map(&mask); > > There are two uses of this function left in continuationHelper.inline.hpp and continuationFreezeThaw.cpp under verification code. Maybe they can be removed? Do the places that call this in verification code still valid for preempted class initialization? Do they need to count arguments now? The verification code in `verify_frame_top` is still valid. Technically we should count arguments if `f` is the top frame when this is a preemption on invokestatic case, and assert that the result equals `top`. But that would overcomplicate the code for not much gain. The other caller is `ContinuationHelper::Frame::frame_top(const frame &f)`, which I see is only called from `ThawBase::recurse_thaw_interpreted_frame`. It is also still valid and is never called for the top frame, so no argument count is needed. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2466827754 From pchilanomate at openjdk.org Mon Oct 27 19:29:07 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 27 Oct 2025 19:29:07 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v6] In-Reply-To: References: Message-ID: <0V1tgaSn_eSqB5MoaNBdWTVCFDIKmOcBCr9Pg0CF9BM=.57e66701-5839-4d38-89c9-4abe90f8e674@github.com> On Fri, 24 Oct 2025 14:00:33 GMT, Coleen Phillimore wrote: >> I added `patch_pd_unused` in 8359222 which should have been `patch_unused_pd`. :) > > I like AnchorMark. There are other places that clear_anchor in this code. Can they use AnchorMark? Yes, I added it in `ThawBase::throw_interrupted_exception()` and in `thaw_fast()`. >> A full `HandleMark` works too. It?s just that there is already a `HandleMark` in the callstack (from the original upcall to Java from the carrier thread), so we can use a `HandleMarkCleaner` here. > > The main place we have HandleMarkCleaners are in the JRT_ENTRY. Can you change the comment to: > // Returning to java so cleanup all handles including so._conth Handle > > or something like that. Done. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2466823846 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2466820533 From pchilanomate at openjdk.org Mon Oct 27 19:44:53 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 27 Oct 2025 19:44:53 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v9] In-Reply-To: References: Message-ID: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: Improve comment in anchor_mark_set_pd ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27802/files - new: https://git.openjdk.org/jdk/pull/27802/files/bea5620a..13124869 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=08 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=07-08 Stats: 12 lines in 3 files changed: 6 ins; 0 del; 6 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Mon Oct 27 19:44:55 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Mon, 27 Oct 2025 19:44:55 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v2] In-Reply-To: References: Message-ID: On Sat, 25 Oct 2025 02:40:03 GMT, Dean Long wrote: >> Just to add more context, not clearing last_sp will make this assert [1] fire if we freeze again. That assert is mostly a verification check, because we know the interpreter doesn?t set last_sp for the top frame when calling into the VM. But I don?t see a fundamental reason why it must be cleared (removing the assert and not clearing last_sp works). I don?t see any other code that checks last_sp needs to be cleared for the top frame (other than in the interpreter before calling into the VM). >> How about changing that last sentence with: `We also clear last_sp to match the behavior when calling the VM from the interpreter (we check for this in FreezeBase::prepare_freeze_interpreted_top_frame).` >> >> [1] https://github.com/openjdk/jdk/blob/87092ef1d97e00ddb6674b0e309f2f904d307604/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp#L136 > > FWIW, interpreter_frame_tos_address() behaves differently depending on if last_sp() is cleared or not. I know deoptimization sets last_sp temporarily but makes sure to clear it before giving control back to the interpreter. Right, but if `interpreter_frame_last_sp()` is the same as `sp()` then `interpreter_frame_tos_address()` will return the same value. My guess is that since we are already setting `_last_Java_sp` when making VM calls, there is no point in the extra bookkeeping of setting and clearing `interpreter_frame_last_sp` so we leave it as nullptr. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2466872228 From coleenp at openjdk.org Mon Oct 27 21:30:22 2025 From: coleenp at openjdk.org (Coleen Phillimore) Date: Mon, 27 Oct 2025 21:30:22 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v9] In-Reply-To: References: Message-ID: On Mon, 27 Oct 2025 19:44:53 GMT, Patricio Chilano Mateo wrote: >> If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. >> >> As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. >> >> This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. >> >> ### Summary of implementation >> >> The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. >> >> If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). >> >> ### Notes >> >> `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::mon... > > Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: > > Improve comment in anchor_mark_set_pd I had a very small comment that I noticed, but this looks great. Excellent work solving this class initialization problem for virtual threads. src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1796: > 1794: } > 1795: > 1796: static void log_preempt_after_freeze(ContinuationWrapper& cont) { Does this modify ContinuationWrapper? I don't see how it does. If not, it should be a const reference. src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2680: > 2678: } > 2679: > 2680: intptr_t* ThawBase::redo_vmcall(JavaThread* current, frame& top) { Does this modify "top"? Else should be a const reference too. Looks like a lot of references are non-const. If these methods don't modify their non-const reference parameters, I think you should have a cleanup pass to fix these to be const references. ------------- Marked as reviewed by coleenp (Reviewer). PR Review: https://git.openjdk.org/jdk/pull/27802#pullrequestreview-3385513965 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2467117304 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2467122440 From roland at openjdk.org Tue Oct 28 12:42:14 2025 From: roland at openjdk.org (Roland Westrelin) Date: Tue, 28 Oct 2025 12:42:14 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v15] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: On Fri, 24 Oct 2025 13:31:16 GMT, Emanuel Peter wrote: >> Roland Westrelin has updated the pull request incrementally with two additional commits since the last revision: >> >> - review >> - Roberto's patches > > src/hotspot/share/opto/multnode.cpp line 273: > >> 271: ProjNode::dump_compact_spec(st); >> 272: MemNode::dump_adr_type(_adr_type, st); >> 273: } > > Can you show us an example out put of `dump`? I'm just wondering if there maybe needs to be a space between the two, and if it is immediately readable :) Actually, `Node::dump` already takes care of dumping the `adr_type`. So I removed the `dump` methods from `NarrowMemProjNode`. Here is an example output: 59 Initialize === 83 1 62 1 1 1 121 [[ 124 123 63 64 65 ]] !jvms: TestInitializingStoreCapturing::testInitializeArray @ bci:1 (line 57) TestInitializingStoreCapturing::testInitializeArray @ bci:1 (line 57) 63 NarrowMemProj === 59 [[ 15 ]] #2 Memory: @java/lang/Object *, idx=4; !jvms: TestInitializingStoreCapturing::testInitializeArray @ bci:1 (line 57) 64 NarrowMemProj === 59 [[ 15 ]] #2 Memory: @java/lang/Object+8 * [narrowklass], idx=5; !jvms: TestInitializingStoreCapturing::testInitializeArray @ bci:1 (line 57) 65 NarrowMemProj === 59 [[ 15 ]] #2 Memory: @float[int:>=0] (java/lang/Cloneable,java/io/Serializable):exact+any *, idx=6; !jvms: TestInitializingStoreCapturing::testInitializeArray @ bci:1 (line 57) 66 CheckCastPP === 125 86 [[ 79 ]] #float[int:1] (java/lang/Cloneable,java/io/Serializable):NotNull:exact * !jvms: TestInitializingStoreCapturing::testInitializeArray @ bci:1 (line 57) ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2469429116 From roland at openjdk.org Tue Oct 28 13:35:19 2025 From: roland at openjdk.org (Roland Westrelin) Date: Tue, 28 Oct 2025 13:35:19 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v15] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: On Fri, 24 Oct 2025 13:21:04 GMT, Emanuel Peter wrote: >> Roland Westrelin has updated the pull request incrementally with two additional commits since the last revision: >> >> - review >> - Roberto's patches > > src/hotspot/share/opto/multnode.hpp line 232: > >> 230: }; >> 231: >> 232: template ProjNode* MultiNode::apply_to_projs(DUIterator_Fast& imax, DUIterator_Fast& i, Callback callback, uint which_proj) const { > > Does this not belong right after the `MultiNode`? Or even in `multnode.cpp`? It needs the `ProjNode` declaration because it accesses `proj->_con` and can't be in `multnode.cpp` because of the template parameter. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2469591960 From roland at openjdk.org Tue Oct 28 13:38:48 2025 From: roland at openjdk.org (Roland Westrelin) Date: Tue, 28 Oct 2025 13:38:48 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v16] In-Reply-To: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: > An `Initialize` node for an `Allocate` node is created with a memory > `Proj` of adr type raw memory. In order for stores to be captured, the > memory state out of the allocation is a `MergeMem` with slices for the > various object fields/array element set to the raw memory `Proj` of > the `Initialize` node. If `Phi`s need to be created during later > transformations from this memory state, The `Phi` for a particular > slice gets its adr type from the type of the `Proj` which is raw > memory. If during macro expansion, the `Allocate` is found to have no > use and so can be removed, the `Proj` out of the `Initialize` is > replaced by the memory state on input to the `Allocate`. A `Phi` for > some slice for a field of an object will end up with the raw memory > state on input to the `Allocate` node. As a result, memory state at > the `Phi` is incorrect and incorrect execution can happen. > > The fix I propose is, rather than have a single `Proj` for the memory > state out of the `Initialize` with adr type raw memory, to use one > `Proj` per slice added to the memory state after the `Initalize`. Each > of the `Proj` should return the right adr type for its slice. For that > I propose having a new type of `Proj`: `NarrowMemProj` that captures > the right adr type. > > Logic for the construction of the `Allocate`/`Initialize` subgraph is > tweaked so the right adr type captured in is own `NarrowMemProj` is > added to the memory sugraph. Code that removes an allocation or moves > it also has to be changed so it correctly takes the multiple memory > projections out of the `Initialize` node into account. > > One tricky issue is that when EA split types for a scalar replaceable > `Allocate` node: > > 1- the adr type captured in the `NarrowMemProj` becomes out of sync > with the type of the slices for the allocation > > 2- before EA, the memory state for one particular field out of the > `Initialize` node can be used for a `Store` to the just allocated > object or some other. So we can have a chain of `Store`s, some to > the newly allocated object, some to some other objects, all of them > using the state of `NarrowMemProj` out of the `Initialize`. After > split unique types, the `NarrowMemProj` is for the slice of a > particular allocation. So `Store`s to some other objects shouldn't > use that memory state but the memory state before the `Allocate`. > > For that, I added logic to update the adr type of `NarrowMemProj` > during split unique types and update the memory input of `Store`s that > don't depend on the memory state ... Roland Westrelin has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 57 commits: - review - Merge branch 'master' into JDK-8327963 - review - Roberto's patches - review - Update src/hotspot/share/opto/macro.cpp Co-authored-by: Roberto Casta?eda Lozano - Update src/hotspot/share/opto/macro.cpp Co-authored-by: Roberto Casta?eda Lozano - Update src/hotspot/share/opto/graphKit.cpp Co-authored-by: Roberto Casta?eda Lozano - Update src/hotspot/share/opto/graphKit.cpp Co-authored-by: Roberto Casta?eda Lozano - Update src/hotspot/share/opto/multnode.hpp Co-authored-by: Roberto Casta?eda Lozano - ... and 47 more: https://git.openjdk.org/jdk/compare/96259936...957be06e ------------- Changes: https://git.openjdk.org/jdk/pull/24570/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=24570&range=15 Stats: 955 lines in 24 files changed: 864 ins; 25 del; 66 mod Patch: https://git.openjdk.org/jdk/pull/24570.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/24570/head:pull/24570 PR: https://git.openjdk.org/jdk/pull/24570 From roland at openjdk.org Tue Oct 28 13:38:48 2025 From: roland at openjdk.org (Roland Westrelin) Date: Tue, 28 Oct 2025 13:38:48 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v6] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> <8BJorsTgiK1pTElabu0NZFko5n4mlpAhadlt87w_v2s=.f19e86d1-d646-46eb-860f-cbbadf37ada3@github.com> Message-ID: On Tue, 27 May 2025 07:56:46 GMT, Emanuel Peter wrote: >>> > I also think it would be good to investigate, separately, early elimination of dead array allocations, even after the integration of this work. Dead allocations may inhibit later optimizations so it would be good to eliminate them as early as possible anyway. One difficulty (not addressed in [c28f81a](https://github.com/openjdk/jdk/commit/c28f81a7ef2a4f3d3cb761ea23a80c09276e7e58)) is that early array elimination should still generate the nonnegative array size check code. >>> >>> That makes sense. It would be useful to have a bugs to track that one. >> >> Turns out there is one already: [JDK-8180290](https://bugs.openjdk.org/browse/JDK-8180290), I just added a comment there. > >> > > I also think it would be good to investigate, separately, early elimination of dead array allocations, even after the integration of this work. Dead allocations may inhibit later optimizations so it would be good to eliminate them as early as possible anyway. One difficulty (not addressed in [c28f81a](https://github.com/openjdk/jdk/commit/c28f81a7ef2a4f3d3cb761ea23a80c09276e7e58)) is that early array elimination should still generate the nonnegative array size check code. >> > >> > >> > That makes sense. It would be useful to have a bugs to track that one. >> >> Turns out there is one already: [JDK-8180290](https://bugs.openjdk.org/browse/JDK-8180290), I just added a comment there. > > Should we link it on JIRA? @eme64 I pushed a new commit which should address all your comments. ------------- PR Comment: https://git.openjdk.org/jdk/pull/24570#issuecomment-3456505508 From epeter at openjdk.org Tue Oct 28 17:13:01 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Tue, 28 Oct 2025 17:13:01 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v16] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: On Tue, 28 Oct 2025 13:38:48 GMT, Roland Westrelin wrote: >> An `Initialize` node for an `Allocate` node is created with a memory >> `Proj` of adr type raw memory. In order for stores to be captured, the >> memory state out of the allocation is a `MergeMem` with slices for the >> various object fields/array element set to the raw memory `Proj` of >> the `Initialize` node. If `Phi`s need to be created during later >> transformations from this memory state, The `Phi` for a particular >> slice gets its adr type from the type of the `Proj` which is raw >> memory. If during macro expansion, the `Allocate` is found to have no >> use and so can be removed, the `Proj` out of the `Initialize` is >> replaced by the memory state on input to the `Allocate`. A `Phi` for >> some slice for a field of an object will end up with the raw memory >> state on input to the `Allocate` node. As a result, memory state at >> the `Phi` is incorrect and incorrect execution can happen. >> >> The fix I propose is, rather than have a single `Proj` for the memory >> state out of the `Initialize` with adr type raw memory, to use one >> `Proj` per slice added to the memory state after the `Initalize`. Each >> of the `Proj` should return the right adr type for its slice. For that >> I propose having a new type of `Proj`: `NarrowMemProj` that captures >> the right adr type. >> >> Logic for the construction of the `Allocate`/`Initialize` subgraph is >> tweaked so the right adr type captured in is own `NarrowMemProj` is >> added to the memory sugraph. Code that removes an allocation or moves >> it also has to be changed so it correctly takes the multiple memory >> projections out of the `Initialize` node into account. >> >> One tricky issue is that when EA split types for a scalar replaceable >> `Allocate` node: >> >> 1- the adr type captured in the `NarrowMemProj` becomes out of sync >> with the type of the slices for the allocation >> >> 2- before EA, the memory state for one particular field out of the >> `Initialize` node can be used for a `Store` to the just allocated >> object or some other. So we can have a chain of `Store`s, some to >> the newly allocated object, some to some other objects, all of them >> using the state of `NarrowMemProj` out of the `Initialize`. After >> split unique types, the `NarrowMemProj` is for the slice of a >> particular allocation. So `Store`s to some other objects shouldn't >> use that memory state but the memory state before the `Allocate`. >> >> For that, I added logic to update the adr type of `NarrowMemProj` >> during split uni... > > Roland Westrelin has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 57 commits: > > - review > - Merge branch 'master' into JDK-8327963 > - review > - Roberto's patches > - review > - Update src/hotspot/share/opto/macro.cpp > > Co-authored-by: Roberto Casta?eda Lozano > - Update src/hotspot/share/opto/macro.cpp > > Co-authored-by: Roberto Casta?eda Lozano > - Update src/hotspot/share/opto/graphKit.cpp > > Co-authored-by: Roberto Casta?eda Lozano > - Update src/hotspot/share/opto/graphKit.cpp > > Co-authored-by: Roberto Casta?eda Lozano > - Update src/hotspot/share/opto/multnode.hpp > > Co-authored-by: Roberto Casta?eda Lozano > - ... and 47 more: https://git.openjdk.org/jdk/compare/96259936...957be06e @rwestrel Thanks for the updates, it already looks better :) I had a few minutes to look over the `apply_..` solutions. I left a few comments, and hope that we can make the code just a little slicker still ;) src/hotspot/share/opto/memnode.cpp line 5484: > 5482: }; > 5483: return apply_to_narrow_mem_projs(filter); > 5484: } It seems to me that the upper method is only used by the lower here. Why not just collapse them? It would also reduce the "overloading noise". src/hotspot/share/opto/memnode.hpp line 1428: > 1426: template NarrowMemProjNode* apply_to_narrow_mem_projs(DUIterator& i, Callback callback) const { > 1427: return apply_to_narrow_mem_projs_any_iterator(UsesIterator(i, this), callback); > 1428: } Is this one still needed? src/hotspot/share/opto/multnode.hpp line 141: > 139: > 140: // Same but for matching _con and _is_io_use > 141: template ProjNode* apply_to_projs(Callback callback, uint which_proj, bool is_io_use) const; Do these need to be `public`? Or could they be `protected`, so they are only available to subtypes? And do we really need all the variants of `apply_to_projs`, or could we collapse them a little? ------------- PR Review: https://git.openjdk.org/jdk/pull/24570#pullrequestreview-3389846667 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2470344461 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2470334385 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2470369879 From epeter at openjdk.org Tue Oct 28 17:13:03 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Tue, 28 Oct 2025 17:13:03 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v16] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: On Tue, 28 Oct 2025 16:55:39 GMT, Emanuel Peter wrote: >> Roland Westrelin has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 57 commits: >> >> - review >> - Merge branch 'master' into JDK-8327963 >> - review >> - Roberto's patches >> - review >> - Update src/hotspot/share/opto/macro.cpp >> >> Co-authored-by: Roberto Casta?eda Lozano >> - Update src/hotspot/share/opto/macro.cpp >> >> Co-authored-by: Roberto Casta?eda Lozano >> - Update src/hotspot/share/opto/graphKit.cpp >> >> Co-authored-by: Roberto Casta?eda Lozano >> - Update src/hotspot/share/opto/graphKit.cpp >> >> Co-authored-by: Roberto Casta?eda Lozano >> - Update src/hotspot/share/opto/multnode.hpp >> >> Co-authored-by: Roberto Casta?eda Lozano >> - ... and 47 more: https://git.openjdk.org/jdk/compare/96259936...957be06e > > src/hotspot/share/opto/memnode.hpp line 1428: > >> 1426: template NarrowMemProjNode* apply_to_narrow_mem_projs(DUIterator& i, Callback callback) const { >> 1427: return apply_to_narrow_mem_projs_any_iterator(UsesIterator(i, this), callback); >> 1428: } > > Is this one still needed? It also seems that the upper two can be merged. Maybe all "overloadings" of `apply_to_narrow_mem_projs` can be merged, no? Or are there really multiple uses? > src/hotspot/share/opto/multnode.hpp line 141: > >> 139: >> 140: // Same but for matching _con and _is_io_use >> 141: template ProjNode* apply_to_projs(Callback callback, uint which_proj, bool is_io_use) const; > > Do these need to be `public`? Or could they be `protected`, so they are only available to subtypes? > > And do we really need all the variants of `apply_to_projs`, or could we collapse them a little? It is just a lot of boilerplate, would be nice if it was a little slicker ;) ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2470349998 PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2470370657 From epeter at openjdk.org Tue Oct 28 17:13:03 2025 From: epeter at openjdk.org (Emanuel Peter) Date: Tue, 28 Oct 2025 17:13:03 GMT Subject: RFR: 8327963: C2: fix construction of memory graph around Initialize node to prevent incorrect execution if allocation is removed [v16] In-Reply-To: References: <3jUFOPYDIqmzEywhzf58guwS0qZGBUCMZ3lXeltlS3c=.5c82601f-cf4d-4b2a-a525-1f8f4c7c4a3b@github.com> Message-ID: On Tue, 28 Oct 2025 16:59:41 GMT, Emanuel Peter wrote: >> src/hotspot/share/opto/memnode.hpp line 1428: >> >>> 1426: template NarrowMemProjNode* apply_to_narrow_mem_projs(DUIterator& i, Callback callback) const { >>> 1427: return apply_to_narrow_mem_projs_any_iterator(UsesIterator(i, this), callback); >>> 1428: } >> >> Is this one still needed? > > It also seems that the upper two can be merged. Maybe all "overloadings" of `apply_to_narrow_mem_projs` can be merged, no? Or are there really multiple uses? Basically we could call `apply_to_narrow_mem_projs_any_iterator` directly from the two uses: - `already_has_narrow_mem_proj_with_adr_type` - `for_each_narrow_mem_proj_with_new_uses` ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/24570#discussion_r2470355697 From pchilanomate at openjdk.org Tue Oct 28 19:37:34 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Tue, 28 Oct 2025 19:37:34 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v10] In-Reply-To: References: Message-ID: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: add const to references ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27802/files - new: https://git.openjdk.org/jdk/pull/27802/files/13124869..3bf8ebd6 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=09 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=08-09 Stats: 3 lines in 1 file changed: 0 ins; 0 del; 3 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Tue Oct 28 19:37:36 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Tue, 28 Oct 2025 19:37:36 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v9] In-Reply-To: References: Message-ID: On Mon, 27 Oct 2025 19:44:53 GMT, Patricio Chilano Mateo wrote: >> If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. >> >> As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. >> >> This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. >> >> ### Summary of implementation >> >> The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. >> >> If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). >> >> ### Notes >> >> `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::mon... > > Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: > > Improve comment in anchor_mark_set_pd Thanks for the review Coleen! ------------- PR Comment: https://git.openjdk.org/jdk/pull/27802#issuecomment-3458170668 From pchilanomate at openjdk.org Tue Oct 28 19:37:38 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Tue, 28 Oct 2025 19:37:38 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v9] In-Reply-To: References: Message-ID: <7x8l5QaY7Dw_gSI4vFPP0OEHg7FrZ5MbdBj45TpIMjM=.d272b08d-26e8-4b20-8a43-5ad9f1990e69@github.com> On Mon, 27 Oct 2025 21:20:39 GMT, Coleen Phillimore wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> Improve comment in anchor_mark_set_pd > > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1796: > >> 1794: } >> 1795: >> 1796: static void log_preempt_after_freeze(ContinuationWrapper& cont) { > > Does this modify ContinuationWrapper? I don't see how it does. If not, it should be a const reference. Done. > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2680: > >> 2678: } >> 2679: >> 2680: intptr_t* ThawBase::redo_vmcall(JavaThread* current, frame& top) { > > Does this modify "top"? Else should be a const reference too. Looks like a lot of references are non-const. If these methods don't modify their non-const reference parameters, I think you should have a cleanup pass to fix these to be const references. It?s used in `AnchorMark` to set `_top_frame`, which can?t be const because of the `interpreter_frame_set_last_sp` usage. I fixed `push_return_frame` too, let me know if you spotted other ones. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2470809959 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2470811686 From coleenp at openjdk.org Tue Oct 28 19:55:14 2025 From: coleenp at openjdk.org (Coleen Phillimore) Date: Tue, 28 Oct 2025 19:55:14 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v9] In-Reply-To: <7x8l5QaY7Dw_gSI4vFPP0OEHg7FrZ5MbdBj45TpIMjM=.d272b08d-26e8-4b20-8a43-5ad9f1990e69@github.com> References: <7x8l5QaY7Dw_gSI4vFPP0OEHg7FrZ5MbdBj45TpIMjM=.d272b08d-26e8-4b20-8a43-5ad9f1990e69@github.com> Message-ID: On Tue, 28 Oct 2025 19:33:07 GMT, Patricio Chilano Mateo wrote: >> src/hotspot/share/runtime/continuationFreezeThaw.cpp line 1796: >> >>> 1794: } >>> 1795: >>> 1796: static void log_preempt_after_freeze(ContinuationWrapper& cont) { >> >> Does this modify ContinuationWrapper? I don't see how it does. If not, it should be a const reference. > > Done. Thank you! ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2470859813 From fyang at openjdk.org Wed Oct 29 01:48:08 2025 From: fyang at openjdk.org (Fei Yang) Date: Wed, 29 Oct 2025 01:48:08 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v9] In-Reply-To: References: Message-ID: <77E6HxJSWjDdGYLpk_R2W72ad6fJnI952ul_daWCvsM=.bf30b1de-6f62-4889-861a-33b96942aca8@github.com> On Tue, 28 Oct 2025 19:34:08 GMT, Patricio Chilano Mateo wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> Improve comment in anchor_mark_set_pd > > Thanks for the review Coleen! Just FYI: I am witnessing build issues after applying this on JDK HEAD. So we might want to merge with latest master. @pchilano ------------- PR Comment: https://git.openjdk.org/jdk/pull/27802#issuecomment-3459300121 From duke at openjdk.org Wed Oct 29 02:52:12 2025 From: duke at openjdk.org (duke) Date: Wed, 29 Oct 2025 02:52:12 GMT Subject: Withdrawn: 8361417: JVMCI getModifiers incorrect for inner classes In-Reply-To: References: Message-ID: <_pOTDXcK2qzlMiJwjuZfM3HXnAM3EvWYTs6_lsuFxd0=.e272a768-bbfa-4dcd-bc53-ce8740307b68@github.com> On Fri, 4 Jul 2025 16:10:22 GMT, Doug Simon wrote: > The result of `ResolvedJavaType.getModifiers()` should always have been the same as `Class.getModifiers()`. This is currently not the case for inner classes. Instead, the value is derived from `Klass::_access_flags` where as it should be derived from the `InnerClasses` attribute (as it is for `Class`). > > This PR aligns `ResolvedJavaType.getModifiers()` with `Class.getModifiers()`. This pull request has been closed without being integrated. ------------- PR: https://git.openjdk.org/jdk/pull/26135 From dholmes at openjdk.org Wed Oct 29 05:56:07 2025 From: dholmes at openjdk.org (David Holmes) Date: Wed, 29 Oct 2025 05:56:07 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v6] In-Reply-To: References: Message-ID: On Fri, 24 Oct 2025 14:06:03 GMT, Coleen Phillimore wrote: >> Yes, but don?t really see the benefit. It?s replacing a null string for `precond` in a crash. > > These null strings make me wish we had an assert with no strings if one isn't provided. I suppose the "precond" string isn't much better. I don't like null strings - it seems like you want to say why you're asserting this condition or what it means, ie take the opportunity to provide a bit more documentation. Like here you could say that monitorenter is only preempted when the top frame is interpreted or runtime (which is coming from the compiler right?), which I suppose is redundant with the condition. I suppose nothing is better than "sanity" or "should be". I retract my suggestion to use precond though. Others might believe it's better but I'm agnostic. So is it a compiled frame otherwise? Reporting the unexpected frame type might be useful. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2471812478 From dholmes at openjdk.org Wed Oct 29 06:27:11 2025 From: dholmes at openjdk.org (David Holmes) Date: Wed, 29 Oct 2025 06:27:11 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v10] In-Reply-To: References: Message-ID: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> On Tue, 28 Oct 2025 19:37:34 GMT, Patricio Chilano Mateo wrote: >> If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. >> >> As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. >> >> This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. >> >> ### Summary of implementation >> >> The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. >> >> If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). >> >> ### Notes >> >> `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::mon... > > Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: > > add const to references A few additional comments/suggestions, but overall this looks good (to the extent I understand the details). Thanks. src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2735: > 2733: HandleMarkCleaner hm(current); // Cleanup all handles (including so._conth) before returning to Java. > 2734: ContinuationWrapper::SafepointOp so(current, _cont); > 2735: AnchorMark am(current, top); // Set the anchor so that the stack is walkable. Shouldn't you delete the `clear_anchor` at line 2739 below? src/hotspot/share/runtime/javaCalls.cpp line 61: > 59: assert(!thread->owns_locks(), "must release all locks when leaving VM"); > 60: guarantee(thread->can_call_java(), "cannot make java calls from the native compiler"); > 61: assert(!thread->preempting(), ""); I'm not sure why this is checked here, and there is no error message to tell me. If we did get here with `preempting` set what would that mean? src/hotspot/share/runtime/thread.cpp line 578: > 576: > 577: bool Thread::TrySpinAcquire(volatile int * adr) { > 578: return AtomicAccess::cmpxchg(adr, 0, 1) == 0; How is this a try-spin-acquire operation ??? I don't think we need this, we can just inline the `cmpxchg` where needed. src/hotspot/share/utilities/exceptions.cpp line 350: > 348: // the exception is propagated we might make an upcall to > 349: // Java to initialize the object with the cause of exception. > 350: NoPreemptMark npm(thread); Could you explain the control flow in more detail here please. I'm unclear both how we get here and exactly what the affect of the NoPreemptMark is. ------------- PR Review: https://git.openjdk.org/jdk/pull/27802#pullrequestreview-3391793337 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2471824170 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2471827558 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2471843917 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2471852020 From dholmes at openjdk.org Wed Oct 29 06:27:13 2025 From: dholmes at openjdk.org (David Holmes) Date: Wed, 29 Oct 2025 06:27:13 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: References: Message-ID: <8Oq4qcIh0vAMYwzAXVPZJwbx7Zx7b6nMMCN9gqFFNkE=.ea219d84-de39-4d64-8117-d919e41c4eeb@github.com> On Fri, 24 Oct 2025 14:19:44 GMT, Coleen Phillimore wrote: >> src/hotspot/share/runtime/synchronizer.hpp line 230: >> >>> 228: bool _skip_exit; >>> 229: public: >>> 230: ObjectLocker(Handle obj, TRAPS); >> >> I wonder if we should declare `PREEMPTABLE_TRAPS` as an indicator that the only exception expected to come out of a call is the preempted-exception? > > Not sure if I like that idea because then we might have to change other callers along the way for this new convention and everybody's already confused by TRAPS so then they'd be confused by a new TRAPS too. I don't think we would have to. There are a handful of methods that now declare TRAPS but the only exception they should ever encounter is the PreemptException. It would be easier to understand the code if this was evident in their use of TRAPS. Also note it is purely documentation - the definition of `PREEMPTABLE_TRAPS` is exactly the same as `TRAPS` ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2471840756 From pchilanomate at openjdk.org Wed Oct 29 20:25:11 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Wed, 29 Oct 2025 20:25:11 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v11] In-Reply-To: References: Message-ID: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 17 commits: - More fixes from David's comments - Merge branch 'master' into JDK-8369238 - add const to references - Improve comment in anchor_mark_set_pd - More comments from Coleen - rename _monitor to _init_lock - Extra comments from Coleen - define methods in resolve_from_cache with TRAPS and use CHECK_AND_CLEAR_PREEMPTED - PPC support - Remove extra debugging param - ... and 7 more: https://git.openjdk.org/jdk/compare/28f2591b...c7d6f5c5 ------------- Changes: https://git.openjdk.org/jdk/pull/27802/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=10 Stats: 2179 lines in 98 files changed: 1753 ins; 120 del; 306 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Wed Oct 29 20:38:22 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Wed, 29 Oct 2025 20:38:22 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v10] In-Reply-To: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> References: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> Message-ID: <37qXDxeUoKrmQSBxgOnJrvoxgjR6eMai0HNqY1ysYdE=.e3bf3e8e-3ae4-4baf-8681-3c0edc2a7e73@github.com> On Wed, 29 Oct 2025 06:00:40 GMT, David Holmes wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> add const to references > > src/hotspot/share/runtime/continuationFreezeThaw.cpp line 2735: > >> 2733: HandleMarkCleaner hm(current); // Cleanup all handles (including so._conth) before returning to Java. >> 2734: ContinuationWrapper::SafepointOp so(current, _cont); >> 2735: AnchorMark am(current, top); // Set the anchor so that the stack is walkable. > > Shouldn't you delete the `clear_anchor` at line 2739 below? Yes, good catch. Removed. > src/hotspot/share/runtime/thread.cpp line 578: > >> 576: >> 577: bool Thread::TrySpinAcquire(volatile int * adr) { >> 578: return AtomicAccess::cmpxchg(adr, 0, 1) == 0; > > How is this a try-spin-acquire operation ??? I don't think we need this, we can just inline the `cmpxchg` where needed. Right, not sure why I used that class. I replaced it with the atomic operations. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2475345821 PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2475349441 From pchilanomate at openjdk.org Wed Oct 29 20:38:19 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Wed, 29 Oct 2025 20:38:19 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v6] In-Reply-To: References: Message-ID: On Wed, 29 Oct 2025 05:53:34 GMT, David Holmes wrote: >> These null strings make me wish we had an assert with no strings if one isn't provided. I suppose the "precond" string isn't much better. I don't like null strings - it seems like you want to say why you're asserting this condition or what it means, ie take the opportunity to provide a bit more documentation. Like here you could say that monitorenter is only preempted when the top frame is interpreted or runtime (which is coming from the compiler right?), which I suppose is redundant with the condition. I suppose nothing is better than "sanity" or "should be". I retract my suggestion to use precond though. Others might believe it's better but I'm agnostic. > > So is it a compiled frame otherwise? Reporting the unexpected frame type might be useful. I added a check for compiled or native. If we want to check all possibilities we could add a method in frame class to return the name of the frame type. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2475342971 From pchilanomate at openjdk.org Wed Oct 29 20:38:24 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Wed, 29 Oct 2025 20:38:24 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: <8Oq4qcIh0vAMYwzAXVPZJwbx7Zx7b6nMMCN9gqFFNkE=.ea219d84-de39-4d64-8117-d919e41c4eeb@github.com> References: <8Oq4qcIh0vAMYwzAXVPZJwbx7Zx7b6nMMCN9gqFFNkE=.ea219d84-de39-4d64-8117-d919e41c4eeb@github.com> Message-ID: On Wed, 29 Oct 2025 06:11:56 GMT, David Holmes wrote: >> Not sure if I like that idea because then we might have to change other callers along the way for this new convention and everybody's already confused by TRAPS so then they'd be confused by a new TRAPS too. > > I don't think we would have to. There are a handful of methods that now declare TRAPS but the only exception they should ever encounter is the PreemptException. It would be easier to understand the code if this was evident in their use of TRAPS. Also note it is purely documentation - the definition of `PREEMPTABLE_TRAPS` is exactly the same as `TRAPS` If we want to avoid confusion with other users of `ObjectLocker` maybe we can leave it as it is and subclass it with a preemptable version? https://github.com/pchilano/jdk/compare/JDK-8369238...pchilano:jdk:PreemptableObjectLocker (this version could also use `PREEMPTABLE_TRAPS`) ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2475339482 From pchilanomate at openjdk.org Wed Oct 29 20:43:09 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Wed, 29 Oct 2025 20:43:09 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v10] In-Reply-To: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> References: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> Message-ID: On Wed, 29 Oct 2025 06:19:07 GMT, David Holmes wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> add const to references > > src/hotspot/share/utilities/exceptions.cpp line 350: > >> 348: // the exception is propagated we might make an upcall to >> 349: // Java to initialize the object with the cause of exception. >> 350: NoPreemptMark npm(thread); > > Could you explain the control flow in more detail here please. I'm unclear both how we get here and exactly what the affect of the NoPreemptMark is. We can get here from a preemptable path if initialization of the klass failed: https://github.com/pchilano/jdk/blob/c7d6f5c5220a93653dea37488d238a76e2ad627d/src/hotspot/share/oops/instanceKlass.cpp#L1292 Also from here at linking step: https://github.com/pchilano/jdk/blob/c7d6f5c5220a93653dea37488d238a76e2ad627d/src/hotspot/share/oops/instanceKlass.cpp#L970. The klass of the exception might need to be initialized, so without this `NoPreemptMark` the thread could be preempted while trying to initialize it. The problem is that this method is called here https://github.com/pchilano/jdk/blob/c7d6f5c5220a93653dea37488d238a76e2ad627d/src/hotspot/share/utilities/exceptions.cpp#L372, which will continue executing and possibly make an upcall to Java. We could potentially change these methods to identify a `PreemptedException` and use the `CHECK` macros to return, but I think it is simpler to disable preemption for these cases. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2475371881 From pchilanomate at openjdk.org Wed Oct 29 20:46:02 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Wed, 29 Oct 2025 20:46:02 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v10] In-Reply-To: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> References: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> Message-ID: On Wed, 29 Oct 2025 06:02:57 GMT, David Holmes wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> add const to references > > src/hotspot/share/runtime/javaCalls.cpp line 61: > >> 59: assert(!thread->owns_locks(), "must release all locks when leaving VM"); >> 60: guarantee(thread->can_call_java(), "cannot make java calls from the native compiler"); >> 61: assert(!thread->preempting(), ""); > > I'm not sure why this is checked here, and there is no error message to tell me. If we did get here with `preempting` set what would that mean? This is a safety check since a thread marked as preempted should not be making upcalls to Java. It should be bailing out from methods and returning to the VM entry point. I found we could get here from the exception path (from your other comment below) when there was no `NoPreemptMark` there. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2475389912 From pchilanomate at openjdk.org Wed Oct 29 20:51:26 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Wed, 29 Oct 2025 20:51:26 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v9] In-Reply-To: References: Message-ID: <_xAmFl3Kbf_weqAcF-cabI0-iIO4mHeaBcFdBwOSvSY=.36e530f9-5bd5-4073-a3ff-b9a2d969eef0@github.com> On Tue, 28 Oct 2025 19:34:08 GMT, Patricio Chilano Mateo wrote: >> Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: >> >> Improve comment in anchor_mark_set_pd > > Thanks for the review Coleen! > Just FYI: I am witnessing build issues after applying this on JDK HEAD. So we might want to merge with latest master. @pchilano > Thanks, merged with latest master. ------------- PR Comment: https://git.openjdk.org/jdk/pull/27802#issuecomment-3463940663 From dholmes at openjdk.org Thu Oct 30 06:21:08 2025 From: dholmes at openjdk.org (David Holmes) Date: Thu, 30 Oct 2025 06:21:08 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v10] In-Reply-To: References: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> Message-ID: On Wed, 29 Oct 2025 20:43:24 GMT, Patricio Chilano Mateo wrote: >> src/hotspot/share/runtime/javaCalls.cpp line 61: >> >>> 59: assert(!thread->owns_locks(), "must release all locks when leaving VM"); >>> 60: guarantee(thread->can_call_java(), "cannot make java calls from the native compiler"); >>> 61: assert(!thread->preempting(), ""); >> >> I'm not sure why this is checked here, and there is no error message to tell me. If we did get here with `preempting` set what would that mean? > > This is a safety check since a thread marked as preempted should not be making upcalls to Java. It should be bailing out from methods and returning to the VM entry point. I found we could get here from the exception path (from your other comment below) when there was no `NoPreemptMark` there. Okay then how about `"Unexpected Java upcall whilst processing preemption"` ? ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2476538796 From dholmes at openjdk.org Thu Oct 30 06:26:06 2025 From: dholmes at openjdk.org (David Holmes) Date: Thu, 30 Oct 2025 06:26:06 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v3] In-Reply-To: References: <8Oq4qcIh0vAMYwzAXVPZJwbx7Zx7b6nMMCN9gqFFNkE=.ea219d84-de39-4d64-8117-d919e41c4eeb@github.com> Message-ID: On Wed, 29 Oct 2025 20:34:24 GMT, Patricio Chilano Mateo wrote: >> I don't think we would have to. There are a handful of methods that now declare TRAPS but the only exception they should ever encounter is the PreemptException. It would be easier to understand the code if this was evident in their use of TRAPS. Also note it is purely documentation - the definition of `PREEMPTABLE_TRAPS` is exactly the same as `TRAPS` > > If we want to avoid confusion with other users of `ObjectLocker` maybe we can leave it as it is and subclass it with a preemptable version? https://github.com/pchilano/jdk/compare/JDK-8369238...pchilano:jdk:PreemptableObjectLocker (this version could also use `PREEMPTABLE_TRAPS`) Never mind - I thought there were a few functions that had TRAPS added just for the preempt case but now I don't see them. Thanks ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2476548285 From dholmes at openjdk.org Thu Oct 30 06:30:09 2025 From: dholmes at openjdk.org (David Holmes) Date: Thu, 30 Oct 2025 06:30:09 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v11] In-Reply-To: References: Message-ID: On Wed, 29 Oct 2025 20:25:11 GMT, Patricio Chilano Mateo wrote: >> If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. >> >> As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. >> >> This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. >> >> ### Summary of implementation >> >> The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. >> >> If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). >> >> ### Notes >> >> `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::mon... > > Patricio Chilano Mateo has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 17 commits: > > - More fixes from David's comments > - Merge branch 'master' into JDK-8369238 > - add const to references > - Improve comment in anchor_mark_set_pd > - More comments from Coleen > - rename _monitor to _init_lock > - Extra comments from Coleen > - define methods in resolve_from_cache with TRAPS and use CHECK_AND_CLEAR_PREEMPTED > - PPC support > - Remove extra debugging param > - ... and 7 more: https://git.openjdk.org/jdk/compare/28f2591b...c7d6f5c5 Thanks for explanations and updates. Nominal approval but obviously you need additional approvers for this one. ------------- PR Review: https://git.openjdk.org/jdk/pull/27802#pullrequestreview-3397609556 Marked as reviewed by dholmes (Reviewer). PR Review: https://git.openjdk.org/jdk/pull/27802#pullrequestreview-3397614596 From rrich at openjdk.org Thu Oct 30 07:37:13 2025 From: rrich at openjdk.org (Richard Reingruber) Date: Thu, 30 Oct 2025 07:37:13 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v10] In-Reply-To: References: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> Message-ID: On Wed, 29 Oct 2025 20:40:14 GMT, Patricio Chilano Mateo wrote: >> src/hotspot/share/utilities/exceptions.cpp line 350: >> >>> 348: // the exception is propagated we might make an upcall to >>> 349: // Java to initialize the object with the cause of exception. >>> 350: NoPreemptMark npm(thread); >> >> Could you explain the control flow in more detail here please. I'm unclear both how we get here and exactly what the affect of the NoPreemptMark is. > > We can get here from a preemptable path if initialization of the klass failed: https://github.com/pchilano/jdk/blob/c7d6f5c5220a93653dea37488d238a76e2ad627d/src/hotspot/share/oops/instanceKlass.cpp#L1292 > Also from here at linking step: https://github.com/pchilano/jdk/blob/c7d6f5c5220a93653dea37488d238a76e2ad627d/src/hotspot/share/oops/instanceKlass.cpp#L970. > The klass of the exception might need to be initialized, so without this `NoPreemptMark` the thread could be preempted while trying to initialize it. The problem is that this method is called here https://github.com/pchilano/jdk/blob/c7d6f5c5220a93653dea37488d238a76e2ad627d/src/hotspot/share/utilities/exceptions.cpp#L372, which will continue executing and possibly make an upcall to Java. We could potentially change these methods to identify a `PreemptedException` and use the `CHECK` macros to return, but I think it is simpler to disable preemption for these cases. I'm a correct that at line 348 "the *exception* is propagated we might make an upcall to" you are referring to an `PreemptedException`? You could change the comment to distinguish this one with the exception being created better. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2476701445 From pchilanomate at openjdk.org Thu Oct 30 15:54:18 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Thu, 30 Oct 2025 15:54:18 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v12] In-Reply-To: References: Message-ID: > If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. > > As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. > > This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. > > ### Summary of implementation > > The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. > > If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). > > ### Notes > > `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::monitorenter`, was renamed to `In... Patricio Chilano Mateo has updated the pull request incrementally with one additional commit since the last revision: Improve comment and assert msg ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27802/files - new: https://git.openjdk.org/jdk/pull/27802/files/c7d6f5c5..ffcd92a6 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=11 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27802&range=10-11 Stats: 3 lines in 2 files changed: 0 ins; 0 del; 3 mod Patch: https://git.openjdk.org/jdk/pull/27802.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27802/head:pull/27802 PR: https://git.openjdk.org/jdk/pull/27802 From pchilanomate at openjdk.org Thu Oct 30 15:54:21 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Thu, 30 Oct 2025 15:54:21 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v11] In-Reply-To: References: Message-ID: On Wed, 29 Oct 2025 20:25:11 GMT, Patricio Chilano Mateo wrote: >> If a thread tries to initialize a class that is already being initialized by another thread, it will block until notified. Since at this blocking point there are native frames on the stack, a virtual thread cannot be unmounted and is pinned to its carrier. Besides harming scalability, this can, in some pathological cases, lead to a deadlock, for example, if the thread executing the class initialization method is blocked waiting for some unmounted virtual thread to run, but all carriers are blocked waiting for that class to be initialized. >> >> As of JDK-8338383, virtual threads blocked in the VM on `ObjectMonitor` operations can be unmounted. Since synchronization on class initialization is implemented using `ObjectLocker`, we can reuse the same mechanism to unmount virtual threads on these cases too. >> >> This patch adds support for unmounting virtual threads on some of the most common class initialization paths, specifically when calling `InterpreterRuntime::_new` (`new` bytecode), and `InterpreterRuntime::resolve_from_cache` for `invokestatic`, `getstatic` or `putstatic` bytecodes. In the future we might consider extending this mechanism to include initialization calls originating from native methods such as `Class.forName0`. >> >> ### Summary of implementation >> >> The ObjectLocker class was modified to not pin the continuation if we are coming from a preemptable path, which will be the case when calling `InstanceKlass::initialize_impl` from new method `InstanceKlass::initialize_preemptable`. This means that for these cases, a virtual thread can now be unmounted either when contending for the init_lock in the `ObjectLocker` constructor, or in the call to `wait_uninterruptibly`. Also, since the call to initialize a class includes a previous call to `link_class` which also uses `ObjectLocker` to protect concurrent calls from multiple threads, we will allow preemption there too. >> >> If preempted, we will throw a pre-allocated exception which will get propagated with the `TRAPS/CHECK` macros all the way back to the VM entry point. The exception will be cleared and on return back to Java the virtual thread will go through the preempt stub and unmount. When running again, at the end of the thaw call we will identify this preemption case and redo the original VM call (either `InterpreterRuntime::_new` or `InterpreterRuntime::resolve_from_cache`). >> >> ### Notes >> >> `InterpreterRuntime::call_VM_preemptable` used previously only for `InterpreterRuntime::mon... > > Patricio Chilano Mateo has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains 17 commits: > > - More fixes from David's comments > - Merge branch 'master' into JDK-8369238 > - add const to references > - Improve comment in anchor_mark_set_pd > - More comments from Coleen > - rename _monitor to _init_lock > - Extra comments from Coleen > - define methods in resolve_from_cache with TRAPS and use CHECK_AND_CLEAR_PREEMPTED > - PPC support > - Remove extra debugging param > - ... and 7 more: https://git.openjdk.org/jdk/compare/28f2591b...c7d6f5c5 Thanks for the review David! ------------- PR Comment: https://git.openjdk.org/jdk/pull/27802#issuecomment-3468707728 From pchilanomate at openjdk.org Thu Oct 30 15:54:22 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Thu, 30 Oct 2025 15:54:22 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v10] In-Reply-To: References: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> Message-ID: On Thu, 30 Oct 2025 06:18:29 GMT, David Holmes wrote: >> This is a safety check since a thread marked as preempted should not be making upcalls to Java. It should be bailing out from methods and returning to the VM entry point. I found we could get here from the exception path (from your other comment below) when there was no `NoPreemptMark` there. > > Okay then how about `"Unexpected Java upcall whilst processing preemption"` ? Done. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2478633316 From pchilanomate at openjdk.org Thu Oct 30 15:54:22 2025 From: pchilanomate at openjdk.org (Patricio Chilano Mateo) Date: Thu, 30 Oct 2025 15:54:22 GMT Subject: RFR: 8369238: Allow virtual thread preemption on some common class initialization paths [v10] In-Reply-To: References: <8lMiLlH9mRrGr7vGnywhk1tuTnaM8PanfOI20v5KYGU=.b428de44-95e5-459c-92f0-55a51b9ec910@github.com> Message-ID: <6QZNGgZgFS5TcRwwRZB0VSZpA-574sJqqbmAJVTo-bU=.ca3f8ef5-f1bf-49c4-99f1-dee3f5651d1d@github.com> On Thu, 30 Oct 2025 07:34:52 GMT, Richard Reingruber wrote: >> We can get here from a preemptable path if initialization of the klass failed: https://github.com/pchilano/jdk/blob/c7d6f5c5220a93653dea37488d238a76e2ad627d/src/hotspot/share/oops/instanceKlass.cpp#L1292 >> Also from here at linking step: https://github.com/pchilano/jdk/blob/c7d6f5c5220a93653dea37488d238a76e2ad627d/src/hotspot/share/oops/instanceKlass.cpp#L970. >> The klass of the exception might need to be initialized, so without this `NoPreemptMark` the thread could be preempted while trying to initialize it. The problem is that this method is called here https://github.com/pchilano/jdk/blob/c7d6f5c5220a93653dea37488d238a76e2ad627d/src/hotspot/share/utilities/exceptions.cpp#L372, which will continue executing and possibly make an upcall to Java. We could potentially change these methods to identify a `PreemptedException` and use the `CHECK` macros to return, but I think it is simpler to disable preemption for these cases. > > I'm a correct that at line 348 "the *exception* is propagated we might make an upcall to" you are referring to an `PreemptedException`? You could change the comment to distinguish this one with the exception being created better. Yes, I updated the comment to make the distinction clearer. ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27802#discussion_r2478637299