From snazarki at openjdk.org Wed Mar 1 08:58:29 2023 From: snazarki at openjdk.org (Sergey Nazarkin) Date: Wed, 1 Mar 2023 08:58:29 GMT Subject: git: openjdk/aarch32-port-jdk8u: master: 13 new changesets Message-ID: <828a5509-94a4-4754-8bc1-9d22b09f0230@openjdk.org> Changeset: 89953562 Author: Sergey Bylokhov Date: 2023-02-13 02:41:59 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/89953562e5bca442b30e8fbbe228697bcc7ad961 6734341: REGTEST fails: SelectionAutoscrollTest.html Reviewed-by: andrew Backport-of: 3dc3d0c3e52d91bb3a205e6f8c6b1e79d9aa1928 - jdk/test/java/awt/TextArea/UsingWithMouse/SelectionAutoscrollTest.html ! jdk/test/java/awt/TextArea/UsingWithMouse/SelectionAutoscrollTest.java Changeset: 3af19d3c Author: Roman Marchenko Committer: Yuri Nesterenko Date: 2023-02-13 06:31:12 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/3af19d3c08d952be63c2c80f5f18aaf5e278907f 8282511: Use fixed certificate validation date in SSLExampleCert template 8299445: EndingDotHostname.java fails because of compilation errors Reviewed-by: yan, andrew Backport-of: 268fa693188b685de6289927ee5a1e99473a50f6 ! jdk/test/javax/net/ssl/ServerName/EndingDotHostname.java ! jdk/test/javax/net/ssl/templates/SSLExampleCert.java Changeset: 29a1b794 Author: Sergey Bylokhov Date: 2023-02-13 08:13:56 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/29a1b794b80c1563fd1674b26f2f278ca382137a 7124238: [macosx] Font in BasicHTML document is bigger than it should be Reviewed-by: andrew Backport-of: 8ccf075f78b5645dd186d11637bd11d7e6938b86 + jdk/test/javax/swing/plaf/basic/BasicHTML/4960629/bug4960629.java Changeset: cbde7449 Author: Sergey Bylokhov Date: 2023-02-13 08:30:26 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/cbde7449bd8f027f258b08fbfe786222fa3ed7fe 8137101: [TEST_BUG] javax/swing/plaf/basic/BasicHTML/4251579/bug4251579.java failure due to timing Reviewed-by: andrew Backport-of: 4940e2e8d85f2eef88c6132923b165253c13aa73 ! jdk/test/javax/swing/plaf/basic/BasicHTML/4251579/bug4251579.java Changeset: c183dc59 Author: Sergey Bylokhov Date: 2023-02-13 08:36:53 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/c183dc59f9027f7fb5253e68fa4ac8df57a9d267 8142540: [TEST_BUG] Test sun/awt/dnd/8024061/bug8024061.java fails on ubuntu Reviewed-by: andrew Backport-of: 84dd5699d5571e586b2763da34d2a76851695854 ! jdk/test/sun/awt/dnd/8024061/bug8024061.java Changeset: ec53e001 Author: Ji?? Van?k Committer: Paul Hohensee Date: 2023-02-15 15:38:45 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/ec53e00169fabf8f53ca4a5bdd4c92f7b1f10a00 8282947: JFR: Dump on shutdown live-locks in some conditions Reviewed-by: phh Backport-of: 63eb0b7e8606dd9cd145e92eeeb744ff5b7be569 ! jdk/src/share/classes/jdk/jfr/internal/ChunksChannel.java ! jdk/src/share/classes/jdk/jfr/internal/PlatformRecording.java + jdk/test/jdk/jfr/api/recording/dump/TestDumpDevNull.java Changeset: 71108a5c Author: Ji?? Van?k Committer: Severin Gehwolf Date: 2023-02-15 17:14:01 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/71108a5c86ab709bb4f3fe1f7ad4f72a554b3830 8287463: JFR: Disable TestDevNull.java on Windows Reviewed-by: sgehwolf Backport-of: 0df4748216ab71937cfcfb86a2d66f3568bced1e ! jdk/test/jdk/jfr/api/recording/dump/TestDumpDevNull.java Changeset: 713c020d Author: Yuri Nesterenko Date: 2023-02-16 06:38:22 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/713c020dc6d3e28a3d732aee5914f68bd5f67a11 8301760: Fix possible leak in SpNegoContext dispose Reviewed-by: phh Backport-of: 3f3356bcbd2b2fbc545263dc70a48ee931a4e56b ! jdk/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java Changeset: aed31d98 Author: Paul Hohensee Date: 2023-02-16 13:34:11 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/aed31d98c375742bb986e06e19b056cb049a2a38 8270317: Large Allocation in CipherSuite Reviewed-by: simonis, mbalao Backport-of: e627caec84c169c99c04e0d355c29b806a0266ed ! jdk/src/share/classes/sun/security/ssl/CipherSuite.java Changeset: b3e23801 Author: Ji?? Van?k Committer: Paul Hohensee Date: 2023-02-16 15:01:56 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/b3e2380183b33a3b1131ad7a5f118e876914f810 8301143: [TESTBUG] jfr/event/sampling/TestNative was backported to JDK8u without proper native wrapper Reviewed-by: sgehwolf, phh ! jdk/test/jdk/jfr/event/sampling/TestNative.java + jdk/test/jdk/jfr/event/sampling/TestNative.sh Changeset: ca0a6be2 Author: Alexander Scherbatiy Date: 2023-02-17 08:58:26 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/ca0a6be2d76ce755ccfcce0d45a8943b11868067 8301246: NPE in FcFontManager.getDefaultPlatformFont() on Linux without installed fontconfig Reviewed-by: serb ! jdk/src/solaris/classes/sun/awt/FcFontManager.java Changeset: b51619d2 Author: Alexey Bakhtin Date: 2023-02-17 11:26:58 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/b51619d24e0643aa0afdba87ac20b371dbb594e8 8275535: Retrying a failed authentication on multiple LDAP servers can lead to users blocked Reviewed-by: phh Backport-of: 3be394e1606dd17c2c14ce806c796f5eb2b1ad6e ! jdk/src/share/classes/com/sun/jndi/ldap/LdapCtxFactory.java Changeset: d55468c8 Author: Sergey Nazarkin Date: 2023-02-28 13:05:33 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/d55468c82e4946970b52587a34e5ccc485f4f484 Merge From snazarki at openjdk.org Fri Mar 10 15:19:40 2023 From: snazarki at openjdk.org (Sergey Nazarkin) Date: Fri, 10 Mar 2023 15:19:40 GMT Subject: git: openjdk/aarch32-port-jdk8u: master: 5 new changesets Message-ID: <26df71e9-8ccb-419d-9389-a708277cdc06@openjdk.org> Changeset: 4a837299 Author: Deepa Kumari Committer: Severin Gehwolf Date: 2023-03-03 10:34:21 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/4a837299c68071be563e376efe6f12852bc130e7 8303408: [AIX] Broken jdk8u build after JDK-8266391 Reviewed-by: phh, sgehwolf = jdk/src/aix/classes/jdk/internal/platform/SystemMetrics.java Changeset: 7c0d5b49 Author: Severin Gehwolf Date: 2023-03-03 17:29:32 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/7c0d5b49c05b5990776584de9b3d9f53b8b55e94 8243543: jtreg test security/infra/java/security/cert/CertPathValidator/certification/BuypassCA.java fails Reviewed-by: phh, andrew Backport-of: 45abbeed2f4f2899a3c1595b0cd8e573990a16fa ! jdk/test/security/infra/java/security/cert/CertPathValidator/certification/BuypassCA.java Changeset: 3b80e877 Author: Severin Gehwolf Date: 2023-03-03 17:31:14 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/3b80e87798b41453738354aa4be030d420a0562a 8248899: security/infra/java/security/cert/CertPathValidator/certification/QuoVadisCA.java fails, Certificate has been revoked Reviewed-by: phh, andrew Backport-of: d6bb846159be7e46fba0c3ca2915617f945e0b42 ! jdk/test/security/infra/java/security/cert/CertPathValidator/certification/QuoVadisCA.java Changeset: 5806429f Author: Severin Gehwolf Date: 2023-03-03 17:32:53 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/5806429f7ac582c400896a7ac61e147e93ddeac8 8245654: Add Certigna Root CA Reviewed-by: phh, andrew Backport-of: c8ee076241cc7d83f423683d97ec37f9e0d6ebaa + jdk/make/data/cacerts/certignaca + jdk/test/security/infra/java/security/cert/CertPathValidator/certification/CertignaCA.java ! jdk/test/sun/security/lib/cacerts/VerifyCACerts.java Changeset: e0c053cc Author: Sergey Nazarkin Date: 2023-03-08 17:02:06 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/e0c053cc6b8091870344e3888fdb993b8bbdbeff Merge From david.holmes at oracle.com Mon Mar 13 02:06:55 2023 From: david.holmes at oracle.com (David Holmes) Date: Mon, 13 Mar 2023 12:06:55 +1000 Subject: Question about CAS via LDREX/STREX on 32-bit arm In-Reply-To: References: Message-ID: <455cb951-fbe4-c3dd-02f2-a0a9f058aa73@oracle.com> Hi Thomas, I'm far too rusty of the details to answer most of your questions but: > - Do we not need an explicit CLREX after the operation? Or does the > STREX also clear the hardware monitor? Or does it just not matter? STREX clears the reservation so CLREX is not needed. From Arm ARM: "A Load-Exclusive instruction marks a small block of memory for exclusive access. The size of the marked block is IMPLEMENTATION DEFINED, see Marking and the size of the marked memory block on page B2-105. A Store-Exclusive instruction to any address in the marked block clears the marking." > - We have VM_Version::supports_ldrex(). Code seems to me sometimes > guarded by this (e.g MacroAssembler::atomic_cas_bool), sometimes code > just executes ldrex/strex (e.g. the one-shot path of > MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now > generally available? Does ARMv6 mean STREX and LDREX are available? I think you will find that where the ldrex guard is missing, the code is for C2 only and a C2 build is only possible if ldrex is available. (C2 was only supported on ARMv7+.). Also the ARM conditional instructions as used in atomic_cas_bool can cause confusion when trying to understand the logic. :) Cheers, David ----- On 11/03/2023 8:18 pm, Thomas St?fe wrote: > Hi ARM experts, > > I am trying to understand how CAS is implemented on arm; in particular, > "MacroAssembler::atomic_cas_bool": > > MacroAssembler::atomic_cas_bool > > ``` > ? ? assert_different_registers(tmp_reg, oldval, newval, base); > ? ? Label loop; > ? ? bind(loop); > A ? ldrex(tmp_reg, Address(base, offset)); > B ? subs(tmp_reg, tmp_reg, oldval); > C ? strex(tmp_reg, newval, Address(base, offset), eq); > D ? cmp(tmp_reg, 1, eq); > E ? b(loop, eq); > F ? cmp(tmp_reg, 0); > ? ? if (tmpreg == noreg) { > ? ? ? pop(tmp_reg); > ? ? } > ``` > > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval > to newval. It does so in a loop. The code distinguishes two failures: > STREX failing, and a "semantically failed" CAS. > > Here is what I think this code does: > > A) LDREX: tmp=*(base+offset) > B) tmp -= oldvalue > ? ?If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. > ? ?After this, if the store succeeded, tmp_reg is 0, if the store > failed its 1. > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if > *(base+offset) had been modified before LDREX. > ? ?We now compare with 1 and ... > E) ...repeat the loop if tmp_reg was 1 > > So we loop until either *(base+offset) had been changed to some other > value concurrently before out LDREX. Or until our store succeeded. > > I wondered what the loop guards against. And why it would be okay > sometimes to omit it. > > IIUC, STREX fails if the core did lose its exclusive access to the > memory location since the LDREX. This can be one of three things, right? : > - another core slipped in an LDREX+STREX to the same location between > our LDREX and STREX > - Or we context switched to another thread or process. I assume it does > a CLREX then, right? Because how could you prevent a sequence like > "LDREX(p1) -> switch -> LDREX(p2) -> switch back STREX(p1)" - if I > understand the ARM manual [1] correctly, a STREX to a different location > than the preceding LDREX is undefined. > - Or we had a signal after LDREX and did a second LDREX in the signal > handler. Does the kernel do a CLREX when invoking a signal handler? > > More questions: > > - If I got it right, at (D), tmp_reg value "1" has two meanings: either > STREX failed or some thread increased the value concurrently by 1. We > repeat the loop either way. Is this just accepted behavior? Increasing > by 1 is maybe not that rare. > > - If I understood this correctly, the loop guards us mainly against > context switches. Without the loop a context switch would count as a > "semantically failed" CAS. Why would that be okay? Should we not do this > loop always? > > - Do we not need an explicit CLREX after the operation? Or does the > STREX also clear the hardware monitor? Or does it just not matter? > > - We have VM_Version::supports_ldrex(). Code seems to me sometimes > guarded by this (e.g MacroAssembler::atomic_cas_bool), sometimes code > just executes ldrex/strex (e.g. the one-shot path of > MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now > generally available? Does ARMv6 mean STREX and LDREX are available? > > > Thanks a lot! > > Cheers, Thomas > > > From thomas.stuefe at gmail.com Sat Mar 11 10:18:32 2023 From: thomas.stuefe at gmail.com (=?UTF-8?Q?Thomas_St=C3=BCfe?=) Date: Sat, 11 Mar 2023 11:18:32 +0100 Subject: Question about CAS via LDREX/STREX on 32-bit arm Message-ID: Hi ARM experts, I am trying to understand how CAS is implemented on arm; in particular, "MacroAssembler::atomic_cas_bool": MacroAssembler::atomic_cas_bool ``` assert_different_registers(tmp_reg, oldval, newval, base); Label loop; bind(loop); A ldrex(tmp_reg, Address(base, offset)); B subs(tmp_reg, tmp_reg, oldval); C strex(tmp_reg, newval, Address(base, offset), eq); D cmp(tmp_reg, 1, eq); E b(loop, eq); F cmp(tmp_reg, 0); if (tmpreg == noreg) { pop(tmp_reg); } ``` It uses LDREX and STREX to perform a cas of *(base+offset) from oldval to newval. It does so in a loop. The code distinguishes two failures: STREX failing, and a "semantically failed" CAS. Here is what I think this code does: A) LDREX: tmp=*(base+offset) B) tmp -= oldvalue If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. After this, if the store succeeded, tmp_reg is 0, if the store failed its 1. D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if *(base+offset) had been modified before LDREX. We now compare with 1 and ... E) ...repeat the loop if tmp_reg was 1 So we loop until either *(base+offset) had been changed to some other value concurrently before out LDREX. Or until our store succeeded. I wondered what the loop guards against. And why it would be okay sometimes to omit it. IIUC, STREX fails if the core did lose its exclusive access to the memory location since the LDREX. This can be one of three things, right? : - another core slipped in an LDREX+STREX to the same location between our LDREX and STREX - Or we context switched to another thread or process. I assume it does a CLREX then, right? Because how could you prevent a sequence like "LDREX(p1) -> switch -> LDREX(p2) -> switch back STREX(p1)" - if I understand the ARM manual [1] correctly, a STREX to a different location than the preceding LDREX is undefined. - Or we had a signal after LDREX and did a second LDREX in the signal handler. Does the kernel do a CLREX when invoking a signal handler? More questions: - If I got it right, at (D), tmp_reg value "1" has two meanings: either STREX failed or some thread increased the value concurrently by 1. We repeat the loop either way. Is this just accepted behavior? Increasing by 1 is maybe not that rare. - If I understood this correctly, the loop guards us mainly against context switches. Without the loop a context switch would count as a "semantically failed" CAS. Why would that be okay? Should we not do this loop always? - Do we not need an explicit CLREX after the operation? Or does the STREX also clear the hardware monitor? Or does it just not matter? - We have VM_Version::supports_ldrex(). Code seems to me sometimes guarded by this (e.g MacroAssembler::atomic_cas_bool), sometimes code just executes ldrex/strex (e.g. the one-shot path of MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now generally available? Does ARMv6 mean STREX and LDREX are available? Thanks a lot! Cheers, Thomas -------------- next part -------------- An HTML attachment was scrubbed... URL: From thomas.stuefe at gmail.com Mon Mar 13 05:35:23 2023 From: thomas.stuefe at gmail.com (=?UTF-8?Q?Thomas_St=C3=BCfe?=) Date: Mon, 13 Mar 2023 06:35:23 +0100 Subject: Question about CAS via LDREX/STREX on 32-bit arm In-Reply-To: <455cb951-fbe4-c3dd-02f2-a0a9f058aa73@oracle.com> References: <455cb951-fbe4-c3dd-02f2-a0a9f058aa73@oracle.com> Message-ID: Hi David, On Mon, Mar 13, 2023 at 3:07?AM David Holmes wrote: > Hi Thomas, > > I'm far too rusty of the details to answer most of your questions but: > > > - Do we not need an explicit CLREX after the operation? Or does the > > STREX also clear the hardware monitor? Or does it just not matter? > > STREX clears the reservation so CLREX is not needed. From Arm ARM: > > "A Load-Exclusive instruction marks a small block of memory for > exclusive access. The size of the marked block is IMPLEMENTATION > DEFINED, see Marking and the size of the marked memory block on page > B2-105. A Store-Exclusive instruction to any address in the marked block > clears the marking." > > > - We have VM_Version::supports_ldrex(). Code seems to me sometimes > > guarded by this (e.g MacroAssembler::atomic_cas_bool), sometimes code > > just executes ldrex/strex (e.g. the one-shot path of > > MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now > > generally available? Does ARMv6 mean STREX and LDREX are available? > > I think you will find that where the ldrex guard is missing, the code is > for C2 only and a C2 build is only possible if ldrex is available. (C2 > was only supported on ARMv7+.). > > Also the ARM conditional instructions as used in atomic_cas_bool can > cause confusion when trying to understand the logic. :) > > Thank you, that already helps! Not that rusty, it seems :-) Cheers, Thomas Cheers, > David > ----- > > > On 11/03/2023 8:18 pm, Thomas St?fe wrote: > > Hi ARM experts, > > > > I am trying to understand how CAS is implemented on arm; in particular, > > "MacroAssembler::atomic_cas_bool": > > > > MacroAssembler::atomic_cas_bool > > > > ``` > > assert_different_registers(tmp_reg, oldval, newval, base); > > Label loop; > > bind(loop); > > A ldrex(tmp_reg, Address(base, offset)); > > B subs(tmp_reg, tmp_reg, oldval); > > C strex(tmp_reg, newval, Address(base, offset), eq); > > D cmp(tmp_reg, 1, eq); > > E b(loop, eq); > > F cmp(tmp_reg, 0); > > if (tmpreg == noreg) { > > pop(tmp_reg); > > } > > ``` > > > > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval > > to newval. It does so in a loop. The code distinguishes two failures: > > STREX failing, and a "semantically failed" CAS. > > > > Here is what I think this code does: > > > > A) LDREX: tmp=*(base+offset) > > B) tmp -= oldvalue > > If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, > omit. > > After this, if the store succeeded, tmp_reg is 0, if the store > > failed its 1. > > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if > > *(base+offset) had been modified before LDREX. > > We now compare with 1 and ... > > E) ...repeat the loop if tmp_reg was 1 > > > > So we loop until either *(base+offset) had been changed to some other > > value concurrently before out LDREX. Or until our store succeeded. > > > > I wondered what the loop guards against. And why it would be okay > > sometimes to omit it. > > > > IIUC, STREX fails if the core did lose its exclusive access to the > > memory location since the LDREX. This can be one of three things, right? > : > > - another core slipped in an LDREX+STREX to the same location between > > our LDREX and STREX > > - Or we context switched to another thread or process. I assume it does > > a CLREX then, right? Because how could you prevent a sequence like > > "LDREX(p1) -> switch -> LDREX(p2) -> switch back STREX(p1)" - if I > > understand the ARM manual [1] correctly, a STREX to a different location > > than the preceding LDREX is undefined. > > - Or we had a signal after LDREX and did a second LDREX in the signal > > handler. Does the kernel do a CLREX when invoking a signal handler? > > > > More questions: > > > > - If I got it right, at (D), tmp_reg value "1" has two meanings: either > > STREX failed or some thread increased the value concurrently by 1. We > > repeat the loop either way. Is this just accepted behavior? Increasing > > by 1 is maybe not that rare. > > > > - If I understood this correctly, the loop guards us mainly against > > context switches. Without the loop a context switch would count as a > > "semantically failed" CAS. Why would that be okay? Should we not do this > > loop always? > > > > - Do we not need an explicit CLREX after the operation? Or does the > > STREX also clear the hardware monitor? Or does it just not matter? > > > > - We have VM_Version::supports_ldrex(). Code seems to me sometimes > > guarded by this (e.g MacroAssembler::atomic_cas_bool), sometimes code > > just executes ldrex/strex (e.g. the one-shot path of > > MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now > > generally available? Does ARMv6 mean STREX and LDREX are available? > > > > > > Thanks a lot! > > > > Cheers, Thomas > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From richard.reingruber at sap.com Mon Mar 13 09:02:09 2023 From: richard.reingruber at sap.com (Reingruber, Richard) Date: Mon, 13 Mar 2023 09:02:09 +0000 Subject: Question about CAS via LDREX/STREX on 32-bit arm In-Reply-To: References: Message-ID: > Hi ARM experts, Hi Thomas, not at all an ARM expert... :) but I think I understand the code. > I am trying to understand how CAS is implemented on arm; in particular, "MacroAssembler::atomic_cas_bool": > MacroAssembler::atomic_cas_bool > ``` > assert_different_registers(tmp_reg, oldval, newval, base); > Label loop; > bind(loop); > A ldrex(tmp_reg, Address(base, offset)); > B subs(tmp_reg, tmp_reg, oldval); > C strex(tmp_reg, newval, Address(base, offset), eq); > D cmp(tmp_reg, 1, eq); > E b(loop, eq); > F cmp(tmp_reg, 0); > if (tmpreg == noreg) { > pop(tmp_reg); > } > ``` > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval to newval. It does so in a loop. The code distinguishes two failures: STREX failing, and a "semantically failed" CAS. > Here is what I think this code does: > A) LDREX: tmp=*(base+offset) > B) tmp -= oldvalue > If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. > After this, if the store succeeded, tmp_reg is 0, if the store failed its 1. > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if *(base+offset) had been modified before LDREX. > We now compare with 1 and ... > E) ...repeat the loop if tmp_reg was 1 > So we loop until either *(base+offset) had been changed to some other value concurrently before out LDREX. Or until our store succeeded. > I wondered what the loop guards against. And why it would be okay sometimes to omit it. The loop is needed to try again if the reservation was lost until the STREX succeeds or *(base+offset) != oldvalue. So there are two cases. The loop is left iff (1) *(base+offset) != oldvalue (2) the STREX succeeded First it is important to understand that C, D, E are only executed if at B the eq-condition is set to true. This is based on the "Conditional Execution" feature of ARM: execution of most instructions can be made dependent on a condition (see https://developer.arm.com/documentation/den0013/d/ARM-Thumb-Unified-Assembly-Language-Instructions/Instruction-set-basics/Conditional-execution?lang=en) So in case (1) C, D, E are not executed because B indicates that *(base+offset) and oldvalue are not-eq and the loop is left. In case (2) C, D, E are executed. At C, if the reservation from A still exists, tmp_reg will be set to 0 otherwise to 1. At E the branch is taken if D indicated tmp_reg == 1 (reservation was lost) otherwise the loop is left. Cheers, Richard. From: porters-dev on behalf of Thomas St?fe Date: Saturday, 11. March 2023 at 11:19 To: porters-dev at openjdk.org , aarch32-port-dev at openjdk.org Subject: Question about CAS via LDREX/STREX on 32-bit arm Hi ARM experts, I am trying to understand how CAS is implemented on arm; in particular, "MacroAssembler::atomic_cas_bool": MacroAssembler::atomic_cas_bool ``` assert_different_registers(tmp_reg, oldval, newval, base); Label loop; bind(loop); A ldrex(tmp_reg, Address(base, offset)); B subs(tmp_reg, tmp_reg, oldval); C strex(tmp_reg, newval, Address(base, offset), eq); D cmp(tmp_reg, 1, eq); E b(loop, eq); F cmp(tmp_reg, 0); if (tmpreg == noreg) { pop(tmp_reg); } ``` It uses LDREX and STREX to perform a cas of *(base+offset) from oldval to newval. It does so in a loop. The code distinguishes two failures: STREX failing, and a "semantically failed" CAS. Here is what I think this code does: A) LDREX: tmp=*(base+offset) B) tmp -= oldvalue If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. After this, if the store succeeded, tmp_reg is 0, if the store failed its 1. D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if *(base+offset) had been modified before LDREX. We now compare with 1 and ... E) ...repeat the loop if tmp_reg was 1 So we loop until either *(base+offset) had been changed to some other value concurrently before out LDREX. Or until our store succeeded. I wondered what the loop guards against. And why it would be okay sometimes to omit it. IIUC, STREX fails if the core did lose its exclusive access to the memory location since the LDREX. This can be one of three things, right? : - another core slipped in an LDREX+STREX to the same location between our LDREX and STREX - Or we context switched to another thread or process. I assume it does a CLREX then, right? Because how could you prevent a sequence like "LDREX(p1) -> switch -> LDREX(p2) -> switch back STREX(p1)" - if I understand the ARM manual [1] correctly, a STREX to a different location than the preceding LDREX is undefined. - Or we had a signal after LDREX and did a second LDREX in the signal handler. Does the kernel do a CLREX when invoking a signal handler? More questions: - If I got it right, at (D), tmp_reg value "1" has two meanings: either STREX failed or some thread increased the value concurrently by 1. We repeat the loop either way. Is this just accepted behavior? Increasing by 1 is maybe not that rare. - If I understood this correctly, the loop guards us mainly against context switches. Without the loop a context switch would count as a "semantically failed" CAS. Why would that be okay? Should we not do this loop always? - Do we not need an explicit CLREX after the operation? Or does the STREX also clear the hardware monitor? Or does it just not matter? - We have VM_Version::supports_ldrex(). Code seems to me sometimes guarded by this (e.g MacroAssembler::atomic_cas_bool), sometimes code just executes ldrex/strex (e.g. the one-shot path of MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now generally available? Does ARMv6 mean STREX and LDREX are available? Thanks a lot! Cheers, Thomas -------------- next part -------------- An HTML attachment was scrubbed... URL: From thomas.stuefe at gmail.com Mon Mar 13 09:54:14 2023 From: thomas.stuefe at gmail.com (=?UTF-8?Q?Thomas_St=C3=BCfe?=) Date: Mon, 13 Mar 2023 10:54:14 +0100 Subject: Question about CAS via LDREX/STREX on 32-bit arm In-Reply-To: References: Message-ID: Hi Richard :) On Mon, Mar 13, 2023 at 10:02?AM Reingruber, Richard < richard.reingruber at sap.com> wrote: > > Hi ARM experts, > > > > Hi Thomas, not at all an ARM expert... :) > > but I think I understand the code. > > > > > I am trying to understand how CAS is implemented on arm; in particular, > "MacroAssembler::atomic_cas_bool": > > > > > MacroAssembler::atomic_cas_bool > > > > > ``` > > > assert_different_registers(tmp_reg, oldval, newval, base); > > > Label loop; > > > bind(loop); > > > A ldrex(tmp_reg, Address(base, offset)); > > > B subs(tmp_reg, tmp_reg, oldval); > > > C strex(tmp_reg, newval, Address(base, offset), eq); > > > D cmp(tmp_reg, 1, eq); > > > E b(loop, eq); > > > F cmp(tmp_reg, 0); > > > if (tmpreg == noreg) { > > > pop(tmp_reg); > > > } > > > ``` > > > > > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval > to newval. It does so in a loop. The code distinguishes two failures: STREX > failing, and a "semantically failed" CAS. > > > > > Here is what I think this code does: > > > > > A) LDREX: tmp=*(base+offset) > > > B) tmp -= oldvalue > > > If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > > > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, > omit. > > > After this, if the store succeeded, tmp_reg is 0, if the store failed > its 1. > > > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if > *(base+offset) had been modified before LDREX. > > > We now compare with 1 and ... > > > E) ...repeat the loop if tmp_reg was 1 > > > > > So we loop until either *(base+offset) had been changed to some other > value concurrently before out LDREX. Or until our store succeeded. > > > > > I wondered what the loop guards against. And why it would be okay > sometimes to omit it. > > > > The loop is needed to try again if the reservation was lost until the > STREX succeeds or *(base+offset) != oldvalue. > > > > So there are two cases. The loop is left iff > > > > (1) *(base+offset) != oldvalue > > (2) the STREX succeeded > > > > First it is important to understand that C, D, E are only executed if at > B the eq-condition is set to true. > > This is based on the "Conditional Execution" feature of ARM: execution of > most instructions can be made dependent on a condition (see > https://developer.arm.com/documentation/den0013/d/ARM-Thumb-Unified-Assembly-Language-Instructions/Instruction-set-basics/Conditional-execution?lang=en > ) > > > > So in case (1) C, D, E are not executed because B indicates that > *(base+offset) and oldvalue are not-eq and the loop is left. > > > Ah, thanks, that was my thinking error. I did not realize that CMP was also conditional. I assumed the "eq" in the CMP (D) was a condition for the CMP. Which makes no sense, as I know now, since CMP just does a sub and only needs two arguments. So that meant the full branch CDE was controlled from the subtraction result at B. That also resolves the "1 has a double meaning" question. It hasn't. > In case (2) C, D, E are executed. At C, if the reservation from A still > exists, tmp_reg will be set to 0 otherwise to 1. At E the branch is taken > if D indicated tmp_reg == 1 (reservation was lost) otherwise the loop is > left. > Yes, I read it the same way. So we repeat the CAS for lost reservation. I'm interested in when this could happen and why it would be okay to sometimes omit this loop and do the "raw" LDREX-STREX sequence. See my original mail. I suspect it has something to do with context switches. That the kernel does a CLREX when we switch, so if we switch between LDREX and STREX, the reservation could be lost. But why would it then be okay to ignore this sometimes? Thanks! Thomas > > Cheers, Richard. > > > > *From: *porters-dev on behalf of Thomas > St?fe > *Date: *Saturday, 11. March 2023 at 11:19 > *To: *porters-dev at openjdk.org , > aarch32-port-dev at openjdk.org > *Subject: *Question about CAS via LDREX/STREX on 32-bit arm > > Hi ARM experts, > > I am trying to understand how CAS is implemented on arm; in particular, > "MacroAssembler::atomic_cas_bool": > > MacroAssembler::atomic_cas_bool > > > > ``` > > assert_different_registers(tmp_reg, oldval, newval, base); > Label loop; > bind(loop); > A ldrex(tmp_reg, Address(base, offset)); > B subs(tmp_reg, tmp_reg, oldval); > C strex(tmp_reg, newval, Address(base, offset), eq); > D cmp(tmp_reg, 1, eq); > E b(loop, eq); > F cmp(tmp_reg, 0); > if (tmpreg == noreg) { > pop(tmp_reg); > } > ``` > > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval to > newval. It does so in a loop. The code distinguishes two failures: STREX > failing, and a "semantically failed" CAS. > > Here is what I think this code does: > > > > A) LDREX: tmp=*(base+offset) > B) tmp -= oldvalue > If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. > After this, if the store succeeded, tmp_reg is 0, if the store failed > its 1. > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if > *(base+offset) had been modified before LDREX. > We now compare with 1 and ... > E) ...repeat the loop if tmp_reg was 1 > > So we loop until either *(base+offset) had been changed to some other > value concurrently before out LDREX. Or until our store succeeded. > > I wondered what the loop guards against. And why it would be okay > sometimes to omit it. > > IIUC, STREX fails if the core did lose its exclusive access to the memory > location since the LDREX. This can be one of three things, right? : > - another core slipped in an LDREX+STREX to the same location between our > LDREX and STREX > - Or we context switched to another thread or process. I assume it does a > CLREX then, right? Because how could you prevent a sequence like "LDREX(p1) > -> switch -> LDREX(p2) -> switch back STREX(p1)" - if I understand the ARM > manual [1] correctly, a STREX to a different location than the preceding > LDREX is undefined. > - Or we had a signal after LDREX and did a second LDREX in the signal > handler. Does the kernel do a CLREX when invoking a signal handler? > > More questions: > > - If I got it right, at (D), tmp_reg value "1" has two meanings: either > STREX failed or some thread increased the value concurrently by 1. We > repeat the loop either way. Is this just accepted behavior? Increasing by 1 > is maybe not that rare. > > - If I understood this correctly, the loop guards us mainly against > context switches. Without the loop a context switch would count as a > "semantically failed" CAS. Why would that be okay? Should we not do this > loop always? > > - Do we not need an explicit CLREX after the operation? Or does the STREX > also clear the hardware monitor? Or does it just not matter? > > - We have VM_Version::supports_ldrex(). Code seems to me sometimes guarded > by this (e.g MacroAssembler::atomic_cas_bool), sometimes code just executes > ldrex/strex (e.g. the one-shot path of > MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now > generally available? Does ARMv6 mean STREX and LDREX are available? > > > Thanks a lot! > > Cheers, Thomas > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From richard.reingruber at sap.com Mon Mar 13 10:03:11 2023 From: richard.reingruber at sap.com (Reingruber, Richard) Date: Mon, 13 Mar 2023 10:03:11 +0000 Subject: Question about CAS via LDREX/STREX on 32-bit arm In-Reply-To: References: Message-ID: > Yes, I read it the same way. So we repeat the CAS for lost reservation. I'm > interested in when this could happen and why it would be okay to sometimes > omit this loop and do the "raw" LDREX-STREX sequence. See my original mail. Hm, I don't understand. The loop in the sequence A-F is always there. How is it omitted? From: Thomas St?fe Date: Monday, 13. March 2023 at 10:54 To: Reingruber, Richard Cc: porters-dev at openjdk.org , aarch32-port-dev at openjdk.org Subject: Re: Question about CAS via LDREX/STREX on 32-bit arm Hi Richard :) On Mon, Mar 13, 2023 at 10:02?AM Reingruber, Richard > wrote: > Hi ARM experts, Hi Thomas, not at all an ARM expert... :) but I think I understand the code. > I am trying to understand how CAS is implemented on arm; in particular, "MacroAssembler::atomic_cas_bool": > MacroAssembler::atomic_cas_bool > ``` > assert_different_registers(tmp_reg, oldval, newval, base); > Label loop; > bind(loop); > A ldrex(tmp_reg, Address(base, offset)); > B subs(tmp_reg, tmp_reg, oldval); > C strex(tmp_reg, newval, Address(base, offset), eq); > D cmp(tmp_reg, 1, eq); > E b(loop, eq); > F cmp(tmp_reg, 0); > if (tmpreg == noreg) { > pop(tmp_reg); > } > ``` > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval to newval. It does so in a loop. The code distinguishes two failures: STREX failing, and a "semantically failed" CAS. > Here is what I think this code does: > A) LDREX: tmp=*(base+offset) > B) tmp -= oldvalue > If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. > After this, if the store succeeded, tmp_reg is 0, if the store failed its 1. > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if *(base+offset) had been modified before LDREX. > We now compare with 1 and ... > E) ...repeat the loop if tmp_reg was 1 > So we loop until either *(base+offset) had been changed to some other value concurrently before out LDREX. Or until our store succeeded. > I wondered what the loop guards against. And why it would be okay sometimes to omit it. The loop is needed to try again if the reservation was lost until the STREX succeeds or *(base+offset) != oldvalue. So there are two cases. The loop is left iff (1) *(base+offset) != oldvalue (2) the STREX succeeded First it is important to understand that C, D, E are only executed if at B the eq-condition is set to true. This is based on the "Conditional Execution" feature of ARM: execution of most instructions can be made dependent on a condition (see https://developer.arm.com/documentation/den0013/d/ARM-Thumb-Unified-Assembly-Language-Instructions/Instruction-set-basics/Conditional-execution?lang=en) So in case (1) C, D, E are not executed because B indicates that *(base+offset) and oldvalue are not-eq and the loop is left. Ah, thanks, that was my thinking error. I did not realize that CMP was also conditional. I assumed the "eq" in the CMP (D) was a condition for the CMP. Which makes no sense, as I know now, since CMP just does a sub and only needs two arguments. So that meant the full branch CDE was controlled from the subtraction result at B. That also resolves the "1 has a double meaning" question. It hasn't. In case (2) C, D, E are executed. At C, if the reservation from A still exists, tmp_reg will be set to 0 otherwise to 1. At E the branch is taken if D indicated tmp_reg == 1 (reservation was lost) otherwise the loop is left. Yes, I read it the same way. So we repeat the CAS for lost reservation. I'm interested in when this could happen and why it would be okay to sometimes omit this loop and do the "raw" LDREX-STREX sequence. See my original mail. I suspect it has something to do with context switches. That the kernel does a CLREX when we switch, so if we switch between LDREX and STREX, the reservation could be lost. But why would it then be okay to ignore this sometimes? Thanks! Thomas Cheers, Richard. From: porters-dev > on behalf of Thomas St?fe > Date: Saturday, 11. March 2023 at 11:19 To: porters-dev at openjdk.org >, aarch32-port-dev at openjdk.org > Subject: Question about CAS via LDREX/STREX on 32-bit arm Hi ARM experts, I am trying to understand how CAS is implemented on arm; in particular, "MacroAssembler::atomic_cas_bool": MacroAssembler::atomic_cas_bool ``` assert_different_registers(tmp_reg, oldval, newval, base); Label loop; bind(loop); A ldrex(tmp_reg, Address(base, offset)); B subs(tmp_reg, tmp_reg, oldval); C strex(tmp_reg, newval, Address(base, offset), eq); D cmp(tmp_reg, 1, eq); E b(loop, eq); F cmp(tmp_reg, 0); if (tmpreg == noreg) { pop(tmp_reg); } ``` It uses LDREX and STREX to perform a cas of *(base+offset) from oldval to newval. It does so in a loop. The code distinguishes two failures: STREX failing, and a "semantically failed" CAS. Here is what I think this code does: A) LDREX: tmp=*(base+offset) B) tmp -= oldvalue If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. After this, if the store succeeded, tmp_reg is 0, if the store failed its 1. D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if *(base+offset) had been modified before LDREX. We now compare with 1 and ... E) ...repeat the loop if tmp_reg was 1 So we loop until either *(base+offset) had been changed to some other value concurrently before out LDREX. Or until our store succeeded. I wondered what the loop guards against. And why it would be okay sometimes to omit it. IIUC, STREX fails if the core did lose its exclusive access to the memory location since the LDREX. This can be one of three things, right? : - another core slipped in an LDREX+STREX to the same location between our LDREX and STREX - Or we context switched to another thread or process. I assume it does a CLREX then, right? Because how could you prevent a sequence like "LDREX(p1) -> switch -> LDREX(p2) -> switch back STREX(p1)" - if I understand the ARM manual [1] correctly, a STREX to a different location than the preceding LDREX is undefined. - Or we had a signal after LDREX and did a second LDREX in the signal handler. Does the kernel do a CLREX when invoking a signal handler? More questions: - If I got it right, at (D), tmp_reg value "1" has two meanings: either STREX failed or some thread increased the value concurrently by 1. We repeat the loop either way. Is this just accepted behavior? Increasing by 1 is maybe not that rare. - If I understood this correctly, the loop guards us mainly against context switches. Without the loop a context switch would count as a "semantically failed" CAS. Why would that be okay? Should we not do this loop always? - Do we not need an explicit CLREX after the operation? Or does the STREX also clear the hardware monitor? Or does it just not matter? - We have VM_Version::supports_ldrex(). Code seems to me sometimes guarded by this (e.g MacroAssembler::atomic_cas_bool), sometimes code just executes ldrex/strex (e.g. the one-shot path of MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now generally available? Does ARMv6 mean STREX and LDREX are available? Thanks a lot! Cheers, Thomas -------------- next part -------------- An HTML attachment was scrubbed... URL: From thomas.stuefe at gmail.com Mon Mar 13 10:07:31 2023 From: thomas.stuefe at gmail.com (=?UTF-8?Q?Thomas_St=C3=BCfe?=) Date: Mon, 13 Mar 2023 11:07:31 +0100 Subject: Question about CAS via LDREX/STREX on 32-bit arm In-Reply-To: References: Message-ID: This whole coding lives in MacroAssembler::atomic_cas_bool, but that function is not always called. There are plenty of direct usages of ldrex+strex. There is even a condition argument for MacroAssembler::cas_for_lock_acquire to either do strex directly or to dive down into atomic_cas_bool. On Mon, Mar 13, 2023 at 11:03?AM Reingruber, Richard < richard.reingruber at sap.com> wrote: > > Yes, I read it the same way. So we repeat the CAS for lost reservation. > I'm > > > interested in when this could happen and why it would be okay to > sometimes > > > omit this loop and do the "raw" LDREX-STREX sequence. See my original > mail. > > > > Hm, I don't understand. The loop in the sequence A-F is always there. How > is it omitted? > > > > > > *From: *Thomas St?fe > *Date: *Monday, 13. March 2023 at 10:54 > *To: *Reingruber, Richard > *Cc: *porters-dev at openjdk.org , > aarch32-port-dev at openjdk.org > *Subject: *Re: Question about CAS via LDREX/STREX on 32-bit arm > > Hi Richard :) > > > > > > On Mon, Mar 13, 2023 at 10:02?AM Reingruber, Richard < > richard.reingruber at sap.com> wrote: > > > Hi ARM experts, > > > > Hi Thomas, not at all an ARM expert... :) > > but I think I understand the code. > > > > > I am trying to understand how CAS is implemented on arm; in particular, > "MacroAssembler::atomic_cas_bool": > > > > > MacroAssembler::atomic_cas_bool > > > > > ``` > > > assert_different_registers(tmp_reg, oldval, newval, base); > > > Label loop; > > > bind(loop); > > > A ldrex(tmp_reg, Address(base, offset)); > > > B subs(tmp_reg, tmp_reg, oldval); > > > C strex(tmp_reg, newval, Address(base, offset), eq); > > > D cmp(tmp_reg, 1, eq); > > > E b(loop, eq); > > > F cmp(tmp_reg, 0); > > > if (tmpreg == noreg) { > > > pop(tmp_reg); > > > } > > > ``` > > > > > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval > to newval. It does so in a loop. The code distinguishes two failures: STREX > failing, and a "semantically failed" CAS. > > > > > Here is what I think this code does: > > > > > A) LDREX: tmp=*(base+offset) > > > B) tmp -= oldvalue > > > If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > > > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, > omit. > > > After this, if the store succeeded, tmp_reg is 0, if the store failed > its 1. > > > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if > *(base+offset) had been modified before LDREX. > > > We now compare with 1 and ... > > > E) ...repeat the loop if tmp_reg was 1 > > > > > So we loop until either *(base+offset) had been changed to some other > value concurrently before out LDREX. Or until our store succeeded. > > > > > I wondered what the loop guards against. And why it would be okay > sometimes to omit it. > > > > The loop is needed to try again if the reservation was lost until the > STREX succeeds or *(base+offset) != oldvalue. > > > > So there are two cases. The loop is left iff > > > > (1) *(base+offset) != oldvalue > > (2) the STREX succeeded > > > > First it is important to understand that C, D, E are only executed if at > B the eq-condition is set to true. > > This is based on the "Conditional Execution" feature of ARM: execution of > most instructions can be made dependent on a condition (see > https://developer.arm.com/documentation/den0013/d/ARM-Thumb-Unified-Assembly-Language-Instructions/Instruction-set-basics/Conditional-execution?lang=en > > ) > > > > So in case (1) C, D, E are not executed because B indicates that > *(base+offset) and oldvalue are not-eq and the loop is left. > > > > > > Ah, thanks, that was my thinking error. I did not realize that CMP was > also conditional. I assumed the "eq" in the CMP (D) was a condition for the > CMP. Which makes no sense, as I know now, since CMP just does a sub and > only needs two arguments. So that meant the full branch CDE was controlled > from the subtraction result at B. > > > > That also resolves the "1 has a double meaning" question. It hasn't. > > > > In case (2) C, D, E are executed. At C, if the reservation from A still > exists, tmp_reg will be set to 0 otherwise to 1. At E the branch is taken > if D indicated tmp_reg == 1 (reservation was lost) otherwise the loop is > left. > > > > Yes, I read it the same way. So we repeat the CAS for lost reservation. > I'm interested in when this could happen and why it would be okay to > sometimes omit this loop and do the "raw" LDREX-STREX sequence. See my > original mail. > > > > I suspect it has something to do with context switches. That the kernel > does a CLREX when we switch, so if we switch between LDREX and STREX, the > reservation could be lost. But why would it then be okay to ignore this > sometimes? > > > > Thanks! > > > > Thomas > > > > > > Cheers, Richard. > > > > *From: *porters-dev on behalf of Thomas > St?fe > *Date: *Saturday, 11. March 2023 at 11:19 > *To: *porters-dev at openjdk.org , > aarch32-port-dev at openjdk.org > *Subject: *Question about CAS via LDREX/STREX on 32-bit arm > > Hi ARM experts, > > I am trying to understand how CAS is implemented on arm; in particular, > "MacroAssembler::atomic_cas_bool": > > MacroAssembler::atomic_cas_bool > > > > ``` > > assert_different_registers(tmp_reg, oldval, newval, base); > Label loop; > bind(loop); > A ldrex(tmp_reg, Address(base, offset)); > B subs(tmp_reg, tmp_reg, oldval); > C strex(tmp_reg, newval, Address(base, offset), eq); > D cmp(tmp_reg, 1, eq); > E b(loop, eq); > F cmp(tmp_reg, 0); > if (tmpreg == noreg) { > pop(tmp_reg); > } > ``` > > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval to > newval. It does so in a loop. The code distinguishes two failures: STREX > failing, and a "semantically failed" CAS. > > Here is what I think this code does: > > > > A) LDREX: tmp=*(base+offset) > B) tmp -= oldvalue > If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. > After this, if the store succeeded, tmp_reg is 0, if the store failed > its 1. > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if > *(base+offset) had been modified before LDREX. > We now compare with 1 and ... > E) ...repeat the loop if tmp_reg was 1 > > So we loop until either *(base+offset) had been changed to some other > value concurrently before out LDREX. Or until our store succeeded. > > I wondered what the loop guards against. And why it would be okay > sometimes to omit it. > > IIUC, STREX fails if the core did lose its exclusive access to the memory > location since the LDREX. This can be one of three things, right? : > - another core slipped in an LDREX+STREX to the same location between our > LDREX and STREX > - Or we context switched to another thread or process. I assume it does a > CLREX then, right? Because how could you prevent a sequence like "LDREX(p1) > -> switch -> LDREX(p2) -> switch back STREX(p1)" - if I understand the ARM > manual [1] correctly, a STREX to a different location than the preceding > LDREX is undefined. > - Or we had a signal after LDREX and did a second LDREX in the signal > handler. Does the kernel do a CLREX when invoking a signal handler? > > More questions: > > - If I got it right, at (D), tmp_reg value "1" has two meanings: either > STREX failed or some thread increased the value concurrently by 1. We > repeat the loop either way. Is this just accepted behavior? Increasing by 1 > is maybe not that rare. > > - If I understood this correctly, the loop guards us mainly against > context switches. Without the loop a context switch would count as a > "semantically failed" CAS. Why would that be okay? Should we not do this > loop always? > > - Do we not need an explicit CLREX after the operation? Or does the STREX > also clear the hardware monitor? Or does it just not matter? > > - We have VM_Version::supports_ldrex(). Code seems to me sometimes guarded > by this (e.g MacroAssembler::atomic_cas_bool), sometimes code just executes > ldrex/strex (e.g. the one-shot path of > MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now > generally available? Does ARMv6 mean STREX and LDREX are available? > > > Thanks a lot! > > Cheers, Thomas > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From richard.reingruber at sap.com Mon Mar 13 10:25:13 2023 From: richard.reingruber at sap.com (Reingruber, Richard) Date: Mon, 13 Mar 2023 10:25:13 +0000 Subject: Question about CAS via LDREX/STREX on 32-bit arm In-Reply-To: References: Message-ID: > This whole coding lives in MacroAssembler::atomic_cas_bool, but that function > is not always called. There are plenty of direct usages of ldrex+strex. There > is even a condition argument for MacroAssembler::cas_for_lock_acquire to > either do strex directly or to dive down into atomic_cas_bool. The check of the strex result is not local in these cases. MacroAssembler::cas_for_lock_acquire checks here: https://github.com/openjdk/jdk/blob/25e7ac226a3be9c064c0a65c398a8165596150f7/src/hotspot/cpu/arm/macroAssembler_arm.cpp#L1195 Failure handling is done in the slow_case. From: Thomas St?fe Date: Monday, 13. March 2023 at 11:07 To: Reingruber, Richard Cc: porters-dev at openjdk.org , aarch32-port-dev at openjdk.org Subject: Re: Question about CAS via LDREX/STREX on 32-bit arm This whole coding lives in MacroAssembler::atomic_cas_bool, but that function is not always called. There are plenty of direct usages of ldrex+strex. There is even a condition argument for MacroAssembler::cas_for_lock_acquire to either do strex directly or to dive down into atomic_cas_bool. On Mon, Mar 13, 2023 at 11:03?AM Reingruber, Richard > wrote: > Yes, I read it the same way. So we repeat the CAS for lost reservation. I'm > interested in when this could happen and why it would be okay to sometimes > omit this loop and do the "raw" LDREX-STREX sequence. See my original mail. Hm, I don't understand. The loop in the sequence A-F is always there. How is it omitted? From: Thomas St?fe > Date: Monday, 13. March 2023 at 10:54 To: Reingruber, Richard > Cc: porters-dev at openjdk.org >, aarch32-port-dev at openjdk.org > Subject: Re: Question about CAS via LDREX/STREX on 32-bit arm Hi Richard :) On Mon, Mar 13, 2023 at 10:02?AM Reingruber, Richard > wrote: > Hi ARM experts, Hi Thomas, not at all an ARM expert... :) but I think I understand the code. > I am trying to understand how CAS is implemented on arm; in particular, "MacroAssembler::atomic_cas_bool": > MacroAssembler::atomic_cas_bool > ``` > assert_different_registers(tmp_reg, oldval, newval, base); > Label loop; > bind(loop); > A ldrex(tmp_reg, Address(base, offset)); > B subs(tmp_reg, tmp_reg, oldval); > C strex(tmp_reg, newval, Address(base, offset), eq); > D cmp(tmp_reg, 1, eq); > E b(loop, eq); > F cmp(tmp_reg, 0); > if (tmpreg == noreg) { > pop(tmp_reg); > } > ``` > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval to newval. It does so in a loop. The code distinguishes two failures: STREX failing, and a "semantically failed" CAS. > Here is what I think this code does: > A) LDREX: tmp=*(base+offset) > B) tmp -= oldvalue > If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. > After this, if the store succeeded, tmp_reg is 0, if the store failed its 1. > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if *(base+offset) had been modified before LDREX. > We now compare with 1 and ... > E) ...repeat the loop if tmp_reg was 1 > So we loop until either *(base+offset) had been changed to some other value concurrently before out LDREX. Or until our store succeeded. > I wondered what the loop guards against. And why it would be okay sometimes to omit it. The loop is needed to try again if the reservation was lost until the STREX succeeds or *(base+offset) != oldvalue. So there are two cases. The loop is left iff (1) *(base+offset) != oldvalue (2) the STREX succeeded First it is important to understand that C, D, E are only executed if at B the eq-condition is set to true. This is based on the "Conditional Execution" feature of ARM: execution of most instructions can be made dependent on a condition (see https://developer.arm.com/documentation/den0013/d/ARM-Thumb-Unified-Assembly-Language-Instructions/Instruction-set-basics/Conditional-execution?lang=en) So in case (1) C, D, E are not executed because B indicates that *(base+offset) and oldvalue are not-eq and the loop is left. Ah, thanks, that was my thinking error. I did not realize that CMP was also conditional. I assumed the "eq" in the CMP (D) was a condition for the CMP. Which makes no sense, as I know now, since CMP just does a sub and only needs two arguments. So that meant the full branch CDE was controlled from the subtraction result at B. That also resolves the "1 has a double meaning" question. It hasn't. In case (2) C, D, E are executed. At C, if the reservation from A still exists, tmp_reg will be set to 0 otherwise to 1. At E the branch is taken if D indicated tmp_reg == 1 (reservation was lost) otherwise the loop is left. Yes, I read it the same way. So we repeat the CAS for lost reservation. I'm interested in when this could happen and why it would be okay to sometimes omit this loop and do the "raw" LDREX-STREX sequence. See my original mail. I suspect it has something to do with context switches. That the kernel does a CLREX when we switch, so if we switch between LDREX and STREX, the reservation could be lost. But why would it then be okay to ignore this sometimes? Thanks! Thomas Cheers, Richard. From: porters-dev > on behalf of Thomas St?fe > Date: Saturday, 11. March 2023 at 11:19 To: porters-dev at openjdk.org >, aarch32-port-dev at openjdk.org > Subject: Question about CAS via LDREX/STREX on 32-bit arm Hi ARM experts, I am trying to understand how CAS is implemented on arm; in particular, "MacroAssembler::atomic_cas_bool": MacroAssembler::atomic_cas_bool ``` assert_different_registers(tmp_reg, oldval, newval, base); Label loop; bind(loop); A ldrex(tmp_reg, Address(base, offset)); B subs(tmp_reg, tmp_reg, oldval); C strex(tmp_reg, newval, Address(base, offset), eq); D cmp(tmp_reg, 1, eq); E b(loop, eq); F cmp(tmp_reg, 0); if (tmpreg == noreg) { pop(tmp_reg); } ``` It uses LDREX and STREX to perform a cas of *(base+offset) from oldval to newval. It does so in a loop. The code distinguishes two failures: STREX failing, and a "semantically failed" CAS. Here is what I think this code does: A) LDREX: tmp=*(base+offset) B) tmp -= oldvalue If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. After this, if the store succeeded, tmp_reg is 0, if the store failed its 1. D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if *(base+offset) had been modified before LDREX. We now compare with 1 and ... E) ...repeat the loop if tmp_reg was 1 So we loop until either *(base+offset) had been changed to some other value concurrently before out LDREX. Or until our store succeeded. I wondered what the loop guards against. And why it would be okay sometimes to omit it. IIUC, STREX fails if the core did lose its exclusive access to the memory location since the LDREX. This can be one of three things, right? : - another core slipped in an LDREX+STREX to the same location between our LDREX and STREX - Or we context switched to another thread or process. I assume it does a CLREX then, right? Because how could you prevent a sequence like "LDREX(p1) -> switch -> LDREX(p2) -> switch back STREX(p1)" - if I understand the ARM manual [1] correctly, a STREX to a different location than the preceding LDREX is undefined. - Or we had a signal after LDREX and did a second LDREX in the signal handler. Does the kernel do a CLREX when invoking a signal handler? More questions: - If I got it right, at (D), tmp_reg value "1" has two meanings: either STREX failed or some thread increased the value concurrently by 1. We repeat the loop either way. Is this just accepted behavior? Increasing by 1 is maybe not that rare. - If I understood this correctly, the loop guards us mainly against context switches. Without the loop a context switch would count as a "semantically failed" CAS. Why would that be okay? Should we not do this loop always? - Do we not need an explicit CLREX after the operation? Or does the STREX also clear the hardware monitor? Or does it just not matter? - We have VM_Version::supports_ldrex(). Code seems to me sometimes guarded by this (e.g MacroAssembler::atomic_cas_bool), sometimes code just executes ldrex/strex (e.g. the one-shot path of MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now generally available? Does ARMv6 mean STREX and LDREX are available? Thanks a lot! Cheers, Thomas -------------- next part -------------- An HTML attachment was scrubbed... URL: From thomas.stuefe at gmail.com Mon Mar 13 10:34:53 2023 From: thomas.stuefe at gmail.com (=?UTF-8?Q?Thomas_St=C3=BCfe?=) Date: Mon, 13 Mar 2023 11:34:53 +0100 Subject: Question about CAS via LDREX/STREX on 32-bit arm In-Reply-To: References: Message-ID: Got it, thanks. In the one-shot case, if STREX failed because we lost exclusive access, we would fall through to the slow case. So there should be nothing semantically different happening. On Mon, Mar 13, 2023 at 11:25?AM Reingruber, Richard < richard.reingruber at sap.com> wrote: > > This whole coding lives in MacroAssembler::atomic_cas_bool, but that > function > > > is not always called. There are plenty of direct usages of ldrex+strex. > There > > > is even a condition argument for MacroAssembler::cas_for_lock_acquire to > > > either do strex directly or to dive down into atomic_cas_bool. > > > > The check of the strex result is not local in these cases. > > MacroAssembler::cas_for_lock_acquire checks here: > https://github.com/openjdk/jdk/blob/25e7ac226a3be9c064c0a65c398a8165596150f7/src/hotspot/cpu/arm/macroAssembler_arm.cpp#L1195 > > Failure handling is done in the slow_case. > > > > *From: *Thomas St?fe > *Date: *Monday, 13. March 2023 at 11:07 > *To: *Reingruber, Richard > *Cc: *porters-dev at openjdk.org , > aarch32-port-dev at openjdk.org > *Subject: *Re: Question about CAS via LDREX/STREX on 32-bit arm > > This whole coding lives in MacroAssembler::atomic_cas_bool, but that > function is not always called. There are plenty of direct usages of > ldrex+strex. There is even a condition argument for > MacroAssembler::cas_for_lock_acquire to either do strex directly or to dive > down into atomic_cas_bool. > > > > On Mon, Mar 13, 2023 at 11:03?AM Reingruber, Richard < > richard.reingruber at sap.com> wrote: > > > Yes, I read it the same way. So we repeat the CAS for lost reservation. > I'm > > > interested in when this could happen and why it would be okay to > sometimes > > > omit this loop and do the "raw" LDREX-STREX sequence. See my original > mail. > > > > Hm, I don't understand. The loop in the sequence A-F is always there. How > is it omitted? > > > > > > *From: *Thomas St?fe > *Date: *Monday, 13. March 2023 at 10:54 > *To: *Reingruber, Richard > *Cc: *porters-dev at openjdk.org , > aarch32-port-dev at openjdk.org > *Subject: *Re: Question about CAS via LDREX/STREX on 32-bit arm > > Hi Richard :) > > > > > > On Mon, Mar 13, 2023 at 10:02?AM Reingruber, Richard < > richard.reingruber at sap.com> wrote: > > > Hi ARM experts, > > > > Hi Thomas, not at all an ARM expert... :) > > but I think I understand the code. > > > > > I am trying to understand how CAS is implemented on arm; in particular, > "MacroAssembler::atomic_cas_bool": > > > > > MacroAssembler::atomic_cas_bool > > > > > ``` > > > assert_different_registers(tmp_reg, oldval, newval, base); > > > Label loop; > > > bind(loop); > > > A ldrex(tmp_reg, Address(base, offset)); > > > B subs(tmp_reg, tmp_reg, oldval); > > > C strex(tmp_reg, newval, Address(base, offset), eq); > > > D cmp(tmp_reg, 1, eq); > > > E b(loop, eq); > > > F cmp(tmp_reg, 0); > > > if (tmpreg == noreg) { > > > pop(tmp_reg); > > > } > > > ``` > > > > > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval > to newval. It does so in a loop. The code distinguishes two failures: STREX > failing, and a "semantically failed" CAS. > > > > > Here is what I think this code does: > > > > > A) LDREX: tmp=*(base+offset) > > > B) tmp -= oldvalue > > > If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > > > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, > omit. > > > After this, if the store succeeded, tmp_reg is 0, if the store failed > its 1. > > > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if > *(base+offset) had been modified before LDREX. > > > We now compare with 1 and ... > > > E) ...repeat the loop if tmp_reg was 1 > > > > > So we loop until either *(base+offset) had been changed to some other > value concurrently before out LDREX. Or until our store succeeded. > > > > > I wondered what the loop guards against. And why it would be okay > sometimes to omit it. > > > > The loop is needed to try again if the reservation was lost until the > STREX succeeds or *(base+offset) != oldvalue. > > > > So there are two cases. The loop is left iff > > > > (1) *(base+offset) != oldvalue > > (2) the STREX succeeded > > > > First it is important to understand that C, D, E are only executed if at > B the eq-condition is set to true. > > This is based on the "Conditional Execution" feature of ARM: execution of > most instructions can be made dependent on a condition (see > https://developer.arm.com/documentation/den0013/d/ARM-Thumb-Unified-Assembly-Language-Instructions/Instruction-set-basics/Conditional-execution?lang=en > > ) > > > > So in case (1) C, D, E are not executed because B indicates that > *(base+offset) and oldvalue are not-eq and the loop is left. > > > > > > Ah, thanks, that was my thinking error. I did not realize that CMP was > also conditional. I assumed the "eq" in the CMP (D) was a condition for the > CMP. Which makes no sense, as I know now, since CMP just does a sub and > only needs two arguments. So that meant the full branch CDE was controlled > from the subtraction result at B. > > > > That also resolves the "1 has a double meaning" question. It hasn't. > > > > In case (2) C, D, E are executed. At C, if the reservation from A still > exists, tmp_reg will be set to 0 otherwise to 1. At E the branch is taken > if D indicated tmp_reg == 1 (reservation was lost) otherwise the loop is > left. > > > > Yes, I read it the same way. So we repeat the CAS for lost reservation. > I'm interested in when this could happen and why it would be okay to > sometimes omit this loop and do the "raw" LDREX-STREX sequence. See my > original mail. > > > > I suspect it has something to do with context switches. That the kernel > does a CLREX when we switch, so if we switch between LDREX and STREX, the > reservation could be lost. But why would it then be okay to ignore this > sometimes? > > > > Thanks! > > > > Thomas > > > > > > Cheers, Richard. > > > > *From: *porters-dev on behalf of Thomas > St?fe > *Date: *Saturday, 11. March 2023 at 11:19 > *To: *porters-dev at openjdk.org , > aarch32-port-dev at openjdk.org > *Subject: *Question about CAS via LDREX/STREX on 32-bit arm > > Hi ARM experts, > > I am trying to understand how CAS is implemented on arm; in particular, > "MacroAssembler::atomic_cas_bool": > > MacroAssembler::atomic_cas_bool > > > > ``` > > assert_different_registers(tmp_reg, oldval, newval, base); > Label loop; > bind(loop); > A ldrex(tmp_reg, Address(base, offset)); > B subs(tmp_reg, tmp_reg, oldval); > C strex(tmp_reg, newval, Address(base, offset), eq); > D cmp(tmp_reg, 1, eq); > E b(loop, eq); > F cmp(tmp_reg, 0); > if (tmpreg == noreg) { > pop(tmp_reg); > } > ``` > > It uses LDREX and STREX to perform a cas of *(base+offset) from oldval to > newval. It does so in a loop. The code distinguishes two failures: STREX > failing, and a "semantically failed" CAS. > > Here is what I think this code does: > > > > A) LDREX: tmp=*(base+offset) > B) tmp -= oldvalue > If *(base+offset) was unchanged, tmp_reg is now 0 and Z is 1 > C) If Z is 1: STREX the new value: *(base+offset)=newval. Otherwise, omit. > After this, if the store succeeded, tmp_reg is 0, if the store failed > its 1. > D) Here, tmp_reg is: 0 if the store succeeded, 1 if it failed, 1...n if > *(base+offset) had been modified before LDREX. > We now compare with 1 and ... > E) ...repeat the loop if tmp_reg was 1 > > So we loop until either *(base+offset) had been changed to some other > value concurrently before out LDREX. Or until our store succeeded. > > I wondered what the loop guards against. And why it would be okay > sometimes to omit it. > > IIUC, STREX fails if the core did lose its exclusive access to the memory > location since the LDREX. This can be one of three things, right? : > - another core slipped in an LDREX+STREX to the same location between our > LDREX and STREX > - Or we context switched to another thread or process. I assume it does a > CLREX then, right? Because how could you prevent a sequence like "LDREX(p1) > -> switch -> LDREX(p2) -> switch back STREX(p1)" - if I understand the ARM > manual [1] correctly, a STREX to a different location than the preceding > LDREX is undefined. > - Or we had a signal after LDREX and did a second LDREX in the signal > handler. Does the kernel do a CLREX when invoking a signal handler? > > More questions: > > - If I got it right, at (D), tmp_reg value "1" has two meanings: either > STREX failed or some thread increased the value concurrently by 1. We > repeat the loop either way. Is this just accepted behavior? Increasing by 1 > is maybe not that rare. > > - If I understood this correctly, the loop guards us mainly against > context switches. Without the loop a context switch would count as a > "semantically failed" CAS. Why would that be okay? Should we not do this > loop always? > > - Do we not need an explicit CLREX after the operation? Or does the STREX > also clear the hardware monitor? Or does it just not matter? > > - We have VM_Version::supports_ldrex(). Code seems to me sometimes guarded > by this (e.g MacroAssembler::atomic_cas_bool), sometimes code just executes > ldrex/strex (e.g. the one-shot path of > MacroAssembler::cas_for_lock_acquire). Am I mistaken? Or is LDREX now > generally available? Does ARMv6 mean STREX and LDREX are available? > > > Thanks a lot! > > Cheers, Thomas > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From snazarkin at azul.com Mon Mar 13 12:27:34 2023 From: snazarkin at azul.com (Sergey Nazarkin) Date: Mon, 13 Mar 2023 12:27:34 +0000 Subject: 8u345 availability In-Reply-To: References: Message-ID: <8A034C70-2617-4821-905A-E6CF4399E2E2@azul.com> Hi Stewart, The tags are there commit 37006c6efdddc29c6a1f3a225a1c47390650e22e (tag: jdk8u345-ga-aarch32-20220802, tag: jdk8u345-b01-aarch32-20220802) Merge: 50ed309285 2dadc2bf31 Author: Sergey Nazarkin Date: Tue Aug 2 22:27:43 2022 +0300 Merge On 2 Aug 2022, at 16:46, Stewart Addison wrote: Now that JDK8u345 has become a thing, can we get the aarch32-port-jdk8u updated with the `-b01` and `-ga` tags for that release please? We're looking to ship a version based on this, so hopefully it'll be fairly straightforward to bring it up to date. Regards, Stewart... -- Working for Red Hat on Adoptium Temurin and Node.js https://twitter.com/sxaTech https://github.com/sxa -------------- next part -------------- An HTML attachment was scrubbed... URL: From snazarki at openjdk.org Tue Mar 28 16:05:05 2023 From: snazarki at openjdk.org (Sergey Nazarkin) Date: Tue, 28 Mar 2023 16:05:05 GMT Subject: git: openjdk/aarch32-port-jdk8u: master: 3 new changesets Message-ID: Changeset: ebbd3a87 Author: Adam Farley Committer: Andrew John Hughes Date: 2023-03-09 17:15:22 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/ebbd3a87b0c473a744499d5f7691e5207cd73186 8303828: [Solaris] Broken jdk8u build after JDK-8266391 Reviewed-by: sgehwolf, andrew = jdk/src/solaris/classes/jdk/internal/platform/SystemMetrics.java Changeset: 89aeae16 Author: Adam Farley Committer: Severin Gehwolf Date: 2023-03-15 14:11:17 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/89aeae16e85ddfbd581cb86d0b0480b1e2d50e99 8304053: Revert os specific stubs for SystemMetrics Reviewed-by: sgehwolf, andrew ! jdk/make/CompileJavaClasses.gmk - jdk/src/aix/classes/jdk/internal/platform/SystemMetrics.java - jdk/src/macosx/classes/jdk/internal/platform/SystemMetrics.java - jdk/src/unix/classes/jdk/internal/platform/SystemMetrics.java Changeset: 08822af2 Author: Sergey Nazarkin Date: 2023-03-27 14:25:21 +0000 URL: https://git.openjdk.org/aarch32-port-jdk8u/commit/08822af2443c69b545d22120db4a6159b90a5765 Merge