diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0e64ad78625..8c058440ad7 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -310,7 +310,7 @@ jobs:
uses: ./.github/workflows/build-windows.yml
with:
platform: windows-x64
- msvc-toolset-version: '14.43'
+ msvc-toolset-version: '14.44'
msvc-toolset-architecture: 'x86.x64'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
@@ -322,7 +322,7 @@ jobs:
uses: ./.github/workflows/build-windows.yml
with:
platform: windows-aarch64
- msvc-toolset-version: '14.43'
+ msvc-toolset-version: '14.44'
msvc-toolset-architecture: 'arm64'
make-target: 'hotspot'
extra-conf-options: '--openjdk-target=aarch64-unknown-cygwin'
diff --git a/doc/hotspot-style.html b/doc/hotspot-style.html
index d4c06fbd6bd..dafd29d6f54 100644
--- a/doc/hotspot-style.html
+++ b/doc/hotspot-style.html
@@ -77,6 +77,9 @@
HotSpot Coding Style
thread_local
nullptr
<atomic>
+Initializing
+variables with static storage duration
Uniform Initialization
HotSpot Coding Style
Inheriting constructors
Attributes
+noexcept
Additional Permitted
Features
@@ -791,6 +795,33 @@ <atomic>
"conservative" memory ordering, which may differ from (may be stronger
than) sequentially consistent. There are algorithms in HotSpot that are
believed to rely on that ordering.
+Initializing
+variables with static storage duration
+Variables with static storage duration and dynamic
+initialization C++14
+3.6.2). should be avoided, unless an implementation is permitted to
+perform the initialization as a static initialization. The order in
+which dynamic initializations occur is incompletely specified.
+Initialization order problems can be difficult to deal with and lead to
+surprises.
+Variables with static storage duration and non-trivial destructors
+should be avoided. HotSpot doesn't generally try to cleanup on exit, and
+running destructors at exit can lead to problems.
+Some of the approaches used in HotSpot to avoid dynamic
+initialization include:
+
+Use the Deferred<T>
class template. Add a call
+to its initialization function at an appropriate place during VM
+initialization. The underlying object is never destroyed.
+For objects of class type, use a variable whose value is a
+pointer to the class, initialized to nullptr
. Provide an
+initialization function that sets the variable to a dynamically
+allocated object. Add a call to that function at an appropriate place
+during VM initialization. Such objects are usually never
+destroyed.
+
The use of uniform initialization (n2672),
@@ -1110,6 +1141,58 @@
Attributes
memory_order_consume
.
[[deprecated]]
- Not relevant in HotSpot code.
+noexcept
+Use of noexcept
exception specifications (n3050) are permitted with restrictions
+described below.
+
+- Only the argument-less form of
noexcept
exception
+specifications are permitted.
+- Allocation functions that may return
nullptr
to
+indicate allocation failure must be declared noexcept
.
+- All other uses of
noexcept
exception specifications are
+forbidden.
+noexcept
expressions are forbidden.
+- Dynamic exception specifications are forbidden.
+
+HotSpot is built with exceptions disabled, e.g. compile with
+-fno-exceptions
(gcc, clang) or no /EH
option
+(MSVC++). So why do we need to consider noexcept
at all?
+It's because noexcept
exception specifications serve two
+distinct purposes.
+The first is to allow the compiler to avoid generating code or data
+in support of exceptions being thrown by a function. But this is
+unnecessary, because exceptions are disabled.
+The second is to allow the compiler and library code to choose
+different algorithms, depending on whether some function may throw
+exceptions. This is only relevant to a certain set of functions.
+
+Some allocation functions (operator new
and
+operator new[]
) return nullptr
to indicate
+allocation failure. If a new
expression calls such an
+allocation function, it must check for and handle that possibility.
+Declaring such a function noexcept
informs the compiler
+that nullptr
is a possible result. If an allocation
+function is not declared noexcept
then the compiler may
+elide that checking and handling for a new
expression
+calling that function.
+Certain Standard Library facilities (notably containers) provide
+different guarantees for some operations (and may choose different
+algorithms to implement those operations), depending on whether certain
+functions (constructors, copy/move operations, swap) are nothrow or not.
+They detect this using type traits that test whether a function is
+declared noexcept
. This can have a significant performance
+impact if, for example, copying is chosen over a potentially throwing
+move. But this isn't relevant, since HotSpot forbids the use of most
+Standard Library facilities.
+
+HotSpot code can assume no exceptions will ever be thrown, even from
+functions not declared noexcept
. So HotSpot code doesn't
+ever need to check, either with conditional exception specifications or
+with noexcept
expressions.
+Dynamic exception specifications were deprecated in C++11. C++17
+removed all but throw()
, with that remaining a deprecated
+equivalent to noexcept
.
Additional Permitted
Features
@@ -1198,12 +1281,6 @@ Excluded Features
href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html">n2179)
— HotSpot does not permit the use of exceptions, so this feature isn't
useful.
-Avoid non-local variables with non-constexpr initialization. In
-particular, avoid variables with types requiring non-trivial
-initialization or destruction. Initialization order problems can be
-difficult to deal with and lead to surprises, as can destruction
-ordering. HotSpot doesn't generally try to cleanup on exit, and running
-destructors at exit can also lead to problems.
Avoid most operator overloading, preferring named functions. When
operator overloading is used, ensure the semantics conform to the normal
expected behavior of the operation.
diff --git a/doc/hotspot-style.md b/doc/hotspot-style.md
index f4348bea51d..0a0089ee454 100644
--- a/doc/hotspot-style.md
+++ b/doc/hotspot-style.md
@@ -770,6 +770,32 @@ ordering, which may differ from (may be stronger than) sequentially
consistent. There are algorithms in HotSpot that are believed to rely
on that ordering.
+### Initializing variables with static storage duration
+
+Variables with static storage duration and _dynamic initialization_
+[C++14 3.6.2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf)).
+should be avoided, unless an implementation is permitted to perform the
+initialization as a static initialization. The order in which dynamic
+initializations occur is incompletely specified. Initialization order
+problems can be difficult to deal with and lead to surprises.
+
+Variables with static storage duration and non-trivial destructors should be
+avoided. HotSpot doesn't generally try to cleanup on exit, and running
+destructors at exit can lead to problems.
+
+Some of the approaches used in HotSpot to avoid dynamic initialization
+include:
+
+* Use the `Deferred` class template. Add a call to its initialization
+function at an appropriate place during VM initialization. The underlying
+object is never destroyed.
+
+* For objects of class type, use a variable whose value is a pointer to the
+class, initialized to `nullptr`. Provide an initialization function that sets
+the variable to a dynamically allocated object. Add a call to that function at
+an appropriate place during VM initialization. Such objects are usually never
+destroyed.
+
### Uniform Initialization
The use of _uniform initialization_
@@ -1104,6 +1130,57 @@ The following attributes are expressly forbidden:
* `[[carries_dependency]]` - Related to `memory_order_consume`.
* `[[deprecated]]` - Not relevant in HotSpot code.
+### noexcept
+
+Use of `noexcept` exception specifications
+([n3050](http://wg21.link/n3050))
+are permitted with restrictions described below.
+
+* Only the argument-less form of `noexcept` exception specifications are
+permitted.
+* Allocation functions that may return `nullptr` to indicate allocation
+failure must be declared `noexcept`.
+* All other uses of `noexcept` exception specifications are forbidden.
+* `noexcept` expressions are forbidden.
+* Dynamic exception specifications are forbidden.
+
+HotSpot is built with exceptions disabled, e.g. compile with `-fno-exceptions`
+(gcc, clang) or no `/EH` option (MSVC++). So why do we need to consider
+`noexcept` at all? It's because `noexcept` exception specifications serve two
+distinct purposes.
+
+The first is to allow the compiler to avoid generating code or data in support
+of exceptions being thrown by a function. But this is unnecessary, because
+exceptions are disabled.
+
+The second is to allow the compiler and library code to choose different
+algorithms, depending on whether some function may throw exceptions. This is
+only relevant to a certain set of functions.
+
+* Some allocation functions (`operator new` and `operator new[]`) return
+`nullptr` to indicate allocation failure. If a `new` expression calls such an
+allocation function, it must check for and handle that possibility. Declaring
+such a function `noexcept` informs the compiler that `nullptr` is a possible
+result. If an allocation function is not declared `noexcept` then the compiler
+may elide that checking and handling for a `new` expression calling that
+function.
+
+* Certain Standard Library facilities (notably containers) provide different
+guarantees for some operations (and may choose different algorithms to
+implement those operations), depending on whether certain functions
+(constructors, copy/move operations, swap) are nothrow or not. They detect
+this using type traits that test whether a function is declared `noexcept`.
+This can have a significant performance impact if, for example, copying is
+chosen over a potentially throwing move. But this isn't relevant, since
+HotSpot forbids the use of most Standard Library facilities.
+
+HotSpot code can assume no exceptions will ever be thrown, even from functions
+not declared `noexcept`. So HotSpot code doesn't ever need to check, either
+with conditional exception specifications or with `noexcept` expressions.
+
+Dynamic exception specifications were deprecated in C++11. C++17 removed all
+but `throw()`, with that remaining a deprecated equivalent to `noexcept`.
+
### Additional Permitted Features
* `alignof`
@@ -1199,13 +1276,6 @@ namespace std;` to avoid needing to qualify Standard Library names.
([n2179](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html)) —
HotSpot does not permit the use of exceptions, so this feature isn't useful.
-* Avoid non-local variables with non-constexpr initialization.
-In particular, avoid variables with types requiring non-trivial
-initialization or destruction. Initialization order problems can be
-difficult to deal with and lead to surprises, as can destruction
-ordering. HotSpot doesn't generally try to cleanup on exit, and
-running destructors at exit can also lead to problems.
-
* Avoid most operator overloading, preferring named functions. When
operator overloading is used, ensure the semantics conform to the
normal expected behavior of the operation.
diff --git a/make/autoconf/configure b/make/autoconf/configure
index 443a37bae77..98126d1558d 100644
--- a/make/autoconf/configure
+++ b/make/autoconf/configure
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -366,7 +366,7 @@ EOT
# Print additional help, e.g. a list of toolchains and JVM features.
# This must be done by the autoconf script.
- ( CONFIGURE_PRINT_ADDITIONAL_HELP=true . $generated_script PRINTF=printf )
+ ( CONFIGURE_PRINT_ADDITIONAL_HELP=true . $generated_script PRINTF=printf ECHO=echo )
cat <= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
if (count != 0) {
- converter.convert(calendarType, lastLetter, count, jrePattern);
+ converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
lastLetter = 0;
count = 0;
}
@@ -627,7 +627,7 @@ private String translateDateFormatLetters(CalendarType calendarType, String cldr
count++;
continue;
}
- converter.convert(calendarType, lastLetter, count, jrePattern);
+ converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
lastLetter = c;
count = 1;
}
@@ -637,7 +637,7 @@ private String translateDateFormatLetters(CalendarType calendarType, String cldr
}
if (count != 0) {
- converter.convert(calendarType, lastLetter, count, jrePattern);
+ converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
}
if (cldrFormat.contentEquals(jrePattern)) {
return cldrFormat;
@@ -661,7 +661,7 @@ private String toMetaZoneKey(String tzKey) {
* on the support given by the SimpleDateFormat and the j.t.f.DateTimeFormatter
* for date-time formatting.
*/
- private void convertDateTimePatternLetter(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) {
+ private void convertDateTimePatternLetter(CalendarType calendarType, String patternKey, char cldrLetter, int count, StringBuilder sb) {
switch (cldrLetter) {
case 'u':
case 'U':
@@ -683,7 +683,7 @@ private void convertDateTimePatternLetter(CalendarType calendarType, char cldrLe
* Perform a conversion of CLDR date-time format pattern letter which is
* specific to the SimpleDateFormat.
*/
- private void convertSDFLetter(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) {
+ private void convertSDFLetter(CalendarType calendarType, String patternKey, char cldrLetter, int count, StringBuilder sb) {
switch (cldrLetter) {
case 'G':
if (calendarType != CalendarType.GREGORIAN) {
@@ -722,6 +722,17 @@ private void convertSDFLetter(CalendarType calendarType, char cldrLetter, int co
appendN('z', count, sb);
break;
+ case 'y':
+ // If the style is FULL/LONG for a Japanese Calendar, make the
+ // count == 4 for Gan-nen
+ if (calendarType == CalendarType.JAPANESE &&
+ (patternKey.contains("full-") ||
+ patternKey.contains("long-"))) {
+ count = 4;
+ }
+ appendN(cldrLetter, count, sb);
+ break;
+
case 'Z':
if (count == 4 || count == 5) {
sb.append("XXX");
@@ -767,6 +778,7 @@ private void handleSkeletonPatterns(Map myMap, CalendarType cale
.collect(Collectors.toMap(
e -> calendarPrefix + e.getKey(),
e -> translateDateFormatLetters(calendarType,
+ e.getKey(),
(String)e.getValue(),
this::convertDateTimePatternLetter)
))
@@ -775,7 +787,7 @@ private void handleSkeletonPatterns(Map myMap, CalendarType cale
@FunctionalInterface
private interface ConvertDateTimeLetters {
- void convert(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb);
+ void convert(CalendarType calendarType, String patternKey, char cldrLetter, int count, StringBuilder sb);
}
/**
diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk
index 60a88ca1c9a..f5970527648 100644
--- a/make/test/JtregNativeJdk.gmk
+++ b/make/test/JtregNativeJdk.gmk
@@ -62,7 +62,7 @@ BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libGetXSpace := java.base:libjava
ifeq ($(call isTargetOs, windows), true)
BUILD_JDK_JTREG_EXCLUDE += libDirectIO.c libInheritedChannel.c \
libExplicitAttach.c libImplicitAttach.c \
- exelauncher.c
+ exelauncher.c libFDLeaker.c exeFDLeakTester.c
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerTest := $(LIBCXX)
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib
diff --git a/src/demo/share/jfc/SwingSet2/resources/swingset_de.properties b/src/demo/share/jfc/SwingSet2/resources/swingset_de.properties
index cd6de142a26..c5d1d682fe1 100644
--- a/src/demo/share/jfc/SwingSet2/resources/swingset_de.properties
+++ b/src/demo/share/jfc/SwingSet2/resources/swingset_de.properties
@@ -456,13 +456,13 @@ SliderDemo.horizontal=Horizontal
SliderDemo.vertical=Vertikal
SliderDemo.plain=Einfach
SliderDemo.a_plain_slider=Ein einfacher Schieberegler
-SliderDemo.majorticks=Grobteilungen
-SliderDemo.majorticksdescription=Ein Schieberegler mit Grobteilungsmarkierungen
-SliderDemo.ticks=Feinteilungen, Teilungen zum Einrasten und Labels
-SliderDemo.minorticks=Feinteilungen
-SliderDemo.minorticksdescription=Ein Schieberegler mit Grob- und Feinteilungen, mit Teilungen, in die der Schieberegler einrastet, wobei einige Teilungen mit einem sichtbaren Label versehen sind
+SliderDemo.majorticks=Hauptteilstriche
+SliderDemo.majorticksdescription=Ein Schieberegler mit Hauptteilstrichen
+SliderDemo.ticks=Hilfsteilstriche, zum Einrasten und Beschriften
+SliderDemo.minorticks=Hilfsteilstriche
+SliderDemo.minorticksdescription=Ein Schieberegler mit Haupt- und Hilfsteilstrichen, in die der Schieberegler einrastet, wobei einige Teilstriche mit einer sichtbaren Beschriftung versehen sind
SliderDemo.disabled=Deaktiviert
-SliderDemo.disableddescription=Ein Schieberegler mit Grob- und Feinteilungen, der nicht aktiviert ist (kann nicht bearbeitet werden)
+SliderDemo.disableddescription=Ein Schieberegler mit Haupt- und Hilfsteilstrichen, der nicht aktiviert ist (kann nicht bearbeitet werden)
### SplitPane Demo ###
diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad
index 75baef153f8..456712cf8da 100644
--- a/src/hotspot/cpu/aarch64/aarch64.ad
+++ b/src/hotspot/cpu/aarch64/aarch64.ad
@@ -1765,10 +1765,6 @@ void MachPrologNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const {
// n.b. frame size includes space for return pc and rfp
const int framesize = C->output()->frame_size_in_bytes();
- // insert a nop at the start of the prolog so we can patch in a
- // branch if we need to invalidate the method later
- __ nop();
-
if (C->clinit_barrier_on_entry()) {
assert(!C->method()->holder()->is_not_initialized(), "initialization should have been started");
@@ -1888,7 +1884,7 @@ void MachEpilogNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const {
code_stub = &stub->entry();
}
__ relocate(relocInfo::poll_return_type);
- __ safepoint_poll(*code_stub, true /* at_return */, false /* acquire */, true /* in_nmethod */);
+ __ safepoint_poll(*code_stub, true /* at_return */, true /* in_nmethod */);
}
}
diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
index 573b451261f..2e35763aa43 100644
--- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
@@ -1136,6 +1136,10 @@ class Assembler : public AbstractAssembler {
system(0b00, 0b011, 0b00011, SY, 0b110);
}
+ void sb() {
+ system(0b00, 0b011, 0b00011, 0b0000, 0b111);
+ }
+
void sys(int op1, int CRn, int CRm, int op2,
Register rt = as_Register(0b11111)) {
system(0b01, op1, CRn, CRm, op2, rt);
diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
index 9730e75f266..9e96002187f 100644
--- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
@@ -483,7 +483,7 @@ void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
code_stub->set_safepoint_offset(__ offset());
__ relocate(relocInfo::poll_return_type);
- __ safepoint_poll(*code_stub->entry(), true /* at_return */, false /* acquire */, true /* in_nmethod */);
+ __ safepoint_poll(*code_stub->entry(), true /* at_return */, true /* in_nmethod */);
__ ret(lr);
}
diff --git a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp
index c2d1405b005..a8a2fa8b2ee 100644
--- a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -43,15 +43,15 @@ define_pd_global(intx, CompileThreshold, 1500 );
define_pd_global(intx, OnStackReplacePercentage, 933 );
define_pd_global(intx, NewSizeThreadIncrease, 4*K );
-define_pd_global(intx, InitialCodeCacheSize, 160*K);
-define_pd_global(intx, ReservedCodeCacheSize, 32*M );
-define_pd_global(intx, NonProfiledCodeHeapSize, 13*M );
-define_pd_global(intx, ProfiledCodeHeapSize, 14*M );
-define_pd_global(intx, NonNMethodCodeHeapSize, 5*M );
+define_pd_global(size_t, InitialCodeCacheSize, 160*K);
+define_pd_global(size_t, ReservedCodeCacheSize, 32*M );
+define_pd_global(size_t, NonProfiledCodeHeapSize, 13*M );
+define_pd_global(size_t, ProfiledCodeHeapSize, 14*M );
+define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M );
define_pd_global(bool, ProfileInterpreter, false);
-define_pd_global(intx, CodeCacheExpansionSize, 32*K );
-define_pd_global(uintx, CodeCacheMinBlockLength, 1);
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K);
+define_pd_global(size_t, CodeCacheExpansionSize, 32*K );
+define_pd_global(size_t, CodeCacheMinBlockLength, 1);
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
define_pd_global(bool, NeverActAsServerClassMachine, true );
define_pd_global(uint64_t,MaxRAM, 1ULL*G);
define_pd_global(bool, CICompileOSR, true );
diff --git a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp
index 6c5c469ca27..94a80dec3ea 100644
--- a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -51,8 +51,8 @@ define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K));
define_pd_global(intx, LoopUnrollLimit, 60);
define_pd_global(intx, LoopPercentProfileLimit, 10);
// InitialCodeCacheSize derived from specjbb2000 run.
-define_pd_global(intx, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize
-define_pd_global(intx, CodeCacheExpansionSize, 64*K);
+define_pd_global(size_t, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize
+define_pd_global(size_t, CodeCacheExpansionSize, 64*K);
// Ergonomics related flags
define_pd_global(uint64_t,MaxRAM, 128ULL*G);
@@ -69,12 +69,12 @@ define_pd_global(bool, SuperWordLoopUnrollAnalysis, true);
define_pd_global(uint, SuperWordStoreToLoadForwardingFailureDetection, 8);
define_pd_global(bool, IdealizeClearArrayNode, true);
-define_pd_global(intx, ReservedCodeCacheSize, 48*M);
-define_pd_global(intx, NonProfiledCodeHeapSize, 21*M);
-define_pd_global(intx, ProfiledCodeHeapSize, 22*M);
-define_pd_global(intx, NonNMethodCodeHeapSize, 5*M );
-define_pd_global(uintx, CodeCacheMinBlockLength, 6);
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K);
+define_pd_global(size_t, ReservedCodeCacheSize, 48*M);
+define_pd_global(size_t, NonProfiledCodeHeapSize, 21*M);
+define_pd_global(size_t, ProfiledCodeHeapSize, 22*M);
+define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M );
+define_pd_global(size_t, CodeCacheMinBlockLength, 6);
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
// Ergonomics related flags
define_pd_global(bool, NeverActAsServerClassMachine, false);
diff --git a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp
index 24a7a78b800..6fe3315014b 100644
--- a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp
@@ -90,13 +90,15 @@ void CompiledDirectCall::set_to_interpreted(const methodHandle& callee, address
= nativeMovConstReg_at(stub + NativeInstruction::instruction_size);
#ifdef ASSERT
- NativeGeneralJump* jump = nativeGeneralJump_at(method_holder->next_instruction_address());
+ NativeJump* jump = MacroAssembler::codestub_branch_needs_far_jump()
+ ? nativeGeneralJump_at(method_holder->next_instruction_address())
+ : nativeJump_at(method_holder->next_instruction_address());
verify_mt_safe(callee, entry, method_holder, jump);
#endif
// Update stub.
method_holder->set_data((intptr_t)callee());
- NativeGeneralJump::insert_unconditional(method_holder->next_instruction_address(), entry);
+ MacroAssembler::pd_patch_instruction(method_holder->next_instruction_address(), entry);
ICache::invalidate_range(stub, to_interp_stub_size());
// Update jump to call.
set_destination_mt_safe(stub);
diff --git a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp
index f30b5d7cf2c..65d448f908c 100644
--- a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp
@@ -289,7 +289,7 @@ void DowncallLinker::StubGenerator::generate() {
__ verify_sve_vector_length(tmp1);
- __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */, tmp1);
+ __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, false /* in_nmethod */, tmp1);
__ ldrw(tmp1, Address(rthread, JavaThread::suspend_flags_offset()));
__ cbnzw(tmp1, L_safepoint_poll_slow_path);
diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
index b316103d656..ef741c2007a 100644
--- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
@@ -38,7 +38,7 @@ define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap nulls
define_pd_global(bool, DelayCompilerStubsGeneration, COMPILER2_OR_JVMCI);
-define_pd_global(uintx, CodeCacheSegmentSize, 64);
+define_pd_global(size_t, CodeCacheSegmentSize, 64);
define_pd_global(intx, CodeEntryAlignment, 64);
define_pd_global(intx, OptoLoopAlignment, 16);
@@ -117,7 +117,7 @@ define_pd_global(intx, InlineSmallCode, 1000);
product(ccstr, OnSpinWaitInst, "yield", DIAGNOSTIC, \
"The instruction to use to implement " \
"java.lang.Thread.onSpinWait()." \
- "Options: none, nop, isb, yield.") \
+ "Options: none, nop, isb, yield, sb.") \
product(uint, OnSpinWaitInstCount, 1, DIAGNOSTIC, \
"The number of OnSpinWaitInst instructions to generate." \
"It cannot be used with OnSpinWaitInst=none.") \
diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
index 276fdd013db..e14829b7c89 100644
--- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
@@ -603,7 +603,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state,
// the stack, will call InterpreterRuntime::at_unwind.
Label slow_path;
Label fast_path;
- safepoint_poll(slow_path, true /* at_return */, false /* acquire */, false /* in_nmethod */);
+ safepoint_poll(slow_path, true /* at_return */, false /* in_nmethod */);
br(Assembler::AL, fast_path);
bind(slow_path);
push(state);
@@ -1016,31 +1016,16 @@ void InterpreterMacroAssembler::update_mdp_for_ret(Register return_bci) {
}
-void InterpreterMacroAssembler::profile_taken_branch(Register mdp,
- Register bumped_count) {
+void InterpreterMacroAssembler::profile_taken_branch(Register mdp) {
if (ProfileInterpreter) {
Label profile_continue;
// If no method data exists, go to profile_continue.
- // Otherwise, assign to mdp
test_method_data_pointer(mdp, profile_continue);
// We are taking a branch. Increment the taken count.
- // We inline increment_mdp_data_at to return bumped_count in a register
- //increment_mdp_data_at(mdp, in_bytes(JumpData::taken_offset()));
- Address data(mdp, in_bytes(JumpData::taken_offset()));
- ldr(bumped_count, data);
- assert(DataLayout::counter_increment == 1,
- "flow-free idiom only works with 1");
- // Intel does this to catch overflow
- // addptr(bumped_count, DataLayout::counter_increment);
- // sbbptr(bumped_count, 0);
- // so we do this
- adds(bumped_count, bumped_count, DataLayout::counter_increment);
- Label L;
- br(Assembler::CS, L); // skip store if counter overflow
- str(bumped_count, data);
- bind(L);
+ increment_mdp_data_at(mdp, in_bytes(JumpData::taken_offset()));
+
// The method data pointer needs to be updated to reflect the new target.
update_mdp_by_offset(mdp, in_bytes(JumpData::displacement_offset()));
bind(profile_continue);
@@ -1055,7 +1040,7 @@ void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp) {
// If no method data exists, go to profile_continue.
test_method_data_pointer(mdp, profile_continue);
- // We are taking a branch. Increment the not taken count.
+ // We are not taking a branch. Increment the not taken count.
increment_mdp_data_at(mdp, in_bytes(BranchData::not_taken_offset()));
// The method data pointer needs to be updated to correspond to
diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
index 447d4f8244e..e896a2a9430 100644
--- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
@@ -276,7 +276,7 @@ class InterpreterMacroAssembler: public MacroAssembler {
// narrow int return value
void narrow(Register result);
- void profile_taken_branch(Register mdp, Register bumped_count);
+ void profile_taken_branch(Register mdp);
void profile_not_taken_branch(Register mdp);
void profile_call(Register mdp);
void profile_final_call(Register mdp);
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
index a277a689280..44dbf3692f6 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
@@ -553,13 +553,8 @@ address MacroAssembler::target_addr_for_insn_or_null(address insn_addr, unsigned
return MacroAssembler::target_addr_for_insn(insn_addr, insn);
}
-void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool acquire, bool in_nmethod, Register tmp) {
- if (acquire) {
- lea(tmp, Address(rthread, JavaThread::polling_word_offset()));
- ldar(tmp, tmp);
- } else {
- ldr(tmp, Address(rthread, JavaThread::polling_word_offset()));
- }
+void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp) {
+ ldr(tmp, Address(rthread, JavaThread::polling_word_offset()));
if (at_return) {
// Note that when in_nmethod is set, the stack pointer is incremented before the poll. Therefore,
// we may safely use the sp instead to perform the stack watermark check.
@@ -989,11 +984,19 @@ void MacroAssembler::emit_static_call_stub() {
mov_metadata(rmethod, nullptr);
// Jump to the entry point of the c2i stub.
- movptr(rscratch1, 0);
- br(rscratch1);
+ if (codestub_branch_needs_far_jump()) {
+ movptr(rscratch1, 0);
+ br(rscratch1);
+ } else {
+ b(pc());
+ }
}
int MacroAssembler::static_call_stub_size() {
+ if (!codestub_branch_needs_far_jump()) {
+ // isb; movk; movz; movz; b
+ return 5 * NativeInstruction::instruction_size;
+ }
// isb; movk; movz; movz; movk; movz; movz; br
return 8 * NativeInstruction::instruction_size;
}
@@ -6811,6 +6814,9 @@ void MacroAssembler::spin_wait() {
case SpinWait::YIELD:
yield();
break;
+ case SpinWait::SB:
+ sb();
+ break;
default:
ShouldNotReachHere();
}
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
index f5f0f630c0c..fe2440fd3fd 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
@@ -120,7 +120,7 @@ class MacroAssembler: public Assembler {
virtual void check_and_handle_popframe(Register java_thread);
virtual void check_and_handle_earlyret(Register java_thread);
- void safepoint_poll(Label& slow_path, bool at_return, bool acquire, bool in_nmethod, Register tmp = rscratch1);
+ void safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp = rscratch1);
void rt_call(address dest, Register tmp = rscratch1);
// Load Effective Address
diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
index 33158d6b97a..5a7fececafa 100644
--- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
@@ -212,11 +212,6 @@ void NativeMovRegMem::verify() {
void NativeJump::verify() { ; }
-
-void NativeJump::check_verified_entry_alignment(address entry, address verified_entry) {
-}
-
-
address NativeJump::jump_destination() const {
address dest = MacroAssembler::target_addr_for_insn_or_null(instruction_address());
@@ -345,10 +340,6 @@ bool NativeInstruction::is_movk() {
return Instruction_aarch64::extract(int_at(0), 30, 23) == 0b11100101;
}
-bool NativeInstruction::is_sigill_not_entrant() {
- return uint_at(0) == 0xd4bbd5a1; // dcps1 #0xdead
-}
-
void NativeIllegalInstruction::insert(address code_pos) {
*(juint*)code_pos = 0xd4bbd5a1; // dcps1 #0xdead
}
@@ -359,45 +350,8 @@ bool NativeInstruction::is_stop() {
//-------------------------------------------------------------------
-// MT-safe inserting of a jump over a jump or a nop (used by
-// nmethod::make_not_entrant)
-
-void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) {
-
- assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "expected fixed destination of patch");
- assert(nativeInstruction_at(verified_entry)->is_jump_or_nop()
- || nativeInstruction_at(verified_entry)->is_sigill_not_entrant(),
- "Aarch64 cannot replace non-jump with jump");
-
- // Patch this nmethod atomically.
- if (Assembler::reachable_from_branch_at(verified_entry, dest)) {
- ptrdiff_t disp = dest - verified_entry;
- guarantee(disp < 1 << 27 && disp > - (1 << 27), "branch overflow");
-
- unsigned int insn = (0b000101 << 26) | ((disp >> 2) & 0x3ffffff);
- *(unsigned int*)verified_entry = insn;
- } else {
- // We use an illegal instruction for marking a method as not_entrant.
- NativeIllegalInstruction::insert(verified_entry);
- }
-
- ICache::invalidate_range(verified_entry, instruction_size);
-}
-
void NativeGeneralJump::verify() { }
-void NativeGeneralJump::insert_unconditional(address code_pos, address entry) {
- NativeGeneralJump* n_jump = (NativeGeneralJump*)code_pos;
-
- CodeBuffer cb(code_pos, instruction_size);
- MacroAssembler a(&cb);
-
- a.movptr(rscratch1, (uintptr_t)entry);
- a.br(rscratch1);
-
- ICache::invalidate_range(code_pos, instruction_size);
-}
-
// MT-safe patching of a long jump instruction.
void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) {
ShouldNotCallThis();
diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp
index 0eb5ff815be..df5d97c2376 100644
--- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, 2108, Red Hat Inc. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2025, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -83,7 +83,6 @@ class NativeInstruction {
bool is_safepoint_poll();
bool is_movz();
bool is_movk();
- bool is_sigill_not_entrant();
bool is_stop();
protected:
@@ -360,9 +359,6 @@ class NativeJump: public NativeInstruction {
// Insertion of native jump instruction
static void insert(address code_pos, address entry);
- // MT-safe insertion of native jump at verified method entry
- static void check_verified_entry_alignment(address entry, address verified_entry);
- static void patch_verified_entry(address entry, address verified_entry, address dest);
};
inline NativeJump* nativeJump_at(address address) {
@@ -383,7 +379,6 @@ class NativeGeneralJump: public NativeJump {
address jump_destination() const;
void set_jump_destination(address dest);
- static void insert_unconditional(address code_pos, address entry);
static void replace_mt_safe(address instr_addr, address code_buffer);
static void verify();
};
diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
index 51f18cb1bbe..da2f939e7af 100644
--- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
@@ -1877,7 +1877,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
// Check for safepoint operation in progress and/or pending suspend requests.
{
// No need for acquire as Java threads always disarm themselves.
- __ safepoint_poll(safepoint_in_progress, true /* at_return */, false /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(safepoint_in_progress, true /* at_return */, false /* in_nmethod */);
__ ldrw(rscratch1, Address(rthread, JavaThread::suspend_flags_offset()));
__ cbnzw(rscratch1, safepoint_in_progress);
__ bind(safepoint_in_progress_done);
diff --git a/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp b/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp
index 28ffeafda48..08850f05f53 100644
--- a/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp
@@ -31,7 +31,8 @@ class SpinWait {
NONE = -1,
NOP,
ISB,
- YIELD
+ YIELD,
+ SB
};
private:
diff --git a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp
index e0ca01ba6ce..695534604b8 100644
--- a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp
@@ -26,6 +26,13 @@
#ifndef CPU_AARCH64_STUBDECLARATIONS_HPP
#define CPU_AARCH64_STUBDECLARATIONS_HPP
+#define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \
+ do_arch_blob, \
+ do_arch_entry, \
+ do_arch_entry_init) \
+ do_arch_blob(preuniverse, 0) \
+
+
#define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \
do_arch_blob, \
do_arch_entry, \
diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
index a0d1e22ff96..8241e94c108 100644
--- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
@@ -11653,6 +11653,10 @@ class StubGenerator: public StubCodeGenerator {
};
// Initialization
+ void generate_preuniverse_stubs() {
+ // preuniverse stubs are not needed for aarch64
+ }
+
void generate_initial_stubs() {
// Generate initial stubs and initializes the entry points
@@ -11898,6 +11902,9 @@ class StubGenerator: public StubCodeGenerator {
public:
StubGenerator(CodeBuffer* code, StubGenBlobId blob_id) : StubCodeGenerator(code, blob_id) {
switch(blob_id) {
+ case preuniverse_id:
+ generate_preuniverse_stubs();
+ break;
case initial_id:
generate_initial_stubs();
break;
diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp
index 710970d1ea2..6593406902f 100644
--- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp
@@ -1018,7 +1018,7 @@ address TemplateInterpreterGenerator::generate_CRC32_update_entry() {
Label slow_path;
// If we need a safepoint check, generate full interpreter entry.
- __ safepoint_poll(slow_path, false /* at_return */, false /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(slow_path, false /* at_return */, false /* in_nmethod */);
// We don't generate local frame and don't align stack because
// we call stub code and there is no safepoint on this path.
@@ -1065,7 +1065,7 @@ address TemplateInterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractI
Label slow_path;
// If we need a safepoint check, generate full interpreter entry.
- __ safepoint_poll(slow_path, false /* at_return */, false /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(slow_path, false /* at_return */, false /* in_nmethod */);
// We don't generate local frame and don't align stack because
// we call stub code and there is no safepoint on this path.
@@ -1455,7 +1455,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
Label L, Continue;
// No need for acquire as Java threads always disarm themselves.
- __ safepoint_poll(L, true /* at_return */, false /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(L, true /* at_return */, false /* in_nmethod */);
__ ldrw(rscratch2, Address(rthread, JavaThread::suspend_flags_offset()));
__ cbz(rscratch2, Continue);
__ bind(L);
@@ -1608,7 +1608,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
Label slow_path;
Label fast_path;
- __ safepoint_poll(slow_path, true /* at_return */, false /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(slow_path, true /* at_return */, false /* in_nmethod */);
__ br(Assembler::AL, fast_path);
__ bind(slow_path);
__ push(dtos);
diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
index fcfe153a9a5..2ccde98d98d 100644
--- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
@@ -1759,7 +1759,7 @@ void TemplateTable::float_cmp(bool is_float, int unordered_result)
void TemplateTable::branch(bool is_jsr, bool is_wide)
{
- __ profile_taken_branch(r0, r1);
+ __ profile_taken_branch(r0);
const ByteSize be_offset = MethodCounters::backedge_counter_offset() +
InvocationCounter::counter_offset();
const ByteSize inv_offset = MethodCounters::invocation_counter_offset() +
@@ -1809,7 +1809,6 @@ void TemplateTable::branch(bool is_jsr, bool is_wide)
if (UseLoopCounter) {
// increment backedge counter for backward branches
// r0: MDO
- // w1: MDO bumped taken-count
// r2: target offset
__ cmp(r2, zr);
__ br(Assembler::GT, dispatch); // count only if backward branch
@@ -1820,12 +1819,10 @@ void TemplateTable::branch(bool is_jsr, bool is_wide)
__ ldr(rscratch1, Address(rmethod, Method::method_counters_offset()));
__ cbnz(rscratch1, has_counters);
__ push(r0);
- __ push(r1);
__ push(r2);
__ call_VM(noreg, CAST_FROM_FN_PTR(address,
InterpreterRuntime::build_method_counters), rmethod);
__ pop(r2);
- __ pop(r1);
__ pop(r0);
__ ldr(rscratch1, Address(rmethod, Method::method_counters_offset()));
__ cbz(rscratch1, dispatch); // No MethodCounters allocated, OutOfMemory
diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
index 941cb254532..6ee4a0023c6 100644
--- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
@@ -57,8 +57,13 @@ static SpinWait get_spin_wait_desc() {
return SpinWait(SpinWait::ISB, OnSpinWaitInstCount);
} else if (strcmp(OnSpinWaitInst, "yield") == 0) {
return SpinWait(SpinWait::YIELD, OnSpinWaitInstCount);
+ } else if (strcmp(OnSpinWaitInst, "sb") == 0) {
+ if (!VM_Version::supports_sb()) {
+ vm_exit_during_initialization("OnSpinWaitInst is SB but current CPU does not support SB instruction");
+ }
+ return SpinWait(SpinWait::SB, OnSpinWaitInstCount);
} else if (strcmp(OnSpinWaitInst, "none") != 0) {
- vm_exit_during_initialization("The options for OnSpinWaitInst are nop, isb, yield, and none", OnSpinWaitInst);
+ vm_exit_during_initialization("The options for OnSpinWaitInst are nop, isb, yield, sb, and none", OnSpinWaitInst);
}
if (!FLAG_IS_DEFAULT(OnSpinWaitInstCount) && OnSpinWaitInstCount > 0) {
diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp
index 373f8da5405..99450d3dde1 100644
--- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp
@@ -131,6 +131,7 @@ enum Ampere_CPU_Model {
decl(SHA3, sha3, 17) \
decl(SHA512, sha512, 21) \
decl(SVE, sve, 22) \
+ decl(SB, sb, 29) \
decl(PACA, paca, 30) \
/* flags above must follow Linux HWCAP */ \
decl(SVEBITPERM, svebitperm, 27) \
diff --git a/src/hotspot/cpu/arm/c1_globals_arm.hpp b/src/hotspot/cpu/arm/c1_globals_arm.hpp
index d22d39d6369..396f206975b 100644
--- a/src/hotspot/cpu/arm/c1_globals_arm.hpp
+++ b/src/hotspot/cpu/arm/c1_globals_arm.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -51,7 +51,7 @@ define_pd_global(size_t, ProfiledCodeHeapSize, 14*M );
define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M );
define_pd_global(bool, ProfileInterpreter, false);
define_pd_global(size_t, CodeCacheExpansionSize, 32*K );
-define_pd_global(uintx, CodeCacheMinBlockLength, 1);
+define_pd_global(size_t, CodeCacheMinBlockLength, 1);
define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
define_pd_global(bool, NeverActAsServerClassMachine, true);
define_pd_global(uint64_t, MaxRAM, 1ULL*G);
diff --git a/src/hotspot/cpu/arm/c2_globals_arm.hpp b/src/hotspot/cpu/arm/c2_globals_arm.hpp
index abd3ef6aef5..d739e67360a 100644
--- a/src/hotspot/cpu/arm/c2_globals_arm.hpp
+++ b/src/hotspot/cpu/arm/c2_globals_arm.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -94,7 +94,7 @@ define_pd_global(size_t, CodeCacheExpansionSize, 32*K);
// Ergonomics related flags
define_pd_global(uint64_t, MaxRAM, 4ULL*G);
#endif
-define_pd_global(uintx, CodeCacheMinBlockLength, 6);
+define_pd_global(size_t, CodeCacheMinBlockLength, 6);
define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed
diff --git a/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.cpp b/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.cpp
index 4492c9da33e..029e5131a84 100644
--- a/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.cpp
+++ b/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.cpp
@@ -193,10 +193,7 @@ void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) {
__ bind(guard);
// nmethod guard value. Skipped over in common case.
- //
- // Put a debug value to make any offsets skew
- // clearly visible in coredump
- __ emit_int32(0xDEADBEAF);
+ __ emit_int32(0); // initial armed value, will be reset later
__ bind(skip);
__ block_comment("nmethod_barrier end");
diff --git a/src/hotspot/cpu/arm/globals_arm.hpp b/src/hotspot/cpu/arm/globals_arm.hpp
index 9c4b8500e18..363a9a2c25c 100644
--- a/src/hotspot/cpu/arm/globals_arm.hpp
+++ b/src/hotspot/cpu/arm/globals_arm.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -36,9 +36,9 @@ define_pd_global(bool, TrapBasedNullChecks, false); // Not needed
define_pd_global(bool, DelayCompilerStubsGeneration, false); // No need - only few compiler's stubs
-define_pd_global(uintx, CodeCacheSegmentSize, 64);
-define_pd_global(intx, CodeEntryAlignment, 16);
-define_pd_global(intx, OptoLoopAlignment, 16);
+define_pd_global(size_t, CodeCacheSegmentSize, 64);
+define_pd_global(intx, CodeEntryAlignment, 16);
+define_pd_global(intx, OptoLoopAlignment, 16);
#define DEFAULT_STACK_YELLOW_PAGES (2)
#define DEFAULT_STACK_RED_PAGES (1)
diff --git a/src/hotspot/cpu/arm/nativeInst_arm_32.cpp b/src/hotspot/cpu/arm/nativeInst_arm_32.cpp
index 2caf2d7587e..232294b246a 100644
--- a/src/hotspot/cpu/arm/nativeInst_arm_32.cpp
+++ b/src/hotspot/cpu/arm/nativeInst_arm_32.cpp
@@ -282,16 +282,6 @@ void NativeMovConstReg::set_pc_relative_offset(address addr, address pc) {
}
}
-void RawNativeJump::check_verified_entry_alignment(address entry, address verified_entry) {
-}
-
-void RawNativeJump::patch_verified_entry(address entry, address verified_entry, address dest) {
- assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "should be");
- int *a = (int *)verified_entry;
- a[0] = not_entrant_illegal_instruction; // always illegal
- ICache::invalidate_range((address)&a[0], sizeof a[0]);
-}
-
void NativeGeneralJump::insert_unconditional(address code_pos, address entry) {
int offset = (int)(entry - code_pos - 8);
assert(offset < 0x2000000 && offset > -0x2000000, "encoding constraint");
diff --git a/src/hotspot/cpu/arm/nativeInst_arm_32.hpp b/src/hotspot/cpu/arm/nativeInst_arm_32.hpp
index e26c23cd983..ee856bcfe60 100644
--- a/src/hotspot/cpu/arm/nativeInst_arm_32.hpp
+++ b/src/hotspot/cpu/arm/nativeInst_arm_32.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -61,10 +61,6 @@ class RawNativeInstruction {
instr_fld_fst = 0xd0
};
- // illegal instruction used by NativeJump::patch_verified_entry
- // permanently undefined (UDF): 0xe << 28 | 0b1111111 << 20 | 0b1111 << 4
- static const int not_entrant_illegal_instruction = 0xe7f000f0;
-
static int decode_rotated_imm12(int encoding) {
int base = encoding & 0xff;
int right_rotation = (encoding & 0xf00) >> 7;
@@ -274,10 +270,6 @@ class RawNativeJump: public NativeInstruction {
}
}
- static void check_verified_entry_alignment(address entry, address verified_entry);
-
- static void patch_verified_entry(address entry, address verified_entry, address dest);
-
};
inline RawNativeJump* rawNativeJump_at(address address) {
diff --git a/src/hotspot/cpu/arm/stubDeclarations_arm.hpp b/src/hotspot/cpu/arm/stubDeclarations_arm.hpp
index 35df4b924d2..93ab16791b5 100644
--- a/src/hotspot/cpu/arm/stubDeclarations_arm.hpp
+++ b/src/hotspot/cpu/arm/stubDeclarations_arm.hpp
@@ -26,6 +26,13 @@
#ifndef CPU_ARM_STUBDECLARATIONS_HPP
#define CPU_ARM_STUBDECLARATIONS_HPP
+#define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \
+ do_arch_blob, \
+ do_arch_entry, \
+ do_arch_entry_init) \
+ do_arch_blob(preuniverse, 0) \
+
+
#define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \
do_arch_blob, \
do_arch_entry, \
diff --git a/src/hotspot/cpu/arm/stubGenerator_arm.cpp b/src/hotspot/cpu/arm/stubGenerator_arm.cpp
index fc5bcb4e0e6..5f2fd2e3b6d 100644
--- a/src/hotspot/cpu/arm/stubGenerator_arm.cpp
+++ b/src/hotspot/cpu/arm/stubGenerator_arm.cpp
@@ -3126,6 +3126,10 @@ class StubGenerator: public StubCodeGenerator {
//---------------------------------------------------------------------------
// Initialization
+ void generate_preuniverse_stubs() {
+ // preuniverse stubs are not needed for arm
+ }
+
void generate_initial_stubs() {
// Generates all stubs and initializes the entry points
@@ -3201,6 +3205,9 @@ class StubGenerator: public StubCodeGenerator {
public:
StubGenerator(CodeBuffer* code, StubGenBlobId blob_id) : StubCodeGenerator(code, blob_id) {
switch(blob_id) {
+ case preuniverse_id:
+ generate_preuniverse_stubs();
+ break;
case initial_id:
generate_initial_stubs();
break;
diff --git a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp
index 77d3653aefd..02e069b6be1 100644
--- a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp
@@ -46,7 +46,6 @@ void C1_MacroAssembler::explicit_null_check(Register base) {
void C1_MacroAssembler::build_frame(int frame_size_in_bytes, int bang_size_in_bytes) {
- // Avoid stack bang as first instruction. It may get overwritten by patch_verified_entry.
const Register return_pc = R20;
mflr(return_pc);
diff --git a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp
index 23604b5c083..ab014287250 100644
--- a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp
+++ b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2019 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -44,17 +44,17 @@ define_pd_global(intx, CompileThreshold, 1000);
define_pd_global(intx, OnStackReplacePercentage, 1400);
define_pd_global(bool, ProfileInterpreter, false);
-define_pd_global(uintx, ReservedCodeCacheSize, 32*M);
-define_pd_global(uintx, NonProfiledCodeHeapSize, 13*M );
-define_pd_global(uintx, ProfiledCodeHeapSize, 14*M );
-define_pd_global(uintx, NonNMethodCodeHeapSize, 5*M );
-define_pd_global(uintx, CodeCacheExpansionSize, 32*K);
-define_pd_global(uintx, CodeCacheMinBlockLength, 1);
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K);
+define_pd_global(size_t, ReservedCodeCacheSize, 32*M);
+define_pd_global(size_t, NonProfiledCodeHeapSize, 13*M );
+define_pd_global(size_t, ProfiledCodeHeapSize, 14*M );
+define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M );
+define_pd_global(size_t, CodeCacheExpansionSize, 32*K);
+define_pd_global(size_t, CodeCacheMinBlockLength, 1);
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
define_pd_global(bool, NeverActAsServerClassMachine, true);
define_pd_global(size_t, NewSizeThreadIncrease, 16*K);
define_pd_global(uint64_t, MaxRAM, 1ULL*G);
-define_pd_global(uintx, InitialCodeCacheSize, 160*K);
+define_pd_global(size_t, InitialCodeCacheSize, 160*K);
#endif // !COMPILER2
define_pd_global(bool, UseTypeProfile, false);
diff --git a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp
index 3e83ac4f3a4..706255d035a 100644
--- a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp
+++ b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2019 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -78,17 +78,17 @@ define_pd_global(uint, SuperWordStoreToLoadForwardingFailureDetection, 16);
define_pd_global(bool, OptoScheduling, false);
define_pd_global(bool, IdealizeClearArrayNode, true);
-define_pd_global(uintx, InitialCodeCacheSize, 2048*K); // Integral multiple of CodeCacheExpansionSize
-define_pd_global(uintx, ReservedCodeCacheSize, 48*M);
-define_pd_global(uintx, NonProfiledCodeHeapSize, 21*M);
-define_pd_global(uintx, ProfiledCodeHeapSize, 22*M);
-define_pd_global(uintx, NonNMethodCodeHeapSize, 5*M );
-define_pd_global(uintx, CodeCacheExpansionSize, 64*K);
+define_pd_global(size_t, InitialCodeCacheSize, 2048*K); // Integral multiple of CodeCacheExpansionSize
+define_pd_global(size_t, ReservedCodeCacheSize, 48*M);
+define_pd_global(size_t, NonProfiledCodeHeapSize, 21*M);
+define_pd_global(size_t, ProfiledCodeHeapSize, 22*M);
+define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M );
+define_pd_global(size_t, CodeCacheExpansionSize, 64*K);
// Ergonomics related flags
define_pd_global(uint64_t, MaxRAM, 128ULL*G);
-define_pd_global(uintx, CodeCacheMinBlockLength, 6);
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K);
+define_pd_global(size_t, CodeCacheMinBlockLength, 6);
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
define_pd_global(bool, TrapBasedRangeChecks, true);
diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
index 32a7011ac26..405ac4b2310 100644
--- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
@@ -333,9 +333,9 @@ int SaveLiveRegisters::iterate_over_register_mask(IterationAction action, int of
}
} else if (vm_reg->is_ConditionRegister()) {
// NOP. Conditions registers are covered by save_LR_CR
- } else if (vm_reg->is_VectorSRegister()) {
+ } else if (vm_reg->is_VectorRegister()) {
assert(SuperwordUseVSX, "or should not reach here");
- VectorSRegister vs_reg = vm_reg->as_VectorSRegister();
+ VectorSRegister vs_reg = (vm_reg->as_VectorRegister()).to_vsr();
if (vs_reg->encoding() >= VSR32->encoding() && vs_reg->encoding() <= VSR51->encoding()) {
reg_save_index += (2 + (reg_save_index & 1)); // 2 slots + alignment if needed
diff --git a/src/hotspot/cpu/ppc/globals_ppc.hpp b/src/hotspot/cpu/ppc/globals_ppc.hpp
index f944408fe29..65334bf0389 100644
--- a/src/hotspot/cpu/ppc/globals_ppc.hpp
+++ b/src/hotspot/cpu/ppc/globals_ppc.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -59,10 +59,10 @@ define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES);
define_pd_global(bool, VMContinuations, true);
// Use large code-entry alignment.
-define_pd_global(uintx, CodeCacheSegmentSize, 128);
-define_pd_global(intx, CodeEntryAlignment, 64);
-define_pd_global(intx, OptoLoopAlignment, 16);
-define_pd_global(intx, InlineSmallCode, 1500);
+define_pd_global(size_t, CodeCacheSegmentSize, 128);
+define_pd_global(intx, CodeEntryAlignment, 64);
+define_pd_global(intx, OptoLoopAlignment, 16);
+define_pd_global(intx, InlineSmallCode, 1500);
// Flags for template interpreter.
define_pd_global(bool, RewriteBytecodes, true);
diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
index 857911214c5..396a50427f8 100644
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
@@ -3928,8 +3928,10 @@ void MacroAssembler::kernel_crc32_vpmsum_aligned(Register crc, Register buf, Reg
Label L_outer_loop, L_inner_loop, L_last;
// Set DSCR pre-fetch to deepest.
- load_const_optimized(t0, VM_Version::_dscr_val | 7);
- mtdscr(t0);
+ if (VM_Version::has_mfdscr()) {
+ load_const_optimized(t0, VM_Version::_dscr_val | 7);
+ mtdscr(t0);
+ }
mtvrwz(VCRC, crc); // crc lives in VCRC, now
@@ -4073,8 +4075,10 @@ void MacroAssembler::kernel_crc32_vpmsum_aligned(Register crc, Register buf, Reg
// ********** Main loop end **********
// Restore DSCR pre-fetch value.
- load_const_optimized(t0, VM_Version::_dscr_val);
- mtdscr(t0);
+ if (VM_Version::has_mfdscr()) {
+ load_const_optimized(t0, VM_Version::_dscr_val);
+ mtdscr(t0);
+ }
// ********** Simple loop for remaining 16 byte blocks **********
{
diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp
index 1114da60d2b..ca492329729 100644
--- a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp
+++ b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp
@@ -39,18 +39,6 @@
#include "c1/c1_Runtime1.hpp"
#endif
-// We use an illtrap for marking a method as not_entrant
-// Work around a C++ compiler bug which changes 'this'
-bool NativeInstruction::is_sigill_not_entrant_at(address addr) {
- if (!Assembler::is_illtrap(addr)) return false;
- CodeBlob* cb = CodeCache::find_blob(addr);
- if (cb == nullptr || !cb->is_nmethod()) return false;
- nmethod *nm = (nmethod *)cb;
- // This method is not_entrant iff the illtrap instruction is
- // located at the verified entry point.
- return nm->verified_entry_point() == addr;
-}
-
#ifdef ASSERT
void NativeInstruction::verify() {
// Make sure code pattern is actually an instruction address.
@@ -331,25 +319,6 @@ void NativeMovConstReg::verify() {
}
#endif // ASSERT
-void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) {
- ResourceMark rm;
- int code_size = 1 * BytesPerInstWord;
- CodeBuffer cb(verified_entry, code_size + 1);
- MacroAssembler* a = new MacroAssembler(&cb);
-#ifdef COMPILER2
- assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "expected fixed destination of patch");
-#endif
- // Patch this nmethod atomically. Always use illtrap/trap in debug build.
- if (DEBUG_ONLY(false &&) a->is_within_range_of_b(dest, a->pc())) {
- a->b(dest);
- } else {
- // The signal handler will continue at dest=OptoRuntime::handle_wrong_method_stub().
- // We use an illtrap for marking a method as not_entrant.
- a->illtrap();
- }
- ICache::ppc64_flush_icache_bytes(verified_entry, code_size);
-}
-
#ifdef ASSERT
void NativeJump::verify() {
address addr = addr_at(0);
@@ -462,9 +431,7 @@ bool NativeDeoptInstruction::is_deopt_at(address code_pos) {
if (!Assembler::is_illtrap(code_pos)) return false;
CodeBlob* cb = CodeCache::find_blob(code_pos);
if (cb == nullptr || !cb->is_nmethod()) return false;
- nmethod *nm = (nmethod *)cb;
- // see NativeInstruction::is_sigill_not_entrant_at()
- return nm->verified_entry_point() != code_pos;
+ return true;
}
// Inserts an instruction which is specified to cause a SIGILL at a given pc
diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.hpp b/src/hotspot/cpu/ppc/nativeInst_ppc.hpp
index f4d570116a8..38126ec858d 100644
--- a/src/hotspot/cpu/ppc/nativeInst_ppc.hpp
+++ b/src/hotspot/cpu/ppc/nativeInst_ppc.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -69,13 +69,6 @@ class NativeInstruction {
return MacroAssembler::tdi_get_si16(long_at(0), Assembler::traptoUnconditional, 0);
}
- // We use an illtrap for marking a method as not_entrant.
- bool is_sigill_not_entrant() {
- // Work around a C++ compiler bug which changes 'this'.
- return NativeInstruction::is_sigill_not_entrant_at(addr_at(0));
- }
- static bool is_sigill_not_entrant_at(address addr);
-
#ifdef COMPILER2
// SIGTRAP-based implicit range checks
bool is_sigtrap_range_check() {
@@ -328,15 +321,7 @@ class NativeJump: public NativeInstruction {
}
}
- // MT-safe insertion of native jump at verified method entry
- static void patch_verified_entry(address entry, address verified_entry, address dest);
-
void verify() NOT_DEBUG_RETURN;
-
- static void check_verified_entry_alignment(address entry, address verified_entry) {
- // We just patch one instruction on ppc64, so the jump doesn't have to
- // be aligned. Nothing to do here.
- }
};
// Instantiates a NativeJump object starting at the given instruction
diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad
index d8e00cfef89..5f85ff96664 100644
--- a/src/hotspot/cpu/ppc/ppc.ad
+++ b/src/hotspot/cpu/ppc/ppc.ad
@@ -255,329 +255,168 @@ register %{
reg_def SR_PPR( SOC, SOC, Op_RegP, 5, SR_PPR->as_VMReg()); // v
// ----------------------------
-// Vector-Scalar Registers
+// Vector Registers
// ----------------------------
- // 1st 32 VSRs are aliases for the FPRs which are already defined above.
- reg_def VSR0 (SOC, SOC, Op_RegF, 0, VMRegImpl::Bad());
- reg_def VSR0_H (SOC, SOC, Op_RegF, 0, VMRegImpl::Bad());
- reg_def VSR0_J (SOC, SOC, Op_RegF, 0, VMRegImpl::Bad());
- reg_def VSR0_K (SOC, SOC, Op_RegF, 0, VMRegImpl::Bad());
-
- reg_def VSR1 (SOC, SOC, Op_RegF, 1, VMRegImpl::Bad());
- reg_def VSR1_H (SOC, SOC, Op_RegF, 1, VMRegImpl::Bad());
- reg_def VSR1_J (SOC, SOC, Op_RegF, 1, VMRegImpl::Bad());
- reg_def VSR1_K (SOC, SOC, Op_RegF, 1, VMRegImpl::Bad());
-
- reg_def VSR2 (SOC, SOC, Op_RegF, 2, VMRegImpl::Bad());
- reg_def VSR2_H (SOC, SOC, Op_RegF, 2, VMRegImpl::Bad());
- reg_def VSR2_J (SOC, SOC, Op_RegF, 2, VMRegImpl::Bad());
- reg_def VSR2_K (SOC, SOC, Op_RegF, 2, VMRegImpl::Bad());
-
- reg_def VSR3 (SOC, SOC, Op_RegF, 3, VMRegImpl::Bad());
- reg_def VSR3_H (SOC, SOC, Op_RegF, 3, VMRegImpl::Bad());
- reg_def VSR3_J (SOC, SOC, Op_RegF, 3, VMRegImpl::Bad());
- reg_def VSR3_K (SOC, SOC, Op_RegF, 3, VMRegImpl::Bad());
-
- reg_def VSR4 (SOC, SOC, Op_RegF, 4, VMRegImpl::Bad());
- reg_def VSR4_H (SOC, SOC, Op_RegF, 4, VMRegImpl::Bad());
- reg_def VSR4_J (SOC, SOC, Op_RegF, 4, VMRegImpl::Bad());
- reg_def VSR4_K (SOC, SOC, Op_RegF, 4, VMRegImpl::Bad());
-
- reg_def VSR5 (SOC, SOC, Op_RegF, 5, VMRegImpl::Bad());
- reg_def VSR5_H (SOC, SOC, Op_RegF, 5, VMRegImpl::Bad());
- reg_def VSR5_J (SOC, SOC, Op_RegF, 5, VMRegImpl::Bad());
- reg_def VSR5_K (SOC, SOC, Op_RegF, 5, VMRegImpl::Bad());
-
- reg_def VSR6 (SOC, SOC, Op_RegF, 6, VMRegImpl::Bad());
- reg_def VSR6_H (SOC, SOC, Op_RegF, 6, VMRegImpl::Bad());
- reg_def VSR6_J (SOC, SOC, Op_RegF, 6, VMRegImpl::Bad());
- reg_def VSR6_K (SOC, SOC, Op_RegF, 6, VMRegImpl::Bad());
-
- reg_def VSR7 (SOC, SOC, Op_RegF, 7, VMRegImpl::Bad());
- reg_def VSR7_H (SOC, SOC, Op_RegF, 7, VMRegImpl::Bad());
- reg_def VSR7_J (SOC, SOC, Op_RegF, 7, VMRegImpl::Bad());
- reg_def VSR7_K (SOC, SOC, Op_RegF, 7, VMRegImpl::Bad());
-
- reg_def VSR8 (SOC, SOC, Op_RegF, 8, VMRegImpl::Bad());
- reg_def VSR8_H (SOC, SOC, Op_RegF, 8, VMRegImpl::Bad());
- reg_def VSR8_J (SOC, SOC, Op_RegF, 8, VMRegImpl::Bad());
- reg_def VSR8_K (SOC, SOC, Op_RegF, 8, VMRegImpl::Bad());
-
- reg_def VSR9 (SOC, SOC, Op_RegF, 9, VMRegImpl::Bad());
- reg_def VSR9_H (SOC, SOC, Op_RegF, 9, VMRegImpl::Bad());
- reg_def VSR9_J (SOC, SOC, Op_RegF, 9, VMRegImpl::Bad());
- reg_def VSR9_K (SOC, SOC, Op_RegF, 9, VMRegImpl::Bad());
-
- reg_def VSR10 (SOC, SOC, Op_RegF, 10, VMRegImpl::Bad());
- reg_def VSR10_H(SOC, SOC, Op_RegF, 10, VMRegImpl::Bad());
- reg_def VSR10_J(SOC, SOC, Op_RegF, 10, VMRegImpl::Bad());
- reg_def VSR10_K(SOC, SOC, Op_RegF, 10, VMRegImpl::Bad());
-
- reg_def VSR11 (SOC, SOC, Op_RegF, 11, VMRegImpl::Bad());
- reg_def VSR11_H(SOC, SOC, Op_RegF, 11, VMRegImpl::Bad());
- reg_def VSR11_J(SOC, SOC, Op_RegF, 11, VMRegImpl::Bad());
- reg_def VSR11_K(SOC, SOC, Op_RegF, 11, VMRegImpl::Bad());
-
- reg_def VSR12 (SOC, SOC, Op_RegF, 12, VMRegImpl::Bad());
- reg_def VSR12_H(SOC, SOC, Op_RegF, 12, VMRegImpl::Bad());
- reg_def VSR12_J(SOC, SOC, Op_RegF, 12, VMRegImpl::Bad());
- reg_def VSR12_K(SOC, SOC, Op_RegF, 12, VMRegImpl::Bad());
-
- reg_def VSR13 (SOC, SOC, Op_RegF, 13, VMRegImpl::Bad());
- reg_def VSR13_H(SOC, SOC, Op_RegF, 13, VMRegImpl::Bad());
- reg_def VSR13_J(SOC, SOC, Op_RegF, 13, VMRegImpl::Bad());
- reg_def VSR13_K(SOC, SOC, Op_RegF, 13, VMRegImpl::Bad());
-
- reg_def VSR14 (SOC, SOC, Op_RegF, 14, VMRegImpl::Bad());
- reg_def VSR14_H(SOC, SOC, Op_RegF, 14, VMRegImpl::Bad());
- reg_def VSR14_J(SOC, SOC, Op_RegF, 14, VMRegImpl::Bad());
- reg_def VSR14_K(SOC, SOC, Op_RegF, 14, VMRegImpl::Bad());
-
- reg_def VSR15 (SOC, SOC, Op_RegF, 15, VMRegImpl::Bad());
- reg_def VSR15_H(SOC, SOC, Op_RegF, 15, VMRegImpl::Bad());
- reg_def VSR15_J(SOC, SOC, Op_RegF, 15, VMRegImpl::Bad());
- reg_def VSR15_K(SOC, SOC, Op_RegF, 15, VMRegImpl::Bad());
-
- reg_def VSR16 (SOC, SOC, Op_RegF, 16, VMRegImpl::Bad());
- reg_def VSR16_H(SOC, SOC, Op_RegF, 16, VMRegImpl::Bad());
- reg_def VSR16_J(SOC, SOC, Op_RegF, 16, VMRegImpl::Bad());
- reg_def VSR16_K(SOC, SOC, Op_RegF, 16, VMRegImpl::Bad());
-
- reg_def VSR17 (SOC, SOC, Op_RegF, 17, VMRegImpl::Bad());
- reg_def VSR17_H(SOC, SOC, Op_RegF, 17, VMRegImpl::Bad());
- reg_def VSR17_J(SOC, SOC, Op_RegF, 17, VMRegImpl::Bad());
- reg_def VSR17_K(SOC, SOC, Op_RegF, 17, VMRegImpl::Bad());
-
- reg_def VSR18 (SOC, SOC, Op_RegF, 18, VMRegImpl::Bad());
- reg_def VSR18_H(SOC, SOC, Op_RegF, 18, VMRegImpl::Bad());
- reg_def VSR18_J(SOC, SOC, Op_RegF, 18, VMRegImpl::Bad());
- reg_def VSR18_K(SOC, SOC, Op_RegF, 18, VMRegImpl::Bad());
-
- reg_def VSR19 (SOC, SOC, Op_RegF, 19, VMRegImpl::Bad());
- reg_def VSR19_H(SOC, SOC, Op_RegF, 19, VMRegImpl::Bad());
- reg_def VSR19_J(SOC, SOC, Op_RegF, 19, VMRegImpl::Bad());
- reg_def VSR19_K(SOC, SOC, Op_RegF, 19, VMRegImpl::Bad());
-
- reg_def VSR20 (SOC, SOC, Op_RegF, 20, VMRegImpl::Bad());
- reg_def VSR20_H(SOC, SOC, Op_RegF, 20, VMRegImpl::Bad());
- reg_def VSR20_J(SOC, SOC, Op_RegF, 20, VMRegImpl::Bad());
- reg_def VSR20_K(SOC, SOC, Op_RegF, 20, VMRegImpl::Bad());
-
- reg_def VSR21 (SOC, SOC, Op_RegF, 21, VMRegImpl::Bad());
- reg_def VSR21_H(SOC, SOC, Op_RegF, 21, VMRegImpl::Bad());
- reg_def VSR21_J(SOC, SOC, Op_RegF, 21, VMRegImpl::Bad());
- reg_def VSR21_K(SOC, SOC, Op_RegF, 21, VMRegImpl::Bad());
-
- reg_def VSR22 (SOC, SOC, Op_RegF, 22, VMRegImpl::Bad());
- reg_def VSR22_H(SOC, SOC, Op_RegF, 22, VMRegImpl::Bad());
- reg_def VSR22_J(SOC, SOC, Op_RegF, 22, VMRegImpl::Bad());
- reg_def VSR22_K(SOC, SOC, Op_RegF, 22, VMRegImpl::Bad());
-
- reg_def VSR23 (SOC, SOC, Op_RegF, 23, VMRegImpl::Bad());
- reg_def VSR23_H(SOC, SOC, Op_RegF, 23, VMRegImpl::Bad());
- reg_def VSR23_J(SOC, SOC, Op_RegF, 23, VMRegImpl::Bad());
- reg_def VSR23_K(SOC, SOC, Op_RegF, 23, VMRegImpl::Bad());
-
- reg_def VSR24 (SOC, SOC, Op_RegF, 24, VMRegImpl::Bad());
- reg_def VSR24_H(SOC, SOC, Op_RegF, 24, VMRegImpl::Bad());
- reg_def VSR24_J(SOC, SOC, Op_RegF, 24, VMRegImpl::Bad());
- reg_def VSR24_K(SOC, SOC, Op_RegF, 24, VMRegImpl::Bad());
-
- reg_def VSR25 (SOC, SOC, Op_RegF, 25, VMRegImpl::Bad());
- reg_def VSR25_H(SOC, SOC, Op_RegF, 25, VMRegImpl::Bad());
- reg_def VSR25_J(SOC, SOC, Op_RegF, 25, VMRegImpl::Bad());
- reg_def VSR25_K(SOC, SOC, Op_RegF, 25, VMRegImpl::Bad());
-
- reg_def VSR26 (SOC, SOC, Op_RegF, 26, VMRegImpl::Bad());
- reg_def VSR26_H(SOC, SOC, Op_RegF, 26, VMRegImpl::Bad());
- reg_def VSR26_J(SOC, SOC, Op_RegF, 26, VMRegImpl::Bad());
- reg_def VSR26_K(SOC, SOC, Op_RegF, 26, VMRegImpl::Bad());
-
- reg_def VSR27 (SOC, SOC, Op_RegF, 27, VMRegImpl::Bad());
- reg_def VSR27_H(SOC, SOC, Op_RegF, 27, VMRegImpl::Bad());
- reg_def VSR27_J(SOC, SOC, Op_RegF, 27, VMRegImpl::Bad());
- reg_def VSR27_K(SOC, SOC, Op_RegF, 27, VMRegImpl::Bad());
-
- reg_def VSR28 (SOC, SOC, Op_RegF, 28, VMRegImpl::Bad());
- reg_def VSR28_H(SOC, SOC, Op_RegF, 28, VMRegImpl::Bad());
- reg_def VSR28_J(SOC, SOC, Op_RegF, 28, VMRegImpl::Bad());
- reg_def VSR28_K(SOC, SOC, Op_RegF, 28, VMRegImpl::Bad());
-
- reg_def VSR29 (SOC, SOC, Op_RegF, 29, VMRegImpl::Bad());
- reg_def VSR29_H(SOC, SOC, Op_RegF, 29, VMRegImpl::Bad());
- reg_def VSR29_J(SOC, SOC, Op_RegF, 29, VMRegImpl::Bad());
- reg_def VSR29_K(SOC, SOC, Op_RegF, 29, VMRegImpl::Bad());
-
- reg_def VSR30 (SOC, SOC, Op_RegF, 30, VMRegImpl::Bad());
- reg_def VSR30_H(SOC, SOC, Op_RegF, 30, VMRegImpl::Bad());
- reg_def VSR30_J(SOC, SOC, Op_RegF, 30, VMRegImpl::Bad());
- reg_def VSR30_K(SOC, SOC, Op_RegF, 30, VMRegImpl::Bad());
-
- reg_def VSR31 (SOC, SOC, Op_RegF, 31, VMRegImpl::Bad());
- reg_def VSR31_H(SOC, SOC, Op_RegF, 31, VMRegImpl::Bad());
- reg_def VSR31_J(SOC, SOC, Op_RegF, 31, VMRegImpl::Bad());
- reg_def VSR31_K(SOC, SOC, Op_RegF, 31, VMRegImpl::Bad());
-
- // 2nd 32 VSRs are aliases for the VRs which are only defined here.
- reg_def VSR32 (SOC, SOC, Op_RegF, 32, VSR32->as_VMReg() );
- reg_def VSR32_H(SOC, SOC, Op_RegF, 32, VSR32->as_VMReg()->next() );
- reg_def VSR32_J(SOC, SOC, Op_RegF, 32, VSR32->as_VMReg()->next(2));
- reg_def VSR32_K(SOC, SOC, Op_RegF, 32, VSR32->as_VMReg()->next(3));
-
- reg_def VSR33 (SOC, SOC, Op_RegF, 33, VSR33->as_VMReg() );
- reg_def VSR33_H(SOC, SOC, Op_RegF, 33, VSR33->as_VMReg()->next() );
- reg_def VSR33_J(SOC, SOC, Op_RegF, 33, VSR33->as_VMReg()->next(2));
- reg_def VSR33_K(SOC, SOC, Op_RegF, 33, VSR33->as_VMReg()->next(3));
-
- reg_def VSR34 (SOC, SOC, Op_RegF, 34, VSR34->as_VMReg() );
- reg_def VSR34_H(SOC, SOC, Op_RegF, 34, VSR34->as_VMReg()->next() );
- reg_def VSR34_J(SOC, SOC, Op_RegF, 34, VSR34->as_VMReg()->next(2));
- reg_def VSR34_K(SOC, SOC, Op_RegF, 34, VSR34->as_VMReg()->next(3));
-
- reg_def VSR35 (SOC, SOC, Op_RegF, 35, VSR35->as_VMReg() );
- reg_def VSR35_H(SOC, SOC, Op_RegF, 35, VSR35->as_VMReg()->next() );
- reg_def VSR35_J(SOC, SOC, Op_RegF, 35, VSR35->as_VMReg()->next(2));
- reg_def VSR35_K(SOC, SOC, Op_RegF, 35, VSR35->as_VMReg()->next(3));
-
- reg_def VSR36 (SOC, SOC, Op_RegF, 36, VSR36->as_VMReg() );
- reg_def VSR36_H(SOC, SOC, Op_RegF, 36, VSR36->as_VMReg()->next() );
- reg_def VSR36_J(SOC, SOC, Op_RegF, 36, VSR36->as_VMReg()->next(2));
- reg_def VSR36_K(SOC, SOC, Op_RegF, 36, VSR36->as_VMReg()->next(3));
-
- reg_def VSR37 (SOC, SOC, Op_RegF, 37, VSR37->as_VMReg() );
- reg_def VSR37_H(SOC, SOC, Op_RegF, 37, VSR37->as_VMReg()->next() );
- reg_def VSR37_J(SOC, SOC, Op_RegF, 37, VSR37->as_VMReg()->next(2));
- reg_def VSR37_K(SOC, SOC, Op_RegF, 37, VSR37->as_VMReg()->next(3));
-
- reg_def VSR38 (SOC, SOC, Op_RegF, 38, VSR38->as_VMReg() );
- reg_def VSR38_H(SOC, SOC, Op_RegF, 38, VSR38->as_VMReg()->next() );
- reg_def VSR38_J(SOC, SOC, Op_RegF, 38, VSR38->as_VMReg()->next(2));
- reg_def VSR38_K(SOC, SOC, Op_RegF, 38, VSR38->as_VMReg()->next(3));
-
- reg_def VSR39 (SOC, SOC, Op_RegF, 39, VSR39->as_VMReg() );
- reg_def VSR39_H(SOC, SOC, Op_RegF, 39, VSR39->as_VMReg()->next() );
- reg_def VSR39_J(SOC, SOC, Op_RegF, 39, VSR39->as_VMReg()->next(2));
- reg_def VSR39_K(SOC, SOC, Op_RegF, 39, VSR39->as_VMReg()->next(3));
-
- reg_def VSR40 (SOC, SOC, Op_RegF, 40, VSR40->as_VMReg() );
- reg_def VSR40_H(SOC, SOC, Op_RegF, 40, VSR40->as_VMReg()->next() );
- reg_def VSR40_J(SOC, SOC, Op_RegF, 40, VSR40->as_VMReg()->next(2));
- reg_def VSR40_K(SOC, SOC, Op_RegF, 40, VSR40->as_VMReg()->next(3));
-
- reg_def VSR41 (SOC, SOC, Op_RegF, 41, VSR41->as_VMReg() );
- reg_def VSR41_H(SOC, SOC, Op_RegF, 41, VSR41->as_VMReg()->next() );
- reg_def VSR41_J(SOC, SOC, Op_RegF, 41, VSR41->as_VMReg()->next(2));
- reg_def VSR41_K(SOC, SOC, Op_RegF, 41, VSR41->as_VMReg()->next(3));
-
- reg_def VSR42 (SOC, SOC, Op_RegF, 42, VSR42->as_VMReg() );
- reg_def VSR42_H(SOC, SOC, Op_RegF, 42, VSR42->as_VMReg()->next() );
- reg_def VSR42_J(SOC, SOC, Op_RegF, 42, VSR42->as_VMReg()->next(2));
- reg_def VSR42_K(SOC, SOC, Op_RegF, 42, VSR42->as_VMReg()->next(3));
-
- reg_def VSR43 (SOC, SOC, Op_RegF, 43, VSR43->as_VMReg() );
- reg_def VSR43_H(SOC, SOC, Op_RegF, 43, VSR43->as_VMReg()->next() );
- reg_def VSR43_J(SOC, SOC, Op_RegF, 43, VSR43->as_VMReg()->next(2));
- reg_def VSR43_K(SOC, SOC, Op_RegF, 43, VSR43->as_VMReg()->next(3));
-
- reg_def VSR44 (SOC, SOC, Op_RegF, 44, VSR44->as_VMReg() );
- reg_def VSR44_H(SOC, SOC, Op_RegF, 44, VSR44->as_VMReg()->next() );
- reg_def VSR44_J(SOC, SOC, Op_RegF, 44, VSR44->as_VMReg()->next(2));
- reg_def VSR44_K(SOC, SOC, Op_RegF, 44, VSR44->as_VMReg()->next(3));
-
- reg_def VSR45 (SOC, SOC, Op_RegF, 45, VSR45->as_VMReg() );
- reg_def VSR45_H(SOC, SOC, Op_RegF, 45, VSR45->as_VMReg()->next() );
- reg_def VSR45_J(SOC, SOC, Op_RegF, 45, VSR45->as_VMReg()->next(2));
- reg_def VSR45_K(SOC, SOC, Op_RegF, 45, VSR45->as_VMReg()->next(3));
-
- reg_def VSR46 (SOC, SOC, Op_RegF, 46, VSR46->as_VMReg() );
- reg_def VSR46_H(SOC, SOC, Op_RegF, 46, VSR46->as_VMReg()->next() );
- reg_def VSR46_J(SOC, SOC, Op_RegF, 46, VSR46->as_VMReg()->next(2));
- reg_def VSR46_K(SOC, SOC, Op_RegF, 46, VSR46->as_VMReg()->next(3));
-
- reg_def VSR47 (SOC, SOC, Op_RegF, 47, VSR47->as_VMReg() );
- reg_def VSR47_H(SOC, SOC, Op_RegF, 47, VSR47->as_VMReg()->next() );
- reg_def VSR47_J(SOC, SOC, Op_RegF, 47, VSR47->as_VMReg()->next(2));
- reg_def VSR47_K(SOC, SOC, Op_RegF, 47, VSR47->as_VMReg()->next(3));
-
- reg_def VSR48 (SOC, SOC, Op_RegF, 48, VSR48->as_VMReg() );
- reg_def VSR48_H(SOC, SOC, Op_RegF, 48, VSR48->as_VMReg()->next() );
- reg_def VSR48_J(SOC, SOC, Op_RegF, 48, VSR48->as_VMReg()->next(2));
- reg_def VSR48_K(SOC, SOC, Op_RegF, 48, VSR48->as_VMReg()->next(3));
-
- reg_def VSR49 (SOC, SOC, Op_RegF, 49, VSR49->as_VMReg() );
- reg_def VSR49_H(SOC, SOC, Op_RegF, 49, VSR49->as_VMReg()->next() );
- reg_def VSR49_J(SOC, SOC, Op_RegF, 49, VSR49->as_VMReg()->next(2));
- reg_def VSR49_K(SOC, SOC, Op_RegF, 49, VSR49->as_VMReg()->next(3));
-
- reg_def VSR50 (SOC, SOC, Op_RegF, 50, VSR50->as_VMReg() );
- reg_def VSR50_H(SOC, SOC, Op_RegF, 50, VSR50->as_VMReg()->next() );
- reg_def VSR50_J(SOC, SOC, Op_RegF, 50, VSR50->as_VMReg()->next(2));
- reg_def VSR50_K(SOC, SOC, Op_RegF, 50, VSR50->as_VMReg()->next(3));
-
- reg_def VSR51 (SOC, SOC, Op_RegF, 51, VSR51->as_VMReg() );
- reg_def VSR51_H(SOC, SOC, Op_RegF, 51, VSR51->as_VMReg()->next() );
- reg_def VSR51_J(SOC, SOC, Op_RegF, 51, VSR51->as_VMReg()->next(2));
- reg_def VSR51_K(SOC, SOC, Op_RegF, 51, VSR51->as_VMReg()->next(3));
-
- reg_def VSR52 (SOC, SOE, Op_RegF, 52, VSR52->as_VMReg() );
- reg_def VSR52_H(SOC, SOE, Op_RegF, 52, VSR52->as_VMReg()->next() );
- reg_def VSR52_J(SOC, SOE, Op_RegF, 52, VSR52->as_VMReg()->next(2));
- reg_def VSR52_K(SOC, SOE, Op_RegF, 52, VSR52->as_VMReg()->next(3));
-
- reg_def VSR53 (SOC, SOE, Op_RegF, 53, VSR53->as_VMReg() );
- reg_def VSR53_H(SOC, SOE, Op_RegF, 53, VSR53->as_VMReg()->next() );
- reg_def VSR53_J(SOC, SOE, Op_RegF, 53, VSR53->as_VMReg()->next(2));
- reg_def VSR53_K(SOC, SOE, Op_RegF, 53, VSR53->as_VMReg()->next(3));
-
- reg_def VSR54 (SOC, SOE, Op_RegF, 54, VSR54->as_VMReg() );
- reg_def VSR54_H(SOC, SOE, Op_RegF, 54, VSR54->as_VMReg()->next() );
- reg_def VSR54_J(SOC, SOE, Op_RegF, 54, VSR54->as_VMReg()->next(2));
- reg_def VSR54_K(SOC, SOE, Op_RegF, 54, VSR54->as_VMReg()->next(3));
-
- reg_def VSR55 (SOC, SOE, Op_RegF, 55, VSR55->as_VMReg() );
- reg_def VSR55_H(SOC, SOE, Op_RegF, 55, VSR55->as_VMReg()->next() );
- reg_def VSR55_J(SOC, SOE, Op_RegF, 55, VSR55->as_VMReg()->next(2));
- reg_def VSR55_K(SOC, SOE, Op_RegF, 55, VSR55->as_VMReg()->next(3));
-
- reg_def VSR56 (SOC, SOE, Op_RegF, 56, VSR56->as_VMReg() );
- reg_def VSR56_H(SOC, SOE, Op_RegF, 56, VSR56->as_VMReg()->next() );
- reg_def VSR56_J(SOC, SOE, Op_RegF, 56, VSR56->as_VMReg()->next(2));
- reg_def VSR56_K(SOC, SOE, Op_RegF, 56, VSR56->as_VMReg()->next(3));
-
- reg_def VSR57 (SOC, SOE, Op_RegF, 57, VSR57->as_VMReg() );
- reg_def VSR57_H(SOC, SOE, Op_RegF, 57, VSR57->as_VMReg()->next() );
- reg_def VSR57_J(SOC, SOE, Op_RegF, 57, VSR57->as_VMReg()->next(2));
- reg_def VSR57_K(SOC, SOE, Op_RegF, 57, VSR57->as_VMReg()->next(3));
-
- reg_def VSR58 (SOC, SOE, Op_RegF, 58, VSR58->as_VMReg() );
- reg_def VSR58_H(SOC, SOE, Op_RegF, 58, VSR58->as_VMReg()->next() );
- reg_def VSR58_J(SOC, SOE, Op_RegF, 58, VSR58->as_VMReg()->next(2));
- reg_def VSR58_K(SOC, SOE, Op_RegF, 58, VSR58->as_VMReg()->next(3));
-
- reg_def VSR59 (SOC, SOE, Op_RegF, 59, VSR59->as_VMReg() );
- reg_def VSR59_H(SOC, SOE, Op_RegF, 59, VSR59->as_VMReg()->next() );
- reg_def VSR59_J(SOC, SOE, Op_RegF, 59, VSR59->as_VMReg()->next(2));
- reg_def VSR59_K(SOC, SOE, Op_RegF, 59, VSR59->as_VMReg()->next(3));
-
- reg_def VSR60 (SOC, SOE, Op_RegF, 60, VSR60->as_VMReg() );
- reg_def VSR60_H(SOC, SOE, Op_RegF, 60, VSR60->as_VMReg()->next() );
- reg_def VSR60_J(SOC, SOE, Op_RegF, 60, VSR60->as_VMReg()->next(2));
- reg_def VSR60_K(SOC, SOE, Op_RegF, 60, VSR60->as_VMReg()->next(3));
-
- reg_def VSR61 (SOC, SOE, Op_RegF, 61, VSR61->as_VMReg() );
- reg_def VSR61_H(SOC, SOE, Op_RegF, 61, VSR61->as_VMReg()->next() );
- reg_def VSR61_J(SOC, SOE, Op_RegF, 61, VSR61->as_VMReg()->next(2));
- reg_def VSR61_K(SOC, SOE, Op_RegF, 61, VSR61->as_VMReg()->next(3));
-
- reg_def VSR62 (SOC, SOE, Op_RegF, 62, VSR62->as_VMReg() );
- reg_def VSR62_H(SOC, SOE, Op_RegF, 62, VSR62->as_VMReg()->next() );
- reg_def VSR62_J(SOC, SOE, Op_RegF, 62, VSR62->as_VMReg()->next(2));
- reg_def VSR62_K(SOC, SOE, Op_RegF, 62, VSR62->as_VMReg()->next(3));
-
- reg_def VSR63 (SOC, SOE, Op_RegF, 63, VSR63->as_VMReg() );
- reg_def VSR63_H(SOC, SOE, Op_RegF, 63, VSR63->as_VMReg()->next() );
- reg_def VSR63_J(SOC, SOE, Op_RegF, 63, VSR63->as_VMReg()->next(2));
- reg_def VSR63_K(SOC, SOE, Op_RegF, 63, VSR63->as_VMReg()->next(3));
+
+ reg_def VR0 (SOC, SOC, Op_RegF, 0, VR0->as_VMReg() );
+ reg_def VR0_H(SOC, SOC, Op_RegF, 0, VR0->as_VMReg()->next() );
+ reg_def VR0_J(SOC, SOC, Op_RegF, 0, VR0->as_VMReg()->next(2));
+ reg_def VR0_K(SOC, SOC, Op_RegF, 0, VR0->as_VMReg()->next(3));
+
+ reg_def VR1 (SOC, SOC, Op_RegF, 1, VR1->as_VMReg() );
+ reg_def VR1_H(SOC, SOC, Op_RegF, 1, VR1->as_VMReg()->next() );
+ reg_def VR1_J(SOC, SOC, Op_RegF, 1, VR1->as_VMReg()->next(2));
+ reg_def VR1_K(SOC, SOC, Op_RegF, 1, VR1->as_VMReg()->next(3));
+
+ reg_def VR2 (SOC, SOC, Op_RegF, 2, VR2->as_VMReg() );
+ reg_def VR2_H(SOC, SOC, Op_RegF, 2, VR2->as_VMReg()->next() );
+ reg_def VR2_J(SOC, SOC, Op_RegF, 2, VR2->as_VMReg()->next(2));
+ reg_def VR2_K(SOC, SOC, Op_RegF, 2, VR2->as_VMReg()->next(3));
+
+ reg_def VR3 (SOC, SOC, Op_RegF, 3, VR3->as_VMReg() );
+ reg_def VR3_H(SOC, SOC, Op_RegF, 3, VR3->as_VMReg()->next() );
+ reg_def VR3_J(SOC, SOC, Op_RegF, 3, VR3->as_VMReg()->next(2));
+ reg_def VR3_K(SOC, SOC, Op_RegF, 3, VR3->as_VMReg()->next(3));
+
+ reg_def VR4 (SOC, SOC, Op_RegF, 4, VR4->as_VMReg() );
+ reg_def VR4_H(SOC, SOC, Op_RegF, 4, VR4->as_VMReg()->next() );
+ reg_def VR4_J(SOC, SOC, Op_RegF, 4, VR4->as_VMReg()->next(2));
+ reg_def VR4_K(SOC, SOC, Op_RegF, 4, VR4->as_VMReg()->next(3));
+
+ reg_def VR5 (SOC, SOC, Op_RegF, 5, VR5->as_VMReg() );
+ reg_def VR5_H(SOC, SOC, Op_RegF, 5, VR5->as_VMReg()->next() );
+ reg_def VR5_J(SOC, SOC, Op_RegF, 5, VR5->as_VMReg()->next(2));
+ reg_def VR5_K(SOC, SOC, Op_RegF, 5, VR5->as_VMReg()->next(3));
+
+ reg_def VR6 (SOC, SOC, Op_RegF, 6, VR6->as_VMReg() );
+ reg_def VR6_H(SOC, SOC, Op_RegF, 6, VR6->as_VMReg()->next() );
+ reg_def VR6_J(SOC, SOC, Op_RegF, 6, VR6->as_VMReg()->next(2));
+ reg_def VR6_K(SOC, SOC, Op_RegF, 6, VR6->as_VMReg()->next(3));
+
+ reg_def VR7 (SOC, SOC, Op_RegF, 7, VR7->as_VMReg() );
+ reg_def VR7_H(SOC, SOC, Op_RegF, 7, VR7->as_VMReg()->next() );
+ reg_def VR7_J(SOC, SOC, Op_RegF, 7, VR7->as_VMReg()->next(2));
+ reg_def VR7_K(SOC, SOC, Op_RegF, 7, VR7->as_VMReg()->next(3));
+
+ reg_def VR8 (SOC, SOC, Op_RegF, 8, VR8->as_VMReg() );
+ reg_def VR8_H(SOC, SOC, Op_RegF, 8, VR8->as_VMReg()->next() );
+ reg_def VR8_J(SOC, SOC, Op_RegF, 8, VR8->as_VMReg()->next(2));
+ reg_def VR8_K(SOC, SOC, Op_RegF, 8, VR8->as_VMReg()->next(3));
+
+ reg_def VR9 (SOC, SOC, Op_RegF, 9, VR9->as_VMReg() );
+ reg_def VR9_H(SOC, SOC, Op_RegF, 9, VR9->as_VMReg()->next() );
+ reg_def VR9_J(SOC, SOC, Op_RegF, 9, VR9->as_VMReg()->next(2));
+ reg_def VR9_K(SOC, SOC, Op_RegF, 9, VR9->as_VMReg()->next(3));
+
+ reg_def VR10 (SOC, SOC, Op_RegF, 10, VR10->as_VMReg() );
+ reg_def VR10_H(SOC, SOC, Op_RegF, 10, VR10->as_VMReg()->next() );
+ reg_def VR10_J(SOC, SOC, Op_RegF, 10, VR10->as_VMReg()->next(2));
+ reg_def VR10_K(SOC, SOC, Op_RegF, 10, VR10->as_VMReg()->next(3));
+
+ reg_def VR11 (SOC, SOC, Op_RegF, 11, VR11->as_VMReg() );
+ reg_def VR11_H(SOC, SOC, Op_RegF, 11, VR11->as_VMReg()->next() );
+ reg_def VR11_J(SOC, SOC, Op_RegF, 11, VR11->as_VMReg()->next(2));
+ reg_def VR11_K(SOC, SOC, Op_RegF, 11, VR11->as_VMReg()->next(3));
+
+ reg_def VR12 (SOC, SOC, Op_RegF, 12, VR12->as_VMReg() );
+ reg_def VR12_H(SOC, SOC, Op_RegF, 12, VR12->as_VMReg()->next() );
+ reg_def VR12_J(SOC, SOC, Op_RegF, 12, VR12->as_VMReg()->next(2));
+ reg_def VR12_K(SOC, SOC, Op_RegF, 12, VR12->as_VMReg()->next(3));
+
+ reg_def VR13 (SOC, SOC, Op_RegF, 13, VR13->as_VMReg() );
+ reg_def VR13_H(SOC, SOC, Op_RegF, 13, VR13->as_VMReg()->next() );
+ reg_def VR13_J(SOC, SOC, Op_RegF, 13, VR13->as_VMReg()->next(2));
+ reg_def VR13_K(SOC, SOC, Op_RegF, 13, VR13->as_VMReg()->next(3));
+
+ reg_def VR14 (SOC, SOC, Op_RegF, 14, VR14->as_VMReg() );
+ reg_def VR14_H(SOC, SOC, Op_RegF, 14, VR14->as_VMReg()->next() );
+ reg_def VR14_J(SOC, SOC, Op_RegF, 14, VR14->as_VMReg()->next(2));
+ reg_def VR14_K(SOC, SOC, Op_RegF, 14, VR14->as_VMReg()->next(3));
+
+ reg_def VR15 (SOC, SOC, Op_RegF, 15, VR15->as_VMReg() );
+ reg_def VR15_H(SOC, SOC, Op_RegF, 15, VR15->as_VMReg()->next() );
+ reg_def VR15_J(SOC, SOC, Op_RegF, 15, VR15->as_VMReg()->next(2));
+ reg_def VR15_K(SOC, SOC, Op_RegF, 15, VR15->as_VMReg()->next(3));
+
+ reg_def VR16 (SOC, SOC, Op_RegF, 16, VR16->as_VMReg() );
+ reg_def VR16_H(SOC, SOC, Op_RegF, 16, VR16->as_VMReg()->next() );
+ reg_def VR16_J(SOC, SOC, Op_RegF, 16, VR16->as_VMReg()->next(2));
+ reg_def VR16_K(SOC, SOC, Op_RegF, 16, VR16->as_VMReg()->next(3));
+
+ reg_def VR17 (SOC, SOC, Op_RegF, 17, VR17->as_VMReg() );
+ reg_def VR17_H(SOC, SOC, Op_RegF, 17, VR17->as_VMReg()->next() );
+ reg_def VR17_J(SOC, SOC, Op_RegF, 17, VR17->as_VMReg()->next(2));
+ reg_def VR17_K(SOC, SOC, Op_RegF, 17, VR17->as_VMReg()->next(3));
+
+ reg_def VR18 (SOC, SOC, Op_RegF, 18, VR18->as_VMReg() );
+ reg_def VR18_H(SOC, SOC, Op_RegF, 18, VR18->as_VMReg()->next() );
+ reg_def VR18_J(SOC, SOC, Op_RegF, 18, VR18->as_VMReg()->next(2));
+ reg_def VR18_K(SOC, SOC, Op_RegF, 18, VR18->as_VMReg()->next(3));
+
+ reg_def VR19 (SOC, SOC, Op_RegF, 19, VR19->as_VMReg() );
+ reg_def VR19_H(SOC, SOC, Op_RegF, 19, VR19->as_VMReg()->next() );
+ reg_def VR19_J(SOC, SOC, Op_RegF, 19, VR19->as_VMReg()->next(2));
+ reg_def VR19_K(SOC, SOC, Op_RegF, 19, VR19->as_VMReg()->next(3));
+
+ reg_def VR20 (SOC, SOE, Op_RegF, 20, VR20->as_VMReg() );
+ reg_def VR20_H(SOC, SOE, Op_RegF, 20, VR20->as_VMReg()->next() );
+ reg_def VR20_J(SOC, SOE, Op_RegF, 20, VR20->as_VMReg()->next(2));
+ reg_def VR20_K(SOC, SOE, Op_RegF, 20, VR20->as_VMReg()->next(3));
+
+ reg_def VR21 (SOC, SOE, Op_RegF, 21, VR21->as_VMReg() );
+ reg_def VR21_H(SOC, SOE, Op_RegF, 21, VR21->as_VMReg()->next() );
+ reg_def VR21_J(SOC, SOE, Op_RegF, 21, VR21->as_VMReg()->next(2));
+ reg_def VR21_K(SOC, SOE, Op_RegF, 21, VR21->as_VMReg()->next(3));
+
+ reg_def VR22 (SOC, SOE, Op_RegF, 22, VR22->as_VMReg() );
+ reg_def VR22_H(SOC, SOE, Op_RegF, 22, VR22->as_VMReg()->next() );
+ reg_def VR22_J(SOC, SOE, Op_RegF, 22, VR22->as_VMReg()->next(2));
+ reg_def VR22_K(SOC, SOE, Op_RegF, 22, VR22->as_VMReg()->next(3));
+
+ reg_def VR23 (SOC, SOE, Op_RegF, 23, VR23->as_VMReg() );
+ reg_def VR23_H(SOC, SOE, Op_RegF, 23, VR23->as_VMReg()->next() );
+ reg_def VR23_J(SOC, SOE, Op_RegF, 23, VR23->as_VMReg()->next(2));
+ reg_def VR23_K(SOC, SOE, Op_RegF, 23, VR23->as_VMReg()->next(3));
+
+ reg_def VR24 (SOC, SOE, Op_RegF, 24, VR24->as_VMReg() );
+ reg_def VR24_H(SOC, SOE, Op_RegF, 24, VR24->as_VMReg()->next() );
+ reg_def VR24_J(SOC, SOE, Op_RegF, 24, VR24->as_VMReg()->next(2));
+ reg_def VR24_K(SOC, SOE, Op_RegF, 24, VR24->as_VMReg()->next(3));
+
+ reg_def VR25 (SOC, SOE, Op_RegF, 25, VR25->as_VMReg() );
+ reg_def VR25_H(SOC, SOE, Op_RegF, 25, VR25->as_VMReg()->next() );
+ reg_def VR25_J(SOC, SOE, Op_RegF, 25, VR25->as_VMReg()->next(2));
+ reg_def VR25_K(SOC, SOE, Op_RegF, 25, VR25->as_VMReg()->next(3));
+
+ reg_def VR26 (SOC, SOE, Op_RegF, 26, VR26->as_VMReg() );
+ reg_def VR26_H(SOC, SOE, Op_RegF, 26, VR26->as_VMReg()->next() );
+ reg_def VR26_J(SOC, SOE, Op_RegF, 26, VR26->as_VMReg()->next(2));
+ reg_def VR26_K(SOC, SOE, Op_RegF, 26, VR26->as_VMReg()->next(3));
+
+ reg_def VR27 (SOC, SOE, Op_RegF, 27, VR27->as_VMReg() );
+ reg_def VR27_H(SOC, SOE, Op_RegF, 27, VR27->as_VMReg()->next() );
+ reg_def VR27_J(SOC, SOE, Op_RegF, 27, VR27->as_VMReg()->next(2));
+ reg_def VR27_K(SOC, SOE, Op_RegF, 27, VR27->as_VMReg()->next(3));
+
+ reg_def VR28 (SOC, SOE, Op_RegF, 28, VR28->as_VMReg() );
+ reg_def VR28_H(SOC, SOE, Op_RegF, 28, VR28->as_VMReg()->next() );
+ reg_def VR28_J(SOC, SOE, Op_RegF, 28, VR28->as_VMReg()->next(2));
+ reg_def VR28_K(SOC, SOE, Op_RegF, 28, VR28->as_VMReg()->next(3));
+
+ reg_def VR29 (SOC, SOE, Op_RegF, 29, VR29->as_VMReg() );
+ reg_def VR29_H(SOC, SOE, Op_RegF, 29, VR29->as_VMReg()->next() );
+ reg_def VR29_J(SOC, SOE, Op_RegF, 29, VR29->as_VMReg()->next(2));
+ reg_def VR29_K(SOC, SOE, Op_RegF, 29, VR29->as_VMReg()->next(3));
+
+ reg_def VR30 (SOC, SOE, Op_RegF, 30, VR30->as_VMReg() );
+ reg_def VR30_H(SOC, SOE, Op_RegF, 30, VR30->as_VMReg()->next() );
+ reg_def VR30_J(SOC, SOE, Op_RegF, 30, VR30->as_VMReg()->next(2));
+ reg_def VR30_K(SOC, SOE, Op_RegF, 30, VR30->as_VMReg()->next(3));
+
+ reg_def VR31 (SOC, SOE, Op_RegF, 31, VR31->as_VMReg() );
+ reg_def VR31_H(SOC, SOE, Op_RegF, 31, VR31->as_VMReg()->next() );
+ reg_def VR31_J(SOC, SOE, Op_RegF, 31, VR31->as_VMReg()->next(2));
+ reg_def VR31_K(SOC, SOE, Op_RegF, 31, VR31->as_VMReg()->next(3));
// ----------------------------
// Specify priority of register selection within phases of register
@@ -696,70 +535,38 @@ alloc_class chunk1 (
);
alloc_class chunk2 (
- VSR0 , VSR0_H , VSR0_J , VSR0_K ,
- VSR1 , VSR1_H , VSR1_J , VSR1_K ,
- VSR2 , VSR2_H , VSR2_J , VSR2_K ,
- VSR3 , VSR3_H , VSR3_J , VSR3_K ,
- VSR4 , VSR4_H , VSR4_J , VSR4_K ,
- VSR5 , VSR5_H , VSR5_J , VSR5_K ,
- VSR6 , VSR6_H , VSR6_J , VSR6_K ,
- VSR7 , VSR7_H , VSR7_J , VSR7_K ,
- VSR8 , VSR8_H , VSR8_J , VSR8_K ,
- VSR9 , VSR9_H , VSR9_J , VSR9_K ,
- VSR10, VSR10_H, VSR10_J, VSR10_K,
- VSR11, VSR11_H, VSR11_J, VSR11_K,
- VSR12, VSR12_H, VSR12_J, VSR12_K,
- VSR13, VSR13_H, VSR13_J, VSR13_K,
- VSR14, VSR14_H, VSR14_J, VSR14_K,
- VSR15, VSR15_H, VSR15_J, VSR15_K,
- VSR16, VSR16_H, VSR16_J, VSR16_K,
- VSR17, VSR17_H, VSR17_J, VSR17_K,
- VSR18, VSR18_H, VSR18_J, VSR18_K,
- VSR19, VSR19_H, VSR19_J, VSR19_K,
- VSR20, VSR20_H, VSR20_J, VSR20_K,
- VSR21, VSR21_H, VSR21_J, VSR21_K,
- VSR22, VSR22_H, VSR22_J, VSR22_K,
- VSR23, VSR23_H, VSR23_J, VSR23_K,
- VSR24, VSR24_H, VSR24_J, VSR24_K,
- VSR25, VSR25_H, VSR25_J, VSR25_K,
- VSR26, VSR26_H, VSR26_J, VSR26_K,
- VSR27, VSR27_H, VSR27_J, VSR27_K,
- VSR28, VSR28_H, VSR28_J, VSR28_K,
- VSR29, VSR29_H, VSR29_J, VSR29_K,
- VSR30, VSR30_H, VSR30_J, VSR30_K,
- VSR31, VSR31_H, VSR31_J, VSR31_K,
- VSR32, VSR32_H, VSR32_J, VSR32_K,
- VSR33, VSR33_H, VSR33_J, VSR33_K,
- VSR34, VSR34_H, VSR34_J, VSR34_K,
- VSR35, VSR35_H, VSR35_J, VSR35_K,
- VSR36, VSR36_H, VSR36_J, VSR36_K,
- VSR37, VSR37_H, VSR37_J, VSR37_K,
- VSR38, VSR38_H, VSR38_J, VSR38_K,
- VSR39, VSR39_H, VSR39_J, VSR39_K,
- VSR40, VSR40_H, VSR40_J, VSR40_K,
- VSR41, VSR41_H, VSR41_J, VSR41_K,
- VSR42, VSR42_H, VSR42_J, VSR42_K,
- VSR43, VSR43_H, VSR43_J, VSR43_K,
- VSR44, VSR44_H, VSR44_J, VSR44_K,
- VSR45, VSR45_H, VSR45_J, VSR45_K,
- VSR46, VSR46_H, VSR46_J, VSR46_K,
- VSR47, VSR47_H, VSR47_J, VSR47_K,
- VSR48, VSR48_H, VSR48_J, VSR48_K,
- VSR49, VSR49_H, VSR49_J, VSR49_K,
- VSR50, VSR50_H, VSR50_J, VSR50_K,
- VSR51, VSR51_H, VSR51_J, VSR51_K,
- VSR52, VSR52_H, VSR52_J, VSR52_K,
- VSR53, VSR53_H, VSR53_J, VSR53_K,
- VSR54, VSR54_H, VSR54_J, VSR54_K,
- VSR55, VSR55_H, VSR55_J, VSR55_K,
- VSR56, VSR56_H, VSR56_J, VSR56_K,
- VSR57, VSR57_H, VSR57_J, VSR57_K,
- VSR58, VSR58_H, VSR58_J, VSR58_K,
- VSR59, VSR59_H, VSR59_J, VSR59_K,
- VSR60, VSR60_H, VSR60_J, VSR60_K,
- VSR61, VSR61_H, VSR61_J, VSR61_K,
- VSR62, VSR62_H, VSR62_J, VSR62_K,
- VSR63, VSR63_H, VSR63_J, VSR63_K
+ VR0 , VR0_H , VR0_J , VR0_K ,
+ VR1 , VR1_H , VR1_J , VR1_K ,
+ VR2 , VR2_H , VR2_J , VR2_K ,
+ VR3 , VR3_H , VR3_J , VR3_K ,
+ VR4 , VR4_H , VR4_J , VR4_K ,
+ VR5 , VR5_H , VR5_J , VR5_K ,
+ VR6 , VR6_H , VR6_J , VR6_K ,
+ VR7 , VR7_H , VR7_J , VR7_K ,
+ VR8 , VR8_H , VR8_J , VR8_K ,
+ VR9 , VR9_H , VR9_J , VR9_K ,
+ VR10, VR10_H, VR10_J, VR10_K,
+ VR11, VR11_H, VR11_J, VR11_K,
+ VR12, VR12_H, VR12_J, VR12_K,
+ VR13, VR13_H, VR13_J, VR13_K,
+ VR14, VR14_H, VR14_J, VR14_K,
+ VR15, VR15_H, VR15_J, VR15_K,
+ VR16, VR16_H, VR16_J, VR16_K,
+ VR17, VR17_H, VR17_J, VR17_K,
+ VR18, VR18_H, VR18_J, VR18_K,
+ VR19, VR19_H, VR19_J, VR19_K,
+ VR20, VR20_H, VR20_J, VR20_K,
+ VR21, VR21_H, VR21_J, VR21_K,
+ VR22, VR22_H, VR22_J, VR22_K,
+ VR23, VR23_H, VR23_J, VR23_K,
+ VR24, VR24_H, VR24_J, VR24_K,
+ VR25, VR25_H, VR25_J, VR25_K,
+ VR26, VR26_H, VR26_J, VR26_K,
+ VR27, VR27_H, VR27_J, VR27_K,
+ VR28, VR28_H, VR28_J, VR28_K,
+ VR29, VR29_H, VR29_J, VR29_K,
+ VR30, VR30_H, VR30_J, VR30_K,
+ VR31, VR31_H, VR31_J, VR31_K
);
alloc_class chunk3 (
@@ -1163,39 +970,39 @@ reg_class dbl_reg(
// Vector-Scalar Register Class
// ----------------------------
-reg_class vs_reg(
- VSR32, VSR32_H, VSR32_J, VSR32_K,
- VSR33, VSR33_H, VSR33_J, VSR33_K,
- VSR34, VSR34_H, VSR34_J, VSR34_K,
- VSR35, VSR35_H, VSR35_J, VSR35_K,
- VSR36, VSR36_H, VSR36_J, VSR36_K,
- VSR37, VSR37_H, VSR37_J, VSR37_K,
- VSR38, VSR38_H, VSR38_J, VSR38_K,
- VSR39, VSR39_H, VSR39_J, VSR39_K,
- VSR40, VSR40_H, VSR40_J, VSR40_K,
- VSR41, VSR41_H, VSR41_J, VSR41_K,
- VSR42, VSR42_H, VSR42_J, VSR42_K,
- VSR43, VSR43_H, VSR43_J, VSR43_K,
- VSR44, VSR44_H, VSR44_J, VSR44_K,
- VSR45, VSR45_H, VSR45_J, VSR45_K,
- VSR46, VSR46_H, VSR46_J, VSR46_K,
- VSR47, VSR47_H, VSR47_J, VSR47_K,
- VSR48, VSR48_H, VSR48_J, VSR48_K,
- VSR49, VSR49_H, VSR49_J, VSR49_K,
- VSR50, VSR50_H, VSR50_J, VSR50_K,
- VSR51, VSR51_H, VSR51_J, VSR51_K,
- VSR52, VSR52_H, VSR52_J, VSR52_K, // non-volatile
- VSR53, VSR53_H, VSR53_J, VSR53_K, // non-volatile
- VSR54, VSR54_H, VSR54_J, VSR54_K, // non-volatile
- VSR55, VSR55_H, VSR55_J, VSR55_K, // non-volatile
- VSR56, VSR56_H, VSR56_J, VSR56_K, // non-volatile
- VSR57, VSR57_H, VSR57_J, VSR57_K, // non-volatile
- VSR58, VSR58_H, VSR58_J, VSR58_K, // non-volatile
- VSR59, VSR59_H, VSR59_J, VSR59_K, // non-volatile
- VSR60, VSR60_H, VSR60_J, VSR60_K, // non-volatile
- VSR61, VSR61_H, VSR61_J, VSR61_K, // non-volatile
- VSR62, VSR62_H, VSR62_J, VSR62_K, // non-volatile
- VSR63, VSR63_H, VSR63_J, VSR63_K // non-volatile
+reg_class v_reg(
+ VR0 , VR0_H , VR0_J , VR0_K ,
+ VR1 , VR1_H , VR1_J , VR1_K ,
+ VR2 , VR2_H , VR2_J , VR2_K ,
+ VR3 , VR3_H , VR3_J , VR3_K ,
+ VR4 , VR4_H , VR4_J , VR4_K ,
+ VR5 , VR5_H , VR5_J , VR5_K ,
+ VR6 , VR6_H , VR6_J , VR6_K ,
+ VR7 , VR7_H , VR7_J , VR7_K ,
+ VR8 , VR8_H , VR8_J , VR8_K ,
+ VR9 , VR9_H , VR9_J , VR9_K ,
+ VR10, VR10_H, VR10_J, VR10_K,
+ VR11, VR11_H, VR11_J, VR11_K,
+ VR12, VR12_H, VR12_J, VR12_K,
+ VR13, VR13_H, VR13_J, VR13_K,
+ VR14, VR14_H, VR14_J, VR14_K,
+ VR15, VR15_H, VR15_J, VR15_K,
+ VR16, VR16_H, VR16_J, VR16_K,
+ VR17, VR17_H, VR17_J, VR17_K,
+ VR18, VR18_H, VR18_J, VR18_K,
+ VR19, VR19_H, VR19_J, VR19_K,
+ VR20, VR20_H, VR20_J, VR20_K,
+ VR21, VR21_H, VR21_J, VR21_K,
+ VR22, VR22_H, VR22_J, VR22_K,
+ VR23, VR23_H, VR23_J, VR23_K,
+ VR24, VR24_H, VR24_J, VR24_K,
+ VR25, VR25_H, VR25_J, VR25_K,
+ VR26, VR26_H, VR26_J, VR26_K,
+ VR27, VR27_H, VR27_J, VR27_K,
+ VR28, VR28_H, VR28_J, VR28_K,
+ VR29, VR29_H, VR29_J, VR29_K,
+ VR30, VR30_H, VR30_J, VR30_K,
+ VR31, VR31_H, VR31_J, VR31_K
);
%}
@@ -1686,12 +1493,7 @@ void MachPrologNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const {
const Register toc_temp = R23;
assert_different_registers(R11, return_pc, callers_sp, push_frame_temp, toc_temp);
- if (method_is_frameless) {
- // Add nop at beginning of all frameless methods to prevent any
- // oop instructions from getting overwritten by make_not_entrant
- // (patching attempt would fail).
- __ nop();
- } else {
+ if (!method_is_frameless) {
// Get return pc.
__ mflr(return_pc);
}
@@ -1908,9 +1710,9 @@ const Pipeline * MachEpilogNode::pipeline() const {
// =============================================================================
-// Figure out which register class each belongs in: rc_int, rc_float, rc_vs or
+// Figure out which register class each belongs in: rc_int, rc_float, rc_vec or
// rc_stack.
-enum RC { rc_bad, rc_int, rc_float, rc_vs, rc_stack };
+enum RC { rc_bad, rc_int, rc_float, rc_vec, rc_stack };
static enum RC rc_class(OptoReg::Name reg) {
// Return the register class for the given register. The given register
@@ -1924,12 +1726,12 @@ static enum RC rc_class(OptoReg::Name reg) {
if (reg < ConcreteRegisterImpl::max_gpr) return rc_int;
// We have 64 floating-point register halves, starting at index 64.
- STATIC_ASSERT((int)ConcreteRegisterImpl::max_fpr == (int)MachRegisterNumbers::VSR0_num);
+ STATIC_ASSERT((int)ConcreteRegisterImpl::max_fpr == (int)MachRegisterNumbers::VR0_num);
if (reg < ConcreteRegisterImpl::max_fpr) return rc_float;
// We have 64 vector-scalar registers, starting at index 128.
- STATIC_ASSERT((int)ConcreteRegisterImpl::max_vsr == (int)MachRegisterNumbers::CR0_num);
- if (reg < ConcreteRegisterImpl::max_vsr) return rc_vs;
+ STATIC_ASSERT((int)ConcreteRegisterImpl::max_vr == (int)MachRegisterNumbers::CR0_num);
+ if (reg < ConcreteRegisterImpl::max_vr) return rc_vec;
// Condition and special purpose registers are not allocated. We only accept stack from here.
assert(OptoReg::is_stack(reg), "what else is it?");
@@ -2005,9 +1807,9 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
}
size += 16;
}
- // VectorSRegister->Memory Spill.
- else if (src_lo_rc == rc_vs && dst_lo_rc == rc_stack) {
- VectorSRegister Rsrc = as_VectorSRegister(Matcher::_regEncode[src_lo]);
+ // VectorRegister->Memory Spill.
+ else if (src_lo_rc == rc_vec && dst_lo_rc == rc_stack) {
+ VectorSRegister Rsrc = as_VectorRegister(Matcher::_regEncode[src_lo]).to_vsr();
int dst_offset = ra_->reg2offset(dst_lo);
if (PowerArchitecturePPC64 >= 9) {
if (is_aligned(dst_offset, 16)) {
@@ -2032,9 +1834,9 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
size += 8;
}
}
- // Memory->VectorSRegister Spill.
- else if (src_lo_rc == rc_stack && dst_lo_rc == rc_vs) {
- VectorSRegister Rdst = as_VectorSRegister(Matcher::_regEncode[dst_lo]);
+ // Memory->VectorRegister Spill.
+ else if (src_lo_rc == rc_stack && dst_lo_rc == rc_vec) {
+ VectorSRegister Rdst = as_VectorRegister(Matcher::_regEncode[dst_lo]).to_vsr();
int src_offset = ra_->reg2offset(src_lo);
if (PowerArchitecturePPC64 >= 9) {
if (is_aligned(src_offset, 16)) {
@@ -2057,17 +1859,17 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
size += 8;
}
}
- // VectorSRegister->VectorSRegister.
- else if (src_lo_rc == rc_vs && dst_lo_rc == rc_vs) {
- VectorSRegister Rsrc = as_VectorSRegister(Matcher::_regEncode[src_lo]);
- VectorSRegister Rdst = as_VectorSRegister(Matcher::_regEncode[dst_lo]);
+ // VectorRegister->VectorRegister.
+ else if (src_lo_rc == rc_vec && dst_lo_rc == rc_vec) {
+ VectorSRegister Rsrc = as_VectorRegister(Matcher::_regEncode[src_lo]).to_vsr();
+ VectorSRegister Rdst = as_VectorRegister(Matcher::_regEncode[dst_lo]).to_vsr();
if (masm) {
__ xxlor(Rdst, Rsrc, Rsrc);
}
size += 4;
}
else {
- ShouldNotReachHere(); // No VSR spill.
+ ShouldNotReachHere(); // No VR spill.
}
return size;
}
@@ -4048,7 +3850,7 @@ ins_attrib ins_is_late_expanded_null_check_candidate(false);
// Formats are generated automatically for constants and base registers.
operand vecX() %{
- constraint(ALLOC_IN_RC(vs_reg));
+ constraint(ALLOC_IN_RC(v_reg));
match(VecX);
format %{ %}
@@ -5624,7 +5426,7 @@ instruct loadV16_Power8(vecX dst, indirect mem) %{
format %{ "LXVD2X $dst, $mem \t// load 16-byte Vector" %}
size(4);
ins_encode %{
- __ lxvd2x($dst$$VectorSRegister, $mem$$Register);
+ __ lxvd2x($dst$$VectorRegister.to_vsr(), $mem$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -5637,7 +5439,7 @@ instruct loadV16_Power9(vecX dst, memoryAlg16 mem) %{
format %{ "LXV $dst, $mem \t// load 16-byte Vector" %}
size(4);
ins_encode %{
- __ lxv($dst$$VectorSRegister, $mem$$disp, $mem$$Register);
+ __ lxv($dst$$VectorRegister.to_vsr(), $mem$$disp, $mem$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -6664,7 +6466,7 @@ instruct storeV16_Power8(indirect mem, vecX src) %{
format %{ "STXVD2X $mem, $src \t// store 16-byte Vector" %}
size(4);
ins_encode %{
- __ stxvd2x($src$$VectorSRegister, $mem$$Register);
+ __ stxvd2x($src$$VectorRegister.to_vsr(), $mem$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -6677,7 +6479,7 @@ instruct storeV16_Power9(memoryAlg16 mem, vecX src) %{
format %{ "STXV $mem, $src \t// store 16-byte Vector" %}
size(4);
ins_encode %{
- __ stxv($src$$VectorSRegister, $mem$$disp, $mem$$Register);
+ __ stxv($src$$VectorRegister.to_vsr(), $mem$$disp, $mem$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -6687,6 +6489,7 @@ instruct reinterpretL(iRegLdst dst) %{
match(Set dst (VectorReinterpret dst));
ins_cost(0);
format %{ "reinterpret $dst" %}
+ size(0);
ins_encode( /*empty*/ );
ins_pipe(pipe_class_empty);
%}
@@ -6695,6 +6498,7 @@ instruct reinterpretX(vecX dst) %{
match(Set dst (VectorReinterpret dst));
ins_cost(0);
format %{ "reinterpret $dst" %}
+ size(0);
ins_encode( /*empty*/ );
ins_pipe(pipe_class_empty);
%}
@@ -6814,7 +6618,6 @@ instruct cond_set_0_oop(iRegNdst dst, flagsRegSrc crx, iRegPsrc src1) %{
format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// encode: preserve 0" %}
size(4);
ins_encode %{
- // This is a Power7 instruction for which no machine description exists.
__ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register);
%}
ins_pipe(pipe_class_default);
@@ -6946,7 +6749,6 @@ instruct cond_set_0_ptr(iRegPdst dst, flagsRegSrc crx, iRegPsrc src1) %{
format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// decode: preserve 0" %}
size(4);
ins_encode %{
- // This is a Power7 instruction for which no machine description exists.
__ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register);
%}
ins_pipe(pipe_class_default);
@@ -7423,8 +7225,6 @@ instruct cmovI_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegIdst dst, iRegIsrc src)
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
size(4);
ins_encode %{
- // This is a Power7 instruction for which no machine description
- // exists. Anyways, the scheduler should be off on Power7.
int cc = $cmp$$cmpcode;
__ isel($dst$$Register, $crx$$CondRegister,
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
@@ -7440,8 +7240,6 @@ instruct cmovL_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegLdst dst, iRegLsrc src)
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
size(4);
ins_encode %{
- // This is a Power7 instruction for which no machine description
- // exists. Anyways, the scheduler should be off on Power7.
int cc = $cmp$$cmpcode;
__ isel($dst$$Register, $crx$$CondRegister,
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
@@ -7457,8 +7255,6 @@ instruct cmovN_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegNdst dst, iRegNsrc src)
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
size(4);
ins_encode %{
- // This is a Power7 instruction for which no machine description
- // exists. Anyways, the scheduler should be off on Power7.
int cc = $cmp$$cmpcode;
__ isel($dst$$Register, $crx$$CondRegister,
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
@@ -7474,8 +7270,6 @@ instruct cmovP_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegPdst dst, iRegPsrc src)
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
size(4);
ins_encode %{
- // This is a Power7 instruction for which no machine description
- // exists. Anyways, the scheduler should be off on Power7.
int cc = $cmp$$cmpcode;
__ isel($dst$$Register, $crx$$CondRegister,
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
@@ -9921,13 +9715,6 @@ instruct andcL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{
// of java.lang.Float etc., e.g.
// int floatToIntBits(float value)
// float intBitsToFloat(int bits)
-//
-// Notes on the implementation on ppc64:
-// For Power7 and earlier, the rules are limited to those which move between a
-// register and a stack-location, because we always have to go through memory
-// when moving between a float register and an integer register.
-// This restriction is removed in Power8 with the introduction of the mtfprd
-// and mffprd instructions.
instruct moveL2D_reg(regD dst, iRegLsrc src) %{
match(Set dst (MoveL2D src));
@@ -12434,6 +12221,7 @@ instruct minI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
effect(KILL cr0);
ins_cost(DEFAULT_COST*2);
+ size(8);
ins_encode %{
__ cmpw(CR0, $src1$$Register, $src2$$Register);
__ isel($dst$$Register, CR0, Assembler::less, /*invert*/false, $src1$$Register, $src2$$Register);
@@ -12447,6 +12235,7 @@ instruct maxI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
effect(KILL cr0);
ins_cost(DEFAULT_COST*2);
+ size(8);
ins_encode %{
__ cmpw(CR0, $src1$$Register, $src2$$Register);
__ isel($dst$$Register, CR0, Assembler::greater, /*invert*/false, $src1$$Register, $src2$$Register);
@@ -12456,7 +12245,6 @@ instruct maxI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
//---------- Population Count Instructions ------------------------------------
-// Popcnt for Power7.
instruct popCountI(iRegIdst dst, iRegIsrc src) %{
match(Set dst (PopCountI src));
predicate(UsePopCountInstruction);
@@ -12470,7 +12258,6 @@ instruct popCountI(iRegIdst dst, iRegIsrc src) %{
ins_pipe(pipe_class_default);
%}
-// Popcnt for Power7.
instruct popCountL(iRegIdst dst, iRegLsrc src) %{
predicate(UsePopCountInstruction);
match(Set dst (PopCountL src));
@@ -12649,9 +12436,9 @@ instruct bytes_reverse_int_vec(iRegIdst dst, iRegIsrc src, vecX tmpV) %{
"\tMFVSRWZ $dst, $tmpV" %}
ins_encode %{
- __ mtvsrwz($tmpV$$VectorSRegister, $src$$Register);
- __ xxbrw($tmpV$$VectorSRegister, $tmpV$$VectorSRegister);
- __ mfvsrwz($dst$$Register, $tmpV$$VectorSRegister);
+ __ mtvsrwz($tmpV$$VectorRegister.to_vsr(), $src$$Register);
+ __ xxbrw($tmpV$$VectorRegister.to_vsr(), $tmpV$$VectorRegister->to_vsr());
+ __ mfvsrwz($dst$$Register, $tmpV$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -12721,9 +12508,9 @@ instruct bytes_reverse_long_vec(iRegLdst dst, iRegLsrc src, vecX tmpV) %{
"\tMFVSRD $dst, $tmpV" %}
ins_encode %{
- __ mtvsrd($tmpV$$VectorSRegister, $src$$Register);
- __ xxbrd($tmpV$$VectorSRegister, $tmpV$$VectorSRegister);
- __ mfvsrd($dst$$Register, $tmpV$$VectorSRegister);
+ __ mtvsrd($tmpV$$VectorRegister->to_vsr(), $src$$Register);
+ __ xxbrd($tmpV$$VectorRegister->to_vsr(), $tmpV$$VectorRegister->to_vsr());
+ __ mfvsrd($dst$$Register, $tmpV$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -12962,7 +12749,7 @@ instruct mtvsrwz(vecX temp1, iRegIsrc src) %{
format %{ "MTVSRWZ $temp1, $src \t// Move to 16-byte register" %}
size(4);
ins_encode %{
- __ mtvsrwz($temp1$$VectorSRegister, $src$$Register);
+ __ mtvsrwz($temp1$$VectorRegister->to_vsr(), $src$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -12973,7 +12760,7 @@ instruct xxspltw(vecX dst, vecX src, immI8 imm1) %{
format %{ "XXSPLTW $dst, $src, $imm1 \t// Splat word" %}
size(4);
ins_encode %{
- __ xxspltw($dst$$VectorSRegister, $src$$VectorSRegister, $imm1$$constant);
+ __ xxspltw($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr(), $imm1$$constant);
%}
ins_pipe(pipe_class_default);
%}
@@ -12984,7 +12771,7 @@ instruct xscvdpspn_regF(vecX dst, regF src) %{
format %{ "XSCVDPSPN $dst, $src \t// Convert scalar single precision to vector single precision" %}
size(4);
ins_encode %{
- __ xscvdpspn($dst$$VectorSRegister, $src$$FloatRegister->to_vsr());
+ __ xscvdpspn($dst$$VectorRegister->to_vsr(), $src$$FloatRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13091,7 +12878,7 @@ instruct repl16B_immI0(vecX dst, immI_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate16B" %}
size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13104,7 +12891,7 @@ instruct repl16B_immIminus1(vecX dst, immI_minus1 src) %{
format %{ "XXLEQV $dst, $src \t// replicate16B" %}
size(4);
ins_encode %{
- __ xxleqv($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxleqv($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13169,7 +12956,7 @@ instruct repl8S_immI0(vecX dst, immI_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate8S" %}
size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13182,7 +12969,7 @@ instruct repl8S_immIminus1(vecX dst, immI_minus1 src) %{
format %{ "XXLEQV $dst, $src \t// replicate8S" %}
size(4);
ins_encode %{
- __ xxleqv($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxleqv($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13247,7 +13034,7 @@ instruct repl4I_immI0(vecX dst, immI_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate4I" %}
size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13260,7 +13047,7 @@ instruct repl4I_immIminus1(vecX dst, immI_minus1 src) %{
format %{ "XXLEQV $dst, $dst, $dst \t// replicate4I" %}
size(4);
ins_encode %{
- __ xxleqv($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxleqv($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13299,6 +13086,7 @@ instruct repl2F_immF0(iRegLdst dst, immF_0 zero) %{
Matcher::vector_element_basic_type(n) == T_FLOAT);
format %{ "LI $dst, #0 \t// replicate2F" %}
+ size(4);
ins_encode %{
__ li($dst$$Register, 0x0);
%}
@@ -13316,7 +13104,7 @@ instruct vadd16B_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VADDUBM $dst,$src1,$src2\t// add packed16B" %}
size(4);
ins_encode %{
- __ vaddubm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vaddubm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13327,7 +13115,7 @@ instruct vadd8S_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VADDUHM $dst,$src1,$src2\t// add packed8S" %}
size(4);
ins_encode %{
- __ vadduhm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vadduhm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13338,7 +13126,7 @@ instruct vadd4I_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VADDUWM $dst,$src1,$src2\t// add packed4I" %}
size(4);
ins_encode %{
- __ vadduwm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vadduwm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13349,7 +13137,7 @@ instruct vadd4F_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VADDFP $dst,$src1,$src2\t// add packed4F" %}
size(4);
ins_encode %{
- __ vaddfp($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vaddfp($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13360,7 +13148,7 @@ instruct vadd2L_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VADDUDM $dst,$src1,$src2\t// add packed2L" %}
size(4);
ins_encode %{
- __ vaddudm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vaddudm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13371,7 +13159,7 @@ instruct vadd2D_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVADDDP $dst,$src1,$src2\t// add packed2D" %}
size(4);
ins_encode %{
- __ xvadddp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvadddp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13384,7 +13172,7 @@ instruct vsub16B_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VSUBUBM $dst,$src1,$src2\t// sub packed16B" %}
size(4);
ins_encode %{
- __ vsububm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vsububm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13395,7 +13183,7 @@ instruct vsub8S_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VSUBUHM $dst,$src1,$src2\t// sub packed8S" %}
size(4);
ins_encode %{
- __ vsubuhm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vsubuhm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13406,7 +13194,7 @@ instruct vsub4I_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VSUBUWM $dst,$src1,$src2\t// sub packed4I" %}
size(4);
ins_encode %{
- __ vsubuwm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vsubuwm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13417,7 +13205,7 @@ instruct vsub4F_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VSUBFP $dst,$src1,$src2\t// sub packed4F" %}
size(4);
ins_encode %{
- __ vsubfp($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vsubfp($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13428,7 +13216,7 @@ instruct vsub2L_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VSUBUDM $dst,$src1,$src2\t// sub packed2L" %}
size(4);
ins_encode %{
- __ vsubudm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vsubudm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13439,7 +13227,7 @@ instruct vsub2D_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVSUBDP $dst,$src1,$src2\t// sub packed2D" %}
size(4);
ins_encode %{
- __ xvsubdp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvsubdp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13454,8 +13242,8 @@ instruct vmul8S_reg(vecX dst, vecX src1, vecX src2, vecX tmp) %{
format %{ "VMLADDUHM $dst,$src1,$src2\t// mul packed8S" %}
size(8);
ins_encode %{
- __ vspltish($tmp$$VectorSRegister->to_vr(), 0);
- __ vmladduhm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr(), $tmp$$VectorSRegister->to_vr());
+ __ vspltish($tmp$$VectorRegister, 0);
+ __ vmladduhm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister, $tmp$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13466,7 +13254,7 @@ instruct vmul4I_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VMULUWM $dst,$src1,$src2\t// mul packed4I" %}
size(4);
ins_encode %{
- __ vmuluwm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vmuluwm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13477,7 +13265,7 @@ instruct vmul4F_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVMULSP $dst,$src1,$src2\t// mul packed4F" %}
size(4);
ins_encode %{
- __ xvmulsp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmulsp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13488,7 +13276,7 @@ instruct vmul2D_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVMULDP $dst,$src1,$src2\t// mul packed2D" %}
size(4);
ins_encode %{
- __ xvmuldp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmuldp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13501,7 +13289,7 @@ instruct vdiv4F_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVDIVSP $dst,$src1,$src2\t// div packed4F" %}
size(4);
ins_encode %{
- __ xvdivsp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvdivsp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13512,7 +13300,7 @@ instruct vdiv2D_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVDIVDP $dst,$src1,$src2\t// div packed2D" %}
size(4);
ins_encode %{
- __ xvdivdp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvdivdp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13527,10 +13315,10 @@ instruct vmin_reg(vecX dst, vecX src1, vecX src2) %{
BasicType bt = Matcher::vector_element_basic_type(this);
switch (bt) {
case T_INT:
- __ vminsw($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vminsw($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
break;
case T_LONG:
- __ vminsd($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vminsd($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
break;
default:
ShouldNotReachHere();
@@ -13547,10 +13335,10 @@ instruct vmax_reg(vecX dst, vecX src1, vecX src2) %{
BasicType bt = Matcher::vector_element_basic_type(this);
switch (bt) {
case T_INT:
- __ vmaxsw($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vmaxsw($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
break;
case T_LONG:
- __ vmaxsd($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vmaxsd($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
break;
default:
ShouldNotReachHere();
@@ -13564,7 +13352,7 @@ instruct vand(vecX dst, vecX src1, vecX src2) %{
size(4);
format %{ "VAND $dst,$src1,$src2\t// and vectors" %}
ins_encode %{
- __ vand($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vand($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13574,7 +13362,7 @@ instruct vor(vecX dst, vecX src1, vecX src2) %{
size(4);
format %{ "VOR $dst,$src1,$src2\t// or vectors" %}
ins_encode %{
- __ vor($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vor($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13584,7 +13372,7 @@ instruct vxor(vecX dst, vecX src1, vecX src2) %{
size(4);
format %{ "VXOR $dst,$src1,$src2\t// xor vectors" %}
ins_encode %{
- __ vxor($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vxor($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13602,8 +13390,8 @@ instruct reductionI_arith_logic(iRegIdst dst, iRegIsrc srcInt, vecX srcVec, vecX
size(24);
ins_encode %{
int opcode = this->ideal_Opcode();
- __ reduceI(opcode, $dst$$Register, $srcInt$$Register, $srcVec$$VectorSRegister->to_vr(),
- $tmp1$$VectorSRegister->to_vr(), $tmp2$$VectorSRegister->to_vr());
+ __ reduceI(opcode, $dst$$Register, $srcInt$$Register, $srcVec$$VectorRegister,
+ $tmp1$$VectorRegister, $tmp2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13618,8 +13406,8 @@ instruct reductionI_min_max(iRegIdst dst, iRegIsrc srcInt, vecX srcVec, vecX tmp
size(28);
ins_encode %{
int opcode = this->ideal_Opcode();
- __ reduceI(opcode, $dst$$Register, $srcInt$$Register, $srcVec$$VectorSRegister->to_vr(),
- $tmp1$$VectorSRegister->to_vr(), $tmp2$$VectorSRegister->to_vr());
+ __ reduceI(opcode, $dst$$Register, $srcInt$$Register, $srcVec$$VectorRegister,
+ $tmp1$$VectorRegister, $tmp2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13632,7 +13420,7 @@ instruct vabs4F_reg(vecX dst, vecX src) %{
format %{ "XVABSSP $dst,$src\t// absolute packed4F" %}
size(4);
ins_encode %{
- __ xvabssp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvabssp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13643,7 +13431,7 @@ instruct vabs2D_reg(vecX dst, vecX src) %{
format %{ "XVABSDP $dst,$src\t// absolute packed2D" %}
size(4);
ins_encode %{
- __ xvabsdp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvabsdp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13680,13 +13468,13 @@ instruct vround2D_reg(vecX dst, vecX src, immI8 rmode) %{
ins_encode %{
switch ($rmode$$constant) {
case RoundDoubleModeNode::rmode_rint:
- __ xvrdpic($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvrdpic($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
break;
case RoundDoubleModeNode::rmode_floor:
- __ xvrdpim($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvrdpim($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
break;
case RoundDoubleModeNode::rmode_ceil:
- __ xvrdpip($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvrdpip($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
break;
default:
ShouldNotReachHere();
@@ -13703,7 +13491,7 @@ instruct vneg4F_reg(vecX dst, vecX src) %{
format %{ "XVNEGSP $dst,$src\t// negate packed4F" %}
size(4);
ins_encode %{
- __ xvnegsp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvnegsp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13714,7 +13502,7 @@ instruct vneg2D_reg(vecX dst, vecX src) %{
format %{ "XVNEGDP $dst,$src\t// negate packed2D" %}
size(4);
ins_encode %{
- __ xvnegdp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvnegdp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13727,7 +13515,7 @@ instruct vsqrt4F_reg(vecX dst, vecX src) %{
format %{ "XVSQRTSP $dst,$src\t// sqrt packed4F" %}
size(4);
ins_encode %{
- __ xvsqrtsp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvsqrtsp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13738,7 +13526,7 @@ instruct vsqrt2D_reg(vecX dst, vecX src) %{
format %{ "XVSQRTDP $dst,$src\t// sqrt packed2D" %}
size(4);
ins_encode %{
- __ xvsqrtdp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvsqrtdp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13754,16 +13542,16 @@ instruct vpopcnt_reg(vecX dst, vecX src) %{
BasicType bt = Matcher::vector_element_basic_type(this);
switch (bt) {
case T_BYTE:
- __ vpopcntb($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vpopcntb($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_SHORT:
- __ vpopcnth($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vpopcnth($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_INT:
- __ vpopcntw($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vpopcntw($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_LONG:
- __ vpopcntd($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vpopcntd($dst$$VectorRegister, $src$$VectorRegister);
break;
default:
ShouldNotReachHere();
@@ -13780,16 +13568,16 @@ instruct vcount_leading_zeros_reg(vecX dst, vecX src) %{
BasicType bt = Matcher::vector_element_basic_type(this);
switch (bt) {
case T_BYTE:
- __ vclzb($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vclzb($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_SHORT:
- __ vclzh($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vclzh($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_INT:
- __ vclzw($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vclzw($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_LONG:
- __ vclzd($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vclzd($dst$$VectorRegister, $src$$VectorRegister);
break;
default:
ShouldNotReachHere();
@@ -13806,16 +13594,16 @@ instruct vcount_trailing_zeros_reg(vecX dst, vecX src) %{
BasicType bt = Matcher::vector_element_basic_type(this);
switch (bt) {
case T_BYTE:
- __ vctzb($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vctzb($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_SHORT:
- __ vctzh($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vctzh($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_INT:
- __ vctzw($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vctzw($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_LONG:
- __ vctzd($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vctzd($dst$$VectorRegister, $src$$VectorRegister);
break;
default:
ShouldNotReachHere();
@@ -13835,7 +13623,7 @@ instruct vfma4F(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvmaddasp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmaddasp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13851,7 +13639,7 @@ instruct vfma4F_neg1(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvnmsubasp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvnmsubasp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13866,7 +13654,7 @@ instruct vfma4F_neg2(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvmsubasp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmsubasp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13881,7 +13669,7 @@ instruct vfma2D(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvmaddadp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmaddadp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13897,7 +13685,7 @@ instruct vfma2D_neg1(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvnmsubadp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvnmsubadp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13912,7 +13700,7 @@ instruct vfma2D_neg2(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvmsubadp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmsubadp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13927,6 +13715,7 @@ instruct overflowAddL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{
match(Set cr0 (OverflowAddL op1 op2));
format %{ "add_ $op1, $op2\t# overflow check long" %}
+ size(12);
ins_encode %{
__ li(R0, 0);
__ mtxer(R0); // clear XER.SO
@@ -13939,6 +13728,7 @@ instruct overflowSubL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{
match(Set cr0 (OverflowSubL op1 op2));
format %{ "subfo_ R0, $op2, $op1\t# overflow check long" %}
+ size(12);
ins_encode %{
__ li(R0, 0);
__ mtxer(R0); // clear XER.SO
@@ -13951,6 +13741,7 @@ instruct overflowNegL_reg(flagsRegCR0 cr0, immL_0 zero, iRegLsrc op2) %{
match(Set cr0 (OverflowSubL zero op2));
format %{ "nego_ R0, $op2\t# overflow check long" %}
+ size(12);
ins_encode %{
__ li(R0, 0);
__ mtxer(R0); // clear XER.SO
@@ -13963,6 +13754,7 @@ instruct overflowMulL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{
match(Set cr0 (OverflowMulL op1 op2));
format %{ "mulldo_ R0, $op1, $op2\t# overflow check long" %}
+ size(12);
ins_encode %{
__ li(R0, 0);
__ mtxer(R0); // clear XER.SO
@@ -14001,8 +13793,9 @@ instruct repl4F_immF0(vecX dst, immF_0 zero) %{
Matcher::vector_element_basic_type(n) == T_FLOAT);
format %{ "XXLXOR $dst, $zero \t// replicate4F" %}
+ size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -14015,7 +13808,7 @@ instruct repl2D_reg_Ex(vecX dst, regD src) %{
format %{ "XXPERMDI $dst, $src, $src, 0 \t// Splat doubleword" %}
size(4);
ins_encode %{
- __ xxpermdi($dst$$VectorSRegister, $src$$FloatRegister->to_vsr(), $src$$FloatRegister->to_vsr(), 0);
+ __ xxpermdi($dst$$VectorRegister->to_vsr(), $src$$FloatRegister->to_vsr(), $src$$FloatRegister->to_vsr(), 0);
%}
ins_pipe(pipe_class_default);
%}
@@ -14028,7 +13821,7 @@ instruct repl2D_immD0(vecX dst, immD_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate2D" %}
size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -14040,7 +13833,7 @@ instruct mtvsrd(vecX dst, iRegLsrc src) %{
format %{ "MTVSRD $dst, $src \t// Move to 16-byte register" %}
size(4);
ins_encode %{
- __ mtvsrd($dst$$VectorSRegister, $src$$Register);
+ __ mtvsrd($dst$$VectorRegister->to_vsr(), $src$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -14051,7 +13844,7 @@ instruct xxspltd(vecX dst, vecX src, immI8 zero) %{
format %{ "XXSPLATD $dst, $src, $zero \t// Splat doubleword" %}
size(4);
ins_encode %{
- __ xxpermdi($dst$$VectorSRegister, $src$$VectorSRegister, $src$$VectorSRegister, $zero$$constant);
+ __ xxpermdi($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr(), $zero$$constant);
%}
ins_pipe(pipe_class_default);
%}
@@ -14062,7 +13855,7 @@ instruct xxpermdi(vecX dst, vecX src1, vecX src2, immI8 zero) %{
format %{ "XXPERMDI $dst, $src1, $src2, $zero \t// Splat doubleword" %}
size(4);
ins_encode %{
- __ xxpermdi($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister, $zero$$constant);
+ __ xxpermdi($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr(), $zero$$constant);
%}
ins_pipe(pipe_class_default);
%}
@@ -14087,7 +13880,7 @@ instruct repl2L_immI0(vecX dst, immI_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate2L" %}
size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -14100,7 +13893,7 @@ instruct repl2L_immIminus1(vecX dst, immI_minus1 src) %{
format %{ "XXLEQV $dst, $src \t// replicate2L" %}
size(4);
ins_encode %{
- __ xxleqv($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxleqv($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
diff --git a/src/hotspot/cpu/ppc/register_ppc.hpp b/src/hotspot/cpu/ppc/register_ppc.hpp
index b7949750dcc..2613d6c9822 100644
--- a/src/hotspot/cpu/ppc/register_ppc.hpp
+++ b/src/hotspot/cpu/ppc/register_ppc.hpp
@@ -321,6 +321,7 @@ class VectorRegister {
// accessors
constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; }
+ inline VMReg as_VMReg() const;
// testers
constexpr bool is_valid() const { return (0 <= _encoding && _encoding < number_of_registers); }
@@ -392,7 +393,6 @@ class VectorSRegister {
// accessors
constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; }
- inline VMReg as_VMReg() const;
VectorSRegister successor() const { return VectorSRegister(encoding() + 1); }
// testers
@@ -484,8 +484,8 @@ class ConcreteRegisterImpl : public AbstractRegisterImpl {
enum {
max_gpr = Register::number_of_registers * 2,
max_fpr = max_gpr + FloatRegister::number_of_registers * 2,
- max_vsr = max_fpr + VectorSRegister::number_of_registers * 4,
- max_cnd = max_vsr + ConditionRegister::number_of_registers,
+ max_vr = max_fpr + VectorRegister::number_of_registers * 4,
+ max_cnd = max_vr + ConditionRegister::number_of_registers,
max_spr = max_cnd + SpecialRegister::number_of_registers,
// This number must be large enough to cover REG_COUNT (defined by c2) registers.
// There is no requirement that any ordering here matches any ordering c2 gives
diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
index 4ec2483b267..37d6c9e6d51 100644
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
@@ -111,13 +111,13 @@ class RegisterSaver {
int_reg,
float_reg,
special_reg,
- vs_reg
+ vec_reg
} RegisterType;
typedef enum {
reg_size = 8,
half_reg_size = reg_size / 2,
- vs_reg_size = 16
+ vec_reg_size = 16
} RegisterConstants;
typedef struct {
@@ -137,8 +137,8 @@ class RegisterSaver {
#define RegisterSaver_LiveSpecialReg(regname) \
{ RegisterSaver::special_reg, regname->encoding(), regname->as_VMReg() }
-#define RegisterSaver_LiveVSReg(regname) \
- { RegisterSaver::vs_reg, regname->encoding(), regname->as_VMReg() }
+#define RegisterSaver_LiveVecReg(regname) \
+ { RegisterSaver::vec_reg, regname->encoding(), regname->as_VMReg() }
static const RegisterSaver::LiveRegType RegisterSaver_LiveRegs[] = {
// Live registers which get spilled to the stack. Register
@@ -220,42 +220,42 @@ static const RegisterSaver::LiveRegType RegisterSaver_LiveRegs[] = {
RegisterSaver_LiveIntReg( R31 ) // must be the last register (see save/restore functions below)
};
-static const RegisterSaver::LiveRegType RegisterSaver_LiveVSRegs[] = {
+static const RegisterSaver::LiveRegType RegisterSaver_LiveVecRegs[] = {
//
- // live vector scalar registers (optional, only these ones are used by C2):
+ // live vector registers (optional, only these ones are used by C2):
//
- RegisterSaver_LiveVSReg( VSR32 ),
- RegisterSaver_LiveVSReg( VSR33 ),
- RegisterSaver_LiveVSReg( VSR34 ),
- RegisterSaver_LiveVSReg( VSR35 ),
- RegisterSaver_LiveVSReg( VSR36 ),
- RegisterSaver_LiveVSReg( VSR37 ),
- RegisterSaver_LiveVSReg( VSR38 ),
- RegisterSaver_LiveVSReg( VSR39 ),
- RegisterSaver_LiveVSReg( VSR40 ),
- RegisterSaver_LiveVSReg( VSR41 ),
- RegisterSaver_LiveVSReg( VSR42 ),
- RegisterSaver_LiveVSReg( VSR43 ),
- RegisterSaver_LiveVSReg( VSR44 ),
- RegisterSaver_LiveVSReg( VSR45 ),
- RegisterSaver_LiveVSReg( VSR46 ),
- RegisterSaver_LiveVSReg( VSR47 ),
- RegisterSaver_LiveVSReg( VSR48 ),
- RegisterSaver_LiveVSReg( VSR49 ),
- RegisterSaver_LiveVSReg( VSR50 ),
- RegisterSaver_LiveVSReg( VSR51 ),
- RegisterSaver_LiveVSReg( VSR52 ),
- RegisterSaver_LiveVSReg( VSR53 ),
- RegisterSaver_LiveVSReg( VSR54 ),
- RegisterSaver_LiveVSReg( VSR55 ),
- RegisterSaver_LiveVSReg( VSR56 ),
- RegisterSaver_LiveVSReg( VSR57 ),
- RegisterSaver_LiveVSReg( VSR58 ),
- RegisterSaver_LiveVSReg( VSR59 ),
- RegisterSaver_LiveVSReg( VSR60 ),
- RegisterSaver_LiveVSReg( VSR61 ),
- RegisterSaver_LiveVSReg( VSR62 ),
- RegisterSaver_LiveVSReg( VSR63 )
+ RegisterSaver_LiveVecReg( VR0 ),
+ RegisterSaver_LiveVecReg( VR1 ),
+ RegisterSaver_LiveVecReg( VR2 ),
+ RegisterSaver_LiveVecReg( VR3 ),
+ RegisterSaver_LiveVecReg( VR4 ),
+ RegisterSaver_LiveVecReg( VR5 ),
+ RegisterSaver_LiveVecReg( VR6 ),
+ RegisterSaver_LiveVecReg( VR7 ),
+ RegisterSaver_LiveVecReg( VR8 ),
+ RegisterSaver_LiveVecReg( VR9 ),
+ RegisterSaver_LiveVecReg( VR10 ),
+ RegisterSaver_LiveVecReg( VR11 ),
+ RegisterSaver_LiveVecReg( VR12 ),
+ RegisterSaver_LiveVecReg( VR13 ),
+ RegisterSaver_LiveVecReg( VR14 ),
+ RegisterSaver_LiveVecReg( VR15 ),
+ RegisterSaver_LiveVecReg( VR16 ),
+ RegisterSaver_LiveVecReg( VR17 ),
+ RegisterSaver_LiveVecReg( VR18 ),
+ RegisterSaver_LiveVecReg( VR19 ),
+ RegisterSaver_LiveVecReg( VR20 ),
+ RegisterSaver_LiveVecReg( VR21 ),
+ RegisterSaver_LiveVecReg( VR22 ),
+ RegisterSaver_LiveVecReg( VR23 ),
+ RegisterSaver_LiveVecReg( VR24 ),
+ RegisterSaver_LiveVecReg( VR25 ),
+ RegisterSaver_LiveVecReg( VR26 ),
+ RegisterSaver_LiveVecReg( VR27 ),
+ RegisterSaver_LiveVecReg( VR28 ),
+ RegisterSaver_LiveVecReg( VR29 ),
+ RegisterSaver_LiveVecReg( VR30 ),
+ RegisterSaver_LiveVecReg( VR31 )
};
@@ -277,10 +277,10 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble
// calculate frame size
const int regstosave_num = sizeof(RegisterSaver_LiveRegs) /
sizeof(RegisterSaver::LiveRegType);
- const int vsregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVSRegs) /
+ const int vecregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVecRegs) /
sizeof(RegisterSaver::LiveRegType))
: 0;
- const int register_save_size = regstosave_num * reg_size + vsregstosave_num * vs_reg_size;
+ const int register_save_size = regstosave_num * reg_size + vecregstosave_num * vec_reg_size;
const int frame_size_in_bytes = align_up(register_save_size, frame::alignment_in_bytes)
+ frame::native_abi_reg_args_size;
@@ -298,8 +298,8 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble
// Save some registers in the last (non-vector) slots of the new frame so we
// can use them as scratch regs or to determine the return pc.
- __ std(R31, frame_size_in_bytes - reg_size - vsregstosave_num * vs_reg_size, R1_SP);
- __ std(R30, frame_size_in_bytes - 2*reg_size - vsregstosave_num * vs_reg_size, R1_SP);
+ __ std(R31, frame_size_in_bytes - reg_size - vecregstosave_num * vec_reg_size, R1_SP);
+ __ std(R30, frame_size_in_bytes - 2*reg_size - vecregstosave_num * vec_reg_size, R1_SP);
// save the flags
// Do the save_LR by hand and adjust the return pc if requested.
@@ -360,37 +360,37 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble
// the utilized instructions (PowerArchitecturePPC64).
assert(is_aligned(offset, StackAlignmentInBytes), "should be");
if (PowerArchitecturePPC64 >= 10) {
- assert(is_even(vsregstosave_num), "expectation");
- for (int i = 0; i < vsregstosave_num; i += 2) {
- int reg_num = RegisterSaver_LiveVSRegs[i].reg_num;
- assert(RegisterSaver_LiveVSRegs[i + 1].reg_num == reg_num + 1, "or use other instructions!");
+ assert(is_even(vecregstosave_num), "expectation");
+ for (int i = 0; i < vecregstosave_num; i += 2) {
+ int reg_num = RegisterSaver_LiveVecRegs[i].reg_num;
+ assert(RegisterSaver_LiveVecRegs[i + 1].reg_num == reg_num + 1, "or use other instructions!");
- __ stxvp(as_VectorSRegister(reg_num), offset, R1_SP);
+ __ stxvp(as_VectorRegister(reg_num).to_vsr(), offset, R1_SP);
// Note: The contents were read in the same order (see loadV16_Power9 node in ppc.ad).
if (generate_oop_map) {
map->set_callee_saved(VMRegImpl::stack2reg(offset >> 2),
- RegisterSaver_LiveVSRegs[i LITTLE_ENDIAN_ONLY(+1) ].vmreg);
- map->set_callee_saved(VMRegImpl::stack2reg((offset + vs_reg_size) >> 2),
- RegisterSaver_LiveVSRegs[i BIG_ENDIAN_ONLY(+1) ].vmreg);
+ RegisterSaver_LiveVecRegs[i LITTLE_ENDIAN_ONLY(+1) ].vmreg);
+ map->set_callee_saved(VMRegImpl::stack2reg((offset + vec_reg_size) >> 2),
+ RegisterSaver_LiveVecRegs[i BIG_ENDIAN_ONLY(+1) ].vmreg);
}
- offset += (2 * vs_reg_size);
+ offset += (2 * vec_reg_size);
}
} else {
- for (int i = 0; i < vsregstosave_num; i++) {
- int reg_num = RegisterSaver_LiveVSRegs[i].reg_num;
+ for (int i = 0; i < vecregstosave_num; i++) {
+ int reg_num = RegisterSaver_LiveVecRegs[i].reg_num;
if (PowerArchitecturePPC64 >= 9) {
- __ stxv(as_VectorSRegister(reg_num), offset, R1_SP);
+ __ stxv(as_VectorRegister(reg_num)->to_vsr(), offset, R1_SP);
} else {
__ li(R31, offset);
- __ stxvd2x(as_VectorSRegister(reg_num), R31, R1_SP);
+ __ stxvd2x(as_VectorRegister(reg_num)->to_vsr(), R31, R1_SP);
}
// Note: The contents were read in the same order (see loadV16_Power8 / loadV16_Power9 node in ppc.ad).
if (generate_oop_map) {
- VMReg vsr = RegisterSaver_LiveVSRegs[i].vmreg;
+ VMReg vsr = RegisterSaver_LiveVecRegs[i].vmreg;
map->set_callee_saved(VMRegImpl::stack2reg(offset >> 2), vsr);
}
- offset += vs_reg_size;
+ offset += vec_reg_size;
}
}
@@ -411,10 +411,10 @@ void RegisterSaver::restore_live_registers_and_pop_frame(MacroAssembler* masm,
bool save_vectors) {
const int regstosave_num = sizeof(RegisterSaver_LiveRegs) /
sizeof(RegisterSaver::LiveRegType);
- const int vsregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVSRegs) /
+ const int vecregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVecRegs) /
sizeof(RegisterSaver::LiveRegType))
: 0;
- const int register_save_size = regstosave_num * reg_size + vsregstosave_num * vs_reg_size;
+ const int register_save_size = regstosave_num * reg_size + vecregstosave_num * vec_reg_size;
const int register_save_offset = frame_size_in_bytes - register_save_size;
@@ -456,26 +456,26 @@ void RegisterSaver::restore_live_registers_and_pop_frame(MacroAssembler* masm,
assert(is_aligned(offset, StackAlignmentInBytes), "should be");
if (PowerArchitecturePPC64 >= 10) {
- for (int i = 0; i < vsregstosave_num; i += 2) {
- int reg_num = RegisterSaver_LiveVSRegs[i].reg_num;
- assert(RegisterSaver_LiveVSRegs[i + 1].reg_num == reg_num + 1, "or use other instructions!");
+ for (int i = 0; i < vecregstosave_num; i += 2) {
+ int reg_num = RegisterSaver_LiveVecRegs[i].reg_num;
+ assert(RegisterSaver_LiveVecRegs[i + 1].reg_num == reg_num + 1, "or use other instructions!");
- __ lxvp(as_VectorSRegister(reg_num), offset, R1_SP);
+ __ lxvp(as_VectorRegister(reg_num).to_vsr(), offset, R1_SP);
- offset += (2 * vs_reg_size);
+ offset += (2 * vec_reg_size);
}
} else {
- for (int i = 0; i < vsregstosave_num; i++) {
- int reg_num = RegisterSaver_LiveVSRegs[i].reg_num;
+ for (int i = 0; i < vecregstosave_num; i++) {
+ int reg_num = RegisterSaver_LiveVecRegs[i].reg_num;
if (PowerArchitecturePPC64 >= 9) {
- __ lxv(as_VectorSRegister(reg_num), offset, R1_SP);
+ __ lxv(as_VectorRegister(reg_num).to_vsr(), offset, R1_SP);
} else {
__ li(R31, offset);
- __ lxvd2x(as_VectorSRegister(reg_num), R31, R1_SP);
+ __ lxvd2x(as_VectorRegister(reg_num).to_vsr(), R31, R1_SP);
}
- offset += vs_reg_size;
+ offset += vec_reg_size;
}
}
@@ -486,7 +486,7 @@ void RegisterSaver::restore_live_registers_and_pop_frame(MacroAssembler* masm,
__ mtlr(R31);
// restore scratch register's value
- __ ld(R31, frame_size_in_bytes - reg_size - vsregstosave_num * vs_reg_size, R1_SP);
+ __ ld(R31, frame_size_in_bytes - reg_size - vecregstosave_num * vec_reg_size, R1_SP);
// pop the frame
__ addi(R1_SP, R1_SP, frame_size_in_bytes);
diff --git a/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp b/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp
index 1a19f1b8cf2..be51afe42a4 100644
--- a/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp
+++ b/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp
@@ -26,6 +26,13 @@
#ifndef CPU_PPC_STUBDECLARATIONS_HPP
#define CPU_PPC_STUBDECLARATIONS_HPP
+#define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \
+ do_arch_blob, \
+ do_arch_entry, \
+ do_arch_entry_init) \
+ do_arch_blob(preuniverse, 0) \
+
+
#define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \
do_arch_blob, \
do_arch_entry, \
diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp
index 2624131033c..a2b25376837 100644
--- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp
+++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp
@@ -952,8 +952,10 @@ class StubGenerator: public StubCodeGenerator {
address start_pc = __ pc();
Register tmp1 = R6_ARG4;
// probably copy stub would have changed value reset it.
- __ load_const_optimized(tmp1, VM_Version::_dscr_val);
- __ mtdscr(tmp1);
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp1, VM_Version::_dscr_val);
+ __ mtdscr(tmp1);
+ }
__ li(R3_RET, 0); // return 0
__ blr();
return start_pc;
@@ -1070,9 +1072,10 @@ class StubGenerator: public StubCodeGenerator {
__ dcbt(R3_ARG1, 0);
// If supported set DSCR pre-fetch to deepest.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
- __ mtdscr(tmp2);
-
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
+ __ mtdscr(tmp2);
+ }
__ li(tmp1, 16);
// Backbranch target aligned to 32-byte. Not 16-byte align as
@@ -1092,8 +1095,10 @@ class StubGenerator: public StubCodeGenerator {
__ bdnz(l_10); // Dec CTR and loop if not zero.
// Restore DSCR pre-fetch value.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val);
- __ mtdscr(tmp2);
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val);
+ __ mtdscr(tmp2);
+ }
} // FasterArrayCopy
@@ -1344,8 +1349,10 @@ class StubGenerator: public StubCodeGenerator {
__ dcbt(R3_ARG1, 0);
// If supported set DSCR pre-fetch to deepest.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
- __ mtdscr(tmp2);
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
+ __ mtdscr(tmp2);
+ }
__ li(tmp1, 16);
// Backbranch target aligned to 32-byte. It's not aligned 16-byte
@@ -1365,8 +1372,11 @@ class StubGenerator: public StubCodeGenerator {
__ bdnz(l_9); // Dec CTR and loop if not zero.
// Restore DSCR pre-fetch value.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val);
- __ mtdscr(tmp2);
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val);
+ __ mtdscr(tmp2);
+ }
+
} // FasterArrayCopy
__ bind(l_6);
@@ -1527,9 +1537,10 @@ class StubGenerator: public StubCodeGenerator {
__ dcbt(R3_ARG1, 0);
// Set DSCR pre-fetch to deepest.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
- __ mtdscr(tmp2);
-
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
+ __ mtdscr(tmp2);
+ }
__ li(tmp1, 16);
// Backbranch target aligned to 32-byte. Not 16-byte align as
@@ -1549,9 +1560,10 @@ class StubGenerator: public StubCodeGenerator {
__ bdnz(l_7); // Dec CTR and loop if not zero.
// Restore DSCR pre-fetch value.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val);
- __ mtdscr(tmp2);
-
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val);
+ __ mtdscr(tmp2);
+ }
} // FasterArrayCopy
@@ -1672,9 +1684,10 @@ class StubGenerator: public StubCodeGenerator {
__ dcbt(R3_ARG1, 0);
// Set DSCR pre-fetch to deepest.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
- __ mtdscr(tmp2);
-
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
+ __ mtdscr(tmp2);
+ }
__ li(tmp1, 16);
// Backbranch target aligned to 32-byte. Not 16-byte align as
@@ -1694,8 +1707,10 @@ class StubGenerator: public StubCodeGenerator {
__ bdnz(l_4);
// Restore DSCR pre-fetch value.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val);
- __ mtdscr(tmp2);
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val);
+ __ mtdscr(tmp2);
+ }
__ cmpwi(CR0, R5_ARG3, 0);
__ beq(CR0, l_6);
@@ -1788,9 +1803,10 @@ class StubGenerator: public StubCodeGenerator {
__ dcbt(R3_ARG1, 0);
// Set DSCR pre-fetch to deepest.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
- __ mtdscr(tmp2);
-
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
+ __ mtdscr(tmp2);
+ }
__ li(tmp1, 16);
// Backbranch target aligned to 32-byte. Not 16-byte align as
@@ -1810,8 +1826,10 @@ class StubGenerator: public StubCodeGenerator {
__ bdnz(l_5); // Dec CTR and loop if not zero.
// Restore DSCR pre-fetch value.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val);
- __ mtdscr(tmp2);
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val);
+ __ mtdscr(tmp2);
+ }
} // FasterArrayCopy
@@ -1910,9 +1928,10 @@ class StubGenerator: public StubCodeGenerator {
__ dcbt(R3_ARG1, 0);
// Set DSCR pre-fetch to deepest.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
- __ mtdscr(tmp2);
-
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7);
+ __ mtdscr(tmp2);
+ }
__ li(tmp1, 16);
// Backbranch target aligned to 32-byte. Not 16-byte align as
@@ -1932,8 +1951,10 @@ class StubGenerator: public StubCodeGenerator {
__ bdnz(l_4);
// Restore DSCR pre-fetch value.
- __ load_const_optimized(tmp2, VM_Version::_dscr_val);
- __ mtdscr(tmp2);
+ if (VM_Version::has_mfdscr()) {
+ __ load_const_optimized(tmp2, VM_Version::_dscr_val);
+ __ mtdscr(tmp2);
+ }
__ cmpwi(CR0, R5_ARG3, 0);
__ beq(CR0, l_1);
@@ -4938,6 +4959,10 @@ void generate_lookup_secondary_supers_table_stub() {
}
// Initialization
+ void generate_preuniverse_stubs() {
+ // preuniverse stubs are not needed for ppc
+ }
+
void generate_initial_stubs() {
// Generates all stubs and initializes the entry points
@@ -5067,6 +5092,9 @@ void generate_lookup_secondary_supers_table_stub() {
public:
StubGenerator(CodeBuffer* code, StubGenBlobId blob_id) : StubCodeGenerator(code, blob_id) {
switch(blob_id) {
+ case preuniverse_id:
+ generate_preuniverse_stubs();
+ break;
case initial_id:
generate_initial_stubs();
break;
diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp
index ad5e915a838..ec2766ac75b 100644
--- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp
+++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp
@@ -80,7 +80,9 @@ void VM_Version::initialize() {
"%zu on this machine", PowerArchitecturePPC64);
// Power 8: Configure Data Stream Control Register.
- config_dscr();
+ if (VM_Version::has_mfdscr()) {
+ config_dscr();
+ }
if (!UseSIGTRAP) {
MSG(TrapBasedICMissChecks);
@@ -170,7 +172,8 @@ void VM_Version::initialize() {
// Create and print feature-string.
char buf[(num_features+1) * 16]; // Max 16 chars per feature.
jio_snprintf(buf, sizeof(buf),
- "ppc64 sha aes%s%s",
+ "ppc64 sha aes%s%s%s",
+ (has_mfdscr() ? " mfdscr" : ""),
(has_darn() ? " darn" : ""),
(has_brw() ? " brw" : "")
// Make sure number of %s matches num_features!
@@ -488,6 +491,7 @@ void VM_Version::determine_features() {
uint32_t *code = (uint32_t *)a->pc();
// Keep R3_ARG1 unmodified, it contains &field (see below).
// Keep R4_ARG2 unmodified, it contains offset = 0 (see below).
+ a->mfdscr(R0);
a->darn(R7);
a->brw(R5, R6);
a->blr();
@@ -524,6 +528,7 @@ void VM_Version::determine_features() {
// determine which instructions are legal.
int feature_cntr = 0;
+ if (code[feature_cntr++]) features |= mfdscr_m;
if (code[feature_cntr++]) features |= darn_m;
if (code[feature_cntr++]) features |= brw_m;
diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.hpp b/src/hotspot/cpu/ppc/vm_version_ppc.hpp
index 18dfd843c19..9588edc4bf8 100644
--- a/src/hotspot/cpu/ppc/vm_version_ppc.hpp
+++ b/src/hotspot/cpu/ppc/vm_version_ppc.hpp
@@ -32,12 +32,14 @@
class VM_Version: public Abstract_VM_Version {
protected:
enum Feature_Flag {
+ mfdscr,
darn,
brw,
num_features // last entry to count features
};
enum Feature_Flag_Set {
unknown_m = 0,
+ mfdscr_m = (1 << mfdscr ),
darn_m = (1 << darn ),
brw_m = (1 << brw ),
all_features_m = (unsigned long)-1
@@ -67,8 +69,9 @@ class VM_Version: public Abstract_VM_Version {
static bool is_determine_features_test_running() { return _is_determine_features_test_running; }
// CPU instruction support
- static bool has_darn() { return (_features & darn_m) != 0; }
- static bool has_brw() { return (_features & brw_m) != 0; }
+ static bool has_mfdscr() { return (_features & mfdscr_m) != 0; } // Power8, but may be unavailable (QEMU)
+ static bool has_darn() { return (_features & darn_m) != 0; }
+ static bool has_brw() { return (_features & brw_m) != 0; }
// Assembler testing
static void allow_all();
diff --git a/src/hotspot/cpu/ppc/vmreg_ppc.cpp b/src/hotspot/cpu/ppc/vmreg_ppc.cpp
index 2ed68578a80..0edbf700ec6 100644
--- a/src/hotspot/cpu/ppc/vmreg_ppc.cpp
+++ b/src/hotspot/cpu/ppc/vmreg_ppc.cpp
@@ -47,7 +47,7 @@ void VMRegImpl::set_regName() {
}
VectorSRegister vsreg = ::as_VectorSRegister(0);
- for ( ; i < ConcreteRegisterImpl::max_vsr; ) {
+ for ( ; i < ConcreteRegisterImpl::max_vr; ) {
regName[i++] = vsreg->name();
regName[i++] = vsreg->name();
regName[i++] = vsreg->name();
diff --git a/src/hotspot/cpu/ppc/vmreg_ppc.hpp b/src/hotspot/cpu/ppc/vmreg_ppc.hpp
index 4e25c8b3cea..194b5fd93ef 100644
--- a/src/hotspot/cpu/ppc/vmreg_ppc.hpp
+++ b/src/hotspot/cpu/ppc/vmreg_ppc.hpp
@@ -35,13 +35,13 @@ inline bool is_FloatRegister() {
value() < ConcreteRegisterImpl::max_fpr;
}
-inline bool is_VectorSRegister() {
+inline bool is_VectorRegister() {
return value() >= ConcreteRegisterImpl::max_fpr &&
- value() < ConcreteRegisterImpl::max_vsr;
+ value() < ConcreteRegisterImpl::max_vr;
}
inline bool is_ConditionRegister() {
- return value() >= ConcreteRegisterImpl::max_vsr &&
+ return value() >= ConcreteRegisterImpl::max_vr &&
value() < ConcreteRegisterImpl::max_cnd;
}
@@ -60,15 +60,15 @@ inline FloatRegister as_FloatRegister() {
return ::as_FloatRegister((value() - ConcreteRegisterImpl::max_gpr) >> 1);
}
-inline VectorSRegister as_VectorSRegister() {
- assert(is_VectorSRegister(), "must be");
- return ::as_VectorSRegister((value() - ConcreteRegisterImpl::max_fpr) >> 2);
+inline VectorRegister as_VectorRegister() {
+ assert(is_VectorRegister(), "must be");
+ return ::as_VectorRegister((value() - ConcreteRegisterImpl::max_fpr) >> 2);
}
inline bool is_concrete() {
assert(is_reg(), "must be");
if (is_Register() || is_FloatRegister()) return is_even(value());
- if (is_VectorSRegister()) {
+ if (is_VectorRegister()) {
int base = value() - ConcreteRegisterImpl::max_fpr;
return (base & 3) == 0;
}
diff --git a/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp b/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp
index 2424df8da01..a7810266b89 100644
--- a/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp
+++ b/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp
@@ -40,13 +40,13 @@ inline VMReg FloatRegister::as_VMReg() const {
return VMRegImpl::as_VMReg((encoding() << 1) + ConcreteRegisterImpl::max_gpr);
}
-inline VMReg VectorSRegister::as_VMReg() const {
+inline VMReg VectorRegister::as_VMReg() const {
// Four halves, multiply by 4.
return VMRegImpl::as_VMReg((encoding() << 2) + ConcreteRegisterImpl::max_fpr);
}
inline VMReg ConditionRegister::as_VMReg() const {
- return VMRegImpl::as_VMReg((encoding()) + ConcreteRegisterImpl::max_vsr);
+ return VMRegImpl::as_VMReg((encoding()) + ConcreteRegisterImpl::max_vr);
}
inline VMReg SpecialRegister::as_VMReg() const {
diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
index 08d35c2831a..c7587765da0 100644
--- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
@@ -401,7 +401,7 @@ void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
code_stub->set_safepoint_offset(__ offset());
__ relocate(relocInfo::poll_return_type);
- __ safepoint_poll(*code_stub->entry(), true /* at_return */, false /* acquire */, true /* in_nmethod */);
+ __ safepoint_poll(*code_stub->entry(), true /* at_return */, true /* in_nmethod */);
__ ret();
}
diff --git a/src/hotspot/cpu/riscv/c1_globals_riscv.hpp b/src/hotspot/cpu/riscv/c1_globals_riscv.hpp
index 311405dbc2b..d64b3b66fa2 100644
--- a/src/hotspot/cpu/riscv/c1_globals_riscv.hpp
+++ b/src/hotspot/cpu/riscv/c1_globals_riscv.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -43,15 +43,15 @@ define_pd_global(intx, CompileThreshold, 1500 );
define_pd_global(intx, OnStackReplacePercentage, 933 );
define_pd_global(intx, NewSizeThreadIncrease, 4*K );
-define_pd_global(intx, InitialCodeCacheSize, 160*K);
-define_pd_global(intx, ReservedCodeCacheSize, 32*M );
-define_pd_global(intx, NonProfiledCodeHeapSize, 13*M );
-define_pd_global(intx, ProfiledCodeHeapSize, 14*M );
-define_pd_global(intx, NonNMethodCodeHeapSize, 5*M );
+define_pd_global(size_t, InitialCodeCacheSize, 160*K);
+define_pd_global(size_t, ReservedCodeCacheSize, 32*M );
+define_pd_global(size_t, NonProfiledCodeHeapSize, 13*M );
+define_pd_global(size_t, ProfiledCodeHeapSize, 14*M );
+define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M );
define_pd_global(bool, ProfileInterpreter, false);
-define_pd_global(intx, CodeCacheExpansionSize, 32*K );
-define_pd_global(uintx, CodeCacheMinBlockLength, 1);
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K);
+define_pd_global(size_t, CodeCacheExpansionSize, 32*K );
+define_pd_global(size_t, CodeCacheMinBlockLength, 1);
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
define_pd_global(bool, NeverActAsServerClassMachine, true );
define_pd_global(uint64_t, MaxRAM, 1ULL*G);
define_pd_global(bool, CICompileOSR, true );
diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
index 77b4e26cc92..ce13ebde74f 100644
--- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
@@ -2170,15 +2170,13 @@ void C2_MacroAssembler::enc_cmove_cmp_fp(int cmpFlag, FloatRegister op1, FloatRe
cmov_cmp_fp_le(op1, op2, dst, src, is_single);
break;
case BoolTest::ge:
- assert(false, "Should go to BoolTest::le case");
- ShouldNotReachHere();
+ cmov_cmp_fp_ge(op1, op2, dst, src, is_single);
break;
case BoolTest::lt:
cmov_cmp_fp_lt(op1, op2, dst, src, is_single);
break;
case BoolTest::gt:
- assert(false, "Should go to BoolTest::lt case");
- ShouldNotReachHere();
+ cmov_cmp_fp_gt(op1, op2, dst, src, is_single);
break;
default:
assert(false, "unsupported compare condition");
diff --git a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp
index 79bdc4917c9..372865fc291 100644
--- a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp
+++ b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -51,8 +51,8 @@ define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K));
define_pd_global(intx, LoopUnrollLimit, 60);
define_pd_global(intx, LoopPercentProfileLimit, 10);
// InitialCodeCacheSize derived from specjbb2000 run.
-define_pd_global(intx, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize
-define_pd_global(intx, CodeCacheExpansionSize, 64*K);
+define_pd_global(size_t, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize
+define_pd_global(size_t, CodeCacheExpansionSize, 64*K);
// Ergonomics related flags
define_pd_global(uint64_t,MaxRAM, 128ULL*G);
@@ -69,12 +69,12 @@ define_pd_global(bool, SuperWordLoopUnrollAnalysis, true);
define_pd_global(uint, SuperWordStoreToLoadForwardingFailureDetection, 16);
define_pd_global(bool, IdealizeClearArrayNode, true);
-define_pd_global(intx, ReservedCodeCacheSize, 48*M);
-define_pd_global(intx, NonProfiledCodeHeapSize, 21*M);
-define_pd_global(intx, ProfiledCodeHeapSize, 22*M);
-define_pd_global(intx, NonNMethodCodeHeapSize, 5*M );
-define_pd_global(uintx, CodeCacheMinBlockLength, 6);
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K);
+define_pd_global(size_t, ReservedCodeCacheSize, 48*M);
+define_pd_global(size_t, NonProfiledCodeHeapSize, 21*M);
+define_pd_global(size_t, ProfiledCodeHeapSize, 22*M);
+define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M );
+define_pd_global(size_t, CodeCacheMinBlockLength, 6);
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
// Ergonomics related flags
define_pd_global(bool, NeverActAsServerClassMachine, false);
diff --git a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp
index d1278c419a0..cc685645ec5 100644
--- a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp
+++ b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp
@@ -287,7 +287,7 @@ void DowncallLinker::StubGenerator::generate() {
__ membar(MacroAssembler::AnyAny);
}
- __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, false /* in_nmethod */);
__ lwu(t0, Address(xthread, JavaThread::suspend_flags_offset()));
__ bnez(t0, L_safepoint_poll_slow_path);
diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp
index d67e05bbb6d..c78bfaa8ffd 100644
--- a/src/hotspot/cpu/riscv/globals_riscv.hpp
+++ b/src/hotspot/cpu/riscv/globals_riscv.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -38,7 +38,7 @@ define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap nulls
define_pd_global(bool, DelayCompilerStubsGeneration, COMPILER2_OR_JVMCI);
-define_pd_global(uintx, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRESENT(+64)); // Tiered compilation has large code-entry alignment.
+define_pd_global(size_t, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRESENT(+64)); // Tiered compilation has large code-entry alignment.
define_pd_global(intx, CodeEntryAlignment, 64);
define_pd_global(intx, OptoLoopAlignment, 16);
diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
index fae34a9c770..92f7169f471 100644
--- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
+++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
@@ -645,7 +645,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state,
// the stack, will call InterpreterRuntime::at_unwind.
Label slow_path;
Label fast_path;
- safepoint_poll(slow_path, true /* at_return */, false /* acquire */, false /* in_nmethod */);
+ safepoint_poll(slow_path, true /* at_return */, false /* in_nmethod */);
j(fast_path);
bind(slow_path);
@@ -1050,26 +1050,16 @@ void InterpreterMacroAssembler::update_mdp_for_ret(Register return_bci) {
addi(sp, sp, 2 * wordSize);
}
-void InterpreterMacroAssembler::profile_taken_branch(Register mdp,
- Register bumped_count) {
+void InterpreterMacroAssembler::profile_taken_branch(Register mdp) {
if (ProfileInterpreter) {
Label profile_continue;
// If no method data exists, go to profile_continue.
- // Otherwise, assign to mdp
test_method_data_pointer(mdp, profile_continue);
// We are taking a branch. Increment the taken count.
- Address data(mdp, in_bytes(JumpData::taken_offset()));
- ld(bumped_count, data);
- assert(DataLayout::counter_increment == 1,
- "flow-free idiom only works with 1");
- addi(bumped_count, bumped_count, DataLayout::counter_increment);
- Label L;
- // eg: bumped_count=0x7fff ffff ffff ffff + 1 < 0. so we use <= 0;
- blez(bumped_count, L); // skip store if counter overflow,
- sd(bumped_count, data);
- bind(L);
+ increment_mdp_data_at(mdp, in_bytes(JumpData::taken_offset()));
+
// The method data pointer needs to be updated to reflect the new target.
update_mdp_by_offset(mdp, in_bytes(JumpData::displacement_offset()));
bind(profile_continue);
@@ -1083,7 +1073,7 @@ void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp) {
// If no method data exists, go to profile_continue.
test_method_data_pointer(mdp, profile_continue);
- // We are taking a branch. Increment the not taken count.
+ // We are not taking a branch. Increment the not taken count.
increment_mdp_data_at(mdp, in_bytes(BranchData::not_taken_offset()));
// The method data pointer needs to be updated to correspond to
diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
index 891db16b243..0732191ea83 100644
--- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
+++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
@@ -261,7 +261,7 @@ class InterpreterMacroAssembler: public MacroAssembler {
// narrow int return value
void narrow(Register result);
- void profile_taken_branch(Register mdp, Register bumped_count);
+ void profile_taken_branch(Register mdp);
void profile_not_taken_branch(Register mdp);
void profile_call(Register mdp);
void profile_final_call(Register mdp);
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
index c755d9ae23d..a72a2a50fd7 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
@@ -1268,12 +1268,19 @@ void MacroAssembler::cmov_gtu(Register cmp1, Register cmp2, Register dst, Regist
}
// ----------- cmove, compare float -----------
+//
+// For CmpF/D + CMoveI/L, ordered ones are quite straight and simple,
+// so, just list behaviour of unordered ones as follow.
+//
+// Set dst (CMoveI (Binary cop (CmpF/D op1 op2)) (Binary dst src))
+// (If one or both inputs to the compare are NaN, then)
+// 1. (op1 lt op2) => true => CMove: dst = src
+// 2. (op1 le op2) => true => CMove: dst = src
+// 3. (op1 gt op2) => false => CMove: dst = dst
+// 4. (op1 ge op2) => false => CMove: dst = dst
+// 5. (op1 eq op2) => false => CMove: dst = dst
+// 6. (op1 ne op2) => true => CMove: dst = src
-// Move src to dst only if cmp1 == cmp2,
-// otherwise leave dst unchanged, including the case where one of them is NaN.
-// Clarification:
-// java code : cmp1 != cmp2 ? dst : src
-// transformed to : CMove dst, (cmp1 eq cmp2), dst, src
void MacroAssembler::cmov_cmp_fp_eq(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) {
if (UseZicond) {
if (is_single) {
@@ -1289,7 +1296,7 @@ void MacroAssembler::cmov_cmp_fp_eq(FloatRegister cmp1, FloatRegister cmp2, Regi
Label no_set;
if (is_single) {
// jump if cmp1 != cmp2, including the case of NaN
- // not jump (i.e. move src to dst) if cmp1 == cmp2
+ // fallthrough (i.e. move src to dst) if cmp1 == cmp2
float_bne(cmp1, cmp2, no_set);
} else {
double_bne(cmp1, cmp2, no_set);
@@ -1298,11 +1305,6 @@ void MacroAssembler::cmov_cmp_fp_eq(FloatRegister cmp1, FloatRegister cmp2, Regi
bind(no_set);
}
-// Keep dst unchanged only if cmp1 == cmp2,
-// otherwise move src to dst, including the case where one of them is NaN.
-// Clarification:
-// java code : cmp1 == cmp2 ? dst : src
-// transformed to : CMove dst, (cmp1 ne cmp2), dst, src
void MacroAssembler::cmov_cmp_fp_ne(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) {
if (UseZicond) {
if (is_single) {
@@ -1318,7 +1320,7 @@ void MacroAssembler::cmov_cmp_fp_ne(FloatRegister cmp1, FloatRegister cmp2, Regi
Label no_set;
if (is_single) {
// jump if cmp1 == cmp2
- // not jump (i.e. move src to dst) if cmp1 != cmp2, including the case of NaN
+ // fallthrough (i.e. move src to dst) if cmp1 != cmp2, including the case of NaN
float_beq(cmp1, cmp2, no_set);
} else {
double_beq(cmp1, cmp2, no_set);
@@ -1327,14 +1329,6 @@ void MacroAssembler::cmov_cmp_fp_ne(FloatRegister cmp1, FloatRegister cmp2, Regi
bind(no_set);
}
-// When cmp1 <= cmp2 or any of them is NaN then dst = src, otherwise, dst = dst
-// Clarification
-// scenario 1:
-// java code : cmp2 < cmp1 ? dst : src
-// transformed to : CMove dst, (cmp1 le cmp2), dst, src
-// scenario 2:
-// java code : cmp1 > cmp2 ? dst : src
-// transformed to : CMove dst, (cmp1 le cmp2), dst, src
void MacroAssembler::cmov_cmp_fp_le(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) {
if (UseZicond) {
if (is_single) {
@@ -1350,7 +1344,7 @@ void MacroAssembler::cmov_cmp_fp_le(FloatRegister cmp1, FloatRegister cmp2, Regi
Label no_set;
if (is_single) {
// jump if cmp1 > cmp2
- // not jump (i.e. move src to dst) if cmp1 <= cmp2 or either is NaN
+ // fallthrough (i.e. move src to dst) if cmp1 <= cmp2 or either is NaN
float_bgt(cmp1, cmp2, no_set);
} else {
double_bgt(cmp1, cmp2, no_set);
@@ -1359,14 +1353,30 @@ void MacroAssembler::cmov_cmp_fp_le(FloatRegister cmp1, FloatRegister cmp2, Regi
bind(no_set);
}
-// When cmp1 < cmp2 or any of them is NaN then dst = src, otherwise, dst = dst
-// Clarification
-// scenario 1:
-// java code : cmp2 <= cmp1 ? dst : src
-// transformed to : CMove dst, (cmp1 lt cmp2), dst, src
-// scenario 2:
-// java code : cmp1 >= cmp2 ? dst : src
-// transformed to : CMove dst, (cmp1 lt cmp2), dst, src
+void MacroAssembler::cmov_cmp_fp_ge(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) {
+ if (UseZicond) {
+ if (is_single) {
+ fle_s(t0, cmp2, cmp1);
+ } else {
+ fle_d(t0, cmp2, cmp1);
+ }
+ czero_nez(dst, dst, t0);
+ czero_eqz(t0 , src, t0);
+ orr(dst, dst, t0);
+ return;
+ }
+ Label no_set;
+ if (is_single) {
+ // jump if cmp1 < cmp2 or either is NaN
+ // fallthrough (i.e. move src to dst) if cmp1 >= cmp2
+ float_blt(cmp1, cmp2, no_set, false, true);
+ } else {
+ double_blt(cmp1, cmp2, no_set, false, true);
+ }
+ mv(dst, src);
+ bind(no_set);
+}
+
void MacroAssembler::cmov_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) {
if (UseZicond) {
if (is_single) {
@@ -1382,7 +1392,7 @@ void MacroAssembler::cmov_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, Regi
Label no_set;
if (is_single) {
// jump if cmp1 >= cmp2
- // not jump (i.e. move src to dst) if cmp1 < cmp2 or either is NaN
+ // fallthrough (i.e. move src to dst) if cmp1 < cmp2 or either is NaN
float_bge(cmp1, cmp2, no_set);
} else {
double_bge(cmp1, cmp2, no_set);
@@ -1391,6 +1401,30 @@ void MacroAssembler::cmov_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, Regi
bind(no_set);
}
+void MacroAssembler::cmov_cmp_fp_gt(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) {
+ if (UseZicond) {
+ if (is_single) {
+ flt_s(t0, cmp2, cmp1);
+ } else {
+ flt_d(t0, cmp2, cmp1);
+ }
+ czero_nez(dst, dst, t0);
+ czero_eqz(t0 , src, t0);
+ orr(dst, dst, t0);
+ return;
+ }
+ Label no_set;
+ if (is_single) {
+ // jump if cmp1 <= cmp2 or either is NaN
+ // fallthrough (i.e. move src to dst) if cmp1 > cmp2
+ float_ble(cmp1, cmp2, no_set, false, true);
+ } else {
+ double_ble(cmp1, cmp2, no_set, false, true);
+ }
+ mv(dst, src);
+ bind(no_set);
+}
+
// Float compare branch instructions
#define INSN(NAME, FLOATCMP, BRANCH) \
@@ -3739,11 +3773,8 @@ void MacroAssembler::check_klass_subtype(Register sub_klass,
bind(L_failure);
}
-void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool acquire, bool in_nmethod, Register tmp_reg) {
+void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp_reg) {
ld(tmp_reg, Address(xthread, JavaThread::polling_word_offset()));
- if (acquire) {
- membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore);
- }
if (at_return) {
bgtu(in_nmethod ? sp : fp, tmp_reg, slow_path, /* is_far */ true);
} else {
@@ -5310,42 +5341,6 @@ void MacroAssembler::add2_with_carry(Register final_dest_hi, Register dest_hi, R
add(final_dest_hi, dest_hi, carry);
}
-/**
- * Multiply 32 bit by 32 bit first loop.
- */
-void MacroAssembler::multiply_32_x_32_loop(Register x, Register xstart, Register x_xstart,
- Register y, Register y_idx, Register z,
- Register carry, Register product,
- Register idx, Register kdx) {
- // jlong carry, x[], y[], z[];
- // for (int idx=ystart, kdx=ystart+1+xstart; idx >= 0; idx--, kdx--) {
- // long product = y[idx] * x[xstart] + carry;
- // z[kdx] = (int)product;
- // carry = product >>> 32;
- // }
- // z[xstart] = (int)carry;
-
- Label L_first_loop, L_first_loop_exit;
- blez(idx, L_first_loop_exit);
-
- shadd(t0, xstart, x, t0, LogBytesPerInt);
- lwu(x_xstart, Address(t0, 0));
-
- bind(L_first_loop);
- subiw(idx, idx, 1);
- shadd(t0, idx, y, t0, LogBytesPerInt);
- lwu(y_idx, Address(t0, 0));
- mul(product, x_xstart, y_idx);
- add(product, product, carry);
- srli(carry, product, 32);
- subiw(kdx, kdx, 1);
- shadd(t0, kdx, z, t0, LogBytesPerInt);
- sw(product, Address(t0, 0));
- bgtz(idx, L_first_loop);
-
- bind(L_first_loop_exit);
-}
-
/**
* Multiply 64 bit by 64 bit first loop.
*/
@@ -5562,77 +5557,16 @@ void MacroAssembler::multiply_to_len(Register x, Register xlen, Register y, Regi
const Register carry = tmp5;
const Register product = xlen;
const Register x_xstart = tmp0;
+ const Register jdx = tmp1;
mv(idx, ylen); // idx = ylen;
addw(kdx, xlen, ylen); // kdx = xlen+ylen;
mv(carry, zr); // carry = 0;
- Label L_multiply_64_x_64_loop, L_done;
-
+ Label L_done;
subiw(xstart, xlen, 1);
bltz(xstart, L_done);
- const Register jdx = tmp1;
-
- if (AvoidUnalignedAccesses) {
- int base_offset = arrayOopDesc::base_offset_in_bytes(T_INT);
- assert((base_offset % (UseCompactObjectHeaders ? 4 :
- (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be");
-
- if ((base_offset % 8) == 0) {
- // multiply_64_x_64_loop emits 8-byte load/store to access two elements
- // at a time from int arrays x and y. When base_offset is 8 bytes, these
- // accesses are naturally aligned if both xlen and ylen are even numbers.
- orr(t0, xlen, ylen);
- test_bit(t0, t0, 0);
- beqz(t0, L_multiply_64_x_64_loop);
- }
-
- Label L_second_loop_unaligned, L_third_loop, L_third_loop_exit;
-
- multiply_32_x_32_loop(x, xstart, x_xstart, y, y_idx, z, carry, product, idx, kdx);
- shadd(t0, xstart, z, t0, LogBytesPerInt);
- sw(carry, Address(t0, 0));
-
- bind(L_second_loop_unaligned);
- mv(carry, zr);
- mv(jdx, ylen);
- subiw(xstart, xstart, 1);
- bltz(xstart, L_done);
-
- subi(sp, sp, 2 * wordSize);
- sd(z, Address(sp, 0));
- sd(zr, Address(sp, wordSize));
- shadd(t0, xstart, z, t0, LogBytesPerInt);
- addi(z, t0, 4);
- shadd(t0, xstart, x, t0, LogBytesPerInt);
- lwu(product, Address(t0, 0));
-
- blez(jdx, L_third_loop_exit);
-
- bind(L_third_loop);
- subiw(jdx, jdx, 1);
- shadd(t0, jdx, y, t0, LogBytesPerInt);
- lwu(t0, Address(t0, 0));
- mul(t1, t0, product);
- add(t0, t1, carry);
- shadd(tmp6, jdx, z, t1, LogBytesPerInt);
- lwu(t1, Address(tmp6, 0));
- add(t0, t0, t1);
- sw(t0, Address(tmp6, 0));
- srli(carry, t0, 32);
- bgtz(jdx, L_third_loop);
-
- bind(L_third_loop_exit);
- ld(z, Address(sp, 0));
- addi(sp, sp, 2 * wordSize);
- shadd(t0, xstart, z, t0, LogBytesPerInt);
- sw(carry, Address(t0, 0));
-
- j(L_second_loop_unaligned);
- }
-
- bind(L_multiply_64_x_64_loop);
multiply_64_x_64_loop(x, xstart, x_xstart, y, y_idx, z, carry, product, idx, kdx);
Label L_second_loop_aligned;
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
index 7fa7f931044..8968f3858af 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
@@ -44,7 +44,7 @@ class MacroAssembler: public Assembler {
MacroAssembler(CodeBuffer* code) : Assembler(code) {}
- void safepoint_poll(Label& slow_path, bool at_return, bool acquire, bool in_nmethod, Register tmp_reg = t0);
+ void safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp_reg = t0);
// Alignment
int align(int modulus, int extra_offset = 0);
@@ -660,7 +660,9 @@ class MacroAssembler: public Assembler {
void cmov_cmp_fp_eq(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single);
void cmov_cmp_fp_ne(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single);
void cmov_cmp_fp_le(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single);
+ void cmov_cmp_fp_ge(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single);
void cmov_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single);
+ void cmov_cmp_fp_gt(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single);
public:
// We try to follow risc-v asm menomics.
@@ -1382,10 +1384,6 @@ class MacroAssembler: public Assembler {
void adc(Register dst, Register src1, Register src2, Register carry);
void add2_with_carry(Register final_dest_hi, Register dest_hi, Register dest_lo,
Register src1, Register src2, Register carry);
- void multiply_32_x_32_loop(Register x, Register xstart, Register x_xstart,
- Register y, Register y_idx, Register z,
- Register carry, Register product,
- Register idx, Register kdx);
void multiply_64_x_64_loop(Register x, Register xstart, Register x_xstart,
Register y, Register y_idx, Register z,
Register carry, Register product,
diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
index 31947b520d0..3834a0d3d3d 100644
--- a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
+++ b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
@@ -356,18 +356,6 @@ void NativeMovRegMem::verify() {
void NativeJump::verify() { }
-
-void NativeJump::check_verified_entry_alignment(address entry, address verified_entry) {
- // Patching to not_entrant can happen while activations of the method are
- // in use. The patching in that instance must happen only when certain
- // alignment restrictions are true. These guarantees check those
- // conditions.
-
- // Must be 4 bytes aligned
- MacroAssembler::assert_alignment(verified_entry);
-}
-
-
address NativeJump::jump_destination() const {
address dest = MacroAssembler::target_addr_for_insn(instruction_address());
@@ -420,12 +408,6 @@ bool NativeInstruction::is_safepoint_poll() {
return MacroAssembler::is_lwu_to_zr(address(this));
}
-// A 16-bit instruction with all bits ones is permanently reserved as an illegal instruction.
-bool NativeInstruction::is_sigill_not_entrant() {
- // jvmci
- return uint_at(0) == 0xffffffff;
-}
-
void NativeIllegalInstruction::insert(address code_pos) {
assert_cond(code_pos != nullptr);
Assembler::sd_instr(code_pos, 0xffffffff); // all bits ones is permanently reserved as an illegal instruction
@@ -437,45 +419,6 @@ bool NativeInstruction::is_stop() {
//-------------------------------------------------------------------
-// MT-safe inserting of a jump over a jump or a nop (used by
-// nmethod::make_not_entrant)
-
-void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) {
-
- assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "expected fixed destination of patch");
-
- assert(nativeInstruction_at(verified_entry)->is_jump_or_nop() ||
- nativeInstruction_at(verified_entry)->is_sigill_not_entrant(),
- "riscv cannot replace non-jump with jump");
-
- check_verified_entry_alignment(entry, verified_entry);
-
- // Patch this nmethod atomically.
- if (Assembler::reachable_from_branch_at(verified_entry, dest)) {
- ptrdiff_t offset = dest - verified_entry;
- guarantee(Assembler::is_simm21(offset) && ((offset % 2) == 0),
- "offset is too large to be patched in one jal instruction."); // 1M
-
- uint32_t insn = 0;
- address pInsn = (address)&insn;
- Assembler::patch(pInsn, 31, 31, (offset >> 20) & 0x1);
- Assembler::patch(pInsn, 30, 21, (offset >> 1) & 0x3ff);
- Assembler::patch(pInsn, 20, 20, (offset >> 11) & 0x1);
- Assembler::patch(pInsn, 19, 12, (offset >> 12) & 0xff);
- Assembler::patch(pInsn, 11, 7, 0); // zero, no link jump
- Assembler::patch(pInsn, 6, 0, 0b1101111); // j, (jal x0 offset)
- Assembler::sd_instr(verified_entry, insn);
- } else {
- // We use an illegal instruction for marking a method as
- // not_entrant.
- NativeIllegalInstruction::insert(verified_entry);
- }
-
- ICache::invalidate_range(verified_entry, instruction_size);
-}
-
-//-------------------------------------------------------------------
-
void NativeGeneralJump::insert_unconditional(address code_pos, address entry) {
CodeBuffer cb(code_pos, instruction_size);
MacroAssembler a(&cb);
diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp
index d8f5fa57816..1598dfb8398 100644
--- a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp
+++ b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2018, Red Hat Inc. All rights reserved.
* Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -74,7 +74,6 @@ class NativeInstruction {
bool is_nop() const;
bool is_jump_or_nop();
bool is_safepoint_poll();
- bool is_sigill_not_entrant();
bool is_stop();
protected:
@@ -274,9 +273,6 @@ class NativeJump: public NativeInstruction {
// Insertion of native jump instruction
static void insert(address code_pos, address entry);
- // MT-safe insertion of native jump at verified method entry
- static void check_verified_entry_alignment(address entry, address verified_entry);
- static void patch_verified_entry(address entry, address verified_entry, address dest);
};
inline NativeJump* nativeJump_at(address addr) {
diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad
index 0d44acc803f..716ffae27b5 100644
--- a/src/hotspot/cpu/riscv/riscv.ad
+++ b/src/hotspot/cpu/riscv/riscv.ad
@@ -1368,14 +1368,6 @@ void MachPrologNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const {
// n.b. frame size includes space for return pc and fp
const int framesize = C->output()->frame_size_in_bytes();
- // insert a nop at the start of the prolog so we can patch in a
- // branch if we need to invalidate the method later
- {
- Assembler::IncompressibleScope scope(masm); // keep the nop as 4 bytes for patching.
- MacroAssembler::assert_alignment(__ pc());
- __ nop(); // 4 bytes
- }
-
assert_cond(C != nullptr);
if (C->clinit_barrier_on_entry()) {
@@ -1493,7 +1485,7 @@ void MachEpilogNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const {
code_stub = &stub->entry();
}
__ relocate(relocInfo::poll_return_type);
- __ safepoint_poll(*code_stub, true /* at_return */, false /* acquire */, true /* in_nmethod */);
+ __ safepoint_poll(*code_stub, true /* at_return */, true /* in_nmethod */);
}
}
@@ -1804,7 +1796,6 @@ void MachUEPNode::emit(C2_MacroAssembler* masm, PhaseRegAlloc* ra_) const
// This is the unverified entry point.
__ ic_check(CodeEntryAlignment);
- // Verified entry point must be properly 4 bytes aligned for patching by NativeJump::patch_verified_entry().
// ic_check() aligns to CodeEntryAlignment >= InteriorEntryAlignment(min 16) > NativeInstruction::instruction_size(4).
assert(((__ offset()) % CodeEntryAlignment) == 0, "Misaligned verified entry point");
}
@@ -8199,7 +8190,7 @@ instruct unnecessary_membar_volatile_rvtso() %{
ins_cost(0);
size(0);
-
+
format %{ "#@unnecessary_membar_volatile_rvtso (unnecessary so empty encoding)" %}
ins_encode %{
__ block_comment("unnecessary_membar_volatile_rvtso");
diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
index 391be81c1ae..216323c55fc 100644
--- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
+++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
@@ -1777,15 +1777,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
// check for safepoint operation in progress and/or pending suspend requests
{
- // We need an acquire here to ensure that any subsequent load of the
- // global SafepointSynchronize::_state flag is ordered after this load
- // of the thread-local polling word. We don't want this poll to
- // return false (i.e. not safepointing) and a later poll of the global
- // SafepointSynchronize::_state spuriously to return true.
- // This is to avoid a race when we're in a native->Java transition
- // racing the code which wakes up from a safepoint.
-
- __ safepoint_poll(safepoint_in_progress, true /* at_return */, true /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(safepoint_in_progress, true /* at_return */, false /* in_nmethod */);
__ lwu(t0, Address(xthread, JavaThread::suspend_flags_offset()));
__ bnez(t0, safepoint_in_progress);
__ bind(safepoint_in_progress_done);
diff --git a/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp b/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp
index 4905566c233..fe7f52884fa 100644
--- a/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp
+++ b/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp
@@ -26,6 +26,13 @@
#ifndef CPU_RISCV_STUBDECLARATIONS_HPP
#define CPU_RISCV_STUBDECLARATIONS_HPP
+#define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \
+ do_arch_blob, \
+ do_arch_entry, \
+ do_arch_entry_init) \
+ do_arch_blob(preuniverse, 0) \
+
+
#define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \
do_arch_blob, \
do_arch_entry, \
diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp
index c58f6bc338d..f7d13578caf 100644
--- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp
+++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp
@@ -6660,6 +6660,10 @@ static const int64_t right_3_bits = right_n_bits(3);
#undef __
// Initialization
+ void generate_preuniverse_stubs() {
+ // preuniverse stubs are not needed for riscv
+ }
+
void generate_initial_stubs() {
// Generate initial stubs and initializes the entry points
@@ -6815,6 +6819,9 @@ static const int64_t right_3_bits = right_n_bits(3);
public:
StubGenerator(CodeBuffer* code, StubGenBlobId blob_id) : StubCodeGenerator(code, blob_id) {
switch(blob_id) {
+ case preuniverse_id:
+ generate_preuniverse_stubs();
+ break;
case initial_id:
generate_initial_stubs();
break;
diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp
index b8de3547c83..21164107053 100644
--- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp
+++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp
@@ -1229,15 +1229,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
{
Label L, Continue;
- // We need an acquire here to ensure that any subsequent load of the
- // global SafepointSynchronize::_state flag is ordered after this load
- // of the thread-local polling word. We don't want this poll to
- // return false (i.e. not safepointing) and a later poll of the global
- // SafepointSynchronize::_state spuriously to return true.
- //
- // This is to avoid a race when we're in a native->Java transition
- // racing the code which wakes up from a safepoint.
- __ safepoint_poll(L, true /* at_return */, true /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(L, true /* at_return */, false /* in_nmethod */);
__ lwu(t1, Address(xthread, JavaThread::suspend_flags_offset()));
__ beqz(t1, Continue);
__ bind(L);
@@ -1388,7 +1380,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
Label slow_path;
Label fast_path;
- __ safepoint_poll(slow_path, true /* at_return */, false /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(slow_path, true /* at_return */, false /* in_nmethod */);
__ j(fast_path);
__ bind(slow_path);
diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp
index f6bf1e79f92..1011b8f1e7b 100644
--- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp
+++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp
@@ -1608,7 +1608,7 @@ void TemplateTable::float_cmp(bool is_float, int unordered_result) {
}
void TemplateTable::branch(bool is_jsr, bool is_wide) {
- __ profile_taken_branch(x10, x11);
+ __ profile_taken_branch(x10);
const ByteSize be_offset = MethodCounters::backedge_counter_offset() +
InvocationCounter::counter_offset();
const ByteSize inv_offset = MethodCounters::invocation_counter_offset() +
@@ -1657,7 +1657,6 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) {
if (UseLoopCounter) {
// increment backedge counter for backward branches
// x10: MDO
- // x11: MDO bumped taken-count
// x12: target offset
__ bgtz(x12, dispatch); // count only if backward branch
@@ -1666,12 +1665,10 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) {
__ ld(t0, Address(xmethod, Method::method_counters_offset()));
__ bnez(t0, has_counters);
__ push_reg(x10);
- __ push_reg(x11);
__ push_reg(x12);
__ call_VM(noreg, CAST_FROM_FN_PTR(address,
InterpreterRuntime::build_method_counters), xmethod);
__ pop_reg(x12);
- __ pop_reg(x11);
__ pop_reg(x10);
__ ld(t0, Address(xmethod, Method::method_counters_offset()));
__ beqz(t0, dispatch); // No MethodCounters allocated, OutOfMemory
diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp
index eca1bb83ab6..46324815001 100644
--- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp
+++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp
@@ -203,15 +203,15 @@ void VM_Version::common_initialize() {
}
}
- // Misc Intrinsics could depend on RVV
+ // Misc Intrinsics that could depend on RVV.
- if (UseZba || UseRVV) {
+ if (!AvoidUnalignedAccesses && (UseZba || UseRVV)) {
if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
FLAG_SET_DEFAULT(UseCRC32Intrinsics, true);
}
} else {
if (!FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
- warning("CRC32 intrinsic requires Zba or RVV instructions (not available on this CPU)");
+ warning("CRC32 intrinsic are not available on this CPU.");
}
FLAG_SET_DEFAULT(UseCRC32Intrinsics, false);
}
@@ -325,20 +325,40 @@ void VM_Version::c2_initialize() {
FLAG_SET_DEFAULT(UseMulAddIntrinsic, true);
}
- if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) {
- FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true);
+ if (!AvoidUnalignedAccesses) {
+ if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) {
+ FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true);
+ }
+ } else if (UseMultiplyToLenIntrinsic) {
+ warning("Intrinsics for BigInteger.multiplyToLen() not available on this CPU.");
+ FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, false);
}
- if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) {
- FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, true);
+ if (!AvoidUnalignedAccesses) {
+ if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) {
+ FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, true);
+ }
+ } else if (UseSquareToLenIntrinsic) {
+ warning("Intrinsics for BigInteger.squareToLen() not available on this CPU.");
+ FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, false);
}
- if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) {
- FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, true);
+ if (!AvoidUnalignedAccesses) {
+ if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) {
+ FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, true);
+ }
+ } else if (UseMontgomeryMultiplyIntrinsic) {
+ warning("Intrinsics for BigInteger.montgomeryMultiply() not available on this CPU.");
+ FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, false);
}
- if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) {
- FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, true);
+ if (!AvoidUnalignedAccesses) {
+ if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) {
+ FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, true);
+ }
+ } else if (UseMontgomerySquareIntrinsic) {
+ warning("Intrinsics for BigInteger.montgomerySquare() not available on this CPU.");
+ FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, false);
}
// Adler32
diff --git a/src/hotspot/cpu/s390/c1_globals_s390.hpp b/src/hotspot/cpu/s390/c1_globals_s390.hpp
index 130a53ad084..1b2b698a737 100644
--- a/src/hotspot/cpu/s390/c1_globals_s390.hpp
+++ b/src/hotspot/cpu/s390/c1_globals_s390.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -44,17 +44,17 @@ define_pd_global(intx, CompileThreshold, 1000);
define_pd_global(intx, OnStackReplacePercentage, 1400);
define_pd_global(bool, ProfileInterpreter, false);
-define_pd_global(uintx, ReservedCodeCacheSize, 32*M);
-define_pd_global(uintx, NonProfiledCodeHeapSize, 13*M);
-define_pd_global(uintx, ProfiledCodeHeapSize, 14*M);
-define_pd_global(uintx, NonNMethodCodeHeapSize, 5*M);
-define_pd_global(uintx, CodeCacheExpansionSize, 32*K);
-define_pd_global(uintx, CodeCacheMinBlockLength, 1);
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K);
+define_pd_global(size_t, ReservedCodeCacheSize, 32*M);
+define_pd_global(size_t, NonProfiledCodeHeapSize, 13*M);
+define_pd_global(size_t, ProfiledCodeHeapSize, 14*M);
+define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M);
+define_pd_global(size_t, CodeCacheExpansionSize, 32*K);
+define_pd_global(size_t, CodeCacheMinBlockLength, 1);
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
define_pd_global(bool, NeverActAsServerClassMachine, true);
define_pd_global(size_t, NewSizeThreadIncrease, 16*K);
define_pd_global(uint64_t, MaxRAM, 1ULL*G);
-define_pd_global(uintx, InitialCodeCacheSize, 160*K);
+define_pd_global(size_t, InitialCodeCacheSize, 160*K);
#endif // !COMPILER2
define_pd_global(bool, UseTypeProfile, false);
diff --git a/src/hotspot/cpu/s390/c2_globals_s390.hpp b/src/hotspot/cpu/s390/c2_globals_s390.hpp
index b8b45cbc80e..94190c25f5b 100644
--- a/src/hotspot/cpu/s390/c2_globals_s390.hpp
+++ b/src/hotspot/cpu/s390/c2_globals_s390.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -67,17 +67,17 @@ define_pd_global(uint, SuperWordStoreToLoadForwardingFailureDetection, 16);
define_pd_global(bool, IdealizeClearArrayNode, false);
// InitialCodeCacheSize derived from specjbb2000 run.
-define_pd_global(uintx, InitialCodeCacheSize, 2048*K); // Integral multiple of CodeCacheExpansionSize
-define_pd_global(uintx, ReservedCodeCacheSize, 48*M);
-define_pd_global(uintx, NonProfiledCodeHeapSize, 21*M);
-define_pd_global(uintx, ProfiledCodeHeapSize, 22*M);
-define_pd_global(uintx, NonNMethodCodeHeapSize, 5*M);
-define_pd_global(uintx, CodeCacheExpansionSize, 64*K);
+define_pd_global(size_t, InitialCodeCacheSize, 2048*K); // Integral multiple of CodeCacheExpansionSize
+define_pd_global(size_t, ReservedCodeCacheSize, 48*M);
+define_pd_global(size_t, NonProfiledCodeHeapSize, 21*M);
+define_pd_global(size_t, ProfiledCodeHeapSize, 22*M);
+define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M);
+define_pd_global(size_t, CodeCacheExpansionSize, 64*K);
// Ergonomics related flags
define_pd_global(uint64_t, MaxRAM, 128ULL*G);
-define_pd_global(uintx, CodeCacheMinBlockLength, 4);
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K);
+define_pd_global(size_t, CodeCacheMinBlockLength, 4);
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on z/Architecture.
diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp
index e78906708af..2d663061aec 100644
--- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp
+++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp
@@ -180,7 +180,7 @@ void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) {
__ z_lg(Z_R0_scratch, in_bytes(bs_nm->thread_disarmed_guard_value_offset()), Z_thread); // 6 bytes
// Compare to current patched value:
- __ z_cfi(Z_R0_scratch, /* to be patched */ -1); // 6 bytes (2 + 4 byte imm val)
+ __ z_cfi(Z_R0_scratch, /* to be patched */ 0); // 6 bytes (2 + 4 byte imm val)
// Conditional Jump
__ z_larl(Z_R14, (Assembler::instr_len((unsigned long)LARL_ZOPC) + Assembler::instr_len((unsigned long)BCR_ZOPC)) / 2); // 6 bytes
diff --git a/src/hotspot/cpu/s390/globals_s390.hpp b/src/hotspot/cpu/s390/globals_s390.hpp
index cf4be20397c..07987ea3469 100644
--- a/src/hotspot/cpu/s390/globals_s390.hpp
+++ b/src/hotspot/cpu/s390/globals_s390.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -38,7 +38,7 @@ define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap nu
define_pd_global(bool, DelayCompilerStubsGeneration, COMPILER2_OR_JVMCI);
-define_pd_global(uintx, CodeCacheSegmentSize, 256);
+define_pd_global(size_t, CodeCacheSegmentSize, 256);
// This shall be at least 32 for proper branch target alignment.
// Ideally, this is 256 (cache line size). This keeps code end data
// on separate lines. But we reduced it to 64 since 256 increased
diff --git a/src/hotspot/cpu/s390/nativeInst_s390.cpp b/src/hotspot/cpu/s390/nativeInst_s390.cpp
index 9990c225a89..546f8b13397 100644
--- a/src/hotspot/cpu/s390/nativeInst_s390.cpp
+++ b/src/hotspot/cpu/s390/nativeInst_s390.cpp
@@ -167,27 +167,6 @@ bool NativeInstruction::is_illegal() {
return halfword_at(-2) == illegal_instruction();
}
-// We use an illtrap for marking a method as not_entrant.
-bool NativeInstruction::is_sigill_not_entrant() {
- if (!is_illegal()) return false; // Just a quick path.
-
- // One-sided error of is_illegal tolerable here
- // (see implementation of is_illegal() for details).
-
- CodeBlob* cb = CodeCache::find_blob(addr_at(0));
- if (cb == nullptr || !cb->is_nmethod()) {
- return false;
- }
-
- nmethod *nm = (nmethod *)cb;
- // This method is not_entrant if the illtrap instruction
- // is located at the verified entry point.
- // BE AWARE: the current pc (this) points to the instruction after the
- // "illtrap" location.
- address sig_addr = ((address) this) - 2;
- return nm->verified_entry_point() == sig_addr;
-}
-
bool NativeInstruction::is_jump() {
unsigned long inst;
Assembler::get_instruction((address)this, &inst);
@@ -620,19 +599,6 @@ void NativeJump::verify() {
fatal("this is not a `NativeJump' site");
}
-// Patch atomically with an illtrap.
-void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) {
- ResourceMark rm;
- int code_size = 2;
- CodeBuffer cb(verified_entry, code_size + 1);
- MacroAssembler* a = new MacroAssembler(&cb);
-#ifdef COMPILER2
- assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "expected fixed destination of patch");
-#endif
- a->z_illtrap();
- ICache::invalidate_range(verified_entry, code_size);
-}
-
#undef LUCY_DBG
//-------------------------------------
diff --git a/src/hotspot/cpu/s390/nativeInst_s390.hpp b/src/hotspot/cpu/s390/nativeInst_s390.hpp
index fcae833769f..16400df3f26 100644
--- a/src/hotspot/cpu/s390/nativeInst_s390.hpp
+++ b/src/hotspot/cpu/s390/nativeInst_s390.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2024 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -85,9 +85,6 @@ class NativeInstruction {
// Bcrl is currently the only accepted instruction here.
bool is_jump();
- // We use an illtrap for marking a method as not_entrant.
- bool is_sigill_not_entrant();
-
bool is_safepoint_poll() {
// Is the current instruction a POTENTIAL read access to the polling page?
// The instruction's current arguments are not checked!
@@ -609,11 +606,6 @@ class NativeJump: public NativeInstruction {
// Insertion of native jump instruction.
static void insert(address code_pos, address entry);
-
- // MT-safe insertion of native jump at verified method entry.
- static void check_verified_entry_alignment(address entry, address verified_entry) { }
-
- static void patch_verified_entry(address entry, address verified_entry, address dest);
};
//-------------------------------------
diff --git a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp
index f382a319c48..c3ad3cefeb9 100644
--- a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp
+++ b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp
@@ -26,6 +26,13 @@
#ifndef CPU_S390_STUBDECLARATIONS_HPP
#define CPU_S390_STUBDECLARATIONS_HPP
+#define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \
+ do_arch_blob, \
+ do_arch_entry, \
+ do_arch_entry_init) \
+ do_arch_blob(preuniverse, 0) \
+
+
#define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \
do_arch_blob, \
do_arch_entry, \
diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp
index d3f6540a3ea..45a9e02ac27 100644
--- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp
+++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp
@@ -3197,7 +3197,7 @@ class StubGenerator: public StubCodeGenerator {
// VM-Call: BarrierSetNMethod::nmethod_stub_entry_barrier(address* return_address_ptr)
__ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetNMethod::nmethod_stub_entry_barrier));
- __ z_ltr(Z_R0_scratch, Z_RET);
+ __ z_ltr(Z_RET, Z_RET);
// VM-Call Epilogue
__ restore_volatile_regs(Z_SP, frame::z_abi_160_size, true, false);
@@ -3283,6 +3283,10 @@ class StubGenerator: public StubCodeGenerator {
return start;
}
+ void generate_preuniverse_stubs() {
+ // preuniverse stubs are not needed for s390
+ }
+
void generate_initial_stubs() {
// Generates all stubs and initializes the entry points.
@@ -3418,6 +3422,9 @@ class StubGenerator: public StubCodeGenerator {
public:
StubGenerator(CodeBuffer* code, StubGenBlobId blob_id) : StubCodeGenerator(code, blob_id) {
switch(blob_id) {
+ case preuniverse_id:
+ generate_preuniverse_stubs();
+ break;
case initial_id:
generate_initial_stubs();
break;
diff --git a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp
index 684347e35fa..cde429b0383 100644
--- a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp
@@ -325,16 +325,6 @@ void C1_MacroAssembler::remove_frame(int frame_size_in_bytes) {
void C1_MacroAssembler::verified_entry(bool breakAtEntry) {
- if (breakAtEntry) {
- // Verified Entry first instruction should be 5 bytes long for correct
- // patching by patch_verified_entry().
- //
- // Breakpoint has one byte first instruction.
- // Also first instruction will be one byte "push(rbp)" if stack banging
- // code is not generated (see build_frame() above).
- // For all these cases generate long instruction first.
- fat_nop();
- }
if (breakAtEntry) int3();
// build frame
}
diff --git a/src/hotspot/cpu/x86/c1_globals_x86.hpp b/src/hotspot/cpu/x86/c1_globals_x86.hpp
index b2085a0b4b3..be5c443a695 100644
--- a/src/hotspot/cpu/x86/c1_globals_x86.hpp
+++ b/src/hotspot/cpu/x86/c1_globals_x86.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,15 +42,15 @@ define_pd_global(intx, CompileThreshold, 1500 );
define_pd_global(intx, OnStackReplacePercentage, 933 );
define_pd_global(size_t, NewSizeThreadIncrease, 4*K );
-define_pd_global(uintx, InitialCodeCacheSize, 160*K);
-define_pd_global(uintx, ReservedCodeCacheSize, 32*M );
-define_pd_global(uintx, NonProfiledCodeHeapSize, 13*M );
-define_pd_global(uintx, ProfiledCodeHeapSize, 14*M );
-define_pd_global(uintx, NonNMethodCodeHeapSize, 5*M );
+define_pd_global(size_t, InitialCodeCacheSize, 160*K);
+define_pd_global(size_t, ReservedCodeCacheSize, 32*M );
+define_pd_global(size_t, NonProfiledCodeHeapSize, 13*M );
+define_pd_global(size_t, ProfiledCodeHeapSize, 14*M );
+define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M );
define_pd_global(bool, ProfileInterpreter, false);
-define_pd_global(uintx, CodeCacheExpansionSize, 32*K );
-define_pd_global(uintx, CodeCacheMinBlockLength, 1 );
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K);
+define_pd_global(size_t, CodeCacheExpansionSize, 32*K );
+define_pd_global(size_t, CodeCacheMinBlockLength, 1 );
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
define_pd_global(bool, NeverActAsServerClassMachine, true );
define_pd_global(uint64_t, MaxRAM, 1ULL*G);
define_pd_global(bool, CICompileOSR, true );
diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
index 177be6e59f7..d9a9ef0de3b 100644
--- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
@@ -50,13 +50,6 @@
// C2 compiled method's prolog code.
void C2_MacroAssembler::verified_entry(int framesize, int stack_bang_size, bool fp_mode_24b, bool is_stub) {
-
- // WARNING: Initial instruction MUST be 5 bytes or longer so that
- // NativeJump::patch_verified_entry will be able to patch out the entry
- // code safely. The push to verify stack depth is ok at 5 bytes,
- // the frame allocation can be either 3 or 6 bytes. So if we don't do
- // stack bang then we must use the 6 byte frame allocation even if
- // we have no frame. :-(
assert(stack_bang_size >= framesize || stack_bang_size <= 0, "stack bang size incorrect");
assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned");
@@ -87,8 +80,7 @@ void C2_MacroAssembler::verified_entry(int framesize, int stack_bang_size, bool
subptr(rsp, framesize);
}
} else {
- // Create frame (force generation of a 4 byte immediate value)
- subptr_imm32(rsp, framesize);
+ subptr(rsp, framesize);
// Save RBP register now.
framesize -= wordSize;
@@ -4655,6 +4647,7 @@ static void convertF2I_slowpath(C2_MacroAssembler& masm, C2GeneralStub(dst, src, slowpath_target, 23, convertF2I_slowpath);
+ // Using the APX extended general purpose registers increases the instruction encoding size by 1 byte.
+ int max_size = 23 + (UseAPX ? 1 : 0);
+ auto stub = C2CodeStub::make(dst, src, slowpath_target, max_size, convertF2I_slowpath);
jcc(Assembler::equal, stub->entry());
bind(stub->continuation());
}
diff --git a/src/hotspot/cpu/x86/c2_globals_x86.hpp b/src/hotspot/cpu/x86/c2_globals_x86.hpp
index 64dda0a8947..a25f5da5e56 100644
--- a/src/hotspot/cpu/x86/c2_globals_x86.hpp
+++ b/src/hotspot/cpu/x86/c2_globals_x86.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -50,8 +50,8 @@ define_pd_global(intx, InteriorEntryAlignment, 16);
define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K));
define_pd_global(intx, LoopUnrollLimit, 60);
// InitialCodeCacheSize derived from specjbb2000 run.
-define_pd_global(uintx, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize
-define_pd_global(uintx, CodeCacheExpansionSize, 64*K);
+define_pd_global(size_t, InitialCodeCacheSize, 2496*K); // Integral multiple of CodeCacheExpansionSize
+define_pd_global(size_t, CodeCacheExpansionSize, 64*K);
// Ergonomics related flags
define_pd_global(uint64_t, MaxRAM, 128ULL*G);
@@ -60,8 +60,8 @@ define_pd_global(intx, InteriorEntryAlignment, 4);
define_pd_global(size_t, NewSizeThreadIncrease, 4*K);
define_pd_global(intx, LoopUnrollLimit, 50); // Design center runs on 1.3.1
// InitialCodeCacheSize derived from specjbb2000 run.
-define_pd_global(uintx, InitialCodeCacheSize, 2304*K); // Integral multiple of CodeCacheExpansionSize
-define_pd_global(uintx, CodeCacheExpansionSize, 32*K);
+define_pd_global(size_t, InitialCodeCacheSize, 2304*K); // Integral multiple of CodeCacheExpansionSize
+define_pd_global(size_t, CodeCacheExpansionSize, 32*K);
// Ergonomics related flags
define_pd_global(uint64_t, MaxRAM, 4ULL*G);
@@ -79,12 +79,12 @@ define_pd_global(bool, SuperWordLoopUnrollAnalysis, true);
define_pd_global(uint, SuperWordStoreToLoadForwardingFailureDetection, 16);
define_pd_global(bool, IdealizeClearArrayNode, true);
-define_pd_global(uintx, ReservedCodeCacheSize, 48*M);
-define_pd_global(uintx, NonProfiledCodeHeapSize, 21*M);
-define_pd_global(uintx, ProfiledCodeHeapSize, 22*M);
-define_pd_global(uintx, NonNMethodCodeHeapSize, 5*M );
-define_pd_global(uintx, CodeCacheMinBlockLength, 6);
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K);
+define_pd_global(size_t, ReservedCodeCacheSize, 48*M);
+define_pd_global(size_t, NonProfiledCodeHeapSize, 21*M);
+define_pd_global(size_t, ProfiledCodeHeapSize, 22*M);
+define_pd_global(size_t, NonNMethodCodeHeapSize, 5*M );
+define_pd_global(size_t, CodeCacheMinBlockLength, 6);
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K);
define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on x86.
diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp
index a1d4a71874f..103e22d0185 100644
--- a/src/hotspot/cpu/x86/globals_x86.hpp
+++ b/src/hotspot/cpu/x86/globals_x86.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,7 +37,7 @@ define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap nulls
define_pd_global(bool, DelayCompilerStubsGeneration, COMPILER2_OR_JVMCI);
-define_pd_global(uintx, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRESENT(+64)); // Tiered compilation has large code-entry alignment.
+define_pd_global(size_t, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRESENT(+64)); // Tiered compilation has large code-entry alignment.
// See 4827828 for this change. There is no globals_core_i486.hpp. I can't
// assign a different value for C2 without touching a number of files. Use
// #ifdef to minimize the change as it's late in Mantis. -- FIXME.
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
index 803bce48945..c401863d7cd 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
@@ -1621,19 +1621,6 @@ void MacroAssembler::post_call_nop() {
emit_int32(0x00);
}
-// A 5 byte nop that is safe for patching (see patch_verified_entry)
-void MacroAssembler::fat_nop() {
- if (UseAddressNop) {
- addr_nop_5();
- } else {
- emit_int8((uint8_t)0x26); // es:
- emit_int8((uint8_t)0x2e); // cs:
- emit_int8((uint8_t)0x64); // fs:
- emit_int8((uint8_t)0x65); // gs:
- emit_int8((uint8_t)0x90);
- }
-}
-
void MacroAssembler::mulpd(XMMRegister dst, AddressLiteral src, Register rscratch) {
assert(rscratch != noreg || always_reachable(src), "missing");
if (reachable(src)) {
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
index f7ac6fb4297..d75c9b624fd 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
@@ -209,8 +209,6 @@ class MacroAssembler: public Assembler {
void align(uint modulus, uint target);
void post_call_nop();
- // A 5 byte nop that is safe for patching (see patch_verified_entry)
- void fat_nop();
// Stack frame creation/removal
void enter();
diff --git a/src/hotspot/cpu/x86/nativeInst_x86.cpp b/src/hotspot/cpu/x86/nativeInst_x86.cpp
index c3345be2172..aad1f77e173 100644
--- a/src/hotspot/cpu/x86/nativeInst_x86.cpp
+++ b/src/hotspot/cpu/x86/nativeInst_x86.cpp
@@ -336,55 +336,6 @@ void NativeJump::insert(address code_pos, address entry) {
ICache::invalidate_range(code_pos, instruction_size);
}
-void NativeJump::check_verified_entry_alignment(address entry, address verified_entry) {
- // Patching to not_entrant can happen while activations of the method are
- // in use. The patching in that instance must happen only when certain
- // alignment restrictions are true. These guarantees check those
- // conditions.
- const int linesize = 64;
-
- // Must be wordSize aligned
- guarantee(((uintptr_t) verified_entry & (wordSize -1)) == 0,
- "illegal address for code patching 2");
- // First 5 bytes must be within the same cache line - 4827828
- guarantee((uintptr_t) verified_entry / linesize ==
- ((uintptr_t) verified_entry + 4) / linesize,
- "illegal address for code patching 3");
-}
-
-
-// MT safe inserting of a jump over an unknown instruction sequence (used by nmethod::make_not_entrant)
-// The problem: jmp is a 5-byte instruction. Atomic write can be only with 4 bytes.
-// First patches the first word atomically to be a jump to itself.
-// Then patches the last byte and then atomically patches the first word (4-bytes),
-// thus inserting the desired jump
-// This code is mt-safe with the following conditions: entry point is 4 byte aligned,
-// entry point is in same cache line as unverified entry point, and the instruction being
-// patched is >= 5 byte (size of patch).
-//
-// In C2 the 5+ byte sized instruction is enforced by code in MachPrologNode::emit.
-// In C1 the restriction is enforced by CodeEmitter::method_entry
-// In JVMCI, the restriction is enforced by HotSpotFrameContext.enter(...)
-//
-void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) {
- // complete jump instruction (to be inserted) is in code_buffer;
- union {
- jlong cb_long;
- unsigned char code_buffer[8];
- } u;
-
- u.cb_long = *(jlong *)verified_entry;
-
- intptr_t disp = (intptr_t)dest - ((intptr_t)verified_entry + 1 + 4);
- guarantee(disp == (intptr_t)(int32_t)disp, "must be 32-bit offset");
-
- u.code_buffer[0] = instruction_code;
- *(int32_t*)(u.code_buffer + 1) = (int32_t)disp;
-
- Atomic::store((jlong *) verified_entry, u.cb_long);
- ICache::invalidate_range(verified_entry, 8);
-}
-
void NativeIllegalInstruction::insert(address code_pos) {
assert(NativeIllegalInstruction::instruction_size == sizeof(short), "right address unit for update");
*(short *)code_pos = instruction_code;
diff --git a/src/hotspot/cpu/x86/nativeInst_x86.hpp b/src/hotspot/cpu/x86/nativeInst_x86.hpp
index b2448cb99fd..3e767006480 100644
--- a/src/hotspot/cpu/x86/nativeInst_x86.hpp
+++ b/src/hotspot/cpu/x86/nativeInst_x86.hpp
@@ -445,9 +445,6 @@ class NativeJump: public NativeInstruction {
// Insertion of native jump instruction
static void insert(address code_pos, address entry);
- // MT-safe insertion of native jump at verified method entry
- static void check_verified_entry_alignment(address entry, address verified_entry);
- static void patch_verified_entry(address entry, address verified_entry, address dest);
};
inline NativeJump* nativeJump_at(address address) {
diff --git a/src/hotspot/cpu/x86/peephole_x86_64.cpp b/src/hotspot/cpu/x86/peephole_x86_64.cpp
index 2197055d1ec..59188962d0c 100644
--- a/src/hotspot/cpu/x86/peephole_x86_64.cpp
+++ b/src/hotspot/cpu/x86/peephole_x86_64.cpp
@@ -24,6 +24,7 @@
#ifdef COMPILER2
+#include "opto/addnode.hpp"
#include "peephole_x86_64.hpp"
#include "adfiles/ad_x86.hpp"
@@ -235,6 +236,143 @@ bool Peephole::test_may_remove(Block* block, int block_index, PhaseCFG* cfg_, Ph
return false;
}
+// This function removes redundant lea instructions that result from chained dereferences that
+// match to leaPCompressedOopOffset, leaP8Narrow, or leaP32Narrow. This happens for ideal graphs
+// of the form LoadN -> DecodeN -> AddP. Matching with any leaP* rule consumes both the AddP and
+// the DecodeN. However, after matching the DecodeN is added back as the base for the leaP*,
+// which is necessary if the oop derived by the leaP* gets added to an OopMap, because OopMaps
+// cannot contain derived oops with narrow oops as a base.
+// This results in the following graph after matching:
+// LoadN
+// | \
+// | decodeHeapOop_not_null
+// | / \
+// leaP* MachProj (leaf)
+// The decode_heap_oop_not_null will emit a lea with an unused result if the derived oop does
+// not end up in an OopMap.
+// This peephole recognizes graphs of the shape as shown above, ensures that the result of the
+// decode is only used by the derived oop and removes that decode if this is the case. Further,
+// multiple leaP*s can have the same decode as their base. This peephole will remove the decode
+// if all leaP*s and the decode share the same parent.
+// Additionally, if the register allocator spills the result of the LoadN we can get such a graph:
+// LoadN
+// |
+// DefinitionSpillCopy
+// / \
+// MemToRegSpillCopy MemToRegSpillCopy
+// | /
+// | decodeHeapOop_not_null
+// | / \
+// leaP* MachProj (leaf)
+// In this case where the common parent of the leaP* and the decode is one MemToRegSpillCopy
+// away, this peephole can also recognize the decode as redundant and also remove the spill copy
+// if that is only used by the decode.
+bool Peephole::lea_remove_redundant(Block* block, int block_index, PhaseCFG* cfg_, PhaseRegAlloc* ra_,
+ MachNode* (*new_root)(), uint inst0_rule) {
+ MachNode* lea_derived_oop = block->get_node(block_index)->as_Mach();
+ assert(lea_derived_oop->rule() == inst0_rule, "sanity");
+ assert(lea_derived_oop->ideal_Opcode() == Op_AddP, "sanity");
+
+ MachNode* decode = lea_derived_oop->in(AddPNode::Base)->isa_Mach();
+ if (decode == nullptr || decode->ideal_Opcode() != Op_DecodeN) {
+ return false;
+ }
+
+ // Check that the lea and the decode live in the same block.
+ if (!block->contains(decode)) {
+ return false;
+ }
+
+ Node* lea_address = lea_derived_oop->in(AddPNode::Address);
+ Node* decode_address = decode->in(1);
+
+ bool is_spill = lea_address != decode_address &&
+ lea_address->is_SpillCopy() &&
+ decode_address->is_SpillCopy();
+
+ // If this is a spill, move lea_address and decode_address one node further up to the
+ // grandparents of lea_derived_oop and decode respectively. This lets us look through
+ // the indirection of the spill.
+ if (is_spill) {
+ decode_address = decode_address->in(1);
+ lea_address = lea_address->in(1);
+ }
+
+ // The leaP* and the decode must have the same parent. If we have a spill, they must have
+ // the same grandparent.
+ if (lea_address != decode_address) {
+ return false;
+ }
+
+ // Ensure the decode only has the leaP*s (with the same (grand)parent) and a MachProj leaf as children.
+ MachProjNode* proj = nullptr;
+ for (DUIterator_Fast imax, i = decode->fast_outs(imax); i < imax; i++) {
+ Node* out = decode->fast_out(i);
+ if (out == lea_derived_oop) {
+ continue;
+ }
+ if (out->is_MachProj() && out->outcnt() == 0) {
+ proj = out->as_MachProj();
+ continue;
+ }
+ if (out->is_Mach()) {
+ MachNode* other_lea = out->as_Mach();
+ if ((other_lea->rule() == leaP32Narrow_rule ||
+ other_lea->rule() == leaP8Narrow_rule ||
+ other_lea->rule() == leaPCompressedOopOffset_rule) &&
+ other_lea->in(AddPNode::Base) == decode &&
+ (is_spill ? other_lea->in(AddPNode::Address)->in(1)
+ : other_lea->in(AddPNode::Address)) == decode_address) {
+ continue;
+ }
+ }
+ // There is other stuff we do not expect...
+ return false;
+ }
+
+ // Ensure the MachProj is in the same block as the decode and the lea.
+ if (proj == nullptr || !block->contains(proj)) {
+ // This should only fail if we are stressing scheduling.
+ assert(StressGCM, "should be scheduled contiguously otherwise");
+ return false;
+ }
+
+ // We now have verified that the decode is redundant and can be removed with a peephole.
+ // Remove the projection
+ block->find_remove(proj);
+ cfg_->map_node_to_block(proj, nullptr);
+
+ // Rewire the base of all leas currently depending on the decode we are removing.
+ for (DUIterator_Fast imax, i = decode->fast_outs(imax); i < imax; i++) {
+ Node* dependant_lea = decode->fast_out(i);
+ if (dependant_lea->is_Mach() && dependant_lea->as_Mach()->ideal_Opcode() == Op_AddP) {
+ dependant_lea->set_req(AddPNode::Base, decode_address);
+ // This deleted something in the out array, hence adjust i, imax.
+ --i;
+ --imax;
+ }
+ }
+
+ // Remove spill for the decode if the spill node does not have any other uses.
+ if (is_spill) {
+ MachNode* decode_spill = decode->in(1)->as_Mach();
+ if (decode_spill->outcnt() == 1 && block->contains(decode_spill)) {
+ decode_spill->set_removed();
+ block->find_remove(decode_spill);
+ cfg_->map_node_to_block(decode_spill, nullptr);
+ decode_spill->del_req(1);
+ }
+ }
+
+ // Remove the decode
+ decode->set_removed();
+ block->find_remove(decode);
+ cfg_->map_node_to_block(decode, nullptr);
+ decode->del_req(1);
+
+ return true;
+}
+
bool Peephole::lea_coalesce_reg(Block* block, int block_index, PhaseCFG* cfg_, PhaseRegAlloc* ra_,
MachNode* (*new_root)(), uint inst0_rule) {
return lea_coalesce_helper(block, block_index, cfg_, ra_, new_root, inst0_rule, false);
diff --git a/src/hotspot/cpu/x86/peephole_x86_64.hpp b/src/hotspot/cpu/x86/peephole_x86_64.hpp
index deb53f0dfd7..954297f555a 100644
--- a/src/hotspot/cpu/x86/peephole_x86_64.hpp
+++ b/src/hotspot/cpu/x86/peephole_x86_64.hpp
@@ -36,6 +36,8 @@ class Peephole {
MachNode* (*new_root)(), uint inst0_rule);
static bool test_may_remove(Block* block, int block_index, PhaseCFG* cfg_, PhaseRegAlloc* ra_,
MachNode* (*new_root)(), uint inst0_rule);
+ static bool lea_remove_redundant(Block* block, int block_index, PhaseCFG* cfg_, PhaseRegAlloc* ra_,
+ MachNode* (*new_root)(), uint inst0_rule);
};
#endif // CPU_X86_PEEPHOLE_X86_64_HPP
diff --git a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp
index dcb919ddcd0..7cbe3829783 100644
--- a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp
+++ b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp
@@ -26,6 +26,13 @@
#ifndef CPU_X86_STUBDECLARATIONS_HPP
#define CPU_X86_STUBDECLARATIONS_HPP
+#define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \
+ do_arch_blob, \
+ do_arch_entry, \
+ do_arch_entry_init) \
+ do_arch_blob(preuniverse, 500) \
+
+
#define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \
do_arch_blob, \
do_arch_entry, \
@@ -239,7 +246,7 @@
do_arch_blob, \
do_arch_entry, \
do_arch_entry_init) \
- do_arch_blob(final, 31000 \
+ do_arch_blob(final, 33000 \
WINDOWS_ONLY(+22000) ZGC_ONLY(+20000)) \
#endif // CPU_X86_STUBDECLARATIONS_HPP
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
index 1014c1c376f..e6dd880527c 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
@@ -4049,6 +4049,11 @@ void StubGenerator::create_control_words() {
}
// Initialization
+void StubGenerator::generate_preuniverse_stubs() {
+ // atomic calls
+ StubRoutines::_fence_entry = generate_orderaccess_fence();
+}
+
void StubGenerator::generate_initial_stubs() {
// Generates all stubs and initializes the entry points
@@ -4074,9 +4079,6 @@ void StubGenerator::generate_initial_stubs() {
// is referenced by megamorphic call
StubRoutines::_catch_exception_entry = generate_catch_exception();
- // atomic calls
- StubRoutines::_fence_entry = generate_orderaccess_fence();
-
// platform dependent
StubRoutines::x86::_get_previous_sp_entry = generate_get_previous_sp();
@@ -4344,6 +4346,9 @@ void StubGenerator::generate_compiler_stubs() {
StubGenerator::StubGenerator(CodeBuffer* code, StubGenBlobId blob_id) : StubCodeGenerator(code, blob_id) {
switch(blob_id) {
+ case preuniverse_id:
+ generate_preuniverse_stubs();
+ break;
case initial_id:
generate_initial_stubs();
break;
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
index 2f1e46f3132..032e05f4b4f 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
@@ -634,6 +634,7 @@ class StubGenerator: public StubCodeGenerator {
void create_control_words();
// Initialization
+ void generate_preuniverse_stubs();
void generate_initial_stubs();
void generate_continuation_stubs();
void generate_compiler_stubs();
diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp
index 9ea4aeeccfa..46af93e9760 100644
--- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp
+++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp
@@ -465,13 +465,19 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M
__ call_VM_leaf0(CAST_FROM_FN_PTR(address, SharedRuntime::dtan));
}
} else if (kind == Interpreter::java_lang_math_tanh) {
- assert(StubRoutines::dtanh() != nullptr, "not initialized");
+ if (StubRoutines::dtanh() != nullptr) {
__ movdbl(xmm0, Address(rsp, wordSize));
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dtanh())));
+ } else {
+ return nullptr; // Fallback to default implementation
+ }
} else if (kind == Interpreter::java_lang_math_cbrt) {
- assert(StubRoutines::dcbrt() != nullptr, "not initialized");
- __ movdbl(xmm0, Address(rsp, wordSize));
- __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dcbrt())));
+ if (StubRoutines::dcbrt() != nullptr) {
+ __ movdbl(xmm0, Address(rsp, wordSize));
+ __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dcbrt())));
+ } else {
+ return nullptr; // Fallback to default implementation
+ }
} else if (kind == Interpreter::java_lang_math_abs) {
assert(StubRoutines::x86::double_sign_mask() != nullptr, "not initialized");
__ movdbl(xmm0, Address(rsp, wordSize));
diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp
index 152866e65f3..e4a101a5977 100644
--- a/src/hotspot/cpu/x86/vm_version_x86.cpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.cpp
@@ -2111,7 +2111,7 @@ bool VM_Version::is_intel_cascade_lake() {
// has improved implementation of 64-byte load/stores and so the default
// threshold is set to 0 for these platforms.
int VM_Version::avx3_threshold() {
- return (is_intel_family_core() &&
+ return (is_intel_server_family() &&
supports_serialize() &&
FLAG_IS_DEFAULT(AVX3Threshold)) ? 0 : AVX3Threshold;
}
diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad
index d17bdf5e2c9..3839813c4e3 100644
--- a/src/hotspot/cpu/x86/x86_64.ad
+++ b/src/hotspot/cpu/x86/x86_64.ad
@@ -10531,7 +10531,8 @@ instruct xorI_rReg_im1_ndd(rRegI dst, rRegI src, immI_M1 imm)
// Xor Register with Immediate
instruct xorI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
%{
- predicate(!UseAPX);
+ // Strict predicate check to make selection of xorI_rReg_im1 cost agnostic if immI src is -1.
+ predicate(!UseAPX && n->in(2)->bottom_type()->is_int()->get_con() != -1);
match(Set dst (XorI dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
@@ -10545,7 +10546,8 @@ instruct xorI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
instruct xorI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr)
%{
- predicate(UseAPX);
+ // Strict predicate check to make selection of xorI_rReg_im1_ndd cost agnostic if immI src2 is -1.
+ predicate(UseAPX && n->in(2)->bottom_type()->is_int()->get_con() != -1);
match(Set dst (XorI src1 src2));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
@@ -10563,6 +10565,7 @@ instruct xorI_rReg_mem_imm_ndd(rRegI dst, memory src1, immI src2, rFlagsReg cr)
predicate(UseAPX);
match(Set dst (XorI (LoadI src1) src2));
effect(KILL cr);
+ ins_cost(150);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "exorl $dst, $src1, $src2\t# int ndd" %}
@@ -11205,7 +11208,8 @@ instruct xorL_rReg_im1_ndd(rRegL dst,rRegL src, immL_M1 imm)
// Xor Register with Immediate
instruct xorL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
%{
- predicate(!UseAPX);
+ // Strict predicate check to make selection of xorL_rReg_im1 cost agnostic if immL32 src is -1.
+ predicate(!UseAPX && n->in(2)->bottom_type()->is_long()->get_con() != -1L);
match(Set dst (XorL dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
@@ -11219,7 +11223,8 @@ instruct xorL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
instruct xorL_rReg_rReg_imm(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr)
%{
- predicate(UseAPX);
+ // Strict predicate check to make selection of xorL_rReg_im1_ndd cost agnostic if immL32 src2 is -1.
+ predicate(UseAPX && n->in(2)->bottom_type()->is_long()->get_con() != -1L);
match(Set dst (XorL src1 src2));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
@@ -11238,6 +11243,7 @@ instruct xorL_rReg_mem_imm(rRegL dst, memory src1, immL32 src2, rFlagsReg cr)
match(Set dst (XorL (LoadL src1) src2));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
+ ins_cost(150);
format %{ "exorq $dst, $src1, $src2\t# long ndd" %}
ins_encode %{
@@ -14587,6 +14593,24 @@ peephole
peepreplace (leaL_rReg_immI2_peep());
%}
+peephole
+%{
+ peepmatch (leaPCompressedOopOffset);
+ peepprocedure (lea_remove_redundant);
+%}
+
+peephole
+%{
+ peepmatch (leaP8Narrow);
+ peepprocedure (lea_remove_redundant);
+%}
+
+peephole
+%{
+ peepmatch (leaP32Narrow);
+ peepprocedure (lea_remove_redundant);
+%}
+
// These peephole rules matches instructions which set flags and are followed by a testI/L_reg
// The test instruction is redudanent in case the downstream instuctions (like JCC or CMOV) only use flags that are already set by the previous instruction
diff --git a/src/hotspot/cpu/zero/globals_zero.hpp b/src/hotspot/cpu/zero/globals_zero.hpp
index 76f9692e202..6b6c6ea983c 100644
--- a/src/hotspot/cpu/zero/globals_zero.hpp
+++ b/src/hotspot/cpu/zero/globals_zero.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -38,10 +38,10 @@ define_pd_global(bool, UncommonNullCast, true);
define_pd_global(bool, DelayCompilerStubsGeneration, false); // Don't have compiler's stubs
-define_pd_global(uintx, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRESENT(+64)); // Tiered compilation has large code-entry alignment.
-define_pd_global(intx, CodeEntryAlignment, 32);
-define_pd_global(intx, OptoLoopAlignment, 16);
-define_pd_global(intx, InlineSmallCode, 1000);
+define_pd_global(size_t, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRESENT(+64)); // Tiered compilation has large code-entry alignment.
+define_pd_global(intx, CodeEntryAlignment, 32);
+define_pd_global(intx, OptoLoopAlignment, 16);
+define_pd_global(intx, InlineSmallCode, 1000);
// not used, but must satisfy following constraints:
// 1.) must be in the allowed range for intx *and*
diff --git a/src/hotspot/cpu/zero/nativeInst_zero.cpp b/src/hotspot/cpu/zero/nativeInst_zero.cpp
deleted file mode 100644
index 0d2747f7fa6..00000000000
--- a/src/hotspot/cpu/zero/nativeInst_zero.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2008 Red Hat, Inc.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#include "asm/assembler.inline.hpp"
-#include "entry_zero.hpp"
-#include "interpreter/zero/zeroInterpreter.hpp"
-#include "nativeInst_zero.hpp"
-#include "runtime/sharedRuntime.hpp"
-
-// This method is called by nmethod::make_not_entrant to
-// insert a jump to SharedRuntime::get_handle_wrong_method_stub()
-// (dest) at the start of a compiled method (verified_entry) to avoid
-// a race where a method is invoked while being made non-entrant.
-
-void NativeJump::patch_verified_entry(address entry,
- address verified_entry,
- address dest) {
- assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "should be");
-
- ((ZeroEntry*) verified_entry)->set_entry_point(
- (address) ZeroInterpreter::normal_entry);
-}
diff --git a/src/hotspot/cpu/zero/nativeInst_zero.hpp b/src/hotspot/cpu/zero/nativeInst_zero.hpp
index fd8f03f1a59..399a8e96bc3 100644
--- a/src/hotspot/cpu/zero/nativeInst_zero.hpp
+++ b/src/hotspot/cpu/zero/nativeInst_zero.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright 2007 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -177,14 +177,6 @@ class NativeJump : public NativeInstruction {
void set_jump_destination(address dest) {
ShouldNotCallThis();
}
-
- static void check_verified_entry_alignment(address entry,
- address verified_entry) {
- }
-
- static void patch_verified_entry(address entry,
- address verified_entry,
- address dest);
};
inline NativeJump* nativeJump_at(address address) {
diff --git a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp
index 5808ae3bd8f..2357bbb5169 100644
--- a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp
+++ b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp
@@ -26,6 +26,13 @@
#ifndef CPU_ZERO_STUBDECLARATIONS_HPP
#define CPU_ZERO_STUBDECLARATIONS_HPP
+#define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \
+ do_arch_blob, \
+ do_arch_entry, \
+ do_arch_entry_init) \
+ do_arch_blob(preuniverse, 0) \
+
+
#define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \
do_arch_blob, \
do_arch_entry, \
diff --git a/src/hotspot/cpu/zero/stubGenerator_zero.cpp b/src/hotspot/cpu/zero/stubGenerator_zero.cpp
index 07b4e2a92af..65fe33dcba4 100644
--- a/src/hotspot/cpu/zero/stubGenerator_zero.cpp
+++ b/src/hotspot/cpu/zero/stubGenerator_zero.cpp
@@ -178,6 +178,10 @@ class StubGenerator: public StubCodeGenerator {
StubRoutines::_oop_arraycopy;
}
+ void generate_preuniverse_stubs() {
+ StubRoutines::_fence_entry = ShouldNotCallThisStub();
+ }
+
void generate_initial_stubs() {
// entry points that exist in all platforms Note: This is code
// that could be shared among different platforms - however the
@@ -194,7 +198,6 @@ class StubGenerator: public StubCodeGenerator {
StubRoutines::_atomic_cmpxchg_entry = ShouldNotCallThisStub();
StubRoutines::_atomic_cmpxchg_long_entry = ShouldNotCallThisStub();
StubRoutines::_atomic_add_entry = ShouldNotCallThisStub();
- StubRoutines::_fence_entry = ShouldNotCallThisStub();
}
void generate_continuation_stubs() {
@@ -214,6 +217,9 @@ class StubGenerator: public StubCodeGenerator {
public:
StubGenerator(CodeBuffer* code, StubGenBlobId blob_id) : StubCodeGenerator(code, blob_id) {
switch(blob_id) {
+ case preuniverse_id:
+ generate_preuniverse_stubs();
+ break;
case initial_id:
generate_initial_stubs();
break;
diff --git a/src/hotspot/os/aix/c1_globals_aix.hpp b/src/hotspot/os/aix/c1_globals_aix.hpp
deleted file mode 100644
index 1d5cf980104..00000000000
--- a/src/hotspot/os/aix/c1_globals_aix.hpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2015 SAP SE. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef OS_AIX_C1_GLOBALS_AIX_HPP
-#define OS_AIX_C1_GLOBALS_AIX_HPP
-
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/macros.hpp"
-
-//
-// Sets the default values for operating system dependent flags used by the
-// client compiler. (see c1_globals.hpp)
-//
-
-#endif // OS_AIX_C1_GLOBALS_AIX_HPP
diff --git a/src/hotspot/os/aix/c2_globals_aix.hpp b/src/hotspot/os/aix/c2_globals_aix.hpp
deleted file mode 100644
index 8bc2a6cdd55..00000000000
--- a/src/hotspot/os/aix/c2_globals_aix.hpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2013 SAP SE. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef OS_AIX_C2_GLOBALS_AIX_HPP
-#define OS_AIX_C2_GLOBALS_AIX_HPP
-
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/macros.hpp"
-
-//
-// Sets the default values for operating system dependent flags used by the
-// server compiler. (see c2_globals.hpp)
-//
-
-#endif // OS_AIX_C2_GLOBALS_AIX_HPP
diff --git a/src/hotspot/os/aix/decoder_aix.hpp b/src/hotspot/os/aix/decoder_aix.hpp
index 0d87ba87b94..2ba3e1c5a3a 100644
--- a/src/hotspot/os/aix/decoder_aix.hpp
+++ b/src/hotspot/os/aix/decoder_aix.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -23,6 +23,9 @@
*
*/
+#ifndef OS_AIX_DECODER_AIX_HPP
+#define OS_AIX_DECODER_AIX_HPP
+
#include "utilities/decoder.hpp"
#include "porting_aix.hpp"
@@ -44,3 +47,4 @@ class AIXDecoder: public AbstractDecoder {
};
+#endif // OS_AIX_DECODER_AIX_HPP
diff --git a/src/hotspot/os/bsd/c1_globals_bsd.hpp b/src/hotspot/os/bsd/c1_globals_bsd.hpp
deleted file mode 100644
index aa24414612a..00000000000
--- a/src/hotspot/os/bsd/c1_globals_bsd.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef OS_BSD_C1_GLOBALS_BSD_HPP
-#define OS_BSD_C1_GLOBALS_BSD_HPP
-
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/macros.hpp"
-
-//
-// Sets the default values for operating system dependent flags used by the
-// client compiler. (see c1_globals.hpp)
-//
-
-#endif // OS_BSD_C1_GLOBALS_BSD_HPP
diff --git a/src/hotspot/os/bsd/c2_globals_bsd.hpp b/src/hotspot/os/bsd/c2_globals_bsd.hpp
deleted file mode 100644
index 1e8a89ef4a8..00000000000
--- a/src/hotspot/os/bsd/c2_globals_bsd.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef OS_BSD_C2_GLOBALS_BSD_HPP
-#define OS_BSD_C2_GLOBALS_BSD_HPP
-
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/macros.hpp"
-
-//
-// Sets the default values for operating system dependent flags used by the
-// server compiler. (see c2_globals.hpp)
-//
-
-#endif // OS_BSD_C2_GLOBALS_BSD_HPP
diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
index 9f77d5a4bde..6f7d9a6de37 100644
--- a/src/hotspot/os/bsd/os_bsd.cpp
+++ b/src/hotspot/os/bsd/os_bsd.cpp
@@ -154,7 +154,8 @@ julong os::Bsd::available_memory() {
assert(kerr == KERN_SUCCESS,
"host_statistics64 failed - check mach_host_self() and count");
if (kerr == KERN_SUCCESS) {
- available = vmstat.free_count * os::vm_page_size();
+ // free_count is just a lowerbound, other page categories can be freed too and make memory available
+ available = (vmstat.free_count + vmstat.inactive_count + vmstat.purgeable_count) * os::vm_page_size();
}
#endif
return available;
diff --git a/src/hotspot/os/linux/c1_globals_linux.hpp b/src/hotspot/os/linux/c1_globals_linux.hpp
deleted file mode 100644
index 8f7f0e367d7..00000000000
--- a/src/hotspot/os/linux/c1_globals_linux.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef OS_LINUX_C1_GLOBALS_LINUX_HPP
-#define OS_LINUX_C1_GLOBALS_LINUX_HPP
-
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/macros.hpp"
-
-//
-// Sets the default values for operating system dependent flags used by the
-// client compiler. (see c1_globals.hpp)
-//
-
-#endif // OS_LINUX_C1_GLOBALS_LINUX_HPP
diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp
index 068914d7049..a9cabc87335 100644
--- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp
+++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp
@@ -62,7 +62,7 @@ CgroupSubsystem* CgroupSubsystemFactory::create() {
CgroupV1MemoryController* memory = nullptr;
CgroupV1Controller* cpuset = nullptr;
CgroupV1CpuController* cpu = nullptr;
- CgroupV1Controller* cpuacct = nullptr;
+ CgroupV1CpuacctController* cpuacct = nullptr;
CgroupV1Controller* pids = nullptr;
CgroupInfo cg_infos[CG_INFO_LENGTH];
u1 cg_type_flags = INVALID_CGROUPS_GENERIC;
@@ -105,9 +105,10 @@ CgroupSubsystem* CgroupSubsystemFactory::create() {
CgroupV2CpuController* cpu = new CgroupV2CpuController(CgroupV2Controller(cg_infos[CPU_IDX]._mount_path,
cg_infos[CPU_IDX]._cgroup_path,
cg_infos[CPU_IDX]._read_only));
+ CgroupV2CpuacctController* cpuacct = new CgroupV2CpuacctController(cpu);
log_debug(os, container)("Detected cgroups v2 unified hierarchy");
cleanup(cg_infos);
- return new CgroupV2Subsystem(memory, cpu, mem_other);
+ return new CgroupV2Subsystem(memory, cpu, cpuacct, mem_other);
}
/*
@@ -150,7 +151,7 @@ CgroupSubsystem* CgroupSubsystemFactory::create() {
cpu = new CgroupV1CpuController(CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only));
cpu->set_subsystem_path(info._cgroup_path);
} else if (strcmp(info._name, "cpuacct") == 0) {
- cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only);
+ cpuacct = new CgroupV1CpuacctController(CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only));
cpuacct->set_subsystem_path(info._cgroup_path);
} else if (strcmp(info._name, "pids") == 0) {
pids = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only);
@@ -856,6 +857,10 @@ jlong CgroupSubsystem::memory_soft_limit_in_bytes() {
return memory_controller()->controller()->memory_soft_limit_in_bytes(phys_mem);
}
+jlong CgroupSubsystem::memory_throttle_limit_in_bytes() {
+ return memory_controller()->controller()->memory_throttle_limit_in_bytes();
+}
+
jlong CgroupSubsystem::memory_usage_in_bytes() {
return memory_controller()->controller()->memory_usage_in_bytes();
}
@@ -884,6 +889,10 @@ int CgroupSubsystem::cpu_shares() {
return cpu_controller()->controller()->cpu_shares();
}
+jlong CgroupSubsystem::cpu_usage_in_micros() {
+ return cpuacct_controller()->cpu_usage_in_micros();
+}
+
void CgroupSubsystem::print_version_specific_info(outputStream* st) {
julong phys_mem = os::Linux::physical_memory();
memory_controller()->controller()->print_version_specific_info(st, phys_mem);
diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
index 53b178397f8..f72d1a7fb1e 100644
--- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
+++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -216,6 +216,18 @@ class CgroupCpuController: public CHeapObj {
virtual const char* cgroup_path() = 0;
};
+// Pure virtual class representing version agnostic CPU accounting controllers
+class CgroupCpuacctController: public CHeapObj {
+ public:
+ virtual jlong cpu_usage_in_micros() = 0;
+ virtual bool needs_hierarchy_adjustment() = 0;
+ virtual bool is_read_only() = 0;
+ virtual const char* subsystem_path() = 0;
+ virtual void set_subsystem_path(const char* cgroup_path) = 0;
+ virtual const char* mount_point() = 0;
+ virtual const char* cgroup_path() = 0;
+};
+
// Pure virtual class representing version agnostic memory controllers
class CgroupMemoryController: public CHeapObj {
public:
@@ -224,6 +236,7 @@ class CgroupMemoryController: public CHeapObj {
virtual jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) = 0;
virtual jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap) = 0;
virtual jlong memory_soft_limit_in_bytes(julong upper_bound) = 0;
+ virtual jlong memory_throttle_limit_in_bytes() = 0;
virtual jlong memory_max_usage_in_bytes() = 0;
virtual jlong rss_usage_in_bytes() = 0;
virtual jlong cache_usage_in_bytes() = 0;
@@ -250,15 +263,19 @@ class CgroupSubsystem: public CHeapObj {
virtual const char * container_type() = 0;
virtual CachingCgroupController* memory_controller() = 0;
virtual CachingCgroupController* cpu_controller() = 0;
+ virtual CgroupCpuacctController* cpuacct_controller() = 0;
int cpu_quota();
int cpu_period();
int cpu_shares();
+ jlong cpu_usage_in_micros();
+
jlong memory_usage_in_bytes();
jlong memory_and_swap_limit_in_bytes();
jlong memory_and_swap_usage_in_bytes();
jlong memory_soft_limit_in_bytes();
+ jlong memory_throttle_limit_in_bytes();
jlong memory_max_usage_in_bytes();
jlong rss_usage_in_bytes();
jlong cache_usage_in_bytes();
diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp
index 8d9c3edb72a..f65da207062 100644
--- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp
+++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp
@@ -248,10 +248,16 @@ jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) {
}
}
+jlong CgroupV1MemoryController::memory_throttle_limit_in_bytes() {
+ // Log this string at trace level so as to make tests happy.
+ log_trace(os, container)("Memory Throttle Limit is not supported.");
+ return OSCONTAINER_ERROR; // not supported
+}
+
// Constructor
CgroupV1Subsystem::CgroupV1Subsystem(CgroupV1Controller* cpuset,
CgroupV1CpuController* cpu,
- CgroupV1Controller* cpuacct,
+ CgroupV1CpuacctController* cpuacct,
CgroupV1Controller* pids,
CgroupV1MemoryController* memory) :
_cpuset(cpuset),
@@ -416,6 +422,13 @@ int CgroupV1CpuController::cpu_shares() {
return shares_int;
}
+jlong CgroupV1CpuacctController::cpu_usage_in_micros() {
+ julong cpu_usage;
+ CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpuacct.usage", "CPU Usage", cpu_usage);
+ // Output is in nanoseconds, convert to microseconds.
+ return (jlong)cpu_usage / 1000;
+}
+
/* pids_max
*
* Return the maximum number of tasks available to the process
diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp
index 0c191ab91c7..a56ad455165 100644
--- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp
+++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -82,6 +82,7 @@ class CgroupV1MemoryController final : public CgroupMemoryController {
jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) override;
jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap) override;
jlong memory_soft_limit_in_bytes(julong upper_bound) override;
+ jlong memory_throttle_limit_in_bytes() override;
jlong memory_max_usage_in_bytes() override;
jlong rss_usage_in_bytes() override;
jlong cache_usage_in_bytes() override;
@@ -140,12 +141,41 @@ class CgroupV1CpuController final : public CgroupCpuController {
}
};
+class CgroupV1CpuacctController final : public CgroupCpuacctController {
+
+ private:
+ CgroupV1Controller _reader;
+ CgroupV1Controller* reader() { return &_reader; }
+ public:
+ jlong cpu_usage_in_micros() override;
+ void set_subsystem_path(const char *cgroup_path) override {
+ reader()->set_subsystem_path(cgroup_path);
+ }
+ bool is_read_only() override {
+ return reader()->is_read_only();
+ }
+ const char* subsystem_path() override {
+ return reader()->subsystem_path();
+ }
+ const char* mount_point() override {
+ return reader()->mount_point();
+ }
+ bool needs_hierarchy_adjustment() override {
+ return reader()->needs_hierarchy_adjustment();
+ }
+ const char* cgroup_path() override { return reader()->cgroup_path(); }
+
+ public:
+ CgroupV1CpuacctController(const CgroupV1Controller& reader) : _reader(reader) {
+ }
+};
+
class CgroupV1Subsystem: public CgroupSubsystem {
public:
CgroupV1Subsystem(CgroupV1Controller* cpuset,
CgroupV1CpuController* cpu,
- CgroupV1Controller* cpuacct,
+ CgroupV1CpuacctController* cpuacct,
CgroupV1Controller* pids,
CgroupV1MemoryController* memory);
@@ -165,13 +195,14 @@ class CgroupV1Subsystem: public CgroupSubsystem {
}
CachingCgroupController* memory_controller() { return _memory; }
CachingCgroupController* cpu_controller() { return _cpu; }
+ CgroupCpuacctController* cpuacct_controller() { return _cpuacct; }
private:
/* controllers */
CachingCgroupController* _memory = nullptr;
CgroupV1Controller* _cpuset = nullptr;
CachingCgroupController* _cpu = nullptr;
- CgroupV1Controller* _cpuacct = nullptr;
+ CgroupV1CpuacctController* _cpuacct = nullptr;
CgroupV1Controller* _pids = nullptr;
};
diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp
index cbadbb9db02..6472fdfccc5 100644
--- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp
+++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, 2025, Red Hat Inc.
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -114,12 +115,14 @@ int CgroupV2CpuController::cpu_quota() {
// Constructor
CgroupV2Subsystem::CgroupV2Subsystem(CgroupV2MemoryController * memory,
CgroupV2CpuController* cpu,
+ CgroupV2CpuacctController* cpuacct,
CgroupV2Controller unified) :
_unified(unified) {
CgroupUtil::adjust_controller(memory);
CgroupUtil::adjust_controller(cpu);
_memory = new CachingCgroupController(memory);
_cpu = new CachingCgroupController(cpu);
+ _cpuacct = cpuacct;
}
bool CgroupV2Subsystem::is_containerized() {
@@ -152,6 +155,17 @@ int CgroupV2CpuController::cpu_period() {
return period;
}
+jlong CgroupV2CpuController::cpu_usage_in_micros() {
+ julong cpu_usage;
+ bool is_ok = reader()->read_numerical_key_value("/cpu.stat", "usage_usec", &cpu_usage);
+ if (!is_ok) {
+ log_trace(os, container)("CPU Usage failed: %d", OSCONTAINER_ERROR);
+ return OSCONTAINER_ERROR;
+ }
+ log_trace(os, container)("CPU Usage is: " JULONG_FORMAT, cpu_usage);
+ return (jlong)cpu_usage;
+}
+
/* memory_usage_in_bytes
*
* Return the amount of used memory used by this cgroup and descendents
@@ -173,10 +187,16 @@ jlong CgroupV2MemoryController::memory_soft_limit_in_bytes(julong phys_mem) {
return mem_soft_limit;
}
+jlong CgroupV2MemoryController::memory_throttle_limit_in_bytes() {
+ jlong mem_throttle_limit;
+ CONTAINER_READ_NUMBER_CHECKED_MAX(reader(), "/memory.high", "Memory Throttle Limit", mem_throttle_limit);
+ return mem_throttle_limit;
+}
+
jlong CgroupV2MemoryController::memory_max_usage_in_bytes() {
- // Log this string at trace level so as to make tests happy.
- log_trace(os, container)("Maximum Memory Usage is not supported.");
- return OSCONTAINER_ERROR; // not supported
+ julong mem_max_usage;
+ CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.peak", "Maximum Memory Usage", mem_max_usage);
+ return mem_max_usage;
}
jlong CgroupV2MemoryController::rss_usage_in_bytes() {
diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp
index 56dcadd670f..e26f37925ca 100644
--- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp
+++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, 2024, Red Hat Inc.
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -61,6 +62,34 @@ class CgroupV2CpuController: public CgroupCpuController {
int cpu_quota() override;
int cpu_period() override;
int cpu_shares() override;
+ jlong cpu_usage_in_micros();
+ bool is_read_only() override {
+ return reader()->is_read_only();
+ }
+ const char* subsystem_path() override {
+ return reader()->subsystem_path();
+ }
+ bool needs_hierarchy_adjustment() override {
+ return reader()->needs_hierarchy_adjustment();
+ }
+ void set_subsystem_path(const char* cgroup_path) override {
+ reader()->set_subsystem_path(cgroup_path);
+ }
+ const char* mount_point() override { return reader()->mount_point(); }
+ const char* cgroup_path() override { return reader()->cgroup_path(); }
+};
+
+class CgroupV2CpuacctController: public CgroupCpuacctController {
+ private:
+ CgroupV2CpuController* _reader;
+ CgroupV2CpuController* reader() { return _reader; }
+ public:
+ CgroupV2CpuacctController(CgroupV2CpuController* reader) : _reader(reader) {
+ }
+ // In cgroup v2, cpu usage is a part of the cpu controller.
+ jlong cpu_usage_in_micros() override {
+ return reader()->cpu_usage_in_micros();
+ }
bool is_read_only() override {
return reader()->is_read_only();
}
@@ -89,6 +118,7 @@ class CgroupV2MemoryController final: public CgroupMemoryController {
jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swp) override;
jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swp) override;
jlong memory_soft_limit_in_bytes(julong upper_bound) override;
+ jlong memory_throttle_limit_in_bytes() override;
jlong memory_usage_in_bytes() override;
jlong memory_max_usage_in_bytes() override;
jlong rss_usage_in_bytes() override;
@@ -118,11 +148,14 @@ class CgroupV2Subsystem: public CgroupSubsystem {
CachingCgroupController* _memory = nullptr;
CachingCgroupController* _cpu = nullptr;
+ CgroupCpuacctController* _cpuacct = nullptr;
+
CgroupV2Controller* unified() { return &_unified; }
public:
CgroupV2Subsystem(CgroupV2MemoryController * memory,
CgroupV2CpuController* cpu,
+ CgroupV2CpuacctController* cpuacct,
CgroupV2Controller unified);
char * cpu_cpuset_cpus() override;
@@ -137,6 +170,7 @@ class CgroupV2Subsystem: public CgroupSubsystem {
}
CachingCgroupController* memory_controller() override { return _memory; }
CachingCgroupController* cpu_controller() override { return _cpu; }
+ CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; };
};
#endif // CGROUP_V2_SUBSYSTEM_LINUX_HPP
diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp
index ecac7307c30..2ba4c252659 100644
--- a/src/hotspot/os/linux/osContainer_linux.cpp
+++ b/src/hotspot/os/linux/osContainer_linux.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -122,6 +122,11 @@ jlong OSContainer::memory_soft_limit_in_bytes() {
return cgroup_subsystem->memory_soft_limit_in_bytes();
}
+jlong OSContainer::memory_throttle_limit_in_bytes() {
+ assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
+ return cgroup_subsystem->memory_throttle_limit_in_bytes();
+}
+
jlong OSContainer::memory_usage_in_bytes() {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->memory_usage_in_bytes();
@@ -177,6 +182,11 @@ int OSContainer::cpu_shares() {
return cgroup_subsystem->cpu_shares();
}
+jlong OSContainer::cpu_usage_in_micros() {
+ assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
+ return cgroup_subsystem->cpu_usage_in_micros();
+}
+
jlong OSContainer::pids_max() {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->pids_max();
diff --git a/src/hotspot/os/linux/osContainer_linux.hpp b/src/hotspot/os/linux/osContainer_linux.hpp
index dd29c7a4769..3c270e8ea50 100644
--- a/src/hotspot/os/linux/osContainer_linux.hpp
+++ b/src/hotspot/os/linux/osContainer_linux.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -54,6 +54,7 @@ class OSContainer: AllStatic {
static jlong memory_and_swap_limit_in_bytes();
static jlong memory_and_swap_usage_in_bytes();
static jlong memory_soft_limit_in_bytes();
+ static jlong memory_throttle_limit_in_bytes();
static jlong memory_usage_in_bytes();
static jlong memory_max_usage_in_bytes();
static jlong rss_usage_in_bytes();
@@ -69,6 +70,8 @@ class OSContainer: AllStatic {
static int cpu_shares();
+ static jlong cpu_usage_in_micros();
+
static jlong pids_max();
static jlong pids_current();
};
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index ff2195d3839..1a23c956f35 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -2487,9 +2487,18 @@ bool os::Linux::print_container_info(outputStream* st) {
st->print_cr("%s", i == OSCONTAINER_ERROR ? "not supported" : "no shares");
}
+ jlong j = OSContainer::cpu_usage_in_micros();
+ st->print("cpu_usage_in_micros: ");
+ if (j >= 0) {
+ st->print_cr(JLONG_FORMAT, j);
+ } else {
+ st->print_cr("%s", j == OSCONTAINER_ERROR ? "not supported" : "no usage");
+ }
+
OSContainer::print_container_helper(st, OSContainer::memory_limit_in_bytes(), "memory_limit_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_and_swap_limit_in_bytes(), "memory_and_swap_limit_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_soft_limit_in_bytes(), "memory_soft_limit_in_bytes");
+ OSContainer::print_container_helper(st, OSContainer::memory_throttle_limit_in_bytes(), "memory_throttle_limit_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_usage_in_bytes(), "memory_usage_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_max_usage_in_bytes(), "memory_max_usage_in_bytes");
OSContainer::print_container_helper(st, OSContainer::rss_usage_in_bytes(), "rss_usage_in_bytes");
@@ -2497,7 +2506,7 @@ bool os::Linux::print_container_info(outputStream* st) {
OSContainer::print_version_specific_info(st);
- jlong j = OSContainer::pids_max();
+ j = OSContainer::pids_max();
st->print("maximum number of tasks: ");
if (j > 0) {
st->print_cr(JLONG_FORMAT, j);
diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp
index 68bdec3875c..303e44eadcb 100644
--- a/src/hotspot/os/posix/os_posix.cpp
+++ b/src/hotspot/os/posix/os_posix.cpp
@@ -59,6 +59,7 @@
#ifdef AIX
#include "loadlib_aix.hpp"
#include "os_aix.hpp"
+#include "porting_aix.hpp"
#endif
#ifdef LINUX
#include "os_linux.hpp"
@@ -1076,7 +1077,7 @@ void os::jvm_path(char *buf, jint buflen) {
return;
}
- char* fname;
+ const char* fname;
#ifdef AIX
Dl_info dlinfo;
int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo);
@@ -1099,7 +1100,7 @@ void os::jvm_path(char *buf, jint buflen) {
#endif // AIX
char* rp = nullptr;
if (fname[0] != '\0') {
- rp = os::realpath(dli_fname, buf, buflen);
+ rp = os::realpath(fname, buf, buflen);
}
if (rp == nullptr) {
return;
@@ -1137,7 +1138,7 @@ void os::jvm_path(char *buf, jint buflen) {
"buf has been truncated");
} else {
// Go back to path of .so
- rp = os::realpath(dli_fname, buf, buflen);
+ rp = os::realpath(fname, buf, buflen);
if (rp == nullptr) {
return;
}
diff --git a/src/hotspot/os/windows/c1_globals_windows.hpp b/src/hotspot/os/windows/c1_globals_windows.hpp
deleted file mode 100644
index 06e6da5d68c..00000000000
--- a/src/hotspot/os/windows/c1_globals_windows.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef OS_WINDOWS_C1_GLOBALS_WINDOWS_HPP
-#define OS_WINDOWS_C1_GLOBALS_WINDOWS_HPP
-
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/macros.hpp"
-
-//
-// Sets the default values for operating system dependent flags used by the
-// client compiler. (see c1_globals.hpp)
-//
-
-#endif // OS_WINDOWS_C1_GLOBALS_WINDOWS_HPP
diff --git a/src/hotspot/os/windows/c2_globals_windows.hpp b/src/hotspot/os/windows/c2_globals_windows.hpp
deleted file mode 100644
index 6891113d2cd..00000000000
--- a/src/hotspot/os/windows/c2_globals_windows.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef OS_WINDOWS_C2_GLOBALS_WINDOWS_HPP
-#define OS_WINDOWS_C2_GLOBALS_WINDOWS_HPP
-
-#include "utilities/globalDefinitions.hpp"
-#include "utilities/macros.hpp"
-
-//
-// Sets the default values for operating system dependent flags used by the
-// server compiler. (see c2_globals.hpp)
-//
-
-#endif // OS_WINDOWS_C2_GLOBALS_WINDOWS_HPP
diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
index 9e536d2df97..24969683a1f 100644
--- a/src/hotspot/os/windows/os_windows.cpp
+++ b/src/hotspot/os/windows/os_windows.cpp
@@ -2752,19 +2752,6 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
}
}
-#ifdef _M_ARM64
- if (in_java &&
- (exception_code == EXCEPTION_ILLEGAL_INSTRUCTION ||
- exception_code == EXCEPTION_ILLEGAL_INSTRUCTION_2)) {
- if (nativeInstruction_at(pc)->is_sigill_not_entrant()) {
- if (TraceTraps) {
- tty->print_cr("trap: not_entrant");
- }
- return Handle_Exception(exceptionInfo, SharedRuntime::get_handle_wrong_method_stub());
- }
- }
-#endif
-
if (in_java) {
switch (exception_code) {
case EXCEPTION_INT_DIVIDE_BY_ZERO:
diff --git a/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp b/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp
index 3dbb8adddd6..98b17aaacfc 100644
--- a/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp
+++ b/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp
@@ -229,16 +229,7 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
CodeBlob *cb = nullptr;
int stop_type = -1;
- // Handle signal from NativeJump::patch_verified_entry().
- if (sig == SIGILL && nativeInstruction_at(pc)->is_sigill_not_entrant()) {
- if (TraceTraps) {
- tty->print_cr("trap: not_entrant");
- }
- stub = SharedRuntime::get_handle_wrong_method_stub();
- goto run_stub;
- }
-
- else if ((sig == (USE_POLL_BIT_ONLY ? SIGTRAP : SIGSEGV)) &&
+ if ((sig == (USE_POLL_BIT_ONLY ? SIGTRAP : SIGSEGV)) &&
((NativeInstruction*)pc)->is_safepoint_poll() &&
CodeCache::contains((void*) pc) &&
((cb = CodeCache::find_blob(pc)) != nullptr) &&
diff --git a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp
index ad32ee150e8..b7556ca69da 100644
--- a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp
+++ b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp
@@ -271,14 +271,7 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
// Java thread running in Java code => find exception handler if any
// a fault inside compiled code, the interpreter, or a stub
- // Handle signal from NativeJump::patch_verified_entry().
- if ((sig == SIGILL)
- && nativeInstruction_at(pc)->is_sigill_not_entrant()) {
- if (TraceTraps) {
- tty->print_cr("trap: not_entrant");
- }
- stub = SharedRuntime::get_handle_wrong_method_stub();
- } else if ((sig == SIGSEGV || sig == SIGBUS) && SafepointMechanism::is_poll_address((address)info->si_addr)) {
+ if ((sig == SIGSEGV || sig == SIGBUS) && SafepointMechanism::is_poll_address((address)info->si_addr)) {
stub = SharedRuntime::get_poll_stub(pc);
#if defined(__APPLE__)
// 32-bit Darwin reports a SIGBUS for nearly all memory access exceptions.
diff --git a/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp
index 5424b58da6d..b55c1cd27c8 100644
--- a/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp
+++ b/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp
@@ -92,6 +92,9 @@ void VM_Version::get_os_cpu_info() {
cpu_has("hw.optional.armv8_2_sha3")) {
_features |= CPU_SHA3;
}
+ if (cpu_has("hw.optional.arm.FEAT_SB")) {
+ _features |= CPU_SB;
+ }
int cache_line_size;
int hw_conf_cache_line[] = { CTL_HW, HW_CACHELINE };
diff --git a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp
index 6900283418d..e565f353382 100644
--- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp
+++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp
@@ -232,14 +232,7 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
// Java thread running in Java code => find exception handler if any
// a fault inside compiled code, the interpreter, or a stub
- // Handle signal from NativeJump::patch_verified_entry().
- if ((sig == SIGILL || sig == SIGTRAP)
- && nativeInstruction_at(pc)->is_sigill_not_entrant()) {
- if (TraceTraps) {
- tty->print_cr("trap: not_entrant (%s)", (sig == SIGTRAP) ? "SIGTRAP" : "SIGILL");
- }
- stub = SharedRuntime::get_handle_wrong_method_stub();
- } else if (sig == SIGSEGV && SafepointMechanism::is_poll_address((address)info->si_addr)) {
+ if (sig == SIGSEGV && SafepointMechanism::is_poll_address((address)info->si_addr)) {
stub = SharedRuntime::get_poll_stub(pc);
} else if (sig == SIGBUS /* && info->si_code == BUS_OBJERR */) {
// BugId 4454115: A read from a MappedByteBuffer can fault
diff --git a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp
index 9725c6cd6c0..a8edeefc885 100644
--- a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp
+++ b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp
@@ -71,6 +71,10 @@
#define HWCAP_SVE (1 << 22)
#endif
+#ifndef HWCAP_SB
+#define HWCAP_SB (1 << 29)
+#endif
+
#ifndef HWCAP_PACA
#define HWCAP_PACA (1 << 30)
#endif
@@ -143,6 +147,7 @@ void VM_Version::get_os_cpu_info() {
HWCAP_SHA3 |
HWCAP_SHA512 |
HWCAP_SVE |
+ HWCAP_SB |
HWCAP_PACA |
HWCAP_FPHP |
HWCAP_ASIMDHP);
diff --git a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp
index 6c245f8f1a6..28b47877264 100644
--- a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp
+++ b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp
@@ -359,11 +359,6 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
stub = SharedRuntime::continuation_for_implicit_exception(
thread, pc, SharedRuntime::IMPLICIT_NULL);
}
- } else if (sig == SIGILL &&
- *(int*)pc ==
- NativeInstruction::not_entrant_illegal_instruction) {
- // Not entrant
- stub = SharedRuntime::get_handle_wrong_method_stub();
}
} else if ((thread->thread_state() == _thread_in_vm ||
thread->thread_state() == _thread_in_native) &&
diff --git a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp
index 5fe37be0d20..55618947143 100644
--- a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp
+++ b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp
@@ -258,15 +258,7 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
CodeBlob *cb = nullptr;
int stop_type = -1;
- // Handle signal from NativeJump::patch_verified_entry().
- if (sig == SIGILL && nativeInstruction_at(pc)->is_sigill_not_entrant()) {
- if (TraceTraps) {
- tty->print_cr("trap: not_entrant");
- }
- stub = SharedRuntime::get_handle_wrong_method_stub();
- }
-
- else if ((sig == (USE_POLL_BIT_ONLY ? SIGTRAP : SIGSEGV)) &&
+ if ((sig == (USE_POLL_BIT_ONLY ? SIGTRAP : SIGSEGV)) &&
// A linux-ppc64 kernel before 2.6.6 doesn't set si_addr on some segfaults
// in 64bit mode (cf. http://www.kernel.org/pub/linux/kernel/v2.6/ChangeLog-2.6.6),
// especially when we try to read from the safepoint polling page. So the check
diff --git a/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp
index 8366a7249fa..7a5929b0f41 100644
--- a/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp
+++ b/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp
@@ -223,14 +223,7 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
// Java thread running in Java code => find exception handler if any
// a fault inside compiled code, the interpreter, or a stub
- // Handle signal from NativeJump::patch_verified_entry().
- if ((sig == SIGILL || sig == SIGTRAP)
- && nativeInstruction_at(pc)->is_sigill_not_entrant()) {
- if (TraceTraps) {
- tty->print_cr("trap: not_entrant (%s)", (sig == SIGTRAP) ? "SIGTRAP" : "SIGILL");
- }
- stub = SharedRuntime::get_handle_wrong_method_stub();
- } else if (sig == SIGSEGV && SafepointMechanism::is_poll_address((address)info->si_addr)) {
+ if (sig == SIGSEGV && SafepointMechanism::is_poll_address((address)info->si_addr)) {
stub = SharedRuntime::get_poll_stub(pc);
} else if (sig == SIGBUS /* && info->si_code == BUS_OBJERR */) {
// BugId 4454115: A read from a MappedByteBuffer can fault
diff --git a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp
index 7e4d72fc066..4e074512e34 100644
--- a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp
+++ b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp
@@ -257,15 +257,7 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
// Java thread running in Java code => find exception handler if any
// a fault inside compiled code, the interpreter, or a stub
- // Handle signal from NativeJump::patch_verified_entry().
- if (sig == SIGILL && nativeInstruction_at(pc)->is_sigill_not_entrant()) {
- if (TraceTraps) {
- tty->print_cr("trap: not_entrant (SIGILL)");
- }
- stub = SharedRuntime::get_handle_wrong_method_stub();
- }
-
- else if (sig == SIGSEGV &&
+ if (sig == SIGSEGV &&
SafepointMechanism::is_poll_address((address)info->si_addr)) {
if (TraceTraps) {
tty->print_cr("trap: safepoint_poll at " INTPTR_FORMAT " (SIGSEGV)", p2i(pc));
diff --git a/src/hotspot/share/c1/c1_Compilation.cpp b/src/hotspot/share/c1/c1_Compilation.cpp
index cf1f7e50086..bb5ceb0106b 100644
--- a/src/hotspot/share/c1/c1_Compilation.cpp
+++ b/src/hotspot/share/c1/c1_Compilation.cpp
@@ -330,7 +330,7 @@ bool Compilation::setup_code_buffer(CodeBuffer* code, int call_stub_estimate) {
char* locs_buffer = NEW_RESOURCE_ARRAY(char, locs_buffer_size);
code->insts()->initialize_shared_locs((relocInfo*)locs_buffer,
locs_buffer_size / sizeof(relocInfo));
- code->initialize_consts_size(Compilation::desired_max_constant_size());
+ code->initialize_consts_size(Compilation::desired_max_constant_size);
// Call stubs + two deopt handlers (regular and MH) + exception handler
int stub_size = (call_stub_estimate * LIR_Assembler::call_stub_size()) +
LIR_Assembler::exception_handler_size() +
@@ -668,7 +668,7 @@ ciKlass* Compilation::cha_exact_type(ciType* type) {
if (type != nullptr && type->is_loaded() && type->is_instance_klass()) {
ciInstanceKlass* ik = type->as_instance_klass();
assert(ik->exact_klass() == nullptr, "no cha for final klass");
- if (DeoptC1 && UseCHA && !(ik->has_subklass() || ik->is_interface())) {
+ if (UseCHA && !(ik->has_subklass() || ik->is_interface())) {
dependency_recorder()->assert_leaf_type(ik);
return ik;
}
diff --git a/src/hotspot/share/c1/c1_Compilation.hpp b/src/hotspot/share/c1/c1_Compilation.hpp
index dab584ac3b0..a4bbe3442dd 100644
--- a/src/hotspot/share/c1/c1_Compilation.hpp
+++ b/src/hotspot/share/c1/c1_Compilation.hpp
@@ -216,12 +216,8 @@ class Compilation: public StackObj {
const char* bailout_msg() const { return _bailout_msg; }
const CompilationFailureInfo* first_failure_details() const { return _first_failure_details; }
- static uint desired_max_code_buffer_size() {
- return (uint)NMethodSizeLimit; // default 64K
- }
- static uint desired_max_constant_size() {
- return desired_max_code_buffer_size() / 10;
- }
+ const static uint desired_max_code_buffer_size = 64*K * wordSize;
+ const static uint desired_max_constant_size = desired_max_code_buffer_size / 10;
static bool setup_code_buffer(CodeBuffer* cb, int call_stub_estimate);
diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp
index b5fa7dcf247..64a2e6456d8 100644
--- a/src/hotspot/share/c1/c1_Compiler.cpp
+++ b/src/hotspot/share/c1/c1_Compiler.cpp
@@ -79,7 +79,7 @@ void Compiler::initialize() {
}
uint Compiler::code_buffer_size() {
- return Compilation::desired_max_code_buffer_size() + Compilation::desired_max_constant_size();
+ return Compilation::desired_max_code_buffer_size + Compilation::desired_max_constant_size;
}
BufferBlob* Compiler::init_buffer_blob() {
@@ -87,8 +87,7 @@ BufferBlob* Compiler::init_buffer_blob() {
// compilation seems to be too expensive (at least on Intel win32).
assert (CompilerThread::current()->get_buffer_blob() == nullptr, "Should initialize only once");
- // setup CodeBuffer. Preallocate a BufferBlob of size
- // NMethodSizeLimit plus some extra space for constants.
+ // Setup CodeBuffer.
BufferBlob* buffer_blob = BufferBlob::create("C1 temporary CodeBuffer", code_buffer_size());
if (buffer_blob != nullptr) {
CompilerThread::current()->set_buffer_blob(buffer_blob);
@@ -142,7 +141,7 @@ bool Compiler::is_intrinsic_supported(vmIntrinsics::ID id) {
case vmIntrinsics::_arraycopy:
case vmIntrinsics::_currentTimeMillis:
case vmIntrinsics::_nanoTime:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
// Use the intrinsic version of Reference.get() so that the value in
// the referent field can be registered by the G1 pre-barrier code.
// Also to prevent commoning reads from this field across safepoint
diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp
index 8658bebdaee..1e4e07360cc 100644
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp
@@ -1855,7 +1855,6 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
Dependencies* GraphBuilder::dependency_recorder() const {
- assert(DeoptC1, "need debug information");
return compilation()->dependency_recorder();
}
@@ -2001,7 +2000,7 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
ciMethod* cha_monomorphic_target = nullptr;
ciMethod* exact_target = nullptr;
Value better_receiver = nullptr;
- if (UseCHA && DeoptC1 && target->is_loaded() &&
+ if (UseCHA && target->is_loaded() &&
!(// %%% FIXME: Are both of these relevant?
target->is_method_handle_intrinsic() ||
target->is_compiled_lambda_form()) &&
@@ -2252,7 +2251,7 @@ bool GraphBuilder::direct_compare(ciKlass* k) {
if (ik->is_final()) {
return true;
} else {
- if (DeoptC1 && UseCHA && !(ik->has_subklass() || ik->is_interface())) {
+ if (UseCHA && !(ik->has_subklass() || ik->is_interface())) {
// test class is leaf class
dependency_recorder()->assert_leaf_type(ik);
return true;
@@ -3341,7 +3340,7 @@ GraphBuilder::GraphBuilder(Compilation* compilation, IRScope* scope)
break;
}
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
{
{
// With java.lang.ref.reference.get() we must go through the
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp
index 341de0ac0c2..850a196c898 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.cpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp
@@ -1185,7 +1185,7 @@ void LIRGenerator::do_Return(Return* x) {
// Example: ref.get()
// Combination of LoadField and g1 pre-write barrier
-void LIRGenerator::do_Reference_get(Intrinsic* x) {
+void LIRGenerator::do_Reference_get0(Intrinsic* x) {
const int referent_offset = java_lang_ref_Reference::referent_offset();
@@ -2914,8 +2914,8 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) {
case vmIntrinsics::_onSpinWait:
__ on_spin_wait();
break;
- case vmIntrinsics::_Reference_get:
- do_Reference_get(x);
+ case vmIntrinsics::_Reference_get0:
+ do_Reference_get0(x);
break;
case vmIntrinsics::_updateCRC32:
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp
index e70bbd96189..ec0ea5dc047 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.hpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp
@@ -266,7 +266,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure {
void do_CompareAndSwap(Intrinsic* x, ValueType* type);
void do_PreconditionsCheckIndex(Intrinsic* x, BasicType type);
void do_FPIntrinsics(Intrinsic* x);
- void do_Reference_get(Intrinsic* x);
+ void do_Reference_get0(Intrinsic* x);
void do_update_CRC32(Intrinsic* x);
void do_update_CRC32C(Intrinsic* x);
void do_vectorizedMismatch(Intrinsic* x);
diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp
index e2760689daa..57d22a38324 100644
--- a/src/hotspot/share/c1/c1_Runtime1.cpp
+++ b/src/hotspot/share/c1/c1_Runtime1.cpp
@@ -818,7 +818,7 @@ JRT_ENTRY(void, Runtime1::deoptimize(JavaThread* current, jint trap_request))
Deoptimization::DeoptReason reason = Deoptimization::trap_request_reason(trap_request);
if (action == Deoptimization::Action_make_not_entrant) {
- if (nm->make_not_entrant(nmethod::ChangeReason::C1_deoptimize)) {
+ if (nm->make_not_entrant(nmethod::InvalidationReason::C1_DEOPTIMIZE)) {
if (reason == Deoptimization::Reason_tenured) {
MethodData* trap_mdo = Deoptimization::get_method_data(current, method, true /*create_if_missing*/);
if (trap_mdo != nullptr) {
@@ -1110,7 +1110,7 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* current, C1StubId stub_id ))
// safepoint, but if it's still alive then make it not_entrant.
nmethod* nm = CodeCache::find_nmethod(caller_frame.pc());
if (nm != nullptr) {
- nm->make_not_entrant(nmethod::ChangeReason::C1_codepatch);
+ nm->make_not_entrant(nmethod::InvalidationReason::C1_CODEPATCH);
}
Deoptimization::deoptimize_frame(current, caller_frame.id());
@@ -1358,7 +1358,7 @@ void Runtime1::patch_code(JavaThread* current, C1StubId stub_id) {
// Make sure the nmethod is invalidated, i.e. made not entrant.
nmethod* nm = CodeCache::find_nmethod(caller_frame.pc());
if (nm != nullptr) {
- nm->make_not_entrant(nmethod::ChangeReason::C1_deoptimize_for_patching);
+ nm->make_not_entrant(nmethod::InvalidationReason::C1_DEOPTIMIZE_FOR_PATCHING);
}
}
@@ -1486,7 +1486,7 @@ JRT_ENTRY(void, Runtime1::predicate_failed_trap(JavaThread* current))
nmethod* nm = CodeCache::find_nmethod(caller_frame.pc());
assert (nm != nullptr, "no more nmethod?");
- nm->make_not_entrant(nmethod::ChangeReason::C1_predicate_failed_trap);
+ nm->make_not_entrant(nmethod::InvalidationReason::C1_PREDICATE_FAILED_TRAP);
methodHandle m(current, nm->method());
MethodData* mdo = m->method_data();
diff --git a/src/hotspot/share/c1/c1_ValueMap.hpp b/src/hotspot/share/c1/c1_ValueMap.hpp
index 12c372f27c8..6583a07c920 100644
--- a/src/hotspot/share/c1/c1_ValueMap.hpp
+++ b/src/hotspot/share/c1/c1_ValueMap.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -187,7 +187,13 @@ class ValueNumberingVisitor: public InstructionVisitor {
void do_Convert (Convert* x) { /* nothing to do */ }
void do_NullCheck (NullCheck* x) { /* nothing to do */ }
void do_TypeCast (TypeCast* x) { /* nothing to do */ }
- void do_NewInstance (NewInstance* x) { /* nothing to do */ }
+ void do_NewInstance (NewInstance* x) {
+ ciInstanceKlass* c = x->klass();
+ if (c != nullptr && !c->is_initialized() &&
+ (!c->is_loaded() || c->has_class_initializer())) {
+ kill_memory();
+ }
+ }
void do_NewTypeArray (NewTypeArray* x) { /* nothing to do */ }
void do_NewObjectArray (NewObjectArray* x) { /* nothing to do */ }
void do_NewMultiArray (NewMultiArray* x) { /* nothing to do */ }
diff --git a/src/hotspot/share/c1/c1_globals.hpp b/src/hotspot/share/c1/c1_globals.hpp
index ec01bc7a790..755a1572e54 100644
--- a/src/hotspot/share/c1/c1_globals.hpp
+++ b/src/hotspot/share/c1/c1_globals.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,9 +25,9 @@
#ifndef SHARE_C1_C1_GLOBALS_HPP
#define SHARE_C1_C1_GLOBALS_HPP
-#include "c1/c1_globals_pd.hpp"
#include "runtime/globals_shared.hpp"
#include "utilities/macros.hpp"
+#include CPU_HEADER(c1_globals)
//
// Declare all global flags used by the client compiler.
//
@@ -244,9 +244,6 @@
develop(bool, GenerateArrayStoreCheck, true, \
"Generates code for array store checks") \
\
- develop(bool, DeoptC1, true, \
- "Use deoptimization in C1") \
- \
develop(bool, PrintBailouts, false, \
"Print bailout and its reason") \
\
@@ -274,12 +271,6 @@
develop(bool, InstallMethods, true, \
"Install methods at the end of successful compilations") \
\
- /* The compiler assumes, in many places, that methods are at most 1MB. */ \
- /* Therefore, we restrict this flag to at most 1MB. */ \
- develop(intx, NMethodSizeLimit, (64*K)*wordSize, \
- "Maximum size of a compiled method.") \
- range(0, 1*M) \
- \
develop(intx, InstructionCountCutoff, 37000, \
"If GraphBuilder adds this many instructions, bails out") \
range(0, max_jint) \
diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp
index 6ab63418e09..0eb7ddfbbf6 100644
--- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp
+++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp
@@ -392,7 +392,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_signature(ConstantPool* c
}
bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) {
- int mt_index = cp->operand_argument_index_at(bsms_attribute_index, arg_i);
+ int mt_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument_index(arg_i);
if (!cp->tag_at(mt_index).is_method_type()) {
// malformed class?
return false;
@@ -408,7 +408,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPo
}
bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) {
- int mh_index = cp->operand_argument_index_at(bsms_attribute_index, arg_i);
+ int mh_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument_index(arg_i);
if (!cp->tag_at(mh_index).is_method_handle()) {
// malformed class?
return false;
@@ -514,7 +514,7 @@ bool AOTConstantPoolResolver::is_indy_resolution_deterministic(ConstantPool* cp,
}
int bsms_attribute_index = cp->bootstrap_methods_attribute_index(cp_index);
- int arg_count = cp->operand_argument_count_at(bsms_attribute_index);
+ int arg_count = cp->bsm_attribute_entry(bsms_attribute_index)->argument_count();
if (arg_count != 3) {
// Malformed class?
return false;
diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp
index b93238eca88..c19c0776465 100644
--- a/src/hotspot/share/cds/cdsConfig.cpp
+++ b/src/hotspot/share/cds/cdsConfig.cpp
@@ -30,6 +30,7 @@
#include "cds/heapShared.hpp"
#include "classfile/classLoaderDataShared.hpp"
#include "classfile/moduleEntry.hpp"
+#include "code/aotCodeCache.hpp"
#include "include/jvm_io.h"
#include "logging/log.hpp"
#include "memory/universe.hpp"
@@ -705,24 +706,29 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla
}
void CDSConfig::setup_compiler_args() {
- // AOT profiles are supported only in the JEP 483 workflow.
- bool can_dump_profiles = AOTClassLinking && new_aot_flags_used();
+ // AOT profiles and AOT-compiled code are supported only in the JEP 483 workflow.
+ bool can_dump_profile_and_compiled_code = AOTClassLinking && new_aot_flags_used();
- if (is_dumping_preimage_static_archive() && can_dump_profiles) {
+ if (is_dumping_preimage_static_archive() && can_dump_profile_and_compiled_code) {
// JEP 483 workflow -- training
FLAG_SET_ERGO_IF_DEFAULT(AOTRecordTraining, true);
FLAG_SET_ERGO(AOTReplayTraining, false);
- } else if (is_dumping_final_static_archive() && can_dump_profiles) {
+ AOTCodeCache::disable_caching(); // No AOT code generation during training run
+ } else if (is_dumping_final_static_archive() && can_dump_profile_and_compiled_code) {
// JEP 483 workflow -- assembly
FLAG_SET_ERGO(AOTRecordTraining, false);
FLAG_SET_ERGO_IF_DEFAULT(AOTReplayTraining, true);
+ AOTCodeCache::enable_caching(); // Generate AOT code during assembly phase.
+ disable_dumping_aot_code(); // Don't dump AOT code until metadata and heap are dumped.
} else if (is_using_archive() && new_aot_flags_used()) {
// JEP 483 workflow -- production
FLAG_SET_ERGO(AOTRecordTraining, false);
FLAG_SET_ERGO_IF_DEFAULT(AOTReplayTraining, true);
+ AOTCodeCache::enable_caching();
} else {
FLAG_SET_ERGO(AOTReplayTraining, false);
FLAG_SET_ERGO(AOTRecordTraining, false);
+ AOTCodeCache::disable_caching();
}
}
diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp
index b1072dbce9b..36afbc65ce3 100644
--- a/src/hotspot/share/cds/dynamicArchive.cpp
+++ b/src/hotspot/share/cds/dynamicArchive.cpp
@@ -32,8 +32,8 @@
#include "cds/cds_globals.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/dynamicArchive.hpp"
-#include "cds/lambdaProxyClassDictionary.hpp"
#include "cds/lambdaFormInvokers.hpp"
+#include "cds/lambdaProxyClassDictionary.hpp"
#include "cds/metaspaceShared.hpp"
#include "cds/regeneratedClasses.hpp"
#include "classfile/classLoader.hpp"
@@ -382,11 +382,11 @@ void DynamicArchiveBuilder::gather_array_klasses() {
log_debug(aot)("Total array klasses gathered for dynamic archive: %d", DynamicArchive::num_array_klasses());
}
-class VM_PopulateDynamicDumpSharedSpace: public VM_GC_Sync_Operation {
+class VM_PopulateDynamicDumpSharedSpace: public VM_Heap_Sync_Operation {
DynamicArchiveBuilder _builder;
public:
VM_PopulateDynamicDumpSharedSpace(const char* archive_name)
- : VM_GC_Sync_Operation(), _builder(archive_name) {}
+ : VM_Heap_Sync_Operation(), _builder(archive_name) {}
VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
void doit() {
ResourceMark rm;
diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp
index a413aa2d8e8..35b46099e1b 100644
--- a/src/hotspot/share/cds/filemap.cpp
+++ b/src/hotspot/share/cds/filemap.cpp
@@ -44,6 +44,7 @@
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
+#include "compiler/compilerDefinitions.inline.hpp"
#include "jvm.h"
#include "logging/log.hpp"
#include "logging/logMessage.hpp"
@@ -232,6 +233,8 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment,
} else {
_narrow_klass_pointer_bits = _narrow_klass_shift = -1;
}
+ // Which JIT compier is used
+ _compiler_type = (u1)CompilerConfig::compiler_type();
_type_profile_level = TypeProfileLevel;
_type_profile_args_limit = TypeProfileArgsLimit;
_type_profile_parms_limit = TypeProfileParmsLimit;
@@ -920,7 +923,7 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
" crc 0x%08x",
region_name(region), region, size, p2i(requested_base), _file_offset, crc);
} else {
- aot_log_info(aot)("Shared file region (%s) %d: %8zu"
+ aot_log_info(aot)("Shared file region (%s) %d: %8zu"
" bytes", region_name(region), region, size);
}
@@ -1935,6 +1938,23 @@ bool FileMapHeader::validate() {
CompactStrings ? "enabled" : "disabled");
return false;
}
+ bool jvmci_compiler_is_enabled = CompilerConfig::is_jvmci_compiler_enabled();
+ CompilerType compiler_type = CompilerConfig::compiler_type();
+ CompilerType archive_compiler_type = CompilerType(_compiler_type);
+ // JVMCI compiler does different type profiling settigns and generate
+ // different code. We can't use archive which was produced
+ // without it and reverse.
+ // Only allow mix when JIT compilation is disabled.
+ // Interpreter is used by default when dumping archive.
+ bool intepreter_is_used = (archive_compiler_type == CompilerType::compiler_none) ||
+ (compiler_type == CompilerType::compiler_none);
+ if (!intepreter_is_used &&
+ jvmci_compiler_is_enabled != (archive_compiler_type == CompilerType::compiler_jvmci)) {
+ MetaspaceShared::report_loading_error("The %s's JIT compiler setting (%s)"
+ " does not equal the current setting (%s).", file_type,
+ compilertype2name(archive_compiler_type), compilertype2name(compiler_type));
+ return false;
+ }
if (TrainingData::have_data()) {
if (_type_profile_level != TypeProfileLevel) {
MetaspaceShared::report_loading_error("The %s's TypeProfileLevel setting (%d)"
diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp
index e0b33fc8245..02390874f39 100644
--- a/src/hotspot/share/cds/filemap.hpp
+++ b/src/hotspot/share/cds/filemap.hpp
@@ -147,6 +147,7 @@ class FileMapHeader: private CDSFileMapHeaderBase {
size_t _ro_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the ro region
// The following are parameters that affect MethodData layout.
+ u1 _compiler_type;
uint _type_profile_level;
int _type_profile_args_limit;
int _type_profile_parms_limit;
diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp
index 233e64c5e3e..6c6b64a0cb2 100644
--- a/src/hotspot/share/cds/metaspaceShared.cpp
+++ b/src/hotspot/share/cds/metaspaceShared.cpp
@@ -1288,6 +1288,10 @@ void MetaspaceShared::report_loading_error(const char* format, ...) {
LogStream ls_cds(level, LogTagSetMapping::tagset());
LogStream& ls = CDSConfig::new_aot_flags_used() ? ls_aot : ls_cds;
+ if (!ls.is_enabled()) {
+ return;
+ }
+
va_list ap;
va_start(ap, format);
@@ -2014,10 +2018,7 @@ void MetaspaceShared::initialize_shared_spaces() {
TrainingData::print_archived_training_data_on(tty);
- if (AOTCodeCache::is_on_for_use()) {
- tty->print_cr("\n\nAOT Code");
- AOTCodeCache::print_on(tty);
- }
+ AOTCodeCache::print_on(tty);
// collect shared symbols and strings
CountSharedSymbols cl;
diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp
index a24a68f874b..c367170f2c2 100644
--- a/src/hotspot/share/ci/ciInstanceKlass.cpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.cpp
@@ -549,6 +549,11 @@ bool ciInstanceKlass::compute_has_trusted_loader() {
return java_lang_ClassLoader::is_trusted_loader(loader_oop);
}
+bool ciInstanceKlass::has_class_initializer() {
+ VM_ENTRY_MARK;
+ return get_instanceKlass()->class_initializer() != nullptr;
+}
+
// ------------------------------------------------------------------
// ciInstanceKlass::find_method
//
diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp
index 92a7434e8ba..d3d988e8735 100644
--- a/src/hotspot/share/ci/ciInstanceKlass.hpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.hpp
@@ -231,6 +231,8 @@ class ciInstanceKlass : public ciKlass {
ciInstanceKlass* unique_concrete_subklass();
bool has_finalizable_subclass();
+ bool has_class_initializer();
+
bool contains_field_offset(int offset);
// Get the instance of java.lang.Class corresponding to
diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp
index f9829e88c4a..72ec2866c6e 100644
--- a/src/hotspot/share/ci/ciReplay.cpp
+++ b/src/hotspot/share/ci/ciReplay.cpp
@@ -802,7 +802,7 @@ class CompileReplay : public StackObj {
// Make sure the existence of a prior compile doesn't stop this one
nmethod* nm = (entry_bci != InvocationEntryBci) ? method->lookup_osr_nmethod_for(entry_bci, comp_level, true) : method->code();
if (nm != nullptr) {
- nm->make_not_entrant(nmethod::ChangeReason::CI_replay);
+ nm->make_not_entrant(nmethod::InvalidationReason::CI_REPLAY);
}
replay_state = this;
CompileBroker::compile_method(methodHandle(THREAD, method), entry_bci, comp_level,
diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index bfb41f8384a..edec019bd70 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -3740,6 +3740,7 @@ void ClassFileParser::apply_parsed_class_metadata(
_cp->set_pool_holder(this_klass);
this_klass->set_constants(_cp);
this_klass->set_fieldinfo_stream(_fieldinfo_stream);
+ this_klass->set_fieldinfo_search_table(_fieldinfo_search_table);
this_klass->set_fields_status(_fields_status);
this_klass->set_methods(_methods);
this_klass->set_inner_classes(_inner_classes);
@@ -3749,6 +3750,8 @@ void ClassFileParser::apply_parsed_class_metadata(
this_klass->set_permitted_subclasses(_permitted_subclasses);
this_klass->set_record_components(_record_components);
+ DEBUG_ONLY(FieldInfoStream::validate_search_table(_cp, _fieldinfo_stream, _fieldinfo_search_table));
+
// Delay the setting of _local_interfaces and _transitive_interfaces until after
// initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
// be shared with _transitive_interfaces and _transitive_interfaces may be shared with
@@ -5056,6 +5059,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
// note that is not safe to use the fields in the parser from this point on
assert(nullptr == _cp, "invariant");
assert(nullptr == _fieldinfo_stream, "invariant");
+ assert(nullptr == _fieldinfo_search_table, "invariant");
assert(nullptr == _fields_status, "invariant");
assert(nullptr == _methods, "invariant");
assert(nullptr == _inner_classes, "invariant");
@@ -5276,6 +5280,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
_super_klass(),
_cp(nullptr),
_fieldinfo_stream(nullptr),
+ _fieldinfo_search_table(nullptr),
_fields_status(nullptr),
_methods(nullptr),
_inner_classes(nullptr),
@@ -5352,6 +5357,7 @@ void ClassFileParser::clear_class_metadata() {
// deallocated if classfile parsing returns an error.
_cp = nullptr;
_fieldinfo_stream = nullptr;
+ _fieldinfo_search_table = nullptr;
_fields_status = nullptr;
_methods = nullptr;
_inner_classes = nullptr;
@@ -5374,6 +5380,7 @@ ClassFileParser::~ClassFileParser() {
if (_fieldinfo_stream != nullptr) {
MetadataFactory::free_array(_loader_data, _fieldinfo_stream);
}
+ MetadataFactory::free_array(_loader_data, _fieldinfo_search_table);
if (_fields_status != nullptr) {
MetadataFactory::free_array(_loader_data, _fields_status);
@@ -5774,6 +5781,7 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st
_fieldinfo_stream =
FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count,
injected_fields_count, loader_data(), CHECK);
+ _fieldinfo_search_table = FieldInfoStream::create_search_table(_cp, _fieldinfo_stream, _loader_data, CHECK);
_fields_status =
MetadataFactory::new_array(_loader_data, _temp_field_info->length(),
FieldStatus(0), CHECK);
diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp
index 8f9f4ebea4d..707fbf6985f 100644
--- a/src/hotspot/share/classfile/classFileParser.hpp
+++ b/src/hotspot/share/classfile/classFileParser.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -123,6 +123,7 @@ class ClassFileParser {
const InstanceKlass* _super_klass;
ConstantPool* _cp;
Array* _fieldinfo_stream;
+ Array* _fieldinfo_search_table;
Array* _fields_status;
Array* _methods;
Array* _inner_classes;
diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp
index 825072cb13b..d6677faa91d 100644
--- a/src/hotspot/share/classfile/classLoaderData.cpp
+++ b/src/hotspot/share/classfile/classLoaderData.cpp
@@ -65,6 +65,7 @@
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/access.inline.hpp"
+#include "oops/jmethodIDTable.hpp"
#include "oops/klass.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/oopHandle.inline.hpp"
@@ -578,6 +579,33 @@ void ClassLoaderData::remove_class(Klass* scratch_class) {
ShouldNotReachHere(); // should have found this class!!
}
+void ClassLoaderData::add_jmethod_id(jmethodID mid) {
+ MutexLocker m1(metaspace_lock(), Mutex::_no_safepoint_check_flag);
+ if (_jmethod_ids == nullptr) {
+ _jmethod_ids = new (mtClass) GrowableArray(32, mtClass);
+ }
+ _jmethod_ids->push(mid);
+}
+
+// Method::remove_jmethod_ids removes jmethodID entries from the table which
+// releases memory.
+// Because native code (e.g., JVMTI agent) holding jmethod_ids may access them
+// after the associated classes and class loader are unloaded, subsequent lookups
+// for these ids will return null since they are no longer found in the table.
+// The Java Native Interface Specification says "method ID
+// does not prevent the VM from unloading the class from which the ID has
+// been derived. After the class is unloaded, the method or field ID becomes
+// invalid".
+void ClassLoaderData::remove_jmethod_ids() {
+ MutexLocker ml(JmethodIdCreation_lock, Mutex::_no_safepoint_check_flag);
+ for (int i = 0; i < _jmethod_ids->length(); i++) {
+ jmethodID mid = _jmethod_ids->at(i);
+ JmethodIDTable::remove(mid);
+ }
+ delete _jmethod_ids;
+ _jmethod_ids = nullptr;
+}
+
void ClassLoaderData::unload() {
_unloading = true;
@@ -599,19 +627,8 @@ void ClassLoaderData::unload() {
// after erroneous classes are released.
classes_do(InstanceKlass::unload_class);
- // Method::clear_jmethod_ids only sets the jmethod_ids to null without
- // releasing the memory for related JNIMethodBlocks and JNIMethodBlockNodes.
- // This is done intentionally because native code (e.g. JVMTI agent) holding
- // jmethod_ids may access them after the associated classes and class loader
- // are unloaded. The Java Native Interface Specification says "method ID
- // does not prevent the VM from unloading the class from which the ID has
- // been derived. After the class is unloaded, the method or field ID becomes
- // invalid". In real world usages, the native code may rely on jmethod_ids
- // being null after class unloading. Hence, it is unsafe to free the memory
- // from the VM side without knowing when native code is going to stop using
- // them.
if (_jmethod_ids != nullptr) {
- Method::clear_jmethod_ids(this);
+ remove_jmethod_ids();
}
}
@@ -1037,9 +1054,7 @@ void ClassLoaderData::print_on(outputStream* out) const {
out->print_cr(" - dictionary " INTPTR_FORMAT, p2i(_dictionary));
}
if (_jmethod_ids != nullptr) {
- out->print (" - jmethod count ");
- Method::print_jmethod_ids_count(this, out);
- out->print_cr("");
+ out->print_cr(" - jmethod count %d", _jmethod_ids->length());
}
out->print_cr(" - deallocate list " INTPTR_FORMAT, p2i(_deallocate_list));
out->print_cr(" - next CLD " INTPTR_FORMAT, p2i(_next));
diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp
index 19dbb0b1b36..e6302888283 100644
--- a/src/hotspot/share/classfile/classLoaderData.hpp
+++ b/src/hotspot/share/classfile/classLoaderData.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -53,7 +53,6 @@
// and provides iterators for root tracing and other GC operations.
class ClassLoaderDataGraph;
-class JNIMethodBlock;
class ModuleEntry;
class PackageEntry;
class ModuleEntryTable;
@@ -143,10 +142,9 @@ class ClassLoaderData : public CHeapObj {
ModuleEntry* _unnamed_module; // This class loader's unnamed module.
Dictionary* _dictionary; // The loaded InstanceKlasses, including initiated by this class loader
- // These method IDs are created for the class loader and set to null when the
- // class loader is unloaded. They are rarely freed, only for redefine classes
- // and if they lose a data race in InstanceKlass.
- JNIMethodBlock* _jmethod_ids;
+ // These method IDs are created for the class loader and removed when the
+ // class loader is unloaded.
+ GrowableArray* _jmethod_ids;
// Metadata to be deallocated when it's safe at class unloading, when
// this class loader isn't unloaded itself.
@@ -316,8 +314,9 @@ class ClassLoaderData : public CHeapObj {
void classes_do(KlassClosure* klass_closure);
Klass* klasses() { return _klasses; }
- JNIMethodBlock* jmethod_ids() const { return _jmethod_ids; }
- void set_jmethod_ids(JNIMethodBlock* new_block) { _jmethod_ids = new_block; }
+ void add_jmethod_id(jmethodID id);
+ void remove_jmethod_ids();
+ GrowableArray* jmethod_ids() const { return _jmethod_ids; }
void print() const;
void print_on(outputStream* out) const PRODUCT_RETURN;
diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp
index b6007923907..03afe89f4f8 100644
--- a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp
+++ b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp
@@ -301,7 +301,7 @@ void FieldLayout::reconstruct_layout(const InstanceKlass* ik, bool& has_instance
BasicType last_type;
int last_offset = -1;
while (ik != nullptr) {
- for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
+ for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
BasicType type = Signature::basic_type(fs.signature());
// distinction between static and non-static fields is missing
if (fs.access_flags().is_static()) continue;
@@ -461,7 +461,7 @@ void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlas
bool found = false;
const InstanceKlass* ik = super;
while (!found && ik != nullptr) {
- for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
+ for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
if (fs.offset() == b->offset()) {
output->print_cr(" @%d \"%s\" %s %d/%d %s",
b->offset(),
diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp
index 8d32d73fb47..0e8467289ad 100644
--- a/src/hotspot/share/classfile/javaClasses.cpp
+++ b/src/hotspot/share/classfile/javaClasses.cpp
@@ -967,6 +967,13 @@ void java_lang_Class::fixup_mirror(Klass* k, TRAPS) {
Array* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, k->class_loader_data(), CHECK);
ik->set_fieldinfo_stream(new_fis);
MetadataFactory::free_array(k->class_loader_data(), old_stream);
+
+ Array* old_table = ik->fieldinfo_search_table();
+ Array* search_table = FieldInfoStream::create_search_table(ik->constants(), new_fis, k->class_loader_data(), CHECK);
+ ik->set_fieldinfo_search_table(search_table);
+ MetadataFactory::free_array(k->class_loader_data(), old_table);
+
+ DEBUG_ONLY(FieldInfoStream::validate_search_table(ik->constants(), new_fis, search_table));
}
}
diff --git a/src/hotspot/share/classfile/stackMapTable.cpp b/src/hotspot/share/classfile/stackMapTable.cpp
index 1ed72e998fb..9e02956aceb 100644
--- a/src/hotspot/share/classfile/stackMapTable.cpp
+++ b/src/hotspot/share/classfile/stackMapTable.cpp
@@ -244,7 +244,7 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
int offset;
VerificationType* locals = nullptr;
u1 frame_type = _stream->get_u1(CHECK_NULL);
- if (frame_type < 64) {
+ if (frame_type <= SAME_FRAME_END) {
// same_frame
if (_first) {
offset = frame_type;
@@ -266,17 +266,17 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
_first = false;
return frame;
}
- if (frame_type < 128) {
+ if (frame_type <= SAME_LOCALS_1_STACK_ITEM_FRAME_END) {
// same_locals_1_stack_item_frame
if (_first) {
- offset = frame_type - 64;
+ offset = frame_type - SAME_LOCALS_1_STACK_ITEM_FRAME_START;
// Can't share the locals array since that is updated by the verifier.
if (_prev_frame->locals_size() > 0) {
locals = NEW_RESOURCE_ARRAY_IN_THREAD(
THREAD, VerificationType, _prev_frame->locals_size());
}
} else {
- offset = _prev_frame->offset() + frame_type - 63;
+ offset = _prev_frame->offset() + frame_type - (SAME_LOCALS_1_STACK_ITEM_FRAME_START - 1);
locals = _prev_frame->locals();
}
VerificationType* stack = NEW_RESOURCE_ARRAY_IN_THREAD(
@@ -340,13 +340,14 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
return frame;
}
- if (frame_type <= SAME_EXTENDED) {
+ if (frame_type <= SAME_FRAME_EXTENDED) {
// chop_frame or same_frame_extended
locals = _prev_frame->locals();
int length = _prev_frame->locals_size();
- int chops = SAME_EXTENDED - frame_type;
+ int chops = SAME_FRAME_EXTENDED - frame_type;
int new_length = length;
u1 flags = _prev_frame->flags();
+ assert(chops == 0 || (frame_type >= CHOP_FRAME_START && frame_type <= CHOP_FRAME_END), "should be");
if (chops != 0) {
new_length = chop(locals, length, chops);
check_verification_type_array_size(
@@ -380,9 +381,10 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
}
_first = false;
return frame;
- } else if (frame_type < SAME_EXTENDED + 4) {
+ } else if (frame_type <= APPEND_FRAME_END) {
// append_frame
- int appends = frame_type - SAME_EXTENDED;
+ assert(frame_type >= APPEND_FRAME_START && frame_type <= APPEND_FRAME_END, "should be");
+ int appends = frame_type - APPEND_FRAME_START + 1;
int real_length = _prev_frame->locals_size();
int new_length = real_length + appends*2;
locals = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, VerificationType, new_length);
@@ -412,7 +414,7 @@ StackMapFrame* StackMapReader::next_helper(TRAPS) {
_first = false;
return frame;
}
- if (frame_type == FULL) {
+ if (frame_type == FULL_FRAME) {
// full_frame
u1 flags = 0;
u2 locals_size = _stream->get_u2(CHECK_NULL);
diff --git a/src/hotspot/share/classfile/stackMapTable.hpp b/src/hotspot/share/classfile/stackMapTable.hpp
index cc4202f3280..6d4c0ce36c0 100644
--- a/src/hotspot/share/classfile/stackMapTable.hpp
+++ b/src/hotspot/share/classfile/stackMapTable.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -106,6 +106,7 @@ class StackMapStream : StackObj {
};
class StackMapReader : StackObj {
+ friend class VM_RedefineClasses;
private:
// information about the class and method
constantPoolHandle _cp;
@@ -148,9 +149,19 @@ class StackMapReader : StackObj {
}
enum {
+ SAME_FRAME_START = 0,
+ SAME_FRAME_END = 63,
+ SAME_LOCALS_1_STACK_ITEM_FRAME_START = 64,
+ SAME_LOCALS_1_STACK_ITEM_FRAME_END = 127,
+ RESERVED_START = 128,
+ RESERVED_END = 246,
SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247,
- SAME_EXTENDED = 251,
- FULL = 255
+ CHOP_FRAME_START = 248,
+ CHOP_FRAME_END = 250,
+ SAME_FRAME_EXTENDED = 251,
+ APPEND_FRAME_START = 252,
+ APPEND_FRAME_END = 254,
+ FULL_FRAME = 255
};
public:
diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp
index 6fd5c07e137..dd28e1a898c 100644
--- a/src/hotspot/share/classfile/vmIntrinsics.cpp
+++ b/src/hotspot/share/classfile/vmIntrinsics.cpp
@@ -99,7 +99,7 @@ bool vmIntrinsics::preserves_state(vmIntrinsics::ID id) {
case vmIntrinsics::_dpow:
case vmIntrinsics::_Preconditions_checkIndex:
case vmIntrinsics::_Preconditions_checkLongIndex:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
case vmIntrinsics::_Continuation_doYield:
case vmIntrinsics::_updateCRC32:
case vmIntrinsics::_updateBytesCRC32:
@@ -244,7 +244,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_storeFence:
case vmIntrinsics::_fullFence:
case vmIntrinsics::_countPositives:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
case vmIntrinsics::_Continuation_doYield:
case vmIntrinsics::_Continuation_enterSpecial:
case vmIntrinsics::_Continuation_pin:
@@ -289,8 +289,6 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_dsin:
case vmIntrinsics::_dcos:
case vmIntrinsics::_dtan:
- case vmIntrinsics::_dtanh:
- case vmIntrinsics::_dcbrt:
case vmIntrinsics::_dlog:
case vmIntrinsics::_dexp:
case vmIntrinsics::_dpow:
@@ -316,6 +314,13 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_fmaF:
if (!InlineMathNatives || !UseFMA) return true;
break;
+ case vmIntrinsics::_dtanh:
+ case vmIntrinsics::_dcbrt:
+ if (!InlineMathNatives || !InlineIntrinsics) return true;
+#if defined(AMD64) && (defined(COMPILER1) || defined(COMPILER2))
+ if (!UseLibmIntrinsic) return true;
+#endif
+ break;
case vmIntrinsics::_floatToFloat16:
case vmIntrinsics::_float16ToFloat:
if (!InlineIntrinsics) return true;
diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp
index eeefddfedfc..5be372075ed 100644
--- a/src/hotspot/share/classfile/vmIntrinsics.hpp
+++ b/src/hotspot/share/classfile/vmIntrinsics.hpp
@@ -461,7 +461,7 @@ class methodHandle;
do_signature(vectorizedMismatch_signature, "(Ljava/lang/Object;JLjava/lang/Object;JII)I") \
\
/* java/lang/ref/Reference */ \
- do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \
+ do_intrinsic(_Reference_get0, java_lang_ref_Reference, get0_name, void_object_signature, F_RN) \
do_intrinsic(_Reference_refersTo0, java_lang_ref_Reference, refersTo0_name, object_boolean_signature, F_RN) \
do_intrinsic(_PhantomReference_refersTo0, java_lang_ref_PhantomReference, refersTo0_name, object_boolean_signature, F_RN) \
do_intrinsic(_Reference_clear0, java_lang_ref_Reference, clear0_name, void_method_signature, F_RN) \
diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
index dc9ce61627b..c562e3e6ccf 100644
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -422,7 +422,7 @@ class SerializeClosure;
template(sp_name, "sp") \
template(pc_name, "pc") \
template(cs_name, "cs") \
- template(get_name, "get") \
+ template(get0_name, "get0") \
template(refersTo0_name, "refersTo0") \
template(clear0_name, "clear0") \
template(put_name, "put") \
diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp
index d0692ee678b..da0be2403fa 100644
--- a/src/hotspot/share/code/aotCodeCache.cpp
+++ b/src/hotspot/share/code/aotCodeCache.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -74,8 +74,7 @@ static void report_load_failure() {
vm_exit_during_initialization("Unable to use AOT Code Cache.", nullptr);
}
log_info(aot, codecache, init)("Unable to use AOT Code Cache.");
- AOTAdapterCaching = false;
- AOTStubCaching = false;
+ AOTCodeCache::disable_caching();
}
static void report_store_failure() {
@@ -84,10 +83,30 @@ static void report_store_failure() {
vm_abort(false);
}
log_info(aot, codecache, exit)("Unable to create AOT Code Cache.");
- AOTAdapterCaching = false;
- AOTStubCaching = false;
+ AOTCodeCache::disable_caching();
}
+// The sequence of AOT code caching flags and parametters settings.
+//
+// 1. The initial AOT code caching flags setting is done
+// during call to CDSConfig::check_vm_args_consistency().
+//
+// 2. The earliest AOT code state check done in compilationPolicy_init()
+// where we set number of compiler threads for AOT assembly phase.
+//
+// 3. We determine presence of AOT code in AOT Cache in
+// MetaspaceShared::open_static_archive() which is calles
+// after compilationPolicy_init() but before codeCache_init().
+//
+// 4. AOTCodeCache::initialize() is called during universe_init()
+// and does final AOT state and flags settings.
+//
+// 5. Finally AOTCodeCache::init2() is called after universe_init()
+// when all GC settings are finalized.
+
+// Next methods determine which action we do with AOT code depending
+// on phase of AOT process: assembly or production.
+
bool AOTCodeCache::is_dumping_adapter() {
return AOTAdapterCaching && is_on_for_dump();
}
@@ -104,6 +123,23 @@ bool AOTCodeCache::is_using_stub() {
return AOTStubCaching && is_on_for_use();
}
+// Next methods could be called regardless AOT code cache status.
+// Initially they are called during flags parsing and finilized
+// in AOTCodeCache::initialize().
+void AOTCodeCache::enable_caching() {
+ FLAG_SET_ERGO_IF_DEFAULT(AOTStubCaching, true);
+ FLAG_SET_ERGO_IF_DEFAULT(AOTAdapterCaching, true);
+}
+
+void AOTCodeCache::disable_caching() {
+ FLAG_SET_ERGO(AOTStubCaching, false);
+ FLAG_SET_ERGO(AOTAdapterCaching, false);
+}
+
+bool AOTCodeCache::is_caching_enabled() {
+ return AOTStubCaching || AOTAdapterCaching;
+}
+
static uint32_t encode_id(AOTCodeEntry::Kind kind, int id) {
assert(AOTCodeEntry::is_valid_entry_kind(kind), "invalid AOTCodeEntry kind %d", (int)kind);
// There can be a conflict of id between an Adapter and *Blob, but that should not cause any functional issue
@@ -125,19 +161,19 @@ uint AOTCodeCache::max_aot_code_size() {
return _max_aot_code_size;
}
-// This method is called during universe_init()
-// and does final AOT state and flags settings.
+// It is called from MetaspaceShared::initialize_shared_spaces()
+// which is called from universe_init().
+// At this point all AOT class linking seetings are finilized
+// and AOT cache is open so we can map AOT code region.
void AOTCodeCache::initialize() {
#if defined(ZERO) || !(defined(AMD64) || defined(AARCH64))
log_info(aot, codecache, init)("AOT Code Cache is not supported on this platform.");
- AOTAdapterCaching = false;
- AOTStubCaching = false;
+ disable_caching();
return;
#else
if (FLAG_IS_DEFAULT(AOTCache)) {
log_info(aot, codecache, init)("AOT Code Cache is not used: AOTCache is not specified.");
- AOTAdapterCaching = false;
- AOTStubCaching = false;
+ disable_caching();
return; // AOTCache must be specified to dump and use AOT code
}
@@ -158,18 +194,19 @@ void AOTCodeCache::initialize() {
bool is_dumping = false;
bool is_using = false;
if (CDSConfig::is_dumping_final_static_archive() && CDSConfig::is_dumping_aot_linked_classes()) {
- FLAG_SET_ERGO_IF_DEFAULT(AOTAdapterCaching, true);
- FLAG_SET_ERGO_IF_DEFAULT(AOTStubCaching, true);
is_dumping = true;
+ enable_caching();
+ is_dumping = is_caching_enabled();
} else if (CDSConfig::is_using_archive() && CDSConfig::is_using_aot_linked_classes()) {
- FLAG_SET_ERGO_IF_DEFAULT(AOTAdapterCaching, true);
- FLAG_SET_ERGO_IF_DEFAULT(AOTStubCaching, true);
- is_using = true;
+ enable_caching();
+ is_using = is_caching_enabled();
} else {
log_info(aot, codecache, init)("AOT Code Cache is not used: AOT Class Linking is not used.");
+ disable_caching();
return; // nothing to do
}
- if (!AOTAdapterCaching && !AOTStubCaching) {
+ if (!(is_dumping || is_using)) {
+ disable_caching();
return; // AOT code caching disabled on command line
}
_max_aot_code_size = AOTCodeMaxSize;
@@ -182,6 +219,7 @@ void AOTCodeCache::initialize() {
size_t aot_code_size = is_using ? AOTCacheAccess::get_aot_code_region_size() : 0;
if (is_using && aot_code_size == 0) {
log_info(aot, codecache, init)("AOT Code Cache is empty");
+ disable_caching();
return;
}
if (!open_cache(is_dumping, is_using)) {
@@ -201,10 +239,11 @@ void AOTCodeCache::initialize() {
static AOTCodeCache* opened_cache = nullptr; // Use this until we verify the cache
AOTCodeCache* AOTCodeCache::_cache = nullptr;
+DEBUG_ONLY( bool AOTCodeCache::_passed_init2 = false; )
-// This method is called after universe_init()
-// when all GC settings are finalized.
+// It is called after universe_init() when all GC settings are finalized.
void AOTCodeCache::init2() {
+ DEBUG_ONLY( _passed_init2 = true; )
if (opened_cache == nullptr) {
return;
}
@@ -220,7 +259,6 @@ void AOTCodeCache::init2() {
AOTCodeAddressTable* table = opened_cache->_table;
assert(table != nullptr, "should be initialized already");
table->init_extrs();
- table->init_early_stubs();
// Now cache and address table are ready for AOT code generation
_cache = opened_cache;
@@ -312,6 +350,13 @@ AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) :
_table = new AOTCodeAddressTable();
}
+void AOTCodeCache::init_early_stubs_table() {
+ AOTCodeAddressTable* table = addr_table();
+ if (table != nullptr) {
+ table->init_early_stubs();
+ }
+}
+
void AOTCodeCache::init_shared_blobs_table() {
AOTCodeAddressTable* table = addr_table();
if (table != nullptr) {
@@ -344,6 +389,7 @@ AOTCodeCache::~AOTCodeCache() {
_store_buffer = nullptr;
}
if (_table != nullptr) {
+ MutexLocker ml(AOTCodeCStrings_lock, Mutex::_no_safepoint_check_flag);
delete _table;
_table = nullptr;
}
@@ -774,6 +820,9 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind
// we need to take a lock to prevent race between compiler threads generating AOT code
// and the main thread generating adapter
MutexLocker ml(Compile_lock);
+ if (!is_on()) {
+ return false; // AOT code cache was already dumped and closed.
+ }
if (!cache->align_write()) {
return false;
}
@@ -1485,6 +1534,7 @@ void AOTCodeCache::load_strings() {
int AOTCodeCache::store_strings() {
if (_C_strings_used > 0) {
+ MutexLocker ml(AOTCodeCStrings_lock, Mutex::_no_safepoint_check_flag);
uint offset = _write_position;
uint length = 0;
uint* lengths = (uint *)reserve_bytes(sizeof(uint) * _C_strings_used);
@@ -1510,15 +1560,17 @@ int AOTCodeCache::store_strings() {
const char* AOTCodeCache::add_C_string(const char* str) {
if (is_on_for_dump() && str != nullptr) {
- return _cache->_table->add_C_string(str);
+ MutexLocker ml(AOTCodeCStrings_lock, Mutex::_no_safepoint_check_flag);
+ AOTCodeAddressTable* table = addr_table();
+ if (table != nullptr) {
+ return table->add_C_string(str);
+ }
}
return str;
}
const char* AOTCodeAddressTable::add_C_string(const char* str) {
if (_extrs_complete) {
- LogStreamHandle(Trace, aot, codecache, stringtable) log; // ctor outside lock
- MutexLocker ml(AOTCodeCStrings_lock, Mutex::_no_safepoint_check_flag);
// Check previous strings address
for (int i = 0; i < _C_strings_count; i++) {
if (_C_strings_in[i] == str) {
@@ -1535,9 +1587,7 @@ const char* AOTCodeAddressTable::add_C_string(const char* str) {
_C_strings_in[_C_strings_count] = str;
const char* dup = os::strdup(str);
_C_strings[_C_strings_count++] = dup;
- if (log.is_enabled()) {
- log.print_cr("add_C_string: [%d] " INTPTR_FORMAT " '%s'", _C_strings_count, p2i(dup), dup);
- }
+ log_trace(aot, codecache, stringtable)("add_C_string: [%d] " INTPTR_FORMAT " '%s'", _C_strings_count, p2i(dup), dup);
return dup;
} else {
assert(false, "Number of C strings >= MAX_STR_COUNT");
@@ -1691,11 +1741,13 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB
return id;
}
+// This is called after initialize() but before init2()
+// and _cache is not set yet.
void AOTCodeCache::print_on(outputStream* st) {
- AOTCodeCache* cache = open_for_use();
- if (cache != nullptr) {
- uint count = cache->_load_header->entries_count();
- uint* search_entries = (uint*)cache->addr(cache->_load_header->entries_offset()); // [id, index]
+ if (opened_cache != nullptr && opened_cache->for_use()) {
+ st->print_cr("\nAOT Code Cache");
+ uint count = opened_cache->_load_header->entries_count();
+ uint* search_entries = (uint*)opened_cache->addr(opened_cache->_load_header->entries_offset()); // [id, index]
AOTCodeEntry* load_entries = (AOTCodeEntry*)(search_entries + 2 * count);
for (uint i = 0; i < count; i++) {
@@ -1705,12 +1757,10 @@ void AOTCodeCache::print_on(outputStream* st) {
uint entry_position = entry->offset();
uint name_offset = entry->name_offset() + entry_position;
- const char* saved_name = cache->addr(name_offset);
+ const char* saved_name = opened_cache->addr(name_offset);
- st->print_cr("%4u: entry_idx:%4u Kind:%u Id:%u size=%u '%s'",
- i, index, entry->kind(), entry->id(), entry->size(), saved_name);
+ st->print_cr("%4u: %10s idx:%4u Id:%u size=%u '%s'",
+ i, aot_code_entry_kind_name[entry->kind()], index, entry->id(), entry->size(), saved_name);
}
- } else {
- st->print_cr("failed to map code cache");
}
}
diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp
index e7b056ac974..1595f19e905 100644
--- a/src/hotspot/share/code/aotCodeCache.hpp
+++ b/src/hotspot/share/code/aotCodeCache.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -297,6 +297,7 @@ class AOTCodeCache : public CHeapObj {
void load_strings();
int store_strings();
+ static void init_early_stubs_table() NOT_CDS_RETURN;
static void init_shared_blobs_table() NOT_CDS_RETURN;
static void init_early_c1_table() NOT_CDS_RETURN;
@@ -346,7 +347,8 @@ class AOTCodeCache : public CHeapObj {
// Static access
private:
- static AOTCodeCache* _cache;
+ static AOTCodeCache* _cache;
+ DEBUG_ONLY( static bool _passed_init2; )
static bool open_cache(bool is_dumping, bool is_using);
bool verify_config() {
@@ -356,19 +358,20 @@ class AOTCodeCache : public CHeapObj {
return true;
}
public:
- static AOTCodeCache* cache() { return _cache; }
+ static AOTCodeCache* cache() { assert(_passed_init2, "Too early to ask"); return _cache; }
static void initialize() NOT_CDS_RETURN;
static void init2() NOT_CDS_RETURN;
static void close() NOT_CDS_RETURN;
- static bool is_on() CDS_ONLY({ return _cache != nullptr && !_cache->closing(); }) NOT_CDS_RETURN_(false);
+ static bool is_on() CDS_ONLY({ return cache() != nullptr && !_cache->closing(); }) NOT_CDS_RETURN_(false);
static bool is_on_for_use() { return is_on() && _cache->for_use(); }
static bool is_on_for_dump() { return is_on() && _cache->for_dump(); }
-
- static bool is_dumping_adapter() NOT_CDS_RETURN_(false);
- static bool is_using_adapter() NOT_CDS_RETURN_(false);
-
static bool is_dumping_stub() NOT_CDS_RETURN_(false);
+ static bool is_dumping_adapter() NOT_CDS_RETURN_(false);
static bool is_using_stub() NOT_CDS_RETURN_(false);
+ static bool is_using_adapter() NOT_CDS_RETURN_(false);
+ static void enable_caching() NOT_CDS_RETURN;
+ static void disable_caching() NOT_CDS_RETURN;
+ static bool is_caching_enabled() NOT_CDS_RETURN_(false);
static const char* add_C_string(const char* str) NOT_CDS_RETURN_(str);
diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp
index 3a5da1d7cd2..6a5cc0f4a40 100644
--- a/src/hotspot/share/code/codeCache.cpp
+++ b/src/hotspot/share/code/codeCache.cpp
@@ -433,7 +433,7 @@ void CodeCache::add_heap(ReservedSpace rs, const char* name, CodeBlobType code_b
add_heap(heap);
// Reserve Space
- size_t size_initial = MIN2((size_t)InitialCodeCacheSize, rs.size());
+ size_t size_initial = MIN2(InitialCodeCacheSize, rs.size());
size_initial = align_up(size_initial, rs.page_size());
if (!heap->reserve(rs, size_initial, CodeCacheSegmentSize)) {
vm_exit_during_initialization(err_msg("Could not reserve enough space in %s (%zuK)",
@@ -1106,9 +1106,9 @@ size_t CodeCache::freelists_length() {
void icache_init();
void CodeCache::initialize() {
- assert(CodeCacheSegmentSize >= (uintx)CodeEntryAlignment, "CodeCacheSegmentSize must be large enough to align entry points");
+ assert(CodeCacheSegmentSize >= (size_t)CodeEntryAlignment, "CodeCacheSegmentSize must be large enough to align entry points");
#ifdef COMPILER2
- assert(CodeCacheSegmentSize >= (uintx)OptoLoopAlignment, "CodeCacheSegmentSize must be large enough to align inner loops");
+ assert(CodeCacheSegmentSize >= (size_t)OptoLoopAlignment, "CodeCacheSegmentSize must be large enough to align inner loops");
#endif
assert(CodeCacheSegmentSize >= sizeof(jdouble), "CodeCacheSegmentSize must be large enough to align constants");
// This was originally just a check of the alignment, causing failure, instead, round
@@ -1361,7 +1361,7 @@ void CodeCache::make_marked_nmethods_deoptimized() {
while(iter.next()) {
nmethod* nm = iter.method();
if (nm->is_marked_for_deoptimization() && !nm->has_been_deoptimized() && nm->can_be_deoptimized()) {
- nm->make_not_entrant(nmethod::ChangeReason::marked_for_deoptimization);
+ nm->make_not_entrant(nmethod::InvalidationReason::MARKED_FOR_DEOPTIMIZATION);
nm->make_deoptimized();
}
}
diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp
index acebaae6ba4..cfd31b8104a 100644
--- a/src/hotspot/share/code/nmethod.cpp
+++ b/src/hotspot/share/code/nmethod.cpp
@@ -28,7 +28,6 @@
#include "code/dependencies.hpp"
#include "code/nativeInst.hpp"
#include "code/nmethod.inline.hpp"
-#include "code/relocInfo.hpp"
#include "code/scopeDesc.hpp"
#include "compiler/abstractCompiler.hpp"
#include "compiler/compilationLog.hpp"
@@ -692,13 +691,6 @@ address nmethod::oops_reloc_begin() const {
}
address low_boundary = verified_entry_point();
- if (!is_in_use()) {
- low_boundary += NativeJump::instruction_size;
- // %%% Note: On SPARC we patch only a 4-byte trap, not a full NativeJump.
- // This means that the low_boundary is going to be a little too high.
- // This shouldn't matter, since oops of non-entrant methods are never used.
- // In fact, why are we bothering to look at oops in a non-entrant method??
- }
return low_boundary;
}
@@ -1653,10 +1645,6 @@ void nmethod::maybe_print_nmethod(const DirectiveSet* directive) {
}
void nmethod::print_nmethod(bool printmethod) {
- // Enter a critical section to prevent a race with deopts that patch code and updates the relocation info.
- // Unfortunately, we have to lock the NMethodState_lock before the tty lock due to the deadlock rules and
- // cannot lock in a more finely grained manner.
- ConditionalMutexLocker ml(NMethodState_lock, !NMethodState_lock->owned_by_self(), Mutex::_no_safepoint_check_flag);
ttyLocker ttyl; // keep the following output all in one block
if (xtty != nullptr) {
xtty->begin_head("print_nmethod");
@@ -1975,12 +1963,12 @@ void nmethod::invalidate_osr_method() {
}
}
-void nmethod::log_state_change(ChangeReason change_reason) const {
+void nmethod::log_state_change(InvalidationReason invalidation_reason) const {
if (LogCompilation) {
if (xtty != nullptr) {
ttyLocker ttyl; // keep the following output all in one block
xtty->begin_elem("make_not_entrant thread='%zu' reason='%s'",
- os::current_thread_id(), change_reason_to_string(change_reason));
+ os::current_thread_id(), invalidation_reason_to_string(invalidation_reason));
log_identity(xtty);
xtty->stamp();
xtty->end_elem();
@@ -1989,7 +1977,7 @@ void nmethod::log_state_change(ChangeReason change_reason) const {
ResourceMark rm;
stringStream ss(NEW_RESOURCE_ARRAY(char, 256), 256);
- ss.print("made not entrant: %s", change_reason_to_string(change_reason));
+ ss.print("made not entrant: %s", invalidation_reason_to_string(invalidation_reason));
CompileTask::print_ul(this, ss.freeze());
if (PrintCompilation) {
@@ -2004,7 +1992,7 @@ void nmethod::unlink_from_method() {
}
// Invalidate code
-bool nmethod::make_not_entrant(ChangeReason change_reason) {
+bool nmethod::make_not_entrant(InvalidationReason invalidation_reason) {
// This can be called while the system is already at a safepoint which is ok
NoSafepointVerifier nsv;
@@ -2040,19 +2028,7 @@ bool nmethod::make_not_entrant(ChangeReason change_reason) {
} else {
// The caller can be calling the method statically or through an inline
// cache call.
- NativeJump::patch_verified_entry(entry_point(), verified_entry_point(),
- SharedRuntime::get_handle_wrong_method_stub());
-
- // Update the relocation info for the patched entry.
- // First, get the old relocation info...
- RelocIterator iter(this, verified_entry_point(), verified_entry_point() + 8);
- if (iter.next() && iter.addr() == verified_entry_point()) {
- Relocation* old_reloc = iter.reloc();
- // ...then reset the iterator to update it.
- RelocIterator iter(this, verified_entry_point(), verified_entry_point() + 8);
- relocInfo::change_reloc_info_for_address(&iter, verified_entry_point(), old_reloc->type(),
- relocInfo::relocType::runtime_call_type);
- }
+ BarrierSet::barrier_set()->barrier_set_nmethod()->make_not_entrant(this);
}
if (update_recompile_counts()) {
@@ -2073,7 +2049,7 @@ bool nmethod::make_not_entrant(ChangeReason change_reason) {
assert(success, "Transition can't fail");
// Log the transition once
- log_state_change(change_reason);
+ log_state_change(invalidation_reason);
// Remove nmethod from method.
unlink_from_method();
@@ -2084,7 +2060,7 @@ bool nmethod::make_not_entrant(ChangeReason change_reason) {
// Invalidate can't occur while holding the NMethodState_lock
JVMCINMethodData* nmethod_data = jvmci_nmethod_data();
if (nmethod_data != nullptr) {
- nmethod_data->invalidate_nmethod_mirror(this);
+ nmethod_data->invalidate_nmethod_mirror(this, invalidation_reason);
}
#endif
@@ -2122,7 +2098,9 @@ void nmethod::unlink() {
// Clear the link between this nmethod and a HotSpotNmethod mirror
JVMCINMethodData* nmethod_data = jvmci_nmethod_data();
if (nmethod_data != nullptr) {
- nmethod_data->invalidate_nmethod_mirror(this);
+ nmethod_data->invalidate_nmethod_mirror(this, is_cold() ?
+ nmethod::InvalidationReason::UNLOADING_COLD :
+ nmethod::InvalidationReason::UNLOADING);
}
#endif
@@ -2942,9 +2920,6 @@ void nmethod::verify() {
if (is_not_entrant())
return;
- // Make sure all the entry points are correctly aligned for patching.
- NativeJump::check_verified_entry_alignment(entry_point(), verified_entry_point());
-
// assert(oopDesc::is_oop(method()), "must be valid");
ResourceMark rm;
diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp
index 7453bdfa0ef..55e63c33e1a 100644
--- a/src/hotspot/share/code/nmethod.hpp
+++ b/src/hotspot/share/code/nmethod.hpp
@@ -471,77 +471,74 @@ class nmethod : public CodeBlob {
void oops_do_set_strong_done(nmethod* old_head);
public:
- enum class ChangeReason : u1 {
- C1_codepatch,
- C1_deoptimize,
- C1_deoptimize_for_patching,
- C1_predicate_failed_trap,
- CI_replay,
- JVMCI_invalidate_nmethod,
- JVMCI_invalidate_nmethod_mirror,
- JVMCI_materialize_virtual_object,
- JVMCI_new_installation,
- JVMCI_register_method,
- JVMCI_replacing_with_new_code,
- JVMCI_reprofile,
- marked_for_deoptimization,
- missing_exception_handler,
- not_used,
- OSR_invalidation_back_branch,
- OSR_invalidation_for_compiling_with_C1,
- OSR_invalidation_of_lower_level,
- set_native_function,
- uncommon_trap,
- whitebox_deoptimization,
- zombie,
+ // If you change anything in this enum please patch
+ // vmStructs_jvmci.cpp accordingly.
+ enum class InvalidationReason : s1 {
+ NOT_INVALIDATED = -1,
+ C1_CODEPATCH,
+ C1_DEOPTIMIZE,
+ C1_DEOPTIMIZE_FOR_PATCHING,
+ C1_PREDICATE_FAILED_TRAP,
+ CI_REPLAY,
+ UNLOADING,
+ UNLOADING_COLD,
+ JVMCI_INVALIDATE,
+ JVMCI_MATERIALIZE_VIRTUAL_OBJECT,
+ JVMCI_REPLACED_WITH_NEW_CODE,
+ JVMCI_REPROFILE,
+ MARKED_FOR_DEOPTIMIZATION,
+ MISSING_EXCEPTION_HANDLER,
+ NOT_USED,
+ OSR_INVALIDATION_BACK_BRANCH,
+ OSR_INVALIDATION_FOR_COMPILING_WITH_C1,
+ OSR_INVALIDATION_OF_LOWER_LEVEL,
+ SET_NATIVE_FUNCTION,
+ UNCOMMON_TRAP,
+ WHITEBOX_DEOPTIMIZATION,
+ ZOMBIE,
+ INVALIDATION_REASONS_COUNT
};
- static const char* change_reason_to_string(ChangeReason change_reason) {
- switch (change_reason) {
- case ChangeReason::C1_codepatch:
+ static const char* invalidation_reason_to_string(InvalidationReason invalidation_reason) {
+ switch (invalidation_reason) {
+ case InvalidationReason::C1_CODEPATCH:
return "C1 code patch";
- case ChangeReason::C1_deoptimize:
+ case InvalidationReason::C1_DEOPTIMIZE:
return "C1 deoptimized";
- case ChangeReason::C1_deoptimize_for_patching:
+ case InvalidationReason::C1_DEOPTIMIZE_FOR_PATCHING:
return "C1 deoptimize for patching";
- case ChangeReason::C1_predicate_failed_trap:
+ case InvalidationReason::C1_PREDICATE_FAILED_TRAP:
return "C1 predicate failed trap";
- case ChangeReason::CI_replay:
+ case InvalidationReason::CI_REPLAY:
return "CI replay";
- case ChangeReason::JVMCI_invalidate_nmethod:
- return "JVMCI invalidate nmethod";
- case ChangeReason::JVMCI_invalidate_nmethod_mirror:
- return "JVMCI invalidate nmethod mirror";
- case ChangeReason::JVMCI_materialize_virtual_object:
+ case InvalidationReason::JVMCI_INVALIDATE:
+ return "JVMCI invalidate";
+ case InvalidationReason::JVMCI_MATERIALIZE_VIRTUAL_OBJECT:
return "JVMCI materialize virtual object";
- case ChangeReason::JVMCI_new_installation:
- return "JVMCI new installation";
- case ChangeReason::JVMCI_register_method:
- return "JVMCI register method";
- case ChangeReason::JVMCI_replacing_with_new_code:
- return "JVMCI replacing with new code";
- case ChangeReason::JVMCI_reprofile:
+ case InvalidationReason::JVMCI_REPLACED_WITH_NEW_CODE:
+ return "JVMCI replaced with new code";
+ case InvalidationReason::JVMCI_REPROFILE:
return "JVMCI reprofile";
- case ChangeReason::marked_for_deoptimization:
+ case InvalidationReason::MARKED_FOR_DEOPTIMIZATION:
return "marked for deoptimization";
- case ChangeReason::missing_exception_handler:
+ case InvalidationReason::MISSING_EXCEPTION_HANDLER:
return "missing exception handler";
- case ChangeReason::not_used:
+ case InvalidationReason::NOT_USED:
return "not used";
- case ChangeReason::OSR_invalidation_back_branch:
+ case InvalidationReason::OSR_INVALIDATION_BACK_BRANCH:
return "OSR invalidation back branch";
- case ChangeReason::OSR_invalidation_for_compiling_with_C1:
+ case InvalidationReason::OSR_INVALIDATION_FOR_COMPILING_WITH_C1:
return "OSR invalidation for compiling with C1";
- case ChangeReason::OSR_invalidation_of_lower_level:
+ case InvalidationReason::OSR_INVALIDATION_OF_LOWER_LEVEL:
return "OSR invalidation of lower level";
- case ChangeReason::set_native_function:
+ case InvalidationReason::SET_NATIVE_FUNCTION:
return "set native function";
- case ChangeReason::uncommon_trap:
+ case InvalidationReason::UNCOMMON_TRAP:
return "uncommon trap";
- case ChangeReason::whitebox_deoptimization:
+ case InvalidationReason::WHITEBOX_DEOPTIMIZATION:
return "whitebox deoptimization";
- case ChangeReason::zombie:
+ case InvalidationReason::ZOMBIE:
return "zombie";
default: {
assert(false, "Unhandled reason");
@@ -712,8 +709,8 @@ class nmethod : public CodeBlob {
// alive. It is used when an uncommon trap happens. Returns true
// if this thread changed the state of the nmethod or false if
// another thread performed the transition.
- bool make_not_entrant(ChangeReason change_reason);
- bool make_not_used() { return make_not_entrant(ChangeReason::not_used); }
+ bool make_not_entrant(InvalidationReason invalidation_reason);
+ bool make_not_used() { return make_not_entrant(InvalidationReason::NOT_USED); }
bool is_marked_for_deoptimization() const { return deoptimization_status() != not_marked; }
bool has_been_deoptimized() const { return deoptimization_status() == deoptimize_done; }
@@ -1026,7 +1023,7 @@ class nmethod : public CodeBlob {
// Logging
void log_identity(xmlStream* log) const;
void log_new_nmethod() const;
- void log_state_change(ChangeReason change_reason) const;
+ void log_state_change(InvalidationReason invalidation_reason) const;
// Prints block-level comments, including nmethod specific block labels:
void print_nmethod_labels(outputStream* stream, address block_begin, bool print_section_labels=true) const;
diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp
index bab437eaade..fd1357398f5 100644
--- a/src/hotspot/share/compiler/compilationPolicy.cpp
+++ b/src/hotspot/share/compiler/compilationPolicy.cpp
@@ -385,7 +385,7 @@ class CallPredicate : AllStatic {
double CompilationPolicy::threshold_scale(CompLevel level, int feedback_k) {
int comp_count = compiler_count(level);
- if (comp_count > 0) {
+ if (comp_count > 0 && feedback_k > 0) {
double queue_size = CompileBroker::queue_size(level);
double k = (double)queue_size / ((double)feedback_k * (double)comp_count) + 1;
@@ -571,11 +571,18 @@ void CompilationPolicy::initialize() {
#ifdef COMPILER2
c2_size = C2Compiler::initial_code_buffer_size();
#endif
- size_t buffer_size = c1_only ? c1_size : (c1_size/3 + 2*c2_size/3);
- int max_count = (ReservedCodeCacheSize - (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3))) / (int)buffer_size;
- if (count > max_count) {
+ size_t buffer_size = 0;
+ if (c1_only) {
+ buffer_size = c1_size;
+ } else if (c2_only) {
+ buffer_size = c2_size;
+ } else {
+ buffer_size = c1_size / 3 + 2 * c2_size / 3;
+ }
+ size_t max_count = (NonNMethodCodeHeapSize - (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3))) / buffer_size;
+ if ((size_t)count > max_count) {
// Lower the compiler count such that all buffers fit into the code cache
- count = MAX2(max_count, min_count);
+ count = MAX2((int)max_count, min_count);
}
FLAG_SET_ERGO(CICompilerCount, count);
}
@@ -591,7 +598,7 @@ void CompilationPolicy::initialize() {
count = 3;
FLAG_SET_ERGO(CICompilerCount, count);
}
-#endif
+#endif // _LP64
if (c1_only) {
// No C2 compiler threads are needed
@@ -924,7 +931,7 @@ void CompilationPolicy::compile(const methodHandle& mh, int bci, CompLevel level
nmethod* osr_nm = mh->lookup_osr_nmethod_for(bci, CompLevel_simple, false);
if (osr_nm != nullptr && osr_nm->comp_level() > CompLevel_simple) {
// Invalidate the existing OSR nmethod so that a compile at CompLevel_simple is permitted.
- osr_nm->make_not_entrant(nmethod::ChangeReason::OSR_invalidation_for_compiling_with_C1);
+ osr_nm->make_not_entrant(nmethod::InvalidationReason::OSR_INVALIDATION_FOR_COMPILING_WITH_C1);
}
compile(mh, bci, CompLevel_simple, THREAD);
}
@@ -1516,7 +1523,7 @@ void CompilationPolicy::method_back_branch_event(const methodHandle& mh, const m
int osr_bci = nm->is_osr_method() ? nm->osr_entry_bci() : InvocationEntryBci;
print_event(MAKE_NOT_ENTRANT, mh(), mh(), osr_bci, level);
}
- nm->make_not_entrant(nmethod::ChangeReason::OSR_invalidation_back_branch);
+ nm->make_not_entrant(nmethod::InvalidationReason::OSR_INVALIDATION_BACK_BRANCH);
}
}
// Fix up next_level if necessary to avoid deopts
diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp
index f82e96be6ae..fa894559fef 100644
--- a/src/hotspot/share/compiler/compilerDefinitions.cpp
+++ b/src/hotspot/share/compiler/compilerDefinitions.cpp
@@ -313,7 +313,7 @@ void CompilerConfig::set_compilation_policy_flags() {
// Increase the code cache size - tiered compiles a lot more.
if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) {
FLAG_SET_ERGO(ReservedCodeCacheSize,
- MIN2(CODE_CACHE_DEFAULT_LIMIT, (size_t)ReservedCodeCacheSize * 5));
+ MIN2(CODE_CACHE_DEFAULT_LIMIT, ReservedCodeCacheSize * 5));
}
// Enable SegmentedCodeCache if tiered compilation is enabled, ReservedCodeCacheSize >= 240M
// and the code cache contains at least 8 pages (segmentation disables advantage of huge pages).
@@ -475,26 +475,26 @@ void CompilerConfig::set_jvmci_specific_flags() {
bool CompilerConfig::check_args_consistency(bool status) {
// Check lower bounds of the code cache
// Template Interpreter code is approximately 3X larger in debug builds.
- uint min_code_cache_size = CodeCacheMinimumUseSpace DEBUG_ONLY(* 3);
+ size_t min_code_cache_size = CodeCacheMinimumUseSpace DEBUG_ONLY(* 3);
if (ReservedCodeCacheSize < InitialCodeCacheSize) {
jio_fprintf(defaultStream::error_stream(),
- "Invalid ReservedCodeCacheSize: %dK. Must be at least InitialCodeCacheSize=%dK.\n",
+ "Invalid ReservedCodeCacheSize: %zuK. Must be at least InitialCodeCacheSize=%zuK.\n",
ReservedCodeCacheSize/K, InitialCodeCacheSize/K);
status = false;
} else if (ReservedCodeCacheSize < min_code_cache_size) {
jio_fprintf(defaultStream::error_stream(),
- "Invalid ReservedCodeCacheSize=%dK. Must be at least %uK.\n", ReservedCodeCacheSize/K,
+ "Invalid ReservedCodeCacheSize=%zuK. Must be at least %zuK.\n", ReservedCodeCacheSize/K,
min_code_cache_size/K);
status = false;
} else if (ReservedCodeCacheSize > CODE_CACHE_SIZE_LIMIT) {
// Code cache size larger than CODE_CACHE_SIZE_LIMIT is not supported.
jio_fprintf(defaultStream::error_stream(),
- "Invalid ReservedCodeCacheSize=%dM. Must be at most %uM.\n", ReservedCodeCacheSize/M,
+ "Invalid ReservedCodeCacheSize=%zuM. Must be at most %zuM.\n", ReservedCodeCacheSize/M,
CODE_CACHE_SIZE_LIMIT/M);
status = false;
} else if (NonNMethodCodeHeapSize < min_code_cache_size) {
jio_fprintf(defaultStream::error_stream(),
- "Invalid NonNMethodCodeHeapSize=%dK. Must be at least %uK.\n", NonNMethodCodeHeapSize/K,
+ "Invalid NonNMethodCodeHeapSize=%zuK. Must be at least %zuK.\n", NonNMethodCodeHeapSize/K,
min_code_cache_size/K);
status = false;
}
diff --git a/src/hotspot/share/compiler/compilerDefinitions.hpp b/src/hotspot/share/compiler/compilerDefinitions.hpp
index bacc1f73f97..1c8c65b2a53 100644
--- a/src/hotspot/share/compiler/compilerDefinitions.hpp
+++ b/src/hotspot/share/compiler/compilerDefinitions.hpp
@@ -149,6 +149,8 @@ class CompilerConfig : public AllStatic {
inline static bool is_c2_or_jvmci_compiler_only();
inline static bool is_c2_or_jvmci_compiler_enabled();
+ inline static CompilerType compiler_type();
+
private:
static bool is_compilation_mode_selected();
static void set_compilation_policy_flags();
diff --git a/src/hotspot/share/compiler/compilerDefinitions.inline.hpp b/src/hotspot/share/compiler/compilerDefinitions.inline.hpp
index fe17b77535f..21bf80549f4 100644
--- a/src/hotspot/share/compiler/compilerDefinitions.inline.hpp
+++ b/src/hotspot/share/compiler/compilerDefinitions.inline.hpp
@@ -131,4 +131,17 @@ inline bool CompilerConfig::is_c2_or_jvmci_compiler_enabled() {
return is_c2_enabled() || is_jvmci_compiler_enabled();
}
+// Return type of most optimizing compiler which is used
+inline CompilerType CompilerConfig::compiler_type() {
+ CompilerType compiler_type = CompilerType::compiler_none; // Interpreter only
+ if (CompilerConfig::is_c2_enabled()) {
+ compiler_type = CompilerType::compiler_c2;
+ } else if (CompilerConfig::is_jvmci_compiler_enabled()) {
+ compiler_type = CompilerType::compiler_jvmci;
+ } else if (CompilerConfig::is_c1_enabled()) {
+ compiler_type = CompilerType::compiler_c1;
+ }
+ return compiler_type;
+}
+
#endif // SHARE_COMPILER_COMPILERDEFINITIONS_INLINE_HPP
diff --git a/src/hotspot/share/compiler/compiler_globals.hpp b/src/hotspot/share/compiler/compiler_globals.hpp
index 3c47b991120..605c0c58869 100644
--- a/src/hotspot/share/compiler/compiler_globals.hpp
+++ b/src/hotspot/share/compiler/compiler_globals.hpp
@@ -234,12 +234,14 @@
\
product(intx, Tier3LoadFeedback, 5, \
"Tier 3 thresholds will increase twofold when C1 queue size " \
- "reaches this amount per compiler thread") \
+ "reaches this amount per compiler thread" \
+ "Passing 0 disables the threshold scaling") \
range(0, max_jint) \
\
product(intx, Tier4LoadFeedback, 3, \
"Tier 4 thresholds will increase twofold when C2 queue size " \
- "reaches this amount per compiler thread") \
+ "reaches this amount per compiler thread" \
+ "Passing 0 disables the threshold scaling") \
range(0, max_jint) \
\
product(intx, TieredCompileTaskTimeout, 50, \
@@ -263,11 +265,11 @@
\
product(intx, TieredRateUpdateMinTime, 1, \
"Minimum rate sampling interval (in milliseconds)") \
- range(0, max_intx) \
+ range(1, max_intx) \
\
product(intx, TieredRateUpdateMaxTime, 25, \
"Maximum rate sampling interval (in milliseconds)") \
- range(0, max_intx) \
+ range(1, max_intx) \
\
product(double, Tier0ProfileDelayFactor, 100.0, DIAGNOSTIC, \
"Delay profiling/compiling of methods that were " \
diff --git a/src/hotspot/share/compiler/compiler_globals_pd.hpp b/src/hotspot/share/compiler/compiler_globals_pd.hpp
index 1251329a1b6..90edd952c77 100644
--- a/src/hotspot/share/compiler/compiler_globals_pd.hpp
+++ b/src/hotspot/share/compiler/compiler_globals_pd.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,10 +33,10 @@
#include "runtime/globals_shared.hpp"
#ifdef COMPILER1
-#include "c1/c1_globals_pd.hpp"
+#include "c1/c1_globals.hpp"
#endif // COMPILER1
#ifdef COMPILER2
-#include "opto/c2_globals_pd.hpp"
+#include "opto/c2_globals.hpp"
#endif // COMPILER2
// JVMCI has no platform-specific global definitions
@@ -61,15 +61,15 @@ define_pd_global(intx, OnStackReplacePercentage, 0);
define_pd_global(size_t, NewSizeThreadIncrease, 4*K);
define_pd_global(bool, InlineClassNatives, true);
define_pd_global(bool, InlineUnsafeOps, true);
-define_pd_global(uintx, InitialCodeCacheSize, 160*K);
-define_pd_global(uintx, ReservedCodeCacheSize, 32*M);
-define_pd_global(uintx, NonProfiledCodeHeapSize, 0);
-define_pd_global(uintx, ProfiledCodeHeapSize, 0);
-define_pd_global(uintx, NonNMethodCodeHeapSize, 32*M);
+define_pd_global(size_t, InitialCodeCacheSize, 160*K);
+define_pd_global(size_t, ReservedCodeCacheSize, 32*M);
+define_pd_global(size_t, NonProfiledCodeHeapSize, 0);
+define_pd_global(size_t, ProfiledCodeHeapSize, 0);
+define_pd_global(size_t, NonNMethodCodeHeapSize, 32*M);
-define_pd_global(uintx, CodeCacheExpansionSize, 32*K);
-define_pd_global(uintx, CodeCacheMinBlockLength, 1);
-define_pd_global(uintx, CodeCacheMinimumUseSpace, 200*K);
+define_pd_global(size_t, CodeCacheExpansionSize, 32*K);
+define_pd_global(size_t, CodeCacheMinBlockLength, 1);
+define_pd_global(size_t, CodeCacheMinimumUseSpace, 200*K);
#ifndef ZERO
define_pd_global(bool, NeverActAsServerClassMachine, true);
define_pd_global(uint64_t,MaxRAM, 1ULL*G);
diff --git a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp
index a2ae6f8a14a..33a2690f777 100644
--- a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp
+++ b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp
@@ -91,7 +91,7 @@ class EpsilonGenerationCounters : public GenerationCounters {
{};
void update_all() {
- GenerationCounters::update_all(_heap->capacity());
+ GenerationCounters::update_capacity(_heap->capacity());
}
};
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
index 705dcd7895b..b6c18420b82 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
@@ -347,6 +347,10 @@ HeapWord* G1CollectedHeap::humongous_obj_allocate(size_t word_size) {
_verifier->verify_region_sets_optional();
uint obj_regions = (uint) humongous_obj_size_in_regions(word_size);
+ if (obj_regions > num_available_regions()) {
+ // Can't satisfy this allocation; early-return.
+ return nullptr;
+ }
// Policy: First try to allocate a humongous object in the free list.
G1HeapRegion* humongous_start = _hrm.allocate_humongous(obj_regions);
@@ -495,7 +499,7 @@ HeapWord* G1CollectedHeap::alloc_archive_region(size_t word_size, HeapWord* pref
if (reserved.word_size() <= word_size) {
log_info(gc, heap)("Unable to allocate regions as archive heap is too large; size requested = %zu"
- " bytes, heap = %zu bytes", word_size, reserved.word_size());
+ " bytes, heap = %zu bytes", word_size * HeapWordSize, reserved.byte_size());
return nullptr;
}
@@ -997,7 +1001,7 @@ HeapWord* G1CollectedHeap::expand_and_allocate(size_t word_size) {
return nullptr;
}
-bool G1CollectedHeap::expand(size_t expand_bytes, WorkerThreads* pretouch_workers, double* expand_time_ms) {
+bool G1CollectedHeap::expand(size_t expand_bytes, WorkerThreads* pretouch_workers) {
size_t aligned_expand_bytes = os::align_up_vm_page_size(expand_bytes);
aligned_expand_bytes = align_up(aligned_expand_bytes, G1HeapRegion::GrainBytes);
@@ -1009,15 +1013,10 @@ bool G1CollectedHeap::expand(size_t expand_bytes, WorkerThreads* pretouch_worker
return false;
}
- double expand_heap_start_time_sec = os::elapsedTime();
uint regions_to_expand = (uint)(aligned_expand_bytes / G1HeapRegion::GrainBytes);
assert(regions_to_expand > 0, "Must expand by at least one region");
uint expanded_by = _hrm.expand_by(regions_to_expand, pretouch_workers);
- if (expand_time_ms != nullptr) {
- *expand_time_ms = (os::elapsedTime() - expand_heap_start_time_sec) * MILLIUNITS;
- }
-
assert(expanded_by > 0, "must have failed during commit.");
size_t actual_expand_bytes = expanded_by * G1HeapRegion::GrainBytes;
@@ -2393,11 +2392,11 @@ void G1CollectedHeap::expand_heap_after_young_collection(){
if (expand_bytes > 0) {
// No need for an ergo logging here,
// expansion_amount() does this when it returns a value > 0.
- double expand_ms = 0.0;
- if (!expand(expand_bytes, _workers, &expand_ms)) {
- // We failed to expand the heap. Cannot do anything about it.
+ Ticks expand_start = Ticks::now();
+ if (expand(expand_bytes, _workers)) {
+ double expand_ms = (Ticks::now() - expand_start).seconds() * MILLIUNITS;
+ phase_times()->record_expand_heap_time(expand_ms);
}
- phase_times()->record_expand_heap_time(expand_ms);
}
}
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
index fbd1fe6165f..ad440577f2d 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
@@ -572,7 +572,7 @@ class G1CollectedHeap : public CollectedHeap {
// Returns true if the heap was expanded by the requested amount;
// false otherwise.
// (Rounds up to a G1HeapRegion boundary.)
- bool expand(size_t expand_bytes, WorkerThreads* pretouch_workers = nullptr, double* expand_time_ms = nullptr);
+ bool expand(size_t expand_bytes, WorkerThreads* pretouch_workers);
bool expand_single_region(uint node_index);
// Returns the PLAB statistics for a given destination.
@@ -969,7 +969,7 @@ class G1CollectedHeap : public CollectedHeap {
// Returns true if an incremental GC should be upgrade to a full gc. This
// is done when there are no free regions and the heap can't be expanded.
bool should_upgrade_to_full_gc() const {
- return num_inactive_regions() == 0 && num_free_regions() == 0;
+ return num_available_regions() == 0;
}
// The number of inactive regions.
@@ -988,13 +988,12 @@ class G1CollectedHeap : public CollectedHeap {
uint num_used_regions() const { return _hrm.num_used_regions(); }
// The number of regions that can be allocated into.
- uint num_available_regions() const { return _hrm.num_available_regions(); }
+ uint num_available_regions() const { return num_free_regions() + num_inactive_regions(); }
MemoryUsage get_auxiliary_data_memory_usage() const {
return _hrm.get_auxiliary_data_memory_usage();
}
-
#ifdef ASSERT
bool is_on_master_free_list(G1HeapRegion* hr) {
return _hrm.is_free(hr);
diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp
index df4786d7fa5..d8c6859c1aa 100644
--- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp
+++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp
@@ -477,10 +477,6 @@ uint G1HeapRegionManager::find_contiguous_in_free_list(uint num_regions) {
}
uint G1HeapRegionManager::find_contiguous_allow_expand(uint num_regions) {
- // Check if we can actually satisfy the allocation.
- if (num_regions > num_available_regions()) {
- return G1_NO_HRM_INDEX;
- }
// Find any candidate.
return find_contiguous_in_range(0, max_num_regions(), num_regions);
}
diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp
index 4429384bcc5..19ae9887e94 100644
--- a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp
+++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp
@@ -239,8 +239,6 @@ class G1HeapRegionManager: public CHeapObj {
// The number of regions reserved for the heap.
uint max_num_regions() const { return (uint)_regions.length(); }
- uint num_available_regions() const { return num_free_regions() + num_inactive_regions(); }
-
MemoryUsage get_auxiliary_data_memory_usage() const;
MemRegion reserved() const { return MemRegion(heap_bottom(), heap_end()); }
diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp
index 6d1ffdb7c00..26364705a9d 100644
--- a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp
+++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp
@@ -62,7 +62,7 @@ class G1YoungGenerationCounters : public G1GenerationCounters {
void update_all() {
size_t committed = _monitoring_support->young_gen_committed();
- GenerationCounters::update_all(committed);
+ GenerationCounters::update_capacity(committed);
}
};
@@ -83,7 +83,7 @@ class G1OldGenerationCounters : public G1GenerationCounters {
void update_all() {
size_t committed = _monitoring_support->old_gen_committed();
- GenerationCounters::update_all(committed);
+ GenerationCounters::update_capacity(committed);
}
};
diff --git a/src/hotspot/share/gc/g1/g1SharedClosures.hpp b/src/hotspot/share/gc/g1/g1SharedClosures.hpp
index 5541d6274a3..a81f62ff308 100644
--- a/src/hotspot/share/gc/g1/g1SharedClosures.hpp
+++ b/src/hotspot/share/gc/g1/g1SharedClosures.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,6 +22,9 @@
*
*/
+#ifndef SHARE_GC_G1_G1SHAREDCLOSURES_HPP
+#define SHARE_GC_G1_G1SHAREDCLOSURES_HPP
+
#include "gc/g1/g1NMethodClosure.hpp"
#include "gc/g1/g1OopClosures.hpp"
#include "memory/iterator.hpp"
@@ -54,3 +57,5 @@ class G1SharedClosures {
_clds(&_oops_in_cld, process_only_dirty),
_nmethods(pss->worker_id(), &_oops_in_nmethod, should_mark) {}
};
+
+#endif // SHARE_GC_G1_G1SHAREDCLOSURES_HPP
diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp
index 69cdd8d5ca6..87fa751691e 100644
--- a/src/hotspot/share/gc/g1/g1VMOperations.cpp
+++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp
@@ -57,7 +57,7 @@ void VM_G1CollectFull::doit() {
VM_G1TryInitiateConcMark::VM_G1TryInitiateConcMark(uint gc_count_before,
GCCause::Cause gc_cause) :
- VM_GC_Operation(gc_count_before, gc_cause),
+ VM_GC_Collect_Operation(gc_count_before, gc_cause),
_transient_failure(false),
_cycle_already_in_progress(false),
_whitebox_attached(false),
diff --git a/src/hotspot/share/gc/g1/g1VMOperations.hpp b/src/hotspot/share/gc/g1/g1VMOperations.hpp
index a201d57db76..f2ac4c6f638 100644
--- a/src/hotspot/share/gc/g1/g1VMOperations.hpp
+++ b/src/hotspot/share/gc/g1/g1VMOperations.hpp
@@ -30,7 +30,7 @@
// VM_operations for the G1 collector.
-class VM_G1CollectFull : public VM_GC_Operation {
+class VM_G1CollectFull : public VM_GC_Collect_Operation {
protected:
bool skip_operation() const override;
@@ -38,12 +38,12 @@ class VM_G1CollectFull : public VM_GC_Operation {
VM_G1CollectFull(uint gc_count_before,
uint full_gc_count_before,
GCCause::Cause cause) :
- VM_GC_Operation(gc_count_before, cause, full_gc_count_before, true) { }
+ VM_GC_Collect_Operation(gc_count_before, cause, full_gc_count_before, true) { }
VMOp_Type type() const override { return VMOp_G1CollectFull; }
void doit() override;
};
-class VM_G1TryInitiateConcMark : public VM_GC_Operation {
+class VM_G1TryInitiateConcMark : public VM_GC_Collect_Operation {
bool _transient_failure;
bool _cycle_already_in_progress;
bool _whitebox_attached;
@@ -89,6 +89,7 @@ class VM_G1PauseConcurrent : public VM_Operation {
bool doit_prologue() override;
void doit_epilogue() override;
void doit() override;
+ bool is_gc_operation() const override { return true; }
};
class VM_G1PauseRemark : public VM_G1PauseConcurrent {
diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp
index 44d0d22257e..7ca2e997676 100644
--- a/src/hotspot/share/gc/g1/g1_globals.hpp
+++ b/src/hotspot/share/gc/g1/g1_globals.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -144,7 +144,7 @@
\
product(size_t, G1SATBBufferSize, 1*K, \
"Number of entries in an SATB log buffer.") \
- constraint(G1SATBBufferSizeConstraintFunc, AtParse) \
+ constraint(G1SATBBufferSizeConstraintFunc, AfterErgo) \
\
develop(uintx, G1SATBProcessCompletedThreshold, 20, \
"Number of completed buffers that triggers log processing.") \
@@ -163,7 +163,7 @@
\
product(size_t, G1UpdateBufferSize, 256, \
"Size of an update buffer") \
- constraint(G1UpdateBufferSizeConstraintFunc, AtParse) \
+ constraint(G1UpdateBufferSizeConstraintFunc, AfterErgo) \
\
product(uint, G1RSetUpdatingPauseTimePercent, 10, \
"A target percentage of time that is allowed to be spend on " \
diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp
index 0c7d8de9bb5..122f4945ec3 100644
--- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp
+++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp
@@ -92,18 +92,9 @@ void MutableNUMASpace::ensure_parsability() {
LGRPSpace *ls = lgrp_spaces()->at(i);
MutableSpace *s = ls->space();
if (s->top() < top()) { // For all spaces preceding the one containing top()
- if (s->free_in_words() > 0) {
- HeapWord* cur_top = s->top();
- size_t words_left_to_fill = pointer_delta(s->end(), s->top());;
- while (words_left_to_fill > 0) {
- size_t words_to_fill = MIN2(words_left_to_fill, CollectedHeap::filler_array_max_size());
- assert(words_to_fill >= CollectedHeap::min_fill_size(),
- "Remaining size (%zu) is too small to fill (based on %zu and %zu)",
- words_to_fill, words_left_to_fill, CollectedHeap::filler_array_max_size());
- CollectedHeap::fill_with_object(cur_top, words_to_fill);
- cur_top += words_to_fill;
- words_left_to_fill -= words_to_fill;
- }
+ size_t free_words = s->free_in_words();
+ if (free_words > 0) {
+ CollectedHeap::fill_with_objects(s->top(), free_words);
}
} else {
return;
@@ -210,8 +201,6 @@ void MutableNUMASpace::bias_region(MemRegion mr, uint lgrp_id) {
const size_t os_align = UseLargePages ? page_size() : os::vm_page_size();
os::realign_memory((char*)aligned_region.start(), aligned_region.byte_size(), os_align);
// Then we uncommit the pages in the range.
- // The alignment_hint argument must be less than or equal to the small page
- // size if not using large pages or else this function does nothing.
os::disclaim_memory((char*)aligned_region.start(), aligned_region.byte_size());
// And make them local/first-touch biased.
os::numa_make_local((char*)aligned_region.start(), aligned_region.byte_size(), checked_cast(lgrp_id));
@@ -421,7 +410,7 @@ void MutableNUMASpace::initialize(MemRegion mr,
MutableSpace *s = ls->space();
old_region = s->region();
- size_t chunk_byte_size = 0, old_chunk_byte_size = 0;
+ size_t chunk_byte_size = 0;
if (i < lgrp_spaces()->length() - 1) {
if (!UseAdaptiveNUMAChunkSizing ||
(UseAdaptiveNUMAChunkSizing && NUMAChunkResizeWeight == 0) ||
@@ -498,7 +487,6 @@ void MutableNUMASpace::set_top(HeapWord* value) {
for (int i = 0; i < lgrp_spaces()->length();) {
LGRPSpace *ls = lgrp_spaces()->at(i);
MutableSpace *s = ls->space();
- HeapWord *top = MAX2(align_down(s->top(), page_size()), s->bottom());
if (s->contains(value)) {
// Check if setting the chunk's top to a given value would create a hole less than
diff --git a/src/hotspot/share/gc/parallel/parallelArguments.cpp b/src/hotspot/share/gc/parallel/parallelArguments.cpp
index cae6f9a93d5..dcd5b6ae454 100644
--- a/src/hotspot/share/gc/parallel/parallelArguments.cpp
+++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp
@@ -70,7 +70,7 @@ void ParallelArguments::initialize() {
if (FLAG_IS_CMDLINE(InitialSurvivorRatio)) {
if (FLAG_IS_CMDLINE(MinSurvivorRatio)) {
jio_fprintf(defaultStream::error_stream(),
- "Inconsistent MinSurvivorRatio vs InitialSurvivorRatio: %d vs %d\n", MinSurvivorRatio, InitialSurvivorRatio);
+ "Inconsistent MinSurvivorRatio vs InitialSurvivorRatio: %zu vs %zu\n", MinSurvivorRatio, InitialSurvivorRatio);
}
FLAG_SET_DEFAULT(MinSurvivorRatio, InitialSurvivorRatio);
} else {
diff --git a/src/hotspot/share/gc/parallel/psOldGen.cpp b/src/hotspot/share/gc/parallel/psOldGen.cpp
index bc25683e00f..cedad78be30 100644
--- a/src/hotspot/share/gc/parallel/psOldGen.cpp
+++ b/src/hotspot/share/gc/parallel/psOldGen.cpp
@@ -234,7 +234,7 @@ bool PSOldGen::expand_by(size_t bytes) {
post_resize();
if (UsePerfData) {
_space_counters->update_capacity();
- _gen_counters->update_all(_virtual_space->committed_size());
+ _gen_counters->update_capacity(_virtual_space->committed_size());
}
}
@@ -366,7 +366,7 @@ void PSOldGen::print_on(outputStream* st) const {
void PSOldGen::update_counters() {
if (UsePerfData) {
_space_counters->update_all();
- _gen_counters->update_all(_virtual_space->committed_size());
+ _gen_counters->update_capacity(_virtual_space->committed_size());
}
}
diff --git a/src/hotspot/share/gc/parallel/psVMOperations.cpp b/src/hotspot/share/gc/parallel/psVMOperations.cpp
index dc96d9506e2..98fe1845825 100644
--- a/src/hotspot/share/gc/parallel/psVMOperations.cpp
+++ b/src/hotspot/share/gc/parallel/psVMOperations.cpp
@@ -52,9 +52,9 @@ static bool is_cause_full(GCCause::Cause cause) {
// Only used for System.gc() calls
VM_ParallelGCCollect::VM_ParallelGCCollect(uint gc_count,
- uint full_gc_count,
- GCCause::Cause gc_cause) :
- VM_GC_Operation(gc_count, gc_cause, full_gc_count, is_cause_full(gc_cause)) {}
+ uint full_gc_count,
+ GCCause::Cause gc_cause) :
+ VM_GC_Collect_Operation(gc_count, gc_cause, full_gc_count, is_cause_full(gc_cause)) {}
void VM_ParallelGCCollect::doit() {
ParallelScavengeHeap* heap = ParallelScavengeHeap::heap();
diff --git a/src/hotspot/share/gc/parallel/psVMOperations.hpp b/src/hotspot/share/gc/parallel/psVMOperations.hpp
index ba896de8680..1a542d59f8e 100644
--- a/src/hotspot/share/gc/parallel/psVMOperations.hpp
+++ b/src/hotspot/share/gc/parallel/psVMOperations.hpp
@@ -40,7 +40,7 @@ class VM_ParallelCollectForAllocation : public VM_CollectForAllocation {
virtual void doit();
};
-class VM_ParallelGCCollect: public VM_GC_Operation {
+class VM_ParallelGCCollect: public VM_GC_Collect_Operation {
public:
VM_ParallelGCCollect(uint gc_count, uint full_gc_count, GCCause::Cause gc_cause);
virtual VMOp_Type type() const { return VMOp_ParallelGCCollect; }
diff --git a/src/hotspot/share/gc/parallel/psYoungGen.cpp b/src/hotspot/share/gc/parallel/psYoungGen.cpp
index bf77a0dde93..4024109a4a0 100644
--- a/src/hotspot/share/gc/parallel/psYoungGen.cpp
+++ b/src/hotspot/share/gc/parallel/psYoungGen.cpp
@@ -810,7 +810,7 @@ void PSYoungGen::update_counters() {
_eden_counters->update_all();
_from_counters->update_all();
_to_counters->update_all();
- _gen_counters->update_all(_virtual_space->committed_size());
+ _gen_counters->update_capacity(_virtual_space->committed_size());
}
}
diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp
index 61b0ccad191..cb36207ff47 100644
--- a/src/hotspot/share/gc/serial/defNewGeneration.cpp
+++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp
@@ -814,7 +814,7 @@ void DefNewGeneration::update_counters() {
_eden_counters->update_all();
_from_counters->update_all();
_to_counters->update_all();
- _gen_counters->update_all(_virtual_space.committed_size());
+ _gen_counters->update_capacity(_virtual_space.committed_size());
}
}
diff --git a/src/hotspot/share/gc/serial/serialVMOperations.hpp b/src/hotspot/share/gc/serial/serialVMOperations.hpp
index 294920f4ae2..31f8c21e61a 100644
--- a/src/hotspot/share/gc/serial/serialVMOperations.hpp
+++ b/src/hotspot/share/gc/serial/serialVMOperations.hpp
@@ -45,13 +45,13 @@ class VM_SerialCollectForAllocation : public VM_CollectForAllocation {
// VM operation to invoke a collection of the heap as a
// SerialHeap heap.
-class VM_SerialGCCollect: public VM_GC_Operation {
+class VM_SerialGCCollect: public VM_GC_Collect_Operation {
public:
VM_SerialGCCollect(bool full,
uint gc_count_before,
uint full_gc_count_before,
GCCause::Cause gc_cause)
- : VM_GC_Operation(gc_count_before, gc_cause, full_gc_count_before, full) {}
+ : VM_GC_Collect_Operation(gc_count_before, gc_cause, full_gc_count_before, full) {}
virtual VMOp_Type type() const { return VMOp_SerialGCCollect; }
virtual void doit();
diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.cpp b/src/hotspot/share/gc/serial/tenuredGeneration.cpp
index 2725f1105f1..589e670a2e4 100644
--- a/src/hotspot/share/gc/serial/tenuredGeneration.cpp
+++ b/src/hotspot/share/gc/serial/tenuredGeneration.cpp
@@ -368,7 +368,7 @@ void TenuredGeneration::update_promote_stats() {
void TenuredGeneration::update_counters() {
if (UsePerfData) {
_space_counters->update_all();
- _gen_counters->update_all(_virtual_space.committed_size());
+ _gen_counters->update_capacity(_virtual_space.committed_size());
}
}
diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp
index 41eb0a24b62..522000e0a99 100644
--- a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp
+++ b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp
@@ -72,11 +72,25 @@ bool BarrierSetNMethod::supports_entry_barrier(nmethod* nm) {
}
void BarrierSetNMethod::disarm(nmethod* nm) {
- set_guard_value(nm, disarmed_guard_value());
+ guard_with(nm, disarmed_guard_value());
+}
+
+void BarrierSetNMethod::guard_with(nmethod* nm, int value) {
+ assert((value & not_entrant) == 0, "not_entrant bit is reserved");
+ // Enter critical section. Does not block for safepoint.
+ ConditionalMutexLocker ml(NMethodEntryBarrier_lock, !NMethodEntryBarrier_lock->owned_by_self(), Mutex::_no_safepoint_check_flag);
+ // Do not undo sticky bit
+ if (is_not_entrant(nm)) {
+ value |= not_entrant;
+ }
+ if (guard_value(nm) != value) {
+ // Patch the code only if needed.
+ set_guard_value(nm, value);
+ }
}
bool BarrierSetNMethod::is_armed(nmethod* nm) {
- return guard_value(nm) != disarmed_guard_value();
+ return (guard_value(nm) & ~not_entrant) != disarmed_guard_value();
}
bool BarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) {
@@ -152,7 +166,7 @@ void BarrierSetNMethod::arm_all_nmethods() {
// seriously wrong.
++_current_phase;
if (_current_phase == INT_MAX) {
- _current_phase = 1;
+ _current_phase = initial;
}
BarrierSetNMethodArmClosure cl(_current_phase);
Threads::threads_do(&cl);
@@ -178,23 +192,25 @@ int BarrierSetNMethod::nmethod_stub_entry_barrier(address* return_address_ptr) {
BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
// Called upon first entry after being armed
- bool may_enter = bs_nm->nmethod_entry_barrier(nm);
+ bool may_enter = !bs_nm->is_not_entrant(nm) && bs_nm->nmethod_entry_barrier(nm);
assert(!nm->is_osr_method() || may_enter, "OSR nmethods should always be entrant after migration");
- // In case a concurrent thread disarmed the nmethod, we need to ensure the new instructions
- // are made visible, by using a cross modify fence. Note that this is synchronous cross modifying
- // code, where the existence of new instructions is communicated via data (the guard value).
- // This cross modify fence is only needed when the nmethod entry barrier modifies the
- // instructions. Not all platforms currently do that, so if this check becomes expensive,
- // it can be made conditional on the nmethod_patching_type.
- OrderAccess::cross_modify_fence();
-
- // Diagnostic option to force deoptimization 1 in 10 times. It is otherwise
- // a very rare event.
- if (DeoptimizeNMethodBarriersALot && !nm->is_osr_method()) {
- static volatile uint32_t counter=0;
- if (Atomic::add(&counter, 1u) % 10 == 0) {
- may_enter = false;
+ if (may_enter) {
+ // In case a concurrent thread disarmed the nmethod, we need to ensure the new instructions
+ // are made visible, by using a cross modify fence. Note that this is synchronous cross modifying
+ // code, where the existence of new instructions is communicated via data (the guard value).
+ // This cross modify fence is only needed when the nmethod entry barrier modifies the
+ // instructions. Not all platforms currently do that, so if this check becomes expensive,
+ // it can be made conditional on the nmethod_patching_type.
+ OrderAccess::cross_modify_fence();
+
+ // Diagnostic option to force deoptimization 1 in 10 times. It is otherwise
+ // a very rare event.
+ if (DeoptimizeNMethodBarriersALot && !nm->is_osr_method()) {
+ static volatile uint32_t counter=0;
+ if (Atomic::add(&counter, 1u) % 10 == 0) {
+ may_enter = false;
+ }
}
}
@@ -220,3 +236,22 @@ oop BarrierSetNMethod::oop_load_no_keepalive(const nmethod* nm, int index) {
oop BarrierSetNMethod::oop_load_phantom(const nmethod* nm, int index) {
return NativeAccess::oop_load(nm->oop_addr_at(index));
}
+
+// Make the nmethod permanently not-entrant, so that nmethod_stub_entry_barrier() will call
+// deoptimize() to redirect the caller to SharedRuntime::get_handle_wrong_method_stub().
+// A sticky armed bit is set and other bits are preserved. As a result, a call to
+// nmethod_stub_entry_barrier() may appear to be spurious, because is_armed() still returns
+// false and nmethod_entry_barrier() is not called.
+void BarrierSetNMethod::make_not_entrant(nmethod* nm) {
+ // Enter critical section. Does not block for safepoint.
+ ConditionalMutexLocker ml(NMethodEntryBarrier_lock, !NMethodEntryBarrier_lock->owned_by_self(), Mutex::_no_safepoint_check_flag);
+ int value = guard_value(nm) | not_entrant;
+ if (guard_value(nm) != value) {
+ // Patch the code only if needed.
+ set_guard_value(nm, value);
+ }
+}
+
+bool BarrierSetNMethod::is_not_entrant(nmethod* nm) {
+ return (guard_value(nm) & not_entrant) != 0;
+}
diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.hpp b/src/hotspot/share/gc/shared/barrierSetNMethod.hpp
index 756dc43b3f1..b905e8869b5 100644
--- a/src/hotspot/share/gc/shared/barrierSetNMethod.hpp
+++ b/src/hotspot/share/gc/shared/barrierSetNMethod.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -36,10 +36,20 @@ class nmethod;
class BarrierSetNMethod: public CHeapObj {
private:
int _current_phase;
+ enum {
+ not_entrant = 1 << 31, // armed sticky bit, see make_not_entrant
+ armed = 0,
+ initial = 1,
+ };
+
void deoptimize(nmethod* nm, address* return_addr_ptr);
+protected:
+ virtual int guard_value(nmethod* nm);
+ void set_guard_value(nmethod* nm, int value);
+
public:
- BarrierSetNMethod() : _current_phase(1) {}
+ BarrierSetNMethod() : _current_phase(initial) {}
bool supports_entry_barrier(nmethod* nm);
virtual bool nmethod_entry_barrier(nmethod* nm);
@@ -50,13 +60,15 @@ class BarrierSetNMethod: public CHeapObj {
static int nmethod_stub_entry_barrier(address* return_address_ptr);
bool nmethod_osr_entry_barrier(nmethod* nm);
- bool is_armed(nmethod* nm);
+ virtual bool is_armed(nmethod* nm);
+ void arm(nmethod* nm) { guard_with(nm, armed); }
void disarm(nmethod* nm);
+ virtual void make_not_entrant(nmethod* nm);
+ virtual bool is_not_entrant(nmethod* nm);
- int guard_value(nmethod* nm);
- void set_guard_value(nmethod* nm, int value);
+ virtual void guard_with(nmethod* nm, int value);
- void arm_all_nmethods();
+ virtual void arm_all_nmethods();
virtual oop oop_load_no_keepalive(const nmethod* nm, int index);
virtual oop oop_load_phantom(const nmethod* nm, int index);
diff --git a/src/hotspot/share/gc/shared/gcCause.cpp b/src/hotspot/share/gc/shared/gcCause.cpp
index 51ce73861e0..489a5d32a90 100644
--- a/src/hotspot/share/gc/shared/gcCause.cpp
+++ b/src/hotspot/share/gc/shared/gcCause.cpp
@@ -89,11 +89,14 @@ const char* GCCause::to_string(GCCause::Cause cause) {
case _dcmd_gc_run:
return "Diagnostic Command";
+ case _shenandoah_stop_vm:
+ return "Stopping VM";
+
case _shenandoah_allocation_failure_evac:
return "Allocation Failure During Evacuation";
- case _shenandoah_stop_vm:
- return "Stopping VM";
+ case _shenandoah_humongous_allocation_failure:
+ return "Humongous Allocation Failure";
case _shenandoah_concurrent_gc:
return "Concurrent GC";
diff --git a/src/hotspot/share/gc/shared/gcCause.hpp b/src/hotspot/share/gc/shared/gcCause.hpp
index ef96bf21567..ec7c664bbc1 100644
--- a/src/hotspot/share/gc/shared/gcCause.hpp
+++ b/src/hotspot/share/gc/shared/gcCause.hpp
@@ -55,7 +55,6 @@ class GCCause : public AllStatic {
/* implementation independent, but reserved for GC use */
_no_gc,
- _no_cause_specified,
_allocation_failure,
/* implementation specific */
@@ -99,14 +98,13 @@ class GCCause : public AllStatic {
cause == GCCause::_wb_full_gc);
}
- inline static bool is_serviceability_requested_gc(GCCause::Cause
- cause) {
+ inline static bool is_serviceability_requested_gc(GCCause::Cause cause) {
return (cause == GCCause::_jvmti_force_gc ||
cause == GCCause::_heap_inspection ||
cause == GCCause::_heap_dump);
}
- // Causes for collection of the tenured gernation
+ // Causes for collection of the tenured generation
inline static bool is_tenured_allocation_failure_gc(GCCause::Cause cause) {
// _allocation_failure is the generic cause a collection which could result
// in the collection of the tenured generation if there is not enough space
@@ -117,8 +115,7 @@ class GCCause : public AllStatic {
// Causes for collection of the young generation
inline static bool is_allocation_failure_gc(GCCause::Cause cause) {
// _allocation_failure is the generic cause a collection for allocation failure
- return (cause == GCCause::_allocation_failure ||
- cause == GCCause::_shenandoah_allocation_failure_evac);
+ return cause == GCCause::_allocation_failure;
}
// Return a string describing the GCCause.
diff --git a/src/hotspot/share/gc/shared/gcVMOperations.cpp b/src/hotspot/share/gc/shared/gcVMOperations.cpp
index 1632dca61d5..19a81504722 100644
--- a/src/hotspot/share/gc/shared/gcVMOperations.cpp
+++ b/src/hotspot/share/gc/shared/gcVMOperations.cpp
@@ -48,12 +48,12 @@
#include "gc/g1/g1Policy.hpp"
#endif // INCLUDE_G1GC
-bool VM_GC_Sync_Operation::doit_prologue() {
+bool VM_Heap_Sync_Operation::doit_prologue() {
Heap_lock->lock();
return true;
}
-void VM_GC_Sync_Operation::doit_epilogue() {
+void VM_Heap_Sync_Operation::doit_epilogue() {
Heap_lock->unlock();
}
@@ -99,8 +99,7 @@ static bool should_use_gclocker() {
}
bool VM_GC_Operation::doit_prologue() {
- assert(((_gc_cause != GCCause::_no_gc) &&
- (_gc_cause != GCCause::_no_cause_specified)), "Illegal GCCause");
+ assert(_gc_cause != GCCause::_no_gc, "Illegal GCCause");
// To be able to handle a GC the VM initialization needs to be completed.
if (!is_init_completed()) {
@@ -115,7 +114,7 @@ bool VM_GC_Operation::doit_prologue() {
if (should_use_gclocker()) {
GCLocker::block();
}
- VM_GC_Sync_Operation::doit_prologue();
+ VM_Heap_Sync_Operation::doit_prologue();
// Check invocations
if (skip_operation()) {
@@ -139,7 +138,7 @@ void VM_GC_Operation::doit_epilogue() {
if (Universe::has_reference_pending_list()) {
Heap_lock->notify_all();
}
- VM_GC_Sync_Operation::doit_epilogue();
+ VM_Heap_Sync_Operation::doit_epilogue();
if (should_use_gclocker()) {
GCLocker::unblock();
}
@@ -206,7 +205,7 @@ VM_CollectForMetadataAllocation::VM_CollectForMetadataAllocation(ClassLoaderData
uint gc_count_before,
uint full_gc_count_before,
GCCause::Cause gc_cause)
- : VM_GC_Operation(gc_count_before, gc_cause, full_gc_count_before, true),
+ : VM_GC_Collect_Operation(gc_count_before, gc_cause, full_gc_count_before, true),
_result(nullptr), _size(size), _mdtype(mdtype), _loader_data(loader_data) {
assert(_size != 0, "An allocation should always be requested with this operation.");
AllocTracer::send_allocation_requiring_gc_event(_size * HeapWordSize, GCId::peek());
@@ -269,7 +268,7 @@ void VM_CollectForMetadataAllocation::doit() {
}
VM_CollectForAllocation::VM_CollectForAllocation(size_t word_size, uint gc_count_before, GCCause::Cause cause)
- : VM_GC_Operation(gc_count_before, cause), _word_size(word_size), _result(nullptr) {
+ : VM_GC_Collect_Operation(gc_count_before, cause), _word_size(word_size), _result(nullptr) {
// Only report if operation was really caused by an allocation.
if (_word_size != 0) {
AllocTracer::send_allocation_requiring_gc_event(_word_size * HeapWordSize, GCId::peek());
diff --git a/src/hotspot/share/gc/shared/gcVMOperations.hpp b/src/hotspot/share/gc/shared/gcVMOperations.hpp
index bc370358ff8..6752e0bc32d 100644
--- a/src/hotspot/share/gc/shared/gcVMOperations.hpp
+++ b/src/hotspot/share/gc/shared/gcVMOperations.hpp
@@ -37,19 +37,22 @@
// a set of operations (VM_Operation) related to GC.
//
// VM_Operation
-// VM_GC_Sync_Operation
+// VM_Heap_Sync_Operation
// VM_GC_Operation
-// VM_GC_HeapInspection
-// VM_PopulateDynamicDumpSharedSpace
-// VM_SerialGCCollect
-// VM_ParallelGCCollect
-// VM_CollectForAllocation
-// VM_SerialCollectForAllocation
-// VM_ParallelCollectForAllocation
+// VM_GC_Collect_Operation
+// VM_SerialGCCollect
+// VM_ParallelGCCollect
+// VM_CollectForAllocation
+// VM_SerialCollectForAllocation
+// VM_ParallelCollectForAllocation
+// VM_CollectForMetadataAllocation
+// VM_GC_Service_Operation
+// VM_GC_HeapInspection
// VM_Verify
+// VM_PopulateDynamicDumpSharedSpace
// VM_PopulateDumpSharedSpace
//
-// VM_GC_Sync_Operation
+// VM_Heap_Sync_Operation
// - implements only synchronization with other VM operations of the
// same kind using the Heap_lock, not actually doing a GC.
//
@@ -84,10 +87,10 @@
// - creates the CDS archive
//
-class VM_GC_Sync_Operation : public VM_Operation {
+class VM_Heap_Sync_Operation : public VM_Operation {
public:
- VM_GC_Sync_Operation() : VM_Operation() { }
+ VM_Heap_Sync_Operation() : VM_Operation() { }
// Acquires the Heap_lock.
virtual bool doit_prologue();
@@ -95,13 +98,13 @@ class VM_GC_Sync_Operation : public VM_Operation {
virtual void doit_epilogue();
};
-class VM_Verify : public VM_GC_Sync_Operation {
+class VM_Verify : public VM_Heap_Sync_Operation {
public:
VMOp_Type type() const { return VMOp_Verify; }
void doit();
};
-class VM_GC_Operation: public VM_GC_Sync_Operation {
+class VM_GC_Operation: public VM_Heap_Sync_Operation {
protected:
uint _gc_count_before; // gc count before acquiring the Heap_lock
uint _full_gc_count_before; // full gc count before acquiring the Heap_lock
@@ -115,7 +118,7 @@ class VM_GC_Operation: public VM_GC_Sync_Operation {
VM_GC_Operation(uint gc_count_before,
GCCause::Cause _cause,
uint full_gc_count_before = 0,
- bool full = false) : VM_GC_Sync_Operation() {
+ bool full = false) : VM_Heap_Sync_Operation() {
_full = full;
_prologue_succeeded = false;
_gc_count_before = gc_count_before;
@@ -149,31 +152,53 @@ class VM_GC_Operation: public VM_GC_Sync_Operation {
static void notify_gc_end();
};
+class VM_GC_Service_Operation : public VM_GC_Operation {
+public:
+ VM_GC_Service_Operation(uint gc_count_before,
+ GCCause::Cause _cause,
+ uint full_gc_count_before = 0,
+ bool full = false) :
+ VM_GC_Operation(gc_count_before, _cause, full_gc_count_before, full) {}
+};
-class VM_GC_HeapInspection: public VM_GC_Operation {
+class VM_GC_Collect_Operation : public VM_GC_Operation {
+public:
+ VM_GC_Collect_Operation(uint gc_count_before,
+ GCCause::Cause _cause,
+ uint full_gc_count_before = 0,
+ bool full = false) :
+ VM_GC_Operation(gc_count_before, _cause, full_gc_count_before, full) {}
+
+ bool is_gc_operation() const { return true; }
+};
+
+
+class VM_GC_HeapInspection : public VM_GC_Service_Operation {
private:
outputStream* _out;
bool _full_gc;
uint _parallel_thread_num;
+
public:
VM_GC_HeapInspection(outputStream* out, bool request_full_gc,
uint parallel_thread_num = 1) :
- VM_GC_Operation(0 /* total collections, dummy, ignored */,
- GCCause::_heap_inspection /* GC Cause */,
- 0 /* total full collections, dummy, ignored */,
- request_full_gc), _out(out), _full_gc(request_full_gc),
- _parallel_thread_num(parallel_thread_num) {}
+ VM_GC_Service_Operation(0 /* total collections, dummy, ignored */,
+ GCCause::_heap_inspection /* GC Cause */,
+ 0 /* total full collections, dummy, ignored */,
+ request_full_gc), _out(out), _full_gc(request_full_gc),
+ _parallel_thread_num(parallel_thread_num) {}
~VM_GC_HeapInspection() {}
virtual VMOp_Type type() const { return VMOp_GC_HeapInspection; }
virtual bool skip_operation() const;
virtual bool doit_prologue();
virtual void doit();
+
protected:
bool collect();
};
-class VM_CollectForAllocation : public VM_GC_Operation {
+class VM_CollectForAllocation : public VM_GC_Collect_Operation {
protected:
size_t _word_size; // Size of object to be allocated (in number of words)
HeapWord* _result; // Allocation result (null if allocation failed)
@@ -186,7 +211,7 @@ class VM_CollectForAllocation : public VM_GC_Operation {
}
};
-class VM_CollectForMetadataAllocation: public VM_GC_Operation {
+class VM_CollectForMetadataAllocation: public VM_GC_Collect_Operation {
private:
MetaWord* _result;
size_t _size; // size of object to be allocated
diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp
index f693ab910ba..b15518ab225 100644
--- a/src/hotspot/share/gc/shared/gc_globals.hpp
+++ b/src/hotspot/share/gc/shared/gc_globals.hpp
@@ -199,10 +199,11 @@
range(1, (INT_MAX - 1)) \
\
product(bool, ParallelRefProcEnabled, false, \
- "Enable parallel reference processing whenever possible") \
+ "(Deprecated) Enable parallel reference processing " \
+ "whenever possible") \
\
product(bool, ParallelRefProcBalancingEnabled, true, \
- "Enable balancing of reference processing queues") \
+ "(Deprecated) Enable balancing of reference processing queues") \
\
product(size_t, ReferencesPerThread, 1000, EXPERIMENTAL, \
"Ergonomically start one thread for this amount of " \
diff --git a/src/hotspot/share/gc/shared/generationCounters.cpp b/src/hotspot/share/gc/shared/generationCounters.cpp
index 4af7b412aea..f6bbc1c2618 100644
--- a/src/hotspot/share/gc/shared/generationCounters.cpp
+++ b/src/hotspot/share/gc/shared/generationCounters.cpp
@@ -67,7 +67,7 @@ GenerationCounters::~GenerationCounters() {
FREE_C_HEAP_ARRAY(char, _name_space);
}
-void GenerationCounters::update_all(size_t curr_capacity) {
+void GenerationCounters::update_capacity(size_t curr_capacity) {
_current_size->set_value(curr_capacity);
}
diff --git a/src/hotspot/share/gc/shared/generationCounters.hpp b/src/hotspot/share/gc/shared/generationCounters.hpp
index 50672ab5d74..cb835b44e25 100644
--- a/src/hotspot/share/gc/shared/generationCounters.hpp
+++ b/src/hotspot/share/gc/shared/generationCounters.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -52,7 +52,7 @@ class GenerationCounters: public CHeapObj {
~GenerationCounters();
- void update_all(size_t curr_capacity);
+ void update_capacity(size_t curr_capacity);
const char* name_space() const { return _name_space; }
};
diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp
index 56d88d44d27..807d4197b12 100644
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp
@@ -625,6 +625,34 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) {
}
#endif
+bool ShenandoahBarrierC2Support::is_anti_dependent_load_at_control(PhaseIdealLoop* phase, Node* maybe_load, Node* store,
+ Node* control) {
+ return maybe_load->is_Load() && phase->C->can_alias(store->adr_type(), phase->C->get_alias_index(maybe_load->adr_type())) &&
+ phase->ctrl_or_self(maybe_load) == control;
+}
+
+void ShenandoahBarrierC2Support::maybe_push_anti_dependent_loads(PhaseIdealLoop* phase, Node* maybe_store, Node* control, Unique_Node_List &wq) {
+ if (!maybe_store->is_Store() && !maybe_store->is_LoadStore()) {
+ return;
+ }
+ Node* mem = maybe_store->in(MemNode::Memory);
+ for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
+ Node* u = mem->fast_out(i);
+ if (is_anti_dependent_load_at_control(phase, u, maybe_store, control)) {
+ wq.push(u);
+ }
+ }
+}
+
+void ShenandoahBarrierC2Support::push_data_inputs_at_control(PhaseIdealLoop* phase, Node* n, Node* ctrl, Unique_Node_List &wq) {
+ for (uint i = 0; i < n->req(); i++) {
+ Node* in = n->in(i);
+ if (in != nullptr && phase->has_ctrl(in) && phase->get_ctrl(in) == ctrl) {
+ wq.push(in);
+ }
+ }
+}
+
bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node* n, PhaseIdealLoop* phase) {
// That both nodes have the same control is not sufficient to prove
// domination, verify that there's no path from d to n
@@ -639,22 +667,9 @@ bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node*
if (m->is_Phi() && m->in(0)->is_Loop()) {
assert(phase->ctrl_or_self(m->in(LoopNode::EntryControl)) != c, "following loop entry should lead to new control");
} else {
- if (m->is_Store() || m->is_LoadStore()) {
- // Take anti-dependencies into account
- Node* mem = m->in(MemNode::Memory);
- for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
- Node* u = mem->fast_out(i);
- if (u->is_Load() && phase->C->can_alias(m->adr_type(), phase->C->get_alias_index(u->adr_type())) &&
- phase->ctrl_or_self(u) == c) {
- wq.push(u);
- }
- }
- }
- for (uint i = 0; i < m->req(); i++) {
- if (m->in(i) != nullptr && phase->ctrl_or_self(m->in(i)) == c) {
- wq.push(m->in(i));
- }
- }
+ // Take anti-dependencies into account
+ maybe_push_anti_dependent_loads(phase, m, c, wq);
+ push_data_inputs_at_control(phase, m, c, wq);
}
}
return true;
@@ -1006,7 +1021,20 @@ void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* lo
phase->register_new_node(val, ctrl);
}
-void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& uses_to_ignore, uint last, PhaseIdealLoop* phase) {
+void ShenandoahBarrierC2Support::collect_nodes_above_barrier(Unique_Node_List &nodes_above_barrier, PhaseIdealLoop* phase, Node* ctrl, Node* init_raw_mem) {
+ nodes_above_barrier.clear();
+ if (phase->has_ctrl(init_raw_mem) && phase->get_ctrl(init_raw_mem) == ctrl && !init_raw_mem->is_Phi()) {
+ nodes_above_barrier.push(init_raw_mem);
+ }
+ for (uint next = 0; next < nodes_above_barrier.size(); next++) {
+ Node* n = nodes_above_barrier.at(next);
+ // Take anti-dependencies into account
+ maybe_push_anti_dependent_loads(phase, n, ctrl, nodes_above_barrier);
+ push_data_inputs_at_control(phase, n, ctrl, nodes_above_barrier);
+ }
+}
+
+void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& nodes_above_barrier, uint last, PhaseIdealLoop* phase) {
Node* ctrl = phase->get_ctrl(barrier);
Node* init_raw_mem = fixer.find_mem(ctrl, barrier);
@@ -1017,30 +1045,17 @@ void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const Mem
// control will be after the expanded barrier. The raw memory (if
// its memory is control dependent on the barrier's input control)
// must stay above the barrier.
- uses_to_ignore.clear();
- if (phase->has_ctrl(init_raw_mem) && phase->get_ctrl(init_raw_mem) == ctrl && !init_raw_mem->is_Phi()) {
- uses_to_ignore.push(init_raw_mem);
- }
- for (uint next = 0; next < uses_to_ignore.size(); next++) {
- Node *n = uses_to_ignore.at(next);
- for (uint i = 0; i < n->req(); i++) {
- Node* in = n->in(i);
- if (in != nullptr && phase->has_ctrl(in) && phase->get_ctrl(in) == ctrl) {
- uses_to_ignore.push(in);
- }
- }
- }
+ collect_nodes_above_barrier(nodes_above_barrier, phase, ctrl, init_raw_mem);
for (DUIterator_Fast imax, i = ctrl->fast_outs(imax); i < imax; i++) {
Node* u = ctrl->fast_out(i);
if (u->_idx < last &&
u != barrier &&
!u->depends_only_on_test() && // preserve dependency on test
- !uses_to_ignore.member(u) &&
+ !nodes_above_barrier.member(u) &&
(u->in(0) != ctrl || (!u->is_Region() && !u->is_Phi())) &&
(ctrl->Opcode() != Op_CatchProj || u->Opcode() != Op_CreateEx)) {
Node* old_c = phase->ctrl_or_self(u);
- Node* c = old_c;
- if (c != ctrl ||
+ if (old_c != ctrl ||
is_dominator_same_ctrl(old_c, barrier, u, phase) ||
ShenandoahBarrierSetC2::is_shenandoah_state_load(u)) {
phase->igvn().rehash_node_delayed(u);
@@ -1315,7 +1330,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
// Expand load-reference-barriers
MemoryGraphFixer fixer(Compile::AliasIdxRaw, true, phase);
- Unique_Node_List uses_to_ignore;
+ Unique_Node_List nodes_above_barriers;
for (int i = state->load_reference_barriers_count() - 1; i >= 0; i--) {
ShenandoahLoadReferenceBarrierNode* lrb = state->load_reference_barrier(i);
uint last = phase->C->unique();
@@ -1410,7 +1425,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
Node* out_val = val_phi;
phase->register_new_node(val_phi, region);
- fix_ctrl(lrb, region, fixer, uses, uses_to_ignore, last, phase);
+ fix_ctrl(lrb, region, fixer, uses, nodes_above_barriers, last, phase);
ctrl = orig_ctrl;
diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp
index 93572cddc5b..63e8412a307 100644
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp
@@ -62,8 +62,12 @@ class ShenandoahBarrierC2Support : public AllStatic {
PhaseIdealLoop* phase, int flags);
static void call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr,
DecoratorSet decorators, PhaseIdealLoop* phase);
+
+ static void collect_nodes_above_barrier(Unique_Node_List &nodes_above_barrier, PhaseIdealLoop* phase, Node* ctrl,
+ Node* init_raw_mem);
+
static void test_in_cset(Node*& ctrl, Node*& not_cset_ctrl, Node* val, Node* raw_mem, PhaseIdealLoop* phase);
- static void fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& uses_to_ignore, uint last, PhaseIdealLoop* phase);
+ static void fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& nodes_above_barrier, uint last, PhaseIdealLoop* phase);
static Node* get_load_addr(PhaseIdealLoop* phase, VectorSet& visited, Node* lrb);
public:
@@ -76,6 +80,11 @@ class ShenandoahBarrierC2Support : public AllStatic {
static bool expand(Compile* C, PhaseIterGVN& igvn);
static void pin_and_expand(PhaseIdealLoop* phase);
+ static void push_data_inputs_at_control(PhaseIdealLoop* phase, Node* n, Node* ctrl,
+ Unique_Node_List &wq);
+ static bool is_anti_dependent_load_at_control(PhaseIdealLoop* phase, Node* maybe_load, Node* store, Node* control);
+
+ static void maybe_push_anti_dependent_loads(PhaseIdealLoop* phase, Node* maybe_store, Node* control, Unique_Node_List &wq);
#ifdef ASSERT
static void verify(RootNode* root);
#endif
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
index d635781f90f..df2cd18042f 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
@@ -184,9 +184,10 @@ void ShenandoahArguments::initialize() {
// Current default is good for generational collectors that run frequent young GCs.
// With Shenandoah, GC cycles are much less frequent, so we need we need sizing policy
// to converge faster over smaller number of resizing decisions.
- if (FLAG_IS_DEFAULT(TLABAllocationWeight)) {
+ if (strcmp(ShenandoahGCMode, "generational") && FLAG_IS_DEFAULT(TLABAllocationWeight)) {
FLAG_SET_DEFAULT(TLABAllocationWeight, 90);
}
+ // In generational mode, let TLABAllocationWeight keeps its default value of 35.
if (GCCardSizeInBytes < ShenandoahMinCardSizeInBytes) {
vm_exit_during_initialization(
@@ -217,6 +218,10 @@ void ShenandoahArguments::initialize_alignments() {
}
SpaceAlignment = align;
HeapAlignment = align;
+
+ if (FLAG_IS_DEFAULT(TLABSize)) {
+ TLABSize = MAX2(ShenandoahHeapRegion::region_size_bytes() / 256, (size_t) 32 * 1024);
+ }
}
CollectedHeap* ShenandoahArguments::create_heap() {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp
index 49d2df0cc93..0d9077be226 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp
@@ -150,7 +150,7 @@ class ShenandoahNMethodUnlinkClosure : public NMethodClosure {
ShenandoahNMethod::heal_nmethod_metadata(nm_data);
// Code cache unloading needs to know about on-stack nmethods. Arm the nmethods to get
// mark_as_maybe_on_stack() callbacks when they are used again.
- _bs->set_guard_value(nm, 0);
+ _bs->arm(nm);
}
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
index 5b5fe2c1af6..fda97c4836e 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
@@ -415,10 +415,6 @@ void ShenandoahConcurrentGC::entry_reset() {
msg);
op_reset();
}
-
- if (heap->mode()->is_generational()) {
- heap->old_generation()->card_scan()->mark_read_table_as_clean();
- }
}
void ShenandoahConcurrentGC::entry_scan_remembered_set() {
@@ -644,6 +640,10 @@ void ShenandoahConcurrentGC::op_reset() {
} else {
_generation->prepare_gc();
}
+
+ if (heap->mode()->is_generational()) {
+ heap->old_generation()->card_scan()->mark_read_table_as_clean();
+ }
}
class ShenandoahInitMarkUpdateRegionStateClosure : public ShenandoahHeapRegionClosure {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
index d8eb8e0a4e1..c0f3cf1a6a1 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
@@ -42,7 +42,7 @@
ShenandoahControlThread::ShenandoahControlThread() :
ShenandoahController(),
- _requested_gc_cause(GCCause::_no_cause_specified),
+ _requested_gc_cause(GCCause::_no_gc),
_degen_point(ShenandoahGC::_degenerated_outside_cycle) {
set_name("Shenandoah Control Thread");
create_and_start();
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
index 7aaf7bd9f48..c941379d576 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
@@ -136,9 +136,15 @@ void ShenandoahDegenGC::op_degenerated() {
heap->set_unload_classes(_generation->heuristics()->can_unload_classes() &&
(!heap->mode()->is_generational() || _generation->is_global()));
- if (heap->mode()->is_generational() && _generation->is_young()) {
- // Swap remembered sets for young
- _generation->swap_card_tables();
+ if (heap->mode()->is_generational()) {
+ // Clean the read table before swapping it. The end goal here is to have a clean
+ // write table, and to have the read table updated with the previous write table.
+ heap->old_generation()->card_scan()->mark_read_table_as_clean();
+
+ if (_generation->is_young()) {
+ // Swap remembered sets for young
+ _generation->swap_card_tables();
+ }
}
case _degenerated_roots:
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
index 1f84feb20e8..731868310f4 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
@@ -183,6 +183,29 @@ void ShenandoahGenerationalHeap::stop() {
regulator_thread()->stop();
}
+bool ShenandoahGenerationalHeap::requires_barriers(stackChunkOop obj) const {
+ if (is_idle()) {
+ return false;
+ }
+
+ if (is_concurrent_young_mark_in_progress() && is_in_young(obj) && !marking_context()->allocated_after_mark_start(obj)) {
+ // We are marking young, this object is in young, and it is below the TAMS
+ return true;
+ }
+
+ if (is_in_old(obj)) {
+ // Card marking barriers are required for objects in the old generation
+ return true;
+ }
+
+ if (has_forwarded_objects()) {
+ // Object may have pointers that need to be updated
+ return true;
+ }
+
+ return false;
+}
+
void ShenandoahGenerationalHeap::evacuate_collection_set(bool concurrent) {
ShenandoahRegionIterator regions;
ShenandoahGenerationalEvacuationTask task(this, ®ions, concurrent, false /* only promote regions */);
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
index cef5dfd7070..ed5a6f3d9a5 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
@@ -128,6 +128,8 @@ class ShenandoahGenerationalHeap : public ShenandoahHeap {
void stop() override;
+ bool requires_barriers(stackChunkOop obj) const override;
+
// Used for logging the result of a region transfer outside the heap lock
struct TransferResult {
bool success;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
index d00a99ee728..05eb0c299a5 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
@@ -790,8 +790,10 @@ size_t ShenandoahHeapRegion::setup_sizes(size_t max_heap_size) {
RegionCount = align_up(max_heap_size, RegionSizeBytes) / RegionSizeBytes;
guarantee(RegionCount >= MIN_NUM_REGIONS, "Should have at least minimum regions");
+ // Limit TLAB size for better startup behavior and more equitable distribution of memory between contending mutator threads.
guarantee(MaxTLABSizeWords == 0, "we should only set it once");
- MaxTLABSizeWords = align_down(RegionSizeWords, MinObjAlignment);
+ MaxTLABSizeWords = align_down(MIN2(RegionSizeWords, MAX2(RegionSizeWords / 32, (size_t) (256 * 1024) / HeapWordSize)),
+ MinObjAlignment);
guarantee(MaxTLABSizeBytes == 0, "we should only set it once");
MaxTLABSizeBytes = MaxTLABSizeWords * HeapWordSize;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMonitoringSupport.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMonitoringSupport.cpp
index 6b72cbdd62b..a2cf404cf55 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMonitoringSupport.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMonitoringSupport.cpp
@@ -51,7 +51,7 @@ class ShenandoahGenerationCounters : public GenerationCounters {
{};
void update_all() {
- GenerationCounters::update_all(_heap->capacity());
+ GenerationCounters::update_capacity(_heap->capacity());
}
};
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp
index 09971bb2630..291fadd1887 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp
@@ -55,6 +55,8 @@ class VM_ShenandoahOperation : public VM_Operation {
void log_active_generation(const char* prefix);
bool doit_prologue() override;
void doit_epilogue() override;
+
+ bool is_gc_operation() const override { return true; };
};
class VM_ShenandoahReferenceOperation : public VM_ShenandoahOperation {
diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp
index 0d6be2b789f..392d194a65b 100644
--- a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp
+++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp
@@ -105,3 +105,36 @@ oop ZBarrierSetNMethod::oop_load_no_keepalive(const nmethod* nm, int index) {
oop ZBarrierSetNMethod::oop_load_phantom(const nmethod* nm, int index) {
return ZNMethod::oop_load_phantom(nm, index);
}
+
+void ZBarrierSetNMethod::guard_with(nmethod* nm, int value) {
+ assert((value & not_entrant) == 0, "not_entrant bit is reserved");
+ ZLocker locker(ZNMethod::lock_for_nmethod(nm));
+ // Preserve the sticky bit
+ if (is_not_entrant(nm)) {
+ value |= not_entrant;
+ }
+ if (guard_value(nm) != value) {
+ // Patch the code only if needed.
+ set_guard_value(nm, value);
+ }
+}
+
+bool ZBarrierSetNMethod::is_armed(nmethod* nm) {
+ int value = guard_value(nm) & ~not_entrant;
+ return value != disarmed_guard_value();
+}
+
+void ZBarrierSetNMethod::make_not_entrant(nmethod* nm) {
+ ZLocker locker(ZNMethod::lock_for_nmethod(nm));
+ int value = guard_value(nm) | not_entrant; // permanent sticky value
+ set_guard_value(nm, value);
+}
+
+bool ZBarrierSetNMethod::is_not_entrant(nmethod* nm) {
+ return (guard_value(nm) & not_entrant) != 0;
+}
+
+uintptr_t ZBarrierSetNMethod::color(nmethod* nm) {
+ // color is stored at low order bits of int; conversion to uintptr_t is fine
+ return uintptr_t(guard_value(nm) & ~not_entrant);
+}
diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp
index 780d3772123..e0b7ba6c773 100644
--- a/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp
+++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.hpp
@@ -30,15 +30,27 @@
class nmethod;
class ZBarrierSetNMethod : public BarrierSetNMethod {
+ enum : int {
+ not_entrant = 1 << 31, // armed sticky bit, see make_not_entrant
+ };
+
protected:
virtual bool nmethod_entry_barrier(nmethod* nm);
public:
+ uintptr_t color(nmethod* nm);
+
virtual ByteSize thread_disarmed_guard_value_offset() const;
virtual int* disarmed_guard_value_address() const;
virtual oop oop_load_no_keepalive(const nmethod* nm, int index);
virtual oop oop_load_phantom(const nmethod* nm, int index);
+
+ virtual void make_not_entrant(nmethod* nm);
+ virtual bool is_not_entrant(nmethod* nm);
+ virtual void guard_with(nmethod* nm, int value);
+ virtual bool is_armed(nmethod* nm);
+ virtual void arm_all_nmethods() { ShouldNotCallThis(); }
};
#endif // SHARE_GC_Z_ZBARRIERSETNMETHOD_HPP
diff --git a/src/hotspot/share/gc/z/zGeneration.cpp b/src/hotspot/share/gc/z/zGeneration.cpp
index 534f0195c90..29c4d1fcc6a 100644
--- a/src/hotspot/share/gc/z/zGeneration.cpp
+++ b/src/hotspot/share/gc/z/zGeneration.cpp
@@ -442,6 +442,10 @@ class VM_ZOperation : public VM_Operation {
OopMapCache::try_trigger_cleanup();
}
+ virtual bool is_gc_operation() const {
+ return true;
+ }
+
bool success() const {
return _success;
}
@@ -1308,6 +1312,10 @@ class ZRendezvousGCThreads: public VM_Operation {
return true;
}
+ virtual bool is_gc_operation() const {
+ return true;
+ }
+
void doit() {
// Light weight "handshake" of the GC threads
SuspendibleThreadSet::synchronize();
diff --git a/src/hotspot/share/gc/z/zMark.cpp b/src/hotspot/share/gc/z/zMark.cpp
index 9846f1244ec..d1646da7604 100644
--- a/src/hotspot/share/gc/z/zMark.cpp
+++ b/src/hotspot/share/gc/z/zMark.cpp
@@ -578,6 +578,10 @@ class VM_ZMarkFlushOperation : public VM_Operation {
virtual VMOp_Type type() const {
return VMOp_ZMarkFlushOperation;
}
+
+ virtual bool is_gc_operation() const {
+ return true;
+ }
};
bool ZMark::flush() {
@@ -769,7 +773,7 @@ class ZMarkYoungNMethodClosure : public NMethodClosure {
ZNMethod::nmethod_patch_barriers(nm);
}
- _bs_nm->set_guard_value(nm, (int)untype(new_disarm_value_ptr));
+ _bs_nm->guard_with(nm, (int)untype(new_disarm_value_ptr));
if (complete_disarm) {
log_trace(gc, nmethod)("nmethod: " PTR_FORMAT " visited by young (complete) [" PTR_FORMAT " -> " PTR_FORMAT "]", p2i(nm), prev_color, untype(new_disarm_value_ptr));
diff --git a/src/hotspot/share/gc/z/zNMethod.cpp b/src/hotspot/share/gc/z/zNMethod.cpp
index bf592c20fa2..3f65d2eea97 100644
--- a/src/hotspot/share/gc/z/zNMethod.cpp
+++ b/src/hotspot/share/gc/z/zNMethod.cpp
@@ -241,7 +241,7 @@ void ZNMethod::disarm(nmethod* nm) {
void ZNMethod::set_guard_value(nmethod* nm, int value) {
BarrierSetNMethod* const bs = BarrierSet::barrier_set()->barrier_set_nmethod();
- bs->set_guard_value(nm, value);
+ bs->guard_with(nm, value);
}
void ZNMethod::nmethod_patch_barriers(nmethod* nm) {
@@ -300,9 +300,8 @@ void ZNMethod::nmethods_do(bool secondary, NMethodClosure* cl) {
}
uintptr_t ZNMethod::color(nmethod* nm) {
- BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
- // color is stored at low order bits of int; conversion to uintptr_t is fine
- return (uintptr_t)bs_nm->guard_value(nm);
+ ZBarrierSetNMethod* bs_nm = static_cast(BarrierSet::barrier_set()->barrier_set_nmethod());
+ return bs_nm->color(nm);
}
oop ZNMethod::oop_load_no_keepalive(const nmethod* nm, int index) {
diff --git a/src/hotspot/share/gc/z/zServiceability.cpp b/src/hotspot/share/gc/z/zServiceability.cpp
index 076d310ff71..e5ed99bfccc 100644
--- a/src/hotspot/share/gc/z/zServiceability.cpp
+++ b/src/hotspot/share/gc/z/zServiceability.cpp
@@ -50,31 +50,11 @@ static ZMemoryUsageInfo compute_memory_usage_info() {
return info;
}
-class ZGenerationCounters : public GenerationCounters {
-public:
- ZGenerationCounters(const char* name,
- int ordinal,
- int spaces,
- size_t min_capacity,
- size_t max_capacity,
- size_t curr_capacity)
- : GenerationCounters(name,
- ordinal,
- spaces,
- min_capacity,
- max_capacity,
- curr_capacity) {}
-
- void update_capacity(size_t capacity) {
- update_all(capacity);
- }
-};
-
// Class to expose perf counters used by jstat.
class ZServiceabilityCounters : public CHeapObj {
private:
- ZGenerationCounters _generation_young_counters;
- ZGenerationCounters _generation_old_counters;
+ GenerationCounters _generation_young_counters;
+ GenerationCounters _generation_old_counters;
HSpaceCounters _space_young_counters;
HSpaceCounters _space_old_counters;
CollectorCounters _minor_collection_counters;
diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h
index 73f60765a70..f97374553ca 100644
--- a/src/hotspot/share/include/jvm.h
+++ b/src/hotspot/share/include/jvm.h
@@ -353,6 +353,9 @@ JVM_HasReferencePendingList(JNIEnv *env);
JNIEXPORT void JNICALL
JVM_WaitForReferencePendingList(JNIEnv *env);
+JNIEXPORT jobject JNICALL
+JVM_ReferenceGet(JNIEnv *env, jobject ref);
+
JNIEXPORT jboolean JNICALL
JVM_ReferenceRefersTo(JNIEnv *env, jobject ref, jobject o);
diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp
index 1de7dd824f8..ad39169bca0 100644
--- a/src/hotspot/share/interpreter/abstractInterpreter.cpp
+++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp
@@ -148,7 +148,7 @@ AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(const methodHan
case vmIntrinsics::_fmaF: return java_lang_math_fmaF;
case vmIntrinsics::_dsqrt: return java_lang_math_sqrt;
case vmIntrinsics::_dsqrt_strict: return java_lang_math_sqrt_strict;
- case vmIntrinsics::_Reference_get: return java_lang_ref_reference_get;
+ case vmIntrinsics::_Reference_get0: return java_lang_ref_reference_get0;
case vmIntrinsics::_Object_init:
if (m->code_size() == 1) {
// We need to execute the special return bytecode to check for
@@ -210,7 +210,7 @@ vmIntrinsics::ID AbstractInterpreter::method_intrinsic(MethodKind kind) {
case java_lang_math_exp : return vmIntrinsics::_dexp;
case java_lang_math_fmaD : return vmIntrinsics::_fmaD;
case java_lang_math_fmaF : return vmIntrinsics::_fmaF;
- case java_lang_ref_reference_get: return vmIntrinsics::_Reference_get;
+ case java_lang_ref_reference_get0: return vmIntrinsics::_Reference_get0;
case java_util_zip_CRC32_update : return vmIntrinsics::_updateCRC32;
case java_util_zip_CRC32_updateBytes
: return vmIntrinsics::_updateBytesCRC32;
@@ -320,7 +320,7 @@ void AbstractInterpreter::print_method_kind(MethodKind kind) {
case java_util_zip_CRC32_updateByteBuffer : tty->print("java_util_zip_CRC32_updateByteBuffer"); break;
case java_util_zip_CRC32C_updateBytes : tty->print("java_util_zip_CRC32C_updateBytes"); break;
case java_util_zip_CRC32C_updateDirectByteBuffer: tty->print("java_util_zip_CRC32C_updateDirectByteByffer"); break;
- case java_lang_ref_reference_get : tty->print("java_lang_ref_reference_get"); break;
+ case java_lang_ref_reference_get0 : tty->print("java_lang_ref_reference_get0"); break;
case java_lang_Thread_currentThread : tty->print("java_lang_Thread_currentThread"); break;
case java_lang_Float_float16ToFloat : tty->print("java_lang_Float_float16ToFloat"); break;
case java_lang_Float_floatToFloat16 : tty->print("java_lang_Float_floatToFloat16"); break;
diff --git a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp
index b6876b3a2da..a3e93aa0a30 100644
--- a/src/hotspot/share/interpreter/abstractInterpreter.hpp
+++ b/src/hotspot/share/interpreter/abstractInterpreter.hpp
@@ -83,7 +83,7 @@ class AbstractInterpreter: AllStatic {
java_lang_math_exp, // implementation of java.lang.Math.exp (x)
java_lang_math_fmaF, // implementation of java.lang.Math.fma (x, y, z)
java_lang_math_fmaD, // implementation of java.lang.Math.fma (x, y, z)
- java_lang_ref_reference_get, // implementation of java.lang.ref.Reference.get()
+ java_lang_ref_reference_get0, // implementation of java.lang.ref.Reference.get()
java_util_zip_CRC32_update, // implementation of java.util.zip.CRC32.update()
java_util_zip_CRC32_updateBytes, // implementation of java.util.zip.CRC32.updateBytes()
java_util_zip_CRC32_updateByteBuffer, // implementation of java.util.zip.CRC32.updateByteBuffer()
diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp
index 798c0bfc7d8..1f912b2b00f 100644
--- a/src/hotspot/share/interpreter/bytecodeTracer.cpp
+++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp
@@ -29,18 +29,14 @@
#include "interpreter/bytecodeTracer.hpp"
#include "interpreter/bytecodes.hpp"
#include "interpreter/interpreter.hpp"
-#include "interpreter/interpreterRuntime.hpp"
#include "memory/resourceArea.hpp"
#include "oops/constantPool.inline.hpp"
#include "oops/methodData.hpp"
#include "oops/method.hpp"
-#include "oops/resolvedFieldEntry.hpp"
-#include "oops/resolvedIndyEntry.hpp"
-#include "oops/resolvedMethodEntry.hpp"
+#include "runtime/atomic.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/osThread.hpp"
-#include "runtime/timer.hpp"
#include "utilities/align.hpp"
// Prints the current bytecode and its attributes using bytecode-specific information.
@@ -85,10 +81,11 @@ class BytecodePrinter {
void bytecode_epilog(int bci, outputStream* st);
public:
- BytecodePrinter(int flags = 0) {
- _is_wide = false;
- _code = Bytecodes::_illegal;
- _flags = flags;
+ BytecodePrinter(int flags = 0) : _is_wide(false), _code(Bytecodes::_illegal), _flags(flags) {}
+
+#ifndef PRODUCT
+ BytecodePrinter(Method* prev_method) : BytecodePrinter(0) {
+ _current_method = prev_method;
}
// This method is called while executing the raw bytecodes, so none of
@@ -96,6 +93,10 @@ class BytecodePrinter {
void trace(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st) {
ResourceMark rm;
bool method_changed = _current_method != method();
+ _current_method = method();
+ _is_linked = method->method_holder()->is_linked();
+ assert(_is_linked, "this function must be called on methods that are already executing");
+
if (method_changed) {
// Note 1: This code will not work as expected with true MT/MP.
// Need an explicit lock or a different solution.
@@ -107,9 +108,6 @@ class BytecodePrinter {
st->print("[%zu] ", Thread::current()->osthread()->thread_id_for_printing());
method->print_name(st);
st->cr();
- _current_method = method();
- _is_linked = method->method_holder()->is_linked();
- assert(_is_linked, "this function must be called on methods that are already executing");
}
Bytecodes::Code code;
if (is_wide()) {
@@ -142,14 +140,13 @@ class BytecodePrinter {
_is_wide = (code == Bytecodes::_wide);
_code = Bytecodes::_illegal;
-#ifndef PRODUCT
if (TraceBytecodesStopAt != 0 && BytecodeCounter::counter_value() >= TraceBytecodesStopAt) {
TraceBytecodes = false;
}
-#endif
}
+#endif
- // Used for Method*::print_codes(). The input bcp comes from
+ // Used for Method::print_codes(). The input bcp comes from
// BytecodeStream, which will skip wide bytecodes.
void trace(const methodHandle& method, address bcp, outputStream* st) {
_current_method = method();
@@ -179,19 +176,18 @@ class BytecodePrinter {
};
#ifndef PRODUCT
-// We need a global instance to keep track of the states when the bytecodes
-// are executed. Access by multiple threads are controlled by ttyLocker.
-static BytecodePrinter _interpreter_printer;
+// We need a global instance to keep track of the method being printed so we can report that
+// the method has changed. If this method is redefined and removed, that's ok because the method passed
+// in won't match, and this will print the method passed in again. Racing threads changing this global
+// will result in reprinting the method passed in again.
+static Method* _method_currently_being_printed = nullptr;
void BytecodeTracer::trace_interpreter(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st) {
if (TraceBytecodes && BytecodeCounter::counter_value() >= TraceBytecodesAt) {
- ttyLocker ttyl; // 5065316: keep the following output coherent
- // The ttyLocker also prevents races between two threads
- // trying to use the single instance of BytecodePrinter.
- //
- // There used to be a leaf mutex here, but the ttyLocker will
- // work just as well, as long as the printing operations never block.
- _interpreter_printer.trace(method, bcp, tos, tos2, st);
+ BytecodePrinter printer(Atomic::load_acquire(&_method_currently_being_printed));
+ printer.trace(method, bcp, tos, tos2, st);
+ // Save method currently being printed to detect when method printing changes.
+ Atomic::release_store(&_method_currently_being_printed, method());
}
}
#endif
diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp
index fcc0b96efa4..8e7e5772ba6 100644
--- a/src/hotspot/share/interpreter/interpreterRuntime.cpp
+++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp
@@ -500,6 +500,10 @@ JRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThrea
h_method->print_value_string(), current_bci, p2i(current), current->name());
Exceptions::log_exception(h_exception, tempst.as_string());
}
+ if (log_is_enabled(Info, exceptions, stacktrace)) {
+ Exceptions::log_exception_stacktrace(h_exception, h_method, current_bci);
+ }
+
// Don't go paging in something which won't be used.
// else if (extable->length() == 0) {
// // disabled for now - interpreter is not using shortcut yet
diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
index 533c88cce9e..928d1ac9f9c 100644
--- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
+++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
@@ -204,7 +204,7 @@ void TemplateInterpreterGenerator::generate_all() {
method_entry(java_lang_math_pow )
method_entry(java_lang_math_fmaF )
method_entry(java_lang_math_fmaD )
- method_entry(java_lang_ref_reference_get)
+ method_entry(java_lang_ref_reference_get0)
AbstractInterpreter::initialize_method_handle_entries();
method_entry(java_util_zip_CRC32C_updateBytes)
@@ -228,6 +228,7 @@ void TemplateInterpreterGenerator::generate_all() {
// entries for `native` methods to use the same address in case
// intrinsic is disabled.
native_method_entry(java_lang_Thread_currentThread)
+ native_method_entry(java_lang_ref_reference_get0)
native_method_entry(java_util_zip_CRC32_update)
native_method_entry(java_util_zip_CRC32_updateBytes)
@@ -465,7 +466,7 @@ address TemplateInterpreterGenerator::generate_intrinsic_entry(AbstractInterpret
case Interpreter::java_lang_math_fmaF : entry_point = generate_math_entry(kind); break;
case Interpreter::java_lang_math_sqrt_strict
: entry_point = generate_math_entry(Interpreter::java_lang_math_sqrt); break;
- case Interpreter::java_lang_ref_reference_get
+ case Interpreter::java_lang_ref_reference_get0
: entry_point = generate_Reference_get_entry(); break;
case Interpreter::java_util_zip_CRC32_update
: entry_point = generate_CRC32_update_entry(); break;
diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
index c4eeb3fa840..8fa0835216d 100644
--- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
+++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
@@ -64,7 +64,7 @@ void ZeroInterpreterGenerator::generate_all() {
method_entry(java_lang_math_exp );
method_entry(java_lang_math_fmaD );
method_entry(java_lang_math_fmaF );
- method_entry(java_lang_ref_reference_get);
+ method_entry(java_lang_ref_reference_get0);
AbstractInterpreter::initialize_method_handle_entries();
@@ -107,7 +107,7 @@ address ZeroInterpreterGenerator::generate_method_entry(
case Interpreter::java_lang_math_exp : // fall thru
case Interpreter::java_lang_math_fmaD : // fall thru
case Interpreter::java_lang_math_fmaF : entry_point = generate_math_entry(kind); break;
- case Interpreter::java_lang_ref_reference_get
+ case Interpreter::java_lang_ref_reference_get0
: entry_point = generate_Reference_get_entry(); break;
default:
fatal("unexpected method kind: %d", kind);
diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.cpp
index 71b7014e6ec..aa594ea6d1f 100644
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.cpp
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.cpp
@@ -36,14 +36,6 @@
#include "utilities/preserveException.hpp"
#include "utilities/macros.hpp"
-class JfrRecorderThread : public JavaThread {
- public:
- JfrRecorderThread(ThreadFunction entry_point) : JavaThread(entry_point) {}
- virtual ~JfrRecorderThread() {}
-
- virtual bool is_JfrRecorder_thread() const { return true; }
-};
-
static Thread* start_thread(instanceHandle thread_oop, ThreadFunction proc, TRAPS) {
assert(thread_oop.not_null(), "invariant");
assert(proc != nullptr, "invariant");
diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.hpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.hpp
index df8b1f55a4e..34993e4e6cf 100644
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.hpp
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.hpp
@@ -26,9 +26,9 @@
#define SHARE_JFR_RECORDER_SERVICE_JFRRECORDERTHREAD_HPP
#include "memory/allStatic.hpp"
+#include "runtime/javaThread.hpp"
#include "utilities/debug.hpp"
-class JavaThread;
class JfrCheckpointManager;
class JfrPostBox;
class Thread;
@@ -42,4 +42,12 @@ class JfrRecorderThreadEntry : AllStatic {
static bool start(JfrCheckpointManager* cp_manager, JfrPostBox* post_box, TRAPS);
};
+class JfrRecorderThread : public JavaThread {
+ public:
+ JfrRecorderThread(ThreadFunction entry_point) : JavaThread(entry_point) {}
+ virtual ~JfrRecorderThread() {}
+
+ virtual bool is_JfrRecorder_thread() const { return true; }
+};
+
#endif // SHARE_JFR_RECORDER_SERVICE_JFRRECORDERTHREAD_HPP
diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp
index 4b805a98a32..67d311e2a6e 100644
--- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp
+++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp
@@ -79,7 +79,8 @@ JfrThreadLocal::JfrThreadLocal() :
_enqueued_requests(false),
_vthread(false),
_notified(false),
- _dead(false)
+ _dead(false),
+ _sampling_critical_section(false)
#ifdef LINUX
,_cpu_timer(nullptr),
_cpu_time_jfr_locked(UNLOCKED),
diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
index 001a40f74bc..22ced5a4411 100644
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
@@ -1207,7 +1207,7 @@ C2V_VMENTRY_0(jint, installCode0, (JNIEnv *env, jobject,
assert(JVMCIENV->isa_HotSpotNmethod(installed_code_handle), "wrong type");
// Clear the link to an old nmethod first
JVMCIObject nmethod_mirror = installed_code_handle;
- JVMCIENV->invalidate_nmethod_mirror(nmethod_mirror, true, nmethod::ChangeReason::JVMCI_replacing_with_new_code, JVMCI_CHECK_0);
+ JVMCIENV->invalidate_nmethod_mirror(nmethod_mirror, true, nmethod::InvalidationReason::JVMCI_REPLACED_WITH_NEW_CODE, JVMCI_CHECK_0);
} else {
assert(JVMCIENV->isa_InstalledCode(installed_code_handle), "wrong type");
}
@@ -1218,6 +1218,14 @@ C2V_VMENTRY_0(jint, installCode0, (JNIEnv *env, jobject,
return result;
C2V_END
+C2V_VMENTRY_0(jobject, getInvalidationReasonDescription, (JNIEnv *env, jobject, jint invalidation_reason))
+ HandleMark hm(THREAD);
+ JNIHandleMark jni_hm(thread);
+ nmethod::InvalidationReason reason = static_cast(invalidation_reason);
+ JVMCIObject desc = JVMCIENV->create_string(nmethod::invalidation_reason_to_string(reason), JVMCI_CHECK_NULL);
+ return JVMCIENV->get_jobject(desc);
+C2V_END
+
C2V_VMENTRY(void, resetCompilationStatistics, (JNIEnv* env, jobject))
JVMCICompiler* compiler = JVMCICompiler::instance(true, CHECK);
CompilerStatistics* stats = compiler->stats();
@@ -1383,7 +1391,7 @@ C2V_VMENTRY(void, reprofile, (JNIEnv* env, jobject, ARGUMENT_PAIR(method)))
nmethod* code = method->code();
if (code != nullptr) {
- code->make_not_entrant(nmethod::ChangeReason::JVMCI_reprofile);
+ code->make_not_entrant(nmethod::InvalidationReason::JVMCI_REPROFILE);
}
MethodData* method_data = method->method_data();
@@ -1396,9 +1404,14 @@ C2V_VMENTRY(void, reprofile, (JNIEnv* env, jobject, ARGUMENT_PAIR(method)))
C2V_END
-C2V_VMENTRY(void, invalidateHotSpotNmethod, (JNIEnv* env, jobject, jobject hs_nmethod, jboolean deoptimize))
+C2V_VMENTRY(void, invalidateHotSpotNmethod, (JNIEnv* env, jobject, jobject hs_nmethod, jboolean deoptimize, jint invalidation_reason))
+ int first = static_cast(nmethod::InvalidationReason::C1_CODEPATCH);
+ int last = static_cast(nmethod::InvalidationReason::INVALIDATION_REASONS_COUNT);
+ if (invalidation_reason < first || invalidation_reason >= last) {
+ JVMCI_THROW_MSG(IllegalArgumentException, err_msg("Invalid invalidation_reason: %d", invalidation_reason));
+ }
JVMCIObject nmethod_mirror = JVMCIENV->wrap(hs_nmethod);
- JVMCIENV->invalidate_nmethod_mirror(nmethod_mirror, deoptimize, nmethod::ChangeReason::JVMCI_invalidate_nmethod, JVMCI_CHECK);
+ JVMCIENV->invalidate_nmethod_mirror(nmethod_mirror, deoptimize, static_cast(invalidation_reason), JVMCI_CHECK);
C2V_END
C2V_VMENTRY_NULL(jlongArray, collectCounters, (JNIEnv* env, jobject))
@@ -1823,7 +1836,7 @@ C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv* env, jobject, jobject _hs_
if (!fst.current()->is_compiled_frame()) {
JVMCI_THROW_MSG(IllegalStateException, "compiled stack frame expected");
}
- fst.current()->cb()->as_nmethod()->make_not_entrant(nmethod::ChangeReason::JVMCI_materialize_virtual_object);
+ fst.current()->cb()->as_nmethod()->make_not_entrant(nmethod::InvalidationReason::JVMCI_MATERIALIZE_VIRTUAL_OBJECT);
}
Deoptimization::deoptimize(thread, *fst.current(), Deoptimization::Reason_none);
// look for the frame again as it has been updated by deopt (pc, deopt state...)
@@ -3352,6 +3365,7 @@ JNINativeMethod CompilerToVM::methods[] = {
{CC "getResolvedJavaType0", CC "(Ljava/lang/Object;JZ)" HS_KLASS, FN_PTR(getResolvedJavaType0)},
{CC "readConfiguration", CC "()[" OBJECT, FN_PTR(readConfiguration)},
{CC "installCode0", CC "(JJZ" HS_COMPILED_CODE "[" OBJECT INSTALLED_CODE "J[B)I", FN_PTR(installCode0)},
+ {CC "getInvalidationReasonDescription", CC "(I)" STRING, FN_PTR(getInvalidationReasonDescription)},
{CC "getInstallCodeFlags", CC "()I", FN_PTR(getInstallCodeFlags)},
{CC "resetCompilationStatistics", CC "()V", FN_PTR(resetCompilationStatistics)},
{CC "disassembleCodeBlob", CC "(" INSTALLED_CODE ")" STRING, FN_PTR(disassembleCodeBlob)},
@@ -3360,7 +3374,7 @@ JNINativeMethod CompilerToVM::methods[] = {
{CC "getLocalVariableTableStart", CC "(" HS_METHOD2 ")J", FN_PTR(getLocalVariableTableStart)},
{CC "getLocalVariableTableLength", CC "(" HS_METHOD2 ")I", FN_PTR(getLocalVariableTableLength)},
{CC "reprofile", CC "(" HS_METHOD2 ")V", FN_PTR(reprofile)},
- {CC "invalidateHotSpotNmethod", CC "(" HS_NMETHOD "Z)V", FN_PTR(invalidateHotSpotNmethod)},
+ {CC "invalidateHotSpotNmethod", CC "(" HS_NMETHOD "ZI)V", FN_PTR(invalidateHotSpotNmethod)},
{CC "collectCounters", CC "()[J", FN_PTR(collectCounters)},
{CC "getCountersSize", CC "()I", FN_PTR(getCountersSize)},
{CC "setCountersSize", CC "(I)Z", FN_PTR(setCountersSize)},
diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp
index 1beb1917b9a..b6d919fdfe9 100644
--- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp
@@ -343,7 +343,7 @@ JVMCIObjectArray CompilerToVM::initialize_intrinsics(JVMCI_TRAPS) {
return vmIntrinsics;
}
-#define PREDEFINED_CONFIG_FLAGS(do_bool_flag, do_int_flag, do_intx_flag, do_uintx_flag) \
+#define PREDEFINED_CONFIG_FLAGS(do_bool_flag, do_int_flag, do_size_t_flag, do_intx_flag, do_uintx_flag) \
do_int_flag(AllocateInstancePrefetchLines) \
do_int_flag(AllocatePrefetchDistance) \
do_intx_flag(AllocatePrefetchInstr) \
@@ -354,7 +354,7 @@ JVMCIObjectArray CompilerToVM::initialize_intrinsics(JVMCI_TRAPS) {
do_bool_flag(BootstrapJVMCI) \
do_bool_flag(CITime) \
do_bool_flag(CITimeEach) \
- do_uintx_flag(CodeCacheSegmentSize) \
+ do_size_t_flag(CodeCacheSegmentSize) \
do_intx_flag(CodeEntryAlignment) \
do_int_flag(ContendedPaddingWidth) \
do_bool_flag(DontCompileHugeMethods) \
@@ -541,16 +541,17 @@ jobjectArray readConfiguration0(JNIEnv *env, JVMCI_TRAPS) {
JVMCIObject vmFlagObj = JVMCIENV->new_VMFlag(fname, ftype, value, JVMCI_CHECK_NULL); \
JVMCIENV->put_object_at(vmFlags, i++, vmFlagObj); \
}
-#define ADD_BOOL_FLAG(name) ADD_FLAG(bool, name, BOXED_BOOLEAN)
-#define ADD_INT_FLAG(name) ADD_FLAG(int, name, BOXED_LONG)
-#define ADD_INTX_FLAG(name) ADD_FLAG(intx, name, BOXED_LONG)
-#define ADD_UINTX_FLAG(name) ADD_FLAG(uintx, name, BOXED_LONG)
+#define ADD_BOOL_FLAG(name) ADD_FLAG(bool, name, BOXED_BOOLEAN)
+#define ADD_INT_FLAG(name) ADD_FLAG(int, name, BOXED_LONG)
+#define ADD_SIZE_T_FLAG(name) ADD_FLAG(size_t, name, BOXED_LONG)
+#define ADD_INTX_FLAG(name) ADD_FLAG(intx, name, BOXED_LONG)
+#define ADD_UINTX_FLAG(name) ADD_FLAG(uintx, name, BOXED_LONG)
- len = 0 + PREDEFINED_CONFIG_FLAGS(COUNT_FLAG, COUNT_FLAG, COUNT_FLAG, COUNT_FLAG);
+ len = 0 + PREDEFINED_CONFIG_FLAGS(COUNT_FLAG, COUNT_FLAG, COUNT_FLAG, COUNT_FLAG, COUNT_FLAG);
JVMCIObjectArray vmFlags = JVMCIENV->new_VMFlag_array(len, JVMCI_CHECK_NULL);
int i = 0;
JVMCIObject value;
- PREDEFINED_CONFIG_FLAGS(ADD_BOOL_FLAG, ADD_INT_FLAG, ADD_INTX_FLAG, ADD_UINTX_FLAG)
+ PREDEFINED_CONFIG_FLAGS(ADD_BOOL_FLAG, ADD_INT_FLAG, ADD_SIZE_T_FLAG, ADD_INTX_FLAG, ADD_UINTX_FLAG)
JVMCIObjectArray vmIntrinsics = CompilerToVM::initialize_intrinsics(JVMCI_CHECK_NULL);
diff --git a/src/hotspot/share/jvmci/jvmciEnv.cpp b/src/hotspot/share/jvmci/jvmciEnv.cpp
index 851ead247f1..ae64daf9f20 100644
--- a/src/hotspot/share/jvmci/jvmciEnv.cpp
+++ b/src/hotspot/share/jvmci/jvmciEnv.cpp
@@ -1749,7 +1749,7 @@ void JVMCIEnv::initialize_installed_code(JVMCIObject installed_code, CodeBlob* c
}
-void JVMCIEnv::invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimize, nmethod::ChangeReason change_reason, JVMCI_TRAPS) {
+void JVMCIEnv::invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimize, nmethod::InvalidationReason invalidation_reason, JVMCI_TRAPS) {
if (mirror.is_null()) {
JVMCI_THROW(NullPointerException);
}
@@ -1772,7 +1772,7 @@ void JVMCIEnv::invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimize, nm
if (!deoptimize) {
// Prevent future executions of the nmethod but let current executions complete.
- nm->make_not_entrant(change_reason);
+ nm->make_not_entrant(invalidation_reason);
// Do not clear the address field here as the Java code may still
// want to later call this method with deoptimize == true. That requires
@@ -1781,7 +1781,7 @@ void JVMCIEnv::invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimize, nm
// Deoptimize the nmethod immediately.
DeoptimizationScope deopt_scope;
deopt_scope.mark(nm);
- nm->make_not_entrant(change_reason);
+ nm->make_not_entrant(invalidation_reason);
nm->make_deoptimized();
deopt_scope.deoptimize_marked();
diff --git a/src/hotspot/share/jvmci/jvmciEnv.hpp b/src/hotspot/share/jvmci/jvmciEnv.hpp
index b7b7c8f6771..b49bba88b6b 100644
--- a/src/hotspot/share/jvmci/jvmciEnv.hpp
+++ b/src/hotspot/share/jvmci/jvmciEnv.hpp
@@ -462,7 +462,7 @@ class JVMCIEnv : public ResourceObj {
// field of `mirror` to prevent it from being called.
// If `deoptimize` is true, the nmethod is immediately deoptimized.
// The HotSpotNmethod.address field is zero upon returning.
- void invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimze, nmethod::ChangeReason change_reason, JVMCI_TRAPS);
+ void invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimze, nmethod::InvalidationReason invalidation_reason, JVMCI_TRAPS);
void initialize_installed_code(JVMCIObject installed_code, CodeBlob* cb, JVMCI_TRAPS);
diff --git a/src/hotspot/share/jvmci/jvmciJavaClasses.hpp b/src/hotspot/share/jvmci/jvmciJavaClasses.hpp
index d5fcd2aaaba..432fefe56d1 100644
--- a/src/hotspot/share/jvmci/jvmciJavaClasses.hpp
+++ b/src/hotspot/share/jvmci/jvmciJavaClasses.hpp
@@ -102,6 +102,7 @@
boolean_field(HotSpotNmethod, isDefault) \
long_field(HotSpotNmethod, compileIdSnapshot) \
object_field(HotSpotNmethod, method, "Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl;") \
+ int_field(HotSpotNmethod, invalidationReason) \
jvmci_constructor(HotSpotNmethod, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl;Ljava/lang/String;ZJ)V") \
end_class \
start_class(HotSpotCompiledCode, jdk_vm_ci_hotspot_HotSpotCompiledCode) \
diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp
index 1f10e132eff..24ea4936822 100644
--- a/src/hotspot/share/jvmci/jvmciRuntime.cpp
+++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp
@@ -797,7 +797,7 @@ void JVMCINMethodData::set_nmethod_mirror(nmethod* nm, oop new_mirror) {
Universe::heap()->register_nmethod(nm);
}
-void JVMCINMethodData::invalidate_nmethod_mirror(nmethod* nm) {
+void JVMCINMethodData::invalidate_nmethod_mirror(nmethod* nm, nmethod::InvalidationReason invalidation_reason) {
oop nmethod_mirror = get_nmethod_mirror(nm);
if (nmethod_mirror == nullptr) {
return;
@@ -816,12 +816,20 @@ void JVMCINMethodData::invalidate_nmethod_mirror(nmethod* nm) {
HotSpotJVMCI::InstalledCode::set_address(jvmciEnv, nmethod_mirror, 0);
HotSpotJVMCI::InstalledCode::set_entryPoint(jvmciEnv, nmethod_mirror, 0);
HotSpotJVMCI::HotSpotInstalledCode::set_codeStart(jvmciEnv, nmethod_mirror, 0);
+ if (HotSpotJVMCI::HotSpotNmethod::invalidationReason(jvmciEnv, nmethod_mirror) ==
+ static_cast(nmethod::InvalidationReason::NOT_INVALIDATED)) {
+ HotSpotJVMCI::HotSpotNmethod::set_invalidationReason(jvmciEnv, nmethod_mirror, static_cast(invalidation_reason));
+ }
} else if (nm->is_not_entrant()) {
// Zero the entry point so any new invocation will fail but keep
// the address link around that so that existing activations can
// be deoptimized via the mirror (i.e. JVMCIEnv::invalidate_installed_code).
HotSpotJVMCI::InstalledCode::set_entryPoint(jvmciEnv, nmethod_mirror, 0);
HotSpotJVMCI::HotSpotInstalledCode::set_codeStart(jvmciEnv, nmethod_mirror, 0);
+ if (HotSpotJVMCI::HotSpotNmethod::invalidationReason(jvmciEnv, nmethod_mirror) ==
+ static_cast(nmethod::InvalidationReason::NOT_INVALIDATED)) {
+ HotSpotJVMCI::HotSpotNmethod::set_invalidationReason(jvmciEnv, nmethod_mirror, static_cast(invalidation_reason));
+ }
}
}
@@ -2184,7 +2192,7 @@ JVMCI::CodeInstallResult JVMCIRuntime::register_method(JVMCIEnv* JVMCIENV,
tty->print_cr("Replacing method %s", method_name);
}
if (old != nullptr) {
- old->make_not_entrant(nmethod::ChangeReason::JVMCI_register_method);
+ old->make_not_entrant(nmethod::InvalidationReason::JVMCI_REPLACED_WITH_NEW_CODE);
}
LogTarget(Info, nmethod, install) lt;
diff --git a/src/hotspot/share/jvmci/jvmciRuntime.hpp b/src/hotspot/share/jvmci/jvmciRuntime.hpp
index b49e09a1884..95c7d32f928 100644
--- a/src/hotspot/share/jvmci/jvmciRuntime.hpp
+++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp
@@ -121,7 +121,7 @@ class JVMCINMethodData : public ResourceObj {
// Clears the HotSpotNmethod.address field in the mirror. If nm
// is dead, the HotSpotNmethod.entryPoint field is also cleared.
- void invalidate_nmethod_mirror(nmethod* nm);
+ void invalidate_nmethod_mirror(nmethod* nm, nmethod::InvalidationReason invalidation_reason);
// Gets the mirror from nm's oops table.
oop get_nmethod_mirror(nmethod* nm);
diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
index e26c815946d..bc930b1e1dc 100644
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
@@ -564,7 +564,30 @@
declare_constant_with_value("LockStack::_end_offset", LockStack::end_offset()) \
declare_constant_with_value("OMCache::oop_to_oop_difference", OMCache::oop_to_oop_difference()) \
declare_constant_with_value("OMCache::oop_to_monitor_difference", OMCache::oop_to_monitor_difference()) \
- \
+ \
+ declare_constant(nmethod::InvalidationReason::NOT_INVALIDATED) \
+ declare_constant(nmethod::InvalidationReason::C1_CODEPATCH) \
+ declare_constant(nmethod::InvalidationReason::C1_DEOPTIMIZE) \
+ declare_constant(nmethod::InvalidationReason::C1_DEOPTIMIZE_FOR_PATCHING) \
+ declare_constant(nmethod::InvalidationReason::C1_PREDICATE_FAILED_TRAP) \
+ declare_constant(nmethod::InvalidationReason::CI_REPLAY) \
+ declare_constant(nmethod::InvalidationReason::UNLOADING) \
+ declare_constant(nmethod::InvalidationReason::UNLOADING_COLD) \
+ declare_constant(nmethod::InvalidationReason::JVMCI_INVALIDATE) \
+ declare_constant(nmethod::InvalidationReason::JVMCI_MATERIALIZE_VIRTUAL_OBJECT) \
+ declare_constant(nmethod::InvalidationReason::JVMCI_REPLACED_WITH_NEW_CODE) \
+ declare_constant(nmethod::InvalidationReason::JVMCI_REPROFILE) \
+ declare_constant(nmethod::InvalidationReason::MARKED_FOR_DEOPTIMIZATION) \
+ declare_constant(nmethod::InvalidationReason::MISSING_EXCEPTION_HANDLER) \
+ declare_constant(nmethod::InvalidationReason::NOT_USED) \
+ declare_constant(nmethod::InvalidationReason::OSR_INVALIDATION_BACK_BRANCH) \
+ declare_constant(nmethod::InvalidationReason::OSR_INVALIDATION_FOR_COMPILING_WITH_C1) \
+ declare_constant(nmethod::InvalidationReason::OSR_INVALIDATION_OF_LOWER_LEVEL) \
+ declare_constant(nmethod::InvalidationReason::SET_NATIVE_FUNCTION) \
+ declare_constant(nmethod::InvalidationReason::UNCOMMON_TRAP) \
+ declare_constant(nmethod::InvalidationReason::WHITEBOX_DEOPTIMIZATION) \
+ declare_constant(nmethod::InvalidationReason::ZOMBIE) \
+ \
declare_constant(CodeInstaller::VERIFIED_ENTRY) \
declare_constant(CodeInstaller::UNVERIFIED_ENTRY) \
declare_constant(CodeInstaller::OSR_ENTRY) \
diff --git a/src/hotspot/share/libadt/vectset.cpp b/src/hotspot/share/libadt/vectset.cpp
index a4224b0adb1..176660d8302 100644
--- a/src/hotspot/share/libadt/vectset.cpp
+++ b/src/hotspot/share/libadt/vectset.cpp
@@ -47,7 +47,6 @@ void VectorSet::init(Arena* arena) {
// Expand the existing set to a bigger size
void VectorSet::grow(uint new_word_capacity) {
- _nesting.check(_set_arena); // Check if a potential reallocation in the arena is safe
assert(new_word_capacity >= _size, "Should have been checked before, use maybe_grow?");
assert(new_word_capacity < (1U << 30), "");
uint x = next_power_of_2(new_word_capacity);
diff --git a/src/hotspot/share/libadt/vectset.hpp b/src/hotspot/share/libadt/vectset.hpp
index 9314778e033..7dbe70000c7 100644
--- a/src/hotspot/share/libadt/vectset.hpp
+++ b/src/hotspot/share/libadt/vectset.hpp
@@ -52,6 +52,7 @@ class VectorSet : public AnyObj {
// Grow vector to required word capacity
void maybe_grow(uint new_word_capacity) {
+ _nesting.check(_set_arena); // Check if a potential reallocation in the arena is safe
if (new_word_capacity >= _size) {
grow(new_word_capacity);
}
diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp
index 34927a9b613..9edf81d3f27 100644
--- a/src/hotspot/share/logging/logTag.hpp
+++ b/src/hotspot/share/logging/logTag.hpp
@@ -216,6 +216,7 @@ class outputStream;
LOG_TAG(valuebasedclasses) \
LOG_TAG(verification) \
LOG_TAG(verify) \
+ LOG_TAG(vmatree) \
LOG_TAG(vmmutex) \
LOG_TAG(vmoperation) \
LOG_TAG(vmthread) \
diff --git a/src/hotspot/share/memory/heap.cpp b/src/hotspot/share/memory/heap.cpp
index b111c61f15a..a1333ed13e9 100644
--- a/src/hotspot/share/memory/heap.cpp
+++ b/src/hotspot/share/memory/heap.cpp
@@ -280,7 +280,7 @@ void* CodeHeap::allocate(size_t instance_size) {
}
// Ensure minimum size for allocation to the heap.
- number_of_segments = MAX2((int)CodeCacheMinBlockLength, (int)number_of_segments);
+ number_of_segments = MAX2(CodeCacheMinBlockLength, number_of_segments);
if (_next_segment + number_of_segments <= _number_of_committed_segments) {
mark_segmap_as_used(_next_segment, _next_segment + number_of_segments, false);
diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp
index ffdeec06ef8..100ed9b42dd 100644
--- a/src/hotspot/share/memory/universe.cpp
+++ b/src/hotspot/share/memory/universe.cpp
@@ -60,6 +60,7 @@
#include "oops/compressedOops.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/instanceMirrorKlass.hpp"
+#include "oops/jmethodIDTable.hpp"
#include "oops/klass.inline.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/objLayout.hpp"
@@ -436,6 +437,9 @@ void Universe::genesis(TRAPS) {
vmSymbols::initialize();
+ // Initialize table for matching jmethodID, before SystemDictionary.
+ JmethodIDTable::initialize();
+
SystemDictionary::initialize(CHECK);
// Create string constants
@@ -444,7 +448,6 @@ void Universe::genesis(TRAPS) {
s = StringTable::intern("-2147483648", CHECK);
_the_min_jint_string = OopHandle(vm_global(), s);
-
#if INCLUDE_CDS
if (CDSConfig::is_using_archive()) {
// Verify shared interfaces array.
diff --git a/src/hotspot/share/nmt/memBaseline.cpp b/src/hotspot/share/nmt/memBaseline.cpp
index 5f5431b765f..8661f91174a 100644
--- a/src/hotspot/share/nmt/memBaseline.cpp
+++ b/src/hotspot/share/nmt/memBaseline.cpp
@@ -160,7 +160,7 @@ bool MemBaseline::baseline_allocation_sites() {
// Virtual memory allocation sites
VirtualMemoryAllocationWalker virtual_memory_walker;
- if (!VirtualMemoryTracker::walk_virtual_memory(&virtual_memory_walker)) {
+ if (!VirtualMemoryTracker::Instance::walk_virtual_memory(&virtual_memory_walker)) {
return false;
}
diff --git a/src/hotspot/share/nmt/memMapPrinter.cpp b/src/hotspot/share/nmt/memMapPrinter.cpp
index 515d3804638..41d977e9639 100644
--- a/src/hotspot/share/nmt/memMapPrinter.cpp
+++ b/src/hotspot/share/nmt/memMapPrinter.cpp
@@ -157,7 +157,7 @@ class CachedNMTInformation : public VirtualMemoryWalker {
// Iterate all NMT virtual memory regions and fill this cache.
bool fill_from_nmt() {
- return VirtualMemoryTracker::walk_virtual_memory(this);
+ return VirtualMemoryTracker::Instance::walk_virtual_memory(this);
}
};
diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp
index f7d7e121462..c1c847a4f2e 100644
--- a/src/hotspot/share/nmt/memReporter.cpp
+++ b/src/hotspot/share/nmt/memReporter.cpp
@@ -22,6 +22,7 @@
*
*/
#include "cds/filemap.hpp"
+#include "logging/log.hpp"
#include "memory/metaspace.hpp"
#include "memory/metaspaceUtils.hpp"
#include "nmt/mallocTracker.hpp"
@@ -29,6 +30,8 @@
#include "nmt/memReporter.hpp"
#include "nmt/memTracker.hpp"
#include "nmt/memoryFileTracker.hpp"
+#include "nmt/regionsTree.hpp"
+#include "nmt/regionsTree.inline.hpp"
#include "nmt/threadStackTracker.hpp"
#include "nmt/virtualMemoryTracker.hpp"
#include "utilities/debug.hpp"
@@ -432,34 +435,45 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion*
}
if (all_committed) {
- CommittedRegionIterator itr = reserved_rgn->iterate_committed_regions();
- const CommittedMemoryRegion* committed_rgn = itr.next();
- if (committed_rgn->size() == reserved_rgn->size() && committed_rgn->call_stack()->equals(*stack)) {
- // One region spanning the entire reserved region, with the same stack trace.
- // Don't print this regions because the "reserved and committed" line above
- // already indicates that the region is committed.
- assert(itr.next() == nullptr, "Unexpectedly more than one regions");
+ bool reserved_and_committed = false;
+ VirtualMemoryTracker::Instance::tree()->visit_committed_regions(*reserved_rgn,
+ [&](CommittedMemoryRegion& committed_rgn) {
+ if (committed_rgn.equals(*reserved_rgn)) {
+ // One region spanning the entire reserved region, with the same stack trace.
+ // Don't print this regions because the "reserved and committed" line above
+ // already indicates that the region is committed.
+ reserved_and_committed = true;
+ return false;
+ }
+ return true;
+ });
+
+ if (reserved_and_committed) {
return;
}
}
- CommittedRegionIterator itr = reserved_rgn->iterate_committed_regions();
- const CommittedMemoryRegion* committed_rgn;
- while ((committed_rgn = itr.next()) != nullptr) {
+ auto print_committed_rgn = [&](const CommittedMemoryRegion& crgn) {
// Don't report if size is too small
- if (amount_in_current_scale(committed_rgn->size()) == 0) continue;
- stack = committed_rgn->call_stack();
+ if (amount_in_current_scale(crgn.size()) == 0) return;
+ stack = crgn.call_stack();
out->cr();
INDENT_BY(8,
- print_virtual_memory_region("committed", committed_rgn->base(), committed_rgn->size());
+ print_virtual_memory_region("committed", crgn.base(), crgn.size());
if (stack->is_empty()) {
out->cr();
} else {
out->print_cr(" from");
- INDENT_BY(4, stack->print_on(out);)
+ INDENT_BY(4, _stackprinter.print_stack(stack);)
}
)
- }
+ };
+
+ VirtualMemoryTracker::Instance::tree()->visit_committed_regions(*reserved_rgn,
+ [&](CommittedMemoryRegion& crgn) {
+ print_committed_rgn(crgn);
+ return true;
+ });
}
void MemDetailReporter::report_memory_file_allocations() {
diff --git a/src/hotspot/share/nmt/memTag.hpp b/src/hotspot/share/nmt/memTag.hpp
index 9255645638d..b1b59c9151a 100644
--- a/src/hotspot/share/nmt/memTag.hpp
+++ b/src/hotspot/share/nmt/memTag.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -58,6 +58,7 @@
f(mtMetaspace, "Metaspace") \
f(mtStringDedup, "String Deduplication") \
f(mtObjectMonitor, "Object Monitors") \
+ f(mtJNI, "JNI") \
f(mtNone, "Unknown") \
//end
diff --git a/src/hotspot/share/nmt/memTracker.cpp b/src/hotspot/share/nmt/memTracker.cpp
index b3d94ceabec..ed5d34bc12a 100644
--- a/src/hotspot/share/nmt/memTracker.cpp
+++ b/src/hotspot/share/nmt/memTracker.cpp
@@ -41,7 +41,7 @@
#include "runtime/vmThread.hpp"
#include "utilities/debug.hpp"
#include "utilities/defaultStream.hpp"
-#include "utilities/deferred.hpp"
+#include "utilities/deferredStatic.hpp"
#include "utilities/vmError.hpp"
#ifdef _WINDOWS
@@ -50,7 +50,7 @@
NMT_TrackingLevel MemTracker::_tracking_level = NMT_unknown;
-Deferred MemTracker::_baseline;
+DeferredStatic MemTracker::_baseline;
bool MemTracker::NmtVirtualMemoryLocker::_safe_to_use;
@@ -71,7 +71,7 @@ void MemTracker::initialize() {
_baseline.initialize();
if (!MallocTracker::initialize(level) ||
!MemoryFileTracker::Instance::initialize(level) ||
- !VirtualMemoryTracker::initialize(level)) {
+ !VirtualMemoryTracker::Instance::initialize(level)) {
assert(false, "NMT initialization failed");
level = NMT_off;
log_warning(nmt)("NMT initialization failed. NMT disabled.");
@@ -126,7 +126,7 @@ void MemTracker::final_report(outputStream* output) {
bool MemTracker::print_containing_region(const void* p, outputStream* out) {
return enabled() &&
(MallocTracker::print_pointer_information(p, out) ||
- VirtualMemoryTracker::print_containing_region(p, out));
+ VirtualMemoryTracker::Instance::print_containing_region(p, out));
}
void MemTracker::report(bool summary_only, outputStream* output, size_t scale) {
diff --git a/src/hotspot/share/nmt/memTracker.hpp b/src/hotspot/share/nmt/memTracker.hpp
index c208a02054f..124c6163d76 100644
--- a/src/hotspot/share/nmt/memTracker.hpp
+++ b/src/hotspot/share/nmt/memTracker.hpp
@@ -34,8 +34,8 @@
#include "nmt/virtualMemoryTracker.hpp"
#include "runtime/mutexLocker.hpp"
#include "utilities/debug.hpp"
+#include "utilities/deferredStatic.hpp"
#include "utilities/nativeCallStack.hpp"
-#include "utilities/deferred.hpp"
#define CURRENT_PC ((MemTracker::tracking_level() == NMT_detail) ? \
NativeCallStack(0) : FAKE_CALLSTACK)
@@ -132,7 +132,7 @@ class MemTracker : AllStatic {
if (!enabled()) return;
if (addr != nullptr) {
NmtVirtualMemoryLocker nvml;
- VirtualMemoryTracker::add_reserved_region((address)addr, size, stack, mem_tag);
+ VirtualMemoryTracker::Instance::add_reserved_region((address)addr, size, stack, mem_tag);
}
}
@@ -140,7 +140,7 @@ class MemTracker : AllStatic {
assert_post_init();
if (!enabled()) return;
if (addr != nullptr) {
- VirtualMemoryTracker::remove_released_region((address)addr, size);
+ VirtualMemoryTracker::Instance::remove_released_region((address)addr, size);
}
}
@@ -148,7 +148,7 @@ class MemTracker : AllStatic {
assert_post_init();
if (!enabled()) return;
if (addr != nullptr) {
- VirtualMemoryTracker::remove_uncommitted_region((address)addr, size);
+ VirtualMemoryTracker::Instance::remove_uncommitted_region((address)addr, size);
}
}
@@ -158,8 +158,8 @@ class MemTracker : AllStatic {
if (!enabled()) return;
if (addr != nullptr) {
NmtVirtualMemoryLocker nvml;
- VirtualMemoryTracker::add_reserved_region((address)addr, size, stack, mem_tag);
- VirtualMemoryTracker::add_committed_region((address)addr, size, stack);
+ VirtualMemoryTracker::Instance::add_reserved_region((address)addr, size, stack, mem_tag);
+ VirtualMemoryTracker::Instance::add_committed_region((address)addr, size, stack);
}
}
@@ -169,7 +169,7 @@ class MemTracker : AllStatic {
if (!enabled()) return;
if (addr != nullptr) {
NmtVirtualMemoryLocker nvml;
- VirtualMemoryTracker::add_committed_region((address)addr, size, stack);
+ VirtualMemoryTracker::Instance::add_committed_region((address)addr, size, stack);
}
}
@@ -217,7 +217,7 @@ class MemTracker : AllStatic {
if (!enabled()) return;
if (addr != nullptr) {
NmtVirtualMemoryLocker nvml;
- VirtualMemoryTracker::split_reserved_region((address)addr, size, split, mem_tag, split_tag);
+ VirtualMemoryTracker::Instance::split_reserved_region((address)addr, size, split, mem_tag, split_tag);
}
}
@@ -230,7 +230,7 @@ class MemTracker : AllStatic {
if (!enabled()) return;
if (addr != nullptr) {
NmtVirtualMemoryLocker nvml;
- VirtualMemoryTracker::set_reserved_region_type((address)addr, size, mem_tag);
+ VirtualMemoryTracker::Instance::set_reserved_region_tag((address)addr, size, mem_tag);
}
}
@@ -319,7 +319,7 @@ class MemTracker : AllStatic {
// Tracking level
static NMT_TrackingLevel _tracking_level;
// Stored baseline
- static Deferred _baseline;
+ static DeferredStatic _baseline;
};
#endif // SHARE_NMT_MEMTRACKER_HPP
diff --git a/src/hotspot/share/nmt/memoryFileTracker.cpp b/src/hotspot/share/nmt/memoryFileTracker.cpp
index d753a57ede7..677cd174650 100644
--- a/src/hotspot/share/nmt/memoryFileTracker.cpp
+++ b/src/hotspot/share/nmt/memoryFileTracker.cpp
@@ -32,7 +32,7 @@
#include "utilities/nativeCallStack.hpp"
#include "utilities/ostream.hpp"
-Deferred MemoryFileTracker::Instance::_tracker;
+DeferredStatic MemoryFileTracker::Instance::_tracker;
MemoryFileTracker::MemoryFileTracker(bool is_detailed_mode)
: _stack_storage(is_detailed_mode), _files() {}
@@ -73,7 +73,7 @@ void MemoryFileTracker::print_report_on(const MemoryFile* file, outputStream* st
if (prev == nullptr) {
// Must be first node.
prev = current;
- return;
+ return true;
}
#ifdef ASSERT
if (broken_start != nullptr && prev->val().out.mem_tag() != current->val().in.mem_tag()) {
@@ -91,11 +91,12 @@ void MemoryFileTracker::print_report_on(const MemoryFile* file, outputStream* st
NMTUtil::tag_to_name(prev->val().out.mem_tag()));
{
StreamIndentor si(stream, 4);
- _stack_storage.get(prev->val().out.stack()).print_on(stream);
+ _stack_storage.get(prev->val().out.reserved_stack()).print_on(stream);
}
stream->cr();
}
prev = current;
+ return true;
});
#ifdef ASSERT
if (broken_start != nullptr) {
diff --git a/src/hotspot/share/nmt/memoryFileTracker.hpp b/src/hotspot/share/nmt/memoryFileTracker.hpp
index cd7a3fb8593..1b23dacab81 100644
--- a/src/hotspot/share/nmt/memoryFileTracker.hpp
+++ b/src/hotspot/share/nmt/memoryFileTracker.hpp
@@ -28,13 +28,13 @@
#include "memory/allocation.hpp"
#include "nmt/nmtCommon.hpp"
#include "nmt/nmtNativeCallStackStorage.hpp"
-#include "nmt/virtualMemoryTracker.hpp"
#include "nmt/vmatree.hpp"
+#include "nmt/virtualMemoryTracker.hpp"
#include "runtime/os.inline.hpp"
+#include "utilities/deferredStatic.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/nativeCallStack.hpp"
#include "utilities/ostream.hpp"
-#include "utilities/deferred.hpp"
// The MemoryFileTracker tracks memory of 'memory files',
// storage with its own memory space separate from the process.
@@ -92,7 +92,7 @@ class MemoryFileTracker {
const GrowableArrayCHeap& files();
class Instance : public AllStatic {
- static Deferred _tracker;
+ static DeferredStatic _tracker;
public:
diff --git a/src/hotspot/share/nmt/nmtNativeCallStackStorage.hpp b/src/hotspot/share/nmt/nmtNativeCallStackStorage.hpp
index 258c3284a18..85e044c1a45 100644
--- a/src/hotspot/share/nmt/nmtNativeCallStackStorage.hpp
+++ b/src/hotspot/share/nmt/nmtNativeCallStackStorage.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -43,11 +43,8 @@
class NativeCallStackStorage : public CHeapObjBase {
public:
using StackIndex = int;
-
-private:
constexpr static const StackIndex invalid = std::numeric_limits::max() - 1;
-public:
static bool equals(const StackIndex a, const StackIndex b) {
return a == b;
}
diff --git a/src/hotspot/share/nmt/nmtTreap.hpp b/src/hotspot/share/nmt/nmtTreap.hpp
index 7e4ad3df95b..13e759cbd3a 100644
--- a/src/hotspot/share/nmt/nmtTreap.hpp
+++ b/src/hotspot/share/nmt/nmtTreap.hpp
@@ -55,6 +55,7 @@ template
class Treap {
friend class NMTVMATreeTest;
friend class NMTTreapTest;
+ friend class VMTWithVMATreeTest;
public:
class TreapNode {
friend Treap;
@@ -212,12 +213,13 @@ class Treap {
seen_count++;
if (last_seen == nullptr) {
last_seen = node;
- return;
+ return true;
}
if (COMPARATOR::cmp(last_seen->key(), node->key()) > 0) {
failed = false;
}
last_seen = node;
+ return true;
});
assert(seen_count == _node_count, "the number of visited nodes do not match with the number of stored nodes");
assert(!failed, "keys was not monotonically strongly increasing when visiting in order");
@@ -382,7 +384,9 @@ class Treap {
head = head->left();
}
head = to_visit.pop();
- f(head);
+ if (!f(head)) {
+ return;
+ }
head = head->right();
}
}
@@ -409,7 +413,9 @@ class Treap {
const int cmp_from = COMPARATOR::cmp(head->key(), from);
const int cmp_to = COMPARATOR::cmp(head->key(), to);
if (cmp_from >= 0 && cmp_to < 0) {
- f(head);
+ if (!f(head)) {
+ return;
+ }
}
if (cmp_to < 0) {
head = head->right();
diff --git a/src/hotspot/share/nmt/nmtUsage.cpp b/src/hotspot/share/nmt/nmtUsage.cpp
index f8ca1b0e1fa..0d2aa40ea07 100644
--- a/src/hotspot/share/nmt/nmtUsage.cpp
+++ b/src/hotspot/share/nmt/nmtUsage.cpp
@@ -29,7 +29,6 @@
#include "nmt/nmtCommon.hpp"
#include "nmt/nmtUsage.hpp"
#include "nmt/threadStackTracker.hpp"
-#include "nmt/virtualMemoryTracker.hpp"
// Enabled all options for snapshot.
const NMTUsageOptions NMTUsage::OptionsAll = { true, true, true };
@@ -48,7 +47,7 @@ void NMTUsage::walk_thread_stacks() {
// much memory had been committed if they are backed by virtual memory. This
// needs to happen before we take the snapshot of the virtual memory since it
// will update this information.
- VirtualMemoryTracker::snapshot_thread_stacks();
+ VirtualMemoryTracker::Instance::snapshot_thread_stacks();
}
void NMTUsage::update_malloc_usage() {
diff --git a/src/hotspot/share/nmt/regionsTree.cpp b/src/hotspot/share/nmt/regionsTree.cpp
new file mode 100644
index 00000000000..09686f52067
--- /dev/null
+++ b/src/hotspot/share/nmt/regionsTree.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "nmt/regionsTree.hpp"
+
+VMATree::SummaryDiff RegionsTree::commit_region(address addr, size_t size, const NativeCallStack& stack) {
+ return commit_mapping((VMATree::position)addr, size, make_region_data(stack, mtNone), /*use tag inplace*/ true);
+}
+
+VMATree::SummaryDiff RegionsTree::uncommit_region(address addr, size_t size) {
+ return uncommit_mapping((VMATree::position)addr, size, make_region_data(NativeCallStack::empty_stack(), mtNone));
+}
+
+#ifdef ASSERT
+void RegionsTree::NodeHelper::print_on(outputStream* st) {
+ auto st_str = [&](VMATree::StateType s){
+ return s == VMATree::StateType::Released ? "Rl" :
+ s == VMATree::StateType::Reserved ? "Rv" : "Cm";
+ };
+ st->print_cr("pos: " INTPTR_FORMAT " "
+ "%s, %s <|> %s, %s",
+ p2i((address)position()),
+ st_str(in_state()),
+ NMTUtil::tag_to_name(in_tag()),
+ st_str(out_state()),
+ NMTUtil::tag_to_name(out_tag())
+ );
+}
+
+void RegionsTree::print_on(outputStream* st) {
+ visit_in_order([&](Node* node) {
+ NodeHelper curr(node);
+ curr.print_on(st);
+ return true;
+ });
+}
+#endif
\ No newline at end of file
diff --git a/src/hotspot/share/nmt/regionsTree.hpp b/src/hotspot/share/nmt/regionsTree.hpp
new file mode 100644
index 00000000000..7fdb4e6fd39
--- /dev/null
+++ b/src/hotspot/share/nmt/regionsTree.hpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef NMT_REGIONSTREE_HPP
+#define NMT_REGIONSTREE_HPP
+
+#include "logging/log.hpp"
+#include "nmt/nmtCommon.hpp"
+#include "nmt/vmatree.hpp"
+
+
+class ReservedMemoryRegion;
+class CommittedMemoryRegion;
+// RegionsTree extends VMATree to add some more specific API and also defines a helper
+// for processing the tree nodes in a shorter and more meaningful way.
+class RegionsTree : public VMATree {
+ NativeCallStackStorage _ncs_storage;
+ bool _with_storage;
+
+ public:
+ RegionsTree(bool with_storage) : VMATree() , _ncs_storage(with_storage), _with_storage(with_storage) { }
+
+ ReservedMemoryRegion find_reserved_region(address addr);
+
+ SummaryDiff commit_region(address addr, size_t size, const NativeCallStack& stack);
+ SummaryDiff uncommit_region(address addr, size_t size);
+
+ using Node = VMATree::TreapNode;
+
+ class NodeHelper {
+ Node* _node;
+ public:
+ NodeHelper() : _node(nullptr) { }
+ NodeHelper(Node* node) : _node(node) { }
+ inline bool is_valid() const { return _node != nullptr; }
+ inline void clear_node() { _node = nullptr; }
+ inline VMATree::position position() const { return _node->key(); }
+ inline bool is_committed_begin() const { return ((uint8_t)out_state() & (uint8_t)VMATree::StateType::Committed) >= 2; }
+ inline bool is_released_begin() const { return out_state() == VMATree::StateType::Released; }
+ inline bool is_reserved_begin() const { return ((uint8_t)out_state() & (uint8_t)VMATree::StateType::Reserved) == 1; }
+ inline VMATree::StateType in_state() const { return _node->val().in.type(); }
+ inline VMATree::StateType out_state() const { return _node->val().out.type(); }
+ inline size_t distance_from(const NodeHelper& other) const {
+ assert (position() > other.position(), "negative distance");
+ return position() - other.position();
+ }
+ inline NativeCallStackStorage::StackIndex out_stack_index() const { return _node->val().out.reserved_stack(); }
+ inline MemTag in_tag() const { return _node->val().in.mem_tag(); }
+ inline MemTag out_tag() const { return _node->val().out.mem_tag(); }
+ inline void set_in_tag(MemTag tag) { _node->val().in.set_tag(tag); }
+ inline void set_out_tag(MemTag tag) { _node->val().out.set_tag(tag); }
+ DEBUG_ONLY(void print_on(outputStream* st);)
+ };
+
+ DEBUG_ONLY(void print_on(outputStream* st);)
+
+ template
+ void visit_committed_regions(const ReservedMemoryRegion& rgn, F func);
+
+ template
+ void visit_reserved_regions(F func);
+
+ inline RegionData make_region_data(const NativeCallStack& ncs, MemTag tag) {
+ return RegionData(_ncs_storage.push(ncs), tag);
+ }
+
+ inline const NativeCallStack stack(NodeHelper& node) {
+ if (!_with_storage) {
+ return NativeCallStack::empty_stack();
+ }
+ NativeCallStackStorage::StackIndex si = node.out_stack_index();
+ return _ncs_storage.get(si);
+ }
+};
+
+#endif // NMT_REGIONSTREE_HPP
\ No newline at end of file
diff --git a/src/hotspot/share/nmt/regionsTree.inline.hpp b/src/hotspot/share/nmt/regionsTree.inline.hpp
new file mode 100644
index 00000000000..f1b7319f5ca
--- /dev/null
+++ b/src/hotspot/share/nmt/regionsTree.inline.hpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_NMT_REGIONSTREE_INLINE_HPP
+#define SHARE_NMT_REGIONSTREE_INLINE_HPP
+
+#include "nmt/regionsTree.hpp"
+#include "nmt/virtualMemoryTracker.hpp"
+
+template
+void RegionsTree::visit_committed_regions(const ReservedMemoryRegion& rgn, F func) {
+ position start = (position)rgn.base();
+ size_t end = reinterpret_cast(rgn.end()) + 1;
+ size_t comm_size = 0;
+
+ NodeHelper prev;
+ visit_range_in_order(start, end, [&](Node* node) {
+ NodeHelper curr(node);
+ if (prev.is_valid() && prev.is_committed_begin()) {
+ CommittedMemoryRegion cmr((address)prev.position(), curr.distance_from(prev), stack(prev));
+ if (!func(cmr)) {
+ return false;
+ }
+ }
+ prev = curr;
+ return true;
+ });
+}
+
+template
+void RegionsTree::visit_reserved_regions(F func) {
+ NodeHelper begin_node, prev;
+ size_t rgn_size = 0;
+
+ visit_in_order([&](Node* node) {
+ NodeHelper curr(node);
+ if (prev.is_valid()) {
+ rgn_size += curr.distance_from(prev);
+ } else {
+ begin_node = curr;
+ rgn_size = 0;
+ }
+ prev = curr;
+ if (curr.is_released_begin() || begin_node.out_tag() != curr.out_tag()) {
+ auto st = stack(begin_node);
+ if (rgn_size == 0) {
+ prev.clear_node();
+ return true;
+ }
+ ReservedMemoryRegion rmr((address)begin_node.position(), rgn_size, st, begin_node.out_tag());
+ if (!func(rmr)) {
+ return false;
+ }
+ rgn_size = 0;
+ if (!curr.is_released_begin()) {
+ begin_node = curr;
+ } else {
+ begin_node.clear_node();
+ prev.clear_node();
+ }
+ }
+
+ return true;
+ });
+}
+
+#endif //SHARE_NMT_REGIONSTREE_INLINE_HPP
diff --git a/src/hotspot/share/nmt/threadStackTracker.cpp b/src/hotspot/share/nmt/threadStackTracker.cpp
index dabb23f0801..3e649d882c4 100644
--- a/src/hotspot/share/nmt/threadStackTracker.cpp
+++ b/src/hotspot/share/nmt/threadStackTracker.cpp
@@ -26,7 +26,6 @@
#include "nmt/memTracker.hpp"
#include "nmt/threadStackTracker.hpp"
-#include "nmt/virtualMemoryTracker.hpp"
#include "runtime/os.hpp"
#include "utilities/align.hpp"
#include "utilities/debug.hpp"
@@ -52,7 +51,7 @@ void ThreadStackTracker::new_thread_stack(void* base, size_t size, const NativeC
align_thread_stack_boundaries_inward(base, size);
MemTracker::NmtVirtualMemoryLocker nvml;
- VirtualMemoryTracker::add_reserved_region((address)base, size, stack, mtThreadStack);
+ VirtualMemoryTracker::Instance::add_reserved_region((address)base, size, stack, mtThreadStack);
_thread_count++;
}
@@ -62,7 +61,7 @@ void ThreadStackTracker::delete_thread_stack(void* base, size_t size) {
align_thread_stack_boundaries_inward(base, size);
MemTracker::NmtVirtualMemoryLocker nvml;
- VirtualMemoryTracker::remove_released_region((address)base, size);
+ MemTracker::record_virtual_memory_release((address)base, size);
_thread_count--;
}
diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp
index 04a72079460..51d8696a9ce 100644
--- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp
+++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp
@@ -21,16 +21,16 @@
* questions.
*
*/
+
#include "logging/log.hpp"
-#include "memory/metaspaceStats.hpp"
-#include "memory/metaspaceUtils.hpp"
#include "nmt/memTracker.hpp"
-#include "nmt/nativeCallStackPrinter.hpp"
-#include "nmt/threadStackTracker.hpp"
#include "nmt/virtualMemoryTracker.hpp"
+#include "nmt/regionsTree.hpp"
+#include "nmt/regionsTree.inline.hpp"
#include "runtime/os.hpp"
#include "utilities/ostream.hpp"
+VirtualMemoryTracker* VirtualMemoryTracker::Instance::_tracker = nullptr;
VirtualMemorySnapshot VirtualMemorySummary::_snapshot;
void VirtualMemory::update_peak(size_t size) {
@@ -47,553 +47,206 @@ void VirtualMemory::update_peak(size_t size) {
void VirtualMemorySummary::snapshot(VirtualMemorySnapshot* s) {
// Snapshot current thread stacks
- VirtualMemoryTracker::snapshot_thread_stacks();
+ VirtualMemoryTracker::Instance::snapshot_thread_stacks();
as_snapshot()->copy_to(s);
}
-SortedLinkedList* VirtualMemoryTracker::_reserved_regions;
-
-int compare_committed_region(const CommittedMemoryRegion& r1, const CommittedMemoryRegion& r2) {
- return r1.compare(r2);
-}
-
-int compare_reserved_region_base(const ReservedMemoryRegion& r1, const ReservedMemoryRegion& r2) {
- return r1.compare(r2);
-}
-
-static bool is_mergeable_with(CommittedMemoryRegion* rgn, address addr, size_t size, const NativeCallStack& stack) {
- return rgn->adjacent_to(addr, size) && rgn->call_stack()->equals(stack);
-}
-
-static bool is_same_as(CommittedMemoryRegion* rgn, address addr, size_t size, const NativeCallStack& stack) {
- // It would have made sense to use rgn->equals(...), but equals returns true for overlapping regions.
- return rgn->same_region(addr, size) && rgn->call_stack()->equals(stack);
-}
-
-static LinkedListNode* find_preceding_node_from(LinkedListNode* from, address addr) {
- LinkedListNode* preceding = nullptr;
-
- for (LinkedListNode* node = from; node != nullptr; node = node->next()) {
- CommittedMemoryRegion* rgn = node->data();
-
- // We searched past the region start.
- if (rgn->end() > addr) {
- break;
- }
-
- preceding = node;
+bool VirtualMemoryTracker::Instance::initialize(NMT_TrackingLevel level) {
+ assert(_tracker == nullptr, "only call once");
+ if (level >= NMT_summary) {
+ void* tracker = os::malloc(sizeof(VirtualMemoryTracker), mtNMT);
+ if (tracker == nullptr) return false;
+ _tracker = new (tracker) VirtualMemoryTracker(level == NMT_detail);
}
-
- return preceding;
+ return true;
}
-static bool try_merge_with(LinkedListNode* node, address addr, size_t size, const NativeCallStack& stack) {
- if (node != nullptr) {
- CommittedMemoryRegion* rgn = node->data();
- if (is_mergeable_with(rgn, addr, size, stack)) {
- rgn->expand_region(addr, size);
- return true;
- }
- }
-
- return false;
+void VirtualMemoryTracker::Instance::add_reserved_region(address base_addr, size_t size,
+ const NativeCallStack& stack, MemTag mem_tag) {
+ assert(_tracker != nullptr, "Sanity check");
+ _tracker->add_reserved_region(base_addr, size, stack, mem_tag);
}
-static bool try_merge_with(LinkedListNode* node, LinkedListNode* other) {
- if (other == nullptr) {
- return false;
- }
-
- CommittedMemoryRegion* rgn = other->data();
- return try_merge_with(node, rgn->base(), rgn->size(), *rgn->call_stack());
+void VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size,
+ const NativeCallStack& stack, MemTag mem_tag) {
+ VMATree::SummaryDiff diff = tree()->reserve_mapping((size_t)base_addr, size, tree()->make_region_data(stack, mem_tag));
+ apply_summary_diff(diff);
}
-bool ReservedMemoryRegion::add_committed_region(address addr, size_t size, const NativeCallStack& stack) {
- assert(addr != nullptr, "Invalid address");
- assert(size > 0, "Invalid size");
- assert(contain_region(addr, size), "Not contain this region");
-
- // Find the region that fully precedes the [addr, addr + size) region.
- LinkedListNode* prev = find_preceding_node_from(_committed_regions.head(), addr);
- LinkedListNode* next = (prev != nullptr ? prev->next() : _committed_regions.head());
-
- if (next != nullptr) {
- // Ignore request if region already exists.
- if (is_same_as(next->data(), addr, size, stack)) {
- return true;
- }
-
- // The new region is after prev, and either overlaps with the
- // next region (and maybe more regions), or overlaps with no region.
- if (next->data()->overlap_region(addr, size)) {
- // Remove _all_ overlapping regions, and parts of regions,
- // in preparation for the addition of this new region.
- remove_uncommitted_region(addr, size);
-
- // The remove could have split a region into two and created a
- // new prev region. Need to reset the prev and next pointers.
- prev = find_preceding_node_from((prev != nullptr ? prev : _committed_regions.head()), addr);
- next = (prev != nullptr ? prev->next() : _committed_regions.head());
- }
- }
-
- // At this point the previous overlapping regions have been
- // cleared, and the full region is guaranteed to be inserted.
- VirtualMemorySummary::record_committed_memory(size, mem_tag());
-
- // Try to merge with prev and possibly next.
- if (try_merge_with(prev, addr, size, stack)) {
- if (try_merge_with(prev, next)) {
- // prev was expanded to contain the new region
- // and next, need to remove next from the list
- _committed_regions.remove_after(prev);
- }
-
- return true;
- }
-
- // Didn't merge with prev, try with next.
- if (try_merge_with(next, addr, size, stack)) {
- return true;
- }
-
- // Couldn't merge with any regions - create a new region.
- return add_committed_region(CommittedMemoryRegion(addr, size, stack));
+void VirtualMemoryTracker::Instance::set_reserved_region_tag(address addr, size_t size, MemTag mem_tag) {
+ assert(_tracker != nullptr, "Sanity check");
+ _tracker->set_reserved_region_tag(addr, size, mem_tag);
}
-bool ReservedMemoryRegion::remove_uncommitted_region(LinkedListNode* node,
- address addr, size_t size) {
- assert(addr != nullptr, "Invalid address");
- assert(size > 0, "Invalid size");
-
- CommittedMemoryRegion* rgn = node->data();
- assert(rgn->contain_region(addr, size), "Has to be contained");
- assert(!rgn->same_region(addr, size), "Can not be the same region");
-
- if (rgn->base() == addr ||
- rgn->end() == addr + size) {
- rgn->exclude_region(addr, size);
- return true;
- } else {
- // split this region
- address top =rgn->end();
- // use this region for lower part
- size_t exclude_size = rgn->end() - addr;
- rgn->exclude_region(addr, exclude_size);
-
- // higher part
- address high_base = addr + size;
- size_t high_size = top - high_base;
-
- CommittedMemoryRegion high_rgn(high_base, high_size, *rgn->call_stack());
- LinkedListNode* high_node = _committed_regions.add(high_rgn);
- assert(high_node == nullptr || node->next() == high_node, "Should be right after");
- return (high_node != nullptr);
- }
-
- return false;
+void VirtualMemoryTracker::set_reserved_region_tag(address addr, size_t size, MemTag mem_tag) {
+ VMATree::SummaryDiff diff = tree()->set_tag((VMATree::position) addr, size, mem_tag);
+ apply_summary_diff(diff);
}
-bool ReservedMemoryRegion::remove_uncommitted_region(address addr, size_t sz) {
- assert(addr != nullptr, "Invalid address");
- assert(sz > 0, "Invalid size");
-
- CommittedMemoryRegion del_rgn(addr, sz, *call_stack());
- address end = addr + sz;
-
- LinkedListNode* head = _committed_regions.head();
- LinkedListNode* prev = nullptr;
- CommittedMemoryRegion* crgn;
-
- while (head != nullptr) {
- crgn = head->data();
-
- if (crgn->same_region(addr, sz)) {
- VirtualMemorySummary::record_uncommitted_memory(crgn->size(), mem_tag());
- _committed_regions.remove_after(prev);
- return true;
- }
-
- // del_rgn contains crgn
- if (del_rgn.contain_region(crgn->base(), crgn->size())) {
- VirtualMemorySummary::record_uncommitted_memory(crgn->size(), mem_tag());
- head = head->next();
- _committed_regions.remove_after(prev);
- continue; // don't update head or prev
- }
-
- // Found addr in the current crgn. There are 2 subcases:
- if (crgn->contain_address(addr)) {
+void VirtualMemoryTracker::Instance::apply_summary_diff(VMATree::SummaryDiff diff) {
+ assert(_tracker != nullptr, "Sanity check");
+ _tracker->apply_summary_diff(diff);
+}
- // (1) Found addr+size in current crgn as well. (del_rgn is contained in crgn)
- if (crgn->contain_address(end - 1)) {
- VirtualMemorySummary::record_uncommitted_memory(sz, mem_tag());
- return remove_uncommitted_region(head, addr, sz); // done!
+void VirtualMemoryTracker::apply_summary_diff(VMATree::SummaryDiff diff) {
+ VMATree::SingleDiff::delta reserve_delta, commit_delta;
+ size_t reserved, committed;
+ MemTag tag = mtNone;
+ auto print_err = [&](const char* str) {
+#ifdef ASSERT
+ log_error(nmt)("summary mismatch, at %s, for %s,"
+ " diff-reserved: %ld"
+ " diff-committed: %ld"
+ " vms-reserved: %zu"
+ " vms-committed: %zu",
+ str, NMTUtil::tag_to_name(tag), (long)reserve_delta, (long)commit_delta, reserved, committed);
+#endif
+ };
+
+ for (int i = 0; i < mt_number_of_tags; i++) {
+ reserve_delta = diff.tag[i].reserve;
+ commit_delta = diff.tag[i].commit;
+ tag = NMTUtil::index_to_tag(i);
+ reserved = VirtualMemorySummary::as_snapshot()->by_tag(tag)->reserved();
+ committed = VirtualMemorySummary::as_snapshot()->by_tag(tag)->committed();
+ if (reserve_delta != 0) {
+ if (reserve_delta > 0) {
+ VirtualMemorySummary::record_reserved_memory(reserve_delta, tag);
} else {
- // (2) Did not find del_rgn's end in crgn.
- size_t size = crgn->end() - del_rgn.base();
- crgn->exclude_region(addr, size);
- VirtualMemorySummary::record_uncommitted_memory(size, mem_tag());
+ if ((size_t)-reserve_delta <= reserved) {
+ VirtualMemorySummary::record_released_memory(-reserve_delta, tag);
+ } else {
+ print_err("release");
+ }
}
-
- } else if (crgn->contain_address(end - 1)) {
- // Found del_rgn's end, but not its base addr.
- size_t size = del_rgn.end() - crgn->base();
- crgn->exclude_region(crgn->base(), size);
- VirtualMemorySummary::record_uncommitted_memory(size, mem_tag());
- return true; // should be done if the list is sorted properly!
- }
-
- prev = head;
- head = head->next();
- }
-
- return true;
-}
-
-void ReservedMemoryRegion::move_committed_regions(address addr, ReservedMemoryRegion& rgn) {
- assert(addr != nullptr, "Invalid address");
-
- // split committed regions
- LinkedListNode* head =
- _committed_regions.head();
- LinkedListNode* prev = nullptr;
-
- while (head != nullptr) {
- if (head->data()->base() >= addr) {
- break;
}
- prev = head;
- head = head->next();
- }
-
- if (head != nullptr) {
- if (prev != nullptr) {
- prev->set_next(head->next());
- } else {
- _committed_regions.set_head(nullptr);
+ if (commit_delta != 0) {
+ if (commit_delta > 0) {
+ if ((size_t)commit_delta <= ((size_t)reserve_delta + reserved)) {
+ VirtualMemorySummary::record_committed_memory(commit_delta, tag);
+ }
+ else {
+ print_err("commit");
+ }
+ }
+ else {
+ if ((size_t)-commit_delta <= committed) {
+ VirtualMemorySummary::record_uncommitted_memory(-commit_delta, tag);
+ } else {
+ print_err("uncommit");
+ }
+ }
}
}
-
- rgn._committed_regions.set_head(head);
}
-size_t ReservedMemoryRegion::committed_size() const {
- size_t committed = 0;
- LinkedListNode* head =
- _committed_regions.head();
- while (head != nullptr) {
- committed += head->data()->size();
- head = head->next();
- }
- return committed;
-}
-
-void ReservedMemoryRegion::set_mem_tag(MemTag new_mem_tag) {
- assert((mem_tag() == mtNone || mem_tag() == new_mem_tag),
- "Overwrite memory tag for region [" INTPTR_FORMAT "-" INTPTR_FORMAT "), %u->%u.",
- p2i(base()), p2i(end()), (unsigned)mem_tag(), (unsigned)new_mem_tag);
- if (mem_tag() != new_mem_tag) {
- VirtualMemorySummary::move_reserved_memory(mem_tag(), new_mem_tag, size());
- VirtualMemorySummary::move_committed_memory(mem_tag(), new_mem_tag, committed_size());
- _mem_tag = new_mem_tag;
- }
+void VirtualMemoryTracker::Instance::add_committed_region(address addr, size_t size,
+ const NativeCallStack& stack) {
+ assert(_tracker != nullptr, "Sanity check");
+ _tracker->add_committed_region(addr, size, stack);
}
-address ReservedMemoryRegion::thread_stack_uncommitted_bottom() const {
- assert(mem_tag() == mtThreadStack, "Only for thread stack");
- LinkedListNode* head = _committed_regions.head();
- address bottom = base();
- address top = base() + size();
- while (head != nullptr) {
- address committed_top = head->data()->base() + head->data()->size();
- if (committed_top < top) {
- // committed stack guard pages, skip them
- bottom = head->data()->base() + head->data()->size();
- head = head->next();
- } else {
- assert(top == committed_top, "Sanity");
- break;
- }
- }
-
- return bottom;
+void VirtualMemoryTracker::add_committed_region(address addr, size_t size,
+ const NativeCallStack& stack) {
+ VMATree::SummaryDiff diff = tree()->commit_region(addr, size, stack);
+ apply_summary_diff(diff);
}
-bool VirtualMemoryTracker::initialize(NMT_TrackingLevel level) {
- assert(_reserved_regions == nullptr, "only call once");
- if (level >= NMT_summary) {
- _reserved_regions = new (std::nothrow, mtNMT)
- SortedLinkedList();
- return (_reserved_regions != nullptr);
- }
- return true;
+void VirtualMemoryTracker::Instance::remove_uncommitted_region(address addr, size_t size) {
+ assert(_tracker != nullptr, "Sanity check");
+ _tracker->remove_uncommitted_region(addr, size);
}
-bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size,
- const NativeCallStack& stack, MemTag mem_tag) {
- assert(base_addr != nullptr, "Invalid address");
- assert(size > 0, "Invalid size");
- assert(_reserved_regions != nullptr, "Sanity check");
+void VirtualMemoryTracker::remove_uncommitted_region(address addr, size_t size) {
MemTracker::assert_locked();
-
- ReservedMemoryRegion rgn(base_addr, size, stack, mem_tag);
- ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn);
-
- log_debug(nmt)("Add reserved region \'%s\' (" INTPTR_FORMAT ", %zu)",
- rgn.mem_tag_name(), p2i(rgn.base()), rgn.size());
- if (reserved_rgn == nullptr) {
- VirtualMemorySummary::record_reserved_memory(size, mem_tag);
- return _reserved_regions->add(rgn) != nullptr;
- } else {
- // Deal with recursive reservation
- // os::reserve_memory() -> pd_reserve_memory() -> os::reserve_memory()
- // See JDK-8198226.
- if (reserved_rgn->same_region(base_addr, size) &&
- (reserved_rgn->mem_tag() == mem_tag || reserved_rgn->mem_tag() == mtNone)) {
- reserved_rgn->set_call_stack(stack);
- reserved_rgn->set_mem_tag(mem_tag);
- return true;
- } else {
- assert(reserved_rgn->overlap_region(base_addr, size), "Must be");
-
- // Overlapped reservation.
- // It can happen when the regions are thread stacks, as JNI
- // thread does not detach from VM before exits, and leads to
- // leak JavaThread object
- if (reserved_rgn->mem_tag() == mtThreadStack) {
- guarantee(!CheckJNICalls, "Attached JNI thread exited without being detached");
- // Overwrite with new region
-
- // Release old region
- VirtualMemorySummary::record_uncommitted_memory(reserved_rgn->committed_size(), reserved_rgn->mem_tag());
- VirtualMemorySummary::record_released_memory(reserved_rgn->size(), reserved_rgn->mem_tag());
-
- // Add new region
- VirtualMemorySummary::record_reserved_memory(rgn.size(), mem_tag);
-
- *reserved_rgn = rgn;
- return true;
- }
-
- // CDS mapping region.
- // CDS reserves the whole region for mapping CDS archive, then maps each section into the region.
- // NMT reports CDS as a whole.
- if (reserved_rgn->mem_tag() == mtClassShared) {
- log_debug(nmt)("CDS reserved region \'%s\' as a whole (" INTPTR_FORMAT ", %zu)",
- reserved_rgn->mem_tag_name(), p2i(reserved_rgn->base()), reserved_rgn->size());
- assert(reserved_rgn->contain_region(base_addr, size), "Reserved CDS region should contain this mapping region");
- return true;
- }
-
- // Mapped CDS string region.
- // The string region(s) is part of the java heap.
- if (reserved_rgn->mem_tag() == mtJavaHeap) {
- log_debug(nmt)("CDS reserved region \'%s\' as a whole (" INTPTR_FORMAT ", %zu)",
- reserved_rgn->mem_tag_name(), p2i(reserved_rgn->base()), reserved_rgn->size());
- assert(reserved_rgn->contain_region(base_addr, size), "Reserved heap region should contain this mapping region");
- return true;
- }
-
- if (reserved_rgn->mem_tag() == mtCode) {
- assert(reserved_rgn->contain_region(base_addr, size), "Reserved code region should contain this mapping region");
- return true;
- }
-
- // Print some more details.
- stringStream ss;
- ss.print_cr("Error: old region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), memory tag %s.\n"
- " new region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), memory tag %s.",
- p2i(reserved_rgn->base()), p2i(reserved_rgn->end()), NMTUtil::tag_to_name(reserved_rgn->mem_tag()),
- p2i(base_addr), p2i(base_addr + size), NMTUtil::tag_to_name(mem_tag));
- if (MemTracker::tracking_level() == NMT_detail) {
- ss.print_cr("Existing region allocated from:");
- reserved_rgn->call_stack()->print_on(&ss);
- ss.print_cr("New region allocated from:");
- stack.print_on(&ss);
- }
- log_debug(nmt)("%s", ss.freeze());
-
- ShouldNotReachHere();
- return false;
- }
- }
+ VMATree::SummaryDiff diff = tree()->uncommit_region(addr, size);
+ apply_summary_diff(diff);
}
-void VirtualMemoryTracker::set_reserved_region_type(address addr, size_t size, MemTag mem_tag) {
- assert(addr != nullptr, "Invalid address");
- assert(_reserved_regions != nullptr, "Sanity check");
- MemTracker::assert_locked();
-
- ReservedMemoryRegion rgn(addr, 1);
- ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn);
- if (reserved_rgn != nullptr) {
- assert(reserved_rgn->contain_address(addr), "Containment");
- if (reserved_rgn->mem_tag() != mem_tag) {
- assert(reserved_rgn->mem_tag() == mtNone, "Overwrite memory tag (should be mtNone, is: \"%s\")",
- NMTUtil::tag_to_name(reserved_rgn->mem_tag()));
- reserved_rgn->set_mem_tag(mem_tag);
- }
- }
+void VirtualMemoryTracker::Instance::remove_released_region(address addr, size_t size) {
+ assert(_tracker != nullptr, "Sanity check");
+ _tracker->remove_released_region(addr, size);
}
-bool VirtualMemoryTracker::add_committed_region(address addr, size_t size,
- const NativeCallStack& stack) {
- assert(addr != nullptr, "Invalid address");
- assert(size > 0, "Invalid size");
- assert(_reserved_regions != nullptr, "Sanity check");
- MemTracker::assert_locked();
-
- ReservedMemoryRegion rgn(addr, size);
- ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn);
-
- if (reserved_rgn == nullptr) {
- log_debug(nmt)("Add committed region \'%s\', No reserved region found for (" INTPTR_FORMAT ", %zu)",
- rgn.mem_tag_name(), p2i(rgn.base()), rgn.size());
- }
- assert(reserved_rgn != nullptr, "Add committed region, No reserved region found");
- assert(reserved_rgn->contain_region(addr, size), "Not completely contained");
- bool result = reserved_rgn->add_committed_region(addr, size, stack);
- log_debug(nmt)("Add committed region \'%s\'(" INTPTR_FORMAT ", %zu) %s",
- reserved_rgn->mem_tag_name(), p2i(rgn.base()), rgn.size(), (result ? "Succeeded" : "Failed"));
- return result;
+void VirtualMemoryTracker::remove_released_region(address addr, size_t size) {
+ VMATree::SummaryDiff diff = tree()->release_mapping((VMATree::position)addr, size);
+ apply_summary_diff(diff);
}
-bool VirtualMemoryTracker::remove_uncommitted_region(address addr, size_t size) {
- assert(addr != nullptr, "Invalid address");
- assert(size > 0, "Invalid size");
- assert(_reserved_regions != nullptr, "Sanity check");
- MemTracker::assert_locked();
-
- ReservedMemoryRegion rgn(addr, size);
- ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn);
- assert(reserved_rgn != nullptr, "No reserved region (" INTPTR_FORMAT ", %zu)", p2i(addr), size);
- assert(reserved_rgn->contain_region(addr, size), "Not completely contained");
- const char* type_name = reserved_rgn->mem_tag_name(); // after remove, info is not complete
- bool result = reserved_rgn->remove_uncommitted_region(addr, size);
- log_debug(nmt)("Removed uncommitted region \'%s\' (" INTPTR_FORMAT ", %zu) %s",
- type_name, p2i(addr), size, (result ? " Succeeded" : "Failed"));
- return result;
+void VirtualMemoryTracker::Instance::split_reserved_region(address addr, size_t size, size_t split, MemTag mem_tag, MemTag split_mem_tag) {
+ assert(_tracker != nullptr, "Sanity check");
+ _tracker->split_reserved_region(addr, size, split, mem_tag, split_mem_tag);
}
-bool VirtualMemoryTracker::remove_released_region(ReservedMemoryRegion* rgn) {
- assert(rgn != nullptr, "Sanity check");
- assert(_reserved_regions != nullptr, "Sanity check");
- MemTracker::assert_locked();
-
- // uncommit regions within the released region
- ReservedMemoryRegion backup(*rgn);
- bool result = rgn->remove_uncommitted_region(rgn->base(), rgn->size());
- log_debug(nmt)("Remove uncommitted region \'%s\' (" INTPTR_FORMAT ", %zu) %s",
- backup.mem_tag_name(), p2i(backup.base()), backup.size(), (result ? "Succeeded" : "Failed"));
- if (!result) {
- return false;
- }
-
- VirtualMemorySummary::record_released_memory(rgn->size(), rgn->mem_tag());
- result = _reserved_regions->remove(*rgn);
- log_debug(nmt)("Removed region \'%s\' (" INTPTR_FORMAT ", %zu) from _reserved_regions %s" ,
- backup.mem_tag_name(), p2i(backup.base()), backup.size(), (result ? "Succeeded" : "Failed"));
- return result;
+void VirtualMemoryTracker::split_reserved_region(address addr, size_t size, size_t split, MemTag mem_tag, MemTag split_mem_tag) {
+ add_reserved_region(addr, split, NativeCallStack::empty_stack(), mem_tag);
+ add_reserved_region(addr + split, size - split, NativeCallStack::empty_stack(), split_mem_tag);
}
-bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) {
- assert(addr != nullptr, "Invalid address");
- assert(size > 0, "Invalid size");
- assert(_reserved_regions != nullptr, "Sanity check");
- MemTracker::assert_locked();
-
- ReservedMemoryRegion rgn(addr, size);
- ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn);
-
- if (reserved_rgn == nullptr) {
- log_debug(nmt)("No reserved region found for (" INTPTR_FORMAT ", %zu)!",
- p2i(rgn.base()), rgn.size());
- }
- assert(reserved_rgn != nullptr, "No reserved region");
- if (reserved_rgn->same_region(addr, size)) {
- return remove_released_region(reserved_rgn);
- }
+bool VirtualMemoryTracker::Instance::print_containing_region(const void* p, outputStream* st) {
+ assert(_tracker != nullptr, "Sanity check");
+ return _tracker->print_containing_region(p, st);
+}
- // uncommit regions within the released region
- if (!reserved_rgn->remove_uncommitted_region(addr, size)) {
+bool VirtualMemoryTracker::print_containing_region(const void* p, outputStream* st) {
+ ReservedMemoryRegion rmr = tree()->find_reserved_region((address)p);
+ if (!rmr.contain_address((address)p)) {
return false;
}
-
- if (reserved_rgn->mem_tag() == mtClassShared) {
- if (reserved_rgn->contain_region(addr, size)) {
- // This is an unmapped CDS region, which is part of the reserved shared
- // memory region.
- // See special handling in VirtualMemoryTracker::add_reserved_region also.
- return true;
- }
-
- if (size > reserved_rgn->size()) {
- // This is from release the whole region spanning from archive space to class space,
- // so we release them altogether.
- ReservedMemoryRegion class_rgn(addr + reserved_rgn->size(),
- (size - reserved_rgn->size()));
- ReservedMemoryRegion* cls_rgn = _reserved_regions->find(class_rgn);
- assert(cls_rgn != nullptr, "Class space region not recorded?");
- assert(cls_rgn->mem_tag() == mtClass, "Must be class mem tag");
- remove_released_region(reserved_rgn);
- remove_released_region(cls_rgn);
- return true;
- }
+ st->print_cr(PTR_FORMAT " in mmap'd memory region [" PTR_FORMAT " - " PTR_FORMAT "], tag %s",
+ p2i(p), p2i(rmr.base()), p2i(rmr.end()), NMTUtil::tag_to_enum_name(rmr.mem_tag()));
+ if (MemTracker::tracking_level() == NMT_detail) {
+ rmr.call_stack()->print_on(st);
}
+ st->cr();
+ return true;
+}
- VirtualMemorySummary::record_released_memory(size, reserved_rgn->mem_tag());
+bool VirtualMemoryTracker::Instance::walk_virtual_memory(VirtualMemoryWalker* walker) {
+ assert(_tracker != nullptr, "Sanity check");
+ return _tracker->walk_virtual_memory(walker);
+}
- assert(reserved_rgn->contain_region(addr, size), "Not completely contained");
- if (reserved_rgn->base() == addr ||
- reserved_rgn->end() == addr + size) {
- reserved_rgn->exclude_region(addr, size);
- return true;
- } else {
- address top = reserved_rgn->end();
- address high_base = addr + size;
- ReservedMemoryRegion high_rgn(high_base, top - high_base,
- *reserved_rgn->call_stack(), reserved_rgn->mem_tag());
-
- // use original region for lower region
- reserved_rgn->exclude_region(addr, top - addr);
- LinkedListNode* new_rgn = _reserved_regions->add(high_rgn);
- if (new_rgn == nullptr) {
+bool VirtualMemoryTracker::walk_virtual_memory(VirtualMemoryWalker* walker) {
+ MemTracker::NmtVirtualMemoryLocker nvml;
+ tree()->visit_reserved_regions([&](ReservedMemoryRegion& rgn) {
+ if (!walker->do_allocation_site(&rgn)) {
return false;
- } else {
- reserved_rgn->move_committed_regions(addr, *new_rgn->data());
- return true;
}
- }
+ return true;
+ });
+ return true;
}
-// Given an existing memory mapping registered with NMT, split the mapping in
-// two. The newly created two mappings will be registered under the call
-// stack and the memory tags of the original section.
-bool VirtualMemoryTracker::split_reserved_region(address addr, size_t size, size_t split, MemTag mem_tag, MemTag split_tag) {
-
- ReservedMemoryRegion rgn(addr, size);
- ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn);
- assert(reserved_rgn->same_region(addr, size), "Must be identical region");
- assert(reserved_rgn != nullptr, "No reserved region");
- assert(reserved_rgn->committed_size() == 0, "Splitting committed region?");
-
- NativeCallStack original_stack = *reserved_rgn->call_stack();
- MemTag original_tag = reserved_rgn->mem_tag();
+size_t ReservedMemoryRegion::committed_size() const {
+ size_t committed = 0;
+ size_t result = 0;
+ VirtualMemoryTracker::Instance::tree()->visit_committed_regions(*this, [&](CommittedMemoryRegion& crgn) {
+ result += crgn.size();
+ return true;
+ });
+ return result;
+}
- const char* name = reserved_rgn->mem_tag_name();
- remove_released_region(reserved_rgn);
- log_debug(nmt)("Split region \'%s\' (" INTPTR_FORMAT ", %zu) with size %zu",
- name, p2i(rgn.base()), rgn.size(), split);
- // Now, create two new regions.
- add_reserved_region(addr, split, original_stack, mem_tag);
- add_reserved_region(addr + split, size - split, original_stack, split_tag);
+address ReservedMemoryRegion::thread_stack_uncommitted_bottom() const {
+ address bottom = base();
+ address top = base() + size();
+ VirtualMemoryTracker::Instance::tree()->visit_committed_regions(*this, [&](CommittedMemoryRegion& crgn) {
+ address committed_top = crgn.base() + crgn.size();
+ if (committed_top < top) {
+ // committed stack guard pages, skip them
+ bottom = crgn.base() + crgn.size();
+ } else {
+ assert(top == committed_top, "Sanity, top=" INTPTR_FORMAT " , com-top=" INTPTR_FORMAT, p2i(top), p2i(committed_top));
+ return false;;
+ }
+ return true;
+ });
- return true;
+ return bottom;
}
-
// Iterate the range, find committed region within its bound.
class RegionIterator : public StackObj {
private:
@@ -645,7 +298,6 @@ class SnapshotThreadStackWalker : public VirtualMemoryWalker {
// Align the size to work with full pages (Alpine and AIX stack top is not page aligned)
size_t aligned_stack_size = align_up(stack_size, os::vm_page_size());
- ReservedMemoryRegion* region = const_cast(rgn);
NativeCallStack ncs; // empty stack
RegionIterator itr(stack_bottom, aligned_stack_size);
@@ -657,7 +309,7 @@ class SnapshotThreadStackWalker : public VirtualMemoryWalker {
if (stack_bottom + stack_size < committed_start + committed_size) {
committed_size = stack_bottom + stack_size - committed_start;
}
- region->add_committed_region(committed_start, committed_size, ncs);
+ VirtualMemoryTracker::Instance::add_committed_region(committed_start, committed_size, ncs);
DEBUG_ONLY(found_stack = true;)
}
#ifdef ASSERT
@@ -670,55 +322,24 @@ class SnapshotThreadStackWalker : public VirtualMemoryWalker {
}
};
-void VirtualMemoryTracker::snapshot_thread_stacks() {
+void VirtualMemoryTracker::Instance::snapshot_thread_stacks() {
SnapshotThreadStackWalker walker;
walk_virtual_memory(&walker);
}
-bool VirtualMemoryTracker::walk_virtual_memory(VirtualMemoryWalker* walker) {
- assert(_reserved_regions != nullptr, "Sanity check");
- MemTracker::NmtVirtualMemoryLocker nvml;
- // Check that the _reserved_regions haven't been deleted.
- if (_reserved_regions != nullptr) {
- LinkedListNode* head = _reserved_regions->head();
- while (head != nullptr) {
- const ReservedMemoryRegion* rgn = head->peek();
- if (!walker->do_allocation_site(rgn)) {
+ReservedMemoryRegion RegionsTree::find_reserved_region(address addr) {
+ ReservedMemoryRegion rmr;
+ auto contain_region = [&](ReservedMemoryRegion& region_in_tree) {
+ if (region_in_tree.contain_address(addr)) {
+ rmr = region_in_tree;
return false;
}
- head = head->next();
- }
- }
- return true;
+ return true;
+ };
+ visit_reserved_regions(contain_region);
+ return rmr;
}
-class PrintRegionWalker : public VirtualMemoryWalker {
-private:
- const address _p;
- outputStream* _st;
- NativeCallStackPrinter _stackprinter;
-public:
- PrintRegionWalker(const void* p, outputStream* st) :
- _p((address)p), _st(st), _stackprinter(st) { }
-
- bool do_allocation_site(const ReservedMemoryRegion* rgn) {
- if (rgn->contain_address(_p)) {
- _st->print_cr(PTR_FORMAT " in mmap'd memory region [" PTR_FORMAT " - " PTR_FORMAT "], tag %s",
- p2i(_p), p2i(rgn->base()), p2i(rgn->base() + rgn->size()), NMTUtil::tag_to_enum_name(rgn->mem_tag()));
- if (MemTracker::tracking_level() == NMT_detail) {
- _stackprinter.print_stack(rgn->call_stack());
- _st->cr();
- }
- return false;
- }
- return true;
- }
-};
-
-// If p is contained within a known memory region, print information about it to the
-// given stream and return true; false otherwise.
-bool VirtualMemoryTracker::print_containing_region(const void* p, outputStream* st) {
- PrintRegionWalker walker(p, st);
- return !walk_virtual_memory(&walker);
-
-}
+bool CommittedMemoryRegion::equals(const ReservedMemoryRegion& rmr) const {
+ return size() == rmr.size() && call_stack()->equals(*(rmr.call_stack()));
+}
\ No newline at end of file
diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.hpp b/src/hotspot/share/nmt/virtualMemoryTracker.hpp
index 2b3b5722571..1c9628cd828 100644
--- a/src/hotspot/share/nmt/virtualMemoryTracker.hpp
+++ b/src/hotspot/share/nmt/virtualMemoryTracker.hpp
@@ -25,16 +25,29 @@
#ifndef SHARE_NMT_VIRTUALMEMORYTRACKER_HPP
#define SHARE_NMT_VIRTUALMEMORYTRACKER_HPP
-#include "memory/allocation.hpp"
-#include "memory/metaspace.hpp" // For MetadataType
-#include "memory/metaspaceStats.hpp"
#include "nmt/allocationSite.hpp"
-#include "nmt/nmtCommon.hpp"
+#include "nmt/vmatree.hpp"
+#include "nmt/regionsTree.hpp"
#include "runtime/atomic.hpp"
-#include "utilities/linkedlist.hpp"
#include "utilities/nativeCallStack.hpp"
#include "utilities/ostream.hpp"
+// VirtualMemoryTracker (VMT) is an internal class of the MemTracker.
+// All the Hotspot code use only the MemTracker interface to register the memory operations in NMT.
+// Memory regions can be reserved/committed/uncommitted/released by calling MemTracker API which in turn call the corresponding functions in VMT.
+// VMT uses RegionsTree to hold and manage the memory regions. Each region has two nodes that each one has address of the region (start/end) and
+// state (reserved/released/committed) and MemTag of the regions before and after it.
+//
+// The memory operations of Reserve/Commit/Uncommit/Release are tracked by updating/inserting/deleting the nodes in the tree. When an operation
+// changes nodes in the tree, the summary of the changes is returned back in a SummaryDiff struct. This struct shows that how much reserve/commit amount
+// of any specific MemTag is changed. The summary of every operation is accumulated in VirtualMemorySummary class.
+//
+// Not all operations are valid in VMT. The following predicates are checked before the operation is applied to the tree and/or VirtualMemorySummary:
+// - committed size of a MemTag should be <= of its reserved size
+// - uncommitted size of a MemTag should be <= of its committed size
+// - released size of a MemTag should be <= of its reserved size
+
+
/*
* Virtual memory counter
*/
@@ -276,136 +289,124 @@ class CommittedMemoryRegion : public VirtualMemoryRegion {
NativeCallStack _stack;
public:
- CommittedMemoryRegion(address addr, size_t size, const NativeCallStack& stack) :
- VirtualMemoryRegion(addr, size), _stack(stack) { }
+ CommittedMemoryRegion()
+ : VirtualMemoryRegion((address)1, 1), _stack(NativeCallStack::empty_stack()) { }
+
+ CommittedMemoryRegion(address addr, size_t size, const NativeCallStack& stack)
+ : VirtualMemoryRegion(addr, size), _stack(stack) { }
inline void set_call_stack(const NativeCallStack& stack) { _stack = stack; }
inline const NativeCallStack* call_stack() const { return &_stack; }
+ bool equals(const ReservedMemoryRegion& other) const;
};
-
-typedef LinkedListIterator CommittedRegionIterator;
-
-int compare_committed_region(const CommittedMemoryRegion&, const CommittedMemoryRegion&);
class ReservedMemoryRegion : public VirtualMemoryRegion {
private:
- SortedLinkedList
- _committed_regions;
-
NativeCallStack _stack;
- MemTag _mem_tag;
+ MemTag _mem_tag;
public:
+ bool is_valid() { return base() != (address)1 && size() != 1;}
+
+ ReservedMemoryRegion()
+ : VirtualMemoryRegion((address)1, 1), _stack(NativeCallStack::empty_stack()), _mem_tag(mtNone) { }
+
ReservedMemoryRegion(address base, size_t size, const NativeCallStack& stack,
- MemTag mem_tag) :
- VirtualMemoryRegion(base, size), _stack(stack), _mem_tag(mem_tag) { }
+ MemTag mem_tag = mtNone)
+ : VirtualMemoryRegion(base, size), _stack(stack), _mem_tag(mem_tag) { }
- ReservedMemoryRegion(address base, size_t size) :
- VirtualMemoryRegion(base, size), _stack(NativeCallStack::empty_stack()), _mem_tag(mtNone) { }
+ ReservedMemoryRegion(address base, size_t size)
+ : VirtualMemoryRegion(base, size), _stack(NativeCallStack::empty_stack()), _mem_tag(mtNone) { }
// Copy constructor
- ReservedMemoryRegion(const ReservedMemoryRegion& rr) :
- VirtualMemoryRegion(rr.base(), rr.size()) {
+ ReservedMemoryRegion(const ReservedMemoryRegion& rr)
+ : VirtualMemoryRegion(rr.base(), rr.size()) {
*this = rr;
}
inline void set_call_stack(const NativeCallStack& stack) { _stack = stack; }
inline const NativeCallStack* call_stack() const { return &_stack; }
- void set_mem_tag(MemTag mem_tag);
inline MemTag mem_tag() const { return _mem_tag; }
// uncommitted thread stack bottom, above guard pages if there is any.
address thread_stack_uncommitted_bottom() const;
- bool add_committed_region(address addr, size_t size, const NativeCallStack& stack);
- bool remove_uncommitted_region(address addr, size_t size);
-
- size_t committed_size() const;
+ size_t committed_size() const;
- // move committed regions that higher than specified address to
- // the new region
- void move_committed_regions(address addr, ReservedMemoryRegion& rgn);
-
- CommittedRegionIterator iterate_committed_regions() const {
- return CommittedRegionIterator(_committed_regions.head());
- }
ReservedMemoryRegion& operator= (const ReservedMemoryRegion& other) {
set_base(other.base());
set_size(other.size());
- _stack = *other.call_stack();
+ _stack = *other.call_stack();
_mem_tag = other.mem_tag();
- _committed_regions.clear();
-
- CommittedRegionIterator itr = other.iterate_committed_regions();
- const CommittedMemoryRegion* rgn = itr.next();
- while (rgn != nullptr) {
- _committed_regions.add(*rgn);
- rgn = itr.next();
- }
return *this;
}
- const char* mem_tag_name() const { return NMTUtil::tag_to_name(_mem_tag); }
-
- private:
- // The committed region contains the uncommitted region, subtract the uncommitted
- // region from this committed region
- bool remove_uncommitted_region(LinkedListNode* node,
- address addr, size_t sz);
-
- bool add_committed_region(const CommittedMemoryRegion& rgn) {
- assert(rgn.base() != nullptr, "Invalid base address");
- assert(size() > 0, "Invalid size");
- return _committed_regions.add(rgn) != nullptr;
- }
+ const char* tag_name() const { return NMTUtil::tag_to_name(_mem_tag); }
};
-int compare_reserved_region_base(const ReservedMemoryRegion& r1, const ReservedMemoryRegion& r2);
-
class VirtualMemoryWalker : public StackObj {
public:
virtual bool do_allocation_site(const ReservedMemoryRegion* rgn) { return false; }
};
-// Main class called from MemTracker to track virtual memory allocations, commits and releases.
-class VirtualMemoryTracker : AllStatic {
- friend class VirtualMemoryTrackerTest;
- friend class CommittedVirtualMemoryTest;
- public:
- static bool initialize(NMT_TrackingLevel level);
+class VirtualMemoryTracker {
+ RegionsTree _tree;
- static bool add_reserved_region (address base_addr, size_t size, const NativeCallStack& stack, MemTag mem_tag);
+ public:
+ VirtualMemoryTracker(bool is_detailed_mode) : _tree(is_detailed_mode) { }
- static bool add_committed_region (address base_addr, size_t size, const NativeCallStack& stack);
- static bool remove_uncommitted_region (address base_addr, size_t size);
- static bool remove_released_region (address base_addr, size_t size);
- static bool remove_released_region (ReservedMemoryRegion* rgn);
- static void set_reserved_region_type (address addr, size_t size, MemTag mem_tag);
+ void add_reserved_region (address base_addr, size_t size, const NativeCallStack& stack, MemTag mem_tag = mtNone);
+ void add_committed_region (address base_addr, size_t size, const NativeCallStack& stack);
+ void remove_uncommitted_region (address base_addr, size_t size);
+ void remove_released_region (address base_addr, size_t size);
+ void set_reserved_region_tag (address addr, size_t size, MemTag mem_tag);
// Given an existing memory mapping registered with NMT, split the mapping in
// two. The newly created two mappings will be registered under the call
- // stack and the memory tag of the original section.
- static bool split_reserved_region(address addr, size_t size, size_t split, MemTag mem_tag, MemTag split_type);
+ // stack and the memory tags of the original section.
+ void split_reserved_region(address addr, size_t size, size_t split, MemTag mem_tag, MemTag split_mem_tag);
// Walk virtual memory data structure for creating baseline, etc.
- static bool walk_virtual_memory(VirtualMemoryWalker* walker);
+ bool walk_virtual_memory(VirtualMemoryWalker* walker);
// If p is contained within a known memory region, print information about it to the
// given stream and return true; false otherwise.
- static bool print_containing_region(const void* p, outputStream* st);
+ bool print_containing_region(const void* p, outputStream* st);
// Snapshot current thread stacks
- static void snapshot_thread_stacks();
-
- private:
- static SortedLinkedList* _reserved_regions;
+ void snapshot_thread_stacks();
+ void apply_summary_diff(VMATree::SummaryDiff diff);
+ RegionsTree* tree() { return &_tree; }
+
+ class Instance : public AllStatic {
+ friend class VirtualMemoryTrackerTest;
+ friend class CommittedVirtualMemoryTest;
+
+ static VirtualMemoryTracker* _tracker;
+
+ public:
+ using RegionData = VMATree::RegionData;
+ static bool initialize(NMT_TrackingLevel level);
+
+ static void add_reserved_region (address base_addr, size_t size, const NativeCallStack& stack, MemTag mem_tag = mtNone);
+ static void add_committed_region (address base_addr, size_t size, const NativeCallStack& stack);
+ static void remove_uncommitted_region (address base_addr, size_t size);
+ static void remove_released_region (address base_addr, size_t size);
+ static void set_reserved_region_tag (address addr, size_t size, MemTag mem_tag);
+ static void split_reserved_region(address addr, size_t size, size_t split, MemTag mem_tag, MemTag split_mem_tag);
+ static bool walk_virtual_memory(VirtualMemoryWalker* walker);
+ static bool print_containing_region(const void* p, outputStream* st);
+ static void snapshot_thread_stacks();
+ static void apply_summary_diff(VMATree::SummaryDiff diff);
+
+ static RegionsTree* tree() { return _tracker->tree(); }
+ };
};
-#endif // SHARE_NMT_VIRTUALMEMORYTRACKER_HPP
-
+#endif // SHARE_NMT_VIRTUALMEMORYTRACKER_HPP
\ No newline at end of file
diff --git a/src/hotspot/share/nmt/vmatree.cpp b/src/hotspot/share/nmt/vmatree.cpp
index 3352a6e5cd4..a60c30c812d 100644
--- a/src/hotspot/share/nmt/vmatree.cpp
+++ b/src/hotspot/share/nmt/vmatree.cpp
@@ -28,21 +28,229 @@
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"
-const VMATree::RegionData VMATree::empty_regiondata{NativeCallStackStorage::StackIndex{}, mtNone};
-const char* VMATree::statetype_strings[3] = {
- "reserved", "committed", "released",
+// Semantics
+// This tree is used to store and track the state of virtual memory regions.
+// The nodes in the tree are key-value pairs where the key is the memory address and the value is the State of the memory regions.
+// The State of a region describes whether the region is released, reserved or committed, which MemTag it has and where in
+// Hotspot (using call-stacks) it is reserved or committed.
+// Each node holds the State of the regions to its left and right. Each memory region is described by two
+// memory addresses for its start and end.
+// For example, to describe the region that starts at memory address 0xA000 with size 0x1000, there will be two nodes
+// with the keys 0xA000 (node A) and 0xB000 (node B) in the tree. The value of the key-value pairs of node A and
+// node B describe the region's State, using right of A and left of B (<--left--A--right-->.....<--left--B--right-->...).
+//
+// Virtual memory can be reserved, committed, uncommitted and released. For each operation a request
+// () is sent to the tree to handle.
+//
+// The expected changes are described here for each operation:
+//
+// ### Reserve a region
+// When a region is reserved, all the overlapping regions in the tree should:
+// - be marked as Reserved
+// - take MemTag of the operation
+// - store call-stack of the request to the reserve call-stack
+// - clear commit call-stack
+//
+// ### Commit a region
+// When a region is committed, all the overlapping regions in the tree should:
+// - be marked as Committed
+// - take MemTag of the operation or MemTag of the existing region, depends on which-tag-to-use in the request
+// - if the region is in Released state
+// - mark the region as both Reserved and Committed
+// - store the call-stack of the request to the reserve call-stack
+// - store the call-stack of the request to the commit call-stack
+//
+// ### Uncommit a region
+// When a region is uncommitted, all the overlapping regions in the tree should:
+// - be ignored if the region is in Released state
+// - be marked as Reserved
+// - not change the MemTag
+// - not change the reserve call-stack
+// - clear commit call-stack
+//
+// ### Release a region
+// When a region is released, all the overlapping regions in the tree should:
+// - be marked as Released
+// - set the MemTag to mtNone
+// - clear both reserve and commit call-stack
+//
+// --- Accounting
+// After each operation, the tree should be able to report how much memory is reserved or committed per MemTag.
+// So for each region that changes to a new State, the report should contain (separately for each tag) the amount
+// of reserve and commit that are changed (increased or decreased) due to the operation.
+
+const VMATree::RegionData VMATree::empty_regiondata{NativeCallStackStorage::invalid, mtNone};
+
+const char* VMATree::statetype_strings[4] = {
+ "released","reserved", "only-committed", "committed",
};
-VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType state,
+VMATree::SIndex VMATree::get_new_reserve_callstack(const SIndex es, const StateType ex, const RequestInfo& req) const {
+ const SIndex ES = NativeCallStackStorage::invalid; // Empty Stack
+ const SIndex rq = req.callstack;
+ const int op = req.op_to_index();
+ const Operation oper = req.op();
+ assert(op >= 0 && op < 4, "should be");
+ assert(op >= 0 && op < 4, "should be");
+ // existing state
+ SIndex result[4][3] = {// Rl Rs C
+ {ES, ES, ES}, // op == Release
+ {rq, rq, rq}, // op == Reserve
+ {es, es, es}, // op == Commit
+ {es, es, es} // op == Uncommit
+ };
+ // When committing a Released region, the reserve-call-stack of the region should also be as what is in the request
+ if (oper == Operation::Commit && ex == StateType::Released) {
+ return rq;
+ } else {
+ return result[op][state_to_index(ex)];
+ }
+}
+
+VMATree::SIndex VMATree::get_new_commit_callstack(const SIndex es, const StateType ex, const RequestInfo& req) const {
+ const SIndex ES = NativeCallStackStorage::invalid; // Empty Stack
+ const SIndex rq = req.callstack;
+ const int op_index = req.op_to_index();
+ const Operation op = req.op();
+ assert(op_index >= 0 && op_index < 4, "should be");
+ // existing state
+ SIndex result[4][3] = {// Rl Rs C
+ {ES, ES, ES}, // op == Release
+ {ES, ES, ES}, // op == Reserve
+ {rq, rq, rq}, // op == Commit
+ {ES, ES, ES} // op == Uncommit
+ };
+ return result[op_index][state_to_index(ex)];
+}
+
+VMATree::StateType VMATree::get_new_state(const StateType ex, const RequestInfo& req) const {
+ const StateType Rl = StateType::Released;
+ const StateType Rs = StateType::Reserved;
+ const StateType C = StateType::Committed;
+ const int op = req.op_to_index();
+ assert(op >= 0 && op < 4, "should be");
+ // existing state
+ StateType result[4][3] = {// Rl Rs C
+ {Rl, Rl, Rl}, // op == Release
+ {Rs, Rs, Rs}, // op == Reserve
+ { C, C, C}, // op == Commit
+ {Rl, Rs, Rs} // op == Uncommit
+ };
+ return result[op][state_to_index(ex)];
+}
+
+MemTag VMATree::get_new_tag(const MemTag ex, const RequestInfo& req) const {
+ switch(req.op()) {
+ case Operation::Release:
+ return mtNone;
+ case Operation::Reserve:
+ return req.tag;
+ case Operation::Commit:
+ return req.use_tag_inplace ? ex : req.tag;
+ case Operation::Uncommit:
+ return ex;
+ default:
+ break;
+ }
+ return mtNone;
+}
+
+void VMATree::compute_summary_diff(const SingleDiff::delta region_size,
+ const MemTag current_tag,
+ const StateType& ex,
+ const RequestInfo& req,
+ const MemTag operation_tag,
+ SummaryDiff& diff) const {
+ const StateType Rl = StateType::Released;
+ const StateType Rs = StateType::Reserved;
+ const StateType C = StateType::Committed;
+ const int op = req.op_to_index();
+ const Operation oper = req.op();
+ assert(op >= 0 && op < 4, "should be");
+
+ SingleDiff::delta a = region_size;
+ // A region with size `a` has a state as and an operation is requested as in
+ // The region has tag `current_tag` and the operation has tag `operation_tag`.
+ // For each state, we decide how much to be added/subtracted from current_tag to operation_tag. Two tables for reserve and commit.
+ // Each pair of in the table means add `x` to current_tag and add `y` to operation_tag. There are 3 pairs in each row for 3 states.
+ // For example, `reserve[1][4,5]` says `-a,a` means:
+ // - we are reserving with operation_tag a region which is already commited with current_tag
+ // - since we are reserving, then `a` will be added to operation_tag. (`y` is `a`)
+ // - since we uncommitting (by reserving) then `a` is to be subtracted from current_tag. (`x` is `-a`).
+ // - amount of uncommitted size is in table `commit[1][4,5]` which is `-a,0` that means subtract `a` from current_tag.
+ // existing state
+ SingleDiff::delta reserve[4][3*2] = {// Rl Rs C
+ {0,0, -a,0, -a,0 }, // op == Release
+ {0,a, -a,a, -a,a }, // op == Reserve
+ {0,a, -a,a, -a,a }, // op == Commit
+ {0,0, 0,0, 0,0 } // op == Uncommit
+ };
+ SingleDiff::delta commit[4][3*2] = {// Rl Rs C
+ {0,0, 0,0, -a,0 }, // op == Release
+ {0,0, 0,0, -a,0 }, // op == Reserve
+ {0,a, 0,a, -a,a }, // op == Commit
+ {0,0, 0,0, -a,0 } // op == Uncommit
+ };
+ SingleDiff& from_rescom = diff.tag[NMTUtil::tag_to_index(current_tag)];
+ SingleDiff& to_rescom = diff.tag[NMTUtil::tag_to_index(operation_tag)];
+ int st = state_to_index(ex);
+ from_rescom.reserve += reserve[op][st * 2 ];
+ to_rescom.reserve += reserve[op][st * 2 + 1];
+ from_rescom.commit += commit[op][st * 2 ];
+ to_rescom.commit += commit[op][st * 2 + 1];
+
+}
+// update the region state between n1 and n2. Since n1 and n2 are pointers, any update of them will be visible from tree.
+// If n1 is noop, it can be removed because its left region (n1->val().in) is already decided and its right state (n1->val().out) is decided here.
+// The state of right of n2 (n2->val().out) cannot be decided here yet.
+void VMATree::update_region(TreapNode* n1, TreapNode* n2, const RequestInfo& req, SummaryDiff& diff) {
+ assert(n1 != nullptr,"sanity");
+ assert(n2 != nullptr,"sanity");
+ //.........n1......n2......
+ // ^------^
+ // |
+ IntervalState exSt = n1->val().out; // existing state info
+
+
+ StateType existing_state = exSt.type();
+ MemTag existing_tag = exSt.mem_tag();
+ SIndex existing_reserve_callstack = exSt.reserved_stack();
+ SIndex existing_commit_callstack = exSt.committed_stack();
+
+ StateType new_state = get_new_state(existing_state, req);
+ MemTag new_tag = get_new_tag(n1->val().out.mem_tag(), req);
+ SIndex new_reserve_callstack = get_new_reserve_callstack(existing_reserve_callstack, existing_state, req);
+ SIndex new_commit_callstack = get_new_commit_callstack(existing_commit_callstack, existing_state, req);
+
+ // n1........n2
+ // out-->
+ n1->val().out.set_tag(new_tag);
+ n1->val().out.set_type(new_state);
+ n1->val().out.set_reserve_stack(new_reserve_callstack);
+ n1->val().out.set_commit_stack(new_commit_callstack);
+
+ // n1........n2
+ // <--in
+ n2->val().in.set_tag(new_tag);
+ n2->val().in.set_type(new_state);
+ n2->val().in.set_reserve_stack(new_reserve_callstack);
+ n2->val().in.set_commit_stack(new_commit_callstack);
+
+ SingleDiff::delta region_size = n2->key() - n1->key();
+ compute_summary_diff(region_size, existing_tag, existing_state, req, new_tag, diff);
+}
+
+
+VMATree::SummaryDiff VMATree::register_mapping(position _A, position _B, StateType state,
const RegionData& metadata, bool use_tag_inplace) {
- assert(!use_tag_inplace || metadata.mem_tag == mtNone,
- "If using use_tag_inplace, then the supplied tag should be mtNone, was instead: %s", NMTUtil::tag_to_name(metadata.mem_tag));
- if (A == B) {
- // A 0-sized mapping isn't worth recording.
+
+ if (_A == _B) {
return SummaryDiff();
}
-
+ assert(_A < _B, "should be");
+ SummaryDiff diff;
+ RequestInfo req{_A, _B, state, metadata.mem_tag, metadata.stack_idx, use_tag_inplace};
IntervalChange stA{
IntervalState{StateType::Released, empty_regiondata},
IntervalState{ state, metadata}
@@ -51,176 +259,401 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType
IntervalState{ state, metadata},
IntervalState{StateType::Released, empty_regiondata}
};
+ stA.out.set_commit_stack(NativeCallStackStorage::invalid);
+ stB.in.set_commit_stack(NativeCallStackStorage::invalid);
+ VMATreap::Range rA = _tree.find_enclosing_range(_A);
+ VMATreap::Range rB = _tree.find_enclosing_range(_B);
- // First handle A.
- // Find closest node that is LEQ A
- bool LEQ_A_found = false;
- AddressState LEQ_A;
- TreapNode* leqA_n = _tree.closest_leq(A);
- if (leqA_n == nullptr) {
- assert(!use_tag_inplace, "Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B);
- if (use_tag_inplace) {
- log_debug(nmt)("Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B);
- }
- // No match. We add the A node directly, unless it would have no effect.
- if (!stA.is_noop()) {
- _tree.upsert(A, stA);
+ // nodes: .....X.......Y...Z......W........U
+ // request: A------------------B
+ // X,Y = enclosing_nodes(A)
+ // W,U = enclosing_nodes(B)
+ // The cases are whether or not X and Y exists and X == A. (A == Y doesn't happen since it is searched by 'lt' predicate)
+ // The cases are whether or not W and U exists and W == B. (B == U doesn't happen since it is searched by 'lt' predicate)
+
+ // We update regions in 3 sections: 1) X..A..Y, 2) Y....W, 3) W..B..U
+ // Y: is the closest node greater than A, but less than B
+ // W: is the closest node less than B, but greater than A
+ // The regions in [Y,W) are updated in a loop. We update X..A..Y before the loop and W..B..U after the loop.
+ // The table below summarizes the overlap cases. The overlapping case depends on whether X, Y, W and U exist or not,
+ // and if they exist whether they are the same or not.
+ // In the notations here, when there is not dot ('.') between two nodes it meaans that they are the same. For example,
+ // ...XA....Y.... means X == A.
+
+
+ // row 0: .........A..................B.....
+ // row 1: .........A...YW.............B..... // it is impossible, since it means only one node exists in the tree.
+ // row 2: .........A...Y..........W...B.....
+ // row 3: .........A...Y.............WB.....
+
+ // row 4: .....X...A..................B.....
+ // row 5: .....X...A...YW.............B.....
+ // row 6: .....X...A...Y..........W...B.....
+ // row 7: .....X...A...Y.............WB.....
+
+ // row 8: ........XA..................B.....
+ // row 9: ........XA...YW.............B.....
+ // row 10: ........XA...Y..........W...B.....
+ // row 11: ........XA...Y.............WB.....
+
+ // row 12: .........A..................B....U
+ // row 13: .........A...YW.............B....U
+ // row 14: .........A...Y..........W...B....U
+ // row 15: .........A...Y.............WB....U
+
+ // row 16: .....X...A..................B....U
+ // row 17: .....X...A...YW.............B....U
+ // row 18: .....X...A...Y..........W...B....U
+ // row 19: .....X...A...Y.............WB....U
+
+ // row 20: ........XA..................B....U
+ // row 21: ........XA...YW.............B....U
+ // row 22: ........XA...Y..........W...B....U
+ // row 23: ........XA...Y.............WB....U
+
+
+ // We intentionally did not summarize/compress the cases to keep them as separate.
+ // This expanded way of describing the cases helps us to understand/analyze/verify/debug/maintain
+ // the corresponding code more easily.
+ // Mapping of table to row, row to switch-case should be consistent. If one changes, the others have
+ // to be updated accordingly. The sequence of dependecies is: table -> row no -> switch(row)-case -> code.
+ // Meaning that whenever any of one item in this sequence is changed, the rest of the consequent items to
+ // be checked/changed.
+
+ TreapNode* X = rA.start;
+ TreapNode* Y = rA.end;
+ TreapNode* W = rB.start;
+ TreapNode* U = rB.end;
+ TreapNode nA{_A, stA, 0}; // the node that represents A
+ TreapNode nB{_B, stB, 0}; // the node that represents B
+ TreapNode* A = &nA;
+ TreapNode* B = &nB;
+ auto upsert_if= [&](TreapNode* node) {
+ if (!node->val().is_noop()) {
+ _tree.upsert(node->key(), node->val());
}
- } else {
- LEQ_A_found = true;
- LEQ_A = AddressState{leqA_n->key(), leqA_n->val()};
- StateType leqA_state = leqA_n->val().out.type();
- StateType new_state = stA.out.type();
- // If we specify use_tag_inplace then the new region takes over the current tag instead of the tag in metadata.
- // This is important because the VirtualMemoryTracker API doesn't require supplying the tag for some operations.
- if (use_tag_inplace) {
- assert(leqA_n->val().out.type() != StateType::Released, "Should not use inplace the tag of a released region");
- MemTag tag = leqA_n->val().out.mem_tag();
- stA.out.set_tag(tag);
- stB.in.set_tag(tag);
+ };
+ // update region between n1 and n2
+ auto update = [&](TreapNode* n1, TreapNode* n2) {
+ update_region(n1, n2, req, diff);
+ };
+ auto remove_if = [&](TreapNode* node) -> bool{
+ if (node->val().is_noop()) {
+ _tree.remove(node->key());
+ return true;
}
-
- // Unless we know better, let B's outgoing state be the outgoing state of the node at or preceding A.
- // Consider the case where the found node is the start of a region enclosing [A,B)
- stB.out = out_state(leqA_n);
-
- // Direct address match.
- if (leqA_n->key() == A) {
- // Take over in state from old address.
- stA.in = in_state(leqA_n);
-
- // We may now be able to merge two regions:
- // If the node's old state matches the new, it becomes a noop. That happens, for example,
- // when expanding a committed area: commit [x1, A); ... commit [A, x3)
- // and the result should be a larger area, [x1, x3). In that case, the middle node (A and le_n)
- // is not needed anymore. So we just remove the old node.
- stB.in = stA.out;
- if (stA.is_noop()) {
- // invalidates leqA_n
- _tree.remove(leqA_n->key());
- } else {
- // If the state is not matching then we have different operations, such as:
- // reserve [x1, A); ... commit [A, x2); or
- // reserve [x1, A), mem_tag1; ... reserve [A, x2), mem_tag2; or
- // reserve [A, x1), mem_tag1; ... reserve [A, x2), mem_tag2;
- // then we re-use the existing out node, overwriting its old metadata.
- leqA_n->val() = stA;
- }
- } else {
- // The address must be smaller.
- assert(A > leqA_n->key(), "must be");
-
- // We add a new node, but only if there would be a state change. If there would not be a
- // state change, we just omit the node.
- // That happens, for example, when reserving within an already reserved region with identical metadata.
- stA.in = out_state(leqA_n); // .. and the region's prior state is the incoming state
- if (stA.is_noop()) {
- // Nothing to do.
- } else {
- // Add new node.
- _tree.upsert(A, stA);
+ return false;
+ };
+ GrowableArrayCHeap to_be_removed;
+ // update regions in range A to B
+ auto update_loop = [&]() {
+ TreapNode* prev = nullptr;
+ _tree.visit_range_in_order(_A + 1, _B + 1, [&](TreapNode* curr) {
+ if (prev != nullptr) {
+ update_region(prev, curr, req, diff);
+ // during visit, structure of the tree should not be changed
+ // keep the keys to be removed, and remove them later
+ if (prev->val().is_noop()) {
+ to_be_removed.push(prev->key());
+ }
}
- }
- }
+ prev = curr;
+ return true;
+ });
+ };
+ // update region of [A,T)
+ auto update_A = [&](TreapNode* T) {
+ A->val().out = A->val().in;
+ update(A, T);
+ };
+ bool X_exists = X != nullptr;
+ bool Y_exists = Y != nullptr && Y->key() <= _B;
+ bool W_exists = W != nullptr && W->key() > _A;
+ bool U_exists = U != nullptr;
+ bool X_eq_A = X_exists && X->key() == _A;
+ bool W_eq_B = W_exists && W->key() == _B;
+ bool Y_eq_W = Y_exists && W_exists && W->key() == Y->key();
+ int row = -1;
+#ifdef ASSERT
+ auto print_case = [&]() {
+ log_trace(vmatree)(" req: %4d---%4d", (int)_A, (int)_B);
+ log_trace(vmatree)(" row: %2d", row);
+ log_trace(vmatree)(" X: %4ld", X_exists ? (long)X->key() : -1);
+ log_trace(vmatree)(" Y: %4ld", Y_exists ? (long)Y->key() : -1);
+ log_trace(vmatree)(" W: %4ld", W_exists ? (long)W->key() : -1);
+ log_trace(vmatree)(" U: %4ld", U_exists ? (long)U->key() : -1);
+ };
+#endif
+ // Order of the nodes if they exist are as: X <= A < Y <= W <= B < U
+ // A---------------------------B
+ // X Y YW WB U
+ // XA Y YW WB U
+ if (!X_exists && !Y_exists && !U_exists) { row = 0; }
+ if (!X_exists && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 1; }
+ if (!X_exists && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 2; }
+ if (!X_exists && Y_exists && W_eq_B && !U_exists) { row = 3; }
- // Now we handle B.
- // We first search all nodes that are (A, B]. All of these nodes
- // need to be deleted and summary accounted for. The last node before B determines B's outgoing state.
- // If there is no node between A and B, its A's incoming state.
- GrowableArrayCHeap to_be_deleted_inbetween_a_b;
- bool B_needs_insert = true;
-
- // Find all nodes between (A, B] and record their addresses and values. Also update B's
- // outgoing state.
- _tree.visit_range_in_order(A + 1, B + 1, [&](TreapNode* head) {
- int cmp_B = PositionComparator::cmp(head->key(), B);
- stB.out = out_state(head);
- if (cmp_B < 0) {
- // Record all nodes preceding B.
- to_be_deleted_inbetween_a_b.push({head->key(), head->val()});
- } else if (cmp_B == 0) {
- // Re-purpose B node, unless it would result in a noop node, in
- // which case record old node at B for deletion and summary accounting.
- if (stB.is_noop()) {
- to_be_deleted_inbetween_a_b.push(AddressState{B, head->val()});
- } else {
- head->val() = stB;
- }
- B_needs_insert = false;
- }
- });
+ if ( X_exists && !Y_exists && !U_exists) { row = 4; }
+ if ( X_exists && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 5; }
+ if ( X_exists && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 6; }
+ if ( X_exists && Y_exists && W_eq_B && !U_exists) { row = 7; }
- // Insert B node if needed
- if (B_needs_insert && // Was not already inserted
- !stB.is_noop()) // The operation is differing
- {
- _tree.upsert(B, stB);
- }
+ if ( X_eq_A && !Y_exists && !U_exists) { row = 8; }
+ if ( X_eq_A && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 9; }
+ if ( X_eq_A && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 10; }
+ if ( X_eq_A && Y_exists && W_eq_B && !U_exists) { row = 11; }
- // We now need to:
- // a) Delete all nodes between (A, B]. Including B in the case of a noop.
- // b) Perform summary accounting
- SummaryDiff diff;
+ if (!X_exists && !Y_exists && U_exists) { row = 12; }
+ if (!X_exists && Y_exists && Y_eq_W && !W_eq_B && U_exists) { row = 13; }
+ if (!X_exists && Y_exists && !Y_eq_W && !W_eq_B && U_exists) { row = 14; }
+ if (!X_exists && Y_exists && W_eq_B && U_exists) { row = 15; }
- if (to_be_deleted_inbetween_a_b.length() == 0 && LEQ_A_found) {
- // We must have smashed a hole in an existing region (or replaced it entirely).
- // LEQ_A < A < B <= C
- SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(LEQ_A.out().mem_tag())];
- if (LEQ_A.out().type() == StateType::Reserved) {
- rescom.reserve -= B - A;
- } else if (LEQ_A.out().type() == StateType::Committed) {
- rescom.commit -= B - A;
- rescom.reserve -= B - A;
- }
- }
+ if ( X_exists && !Y_exists && U_exists) { row = 16; }
+ if ( X_exists && Y_exists && Y_eq_W && !W_eq_B && U_exists) { row = 17; }
+ if ( X_exists && Y_exists && !Y_eq_W && !W_eq_B && U_exists) { row = 18; }
+ if ( X_exists && Y_exists && W_eq_B && U_exists) { row = 19; }
- // Track the previous node.
- AddressState prev{A, stA};
- for (int i = 0; i < to_be_deleted_inbetween_a_b.length(); i++) {
- const AddressState delete_me = to_be_deleted_inbetween_a_b.at(i);
- _tree.remove(delete_me.address);
-
- // Perform summary accounting
- SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(delete_me.in().mem_tag())];
- if (delete_me.in().type() == StateType::Reserved) {
- rescom.reserve -= delete_me.address - prev.address;
- } else if (delete_me.in().type() == StateType::Committed) {
- rescom.commit -= delete_me.address - prev.address;
- rescom.reserve -= delete_me.address - prev.address;
- }
- prev = delete_me;
- }
+ if ( X_eq_A && !Y_exists && U_exists) { row = 20; }
+ if ( X_eq_A && Y_exists && Y_eq_W && !W_eq_B && U_exists) { row = 21; }
+ if ( X_eq_A && Y_exists && !Y_eq_W && !W_eq_B && U_exists) { row = 22; }
+ if ( X_eq_A && Y_exists && W_eq_B && U_exists) { row = 23; }
- if (prev.address != A && prev.out().type() != StateType::Released) {
- // The last node wasn't released, so it must be connected to a node outside of (A, B)
- // A - prev - B - (some node >= B)
- // It might be that prev.address == B == (some node >= B), this is fine.
- if (prev.out().type() == StateType::Reserved) {
- SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(prev.out().mem_tag())];
- rescom.reserve -= B - prev.address;
- } else if (prev.out().type() == StateType::Committed) {
- SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(prev.out().mem_tag())];
- rescom.commit -= B - prev.address;
- rescom.reserve -= B - prev.address;
+ switch(row) {
+ // row 0: .........A..................B.....
+ case 0: {
+ update_A(B);
+ upsert_if(A);
+ upsert_if(B);
+ break;
+ }
+ // row 1: .........A...YW.............B.....
+ case 1: {
+ ShouldNotReachHere();
+ break;
+ }
+ // row 2: .........A...Y..........W...B.....
+ case 2: {
+ update_A(Y);
+ upsert_if(A);
+ update_loop();
+ remove_if(Y);
+ update(W, B);
+ remove_if(W);
+ upsert_if(B);
+ break;
+ }
+ // row 3: .........A...Y.............WB.....
+ case 3: {
+ update_A(Y);
+ upsert_if(A);
+ update_loop();
+ remove_if(W);
+ break;
+ }
+ // row 4: .....X...A..................B.....
+ case 4: {
+ A->val().in = X->val().out;
+ update_A(B);
+ upsert_if(A);
+ upsert_if(B);
+ break;
}
+ // row 5: .....X...A...YW.............B.....
+ case 5: {
+ A->val().in = X->val().out;
+ update_A(Y);
+ upsert_if(A);
+ update(Y, B);
+ remove_if(Y);
+ upsert_if(B);
+ break;
+ }
+ // row 6: .....X...A...Y..........W...B.....
+ case 6: {
+ A->val().in = X->val().out;
+ update_A(Y);
+ upsert_if(A);
+ update_loop();
+ update(W, B);
+ remove_if(W);
+ upsert_if(B);
+ break;
+ }
+ // row 7: .....X...A...Y.............WB.....
+ case 7: {
+ A->val().in = X->val().out;
+ update_A(Y);
+ upsert_if(A);
+ update_loop();
+ remove_if(W);
+ break;
+ }
+ // row 8: ........XA..................B.....
+ case 8: {
+ update(X, B);
+ remove_if(X);
+ upsert_if(B);
+ break;
+ }
+ // row 9: ........XA...YW.............B.....
+ case 9: {
+ update(X, Y);
+ remove_if(X);
+ update(W, B);
+ remove_if(W);
+ upsert_if(B);
+ break;
+ }
+ // row 10: ........XA...Y..........W...B.....
+ case 10: {
+ update(X, Y);
+ remove_if(X);
+ update_loop();
+ update(W, B);
+ remove_if(W);
+ upsert_if(B);
+ break;
+ }
+ // row 11: ........XA...Y.............WB.....
+ case 11: {
+ update(X, Y);
+ remove_if(X);
+ update_loop();
+ remove_if(W);
+ break;
+ }
+ // row 12: .........A..................B....U
+ case 12: {
+ update_A(B);
+ upsert_if(A);
+ upsert_if(B);
+ break;
+ }
+ // row 13: .........A...YW.............B....U
+ case 13: {
+ update_A(Y);
+ upsert_if(A);
+ update(W, B);
+ remove_if(W);
+ B->val().out = U->val().in;
+ upsert_if(B);
+ break;
+ }
+ // row 14: .........A...Y..........W...B....U
+ case 14: {
+ update_A(Y);
+ upsert_if(A);
+ update_loop();
+ update(W, B);
+ remove_if(W);
+ B->val().out = U->val().in;
+ upsert_if(B);
+ break;
+ }
+ // row 15: .........A...Y.............WB....U
+ case 15: {
+ update_A(Y);
+ upsert_if(A);
+ update_loop();
+ remove_if(W);
+ break;
+ }
+ // row 16: .....X...A..................B....U
+ case 16: {
+ A->val().in = X->val().out;
+ update_A(B);
+ upsert_if(A);
+ B->val().out = U->val().in;
+ upsert_if(B);
+ break;
+ }
+ // row 17: .....X...A...YW.............B....U
+ case 17: {
+ A->val().in = X->val().out;
+ update_A(Y);
+ upsert_if(A);
+ update(W, B);
+ remove_if(W);
+ B->val().out = U->val().in;
+ upsert_if(B);
+ break;
+ }
+ // row 18: .....X...A...Y..........W...B....U
+ case 18: {
+ A->val().in = X->val().out;
+ update_A(Y);
+ upsert_if(A);
+ update_loop();
+ update(W, B);
+ remove_if(W);
+ B->val().out = U->val().in;
+ upsert_if(B);
+ break;
+ }
+ // row 19: .....X...A...Y.............WB....U
+ case 19: {
+ A->val().in = X->val().out;
+ update_A(Y);
+ upsert_if(A);
+ update_loop();
+ remove_if(W);
+ break;
+ }
+ // row 20: ........XA..................B....U
+ case 20: {
+ update(X, B);
+ remove_if(X);
+ B->val().out = U->val().in;
+ upsert_if(B);
+ break;
+ }
+ // row 21: ........XA...YW.............B....U
+ case 21: {
+ update(X, Y);
+ remove_if(X);
+ update(W, B);
+ remove_if(W);
+ B->val().out = U->val().in;
+ upsert_if(B);
+ break;
+ }
+ // row 22: ........XA...Y..........W...B....U
+ case 22: {
+ update(X, Y);
+ remove_if(X);
+ update_loop();
+ update(W, B);
+ remove_if(W);
+ B->val().out = U->val().in;
+ upsert_if(B);
+ break;
+ }
+ // row 23: ........XA...Y.............WB....U
+ case 23: {
+ update(X, Y);
+ remove_if(X);
+ update_loop();
+ remove_if(W);
+ break;
+ }
+ default:
+ ShouldNotReachHere();
}
- // Finally, we can register the new region [A, B)'s summary data.
- SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(stA.out.mem_tag())];
- if (state == StateType::Reserved) {
- rescom.reserve += B - A;
- } else if (state == StateType::Committed) {
- rescom.commit += B - A;
- rescom.reserve += B - A;
+ // Remove the 'noop' nodes that found inside the loop
+ while(to_be_removed.length() != 0) {
+ _tree.remove(to_be_removed.pop());
}
+
return diff;
}
#ifdef ASSERT
void VMATree::print_on(outputStream* out) {
visit_in_order([&](TreapNode* current) {
- out->print("%zu (%s) - %s - ", current->key(), NMTUtil::tag_to_name(out_state(current).mem_tag()),
- statetype_to_string(out_state(current).type()));
+ out->print("%zu (%s) - %s [%d, %d]-> ", current->key(), NMTUtil::tag_to_name(out_state(current).mem_tag()),
+ statetype_to_string(out_state(current).type()), current->val().out.reserved_stack(), current->val().out.committed_stack());
+ return true;
});
out->cr();
}
@@ -268,7 +701,7 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con
SummaryDiff diff;
// Ignore any released ranges, these must be mtNone and have no stack
if (type != StateType::Released) {
- RegionData new_data = RegionData(out.stack(), tag);
+ RegionData new_data = RegionData(out.reserved_stack(), tag);
SummaryDiff result = register_mapping(from, end, type, new_data);
diff.add(result);
}
@@ -289,7 +722,7 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con
StateType type = out.type();
if (type != StateType::Released) {
- RegionData new_data = RegionData(out.stack(), tag);
+ RegionData new_data = RegionData(out.reserved_stack(), tag);
SummaryDiff result = register_mapping(from, end, type, new_data);
diff.add(result);
}
diff --git a/src/hotspot/share/nmt/vmatree.hpp b/src/hotspot/share/nmt/vmatree.hpp
index 0c639e929b7..7f52b406309 100644
--- a/src/hotspot/share/nmt/vmatree.hpp
+++ b/src/hotspot/share/nmt/vmatree.hpp
@@ -26,6 +26,7 @@
#ifndef SHARE_NMT_VMATREE_HPP
#define SHARE_NMT_VMATREE_HPP
+#include "nmt/memTag.hpp"
#include "nmt/memTag.hpp"
#include "nmt/nmtNativeCallStackStorage.hpp"
#include "nmt/nmtTreap.hpp"
@@ -40,10 +41,12 @@
// The set of points is stored in a balanced binary tree for efficient querying and updating.
class VMATree {
friend class NMTVMATreeTest;
+ friend class VMTWithVMATreeTest;
// A position in memory.
public:
using position = size_t;
using size = size_t;
+ using SIndex = NativeCallStackStorage::StackIndex;
class PositionComparator {
public:
@@ -55,27 +58,29 @@ class VMATree {
}
};
- enum class StateType : uint8_t { Reserved, Committed, Released, LAST };
+ // Bit fields view: bit 0 for Reserved, bit 1 for Committed.
+ // Setting a region as Committed preserves the Reserved state.
+ enum class StateType : uint8_t { Reserved = 1, Committed = 3, Released = 0, st_number_of_states = 4 };
private:
- static const char* statetype_strings[static_cast(StateType::LAST)];
+ static const char* statetype_strings[static_cast(StateType::st_number_of_states)];
public:
NONCOPYABLE(VMATree);
static const char* statetype_to_string(StateType type) {
- assert(type != StateType::LAST, "must be");
+ assert(type < StateType::st_number_of_states, "must be");
return statetype_strings[static_cast(type)];
}
// Each point has some stack and a tag associated with it.
struct RegionData {
- const NativeCallStackStorage::StackIndex stack_idx;
+ const SIndex stack_idx;
const MemTag mem_tag;
RegionData() : stack_idx(), mem_tag(mtNone) {}
- RegionData(NativeCallStackStorage::StackIndex stack_idx, MemTag mem_tag)
+ RegionData(SIndex stack_idx, MemTag mem_tag)
: stack_idx(stack_idx), mem_tag(mem_tag) {}
static bool equals(const RegionData& a, const RegionData& b) {
@@ -91,15 +96,27 @@ class VMATree {
private:
// Store the type and mem_tag as two bytes
uint8_t type_tag[2];
- NativeCallStackStorage::StackIndex sidx;
+ NativeCallStackStorage::StackIndex _reserved_stack;
+ NativeCallStackStorage::StackIndex _committed_stack;
public:
- IntervalState() : type_tag{0,0}, sidx() {}
+ IntervalState() : type_tag{0,0}, _reserved_stack(NativeCallStackStorage::invalid), _committed_stack(NativeCallStackStorage::invalid) {}
+ IntervalState(const StateType type,
+ const MemTag mt,
+ const NativeCallStackStorage::StackIndex res_stack,
+ const NativeCallStackStorage::StackIndex com_stack) {
+ assert(!(type == StateType::Released) || mt == mtNone, "Released state-type must have memory tag mtNone");
+ type_tag[0] = static_cast(type);
+ type_tag[1] = static_cast(mt);
+ _reserved_stack = res_stack;
+ _committed_stack = com_stack;
+ }
IntervalState(const StateType type, const RegionData data) {
assert(!(type == StateType::Released) || data.mem_tag == mtNone, "Released state-type must have memory tag mtNone");
type_tag[0] = static_cast(type);
type_tag[1] = static_cast(data.mem_tag);
- sidx = data.stack_idx;
+ _reserved_stack = data.stack_idx;
+ _committed_stack = NativeCallStackStorage::invalid;
}
StateType type() const {
@@ -110,16 +127,50 @@ class VMATree {
return static_cast(type_tag[1]);
}
- RegionData regiondata() const {
- return RegionData{sidx, mem_tag()};
+ RegionData reserved_regiondata() const {
+ return RegionData{_reserved_stack, mem_tag()};
+ }
+ RegionData committed_regiondata() const {
+ return RegionData{_committed_stack, mem_tag()};
}
void set_tag(MemTag tag) {
type_tag[1] = static_cast(tag);
}
- NativeCallStackStorage::StackIndex stack() const {
- return sidx;
+ NativeCallStackStorage::StackIndex reserved_stack() const {
+ return _reserved_stack;
+ }
+
+ NativeCallStackStorage::StackIndex committed_stack() const {
+ return _committed_stack;
+ }
+
+ void set_reserve_stack(NativeCallStackStorage::StackIndex idx) {
+ _reserved_stack = idx;
+ }
+
+ void set_commit_stack(NativeCallStackStorage::StackIndex idx) {
+ _committed_stack = idx;
+ }
+
+ bool has_reserved_stack() {
+ return _reserved_stack != NativeCallStackStorage::invalid;
+ }
+
+ bool has_committed_stack() {
+ return _committed_stack != NativeCallStackStorage::invalid;
+ }
+
+ void set_type(StateType t) {
+ type_tag[0] = static_cast(t);
+ }
+
+ bool equals(const IntervalState& other) const {
+ return mem_tag() == other.mem_tag() &&
+ type() == other.type() &&
+ reserved_stack() == other.reserved_stack() &&
+ committed_stack() == other.committed_stack();
}
};
@@ -130,8 +181,14 @@ class VMATree {
IntervalState out;
bool is_noop() {
+ if (in.type() == StateType::Released &&
+ in.type() == out.type() &&
+ in.mem_tag() == out.mem_tag()) {
+ return true;
+ }
return in.type() == out.type() &&
- RegionData::equals(in.regiondata(), out.regiondata());
+ RegionData::equals(in.reserved_regiondata(), out.reserved_regiondata()) &&
+ RegionData::equals(in.committed_regiondata(), out.committed_regiondata());
}
};
@@ -193,8 +250,44 @@ class VMATree {
#endif
};
+ enum Operation {Release, Reserve, Commit, Uncommit};
+ struct RequestInfo {
+ position A, B;
+ StateType _op;
+ MemTag tag;
+ SIndex callstack;
+ bool use_tag_inplace;
+ Operation op() const {
+ return
+ _op == StateType::Reserved && !use_tag_inplace ? Operation::Reserve :
+ _op == StateType::Committed ? Operation::Commit :
+ _op == StateType::Reserved && use_tag_inplace ? Operation::Uncommit :
+ Operation::Release;
+ }
+
+ int op_to_index() const {
+ return
+ _op == StateType::Reserved && !use_tag_inplace ? 1 :
+ _op == StateType::Committed ? 2 :
+ _op == StateType::Reserved && use_tag_inplace ? 3 :
+ 0;
+ }
+ };
+
private:
SummaryDiff register_mapping(position A, position B, StateType state, const RegionData& metadata, bool use_tag_inplace = false);
+ StateType get_new_state(const StateType existinting_state, const RequestInfo& req) const;
+ MemTag get_new_tag(const MemTag existinting_tag, const RequestInfo& req) const;
+ SIndex get_new_reserve_callstack(const SIndex existinting_stack, const StateType ex, const RequestInfo& req) const;
+ SIndex get_new_commit_callstack(const SIndex existinting_stack, const StateType ex, const RequestInfo& req) const;
+ void compute_summary_diff(const SingleDiff::delta region_size, const MemTag t1, const StateType& ex, const RequestInfo& req, const MemTag new_tag, SummaryDiff& diff) const;
+ void update_region(TreapNode* n1, TreapNode* n2, const RequestInfo& req, SummaryDiff& diff);
+ int state_to_index(const StateType st) const {
+ return
+ st == StateType::Released ? 0 :
+ st == StateType::Reserved ? 1 :
+ st == StateType::Committed ? 2 : -1;
+ }
public:
SummaryDiff reserve_mapping(position from, size size, const RegionData& metadata) {
@@ -215,12 +308,8 @@ class VMATree {
return register_mapping(from, from + size, StateType::Reserved, metadata, true);
}
- SummaryDiff release_mapping(position from, size size) {
- return register_mapping(from, from + size, StateType::Released, VMATree::empty_regiondata);
- }
-
- VMATreap& tree() {
- return _tree;
+ SummaryDiff release_mapping(position from, position sz) {
+ return register_mapping(from, from + sz, StateType::Released, VMATree::empty_regiondata);
}
public:
@@ -232,7 +321,10 @@ class VMATree {
#ifdef ASSERT
void print_on(outputStream* out);
#endif
-
+ template
+ void visit_range_in_order(const position& from, const position& to, F f) {
+ _tree.visit_range_in_order(from, to, f);
+ }
+ VMATreap& tree() { return _tree; }
};
-
#endif
diff --git a/src/hotspot/share/oops/compressedKlass.hpp b/src/hotspot/share/oops/compressedKlass.hpp
index 8666d4409e8..a8d79ab838e 100644
--- a/src/hotspot/share/oops/compressedKlass.hpp
+++ b/src/hotspot/share/oops/compressedKlass.hpp
@@ -214,7 +214,7 @@ class CompressedKlassPointers : public AllStatic {
// Can only be used after initialization
static address base() { check_init(_base); return _base; }
- static address base_addr() { return (address)&_base; }
+ static address base_addr() { return (address)&_base; }
static int shift() { check_init(_shift); return _shift; }
static address klass_range_start() { return _klass_range_start; }
diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp
index 5b0ee298ddc..3223c56628e 100644
--- a/src/hotspot/share/oops/constantPool.cpp
+++ b/src/hotspot/share/oops/constantPool.cpp
@@ -1937,18 +1937,20 @@ int ConstantPool::find_matching_entry(int pattern_i,
// Compare this constant pool's bootstrap specifier at idx1 to the constant pool
// cp2's bootstrap specifier at idx2.
bool ConstantPool::compare_operand_to(int idx1, const constantPoolHandle& cp2, int idx2) {
- int k1 = operand_bootstrap_method_ref_index_at(idx1);
- int k2 = cp2->operand_bootstrap_method_ref_index_at(idx2);
+ BSMAttributeEntry* e1 = bsm_attribute_entry(idx1);
+ BSMAttributeEntry* e2 = cp2->bsm_attribute_entry(idx2);
+ int k1 = e1->bootstrap_method_index();
+ int k2 = e2->bootstrap_method_index();
bool match = compare_entry_to(k1, cp2, k2);
if (!match) {
return false;
}
- int argc = operand_argument_count_at(idx1);
- if (argc == cp2->operand_argument_count_at(idx2)) {
+ int argc = e1->argument_count();
+ if (argc == e2->argument_count()) {
for (int j = 0; j < argc; j++) {
- k1 = operand_argument_index_at(idx1, j);
- k2 = cp2->operand_argument_index_at(idx2, j);
+ k1 = e1->argument_index(j);
+ k2 = e2->argument_index(j);
match = compare_entry_to(k1, cp2, k2);
if (!match) {
return false;
diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp
index cc9491d7935..be4a7a474d4 100644
--- a/src/hotspot/share/oops/constantPool.hpp
+++ b/src/hotspot/share/oops/constantPool.hpp
@@ -77,6 +77,43 @@ class CPKlassSlot {
}
};
+class BSMAttributeEntry {
+ friend class ConstantPool;
+ u2 _bootstrap_method_index;
+ u2 _argument_count;
+
+ // The argument indexes are stored right after the object, in a contiguous array.
+ // [ bsmi_0 argc_0 arg_00 arg_01 ... arg_0N bsmi_1 argc_1 arg_10 ... arg_1N ... ]
+ // So in order to find the argument array, jump over ourselves.
+ const u2* argument_indexes() const {
+ return reinterpret_cast(this + 1);
+ }
+ u2* argument_indexes() {
+ return reinterpret_cast(this + 1);
+ }
+ // These are overlays on top of the operands array. Do not construct.
+ BSMAttributeEntry() = delete;
+
+public:
+ // Offsets for SA
+ enum {
+ _bsmi_offset = 0,
+ _argc_offset = 1,
+ _argv_offset = 2
+ };
+
+ int bootstrap_method_index() const {
+ return _bootstrap_method_index;
+ }
+ int argument_count() const {
+ return _argument_count;
+ }
+ int argument_index(int n) const {
+ assert(checked_cast(n) < _argument_count, "oob");
+ return argument_indexes()[n];
+ }
+};
+
class ConstantPool : public Metadata {
friend class VMStructs;
friend class JVMCIVMStructs;
@@ -519,10 +556,6 @@ class ConstantPool : public Metadata {
assert(tag_at(cp_index).has_bootstrap(), "Corrupted constant pool");
return extract_low_short_from_int(*int_at_addr(cp_index));
}
- int bootstrap_operand_base(int cp_index) {
- int bsms_attribute_index = bootstrap_methods_attribute_index(cp_index);
- return operand_offset_at(operands(), bsms_attribute_index);
- }
// The first part of the operands array consists of an index into the second part.
// Extract a 32-bit index value from the first part.
static int operand_offset_at(Array* operands, int bsms_attribute_index) {
@@ -560,47 +593,26 @@ class ConstantPool : public Metadata {
else
return operand_offset_at(operands, nextidx);
}
- int bootstrap_operand_limit(int cp_index) {
- int bsms_attribute_index = bootstrap_methods_attribute_index(cp_index);
- return operand_limit_at(operands(), bsms_attribute_index);
- }
#endif //ASSERT
- // Layout of InvokeDynamic and Dynamic bootstrap method specifier
- // data in second part of operands array. This encodes one record in
- // the BootstrapMethods attribute. The whole specifier also includes
- // the name and type information from the main constant pool entry.
- enum {
- _indy_bsm_offset = 0, // CONSTANT_MethodHandle bsm
- _indy_argc_offset = 1, // u2 argc
- _indy_argv_offset = 2 // u2 argv[argc]
- };
-
// These functions are used in RedefineClasses for CP merge
-
int operand_offset_at(int bsms_attribute_index) {
assert(0 <= bsms_attribute_index &&
bsms_attribute_index < operand_array_length(operands()),
"Corrupted CP operands");
return operand_offset_at(operands(), bsms_attribute_index);
}
- u2 operand_bootstrap_method_ref_index_at(int bsms_attribute_index) {
- int offset = operand_offset_at(bsms_attribute_index);
- return operands()->at(offset + _indy_bsm_offset);
- }
- u2 operand_argument_count_at(int bsms_attribute_index) {
- int offset = operand_offset_at(bsms_attribute_index);
- u2 argc = operands()->at(offset + _indy_argc_offset);
- return argc;
- }
- u2 operand_argument_index_at(int bsms_attribute_index, int j) {
+
+ BSMAttributeEntry* bsm_attribute_entry(int bsms_attribute_index) {
int offset = operand_offset_at(bsms_attribute_index);
- return operands()->at(offset + _indy_argv_offset + j);
+ return reinterpret_cast(operands()->adr_at(offset));
}
+
int operand_next_offset_at(int bsms_attribute_index) {
- int offset = operand_offset_at(bsms_attribute_index) + _indy_argv_offset
- + operand_argument_count_at(bsms_attribute_index);
- return offset;
+ BSMAttributeEntry* bsme = bsm_attribute_entry(bsms_attribute_index);
+ u2* argv_start = bsme->argument_indexes();
+ int offset = argv_start - operands()->data();
+ return offset + bsme->argument_count();
}
// Compare a bootstrap specifier data in the operands arrays
bool compare_operand_to(int bsms_attribute_index1, const constantPoolHandle& cp2,
@@ -617,23 +629,19 @@ class ConstantPool : public Metadata {
u2 bootstrap_method_ref_index_at(int cp_index) {
assert(tag_at(cp_index).has_bootstrap(), "Corrupted constant pool");
- int op_base = bootstrap_operand_base(cp_index);
- return operands()->at(op_base + _indy_bsm_offset);
+ int bsmai = bootstrap_methods_attribute_index(cp_index);
+ return bsm_attribute_entry(bsmai)->bootstrap_method_index();
}
u2 bootstrap_argument_count_at(int cp_index) {
assert(tag_at(cp_index).has_bootstrap(), "Corrupted constant pool");
- int op_base = bootstrap_operand_base(cp_index);
- u2 argc = operands()->at(op_base + _indy_argc_offset);
- DEBUG_ONLY(int end_offset = op_base + _indy_argv_offset + argc;
- int next_offset = bootstrap_operand_limit(cp_index));
- assert(end_offset == next_offset, "matched ending");
- return argc;
+ int bsmai = bootstrap_methods_attribute_index(cp_index);
+ return bsm_attribute_entry(bsmai)->argument_count();
}
u2 bootstrap_argument_index_at(int cp_index, int j) {
- int op_base = bootstrap_operand_base(cp_index);
- DEBUG_ONLY(int argc = operands()->at(op_base + _indy_argc_offset));
- assert((uint)j < (uint)argc, "oob");
- return operands()->at(op_base + _indy_argv_offset + j);
+ int bsmai = bootstrap_methods_attribute_index(cp_index);
+ BSMAttributeEntry* bsme = bsm_attribute_entry(bsmai);
+ assert((uint)j < (uint)bsme->argument_count(), "oob");
+ return bsm_attribute_entry(bsmai)->argument_index(j);
}
// The following methods (name/signature/klass_ref_at, klass_ref_at_noresolve,
diff --git a/src/hotspot/share/oops/fieldInfo.cpp b/src/hotspot/share/oops/fieldInfo.cpp
index 300b45277ad..d0825ba6df8 100644
--- a/src/hotspot/share/oops/fieldInfo.cpp
+++ b/src/hotspot/share/oops/fieldInfo.cpp
@@ -22,8 +22,11 @@
*
*/
+#include "memory/resourceArea.hpp"
+#include "cds/cdsConfig.hpp"
#include "oops/fieldInfo.inline.hpp"
#include "runtime/atomic.hpp"
+#include "utilities/packedTable.hpp"
void FieldInfo::print(outputStream* os, ConstantPool* cp) {
os->print_cr("index=%d name_index=%d name=%s signature_index=%d signature=%s offset=%d "
@@ -37,8 +40,10 @@ void FieldInfo::print(outputStream* os, ConstantPool* cp) {
field_flags().as_uint(),
initializer_index(),
generic_signature_index(),
- _field_flags.is_injected() ? lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8(),
- contended_group());
+ _field_flags.is_generic() ? (_field_flags.is_injected() ?
+ lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8()
+ ) : "",
+ is_contended() ? contended_group() : 0);
}
void FieldInfo::print_from_growable_array(outputStream* os, GrowableArray* array, ConstantPool* cp) {
@@ -62,13 +67,17 @@ Array* FieldInfoStream::create_FieldInfoStream(GrowableArray* fie
StreamSizer s;
StreamFieldSizer sizer(&s);
+ assert(fields->length() == java_fields + injected_fields, "must be");
+
sizer.consumer()->accept_uint(java_fields);
sizer.consumer()->accept_uint(injected_fields);
for (int i = 0; i < fields->length(); i++) {
FieldInfo* fi = fields->adr_at(i);
sizer.map_field_info(*fi);
}
- int storage_size = sizer.consumer()->position() + 1;
+ // Originally there was an extra byte with 0 terminating the reading;
+ // now we check limits instead.
+ int storage_size = sizer.consumer()->position();
Array* const fis = MetadataFactory::new_array(loader_data, storage_size, CHECK_NULL);
using StreamWriter = UNSIGNED5::Writer*, int, ArrayHelper*, int>>;
@@ -79,15 +88,14 @@ Array* FieldInfoStream::create_FieldInfoStream(GrowableArray* fie
writer.consumer()->accept_uint(java_fields);
writer.consumer()->accept_uint(injected_fields);
for (int i = 0; i < fields->length(); i++) {
- FieldInfo* fi = fields->adr_at(i);
- writer.map_field_info(*fi);
+ writer.map_field_info(fields->at(i));
}
#ifdef ASSERT
FieldInfoReader r(fis);
- int jfc = r.next_uint();
+ int jfc, ifc;
+ r.read_field_counts(&jfc, &ifc);
assert(jfc == java_fields, "Must be");
- int ifc = r.next_uint();
assert(ifc == injected_fields, "Must be");
for (int i = 0; i < jfc + ifc; i++) {
FieldInfo fi;
@@ -113,30 +121,221 @@ Array* FieldInfoStream::create_FieldInfoStream(GrowableArray* fie
return fis;
}
+int FieldInfoStream::compare_name_and_sig(const Symbol* n1, const Symbol* s1, const Symbol* n2, const Symbol* s2) {
+ int cmp = n1->fast_compare(n2);
+ return cmp != 0 ? cmp : s1->fast_compare(s2);
+}
+
+
+// We use both name and signature during the comparison; while JLS require unique
+// names for fields, JVMS requires only unique name + signature combination.
+struct field_pos {
+ Symbol* _name;
+ Symbol* _signature;
+ int _index;
+ int _position;
+};
+
+class FieldInfoSupplier: public PackedTableBuilder::Supplier {
+ const field_pos* _positions;
+ size_t _elements;
+
+public:
+ FieldInfoSupplier(const field_pos* positions, size_t elements): _positions(positions), _elements(elements) {}
+
+ bool next(uint32_t* key, uint32_t* value) override {
+ if (_elements == 0) {
+ return false;
+ }
+ *key = _positions->_position;
+ *value = _positions->_index;
+ ++_positions;
+ --_elements;
+ return true;
+ }
+};
+
+Array* FieldInfoStream::create_search_table(ConstantPool* cp, const Array* fis, ClassLoaderData* loader_data, TRAPS) {
+ if (CDSConfig::is_dumping_dynamic_archive()) {
+ // We cannot use search table; in case of dynamic archives it should be sorted by "requested" addresses,
+ // but Symbol* addresses are coming from _constants, which has "buffered" addresses.
+ // For background, see new comments inside allocate_node_impl in symbolTable.cpp
+ return nullptr;
+ }
+
+ FieldInfoReader r(fis);
+ int java_fields;
+ int injected_fields;
+ r.read_field_counts(&java_fields, &injected_fields);
+ assert(java_fields >= 0, "must be");
+ if (java_fields == 0 || fis->length() == 0 || static_cast(java_fields) < BinarySearchThreshold) {
+ return nullptr;
+ }
+
+ ResourceMark rm;
+ field_pos* positions = NEW_RESOURCE_ARRAY(field_pos, java_fields);
+ for (int i = 0; i < java_fields; ++i) {
+ assert(r.has_next(), "number of fields must match");
+
+ positions[i]._position = r.position();
+ FieldInfo fi;
+ r.read_field_info(fi);
+
+ positions[i]._name = fi.name(cp);
+ positions[i]._signature = fi.signature(cp);
+ positions[i]._index = i;
+ }
+ auto compare_pair = [](const void* v1, const void* v2) {
+ const field_pos* p1 = reinterpret_cast(v1);
+ const field_pos* p2 = reinterpret_cast(v2);
+ return compare_name_and_sig(p1->_name, p1->_signature, p2->_name, p2->_signature);
+ };
+ qsort(positions, java_fields, sizeof(field_pos), compare_pair);
+
+ PackedTableBuilder builder(fis->length() - 1, java_fields - 1);
+ Array* table = MetadataFactory::new_array(loader_data, java_fields * builder.element_bytes(), CHECK_NULL);
+ FieldInfoSupplier supplier(positions, java_fields);
+ builder.fill(table->data(), static_cast(table->length()), supplier);
+ return table;
+}
+
GrowableArray* FieldInfoStream::create_FieldInfoArray(const Array* fis, int* java_fields_count, int* injected_fields_count) {
- int length = FieldInfoStream::num_total_fields(fis);
- GrowableArray* array = new GrowableArray(length);
FieldInfoReader r(fis);
- *java_fields_count = r.next_uint();
- *injected_fields_count = r.next_uint();
+ r.read_field_counts(java_fields_count, injected_fields_count);
+ int length = *java_fields_count + *injected_fields_count;
+
+ GrowableArray* array = new GrowableArray(length);
while (r.has_next()) {
FieldInfo fi;
r.read_field_info(fi);
array->append(fi);
}
assert(array->length() == length, "Must be");
- assert(array->length() == *java_fields_count + *injected_fields_count, "Must be");
return array;
}
void FieldInfoStream::print_from_fieldinfo_stream(Array* fis, outputStream* os, ConstantPool* cp) {
- int length = FieldInfoStream::num_total_fields(fis);
FieldInfoReader r(fis);
- int java_field_count = r.next_uint();
- int injected_fields_count = r.next_uint();
+ int java_fields_count;
+ int injected_fields_count;
+ r.read_field_counts(&java_fields_count, &injected_fields_count);
while (r.has_next()) {
FieldInfo fi;
r.read_field_info(fi);
fi.print(os, cp);
}
}
+
+class FieldInfoComparator: public PackedTableLookup::Comparator {
+ const FieldInfoReader* _reader;
+ ConstantPool* _cp;
+ const Symbol* _name;
+ const Symbol* _signature;
+
+public:
+ FieldInfoComparator(const FieldInfoReader* reader, ConstantPool* cp, const Symbol* name, const Symbol* signature):
+ _reader(reader), _cp(cp), _name(name), _signature(signature) {}
+
+ int compare_to(uint32_t position) override {
+ FieldInfoReader r2(*_reader);
+ r2.set_position_and_next_index(position, -1);
+ u2 name_index, sig_index;
+ r2.read_name_and_signature(&name_index, &sig_index);
+ Symbol* mid_name = _cp->symbol_at(name_index);
+ Symbol* mid_sig = _cp->symbol_at(sig_index);
+
+ return FieldInfoStream::compare_name_and_sig(_name, _signature, mid_name, mid_sig);
+ }
+
+#ifdef ASSERT
+ void reset(uint32_t position) override {
+ FieldInfoReader r2(*_reader);
+ r2.set_position_and_next_index(position, -1);
+ u2 name_index, signature_index;
+ r2.read_name_and_signature(&name_index, &signature_index);
+ _name = _cp->symbol_at(name_index);
+ _signature = _cp->symbol_at(signature_index);
+ }
+#endif // ASSERT
+};
+
+#ifdef ASSERT
+void FieldInfoStream::validate_search_table(ConstantPool* cp, const Array* fis, const Array* search_table) {
+ if (search_table == nullptr) {
+ return;
+ }
+ FieldInfoReader reader(fis);
+ int java_fields, injected_fields;
+ reader.read_field_counts(&java_fields, &injected_fields);
+ assert(java_fields > 0, "must be");
+
+ PackedTableLookup lookup(fis->length() - 1, java_fields - 1, search_table);
+ assert(lookup.element_bytes() * java_fields == static_cast(search_table->length()), "size does not match");
+
+ FieldInfoComparator comparator(&reader, cp, nullptr, nullptr);
+ // Check 1: assert that elements have the correct order based on the comparison function
+ lookup.validate_order(comparator);
+
+ // Check 2: Iterate through the original stream (not just search_table) and try if lookup works as expected
+ reader.set_position_and_next_index(0, 0);
+ reader.read_field_counts(&java_fields, &injected_fields);
+ while (reader.has_next()) {
+ int field_start = reader.position();
+ FieldInfo fi;
+ reader.read_field_info(fi);
+ if (fi.field_flags().is_injected()) {
+ // checking only java fields that precede injected ones
+ break;
+ }
+
+ FieldInfoReader r2(fis);
+ int index = r2.search_table_lookup(search_table, fi.name(cp), fi.signature(cp), cp, java_fields);
+ assert(index == static_cast(fi.index()), "wrong index: %d != %u", index, fi.index());
+ assert(index == r2.next_index(), "index should match");
+ assert(field_start == r2.position(), "must find the same position");
+ }
+}
+#endif // ASSERT
+
+void FieldInfoStream::print_search_table(outputStream* st, ConstantPool* cp, const Array* fis, const Array* search_table) {
+ if (search_table == nullptr) {
+ return;
+ }
+ FieldInfoReader reader(fis);
+ int java_fields, injected_fields;
+ reader.read_field_counts(&java_fields, &injected_fields);
+ assert(java_fields > 0, "must be");
+ PackedTableLookup lookup(fis->length() - 1, java_fields - 1, search_table);
+ auto printer = [&] (size_t offset, uint32_t position, uint32_t index) {
+ reader.set_position_and_next_index(position, -1);
+ u2 name_index, sig_index;
+ reader.read_name_and_signature(&name_index, &sig_index);
+ Symbol* name = cp->symbol_at(name_index);
+ Symbol* sig = cp->symbol_at(sig_index);
+ st->print(" [%zu] #%d,#%d = ", offset, name_index, sig_index);
+ name->print_symbol_on(st);
+ st->print(":");
+ sig->print_symbol_on(st);
+ st->print(" @ %p,%p", name, sig);
+ st->cr();
+ };
+
+ lookup.iterate(printer);
+}
+
+int FieldInfoReader::search_table_lookup(const Array* search_table, const Symbol* name, const Symbol* signature, ConstantPool* cp, int java_fields) {
+ assert(java_fields >= 0, "must be");
+ if (java_fields == 0) {
+ return -1;
+ }
+ FieldInfoComparator comp(this, cp, name, signature);
+ PackedTableLookup lookup(_r.limit() - 1, java_fields - 1, search_table);
+ uint32_t position;
+ static_assert(sizeof(uint32_t) == sizeof(_next_index), "field size assert");
+ if (lookup.search(comp, &position, reinterpret_cast(&_next_index))) {
+ _r.set_position(static_cast(position));
+ return _next_index;
+ } else {
+ return -1;
+ }
+}
diff --git a/src/hotspot/share/oops/fieldInfo.hpp b/src/hotspot/share/oops/fieldInfo.hpp
index 8740d539c8f..a98895da9cf 100644
--- a/src/hotspot/share/oops/fieldInfo.hpp
+++ b/src/hotspot/share/oops/fieldInfo.hpp
@@ -222,29 +222,28 @@ class Mapper {
void map_field_info(const FieldInfo& fi);
};
-
// Gadget for decoding and reading the stream of field records.
class FieldInfoReader {
- friend class FieldInfoStream;
- friend class ClassFileParser;
- friend class FieldStreamBase;
- friend class FieldInfo;
-
UNSIGNED5::Reader _r;
int _next_index;
- public:
+public:
FieldInfoReader(const Array* fi);
- private:
- uint32_t next_uint() { return _r.next_uint(); }
+private:
+ inline uint32_t next_uint() { return _r.next_uint(); }
void skip(int n) { int s = _r.try_skip(n); assert(s == n,""); }
public:
- int has_next() { return _r.has_next(); }
- int position() { return _r.position(); }
- int next_index() { return _next_index; }
+ void read_field_counts(int* java_fields, int* injected_fields);
+ int has_next() const { return _r.position() < _r.limit(); }
+ int position() const { return _r.position(); }
+ int next_index() const { return _next_index; }
+ void read_name_and_signature(u2* name_index, u2* signature_index);
void read_field_info(FieldInfo& fi);
+
+ int search_table_lookup(const Array* search_table, const Symbol* name, const Symbol* signature, ConstantPool* cp, int java_fields);
+
// skip a whole field record, both required and optional bits
FieldInfoReader& skip_field_info();
@@ -271,6 +270,11 @@ class FieldInfoStream : AllStatic {
friend class JavaFieldStream;
friend class FieldStreamBase;
friend class ClassFileParser;
+ friend class FieldInfoReader;
+ friend class FieldInfoComparator;
+
+ private:
+ static int compare_name_and_sig(const Symbol* n1, const Symbol* s1, const Symbol* n2, const Symbol* s2);
public:
static int num_java_fields(const Array* fis);
@@ -278,9 +282,14 @@ class FieldInfoStream : AllStatic {
static int num_total_fields(const Array* fis);
static Array* create_FieldInfoStream(GrowableArray* fields, int java_fields, int injected_fields,
- ClassLoaderData* loader_data, TRAPS);
+ ClassLoaderData* loader_data, TRAPS);
+ static Array* create_search_table(ConstantPool* cp, const Array* fis, ClassLoaderData* loader_data, TRAPS);
static GrowableArray* create_FieldInfoArray(const Array* fis, int* java_fields_count, int* injected_fields_count);
static void print_from_fieldinfo_stream(Array* fis, outputStream* os, ConstantPool* cp);
+
+ DEBUG_ONLY(static void validate_search_table(ConstantPool* cp, const Array* fis, const Array* search_table);)
+
+ static void print_search_table(outputStream* st, ConstantPool* cp, const Array* fis, const Array* search_table);
};
class FieldStatus {
diff --git a/src/hotspot/share/oops/fieldInfo.inline.hpp b/src/hotspot/share/oops/fieldInfo.inline.hpp
index d3d4d765081..842393729b2 100644
--- a/src/hotspot/share/oops/fieldInfo.inline.hpp
+++ b/src/hotspot/share/oops/fieldInfo.inline.hpp
@@ -56,16 +56,27 @@ inline Symbol* FieldInfo::lookup_symbol(int symbol_index) const {
inline int FieldInfoStream::num_injected_java_fields(const Array* fis) {
FieldInfoReader fir(fis);
- fir.skip(1);
- return fir.next_uint();
+ int java_fields_count;
+ int injected_fields_count;
+ fir.read_field_counts(&java_fields_count, &injected_fields_count);
+ return injected_fields_count;
}
inline int FieldInfoStream::num_total_fields(const Array* fis) {
FieldInfoReader fir(fis);
- return fir.next_uint() + fir.next_uint();
+ int java_fields_count;
+ int injected_fields_count;
+ fir.read_field_counts(&java_fields_count, &injected_fields_count);
+ return java_fields_count + injected_fields_count;
}
-inline int FieldInfoStream::num_java_fields(const Array* fis) { return FieldInfoReader(fis).next_uint(); }
+inline int FieldInfoStream::num_java_fields(const Array* fis) {
+ FieldInfoReader fir(fis);
+ int java_fields_count;
+ int injected_fields_count;
+ fir.read_field_counts(&java_fields_count, &injected_fields_count);
+ return java_fields_count;
+}
template
inline void Mapper::map_field_info(const FieldInfo& fi) {
@@ -94,13 +105,22 @@ inline void Mapper::map_field_info(const FieldInfo& fi) {
inline FieldInfoReader::FieldInfoReader(const Array* fi)
- : _r(fi->data(), 0),
+ : _r(fi->data(), fi->length()),
_next_index(0) { }
+inline void FieldInfoReader::read_field_counts(int* java_fields, int* injected_fields) {
+ *java_fields = next_uint();
+ *injected_fields = next_uint();
+}
+
+inline void FieldInfoReader::read_name_and_signature(u2* name_index, u2* signature_index) {
+ *name_index = checked_cast(next_uint());
+ *signature_index = checked_cast(next_uint());
+}
+
inline void FieldInfoReader::read_field_info(FieldInfo& fi) {
fi._index = _next_index++;
- fi._name_index = checked_cast(next_uint());
- fi._signature_index = checked_cast(next_uint());
+ read_name_and_signature(&fi._name_index, &fi._signature_index);
fi._offset = next_uint();
fi._access_flags = AccessFlags(checked_cast(next_uint()));
fi._field_flags = FieldInfo::FieldFlags(next_uint());
diff --git a/src/hotspot/share/oops/fieldStreams.hpp b/src/hotspot/share/oops/fieldStreams.hpp
index a1c5d77eeb6..0ae828d73d9 100644
--- a/src/hotspot/share/oops/fieldStreams.hpp
+++ b/src/hotspot/share/oops/fieldStreams.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -56,17 +56,23 @@ class FieldStreamBase : public StackObj {
inline FieldStreamBase(const Array* fieldinfo_stream, ConstantPool* constants, int start, int limit);
- inline FieldStreamBase(Array* fieldinfo_stream, ConstantPool* constants);
+ inline FieldStreamBase(const Array* fieldinfo_stream, ConstantPool* constants);
- private:
+ private:
void initialize() {
- int java_fields_count = _reader.next_uint();
- int injected_fields_count = _reader.next_uint();
- assert( _limit <= java_fields_count + injected_fields_count, "Safety check");
+ int java_fields_count;
+ int injected_fields_count;
+ _reader.read_field_counts(&java_fields_count, &injected_fields_count);
+ if (_limit < _index) {
+ _limit = java_fields_count + injected_fields_count;
+ } else {
+ assert( _limit <= java_fields_count + injected_fields_count, "Safety check");
+ }
if (_limit != 0) {
_reader.read_field_info(_fi_buf);
}
}
+
public:
inline FieldStreamBase(InstanceKlass* klass);
@@ -138,8 +144,11 @@ class FieldStreamBase : public StackObj {
// Iterate over only the Java fields
class JavaFieldStream : public FieldStreamBase {
+ Array* _search_table;
+
public:
- JavaFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants(), 0, k->java_fields_count()) {}
+ JavaFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants(), 0, k->java_fields_count()),
+ _search_table(k->fieldinfo_search_table()) {}
u2 name_index() const {
assert(!field()->field_flags().is_injected(), "regular only");
@@ -149,7 +158,6 @@ class JavaFieldStream : public FieldStreamBase {
u2 signature_index() const {
assert(!field()->field_flags().is_injected(), "regular only");
return field()->signature_index();
- return -1;
}
u2 generic_signature_index() const {
@@ -164,6 +172,10 @@ class JavaFieldStream : public FieldStreamBase {
assert(!field()->field_flags().is_injected(), "regular only");
return field()->initializer_index();
}
+
+ // Performs either a linear search or binary search through the stream
+ // looking for a matching name/signature combo
+ bool lookup(const Symbol* name, const Symbol* signature);
};
@@ -176,7 +188,6 @@ class InternalFieldStream : public FieldStreamBase {
class AllFieldStream : public FieldStreamBase {
public:
- AllFieldStream(Array* fieldinfo, ConstantPool* constants): FieldStreamBase(fieldinfo, constants) {}
AllFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants()) {}
};
diff --git a/src/hotspot/share/oops/fieldStreams.inline.hpp b/src/hotspot/share/oops/fieldStreams.inline.hpp
index 776bcd1671c..51faf88c678 100644
--- a/src/hotspot/share/oops/fieldStreams.inline.hpp
+++ b/src/hotspot/share/oops/fieldStreams.inline.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,22 +33,18 @@
FieldStreamBase::FieldStreamBase(const Array* fieldinfo_stream, ConstantPool* constants, int start, int limit) :
_fieldinfo_stream(fieldinfo_stream),
_reader(FieldInfoReader(_fieldinfo_stream)),
- _constants(constantPoolHandle(Thread::current(), constants)), _index(start) {
- _index = start;
- if (limit < start) {
- _limit = FieldInfoStream::num_total_fields(_fieldinfo_stream);
- } else {
- _limit = limit;
- }
+ _constants(constantPoolHandle(Thread::current(), constants)),
+ _index(start),
+ _limit(limit) {
initialize();
}
-FieldStreamBase::FieldStreamBase(Array* fieldinfo_stream, ConstantPool* constants) :
+FieldStreamBase::FieldStreamBase(const Array* fieldinfo_stream, ConstantPool* constants) :
_fieldinfo_stream(fieldinfo_stream),
_reader(FieldInfoReader(_fieldinfo_stream)),
_constants(constantPoolHandle(Thread::current(), constants)),
_index(0),
- _limit(FieldInfoStream::num_total_fields(_fieldinfo_stream)) {
+ _limit(-1) {
initialize();
}
@@ -57,9 +53,28 @@ FieldStreamBase::FieldStreamBase(InstanceKlass* klass) :
_reader(FieldInfoReader(_fieldinfo_stream)),
_constants(constantPoolHandle(Thread::current(), klass->constants())),
_index(0),
- _limit(FieldInfoStream::num_total_fields(_fieldinfo_stream)) {
+ _limit(-1) {
assert(klass == field_holder(), "");
initialize();
}
+inline bool JavaFieldStream::lookup(const Symbol* name, const Symbol* signature) {
+ if (_search_table != nullptr) {
+ int index = _reader.search_table_lookup(_search_table, name, signature, _constants(), _limit);
+ if (index >= 0) {
+ assert(index < _limit, "must be");
+ _index = index;
+ _reader.read_field_info(_fi_buf);
+ return true;
+ }
+ } else {
+ for (; !done(); next()) {
+ if (this->name() == name && this->signature() == signature) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
#endif // SHARE_OOPS_FIELDSTREAMS_INLINE_HPP
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index 32d7943e1e8..f4ab8c31409 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -686,6 +686,11 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
}
set_fieldinfo_stream(nullptr);
+ if (fieldinfo_search_table() != nullptr && !fieldinfo_search_table()->is_shared()) {
+ MetadataFactory::free_array(loader_data, fieldinfo_search_table());
+ }
+ set_fieldinfo_search_table(nullptr);
+
if (fields_status() != nullptr && !fields_status()->is_shared()) {
MetadataFactory::free_array(loader_data, fields_status());
}
@@ -1786,13 +1791,12 @@ FieldInfo InstanceKlass::field(int index) const {
}
bool InstanceKlass::find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const {
- for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
- Symbol* f_name = fs.name();
- Symbol* f_sig = fs.signature();
- if (f_name == name && f_sig == sig) {
- fd->reinitialize(const_cast(this), fs.to_FieldInfo());
- return true;
- }
+ JavaFieldStream fs(this);
+ if (fs.lookup(name, sig)) {
+ assert(fs.name() == name, "name must match");
+ assert(fs.signature() == sig, "signature must match");
+ fd->reinitialize(const_cast(this), fs.to_FieldInfo());
+ return true;
}
return false;
}
@@ -2391,13 +2395,26 @@ jmethodID InstanceKlass::update_jmethod_id(jmethodID* jmeths, Method* method, in
return new_id;
}
+// Allocate the jmethodID cache.
+static jmethodID* create_jmethod_id_cache(size_t size) {
+ jmethodID* jmeths = NEW_C_HEAP_ARRAY(jmethodID, size + 1, mtClass);
+ memset(jmeths, 0, (size + 1) * sizeof(jmethodID));
+ // cache size is stored in element[0], other elements offset by one
+ jmeths[0] = (jmethodID)size;
+ return jmeths;
+}
+
+// When reading outside a lock, use this.
+jmethodID* InstanceKlass::methods_jmethod_ids_acquire() const {
+ return Atomic::load_acquire(&_methods_jmethod_ids);
+}
+
+void InstanceKlass::release_set_methods_jmethod_ids(jmethodID* jmeths) {
+ Atomic::release_store(&_methods_jmethod_ids, jmeths);
+}
+
// Lookup or create a jmethodID.
-// This code is called by the VMThread and JavaThreads so the
-// locking has to be done very carefully to avoid deadlocks
-// and/or other cache consistency problems.
-//
-jmethodID InstanceKlass::get_jmethod_id(const methodHandle& method_h) {
- Method* method = method_h();
+jmethodID InstanceKlass::get_jmethod_id(Method* method) {
int idnum = method->method_idnum();
jmethodID* jmeths = methods_jmethod_ids_acquire();
@@ -2418,15 +2435,12 @@ jmethodID InstanceKlass::get_jmethod_id(const methodHandle& method_h) {
if (jmeths == nullptr) {
MutexLocker ml(JmethodIdCreation_lock, Mutex::_no_safepoint_check_flag);
- jmeths = methods_jmethod_ids_acquire();
+ jmeths = _methods_jmethod_ids;
// Still null?
if (jmeths == nullptr) {
size_t size = idnum_allocated_count();
assert(size > (size_t)idnum, "should already have space");
- jmeths = NEW_C_HEAP_ARRAY(jmethodID, size + 1, mtClass);
- memset(jmeths, 0, (size + 1) * sizeof(jmethodID));
- // cache size is stored in element[0], other elements offset by one
- jmeths[0] = (jmethodID)size;
+ jmeths = create_jmethod_id_cache(size);
jmethodID new_id = update_jmethod_id(jmeths, method, idnum);
// publish jmeths
@@ -2456,10 +2470,7 @@ void InstanceKlass::update_methods_jmethod_cache() {
if (old_size < size + 1) {
// Allocate a larger one and copy entries to the new one.
// They've already been updated to point to new methods where applicable (i.e., not obsolete).
- jmethodID* new_cache = NEW_C_HEAP_ARRAY(jmethodID, size + 1, mtClass);
- memset(new_cache, 0, (size + 1) * sizeof(jmethodID));
- // The cache size is stored in element[0]; the other elements are offset by one.
- new_cache[0] = (jmethodID)size;
+ jmethodID* new_cache = create_jmethod_id_cache(size);
for (int i = 1; i <= (int)old_size; i++) {
new_cache[i] = cache[i];
@@ -2470,24 +2481,30 @@ void InstanceKlass::update_methods_jmethod_cache() {
}
}
-// Figure out how many jmethodIDs haven't been allocated, and make
-// sure space for them is pre-allocated. This makes getting all
-// method ids much, much faster with classes with more than 8
+// Make a jmethodID for all methods in this class. This makes getting all method
+// ids much, much faster with classes with more than 8
// methods, and has a *substantial* effect on performance with jvmti
// code that loads all jmethodIDs for all classes.
-void InstanceKlass::ensure_space_for_methodids(int start_offset) {
- int new_jmeths = 0;
+void InstanceKlass::make_methods_jmethod_ids() {
+ MutexLocker ml(JmethodIdCreation_lock, Mutex::_no_safepoint_check_flag);
+ jmethodID* jmeths = _methods_jmethod_ids;
+ if (jmeths == nullptr) {
+ jmeths = create_jmethod_id_cache(idnum_allocated_count());
+ release_set_methods_jmethod_ids(jmeths);
+ }
+
int length = methods()->length();
- for (int index = start_offset; index < length; index++) {
+ for (int index = 0; index < length; index++) {
Method* m = methods()->at(index);
- jmethodID id = m->find_jmethod_id_or_null();
- if (id == nullptr) {
- new_jmeths++;
+ int idnum = m->method_idnum();
+ assert(!m->is_old(), "should not have old methods or I'm confused");
+ jmethodID id = Atomic::load_acquire(&jmeths[idnum + 1]);
+ if (!m->is_overpass() && // skip overpasses
+ id == nullptr) {
+ id = Method::make_jmethod_id(class_loader_data(), m);
+ Atomic::release_store(&jmeths[idnum + 1], id);
}
}
- if (new_jmeths != 0) {
- Method::ensure_jmethod_ids(class_loader_data(), new_jmeths);
- }
}
// Lookup a jmethodID, null if not found. Do no blocking, no allocations, no handles
@@ -2610,6 +2627,7 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
}
it->push(&_fieldinfo_stream);
+ it->push(&_fieldinfo_search_table);
// _fields_status might be written into by Rewriter::scan_method() -> fd.set_has_initialized_final_update()
it->push(&_fields_status, MetaspaceClosure::_writable);
@@ -2710,6 +2728,8 @@ void InstanceKlass::remove_unshareable_info() {
DEBUG_ONLY(_shared_class_load_count = 0);
remove_unshareable_flags();
+
+ DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
}
void InstanceKlass::remove_unshareable_flags() {
@@ -2816,6 +2836,8 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
if (DiagnoseSyncOnValueBasedClasses && has_value_based_class_annotation() && !is_value_based()) {
set_is_value_based();
}
+
+ DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
}
// Check if a class or any of its supertypes has a version older than 50.
@@ -2914,7 +2936,7 @@ void InstanceKlass::release_C_heap_structures(bool release_sub_metadata) {
JNIid::deallocate(jni_ids());
set_jni_ids(nullptr);
- jmethodID* jmeths = methods_jmethod_ids_acquire();
+ jmethodID* jmeths = _methods_jmethod_ids;
if (jmeths != nullptr) {
release_set_methods_jmethod_ids(nullptr);
FreeHeap(jmeths);
@@ -3492,7 +3514,7 @@ void InstanceKlass::add_osr_nmethod(nmethod* n) {
for (int l = CompLevel_limited_profile; l < n->comp_level(); l++) {
nmethod *inv = lookup_osr_nmethod(n->method(), n->osr_entry_bci(), l, true);
if (inv != nullptr && inv->is_in_use()) {
- inv->make_not_entrant(nmethod::ChangeReason::OSR_invalidation_of_lower_level);
+ inv->make_not_entrant(nmethod::InvalidationReason::OSR_INVALIDATION_OF_LOWER_LEVEL);
}
}
}
@@ -3760,6 +3782,11 @@ void InstanceKlass::print_on(outputStream* st) const {
map++;
}
st->cr();
+
+ if (fieldinfo_search_table() != nullptr) {
+ st->print_cr(BULLET"---- field info search table:");
+ FieldInfoStream::print_search_table(st, _constants, _fieldinfo_stream, _fieldinfo_search_table);
+ }
}
void InstanceKlass::print_value_on(outputStream* st) const {
@@ -4261,17 +4288,14 @@ bool InstanceKlass::should_clean_previous_versions_and_reset() {
return ret;
}
-// This nulls out jmethodIDs for all methods in 'klass'
-// It needs to be called explicitly for all previous versions of a class because these may not be cleaned up
-// during class unloading.
-// We can not use the jmethodID cache associated with klass directly because the 'previous' versions
-// do not have the jmethodID cache filled in. Instead, we need to lookup jmethodID for each method and this
-// is expensive - O(n) for one jmethodID lookup. For all contained methods it is O(n^2).
-// The reason for expensive jmethodID lookup for each method is that there is no direct link between method and jmethodID.
-void InstanceKlass::clear_jmethod_ids(InstanceKlass* klass) {
+// This nulls out the jmethodID for all obsolete methods in the previous version of the 'klass'.
+// These obsolete methods only exist in the previous version and we're about to delete the memory for them.
+// The jmethodID for these are deallocated when we unload the class, so this doesn't remove them from the table.
+void InstanceKlass::clear_obsolete_jmethod_ids(InstanceKlass* klass) {
Array* method_refs = klass->methods();
for (int k = 0; k < method_refs->length(); k++) {
Method* method = method_refs->at(k);
+ // Only need to clear obsolete methods.
if (method != nullptr && method->is_obsolete()) {
method->clear_jmethod_id();
}
@@ -4321,7 +4345,7 @@ void InstanceKlass::purge_previous_version_list() {
// Unlink from previous version list.
assert(pv_node->class_loader_data() == loader_data, "wrong loader_data");
InstanceKlass* next = pv_node->previous_versions();
- clear_jmethod_ids(pv_node); // jmethodID maintenance for the unloaded class
+ clear_obsolete_jmethod_ids(pv_node); // jmethodID maintenance for the unloaded class
pv_node->link_previous_versions(nullptr); // point next to null
last->link_previous_versions(next);
// Delete this node directly. Nothing is referring to it and we don't
diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp
index 078c5b81841..2512d8d869d 100644
--- a/src/hotspot/share/oops/instanceKlass.hpp
+++ b/src/hotspot/share/oops/instanceKlass.hpp
@@ -276,6 +276,7 @@ class InstanceKlass: public Klass {
// Fields information is stored in an UNSIGNED5 encoded stream (see fieldInfo.hpp)
Array* _fieldinfo_stream;
+ Array* _fieldinfo_search_table;
Array* _fields_status;
// embedded Java vtable follows here
@@ -398,6 +399,9 @@ class InstanceKlass: public Klass {
Array* fieldinfo_stream() const { return _fieldinfo_stream; }
void set_fieldinfo_stream(Array* fis) { _fieldinfo_stream = fis; }
+ Array* fieldinfo_search_table() const { return _fieldinfo_search_table; }
+ void set_fieldinfo_search_table(Array* table) { _fieldinfo_search_table = table; }
+
Array* fields_status() const {return _fields_status; }
void set_fields_status(Array* array) { _fields_status = array; }
@@ -777,8 +781,8 @@ class InstanceKlass: public Klass {
u2 method_index);
// jmethodID support
- jmethodID get_jmethod_id(const methodHandle& method_h);
- void ensure_space_for_methodids(int start_offset = 0);
+ jmethodID get_jmethod_id(Method* method);
+ void make_methods_jmethod_ids();
jmethodID jmethod_id_or_null(Method* method);
void update_methods_jmethod_cache();
@@ -1052,10 +1056,10 @@ class InstanceKlass: public Klass {
Atomic::store(&_init_thread, thread);
}
- inline jmethodID* methods_jmethod_ids_acquire() const;
- inline void release_set_methods_jmethod_ids(jmethodID* jmeths);
- // This nulls out jmethodIDs for all methods in 'klass'
- static void clear_jmethod_ids(InstanceKlass* klass);
+ jmethodID* methods_jmethod_ids_acquire() const;
+ void release_set_methods_jmethod_ids(jmethodID* jmeths);
+ // This nulls out obsolete jmethodIDs for all methods in 'klass'.
+ static void clear_obsolete_jmethod_ids(InstanceKlass* klass);
jmethodID update_jmethod_id(jmethodID* jmeths, Method* method, int idnum);
public:
diff --git a/src/hotspot/share/oops/instanceKlass.inline.hpp b/src/hotspot/share/oops/instanceKlass.inline.hpp
index 1b4664f5a4b..f9db34f4884 100644
--- a/src/hotspot/share/oops/instanceKlass.inline.hpp
+++ b/src/hotspot/share/oops/instanceKlass.inline.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -71,14 +71,6 @@ inline void InstanceKlass::release_set_array_klasses(ObjArrayKlass* k) {
Atomic::release_store(&_array_klasses, k);
}
-inline jmethodID* InstanceKlass::methods_jmethod_ids_acquire() const {
- return Atomic::load_acquire(&_methods_jmethod_ids);
-}
-
-inline void InstanceKlass::release_set_methods_jmethod_ids(jmethodID* jmeths) {
- Atomic::release_store(&_methods_jmethod_ids, jmeths);
-}
-
// The iteration over the oops in objects is a hot path in the GC code.
// By force inlining the following functions, we get similar GC performance
// as the previous macro based implementation.
diff --git a/src/hotspot/share/oops/jmethodIDTable.cpp b/src/hotspot/share/oops/jmethodIDTable.cpp
new file mode 100644
index 00000000000..9dba79229e7
--- /dev/null
+++ b/src/hotspot/share/oops/jmethodIDTable.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "logging/log.hpp"
+#include "memory/allocation.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/jmethodIDTable.hpp"
+#include "oops/method.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "utilities/concurrentHashTable.inline.hpp"
+#include "utilities/concurrentHashTableTasks.inline.hpp"
+#include "utilities/macros.hpp"
+
+// Save (jmethod, Method*) in a hashtable to lookup Method.
+// The CHT is for performance because it has lock free lookup.
+// The value of the next jmethodID. This only increments (always unique IDs).
+static uint64_t _jmethodID_counter = 0;
+// Tracks the number of jmethodID entries in the _jmethod_id_table.
+// Incremented on insert, decremented on remove. Used to track if we need to resize the table.
+static uint64_t _jmethodID_entry_count = 0;
+
+uint64_t JmethodIDTable::get_entry_count() { return _jmethodID_entry_count; }
+
+class JmethodEntry {
+ public:
+ uint64_t _id;
+ Method* _method;
+
+ JmethodEntry(uint64_t id, Method* method) : _id(id), _method(method) {}
+};
+
+class JmethodIDTableConfig : public AllStatic {
+ public:
+ typedef JmethodEntry Value;
+ static void* allocate_node(void* context, size_t size, Value const& value) {
+ return AllocateHeap(size, mtJNI);
+ }
+ static void free_node(void* context, void* memory, Value const& value) {
+ FreeHeap(memory);
+ }
+ static uintx get_hash(Value const& value, bool* is_dead) {
+ *is_dead = false;
+ return value._id;
+ }
+ static bool is_dead(Value const& value) {
+ return false;
+ }
+};
+
+using MethodIdTable = ConcurrentHashTable;
+static MethodIdTable* _jmethod_id_table = nullptr;
+
+void JmethodIDTable::initialize() {
+ const size_t start_size = 10;
+ // 2^24 is max size
+ const size_t end_size = 24;
+ // If a chain gets to 32 something might be wrong.
+ const size_t grow_hint = 32;
+
+ _jmethod_id_table = new MethodIdTable(start_size, end_size, grow_hint);
+}
+
+class JmethodIDLookup : StackObj {
+ private:
+ uint64_t _mid;
+
+ public:
+ JmethodIDLookup(const uint64_t mid) : _mid(mid) {}
+ uintx get_hash() const {
+ return _mid;
+ }
+ bool equals(JmethodEntry* value) {
+ return _mid == value->_id;
+ }
+
+ bool is_dead(JmethodEntry* value) {
+ return false;
+ }
+};
+
+static JmethodEntry* get_jmethod_entry(jmethodID mid) {
+ assert(mid != nullptr, "JNI method id should not be null");
+
+ Thread* current = Thread::current();
+ JmethodIDLookup lookup((uint64_t)mid);
+ JmethodEntry* result = nullptr;
+ auto get = [&] (JmethodEntry* value) {
+ // function called if value is found so is never null
+ result = value;
+ };
+ bool needs_rehashing = false;
+ _jmethod_id_table->get(current, lookup, get, &needs_rehashing);
+ assert(!needs_rehashing, "should never need rehashing");
+ return result;
+}
+
+Method* JmethodIDTable::resolve_jmethod_id(jmethodID mid) {
+ JmethodEntry* result = get_jmethod_entry(mid);
+ return result == nullptr ? nullptr : result->_method;
+}
+
+const unsigned _resize_load_trigger = 5; // load factor that will trigger the resize
+
+static unsigned table_size(Thread* current) {
+ return 1 << _jmethod_id_table->get_size_log2(current);
+}
+
+static bool needs_resize(Thread* current) {
+ return ((_jmethodID_entry_count > (_resize_load_trigger * table_size(current))) &&
+ !_jmethod_id_table->is_max_size_reached());
+}
+
+// Add a method id to the jmethod_ids.
+jmethodID JmethodIDTable::make_jmethod_id(Method* m) {
+ bool grow_hint, clean_hint;
+
+ assert_locked_or_safepoint(JmethodIdCreation_lock);
+ // Update jmethodID global counter.
+ _jmethodID_counter++;
+
+ JmethodEntry new_entry(_jmethodID_counter, m);
+ Thread* current = Thread::current();
+ JmethodIDLookup lookup(_jmethodID_counter);
+ bool created = _jmethod_id_table->insert(current, lookup, new_entry, &grow_hint, &clean_hint);
+ assert(created, "must be");
+ log_debug(jmethod)("Inserted jmethod id " UINT64_FORMAT_X, _jmethodID_counter);
+ // Increment number of entries in the table.
+ _jmethodID_entry_count++;
+
+ // Resize table if it needs to grow. The _jmethod_id_table has a good distribution.
+ if (needs_resize(current)) {
+ _jmethod_id_table->grow(current);
+ log_info(jmethod)("Growing table to %d for " UINT64_FORMAT " entries", table_size(current), _jmethodID_counter);
+ }
+ return (jmethodID)_jmethodID_counter;
+}
+
+void JmethodIDTable::remove(jmethodID jmid) {
+ assert_locked_or_safepoint(JmethodIdCreation_lock);
+ JmethodIDLookup lookup((uint64_t)jmid);
+ Thread* current = Thread::current();
+ JmethodEntry* result;
+ auto get = [&] (JmethodEntry* value) {
+ // The function that is called if value is found, so is never null.
+ result = value;
+ };
+ bool removed = _jmethod_id_table->remove(current, lookup, get);
+ assert(removed, "must be");
+ log_debug(jmethod)("Removed jmethod id " UINT64_FORMAT_X, (uint64_t)jmid);
+ // Decrement number of entries in the table.
+ _jmethodID_entry_count--;
+}
+
+void JmethodIDTable::change_method_associated_with_jmethod_id(jmethodID jmid, Method* new_method) {
+ assert_locked_or_safepoint(JmethodIdCreation_lock);
+ JmethodEntry* result = get_jmethod_entry(jmid);
+ // Change table entry to point to the new method.
+ log_debug(jmethod)("Changed jmethod id " UINT64_FORMAT_X " from " PTR_FORMAT " to " PTR_FORMAT, (uint64_t)jmid,
+ p2i(result->_method), p2i(new_method));
+ result->_method = new_method;
+}
+
+void JmethodIDTable::clear_jmethod_id(jmethodID jmid, Method* obsolete_method) {
+ assert_locked_or_safepoint(JmethodIdCreation_lock);
+ JmethodEntry* result = get_jmethod_entry(jmid);
+ // We need to make sure that jmethodID actually resolves to this method
+ // - multiple redefined versions may share jmethodID slots and if a method
+ // has already been rewired to a newer version we could be clearing
+ // the reference to a still existing method instance.
+ if (result->_method == obsolete_method) {
+ result->_method = nullptr;
+ }
+}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/LockingMode.java b/src/hotspot/share/oops/jmethodIDTable.hpp
similarity index 51%
rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/LockingMode.java
rename to src/hotspot/share/oops/jmethodIDTable.hpp
index 2046fd075ad..cdbe5c87855 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/LockingMode.java
+++ b/src/hotspot/share/oops/jmethodIDTable.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,39 +22,34 @@
*
*/
-package sun.jvm.hotspot.runtime;
+#ifndef SHARE_OOPS_JMETHODIDTABLE_HPP
+#define SHARE_OOPS_JMETHODIDTABLE_HPP
-import sun.jvm.hotspot.types.TypeDataBase;
+#include "jni.h"
+#include "memory/allocation.hpp"
+// Class for associating Method with jmethodID
+class Method;
-/** Encapsulates the LockingMode enum in globalDefinitions.hpp in
- the VM. */
+class JmethodIDTable : public AllStatic {
+ public:
+ static void initialize();
-public class LockingMode {
- private static int monitor;
- private static int legacy;
- private static int lightweight;
+ // Given a Method return a jmethodID.
+ static jmethodID make_jmethod_id(Method* m);
- static {
- VM.registerVMInitializedObserver(
- (o, d) -> initialize(VM.getVM().getTypeDataBase()));
- }
+ // Given a jmethodID, return a Method.
+ static Method* resolve_jmethod_id(jmethodID mid);
- private static synchronized void initialize(TypeDataBase db) {
- monitor = db.lookupIntConstant("LM_MONITOR").intValue();
- legacy = db.lookupIntConstant("LM_LEGACY").intValue();
- lightweight = db.lookupIntConstant("LM_LIGHTWEIGHT").intValue();
- }
+ // Class unloading support, remove the associations from the tables. Stale jmethodID will
+ // not be found and return null.
+ static void remove(jmethodID mid);
- public static int getMonitor() {
- return monitor;
- }
+ // RedefineClasses support
+ static void change_method_associated_with_jmethod_id(jmethodID jmid, Method* new_method);
+ static void clear_jmethod_id(jmethodID jmid, Method* m);
- public static int getLegacy() {
- return legacy;
- }
+ static uint64_t get_entry_count();
+};
- public static int getLightweight() {
- return lightweight;
- }
-}
+#endif // SHARE_OOPS_JMETHODIDTABLE_HPP
diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp
index 1a36fce23aa..7552bf50ed9 100644
--- a/src/hotspot/share/oops/method.cpp
+++ b/src/hotspot/share/oops/method.cpp
@@ -53,6 +53,7 @@
#include "nmt/memTracker.hpp"
#include "oops/constMethod.hpp"
#include "oops/constantPool.hpp"
+#include "oops/jmethodIDTable.hpp"
#include "oops/klass.inline.hpp"
#include "oops/method.inline.hpp"
#include "oops/methodData.hpp"
@@ -1028,7 +1029,7 @@ void Method::set_native_function(address function, bool post_event_flag) {
// If so, we have to make it not_entrant.
nmethod* nm = code(); // Put it into local variable to guard against concurrent updates
if (nm != nullptr) {
- nm->make_not_entrant(nmethod::ChangeReason::set_native_function);
+ nm->make_not_entrant(nmethod::InvalidationReason::SET_NATIVE_FUNCTION);
}
}
@@ -2059,172 +2060,33 @@ void BreakpointInfo::clear(Method* method) {
#endif // INCLUDE_JVMTI
// jmethodID handling
-
-// This is a block allocating object, sort of like JNIHandleBlock, only a
-// lot simpler.
-// It's allocated on the CHeap because once we allocate a jmethodID, we can
-// never get rid of it.
-
-static const int min_block_size = 8;
-
-class JNIMethodBlockNode : public CHeapObj {
- friend class JNIMethodBlock;
- Method** _methods;
- int _number_of_methods;
- int _top;
- JNIMethodBlockNode* _next;
-
- public:
-
- JNIMethodBlockNode(int num_methods = min_block_size);
-
- ~JNIMethodBlockNode() { FREE_C_HEAP_ARRAY(Method*, _methods); }
-
- void ensure_methods(int num_addl_methods) {
- if (_top < _number_of_methods) {
- num_addl_methods -= _number_of_methods - _top;
- if (num_addl_methods <= 0) {
- return;
- }
- }
- if (_next == nullptr) {
- _next = new JNIMethodBlockNode(MAX2(num_addl_methods, min_block_size));
- } else {
- _next->ensure_methods(num_addl_methods);
- }
- }
-};
-
-class JNIMethodBlock : public CHeapObj {
- JNIMethodBlockNode _head;
- JNIMethodBlockNode *_last_free;
- public:
- static Method* const _free_method;
-
- JNIMethodBlock(int initial_capacity = min_block_size)
- : _head(initial_capacity), _last_free(&_head) {}
-
- void ensure_methods(int num_addl_methods) {
- _last_free->ensure_methods(num_addl_methods);
- }
-
- Method** add_method(Method* m) {
- for (JNIMethodBlockNode* b = _last_free; b != nullptr; b = b->_next) {
- if (b->_top < b->_number_of_methods) {
- // top points to the next free entry.
- int i = b->_top;
- b->_methods[i] = m;
- b->_top++;
- _last_free = b;
- return &(b->_methods[i]);
- } else if (b->_top == b->_number_of_methods) {
- // if the next free entry ran off the block see if there's a free entry
- for (int i = 0; i < b->_number_of_methods; i++) {
- if (b->_methods[i] == _free_method) {
- b->_methods[i] = m;
- _last_free = b;
- return &(b->_methods[i]);
- }
- }
- // Only check each block once for frees. They're very unlikely.
- // Increment top past the end of the block.
- b->_top++;
- }
- // need to allocate a next block.
- if (b->_next == nullptr) {
- b->_next = _last_free = new JNIMethodBlockNode();
- }
- }
- guarantee(false, "Should always allocate a free block");
- return nullptr;
- }
-
- bool contains(Method** m) {
- if (m == nullptr) return false;
- for (JNIMethodBlockNode* b = &_head; b != nullptr; b = b->_next) {
- if (b->_methods <= m && m < b->_methods + b->_number_of_methods) {
- // This is a bit of extra checking, for two reasons. One is
- // that contains() deals with pointers that are passed in by
- // JNI code, so making sure that the pointer is aligned
- // correctly is valuable. The other is that <= and > are
- // technically not defined on pointers, so the if guard can
- // pass spuriously; no modern compiler is likely to make that
- // a problem, though (and if one did, the guard could also
- // fail spuriously, which would be bad).
- ptrdiff_t idx = m - b->_methods;
- if (b->_methods + idx == m) {
- return true;
- }
- }
- }
- return false; // not found
- }
-
- // During class unloading the methods are cleared, which is different
- // than freed.
- void clear_all_methods() {
- for (JNIMethodBlockNode* b = &_head; b != nullptr; b = b->_next) {
- for (int i = 0; i< b->_number_of_methods; i++) {
- b->_methods[i] = nullptr;
- }
- }
- }
-#ifndef PRODUCT
- int count_methods() {
- // count all allocated methods
- int count = 0;
- for (JNIMethodBlockNode* b = &_head; b != nullptr; b = b->_next) {
- for (int i = 0; i< b->_number_of_methods; i++) {
- if (b->_methods[i] != _free_method) count++;
- }
- }
- return count;
- }
-#endif // PRODUCT
-};
-
-// Something that can't be mistaken for an address or a markWord
-Method* const JNIMethodBlock::_free_method = (Method*)55;
-
-JNIMethodBlockNode::JNIMethodBlockNode(int num_methods) : _top(0), _next(nullptr) {
- _number_of_methods = MAX2(num_methods, min_block_size);
- _methods = NEW_C_HEAP_ARRAY(Method*, _number_of_methods, mtInternal);
- for (int i = 0; i < _number_of_methods; i++) {
- _methods[i] = JNIMethodBlock::_free_method;
- }
-}
-
-void Method::ensure_jmethod_ids(ClassLoaderData* cld, int capacity) {
- // Have to add jmethod_ids() to class loader data thread-safely.
- // Also have to add the method to the list safely, which the lock
- // protects as well.
- MutexLocker ml(JmethodIdCreation_lock, Mutex::_no_safepoint_check_flag);
- if (cld->jmethod_ids() == nullptr) {
- cld->set_jmethod_ids(new JNIMethodBlock(capacity));
- } else {
- cld->jmethod_ids()->ensure_methods(capacity);
- }
-}
+// jmethodIDs are 64-bit integers that will never run out and are mapped in a table
+// to their Method and vice versa. If JNI code has access to stale jmethodID, this
+// wastes no memory but the Method* returned is null.
// Add a method id to the jmethod_ids
jmethodID Method::make_jmethod_id(ClassLoaderData* cld, Method* m) {
// Have to add jmethod_ids() to class loader data thread-safely.
- // Also have to add the method to the list safely, which the lock
+ // Also have to add the method to the InstanceKlass list safely, which the lock
// protects as well.
assert(JmethodIdCreation_lock->owned_by_self(), "sanity check");
+ jmethodID jmid = JmethodIDTable::make_jmethod_id(m);
+ assert(jmid != nullptr, "must be created");
- ResourceMark rm;
- log_debug(jmethod)("Creating jmethodID for Method %s", m->external_name());
- if (cld->jmethod_ids() == nullptr) {
- cld->set_jmethod_ids(new JNIMethodBlock());
- }
- // jmethodID is a pointer to Method*
- return (jmethodID)cld->jmethod_ids()->add_method(m);
+ // Add to growable array in CLD.
+ cld->add_jmethod_id(jmid);
+ return jmid;
}
+// This looks in the InstanceKlass cache, then calls back to make_jmethod_id if not found.
jmethodID Method::jmethod_id() {
- methodHandle mh(Thread::current(), this);
- return method_holder()->get_jmethod_id(mh);
+ return method_holder()->get_jmethod_id(this);
+}
+
+// Get the Method out of the table given the method id.
+Method* Method::resolve_jmethod_id(jmethodID mid) {
+ assert(mid != nullptr, "JNI method id should not be null");
+ return JmethodIDTable::resolve_jmethod_id(mid);
}
void Method::change_method_associated_with_jmethod_id(jmethodID jmid, Method* new_method) {
@@ -2232,25 +2094,34 @@ void Method::change_method_associated_with_jmethod_id(jmethodID jmid, Method* ne
// scratch method holder.
assert(resolve_jmethod_id(jmid)->method_holder()->class_loader()
== new_method->method_holder()->class_loader() ||
- new_method->method_holder()->class_loader() == nullptr, // allow Unsafe substitution
+ new_method->method_holder()->class_loader() == nullptr, // allow substitution to Unsafe method
"changing to a different class loader");
- // Just change the method in place, jmethodID pointer doesn't change.
- *((Method**)jmid) = new_method;
+ JmethodIDTable::change_method_associated_with_jmethod_id(jmid, new_method);
}
-bool Method::is_method_id(jmethodID mid) {
+// If there's a jmethodID for this method, clear the Method
+// but leave jmethodID for this method in the table.
+// It's deallocated with class unloading.
+void Method::clear_jmethod_id() {
+ jmethodID mid = method_holder()->jmethod_id_or_null(this);
+ if (mid != nullptr) {
+ JmethodIDTable::clear_jmethod_id(mid, this);
+ }
+}
+
+bool Method::validate_jmethod_id(jmethodID mid) {
Method* m = resolve_jmethod_id(mid);
assert(m != nullptr, "should be called with non-null method");
InstanceKlass* ik = m->method_holder();
ClassLoaderData* cld = ik->class_loader_data();
if (cld->jmethod_ids() == nullptr) return false;
- return (cld->jmethod_ids()->contains((Method**)mid));
+ return (cld->jmethod_ids()->contains(mid));
}
Method* Method::checked_resolve_jmethod_id(jmethodID mid) {
if (mid == nullptr) return nullptr;
Method* o = resolve_jmethod_id(mid);
- if (o == nullptr || o == JNIMethodBlock::_free_method) {
+ if (o == nullptr) {
return nullptr;
}
// Method should otherwise be valid. Assert for testing.
@@ -2259,7 +2130,7 @@ Method* Method::checked_resolve_jmethod_id(jmethodID mid) {
// unloaded, we need to return null here too because after a safepoint, its memory
// will be reclaimed.
return o->method_holder()->is_loader_alive() ? o : nullptr;
-};
+}
void Method::set_on_stack(const bool value) {
// Set both the method itself and its constant pool. The constant pool
@@ -2280,25 +2151,6 @@ void Method::record_gc_epoch() {
constants()->cache()->record_gc_epoch();
}
-// Called when the class loader is unloaded to make all methods weak.
-void Method::clear_jmethod_ids(ClassLoaderData* loader_data) {
- loader_data->jmethod_ids()->clear_all_methods();
-}
-
-void Method::clear_jmethod_id() {
- // Being at a safepoint prevents racing against other class redefinitions
- assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint");
- // The jmethodID is not stored in the Method instance, we need to look it up first
- jmethodID methodid = find_jmethod_id_or_null();
- // We need to make sure that jmethodID actually resolves to this method
- // - multiple redefined versions may share jmethodID slots and if a method
- // has already been rewired to a newer version we could be removing reference
- // to a still existing method instance
- if (methodid != nullptr && *((Method**)methodid) == this) {
- *((Method**)methodid) = nullptr;
- }
-}
-
bool Method::has_method_vptr(const void* ptr) {
Method m;
// This assumes that the vtbl pointer is the first word of a C++ object.
@@ -2323,13 +2175,6 @@ bool Method::is_valid_method(const Method* m) {
}
}
-#ifndef PRODUCT
-void Method::print_jmethod_ids_count(const ClassLoaderData* loader_data, outputStream* out) {
- out->print("%d", loader_data->jmethod_ids()->count_methods());
-}
-#endif // PRODUCT
-
-
// Printing
#ifndef PRODUCT
diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp
index ce6b859f7c1..b241104b62c 100644
--- a/src/hotspot/share/oops/method.hpp
+++ b/src/hotspot/share/oops/method.hpp
@@ -704,18 +704,10 @@ class Method : public Metadata {
// refers to null (as is the case for any weak reference).
static jmethodID make_jmethod_id(ClassLoaderData* cld, Method* mh);
- // Ensure there is enough capacity in the internal tracking data
- // structures to hold the number of jmethodIDs you plan to generate.
- // This saves substantial time doing allocations.
- static void ensure_jmethod_ids(ClassLoaderData* cld, int capacity);
-
// Use resolve_jmethod_id() in situations where the caller is expected
// to provide a valid jmethodID; the only sanity checks are in asserts;
// result guaranteed not to be null.
- inline static Method* resolve_jmethod_id(jmethodID mid) {
- assert(mid != nullptr, "JNI method id should not be null");
- return *((Method**)mid);
- }
+ static Method* resolve_jmethod_id(jmethodID mid);
// Use checked_resolve_jmethod_id() in situations where the caller
// should provide a valid jmethodID, but might not. Null is returned
@@ -723,10 +715,9 @@ class Method : public Metadata {
static Method* checked_resolve_jmethod_id(jmethodID mid);
static void change_method_associated_with_jmethod_id(jmethodID old_jmid_ptr, Method* new_method);
- static bool is_method_id(jmethodID mid);
+ static bool validate_jmethod_id(jmethodID mid);
- // Clear methods
- static void clear_jmethod_ids(ClassLoaderData* loader_data);
+ // Clear jmethodID
void clear_jmethod_id();
static void print_jmethod_ids_count(const ClassLoaderData* loader_data, outputStream* out) PRODUCT_RETURN;
diff --git a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp
index 2ae1ac18699..cc15fe944f2 100644
--- a/src/hotspot/share/oops/trainingData.cpp
+++ b/src/hotspot/share/oops/trainingData.cpp
@@ -220,8 +220,8 @@ CompileTrainingData* CompileTrainingData::make(CompileTask* task) {
last_ctd = ctd;
}
} else {
- last_ctd = ctd;
- mtd->notice_toplevel_compilation(level);
+ last_ctd = ctd;
+ mtd->notice_toplevel_compilation(level);
}
}
return ctd;
diff --git a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp
index 2be3026881a..6acf19b4d5e 100644
--- a/src/hotspot/share/oops/trainingData.hpp
+++ b/src/hotspot/share/oops/trainingData.hpp
@@ -34,6 +34,7 @@
#include "memory/metaspaceClosure.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/method.hpp"
+#include "oops/objArrayKlass.hpp"
#include "runtime/handles.hpp"
#include "runtime/mutexLocker.hpp"
#include "utilities/resizeableResourceHash.hpp"
@@ -286,7 +287,12 @@ class TrainingData : public Metadata {
static bool is_klass_loaded(Klass* k) {
if (have_data()) {
// If we're running in AOT mode some classes may not be loaded yet
- return !k->is_instance_klass() || InstanceKlass::cast(k)->is_loaded();
+ if (k->is_objArray_klass()) {
+ k = ObjArrayKlass::cast(k)->bottom_klass();
+ }
+ if (k->is_instance_klass()) {
+ return InstanceKlass::cast(k)->is_loaded();
+ }
}
return true;
}
diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp
index 143398c57d0..92fb54a3a13 100644
--- a/src/hotspot/share/opto/addnode.cpp
+++ b/src/hotspot/share/opto/addnode.cpp
@@ -32,8 +32,8 @@
#include "opto/mulnode.hpp"
#include "opto/phaseX.hpp"
#include "opto/subnode.hpp"
-#include "runtime/stubRoutines.hpp"
#include "opto/utilities/xor.hpp"
+#include "runtime/stubRoutines.hpp"
// Portions of code courtesy of Clifford Click
diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp
index c33f656047d..7d3d4ec16f4 100644
--- a/src/hotspot/share/opto/block.cpp
+++ b/src/hotspot/share/opto/block.cpp
@@ -22,10 +22,10 @@
*
*/
+#include "compiler/compilerDirectives.hpp"
#include "libadt/vectset.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
-#include "compiler/compilerDirectives.hpp"
#include "opto/block.hpp"
#include "opto/cfgnode.hpp"
#include "opto/chaitin.hpp"
@@ -37,11 +37,8 @@
#include "utilities/copy.hpp"
#include "utilities/powerOfTwo.hpp"
-void Block_Array::grow( uint i ) {
- _nesting.check(_arena); // Check if a potential reallocation in the arena is safe
- if (i < Max()) {
- return; // No need to grow
- }
+void Block_Array::grow(uint i) {
+ assert(i >= Max(), "Should have been checked before, use maybe_grow?");
DEBUG_ONLY(_limit = i+1);
if( i < _size ) return;
if( !_size ) {
diff --git a/src/hotspot/share/opto/block.hpp b/src/hotspot/share/opto/block.hpp
index e6a98c28e77..07faca08dc3 100644
--- a/src/hotspot/share/opto/block.hpp
+++ b/src/hotspot/share/opto/block.hpp
@@ -53,7 +53,13 @@ class Block_Array : public ArenaObj {
ReallocMark _nesting; // Safety checks for arena reallocation
protected:
Block **_blocks;
- void grow( uint i ); // Grow array node to fit
+ void maybe_grow(uint i) {
+ _nesting.check(_arena); // Check if a potential reallocation in the arena is safe
+ if (i >= Max()) {
+ grow(i);
+ }
+ }
+ void grow(uint i); // Grow array node to fit
public:
Block_Array(Arena *a) : _size(OptoBlockListSize), _arena(a) {
@@ -68,7 +74,7 @@ class Block_Array : public ArenaObj {
Block *operator[] ( uint i ) const // Lookup, or assert for not mapped
{ assert( i < Max(), "oob" ); return _blocks[i]; }
// Extend the mapping: index i maps to Block *n.
- void map( uint i, Block *n ) { grow(i); _blocks[i] = n; }
+ void map( uint i, Block *n ) { maybe_grow(i); _blocks[i] = n; }
uint Max() const { DEBUG_ONLY(return _limit); return _size; }
};
diff --git a/src/hotspot/share/opto/bytecodeInfo.cpp b/src/hotspot/share/opto/bytecodeInfo.cpp
index e618a708f61..547cf2f6a38 100644
--- a/src/hotspot/share/opto/bytecodeInfo.cpp
+++ b/src/hotspot/share/opto/bytecodeInfo.cpp
@@ -26,8 +26,8 @@
#include "classfile/vmSymbols.hpp"
#include "compiler/compilationPolicy.hpp"
#include "compiler/compileBroker.hpp"
-#include "compiler/compilerEvent.hpp"
#include "compiler/compileLog.hpp"
+#include "compiler/compilerEvent.hpp"
#include "interpreter/linkResolver.hpp"
#include "jfr/jfrEvents.hpp"
#include "oops/objArrayKlass.hpp"
diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp
index fd55f2fd666..ca76114af31 100644
--- a/src/hotspot/share/opto/c2_globals.hpp
+++ b/src/hotspot/share/opto/c2_globals.hpp
@@ -25,9 +25,9 @@
#ifndef SHARE_OPTO_C2_GLOBALS_HPP
#define SHARE_OPTO_C2_GLOBALS_HPP
-#include "opto/c2_globals_pd.hpp"
#include "runtime/globals_shared.hpp"
#include "utilities/macros.hpp"
+#include CPU_HEADER(c2_globals)
//
// Defines all globals flags used by the server compiler.
@@ -678,10 +678,12 @@
"Print progress during Iterative Global Value Numbering") \
\
develop(uint, VerifyIterativeGVN, 0, \
- "Verify Iterative Global Value Numbering" \
- "=XY, with Y: verify Def-Use modifications during IGVN" \
- " X: verify that type(n) == n->Value() after IGVN" \
- "X and Y in 0=off; 1=on") \
+ "Verify Iterative Global Value Numbering =DCBA, with:" \
+ " D: verify Node::Identity did not miss opportunities" \
+ " C: verify Node::Ideal did not miss opportunities" \
+ " B: verify that type(n) == n->Value() after IGVN" \
+ " A: verify Def-Use modifications during IGVN" \
+ "Each can be 0=off or 1=on") \
constraint(VerifyIterativeGVNConstraintFunc, AtParse) \
\
develop(bool, TraceCISCSpill, false, \
diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp
index 0c642211e1f..e5b1e131505 100644
--- a/src/hotspot/share/opto/c2compiler.cpp
+++ b/src/hotspot/share/opto/c2compiler.cpp
@@ -25,15 +25,15 @@
#include "classfile/vmClasses.hpp"
#include "compiler/compilationMemoryStatistic.hpp"
#include "compiler/compilerDefinitions.inline.hpp"
-#include "runtime/handles.inline.hpp"
#include "jfr/support/jfrIntrinsics.hpp"
#include "opto/c2compiler.hpp"
#include "opto/compile.hpp"
#include "opto/optoreg.hpp"
#include "opto/output.hpp"
#include "opto/runtime.hpp"
-#include "runtime/stubRoutines.hpp"
#include "runtime/globals_extension.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/stubRoutines.hpp"
#include "utilities/macros.hpp"
@@ -767,7 +767,7 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) {
case vmIntrinsics::_doubleToRawLongBits:
case vmIntrinsics::_doubleToLongBits:
case vmIntrinsics::_longBitsToDouble:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
case vmIntrinsics::_Reference_refersTo0:
case vmIntrinsics::_PhantomReference_refersTo0:
case vmIntrinsics::_Reference_clear0:
diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp
index e0ad2f00ab5..e09d8cabe2c 100644
--- a/src/hotspot/share/opto/callGenerator.cpp
+++ b/src/hotspot/share/opto/callGenerator.cpp
@@ -24,9 +24,9 @@
#include "ci/bcEscapeAnalyzer.hpp"
#include "ci/ciCallSite.hpp"
-#include "ci/ciObjArray.hpp"
#include "ci/ciMemberName.hpp"
#include "ci/ciMethodHandle.hpp"
+#include "ci/ciObjArray.hpp"
#include "classfile/javaClasses.hpp"
#include "compiler/compileLog.hpp"
#include "opto/addnode.hpp"
diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp
index 3527e46c24b..a6203c94cff 100644
--- a/src/hotspot/share/opto/callnode.cpp
+++ b/src/hotspot/share/opto/callnode.cpp
@@ -22,8 +22,9 @@
*
*/
-#include "compiler/compileLog.hpp"
#include "ci/bcEscapeAnalyzer.hpp"
+#include "code/vmreg.hpp"
+#include "compiler/compileLog.hpp"
#include "compiler/oopMap.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/c2/barrierSetC2.hpp"
@@ -43,7 +44,6 @@
#include "opto/runtime.hpp"
#include "runtime/sharedRuntime.hpp"
#include "utilities/powerOfTwo.hpp"
-#include "code/vmreg.hpp"
// Portions of code courtesy of Clifford Click
diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp
index 6b3058d94f6..96f5ba7e693 100644
--- a/src/hotspot/share/opto/castnode.cpp
+++ b/src/hotspot/share/opto/castnode.cpp
@@ -22,6 +22,7 @@
*
*/
+#include "castnode.hpp"
#include "opto/addnode.hpp"
#include "opto/callnode.hpp"
#include "opto/castnode.hpp"
@@ -30,7 +31,6 @@
#include "opto/phaseX.hpp"
#include "opto/subnode.hpp"
#include "opto/type.hpp"
-#include "castnode.hpp"
#include "utilities/checkedCast.hpp"
//=============================================================================
@@ -498,7 +498,11 @@ Node* ConstraintCastNode::make_cast_for_type(Node* c, Node* in, const Type* type
Node* ConstraintCastNode::optimize_integer_cast(PhaseGVN* phase, BasicType bt) {
PhaseIterGVN *igvn = phase->is_IterGVN();
- const TypeInteger* this_type = this->type()->is_integer(bt);
+ const TypeInteger* this_type = this->type()->isa_integer(bt);
+ if (this_type == nullptr) {
+ return nullptr;
+ }
+
Node* z = in(1);
const TypeInteger* rx = nullptr;
const TypeInteger* ry = nullptr;
diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp
index 92f6c938dba..ef912ff471a 100644
--- a/src/hotspot/share/opto/cfgnode.cpp
+++ b/src/hotspot/share/opto/cfgnode.cpp
@@ -35,8 +35,8 @@
#include "opto/loopnode.hpp"
#include "opto/machnode.hpp"
#include "opto/movenode.hpp"
-#include "opto/narrowptrnode.hpp"
#include "opto/mulnode.hpp"
+#include "opto/narrowptrnode.hpp"
#include "opto/phaseX.hpp"
#include "opto/regalloc.hpp"
#include "opto/regmask.hpp"
diff --git a/src/hotspot/share/opto/classes.cpp b/src/hotspot/share/opto/classes.cpp
index 61d164773a2..b760a179b57 100644
--- a/src/hotspot/share/opto/classes.cpp
+++ b/src/hotspot/share/opto/classes.cpp
@@ -35,8 +35,8 @@
#include "opto/locknode.hpp"
#include "opto/loopnode.hpp"
#include "opto/machnode.hpp"
-#include "opto/memnode.hpp"
#include "opto/mathexactnode.hpp"
+#include "opto/memnode.hpp"
#include "opto/movenode.hpp"
#include "opto/mulnode.hpp"
#include "opto/multnode.hpp"
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index a5023408cdf..4c5f382ceee 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -33,9 +33,9 @@
#include "compiler/compilationMemoryStatistic.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
+#include "compiler/compiler_globals.hpp"
#include "compiler/compilerDefinitions.hpp"
#include "compiler/compilerOracle.hpp"
-#include "compiler/compiler_globals.hpp"
#include "compiler/disassembler.hpp"
#include "compiler/oopMap.hpp"
#include "gc/shared/barrierSet.hpp"
@@ -783,19 +783,9 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci,
StartNode* s = new StartNode(root(), tf()->domain());
initial_gvn()->set_type_bottom(s);
verify_start(s);
- if (method()->intrinsic_id() == vmIntrinsics::_Reference_get) {
- // With java.lang.ref.reference.get() we must go through the
- // intrinsic - even when get() is the root
- // method of the compile - so that, if necessary, the value in
- // the referent field of the reference object gets recorded by
- // the pre-barrier code.
- cg = find_intrinsic(method(), false);
- }
- if (cg == nullptr) {
- float past_uses = method()->interpreter_invocation_count();
- float expected_uses = past_uses;
- cg = CallGenerator::for_inline(method(), expected_uses);
- }
+ float past_uses = method()->interpreter_invocation_count();
+ float expected_uses = past_uses;
+ cg = CallGenerator::for_inline(method(), expected_uses);
}
if (failing()) return;
if (cg == nullptr) {
@@ -4521,7 +4511,9 @@ Node* Compile::conv_I2X_index(PhaseGVN* phase, Node* idx, const TypeInt* sizetyp
// number. (The prior range check has ensured this.)
// This assertion is used by ConvI2LNode::Ideal.
int index_max = max_jint - 1; // array size is max_jint, index is one less
- if (sizetype != nullptr) index_max = sizetype->_hi - 1;
+ if (sizetype != nullptr && sizetype->_hi > 0) {
+ index_max = sizetype->_hi - 1;
+ }
const TypeInt* iidxtype = TypeInt::make(0, index_max, Type::WidenMax);
idx = constrained_convI2L(phase, idx, iidxtype, ctrl);
#endif
@@ -4549,7 +4541,7 @@ void Compile::dump_print_inlining() {
void Compile::log_late_inline(CallGenerator* cg) {
if (log() != nullptr) {
- log()->head("late_inline method='%d' inline_id='" JLONG_FORMAT "'", log()->identify(cg->method()),
+ log()->head("late_inline method='%d' inline_id='" JLONG_FORMAT "'", log()->identify(cg->method()),
cg->unique_id());
JVMState* p = cg->call_node()->jvms();
while (p != nullptr) {
diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp
index 9b6d8db05b0..69244a6d178 100644
--- a/src/hotspot/share/opto/compile.hpp
+++ b/src/hotspot/share/opto/compile.hpp
@@ -28,17 +28,18 @@
#include "asm/codeBuffer.hpp"
#include "ci/compilerInterface.hpp"
#include "code/debugInfoRec.hpp"
-#include "compiler/compiler_globals.hpp"
+#include "compiler/cHeapStringHolder.hpp"
#include "compiler/compileBroker.hpp"
+#include "compiler/compiler_globals.hpp"
#include "compiler/compilerEvent.hpp"
-#include "compiler/cHeapStringHolder.hpp"
#include "libadt/dict.hpp"
#include "libadt/vectset.hpp"
#include "memory/resourceArea.hpp"
#include "oops/methodData.hpp"
#include "opto/idealGraphPrinter.hpp"
-#include "opto/phasetype.hpp"
#include "opto/phase.hpp"
+#include "opto/phasetype.hpp"
+#include "opto/printinlining.hpp"
#include "opto/regmask.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/sharedRuntime.hpp"
@@ -46,7 +47,6 @@
#include "runtime/vmThread.hpp"
#include "utilities/ticks.hpp"
#include "utilities/vmEnums.hpp"
-#include "opto/printinlining.hpp"
class AbstractLockNode;
class AddPNode;
@@ -928,8 +928,8 @@ class Compile : public Phase {
bool copy_node_notes_to(Node* dest, Node* source);
// Workhorse function to sort out the blocked Node_Notes array:
- inline Node_Notes* locate_node_notes(GrowableArray* arr,
- int idx, bool can_grow = false);
+ Node_Notes* locate_node_notes(GrowableArray* arr,
+ int idx, bool can_grow = false);
void grow_node_notes(GrowableArray* arr, int grow_by);
diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp
index c468c66fdfd..a495814da61 100644
--- a/src/hotspot/share/opto/convertnode.cpp
+++ b/src/hotspot/share/opto/convertnode.cpp
@@ -264,8 +264,68 @@ Node* ConvF2HFNode::Ideal(PhaseGVN* phase, bool can_reshape) {
return new ReinterpretHF2SNode(binop);
}
}
+
+ // Detects following ideal graph pattern
+ // ConvF2HF(binopF(conF, ConvHF2F(varS))) =>
+ // ReinterpretHF2SNode(binopHF(conHF, ReinterpretS2HFNode(varS)))
+ if (Float16NodeFactory::is_float32_binary_oper(in(1)->Opcode())) {
+ Node* binopF = in(1);
+ // Check if the incoming binary operation has one floating point constant
+ // input and the other input is a half precision to single precision upcasting node.
+ // We land here because a prior HalfFloat to Float conversion promotes
+ // an integral constant holding Float16 value to a floating point constant.
+ // i.e. ConvHF2F ConI(short) => ConF
+ Node* conF = nullptr;
+ Node* varS = nullptr;
+ if (binopF->in(1)->is_Con() && binopF->in(2)->Opcode() == Op_ConvHF2F) {
+ conF = binopF->in(1);
+ varS = binopF->in(2)->in(1);
+ } else if (binopF->in(2)->is_Con() && binopF->in(1)->Opcode() == Op_ConvHF2F) {
+ conF = binopF->in(2);
+ varS = binopF->in(1)->in(1);
+ }
+
+ if (conF != nullptr &&
+ varS != nullptr &&
+ conF->bottom_type()->isa_float_constant() != nullptr &&
+ Matcher::match_rule_supported(Float16NodeFactory::get_float16_binary_oper(binopF->Opcode())) &&
+ Matcher::match_rule_supported(Op_ReinterpretS2HF) &&
+ Matcher::match_rule_supported(Op_ReinterpretHF2S) &&
+ StubRoutines::hf2f_adr() != nullptr &&
+ StubRoutines::f2hf_adr() != nullptr) {
+ jfloat con = conF->bottom_type()->getf();
+ // Conditions under which floating point constant can be considered for a pattern match.
+ // 1. conF must lie within Float16 value range, otherwise we would have rounding issues:
+ // Doing the operation in float32 and then rounding is not the same as
+ // rounding first and doing the operation in float16.
+ // 2. If a constant value is one of the valid IEEE 754 binary32 NaN bit patterns
+ // then it's safe to consider it for pattern match because of the following reasons:
+ // a. As per section 2.8 of JVMS, Java Virtual Machine does not support
+ // signaling NaN value.
+ // b. Any signaling NaN which takes part in a non-comparison expression
+ // results in a quiet NaN but preserves the significand bits of signaling NaN.
+ // c. The pattern being matched includes a Float to Float16 conversion after binary
+ // expression, this downcast will still preserve the significand bits of binary32 NaN.
+ bool isnan = g_isnan((jdouble)con);
+ if (StubRoutines::hf2f(StubRoutines::f2hf(con)) == con || isnan) {
+ Node* newVarHF = phase->transform(new ReinterpretS2HFNode(varS));
+ Node* conHF = phase->makecon(TypeH::make(con));
+ Node* binopHF = nullptr;
+ // Preserving original input order for semantic correctness
+ // of non-commutative operation.
+ if (binopF->in(1) == conF) {
+ binopHF = phase->transform(Float16NodeFactory::make(binopF->Opcode(), binopF->in(0), conHF, newVarHF));
+ } else {
+ binopHF = phase->transform(Float16NodeFactory::make(binopF->Opcode(), binopF->in(0), newVarHF, conHF));
+ }
+ return new ReinterpretHF2SNode(binopHF);
+ }
+ }
+ }
+
return nullptr;
}
+
//=============================================================================
//------------------------------Value------------------------------------------
const Type* ConvF2INode::Value(PhaseGVN* phase) const {
diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp
index a70194274a7..b3ac0a4a1b0 100644
--- a/src/hotspot/share/opto/divnode.cpp
+++ b/src/hotspot/share/opto/divnode.cpp
@@ -28,13 +28,13 @@
#include "opto/convertnode.hpp"
#include "opto/divnode.hpp"
#include "opto/machnode.hpp"
-#include "opto/movenode.hpp"
#include "opto/matcher.hpp"
+#include "opto/movenode.hpp"
#include "opto/mulnode.hpp"
#include "opto/phaseX.hpp"
+#include "opto/runtime.hpp"
#include "opto/subnode.hpp"
#include "utilities/powerOfTwo.hpp"
-#include "opto/runtime.hpp"
// Portions of code courtesy of Clifford Click
@@ -823,39 +823,11 @@ const Type* DivHFNode::Value(PhaseGVN* phase) const {
return bot;
}
- // x/x == 1, we ignore 0/0.
- // Note: if t1 and t2 are zero then result is NaN (JVMS page 213)
- // Does not work for variables because of NaN's
- if (in(1) == in(2) && t1->base() == Type::HalfFloatCon &&
- !g_isnan(t1->getf()) && g_isfinite(t1->getf()) && t1->getf() != 0.0) { // could be negative ZERO or NaN
- return TypeH::ONE;
- }
-
- if (t2 == TypeH::ONE) {
- return t1;
- }
-
- // If divisor is a constant and not zero, divide the numbers
if (t1->base() == Type::HalfFloatCon &&
- t2->base() == Type::HalfFloatCon &&
- t2->getf() != 0.0) {
- // could be negative zero
+ t2->base() == Type::HalfFloatCon) {
return TypeH::make(t1->getf() / t2->getf());
}
- // If the dividend is a constant zero
- // Note: if t1 and t2 are zero then result is NaN (JVMS page 213)
- // Test TypeHF::ZERO is not sufficient as it could be negative zero
-
- if (t1 == TypeH::ZERO && !g_isnan(t2->getf()) && t2->getf() != 0.0) {
- return TypeH::ZERO;
- }
-
- // If divisor or dividend is nan then result is nan.
- if (g_isnan(t1->getf()) || g_isnan(t2->getf())) {
- return TypeH::make(NAN);
- }
-
// Otherwise we give up all hope
return Type::HALF_FLOAT;
}
diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp
index 3dd43f12b2f..295f7fca9f1 100644
--- a/src/hotspot/share/opto/escape.cpp
+++ b/src/hotspot/share/opto/escape.cpp
@@ -29,18 +29,18 @@
#include "libadt/vectset.hpp"
#include "memory/allocation.hpp"
#include "memory/resourceArea.hpp"
-#include "opto/c2compiler.hpp"
#include "opto/arraycopynode.hpp"
+#include "opto/c2compiler.hpp"
#include "opto/callnode.hpp"
+#include "opto/castnode.hpp"
#include "opto/cfgnode.hpp"
#include "opto/compile.hpp"
#include "opto/escape.hpp"
-#include "opto/macro.hpp"
#include "opto/locknode.hpp"
-#include "opto/phaseX.hpp"
+#include "opto/macro.hpp"
#include "opto/movenode.hpp"
#include "opto/narrowptrnode.hpp"
-#include "opto/castnode.hpp"
+#include "opto/phaseX.hpp"
#include "opto/rootnode.hpp"
#include "utilities/macros.hpp"
diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp
index 6576196268e..8859263d8b6 100644
--- a/src/hotspot/share/opto/gcm.cpp
+++ b/src/hotspot/share/opto/gcm.cpp
@@ -29,12 +29,12 @@
#include "opto/c2compiler.hpp"
#include "opto/callnode.hpp"
#include "opto/cfgnode.hpp"
+#include "opto/chaitin.hpp"
#include "opto/machnode.hpp"
#include "opto/opcodes.hpp"
#include "opto/phaseX.hpp"
#include "opto/rootnode.hpp"
#include "opto/runtime.hpp"
-#include "opto/chaitin.hpp"
#include "runtime/deoptimization.hpp"
// Portions of code courtesy of Clifford Click
diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp
index 20feca26ede..02d61596d31 100644
--- a/src/hotspot/share/opto/graphKit.cpp
+++ b/src/hotspot/share/opto/graphKit.cpp
@@ -22,10 +22,10 @@
*
*/
+#include "asm/register.hpp"
+#include "ci/ciObjArray.hpp"
#include "ci/ciUtilities.hpp"
#include "classfile/javaClasses.hpp"
-#include "ci/ciObjArray.hpp"
-#include "asm/register.hpp"
#include "compiler/compileLog.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/c2/barrierSetC2.hpp"
@@ -47,8 +47,8 @@
#include "runtime/deoptimization.hpp"
#include "runtime/sharedRuntime.hpp"
#include "utilities/bitMap.inline.hpp"
-#include "utilities/powerOfTwo.hpp"
#include "utilities/growableArray.hpp"
+#include "utilities/powerOfTwo.hpp"
//----------------------------GraphKit-----------------------------------------
// Main utility constructor.
@@ -3803,6 +3803,8 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable)
assert(!StressReflectiveCode, "stress mode does not use these paths");
// Increase the size limit if we have exact knowledge of array type.
int log2_esize = Klass::layout_helper_log2_element_size(layout_con);
+ assert(fast_size_limit == 0 || count_leading_zeros(fast_size_limit) > static_cast(LogBytesPerLong - log2_esize),
+ "fast_size_limit (%d) overflow when shifted left by %d", fast_size_limit, LogBytesPerLong - log2_esize);
fast_size_limit <<= (LogBytesPerLong - log2_esize);
}
@@ -3853,7 +3855,9 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable)
if (tilen != nullptr && tilen->_lo < 0) {
// Add a manual constraint to a positive range. Cf. array_element_address.
jint size_max = fast_size_limit;
- if (size_max > tilen->_hi) size_max = tilen->_hi;
+ if (size_max > tilen->_hi && tilen->_hi >= 0) {
+ size_max = tilen->_hi;
+ }
const TypeInt* tlcon = TypeInt::make(0, size_max, Type::WidenMin);
// Only do a narrow I2L conversion if the range check passed.
diff --git a/src/hotspot/share/opto/idealGraphPrinter.cpp b/src/hotspot/share/opto/idealGraphPrinter.cpp
index 3721394c112..1ecb46eaf5a 100644
--- a/src/hotspot/share/opto/idealGraphPrinter.cpp
+++ b/src/hotspot/share/opto/idealGraphPrinter.cpp
@@ -155,7 +155,6 @@ void IdealGraphPrinter::init(const char* file_name, bool use_multiple_files, boo
// in the mach where kill projections have no users but should
// appear in the dump.
_traverse_outs = true;
- _should_send_method = true;
_output = nullptr;
buffer[0] = 0;
_depth = 0;
@@ -300,13 +299,11 @@ void IdealGraphPrinter::print_inline_tree(InlineTree *tree) {
void IdealGraphPrinter::print_inlining() {
// Print inline tree
- if (_should_send_method) {
- InlineTree *inlineTree = C->ilt();
- if (inlineTree != nullptr) {
- print_inline_tree(inlineTree);
- } else {
- // print this method only
- }
+ InlineTree *inlineTree = C->ilt();
+ if (inlineTree != nullptr) {
+ print_inline_tree(inlineTree);
+ } else {
+ // print this method only
}
}
@@ -382,7 +379,6 @@ void IdealGraphPrinter::begin_method() {
tail(PROPERTIES_ELEMENT);
- _should_send_method = true;
this->_current_method = method;
_xml->flush();
@@ -975,7 +971,7 @@ void IdealGraphPrinter::print_graph(const char* name, const frame* fr) {
// Print current ideal graph
void IdealGraphPrinter::print(const char* name, Node* node, GrowableArray& visible_nodes, const frame* fr) {
- if (!_current_method || !_should_send_method || node == nullptr) return;
+ if (!_current_method || node == nullptr) return;
if (name == nullptr) {
stringStream graph_name;
diff --git a/src/hotspot/share/opto/idealGraphPrinter.hpp b/src/hotspot/share/opto/idealGraphPrinter.hpp
index 69ba2841506..7e68ce6c00f 100644
--- a/src/hotspot/share/opto/idealGraphPrinter.hpp
+++ b/src/hotspot/share/opto/idealGraphPrinter.hpp
@@ -110,7 +110,6 @@ class IdealGraphPrinter : public CHeapObj {
ciMethod *_current_method;
int _depth;
char buffer[2048];
- bool _should_send_method;
PhaseChaitin* _chaitin;
bool _traverse_outs;
Compile *C;
diff --git a/src/hotspot/share/opto/idealKit.hpp b/src/hotspot/share/opto/idealKit.hpp
index 9916701fad7..fe07716dcff 100644
--- a/src/hotspot/share/opto/idealKit.hpp
+++ b/src/hotspot/share/opto/idealKit.hpp
@@ -26,8 +26,8 @@
#define SHARE_OPTO_IDEALKIT_HPP
#include "opto/addnode.hpp"
-#include "opto/cfgnode.hpp"
#include "opto/castnode.hpp"
+#include "opto/cfgnode.hpp"
#include "opto/connode.hpp"
#include "opto/divnode.hpp"
#include "opto/graphKit.hpp"
diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp
index 8d810e4202f..9daf2e6741e 100644
--- a/src/hotspot/share/opto/ifnode.cpp
+++ b/src/hotspot/share/opto/ifnode.cpp
@@ -32,8 +32,8 @@
#include "opto/loopnode.hpp"
#include "opto/phaseX.hpp"
#include "opto/predicates_enums.hpp"
-#include "opto/runtime.hpp"
#include "opto/rootnode.hpp"
+#include "opto/runtime.hpp"
#include "opto/subnode.hpp"
#include "opto/subtypenode.hpp"
@@ -1046,8 +1046,7 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f
if (failtype != nullptr) {
const TypeInt* type2 = filtered_int_type(igvn, n, fail);
if (type2 != nullptr) {
- failtype = failtype->join(type2)->is_int();
- if (failtype->empty()) {
+ if (failtype->filter(type2) == Type::TOP) {
// previous if determines the result of this if so
// replace Bool with constant
igvn->replace_input_of(this, 1, igvn->intcon(success->_con));
diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp
index e67352d85bd..4f131a39f38 100644
--- a/src/hotspot/share/opto/intrinsicnode.cpp
+++ b/src/hotspot/share/opto/intrinsicnode.cpp
@@ -22,14 +22,14 @@
*
*/
-#include "opto/intrinsicnode.hpp"
#include "opto/addnode.hpp"
-#include "opto/mulnode.hpp"
+#include "opto/intrinsicnode.hpp"
#include "opto/memnode.hpp"
+#include "opto/mulnode.hpp"
#include "opto/phaseX.hpp"
-#include "utilities/population_count.hpp"
#include "utilities/count_leading_zeros.hpp"
#include "utilities/globalDefinitions.hpp"
+#include "utilities/population_count.hpp"
//=============================================================================
// Do not match memory edge.
@@ -135,7 +135,7 @@ Node* CompressBitsNode::Ideal(PhaseGVN* phase, bool can_reshape) {
Node* src = in(1);
Node* mask = in(2);
if (bottom_type()->isa_int()) {
- if (mask->Opcode() == Op_LShiftI && phase->type(mask->in(1))->is_int()->is_con()) {
+ if (mask->Opcode() == Op_LShiftI && phase->type(mask->in(1))->isa_int() && phase->type(mask->in(1))->is_int()->is_con()) {
// compress(x, 1 << n) == (x >> n & 1)
if (phase->type(mask->in(1))->higher_equal(TypeInt::ONE)) {
Node* rshift = phase->transform(new RShiftINode(in(1), mask->in(2)));
@@ -153,7 +153,7 @@ Node* CompressBitsNode::Ideal(PhaseGVN* phase, bool can_reshape) {
}
} else {
assert(bottom_type()->isa_long(), "");
- if (mask->Opcode() == Op_LShiftL && phase->type(mask->in(1))->is_long()->is_con()) {
+ if (mask->Opcode() == Op_LShiftL && phase->type(mask->in(1))->isa_long() && phase->type(mask->in(1))->is_long()->is_con()) {
// compress(x, 1 << n) == (x >> n & 1)
if (phase->type(mask->in(1))->higher_equal(TypeLong::ONE)) {
Node* rshift = phase->transform(new RShiftLNode(in(1), mask->in(2)));
@@ -193,7 +193,7 @@ Node* ExpandBitsNode::Ideal(PhaseGVN* phase, bool can_reshape) {
Node* src = in(1);
Node* mask = in(2);
if (bottom_type()->isa_int()) {
- if (mask->Opcode() == Op_LShiftI && phase->type(mask->in(1))->is_int()->is_con()) {
+ if (mask->Opcode() == Op_LShiftI && phase->type(mask->in(1))->isa_int() && phase->type(mask->in(1))->is_int()->is_con()) {
// expand(x, 1 << n) == (x & 1) << n
if (phase->type(mask->in(1))->higher_equal(TypeInt::ONE)) {
Node* andnode = phase->transform(new AndINode(in(1), phase->makecon(TypeInt::ONE)));
@@ -210,7 +210,7 @@ Node* ExpandBitsNode::Ideal(PhaseGVN* phase, bool can_reshape) {
}
} else {
assert(bottom_type()->isa_long(), "");
- if (mask->Opcode() == Op_LShiftL && phase->type(mask->in(1))->is_long()->is_con()) {
+ if (mask->Opcode() == Op_LShiftL && phase->type(mask->in(1))->isa_long() && phase->type(mask->in(1))->is_long()->is_con()) {
// expand(x, 1 << n) == (x & 1) << n
if (phase->type(mask->in(1))->higher_equal(TypeLong::ONE)) {
Node* andnode = phase->transform(new AndLNode(in(1), phase->makecon(TypeLong::ONE)));
diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp
index 3c204f88bc6..df34e363259 100644
--- a/src/hotspot/share/opto/intrinsicnode.hpp
+++ b/src/hotspot/share/opto/intrinsicnode.hpp
@@ -25,9 +25,9 @@
#ifndef SHARE_OPTO_INTRINSICNODE_HPP
#define SHARE_OPTO_INTRINSICNODE_HPP
+#include "opto/connode.hpp"
#include "opto/node.hpp"
#include "opto/opcodes.hpp"
-#include "opto/connode.hpp"
//----------------------PartialSubtypeCheckNode--------------------------------
diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp
index 0fe246a7679..66ef4b23e2d 100644
--- a/src/hotspot/share/opto/lcm.cpp
+++ b/src/hotspot/share/opto/lcm.cpp
@@ -31,9 +31,9 @@
#include "opto/c2compiler.hpp"
#include "opto/callnode.hpp"
#include "opto/cfgnode.hpp"
+#include "opto/chaitin.hpp"
#include "opto/machnode.hpp"
#include "opto/runtime.hpp"
-#include "opto/chaitin.hpp"
#include "runtime/os.inline.hpp"
#include "runtime/sharedRuntime.hpp"
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index 29f737bce08..94f047dc115 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -23,8 +23,8 @@
*/
#include "asm/macroAssembler.hpp"
-#include "ci/ciUtilities.inline.hpp"
#include "ci/ciSymbols.hpp"
+#include "ci/ciUtilities.inline.hpp"
#include "classfile/vmIntrinsics.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
@@ -47,8 +47,8 @@
#include "opto/narrowptrnode.hpp"
#include "opto/opaquenode.hpp"
#include "opto/parse.hpp"
-#include "opto/runtime.hpp"
#include "opto/rootnode.hpp"
+#include "opto/runtime.hpp"
#include "opto/subnode.hpp"
#include "opto/vectornode.hpp"
#include "prims/jvmtiExport.hpp"
@@ -564,7 +564,7 @@ bool LibraryCallKit::try_to_inline(int predicate) {
case vmIntrinsics::_getCallerClass: return inline_native_Reflection_getCallerClass();
- case vmIntrinsics::_Reference_get: return inline_reference_get();
+ case vmIntrinsics::_Reference_get0: return inline_reference_get0();
case vmIntrinsics::_Reference_refersTo0: return inline_reference_refersTo0(false);
case vmIntrinsics::_PhantomReference_refersTo0: return inline_reference_refersTo0(true);
case vmIntrinsics::_Reference_clear0: return inline_reference_clear0(false);
@@ -1575,9 +1575,14 @@ bool LibraryCallKit::inline_string_toBytesU() {
Node* src_start = array_element_address(value, offset, T_CHAR);
Node* dst_start = basic_plus_adr(newcopy, arrayOopDesc::base_offset_in_bytes(T_BYTE));
- // Check if src array address is aligned to HeapWordSize (dst is always aligned)
- const TypeInt* toffset = gvn().type(offset)->is_int();
- bool aligned = toffset->is_con() && ((toffset->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
+ // Check if dst array address is aligned to HeapWordSize
+ bool aligned = (arrayOopDesc::base_offset_in_bytes(T_BYTE) % HeapWordSize == 0);
+ // If true, then check if src array address is aligned to HeapWordSize
+ if (aligned) {
+ const TypeInt* toffset = gvn().type(offset)->is_int();
+ aligned = toffset->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) +
+ toffset->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
+ }
// Figure out which arraycopy runtime method to call (disjoint, uninitialized).
const char* copyfunc_name = "arraycopy";
@@ -1658,8 +1663,8 @@ bool LibraryCallKit::inline_string_getCharsU() {
// Check if array addresses are aligned to HeapWordSize
const TypeInt* tsrc = gvn().type(src_begin)->is_int();
const TypeInt* tdst = gvn().type(dst_begin)->is_int();
- bool aligned = tsrc->is_con() && ((tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) &&
- tdst->is_con() && ((tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
+ bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) &&
+ tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
// Figure out which arraycopy runtime method to call (disjoint, uninitialized).
const char* copyfunc_name = "arraycopy";
@@ -6580,6 +6585,10 @@ bool LibraryCallKit::inline_vectorizedMismatch() {
memory_phi = _gvn.transform(memory_phi);
result_phi = _gvn.transform(result_phi);
+ record_for_igvn(exit_block);
+ record_for_igvn(memory_phi);
+ record_for_igvn(result_phi);
+
set_control(exit_block);
set_all_memory(memory_phi);
set_result(result_phi);
@@ -6914,9 +6923,9 @@ bool LibraryCallKit::inline_updateByteBufferAdler32() {
return true;
}
-//----------------------------inline_reference_get----------------------------
+//----------------------------inline_reference_get0----------------------------
// public T java.lang.ref.Reference.get();
-bool LibraryCallKit::inline_reference_get() {
+bool LibraryCallKit::inline_reference_get0() {
const int referent_offset = java_lang_ref_Reference::referent_offset();
// Get the argument:
diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp
index 1be08df32ae..94b02290ccd 100644
--- a/src/hotspot/share/opto/library_call.hpp
+++ b/src/hotspot/share/opto/library_call.hpp
@@ -22,12 +22,15 @@
*
*/
+#ifndef SHARE_OPTO_LIBRARY_CALL_HPP
+#define SHARE_OPTO_LIBRARY_CALL_HPP
+
#include "ci/ciMethod.hpp"
#include "classfile/javaClasses.hpp"
#include "opto/callGenerator.hpp"
-#include "opto/graphKit.hpp"
#include "opto/castnode.hpp"
#include "opto/convertnode.hpp"
+#include "opto/graphKit.hpp"
#include "opto/intrinsicnode.hpp"
#include "opto/movenode.hpp"
@@ -300,7 +303,7 @@ class LibraryCallKit : public GraphKit {
bool inline_bitshuffle_methods(vmIntrinsics::ID id);
bool inline_compare_unsigned(vmIntrinsics::ID id);
bool inline_divmod_methods(vmIntrinsics::ID id);
- bool inline_reference_get();
+ bool inline_reference_get0();
bool inline_reference_refersTo0(bool is_phantom);
bool inline_reference_clear0(bool is_phantom);
bool inline_Class_cast();
@@ -416,3 +419,4 @@ class LibraryCallKit : public GraphKit {
bool inline_blackhole();
};
+#endif // SHARE_OPTO_LIBRARY_CALL_HPP
diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp
index 96e0fd26d0d..477ea48d419 100644
--- a/src/hotspot/share/opto/loopPredicate.cpp
+++ b/src/hotspot/share/opto/loopPredicate.cpp
@@ -35,6 +35,7 @@
#include "opto/predicates.hpp"
#include "opto/rootnode.hpp"
#include "opto/subnode.hpp"
+
#include
#include
diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp
index 58105afd9d5..afe6a076268 100644
--- a/src/hotspot/share/opto/loopTransform.cpp
+++ b/src/hotspot/share/opto/loopTransform.cpp
@@ -33,8 +33,8 @@
#include "opto/convertnode.hpp"
#include "opto/divnode.hpp"
#include "opto/loopnode.hpp"
-#include "opto/mulnode.hpp"
#include "opto/movenode.hpp"
+#include "opto/mulnode.hpp"
#include "opto/opaquenode.hpp"
#include "opto/phase.hpp"
#include "opto/predicates.hpp"
diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp
index 3130df54681..e5efdb2a202 100644
--- a/src/hotspot/share/opto/loopnode.cpp
+++ b/src/hotspot/share/opto/loopnode.cpp
@@ -298,19 +298,52 @@ IdealLoopTree* PhaseIdealLoop::insert_outer_loop(IdealLoopTree* loop, LoopNode*
return outer_ilt;
}
-// Create a skeleton strip mined outer loop: a Loop head before the
-// inner strip mined loop, a safepoint and an exit condition guarded
-// by an opaque node after the inner strip mined loop with a backedge
-// to the loop head. The inner strip mined loop is left as it is. Only
-// once loop optimizations are over, do we adjust the inner loop exit
-// condition to limit its number of iterations, set the outer loop
-// exit condition and add Phis to the outer loop head. Some loop
-// optimizations that operate on the inner strip mined loop need to be
-// aware of the outer strip mined loop: loop unswitching needs to
-// clone the outer loop as well as the inner, unrolling needs to only
-// clone the inner loop etc. No optimizations need to change the outer
-// strip mined loop as it is only a skeleton.
-IdealLoopTree* PhaseIdealLoop::create_outer_strip_mined_loop(BoolNode *test, Node *cmp, Node *init_control,
+// Create a skeleton strip mined outer loop: an OuterStripMinedLoop head before the inner strip mined CountedLoop, a
+// SafePoint on exit of the inner CountedLoopEnd and an OuterStripMinedLoopEnd test that can't constant fold until loop
+// optimizations are over. The inner strip mined loop is left as it is. Only once loop optimizations are over, do we
+// adjust the inner loop exit condition to limit its number of iterations, set the outer loop exit condition and add
+// Phis to the outer loop head. Some loop optimizations that operate on the inner strip mined loop need to be aware of
+// the outer strip mined loop: loop unswitching needs to clone the outer loop as well as the inner, unrolling needs to
+// only clone the inner loop etc. No optimizations need to change the outer strip mined loop as it is only a skeleton.
+//
+// Schematically:
+//
+// OuterStripMinedLoop -------|
+// | |
+// CountedLoop ----------- | |
+// \- Phi (iv) -| | |
+// / \ | | |
+// CmpI AddI --| | |
+// \ | |
+// Bool | |
+// \ | |
+// CountedLoopEnd | |
+// / \ | |
+// IfFalse IfTrue--------| |
+// | |
+// SafePoint |
+// | |
+// OuterStripMinedLoopEnd |
+// / \ |
+// IfFalse IfTrue-----------|
+// |
+//
+//
+// As loop optimizations transform the inner loop, the outer strip mined loop stays mostly unchanged. The only exception
+// is nodes referenced from the SafePoint and sunk from the inner loop: they end up in the outer strip mined loop.
+//
+// Not adding Phis to the outer loop head from the beginning, and only adding them after loop optimizations does not
+// conform to C2's IR rules: any variable or memory slice that is mutated in a loop should have a Phi. The main
+// motivation for such a design that doesn't conform to C2's IR rules is to allow existing loop optimizations to be
+// mostly unaffected by the outer strip mined loop: the only extra step needed in most cases is to step over the
+// OuterStripMinedLoop. The main drawback is that once loop optimizations are over, an extra step is needed to finish
+// constructing the outer loop. This is handled by OuterStripMinedLoopNode::adjust_strip_mined_loop().
+//
+// Adding Phis to the outer loop is largely straightforward: there needs to be one Phi in the outer loop for every Phi
+// in the inner loop. Things may be more complicated for sunk Store nodes: there may not be any inner loop Phi left
+// after sinking for a particular memory slice but the outer loop needs a Phi. See
+// OuterStripMinedLoopNode::handle_sunk_stores_when_finishing_construction()
+IdealLoopTree* PhaseIdealLoop::create_outer_strip_mined_loop(Node* init_control,
IdealLoopTree* loop, float cl_prob, float le_fcnt,
Node*& entry_control, Node*& iffalse) {
Node* outer_test = intcon(0);
@@ -1656,7 +1689,9 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_
return false;
}
- if (xphi->Opcode() == Op_Cast(iv_bt)) {
+ // Iteratively uncast the loop induction variable
+ // until no more CastII/CastLL nodes are found.
+ while (xphi->Opcode() == Op_Cast(iv_bt)) {
xphi = xphi->in(1);
}
@@ -2255,9 +2290,8 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_
is_deleteable_safept(sfpt);
IdealLoopTree* outer_ilt = nullptr;
if (strip_mine_loop) {
- outer_ilt = create_outer_strip_mined_loop(test, cmp, init_control, loop,
- cl_prob, le->_fcnt, entry_control,
- iffalse);
+ outer_ilt = create_outer_strip_mined_loop(init_control, loop, cl_prob, le->_fcnt,
+ entry_control, iffalse);
}
// Now setup a new CountedLoopNode to replace the existing LoopNode
@@ -2870,10 +2904,11 @@ BaseCountedLoopNode* BaseCountedLoopNode::make(Node* entry, Node* backedge, Basi
return new LongCountedLoopNode(entry, backedge);
}
-void OuterStripMinedLoopNode::fix_sunk_stores(CountedLoopEndNode* inner_cle, LoopNode* inner_cl, PhaseIterGVN* igvn,
- PhaseIdealLoop* iloop) {
- Node* cle_out = inner_cle->proj_out(false);
- Node* cle_tail = inner_cle->proj_out(true);
+void OuterStripMinedLoopNode::fix_sunk_stores_when_back_to_counted_loop(PhaseIterGVN* igvn,
+ PhaseIdealLoop* iloop) const {
+ CountedLoopNode* inner_cl = inner_counted_loop();
+ IfFalseNode* cle_out = inner_loop_exit();
+
if (cle_out->outcnt() > 1) {
// Look for chains of stores that were sunk
// out of the inner loop and are in the outer loop
@@ -2988,11 +3023,90 @@ void OuterStripMinedLoopNode::fix_sunk_stores(CountedLoopEndNode* inner_cle, Loo
}
}
+// The outer strip mined loop is initially only partially constructed. In particular Phis are omitted.
+// See comment above: PhaseIdealLoop::create_outer_strip_mined_loop()
+// We're now in the process of finishing the construction of the outer loop. For each Phi in the inner loop, a Phi in
+// the outer loop was just now created. However, Sunk Stores cause an extra challenge:
+// 1) If all Stores in the inner loop were sunk for a particular memory slice, there's no Phi left for that memory slice
+// in the inner loop anymore, and hence we did not yet add a Phi for the outer loop. So an extra Phi must now be
+// added for each chain of sunk Stores for a particular memory slice.
+// 2) If some Stores were sunk and some left in the inner loop, a Phi was already created in the outer loop but
+// its backedge input wasn't wired correctly to the last Store of the chain: the backedge input was set to the
+// backedge of the inner loop Phi instead, but it needs to be the last Store of the chain in the outer loop. We now
+// have to fix that too.
+void OuterStripMinedLoopNode::handle_sunk_stores_when_finishing_construction(PhaseIterGVN* igvn) {
+ IfFalseNode* cle_exit_proj = inner_loop_exit();
+
+ // Find Sunk stores: Sunk stores are pinned on the loop exit projection of the inner loop. Indeed, because Sunk Stores
+ // modify the memory state captured by the SafePoint in the outer strip mined loop, they must be above it. The
+ // SafePoint's control input is the loop exit projection. It's also the only control out of the inner loop above the
+ // SafePoint.
+#ifdef ASSERT
+ int stores_in_outer_loop_cnt = 0;
+ for (DUIterator_Fast imax, i = cle_exit_proj->fast_outs(imax); i < imax; i++) {
+ Node* u = cle_exit_proj->fast_out(i);
+ if (u->is_Store()) {
+ stores_in_outer_loop_cnt++;
+ }
+ }
+#endif
+
+ // Sunk stores are reachable from the memory state of the outer loop safepoint
+ SafePointNode* safepoint = outer_safepoint();
+ MergeMemNode* mm = safepoint->in(TypeFunc::Memory)->isa_MergeMem();
+ if (mm == nullptr) {
+ // There is no MergeMem, which should only happen if there was no memory node
+ // sunk out of the loop.
+ assert(stores_in_outer_loop_cnt == 0, "inconsistent");
+ return;
+ }
+ DEBUG_ONLY(int stores_in_outer_loop_cnt2 = 0);
+ for (MergeMemStream mms(mm); mms.next_non_empty();) {
+ Node* mem = mms.memory();
+ // Traverse up the chain of stores to find the first store pinned
+ // at the loop exit projection.
+ Node* last = mem;
+ Node* first = nullptr;
+ while (mem->is_Store() && mem->in(0) == cle_exit_proj) {
+ DEBUG_ONLY(stores_in_outer_loop_cnt2++);
+ first = mem;
+ mem = mem->in(MemNode::Memory);
+ }
+ if (first != nullptr) {
+ // Found a chain of Stores that were sunk
+ // Do we already have a memory Phi for that slice on the outer loop? If that is the case, that Phi was created
+ // by cloning an inner loop Phi. The inner loop Phi should have mem, the memory state of the first Store out of
+ // the inner loop, as input on the backedge. So does the outer loop Phi given it's a clone.
+ Node* phi = nullptr;
+ for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
+ Node* u = mem->fast_out(i);
+ if (u->is_Phi() && u->in(0) == this && u->in(LoopBackControl) == mem) {
+ assert(phi == nullptr, "there should be only one");
+ phi = u;
+ PRODUCT_ONLY(break);
+ }
+ }
+ if (phi == nullptr) {
+ // No outer loop Phi? create one
+ phi = PhiNode::make(this, last);
+ phi->set_req(EntryControl, mem);
+ phi = igvn->transform(phi);
+ igvn->replace_input_of(first, MemNode::Memory, phi);
+ } else {
+ // Fix memory state along the backedge: it should be the last sunk Store of the chain
+ igvn->replace_input_of(phi, LoopBackControl, last);
+ }
+ }
+ }
+ assert(stores_in_outer_loop_cnt == stores_in_outer_loop_cnt2, "inconsistent");
+}
+
void OuterStripMinedLoopNode::adjust_strip_mined_loop(PhaseIterGVN* igvn) {
+ verify_strip_mined(1);
// Look for the outer & inner strip mined loop, reduce number of
// iterations of the inner loop, set exit condition of outer loop,
// construct required phi nodes for outer loop.
- CountedLoopNode* inner_cl = unique_ctrl_out()->as_CountedLoop();
+ CountedLoopNode* inner_cl = inner_counted_loop();
assert(inner_cl->is_strip_mined(), "inner loop should be strip mined");
if (LoopStripMiningIter == 0) {
remove_outer_loop_and_safepoint(igvn);
@@ -3010,7 +3124,7 @@ void OuterStripMinedLoopNode::adjust_strip_mined_loop(PhaseIterGVN* igvn) {
inner_cl->clear_strip_mined();
return;
}
- CountedLoopEndNode* inner_cle = inner_cl->loopexit();
+ CountedLoopEndNode* inner_cle = inner_counted_loop_end();
int stride = inner_cl->stride_con();
// For a min int stride, LoopStripMiningIter * stride overflows the int range for all values of LoopStripMiningIter
@@ -3091,8 +3205,9 @@ void OuterStripMinedLoopNode::adjust_strip_mined_loop(PhaseIterGVN* igvn) {
}
Node* iv_phi = nullptr;
- // Make a clone of each phi in the inner loop
- // for the outer loop
+ // Make a clone of each phi in the inner loop for the outer loop
+ // When Stores were Sunk, after this step, a Phi may still be missing or its backedge incorrectly wired. See
+ // handle_sunk_stores_when_finishing_construction()
for (uint i = 0; i < inner_cl->outcnt(); i++) {
Node* u = inner_cl->raw_out(i);
if (u->is_Phi()) {
@@ -3111,6 +3226,8 @@ void OuterStripMinedLoopNode::adjust_strip_mined_loop(PhaseIterGVN* igvn) {
}
}
+ handle_sunk_stores_when_finishing_construction(igvn);
+
if (iv_phi != nullptr) {
// Now adjust the inner loop's exit condition
Node* limit = inner_cl->limit();
@@ -3166,7 +3283,7 @@ void OuterStripMinedLoopNode::transform_to_counted_loop(PhaseIterGVN* igvn, Phas
CountedLoopEndNode* inner_cle = inner_cl->loopexit();
Node* safepoint = outer_safepoint();
- fix_sunk_stores(inner_cle, inner_cl, igvn, iloop);
+ fix_sunk_stores_when_back_to_counted_loop(igvn, iloop);
// make counted loop exit test always fail
ConINode* zero = igvn->intcon(0);
diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp
index 7670f6c620e..e056bda28e2 100644
--- a/src/hotspot/share/opto/loopnode.hpp
+++ b/src/hotspot/share/opto/loopnode.hpp
@@ -573,7 +573,8 @@ class LoopLimitNode : public Node {
// Support for strip mining
class OuterStripMinedLoopNode : public LoopNode {
private:
- static void fix_sunk_stores(CountedLoopEndNode* inner_cle, LoopNode* inner_cl, PhaseIterGVN* igvn, PhaseIdealLoop* iloop);
+ void fix_sunk_stores_when_back_to_counted_loop(PhaseIterGVN* igvn, PhaseIdealLoop* iloop) const;
+ void handle_sunk_stores_when_finishing_construction(PhaseIterGVN* igvn);
public:
OuterStripMinedLoopNode(Compile* C, Node *entry, Node *backedge)
@@ -589,6 +590,10 @@ class OuterStripMinedLoopNode : public LoopNode {
virtual OuterStripMinedLoopEndNode* outer_loop_end() const;
virtual IfFalseNode* outer_loop_exit() const;
virtual SafePointNode* outer_safepoint() const;
+ CountedLoopNode* inner_counted_loop() const { return unique_ctrl_out()->as_CountedLoop(); }
+ CountedLoopEndNode* inner_counted_loop_end() const { return inner_counted_loop()->loopexit(); }
+ IfFalseNode* inner_loop_exit() const { return inner_counted_loop_end()->proj_out(false)->as_IfFalse(); }
+
void adjust_strip_mined_loop(PhaseIterGVN* igvn);
void remove_outer_loop_and_safepoint(PhaseIterGVN* igvn) const;
@@ -1293,7 +1298,7 @@ class PhaseIdealLoop : public PhaseTransform {
void add_parse_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt);
SafePointNode* find_safepoint(Node* back_control, Node* x, IdealLoopTree* loop);
IdealLoopTree* insert_outer_loop(IdealLoopTree* loop, LoopNode* outer_l, Node* outer_ift);
- IdealLoopTree* create_outer_strip_mined_loop(BoolNode *test, Node *cmp, Node *init_control,
+ IdealLoopTree* create_outer_strip_mined_loop(Node* init_control,
IdealLoopTree* loop, float cl_prob, float le_fcnt,
Node*& entry_control, Node*& iffalse);
diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp
index ddeb5f84d49..b33895adf5f 100644
--- a/src/hotspot/share/opto/loopopts.cpp
+++ b/src/hotspot/share/opto/loopopts.cpp
@@ -30,12 +30,11 @@
#include "opto/callnode.hpp"
#include "opto/castnode.hpp"
#include "opto/connode.hpp"
-#include "opto/castnode.hpp"
#include "opto/divnode.hpp"
#include "opto/loopnode.hpp"
#include "opto/matcher.hpp"
-#include "opto/mulnode.hpp"
#include "opto/movenode.hpp"
+#include "opto/mulnode.hpp"
#include "opto/opaquenode.hpp"
#include "opto/rootnode.hpp"
#include "opto/subnode.hpp"
diff --git a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp
index e2209fddbdf..10de940c0c2 100644
--- a/src/hotspot/share/opto/macroArrayCopy.cpp
+++ b/src/hotspot/share/opto/macroArrayCopy.cpp
@@ -24,14 +24,14 @@
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/tlab_globals.hpp"
-#include "opto/arraycopynode.hpp"
#include "oops/objArrayKlass.hpp"
+#include "opto/arraycopynode.hpp"
+#include "opto/castnode.hpp"
#include "opto/convertnode.hpp"
-#include "opto/vectornode.hpp"
#include "opto/graphKit.hpp"
#include "opto/macro.hpp"
#include "opto/runtime.hpp"
-#include "opto/castnode.hpp"
+#include "opto/vectornode.hpp"
#include "runtime/stubRoutines.hpp"
#include "utilities/align.hpp"
#include "utilities/powerOfTwo.hpp"
diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp
index baf43b0d538..2ee2ded17b6 100644
--- a/src/hotspot/share/opto/matcher.hpp
+++ b/src/hotspot/share/opto/matcher.hpp
@@ -65,13 +65,8 @@ class Matcher : public PhaseTransform {
Node_Stack::push(n, (uint)ns);
}
void push(Node *n, Node_State ns, Node *parent, int indx) {
- ++_inode_top;
- if ((_inode_top + 1) >= _inode_max) grow();
- _inode_top->node = parent;
- _inode_top->indx = (uint)indx;
- ++_inode_top;
- _inode_top->node = n;
- _inode_top->indx = (uint)ns;
+ Node_Stack::push(parent, (uint)indx);
+ Node_Stack::push(n, (uint)ns);
}
Node *parent() {
pop();
diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp
index 24b81b894cb..1da8f429359 100644
--- a/src/hotspot/share/opto/memnode.cpp
+++ b/src/hotspot/share/opto/memnode.cpp
@@ -34,7 +34,6 @@
#include "opto/addnode.hpp"
#include "opto/arraycopynode.hpp"
#include "opto/cfgnode.hpp"
-#include "opto/regalloc.hpp"
#include "opto/compile.hpp"
#include "opto/connode.hpp"
#include "opto/convertnode.hpp"
@@ -46,6 +45,7 @@
#include "opto/mulnode.hpp"
#include "opto/narrowptrnode.hpp"
#include "opto/phaseX.hpp"
+#include "opto/regalloc.hpp"
#include "opto/regmask.hpp"
#include "opto/rootnode.hpp"
#include "opto/traceMergeStoresTag.hpp"
diff --git a/src/hotspot/share/opto/mempointer.cpp b/src/hotspot/share/opto/mempointer.cpp
index bede753ff07..e400e40e4a2 100644
--- a/src/hotspot/share/opto/mempointer.cpp
+++ b/src/hotspot/share/opto/mempointer.cpp
@@ -22,10 +22,10 @@
*
*/
-#include "opto/mempointer.hpp"
+#include "classfile/vmSymbols.hpp"
#include "opto/addnode.hpp"
+#include "opto/mempointer.hpp"
#include "utilities/resourceHash.hpp"
-#include "classfile/vmSymbols.hpp"
MemPointerParserCallback MemPointerParserCallback::_empty;
diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp
index 7633f68ea1e..8f6c67c16f5 100644
--- a/src/hotspot/share/opto/node.cpp
+++ b/src/hotspot/share/opto/node.cpp
@@ -2798,7 +2798,6 @@ const RegMask &Node::in_RegMask(uint) const {
}
void Node_Array::grow(uint i) {
- _nesting.check(_a); // Check if a potential reallocation in the arena is safe
assert(i >= _max, "Should have been checked before, use maybe_grow?");
assert(_max > 0, "invariant");
uint old = _max;
@@ -3038,10 +3037,6 @@ void Unique_Node_List::remove_useless_nodes(VectorSet &useful) {
//=============================================================================
void Node_Stack::grow() {
- _nesting.check(_a); // Check if a potential reallocation in the arena is safe
- if (_inode_top < _inode_max) {
- return; // No need to grow
- }
size_t old_top = pointer_delta(_inode_top,_inodes,sizeof(INode)); // save _top
size_t old_max = pointer_delta(_inode_max,_inodes,sizeof(INode));
size_t max = old_max << 1; // max * 2
diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp
index 843baf48cf8..2bbb10879f5 100644
--- a/src/hotspot/share/opto/node.hpp
+++ b/src/hotspot/share/opto/node.hpp
@@ -1633,6 +1633,7 @@ class Node_Array : public AnyObj {
// Grow array to required capacity
void maybe_grow(uint i) {
+ _nesting.check(_a); // Check if a potential reallocation in the arena is safe
if (i >= _max) {
grow(i);
}
@@ -1884,7 +1885,15 @@ class Node_Stack {
INode *_inodes; // Array storage for the stack
Arena *_a; // Arena to allocate in
ReallocMark _nesting; // Safety checks for arena reallocation
+
+ void maybe_grow() {
+ _nesting.check(_a); // Check if a potential reallocation in the arena is safe
+ if (_inode_top >= _inode_max) {
+ grow();
+ }
+ }
void grow();
+
public:
Node_Stack(int size) {
size_t max = (size > OptoNodeListSize) ? size : OptoNodeListSize;
@@ -1907,7 +1916,7 @@ class Node_Stack {
}
void push(Node *n, uint i) {
++_inode_top;
- grow();
+ maybe_grow();
INode *top = _inode_top; // optimization
top->node = n;
top->indx = i;
diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp
index f0a7b742998..cadc2511770 100644
--- a/src/hotspot/share/opto/output.cpp
+++ b/src/hotspot/share/opto/output.cpp
@@ -36,8 +36,8 @@
#include "memory/allocation.hpp"
#include "opto/ad.hpp"
#include "opto/block.hpp"
-#include "opto/c2compiler.hpp"
#include "opto/c2_MacroAssembler.hpp"
+#include "opto/c2compiler.hpp"
#include "opto/callnode.hpp"
#include "opto/cfgnode.hpp"
#include "opto/locknode.hpp"
diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp
index 953afb0e830..1ba8e145e7d 100644
--- a/src/hotspot/share/opto/phaseX.cpp
+++ b/src/hotspot/share/opto/phaseX.cpp
@@ -1072,7 +1072,11 @@ void PhaseIterGVN::optimize() {
#ifdef ASSERT
void PhaseIterGVN::verify_optimize() {
- if (is_verify_Value()) {
+ assert(_worklist.size() == 0, "igvn worklist must be empty before verify");
+
+ if (is_verify_Value() ||
+ is_verify_Ideal() ||
+ is_verify_Identity()) {
ResourceMark rm;
Unique_Node_List worklist;
bool failure = false;
@@ -1080,7 +1084,10 @@ void PhaseIterGVN::verify_optimize() {
worklist.push(C->root());
for (uint j = 0; j < worklist.size(); ++j) {
Node* n = worklist.at(j);
- failure |= verify_node_value(n);
+ if (is_verify_Value()) { failure |= verify_Value_for(n); }
+ if (is_verify_Ideal()) { failure |= verify_Ideal_for(n, false); }
+ if (is_verify_Ideal()) { failure |= verify_Ideal_for(n, true); }
+ if (is_verify_Identity()) { failure |= verify_Identity_for(n); }
// traverse all inputs and outputs
for (uint i = 0; i < n->req(); i++) {
if (n->in(i) != nullptr) {
@@ -1097,6 +1104,27 @@ void PhaseIterGVN::verify_optimize() {
// in the verification code above if that is not possible for some reason (like Load nodes).
assert(!failure, "Missed optimization opportunity in PhaseIterGVN");
}
+
+ verify_empty_worklist(nullptr);
+}
+
+void PhaseIterGVN::verify_empty_worklist(Node* node) {
+ // Verify that the igvn worklist is empty. If no optimization happened, then
+ // nothing needs to be on the worklist.
+ if (_worklist.size() == 0) { return; }
+
+ stringStream ss; // Print as a block without tty lock.
+ for (uint j = 0; j < _worklist.size(); j++) {
+ Node* n = _worklist.at(j);
+ ss.print("igvn.worklist[%d] ", j);
+ n->dump("\n", false, &ss);
+ }
+ if (_worklist.size() != 0 && node != nullptr) {
+ ss.print_cr("Previously optimized:");
+ node->dump("\n", false, &ss);
+ }
+ tty->print_cr("%s", ss.as_string());
+ assert(false, "igvn worklist must still be empty after verify");
}
// Check that type(n) == n->Value(), return true if we have a failure.
@@ -1104,7 +1132,7 @@ void PhaseIterGVN::verify_optimize() {
// (1) Integer "widen" changes, but the range is the same.
// (2) LoadNode performs deep traversals. Load is not notified for changes far away.
// (3) CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away.
-bool PhaseIterGVN::verify_node_value(Node* n) {
+bool PhaseIterGVN::verify_Value_for(Node* n) {
// If we assert inside type(n), because the type is still a null, then maybe
// the node never went through gvn.transform, which would be a bug.
const Type* told = type(n);
@@ -1149,15 +1177,873 @@ bool PhaseIterGVN::verify_node_value(Node* n) {
// after loop-opts, so that should take care of many of these cases.
return false;
}
- tty->cr();
- tty->print_cr("Missed Value optimization:");
- n->dump_bfs(1, nullptr, "");
- tty->print_cr("Current type:");
- told->dump_on(tty);
- tty->cr();
- tty->print_cr("Optimized type:");
- tnew->dump_on(tty);
- tty->cr();
+
+ stringStream ss; // Print as a block without tty lock.
+ ss.cr();
+ ss.print_cr("Missed Value optimization:");
+ n->dump_bfs(1, nullptr, "", &ss);
+ ss.print_cr("Current type:");
+ told->dump_on(&ss);
+ ss.cr();
+ ss.print_cr("Optimized type:");
+ tnew->dump_on(&ss);
+ ss.cr();
+ tty->print_cr("%s", ss.as_string());
+ return true;
+}
+
+// Check that all Ideal optimizations that could be done were done.
+// Returns true if it found missed optimization opportunities and
+// false otherwise (no missed optimization, or skipped verification).
+bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) {
+ // First, we check a list of exceptions, where we skip verification,
+ // because there are known cases where Ideal can optimize after IGVN.
+ // Some may be expected and cannot be fixed, and others should be fixed.
+ switch (n->Opcode()) {
+ // RangeCheckNode::Ideal looks up the chain for about 999 nodes
+ // (see "Range-Check scan limit"). So, it is possible that something
+ // is optimized in that input subgraph, and the RangeCheck was not
+ // added to the worklist because it would be too expensive to walk
+ // down the graph for 1000 nodes and put all on the worklist.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=0100 -Xbatch --version
+ case Op_RangeCheck:
+ return false;
+
+ // IfNode::Ideal does:
+ // Node* prev_dom = search_identical(dist, igvn);
+ // which means we seach up the CFG, traversing at most up to a distance.
+ // If anything happens rather far away from the If, we may not put the If
+ // back on the worklist.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=0100 -Xcomp --version
+ case Op_If:
+ return false;
+
+ // IfNode::simple_subsuming
+ // Looks for dominating test that subsumes the current test.
+ // Notification could be difficult because of larger distance.
+ //
+ // Found with:
+ // runtime/exceptionMsgs/ArrayIndexOutOfBoundsException/ArrayIndexOutOfBoundsExceptionTest.java#id1
+ // -XX:VerifyIterativeGVN=1110
+ case Op_CountedLoopEnd:
+ return false;
+
+ // LongCountedLoopEndNode::Ideal
+ // Probably same issue as above.
+ //
+ // Found with:
+ // compiler/predicates/assertion/TestAssertionPredicates.java#NoLoopPredicationXbatch
+ // -XX:StressLongCountedLoop=2000000 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
+ case Op_LongCountedLoopEnd:
+ return false;
+
+ // RegionNode::Ideal does "Skip around the useless IF diamond".
+ // 245 IfTrue === 244
+ // 258 If === 245 257
+ // 259 IfTrue === 258 [[ 263 ]]
+ // 260 IfFalse === 258 [[ 263 ]]
+ // 263 Region === 263 260 259 [[ 263 268 ]]
+ // to
+ // 245 IfTrue === 244
+ // 263 Region === 263 245 _ [[ 263 268 ]]
+ //
+ // "Useless" means that there is no code in either branch of the If.
+ // I found a case where this was not done yet during IGVN.
+ // Why does the Region not get added to IGVN worklist when the If diamond becomes useless?
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=0100 -Xcomp --version
+ case Op_Region:
+ return false;
+
+ // In AddNode::Ideal, we call "commute", which swaps the inputs so
+ // that smaller idx are first. Tracking it back, it led me to
+ // PhaseIdealLoop::remix_address_expressions which swapped the edges.
+ //
+ // Example:
+ // Before PhaseIdealLoop::remix_address_expressions
+ // 154 AddI === _ 12 144
+ // After PhaseIdealLoop::remix_address_expressions
+ // 154 AddI === _ 144 12
+ // After AddNode::Ideal
+ // 154 AddI === _ 12 144
+ //
+ // I suspect that the node should be added to the IGVN worklist after
+ // PhaseIdealLoop::remix_address_expressions
+ //
+ // This is the only case I looked at, there may be others. Found like this:
+ // java -XX:VerifyIterativeGVN=0100 -Xbatch --version
+ //
+ // The following hit the same logic in PhaseIdealLoop::remix_address_expressions.
+ //
+ // Note: currently all of these fail also for other reasons, for example
+ // because of "commute" doing the reordering with the phi below. Once
+ // that is resolved, we can come back to this issue here.
+ //
+ // case Op_AddD:
+ // case Op_AddI:
+ // case Op_AddL:
+ // case Op_AddF:
+ // case Op_MulI:
+ // case Op_MulL:
+ // case Op_MulF:
+ // case Op_MulD:
+ // if (n->in(1)->_idx > n->in(2)->_idx) {
+ // // Expect "commute" to revert this case.
+ // return false;
+ // }
+ // break; // keep verifying
+
+ // AddFNode::Ideal calls "commute", which can reorder the inputs for this:
+ // Check for tight loop increments: Loop-phi of Add of loop-phi
+ // It wants to take the phi into in(1):
+ // 471 Phi === 435 38 390
+ // 390 AddF === _ 471 391
+ //
+ // Other Associative operators are also affected equally.
+ //
+ // Investigate why this does not happen earlier during IGVN.
+ //
+ // Found with:
+ // test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_AddD:
+ //case Op_AddI: // Also affected for other reasons, see case further down.
+ //case Op_AddL: // Also affected for other reasons, see case further down.
+ case Op_AddF:
+ case Op_MulI:
+ case Op_MulL:
+ case Op_MulF:
+ case Op_MulD:
+ case Op_MinF:
+ case Op_MinD:
+ case Op_MaxF:
+ case Op_MaxD:
+ // XorINode::Ideal
+ // Found with:
+ // compiler/intrinsics/chacha/TestChaCha20.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_XorI:
+ case Op_XorL:
+ // It seems we may have similar issues with the HF cases.
+ // Found with aarch64:
+ // compiler/vectorization/TestFloat16VectorOperations.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_AddHF:
+ case Op_MulHF:
+ case Op_MaxHF:
+ case Op_MinHF:
+ return false;
+
+ // In MulNode::Ideal the edges can be swapped to help value numbering:
+ //
+ // // We are OK if right is a constant, or right is a load and
+ // // left is a non-constant.
+ // if( !(t2->singleton() ||
+ // (in(2)->is_Load() && !(t1->singleton() || in(1)->is_Load())) ) ) {
+ // if( t1->singleton() || // Left input is a constant?
+ // // Otherwise, sort inputs (commutativity) to help value numbering.
+ // (in(1)->_idx > in(2)->_idx) ) {
+ // swap_edges(1, 2);
+ //
+ // Why was this not done earlier during IGVN?
+ //
+ // Found with:
+ // test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_AndI:
+ // Same for AndL.
+ // Found with:
+ // compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_AndL:
+ return false;
+
+ // SubLNode::Ideal does transform like:
+ // Convert "c1 - (y+c0)" into "(c1-c0) - y"
+ //
+ // In IGVN before verification:
+ // 8423 ConvI2L === _ 3519 [[ 8424 ]] #long:-2
+ // 8422 ConvI2L === _ 8399 [[ 8424 ]] #long:3..256:www
+ // 8424 AddL === _ 8422 8423 [[ 8383 ]] !orig=[8382]
+ // 8016 ConL === 0 [[ 8383 ]] #long:0
+ // 8383 SubL === _ 8016 8424 [[ 8156 ]] !orig=[8154]
+ //
+ // And then in verification:
+ // 8338 ConL === 0 [[ 8339 8424 ]] #long:-2 <----- Was constant folded.
+ // 8422 ConvI2L === _ 8399 [[ 8424 ]] #long:3..256:www
+ // 8424 AddL === _ 8422 8338 [[ 8383 ]] !orig=[8382]
+ // 8016 ConL === 0 [[ 8383 ]] #long:0
+ // 8383 SubL === _ 8016 8424 [[ 8156 ]] !orig=[8154]
+ //
+ // So the form changed from:
+ // c1 - (y + [8423 ConvI2L])
+ // to
+ // c1 - (y + -2)
+ // but the SubL was not added to the IGVN worklist. Investigate why.
+ // There could be other issues too.
+ //
+ // There seems to be a related AddL IGVN optimization that triggers
+ // the same SubL optimization, so investigate that too.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=0100 -Xcomp --version
+ case Op_SubL:
+ return false;
+
+ // SubINode::Ideal does
+ // Convert "x - (y+c0)" into "(x-y) - c0" AND
+ // Convert "c1 - (y+c0)" into "(c1-c0) - y"
+ //
+ // Investigate why this does not yet happen during IGVN.
+ //
+ // Found with:
+ // test/hotspot/jtreg/compiler/c2/IVTest.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_SubI:
+ return false;
+
+ // AddNode::IdealIL does transform like:
+ // Convert x + (con - y) into "(x - y) + con"
+ //
+ // In IGVN before verification:
+ // 8382 ConvI2L
+ // 8381 ConvI2L === _ 791 [[ 8383 ]] #long:0
+ // 8383 SubL === _ 8381 8382
+ // 8168 ConvI2L
+ // 8156 AddL === _ 8168 8383 [[ 8158 ]]
+ //
+ // And then in verification:
+ // 8424 AddL
+ // 8016 ConL === 0 [[ 8383 ]] #long:0 <--- Was constant folded.
+ // 8383 SubL === _ 8016 8424
+ // 8168 ConvI2L
+ // 8156 AddL === _ 8168 8383 [[ 8158 ]]
+ //
+ // So the form changed from:
+ // x + (ConvI2L(0) - [8382 ConvI2L])
+ // to
+ // x + (0 - [8424 AddL])
+ // but the AddL was not added to the IGVN worklist. Investigate why.
+ // There could be other issues, too. For example with "commute", see above.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=0100 -Xcomp --version
+ case Op_AddL:
+ return false;
+
+ // SubTypeCheckNode::Ideal calls SubTypeCheckNode::verify_helper, which does
+ // Node* cmp = phase->transform(new CmpPNode(subklass, in(SuperKlass)));
+ // record_for_cleanup(cmp, phase);
+ // This verification code in the Ideal code creates new nodes, and checks
+ // if they fold in unexpected ways. This means some nodes are created and
+ // added to the worklist, even if the SubTypeCheck is not optimized. This
+ // goes agains the assumption of the verification here, which assumes that
+ // if the node is not optimized, then no new nodes should be created, and
+ // also no nodes should be added to the worklist.
+ // I see two options:
+ // 1) forbid what verify_helper does, because for each Ideal call it
+ // uses memory and that is suboptimal. But it is not clear how that
+ // verification can be done otherwise.
+ // 2) Special case the verification here. Probably the new nodes that
+ // were just created are dead, i.e. they are not connected down to
+ // root. We could verify that, and remove those nodes from the graph
+ // by setting all their inputs to nullptr. And of course we would
+ // have to remove those nodes from the worklist.
+ // Maybe there are other options too, I did not dig much deeper yet.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=0100 -Xbatch --version
+ case Op_SubTypeCheck:
+ return false;
+
+ // LoopLimitNode::Ideal when stride is constant power-of-2, we can do a lowering
+ // to other nodes: Conv, Add, Sub, Mul, And ...
+ //
+ // 107 ConI === 0 [[ ... ]] #int:2
+ // 84 LoadRange === _ 7 83
+ // 50 ConI === 0 [[ ... ]] #int:0
+ // 549 LoopLimit === _ 50 84 107
+ //
+ // I stepped backward, to see how the node was generated, and I found that it was
+ // created in PhaseIdealLoop::exact_limit and not changed since. It is added to the
+ // IGVN worklist. I quickly checked when it goes into LoopLimitNode::Ideal after
+ // that, and it seems we want to skip lowering it until after loop-opts, but never
+ // add call record_for_post_loop_opts_igvn. This would be an easy fix, but there
+ // could be other issues too.
+ //
+ // Fond with:
+ // java -XX:VerifyIterativeGVN=0100 -Xcomp --version
+ case Op_LoopLimit:
+ return false;
+
+ // PhiNode::Ideal calls split_flow_path, which tries to do this:
+ // "This optimization tries to find two or more inputs of phi with the same constant
+ // value. It then splits them into a separate Phi, and according Region."
+ //
+ // Example:
+ // 130 DecodeN === _ 129
+ // 50 ConP === 0 [[ 18 91 99 18 ]] #null
+ // 18 Phi === 14 50 130 50 [[ 133 ]] #java/lang/Object * Oop:java/lang/Object *
+ //
+ // turns into:
+ //
+ // 50 ConP === 0 [[ 99 91 18 ]] #null
+ // 130 DecodeN === _ 129 [[ 18 ]]
+ // 18 Phi === 14 130 50 [[ 133 ]] #java/lang/Object * Oop:java/lang/Object *
+ //
+ // We would have to investigate why this optimization does not happen during IGVN.
+ // There could also be other issues - I did not investigate further yet.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=0100 -Xcomp --version
+ case Op_Phi:
+ return false;
+
+ // MemBarNode::Ideal does "Eliminate volatile MemBars for scalar replaced objects".
+ // For examle "The allocated object does not escape".
+ //
+ // It seems the difference to earlier calls to MemBarNode::Ideal, is that there
+ // alloc->as_Allocate()->does_not_escape_thread() returned false, but in verification
+ // it returned true. Why does the MemBarStoreStore not get added to the IGVN
+ // worklist when this change happens?
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=0100 -Xcomp --version
+ case Op_MemBarStoreStore:
+ return false;
+
+ // ConvI2LNode::Ideal converts
+ // 648 AddI === _ 583 645 [[ 661 ]]
+ // 661 ConvI2L === _ 648 [[ 664 ]] #long:0..maxint-1:www
+ // into
+ // 772 ConvI2L === _ 645 [[ 773 ]] #long:-120..maxint-61:www
+ // 771 ConvI2L === _ 583 [[ 773 ]] #long:60..120:www
+ // 773 AddL === _ 771 772 [[ ]]
+ //
+ // We have to investigate why this does not happen during IGVN in this case.
+ // There could also be other issues - I did not investigate further yet.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=0100 -Xcomp --version
+ case Op_ConvI2L:
+ return false;
+
+ // AddNode::IdealIL can do this transform (and similar other ones):
+ // Convert "a*b+a*c into a*(b+c)
+ // The example had AddI(MulI(a, b), MulI(a, c)). Why did this not happen
+ // during IGVN? There was a mutation for one of the MulI, and only
+ // after that the pattern was as needed for the optimization. The MulI
+ // was added to the IGVN worklist, but not the AddI. This probably
+ // can be fixed by adding the correct pattern in add_users_of_use_to_worklist.
+ //
+ // Found with:
+ // test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_AddI:
+ return false;
+
+ // ArrayCopyNode::Ideal
+ // calls ArrayCopyNode::prepare_array_copy
+ // calls Compile::conv_I2X_index -> is called with sizetype = intcon(0), I think that
+ // is not expected, and we create a range int:0..-1
+ // calls Compile::constrained_convI2L -> creates ConvI2L(intcon(1), int:0..-1)
+ // note: the type is already empty!
+ // calls PhaseIterGVN::transform
+ // calls PhaseIterGVN::transform_old
+ // calls PhaseIterGVN::subsume_node -> subsume ConvI2L with TOP
+ // calls Unique_Node_List::push -> pushes TOP to worklist
+ //
+ // Once we get back to ArrayCopyNode::prepare_array_copy, we get back TOP, and
+ // return false. This means we eventually return nullptr from ArrayCopyNode::Ideal.
+ //
+ // Question: is it ok to push anything to the worklist during ::Ideal, if we will
+ // return nullptr, indicating nothing happened?
+ // Is it smart to do transform in Compile::constrained_convI2L, and then
+ // check for TOP in calls ArrayCopyNode::prepare_array_copy?
+ // Should we just allow TOP to land on the worklist, as an exception?
+ //
+ // Found with:
+ // compiler/arraycopy/TestArrayCopyAsLoadsStores.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_ArrayCopy:
+ return false;
+
+ // CastLLNode::Ideal
+ // calls ConstraintCastNode::optimize_integer_cast -> pushes CastLL through SubL
+ //
+ // Could be a notification issue, where updates inputs of CastLL do not notify
+ // down through SubL to CastLL.
+ //
+ // Found With:
+ // compiler/c2/TestMergeStoresMemorySegment.java#byte-array
+ // -XX:VerifyIterativeGVN=1110
+ case Op_CastLL:
+ return false;
+
+ // Similar case happens to CastII
+ //
+ // Found With:
+ // compiler/c2/TestScalarReplacementMaxLiveNodes.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_CastII:
+ return false;
+
+ // MaxLNode::Ideal
+ // calls AddNode::Ideal
+ // calls commute -> decides to swap edges
+ //
+ // Another notification issue, because we check inputs of inputs?
+ // MaxL -> Phi -> Loop
+ // MaxL -> Phi -> MaxL
+ //
+ // Found with:
+ // compiler/c2/irTests/TestIfMinMax.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_MaxL:
+ case Op_MinL:
+ return false;
+
+ // OrINode::Ideal
+ // calls AddNode::Ideal
+ // calls commute -> left is Load, right not -> commute.
+ //
+ // Not sure why notification does not work here, seems like
+ // the depth is only 1, so it should work. Needs investigation.
+ //
+ // Found with:
+ // compiler/codegen/TestCharVect2.java#id0
+ // -XX:VerifyIterativeGVN=1110
+ case Op_OrI:
+ case Op_OrL:
+ return false;
+
+ // Bool -> constant folded to 1.
+ // Issue with notification?
+ //
+ // Found with:
+ // compiler/c2/irTests/TestVectorizationMismatchedAccess.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_Bool:
+ return false;
+
+ // LShiftLNode::Ideal
+ // Looks at pattern: "(x + x) << c0", converts it to "x << (c0 + 1)"
+ // Probably a notification issue.
+ //
+ // Found with:
+ // compiler/conversions/TestMoveConvI2LOrCastIIThruAddIs.java
+ // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
+ case Op_LShiftL:
+ return false;
+
+ // LShiftINode::Ideal
+ // pattern: ((x + con1) << con2) -> x << con2 + con1 << con2
+ // Could be issue with notification of inputs of inputs
+ //
+ // Side-note: should cases like these not be shared between
+ // LShiftI and LShiftL?
+ //
+ // Found with:
+ // compiler/escapeAnalysis/Test6689060.java
+ // -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
+ case Op_LShiftI:
+ return false;
+
+ // AddPNode::Ideal seems to do set_req without removing lock first.
+ // Found with various vector tests tier1-tier3.
+ case Op_AddP:
+ return false;
+
+ // StrIndexOfNode::Ideal
+ // Found in tier1-3.
+ case Op_StrIndexOf:
+ case Op_StrIndexOfChar:
+ return false;
+
+ // StrEqualsNode::Identity
+ //
+ // Found (linux x64 only?) with:
+ // serviceability/sa/ClhsdbThreadContext.java
+ // -XX:+UnlockExperimentalVMOptions -XX:LockingMode=1 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
+ case Op_StrEquals:
+ return false;
+
+ // AryEqNode::Ideal
+ // Not investigated. Reshapes itself and adds lots of nodes to the worklist.
+ //
+ // Found with:
+ // vmTestbase/vm/mlvm/meth/stress/compiler/i2c_c2i/Test.java
+ // -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:+StressUnstableIfTraps -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
+ case Op_AryEq:
+ return false;
+
+ // MergeMemNode::Ideal
+ // Found in tier1-3. Did not investigate further yet.
+ case Op_MergeMem:
+ return false;
+
+ // URShiftINode::Ideal
+ // Found in tier1-3. Did not investigate further yet.
+ case Op_URShiftI:
+ return false;
+
+ // CMoveINode::Ideal
+ // Found in tier1-3. Did not investigate further yet.
+ case Op_CMoveI:
+ return false;
+
+ // CmpPNode::Ideal calls isa_const_java_mirror
+ // and generates new constant nodes, even if no progress is made.
+ // We can probably rewrite this so that only types are generated.
+ // It seems that object types are not hashed, we could investigate
+ // if that is an option as well.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=1110 -Xcomp --version
+ case Op_CmpP:
+ return false;
+
+ // MinINode::Ideal
+ // Did not investigate, but there are some patterns that might
+ // need more notification.
+ case Op_MinI:
+ case Op_MaxI: // preemptively removed it as well.
+ return false;
+ }
+
+ if (n->is_Load()) {
+ // LoadNode::Ideal uses tries to find an earlier memory state, and
+ // checks can_see_stored_value for it.
+ //
+ // Investigate why this was not already done during IGVN.
+ // A similar issue happens with Identity.
+ //
+ // There seem to be other cases where loads go up some steps, like
+ // LoadNode::Ideal going up 10x steps to find dominating load.
+ //
+ // Found with:
+ // test/hotspot/jtreg/compiler/arraycopy/TestCloneAccess.java
+ // -XX:VerifyIterativeGVN=1110
+ return false;
+ }
+
+ if (n->is_Store()) {
+ // StoreNode::Ideal can do this:
+ // // Capture an unaliased, unconditional, simple store into an initializer.
+ // // Or, if it is independent of the allocation, hoist it above the allocation.
+ // That replaces the Store with a MergeMem.
+ //
+ // We have to investigate why this does not happen during IGVN in this case.
+ // There could also be other issues - I did not investigate further yet.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=0100 -Xcomp --version
+ return false;
+ }
+
+ if (n->is_Vector()) {
+ // VectorNode::Ideal swaps edges, but only for ops
+ // that are deemed commutable. But swap_edges
+ // requires the hash to be invariant when the edges
+ // are swapped, which is not implemented for these
+ // vector nodes. This seems not to create any trouble
+ // usually, but we can also get graphs where in the
+ // end the nodes are not all commuted, so there is
+ // definitively an issue here.
+ //
+ // Probably we have two options: kill the hash, or
+ // properly make the hash commutation friendly.
+ //
+ // Found with:
+ // compiler/vectorapi/TestMaskedMacroLogicVector.java
+ // -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -XX:+UseParallelGC -XX:+UseNUMA
+ return false;
+ }
+
+ if (n->is_Region()) {
+ // LoopNode::Ideal calls RegionNode::Ideal.
+ // CountedLoopNode::Ideal calls RegionNode::Ideal too.
+ // But I got an issue because RegionNode::optimize_trichotomy
+ // then modifies another node, and pushes nodes to the worklist
+ // Not sure if this is ok, modifying another node like that.
+ // Maybe it is, then we need to look into what to do with
+ // the nodes that are now on the worklist, maybe just clear
+ // them out again. But maybe modifying other nodes like that
+ // is also bad design. In the end, we return nullptr for
+ // the current CountedLoop. But the extra nodes on the worklist
+ // trip the asserts later on.
+ //
+ // Found with:
+ // compiler/eliminateAutobox/TestShortBoxing.java
+ // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
+ return false;
+ }
+
+ if (n->is_CallJava()) {
+ // CallStaticJavaNode::Ideal
+ // Led to a crash:
+ // assert((is_CallStaticJava() && cg->is_mh_late_inline()) || (is_CallDynamicJava() && cg->is_virtual_late_inline())) failed: mismatch
+ //
+ // Did not investigate yet, could be a bug.
+ // Or maybe it does not expect to be called during verification.
+ //
+ // Found with:
+ // test/jdk/jdk/incubator/vector/VectorRuns.java
+ // -XX:VerifyIterativeGVN=1110
+
+ // CallDynamicJavaNode::Ideal, and I think also for CallStaticJavaNode::Ideal
+ // and possibly their subclasses.
+ // During late inlining it can call CallJavaNode::register_for_late_inline
+ // That means we do more rounds of late inlining, but might fail.
+ // Then we do IGVN again, and register the node again for late inlining.
+ // This creates an endless cycle. Everytime we try late inlining, we
+ // are also creating more nodes, especially SafePoint and MergeMem.
+ // These nodes are immediately rejected when the inlining fails in the
+ // do_late_inline_check, but they still grow the memory, until we hit
+ // the MemLimit and crash.
+ // The assumption here seems that CallDynamicJavaNode::Ideal does not get
+ // called repeatedly, and eventually we terminate. I fear this is not
+ // a great assumption to make. We should investigate more.
+ //
+ // Found with:
+ // compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-U
+ // -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
+ return false;
+ }
+
+ // The number of nodes shoud not increase.
+ uint old_unique = C->unique();
+
+ Node* i = n->Ideal(this, can_reshape);
+ // If there was no new Idealization, we are probably happy.
+ if (i == nullptr) {
+ if (old_unique < C->unique()) {
+ stringStream ss; // Print as a block without tty lock.
+ ss.cr();
+ ss.print_cr("Ideal optimization did not make progress but created new unused nodes.");
+ ss.print_cr(" old_unique = %d, unique = %d", old_unique, C->unique());
+ n->dump_bfs(1, nullptr, "", &ss);
+ tty->print_cr("%s", ss.as_string());
+ return true;
+ }
+
+ verify_empty_worklist(n);
+
+ // Everything is good.
+ return false;
+ }
+
+ // We just saw a new Idealization which was not done during IGVN.
+ stringStream ss; // Print as a block without tty lock.
+ ss.cr();
+ ss.print_cr("Missed Ideal optimization (can_reshape=%s):", can_reshape ? "true": "false");
+ if (i == n) {
+ ss.print_cr("The node was reshaped by Ideal.");
+ } else {
+ ss.print_cr("The node was replaced by Ideal.");
+ ss.print_cr("Old node:");
+ n->dump_bfs(1, nullptr, "", &ss);
+ }
+ ss.print_cr("The result after Ideal:");
+ i->dump_bfs(1, nullptr, "", &ss);
+ tty->print_cr("%s", ss.as_string());
+ return true;
+}
+
+// Check that all Identity optimizations that could be done were done.
+// Returns true if it found missed optimization opportunities and
+// false otherwise (no missed optimization, or skipped verification).
+bool PhaseIterGVN::verify_Identity_for(Node* n) {
+ // First, we check a list of exceptions, where we skip verification,
+ // because there are known cases where Ideal can optimize after IGVN.
+ // Some may be expected and cannot be fixed, and others should be fixed.
+ switch (n->Opcode()) {
+ // SafePointNode::Identity can remove SafePoints, but wants to wait until
+ // after loopopts:
+ // // Transforming long counted loops requires a safepoint node. Do not
+ // // eliminate a safepoint until loop opts are over.
+ // if (in(0)->is_Proj() && !phase->C->major_progress()) {
+ //
+ // I think the check for major_progress does delay it until after loopopts
+ // but it does not ensure that the node is on the IGVN worklist after
+ // loopopts. I think we should try to instead check for
+ // phase->C->post_loop_opts_phase() and call record_for_post_loop_opts_igvn.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=1000 -Xcomp --version
+ case Op_SafePoint:
+ return false;
+
+ // MergeMemNode::Identity replaces the MergeMem with its base_memory if it
+ // does not record any other memory splits.
+ //
+ // I did not deeply investigate, but it looks like MergeMemNode::Identity
+ // never got called during IGVN for this node, investigate why.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=1000 -Xcomp --version
+ case Op_MergeMem:
+ return false;
+
+ // ConstraintCastNode::Identity finds casts that are the same, except that
+ // the control is "higher up", i.e. dominates. The call goes via
+ // ConstraintCastNode::dominating_cast to PhaseGVN::is_dominator_helper,
+ // which traverses up to 100 idom steps. If anything gets optimized somewhere
+ // away from the cast, but within 100 idom steps, the cast may not be
+ // put on the IGVN worklist any more.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=1000 -Xcomp --version
+ case Op_CastPP:
+ case Op_CastII:
+ case Op_CastLL:
+ return false;
+
+ // Same issue for CheckCastPP, uses ConstraintCastNode::Identity and
+ // checks dominator, which may be changed, but too far up for notification
+ // to work.
+ //
+ // Found with:
+ // compiler/c2/irTests/TestSkeletonPredicates.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_CheckCastPP:
+ return false;
+
+ // In SubNode::Identity, we do:
+ // Convert "(X+Y) - Y" into X and "(X+Y) - X" into Y
+ // In the example, the AddI had an input replaced, the AddI is
+ // added to the IGVN worklist, but the SubI is one link further
+ // down and is not added. I checked add_users_of_use_to_worklist
+ // where I would expect the SubI would be added, and I cannot
+ // find the pattern, only this one:
+ // If changed AddI/SubI inputs, check CmpU for range check optimization.
+ //
+ // Fix this "notification" issue and check if there are any other
+ // issues.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=1000 -Xcomp --version
+ case Op_SubI:
+ case Op_SubL:
+ return false;
+
+ // PhiNode::Identity checks for patterns like:
+ // r = (x != con) ? x : con;
+ // that can be constant folded to "x".
+ //
+ // Call goes through PhiNode::is_cmove_id and CMoveNode::is_cmove_id.
+ // I suspect there was some earlier change to one of the inputs, but
+ // not all relevant outputs were put on the IGVN worklist.
+ //
+ // Found with:
+ // test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_Phi:
+ return false;
+
+ // ConvI2LNode::Identity does
+ // convert I2L(L2I(x)) => x
+ //
+ // Investigate why this did not already happen during IGVN.
+ //
+ // Found with:
+ // compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-A
+ // -XX:VerifyIterativeGVN=1110
+ case Op_ConvI2L:
+ return false;
+
+ // MaxNode::find_identity_operation
+ // Finds patterns like Max(A, Max(A, B)) -> Max(A, B)
+ // This can be a 2-hop search, so maybe notification is not
+ // good enough.
+ //
+ // Found with:
+ // compiler/codegen/TestBooleanVect.java
+ // -XX:VerifyIterativeGVN=1110
+ case Op_MaxL:
+ case Op_MinL:
+ case Op_MaxI:
+ case Op_MinI:
+ case Op_MaxF:
+ case Op_MinF:
+ case Op_MaxHF:
+ case Op_MinHF:
+ case Op_MaxD:
+ case Op_MinD:
+ return false;
+
+
+ // AddINode::Identity
+ // Converts (x-y)+y to x
+ // Could be issue with notification
+ //
+ // Turns out AddL does the same.
+ //
+ // Found with:
+ // compiler/c2/Test6792161.java
+ // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
+ case Op_AddI:
+ case Op_AddL:
+ return false;
+
+ // AbsINode::Identity
+ // Not investigated yet.
+ case Op_AbsI:
+ return false;
+ }
+
+ if (n->is_Load()) {
+ // LoadNode::Identity tries to look for an earlier store value via
+ // can_see_stored_value. I found an example where this led to
+ // an Allocation, where we could assume the value was still zero.
+ // So the LoadN can be replaced with a zerocon.
+ //
+ // Investigate why this was not already done during IGVN.
+ // A similar issue happens with Ideal.
+ //
+ // Found with:
+ // java -XX:VerifyIterativeGVN=1000 -Xcomp --version
+ return false;
+ }
+
+ if (n->is_Store()) {
+ // StoreNode::Identity
+ // Not investigated, but found missing optimization for StoreI.
+ // Looks like a StoreI is replaced with an InitializeNode.
+ //
+ // Found with:
+ // applications/ctw/modules/java_base_2.java
+ // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -Djava.awt.headless=true -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
+ return false;
+ }
+
+ if (n->is_Vector()) {
+ // Found with tier1-3. Not investigated yet.
+ // The observed issue was with AndVNode::Identity
+ return false;
+ }
+
+ Node* i = n->Identity(this);
+ // If we cannot find any other Identity, we are happy.
+ if (i == n) {
+ verify_empty_worklist(n);
+ return false;
+ }
+
+ // The verification just found a new Identity that was not found during IGVN.
+ stringStream ss; // Print as a block without tty lock.
+ ss.cr();
+ ss.print_cr("Missed Identity optimization:");
+ ss.print_cr("Old node:");
+ n->dump_bfs(1, nullptr, "", &ss);
+ ss.print_cr("New node:");
+ i->dump_bfs(1, nullptr, "", &ss);
+ tty->print_cr("%s", ss.as_string());
return true;
}
#endif
@@ -1890,12 +2776,12 @@ void PhaseCCP::analyze() {
#ifdef ASSERT
// For every node n on verify list, check if type(n) == n->Value()
-// We have a list of exceptions, see comments in verify_node_value.
+// We have a list of exceptions, see comments in verify_Value_for.
void PhaseCCP::verify_analyze(Unique_Node_List& worklist_verify) {
bool failure = false;
while (worklist_verify.size()) {
Node* n = worklist_verify.pop();
- failure |= verify_node_value(n);
+ failure |= verify_Value_for(n);
}
// If we get this assert, check why the reported nodes were not processed again in CCP.
// We should either make sure that these nodes are properly added back to the CCP worklist
diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp
index c2a0f0dbb77..648e911e783 100644
--- a/src/hotspot/share/opto/phaseX.hpp
+++ b/src/hotspot/share/opto/phaseX.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -494,7 +494,10 @@ class PhaseIterGVN : public PhaseGVN {
void optimize();
#ifdef ASSERT
void verify_optimize();
- bool verify_node_value(Node* n);
+ bool verify_Value_for(Node* n);
+ bool verify_Ideal_for(Node* n, bool can_reshape);
+ bool verify_Identity_for(Node* n);
+ void verify_empty_worklist(Node* n);
#endif
#ifndef PRODUCT
@@ -593,6 +596,14 @@ class PhaseIterGVN : public PhaseGVN {
// '-XX:VerifyIterativeGVN=10'
return ((VerifyIterativeGVN % 100) / 10) == 1;
}
+ static bool is_verify_Ideal() {
+ // '-XX:VerifyIterativeGVN=100'
+ return ((VerifyIterativeGVN % 1000) / 100) == 1;
+ }
+ static bool is_verify_Identity() {
+ // '-XX:VerifyIterativeGVN=1000'
+ return ((VerifyIterativeGVN % 10000) / 1000) == 1;
+ }
protected:
// Sub-quadratic implementation of '-XX:VerifyIterativeGVN=1' (Use-Def verification).
julong _verify_counter;
diff --git a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp
index 2546b319762..517f0aa72c7 100644
--- a/src/hotspot/share/opto/phasetype.hpp
+++ b/src/hotspot/share/opto/phasetype.hpp
@@ -47,12 +47,15 @@
flags(EXPAND_VBOX, "Expand VectorBox") \
flags(ELIMINATE_VBOX_ALLOC, "Eliminate VectorBoxAllocate") \
flags(ITER_GVN_BEFORE_EA, "Iter GVN before EA") \
- flags(ITER_GVN_AFTER_VECTOR, "Iter GVN after vector box elimination") \
+ flags(ITER_GVN_AFTER_VECTOR, "Iter GVN after Vector Box Elimination") \
flags(BEFORE_LOOP_OPTS, "Before Loop Optimizations") \
- flags(BEFORE_BEAUTIFY_LOOPS, "Before beautify loops") \
- flags(AFTER_BEAUTIFY_LOOPS, "After beautify loops") \
- flags(BEFORE_LOOP_UNROLLING, "Before Loop Unrolling") \
- flags(AFTER_LOOP_UNROLLING, "After Loop Unrolling") \
+ flags(PHASEIDEAL_BEFORE_EA, "PhaseIdealLoop before EA") \
+ flags(AFTER_EA, "After Escape Analysis") \
+ flags(ITER_GVN_AFTER_EA, "Iter GVN after EA") \
+ flags(BEFORE_BEAUTIFY_LOOPS, "Before Beautify Loops") \
+ flags(AFTER_BEAUTIFY_LOOPS, "After Beautify Loops") \
+ flags(BEFORE_CLOOPS, "Before CountedLoop") \
+ flags(AFTER_CLOOPS, "After CountedLoop") \
flags(BEFORE_SPLIT_IF, "Before Split-If") \
flags(AFTER_SPLIT_IF, "After Split-If") \
flags(BEFORE_LOOP_PREDICATION_IC, "Before Loop Predication IC") \
@@ -69,22 +72,19 @@
flags(AFTER_LOOP_MULTIVERSIONING, "After Loop Multiversioning") \
flags(BEFORE_RANGE_CHECK_ELIMINATION, "Before Range Check Elimination") \
flags(AFTER_RANGE_CHECK_ELIMINATION, "After Range Check Elimination") \
+ flags(ITER_GVN_AFTER_ELIMINATION, "Iter GVN after Eliminating Allocations and Locks") \
flags(BEFORE_PRE_MAIN_POST, "Before Pre/Main/Post Loops") \
flags(AFTER_PRE_MAIN_POST, "After Pre/Main/Post Loops") \
- flags(AUTO_VECTORIZATION1_BEFORE_APPLY, "AutoVectorization 1, Before Apply") \
- flags(AUTO_VECTORIZATION2_AFTER_REORDER, "AutoVectorization 2, After Apply Memop Reordering") \
- flags(AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT, "AutoVectorization 3, After Adjusting Pre-Loop Limit") \
- flags(AUTO_VECTORIZATION4_AFTER_SPECULATIVE_RUNTIME_CHECKS, "AutoVectorization 4, After Adding Speculative Runtime Checks") \
- flags(AUTO_VECTORIZATION5_AFTER_APPLY, "AutoVectorization 5, After Apply") \
- flags(BEFORE_CLOOPS, "Before CountedLoop") \
- flags(AFTER_CLOOPS, "After CountedLoop") \
- flags(PHASEIDEAL_BEFORE_EA, "PhaseIdealLoop before EA") \
- flags(AFTER_EA, "After Escape Analysis") \
- flags(ITER_GVN_AFTER_EA, "Iter GVN after EA") \
- flags(ITER_GVN_AFTER_ELIMINATION, "Iter GVN after eliminating allocations and locks") \
+ flags(BEFORE_LOOP_UNROLLING, "Before Loop Unrolling") \
+ flags(AFTER_LOOP_UNROLLING, "After Loop Unrolling") \
flags(PHASEIDEALLOOP1, "PhaseIdealLoop 1") \
flags(PHASEIDEALLOOP2, "PhaseIdealLoop 2") \
flags(PHASEIDEALLOOP3, "PhaseIdealLoop 3") \
+ flags(AUTO_VECTORIZATION1_BEFORE_APPLY, "AutoVectorization 1, before Apply") \
+ flags(AUTO_VECTORIZATION2_AFTER_REORDER, "AutoVectorization 2, after Apply Memop Reordering") \
+ flags(AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT, "AutoVectorization 3, after Adjusting Pre-loop Limit") \
+ flags(AUTO_VECTORIZATION4_AFTER_SPECULATIVE_RUNTIME_CHECKS, "AutoVectorization 4, after Adding Speculative Runtime Checks") \
+ flags(AUTO_VECTORIZATION5_AFTER_APPLY, "AutoVectorization 5, after Apply") \
flags(BEFORE_CCP1, "Before PhaseCCP 1") \
flags(CCP1, "PhaseCCP 1") \
flags(ITER_GVN2, "Iter GVN 2") \
@@ -94,26 +94,26 @@
flags(BEFORE_MACRO_EXPANSION , "Before Macro Expansion") \
flags(AFTER_MACRO_EXPANSION_STEP, "After Macro Expansion Step") \
flags(AFTER_MACRO_EXPANSION, "After Macro Expansion") \
- flags(BARRIER_EXPANSION, "Barrier expand") \
- flags(OPTIMIZE_FINISHED, "Optimize finished") \
- flags(BEFORE_MATCHING, "Before matching") \
- flags(MATCHING, "After matching") \
- flags(GLOBAL_CODE_MOTION, "Global code motion") \
- flags(INITIAL_LIVENESS, "Initial liveness") \
- flags(LIVE_RANGE_STRETCHING, "Live range stretching") \
- flags(AGGRESSIVE_COALESCING, "Aggressive coalescing") \
- flags(INITIAL_SPILLING, "Initial spilling") \
- flags(CONSERVATIVE_COALESCING, "Conservative coalescing") \
- flags(ITERATIVE_SPILLING, "Iterative spilling") \
- flags(AFTER_ITERATIVE_SPILLING, "After iterative spilling") \
- flags(POST_ALLOCATION_COPY_REMOVAL, "Post-allocation copy removal") \
- flags(MERGE_MULTI_DEFS, "Merge multiple definitions") \
- flags(FIX_UP_SPILLS, "Fix up spills") \
+ flags(BARRIER_EXPANSION, "Barrier Expand") \
+ flags(OPTIMIZE_FINISHED, "Optimize Finished") \
+ flags(BEFORE_MATCHING, "Before Matching") \
+ flags(MATCHING, "After Matching") \
+ flags(GLOBAL_CODE_MOTION, "Global Code Motion") \
+ flags(INITIAL_LIVENESS, "Initial Liveness") \
+ flags(LIVE_RANGE_STRETCHING, "Live Range Stretching") \
+ flags(AGGRESSIVE_COALESCING, "Aggressive Coalescing") \
+ flags(INITIAL_SPILLING, "Initial Spilling") \
+ flags(CONSERVATIVE_COALESCING, "Conservative Coalescing") \
+ flags(ITERATIVE_SPILLING, "Iterative Spilling") \
+ flags(AFTER_ITERATIVE_SPILLING, "After Iterative Spilling") \
+ flags(POST_ALLOCATION_COPY_REMOVAL, "Post-allocation Copy Removal") \
+ flags(MERGE_MULTI_DEFS, "Merge Multiple Definitions") \
+ flags(FIX_UP_SPILLS, "Fix up Spills") \
flags(REGISTER_ALLOCATION, "Register Allocation") \
flags(BLOCK_ORDERING, "Block Ordering") \
flags(PEEPHOLE, "Peephole") \
- flags(POSTALLOC_EXPAND, "Post-Allocation Expand") \
- flags(MACH_ANALYSIS, "After mach analysis") \
+ flags(POSTALLOC_EXPAND, "Post-allocation Expand") \
+ flags(MACH_ANALYSIS, "After Mach Analysis") \
flags(FINAL_CODE, "Final Code") \
flags(END, "End") \
flags(FAILURE, "Failure") \
diff --git a/src/hotspot/share/opto/printinlining.cpp b/src/hotspot/share/opto/printinlining.cpp
index accc3dcc637..be51c08fcfb 100644
--- a/src/hotspot/share/opto/printinlining.cpp
+++ b/src/hotspot/share/opto/printinlining.cpp
@@ -22,10 +22,10 @@
*
*/
-#include "opto/printinlining.hpp"
-#include "opto/callnode.hpp"
#include "memory/allocation.hpp"
#include "memory/resourceArea.hpp"
+#include "opto/callnode.hpp"
+#include "opto/printinlining.hpp"
bool InlinePrinter::is_enabled() const {
return C->print_intrinsics() || C->print_inlining();
@@ -105,5 +105,6 @@ void InlinePrinter::IPInlineSite::dump(outputStream* tty, int level) const {
_children.visit_in_order([=](auto* node) {
node->val().dump(tty, level + 1);
+ return true;
});
}
diff --git a/src/hotspot/share/opto/printinlining.hpp b/src/hotspot/share/opto/printinlining.hpp
index ae79648319b..57f4b51858e 100644
--- a/src/hotspot/share/opto/printinlining.hpp
+++ b/src/hotspot/share/opto/printinlining.hpp
@@ -26,9 +26,9 @@
#define PRINTINLINING_HPP
#include "memory/allocation.hpp"
-#include "utilities/ostream.hpp"
-#include "utilities/growableArray.hpp"
#include "nmt/nmtTreap.hpp"
+#include "utilities/growableArray.hpp"
+#include "utilities/ostream.hpp"
class JVMState;
class ciMethod;
diff --git a/src/hotspot/share/opto/rangeinference.cpp b/src/hotspot/share/opto/rangeinference.cpp
new file mode 100644
index 00000000000..40b9da4bde5
--- /dev/null
+++ b/src/hotspot/share/opto/rangeinference.cpp
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "opto/rangeinference.hpp"
+#include "opto/type.hpp"
+#include "utilities/intn_t.hpp"
+#include "utilities/tuple.hpp"
+
+// If the cardinality of a TypeInt is below this threshold, use min widen, see
+// TypeIntPrototype::normalize_widen
+constexpr juint SMALL_TYPEINT_THRESHOLD = 3;
+
+// This represents the result of an iterative calculation
+template
+class AdjustResult {
+public:
+ bool _progress; // whether there is progress compared to the last iteration
+ bool _present; // whether the result is empty, typically due to the calculation arriving at contradiction
+ T _result;
+
+ bool empty() const {
+ return !_present;
+ }
+
+ static AdjustResult make_empty() {
+ return {true, false, {}};
+ }
+};
+
+// This is the result of canonicalizing a simple interval (see TypeInt at
+// type.hpp)
+template
+class SimpleCanonicalResult {
+ static_assert(U(-1) > U(0), "bit info should be unsigned");
+public:
+ const bool _present; // whether this is an empty set
+ const RangeInt _bounds; // The bounds must be in the same half of the integer domain (see TypeInt)
+ const KnownBits _bits;
+
+ SimpleCanonicalResult(bool present, const RangeInt& bounds, const KnownBits& bits)
+ : _present(present), _bounds(bounds), _bits(bits) {
+ if (!present) {
+ return;
+ }
+ // Do some verification
+ assert(bits.is_satisfied_by(bounds._lo) && bits.is_satisfied_by(bounds._hi), "must be canonical");
+ // 0b1000...
+ constexpr U mid_point = (std::numeric_limits::max() >> 1) + U(1);
+ assert((bounds._lo < mid_point) == (bounds._hi < mid_point), "must be a simple interval, see Lemma 4");
+ }
+
+ bool empty() const {
+ return !_present;
+ }
+
+ static SimpleCanonicalResult make_empty() {
+ return SimpleCanonicalResult(false, {}, {});
+ }
+};
+
+// Find the minimum value that is not less than lo and satisfies bits. If there
+// does not exist one such number, the calculation will return a value < lo.
+//
+// Formally, this function tries to find the minimum value that is not less
+// than lo and satisfies bits, assuming such value exists. The cases where such
+// value does not exists automatically follows.
+//
+// If there exists a value not less than lo and satisfies bits, then this
+// function will always find one such value. The converse is also true, that is
+// if this function finds a value not less than lo and satisfies bits, then it
+// must trivially be the case that there exists one such value. As a result,
+// the negation of those statements are also equivalent, there does not exists
+// a value not less than lo and satisfies bits if and only if this function
+// does not return one such value.
+//
+// In practice, since the algorithm always ensures that the returned value
+// satisfies bits, we only need to check if it is not less than lo.
+//
+// Here, we view a number in binary as a bit string. As a result, the first
+// bit refers to the highest bit (the MSB), the last bit refers to the lowest
+// bit (the LSB), a bit comes before (being higher than) another if it is more
+// significant, and a bit comes after (being lower than) another if it is less
+// significant. For a value n with w bits, we denote n[0] the first (highest)
+// bit of n, n[1] the second bit, ..., n[w - 1] the last (lowest) bit of n.
+template
+static U adjust_lo(U lo, const KnownBits& bits) {
+ // Violation of lo with respects to bits
+ // E.g: lo = 1100
+ // zeros = 0100
+ // ones = 1001
+ // zero_violation = 0100, i.e the second bit should be zero, but it is 1 in
+ // lo. Similarly, one_violation = 0001, i.e the last bit should be one, but
+ // it is 0 in lo. These make lo not satisfy the bit constraints, which
+ // results in us having to find the smallest value that satisfies bits.
+ U zero_violation = lo & bits._zeros;
+ U one_violation = ~lo & bits._ones;
+ if (zero_violation == one_violation) {
+ // This means lo does not violate bits, it is the result
+ assert(zero_violation == U(0), "");
+ return lo;
+ }
+
+ /*
+ 1. Intuition:
+ Call r the lowest value not smaller than lo that satisfies bits, consider the
+ first bit in r that is different from the corresponding bit in lo:
+ - Since r is larger than lo the bit must be 0 in lo and 1 in r
+ - Since r must satisify bits the bit must be 0 in zeros
+ - Since r should be the smallest value, this bit should be the lowest one
+ possible
+
+ E.g: 1 2 3 4 5 6
+ lo = 1 0 0 1 1 0
+ x = 1 0 1 0 1 0
+ y = 0 1 1 1 1 1
+ x would be larger than lo since the first different bit is the 3rd one,
+ while y is smaller than lo because the first different bit is the 1st bit.
+ Next, consider:
+ x1 = 1 0 1 0 1 0
+ x2 = 1 0 0 1 1 1
+ Both x1 and x2 are larger than lo, but x1 > x2 since its first different
+ bit from lo is the 3rd one, while with x2 it is the 7th one. As a result,
+ if both x1 and x2 satisfy bits, x2 would be closer to our true result.
+
+ 2. Formality:
+
+ Call r the smallest value not smaller than lo that satisfies bits. Since lo
+ does not satisfy bits, lo < r (2.1)
+
+ Call i the largest bit index such that:
+
+ - lo[x] satisfies bits for 0 <= x < i (2.2)
+ - zeros[i] = 0 (2.3)
+ - lo[i] = 0 (2.4)
+
+ Consider v:
+
+ - v[x] = lo[x], for 0 <= x < i (2.5)
+ - v[i] = 1 (2.6)
+ - v[x] = ones[x], for x > i (2.7)
+
+ We will prove that v == r.
+
+ a. Firstly, we prove that r <= v:
+
+ a.1. lo < v, since:
+ lo[x] == v[x], for 0 <= x < i (according to 2.5)
+ lo[i] < v[i] (according to 2.4 and 2.6, lo[i] == 0 < v[i] == 1)
+ bits at x > i have lower significance, and are thus irrelevant
+
+ a.2. v satisfies bits, because:
+ v[x] satisfies bits for 0 <= x < i (according to 2.2 and 2.5)
+ v[i] satisfies bits:
+ According to 2.3 and 2.6, zeros[i] == 0 and v[i] == 1, v[i] does not violate
+ bits, which means v[i] satisfies bits
+ v[x] satisfies bits for x > i:
+ Assume bits is not contradictory, we cannot have:
+ ones[x] == 1, v[x] == 0 (according to 2.7, v[x] == ones[x])
+ zeros[x] == 1, v[x] == 1 (according to 2.7, ones[x] == v[x] == 1, which means
+ bits is contradictory)
+
+ From a.1 and a.2, v > lo and v satisfies bits. Which means r <= v since r is the
+ smallest such value.
+
+ b. Secondly, from r <= v, we prove that r == v. Suppose the contradiction r < v:
+
+ Since r < v, there must be a bit position j that:
+
+ r[j] == 0 (2.b.1)
+ v[j] == 1 (2.b.2)
+ r[x] == v[x], for x < j (2.b.3)
+
+ b.1. If j < i
+ This means that:
+ r[j] == 0 (according to 2.b.1)
+ lo[j] == 1 (according to 2.b.2 and 2.5, lo[j] == v[j] == 1 because j < i)
+ r[x] == lo[x], for x < j (according to 2.b.3 and 2.5, lo[x] == v[x] == r[x] with x < j < i)
+ bits at x > j have lower significance, and are thus irrelevant
+
+ Which leads to r < lo, which contradicts that lo < r (acording to 2.1)
+
+ b.2. If j == i
+ Since r > lo (according to 2.1), there must exist a bit index k such that:
+
+ r[k] == 1 (2.b.2.1)
+ lo[k] == 0 (2.b.2.2)
+ r[x] == lo[x], for x < k (2.b.2.3)
+
+ Then, since we have:
+ r[x] == v[x], for x < i (according to 2.b.3)
+ v[x] == lo[x], for x < i (according to 2.5)
+ r[i] == 0 (according to 2.b.1 because i == j)
+ lo[i] == 0 (according to 2.4)
+
+ this leads to: r[x] == lo[x], for x <= i
+ while r[k] == 1 != lo[k] == 0, we can conclude that k > i
+
+ However, since:
+ lo[x] satisfies bits for 0 <= x < k:
+ According to 2.b.2.3, lo[x] == r[x] and r satisfies bits
+ zeros[k] == 0 (according to 2.b.2.1, r[k] == 1 and r satisfies bits)
+ lo[k] == 0 (according to 2.b.2.2)
+
+ This contradicts the assumption that i is the largest bit index satisfying such conditions.
+
+ b.3. If j > i
+ ones[j] == v[j] (according to 2.7 since j > i)
+ v[j] == 1 (according to 2.b.2)
+ r[j] == 0 (according to 2.b.1)
+
+ This means that r[j] == 0 and ones[j] == 1, this contradicts the assumption that r
+ satisfies bits.
+
+ All cases lead to contradictions, which mean r < v is incorrect, which means that
+ r == v, which means the value v having the above form is the lowest value not smaller
+ than lo that satisfies bits.
+
+ 3. Conclusion
+ Our objective now is to find the largest value i that satisfies:
+ - lo[x] satisfies bits for 0 <= x < i (3.1)
+ - zeros[i] = 0 (3.2)
+ - lo[i] = 0 (3.3)
+ */
+
+ // The algorithm depends on whether the first violation violates zeros or
+ // ones. If it violates zeros, we have the bit being 1 in zero_violation and
+ // 0 in one_violation. Since all higher bits are 0 in zero_violation and
+ // one_violation, we have zero_violation > one_violation. Similarly, if the
+ // first violation violates ones, we have zero_violation < one_violation.
+ if (zero_violation < one_violation) {
+ // This means that the first bit that does not satisfy the bit requirement
+ // is a 0 that should be a 1. Obviously, since the bit at that position in
+ // ones is 1, the same bit in zeros is 0.
+ //
+ // From section 3 above, we know i is the largest bit index such that:
+ // - lo[x] satisfies bits for 0 <= x < i (3.1)
+ // - zeros[i] = 0 (3.2)
+ // - lo[i] = 0 (3.3)
+ //
+ // For the given i, we know that lo satisfies all bits before i, hence (3.1)
+ // holds. Further, lo[i] = 0 (3.3), and we have a one violation at i, hence
+ // zero[i] = 0 (3.2). Any smaller i would not be the largest possible such
+ // index. Any larger i would violate (3.1), since lo[i] does not satisfy bits.
+ // As a result, the first violation is the bit i we are looking for.
+ //
+ // E.g: 1 2 3 4 5 6 7 8
+ // lo = 1 1 0 0 0 1 1 0
+ // zeros = 0 0 1 0 0 1 0 0
+ // ones = 0 1 0 1 0 0 1 0
+ // 1-vio = 0 0 0 1 0 0 0 0
+ // 0-vio = 0 0 0 0 0 1 0 0
+ // Since the result must have the 4th bit set, it must be at least:
+ // 1 1 0 1 0 0 0 0
+ // This value must satisfy zeros, because all bits before the 4th bit have
+ // already satisfied zeros, and all bits after the 4th bit are all 0 now.
+ // Just OR this value with ones to obtain the final result.
+
+ // first_violation is the position of the violation counting from the
+ // highest bit down (0-based), since i == 4, first_violation == 3
+ juint first_violation = count_leading_zeros(one_violation);
+ // 1 0 0 0 0 0 0 0
+ constexpr U highest_bit = (std::numeric_limits::max() >> 1) + U(1);
+ // This is the bit at which we want to change the bit 0 in lo to a 1, and
+ // all bits after to zero. This is similar to an operation that aligns lo
+ // up to the next multiple of this modulo.
+ // 0 0 0 1 0 0 0 0
+ U alignment = highest_bit >> first_violation;
+ // This is the first value which have the violated bit being 1, which means
+ // that the result should not be smaller than this.
+ // This is a standard operation to align a value up to the next multiple of
+ // a certain power of 2. Since alignment is a power of 2, -alignment is a
+ // value having all the bits being 1 upto the location of the bit in
+ // alignment (in the example, -alignment = 11110000). As a result,
+ // lo & -alignment set all bits after the bit in alignment to 0, which is
+ // equivalent to rounding lo down to a multiple of alignment. To round lo
+ // up to the next multiple of alignment, we add alignment to the rounded
+ // down value.
+ // Note that this computation cannot overflow as the bit in lo that is at
+ // the same position as the only bit 1 in alignment must be 0. As a result,
+ // this operation just set that bit to 1 and set all the bits after to 0.
+ // We now have:
+ // - new_lo[x] = lo[x], for 0 <= x < i (2.5)
+ // - new_lo[i] = 1 (2.6)
+ // - new_lo[x] = 0, for x > i (not yet 2.7)
+ // 1 1 0 1 0 0 0 0
+ U new_lo = (lo & -alignment) + alignment;
+ // Note that there exists no value x not larger than i such that
+ // new_lo[x] == 0 and ones[x] == 1. This is because all bits of lo before i
+ // should satisfy bits, and new_lo[i] == 1. As a result, doing
+ // new_lo |= bits.ones will give us a value such that:
+ // - new_lo[x] = lo[x], for 0 <= x < i (2.5)
+ // - new_lo[i] = 1 (2.6)
+ // - new_lo[x] = ones[x], for x > i (2.7)
+ // This is the result we are looking for.
+ // 1 1 0 1 0 0 1 0
+ new_lo |= bits._ones;
+ // Note that in this case, new_lo is always a valid answer. That is, it is
+ // a value not less than lo and satisfies bits.
+ assert(lo < new_lo, "the result must be valid");
+ return new_lo;
+ } else {
+ assert(zero_violation > one_violation, "remaining case");
+ // This means that the first bit that does not satisfy the bit requirement
+ // is a 1 that should be a 0.
+ //
+ // From section 3 above, we know i is the largest bit index such that:
+ // - lo[x] satisfies bits for 0 <= x < i (3.1)
+ // - zeros[i] = 0 (3.2)
+ // - lo[i] = 0 (3.3)
+ //
+ // We know that lo satisfies all bits before first_violation, hence (3.1)
+ // holds. However, first_violation is not the value i we are looking for
+ // because lo[first_violation] == 1. We can also see that any larger value
+ // of i would violate (3.1) since lo[first_violation] does not satisfy
+ // bits. As a result, we should find the last index x upto first_violation
+ // such that lo[x] == zeros[x] == 0. That value of x would be the value of
+ // i we are looking for.
+ //
+ // E.g: 1 2 3 4 5 6 7 8
+ // lo = 1 0 0 0 1 1 1 0
+ // zeros = 0 0 0 1 0 1 0 0
+ // ones = 1 0 0 0 0 0 1 1
+ // 1-vio = 0 0 0 0 0 0 0 1
+ // 0-vio = 0 0 0 0 0 1 0 0
+ // The first violation is the 6th bit, which should be 0. We want to flip
+ // it to 0. However, since we must obtain a value larger than lo, we must
+ // find an earlier bit that can be flipped from 0 to 1. The 5th cannot be
+ // the bit we are looking for, because it is already 1, the 4th bit also
+ // cannot be, because it must be 0. As a result, the last bit we can flip,
+ // which is the first different bit between the result and lo must be the
+ // 3rd bit. As a result, the result must not be smaller than:
+ // 1 0 1 0 0 0 0 0
+ // This one satisfies zeros so we can use the logic in the previous case,
+ // just OR with ones to obtain the final result, which is:
+ // 1 0 1 0 0 0 1 1
+
+ juint first_violation = count_leading_zeros(zero_violation);
+ // This masks out all bits after the first violation
+ // 1 1 1 1 1 0 0 0
+ U find_mask = ~(std::numeric_limits::max() >> first_violation);
+ // We want to find the last index x upto first_violation such that
+ // lo[x] == zeros[x] == 0.
+ // We start with all bits where lo[x] == zeros[x] == 0:
+ // 0 1 1 0 0 0 0 1
+ U neither = ~(lo | bits._zeros);
+ // Now let us find all the bit indices x upto first_violation such that
+ // lo[x] == zeros[x] == 0. The last one of these bits must be at index i.
+ // 0 1 1 0 0 0 0 0
+ U neither_upto_first_violation = neither & find_mask;
+ // We now want to select the last one of these candidates, which is exactly
+ // the last index x upto first_violation such that lo[x] == zeros[x] == 0.
+ // This would be the value i we are looking for.
+ // Similar to the other case, we want to obtain the value with only the bit
+ // i set, this is equivalent to extracting the last set bit of
+ // neither_upto_first_violation, do it directly without going through i.
+ // The formula x & (-x) will give us the last set bit of an integer x
+ // (please see the x86 instruction blsi).
+ // In our example, i == 2
+ // 0 0 1 0 0 0 0 0
+ U alignment = neither_upto_first_violation & (-neither_upto_first_violation);
+ // Set the bit of lo at i and unset all the bits after, this is the
+ // smallest value that satisfies bits._zeros. Similar to the above case,
+ // this is similar to aligning lo up to the next multiple of alignment.
+ // Also similar to the above case, this computation cannot overflow.
+ // We now have:
+ // - new_lo[x] = lo[x], for 0 <= x < i (2.5)
+ // - new_lo[i] = 1 (2.6)
+ // - new_lo[x] = 0, for x > i (not yet 2.7)
+ // 1 0 1 0 0 0 0 0
+ U new_lo = (lo & -alignment) + alignment;
+ // Note that there exists no value x not larger than i such that
+ // new_lo[x] == 0 and ones[x] == 1. This is because all bits of lo before i
+ // should satisfy bits, and new_lo[i] == 1. As a result, doing
+ // new_lo |= bits.ones will give us a value such that:
+ // - new_lo[x] = lo[x], for 0 <= x < i (2.5)
+ // - new_lo[i] = 1 (2.6)
+ // - new_lo[x] = ones[x], for x > i (2.7)
+ // This is the result we are looking for.
+ // 1 0 1 0 0 0 1 1
+ new_lo |= bits._ones;
+ // Note that formally, this function assumes that there exists a value not
+ // smaller than lo and satisfies bits. This implies the existence of the
+ // index i satisfies (3.1-3.3), which means that
+ // neither_upto_first_violation != 0. The converse is
+ // also true, if neither_upto_first_violation != 0, then an index i
+ // satisfies (3.1-3.3) exists, which implies the existence of a value not
+ // smaller than lo and satisfies bits. As a result, the negation of those
+ // statements are equivalent. neither_upto_first_violation == 0 if and only
+ // if there does not exists a value not smaller than lo and satisfies bits.
+ // In this case, alignment == 0 and new_lo == bits._ones. We know that, if
+ // the assumption of this function holds, we return a value satisfying
+ // bits, and if the assumption of this function does not hold, the returned
+ // value would be bits._ones, which also satisfies bits. As a result, this
+ // function always returns a value satisfying bits, regardless whether if
+ // the assumption of this function holds. In conclusion, the caller only
+ // needs to check lo <= new_lo to find the cases where there exists no
+ // value not smaller than lo and satisfies bits (see the overview of the
+ // function).
+ assert(lo < new_lo || new_lo == bits._ones, "invalid result must be bits._ones");
+ return new_lo;
+ }
+}
+
+// Try to tighten the bound constraints from the known bit information. I.e, we
+// find the smallest value not smaller than lo, as well as the largest value
+// not larger than hi both of which satisfy bits
+// E.g: lo = 0010, hi = 1001
+// zeros = 0011
+// ones = 0000
+// -> 4-aligned
+//
+// 0 1 2 3 4 5 6 7 8 9 10
+// 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010
+// bits: ok . . . ok . . . ok . .
+// bounds: lo hi
+// adjust: --------> lo hi <---
+template
+static AdjustResult>
+adjust_unsigned_bounds_from_bits(const RangeInt& bounds, const KnownBits& bits) {
+ U new_lo = adjust_lo(bounds._lo, bits);
+ if (new_lo < bounds._lo) {
+ // This means we wrapped around, which means no value not less than lo
+ // satisfies bits
+ return AdjustResult>::make_empty();
+ }
+
+ // We need to find the largest value not larger than hi that satisfies bits
+ // One possible method is to do similar to adjust_lo, just with the other
+ // direction
+ // However, we can observe that if v satisfies {bits._zeros, bits._ones},
+ // then ~v would satisfy {bits._ones, bits._zeros}. Combine with the fact
+ // that bitwise-not is a strictly decreasing function, if new_hi is the
+ // largest value not larger than hi that satisfies {bits._zeros, bits._ones},
+ // then ~new_hi is the smallest value not smaller than ~hi that satisfies
+ // {bits._ones, bits._zeros}.
+ //
+ // Proof:
+ // Calling h the smallest value not smaller than ~hi that satisfies
+ // {bits._ones, bits._zeros}.
+ //
+ // 1. Since h satisfies {bits._ones, bits._zeros}, ~h satisfies
+ // {bits._zeros, bits._ones}. Assume the contradiction ~h does not satisfy
+ // {bits._zeros, bits._ones}, There can be 2 cases:
+ // 1.1. There is a bit in ~h that is 0 where the corresponding bit in ones
+ // is 1. This implies the corresponding bit in h is 1. But this is
+ // contradictory since h satisfies {bits._ones, bits._zeros}.
+ // 1.2. There is a bit in ~h that is 1 where the corresponding bit in zeros
+ // is 1. Similarly, this leads to contradiction because h needs to
+ // satisfy {bits._ones, bits._zeros}.
+ //
+ // 2. Assume there is a value k that is larger than ~h such that k is not
+ // larger than hi, i.e. ~h < k <= hi, and k satisfies {bits._zeros, bits._ones}.
+ // As a result, ~k would satisfy {bits._ones, bits._zeros}. And since bitwise-not
+ // is a strictly decreasing function, given ~h < k <= hi, we have h > ~k >= ~hi.
+ // This contradicts the assumption that h is the smallest value not smaller than
+ // ~hi and satisfies {bits._ones, bits._zeros}.
+ //
+ // As a result, ~h is the largest value not larger than hi that satisfies
+ // bits (QED).
+ U h = adjust_lo(~bounds._hi, {bits._ones, bits._zeros});
+ if (h < ~bounds._hi) {
+ return AdjustResult>::make_empty();
+ }
+
+ U new_hi = ~h;
+ bool progress = (new_lo != bounds._lo) || (new_hi != bounds._hi);
+ bool present = new_lo <= new_hi;
+ return {progress, present, {new_lo, new_hi}};
+}
+
+// Try to tighten the known bit constraints from the bound information by
+// extracting the common prefix of lo and hi and combining with the current
+// bit constraints
+// E.g: lo = 010011
+// hi = 010100,
+// then all values in [lo, hi] would be
+// 010***
+template
+static AdjustResult>
+adjust_bits_from_unsigned_bounds(const KnownBits& bits, const RangeInt& bounds) {
+ // Find the mask to filter the common prefix, all values between bounds._lo
+ // and bounds._hi should share this common prefix in terms of bits
+ U mismatch = bounds._lo ^ bounds._hi;
+ // Find the first mismatch, all bits before it are the same in bounds._lo and
+ // bounds._hi
+ U match_mask = mismatch == U(0) ? std::numeric_limits::max()
+ : ~(std::numeric_limits