From acc953f38f62a05b4be64343384d701b8e76c61b Mon Sep 17 00:00:00 2001
From: Quan Anh Mai
Date: Thu, 8 Oct 2026 09:22:40 +0000
Subject: [PATCH 001/101] 8380089: C2: Elide stores in loops by peeling
Reviewed-by: roland, chagedorn, aseoane
---
src/hotspot/share/opto/loopTransform.cpp | 230 +++++++++++++--
src/hotspot/share/opto/loopnode.hpp | 14 +-
.../TestRemoveStoresAfterPeeling.java | 261 ++++++++++++++++++
3 files changed, 476 insertions(+), 29 deletions(-)
create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestRemoveStoresAfterPeeling.java
diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp
index 09ea9740ce5..209e4bd6bea 100644
--- a/src/hotspot/share/opto/loopTransform.cpp
+++ b/src/hotspot/share/opto/loopTransform.cpp
@@ -26,6 +26,7 @@
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/c2/barrierSetC2.hpp"
#include "memory/allocation.inline.hpp"
+#include "memory/resourceArea.hpp"
#include "opto/addnode.hpp"
#include "opto/callnode.hpp"
#include "opto/castnode.hpp"
@@ -33,12 +34,14 @@
#include "opto/convertnode.hpp"
#include "opto/divnode.hpp"
#include "opto/loopnode.hpp"
+#include "opto/memnode.hpp"
#include "opto/movenode.hpp"
#include "opto/mulnode.hpp"
#include "opto/node.hpp"
#include "opto/opaquenode.hpp"
#include "opto/opcodes.hpp"
#include "opto/phase.hpp"
+#include "opto/phasetype.hpp"
#include "opto/predicates.hpp"
#include "opto/rootnode.hpp"
#include "opto/runtime.hpp"
@@ -564,11 +567,9 @@ void IdealLoopTree::reassociate_invariants(PhaseIdealLoop *phase) {
}
}
-//------------------------------policy_peeling---------------------------------
-// Return TRUE if the loop should be peeled, otherwise return FALSE. Peeling
-// is applicable if we can make a loop-invariant test (usually a null-check)
-// execute before we enter the loop. When TRUE, the estimated node budget is
-// also requested.
+// Return TRUE if the loop should be peeled, otherwise return FALSE. Peeling should be performed if
+// it allows us to remove something in the main body (see elide_redundancies_after_peeling) and the
+// esimated increase in graph size is within the budget.
bool IdealLoopTree::policy_peeling(PhaseIdealLoop *phase) {
uint estimate = estimate_peeling(phase);
@@ -622,9 +623,10 @@ uint IdealLoopTree::estimate_peeling(PhaseIdealLoop *phase) {
// ...otherwise, let's apply our heuristic.
#endif
- Node* test = tail();
-
- while (test != _head) { // Scan till run off top of loop
+ // Walk up dominators to loop _head looking for an exit test which is executed on every path
+ // through the loop. If the tested value is a loop-invariant, then the IfNode can be folded after
+ // peeling, giving us a reason to peel.
+ for (Node* test = tail(); test != _head; test = phase->idom(test)) { // Scan till run off top of loop
if (test->is_If()) { // Test?
Node *ctrl = phase->get_ctrl(test->in(1));
if (ctrl->is_top()) {
@@ -642,18 +644,34 @@ uint IdealLoopTree::estimate_peeling(PhaseIdealLoop *phase) {
return estimate; // Found reason to peel!
}
}
- // Walk up dominators to loop _head looking for test which is executed on
- // every path through the loop.
- test = phase->idom(test);
}
+
+ // Whether peeling allows a store to be elided from the remaining iterations
+ for (uint i = 0; i < _body.size(); i++) {
+ Node* n = _body.at(i);
+ if (n->is_Store() && can_elide_store_if_peeled(n->as_Store())) {
+ return estimate;
+ }
+ }
+
return 0;
}
-//------------------------------peeled_dom_test_elim---------------------------
-// If we got the effect of peeling, either by actually peeling or by making
-// a pre-loop which must execute at least once, we can remove all
-// loop-invariant dominated tests in the main body.
-void PhaseIdealLoop::peeled_dom_test_elim(IdealLoopTree* loop, Node_List& old_new) {
+// After peeling the loop or creating a pre-loop that executes at least once, the first iteration
+// dominates the remaining iterations. This allows us to:
+// - Remove all loop-invariant dominated tests in the main body.
+// - Remove all loop-invariant dominated stores in the main body.
+void PhaseIdealLoop::elide_redundancies_after_peeling(IdealLoopTree* loop, const Node_List& old_new) {
+ elide_redundant_tests_after_peeling(loop, old_new);
+ elide_redundant_stores_after_peeling(loop, old_new);
+}
+
+// If an IfProj 'prev' dominates the tail of 'loop', its clone 'old_new[prev->_idx]' in the peeled
+// iteration or pre-loop will dominate the remaining iterations, which includes the input 'test' of
+// 'prev'. As a result, if the condition of 'test' is a loop invariant, 'test' will be dominated by
+// the IfProj of 'old_new[test->_idx]', and both 'test' and 'old_new[test->_idx]' will have the
+// same Bool input. This allows 'test' to be removed using PhaseIdealLoop::dominated_by.
+void PhaseIdealLoop::elide_redundant_tests_after_peeling(IdealLoopTree* loop, const Node_List& old_new) {
bool progress = true;
while (progress) {
progress = false; // Reset for next iteration
@@ -688,6 +706,174 @@ void PhaseIdealLoop::peeled_dom_test_elim(IdealLoopTree* loop, Node_List& old_ne
} // End of while (progress)
}
+// If a store 'n' dominates the tail of 'loop', its clone 'old_new[n->_idx]' will dominate the
+// remaining iterations. As a result, if the pointer and value inputs of 'n' are loop invariants,
+// we can remove 'n' if we can prove that there is no interfering store between 'n' and
+// 'old_new[n->_idx]'. This is because in those cases we know that the value that is already at
+// the memory location stored into by 'n' is the same as the value that 'n' stores, or in other
+// words, the store 'n' is useless.
+//
+// This complements try_move_store_before_loop. Eliding a store by peeling the loop is a strictly
+// more general transformation. In other words, for all stores that can be hoisted above a loop,
+// they can also be elided from the remaining iterations after peeling. The downside is that
+// peeling a loop increases the graph size much more compared to just hoisting the store. As a
+// result, we always invoke try_move_store_before_loop first, and only after it gives up, we resort
+// to this transformation.
+//
+// To be more specific, conceptually, hoisting a store above a loop can be understood as composed
+// of several steps:
+//
+// 0.
+// loop (0..n) {
+// Store(p1, v1);
+// Store(p, v);
+// StoreLoadFence();
+// }
+//
+// 1. Peel the first iteration
+// Store(p1, v1);
+// Store(p, v);
+// StoreLoadFence();
+// loop (1..n) {
+// Store(p1, v1);
+// Store(p, v);
+// StoreLoadFence();
+// }
+//
+// 2. Remove Store(p, v) inside the loop because there is a similar store dominating it
+// Store(p1, v1);
+// Store(p, v);
+// StoreLoadFence();
+// loop (1..n) {
+// Store(p1, v1);
+// StoreLoadFence();
+// }
+//
+// 3. Move Store(p, v) above the first iteration
+// Store(p, v);
+// Store(p1, v1);
+// StoreLoadFence();
+// loop (1..n) {
+// Store(p1, v1);
+// StoreLoadFence();
+// }
+//
+// 4. Unpeel the first iteration (fold the first iteration back into the loop)
+// Store(p, v);
+// loop (0..n) {
+// Store(p1, v1);
+// StoreLoadFence();
+// }
+//
+// As a result, for any store that can be hoisted above a loop, peeling the loop also allows us to
+// remove it from the main body. Note that this is only an analysis of the transformation as a
+// conceptual explanation. In practice, in try_move_store_before_loop, we don't actually peel and
+// unpeel the loop but we hoist the store directly.
+void PhaseIdealLoop::elide_redundant_stores_after_peeling(IdealLoopTree* loop, const Node_List& old_new) {
+ for (uint i = 0; i < loop->_body.size(); i++) {
+ Node* n = loop->_body.at(i);
+ if (n->is_Store() && loop->can_elide_store_after_peeling(n->as_Store(), old_new[n->_idx]->as_Store())) {
+ // This does not yet handle the case where 'n' has accompanying MemBars. We do not elide
+ // those stores yet. In the future, when the logic to walk past MemBars is implemented, we
+ // should also remove the accompanying MemBars of the elided StoreNode, if any.
+ assert(n->as_Store()->trailing_membar() == nullptr, "should not be elided");
+ loop->_body.yank(n);
+ _loop_or_ctrl.map(n->_idx, nullptr);
+ _igvn.replace_node(n, n->in(MemNode::Memory));
+ i--;
+ }
+ }
+}
+
+// Try to analyze whether peeling 'this' can help elide 'store' from the remaining iterations. This
+// is the case if 'store' writes the same value into the same location in every iteration, the
+// 'store' in the first iteration dominates itself in the remaining iterations, and there is no
+// other access in the loop that can write into the memory location written to by 'store'. In other
+// words, if the execution of 'store' is useless after the first iteration because we know the
+// value being written is the same as the value that is there, then we can elide 'store' after
+// peeling.
+bool IdealLoopTree::can_elide_store_if_peeled(StoreNode* store) {
+ return can_elide_store_after_peeling_impl(store, nullptr);
+}
+
+// After the loop has been peeled, try to see whether 'store' can be elided because it is dominated
+// by 'dominating_store', given that 'dominating_store' is the clone of 'store' in the pre-loop/
+// peeled iteration.
+bool IdealLoopTree::can_elide_store_after_peeling(StoreNode* store, StoreNode* dominating_store) {
+ assert(dominating_store != nullptr, "must have a dominating_store");
+ return can_elide_store_after_peeling_impl(store, dominating_store);
+}
+
+// Common implementation of can_elide_store_if_peeled, which calls this method with
+// dominating_store = nullptr, and can_elide_store_after_peeling, which calls this method with
+// dominating_store = old_new[store->_idx].
+bool IdealLoopTree::can_elide_store_after_peeling_impl(StoreNode* store, StoreNode* dominating_store) {
+ if (store->req() > MemNode::ValueIn + 1) {
+ // It is generally incorrect to analyze StoreVectorMasked, StoreVectorScatter, and
+ // StoreVectorScatterMasked the same as other StoreNodes because their access patterns are
+ // different. For example, they may not access a certain number of bytes starting from their
+ // pointer input.
+ return false;
+ } else if (!_phase->is_dominator(_phase->get_ctrl(store), tail())) {
+ // We can only remove the store after peeling if the store in later iterations is dominated by
+ // the one in the first iteration.
+ return false;
+ } else if (!is_invariant(store->in(MemNode::Address)) || !is_invariant(store->in(MemNode::ValueIn))) {
+ // We can only remove the store after peeling if it stores the same value at the same place
+ // in every iteration.
+ return false;
+ }
+
+ // If we are checking whether 'store' would be elided if 'this' is peeled. Otherwise, we are
+ // checking whether 'store' can be elided now.
+ bool is_profitability_check = dominating_store == nullptr;
+
+ // Walk the memory graph. The check only succeeds if we encounter no store which can interfere
+ // with 'store'. If is_profitability_check is true, walk all the relevant nodes inside 'this'.
+ // Otherwise, walk all the relevant nodes between 'store' and 'dominating_store'.
+ ResourceMark rm;
+ Unique_Node_List worklist;
+ worklist.push(store->in(MemNode::Memory));
+ AccessAnalyzer analyzer(&_phase->_igvn, store);
+ for (uint worklist_idx = 0; worklist_idx < worklist.size(); worklist_idx++) {
+ Node* mem = worklist.at(worklist_idx);
+ assert(mem != nullptr, "cannot be a nullptr");
+ if (mem == store || mem == dominating_store) {
+ continue;
+ }
+
+ if (is_profitability_check) {
+ // We should only be able to step outside the loop from the loop phi, so this means the graph
+ // is broken
+ assert(!is_invariant(mem), "broken memory graph");
+ } else {
+ // The graph is broken, or some transformation incorrectly moves dominating_store
+ assert(mem->in(0) != _phase->C->start(), "dominating_store must dominate store");
+ }
+
+ if (mem->is_Phi()) {
+ if (is_profitability_check && mem->in(0) == _head) {
+ // Don't walk out of the loop when checking for profitability
+ worklist.push(mem->in(LoopNode::LoopBackControl));
+ } else {
+ for (uint i = 1; i < mem->req(); i++) {
+ worklist.push(mem->in(i));
+ }
+ }
+ continue;
+ }
+
+ AccessAnalyzer::AccessIndependence independence = analyzer.detect_access_independence(mem);
+ if (!independence.independent) {
+ return false;
+ }
+
+ worklist.push(independence.mem);
+ }
+
+ return true;
+}
+
//------------------------------do_peeling-------------------------------------
// Peel the first iteration of the given loop.
// Step 1: Clone the loop body. The clone becomes the peeled iteration.
@@ -934,9 +1120,7 @@ void PhaseIdealLoop::do_peeling(IdealLoopTree *loop, Node_List &old_new) {
cast_incr_before_loop(init, init_ctrl, cl);
}
- // Now force out all loop-invariant dominating tests. The optimizer
- // finds some, but we _know_ they are all useless.
- peeled_dom_test_elim(loop,old_new);
+ elide_redundancies_after_peeling(loop, old_new);
loop->record_for_igvn();
@@ -1652,9 +1836,7 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n
post_head->set_profile_trip_cnt(4.0);
pre_head->set_profile_trip_cnt(4.0);
- // Now force out all loop-invariant dominating tests. The optimizer
- // finds some, but we _know_ they are all useless.
- peeled_dom_test_elim(loop,old_new);
+ elide_redundancies_after_peeling(loop, old_new);
loop->record_for_igvn();
C->print_method(PHASE_AFTER_PRE_MAIN_POST, 4, main_head);
@@ -1719,9 +1901,7 @@ void PhaseIdealLoop::insert_vector_post_loop(IdealLoopTree *loop, Node_List &old
// so guess that unit vector trips is a reasonable value.
post_head->set_profile_trip_cnt(cur_unroll);
- // Now force out all loop-invariant dominating tests. The optimizer
- // finds some, but we _know_ they are all useless.
- peeled_dom_test_elim(loop, old_new);
+ elide_redundancies_after_peeling(loop, old_new);
loop->record_for_igvn();
}
diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp
index 54ea9c75f3f..a6f0ddd8b6b 100644
--- a/src/hotspot/share/opto/loopnode.hpp
+++ b/src/hotspot/share/opto/loopnode.hpp
@@ -27,6 +27,7 @@
#include "opto/cfgnode.hpp"
#include "opto/multnode.hpp"
+#include "opto/node.hpp"
#include "opto/phaseX.hpp"
#include "opto/predicates.hpp"
#include "opto/subnode.hpp"
@@ -756,6 +757,9 @@ class IdealLoopTree : public ResourceObj {
uint estimate_peeling(PhaseIdealLoop *phase);
+ bool can_elide_store_if_peeled(StoreNode* store);
+ bool can_elide_store_after_peeling(StoreNode* store, StoreNode* dominating_store);
+
// Return TRUE or FALSE if the loop should be maximally unrolled. Stash any
// known trip count in the counted loop node.
bool policy_maximally_unroll(PhaseIdealLoop *phase) const;
@@ -871,6 +875,8 @@ class IdealLoopTree : public ResourceObj {
bool empty_loop_candidate(PhaseIdealLoop* phase) const;
bool empty_loop_with_extra_nodes_candidate(PhaseIdealLoop* phase) const;
+
+ bool can_elide_store_after_peeling_impl(StoreNode* store, StoreNode* dominating_store);
};
// -----------------------------PhaseIdealLoop---------------------------------
@@ -1281,6 +1287,9 @@ class PhaseIdealLoop : public PhaseTransform {
return n;
}
+ void elide_redundant_tests_after_peeling(IdealLoopTree* loop, const Node_List& old_new);
+ void elide_redundant_stores_after_peeling(IdealLoopTree* loop, const Node_List& old_new);
+
public:
Node* idom(Node* n) const {
return idom(n->_idx);
@@ -1526,10 +1535,7 @@ class PhaseIdealLoop : public PhaseTransform {
IdealLoopTree* outer_loop, int dd, Node_List &old_new,
Node_List& extra_data_nodes);
- // If we got the effect of peeling, either by actually peeling or by
- // making a pre-loop which must execute at least once, we can remove
- // all loop-invariant dominated tests in the main body.
- void peeled_dom_test_elim( IdealLoopTree *loop, Node_List &old_new );
+ void elide_redundancies_after_peeling(IdealLoopTree* loop, const Node_List& old_new);
// Generate code to do a loop peel for the given loop (and body).
// old_new is a temp array.
diff --git a/test/hotspot/jtreg/compiler/loopopts/TestRemoveStoresAfterPeeling.java b/test/hotspot/jtreg/compiler/loopopts/TestRemoveStoresAfterPeeling.java
new file mode 100644
index 00000000000..0a3c69ca464
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/loopopts/TestRemoveStoresAfterPeeling.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package compiler.loopopts;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Platform;
+
+/*
+ * @test
+ * @bug 8380089
+ * @summary Test that stores can be elided by peeling the loop.
+ * @library /test/lib /
+ * @run driver ${test.main.class}
+ */
+public class TestRemoveStoresAfterPeeling {
+ private static class A {
+ int v;
+ }
+
+ public static void main(String[] args) {
+ var framework = new TestFramework();
+ framework.addScenarios(new Scenario(0));
+ if (Platform.isDebugBuild()) {
+ // When a store is not removed from a loop after that loop is peeled, there will be
+ // multiple stores in the graph
+ framework.addScenarios(new Scenario(1, "-XX:+StressLoopPeeling"));
+ }
+ framework.start();
+ }
+
+ @Test
+ @IR(counts = {IRNode.STORE_I, "1"})
+ private void testStoreHoisting(A a, int v) {
+ for (int i = 1; i < 100; i *= 2) {
+ // Simple case, the store can be hoisted above the loop
+ a.v = v;
+ }
+ }
+
+ @Run(test = "testStoreHoisting")
+ public void runStoreHoisting() {
+ A a = new A();
+ testStoreHoisting(a, 1);
+ Asserts.assertEQ(1, a.v);
+ }
+
+ @Test
+ @IR(counts = {IRNode.STORE_I, "1"})
+ private void testStorePeeling1(int limit, A a, int v) {
+ for (int i = 1; i < 100; i *= 2) {
+ // The store does not post-dominate the loop head, so it cannot be hoisted
+ if (i >= limit) {
+ break;
+ }
+
+ a.v = v;
+ }
+ }
+
+ @Run(test = "testStorePeeling1")
+ public void runStorePeeling1() {
+ A a = new A();
+ testStorePeeling1(0, a, 1);
+ Asserts.assertEQ(0, a.v);
+ testStorePeeling1(200, a, 1);
+ Asserts.assertEQ(1, a.v);
+ }
+
+ @Test
+ @IR(counts = {IRNode.STORE_I, "1"}, phase = CompilePhase.BEFORE_MACRO_EXPANSION)
+ private A testStorePeeling2(A a1, int v1, float v2) {
+ A a2 = new A();
+ for (int i = 1; i < 100; i *= 2) {
+ // There are 2 independent stores in the loop
+ a2.v = Float.floatToRawIntBits(i * v2);
+ a1.v = v1;
+ }
+ return a2;
+ }
+
+ @Run(test = "testStorePeeling2")
+ public void runStorePeeling2() {
+ A a1 = new A();
+ A a2 = testStorePeeling2(a1, 1, 1);
+ Asserts.assertEQ(1, a1.v);
+ Asserts.assertEQ(Float.floatToRawIntBits(64), a2.v);
+ }
+
+ @Test
+ @IR(counts = {IRNode.STORE_I, "1"}, phase = CompilePhase.BEFORE_MACRO_EXPANSION)
+ private A testStorePeeling3(int limit, A a1, int v1, float v2) {
+ A a2 = new A();
+ for (int i = 1; i < 100; i *= 2) {
+ // Combine the previous 2 cases
+ a2.v = Float.floatToRawIntBits(i * v2);
+ if (i >= limit) {
+ break;
+ }
+ a1.v = v1;
+ }
+ return a2;
+ }
+
+ @Run(test = "testStorePeeling3")
+ public void runStorePeeling3() {
+ A a1 = new A();
+ A a2 = testStorePeeling3(0, a1, 1, 1);
+ Asserts.assertEQ(0, a1.v);
+ Asserts.assertEQ(Float.floatToRawIntBits(1), a2.v);
+ a2 = testStorePeeling3(200, a1, 1, 1);
+ Asserts.assertEQ(1, a1.v);
+ Asserts.assertEQ(Float.floatToRawIntBits(64), a2.v);
+ }
+
+ @Test
+ @IR(counts = {IRNode.STORE_I, "1"})
+ private int testStorePeeling4(A[] array, A a, int v) {
+ int sum = 0;
+ for (int i = 1; i < 100; i *= 2) {
+ // The store to a.v cannot be directly hoisted because the preceding load array[i].v
+ // may alias it. Peeling preserves the first iteration load-before-store ordering.
+ // Later identical stores are redundant.
+ sum += array[i].v;
+ a.v = v;
+ }
+ return sum;
+ }
+
+ @Run(test = "testStorePeeling4")
+ public void runStorePeeling4() {
+ A[] array = new A[100];
+ for (int i = 0; i < 100; i++) {
+ array[i] = new A();
+ }
+
+ A unrelated = new A();
+ Asserts.assertEQ(0, testStorePeeling4(array, unrelated, 1));
+ Asserts.assertEQ(1, testStorePeeling4(array, array[2], 1));
+ array[2].v = 0;
+ Asserts.assertEQ(0, testStorePeeling4(array, array[1], 1));
+ }
+
+ @Test
+ @IR(counts = {IRNode.STORE_I, "1"})
+ private int testStorePeeling5(boolean b, A[] array, A a, int v) {
+ int sum = 0;
+ for (int i = 1; i < 100; i *= 2) {
+ // Similar to testStorePeeling4, and this test triggers peeling without
+ // StressLoopPeeling
+ if (b) {
+ break;
+ }
+
+ sum += array[i].v;
+ a.v = v;
+ }
+ return sum;
+ }
+
+ @Run(test = "testStorePeeling5")
+ private void runStorePeeling5() {
+ A[] array = new A[100];
+ for (int i = 0; i < 100; i++) {
+ array[i] = new A();
+ }
+ Asserts.assertEQ(0, testStorePeeling5(true, array, array[2], 1));
+
+ A unrelated = new A();
+ Asserts.assertEQ(0, testStorePeeling5(true, array, unrelated, 1));
+ Asserts.assertEQ(0, testStorePeeling5(false, array, unrelated, 1));
+ Asserts.assertEQ(1, testStorePeeling5(false, array, array[2], 1));
+ array[2].v = 0;
+ Asserts.assertEQ(0, testStorePeeling5(false, array, array[1], 1));
+ }
+
+ @Test
+ private void testStoreNotElided1(int limit, A a, int v) {
+ for (int i = 1; i < 100; i *= 2) {
+ // Cannot elide the store after peeling because it does not dominate the back edge
+ if (i >= limit) {
+ a.v = v;
+ }
+ }
+ }
+
+ @Run(test = "testStoreNotElided1")
+ public void runStoreNotElided1() {
+ A a = new A();
+ testStoreNotElided1(-1, a, 1);
+ Asserts.assertEQ(1, a.v);
+ a.v = 0;
+ testStoreNotElided1(50, a, 1);
+ Asserts.assertEQ(1, a.v);
+ a.v = 0;
+ testStoreNotElided1(200, a, 1);
+ Asserts.assertEQ(0, a.v);
+ }
+
+ @Test
+ private void testStoreNotElided2(A a1, A a2, int v1, int v2) {
+ for (int i = 1; i < 100; i *= 2) {
+ // Cannot elide the store after peeling because there is an interfering store
+ a2.v = Float.floatToRawIntBits(i * v2);
+ a1.v = v1;
+ }
+ }
+
+ @Run(test = "testStoreNotElided2")
+ public void runStoreNotElided2() {
+ A a1 = new A();
+ A a2 = new A();
+ testStoreNotElided2(a1, a2, 1, 1);
+ Asserts.assertEQ(1, a1.v);
+ Asserts.assertEQ(Float.floatToRawIntBits(64), a2.v);
+ a1.v = 0;
+ testStoreNotElided2(a1, a1, 1, 1);
+ Asserts.assertEQ(1, a1.v);
+ }
+
+ @Test
+ @IR(counts = {IRNode.STORE_I, "2"}, phase = CompilePhase.AFTER_PRE_MAIN_POST)
+ private int testPreMainPost(int[] array1, int[] array2, int v) {
+ int sum = 0;
+ for (int i = 0; i < array1.length; i++) {
+ // Iteration split also removes the store from the main-loop. It does not elide the
+ // store in the post-loop, though.
+ sum += array1[i];
+ array2[1] = v;
+ }
+ return sum;
+ }
+
+ @Run(test = "testPreMainPost")
+ public void runPreMainPost() {
+ int[] array1 = new int[100];
+ int[] array2 = new int[100];
+ Asserts.assertEQ(0, testPreMainPost(array1, array2, 1));
+ Asserts.assertEQ(1, testPreMainPost(array1, array1, 1));
+ }
+}
From a675ed259f508e99d8b6e10f76b6bbdbdfa8ce9c Mon Sep 17 00:00:00 2001
From: Thomas Schatzl
Date: Thu, 8 Oct 2026 09:58:23 +0000
Subject: [PATCH 002/101] 8393657: G1: Fix num_region naming for heap expansion
and shrinking
Reviewed-by: aboldtch
---
src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 28 +++----
src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 2 +-
.../share/gc/g1/g1CommittedRegionMap.hpp | 6 +-
.../share/gc/g1/g1HeapRegionManager.cpp | 74 +++++++++----------
.../share/gc/g1/g1HeapRegionManager.hpp | 10 +--
src/hotspot/share/gc/g1/g1HeapRegionSet.hpp | 4 +-
6 files changed, 62 insertions(+), 62 deletions(-)
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
index 8577e9e0659..ed7dfa4e3e4 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
@@ -552,18 +552,18 @@ HeapWord* G1CollectedHeap::alloc_archive_region(size_t word_size) {
// when mmap'ing archived heap data in, so pre-touching is wasted.
FlagSetting fs(AlwaysPreTouch, false);
- size_t commits = 0;
+ size_t num_newly_activated_regions = 0;
// Attempt to allocate towards the end of the heap.
HeapWord* start_addr = reserved.end() - align_up(word_size, G1HeapRegion::GrainWords);
MemRegion range = MemRegion(start_addr, word_size);
HeapWord* last_address = range.last();
- if (!_hrm.allocate_containing_regions(range, &commits, workers())) {
+ if (!_hrm.allocate_containing_regions(range, &num_newly_activated_regions, workers())) {
return nullptr;
}
increase_used(word_size * HeapWordSize);
- if (commits != 0) {
+ if (num_newly_activated_regions != 0) {
log_debug(gc, ergo, heap)("Attempt heap expansion (allocate archive regions). Total size: %zuB",
- G1HeapRegion::GrainWords * HeapWordSize * commits);
+ G1HeapRegion::GrainWords * HeapWordSize * num_newly_activated_regions);
}
// Mark each G1 region touched by the range as old, add it to
@@ -607,22 +607,22 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion range) {
p2i(start_address), p2i(last_address));
size_used += range.byte_size();
- uint max_shrink_count = 0;
+ uint max_num_regions_to_shrink = 0;
if (capacity() > MinHeapSize) {
size_t max_shrink_bytes = capacity() - MinHeapSize;
- max_shrink_count = (uint)(max_shrink_bytes / G1HeapRegion::GrainBytes);
+ max_num_regions_to_shrink = (uint)(max_shrink_bytes / G1HeapRegion::GrainBytes);
}
- uint shrink_count = 0;
+ uint num_shrunk_regions = 0;
// Free, empty and uncommit regions with CDS archive content.
auto dealloc_archive_region = [&] (G1HeapRegion* r, bool is_last) {
guarantee(r->is_old(), "Expected old region at index %u", r->hrm_index());
_old_set.remove(r);
r->set_free();
r->set_top(r->bottom());
- if (shrink_count < max_shrink_count) {
+ if (num_shrunk_regions < max_num_regions_to_shrink) {
_hrm.shrink_at(r->hrm_index(), 1);
- shrink_count++;
+ num_shrunk_regions++;
} else {
_hrm.insert_into_free_list(r);
}
@@ -630,11 +630,11 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion range) {
iterate_regions_in_range(range, dealloc_archive_region);
- if (shrink_count != 0) {
+ if (num_shrunk_regions != 0) {
log_debug(gc, ergo, heap)("Attempt heap shrinking (CDS archive regions). Total size: %zuB (%u Regions)",
- G1HeapRegion::GrainWords * HeapWordSize * shrink_count, shrink_count);
+ G1HeapRegion::GrainWords * HeapWordSize * num_shrunk_regions, num_shrunk_regions);
// Explicit uncommit.
- uncommit_regions(shrink_count);
+ uncommit_regions(num_shrunk_regions);
}
decrease_used(size_used);
}
@@ -2512,8 +2512,8 @@ void G1CollectedHeap::gc_epilogue(bool full) {
_refinement_epoch++;
}
-uint G1CollectedHeap::uncommit_regions(uint region_limit) {
- return _hrm.uncommit_inactive_regions(region_limit);
+uint G1CollectedHeap::uncommit_regions(uint max_num_regions_to_uncommit) {
+ return _hrm.uncommit_inactive_regions(max_num_regions_to_uncommit);
}
bool G1CollectedHeap::has_uncommittable_regions() {
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
index cd8ea2124dc..ce57d371506 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
@@ -584,7 +584,7 @@ class G1CollectedHeap : public CollectedHeap {
// Check if there is memory to uncommit and if so schedule a task to do it.
void uncommit_regions_if_necessary();
// Immediately uncommit uncommittable regions.
- uint uncommit_regions(uint region_limit);
+ uint uncommit_regions(uint max_num_regions_to_uncommit);
bool has_uncommittable_regions();
G1NUMA* numa() const { return _numa; }
diff --git a/src/hotspot/share/gc/g1/g1CommittedRegionMap.hpp b/src/hotspot/share/gc/g1/g1CommittedRegionMap.hpp
index 7df88616007..5129f35d903 100644
--- a/src/hotspot/share/gc/g1/g1CommittedRegionMap.hpp
+++ b/src/hotspot/share/gc/g1/g1CommittedRegionMap.hpp
@@ -43,9 +43,9 @@ class G1HeapRegionRange : public StackObj {
uint num_regions() const { return _end - _start; }
};
-// The G1CommittedRegionMap keeps track of which regions are currently committed.
-// It tracks both regions ready for use and if there are any regions ready for
-// uncommit. We basically have three states. Uncommitted, Active, Inactive. All
+// The G1CommittedRegionMap keeps track of the state of regions.
+// It tracks both regions ready for use (active) and if there are any regions ready for
+// uncommit (inactive). We basically have three states. Uncommitted, Active, Inactive. All
// regions that are either Active or Inactive are committed.
//
// State transitions:
diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp
index b637e7e83b4..ff86b003e75 100644
--- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp
+++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp
@@ -245,7 +245,7 @@ void G1HeapRegionManager::reactivate_regions(uint start, uint num_regions) {
}
void G1HeapRegionManager::deactivate_regions(uint start, uint num_regions) {
- assert(num_regions > 0, "Need to specify at least one region to uncommit, tried to uncommit zero regions at %u", start);
+ assert(num_regions > 0, "Need to specify at least one region to deactivate, tried to deactivate zero regions at %u", start);
assert(num_committed_regions() >= num_regions, "pre-condition");
// Reset NUMA index to and print state change.
@@ -290,10 +290,10 @@ bool G1HeapRegionManager::has_inactive_regions() const {
return _committed_map.num_inactive_regions() > 0;
}
-uint G1HeapRegionManager::uncommit_inactive_regions(uint limit) {
- assert(limit > 0, "Need to specify at least one region to uncommit");
+uint G1HeapRegionManager::uncommit_inactive_regions(uint max_num_regions_to_uncommit) {
+ assert(max_num_regions_to_uncommit > 0, "Need to specify at least one region to uncommit");
- uint uncommitted = 0;
+ uint num_uncommitted_regions = 0;
uint offset = 0;
do {
MutexLocker uc(G1Uncommit_lock, Mutex::_no_safepoint_check_flag);
@@ -301,22 +301,22 @@ uint G1HeapRegionManager::uncommit_inactive_regions(uint limit) {
// No more regions available for uncommit. Return the number of regions
// already uncommitted or 0 if there were no longer any inactive regions.
if (range.num_regions() == 0) {
- return uncommitted;
+ return num_uncommitted_regions;
}
uint start = range.start();
- uint num_regions = MIN2(range.num_regions(), limit - uncommitted);
- uncommitted += num_regions;
+ uint num_regions = MIN2(range.num_regions(), max_num_regions_to_uncommit - num_uncommitted_regions);
+ num_uncommitted_regions += num_regions;
uncommit_regions(start, num_regions);
- } while (uncommitted < limit);
+ } while (num_uncommitted_regions < max_num_regions_to_uncommit);
- assert(uncommitted == limit, "Invariant");
- return uncommitted;
+ assert(num_uncommitted_regions == max_num_regions_to_uncommit, "Invariant");
+ return num_uncommitted_regions;
}
uint G1HeapRegionManager::expand_inactive(uint num_regions) {
uint offset = 0;
- uint expanded = 0;
+ uint num_expanded_regions = 0;
do {
G1HeapRegionRange regions = _committed_map.next_inactive_range(offset);
@@ -325,20 +325,20 @@ uint G1HeapRegionManager::expand_inactive(uint num_regions) {
break;
}
- uint to_expand = MIN2(num_regions - expanded, regions.num_regions());
- reactivate_regions(regions.start(), to_expand);
- expanded += to_expand;
+ uint num_regions_to_expand = MIN2(num_regions - num_expanded_regions, regions.num_regions());
+ reactivate_regions(regions.start(), num_regions_to_expand);
+ num_expanded_regions += num_regions_to_expand;
offset = regions.end();
- } while (expanded < num_regions);
+ } while (num_expanded_regions < num_regions);
- return expanded;
+ return num_expanded_regions;
}
uint G1HeapRegionManager::expand_any(uint num_regions, WorkerThreads* pretouch_workers) {
assert(num_regions > 0, "Must expand at least 1 region");
uint offset = 0;
- uint expanded = 0;
+ uint num_expanded_regions = 0;
do {
G1HeapRegionRange regions = _committed_map.next_committable_range(offset);
@@ -347,13 +347,13 @@ uint G1HeapRegionManager::expand_any(uint num_regions, WorkerThreads* pretouch_w
break;
}
- uint to_expand = MIN2(num_regions - expanded, regions.num_regions());
- expand(regions.start(), to_expand, pretouch_workers);
- expanded += to_expand;
+ uint num_regions_to_expand = MIN2(num_regions - num_expanded_regions, regions.num_regions());
+ expand(regions.start(), num_regions_to_expand, pretouch_workers);
+ num_expanded_regions += num_regions_to_expand;
offset = regions.end();
- } while (expanded < num_regions);
+ } while (num_expanded_regions < num_regions);
- return expanded;
+ return num_expanded_regions;
}
uint G1HeapRegionManager::expand_by(uint num_regions, WorkerThreads* pretouch_workers) {
@@ -361,15 +361,15 @@ uint G1HeapRegionManager::expand_by(uint num_regions, WorkerThreads* pretouch_wo
// First "undo" any requests to uncommit memory concurrently by
// reverting such regions to being available.
- uint expanded = expand_inactive(num_regions);
+ uint num_expanded_regions = expand_inactive(num_regions);
// Commit more regions if needed.
- if (expanded < num_regions) {
- expanded += expand_any(num_regions - expanded, pretouch_workers);
+ if (num_expanded_regions < num_regions) {
+ num_expanded_regions += expand_any(num_regions - num_expanded_regions, pretouch_workers);
}
verify_optional();
- return expanded;
+ return num_expanded_regions;
}
void G1HeapRegionManager::expand_exact(uint start, uint num_regions, WorkerThreads* pretouch_workers) {
@@ -526,16 +526,16 @@ void G1HeapRegionManager::iterate(G1HeapRegionIndexClosure* blk) const {
}
}
-bool G1HeapRegionManager::allocate_containing_regions(MemRegion range, size_t* num_regions_committed, WorkerThreads* pretouch_workers) {
- size_t commits = 0;
+bool G1HeapRegionManager::allocate_containing_regions(MemRegion range, size_t* num_newly_activated_regions, WorkerThreads* pretouch_workers) {
+ size_t num_activated_regions = 0;
uint start_index = (uint)_regions.get_index_by_address(range.start());
uint last_index = (uint)_regions.get_index_by_address(range.last());
// Ensure that each G1 region in the range is free, returning false if not.
- // Commit those that are not yet available, and keep count.
+ // Activates inactive regions and commits others (making them Active) if necessary.
for (uint curr_index = start_index; curr_index <= last_index; curr_index++) {
if (!is_available(curr_index)) {
- commits++;
+ num_activated_regions++;
expand_exact(curr_index, 1, pretouch_workers);
}
G1HeapRegion* curr_region = _regions.get_by_index(curr_index);
@@ -545,7 +545,7 @@ bool G1HeapRegionManager::allocate_containing_regions(MemRegion range, size_t* n
}
allocate_free_regions_starting_at(start_index, (last_index - start_index) + 1);
- *num_regions_committed = commits;
+ *num_newly_activated_regions = num_activated_regions;
return true;
}
@@ -588,24 +588,24 @@ uint G1HeapRegionManager::shrink_by(uint num_regions_to_remove) {
return 0;
}
- uint removed = 0;
+ uint num_removed_regions = 0;
uint cur = _next_highest_used_hrm_index;
uint idx_last_found = 0;
uint num_last_found = 0;
- while ((removed < num_regions_to_remove) &&
+ while ((num_removed_regions < num_regions_to_remove) &&
(num_last_found = find_empty_from_idx_reverse(cur, &idx_last_found)) > 0) {
- uint to_remove = MIN2(num_regions_to_remove - removed, num_last_found);
+ uint num_regions_to_remove_from_range = MIN2(num_regions_to_remove - num_removed_regions, num_last_found);
- shrink_at(idx_last_found + num_last_found - to_remove, to_remove);
+ shrink_at(idx_last_found + num_last_found - num_regions_to_remove_from_range, num_regions_to_remove_from_range);
cur = idx_last_found;
- removed += to_remove;
+ num_removed_regions += num_regions_to_remove_from_range;
}
verify_optional();
- return removed;
+ return num_removed_regions;
}
void G1HeapRegionManager::shrink_at(uint index, size_t num_regions) {
diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp
index 0bef96ceba4..69c708717b4 100644
--- a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp
+++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp
@@ -259,8 +259,8 @@ class G1HeapRegionManager: public CHeapObj {
// Allocate the regions that contain the address range specified, committing the
// regions if necessary. Return false if any of the regions is already committed
- // and not free, and return the number of regions newly committed in num_regions_committed.
- bool allocate_containing_regions(MemRegion range, size_t* num_regions_committed, WorkerThreads* pretouch_workers);
+ // and not free, and return the number of regions newly activated in num_newly_activated_regions.
+ bool allocate_containing_regions(MemRegion range, size_t* num_newly_activated_regions, WorkerThreads* pretouch_workers);
// Apply blk->do_heap_region() on all committed regions in address order,
// terminating the iteration early if do_heap_region() returns true.
@@ -269,8 +269,8 @@ class G1HeapRegionManager: public CHeapObj {
void par_iterate(G1HeapRegionClosure* blk, G1HeapRegionClaimer* hrclaimer, const uint start_index) const;
- // Uncommit up to num_regions_to_remove regions that are completely free.
- // Return the actual number of uncommitted regions.
+ // Deactivate up to num_regions_to_remove regions that are completely free for later uncommit.
+ // Return the actual number of deactivated regions.
uint shrink_by(uint num_regions_to_remove);
// Remove a number of regions starting at the specified index, which must be available,
@@ -282,7 +282,7 @@ class G1HeapRegionManager: public CHeapObj {
// Uncommit inactive regions. Limit the number of regions to uncommit and return
// actual number uncommitted.
- uint uncommit_inactive_regions(uint limit);
+ uint uncommit_inactive_regions(uint max_num_regions_to_uncommit);
void verify();
diff --git a/src/hotspot/share/gc/g1/g1HeapRegionSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionSet.hpp
index 32646ea8423..6db3001b117 100644
--- a/src/hotspot/share/gc/g1/g1HeapRegionSet.hpp
+++ b/src/hotspot/share/gc/g1/g1HeapRegionSet.hpp
@@ -124,8 +124,8 @@ class G1HeapRegionSet : public G1HeapRegionSetBase {
G1HeapRegionSetBase(name, checker) {
}
- void bulk_remove(const uint removed) {
- _num_regions -= removed;
+ void bulk_remove(const uint num_removed_regions) {
+ _num_regions -= num_removed_regions;
}
};
From ee4d42c8fae5cd57e484d19d06e26c013f04486c Mon Sep 17 00:00:00 2001
From: Nizar Benalla
Date: Thu, 8 Oct 2026 14:45:14 +0000
Subject: [PATCH 003/101] 8385488: Compiler allows "case _ when" in switches,
but JLS doesn't
Reviewed-by: abimpoudis, vromero
---
.../classes/com/sun/tools/javac/parser/JavacParser.java | 3 +++
test/langtools/tools/javac/patterns/UnnamedErrors.java | 9 ++++++++-
test/langtools/tools/javac/patterns/UnnamedErrors.out | 3 ++-
3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
index 19daed61aa7..2d17523ed4f 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
@@ -982,6 +982,9 @@ public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType
mods = mods != null ? mods : optFinal(0);
JCExpression e;
if (token.kind == UNDERSCORE && parsedType == null) {
+ if (!allowVar && Feature.UNNAMED_VARIABLES.allowedInSource(source)){
+ log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.UseOfUnderscoreNotAllowedNonVariable);
+ }
nextToken();
checkSourceLevel(Feature.UNNAMED_VARIABLES);
pattern = toP(F.at(token.pos).AnyPattern());
diff --git a/test/langtools/tools/javac/patterns/UnnamedErrors.java b/test/langtools/tools/javac/patterns/UnnamedErrors.java
index ed345343cd6..f3c13cfee09 100644
--- a/test/langtools/tools/javac/patterns/UnnamedErrors.java
+++ b/test/langtools/tools/javac/patterns/UnnamedErrors.java
@@ -1,6 +1,6 @@
/*
* @test /nodynamiccopyright/
- * @bug 8304246 8309093
+ * @bug 8304246 8309093 8385488
* @summary Compiler Implementation for Unnamed patterns and variables
* @compile/fail/ref=UnnamedErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW UnnamedErrors.java
*/
@@ -116,6 +116,13 @@ void testUnderscoreInExpression() {
}
}
+ int guardedPattern(String s) {
+ return switch (s) {
+ case _ when s.isEmpty() -> 1;
+ default -> 0;
+ };
+ }
+
class Lock implements AutoCloseable {
@Override
public void close() {}
diff --git a/test/langtools/tools/javac/patterns/UnnamedErrors.out b/test/langtools/tools/javac/patterns/UnnamedErrors.out
index d0bc7514737..1bcd37e8f5b 100644
--- a/test/langtools/tools/javac/patterns/UnnamedErrors.out
+++ b/test/langtools/tools/javac/patterns/UnnamedErrors.out
@@ -24,6 +24,7 @@ UnnamedErrors.java:103:26: compiler.err.expected: =
UnnamedErrors.java:109:13: compiler.err.use.of.underscore.not.allowed.with.brackets
UnnamedErrors.java:110:18: compiler.err.use.of.underscore.not.allowed.with.brackets
UnnamedErrors.java:114:24: compiler.err.use.of.underscore.not.allowed.non.variable
+UnnamedErrors.java:121:18: compiler.err.use.of.underscore.not.allowed.non.variable
UnnamedErrors.java:10:17: compiler.err.already.defined: kindname.variable, x, kindname.class, UnnamedErrors
UnnamedErrors.java:35:13: compiler.err.unconditional.pattern.and.default
UnnamedErrors.java:44:18: compiler.err.pattern.dominated
@@ -33,4 +34,4 @@ UnnamedErrors.java:67:32: compiler.err.flows.through.from.pattern
UnnamedErrors.java:81:56: compiler.err.already.defined: kindname.variable, x2, kindname.method, guardErrors(java.lang.Object,int,int)
UnnamedErrors.java:82:13: compiler.err.switch.mixing.case.types
UnnamedErrors.java:89:30: compiler.err.pattern.dominated
-35 errors
\ No newline at end of file
+36 errors
From fd9cf2b9df52f3e1ea40e69eed7e6b67f260d04d Mon Sep 17 00:00:00 2001
From: Axel Boldt-Christmas
Date: Thu, 8 Oct 2026 15:29:14 +0000
Subject: [PATCH 004/101] 8386128: ZGC: Race when retiring shared medium
relocation target page 8386706: [REDO] ZGC: Incorrect object undo in
relocation race for relocation workers
Co-authored-by: Stefan Karlsson
Reviewed-by: stefank, eosterlund
---
src/hotspot/share/gc/z/zPage.cpp | 9 +-
src/hotspot/share/gc/z/zPage.hpp | 8 +-
src/hotspot/share/gc/z/zPage.inline.hpp | 17 +-
src/hotspot/share/gc/z/zRelocate.cpp | 463 ++++++++++++++++--------
4 files changed, 352 insertions(+), 145 deletions(-)
diff --git a/src/hotspot/share/gc/z/zPage.cpp b/src/hotspot/share/gc/z/zPage.cpp
index b6e0d162ef0..da41375a78e 100644
--- a/src/hotspot/share/gc/z/zPage.cpp
+++ b/src/hotspot/share/gc/z/zPage.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2026, 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,7 +42,8 @@ ZPage::ZPage(ZPageType type, ZPageAge age, const ZVirtualMemory& vmem, ZMultiPar
_livemap(object_max_count()),
_remembered_set(),
_multi_partition_tracker(multi_partition_tracker),
- _relocate_promoted(false) {
+ _relocate_promoted(false),
+ _relocation_target_ref_count(0) {
assert(!_virtual.is_null(), "Should not be null");
assert((_type == ZPageType::small && size() == ZPageSizeSmall) ||
(_type == ZPageType::medium && ZPageSizeMediumMin <= size() && size() <= ZPageSizeMediumMax) ||
@@ -63,6 +64,8 @@ ZPage::ZPage(ZPageType type, ZPageAge age, const ZVirtualMemory& vmem, ZMultiPar
ZPage* ZPage::clone_for_promotion() const {
assert(_age != ZPageAge::old, "must be used for promotion");
+ assert(_relocation_target_ref_count == 0, "Invalid ref count");
+
// Only copy type and memory layouts, and also update _top. Let the rest be
// lazily reconstructed when needed.
ZPage* const page = new ZPage(_type, ZPageAge::old, _virtual, _multi_partition_tracker, _single_partition_id);
@@ -101,6 +104,8 @@ void ZPage::remset_alloc() {
}
ZPage* ZPage::reset(ZPageAge age) {
+ assert(_relocation_target_ref_count == 0, "Invalid ref count");
+
_age = age;
_generation_id = age == ZPageAge::old
diff --git a/src/hotspot/share/gc/z/zPage.hpp b/src/hotspot/share/gc/z/zPage.hpp
index 0fc134e170a..b119992964b 100644
--- a/src/hotspot/share/gc/z/zPage.hpp
+++ b/src/hotspot/share/gc/z/zPage.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2026, 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,6 +53,7 @@ class ZPage : public CHeapObj {
ZRememberedSet _remembered_set;
ZMultiPartitionTracker* const _multi_partition_tracker;
volatile bool _relocate_promoted;
+ uint32_t _relocation_target_ref_count;
const char* type_to_string() const;
@@ -107,6 +108,11 @@ class ZPage : public CHeapObj {
bool allows_raw_null() const;
void set_is_relocate_promoted();
+ // References owned by relocation target slots. These are used to keep medium
+ // relocation target pages alive while shared and worker-local slots overlap.
+ void inc_relocation_target_ref_count();
+ bool dec_relocation_target_ref_count();
+
uint32_t seqnum() const;
bool is_allocating() const;
bool is_relocatable() const;
diff --git a/src/hotspot/share/gc/z/zPage.inline.hpp b/src/hotspot/share/gc/z/zPage.inline.hpp
index 5ff6278ea09..89f490d889f 100644
--- a/src/hotspot/share/gc/z/zPage.inline.hpp
+++ b/src/hotspot/share/gc/z/zPage.inline.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2026, 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
@@ -26,6 +26,7 @@
#include "gc/z/zPage.hpp"
+#include "cppstdlib/limits.hpp"
#include "gc/z/zAddress.inline.hpp"
#include "gc/z/zGeneration.inline.hpp"
#include "gc/z/zGlobals.hpp"
@@ -173,6 +174,20 @@ inline ZPageAge ZPage::age() const {
return _age;
}
+inline void ZPage::inc_relocation_target_ref_count() {
+ assert(_relocation_target_ref_count < std::numeric_limits::max(), "Relocation target ref count overflow");
+
+ _relocation_target_ref_count++;
+}
+
+inline bool ZPage::dec_relocation_target_ref_count() {
+ assert(_relocation_target_ref_count > 0, "Invalid ref count");
+
+ _relocation_target_ref_count--;
+
+ return _relocation_target_ref_count == 0;
+}
+
inline uint32_t ZPage::seqnum() const {
return _seqnum;
}
diff --git a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp
index 1c2a4078904..6cfcb40ccaf 100644
--- a/src/hotspot/share/gc/z/zRelocate.cpp
+++ b/src/hotspot/share/gc/z/zRelocate.cpp
@@ -48,6 +48,7 @@
#include "gc/z/zVerify.hpp"
#include "gc/z/zWorkers.hpp"
#include "prims/jvmtiTagMap.hpp"
+#include "runtime/atomic.hpp"
#include "runtime/atomicAccess.hpp"
#include "utilities/debug.hpp"
@@ -415,113 +416,215 @@ zaddress ZRelocate::forward_object(ZForwarding* forwarding, zaddress_unsafe from
return to_addr;
}
-static ZPage* alloc_page(ZForwarding* forwarding) {
- if (ZStressRelocateInPlace) {
- // Simulate failure to allocate a new page. This will
- // cause the page being relocated to be relocated in-place.
- return nullptr;
- }
+//
+// Base class that handles the low-level allocation and retiring of pages.
+//
+class ZRelocateAllocatorShared {
+private:
+ ZGeneration* const _generation;
- const ZPageType type = forwarding->type();
- const size_t size = forwarding->size();
- const ZPageAge age = forwarding->to_age();
- const uint32_t preferred_partition = forwarding->partition_id();
+protected:
+ Atomic _in_place_count;
- ZAllocationFlags flags;
- flags.set_non_blocking();
- flags.set_gc_relocation();
+public:
+ ZRelocateAllocatorShared(ZGeneration* generation)
+ : _generation(generation),
+ _in_place_count() {}
- return ZHeap::heap()->alloc_page(type, size, flags, age, preferred_partition);
-}
+ ZPage* alloc_page(ZForwarding* forwarding) {
+ if (ZStressRelocateInPlace) {
+ // Simulate failure to allocate a new page. This will
+ // cause the page being relocated to be relocated in-place.
+ return nullptr;
+ }
+
+ const ZPageType type = forwarding->type();
+ const size_t size = forwarding->size();
+ const ZPageAge age = forwarding->to_age();
+ const uint32_t preferred_partition = forwarding->partition_id();
-static void retire_target_page(ZGeneration* generation, ZPage* page) {
- if (generation->is_young() && page->is_old()) {
- generation->increase_promoted(page->used());
- } else {
- generation->increase_compacted(page->used());
+ ZAllocationFlags flags;
+ flags.set_non_blocking();
+ flags.set_gc_relocation();
+
+ return ZHeap::heap()->alloc_page(type, size, flags, age, preferred_partition);
}
- // Free target page if it is empty. We can end up with an empty target
- // page if we allocated a new target page, and then lost the race to
- // relocate the remaining objects, leaving the target page empty when
- // relocation completed.
- if (page->used() == 0) {
- ZHeap::heap()->free_page(page);
+ void retire_target_page(ZPage* page) const {
+ if (_generation->is_young() && page->is_old()) {
+ _generation->increase_promoted(page->used());
+ } else {
+ _generation->increase_compacted(page->used());
+ }
}
-}
-class ZRelocateSmallAllocator {
-private:
- ZGeneration* const _generation;
- Atomic _in_place_count;
+ size_t in_place_count() const {
+ return _in_place_count.load_relaxed();
+ }
+};
+
+//
+// Base class for the handling of the per-worker target pages.
+//
+class ZRelocateAllocatorPerWorker {
+protected:
+ ZRelocationTargets* const _targets;
public:
- ZRelocateSmallAllocator(ZGeneration* generation)
- : _generation(generation),
- _in_place_count(0) {}
+ ZRelocateAllocatorPerWorker(ZRelocationTargets* targets)
+ : _targets(targets) {}
- ZPage* alloc_and_retire_target_page(ZForwarding* forwarding, ZPage* target) {
- ZPage* const page = alloc_page(forwarding);
- if (page == nullptr) {
+ ZPage* target(ZForwarding* forwarding) const {
+ return _targets->get(forwarding->partition_id(), forwarding->to_age());
+ }
+
+ void set_target(ZForwarding* forwarding, ZPage* target) {
+ _targets->set(forwarding->partition_id(), forwarding->to_age(), target);
+ }
+};
+
+//
+// Allocator class to handle life cycle of small pages and related statistics.
+//
+class ZRelocateAllocatorSharedSmall : public ZRelocateAllocatorShared {
+public:
+ ZRelocateAllocatorSharedSmall(ZGeneration* generation)
+ : ZRelocateAllocatorShared(generation) {}
+
+ ZPage* alloc_and_retire_target_page(ZForwarding* forwarding, ZPage* previous_target) {
+ ZPage* const new_target = alloc_page(forwarding);
+
+ if (new_target == nullptr) {
_in_place_count.add_then_fetch(1u);
}
- if (target != nullptr) {
- // Retire the old target page
- retire_target_page(_generation, target);
+ if (previous_target != nullptr) {
+ // Retire the previous target page
+ retire_target_page(previous_target);
}
- return page;
+ return new_target;
}
- void share_target_page(ZPage* page, uint32_t partition_id) {
- // Does nothing
- }
-
- void free_target_page(ZPage* page) {
+ void retire_local_target_page(ZPage* page) {
if (page != nullptr) {
- retire_target_page(_generation, page);
+ retire_target_page(page);
}
}
+};
+
+//
+// The allocator class for the per-worker operations on small pages.
+//
+class ZRelocateAllocatorPerWorkerSmall : public ZRelocateAllocatorPerWorker {
+private:
+ ZRelocateAllocatorSharedSmall* const _shared_allocator;
+
+public:
+ ZRelocateAllocatorPerWorkerSmall(ZRelocationTargets* targets,
+ ZRelocateAllocatorSharedSmall* shared_allocator)
+ : ZRelocateAllocatorPerWorker(targets),
+ _shared_allocator(shared_allocator) {}
+
+ ~ZRelocateAllocatorPerWorkerSmall() {
+ // Retire all local target pages
+ _targets->apply_and_clear_targets([&](ZPage* page) {
+ _shared_allocator->retire_local_target_page(page);
+ });
+ }
- zaddress alloc_object(ZPage* page, size_t size) const {
- return (page != nullptr) ? page->alloc_object(size) : zaddress::null;
+ zaddress alloc_object(ZForwarding* forwarding, size_t size) const {
+ ZPage* const page = target(forwarding);
+ if (page == nullptr) {
+ return zaddress::null;
+ }
+
+ return page->alloc_object(size);
}
- void undo_alloc_object(ZPage* page, zaddress addr, size_t size) const {
+ void undo_alloc_object(ZForwarding* forwarding, zaddress addr, size_t size) const {
+ ZPage* const page = target(forwarding);
+ assert(page != nullptr, "Invalid state");
+
page->undo_alloc_object(addr, size);
}
- size_t in_place_count() const {
- return _in_place_count.load_relaxed();
+ bool alloc_and_set_target_page(ZForwarding* forwarding) {
+ ZPage* const previous_target = target(forwarding);
+
+ // Allocate the new target
+ ZPage* const new_target = _shared_allocator->alloc_and_retire_target_page(forwarding, previous_target);
+
+ // Set the new target page
+ set_target(forwarding, new_target);
+
+ return new_target != nullptr;
+ }
+
+ void set_target_for_in_place_relocation(ZForwarding* forwarding, ZPage* new_target) {
+ precond(target(forwarding) == nullptr);
+
+ set_target(forwarding, new_target);
+ }
+
+ void reuse_in_place_target_page(ZForwarding* forwarding) {
+ // The page is already installed in the target.
+ precond(target(forwarding) != nullptr);
+
+ // Small pages are not shared.
}
};
-class ZRelocateMediumAllocator {
+//
+// Allocator class to handle life cycle of medium pages and related statistics.
+//
+class ZRelocateAllocatorSharedMedium : public ZRelocateAllocatorShared {
private:
- ZGeneration* const _generation;
- ZConditionLock _lock;
- ZRelocationTargets* _shared_targets;
- bool _in_place;
- Atomic _in_place_count;
+ ZRelocationTargets* const _shared_targets;
+ ZConditionLock _lock;
+ bool _in_place;
public:
- ZRelocateMediumAllocator(ZGeneration* generation, ZRelocationTargets* shared_targets)
- : _generation(generation),
- _lock(),
+ ZRelocateAllocatorSharedMedium(ZGeneration* generation, ZRelocationTargets* shared_targets)
+ : ZRelocateAllocatorShared(generation),
_shared_targets(shared_targets),
- _in_place(false),
- _in_place_count(0) {}
+ _lock(),
+ _in_place(false) {}
+
+ ~ZRelocateAllocatorSharedMedium() {
+ ZLocker locker(&_lock);
- ~ZRelocateMediumAllocator() {
+ // Release and clear all shared targets
_shared_targets->apply_and_clear_targets([&](ZPage* page) {
- if (page != nullptr) {
- retire_target_page(_generation, page);
- }
+ release_target_page(page);
});
}
- ZPage* alloc_and_retire_target_page(ZForwarding* forwarding, ZPage* target) {
+private:
+ ZPage* shared_target(ZForwarding* forwarding) const {
+ return _shared_targets->get(forwarding->partition_id(), forwarding->to_age());
+ }
+
+ void set_shared_target(ZForwarding* forwarding, ZPage* target) {
+ _shared_targets->set(forwarding->partition_id(), forwarding->to_age(), target);
+ }
+
+ // Target references are owned by target slots, not by transient local
+ // variables. These helpers must be called with _lock held.
+ void retain_target_page(ZPage* page) const {
+ if (page != nullptr) {
+ page->inc_relocation_target_ref_count();
+ }
+ }
+
+ void release_target_page(ZPage* page) const {
+ if (page != nullptr && page->dec_relocation_target_ref_count()) {
+ retire_target_page(page);
+ }
+ }
+
+public:
+ ZPage* alloc_and_release_target_page(ZForwarding* forwarding, ZPage* previous_target) {
ZLocker locker(&_lock);
// Wait for any ongoing in-place relocation to complete
@@ -529,57 +632,144 @@ class ZRelocateMediumAllocator {
_lock.wait();
}
- // Allocate a new page only if the shared page is the same as the
- // current target page. The shared page will be different from the
- // current target page if another thread shared a page, or allocated
- // a new page.
- const ZPageAge to_age = forwarding->to_age();
- const uint32_t partition_id = forwarding->partition_id();
- if (_shared_targets->get(partition_id, to_age) == target) {
- ZPage* const to_page = alloc_page(forwarding);
- _shared_targets->set(partition_id, to_age, to_page);
- if (to_page == nullptr) {
- _in_place_count.add_then_fetch(1u);
- _in_place = true;
- }
+ ZPage* const previous_shared_target = shared_target(forwarding);
- // This thread is responsible for retiring the shared target page
- if (target != nullptr) {
- retire_target_page(_generation, target);
- }
+ // Check if another thread already installed a new shared target page
+ if (previous_target != previous_shared_target) {
+ // Someone else beat us to installing a new shared target page
+
+ // Release from local usage
+ release_target_page(previous_target);
+
+ // Retain for local usage
+ retain_target_page(previous_shared_target);
+
+ return previous_shared_target;
+ }
+
+ // Allocate new target page
+ ZPage* const new_target = alloc_page(forwarding);
+
+ if (new_target == nullptr) {
+ // No memory available, fallback to in-place relocation
+ _in_place_count.add_then_fetch(1u);
+ _in_place = true;
}
- return _shared_targets->get(partition_id, to_age);
+ // Release from shared usage
+ release_target_page(previous_shared_target);
+
+ // Retain for shared usage
+ retain_target_page(new_target);
+
+ // Replace the previous shared target page
+ set_shared_target(forwarding, new_target);
+
+ // Release from local usage
+ release_target_page(previous_target);
+
+ // Retain for local usage
+ retain_target_page(new_target);
+
+ return new_target;
}
- void share_target_page(ZPage* page, uint32_t partition_id) {
- const ZPageAge age = page->age();
+ void share_in_place_target_page(ZForwarding* forwarding, ZPage* new_target) {
+ ZLocker locker(&_lock);
+
+ // Retain for shared usage
+ retain_target_page(new_target);
+
+ // Set the new, shared target page
+ assert(shared_target(forwarding) == nullptr, "Invalid state");
+ set_shared_target(forwarding, new_target);
+ // Retain for local usage
+ retain_target_page(new_target);
+ }
+
+ void signal_in_place_done(ZForwarding* forwarding) {
ZLocker locker(&_lock);
- assert(_in_place, "Invalid state");
- assert(_shared_targets->get(partition_id, age) == nullptr, "Invalid state");
- assert(page != nullptr, "Invalid page");
- _shared_targets->set(partition_id, age, page);
+ // The page is already installed as the shared target.
+ precond(shared_target(forwarding) != nullptr);
+ precond(_in_place);
+
_in_place = false;
_lock.notify_all();
}
- void free_target_page(ZPage* page) {
- // Does nothing
+ void release_local_target_page(ZPage* page) {
+ if (page != nullptr) {
+ ZLocker locker(&_lock);
+ release_target_page(page);
+ }
+ }
+};
+
+//
+// The allocator class for the per-worker operations on medium pages.
+//
+class ZRelocateAllocatorPerWorkerMedium : public ZRelocateAllocatorPerWorker {
+private:
+ ZRelocateAllocatorSharedMedium* const _shared_allocator;
+
+public:
+ ZRelocateAllocatorPerWorkerMedium(ZRelocationTargets* targets,
+ ZRelocateAllocatorSharedMedium* shared_allocator)
+ : ZRelocateAllocatorPerWorker(targets),
+ _shared_allocator(shared_allocator) {}
+
+ ~ZRelocateAllocatorPerWorkerMedium() {
+ // Release the local target pages
+ _targets->apply_and_clear_targets([&](ZPage* page) {
+ _shared_allocator->release_local_target_page(page);
+ });
+ }
+
+ bool alloc_and_set_target_page(ZForwarding* forwarding) {
+ ZPage* const previous_target = target(forwarding);
+
+ // Allocate and update shared target
+ ZPage* const new_target = _shared_allocator->alloc_and_release_target_page(forwarding, previous_target);
+
+ // Update the local target
+ set_target(forwarding, new_target);
+
+ return new_target != nullptr;
}
- zaddress alloc_object(ZPage* page, size_t size) const {
- return (page != nullptr) ? page->alloc_object_atomic(size) : zaddress::null;
+ void set_target_for_in_place_relocation(ZForwarding* forwarding, ZPage* new_target) {
+ precond(target(forwarding) == nullptr);
+
+ _shared_allocator->share_in_place_target_page(forwarding, new_target);
+
+ set_target(forwarding, new_target);
}
- void undo_alloc_object(ZPage* page, zaddress addr, size_t size) const {
- page->undo_alloc_object_atomic(addr, size);
+ void reuse_in_place_target_page(ZForwarding* forwarding) {
+ // The page is already installed in the target.
+ precond(target(forwarding) != nullptr);
+
+ // Medium pages are shared.
+ _shared_allocator->signal_in_place_done(forwarding);
}
- size_t in_place_count() const {
- return _in_place_count.load_relaxed();
+ zaddress alloc_object(ZForwarding* forwarding, size_t size) const {
+ ZPage* const page = target(forwarding);
+ if (page == nullptr) {
+ return zaddress::null;
+ }
+
+ return page->alloc_object_atomic(size);
+ }
+
+ void undo_alloc_object(ZForwarding* forwarding, zaddress addr, size_t size) const {
+ ZPage* const page = target(forwarding);
+ assert(page != nullptr, "Invalid state");
+
+ page->undo_alloc_object_atomic(addr, size);
}
};
@@ -588,7 +778,6 @@ class ZRelocateWork : public StackObj {
private:
Allocator* const _allocator;
ZForwarding* _forwarding;
- ZRelocationTargets* _targets;
ZGeneration* const _generation;
size_t _other_promoted;
size_t _other_compacted;
@@ -607,11 +796,10 @@ class ZRelocateWork : public StackObj {
}
}
- zaddress try_relocate_object_inner(zaddress from_addr, uint32_t partition_id) {
+ zaddress try_relocate_object_inner(zaddress from_addr) {
ZForwardingCursor cursor;
const size_t size = ZUtils::object_size(from_addr);
- ZPage* const to_page = _targets->get(partition_id, _forwarding->to_age());
// Lookup forwarding
{
@@ -624,8 +812,9 @@ class ZRelocateWork : public StackObj {
}
// Allocate object
- const zaddress allocated_addr = _allocator->alloc_object(to_page, size);
+ const zaddress allocated_addr = _allocator->alloc_object(_forwarding, size);
if (is_null(allocated_addr)) {
+ assert(!_forwarding->in_place_relocation(), "In-place object allocations should never fail");
// Allocation failed
return zaddress::null;
}
@@ -642,7 +831,7 @@ class ZRelocateWork : public StackObj {
const zaddress to_addr = _forwarding->insert(from_addr, allocated_addr, &cursor);
if (to_addr != allocated_addr) {
// Already relocated, undo allocation
- _allocator->undo_alloc_object(to_page, to_addr, size);
+ _allocator->undo_alloc_object(_forwarding, allocated_addr, size);
increase_other_forwarded(size);
}
@@ -821,8 +1010,8 @@ class ZRelocateWork : public StackObj {
}
}
- bool try_relocate_object(zaddress from_addr, uint32_t partition_id) {
- const zaddress to_addr = try_relocate_object_inner(from_addr, partition_id);
+ bool try_relocate_object(zaddress from_addr) {
+ const zaddress to_addr = try_relocate_object_inner(from_addr);
if (is_null(to_addr)) {
return false;
@@ -859,7 +1048,7 @@ class ZRelocateWork : public StackObj {
}
}
- ZPage* start_in_place_relocation(zoffset relocated_watermark) {
+ void start_in_place_relocation(zoffset relocated_watermark) {
_forwarding->in_place_relocation_claim_page();
_forwarding->in_place_relocation_start(relocated_watermark);
@@ -896,51 +1085,38 @@ class ZRelocateWork : public StackObj {
ZGeneration::young()->register_in_place_relocate_promoted(from_page);
}
- return to_page;
+ _allocator->set_target_for_in_place_relocation(_forwarding, to_page);
}
void relocate_object(oop obj) {
const zaddress addr = to_zaddress(obj);
assert(ZHeap::heap()->is_object_live(addr), "Should be live");
- const ZPageAge to_age = _forwarding->to_age();
- const uint32_t partition_id = _forwarding->partition_id();
-
- while (!try_relocate_object(addr, partition_id)) {
+ while (!try_relocate_object(addr)) {
// Failed to relocate object, try to allocate a new target page,
// or if that fails, use the page being relocated as the new target,
// which will cause it to be relocated in-place.
- ZPage* const target_page = _targets->get(partition_id, to_age);
- ZPage* to_page = _allocator->alloc_and_retire_target_page(_forwarding, target_page);
- _targets->set(partition_id, to_age, to_page);
-
- // We got a new page, retry relocation
- if (to_page != nullptr) {
+ if (_allocator->alloc_and_set_target_page(_forwarding)) {
+ // We got a new page, retry relocation
continue;
}
// Start in-place relocation to block other threads from accessing
// the page, or its forwarding table, until it has been released
// (relocation completed).
- to_page = start_in_place_relocation(ZAddress::offset(addr));
- _targets->set(partition_id, to_age, to_page);
+ start_in_place_relocation(ZAddress::offset(addr));
}
}
public:
- ZRelocateWork(Allocator* allocator, ZRelocationTargets* targets, ZGeneration* generation)
+ ZRelocateWork(Allocator* allocator, ZGeneration* generation)
: _allocator(allocator),
_forwarding(nullptr),
- _targets(targets),
_generation(generation),
_other_promoted(0),
_other_compacted(0) {}
~ZRelocateWork() {
- _targets->apply_and_clear_targets([&](ZPage* page) {
- _allocator->free_target_page(page);
- });
-
// Report statistics on-behalf of non-worker threads
_generation->increase_promoted(_other_promoted);
_generation->increase_compacted(_other_compacted);
@@ -1025,17 +1201,19 @@ class ZRelocateWork : public StackObj {
if (in_place) {
// Wait for all other threads to call release_page
- ZPage* const page = _forwarding->detach_page();
+ ZPage* const from_page = _forwarding->detach_page();
// Ensure that previous remset bits are cleared
- clear_remset_before_in_place_reuse(page);
+ clear_remset_before_in_place_reuse(from_page);
- page->log_msg(" (relocate page done in-place)");
+ from_page->log_msg(" (relocate page done in-place)");
- // Different pages when promoting
- const uint32_t target_partition = _forwarding->partition_id();
- ZPage* const target_page = _targets->get(target_partition, _forwarding->to_age());
- _allocator->share_target_page(target_page, target_partition);
+ // ZPage instances are different when pages have been promoted.
+ // The to_space in-place copy resides in the allocator's targets array.
+ _allocator->reuse_in_place_target_page(forwarding);
+
+ // Note that we don't delete the from_page of promoted pages here.
+ // The promoted pages have been registered and will be deleted later on.
} else {
// Wait for all other threads to call release_page
@@ -1084,8 +1262,8 @@ class ZRelocateTask : public ZRestartableTask {
ZPerNUMA* _iters;
ZPerWorker* _small_targets;
ZPerWorker* _medium_targets;
- ZRelocateSmallAllocator _small_allocator;
- ZRelocateMediumAllocator _medium_allocator;
+ ZRelocateAllocatorSharedSmall _shared_small_allocator;
+ ZRelocateAllocatorSharedMedium _shared_medium_allocator;
const size_t _total_forwardings;
public:
@@ -1101,8 +1279,8 @@ class ZRelocateTask : public ZRestartableTask {
_iters(iters),
_small_targets(small_targets),
_medium_targets(medium_targets),
- _small_allocator(_generation),
- _medium_allocator(_generation, shared_medium_targets),
+ _shared_small_allocator(_generation),
+ _shared_medium_allocator(_generation, shared_medium_targets),
_total_forwardings(relocation_set->nforwardings()) {
for (uint32_t i = 0; i < ZNUMA::count(); i++) {
@@ -1118,15 +1296,18 @@ class ZRelocateTask : public ZRestartableTask {
}
~ZRelocateTask() {
- _generation->stat_relocation()->at_relocate_end(_small_allocator.in_place_count(), _medium_allocator.in_place_count());
+ _generation->stat_relocation()->at_relocate_end(_shared_small_allocator.in_place_count(), _shared_medium_allocator.in_place_count());
// Signal that we're not using the queue anymore. Used mostly for asserts.
_queue->deactivate();
}
virtual void work() {
- ZRelocateWork small(&_small_allocator, _small_targets->addr(), _generation);
- ZRelocateWork medium(&_medium_allocator, _medium_targets->addr(), _generation);
+ ZRelocateAllocatorPerWorkerSmall small_allocator(_small_targets->addr(), &_shared_small_allocator);
+ ZRelocateAllocatorPerWorkerMedium medium_allocator(_medium_targets->addr(), &_shared_medium_allocator);
+
+ ZRelocateWork small(&small_allocator, _generation);
+ ZRelocateWork medium(&medium_allocator, _generation);
const uint32_t num_nodes = ZNUMA::count();
const uint32_t start_node = ZNUMA::id();
From 6edf757bc4152dc62c449ad69f30f7c85a1b4f00 Mon Sep 17 00:00:00 2001
From: Nizar Benalla
Date: Thu, 8 Oct 2026 15:36:46 +0000
Subject: [PATCH 005/101] 8393335: Update Pandoc to version 3.6.4 for Oracle
builds
Reviewed-by: erikj, jsjolen
---
doc/building.html | 117 +++++++++++++++---------------
doc/building.md | 4 +-
doc/hotspot-style.html | 8 +-
doc/hotspot-unit-tests.html | 8 +-
doc/ide.html | 8 +-
doc/starting-next-release.html | 8 +-
doc/testing.html | 29 ++++----
doc/value-class-preview.html | 8 +-
make/autoconf/basic_tools.m4 | 2 +-
make/conf/jib-profiles.js | 2 +-
make/devkit/createPandocBundle.sh | 11 ++-
11 files changed, 104 insertions(+), 101 deletions(-)
diff --git a/doc/building.html b/doc/building.html
index 53defa0c61c..fb18b1589c3 100644
--- a/doc/building.html
+++ b/doc/building.html
@@ -11,8 +11,11 @@
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
- ul.task-list{list-style: none;}
+ /* The extra [class] is a hack that increases specificity enough to
+ override a similar rule in reveal.js */
+ ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
+ font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
@@ -21,9 +24,6 @@
-
@@ -302,8 +302,8 @@
Special Considerations
in one of the columns of the ouput. If a directory is missing a short
name, the safest way to get one is to enable short names for that
particular volume with
-fsutil 8dot3name set <drive letter>: 0 (note that
-you need to run as administrator for this), and then re-create the
+fsutil 8dot3name set <drive letter>: 0 (note that you
+need to run as administrator for this), and then re-create the
particular directory. A short name should be generated automatically
then. Another option is to manually assign a short name to the directory
using
@@ -366,13 +366,14 @@
In order to use Branch Protection features in the VM,
---enable-branch-protection must be used. This option requires C++
-compiler support for -mbranch-protection=standard in GCC 9.1.0+ or
-Clang 10+ on Linux/AArch64 or for /guard:signret in Visual Studio
-2019+ on Windows/ARM64. The resulting build can be run on both machines with and
-without support for branch protection in hardware. Branch Protection is fully
-supported for the Linux/AArch64 target and only partially supported for the
-Windows/ARM64 targets.
+--enable-branch-protection must be used. This option
+requires C++ compiler support for
+-mbranch-protection=standard in GCC 9.1.0+ or Clang 10+ on
+Linux/AArch64 or for /guard:signret in Visual Studio 2019+
+on Windows/ARM64. The resulting build can be run on both machines with
+and without support for branch protection in hardware. Branch Protection
+is fully supported for the Linux/AArch64 target and only partially
+supported for the Windows/ARM64 target.
Building on 32-bit ARM
This is not recommended. Instead, see the section on Cross-compiling.
@@ -389,25 +390,25 @@
Operating System
to date at the time of writing.
-
+
Operating system
Vendor/version used
-
+
Linux/x64
Oracle Linux 6.4 / 8.x
-
+
Linux/aarch64
Oracle Linux 7.6 / 8.x
-
+
macOS
macOS 14.x
-
+
Windows
Windows Server 2016
@@ -580,25 +581,25 @@
Native Compiler
system and toolchain.
-
+
Operating system
Supported toolchain
-
+
Linux
gcc, clang
-
+
macOS
Apple Xcode (using clang)
-
+
AIX
IBM Open XL C/C++
-
+
Windows
Microsoft Visual Studio
@@ -612,21 +613,21 @@
Native Compiler
to compile successfully without issues.
-
+
Operating system
Toolchain version
-
+
Linux
gcc 14.2.0
-
+
macOS
Apple Xcode 15.4 (using clang 15.0.0)
-
+
Windows
Microsoft Visual Studio 2022 version 17.13.2
@@ -902,7 +903,7 @@
Graphviz and Pandoc
href="https://pandoc.org">Pandoc is required. For full docs also Graphviz is required. Any recent
versions should work. For reference, and subject to change, Oracle
-builds use Graphviz 9.0.0 and Pandoc 2.19.2.
+builds use Graphviz 9.0.0 and Pandoc 3.6.4.
Running Configure
To build the JDK, you need a "configuration", which consists of a
directory where to store the build output, coupled with information
@@ -1473,27 +1474,27 @@
Using OpenJDK Devkits
following targets are known to work:
-
+
Supported devkit targets
-
+
x86_64-linux-gnu
-
+
aarch64-linux-gnu
-
+
arm-linux-gnueabihf
-
+
ppc64le-linux-gnu
-
+
riscv64-linux-gnu
-
+
s390x-linux-gnu
@@ -1589,7 +1590,7 @@
Using Debian debootstrap
-
+
Target
Debian tree
Debian arch
@@ -1598,98 +1599,98 @@
Using Debian debootstrap
-
+
x86
buster
i386
i386-linux-gnu
(all)
-
+
arm
buster
armhf
arm-linux-gnueabihf
(all)
-
+
aarch64
buster
arm64
aarch64-linux-gnu
(all)
-
+
ppc64le
buster
ppc64el
powerpc64le-linux-gnu
(all)
-
+
s390x
buster
s390x
s390x-linux-gnu
(all)
-
+
mipsle
buster
mipsel
mipsel-linux-gnu
zero
-
+
mips64le
buster
mips64el
mips64el-linux-gnueabi64
zero
-
+
armel
buster
arm
arm-linux-gnueabi
zero
-
+
ppc
sid
powerpc
powerpc-linux-gnu
zero
-
+
ppc64be
sid
ppc64
powerpc64-linux-gnu
(all)
-
+
m68k
sid
m68k
m68k-linux-gnu
zero
-
+
alpha
sid
alpha
alpha-linux-gnu
zero
-
+
sh4
sid
sh4
sh4-linux-gnu
zero
-
+
riscv64
sid
riscv64
@@ -1818,13 +1819,13 @@
Ccache
recommend evaluating it for yourself. To enable it, make sure it's on
the path and configure with --enable-ccache.
Sccache
-
The JDK build supports building with sccache when using gcc, clang, or
-Microsoft toolchains. To enable it, make sure the sccache binary is on the path
-(or specify the path to the binary using the SCCACHE argument to
-the configure script) and configure with --enable-sccache. To
-optionally specify where sccache stores its cache files, use
---with-sccache-dir. Precompiled headers are disabled when sccache
-is enabled.
+
The JDK build supports building with sccache when using gcc, clang,
+or Microsoft toolchains. To enable it, make sure the sccache binary is
+on the path (or specify the path to the binary using the
+SCCACHE argument to the configure script) and configure
+with --enable-sccache. To optionally specify where sccache
+stores its cache files, use --with-sccache-dir. Precompiled
+headers are disabled when sccache is enabled.
Precompiled Headers
By default, the Hotspot build uses pre-compiled headers (PCH) on the
toolchains were it is properly supported (clang, gcc, and Visual
@@ -1961,7 +1962,7 @@
Running "make doctor"
* Checking for warnings from configure...
---
The following warnings were produced. Repeated here for convenience:
-WARNING: pandoc is version 3.1.9, not the recommended version 2.19.2
+WARNING: pandoc is version 3.1.9, not the recommended version 3.6.4
---
! Inspect the warnings, fix any problems, and re-run configure
diff --git a/doc/building.md b/doc/building.md
index 17eaf760b06..d273010ddc1 100644
--- a/doc/building.md
+++ b/doc/building.md
@@ -713,7 +713,7 @@ In order to build man pages and the full docs (see the `--enable-full-docs`
configure option) [Pandoc](https://pandoc.org) is required. For full docs also
[Graphviz](https://www.graphviz.org) is required. Any recent versions should
work. For reference, and subject to change, Oracle builds use Graphviz
-9.0.0 and Pandoc 2.19.2.
+9.0.0 and Pandoc 3.6.4.
## Running Configure
@@ -1720,7 +1720,7 @@ certain well-known problems, but it can never find all possible errors.
* Checking for warnings from configure...
---
The following warnings were produced. Repeated here for convenience:
-WARNING: pandoc is version 3.1.9, not the recommended version 2.19.2
+WARNING: pandoc is version 3.1.9, not the recommended version 3.6.4
---
! Inspect the warnings, fix any problems, and re-run configure
diff --git a/doc/hotspot-style.html b/doc/hotspot-style.html
index c7126622c7d..f0e07630a21 100644
--- a/doc/hotspot-style.html
+++ b/doc/hotspot-style.html
@@ -11,8 +11,11 @@
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
- ul.task-list{list-style: none;}
+ /* The extra [class] is a hack that increases specificity enough to
+ override a similar rule in reveal.js */
+ ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
+ font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
@@ -20,9 +23,6 @@
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
-
diff --git a/doc/hotspot-unit-tests.html b/doc/hotspot-unit-tests.html
index 5cc424322f9..a4a01b47051 100644
--- a/doc/hotspot-unit-tests.html
+++ b/doc/hotspot-unit-tests.html
@@ -11,8 +11,11 @@
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
- ul.task-list{list-style: none;}
+ /* The extra [class] is a hack that increases specificity enough to
+ override a similar rule in reveal.js */
+ ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
+ font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
@@ -20,9 +23,6 @@
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
-
diff --git a/doc/ide.html b/doc/ide.html
index ef6f434013e..2407becf94d 100644
--- a/doc/ide.html
+++ b/doc/ide.html
@@ -11,8 +11,11 @@
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
- ul.task-list{list-style: none;}
+ /* The extra [class] is a hack that increases specificity enough to
+ override a similar rule in reveal.js */
+ ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
+ font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
@@ -20,9 +23,6 @@
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
-
diff --git a/doc/starting-next-release.html b/doc/starting-next-release.html
index bbb48e243ad..a03044097e7 100644
--- a/doc/starting-next-release.html
+++ b/doc/starting-next-release.html
@@ -11,8 +11,11 @@
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
- ul.task-list{list-style: none;}
+ /* The extra [class] is a hack that increases specificity enough to
+ override a similar rule in reveal.js */
+ ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
+ font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
@@ -20,9 +23,6 @@
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
-
diff --git a/doc/testing.html b/doc/testing.html
index f52f72ac2f7..bb6af964c29 100644
--- a/doc/testing.html
+++ b/doc/testing.html
@@ -11,8 +11,11 @@
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
- ul.task-list{list-style: none;}
+ /* The extra [class] is a hack that increases specificity enough to
+ override a similar rule in reveal.js */
+ ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
+ font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
@@ -21,9 +24,6 @@
-
@@ -494,8 +494,8 @@
VALUE_CLASS_PLUGIN
In the meantime, this mode allows test sources to compile and run as
either value classes or regular identity classes without source-level
changes.
-
When set to any non-empty value, the following options are appended to
-every JTReg invocation:
+
When set to any non-empty value, the following options are appended
+to every JTReg invocation:
-cpa:<valueClassPlugin.jar> — appends the plugin
JAR to the compile-time classpath (only when the JAR is present in the
@@ -511,16 +511,15 @@
VALUE_CLASS_PLUGIN
plugin.
The plugin scans each compilation unit after parsing and converts any
-class annotated with
-@jdk.test.lib.valueclass.AsValueClass into a value class by
-setting the internal VALUE_CLASS modifier flag and clearing
-the IDENTITY_TYPE flag. This transformation only takes
-effect when --enable-preview is active; without it the
-annotation is a no-op and the class compiles as an ordinary identity
-class, so the same test source can exercise both code paths.
+class annotated with @jdk.test.lib.valueclass.AsValueClass
+into a value class by setting the internal VALUE_CLASS
+modifier flag and clearing the IDENTITY_TYPE flag. This
+transformation only takes effect when --enable-preview is
+active; without it the annotation is a no-op and the class compiles as
+an ordinary identity class, so the same test source can exercise both
+code paths.
Example:
-
$ make test TEST=jdk_lang JTREG="VALUE_CLASS_PLUGIN=true"
-
+
$ make test TEST=jdk_lang JTREG="VALUE_CLASS_PLUGIN=true"
JVMTI_STRESS_AGENT
Executes JTReg tests with JVM TI stress agent. The stress agent is
the part of test library and located in
diff --git a/doc/value-class-preview.html b/doc/value-class-preview.html
index 04b8ffb0f9b..b6c84f2386d 100644
--- a/doc/value-class-preview.html
+++ b/doc/value-class-preview.html
@@ -11,8 +11,11 @@
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
- ul.task-list{list-style: none;}
+ /* The extra [class] is a hack that increases specificity enough to
+ override a similar rule in reveal.js */
+ ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
+ font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
@@ -21,9 +24,6 @@
-
diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4
index db6c893c8b3..b8b260ab499 100644
--- a/make/autoconf/basic_tools.m4
+++ b/make/autoconf/basic_tools.m4
@@ -26,7 +26,7 @@
################################################################################
# It is recommended to use exactly this version of pandoc, especially for
# re-generating checked in html files
-RECOMMENDED_PANDOC_VERSION=2.19.2
+RECOMMENDED_PANDOC_VERSION=3.6.4
################################################################################
# Setup the most fundamental tools, used for setting up build platform and
diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js
index f64e2ec2270..b757fff55e6 100644
--- a/make/conf/jib-profiles.js
+++ b/make/conf/jib-profiles.js
@@ -1233,7 +1233,7 @@ var getJibProfilesDependencies = function (input, common) {
pandoc: {
organization: common.organization,
ext: "tar.gz",
- revision: "2.19.2+1.0",
+ revision: "3.6.4+1.0",
module: "pandoc-" + input.build_platform,
configure_args: "PANDOC=" + input.get("pandoc", "install_path") + "/pandoc/pandoc",
environment_path: input.get("pandoc", "install_path") + "/pandoc"
diff --git a/make/devkit/createPandocBundle.sh b/make/devkit/createPandocBundle.sh
index f582b2e9b1b..e5d0d5f1ac7 100644
--- a/make/devkit/createPandocBundle.sh
+++ b/make/devkit/createPandocBundle.sh
@@ -1,6 +1,6 @@
#!/bin/bash -e
#
-# Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, 2026, 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
@@ -31,7 +31,7 @@ trap "rm -rf \"$TMPDIR\"" EXIT
ORIG_DIR=`pwd`
cd "$TMPDIR"
-PANDOC_VERSION=2.19.2
+PANDOC_VERSION=3.6.4
PACKAGE_VERSION=1.0
TARGET_PLATFORM=linux_x64
if [ $# = 1 ]; then
@@ -47,7 +47,10 @@ elif [[ $TARGET_PLATFORM == linux_aarch64 ]] ; then
PANDOC_PLATFORM=linux-arm64
PANDOC_SUFFIX=tar.gz
elif [[ $TARGET_PLATFORM == macosx_x64 ]] ; then
- PANDOC_PLATFORM=macOS
+ PANDOC_PLATFORM=x86_64-macOS
+ PANDOC_SUFFIX=zip
+elif [[ $TARGET_PLATFORM == macosx_aarch64 ]] ; then
+ PANDOC_PLATFORM=arm64-macOS
PANDOC_SUFFIX=zip
elif [[ $TARGET_PLATFORM == windows_x64 ]] ; then
PANDOC_PLATFORM=windows-x86_64
@@ -72,7 +75,7 @@ fi
cd ..
mkdir pandoc
-cp tmp/pandoc-$PANDOC_VERSION/$PANDOC_PATH pandoc
+cp tmp/pandoc-"$PANDOC_VERSION"*/"$PANDOC_PATH" pandoc
chmod +x pandoc/$PANDOC_EXE
tar -cvzf ../$BUNDLE_NAME pandoc
From 20c6982976b2e2cdbac636f4d90bb77a64a0a98b Mon Sep 17 00:00:00 2001
From: Naoto Sato
Date: Thu, 8 Oct 2026 16:18:38 +0000
Subject: [PATCH 006/101] 8393848: Align the JDK with Microsoft's new timezone
name for Manitoba
Reviewed-by: jlu
---
src/java.base/share/data/tzdata/tzmappings.override | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/java.base/share/data/tzdata/tzmappings.override b/src/java.base/share/data/tzdata/tzmappings.override
index 8466e3585c3..0ccc52caf9a 100644
--- a/src/java.base/share/data/tzdata/tzmappings.override
+++ b/src/java.base/share/data/tzdata/tzmappings.override
@@ -38,3 +38,7 @@ British Columbia Standard Time:001:America/Vancouver:
# tz2026c: Alberta
# https://techcommunity.microsoft.com/blog/dstblog/interim-guidance-for-alberta-time-zone-changes-2026/4545015
Alberta Standard Time:001:America/Edmonton:
+
+# tz2026e: Manitoba
+# https://techcommunity.microsoft.com/blog/dstblog/interim-guidance-for-manitoba-permanent-dst-2026/4562488
+Manitoba Standard Time:001:America/Winnipeg:
From 36683b7796ce80cad58b0fdb86bf6a4e514845f0 Mon Sep 17 00:00:00 2001
From: Joe Darcy
Date: Thu, 8 Oct 2026 18:54:32 +0000
Subject: [PATCH 007/101] 8393864: Improve equality-related discussion in
BigDecimal
Reviewed-by: psandoz
---
.../share/classes/java/math/BigDecimal.java | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java
index 3cfe18e84c3..a99ccd2fc99 100644
--- a/src/java.base/share/classes/java/math/BigDecimal.java
+++ b/src/java.base/share/classes/java/math/BigDecimal.java
@@ -100,7 +100,7 @@
* used in the result's representation.
*
* The different representations of the same numerical value are
- * called members of the same cohort. The {@linkplain
+ * called members of the same {@index cohort}. The {@linkplain
* compareTo(BigDecimal) natural order} of {@code BigDecimal}
* considers members of the same cohort to be equal to each other. In
* contrast, the {@link equals equals} method requires both the
@@ -109,6 +109,12 @@
* #unscaledValue()} will differ for numerically equal values with
* different representations.
*
+ *
In the terminology {@linkplain Double##fpNumericalEq discussed
+ * for binary floating-point}, {@code BigDecimal.equals} uses
+ * representation equivalence while {@code BigDecimal.compareTo}
+ * uses numerical equality to compare members of the same
+ * cohort.
+ *
*
In general the rounding modes and precision setting determine
* how operations return results with a limited number of digits when
* the exact result has more digits (perhaps infinitely many in the
@@ -3228,7 +3234,9 @@ else if (ys != INFLATED)
/**
* Compares this {@code BigDecimal} with the specified {@code
- * Object} for equality. Unlike {@link #compareTo(BigDecimal)
+ * Object} for equality using {@linkplain Double##fpNumericalEq
+ * representation equivalence}.
+ * Unlike {@link #compareTo(BigDecimal)
* compareTo}, this method considers two {@code BigDecimal}
* objects equal only if they are equal in value and
* scale. Therefore 2.0 is not equal to 2.00 when compared by this
@@ -3244,9 +3252,6 @@ else if (ys != INFLATED)
* HALF_UP)} which evaluates to 0.7 and
* {@code new BigDecimal("2.00").divide(BigDecimal.valueOf(3),
* HALF_UP)} which evaluates to 0.67.
- * The behavior of this method is analogous to checking the
- * {@linkplain Double##repEquivalence representation equivalence}
- * of {@code double} values.
*
* @param x {@code Object} to which this {@code BigDecimal} is
* to be compared.
From c5a311116ab2d360923f4717c9995053279856b7 Mon Sep 17 00:00:00 2001
From: Shiv Shah
Date: Thu, 8 Oct 2026 20:39:02 +0000
Subject: [PATCH 008/101] 8392122: Test
jdk/internal/platform/docker/TestDockerMemoryMetrics.java fails with
OutOfMemoryError
Reviewed-by: cnorrbin, epavlova
---
.../platform/docker/MetricsMemoryTester.java | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java
index 53924b6abad..7306b721937 100644
--- a/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java
+++ b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java
@@ -87,10 +87,10 @@ private static void testMemoryFailCount() {
System.out.println("Initial memory fail count: " + initialFailCount);
- // Allocate 512M of data in 1M chunks per iteration
- byte[][] bytes = new byte[512][];
+ // Allocate up to 256M of data in 1M chunks, two G1 regions per chunk at this heap size
+ byte[][] bytes = new byte[256][];
- for (int i = 0; i < 512; i++) {
+ for (int i = 0; i < bytes.length; i++) {
if (i % 8 == 0) {
System.out.printf("Allocated: %3dM, Memory usage: %3dM, Memory and swap: %3dM\n",
i,
@@ -113,6 +113,13 @@ private static void testMemoryFailCount() {
System.out.println("Final memory fail count: " + newCount);
if (newCount <= initialFailCount) {
+ // A limiting ancestor may account for events not visible in this cgroup.
+ if (initialFailCount >= 0 && newCount == initialFailCount
+ && memLimit > 0 && metrics.getMemoryAndSwapUsage() > memLimit) {
+ System.out.println("Memory and swap usage exceeds the memory limit, "
+ + "but the fail count is unchanged. Ignoring test!");
+ return;
+ }
throw new RuntimeException("Memory fail count did not increase: initial="
+ initialFailCount + ", final=" + newCount);
}
From e976cefbd5864f1eddce2fae0e0318360586ad00 Mon Sep 17 00:00:00 2001
From: Matias Saavedra Silva
Date: Thu, 8 Oct 2026 21:08:25 +0000
Subject: [PATCH 009/101] 8393431: Strict static field read-write state
incorrectly preserved between AOT runs
Reviewed-by: iklam, heidinga, fparain
---
src/hotspot/share/oops/cpCache.cpp | 12 ++++++++++++
src/hotspot/share/oops/instanceKlass.cpp | 13 +++++++++++++
src/hotspot/share/oops/instanceKlass.hpp | 1 +
3 files changed, 26 insertions(+)
diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp
index 73879b7656c..1dc1fa57d08 100644
--- a/src/hotspot/share/oops/cpCache.cpp
+++ b/src/hotspot/share/oops/cpCache.cpp
@@ -436,6 +436,18 @@ void ConstantPoolCache::iterate_resolved_field_entries_with_archivability_check(
bool archivable = (resolved && !CDSConfig::is_dumping_preimage_static_archive() &&
AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index));
+
+ // The state of strict static field accesses is reset in InstanceKlass::remove_unshareable_info
+ // when the class does not have an AOT initialized mirror. Since will be called in
+ // the subsequent runtime, the resolved field entries associated with strict static fields are
+ // not archiveable.
+ if (rfi->is_resolved(Bytecodes::_getstatic) || rfi->is_resolved(Bytecodes::_putstatic)) {
+ InstanceKlass* holder = rfi->field_holder();
+ if (!holder->has_aot_initialized_mirror() && holder->field_is_strict(rfi->field_index())) {
+ archivable = false;
+ }
+ }
+
f(cp, src_cp, cp_index, rfi, resolved, archivable);
}
}
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index 705e04c4c56..44208c22320 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -3124,6 +3124,19 @@ void InstanceKlass::remove_unshareable_info() {
// being added to class hierarchy (see InstanceKlass:::add_to_hierarchy()).
_init_state = allocated;
+ // Classes without an AOT-initialized mirror will run again. Restore
+ // the initial status of their strict static fields for that execution.
+ if (has_strict_static_fields() && !has_aot_initialized_mirror()) {
+ for (AllFieldStream fs(this); !fs.done(); fs.next()) {
+ FieldInfo fi = fs.to_FieldInfo();
+ if (fi.access_flags().is_strict() && fi.access_flags().is_static() && fi.initializer_index() == 0) {
+ FieldStatus& fs = *fields_status()->adr_at(fi.index());
+ fs.update_strict_static_unset(true);
+ fs.update_strict_static_unread(true);
+ }
+ }
+ }
+
{ // Otherwise this needs to take out the Compile_lock.
assert(SafepointSynchronize::is_at_safepoint(), "only called at safepoint");
init_implementor();
diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp
index 5f915ea87d4..1c9ddf84b76 100644
--- a/src/hotspot/share/oops/instanceKlass.hpp
+++ b/src/hotspot/share/oops/instanceKlass.hpp
@@ -455,6 +455,7 @@ class InstanceKlass: public Klass {
bool field_is_flat(int index) const { return field_flags(index).is_flat(); }
bool field_has_null_marker(int index) const { return field_flags(index).has_null_marker(); }
bool field_is_null_free_value_type(int index) const;
+ bool field_is_strict(int index) const { return field(index).access_flags().is_strict(); }
bool is_class_in_loadable_descriptors_attribute(Symbol* name) const;
int field_null_marker_offset(int index) const;
From f6fa65381d0c32e22120159415f8c472e990cdc7 Mon Sep 17 00:00:00 2001
From: Vladimir Kozlov
Date: Thu, 8 Oct 2026 21:31:21 +0000
Subject: [PATCH 010/101] 8380476: Implement JEP 544: Ahead-of-Time Code
Compilation
Co-authored-by: John R Rose
Co-authored-by: Andrew Dinn
Co-authored-by: Ashutosh Mehra
Co-authored-by: Dan Heidinga
Co-authored-by: Ioi Lam
Co-authored-by: Vladimir Ivanov
Co-authored-by: Ekaterina Pavlova
Co-authored-by: Aleksey Shipilev
Co-authored-by: Igor Veresov
Co-authored-by: Dingli Zhang
Reviewed-by: adinn, iveresov, iklam, jrose, asmehra, mhaessig
---
make/hotspot/lib/JvmFeatures.gmk | 1 +
.../cpu/aarch64/c1_LIRGenerator_aarch64.cpp | 9 -
.../cpu/aarch64/c1_MacroAssembler_aarch64.cpp | 2 +-
.../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 4 +-
.../cpu/aarch64/c2_MacroAssembler_aarch64.hpp | 3 +
.../cpu/aarch64/compiledIC_aarch64.cpp | 11 +-
.../gc/g1/g1BarrierSetAssembler_aarch64.cpp | 4 +-
.../gc/z/zBarrierSetAssembler_aarch64.cpp | 2 +-
.../cpu/aarch64/macroAssembler_aarch64.cpp | 26 +-
src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp | 14 +-
src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp | 8 -
src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp | 8 -
.../cpu/riscv/c1_LIRGenerator_riscv.cpp | 7 -
.../cpu/riscv/c2_MacroAssembler_riscv.cpp | 6 +-
.../gc/g1/g1BarrierSetAssembler_riscv.cpp | 3 +-
.../riscv/gc/z/zBarrierSetAssembler_riscv.cpp | 3 +-
.../cpu/riscv/globalDefinitions_riscv.hpp | 8 +
.../cpu/riscv/macroAssembler_riscv.cpp | 27 +-
.../cpu/riscv/macroAssembler_riscv.hpp | 7 +-
src/hotspot/cpu/riscv/relocInfo_riscv.cpp | 13 +-
src/hotspot/cpu/riscv/riscv.ad | 15 +-
src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp | 7 -
.../cpu/s390/c1_MacroAssembler_s390.cpp | 2 +-
src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp | 8 -
src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp | 2 +-
src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 8 +-
src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp | 3 +
.../x86/stubGenerator_x86_64_arraycopy.cpp | 1 +
src/hotspot/cpu/x86/x86.ad | 10 +-
src/hotspot/share/asm/codeBuffer.cpp | 15 +-
src/hotspot/share/asm/codeBuffer.hpp | 6 +
src/hotspot/share/c1/c1_Compilation.cpp | 8 +-
src/hotspot/share/c1/c1_Compiler.cpp | 40 +-
src/hotspot/share/c1/c1_Compiler.hpp | 3 +-
src/hotspot/share/c1/c1_GraphBuilder.cpp | 7 +-
src/hotspot/share/c1/c1_LIRGenerator.cpp | 23 +-
src/hotspot/share/c1/c1_LIRGenerator.hpp | 1 -
src/hotspot/share/c1/c1_Runtime1.cpp | 70 +-
src/hotspot/share/c1/c1_Runtime1.hpp | 12 +-
src/hotspot/share/cds/aotCacheAccess.cpp | 112 +-
src/hotspot/share/cds/aotCacheAccess.hpp | 86 +-
src/hotspot/share/cds/aotClassLinker.cpp | 4 +
.../share/cds/aotCompressedPointers.hpp | 2 +
.../share/cds/aotConstantPoolResolver.cpp | 25 +
src/hotspot/share/cds/aotMetaspace.cpp | 99 +-
src/hotspot/share/cds/aotMetaspace.hpp | 3 +-
.../share/cds/aotStreamedHeapLoader.cpp | 2 +
src/hotspot/share/cds/archiveBuilder.cpp | 13 +-
src/hotspot/share/cds/archiveBuilder.hpp | 7 +-
src/hotspot/share/cds/archiveUtils.cpp | 22 +-
src/hotspot/share/cds/archiveUtils.hpp | 8 +-
src/hotspot/share/cds/cdsConfig.cpp | 43 +-
src/hotspot/share/cds/cdsConfig.hpp | 5 +
src/hotspot/share/cds/cds_globals.hpp | 9 +-
src/hotspot/share/cds/cppVtables.cpp | 37 +-
src/hotspot/share/cds/cppVtables.hpp | 2 +
src/hotspot/share/cds/filemap.cpp | 82 +-
src/hotspot/share/cds/filemap.hpp | 8 +-
src/hotspot/share/cds/heapShared.cpp | 51 +-
src/hotspot/share/cds/heapShared.hpp | 1 +
src/hotspot/share/ci/ciConstant.cpp | 13 +
src/hotspot/share/ci/ciConstant.hpp | 2 +
src/hotspot/share/ci/ciEnv.cpp | 530 ++-
src/hotspot/share/ci/ciEnv.hpp | 39 +-
src/hotspot/share/ci/ciField.cpp | 6 +
src/hotspot/share/ci/ciInstanceKlass.cpp | 30 +-
src/hotspot/share/ci/ciInstanceKlass.hpp | 2 +
src/hotspot/share/ci/ciMethod.cpp | 82 +-
src/hotspot/share/ci/ciMethod.hpp | 9 +-
src/hotspot/share/ci/ciObject.cpp | 28 +-
src/hotspot/share/ci/ciObjectFactory.cpp | 26 +-
src/hotspot/share/ci/ciObjectFactory.hpp | 2 +-
src/hotspot/share/ci/ciReplay.cpp | 2 +-
.../share/classfile/classLoaderDataShared.cpp | 4 +-
src/hotspot/share/classfile/moduleEntry.cpp | 10 +-
.../classfile/systemDictionaryShared.cpp | 2 +-
src/hotspot/share/code/aotCodeCache.cpp | 4106 ++++++++++++++---
src/hotspot/share/code/aotCodeCache.hpp | 768 ++-
src/hotspot/share/code/codeBlob.cpp | 12 +-
src/hotspot/share/code/codeBlob.hpp | 4 +-
src/hotspot/share/code/codeCache.cpp | 48 +-
src/hotspot/share/code/debugInfoRec.cpp | 2 +
src/hotspot/share/code/debugInfoRec.hpp | 4 +
src/hotspot/share/code/dependencies.cpp | 10 +-
src/hotspot/share/code/dependencies.hpp | 6 +
src/hotspot/share/code/nmethod.cpp | 263 +-
src/hotspot/share/code/nmethod.hpp | 78 +-
src/hotspot/share/code/relocInfo.cpp | 22 +-
src/hotspot/share/code/relocInfo.hpp | 27 +-
.../share/compiler/abstractCompiler.cpp | 18 +-
.../share/compiler/abstractCompiler.hpp | 24 +-
.../share/compiler/aotCompileBroker.cpp | 242 +
.../share/compiler/aotCompileBroker.hpp | 38 +
src/hotspot/share/compiler/compilationLog.cpp | 6 +-
.../share/compiler/compilationPolicy.cpp | 199 +-
.../share/compiler/compilationPolicy.hpp | 9 +-
src/hotspot/share/compiler/compileBroker.cpp | 610 ++-
src/hotspot/share/compiler/compileBroker.hpp | 44 +-
src/hotspot/share/compiler/compileTask.cpp | 53 +-
src/hotspot/share/compiler/compileTask.hpp | 49 +-
.../share/compiler/compilerDefinitions.hpp | 3 +
.../share/compiler/compilerDirectives.cpp | 10 +-
.../share/compiler/compilerDirectives.hpp | 5 +-
src/hotspot/share/compiler/compilerOracle.hpp | 3 +
src/hotspot/share/compiler/compilerThread.cpp | 2 +-
src/hotspot/share/compiler/compilerThread.hpp | 5 +
.../share/compiler/compiler_globals.hpp | 35 +
src/hotspot/share/compiler/oopMap.cpp | 1 -
.../shenandoah/c1/shenandoahBarrierSetC1.hpp | 26 +
src/hotspot/share/gc/z/c1/zBarrierSetC1.hpp | 1 +
src/hotspot/share/include/cds.h | 2 +-
src/hotspot/share/oops/instanceKlass.cpp | 7 +-
src/hotspot/share/oops/instanceKlass.hpp | 2 +
src/hotspot/share/oops/klass.cpp | 18 +
src/hotspot/share/oops/klass.hpp | 5 +
src/hotspot/share/oops/method.cpp | 12 +-
src/hotspot/share/oops/methodCounters.cpp | 24 +
src/hotspot/share/oops/methodCounters.hpp | 17 +-
src/hotspot/share/oops/methodData.cpp | 2 +-
src/hotspot/share/oops/trainingData.hpp | 84 +-
src/hotspot/share/opto/c2_globals.hpp | 17 +
src/hotspot/share/opto/c2compiler.cpp | 13 +-
src/hotspot/share/opto/compile.cpp | 44 +-
src/hotspot/share/opto/compile.hpp | 11 +
src/hotspot/share/opto/doCall.cpp | 27 +
src/hotspot/share/opto/graphKit.cpp | 46 +-
src/hotspot/share/opto/graphKit.hpp | 1 +
src/hotspot/share/opto/library_call.cpp | 5 +-
src/hotspot/share/opto/library_call.hpp | 2 +-
src/hotspot/share/opto/memnode.cpp | 17 +-
src/hotspot/share/opto/output.cpp | 31 +-
src/hotspot/share/opto/parse.hpp | 7 +
src/hotspot/share/opto/parse1.cpp | 158 +-
src/hotspot/share/opto/parse2.cpp | 2 +-
src/hotspot/share/opto/rootnode.cpp | 2 +
src/hotspot/share/opto/runtime.cpp | 22 +
src/hotspot/share/opto/runtime.hpp | 12 +-
src/hotspot/share/prims/whitebox.cpp | 2 +-
src/hotspot/share/runtime/deoptimization.cpp | 10 +-
.../flags/jvmFlagConstraintsCompiler.cpp | 21 +
.../flags/jvmFlagConstraintsCompiler.hpp | 1 +
src/hotspot/share/runtime/frame.cpp | 5 +-
src/hotspot/share/runtime/globals.hpp | 9 +-
src/hotspot/share/runtime/init.cpp | 2 +
src/hotspot/share/runtime/java.cpp | 11 +-
src/hotspot/share/runtime/java.hpp | 2 +
src/hotspot/share/runtime/sharedRuntime.cpp | 4 +-
.../share/runtime/stubDeclarations.hpp | 1 +
src/hotspot/share/runtime/threads.cpp | 4 +
src/hotspot/share/runtime/vmStructs.cpp | 5 +
.../share/utilities/nativeStackPrinter.cpp | 2 +-
src/hotspot/share/utilities/vmError.cpp | 2 +-
test/hotspot/jtreg/ProblemList-AotJdk.txt | 11 +
test/hotspot/jtreg/TEST.groups | 7 +-
.../commands/CompileLevelPrintTest.java | 4 +-
.../compiler/inlining/LateInlinePrinting.java | 25 +-
.../lib/ir_framework/TestFramework.java | 1 +
.../spectrapredefineclass/Launcher.java | 4 +-
.../Launcher.java | 4 +-
.../TestMachTempsAcrossSafepoints.java | 2 +
.../jtreg/runtime/cds/MetaspaceAllocGaps.java | 3 +
.../aotCache/AOTCompatibleOopCompression.java | 2 +-
.../appcds/aotCache/AOTCompileEagerly.java | 4 +-
.../aotClassLinking/TestSetupAOTTest.java | 2 +-
.../AOTCodeCPUFeatureIncompatibilityTest.java | 8 +-
.../aotCode/AOTCodeCompressedOopsTest.java | 6 +-
.../cds/appcds/aotCode/AOTCodeFlags.java | 113 +-
.../cds/appcds/aotCode/AOTCodeTest.java | 6 +-
.../AOTCodeTestUnknownExternAddress.java | 156 +
.../appcds/aotCode/TestAOTCodeCounters.java | 124 +
.../appcds/aotCode/TestAOTCodeFeatures.java | 110 +
.../appcds/aotCode/TestDisableAOTCode.java | 219 +
.../cds/appcds/aotCode/TestVerifyAOTCode.java | 120 +
.../runtime/cds/appcds/aotFlags/AOTFlags.java | 19 +-
.../appcds/aotProfile/AOTProfileFlags.java | 4 +
test/jdk/ProblemList-AotJdk.txt | 10 +
test/jtreg-ext/requires/VMProps.java | 1 +
test/lib/jdk/test/lib/cds/CDSAppTester.java | 8 +-
test/setup_aot/TestSetupAOT.java | 2 +-
179 files changed, 8641 insertions(+), 1553 deletions(-)
create mode 100644 src/hotspot/share/compiler/aotCompileBroker.cpp
create mode 100644 src/hotspot/share/compiler/aotCompileBroker.hpp
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeTestUnknownExternAddress.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestAOTCodeCounters.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestAOTCodeFeatures.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestDisableAOTCode.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestVerifyAOTCode.java
diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk
index ced4f1f6350..7097bce89e1 100644
--- a/make/hotspot/lib/JvmFeatures.gmk
+++ b/make/hotspot/lib/JvmFeatures.gmk
@@ -123,6 +123,7 @@ ifneq ($(call check-jvm-feature, cds), true)
aotCodeCache.cpp \
classLoaderDataShared.cpp \
classLoaderExt.cpp \
+ aotCompileBroker.cpp \
systemDictionaryShared.cpp \
trainingData.cpp
JVM_EXCLUDE_PATTERNS += cds/
diff --git a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp
index eba058885b2..674f79afbb3 100644
--- a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp
@@ -236,15 +236,6 @@ LIR_Opr LIRGenerator::load_immediate(jlong x, BasicType type) {
}
-
-void LIRGenerator::increment_counter(address counter, BasicType type, int step) {
- LIR_Opr pointer = new_pointer_register();
- __ move(LIR_OprFact::intptrConst(counter), pointer);
- LIR_Address* addr = new LIR_Address(pointer, type);
- increment_counter(addr, step);
-}
-
-
void LIRGenerator::increment_counter(LIR_Address* addr, int step) {
LIR_Opr imm;
switch(addr->type()) {
diff --git a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp
index 60f8cf99560..72b8c84e237 100644
--- a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp
@@ -315,7 +315,7 @@ int C1_MacroAssembler::scalarized_entry(const CompiledEntrySignature* ces, int f
// C1 code is not hot enough to micro optimize the nmethod entry barrier with an out-of-line stub
bs->nmethod_entry_barrier(this, nullptr /* slow_path */, nullptr /* continuation */, nullptr /* guard */);
- mov(r19, (intptr_t) ces->method());
+ mov_metadata(r19, ces->method());
if (is_value_ro_entry) {
far_call(RuntimeAddress(Runtime1::entry_for(StubId::c1_buffer_value_args_no_receiver_id)));
} else {
diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
index a59e4ed6afe..22b1b807fa6 100644
--- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
@@ -2674,7 +2674,7 @@ bool C2_MacroAssembler::in_scratch_emit_size() {
return MacroAssembler::in_scratch_emit_size();
}
-static void abort_verify_int_in_range(uint idx, jint val, jint lo, jint hi) {
+void C2_MacroAssembler::abort_verify_int_in_range(uint idx, jint val, jint lo, jint hi) {
fatal("Invalid CastII, idx: %u, val: %d, lo: %d, hi: %d", idx, val, lo, hi);
}
@@ -2713,7 +2713,7 @@ void C2_MacroAssembler::verify_int_in_range(uint idx, const TypeInt* t, Register
BLOCK_COMMENT("} verify_int_in_range");
}
-static void abort_verify_long_in_range(uint idx, jlong val, jlong lo, jlong hi) {
+void C2_MacroAssembler::abort_verify_long_in_range(uint idx, jlong val, jlong lo, jlong hi) {
fatal("Invalid CastLL, idx: %u, val: " JLONG_FORMAT ", lo: " JLONG_FORMAT ", hi: " JLONG_FORMAT, idx, val, lo, hi);
}
diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp
index 082cdbe09c1..83739021185 100644
--- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp
@@ -239,6 +239,9 @@
void vector_signum_sve(FloatRegister dst, FloatRegister src, FloatRegister zero,
FloatRegister one, FloatRegister vtmp, PRegister pgtmp, SIMD_RegVariant T);
+ static void abort_verify_int_in_range(uint idx, jint val, jint lo, jint hi);
+ static void abort_verify_long_in_range(uint idx, jlong val, jlong lo, jlong hi);
+
void verify_int_in_range(uint idx, const TypeInt* t, Register val, Register tmp);
void verify_long_in_range(uint idx, const TypeLong* t, Register val, Register tmp);
diff --git a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp
index 640cd495383..571cb3e949a 100644
--- a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp
@@ -114,12 +114,19 @@ void CompiledDirectCall::set_stub_to_clean(static_stub_Relocation* static_stub)
address stub = static_stub->addr();
assert(stub != nullptr, "stub not found");
assert(CompiledICLocker::is_safe(stub), "mt unsafe call");
+
// Creation also verifies the object.
NativeMovConstReg* method_holder
= nativeMovConstReg_at(stub + NativeInstruction::instruction_size);
method_holder->set_data(0);
- NativeJump* jump = nativeJump_at(method_holder->next_instruction_address());
- jump->set_jump_destination((address)-1);
+
+ // See comment about AOT in set_to_interpreted().
+ address next_instr = method_holder->next_instruction_address();
+ if (nativeInstruction_at(next_instr)->is_general_jump()) {
+ nativeGeneralJump_at(next_instr)->set_jump_destination((address)-1);
+ } else {
+ nativeJump_at(next_instr)->set_jump_destination((address)-1);
+ }
}
//-----------------------------------------------------------------------------
diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp
index 2a1987bffd2..98ae760ca89 100644
--- a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp
@@ -246,7 +246,7 @@ static void generate_post_barrier(MacroAssembler* masm,
assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, noreg, rscratch1);
// Does store cross heap regions?
- #if INCLUDE_CDS
+#if INCLUDE_CDS
// AOT code needs to load the barrier grain shift from the aot
// runtime constants area in the code cache otherwise we can compile
// it as an immediate operand
@@ -303,7 +303,7 @@ static void generate_c2_barrier_runtime_call(MacroAssembler* masm, G1BarrierStub
__ mov(c_rarg0, arg);
}
__ mov(c_rarg1, rthread);
- __ mov(rscratch1, runtime_path);
+ __ lea(rscratch1, RuntimeAddress(runtime_path));
__ blr(rscratch1);
}
diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp
index ced01ec3e6a..69a04f0fac5 100644
--- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp
@@ -1150,7 +1150,7 @@ void ZBarrierSetAssembler::generate_c2_load_barrier_stub(MacroAssembler* masm, Z
{
SaveLiveRegisters save_live_registers(masm, stub);
ZSetupArguments setup_arguments(masm, stub);
- __ mov(rscratch1, stub->slow_path());
+ __ lea(rscratch1, RuntimeAddress(stub->slow_path()));
__ blr(rscratch1);
}
// Stub exit
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
index e3731a13609..dd9215c55bd 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
@@ -330,6 +330,16 @@ class Patcher : public AllStatic {
return 2;
}
static int immediate(address insn_addr, address &target) {
+ // Metadata pointers are either narrow (32 bits) or wide (48 bits).
+ // We encode narrow ones by setting the upper 16 bits in the first
+ // instruction.
+ if (Instruction_aarch64::extract(insn_at(insn_addr, 0), 31, 21) == 0b11010010101) {
+ assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
+ narrowKlass nk = CompressedKlassPointers::encode((Klass*)target);
+ Instruction_aarch64::patch(insn_addr, 20, 5, nk >> 16);
+ Instruction_aarch64::patch(insn_addr+4, 20, 5, nk & 0xffff);
+ return 2;
+ }
assert(Instruction_aarch64::extract(insn_at(insn_addr, 0), 31, 21) == 0b11010010100, "must be");
uint64_t dest = (uint64_t)target;
// Move wide constant
@@ -457,6 +467,16 @@ class AArch64Decoder : public AllStatic {
}
static int immediate(address insn_addr, address &target) {
uint32_t *insns = (uint32_t *)insn_addr;
+ // Metadata pointers are either narrow (32 bits) or wide (48 bits).
+ // We encode narrow ones by setting the upper 16 bits in the first
+ // instruction.
+ if (Instruction_aarch64::extract(insns[0], 31, 21) == 0b11010010101) {
+ assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
+ narrowKlass nk = (narrowKlass)((uint32_t(Instruction_aarch64::extract(insns[0], 20, 5)) << 16)
+ + uint32_t(Instruction_aarch64::extract(insns[1], 20, 5)));
+ target = (address)CompressedKlassPointers::decode(nk);
+ return 2;
+ }
assert(Instruction_aarch64::extract(insns[0], 31, 21) == 0b11010010100, "must be");
// Move wide constant: movz, movk, movk. See movptr().
assert(nativeInstruction_at(insns+1)->is_movk(), "wrong insns in patch");
@@ -2468,7 +2488,7 @@ void MacroAssembler::mov(Register r, Address dest) {
// reach anywhere.
void MacroAssembler::movptr(Register r, uintptr_t imm64) {
#ifndef PRODUCT
- {
+ if (!AOTCodeCache::is_on_for_dump()) {
char buffer[64];
os::snprintf_checked(buffer, sizeof(buffer), "0x%" PRIX64, (uint64_t)imm64);
block_comment(buffer);
@@ -2526,7 +2546,7 @@ void MacroAssembler::mov(FloatRegister Vd, SIMD_Arrangement T, uint64_t imm64) {
void MacroAssembler::mov_immediate64(Register dst, uint64_t imm64)
{
#ifndef PRODUCT
- {
+ if (!AOTCodeCache::is_on_for_dump()) {
char buffer[64];
os::snprintf_checked(buffer, sizeof(buffer), "0x%" PRIX64, imm64);
block_comment(buffer);
@@ -2639,7 +2659,7 @@ void MacroAssembler::mov_immediate64(Register dst, uint64_t imm64)
void MacroAssembler::mov_immediate32(Register dst, uint32_t imm32)
{
#ifndef PRODUCT
- {
+ if (!AOTCodeCache::is_on_for_dump()) {
char buffer[64];
os::snprintf_checked(buffer, sizeof(buffer), "0x%" PRIX32, imm32);
block_comment(buffer);
diff --git a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp
index 5d6bcb45613..38be53ac6b6 100644
--- a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp
@@ -24,6 +24,7 @@
*/
#include "asm/macroAssembler.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/nmethod.hpp"
#include "code/relocInfo.hpp"
#include "nativeInst_aarch64.hpp"
@@ -97,7 +98,10 @@ void trampoline_stub_Relocation::pd_fix_owner_after_move() {
NativeCall* call = nativeCall_at(owner());
address trampoline = addr();
address dest = nativeCallTrampolineStub_at(trampoline)->destination();
- if (!Assembler::reachable_from_branch_at(owner(), dest)) {
+ // Force trampoline as call's destination in assembly phase.
+ // The call will be fixed when AOT code is loaded in production run
+ // depending on distance at that time.
+ if (AOTCodeCache::is_on_for_dump() || !Assembler::reachable_from_branch_at(owner(), dest)) {
dest = trampoline;
}
call->set_destination(dest);
@@ -122,3 +126,11 @@ void poll_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffe
void metadata_Relocation::pd_fix_value(address x) {
}
+
+address trampoline_stub_Relocation::pd_destination() {
+ return nativeCallTrampolineStub_at(addr())->destination();
+}
+
+void trampoline_stub_Relocation::pd_set_destination(address x) {
+ nativeCallTrampolineStub_at(addr())->set_destination(x);
+}
diff --git a/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp b/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp
index 7da13da1bf1..fd6f2979333 100644
--- a/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp
+++ b/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp
@@ -297,14 +297,6 @@ LIR_Opr LIRGenerator::load_immediate(jlong x, BasicType type) {
}
-void LIRGenerator::increment_counter(address counter, BasicType type, int step) {
- LIR_Opr pointer = new_pointer_register();
- __ move(LIR_OprFact::intptrConst(counter), pointer);
- LIR_Address* addr = new LIR_Address(pointer, type);
- increment_counter(addr, step);
-}
-
-
void LIRGenerator::increment_counter(LIR_Address* addr, int step) {
LIR_Opr temp = new_register(addr->type());
__ move(addr, temp);
diff --git a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp
index 0dcc19004cc..3c87869879a 100644
--- a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp
+++ b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp
@@ -259,14 +259,6 @@ LIR_Opr LIRGenerator::load_immediate(jlong x, BasicType type) {
}
-void LIRGenerator::increment_counter(address counter, BasicType type, int step) {
- LIR_Opr pointer = new_pointer_register();
- __ move(LIR_OprFact::intptrConst(counter), pointer);
- LIR_Address* addr = new LIR_Address(pointer, type);
- increment_counter(addr, step);
-}
-
-
void LIRGenerator::increment_counter(LIR_Address* addr, int step) {
LIR_Opr temp = new_register(addr->type());
__ move(addr, temp);
diff --git a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp
index 174b93ce06e..49cdb7c86e5 100644
--- a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp
@@ -208,13 +208,6 @@ LIR_Opr LIRGenerator::load_immediate(jlong x, BasicType type) {
return r;
}
-void LIRGenerator::increment_counter(address counter, BasicType type, int step) {
- LIR_Opr pointer = new_pointer_register();
- __ move(LIR_OprFact::intptrConst(counter), pointer);
- LIR_Address* addr = new LIR_Address(pointer, type);
- increment_counter(addr, step);
-}
-
void LIRGenerator::increment_counter(LIR_Address* addr, int step) {
LIR_Opr reg = new_register(addr->type());
__ load(addr, reg);
diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
index bdcf60f41c7..b49a112606f 100644
--- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
@@ -1861,8 +1861,7 @@ void C2_MacroAssembler::arrays_hashcode_v(Register ary, Register cnt, Register r
const VectorRegister v_coeffs = v6;
const VectorRegister v_tmp = v8;
- const address adr_pows31 = StubRoutines::riscv::arrays_hashcode_powers_of_31()
- + sizeof(jint);
+ const address adr_pows31 = StubRoutines::riscv::arrays_hashcode_powers_of_31();
Label VEC_LOOP, DONE, SCALAR_TAIL, SCALAR_TAIL_LOOP;
// NB: at this point (a) 'result' already has some value,
@@ -1872,6 +1871,9 @@ void C2_MacroAssembler::arrays_hashcode_v(Register ary, Register cnt, Register r
beqz(t0, SCALAR_TAIL);
la(t1, ExternalAddress(adr_pows31));
+ // Keep the relocation target at the registered stub entry, then skip the
+ // first element when loading the vector coefficients.
+ addi(t1, t1, sizeof(jint));
lw(pow31_highest, Address(t1, -1 * sizeof(jint)));
vsetvli(consumed, cnt, Assembler::e32, Assembler::m2);
diff --git a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp
index 19b744cf976..7bba2bdaf0b 100644
--- a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp
@@ -324,8 +324,7 @@ static void generate_c2_barrier_runtime_call(MacroAssembler* masm, G1BarrierStub
__ mv(c_rarg0, arg);
}
__ mv(c_rarg1, xthread);
- __ mv(t1, runtime_path);
- __ jalr(t1);
+ __ rt_call(runtime_path);
}
void G1BarrierSetAssembler::g1_write_barrier_pre_c2(MacroAssembler* masm,
diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp
index 9fbc59fe5ce..11f2585dbe8 100644
--- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp
@@ -749,8 +749,7 @@ void ZBarrierSetAssembler::generate_c2_load_barrier_stub(MacroAssembler* masm, Z
{
SaveLiveRegisters save_live_registers(masm, stub);
ZSetupArguments setup_arguments(masm, stub);
- __ mv(t1, stub->slow_path());
- __ jalr(t1);
+ __ rt_call(stub->slow_path());
}
// Stub exit
diff --git a/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp b/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp
index 57223cf4390..3d3590c6047 100644
--- a/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp
+++ b/src/hotspot/cpu/riscv/globalDefinitions_riscv.hpp
@@ -48,6 +48,14 @@ const bool CCallingConventionRequiresIntsAsLongs = false;
#define USE_POINTERS_TO_REGISTER_IMPL_ARRAY
+// A reloc call keeps its destination in an address stub, so the trampoline
+// relocation has to be able to read and write it. Note that we deliberately do
+// not define USE_TRAMPOLINE_STUB_FIX_OWNER: the call site reaches its stub with
+// an auipc pair inside the same blob and so stays in range after a move, and
+// NativeCall::set_destination() already picks jal or jalr through
+// optimize_call() according to the distance to the final destination.
+#define USE_TRAMPOLINE_STUB_DESTINATION
+
// auipc useable for all cc -> cc calls and jumps
#define CODE_CACHE_SIZE_LIMIT ((2*G)-(2*K))
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
index 9c383b6ddd5..6238ea4d3c3 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
@@ -3070,6 +3070,10 @@ static int patch_offset_in_conditional_branch(address branch, int64_t offset) {
static int patch_offset_in_pc_relative(address branch, int64_t offset) {
const int PC_RELATIVE_INSTRUCTION_NUM = 2; // auipc, addi/jalr/load
+ // The auipc pair can only encode a 32-bit pc-relative offset. Check the same
+ // range the emitters check, otherwise the high bits would be silently dropped.
+ guarantee(MacroAssembler::is_valid_32bit_offset(offset),
+ "offset " INT64_FORMAT " out of range for auipc", offset);
Assembler::patch(branch, 31, 12, ((offset + 0x800) >> 12) & 0xfffff); // Auipc. offset[31:12] ==> branch[31:12]
Assembler::patch(branch + 4, 31, 20, offset & 0xfff); // Addi/Jalr/Load. offset[11:0] ==> branch[31:20]
return PC_RELATIVE_INSTRUCTION_NUM * MacroAssembler::instruction_size;
@@ -3254,6 +3258,24 @@ int MacroAssembler::patch_oop(address insn_addr, address o) {
return -1;
}
+int MacroAssembler::patch_metadata(address insn_addr, address metadata) {
+ // Metadata is either a narrow klass (32 bits, materialized by set_narrow_klass()
+ // with li32) or a full 48-bit pointer (materialized by mov_metadata() with movptr).
+ if (MacroAssembler::is_li32_at(insn_addr)) {
+ // Move narrow klass
+ Metadata* value = reinterpret_cast(metadata);
+ assert(value != nullptr && value->is_klass(), "narrow metadata must be a klass");
+ narrowKlass nk = CompressedKlassPointers::encode(static_cast(value));
+ return patch_imm_in_li32(insn_addr, static_cast(nk));
+ } else if (MacroAssembler::is_movptr1_at(insn_addr) ||
+ MacroAssembler::is_movptr2_at(insn_addr)) {
+ // Move wide metadata
+ return pd_patch_instruction_size(insn_addr, metadata);
+ }
+ ShouldNotReachHere();
+ return -1;
+}
+
void MacroAssembler::reinit_heapbase() {
if (UseCompressedOops) {
if (Universe::is_fully_initialized() && !AOTCodeCache::is_on_for_dump()) {
@@ -3280,7 +3302,10 @@ void MacroAssembler::movptr(Register Rd, address addr, Register temp) {
void MacroAssembler::movptr(Register Rd, address addr, int32_t &offset, Register temp) {
uint64_t uimm64 = (uint64_t)addr;
#ifndef PRODUCT
- {
+ // Skip the block comment when dumping the AOT code cache: the comment text is
+ // the build-time address, which is meaningless in a future JVM instance and
+ // would flood the AOT C string table with unique entries.
+ if (!AOTCodeCache::is_on_for_dump()) {
char buffer[64];
os::snprintf_checked(buffer, sizeof(buffer), "0x%" PRIx64, uimm64);
block_comment(buffer);
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
index bd736a985fd..cbb56091aae 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
@@ -514,6 +514,7 @@ class MacroAssembler: public Assembler {
}
static int patch_oop(address insn_addr, address o);
+ static int patch_metadata(address insn_addr, address metadata);
static address get_target_of_li32(address insn_addr);
static int patch_imm_in_li32(address branch, int32_t target);
@@ -847,15 +848,17 @@ class MacroAssembler: public Assembler {
void double_blt(FloatRegister Rs1, FloatRegister Rs2, Label &l, bool is_far = false, bool is_unordered = false);
void double_bgt(FloatRegister Rs1, FloatRegister Rs2, Label &l, bool is_far = false, bool is_unordered = false);
-private:
+public:
// The signed 20-bit upper imm can materialize at most negative 0xF...F80000000, two G.
// The following signed 12-bit imm can at max subtract 0x800, two K, from that previously loaded two G.
- bool is_valid_32bit_offset(int64_t x) {
+ // Also used when patching an auipc pair, so that emit and patch agree on the reachable range.
+ static bool is_valid_32bit_offset(int64_t x) {
constexpr int64_t twoG = (2 * G);
constexpr int64_t twoK = (2 * K);
return x < (twoG - twoK) && x >= (-twoG - twoK);
}
+private:
// Ensure that the auipc can reach the destination at x from anywhere within
// the code cache so that if it is relocated we know it will still reach.
bool is_32bit_offset_from_codecache(int64_t x) {
diff --git a/src/hotspot/cpu/riscv/relocInfo_riscv.cpp b/src/hotspot/cpu/riscv/relocInfo_riscv.cpp
index f7793ce81e3..3632fc35f90 100644
--- a/src/hotspot/cpu/riscv/relocInfo_riscv.cpp
+++ b/src/hotspot/cpu/riscv/relocInfo_riscv.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, Red Hat Inc. 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.
@@ -50,6 +50,9 @@ void Relocation::pd_set_data_value(address x, bool verify_only) {
}
break;
}
+ case relocInfo::metadata_type:
+ bytes = MacroAssembler::patch_metadata(addr(), x);
+ break;
default:
bytes = MacroAssembler::pd_patch_instruction_size(addr(), x);
break;
@@ -128,3 +131,11 @@ void poll_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffe
void metadata_Relocation::pd_fix_value(address x) {
}
+
+address trampoline_stub_Relocation::pd_destination() {
+ return reinterpret_cast(Bytes::get_native_u8(addr()));
+}
+
+void trampoline_stub_Relocation::pd_set_destination(address x) {
+ Bytes::put_native_u8(addr(), reinterpret_cast(x));
+}
diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad
index 9e974a277e9..ed528012ff3 100644
--- a/src/hotspot/cpu/riscv/riscv.ad
+++ b/src/hotspot/cpu/riscv/riscv.ad
@@ -2456,7 +2456,20 @@ encode %{
__ mov_metadata(dst_reg, (Metadata*)con, t0);
} else {
assert(rtype == relocInfo::none || rtype == relocInfo::external_word_type, "unexpected reloc type");
- __ mv(dst_reg, $src$$constant);
+ // Load fake address constants as a plain immediate. They are not real
+ // addresses (a bci may be used as a jsr return address, and compiling
+ // an unreachable unsafe access can produce a bad base) so they are of
+ // no interest to the AOT code cache, and movptr() only materializes
+ // the low 48 bits.
+ if (rtype == relocInfo::none ||
+ (uint64_t)con >= (1ull << 48) ||
+ con < (address)(uintptr_t)os::vm_page_size()) {
+ __ mv(dst_reg, $src$$constant);
+ } else {
+ // Use a relocation so that the address can be fixed up when this code
+ // is loaded from the AOT code cache in a new JVM instance.
+ __ la(dst_reg, Address(con, rtype));
+ }
}
}
%}
diff --git a/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp b/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp
index 4f3c9f860bc..07f5953fc9d 100644
--- a/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp
+++ b/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp
@@ -205,13 +205,6 @@ LIR_Opr LIRGenerator::load_immediate(jlong x, BasicType type) {
return r;
}
-void LIRGenerator::increment_counter(address counter, BasicType type, int step) {
- LIR_Opr pointer = new_pointer_register();
- __ move(LIR_OprFact::intptrConst(counter), pointer);
- LIR_Address* addr = new LIR_Address(pointer, type);
- increment_counter(addr, step);
-}
-
void LIRGenerator::increment_counter(LIR_Address* addr, int step) {
__ add((LIR_Opr)addr, LIR_OprFact::intConst(step), (LIR_Opr)addr);
}
diff --git a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp
index 2ee98f22c3f..c2db1fc6e2c 100644
--- a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp
+++ b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp
@@ -124,7 +124,7 @@ int C1_MacroAssembler::scalarized_entry(const CompiledEntrySignature* ces, int f
// Z_R13 is the method register expected by c1_buffer_value_args (see
// c1_Runtime1_s390.cpp, StubId::c1_buffer_value_args_id handler).
- load_const_optimized(Z_R13, (intptr_t)(ces->method()));
+ load_const(Z_R13, constant_metadata_address(ces->method()));
if (is_value_ro_entry) {
call_c_opt(Runtime1::entry_for(StubId::c1_buffer_value_args_no_receiver_id));
} else {
diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp
index b2efdfd17ff..94009af28b8 100644
--- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp
@@ -217,14 +217,6 @@ LIR_Opr LIRGenerator::load_immediate(jlong x, BasicType type) {
return r;
}
-void LIRGenerator::increment_counter(address counter, BasicType type, int step) {
- LIR_Opr pointer = new_pointer_register();
- __ move(LIR_OprFact::intptrConst(counter), pointer);
- LIR_Address* addr = new LIR_Address(pointer, type);
- increment_counter(addr, step);
-}
-
-
void LIRGenerator::increment_counter(LIR_Address* addr, int step) {
__ add((LIR_Opr)addr, LIR_OprFact::intConst(step), (LIR_Opr)addr);
}
diff --git a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp
index 777e9587918..e64fcfb111e 100644
--- a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp
@@ -299,7 +299,7 @@ int C1_MacroAssembler::scalarized_entry(const CompiledEntrySignature* ces, int f
// C1 code is not hot enough to micro optimize the nmethod entry barrier with an out-of-line stub
bs->nmethod_entry_barrier(this, nullptr /* slow_path */, nullptr /* continuation */);
- movptr(rbx, (intptr_t)(ces->method()));
+ mov_metadata(rbx, ces->method());
if (is_value_ro_entry) {
call(RuntimeAddress(Runtime1::entry_for(StubId::c1_buffer_value_args_no_receiver_id)));
} else {
diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
index 1285b742649..dcce0aa9c00 100644
--- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
@@ -588,7 +588,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t,
// C2 uses the value of ZF to determine the continuation.
}
-static void abort_verify_int_in_range(uint idx, jint val, jint lo, jint hi) {
+void C2_MacroAssembler::abort_verify_int_in_range(uint idx, jint val, jint lo, jint hi) {
fatal("Invalid CastII, idx: %u, val: %d, lo: %d, hi: %d", idx, val, lo, hi);
}
@@ -651,7 +651,7 @@ void C2_MacroAssembler::verify_int_in_range(uint idx, const TypeInt* t, Register
BLOCK_COMMENT("} // CastII");
}
-static void abort_verify_long_in_range(uint idx, jlong val, jlong lo, jlong hi) {
+void C2_MacroAssembler::abort_verify_long_in_range(uint idx, jlong val, jlong lo, jlong hi) {
fatal("Invalid CastLL, idx: %u, val: " JLONG_FORMAT ", lo: " JLONG_FORMAT ", hi: " JLONG_FORMAT, idx, val, lo, hi);
}
@@ -3572,9 +3572,9 @@ void C2_MacroAssembler::arrays_hashcode(Register ary1, Register cnt1, Register r
// release bound
// vresult *= IntVector.fromArray(I256, power_of_31_backwards, 1);
+ lea(tmp2, ExternalAddress(StubRoutines::x86::arrays_hashcode_powers_of_31() + (0 * sizeof(jint))));
for (int idx = 0; idx < 4; idx++) {
- lea(tmp2, ExternalAddress(StubRoutines::x86::arrays_hashcode_powers_of_31() + ((8 * idx + 1) * sizeof(jint))));
- arrays_hashcode_elvload(vcoef[idx], Address(tmp2, 0), T_INT);
+ arrays_hashcode_elvload(vcoef[idx], Address(tmp2, (int)((8 * idx + 1) * sizeof(jint))), T_INT);
vpmulld(vresult[idx], vresult[idx], vcoef[idx], Assembler::AVX_256bit);
}
// result += vresult.reduceLanes(ADD);
diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp
index 926f9390ae0..a6471cbd3fd 100644
--- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp
@@ -40,6 +40,9 @@
Register t, Register thread);
void fast_unlock(Register obj, Register reg_rax, Register t, Register thread);
+ static void abort_verify_int_in_range(uint idx, jint val, jint lo, jint hi);
+ static void abort_verify_long_in_range(uint idx, jlong val, jlong lo, jlong hi);
+
void verify_int_in_range(uint idx, const TypeInt* t, Register val);
void verify_long_in_range(uint idx, const TypeLong* t, Register val, Register tmp);
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp
index 3e84023ef0d..2575d0e5007 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp
@@ -25,6 +25,7 @@
#include "asm/macroAssembler.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/barrierSetAssembler.hpp"
+#include "gc/shared/gc_globals.hpp"
#include "oops/objArrayKlass.hpp"
#include "runtime/arguments.hpp"
#include "runtime/sharedRuntime.hpp"
diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad
index cbbbc3e5e72..62c30a668d7 100644
--- a/src/hotspot/cpu/x86/x86.ad
+++ b/src/hotspot/cpu/x86/x86.ad
@@ -7836,7 +7836,15 @@ instruct loadConP(rRegP dst, immP con) %{
format %{ "movq $dst, $con\t# ptr" %}
ins_encode %{
- __ mov64($dst$$Register, $con$$constant, $con->constant_reloc(), RELOC_IMM64);
+ address con = (address)$con$$constant;
+ relocInfo::relocType rtype = $con->constant_reloc();
+ if (rtype == relocInfo::external_word_type &&
+ (con == (address)-1 ||
+ con < (address)(uintptr_t)os::vm_page_size())) {
+ // Don't use relocation for fake address
+ rtype = relocInfo::none;
+ }
+ __ mov64($dst$$Register, $con$$constant, rtype, RELOC_IMM64);
%}
ins_pipe(ialu_reg_fat); // XXX
%}
diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp
index 55ba27fcbe1..84f158b978e 100644
--- a/src/hotspot/share/asm/codeBuffer.cpp
+++ b/src/hotspot/share/asm/codeBuffer.cpp
@@ -1013,8 +1013,8 @@ void CodeBuffer::verify_section_allocation() {
}
guarantee(other->disjoint(sect), "sanity");
}
- guarantee(sect->end() <= tend, "sanity");
- guarantee(sect->end() <= sect->limit(), "sanity");
+ guarantee(sect->end() <= tend, "sanity, sect_end: " PTR_FORMAT " tend: " PTR_FORMAT " size: %d", p2i(sect->end()), p2i(tend), (int)_total_size);
+ guarantee(sect->end() <= sect->limit(), "sanity, sect_end: " PTR_FORMAT " sect_limit: " PTR_FORMAT, p2i(sect->end()), p2i(sect->limit()));
}
}
@@ -1115,7 +1115,10 @@ AsmRemarks::AsmRemarks() {
}
AsmRemarks::~AsmRemarks() {
- assert(_remarks == nullptr, "Must 'clear()' before deleting!");
+ if (_remarks != nullptr) {
+ clear();
+ }
+ postcond(_remarks == nullptr);
}
void AsmRemarks::init() {
@@ -1172,7 +1175,10 @@ DbgStrings::DbgStrings() {
}
DbgStrings::~DbgStrings() {
- assert(_strings == nullptr, "Must 'clear()' before deleting!");
+ if (_strings != nullptr) {
+ clear();
+ }
+ postcond(_strings == nullptr);
}
void DbgStrings::init() {
@@ -1213,6 +1219,7 @@ const char* AsmRemarkCollection::insert(uint offset, const char* remstr) {
} else {
_remarks->push_back(cell);
}
+ log_trace(codestrings)("AsmRemark::insert: offset=%u '%s'.", offset, remstr);
return cell->string();
}
diff --git a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp
index ff79b391fd1..15da890e008 100644
--- a/src/hotspot/share/asm/codeBuffer.hpp
+++ b/src/hotspot/share/asm/codeBuffer.hpp
@@ -440,6 +440,9 @@ class AsmRemarks {
void share(const AsmRemarks &src);
void clear();
+ // Clear Collection reference when storing AOT code,
+ // new one will be created during AOT code load.
+ void clear_ref() { _remarks = nullptr; }
uint print(uint offset, outputStream* strm = tty) const;
// For testing purposes only.
@@ -468,6 +471,9 @@ class DbgStrings {
void share(const DbgStrings &src);
void clear();
+ // Clear Collection reference when storing AOT code,
+ // new one will be created during AOT code load.
+ void clear_ref() { _strings = nullptr; }
// For testing purposes only.
const DbgStringCollection* ref() const { return _strings; }
diff --git a/src/hotspot/share/c1/c1_Compilation.cpp b/src/hotspot/share/c1/c1_Compilation.cpp
index e6a5c004df6..10f506f5526 100644
--- a/src/hotspot/share/c1/c1_Compilation.cpp
+++ b/src/hotspot/share/c1/c1_Compilation.cpp
@@ -426,11 +426,14 @@ void Compilation::install_code(int frame_size) {
exception_handler_table(),
implicit_exception_table(),
compiler(),
+ false, // has_clinit_barriers
+ false, // for_preload
has_unsafe_access(),
SharedRuntime::is_wide_vector(max_vector_size()),
has_monitors(),
has_scoped_access(),
- _immediate_oops_patched
+ _immediate_oops_patched,
+ should_install_code()
);
}
@@ -475,8 +478,7 @@ void Compilation::compile_method() {
// Note: make sure we mark the method as not compilable!
CHECK_BAILOUT();
- if (should_install_code()) {
- // install code
+ { // install code
PhaseTraceTime timeit(_t_codeinstall);
install_code(frame_size);
}
diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp
index 254c9ef81f9..945772d1fe4 100644
--- a/src/hotspot/share/c1/c1_Compiler.cpp
+++ b/src/hotspot/share/c1/c1_Compiler.cpp
@@ -30,6 +30,8 @@
#include "c1/c1_MacroAssembler.hpp"
#include "c1/c1_Runtime1.hpp"
#include "c1/c1_ValueType.hpp"
+#include "code/aotCodeCache.hpp"
+#include "compiler/compilationPolicy.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compilerDirectives.hpp"
#include "interpreter/linkResolver.hpp"
@@ -64,15 +66,32 @@ bool Compiler::init_c1_runtime() {
void Compiler::initialize() {
- // Buffer blob must be allocated per C1 compiler thread at startup
- BufferBlob* buffer_blob = init_buffer_blob();
-
+ CompilerThread* thread = CompilerThread::current();
+ bool is_aot_comp_thread = thread->is_aot_thread();
+ // AOT code loading does not use scratch buffer but it needs
+ // to wait when normal C1 compiler thread initializes runtime.
+ // Except when C1 JIT compilation is disabled and we need to
+ // initialize runtime by AOT thread.
+ if (is_aot_comp_thread && CompilationPolicy::c1_count() > 0) {
+ wait_for_initialization();
+ return;
+ }
+ // Buffer blob must be allocated per C1 compiler thread at startup.
+ BufferBlob* buffer_blob = is_aot_comp_thread ? nullptr : init_buffer_blob(thread);
if (should_perform_init()) {
+ if (is_aot_comp_thread) {
+ buffer_blob = init_buffer_blob(thread); // for runtime initialization
+ }
if (buffer_blob == nullptr || !init_c1_runtime()) {
// When we come here we are in state 'initializing'; entire C1 compilation
// can be shut down.
set_state(failed);
} else {
+ // AOT code loading does not use scratch buffer.
+ if (is_aot_comp_thread) {
+ CompilerThread::current()->set_buffer_blob(nullptr);
+ BufferBlob::free(buffer_blob);
+ }
set_state(initialized);
}
}
@@ -82,15 +101,15 @@ uint Compiler::code_buffer_size() {
return Compilation::desired_max_code_buffer_size + Compilation::desired_max_constant_size;
}
-BufferBlob* Compiler::init_buffer_blob() {
+BufferBlob* Compiler::init_buffer_blob(CompilerThread* thread) {
// Allocate buffer blob once at startup since allocation for each
// compilation seems to be too expensive (at least on Intel win32).
- assert (CompilerThread::current()->get_buffer_blob() == nullptr, "Should initialize only once");
+ assert (thread->get_buffer_blob() == nullptr, "Should initialize only once");
// Setup CodeBuffer.
BufferBlob* buffer_blob = BufferBlob::create("C1 temporary CodeBuffer", code_buffer_size());
if (buffer_blob != nullptr) {
- CompilerThread::current()->set_buffer_blob(buffer_blob);
+ thread->set_buffer_blob(buffer_blob);
}
return buffer_blob;
@@ -252,6 +271,15 @@ bool Compiler::is_intrinsic_supported(vmIntrinsics::ID id) {
}
void Compiler::compile_method(ciEnv* env, ciMethod* method, int entry_bci, bool install_code, DirectiveSet* directive) {
+ CompileTask* task = env->task();
+ if (task->is_aot_load()) {
+ assert(install_code, "AOT code loading requires install_code");
+ assert(!task->is_aot_preload(), "Pre-loading AOT code is not implemented for C1 code");
+ AOTCodeCache::load_nmethod(env, method, entry_bci, this);
+ // We want to go quickly through AOT code load requests
+ // instead of spending time on normal compilation.
+ return;
+ }
BufferBlob* buffer_blob = CompilerThread::current()->get_buffer_blob();
assert(buffer_blob != nullptr, "Must exist");
// invoke compilation
diff --git a/src/hotspot/share/c1/c1_Compiler.hpp b/src/hotspot/share/c1/c1_Compiler.hpp
index fde7c8e7932..39a74f2ea5d 100644
--- a/src/hotspot/share/c1/c1_Compiler.hpp
+++ b/src/hotspot/share/c1/c1_Compiler.hpp
@@ -28,13 +28,14 @@
#include "compiler/abstractCompiler.hpp"
class DirectiveSet;
+class CompilerThread;
// There is one instance of the Compiler per CompilerThread.
class Compiler: public AbstractCompiler {
private:
static bool init_c1_runtime();
- BufferBlob* init_buffer_blob();
+ BufferBlob* init_buffer_blob(CompilerThread* thread);
public:
// Creation
diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp
index bfa04d36825..50bad1daef2 100644
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp
@@ -1839,7 +1839,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) {
(!field->is_flat() && PatchALot);
ValueStack* state_before = nullptr;
- if (!holder->is_initialized() || needs_patching) {
+ if (!holder->is_initialized() || needs_patching || compilation()->env()->is_aot_compile()) {
// save state before instruction for debug info when
// deoptimization happens during patching
state_before = copy_state_before();
@@ -2430,7 +2430,8 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
callee_holder->is_loaded()) { // the effect of symbolic reference resolution
// callee is known => check if we have static binding
- if ((code == Bytecodes::_invokestatic && klass->is_initialized()) || // invokestatic involves an initialization barrier on declaring class
+ if ((code == Bytecodes::_invokestatic && klass->is_initialized() &&
+ !compilation()->env()->is_aot_compile()) || // invokestatic involves an initialization barrier on declaring class
code == Bytecodes::_invokespecial ||
(code == Bytecodes::_invokevirtual && target->is_final_method()) ||
code == Bytecodes::_invokedynamic) {
@@ -4199,6 +4200,8 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, bool ign
if (!callee->holder()->is_linked()) INLINE_BAILOUT("callee's klass not linked yet");
if (bc == Bytecodes::_invokestatic &&
!callee->holder()->is_initialized()) INLINE_BAILOUT("callee's klass not initialized yet");
+ if (bc == Bytecodes::_invokestatic &&
+ compilation()->env()->is_aot_compile()) INLINE_BAILOUT("AOT compilation");
if (!callee->has_balanced_monitors()) INLINE_BAILOUT("callee's monitors do not match");
// Proper inlining of methods with jsrs requires a little more work.
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp
index 0e13a181e91..045e8cdbbc7 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.cpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp
@@ -36,6 +36,7 @@
#include "ci/ciObjArrayKlass.hpp"
#include "ci/ciUtilities.hpp"
#include "ci/ciValueKlass.hpp"
+#include "code/aotCodeCache.hpp"
#include "compiler/compilerDefinitions.inline.hpp"
#include "compiler/compilerOracle.hpp"
#include "gc/shared/barrierSet.hpp"
@@ -668,7 +669,8 @@ void LIRGenerator::new_instance(LIR_Opr dst, ciInstanceKlass* klass, bool is_unr
if (UseFastNewInstance && klass->is_loaded() && (allow_value || !klass->is_value_klass())
&& !Klass::layout_helper_needs_slow_path(klass->layout_helper())) {
- StubId stub_id = klass->is_initialized() ? StubId::c1_fast_new_instance_id : StubId::c1_fast_new_instance_init_check_id;
+ bool known_initialized = klass->is_initialized() && !compilation()->env()->is_aot_compile();
+ StubId stub_id = known_initialized ? StubId::c1_fast_new_instance_id : StubId::c1_fast_new_instance_init_check_id;
CodeStub* slow_path = new NewInstanceStub(klass_reg, dst, klass, info, stub_id);
@@ -677,7 +679,7 @@ void LIRGenerator::new_instance(LIR_Opr dst, ciInstanceKlass* klass, bool is_unr
assert(klass->size_helper() > 0, "illegal instance size");
const int instance_size = align_object_size(klass->size_helper());
__ allocate_object(dst, scratch1, scratch2, scratch3, scratch4,
- oopDesc::header_size(), instance_size, klass_reg, !klass->is_initialized(), slow_path);
+ oopDesc::header_size(), instance_size, klass_reg, !known_initialized, slow_path);
} else {
CodeStub* slow_path = new NewInstanceStub(klass_reg, dst, klass, info, StubId::c1_new_instance_id);
__ jump(slow_path);
@@ -2500,7 +2502,11 @@ void LIRGenerator::do_Throw(Throw* x) {
#ifndef PRODUCT
if (PrintC1Statistics) {
- increment_counter(Runtime1::throw_count_address(), T_INT);
+ BasicTypeList signature;
+ LIR_OprList* args = new LIR_OprList();
+ call_runtime(&signature, args,
+ CAST_FROM_FN_PTR(address, Runtime1::increment_throw_count),
+ voidType, nullptr);
}
#endif
@@ -3849,13 +3855,18 @@ void LIRGenerator::increment_event_counter_impl(CodeEmitInfo* info,
int offset = -1;
LIR_Opr counter_holder;
if (level == CompLevel_limited_profile) {
- MethodCounters* counters_adr = method->ensure_method_counters();
+ ciMetadata* counters_adr = method->ensure_method_counters();
if (counters_adr == nullptr) {
bailout("method counters allocation failed");
return;
}
- counter_holder = new_pointer_register();
- __ move(LIR_OprFact::intptrConst(counters_adr), counter_holder);
+ if (AOTCodeCache::is_dumping_code()) {
+ counter_holder = new_register(T_METADATA);
+ __ metadata2reg(counters_adr->constant_encoding(), counter_holder);
+ } else {
+ counter_holder = new_pointer_register();
+ __ move(LIR_OprFact::intptrConst(counters_adr->constant_encoding()), counter_holder);
+ }
offset = in_bytes(backedge ? MethodCounters::backedge_counter_offset() :
MethodCounters::invocation_counter_offset());
} else if (level == CompLevel_full_profile) {
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp
index 3ce7e6f4d38..2706384c6f2 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.hpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp
@@ -354,7 +354,6 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure {
void arithmetic_call_op (Bytecodes::Code code, LIR_Opr result, LIR_OprList* args);
- void increment_counter(address counter, BasicType type, int step = 1);
void increment_counter(LIR_Address* addr, int step = 1);
void arithmetic_op(Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, LIR_Opr tmp, CodeEmitInfo* info = nullptr);
diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp
index 1df3a033b7a..40c3f6a99b9 100644
--- a/src/hotspot/share/c1/c1_Runtime1.cpp
+++ b/src/hotspot/share/c1/c1_Runtime1.cpp
@@ -165,8 +165,55 @@ address Runtime1::arraycopy_count_address(BasicType type) {
}
}
+JRT_LEAF(void, Runtime1::increment_throw_count())
+ _throw_count++;
+JRT_END
-#endif
+#endif // not PRODUCT
+
+#if INCLUDE_CDS
+// publish external addresses defined in this file
+void Runtime1::init_AOTAddressTable(GrowableArray& external_addresses) {
+#define ADD(count) external_addresses.append((address)(&count));
+#ifndef PRODUCT
+ ADD(_generic_arraycopystub_cnt);
+ ADD(_arraycopy_slowcase_cnt);
+ ADD(_arraycopy_checkcast_cnt);
+ ADD(_arraycopy_checkcast_attempt_cnt);
+ ADD(_new_type_array_slowcase_cnt);
+ ADD(_new_object_array_slowcase_cnt);
+ ADD(_new_null_free_array_slowcase_cnt);
+ ADD(_new_instance_slowcase_cnt);
+ ADD(_new_multi_array_slowcase_cnt);
+ ADD(_load_flat_array_slowcase_cnt);
+ ADD(_store_flat_array_slowcase_cnt);
+ ADD(_substitutability_check_slowcase_cnt);
+ ADD(_buffer_value_args_slowcase_cnt);
+ ADD(_buffer_value_args_no_receiver_slowcase_cnt);
+ ADD(_monitorenter_slowcase_cnt);
+ ADD(_monitorexit_slowcase_cnt);
+ ADD(_patch_code_slowcase_cnt);
+ ADD(_throw_range_check_exception_count);
+ ADD(_throw_index_exception_count);
+ ADD(_throw_div0_exception_count);
+ ADD(_throw_null_pointer_exception_count);
+ ADD(_throw_class_cast_exception_count);
+ ADD(_throw_incompatible_class_change_error_count);
+ ADD(_throw_illegal_monitor_state_exception_count);
+ ADD(_throw_identity_exception_count);
+ ADD(_throw_count);
+
+ ADD(_byte_arraycopy_stub_cnt);
+ ADD(_short_arraycopy_stub_cnt);
+ ADD(_int_arraycopy_stub_cnt);
+ ADD(_long_arraycopy_stub_cnt);
+ ADD(_oop_arraycopy_stub_cnt);
+
+ ADD(increment_throw_count); // function
+#endif // not PRODUCT
+#undef ADD
+}
+#endif // INCLUDE_CDS
// Simple helper to see if the caller of a runtime stub which
// entered the VM has been deoptimized
@@ -293,8 +340,12 @@ bool Runtime1::initialize(BufferBlob* blob) {
return false;
}
}
- // disallow any further c1 stub generation
- AOTCodeCache::set_c1_stubs_complete();
+ BarrierSetC1* bs = BarrierSet::barrier_set()->barrier_set_c1();
+ bool success = bs->generate_c1_runtime_stubs(blob);
+ if (success) {
+ // disallow any further c1 AOT stub generation
+ AOTCodeCache::set_c1_stubs_complete();
+ }
// printing
#ifndef PRODUCT
if (PrintSimpleStubs) {
@@ -309,8 +360,7 @@ bool Runtime1::initialize(BufferBlob* blob) {
}
}
#endif
- BarrierSetC1* bs = BarrierSet::barrier_set()->barrier_set_c1();
- return bs->generate_c1_runtime_stubs(blob);
+ return success;
}
CodeBlob* Runtime1::blob_for(StubId id) {
@@ -378,6 +428,10 @@ const char* Runtime1::name_for_address(address entry) {
FUNCTION_CASE(entry, StubRoutines::dtanh());
FUNCTION_CASE(entry, StubRoutines::dcbrt());
+#ifndef PRODUCT
+ FUNCTION_CASE(entry, increment_throw_count);
+#endif
+
#undef FUNCTION_CASE
// Soft float adds more runtime names.
@@ -988,7 +1042,8 @@ static Klass* resolve_field_return_klass(const methodHandle& caller, int bci, TR
// We must load class, initialize class and resolve the field
fieldDescriptor result; // initialize class if needed
constantPoolHandle constants(THREAD, caller->constants());
- LinkResolver::resolve_field_access(result, constants, field_access.index(), caller, Bytecodes::java_code(code), CHECK_NULL);
+ LinkResolver::resolve_field_access(result, constants, field_access.index(), caller,
+ Bytecodes::java_code(code), ClassInitMode::init, CHECK_NULL);
return result.field_holder();
}
@@ -1139,7 +1194,8 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* current, StubId stub_id ))
fieldDescriptor result; // initialize class if needed
Bytecodes::Code code = field_access.code();
constantPoolHandle constants(current, caller_method->constants());
- LinkResolver::resolve_field_access(result, constants, field_access.index(), caller_method, Bytecodes::java_code(code), CHECK);
+ LinkResolver::resolve_field_access(result, constants, field_access.index(), caller_method,
+ Bytecodes::java_code(code), ClassInitMode::init, CHECK);
patch_field_offset = result.offset();
patch_field_type = result.field_type();
diff --git a/src/hotspot/share/c1/c1_Runtime1.hpp b/src/hotspot/share/c1/c1_Runtime1.hpp
index ed2e167cb1f..6dbef7675e2 100644
--- a/src/hotspot/share/c1/c1_Runtime1.hpp
+++ b/src/hotspot/share/c1/c1_Runtime1.hpp
@@ -76,6 +76,13 @@ class Runtime1: public AllStatic {
static uint _throw_illegal_monitor_state_exception_count;
static uint _throw_identity_exception_count;
static uint _throw_count;
+
+ static address arraycopy_count_address(BasicType type);
+ static void increment_throw_count();
+#endif
+
+#if INCLUDE_CDS
+static void init_AOTAddressTable(GrowableArray& external_addresses);
#endif
private:
@@ -156,11 +163,6 @@ class Runtime1: public AllStatic {
// method tracing
static void trace_block_entry(jint block_id);
-#ifndef PRODUCT
- static address throw_count_address() { return (address)&_throw_count; }
- static address arraycopy_count_address(BasicType type);
-#endif
-
// directly accessible leaf routine
static int is_instance_of(oopDesc* mirror, oopDesc* obj);
diff --git a/src/hotspot/share/cds/aotCacheAccess.cpp b/src/hotspot/share/cds/aotCacheAccess.cpp
index 117979f41b5..b8d853792dc 100644
--- a/src/hotspot/share/cds/aotCacheAccess.cpp
+++ b/src/hotspot/share/cds/aotCacheAccess.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, 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
@@ -23,33 +23,133 @@
*/
#include "cds/aotCacheAccess.hpp"
-#include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/filemap.hpp"
#include "cds/heapShared.hpp"
#include "classfile/stringTable.hpp"
+#include "compiler/compiler_globals.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "memory/virtualspace.hpp"
#include "oops/instanceKlass.hpp"
+#include "oops/method.hpp"
+
+bool AOTCacheAccess::can_generate_aot_code(address addr) {
+ assert(CDSConfig::is_dumping_final_static_archive(), "must be");
+ return ArchiveBuilder::is_active() && ArchiveBuilder::current()->has_been_archived(addr);
+}
+
+bool AOTCacheAccess::can_generate_aot_code_for(InstanceKlass* ik) {
+ assert(CDSConfig::is_dumping_final_static_archive(), "must be");
+ if (!ArchiveBuilder::is_active()) {
+ return false;
+ }
+ ArchiveBuilder* builder = ArchiveBuilder::current();
+ if (!builder->has_been_archived((address)ik)) {
+ return false;
+ }
+ if (ik->defined_by_other_loaders()) {
+ return false;
+ }
+ return true;
+}
+
+Klass* AOTCacheAccess::try_narrow_ptr_to_klass(narrowPtr narrowp) {
+ Metadata* metadata = try_narrow_ptr_to_metadata(narrowp, sizeof(Klass));
+ if (metadata == nullptr) {
+ return nullptr;
+ }
+ Klass* k = (Klass*)metadata;
+ return Klass::is_valid_aot_klass(k) ? k : nullptr;
+}
+
+Method* AOTCacheAccess::try_narrow_ptr_to_method(narrowPtr narrowp) {
+ Metadata* metadata = try_narrow_ptr_to_metadata(narrowp, sizeof(Method));
+ if (metadata == nullptr) {
+ return nullptr;
+ }
+ Method* m = (Method*)metadata;
+ return Method::is_valid_method(m) ? m : nullptr;
+}
+
+Metadata* AOTCacheAccess::try_narrow_ptr_to_metadata(narrowPtr narrowp, size_t size) {
+ if (narrowp == AOTCompressedPointers::null()) {
+ return nullptr;
+ }
+ uintptr_t base = SharedBaseAddress;
+ uintptr_t low_bound = p2u(MetaspaceObj::aot_metaspace_base());
+ uintptr_t high_bound = p2u(MetaspaceObj::aot_metaspace_top());
+ size_t low_offset = low_bound - base;
+ size_t high_offset = high_bound - base;
+
+ size_t offset = AOTCompressedPointers::get_byte_offset(narrowp);
+ if (offset > AOTCompressedPointers::MaxMetadataOffsetBytes ||
+ offset < low_offset || offset >= high_offset ||
+ size > (high_offset - offset)) {
+ return nullptr;
+ }
+
+ Metadata* meta = reinterpret_cast(base + offset);
+ assert(Metaspace::in_aot_cache(meta), "must be");
+ return meta;
+}
+
+#if INCLUDE_CDS_JAVA_HEAP
+int AOTCacheAccess::get_archived_object_permanent_index(oop obj) {
+ return HeapShared::get_root_index(obj); // -1 if obj is not a root.
+}
+
+oop AOTCacheAccess::get_archived_object(int permanent_index) {
+ oop o = HeapShared::get_root(permanent_index);
+ assert(oopDesc::is_oop_or_null(o), "sanity");
+ return o;
+}
+
+#endif // INCLUDE_CDS_JAVA_HEAP
void* AOTCacheAccess::allocate_aot_code_region(size_t size) {
assert(CDSConfig::is_dumping_final_static_archive(), "must be");
return (void*)ArchiveBuilder::ac_region_alloc(size);
}
+bool AOTCacheAccess::verify_narrow_ptr = false;
+
+static size_t _aot_code_region_size = 0;
+
size_t AOTCacheAccess::get_aot_code_region_size() {
- assert(CDSConfig::is_using_archive(), "must be");
- FileMapInfo* mapinfo = FileMapInfo::current_info();
- assert(mapinfo != nullptr, "must be");
- return mapinfo->region_at(AOTMetaspace::ac)->used_aligned();
+ return _aot_code_region_size;
+}
+
+void AOTCacheAccess::set_aot_code_region_size(size_t sz) {
+ _aot_code_region_size = sz;
}
bool AOTCacheAccess::map_aot_code_region(ReservedSpace rs) {
FileMapInfo* static_mapinfo = FileMapInfo::current_info();
assert(UseSharedSpaces && static_mapinfo != nullptr, "must be");
+ verify_narrow_ptr = VerifyAOTCode;
return static_mapinfo->map_aot_code_region(rs);
}
+
+void AOTCacheAccess::unmap_aot_code_region() {
+ FileMapInfo* static_mapinfo = FileMapInfo::current_info();
+ assert(UseSharedSpaces && static_mapinfo != nullptr, "must be");
+ return static_mapinfo->unmap_aot_code_region();
+}
+
+bool AOTCacheAccess::is_aot_code_region_empty() {
+ assert(CDSConfig::is_dumping_final_static_archive(), "must be");
+ return ArchiveBuilder::current()->ac_region()->is_empty();
+}
+
+void AOTCacheAccess::set_pointer(address* ptr, address value) {
+ ArchiveBuilder* builder = ArchiveBuilder::current();
+ if (value != nullptr && !builder->is_in_buffer_space(value)) {
+ value = builder->get_buffered_addr(value);
+ }
+ *ptr = value;
+ ArchivePtrMarker::mark_pointer(ptr);
+}
diff --git a/src/hotspot/share/cds/aotCacheAccess.hpp b/src/hotspot/share/cds/aotCacheAccess.hpp
index 31fb9da534d..1b26a81cd1e 100644
--- a/src/hotspot/share/cds/aotCacheAccess.hpp
+++ b/src/hotspot/share/cds/aotCacheAccess.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, 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,23 +25,107 @@
#ifndef SHARE_CDS_AOTCACHEACCESS_HPP
#define SHARE_CDS_AOTCACHEACCESS_HPP
+#include "cds/aotCompressedPointers.hpp"
+#include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp"
+#include "cds/cdsConfig.hpp"
#include "memory/allStatic.hpp"
#include "oops/oopsHierarchy.hpp"
#include "utilities/globalDefinitions.hpp"
+class InstanceKlass;
+class Klass;
+class Method;
class ReservedSpace;
// AOT Cache API for AOT compiler
class AOTCacheAccess : AllStatic {
+ using narrowPtr = AOTCompressedPointers::narrowPtr;
+private:
+ static bool can_generate_aot_code(address addr) NOT_CDS_RETURN_(false);
+ static bool verify_narrow_ptr;
public:
+ static bool can_generate_aot_code(Method* m) {
+ return can_generate_aot_code((address)m);
+ }
+ static bool can_generate_aot_code(Klass* k) {
+ assert(!k->is_instance_klass(), "other method should be called");
+ return can_generate_aot_code((address)k);
+ }
+ static bool can_generate_aot_code_for(InstanceKlass* ik) NOT_CDS_RETURN_(false);
+
+ /*
+ * Used during an assembly run to encode metadata object pointer present in the AOT Cache.
+ * The input argument is the address of a metadata object (Method/Klass) loaded by the assembly JVM.
+ */
+ template
+ static narrowPtr to_narrow_ptr(T addr) {
+ assert(CDSConfig::is_dumping_final_static_archive(), "must be");
+ assert(ArchiveBuilder::is_active(), "must be");
+ return AOTCompressedPointers::encode_not_null(addr);
+ }
+
+ // Get metadata with narrowp verification
+ static Metadata* try_narrow_ptr_to_metadata(narrowPtr narrowp, size_t size);
+
+ /*
+ * Used during a production run to materialize a real pointer to a Klass from the encoded pointer located in a loaded AOT Cache.
+ * The encoded pointer is normally obtained by reading a value embedded in some other AOT-ed entry, like an AOT compiled code.
+ */
+ static Klass* narrow_ptr_to_klass(narrowPtr narrowp) {
+ if (verify_narrow_ptr) {
+ return try_narrow_ptr_to_klass(narrowp);
+ }
+ Metadata* metadata = AOTCompressedPointers::decode_not_null(narrowp);
+ assert(metadata->is_klass(), "sanity check");
+ return (Klass*)metadata;
+ }
+ // Version with narrowp verification
+ static Klass* try_narrow_ptr_to_klass(narrowPtr narrowp);
+
+ /*
+ * Used during a production run to materialize a real pointer to a Method from the encoded pointer located in a loaded AOT Cache.
+ * The encoded pointer is normally obtained by reading a value embedded in some other AOT-ed entry, like an AOT compiled code.
+ */
+ static Method* narrow_ptr_to_method(narrowPtr narrowp) {
+ if (verify_narrow_ptr) {
+ return try_narrow_ptr_to_method(narrowp);
+ }
+ Metadata* metadata = AOTCompressedPointers::decode_not_null(narrowp);
+ assert(metadata->is_method(), "sanity check");
+ return (Method*)metadata;
+ }
+ // Version with narrowp verification
+ static Method* try_narrow_ptr_to_method(narrowPtr narrowp);
+
+ // Used during production run to convert a Method in AOTCache to encoded pointer
+ static narrowPtr method_to_narrow_ptr(Method* method) {
+ assert(CDSConfig::is_using_archive() && !CDSConfig::is_dumping_final_static_archive(), "must be");
+ assert(AOTMetaspace::in_aot_cache(method), "method %p is not in AOTCache", method);
+ return AOTCompressedPointers::encode_address_in_cache(method);
+ }
+
+ static int get_archived_object_permanent_index(oop obj) NOT_CDS_JAVA_HEAP_RETURN_(-1);
+ static oop get_archived_object(int permanent_index) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
+
static void* allocate_aot_code_region(size_t size) NOT_CDS_RETURN_(nullptr);
static size_t get_aot_code_region_size() NOT_CDS_RETURN_(0);
+ static void set_aot_code_region_size(size_t sz) NOT_CDS_RETURN;
static bool map_aot_code_region(ReservedSpace rs) NOT_CDS_RETURN_(false);
+ static void unmap_aot_code_region() NOT_CDS_RETURN;
+
+
+ static bool is_aot_code_region_empty() NOT_CDS_RETURN_(true);
+
+ template
+ static void set_pointer(T** ptr, T* value) {
+ set_pointer((address*)ptr, (address)value);
+ }
+ static void set_pointer(address* ptr, address value);
};
#endif // SHARE_CDS_AOTCACHEACCESS_HPP
diff --git a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp
index c66435b03bb..0ed17dd6746 100644
--- a/src/hotspot/share/cds/aotClassLinker.cpp
+++ b/src/hotspot/share/cds/aotClassLinker.cpp
@@ -136,6 +136,10 @@ bool AOTClassLinker::try_add_candidate(InstanceKlass* ik) {
return true;
}
+ if (!ik->is_linked() && SystemDictionaryShared::has_class_failed_verification(ik)) {
+ return false;
+ }
+
if (ik->is_hidden()) {
assert(!ik->defined_by_other_loaders(), "hidden classes are archived only for builtin loaders");
if (!CDSConfig::is_dumping_method_handles()) {
diff --git a/src/hotspot/share/cds/aotCompressedPointers.hpp b/src/hotspot/share/cds/aotCompressedPointers.hpp
index a3919fb95a9..5e783cd5e2d 100644
--- a/src/hotspot/share/cds/aotCompressedPointers.hpp
+++ b/src/hotspot/share/cds/aotCompressedPointers.hpp
@@ -141,4 +141,6 @@ inline u4 cast_to_u4(AOTCompressedPointers::narrowPtr narrowp) {
return checked_cast(narrowp);
}
+using narrowPtr = AOTCompressedPointers::narrowPtr;
+
#endif // SHARE_CDS_AOTCOMPRESSEDPOINTERS_HPP
diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp
index f1a704d4bee..4add165e143 100644
--- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp
+++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp
@@ -26,6 +26,8 @@
#include "cds/aotConstantPoolResolver.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/cdsConfig.hpp"
+#include "cds/heapShared.hpp"
+#include "cds/lambdaFormInvokers.inline.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmClasses.hpp"
@@ -149,6 +151,29 @@ void AOTConstantPoolResolver::preresolve_string_cp_entries(InstanceKlass* ik, TR
break;
}
}
+
+ // Normally, we don't want to archive any CP entries that were not resolved
+ // in the training run. Otherwise the AOT/JIT may inline too much code that has not
+ // been executed.
+ //
+ // However, we want to aggressively resolve all klass/field/method constants for
+ // LambdaForm Invoker Holder classes, Lambda Proxy classes, and LambdaForm classes,
+ // so that the compiler can inline through them.
+ if (SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) {
+ bool eager_resolve = false;
+
+ if (LambdaFormInvokers::may_be_regenerated_class(ik->name())) {
+ eager_resolve = true;
+ }
+ if (ik->is_hidden() && HeapShared::is_archivable_hidden_klass(ik)) {
+ eager_resolve = true;
+ }
+
+ if (eager_resolve) {
+ preresolve_class_cp_entries(THREAD, ik, nullptr);
+ preresolve_field_and_method_cp_entries(THREAD, ik, nullptr);
+ }
+ }
}
// This works only for the boot/platform/app loaders
diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp
index 8733c794ea7..f198f14da13 100644
--- a/src/hotspot/share/cds/aotMetaspace.cpp
+++ b/src/hotspot/share/cds/aotMetaspace.cpp
@@ -23,6 +23,7 @@
*/
#include "cds/aotArtifactFinder.hpp"
+#include "cds/aotCacheAccess.hpp"
#include "cds/aotClassInitializer.hpp"
#include "cds/aotClassLinker.hpp"
#include "cds/aotClassLocation.hpp"
@@ -62,6 +63,7 @@
#include "classfile/vmSymbols.hpp"
#include "code/aotCodeCache.hpp"
#include "code/codeCache.hpp"
+#include "compiler/aotCompileBroker.hpp"
#include "gc/shared/gcVMOperations.hpp"
#include "interpreter/bytecodes.hpp"
#include "interpreter/bytecodeStream.hpp"
@@ -81,6 +83,7 @@
#include "oops/flatArrayKlass.hpp"
#include "oops/instanceMirrorKlass.hpp"
#include "oops/klass.inline.hpp"
+#include "oops/method.inline.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/oop.inline.hpp"
#include "oops/oopHandle.hpp"
@@ -360,7 +363,8 @@ void AOTMetaspace::post_initialize(TRAPS) {
// Close any open file descriptors. However, mmap'ed pages will remain in memory.
static_mapinfo->close();
- if (HeapShared::is_loading() && HeapShared::is_loading_mapping_mode()) {
+ if (!(HeapShared::is_loading() && HeapShared::is_loading_streaming_mode())) {
+ // Streaming mode will unmap the bm region later in AOTStreamedHeapLoader::cleanup()
static_mapinfo->unmap_region(AOTMetaspace::bm);
}
@@ -963,17 +967,16 @@ void AOTMetaspace::dump_static_archive(TRAPS) {
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
- if (CDSConfig::is_dumping_final_static_archive()) {
- if (AOTPrintTrainingInfo) {
- tty->print_cr("==================== archived_training_data ** before dumping ====================");
- TrainingData::print_archived_training_data_on(tty);
- }
- LogStreamHandle(Info, aot, training, data) log;
- if (log.is_enabled()) {
- TrainingData::print_archived_training_data_on(&log);
- }
- }
-
+ if (CDSConfig::is_dumping_final_static_archive()) {
+ if (AOTPrintTrainingInfo) {
+ tty->print_cr("==================== archived_training_data ** before dumping ====================");
+ TrainingData::print_archived_training_data_on(tty);
+ }
+ LogStreamHandle(Info, aot, training, data) log;
+ if (log.is_enabled()) {
+ TrainingData::print_archived_training_data_on(&log);
+ }
+ }
StaticArchiveBuilder builder;
dump_static_archive_impl(builder, THREAD);
@@ -997,6 +1000,8 @@ void AOTMetaspace::dump_static_archive(TRAPS) {
} else {
tty->print_cr("AOTCache creation is complete: %s " INT64_FORMAT " bytes", AOTCache, (int64_t)(st.st_size));
}
+ print_statistics_before_exit();
+ ostream_exit(); // finalize VM log
vm_direct_exit(0);
}
}
@@ -1011,15 +1016,32 @@ void AOTMetaspace::init_heap_settings() {
// We don't need it -- always disable for better jitted code.
FLAG_SET_ERGO(AOTCompatibleOopCompression, false);
} else if (CDSConfig::is_dumping_final_static_archive()) {
- // Obey the command-line switch. Do not override
+ // Enable by default: use the CompressedOops::HeapBasedNarrowOop mode
+ // so that AOT code can always work regardless of runtime heap range.
+ //
+ // If you are *absolutely sure* that the CompressedOops::mode() will be the same
+ // between training and production runs (e.g., if you specify -Xmx128m for
+ // both training and production runs, and you know the OS will always reserve
+ // the heap under 4GB), you can explicitly disable this with:
+ // java -XX:+UnlockDiagnosticVMOptions -XX:-AOTCompatibleOopCompression ...
+ // However, this is risky and there's a chance that the startup in production run
+ // will be slower than expected because it is unable to load the AOT code cache
+ // due to a different compressed oops encoding.
+ //
+ FLAG_SET_ERGO_IF_DEFAULT(AOTCompatibleOopCompression, true);
} else if (CDSConfig::is_using_archive()) {
- precond(FileMapInfo::current_info() == nullptr);
- FileMapInfo* static_mapinfo = open_static_archive();
+ FileMapInfo* static_mapinfo = FileMapInfo::current_info();
+ // may have been opened by get_aot_code_region_size()
+ if (static_mapinfo == nullptr) {
+ static_mapinfo = open_static_archive();
+ }
if (static_mapinfo != nullptr && static_mapinfo->header()->compatible_oop_compression()) {
// Use the same setting as recorded in the archive.
FLAG_SET_ERGO(AOTCompatibleOopCompression, true);
}
}
+ } else {
+ FLAG_SET_ERGO(AOTCompatibleOopCompression, false);
}
if (!CDSConfig::is_dumping_heap() || UseCompressedOops) {
@@ -1182,15 +1204,22 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
VM_PopulateDumpSharedSpace op(builder, _output_mapinfo);
VMThread::execute(&op);
- if (AOTCodeCache::is_on_for_dump() && CDSConfig::is_dumping_final_static_archive()) {
- CDSConfig::enable_dumping_aot_code();
- {
- builder.start_ac_region();
- // Write the contents to AOT code region before packing the region
- AOTCodeCache::dump();
- builder.end_ac_region();
+ if (AOTCodeCache::is_caching_enabled() && CDSConfig::is_dumping_final_static_archive()) {
+ // We have just created the final image. Now dump AOT adapters, stubs and compiled code.
+ builder.start_ac_region();
+ if (AOTCodeCache::is_dumping_code()) {
+ // Let's run the AOT compiler.
+ CDSConfig::enable_dumping_aot_code();
+ log_info(aot)("Compiling AOT code with %d compiler threads", (int)CICompilerCount);
+ AOTCompileBroker::compile_aot_code(&builder, CHECK);
+ log_info(aot)("Finished compiling AOT code");
+ CDSConfig::disable_dumping_aot_code();
+ }
+ // Write the contents to AOT code region before packing the region
+ if (AOTCodeCache::dump()) {
+ log_info(aot)("Dumped AOT code Cache");
}
- CDSConfig::disable_dumping_aot_code();
+ builder.end_ac_region();
}
bool status = write_static_archive(&builder, _output_mapinfo, op.mapped_heap_info(), op.streamed_heap_info());
@@ -1409,7 +1438,7 @@ bool AOTMetaspace::try_link_class(JavaThread* current, InstanceKlass* ik) {
void VM_PopulateDumpSharedSpace::dump_java_heap_objects() {
if (CDSConfig::is_dumping_heap()) {
HeapShared::write_heap(&_mapped_heap_info, &_streamed_heap_info);
- } else {
+ } else if (!CDSConfig::is_dumping_preimage_static_archive()) {
CDSConfig::log_reasons_for_not_dumping_heap();
}
}
@@ -1554,6 +1583,8 @@ void AOTMetaspace::initialize_runtime_shared_and_meta_spaces() {
// Register CDS memory region with LSan.
LSAN_REGISTER_ROOT_REGION(cds_base, cds_end - cds_base);
set_aot_metaspace_range(cds_base, static_mapinfo->mapped_end(), cds_end);
+ guarantee(SharedBaseAddress == p2u(MetaspaceObj::aot_metaspace_base()), "must be");
+ guarantee(p2u(MetaspaceObj::aot_metaspace_base()) <= p2u(MetaspaceObj::aot_metaspace_top()), "must be");
_relocation_delta = static_mapinfo->relocation_delta();
_requested_base_address = static_mapinfo->requested_base_address();
if (dynamic_mapped) {
@@ -1591,7 +1622,25 @@ void AOTMetaspace::initialize_runtime_shared_and_meta_spaces() {
delete dynamic_mapinfo;
}
if (RequireSharedSpaces && has_failed) {
- AOTMetaspace::unrecoverable_loading_error("Unable to map shared spaces");
+ // static archive mapped but dynamic archive failed
+ AOTMetaspace::unrecoverable_loading_error("Unable to map shared spaces");
+ }
+}
+
+// This is called very early at VM start up to get AOT cache header
+// and the size of the AOT code region during production run.
+// We need the size to reserve space to map code from AOT code region.
+void AOTMetaspace::get_aot_code_region_size() {
+ if (!AOTCodeCache::is_caching_enabled() || CDSConfig::is_dumping_final_static_archive()) {
+ return;
+ } else if (CDSConfig::is_using_archive()) { // production run with AOT code cache
+ precond(FileMapInfo::current_info() == nullptr);
+ precond(CodeCache::max_capacity() == 0);
+ FileMapInfo* static_mapinfo = open_static_archive();
+ if (static_mapinfo != nullptr) {
+ FileMapRegion* aot_code_region = static_mapinfo->region_at(AOTMetaspace::ac);
+ AOTCacheAccess::set_aot_code_region_size(aot_code_region->used_aligned());
+ }
}
}
diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp
index cc24bce52e0..40181b96390 100644
--- a/src/hotspot/share/cds/aotMetaspace.hpp
+++ b/src/hotspot/share/cds/aotMetaspace.hpp
@@ -76,7 +76,7 @@ class AOTMetaspace : AllStatic {
static void dump_static_archive(TRAPS) NOT_CDS_RETURN;
#ifdef _LP64
- static void init_heap_settings() NOT_CDS_JAVA_HEAP_RETURN;
+ static void init_heap_settings() NOT_CDS_JAVA_HEAP_RETURN;
#endif
private:
@@ -92,6 +92,7 @@ class AOTMetaspace : AllStatic {
static void initialize_for_static_dump() NOT_CDS_RETURN;
static void initialize_runtime_shared_and_meta_spaces() NOT_CDS_RETURN;
static void post_initialize(TRAPS) NOT_CDS_RETURN;
+ static void get_aot_code_region_size() NOT_CDS_RETURN;
static void print_on(outputStream* st);
diff --git a/src/hotspot/share/cds/aotStreamedHeapLoader.cpp b/src/hotspot/share/cds/aotStreamedHeapLoader.cpp
index 393737e51b9..8fb48edb434 100644
--- a/src/hotspot/share/cds/aotStreamedHeapLoader.cpp
+++ b/src/hotspot/share/cds/aotStreamedHeapLoader.cpp
@@ -799,6 +799,8 @@ void AOTStreamedHeapLoader::cleanup() {
}
FREE_C_HEAP_ARRAY(_object_index_to_heap_object_table);
+ _object_index_to_heap_object_table = nullptr;
+ _roots_archive = nullptr;
// Unmap regions
FileMapInfo::current_info()->unmap_region(AOTMetaspace::hp);
diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp
index 576dc5562a8..4199902b57f 100644
--- a/src/hotspot/share/cds/archiveBuilder.cpp
+++ b/src/hotspot/share/cds/archiveBuilder.cpp
@@ -42,7 +42,6 @@
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmClasses.hpp"
-#include "code/aotCodeCache.hpp"
#include "interpreter/abstractInterpreter.hpp"
#include "jvm.h"
#include "logging/log.hpp"
@@ -183,6 +182,7 @@ ArchiveBuilder::ArchiveBuilder() :
_ptrmap(mtClassShared),
_rw_ptrmap(mtClassShared),
_ro_ptrmap(mtClassShared),
+ _ac_ptrmap(mtClassShared),
_rw_src_objs(),
_ro_src_objs(),
_src_obj_table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE),
@@ -322,9 +322,9 @@ void ArchiveBuilder::sort_klasses() {
address ArchiveBuilder::reserve_buffer() {
// On 64-bit: reserve address space for archives up to the max encoded offset limit.
- // On 32-bit: use 256MB + AOT code size due to limited virtual address space.
+ // On 32-bit: use 256MB due to limited virtual address space.
size_t buffer_size = LP64_ONLY(AOTCompressedPointers::MaxMetadataOffsetBytes)
- NOT_LP64(256 * M + AOTCodeCache::max_aot_code_size());
+ NOT_LP64(256 * M);
ReservedSpace rs = MemoryReserver::reserve(buffer_size,
AOTMetaspace::core_region_alignment(),
os::vm_page_size(),
@@ -1023,7 +1023,7 @@ narrowKlass ArchiveBuilder::get_requested_narrow_klass(Klass* k) {
}
#endif // INCLUDE_CDS_JAVA_HEAP
-// RelocateBufferToRequested --- Relocate all the pointers in rw/ro,
+// RelocateBufferToRequested --- Relocate all the pointers in rw/ro/ac,
// so that the archive can be mapped to the "requested" location without runtime relocation.
//
// - See ArchiveBuilder header for the definition of "buffer", "mapped" and "requested"
@@ -1145,11 +1145,12 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, AOTMappedHeapInfo* mapp
write_region(mapinfo, AOTMetaspace::ac, &_ac_region, /*read_only=*/false,/*allow_exec=*/false);
// Split pointer map into read-write and read-only bitmaps
- ArchivePtrMarker::initialize_rw_ro_maps(&_rw_ptrmap, &_ro_ptrmap);
+ ArchivePtrMarker::initialize_rw_ro_ac_maps(&_rw_ptrmap, &_ro_ptrmap, &_ac_ptrmap);
size_t bitmap_size_in_bytes;
char* bitmap = mapinfo->write_bitmap_region(ArchivePtrMarker::rw_ptrmap(),
ArchivePtrMarker::ro_ptrmap(),
+ ArchivePtrMarker::ac_ptrmap(),
mapped_heap_info,
streamed_heap_info,
bitmap_size_in_bytes);
@@ -1169,8 +1170,8 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, AOTMappedHeapInfo* mapp
mapinfo->write_header();
mapinfo->close();
+ aot_log_info(aot)("Full module graph = %s", CDSConfig::is_dumping_full_module_graph() ? "enabled" : "disabled");
if (log_is_enabled(Info, aot)) {
- log_info(aot)("Full module graph = %s", CDSConfig::is_dumping_full_module_graph() ? "enabled" : "disabled");
print_stats();
}
diff --git a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp
index 6a9df87092b..43c8eb7812c 100644
--- a/src/hotspot/share/cds/archiveBuilder.hpp
+++ b/src/hotspot/share/cds/archiveBuilder.hpp
@@ -221,13 +221,14 @@ class ArchiveBuilder : public StackObj {
DumpRegion _ro_region;
DumpRegion _ac_region; // AOT code
- // Combined bitmap to track pointers in both RW and RO regions. This is updated
- // as objects are copied into RW and RO.
+ // Combined bitmap to track pointers in RW, RO and AC regions. This is updated
+ // as objects are copied into RW, RO and AC.
CHeapBitMap _ptrmap;
- // _ptrmap is split into these two bitmaps which are written into the archive.
+ // _ptrmap is split into these bitmaps which are written into the archive.
CHeapBitMap _rw_ptrmap; // marks pointers in the RW region
CHeapBitMap _ro_ptrmap; // marks pointers in the RO region
+ CHeapBitMap _ac_ptrmap; // marks pointers in the AC region
SourceObjList _rw_src_objs; // objs to put in rw region
SourceObjList _ro_src_objs; // objs to put in ro region
diff --git a/src/hotspot/share/cds/archiveUtils.cpp b/src/hotspot/share/cds/archiveUtils.cpp
index af0ae2c7a44..3d4d36ab486 100644
--- a/src/hotspot/share/cds/archiveUtils.cpp
+++ b/src/hotspot/share/cds/archiveUtils.cpp
@@ -53,6 +53,7 @@
CHeapBitMap* ArchivePtrMarker::_ptrmap = nullptr;
CHeapBitMap* ArchivePtrMarker::_rw_ptrmap = nullptr;
CHeapBitMap* ArchivePtrMarker::_ro_ptrmap = nullptr;
+CHeapBitMap* ArchivePtrMarker::_ac_ptrmap = nullptr;
VirtualSpace* ArchivePtrMarker::_vs;
bool ArchivePtrMarker::_compacted;
@@ -61,6 +62,7 @@ void ArchivePtrMarker::initialize(CHeapBitMap* ptrmap, VirtualSpace* vs) {
assert(_ptrmap == nullptr, "initialize only once");
assert(_rw_ptrmap == nullptr, "initialize only once");
assert(_ro_ptrmap == nullptr, "initialize only once");
+ assert(_ac_ptrmap == nullptr, "initialize only once");
_vs = vs;
_compacted = false;
_ptrmap = ptrmap;
@@ -76,29 +78,34 @@ void ArchivePtrMarker::initialize(CHeapBitMap* ptrmap, VirtualSpace* vs) {
_ptrmap->initialize(estimated_archive_size / sizeof(intptr_t));
}
-void ArchivePtrMarker::initialize_rw_ro_maps(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap) {
+void ArchivePtrMarker::initialize_rw_ro_ac_maps(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap, CHeapBitMap* ac_ptrmap) {
address* buff_bottom = (address*)ArchiveBuilder::current()->buffer_bottom();
address* rw_bottom = (address*)ArchiveBuilder::current()->rw_region()->base();
address* ro_bottom = (address*)ArchiveBuilder::current()->ro_region()->base();
+ address* ac_bottom = (address*)ArchiveBuilder::current()->ac_region()->base();
- // The bit in _ptrmap that cover the very first word in the rw/ro regions.
+ // The bit in _ptrmap that covers the very first word in the rw/ro/ac regions.
size_t rw_start = rw_bottom - buff_bottom;
size_t ro_start = ro_bottom - buff_bottom;
+ size_t ac_start = ac_bottom - buff_bottom;
- // The number of bits used by the rw/ro ptrmaps. We might have lots of zero
- // bits at the bottom and top of rw/ro ptrmaps, but these zeros will be
+ // The number of bits used by the rw/ro/ac ptrmaps. We might have lots of zero
+ // bits at the bottom and top of rw/ro/ac ptrmaps, but these zeros will be
// removed by FileMapInfo::write_bitmap_region().
size_t rw_size = ArchiveBuilder::current()->rw_region()->used() / sizeof(address);
size_t ro_size = ArchiveBuilder::current()->ro_region()->used() / sizeof(address);
+ size_t ac_size = ArchiveBuilder::current()->ac_region()->used() / sizeof(address);
// The last (exclusive) bit in _ptrmap that covers the rw/ro regions.
// Note: _ptrmap is dynamically expanded only when an actual pointer is written, so
// it may not be as large as we want.
size_t rw_end = MIN2(rw_start + rw_size, _ptrmap->size());
size_t ro_end = MIN2(ro_start + ro_size, _ptrmap->size());
+ size_t ac_end = MIN2(ac_start + ac_size, _ptrmap->size());
rw_ptrmap->initialize(rw_size);
ro_ptrmap->initialize(ro_size);
+ ac_ptrmap->initialize(ac_size);
for (size_t rw_bit = rw_start; rw_bit < rw_end; rw_bit++) {
rw_ptrmap->at_put(rw_bit - rw_start, _ptrmap->at(rw_bit));
@@ -108,8 +115,13 @@ void ArchivePtrMarker::initialize_rw_ro_maps(CHeapBitMap* rw_ptrmap, CHeapBitMap
ro_ptrmap->at_put(ro_bit - ro_start, _ptrmap->at(ro_bit));
}
+ for (size_t ac_bit = ac_start; ac_bit < ac_end; ac_bit++) {
+ ac_ptrmap->at_put(ac_bit - ac_start, _ptrmap->at(ac_bit));
+ }
+
_rw_ptrmap = rw_ptrmap;
_ro_ptrmap = ro_ptrmap;
+ _ac_ptrmap = ac_ptrmap;
}
void ArchivePtrMarker::mark_pointer(address* ptr_loc) {
@@ -479,8 +491,6 @@ void DumpRegion::pack(DumpRegion* next) {
_end = (char*)align_up(_top, AOTMetaspace::core_region_alignment());
_is_packed = true;
}
- _end = (char*)align_up(_top, AOTMetaspace::core_region_alignment());
- _is_packed = true;
if (next != nullptr) {
next->_rs = _rs;
next->_vs = _vs;
diff --git a/src/hotspot/share/cds/archiveUtils.hpp b/src/hotspot/share/cds/archiveUtils.hpp
index 5ed3ac140ca..735721abf2b 100644
--- a/src/hotspot/share/cds/archiveUtils.hpp
+++ b/src/hotspot/share/cds/archiveUtils.hpp
@@ -55,6 +55,7 @@ class ArchivePtrMarker : AllStatic {
static CHeapBitMap* _ptrmap;
static CHeapBitMap* _rw_ptrmap;
static CHeapBitMap* _ro_ptrmap;
+ static CHeapBitMap* _ac_ptrmap;
static VirtualSpace* _vs;
// Once _ptrmap is compacted, we don't allow bit marking anymore. This is to
@@ -66,7 +67,7 @@ class ArchivePtrMarker : AllStatic {
public:
static void initialize(CHeapBitMap* ptrmap, VirtualSpace* vs);
- static void initialize_rw_ro_maps(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap);
+ static void initialize_rw_ro_ac_maps(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap, CHeapBitMap* ac_ptrmap);
static void mark_pointer(address* ptr_loc);
static void clear_pointer(address* ptr_loc);
static void compact(address relocatable_base, address relocatable_end);
@@ -95,10 +96,15 @@ class ArchivePtrMarker : AllStatic {
return _ro_ptrmap;
}
+ static CHeapBitMap* ac_ptrmap() {
+ return _ac_ptrmap;
+ }
+
static void reset_map_and_vs() {
_ptrmap = nullptr;
_rw_ptrmap = nullptr;
_ro_ptrmap = nullptr;
+ _ac_ptrmap = nullptr;
_vs = nullptr;
}
};
diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp
index 35d28f5068b..48ec7f98066 100644
--- a/src/hotspot/share/cds/cdsConfig.cpp
+++ b/src/hotspot/share/cds/cdsConfig.cpp
@@ -24,6 +24,8 @@
#include "cds/aotLogging.hpp"
#include "cds/aotMapLogger.hpp"
+#include "cds/aotMetaspace.hpp"
+#include "cds/cds_globals.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/classListWriter.hpp"
#include "cds/filemap.hpp"
@@ -31,10 +33,12 @@
#include "classfile/classLoaderDataShared.hpp"
#include "classfile/moduleEntry.hpp"
#include "code/aotCodeCache.hpp"
+#include "compiler/compilerDefinitions.inline.hpp"
#include "include/jvm_io.h"
#include "logging/log.hpp"
#include "memory/universe.hpp"
#include "prims/jvmtiAgentList.hpp"
+#include "prims/jvmtiExport.hpp"
#include "runtime/arguments.hpp"
#include "runtime/globals.hpp"
#include "runtime/globals_extension.hpp"
@@ -63,6 +67,8 @@ const char* CDSConfig::_input_static_archive_path = nullptr;
const char* CDSConfig::_input_dynamic_archive_path = nullptr;
const char* CDSConfig::_output_archive_path = nullptr;
+double CDSConfig::_aot_compile_threshold_scaling = 1.0;
+
JavaThread* CDSConfig::_dumper_thread = nullptr;
int CDSConfig::get_status() {
@@ -663,11 +669,17 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla
}
if (AOTClassLinking) {
- // If AOTClassLinking is specified, enable all AOT optimizations by default.
+ // If AOTClassLinking is specified, enable all these optimizations by default.
FLAG_SET_ERGO_IF_DEFAULT(AOTInvokeDynamicLinking, true);
} else {
- // AOTInvokeDynamicLinking depends on AOTClassLinking.
+ // All of these *might* depend on AOTClassLinking. Better be safe than sorry.
FLAG_SET_ERGO(AOTInvokeDynamicLinking, false);
+
+ if (CDSConfig::is_dumping_archive()) {
+ FLAG_SET_ERGO(AOTRecordTraining, false);
+ FLAG_SET_ERGO(AOTReplayTraining, false);
+ AOTCodeCache::disable_caching();
+ }
}
if (is_dumping_static_archive()) {
@@ -682,6 +694,12 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla
// with AOT and -Xcomp.
Arguments::set_mode_flags(Arguments::_mixed);
}
+ _aot_compile_threshold_scaling = CompileThresholdScaling; // Preserve for A2 compilation
+ if (CompileThresholdScaling < 1.0) {
+ // Set flag to default value to avoid excessive normal JIT
+ // compilations during assembly phase.
+ FLAG_SET_ERGO(CompileThresholdScaling, 1.0);
+ }
} else if (!mode_flag_cmd_line) {
// By default, -Xshare:dump runs in interpreter-only mode, which is required for deterministic archive.
//
@@ -745,7 +763,8 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla
void CDSConfig::setup_compiler_args() {
// 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();
+ bool can_dump_profile_and_compiled_code = !CompilerConfig::is_interpreter_only() && AOTClassLinking && new_aot_flags_used();
+ bool can_use_profile_and_compiled_code = !CompilerConfig::is_interpreter_only() && new_aot_flags_used();
if (is_dumping_preimage_static_archive() && can_dump_profile_and_compiled_code) {
// JEP 483 workflow -- training
@@ -756,13 +775,23 @@ void CDSConfig::setup_compiler_args() {
// 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.
+ // Generate AOT code only when training data enabled
+ if (AOTReplayTraining) {
+ AOTCodeCache::enable_caching();
+ } else {
+ AOTCodeCache::disable_caching();
+ }
disable_dumping_aot_code(); // Don't dump AOT code until metadata and heap are dumped.
- } else if (is_using_archive() && new_aot_flags_used()) {
+ } else if (is_using_archive() && can_use_profile_and_compiled_code) {
// JEP 483 workflow -- production
FLAG_SET_ERGO(AOTRecordTraining, false);
FLAG_SET_ERGO_IF_DEFAULT(AOTReplayTraining, true);
- AOTCodeCache::enable_caching();
+ // Use AOT code only when training data enabled
+ if (AOTReplayTraining) {
+ AOTCodeCache::enable_caching();
+ } else {
+ AOTCodeCache::disable_caching();
+ }
} else {
FLAG_SET_ERGO(AOTReplayTraining, false);
FLAG_SET_ERGO(AOTRecordTraining, false);
@@ -858,7 +887,7 @@ bool CDSConfig::is_using_only_default_archive() {
}
bool CDSConfig::is_logging_lambda_form_invokers() {
- return ClassListWriter::is_enabled() || is_dumping_dynamic_archive();
+ return ClassListWriter::is_enabled() || is_dumping_dynamic_archive() || is_dumping_preimage_static_archive();
}
bool CDSConfig::is_dumping_regenerated_lambdaform_invokers() {
diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp
index f722ba7b270..bc19167953b 100644
--- a/src/hotspot/share/cds/cdsConfig.hpp
+++ b/src/hotspot/share/cds/cdsConfig.hpp
@@ -54,6 +54,9 @@ class CDSConfig : public AllStatic {
static bool _new_aot_flags_used;
static bool _disable_heap_dumping;
+ // preserve CompileThresholdScaling for A2 compilation
+ static double _aot_compile_threshold_scaling;
+
static JavaThread* _dumper_thread;
#endif
@@ -203,6 +206,8 @@ class CDSConfig : public AllStatic {
static void enable_dumping_aot_code() NOT_CDS_RETURN;
static bool is_dumping_adapters() NOT_CDS_RETURN_(false);
+ static double aot_compile_threshold_scaling() { return CDS_ONLY(_aot_compile_threshold_scaling) NOT_CDS(1.0); }
+
// Some CDS functions assume that they are called only within a single-threaded context. I.e.,
// they are called from:
// - The VM thread (e.g., inside VM_PopulateDumpSharedSpace)
diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp
index e6320ad0f85..17405b6c079 100644
--- a/src/hotspot/share/cds/cds_globals.hpp
+++ b/src/hotspot/share/cds/cds_globals.hpp
@@ -147,18 +147,21 @@
product(bool, AOTVerifyTrainingData, trueInDebug, DIAGNOSTIC, \
"Verify archived training data") \
\
- product(bool, AOTCompileEagerly, false, EXPERIMENTAL, \
- "Compile methods as soon as possible") \
+ product(bool, AOTCompileEagerly, false, DIAGNOSTIC, \
+ "Compile methods as soon as possible during production run") \
\
/* AOT Code flags */ \
\
+ product(bool, AOTCodeCaching, false, DIAGNOSTIC, \
+ "Enable saving and restoring JIT compiled code in AOT cache") \
+ \
product(bool, AOTAdapterCaching, false, DIAGNOSTIC, \
"Enable saving and restoring i2c2i adapters in AOT cache") \
\
product(bool, AOTStubCaching, false, DIAGNOSTIC, \
"Enable saving and restoring stubs and code blobs in AOT cache") \
\
- product(uint, AOTCodeMaxSize, 10*M, DIAGNOSTIC, \
+ product(uint, AOTCodeMaxSize, 512*M, DIAGNOSTIC, \
"Buffer size in bytes for AOT code caching") \
range(1*M, CODE_CACHE_SIZE_LIMIT) \
\
diff --git a/src/hotspot/share/cds/cppVtables.cpp b/src/hotspot/share/cds/cppVtables.cpp
index e300393bb55..13a76b7298b 100644
--- a/src/hotspot/share/cds/cppVtables.cpp
+++ b/src/hotspot/share/cds/cppVtables.cpp
@@ -353,6 +353,39 @@ void CppVtables::zero_archived_vtables() {
bool CppVtables::is_valid_shared_method(const Method* m) {
assert(AOTMetaspace::in_aot_cache(m), "must be");
- return vtable_of(m) == _index[Method_Kind]->cloned_vtable() ||
- vtable_of(m) == _archived_cpp_vtptrs[Method_Kind];
+ const intptr_t* vt = vtable_of(m);
+ // _archived_cpp_vtptrs[kind] is not null only during AOT assembly phase
+ const intptr_t* archived_vt = _archived_cpp_vtptrs[Method_Kind];
+ return vt == _index[Method_Kind]->cloned_vtable() ||
+ (archived_vt != nullptr && vt == archived_vt);
}
+
+#define CPP_VTABLE_KLASS_DO(f) \
+ f(InstanceKlass) \
+ f(InstanceClassLoaderKlass) \
+ f(InstanceMirrorKlass) \
+ f(InstanceRefKlass) \
+ f(InstanceStackChunkKlass) \
+ f(TypeArrayKlass) \
+ f(ObjArrayKlass) \
+ f(RefArrayKlass) \
+ f(FlatArrayKlass) \
+ f(ValueKlass)
+
+#define KLASS_CHECK(c) \
+ archived_vt = _archived_cpp_vtptrs[c##_Kind]; \
+ if (vt == _index[c##_Kind]->cloned_vtable() || \
+ (archived_vt != nullptr && vt == archived_vt)) { \
+ return true; \
+ }
+
+bool CppVtables::is_valid_shared_klass(const Klass* k) {
+ assert(AOTMetaspace::in_aot_cache(k), "must be");
+ const intptr_t* vt = vtable_of(k);
+ intptr_t* archived_vt;
+ CPP_VTABLE_KLASS_DO(KLASS_CHECK);
+ return false;
+}
+
+#undef KLASS_CHECK
+#undef CPP_VTABLE_KLASS_DO
diff --git a/src/hotspot/share/cds/cppVtables.hpp b/src/hotspot/share/cds/cppVtables.hpp
index 9e28ba020ee..81131b96a93 100644
--- a/src/hotspot/share/cds/cppVtables.hpp
+++ b/src/hotspot/share/cds/cppVtables.hpp
@@ -31,6 +31,7 @@
#include "utilities/globalDefinitions.hpp"
class ArchiveBuilder;
+class Klass;
class Method;
class SerializeClosure;
class CppVtableInfo;
@@ -44,6 +45,7 @@ class CppVtables : AllStatic {
static intptr_t* get_archived_vtable(MetaspaceClosureType type, address obj);
static void serialize(SerializeClosure* sc);
static bool is_valid_shared_method(const Method* m) NOT_CDS_RETURN_(false);
+ static bool is_valid_shared_klass(const Klass* k) NOT_CDS_RETURN_(false);
static char* vtables_serialized_base() { return _vtables_serialized_base; }
};
diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp
index 0bf4b164cd4..d0788871221 100644
--- a/src/hotspot/share/cds/filemap.cpp
+++ b/src/hotspot/share/cds/filemap.cpp
@@ -95,6 +95,10 @@ inline void CDSMustMatchFlags::do_print(outputStream* st, bool v) {
inline void CDSMustMatchFlags::do_print(outputStream* st, uint v) {
st->print("%u", v);
}
+
+inline void CDSMustMatchFlags::do_print(outputStream* st, int v) {
+ st->print("%d", v);
+}
#endif
inline void CDSMustMatchFlags::do_print(outputStream* st, intx v) {
@@ -1039,6 +1043,7 @@ size_t FileMapInfo::remove_bitmap_zeros(CHeapBitMap* map) {
char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap,
CHeapBitMap* ro_ptrmap,
+ CHeapBitMap* ac_ptrmap,
AOTMappedHeapInfo* mapped_heap_info,
AOTStreamedHeapInfo* streamed_heap_info,
size_t &size_in_bytes) {
@@ -1046,7 +1051,7 @@ char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap,
size_t removed_ro_leading_zeros = remove_bitmap_zeros(ro_ptrmap);
header()->set_rw_ptrmap_start_pos(removed_rw_leading_zeros);
header()->set_ro_ptrmap_start_pos(removed_ro_leading_zeros);
- size_in_bytes = rw_ptrmap->size_in_bytes() + ro_ptrmap->size_in_bytes();
+ size_in_bytes = rw_ptrmap->size_in_bytes() + ro_ptrmap->size_in_bytes() + ac_ptrmap->size_in_bytes();
if (mapped_heap_info != nullptr && mapped_heap_info->is_used()) {
// Remove leading and trailing zeros
@@ -1064,9 +1069,10 @@ char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap,
size_in_bytes += streamed_heap_info->oopmap()->size_in_bytes();
}
- // The bitmap region contains up to 4 parts:
+ // The bitmap region contains up to 5 parts:
// rw_ptrmap: metaspace pointers inside the read-write region
// ro_ptrmap: metaspace pointers inside the read-only region
+ // ac_ptrmap: metaspace pointers inside the AOT code cache region
// *_heap_info->oopmap(): Java oop pointers in the heap region
// mapped_heap_info->ptrmap(): metaspace pointers in the heap region
char* buffer = NEW_C_HEAP_ARRAY(char, size_in_bytes, mtClassShared);
@@ -1078,6 +1084,11 @@ char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap,
region_at(AOTMetaspace::ro)->init_ptrmap(written, ro_ptrmap->size());
written = write_bitmap(ro_ptrmap, buffer, written);
+ if (ac_ptrmap->size() > 0) {
+ region_at(AOTMetaspace::ac)->init_ptrmap(written, ac_ptrmap->size());
+ written = write_bitmap(ac_ptrmap, buffer, written);
+ }
+
if (mapped_heap_info != nullptr && mapped_heap_info->is_used()) {
assert(HeapShared::is_writing_mapping_mode(), "unexpected dumping mode");
FileMapRegion* r = region_at(AOTMetaspace::hp);
@@ -1273,6 +1284,9 @@ MapArchiveResult FileMapInfo::map_regions(int regions[], int num_regions, char*
header()->set_mapped_base_address(header()->requested_base_address() + addr_delta);
if (addr_delta != 0 && !relocate_pointers_in_core_regions(addr_delta)) {
return MAP_ARCHIVE_OTHER_FAILURE;
+ } else if (VerifySharedSpaces) {
+ // Map and check the CRC of the bitmap region even if it may not be used.
+ map_bitmap_region();
}
return MAP_ARCHIVE_SUCCESS;
@@ -1344,8 +1358,10 @@ MapArchiveResult FileMapInfo::map_region(int i, intx addr_delta, char* mapped_ba
// Note that this may either be a "fresh" mapping into unreserved address
// space (Windows, first mapping attempt), or a mapping into pre-reserved
// space (Posix). See also comment in AOTMetaspace::map_archives().
+ bool read_only = r->read_only() && !CDSConfig::is_dumping_final_static_archive();
+
char* base = map_memory(_fd, _full_path, r->file_offset(),
- requested_addr, size, r->read_only(),
+ requested_addr, size, read_only,
mtClassShared, r->allow_exec());
if (base != requested_addr) {
AOTMetaspace::report_loading_error("Unable to map %s shared space at " INTPTR_FORMAT,
@@ -1441,12 +1457,17 @@ bool FileMapInfo::map_aot_code_region(ReservedSpace rs) {
if (VerifySharedSpaces && !r->check_region_crc(mapped_base)) {
aot_log_error(aot)("region %d CRC error", AOTMetaspace::ac);
- os::unmap_memory(mapped_base, r->used_aligned());
return false;
}
r->set_mapped_from_file(true);
r->set_mapped_base(mapped_base);
+ if (!relocate_pointers_in_aot_code_region()) {
+ r->set_mapped_from_file(false);
+ r->set_mapped_base(nullptr);
+ return false;
+ }
+ r->set_in_reserved_space(true);
aot_log_info(aot)("Mapped static region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)",
AOTMetaspace::ac, p2i(r->mapped_base()), p2i(r->mapped_end()),
shared_region_name[AOTMetaspace::ac]);
@@ -1454,6 +1475,59 @@ bool FileMapInfo::map_aot_code_region(ReservedSpace rs) {
}
}
+void FileMapInfo::unmap_aot_code_region() {
+ unmap_region(AOTMetaspace::ac);
+ return;
+}
+
+class CachedCodeRelocator: public BitMapClosure {
+ address _code_requested_base;
+ address* _patch_base;
+ intx _code_delta;
+ intx _metadata_delta;
+
+public:
+ CachedCodeRelocator(address code_requested_base, address code_mapped_base,
+ intx metadata_delta) {
+ _code_requested_base = code_requested_base;
+ _patch_base = (address*)code_mapped_base;
+ _code_delta = code_mapped_base - code_requested_base;
+ _metadata_delta = metadata_delta;
+ }
+
+ bool do_bit(size_t offset) {
+ address* p = _patch_base + offset;
+ address requested_ptr = *p;
+ if (requested_ptr < _code_requested_base) {
+ *p = requested_ptr + _metadata_delta;
+ } else {
+ *p = requested_ptr + _code_delta;
+ }
+ return true; // keep iterating
+ }
+};
+
+bool FileMapInfo::relocate_pointers_in_aot_code_region() {
+ FileMapRegion* r = region_at(AOTMetaspace::ac);
+ if (map_bitmap_region() == nullptr) {
+ return false; // OOM, or CRC check failure
+ }
+ BitMapView ac_ptrmap = ptrmap_view(AOTMetaspace::ac);
+ if (ac_ptrmap.size() == 0) {
+ return true;
+ }
+
+ address core_regions_requested_base = (address)header()->requested_base_address();
+ address core_regions_mapped_base = (address)header()->mapped_base_address();
+ address ac_region_requested_base = core_regions_requested_base + r->mapping_offset();
+ address ac_region_mapped_base = (address)r->mapped_base();
+
+ CachedCodeRelocator patcher(ac_region_requested_base, ac_region_mapped_base,
+ core_regions_mapped_base - core_regions_requested_base);
+ ac_ptrmap.iterate(&patcher);
+ return true;
+}
+
class SharedDataRelocationTask : public ArchiveWorkerTask {
private:
BitMapView* const _rw_bm;
diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp
index 126e269db7b..5987e6d52d3 100644
--- a/src/hotspot/share/cds/filemap.hpp
+++ b/src/hotspot/share/cds/filemap.hpp
@@ -112,7 +112,9 @@ class FileMapRegion: private CDSFileMapRegion {
f(UseNullFreeAtomicValueFlattening) \
f(UseNullableAtomicValueFlattening) \
f(UseNullableNonAtomicValueFlattening) \
- f(FlatteningBudget)
+ f(FlatteningBudget) \
+ f(RestrictReservedStack) \
+ f(DiagnoseSyncOnValueBasedClasses)
class CDSMustMatchFlags {
@@ -125,6 +127,7 @@ class CDSMustMatchFlags {
inline static void do_print(outputStream* st, bool v);
LP64_ONLY(inline static void do_print(outputStream* st, uint v);)
+ LP64_ONLY(inline static void do_print(outputStream* st, int v);)
inline static void do_print(outputStream* st, intx v);
inline static void do_print(outputStream* st, uintx v);
inline static void do_print(outputStream* st, double v);
@@ -410,6 +413,7 @@ class FileMapInfo : public CHeapObj {
size_t remove_bitmap_zeros(CHeapBitMap* map);
char* write_bitmap_region(CHeapBitMap* rw_ptrmap,
CHeapBitMap* ro_ptrmap,
+ CHeapBitMap* ac_ptrmap,
AOTMappedHeapInfo* mapped_heap_info,
AOTStreamedHeapInfo* streamed_heap_info,
size_t &size_in_bytes);
@@ -430,6 +434,7 @@ class FileMapInfo : public CHeapObj {
bool read_region(int i, char* base, size_t size, bool do_commit);
char* map_bitmap_region();
bool map_aot_code_region(ReservedSpace rs);
+ void unmap_aot_code_region();
char* map_forwarding_region();
void unmap_region(int i);
void close();
@@ -493,6 +498,7 @@ class FileMapInfo : public CHeapObj {
MapArchiveResult map_region(int i, intx addr_delta, char* mapped_base_address, ReservedSpace rs);
bool relocate_pointers_in_core_regions(intx addr_delta);
+ bool relocate_pointers_in_aot_code_region();
char* map_auxiliary_region(int region_index, bool read_only);
public:
diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp
index bb0d1abc3d6..9289bad0c67 100644
--- a/src/hotspot/share/cds/heapShared.cpp
+++ b/src/hotspot/share/cds/heapShared.cpp
@@ -51,6 +51,7 @@
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
+#include "code/aotCodeCache.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "gc/shared/gcLocker.hpp"
#include "gc/shared/gcVMOperations.hpp"
@@ -672,6 +673,10 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra
AOTArtifactFinder::add_cached_class(method_holder);
_dumptime_resolved_methods->append(HeapShared::append_root(obj));
}
+ } else if (AOTCodeCache::is_dumping_code() &&
+ (java_lang_invoke_MethodHandle::is_instance(obj) || is_interned_string(obj))) {
+ // Needed by AOT compiler.
+ append_root(obj);
}
}
@@ -1117,7 +1122,11 @@ void KlassSubGraphInfo::add_subgraph_entry_field(int static_field_offset, oop v)
new (mtClass) GrowableArray(10, mtClass);
}
_subgraph_entry_fields->append(static_field_offset);
- _subgraph_entry_fields->append(HeapShared::append_root(v));
+ if (v == nullptr) {
+ _subgraph_entry_fields->append(-1);
+ } else {
+ _subgraph_entry_fields->append(HeapShared::append_root(v));
+ }
}
// Add the Klass* for an object in the current KlassSubGraphInfo's subgraphs.
@@ -1419,16 +1428,35 @@ void HeapShared::resolve_classes_for_subgraph_of(JavaThread* current, Klass* k)
}
}
+static const char* java_lang_invoke_core_klasses[] = {
+ "java/lang/invoke/Invokers$Holder",
+ "java/lang/invoke/MethodHandle",
+ "java/lang/invoke/MethodHandleNatives",
+ "java/lang/invoke/DirectMethodHandle$Holder",
+ "java/lang/invoke/DelegatingMethodHandle$Holder",
+ "java/lang/invoke/LambdaForm$Holder",
+ "java/lang/invoke/BoundMethodHandle$Species_L",
+};
+
void HeapShared::initialize_java_lang_invoke(TRAPS) {
if (CDSConfig::is_using_aot_linked_classes() || CDSConfig::is_dumping_method_handles()) {
- resolve_or_init("java/lang/invoke/Invokers$Holder", true, CHECK);
- resolve_or_init("java/lang/invoke/MethodHandle", true, CHECK);
- resolve_or_init("java/lang/invoke/MethodHandleNatives", true, CHECK);
- resolve_or_init("java/lang/invoke/DirectMethodHandle$Holder", true, CHECK);
- resolve_or_init("java/lang/invoke/DelegatingMethodHandle$Holder", true, CHECK);
- resolve_or_init("java/lang/invoke/LambdaForm$Holder", true, CHECK);
- resolve_or_init("java/lang/invoke/BoundMethodHandle$Species_L", true, CHECK);
+ int len = sizeof(java_lang_invoke_core_klasses)/sizeof(char*);
+ for (int i = 0; i < len; i++) {
+ resolve_or_init(java_lang_invoke_core_klasses[i], true, CHECK);
+ }
+ }
+}
+
+bool HeapShared::is_core_java_lang_invoke_klass(InstanceKlass* klass) {
+ ResourceMark rm;
+ char* s2 = klass->name()->as_C_string();
+ int len = sizeof(java_lang_invoke_core_klasses)/sizeof(char*);
+ for (int i = 0; i < len; i++) {
+ if (strcmp(java_lang_invoke_core_klasses[i], s2) == 0) {
+ return true;
+ }
}
+ return false;
}
// Initialize the InstanceKlasses of objects that are reachable from the following roots:
@@ -1617,7 +1645,12 @@ void HeapShared::init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphI
int root_index = entry_field_records->at(i+1);
// Load the subgraph entry fields from the record and store them back to
// the corresponding fields within the mirror.
- oop v = get_root(root_index, /*clear=*/true);
+ oop v;
+ if (root_index < 0) {
+ v = nullptr;
+ } else {
+ v = get_root(root_index, /*clear=*/true);
+ }
oop m = k->java_mirror();
if (k->has_aot_initialized_mirror()) {
assert(v == m->obj_field(field_offset), "must be aot-initialized");
diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp
index afdd819f612..78f493d17c8 100644
--- a/src/hotspot/share/cds/heapShared.hpp
+++ b/src/hotspot/share/cds/heapShared.hpp
@@ -477,6 +477,7 @@ class HeapShared: AllStatic {
static void initialize_java_lang_invoke(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
static void init_classes_for_special_subgraph(Handle loader, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
+ static bool is_core_java_lang_invoke_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
static bool is_lambda_form_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
static bool is_lambda_proxy_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
static bool is_string_concat_klass(InstanceKlass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
diff --git a/src/hotspot/share/ci/ciConstant.cpp b/src/hotspot/share/ci/ciConstant.cpp
index 6e09bb7a25a..adcf02beb12 100644
--- a/src/hotspot/share/ci/ciConstant.cpp
+++ b/src/hotspot/share/ci/ciConstant.cpp
@@ -81,6 +81,19 @@ bool ciConstant::is_loaded() const {
return false;
}
+// ------------------------------------------------------------------
+// ciConstant::should_be_constant
+bool ciConstant::should_be_constant() const {
+ if (is_valid()) {
+ if (is_reference_type(basic_type())) {
+ return as_object()->should_be_constant();
+ } else {
+ return true;
+ }
+ }
+ return false;
+}
+
// ------------------------------------------------------------------
// ciConstant::print
void ciConstant::print() const {
diff --git a/src/hotspot/share/ci/ciConstant.hpp b/src/hotspot/share/ci/ciConstant.hpp
index de63fbab10d..f886f98bfe4 100644
--- a/src/hotspot/share/ci/ciConstant.hpp
+++ b/src/hotspot/share/ci/ciConstant.hpp
@@ -118,6 +118,8 @@ class ciConstant {
bool is_loaded() const;
+ bool should_be_constant() const;
+
// Debugging output
void print() const;
};
diff --git a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp
index 783037535f4..505cbd65fba 100644
--- a/src/hotspot/share/ci/ciEnv.cpp
+++ b/src/hotspot/share/ci/ciEnv.cpp
@@ -22,6 +22,9 @@
*
*/
+#include "cds/aotCacheAccess.hpp"
+#include "cds/aotConstantPoolResolver.hpp"
+#include "cds/heapShared.hpp"
#include "ci/ciConstant.hpp"
#include "ci/ciEnv.hpp"
#include "ci/ciField.hpp"
@@ -37,6 +40,7 @@
#include "classfile/systemDictionary.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/codeCache.hpp"
#include "code/scopeDesc.hpp"
#include "compiler/compilationLog.hpp"
@@ -66,6 +70,7 @@
#include "oops/oopCast.inline.hpp"
#include "oops/resolvedIndyEntry.hpp"
#include "oops/symbolHandle.hpp"
+#include "oops/trainingData.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/methodHandles.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
@@ -118,6 +123,7 @@ ciEnv::ciEnv(CompileTask* task)
_debug_info = nullptr;
_dependencies = nullptr;
_inc_decompile_count_on_failure = true;
+ _codecache_full = false;
_compilable = MethodCompilable;
_break_at_compile = false;
_compiler_data = nullptr;
@@ -172,6 +178,8 @@ ciEnv::ciEnv(CompileTask* task)
_jvmti_can_access_local_variables = false;
_jvmti_can_post_on_exceptions = false;
_jvmti_can_pop_frame = false;
+ _jvmti_can_get_owned_monitor_info = false;
+ _jvmti_can_walk_any_space = false;
_dyno_klasses = nullptr;
_dyno_locs = nullptr;
@@ -255,6 +263,7 @@ ciEnv::ciEnv(Arena* arena) : _ciEnv_arena(mtCompiler, Arena::Tag::tag_cienv) {
_debug_info = nullptr;
_dependencies = nullptr;
_inc_decompile_count_on_failure = true;
+ _codecache_full = false;
_compilable = MethodCompilable_never;
_break_at_compile = false;
_compiler_data = nullptr;
@@ -295,6 +304,8 @@ ciEnv::ciEnv(Arena* arena) : _ciEnv_arena(mtCompiler, Arena::Tag::tag_cienv) {
_jvmti_can_access_local_variables = false;
_jvmti_can_post_on_exceptions = false;
_jvmti_can_pop_frame = false;
+ _jvmti_can_get_owned_monitor_info = false;
+ _jvmti_can_walk_any_space = false;
_dyno_klasses = nullptr;
_dyno_locs = nullptr;
@@ -671,7 +682,7 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
ciInstanceKlass* accessor) {
if (obj_index >= 0) {
ciConstant con = get_resolved_constant(cpool, obj_index);
- if (con.is_valid()) {
+ if (con.should_be_constant()) {
return con;
}
}
@@ -825,7 +836,15 @@ ciMethod* ciEnv::get_method_by_index_impl(const constantPoolHandle& cpool,
// Patch the call site to the nmethod entry point of the static compiled lambda form.
// As with other two-component call sites, both values must be independently verified.
assert(index < cpool->cache()->resolved_indy_entries_length(), "impossible");
- Method* adapter = cpool->resolved_indy_entry_at(index)->method();
+ ResolvedIndyEntry* indy_info = cpool->resolved_indy_entry_at(index);
+ Method* adapter = indy_info->method();
+#if INCLUDE_CDS
+ if (is_aot_compile() && !AOTConstantPoolResolver::is_resolution_deterministic(cpool(), indy_info->constant_pool_index())) {
+ // This is an indy callsite that was resolved as a side effect of VM bootstrap, but
+ // it cannot be cached in the resolved state, so AOT code should not reference it.
+ adapter = nullptr;
+ }
+#endif
// Resolved if the adapter is non null.
if (adapter != nullptr) {
return get_method(adapter);
@@ -872,11 +891,11 @@ ciMethod* ciEnv::get_method_by_index_impl(const constantPoolHandle& cpool,
constantTag tag = cpool->tag_ref_at(index, bc);
assert(accessor->get_instanceKlass() == cpool->pool_holder(), "not the pool holder?");
Method* m = lookup_method(accessor, holder, name_sym, sig_sym, bc, tag);
- if (m != nullptr &&
- (bc == Bytecodes::_invokestatic
- ? m->method_holder()->is_not_initialized()
- : !m->method_holder()->is_loaded())) {
- m = nullptr;
+ if (m != nullptr) {
+ ciInstanceKlass* cik = get_instance_klass(m->method_holder());
+ if ((bc == Bytecodes::_invokestatic && cik->is_not_initialized()) || !cik->is_loaded()) {
+ m = nullptr;
+ }
}
if (m != nullptr && ReplayCompiles && !ciReplay::is_loaded(m)) {
m = nullptr;
@@ -957,7 +976,7 @@ bool ciEnv::is_in_vm() {
// Check for changes during compilation (e.g. class loads, evolution,
// breakpoints, call site invalidation).
void ciEnv::validate_compile_task_dependencies(ciMethod* target) {
- if (failing()) return; // no need for further checks
+ assert(!failing(), "should not call this when failing");
Dependencies::DepType result = dependencies()->validate_dependencies(_task);
if (result != Dependencies::end_marker) {
@@ -972,8 +991,211 @@ void ciEnv::validate_compile_task_dependencies(ciMethod* target) {
}
}
+// is_loading_aot_code = true implies loading compiled code from AOT code cache
+bool ciEnv::is_compilation_valid(JavaThread* thread, ciMethod* target, bool install_code, bool is_loading_aot_code, bool aot_preload) {
+ methodHandle method(thread, target->get_Method());
+
+ // Change in Jvmti state may invalidate compilation.
+ if (!failing() && jvmti_state_changed()) {
+ record_failure("Jvmti state change invalidated dependencies");
+ }
+
+ if (!failing() && is_loading_aot_code && !AOTCodeCache::verify_jvmti_state(this)) {
+ record_failure("Jvmti state change invalidated dependencies for AOT code or other AOT code cache failure");
+ }
+
+ // Change in DTrace flags may invalidate compilation.
+ if (!failing() &&
+ ( (!dtrace_method_probes() && DTraceMethodProbes) ||
+ (!dtrace_alloc_probes() && DTraceAllocProbes) )) {
+ record_failure("DTrace flags change invalidated dependencies");
+ }
+
+ if (!failing() && target->needs_clinit_barrier() &&
+ target->holder()->is_in_error_state()) {
+ record_failure("method holder is in error state");
+ }
+
+ if (!failing() && !is_loading_aot_code) {
+ if (log() != nullptr) {
+ // Log the dependencies which this compilation declares.
+ dependencies()->log_all_dependencies();
+ }
+
+ // Encode the dependencies now, so we can check them right away.
+ dependencies()->encode_content_bytes();
+ }
+ // Check for {class loads, evolution, breakpoints, ...} during compilation.
+ // This check is skipped for RepeatCompilation request.
+ // Execute this check for VerifyAOTCode when loading AOT code.
+ // Note, RepeatCompilation request is ignored for AOT code load.
+ if (!failing() && (install_code || is_loading_aot_code)) {
+ // Check for {class loads, evolution, breakpoints, ...} during compilation
+ validate_compile_task_dependencies(target);
+ if (failing() && aot_preload) {
+ ResourceMark rm;
+ char* method_name = method->name_and_sig_as_C_string();
+ log_info(aot, codecache, nmethod)("AOT preload code for '%s' failed dependency check", method_name);
+ }
+ }
+
+ if (failing()) {
+ // While not a true deoptimization, it is a preemptive decompile.
+ MethodData* mdo = method()->method_data();
+ if (mdo != nullptr && _inc_decompile_count_on_failure) {
+ mdo->inc_decompile_count();
+ }
+ return false;
+ }
+ return true;
+}
+
+void ciEnv::make_code_usable(JavaThread* thread, ciMethod* target, bool aot_preload, int entry_bci, AOTCodeEntry* aot_code_entry, nmethod* nm) {
+ methodHandle method(thread, target->get_Method());
+
+ if (entry_bci == InvocationEntryBci) {
+ // With TieredCompilation OFF we still have two versions
+ // of AOT code and also JIT compilation.
+ // During assembly phase we need to replace previous
+ // AOT preload code with normal AOT code to check
+ // success of compilation in AOTCompileIterator.
+ // After JDK-8380476 FIXME: AOT compiler should not be installing
+ // nmethods on the Method::code pointer. Find other way to report success.
+ if (TieredCompilation || AOTCodeCache::is_using_code() || is_aot_compile()) {
+ // If there is an old version we're done with it
+ nmethod* old = method->code();
+ if (TraceMethodReplacement && old != nullptr) {
+ ResourceMark rm;
+ char *method_name = method->name_and_sig_as_C_string();
+ tty->print_cr("Replacing method %s", method_name);
+ }
+ if (old != nullptr) {
+ old->make_not_used();
+ }
+ }
+
+ LogTarget(Info, nmethod, install) lt;
+ if (lt.is_enabled()) {
+ ResourceMark rm;
+ char *method_name = method->name_and_sig_as_C_string();
+ lt.print("Installing method (L%d) %s id=%d aot=%s%s%u",
+ task()->comp_level(), method_name, compile_id(),
+ task()->is_aot_load() ? "A" : "", aot_preload ? "P" : "",
+ (aot_code_entry != nullptr ? aot_code_entry->offset() : 0));
+ }
+ // Allow the code to be executed
+ MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
+ if (nm->make_in_use()) {
+ assert(!aot_preload || method->method_holder()->is_linked(), "AOT code preloaded only for linked method holder");
+ method->set_code(method, nm);
+#if INCLUDE_CDS
+ if (aot_preload) {
+ MethodCounters* mc = method->get_method_counters(thread);
+ assert(mc != nullptr, "CompileBroker should create MethodCounters if it is missing");
+ mc->set_aot_preload_code_entry(aot_code_entry);
+ nm->set_aot_preloaded(true);
+ }
+#endif
+ }
+ } else {
+ LogTarget(Info, nmethod, install) lt;
+ if (lt.is_enabled()) {
+ assert(aot_code_entry == nullptr && !task()->is_aot_load(), "OSR nmethods are not AOT compiled");
+ ResourceMark rm;
+ char *method_name = method->name_and_sig_as_C_string();
+ lt.print("Installing osr method (L%d) %s @ %d id=%u",
+ task()->comp_level(), method_name, entry_bci, compile_id());
+ }
+ MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
+ if (nm->make_in_use()) {
+ method->method_holder()->add_osr_nmethod(nm);
+ }
+ }
+}
+
+#if INCLUDE_CDS
+// Register method loaded from AOT code cache
+nmethod* ciEnv::register_aot_method(JavaThread* thread,
+ ciMethod* target,
+ AbstractCompiler* compiler,
+ nmethod* archived_nm,
+ AOTCodeReader* aot_code_reader,
+ bool install_code)
+{
+ AOTCodeEntry* aot_code_entry = task()->aot_code_entry();
+ precond(aot_code_entry != nullptr);
+ nmethod* nm = nullptr;
+ {
+ methodHandle method(thread, target->get_Method());
+ bool aot_preload = task()->is_aot_preload(); // AOT code to preload before Java method execution
+
+ // We require method counters to store some method state (max compilation levels) required by the compilation policy.
+ if (method->get_method_counters(thread) == nullptr) {
+ record_failure("can't create method counters");
+ return nullptr;
+ }
+
+ // Check if memory should be freed before allocation
+ CodeCache::gc_on_allocation();
+
+ // To prevent compile queue updates.
+ MutexLocker locker(thread, MethodCompileQueue_lock);
+
+ // Prevent InstanceKlass::add_to_hierarchy from running
+ // and invalidating our dependencies until we install this method.
+ // No safepoints are allowed. Otherwise, class redefinition can occur in between.
+ MutexLocker ml(Compile_lock);
+ NoSafepointVerifier nsv;
+
+ // AOTCode entry indicates this shared code was marked invalid while it was loaded.
+ if (aot_code_entry->not_entrant()) {
+ record_failure("AOT entry was marked not-entrant");
+ return nullptr;
+ }
+
+ if (!is_compilation_valid(thread, target, install_code, /*is_loading_aot_code*/ true, aot_preload)) {
+ return nullptr;
+ }
+
+ if (install_code) {
+ nm = nmethod::new_nmethod(archived_nm, method, aot_code_reader);
+ }
+ if (nm != nullptr) {
+#ifdef ASSERT
+ LogStreamHandle(Trace, aot, codecache, nmethod) log;
+ if (log.is_enabled()) {
+ ResourceMark rm;
+ stringStream ss;
+ ss.print_cr("=== loaded AOT nmethod code ===");
+ FlagSetting fs(PrintRelocations, true);
+ nm->print_on_impl(&ss);
+ nm->decode(&ss);
+ ss.print_cr("===============================");
+ log.print_cr("%s", ss.freeze());
+ }
+#endif
+ assert(nm->aot_code_entry() == aot_code_entry, "mismatched entry");
+ assert(aot_code_entry->is_loaded(), "AOT entry should be marked as loaded already");
+ assert(nm->has_clinit_barriers() == aot_code_entry->has_clinit_barriers(), "should match");
+ make_code_usable(thread, target, aot_preload, InvocationEntryBci, aot_code_entry, nm);
+ }
+ }
+
+ NoSafepointVerifier nsv;
+ if (nm != nullptr) {
+ // Compilation succeeded, post what we know about it
+ nm->post_compiled_method(task());
+ } else if (install_code) {
+ // The CodeCache is full.
+ record_codecache_full();
+ }
+ // safepoints are allowed again
+ return nm;
+}
+#endif
+
// ------------------------------------------------------------------
-// ciEnv::register_method
+// Register the result of a compilation including AOT compilation
void ciEnv::register_method(ciMethod* target,
int entry_bci,
CodeOffsets* offsets,
@@ -984,11 +1206,14 @@ void ciEnv::register_method(ciMethod* target,
ExceptionHandlerTable* handler_table,
ImplicitExceptionTable* inc_table,
AbstractCompiler* compiler,
+ bool has_clinit_barriers,
+ bool for_aot_preload,
bool has_unsafe_access,
bool has_wide_vectors,
bool has_monitors,
bool has_scoped_access,
- int immediate_oops_patched) {
+ int immediate_oops_patched,
+ bool install_code) {
VM_ENTRY_MARK;
nmethod* nm = nullptr;
{
@@ -1016,46 +1241,7 @@ void ciEnv::register_method(ciMethod* target,
MutexLocker ml(Compile_lock);
NoSafepointVerifier nsv;
- // Change in Jvmti state may invalidate compilation.
- if (!failing() && jvmti_state_changed()) {
- record_failure("Jvmti state change invalidated dependencies");
- }
-
- // Change in DTrace flags may invalidate compilation.
- if (!failing() &&
- ( (!dtrace_method_probes() && DTraceMethodProbes) ||
- (!dtrace_alloc_probes() && DTraceAllocProbes) )) {
- record_failure("DTrace flags change invalidated dependencies");
- }
-
- if (!failing() && target->needs_clinit_barrier() &&
- target->holder()->is_in_error_state()) {
- record_failure("method holder is in error state");
- }
-
- if (!failing()) {
- if (log() != nullptr) {
- // Log the dependencies which this compilation declares.
- dependencies()->log_all_dependencies();
- }
-
- // Encode the dependencies now, so we can check them right away.
- dependencies()->encode_content_bytes();
-
- // Check for {class loads, evolution, breakpoints, ...} during compilation
- validate_compile_task_dependencies(target);
- }
-
- if (failing()) {
- // While not a true deoptimization, it is a preemptive decompile.
- MethodData* mdo = method()->method_data();
- if (mdo != nullptr && _inc_decompile_count_on_failure) {
- mdo->inc_decompile_count();
- }
-
- // All buffers in the CodeBuffer are allocated in the CodeCache.
- // If the code buffer is created on each compile attempt
- // as in C2, then it must be freed.
+ if (!is_compilation_valid(THREAD, target, install_code, /*is_loading_aot_code*/ false, /*aot_preload*/ false)) {
code_buffer->free_blob();
return;
}
@@ -1065,64 +1251,78 @@ void ciEnv::register_method(ciMethod* target,
assert(compiler->type() == compiler_c2 ||
offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry");
- bool needs_stack_repair = (compiler->is_c2() && method()->needs_stack_repair());
-
- nm = nmethod::new_nmethod(method,
- compile_id(),
- entry_bci,
- offsets,
- orig_pc_offset,
- debug_info(), dependencies(), code_buffer,
- frame_words, oop_map_set,
- handler_table, inc_table,
- compiler, CompLevel(task()->comp_level()),
- nmethod::Flags(has_unsafe_access, has_wide_vectors, has_monitors, has_scoped_access, needs_stack_repair));
-
+ if (install_code) {
+ bool needs_stack_repair = (compiler->is_c2() && method()->needs_stack_repair());
+ nm = nmethod::new_nmethod(method,
+ compile_id(),
+ entry_bci,
+ offsets,
+ orig_pc_offset,
+ debug_info(), dependencies(), code_buffer,
+ frame_words, oop_map_set,
+ handler_table, inc_table,
+ compiler, CompLevel(task()->comp_level()),
+ nmethod::Flags(has_unsafe_access,
+ has_wide_vectors,
+ has_monitors,
+ has_scoped_access,
+ needs_stack_repair,
+ has_clinit_barriers));
+ }
// Free codeBlobs
code_buffer->free_blob();
if (nm != nullptr) {
assert(!method->is_synchronized() || nm->has_monitors(), "");
- if (entry_bci == InvocationEntryBci) {
- if (TieredCompilation) {
- // If there is an old version we're done with it
- nmethod* old = method->code();
- if (TraceMethodReplacement && old != nullptr) {
- ResourceMark rm;
- char *method_name = method->name_and_sig_as_C_string();
- tty->print_cr("Replacing method %s", method_name);
- }
- if (old != nullptr) {
- old->make_not_used();
- }
- }
-
- LogTarget(Info, nmethod, install) lt;
- if (lt.is_enabled()) {
- ResourceMark rm;
- char *method_name = method->name_and_sig_as_C_string();
- lt.print("Installing method (%d) %s ",
- task()->comp_level(), method_name);
- }
- // Allow the code to be executed
- MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
- if (nm->make_in_use()) {
- method->set_code(method, nm);
- }
- } else {
- LogTarget(Info, nmethod, install) lt;
- if (lt.is_enabled()) {
+#if INCLUDE_CDS
+ if (task()->is_aot_compile()) {
+#ifdef ASSERT
+ LogStreamHandle(Trace, aot, codecache, nmethod) log;
+ if (log.is_enabled()) {
ResourceMark rm;
- char *method_name = method->name_and_sig_as_C_string();
- lt.print("Installing osr method (%d) %s @ %d",
- task()->comp_level(), method_name, entry_bci);
+ stringStream ss;
+ ss.print_cr("=== storing AOT nmethod code ===");
+ FlagSetting fs(PrintRelocations, true);
+ nm->print_on_impl(&ss);
+ nm->decode(&ss);
+ ss.print_cr("===============================");
+ log.print_cr("%s", ss.freeze());
}
- MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
- if (nm->make_in_use()) {
- method->method_holder()->add_osr_nmethod(nm);
+#endif
+ AOTCodeEntry* aot_code_entry = AOTCodeCache::store_nmethod(nm, compiler, for_aot_preload);
+ if (aot_code_entry != nullptr) {
+ aot_code_entry->set_has_vectors(debug_info()->has_vectors());
+ nm->set_aot_code_entry(aot_code_entry);
+ aot_code_entry->set_inlined_bytecodes(num_inlined_bytecodes());
+ // Inline size was recorded during training.
+ CompileTrainingData* ctd = nullptr;
+ {
+ TrainingData::TrainingDataLocker l;
+ MethodTrainingData* mtd = MethodTrainingData::find(method);
+ assert(mtd != nullptr, "AOT compiled method should have MethodTrainingData");
+ ctd = mtd->compile_data_for_aot_code(nm->comp_level());
+ }
+ assert(ctd != nullptr, "AOT compiled method should have CompileTrainingData");
+ int inline_size = ctd->inline_instructions_size();
+ aot_code_entry->set_inline_instructions_size(inline_size);
+ if (for_aot_preload) {
+ // Set it only for printing purpose, otherwise it is unused
+ // during assembly phase.
+ nm->set_aot_preloaded(true);
+ }
}
+ // After JDK-8380476 FIXME: The AOT compiler runs in a special AOT assembly
+ // phase, not part of any Java application. The compiled code is not
+ // runnable in this instance of the JVM. For consistency of access,
+ // we will fall through to make_code_usable and link the Method::code
+ // field to the new nmethod nm. But this would be a mistake if (for
+ // any reason) the compiled method were to be invoked in this JVM
+ // instance. Consider returning at this point, relying only on
+ // AOTCodeCache::store_nmethod to keep track of the new nmethod.
}
+#endif
+ make_code_usable(THREAD, target, /* aot_preload */ false, entry_bci, /* aot_code_entry */ nullptr, nm);
}
}
@@ -1131,9 +1331,11 @@ void ciEnv::register_method(ciMethod* target,
// Compilation succeeded, post what we know about it
nm->post_compiled_method(task());
task()->set_num_inlined_bytecodes(num_inlined_bytecodes());
- } else {
+ } else if (install_code) {
// The CodeCache is full.
- record_failure("code cache is full");
+ record_codecache_full();
+ } else {
+ task()->set_num_inlined_bytecodes(num_inlined_bytecodes());
}
// safepoints are allowed again
@@ -1235,6 +1437,11 @@ void ciEnv::record_out_of_memory_failure() {
record_method_not_compilable("out of memory");
}
+void ciEnv::record_codecache_full() {
+ _codecache_full = true;
+ record_failure("code cache is full");
+}
+
ciInstance* ciEnv::unloaded_ciinstance() {
GUARDED_VM_ENTRY(return _factory->get_unloaded_object_constant();)
}
@@ -1705,3 +1912,126 @@ void ciEnv::dump_inline_data(int compile_id) {
void ciEnv::dump_replay_data_version(outputStream* out) {
out->print_cr("version %d", REPLAY_VERSION);
}
+
+#if INCLUDE_CDS
+
+bool ciEnv::is_aot_compile() const {
+ return (task() != nullptr) && task()->is_aot_compile();
+}
+
+// The AOT compiler must model clinit state before classes are actually loaded
+// in the eventual production run. Its model is provisional and approximate,
+// and any nmethod compiled from this model must conserve semantic
+// correctness.
+//
+// For "preload" AOT code (AP tier), the model optimistically speculates that
+// the class is probably loaded, but the speculation is checked before every
+// static field or method use or instance allocation, just like the
+// interpreter. (See Parse::do_call and GraphKit::clinit_barrier.) Thus, we
+// usually return fully_initialized, when compiling preload code.
+//
+// For non-preload AOT code (A tier), we can make some predictions about which
+// classes must be initialized by the time the code gets executed. First,
+// 1. the top-level method's holder can be assumed initialized, since somebody
+// else already triggered initialization before the method could be called.
+// Also 2. the clinit dependencies listed in the CompileTrainingData (CTD) are
+// always checked off before a non-preload nmethod is installed, so all those
+// classes can be assumed initialized. Finally 3. certain core JDK classes are always
+// force-initialized early enough that all compiled code can assume they are
+// initialized.
+//
+// Neither the JIT nor the AOT compiler will attempt to optimize code classes
+// that are unusable because their initializers have failed. This fallback
+// is also used when there is not enough information available about a class
+// during AOT cache assembly. In that case, we return the special sentinel
+// value initialization_error, used here as a sentinel meaning "unknown"; a distinct
+// value (unavailable_aot) is proposed for a later change.
+//
+// One thing this function does NOT do is look at the clinit state of any class.
+// This may be surprising, since a normal JIT compilation task directly queries
+// clinit states, via ciInstanceKlass::compute_init_state. But for an AOT
+// compilation task, ciIK::compute_init_state calls here, and a future clinit state
+// is predicted, instead of a current state. The nmethod code cannot be loaded
+// until all such predictions come true. Eventually, as a future production run
+// initializes classes in some order, a countdown counter on the CTD will tally
+// every clinit event, and eventually reach zero, opening the gate to load the
+// AOT code in the nmethod.
+//
+// These predictions, of which class needs to be initialized for the code to work,
+// have already been made by a previous pass of the compiler, on the same method
+// at the same level, in the earlier training run (TR). The CTD contains a dep-list
+// of every class which the JIT, running in the TR, observed to be initialized.
+// That dep-list gets passed forward to the AOT compiler, which re-runs a similar
+// compilation, after the TR, in the assembly phase (APH) that creates the AOT cache.
+// Since the APH is a fresh instance of the JVM, there are no interesting clinit
+// states to be observed directly in the InstanceKlass metadata. (The JVM running
+// the APH does have a few initialized class states, but they are not useful to
+// AOT compilation, so they are deliberately not examined.)
+//
+InstanceKlass::ClassState ciEnv::compute_init_state_for_aot_compile(InstanceKlass* ik) {
+ ASSERT_IN_VM;
+ ResourceMark rm;
+ assert(is_aot_compile(), "should be called only for AOT compilation in assembly phase");
+ assert(!ik->is_in_error_state(), "comp_id: %d, klass %s", task()->compile_id(), ik->external_name());
+
+ if (!AOTCacheAccess::can_generate_aot_code_for(ik)) {
+ log_debug(aot, compilation)("%d: klass (%s) %s is not archived", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+ // Skip this class
+ return InstanceKlass::ClassState::initialization_error;
+ }
+ if (task()->method()->method_holder() == ik) {
+ log_trace(aot, compilation)("%d: method_holder: (%s) %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+ if (task()->method()->is_class_initializer()) { // Happens with -Xcomp
+ return InstanceKlass::ClassState::being_initialized;
+ } else {
+ return InstanceKlass::ClassState::fully_initialized;
+ }
+ }
+
+ switch (task()->compile_reason()) {
+ case CompileTask::Reason_AOTCompile: {
+ // Check init dependencies
+ MethodTrainingData* mtd = nullptr;
+ GUARDED_VM_ENTRY(mtd = MethodTrainingData::find(methodHandle(Thread::current(), task()->method())); )
+ if (mtd != nullptr) {
+ CompileTrainingData* ctd = mtd->compile_data_for_aot_code(task()->comp_level());
+ if (ctd != nullptr) {
+ for (int i = 0; i < ctd->init_dep_count(); i++) {
+ KlassTrainingData* ktd = ctd->init_dep(i);
+ if (ktd->has_holder() && (ktd->holder() == ik)) {
+ log_trace(aot, compilation)("%d: init_dependency: (%s) %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+ return InstanceKlass::ClassState::fully_initialized; // init dependency present
+ }
+ }
+ }
+ }
+
+ // Core java/lang/invoke classes are peculiar. They include LF invokers, which
+ // are initialized in production run, but they are not recorded as init dependencies.
+ // CI query should report their status as if in production run, otherwise AOT
+ // code would have uncommon traps at invokedynamic calls.
+ if (HeapShared::is_core_java_lang_invoke_klass(ik)) {
+ log_trace(aot, compilation)("%d: core_java_lang_invoke: (%s) %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+ return InstanceKlass::ClassState::fully_initialized;
+ }
+
+ // Class may be not present during TD creation for this method.
+ // It could happen when profiled data for inlined method
+ // was updated after this method was compiled during training.
+ break;
+ }
+ case CompileTask::Reason_AOTCompileForPreload: {
+ // Preload AOT code does not depend on Training Data,
+ // it has class init barriers to initialize class by
+ // going into interpreter or directly calling runtime.
+ log_trace(aot, compilation)("%d: for_aot_preload: (%s) %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+ return InstanceKlass::ClassState::fully_initialized;
+ }
+ default: fatal("%s", CompileTask::reason_name(task()->compile_reason()));
+ }
+ log_debug(aot, compilation)("%d: klass (%s) %s is not recorded for this compilation", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+ // Skip this class
+ return InstanceKlass::ClassState::initialization_error;
+}
+
+#endif // INCLUDE_CDS
diff --git a/src/hotspot/share/ci/ciEnv.hpp b/src/hotspot/share/ci/ciEnv.hpp
index 4a844bd1f51..40633ddfb15 100644
--- a/src/hotspot/share/ci/ciEnv.hpp
+++ b/src/hotspot/share/ci/ciEnv.hpp
@@ -34,11 +34,14 @@
#include "compiler/cHeapStringHolder.hpp"
#include "compiler/compiler_globals.hpp"
#include "compiler/compilerThread.hpp"
+#include "oops/methodCounters.hpp"
#include "oops/methodData.hpp"
#include "runtime/javaThread.hpp"
class CompileTask;
class OopMapSet;
+class AOTCodeEntry;
+class AOTCodeReader;
// ciEnv
//
@@ -59,9 +62,10 @@ class ciEnv : StackObj {
DebugInformationRecorder* _debug_info;
Dependencies* _dependencies;
CHeapStringHolder _failure_reason;
+ bool _codecache_full;
bool _inc_decompile_count_on_failure;
- int _compilable;
bool _break_at_compile;
+ int _compilable;
int _num_inlined_bytecodes;
CompileTask* _task; // faster access to CompilerThread::task
CompileLog* _log; // faster access to CompilerThread::log
@@ -232,6 +236,10 @@ class ciEnv : StackObj {
if (o == nullptr) return nullptr;
return get_metadata(o)->as_method_data();
}
+ ciMetadata* get_method_counters(MethodCounters* o) {
+ if (o == nullptr) return nullptr;
+ return get_metadata((Metadata*)o);
+ }
ciMethod* get_method_from_handle(Method* method);
@@ -245,6 +253,7 @@ class ciEnv : StackObj {
ciInstanceKlass* declared_holder = get_instance_klass_for_declared_method_holder(holder);
return _factory->get_unloaded_method(declared_holder, name, signature, accessor);
}
+
InstanceKlass::ClassState get_cached_init_state(uint id) {
return (InstanceKlass::ClassState)_factory->cached_init_state(id);
}
@@ -303,6 +312,12 @@ class ciEnv : StackObj {
// Helper routine for determining the validity of a compilation with
// respect to method dependencies (e.g. concurrent class loading).
void validate_compile_task_dependencies(ciMethod* target);
+
+ // Helper routines to factor out common code used by routines that register a method
+ // i.e. register_aot_method() and register_method()
+ bool is_compilation_valid(JavaThread* thread, ciMethod* target, bool install_code, bool is_loading_aot_code, bool aot_preload);
+ void make_code_usable(JavaThread* thread, ciMethod* target, bool aot_preload, int entry_bci, AOTCodeEntry* aot_code_entry, nmethod* nm);
+
public:
enum {
MethodCompilable,
@@ -331,6 +346,8 @@ class ciEnv : StackObj {
// Reason this compilation is failing, such as "too many basic blocks".
const char* failure_reason() const { return _failure_reason.get(); }
+ bool codecache_full() const { return _codecache_full; }
+
// Return state of appropriate compatibility
int compilable() { return _compilable; }
@@ -357,6 +374,8 @@ class ciEnv : StackObj {
bool should_retain_local_variables() const {
return _jvmti_can_access_local_variables || _jvmti_can_pop_frame;
}
+ bool jvmti_can_access_local_variables() const { return _jvmti_can_access_local_variables; }
+ bool jvmti_can_pop_frame() const { return _jvmti_can_pop_frame; }
bool jvmti_can_hotswap_or_post_breakpoint() const { return _jvmti_can_hotswap_or_post_breakpoint; }
bool jvmti_can_post_on_exceptions() const { return _jvmti_can_post_on_exceptions; }
bool jvmti_can_get_owned_monitor_info() const { return _jvmti_can_get_owned_monitor_info; }
@@ -375,6 +394,15 @@ class ciEnv : StackObj {
int comp_level(); // task()->comp_level()
int compile_id(); // task()->compile_id()
+#if INCLUDE_CDS
+ // Register method loaded from AOT code cache
+ nmethod* register_aot_method(JavaThread* thread,
+ ciMethod* target,
+ AbstractCompiler* compiler,
+ nmethod* archived_nm,
+ AOTCodeReader* aot_code_reader,
+ bool install_code);
+#endif
// Register the result of a compilation.
void register_method(ciMethod* target,
int entry_bci,
@@ -386,11 +414,14 @@ class ciEnv : StackObj {
ExceptionHandlerTable* handler_table,
ImplicitExceptionTable* inc_table,
AbstractCompiler* compiler,
+ bool has_clinit_barriers,
+ bool for_aot_preload,
bool has_unsafe_access,
bool has_wide_vectors,
bool has_monitors,
bool has_scoped_access,
- int immediate_oops_patched);
+ int immediate_oops_patched,
+ bool install_code);
// Access to certain well known ciObjects.
#define VM_CLASS_FUNC(name, ignore_s) \
@@ -476,6 +507,7 @@ class ciEnv : StackObj {
void report_failure(const char* reason); // Report failure immediately
void record_method_not_compilable(const char* reason, bool all_tiers = false);
void record_out_of_memory_failure();
+ void record_codecache_full();
// RedefineClasses support
void metadata_do(MetadataClosure* f) { _factory->metadata_do(f); }
@@ -532,6 +564,9 @@ class ciEnv : StackObj {
void process_invokedynamic(const constantPoolHandle &cp, int index, JavaThread* thread);
void process_invokehandle(const constantPoolHandle &cp, int index, JavaThread* thread);
void find_dynamic_call_sites();
+
+ bool is_aot_compile() const NOT_CDS_RETURN_(false);
+ InstanceKlass::ClassState compute_init_state_for_aot_compile(InstanceKlass* ik) NOT_CDS_RETURN_((InstanceKlass::ClassState)0);
};
#endif // SHARE_CI_CIENV_HPP
diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp
index 08f6a692d1b..e31e88217aa 100644
--- a/src/hotspot/share/ci/ciField.cpp
+++ b/src/hotspot/share/ci/ciField.cpp
@@ -369,6 +369,9 @@ ciConstant ciField::constant_value() {
if (FoldStableValues && is_stable() && _constant_value.is_null_or_zero()) {
return ciConstant();
}
+ if (CURRENT_ENV->is_aot_compile()) { // Restrict only when we generate AOT code
+ return ciConstant();
+ }
return _constant_value;
}
@@ -382,6 +385,9 @@ ciConstant ciField::constant_value_of(ciObject* object) {
if (FoldStableValues && is_stable() && field_value.is_null_or_zero()) {
return ciConstant();
}
+ if (CURRENT_ENV->is_aot_compile()) { // Restrict only when we generate AOT code
+ return ciConstant();
+ }
return field_value;
}
diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp
index 5cd14de9107..451da87494c 100644
--- a/src/hotspot/share/ci/ciInstanceKlass.cpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.cpp
@@ -56,14 +56,21 @@ ciInstanceKlass::ciInstanceKlass(Klass* k) :
ciKlass(k)
{
assert(get_Klass()->is_instance_klass(), "wrong type");
- assert(get_instanceKlass()->is_loaded(), "must be at least loaded");
+
InstanceKlass* ik = get_instanceKlass();
+#ifdef ASSERT
+ if (!ik->is_loaded()) {
+ ResourceMark rm;
+ assert(false, "must be at least loaded: %s", ik->name()->as_C_string());
+ }
+#endif // ASSERT
+
AccessFlags access_flags = ik->access_flags();
_flags = ciFlags(access_flags);
_has_finalizer = ik->has_finalizer();
_has_subklass = flags().is_final() ? subklass_false : subklass_unknown;
- _init_state = ik->init_state();
+ _init_state = compute_init_state(ik);
_has_nonstatic_fields = ik->has_nonstatic_fields();
_has_nonstatic_concrete_methods = ik->has_nonstatic_concrete_methods();
_is_hidden = ik->is_hidden();
@@ -147,11 +154,28 @@ InstanceKlass::ClassState ciInstanceKlass::compute_init_state() {
// Return cached init state of shared klass
ciEnv* env = CURRENT_ENV;
assert(env->task() != nullptr, "only calls from compilation are expected here");
- return env->get_cached_init_state(ident());
+ if (env->is_aot_compile()) {
+ GUARDED_VM_ENTRY( return env->compute_init_state_for_aot_compile(get_instanceKlass()); )
+ } else {
+ return env->get_cached_init_state(ident());
+ }
}
return _init_state;
}
+InstanceKlass::ClassState ciInstanceKlass::compute_init_state(InstanceKlass* ik) {
+ ASSERT_IN_VM;
+#if INCLUDE_CDS
+ ciEnv* env = CURRENT_ENV;
+ if (env != nullptr && env->is_aot_compile()) {
+ return env->compute_init_state_for_aot_compile(ik);
+ } else
+#endif
+ {
+ return ik->init_state();
+ }
+}
+
// ------------------------------------------------------------------
// ciInstanceKlass::compute_shared_has_subklass
bool ciInstanceKlass::compute_shared_has_subklass() {
diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp
index b2522697a0d..c2ac44bb9bc 100644
--- a/src/hotspot/share/ci/ciInstanceKlass.hpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.hpp
@@ -126,6 +126,8 @@ class ciInstanceKlass : public ciKlass {
void compute_nonstatic_fields_impl(const GrowableArray* super_declared_fields, const GrowableArray* super_fields);
bool compute_has_trusted_loader();
+ static InstanceKlass::ClassState compute_init_state(InstanceKlass* ik);
+
public:
// Has this klass been initialized?
bool is_initialized() {
diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp
index 5e9ca7e8251..1db3f606e0e 100644
--- a/src/hotspot/share/ci/ciMethod.cpp
+++ b/src/hotspot/share/ci/ciMethod.cpp
@@ -34,6 +34,7 @@
#include "ci/ciSymbols.hpp"
#include "ci/ciUtilities.inline.hpp"
#include "classfile/vmIntrinsics.hpp"
+#include "code/aotCodeCache.hpp"
#include "compiler/abstractCompiler.hpp"
#include "compiler/compilerDefinitions.inline.hpp"
#include "compiler/compilerOracle.hpp"
@@ -146,6 +147,7 @@ ciMethod::ciMethod(const methodHandle& h_m, ciInstanceKlass* holder) :
constantPoolHandle cpool(Thread::current(), h_m->constants());
_signature = new (env->arena()) ciSignature(_holder, cpool, sig_symbol);
_method_data = nullptr;
+ _method_data_recorded = nullptr;
// Take a snapshot of these values, so they will be commensurate with the MDO.
if (ProfileInterpreter || CompilerConfig::is_c1_profiling()) {
int invcnt = h_m->interpreter_invocation_count();
@@ -177,6 +179,7 @@ ciMethod::ciMethod(ciInstanceKlass* holder,
_name( name),
_holder( holder),
_method_data( nullptr),
+ _method_data_recorded( nullptr),
_method_blocks( nullptr),
_intrinsic_id( vmIntrinsics::_none),
_inline_instructions_size(-1),
@@ -1061,15 +1064,19 @@ bool ciMethod::has_member_arg() const {
//
// Generate new MethodData* objects at compile time.
// Return true if allocation was successful or no MDO is required.
-bool ciMethod::ensure_method_data(const methodHandle& h_m) {
+bool ciMethod::ensure_method_data(const methodHandle& h_m, bool training_data_only) {
EXCEPTION_CONTEXT;
if (is_native() || is_abstract() || h_m()->is_accessor()) {
return true;
}
if (h_m()->method_data() == nullptr) {
- Method::build_profiling_method_data(h_m, THREAD);
- if (HAS_PENDING_EXCEPTION) {
- CLEAR_PENDING_EXCEPTION;
+ if (training_data_only) {
+ Method::install_training_method_data(h_m);
+ } else {
+ Method::build_profiling_method_data(h_m, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ }
}
}
if (h_m()->method_data() != nullptr) {
@@ -1082,12 +1089,12 @@ bool ciMethod::ensure_method_data(const methodHandle& h_m) {
}
// public, retroactive version
-bool ciMethod::ensure_method_data() {
+bool ciMethod::ensure_method_data(bool training_data_only) {
bool result = true;
if (_method_data == nullptr || _method_data->is_empty()) {
GUARDED_VM_ENTRY({
methodHandle mh(Thread::current(), get_Method());
- result = ensure_method_data(mh);
+ result = ensure_method_data(mh, training_data_only);
});
}
return result;
@@ -1098,22 +1105,44 @@ bool ciMethod::ensure_method_data() {
// ciMethod::method_data
//
ciMethodData* ciMethod::method_data() {
+#if INCLUDE_CDS
+ if (CURRENT_ENV->is_aot_compile() && CURRENT_ENV->task()->comp_level() == CompLevel_full_optimization) {
+ if (_method_data_recorded == nullptr) {
+ VM_ENTRY_MARK;
+ methodHandle h_m(thread, get_Method());
+ MethodTrainingData* mtd = MethodTrainingData::find(h_m);
+ MethodData* mdo = (mtd != nullptr ? mtd->final_profile() : nullptr);
+ int comp_id = CURRENT_ENV->task()->compile_id();
+ if (mdo == nullptr) {
+ ResourceMark rm;
+ log_debug(aot, compilation)("%d: No profile for %s", comp_id, h_m->name_and_sig_as_C_string());
+ _method_data_recorded = CURRENT_ENV->get_empty_methodData();
+ } else {
+ _method_data_recorded = CURRENT_ENV->get_method_data(mdo);
+ _method_data_recorded->load_data();
+ {
+ ResourceMark rm;
+ log_debug(aot, compilation)("%d: Recorded profile " PTR_FORMAT " for %s", comp_id, p2i(mdo), h_m->name_and_sig_as_C_string());
+ }
+ }
+ }
+ assert(_method_data_recorded != nullptr, "");
+ return _method_data_recorded;
+ }
+#endif
if (_method_data != nullptr) {
return _method_data;
}
VM_ENTRY_MARK;
- ciEnv* env = CURRENT_ENV;
- Thread* my_thread = JavaThread::current();
- methodHandle h_m(my_thread, get_Method());
-
- if (h_m()->method_data() != nullptr) {
- _method_data = CURRENT_ENV->get_method_data(h_m()->method_data());
+ methodHandle h_m(thread, get_Method());
+ MethodData* mdo = h_m()->method_data();
+ if (mdo != nullptr) {
+ _method_data = CURRENT_ENV->get_method_data(mdo);
_method_data->load_data();
} else {
_method_data = CURRENT_ENV->get_empty_methodData();
}
return _method_data;
-
}
// ------------------------------------------------------------------
@@ -1131,12 +1160,15 @@ ciMethodData* ciMethod::method_data_or_null() {
// ------------------------------------------------------------------
// ciMethod::ensure_method_counters
//
-MethodCounters* ciMethod::ensure_method_counters() {
+ciMetadata* ciMethod::ensure_method_counters() {
check_is_loaded();
VM_ENTRY_MARK;
methodHandle mh(THREAD, get_Method());
- MethodCounters* method_counters = mh->get_method_counters(CHECK_NULL);
- return method_counters;
+ MethodCounters* method_counters = mh->get_method_counters(THREAD);
+ if (method_counters != nullptr) {
+ return CURRENT_ENV->get_method_counters(method_counters);
+ }
+ return nullptr;
}
// ------------------------------------------------------------------
@@ -1214,7 +1246,7 @@ int ciMethod::inline_instructions_size() {
methodHandle top_level_mh(Thread::current(), CURRENT_ENV->task()->method());
MethodTrainingData* mtd = MethodTrainingData::find(top_level_mh);
if (mtd != nullptr) {
- CompileTrainingData* ctd = mtd->last_toplevel_compile(level);
+ CompileTrainingData* ctd = mtd->compile_data_for_aot_code(level);
if (ctd != nullptr) {
methodHandle mh(Thread::current(), get_Method());
MethodTrainingData* this_mtd = MethodTrainingData::find(mh);
@@ -1233,7 +1265,8 @@ int ciMethod::inline_instructions_size() {
GUARDED_VM_ENTRY(
nmethod* code = get_Method()->code();
if (code != nullptr && (code->comp_level() == CompLevel_full_optimization)) {
- int isize = code->insts_end() - code->verified_entry_point() - code->skipped_instructions_size();
+ int isize = code->is_aot() ? code->aot_code_entry()->inline_instructions_size()
+ : code->inline_instructions_size();
_inline_instructions_size = isize > 0 ? isize : 0;
} else {
_inline_instructions_size = 0;
@@ -1274,6 +1307,19 @@ bool ciMethod::is_not_reached(int bci) {
// ------------------------------------------------------------------
// ciMethod::was_never_executed
bool ciMethod::was_executed_more_than(int times) {
+#if INCLUDE_CDS
+ if (CURRENT_ENV->is_aot_compile()) { // Look only on training data
+ // Invocation counter is reset when the Method* is compiled.
+ // If the method has compiled code we therefore assume it has
+ // been executed more than n times.
+ if (is_accessor() || is_empty() || has_compiled_code()) {
+ // interpreter doesn't bump invocation counter of trivial methods
+ // compiler does not bump invocation counter of compiled methods
+ return true;
+ }
+ return (method_data()->invocation_count() > times);
+ }
+#endif
VM_ENTRY_MARK;
return get_Method()->was_executed_more_than(times);
}
diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp
index f5dee91e725..f690cbcf62f 100644
--- a/src/hotspot/share/ci/ciMethod.hpp
+++ b/src/hotspot/share/ci/ciMethod.hpp
@@ -74,7 +74,8 @@ class ciMethod : public ciMetadata {
ciInstanceKlass* _holder;
ciSignature* _signature;
ciMethodData* _method_data;
- ciMethodBlocks* _method_blocks;
+ ciMethodData* _method_data_recorded;
+ ciMethodBlocks* _method_blocks;
// Code attributes.
int _code_size;
@@ -119,7 +120,7 @@ class ciMethod : public ciMetadata {
void load_code();
- bool ensure_method_data(const methodHandle& h_m);
+ bool ensure_method_data(const methodHandle& h_m, bool training_data_only);
void code_at_put(int bci, Bytecodes::Code code) {
Bytecodes::check(code);
@@ -320,8 +321,8 @@ class ciMethod : public ciMetadata {
bool has_unloaded_classes_in_signature();
bool is_klass_loaded(int refinfo_index, Bytecodes::Code bc, bool must_be_resolved) const;
bool check_call(int refinfo_index, bool is_static) const;
- bool ensure_method_data(); // make sure it exists in the VM also
- MethodCounters* ensure_method_counters();
+ bool ensure_method_data(bool training_data_only = false); // make sure it exists in the VM also
+ ciMetadata* ensure_method_counters();
int inline_instructions_size();
int scale_count(int count, float prof_factor = 1.); // make MDO count commensurate with IIC
diff --git a/src/hotspot/share/ci/ciObject.cpp b/src/hotspot/share/ci/ciObject.cpp
index 23696b6b70a..91de0c9ee0c 100644
--- a/src/hotspot/share/ci/ciObject.cpp
+++ b/src/hotspot/share/ci/ciObject.cpp
@@ -202,20 +202,24 @@ void ciObject::add_to_constant_value_cache(int off, ciConstant val) {
// ------------------------------------------------------------------
// ciObject::should_be_constant()
bool ciObject::should_be_constant() {
- if (ScavengeRootsInCode >= 2) return true; // force everybody to be a constant
- if (is_null_object()) return true;
-
ciEnv* env = CURRENT_ENV;
+ if (ScavengeRootsInCode >= 2 && !env->is_aot_compile()) {
+ return true; // force everybody to be a constant
+ }
+ if (is_null_object()) {
+ return true;
+ }
- // We want Strings and Classes to be embeddable by default since
- // they used to be in the perm world. Not all Strings used to be
- // embeddable but there's no easy way to distinguish the interned
- // from the regulars ones so just treat them all that way.
- if (klass() == env->String_klass() || klass() == env->Class_klass()) {
- return true;
- }
- if (klass()->is_subclass_of(env->MethodHandle_klass()) ||
- klass()->is_subclass_of(env->CallSite_klass())) {
+ // We want Strings and Classes to be embeddable by default since
+ // they used to be in the perm world. Not all Strings used to be
+ // embeddable but there's no easy way to distinguish the interned
+ // from the regulars ones so just treat them all that way.
+ if (klass() == env->String_klass() || klass() == env->Class_klass()) {
+ return true;
+ }
+ if ((klass()->is_subclass_of(env->MethodHandle_klass()) ||
+ klass()->is_subclass_of(env->CallSite_klass())) &&
+ !env->is_aot_compile()) { // For now disable it when compiling AOT code.
// We want to treat these aggressively.
return true;
}
diff --git a/src/hotspot/share/ci/ciObjectFactory.cpp b/src/hotspot/share/ci/ciObjectFactory.cpp
index 3891c4e6ee9..c05dbbc4162 100644
--- a/src/hotspot/share/ci/ciObjectFactory.cpp
+++ b/src/hotspot/share/ci/ciObjectFactory.cpp
@@ -113,7 +113,7 @@ ciObjectFactory::ciObjectFactory(Arena* arena,
ciMetadata* obj = _ci_metadata.at(i);
if (obj->is_loaded() && obj->is_instance_klass()) {
ciInstanceKlass* cik = obj->as_instance_klass();
- precond(cik->is_shared());
+ assert(cik->is_shared(), "only shared instances are expected here");
InstanceKlass::ClassState current_state = cik->_init_state;
InstanceKlass::ClassState state = InstanceKlass::fully_initialized;
if (current_state != state) {
@@ -182,10 +182,10 @@ void ciObjectFactory::init_shared_objects() {
ciEnv::_null_object_instance = new (_arena) ciNullObject();
init_ident_of(ciEnv::_null_object_instance);
-#define VM_CLASS_DEFN(name, ignore_s) \
- if (vmClasses::name##_is_loaded()) \
- ciEnv::_##name = get_metadata(vmClasses::name())->as_instance_klass();
-
+#define VM_CLASS_DEFN(name, ignore_s) \
+ if (vmClasses::name##_is_loaded()) { \
+ ciEnv::_##name = get_metadata(vmClasses::name())->as_instance_klass(); \
+ }
VM_CLASSES_DO(VM_CLASS_DEFN)
#undef VM_CLASS_DEFN
@@ -267,7 +267,9 @@ ciObject* ciObjectFactory::get(oop key) {
NonPermObject* &bucket = find_non_perm(keyHandle);
if (bucket != nullptr) {
- return bucket->object();
+ ciObject* obj = bucket->object();
+ notice_new_object(obj);
+ return obj;
}
// The ciObject does not yet exist. Create it and insert it
@@ -283,6 +285,7 @@ ciObject* ciObjectFactory::get(oop key) {
return new_object;
}
+#if INCLUDE_CDS
void ciObjectFactory::notice_new_object(ciBaseObject* new_object) {
if (TrainingData::need_data()) {
ciEnv* env = ciEnv::current();
@@ -295,6 +298,7 @@ void ciObjectFactory::notice_new_object(ciBaseObject* new_object) {
}
}
}
+#endif
int ciObjectFactory::metadata_compare(Metadata* const& key, ciMetadata* const& elt) {
Metadata* value = elt->constant_encoding();
@@ -378,7 +382,9 @@ ciMetadata* ciObjectFactory::get_metadata(Metadata* key) {
notice_new_object(new_object);
return new_object;
}
- return _ci_metadata.at(index)->as_metadata();
+ ciMetadata* metadata = _ci_metadata.at(index)->as_metadata();
+ notice_new_object(metadata);
+ return metadata;
}
// ------------------------------------------------------------------
@@ -453,9 +459,11 @@ ciMetadata* ciObjectFactory::create_new_metadata(Metadata* o) {
ciInstanceKlass* holder = env->get_instance_klass(h_m()->method_holder());
return new (arena()) ciMethod(h_m, holder);
} else if (o->is_methodData()) {
- // Hold methodHandle alive - might not be necessary ???
- methodHandle h_m(THREAD, ((MethodData*)o)->method());
+ // Callers ciMethod::ensure_method_data() and ::method_data() have MH already.
return new (arena()) ciMethodData((MethodData*)o);
+ } else if (o->is_methodCounters()) {
+ // Caller ciMethod::ensure_method_counters() has MH already.
+ return new (arena()) ciMetadata(o);
}
// The Metadata* is of some type not supported by the compiler interface.
diff --git a/src/hotspot/share/ci/ciObjectFactory.hpp b/src/hotspot/share/ci/ciObjectFactory.hpp
index d495bd83e3b..ff216267139 100644
--- a/src/hotspot/share/ci/ciObjectFactory.hpp
+++ b/src/hotspot/share/ci/ciObjectFactory.hpp
@@ -114,7 +114,7 @@ class ciObjectFactory : public ArenaObj {
static ciSymbol* vm_symbol_at(vmSymbolID index);
// Called on every new object made.
- void notice_new_object(ciBaseObject* new_object);
+ void notice_new_object(ciBaseObject* new_object) NOT_CDS_RETURN;
// Get the ciMethod representing an unloaded/unfound method.
ciMethod* get_unloaded_method(ciInstanceKlass* holder,
diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp
index 83cb354651d..741e095bb6c 100644
--- a/src/hotspot/share/ci/ciReplay.cpp
+++ b/src/hotspot/share/ci/ciReplay.cpp
@@ -809,7 +809,7 @@ class CompileReplay : public StackObj {
}
replay_state = this;
CompileBroker::compile_method(methodHandle(THREAD, method), entry_bci, comp_level,
- 0, CompileTask::Reason_Replay, THREAD);
+ 0, nullptr, CompileTask::Reason_Replay, THREAD);
replay_state = nullptr;
}
diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp
index d415fe64bac..d974d65772b 100644
--- a/src/hotspot/share/classfile/classLoaderDataShared.cpp
+++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp
@@ -122,7 +122,7 @@ void ArchivedClassLoaderData::remove_unshareable_info() {
}
void ArchivedClassLoaderData::restore(ClassLoaderData* loader_data, bool do_entries, bool do_oops) {
- assert(CDSConfig::is_using_archive(), "must be");
+ assert(CDSConfig::is_using_full_module_graph(), "must be");
assert_valid(loader_data);
if (_modules != nullptr) { // Could be null if we have archived no modules for platform/system loaders
ModuleEntryTable* modules = loader_data->modules();
@@ -146,7 +146,7 @@ void ArchivedClassLoaderData::restore(ClassLoaderData* loader_data, bool do_entr
}
void ArchivedClassLoaderData::clear_archived_oops() {
- assert(CDSConfig::is_using_archive(), "must be");
+ assert(!CDSConfig::is_using_full_module_graph(), "must be");
if (_modules != nullptr) {
for (int i = 0; i < _modules->length(); i++) {
_modules->at(i)->clear_archived_oops();
diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp
index c73d15c44d3..456d6843207 100644
--- a/src/hotspot/share/classfile/moduleEntry.cpp
+++ b/src/hotspot/share/classfile/moduleEntry.cpp
@@ -443,7 +443,7 @@ void ModuleEntry::remove_unshareable_info() {
}
void ModuleEntry::load_from_archive(ClassLoaderData* loader_data) {
- assert(CDSConfig::is_using_archive(), "runtime only");
+ assert(CDSConfig::is_using_full_module_graph(), "runtime only");
set_loader_data(loader_data);
JFR_ONLY(INIT_ID(this);)
}
@@ -453,7 +453,7 @@ void ModuleEntry::preload_archived_oops() {
}
void ModuleEntry::restore_archived_oops(ClassLoaderData* loader_data) {
- assert(CDSConfig::is_using_archive(), "runtime only");
+ assert(CDSConfig::is_using_full_module_graph(), "runtime only");
Handle module_handle(Thread::current(), HeapShared::get_root(_archived_module_index, /*clear=*/true));
assert(module_handle.not_null(), "huh");
set_module_handle(loader_data->add_handle(module_handle));
@@ -474,7 +474,7 @@ void ModuleEntry::restore_archived_oops(ClassLoaderData* loader_data) {
}
void ModuleEntry::clear_archived_oops() {
- assert(CDSConfig::is_using_archive(), "runtime only");
+ assert(CDSConfig::is_using_archive() && !CDSConfig::is_using_full_module_graph(), "runtime only");
HeapShared::clear_root(_archived_module_index);
}
@@ -509,7 +509,7 @@ Array* ModuleEntryTable::build_aot_table(ClassLoaderData* loader_d
void ModuleEntryTable::load_archived_entries(ClassLoaderData* loader_data,
Array* archived_modules) {
- assert(CDSConfig::is_using_archive(), "runtime only");
+ assert(CDSConfig::is_using_full_module_graph(), "runtime only");
for (int i = 0; i < archived_modules->length(); i++) {
ModuleEntry* archived_entry = archived_modules->at(i);
@@ -519,7 +519,7 @@ void ModuleEntryTable::load_archived_entries(ClassLoaderData* loader_data,
}
void ModuleEntryTable::restore_archived_oops(ClassLoaderData* loader_data, Array* archived_modules) {
- assert(CDSConfig::is_using_archive(), "runtime only");
+ assert(CDSConfig::is_using_full_module_graph(), "runtime only");
for (int i = 0; i < archived_modules->length(); i++) {
ModuleEntry* archived_entry = archived_modules->at(i);
archived_entry->restore_archived_oops(loader_data);
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp
index 67c64a5c9a2..c7e16fce69d 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.cpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp
@@ -919,7 +919,7 @@ void SystemDictionaryShared::link_all_exclusion_check_candidates(InstanceKlass*
// it can be checked by should_be_excluded_impl().
bool SystemDictionaryShared::should_be_excluded(Klass* k) {
assert(CDSConfig::is_dumping_archive(), "sanity");
- assert(CDSConfig::current_thread_is_vm_or_dumper(), "sanity");
+ // This method could be called during AOT compilation from compiler thread.
if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(k)) {
// We have reached a super type that's already in the base archive. Treat it
diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp
index e79405dc4b4..4d1bb8c8309 100644
--- a/src/hotspot/share/code/aotCodeCache.cpp
+++ b/src/hotspot/share/code/aotCodeCache.cpp
@@ -25,40 +25,75 @@
#include "asm/macroAssembler.hpp"
#include "cds/aotCacheAccess.hpp"
+#include "cds/aotCompressedPointers.hpp"
#include "cds/aotMetaspace.hpp"
#include "cds/cds_globals.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/heapShared.hpp"
-#include "ci/ciUtilities.hpp"
+#include "ci/ciEnv.hpp"
+#include "ci/ciMethod.hpp"
+#include "ci/ciUtilities.inline.hpp"
#include "classfile/javaAssertions.hpp"
+#include "classfile/systemDictionary.hpp"
#include "code/aotCodeCache.hpp"
+#include "code/codeBlob.hpp"
#include "code/codeCache.hpp"
+#include "code/nativeInst.hpp"
+#include "code/oopRecorder.inline.hpp"
+#include "code/relocInfo.hpp"
+#include "compiler/abstractCompiler.hpp"
+#include "compiler/compilationPolicy.hpp"
+#include "compiler/compileBroker.hpp"
+#include "compiler/compilerDefinitions.inline.hpp"
+#include "compiler/compileTask.hpp"
#include "gc/shared/barrierSetAssembler.hpp"
#include "gc/shared/barrierSetNMethod.hpp"
#include "gc/shared/cardTableBarrierSet.hpp"
#include "gc/shared/gcConfig.hpp"
#include "logging/logStream.hpp"
#include "memory/memoryReserver.hpp"
-#include "oops/klass.hpp"
+#include "memory/universe.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/klass.inline.hpp"
+#include "oops/method.inline.hpp"
#include "prims/jvmtiThreadState.hpp"
#include "prims/upcallLinker.hpp"
+#include "runtime/atomicAccess.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/flags/flagSetting.hpp"
#include "runtime/globals_extension.hpp"
+#include "runtime/handles.inline.hpp"
#include "runtime/icache.hpp"
#include "runtime/java.hpp"
+#include "runtime/jniHandles.inline.hpp"
+#include "runtime/mountUnmountDisabler.hpp"
#include "runtime/mutexLocker.hpp"
+#include "runtime/objectMonitorTable.hpp"
#include "runtime/os.inline.hpp"
#include "runtime/sharedRuntime.hpp"
-#include "runtime/stubInfo.hpp"
+#include "runtime/stubCodeGenerator.hpp"
#include "runtime/stubRoutines.hpp"
+#include "runtime/threadIdentifier.hpp"
+#include "runtime/timerTrace.hpp"
#include "utilities/copy.hpp"
+#include "utilities/ostream.hpp"
#ifdef COMPILER1
#include "c1/c1_Runtime1.hpp"
-#endif
+#include "gc/g1/c1/g1BarrierSetC1.hpp"
+#include "gc/shared/c1/barrierSetC1.hpp"
+#if INCLUDE_SHENANDOAHGC
+#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp"
+#endif // INCLUDE_SHENANDOAHGC
+#include "gc/z/c1/zBarrierSetC1.hpp"
+#endif // COMPILER1
#ifdef COMPILER2
+#include "opto/c2_MacroAssembler.hpp"
+#include "opto/parse.hpp"
#include "opto/runtime.hpp"
#endif
+#if INCLUDE_JFR
+#include "jfr/support/jfrIntrinsics.hpp"
+#endif
#if INCLUDE_G1GC
#include "gc/g1/g1BarrierSetRuntime.hpp"
#include "gc/g1/g1HeapRegion.hpp"
@@ -70,6 +105,9 @@
#if INCLUDE_ZGC
#include "gc/z/zBarrierSetRuntime.hpp"
#endif
+#if defined(X86) && !defined(ZERO)
+#include "rdtsc_x86.hpp"
+#endif
#include
#include
@@ -93,6 +131,23 @@ static LogStream& load_failure_log() {
}
}
+// Stream to print AOT code loading info
+static LogStream& load_info_log() {
+ static LogStream log_aot(LogLevel::Info, LogTagSetMapping::tagset());
+ static LogStream log_init(LogLevel::Info, LogTagSetMapping::tagset());
+ return log_init.is_enabled() ? log_init : log_aot;
+}
+
+static elapsedTimer _t_totalLoad;
+static elapsedTimer _t_totalPreload;
+static elapsedTimer _t_totalRegister;
+static elapsedTimer _t_totalFind;
+static elapsedTimer _t_totalStore;
+
+static bool enable_timers() {
+ return CITime || log_is_enabled(Info, aot, codecache, stats);
+}
+
// Report AOT code cache failure and exit VM
// if (AOTMode is `on` and AbortVMOnAOTCodeFailure is default)
// or AbortVMOnAOTCodeFailure is `true`.
@@ -106,20 +161,20 @@ static void report_load_failure() {
if (abort_vm) {
vm_exit_during_initialization("Unable to use AOT Code Cache.", nullptr);
}
- load_failure_log().print_cr("Unable to use AOT Code Cache.");
+ load_failure_log().print_cr("Unable to use AOT Code Cache");
AOTCodeCache::disable_caching();
}
static void report_store_failure() {
if (AbortVMOnAOTCodeFailure) {
- tty->print_cr("Unable to create AOT Code Cache.");
+ tty->print_cr("Unable to create AOT Code Cache");
vm_abort(false);
}
- log_error(aot, codecache, exit)("Unable to create AOT Code Cache.");
+ log_error(aot, codecache, exit)("Unable to create AOT Code Cache");
AOTCodeCache::disable_caching();
}
-// The sequence of AOT code caching flags and parametters settings.
+// The sequence of AOT code caching flags and parameters settings.
//
// 1. The initial AOT code caching flags setting is done
// during call to CDSConfig::check_vm_args_consistency().
@@ -128,14 +183,20 @@ static void report_store_failure() {
// where we set number of compiler threads for AOT assembly phase.
//
// 3. We determine presence of AOT code in AOT Cache in
-// AOTMetaspace::open_static_archive() which is calles
+// AOTMetaspace::get_aot_code_region_size() which is called
// 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.
+// 5. AOTCodeCache::init2() is called after universe_init()
+// when all GC settings are finalized. AOT code cache is
+// verified and AOTCodeAddressTable is populated by
+// external addresses.
+//
+// 6. AOTCodeCache::init3() is called just before
+// continuation_stubs_init() to add more addresses
+// to AOTCodeAddressTable to be used by these stubs.
// Next methods determine which action we do with AOT code depending
// on phase of AOT process: assembly or production.
@@ -156,21 +217,48 @@ 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
+bool AOTCodeCache::is_dumping_code() {
+ return AOTCodeCaching && is_on_for_dump();
+}
+
+bool AOTCodeCache::is_using_code() {
+ return AOTCodeCaching && is_on_for_use();
+}
+
+// This is used before AOTCodeCache is initialized
+// but after AOT (CDS) Cache flags consistency is checked.
+bool AOTCodeCache::maybe_dumping_code() {
+ return AOTCodeCaching && CDSConfig::is_dumping_final_static_archive();
+}
+
+// Next methods could be called regardless of AOT code cache status.
+// Initially they are called during AOT flags parsing and finalized
// in AOTCodeCache::initialize().
void AOTCodeCache::enable_caching() {
+#if defined(ZERO) || !(defined(AMD64) || defined(AARCH64) || defined(RISCV64))
+ load_info_log().print_cr("AOT Code Cache is not supported on this platform");
+ disable_caching();
+#else
+ if (Arguments::is_valhalla_enabled()) {
+ // Disable AOT code caching when Valhalla is enabled.
+ load_info_log().print_cr("AOT Code Caching is not supported with Valhalla");
+ disable_caching();
+ return;
+ }
+ FLAG_SET_ERGO_IF_DEFAULT(AOTCodeCaching, true);
FLAG_SET_ERGO_IF_DEFAULT(AOTStubCaching, true);
FLAG_SET_ERGO_IF_DEFAULT(AOTAdapterCaching, true);
+#endif
}
void AOTCodeCache::disable_caching() {
+ FLAG_SET_ERGO(AOTCodeCaching, false);
FLAG_SET_ERGO(AOTStubCaching, false);
FLAG_SET_ERGO(AOTAdapterCaching, false);
}
bool AOTCodeCache::is_caching_enabled() {
- return AOTStubCaching || AOTAdapterCaching;
+ return AOTCodeCaching || AOTStubCaching || AOTAdapterCaching;
}
static uint32_t encode_id(AOTCodeEntry::Kind kind, int id) {
@@ -200,47 +288,41 @@ uint AOTCodeCache::max_aot_code_size() {
return _max_aot_code_size;
}
+bool AOTCodeCache::is_code_load_thread_on() {
+ return AOTCodeCaching && !CDSConfig::is_dumping_final_static_archive();
+}
+
// It is called from AOTMetaspace::initialize_shared_spaces()
// which is called from universe_init().
-// At this point all AOT class linking seetings are finilized
+// At this point all AOT class linking settings are finalized
// and AOT cache is open so we can map AOT code region.
void AOTCodeCache::initialize() {
-#if defined(ZERO) || !(defined(AMD64) || defined(AARCH64) || defined(RISCV64))
- log_info(aot, codecache, init)("AOT Code Cache is not supported on this platform.");
- disable_caching();
- return;
-#else
- if (FLAG_IS_DEFAULT(AOTCache)) {
- log_info(aot, codecache, init)("AOT Code Cache is not used: AOTCache is not specified.");
- disable_caching();
- return; // AOTCache must be specified to dump and use AOT code
+ if (!is_caching_enabled()) {
+ load_info_log().print_cr("AOT Code Cache is not used: disabled");
+ return;
}
+ assert(!FLAG_IS_DEFAULT(AOTCache), "AOTCache should be specified");
if (VerifyOops && ValueTypePassFieldsAsArgs) {
- log_info(aot, codecache, init)("AOT Adapter Caching is not supported with VerifyOops + ValueTypePassFieldsAsArgs.");
+ load_info_log().print_cr("AOT Adapter Caching is not supported with VerifyOops + ValueTypePassFieldsAsArgs.");
FLAG_SET_ERGO(AOTAdapterCaching, false);
}
bool is_dumping = false;
bool is_using = false;
if (CDSConfig::is_dumping_final_static_archive() && CDSConfig::is_dumping_aot_linked_classes()) {
- is_dumping = true;
- enable_caching();
is_dumping = is_caching_enabled();
} else if (CDSConfig::is_using_archive() && CDSConfig::is_using_aot_linked_classes()) {
- 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 (!(is_dumping || is_using)) {
+ load_info_log().print_cr("AOT Code Cache is not used");
disable_caching();
- return; // AOT code caching disabled on command line
+ return;
}
+ // Set AOT Code Cache max size when dumping.
_max_aot_code_size = AOTCodeMaxSize;
- if (!FLAG_IS_DEFAULT(AOTCodeMaxSize)) {
+ if (is_dumping && !FLAG_IS_DEFAULT(AOTCodeMaxSize)) {
if (!is_aligned(AOTCodeMaxSize, os::vm_allocation_granularity())) {
_max_aot_code_size = align_up(AOTCodeMaxSize, os::vm_allocation_granularity());
log_debug(aot,codecache,init)("Max AOT Code Cache size is aligned up to %uK", (int)(max_aot_code_size()/K));
@@ -248,7 +330,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");
+ load_info_log().print_cr("AOT Code Cache is empty");
disable_caching();
return;
}
@@ -261,10 +343,21 @@ void AOTCodeCache::initialize() {
return;
}
if (is_dumping) {
- FLAG_SET_DEFAULT(ForceUnreachable, true);
+ // We cannot dump the AOT code if these flags get set wrong.
+ // FoldStableValues=true can compile bad code, folding the wrong values for production runs.
+ // ForceUnreachable=false calls might not reach their targets in the production.
+ // DelayCompilerStubsGeneration=true the address table would fail to mention stubs.
+ // Such settings must be enforced.
+ FLAG_SET_ERGO(FoldStableValues, false);
+ FLAG_SET_ERGO(ForceUnreachable, true);
+#if defined(COMPILER2) && !defined(PRODUCT)
+ // With PrintLockStatistics=true C2 creates dynamic named counters embedded
+ // into code. We can't record dynamic addresses in AOT external address table
+ // and, as result, can't patch them when loading AOT code.
+ FLAG_SET_ERGO(PrintLockStatistics, false);
+#endif
}
- FLAG_SET_DEFAULT(DelayCompilerStubsGeneration, false);
-#endif // defined(AMD64) || defined(AARCH64) || defined(RISCV64)
+ FLAG_SET_ERGO(DelayCompilerStubsGeneration, false);
}
static AOTCodeCache* opened_cache = nullptr; // Use this until we verify the cache
@@ -277,7 +370,8 @@ void AOTCodeCache::init2() {
if (opened_cache == nullptr) {
return;
}
- if (!opened_cache->verify_config()) {
+ // After Universe initialized
+ if (!opened_cache->verify_config_on_use()) { // Check on AOT code loading
delete opened_cache;
opened_cache = nullptr;
report_load_failure();
@@ -287,23 +381,35 @@ void AOTCodeCache::init2() {
// Report contents of AOT code cache after verification passed
Header* header = opened_cache->_load_header;
if (header != nullptr) { // Loading AOT code
- log_info (aot, codecache, init)("Loaded %u AOT code entries from AOT Code Cache", header->entries_count());
- log_debug(aot, codecache, init)(" Adapters: total=%u", header->adapters_count());
- log_debug(aot, codecache, init)(" Shared Blobs: total=%u", header->shared_blobs_count());
- log_debug(aot, codecache, init)(" StubGen Blobs: total=%d", header->stubgen_blobs_count());
- log_debug(aot, codecache, init)(" C1 Blobs: total=%u", header->C1_blobs_count());
- log_debug(aot, codecache, init)(" C2 Blobs: total=%u", header->C2_blobs_count());
- log_debug(aot, codecache, init)(" AOT code cache size: %u bytes", header->cache_size());
-
// Read strings
- opened_cache->load_strings();
+ if (!opened_cache->load_strings()) {
+ delete opened_cache;
+ opened_cache = nullptr;
+ report_load_failure();
+ return;
+ }
+ uint count = header->entries_count();
+ uint total_count = count + header->preload_entries_count();
+ load_info_log().print_cr("Loaded %u AOT code entries from AOT Code Cache", total_count);
+ LogStreamHandle(Info, aot, codecache, init) log;
+ if (log.is_enabled()) {
+ log.print_cr(" %s: total=%u", aot_code_entry_kind_name[AOTCodeEntry::Adapter], header->adapters_count());
+ log.print_cr(" %s: total=%u", aot_code_entry_kind_name[AOTCodeEntry::SharedBlob], header->shared_blobs_count());
+ log.print_cr(" %s: total=%u", aot_code_entry_kind_name[AOTCodeEntry::C1Blob], header->C1_blobs_count());
+ log.print_cr(" %s: total=%u", aot_code_entry_kind_name[AOTCodeEntry::C2Blob], header->C2_blobs_count());
+ log.print_cr(" %s: total=%u", aot_code_entry_kind_name[AOTCodeEntry::StubGenBlob], header->stubgen_blobs_count());
+ log.print_cr(" %s: total=%u", aot_code_entry_kind_name[AOTCodeEntry::Nmethod], header->nmethods_count());
+ log.print_cr(" AOT code total size: %u bytes", header->cache_size());
+ }
+ // Set code entry table
+ opened_cache->set_load_entries();
} else if (opened_cache->for_dump()) {
init_C_strings_caching();
}
// initialize aot runtime constants as appropriate to this runtime
AOTRuntimeConstants::initialize_from_runtime();
- // initialize the table of external routines so we can save
+ // initialize the table of external routines and initial stubs so we can save
// generated code blobs that reference them
AOTCodeAddressTable* table = opened_cache->_table;
assert(table != nullptr, "should be initialized already");
@@ -311,6 +417,19 @@ void AOTCodeCache::init2() {
// Now cache and address table are ready for AOT code generation
_cache = opened_cache;
+
+ if (is_using_code()) {
+ FLAG_SET_ERGO_IF_DEFAULT(AOTCompileEagerly, true);
+ }
+ // Set ClassInitBarrierMode after all checks since it affects code generation
+ if (is_dumping_code()) {
+ FLAG_SET_ERGO_IF_DEFAULT(ClassInitBarrierMode, 1);
+ } else {
+ if (ClassInitBarrierMode > 0) {
+ log_info(aot, codecache, init)("Set ClassInitBarrierMode to 0 because AOT Code caching is off");
+ }
+ FLAG_SET_ERGO(ClassInitBarrierMode, 0);
+ }
}
bool AOTCodeCache::open_cache(bool is_dumping, bool is_using) {
@@ -334,18 +453,86 @@ void AOTCodeCache::init3() {
AOTCodeAddressTable* table = opened_cache->_table;
assert(table != nullptr, "should be initialized already");
table->init_extrs2();
+
+ // Verify extrs lengths in production run
+ Header* header = opened_cache->load_header();
+ if (header != nullptr) {
+ uint cached_extrs2_length = header->extrs2_length();
+ uint extrs2_len = table->extrs2_length();
+ if (cached_extrs2_length != extrs2_len) {
+ load_failure_log().print_cr("AOT Code Cache disabled: different extrs2_length %u vs current %u",
+ cached_extrs2_length, extrs2_len);
+ opened_cache->set_failed();
+ report_load_failure();
+ return;
+ }
+ }
}
-void AOTCodeCache::dump() {
- if (is_on()) {
- assert(is_on_for_dump(), "should be called only when dumping AOT code");
+static uint _dump_jvmti_state = 0;
+
+bool AOTCodeCache::dump() {
+ if (is_on_for_dump()) { // It will also return false if there was failure
+ _dump_jvmti_state = get_jvmti_state(); // call before Compile_lock
MutexLocker ml(Compile_lock);
- _cache->finish_write();
+ return _cache->finish_write();
+ }
+ return false;
+}
+
+class CachedCodeDirectory {
+public:
+ uint _aot_code_size;
+ char* _aot_code_data;
+
+ void set_aot_code_data(uint size, char* aot_data) {
+ _aot_code_size = size;
+ AOTCacheAccess::set_pointer(&_aot_code_data, aot_data);
}
+
+ static CachedCodeDirectory* create();
+};
+
+// Storing AOT code in the AOT code region (ac) of AOT Cache:
+//
+// [1] Use CachedCodeDirectory to keep track of all of data related to AOT code.
+// E.g., you can build a hash table to record what methods have been archived.
+//
+// [2] Memory for all data for AOT code, including CachedCodeDirectory, should be
+// allocated using AOTCacheAccess::allocate_aot_code_region().
+//
+// [3] CachedCodeDirectory must be the very first allocation.
+//
+// [4] Two kinds of pointer can be stored:
+// - A pointer p that points to metadata. AOTCacheAccess::can_generate_aot_code(p) must return true.
+// - A pointer to a buffer returned by AOTCacheAccess::allocate_aot_code_region().
+// (It's OK to point to an interior location within this buffer).
+// Such pointers must be stored using AOTCacheAccess::set_pointer()
+//
+// The buffers allocated by AOTCacheAccess::allocate_aot_code_region() are in a contiguous region. At runtime, this
+// region is mapped to the process address space. All the pointers in this buffer are relocated as necessary
+// (e.g., to account for the runtime location of the CodeCache).
+//
+// This is always at the very beginning of the mmapped CDS "ac" (AOT code) region
+static CachedCodeDirectory* _aot_code_directory = nullptr;
+
+CachedCodeDirectory* CachedCodeDirectory::create() {
+ assert(AOTCacheAccess::is_aot_code_region_empty(), "must be");
+ CachedCodeDirectory* dir = (CachedCodeDirectory*)AOTCacheAccess::allocate_aot_code_region(sizeof(CachedCodeDirectory));
+ dir->set_aot_code_data(0, nullptr);
+ return dir;
}
#define DATA_ALIGNMENT HeapWordSize
+AOTCodeCache::~AOTCodeCache() {
+ if (_reserved_space.is_reserved()) {
+ AOTCacheAccess::unmap_aot_code_region();
+ MemoryReserver::release(_reserved_space);
+ _reserved_space = {};
+ }
+}
+
AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) :
_load_header(nullptr),
_load_buffer(nullptr),
@@ -362,34 +549,63 @@ AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) :
_load_entries(nullptr),
_search_entries(nullptr),
_store_entries(nullptr),
- _store_entries_cnt(0)
+ _store_entries_cnt(0),
+ _compile_id(0),
+ _comp_level(0)
{
// Read header at the begining of cache
if (_for_use) {
// Read cache
size_t load_size = AOTCacheAccess::get_aot_code_region_size();
+ size_t header_size = sizeof(CachedCodeDirectory) + sizeof(Header);
+ assert(load_size >= header_size, "aot code region is too small: %zu < %zu", load_size, header_size);
ReservedSpace rs = MemoryReserver::reserve(load_size, mtCode);
if (!rs.is_reserved()) {
- log_warning(aot, codecache, init)("Failed to reserved %u bytes of memory for mapping AOT code region into AOT Code Cache", (uint)load_size);
+ log_warning(aot, codecache, init)("Failed to reserve %u bytes of memory for mapping AOT code region from AOT Cache", (uint)load_size);
set_failed();
return;
}
+ _reserved_space = rs;
if (!AOTCacheAccess::map_aot_code_region(rs)) {
- log_warning(aot, codecache, init)("Failed to read/mmap cached code region into AOT Code Cache");
+ log_warning(aot, codecache, init)("Failed to read/mmap AOT code region from AOT Cache");
+ set_failed();
+ return;
+ }
+ _aot_code_directory = (CachedCodeDirectory*)rs.base();
+
+ // Do several checks to make sure cache is valid.
+
+ _load_size = _aot_code_directory->_aot_code_size;
+ if (_load_size < (uint)sizeof(Header)) {
+ load_failure_log().print_cr("AOT Code Cache disabled: _aot_code_size is small %u", _load_size);
set_failed();
return;
}
- _load_size = (uint)load_size;
- _load_buffer = (char*)rs.base();
- assert(is_aligned(_load_buffer, DATA_ALIGNMENT), "load_buffer is not aligned");
- log_debug(aot, codecache, init)("Mapped %u bytes at address " INTPTR_FORMAT " at AOT Code Cache", _load_size, p2i(_load_buffer));
+ _load_buffer = _aot_code_directory->_aot_code_data;
+ if (!is_aligned(_load_buffer, DATA_ALIGNMENT)) {
+ load_failure_log().print_cr("AOT Code Cache disabled: _aot_code_data is not aligned");
+ set_failed();
+ return;
+ }
+ if (p2u(_load_buffer) < p2u(rs.base())) {
+ load_failure_log().print_cr("AOT Code Cache disabled: _aot_code_data " INTPTR_FORMAT " points outside reserved space " INTPTR_FORMAT, p2i(_load_buffer), p2i(rs.base()));
+ set_failed();
+ return;
+ }
+ uintptr_t offset = p2u(_load_buffer) - p2u(rs.base());
+ if (offset < sizeof(CachedCodeDirectory) || offset > load_size || _load_size > load_size - offset) {
+ load_failure_log().print_cr("AOT Code Cache disabled: damaged CachedCodeDirectory data");
+ set_failed();
+ return;
+ }
_load_header = (Header*)addr(0);
if (!_load_header->verify(_load_size)) {
set_failed();
return;
}
+ load_info_log().print_cr("Mapped %u bytes at address " INTPTR_FORMAT " from AOT Code Cache", _load_size, p2i(_load_buffer));
}
if (_for_dump) {
_C_store_buffer = NEW_C_HEAP_ARRAY(char, max_aot_code_size() + DATA_ALIGNMENT, mtCode);
@@ -401,6 +617,14 @@ AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) :
_table = new AOTCodeAddressTable();
}
+void AOTCodeCache::invalidate(AOTCodeEntry* entry) {
+ // Invalidate AOT code during production run.
+ // This could be concurrent execution.
+ if (entry != nullptr && is_on_for_use()) {
+ _cache->invalidate_entry(entry);
+ }
+}
+
void AOTCodeCache::add_stub_entries(StubId stub_id, address start, GrowableArray *entries, int begin_idx) {
EntryId entry_id = StubInfo::entry_base(stub_id);
add_stub_entry(entry_id, start);
@@ -438,8 +662,38 @@ void AOTCodeCache::set_shared_stubs_complete() {
void AOTCodeCache::set_c1_stubs_complete() {
AOTCodeAddressTable* table = addr_table();
+ bool success = true;
if (table != nullptr) {
+ // Called by C1 compiler thread set_c1_stubs_complete() concurrently
+ // modifies external address table while other threads read it.
+ bool for_dump = opened_cache->_for_dump;
+ bool for_use = opened_cache->_for_use;
+ ConditionalMutexLocker mld(Compile_lock, for_dump, // Used during assembly phase
+ Mutex::_safepoint_check_flag);
+ ConditionalMutexLocker mlu(CodeCache_lock, for_use, // Used during production run
+ Mutex::_no_safepoint_check_flag);
+ if (opened_cache->failed()) {
+ return;
+ }
table->set_c1_stubs_complete();
+ Header* header = opened_cache->_load_header;
+ if (header != nullptr) { // Production run
+ uint cached_extrs2_length = header->extrs2_length();
+ uint cached_final_extrs_length = header->final_extrs_length();
+ // During assembly phase C1 may not be used, in such case
+ // extrs2 and final_extrs lengths will be equal.
+ // Do not discard AOT code cache if C1 was enabled only in production run.
+ if ((cached_final_extrs_length != cached_extrs2_length) &&
+ (cached_final_extrs_length != table->extrs_length())) {
+ load_failure_log().print_cr("AOT Code Cache disabled: different final_extrs_length %u vs current %u",
+ cached_final_extrs_length, table->extrs_length());
+ opened_cache->set_failed();
+ success = false;
+ }
+ }
+ }
+ if (!success) {
+ report_load_failure();
}
}
@@ -457,7 +711,22 @@ void AOTCodeCache::set_stubgen_stubs_complete() {
}
}
-void AOTCodeCache::Config::record(uint cpu_features_offset) {
+#ifdef COMPILER2
+static uint get_c2_ea_state() {
+ uint state = 0;
+ bool eliminate_alloc = (DoEscapeAnalysis && EliminateAllocations)
+ || EliminateAutoBox
+ || EnableVectorAggressiveReboxing;
+ bool eliminate_lock = (DoEscapeAnalysis || EliminateNestedLocks)
+ && EliminateLocks;
+ state += DoEscapeAnalysis ? 1 : 0;
+ state += eliminate_alloc ? 2 : 0;
+ state += eliminate_lock ? 4 : 0;
+ return state;
+}
+#endif // COMPILER2
+
+void AOTCodeCache::Config::record() {
#define AOTCODECACHE_SAVE_VAR(type, name) _saved_ ## name = name;
#define AOTCODECACHE_SAVE_FUN(type, name, fun) _saved_ ## name = fun;
@@ -467,6 +736,8 @@ void AOTCodeCache::Config::record(uint cpu_features_offset) {
// Special configs that cannot be checked with macros
_compressedOopBase = CompressedOops::base();
_compressedOopShift = CompressedOops::shift();
+ _compressedKlassBase = CompressedKlassPointers::base();
+ _codeCacheSize = pointer_delta(CodeCache::high_bound(), CodeCache::low_bound(), 1);
#if defined(X86) && !defined(ZERO)
_useUnalignedLoadStores = UseUnalignedLoadStores;
@@ -476,16 +747,92 @@ void AOTCodeCache::Config::record(uint cpu_features_offset) {
_avoidUnalignedAccesses = AvoidUnalignedAccesses;
#endif
- _cpu_features_offset = cpu_features_offset;
+#ifdef COMPILER2
+ _reduceInitialCardMarks = ReduceInitialCardMarks;
+ _c2_ea_state = get_c2_ea_state();
+#endif // COMPILER2
+
+ _jvmti_state = _dump_jvmti_state;
+}
+
+static uint encode_jvmti_state(bool hotswap_or_breakpoint,
+ bool access_local,
+ bool post_on_exceptions,
+ bool pop_frame,
+ bool owned_monitor_info,
+ bool walk_any_space) {
+ return (hotswap_or_breakpoint ? 1u << 0 : 0u) |
+ (access_local ? 1u << 1 : 0u) |
+ (post_on_exceptions ? 1u << 2 : 0u) |
+ (pop_frame ? 1u << 3 : 0u) |
+ (owned_monitor_info ? 1u << 4 : 0u) |
+ (walk_any_space ? 1u << 5 : 0u);
+}
+
+uint AOTCodeCache::get_jvmti_state() {
+ // Get Jvmti capabilities under lock to get consistent values.
+ MutexLocker mu(JvmtiThreadState_lock);
+ return encode_jvmti_state(JvmtiExport::can_hotswap_or_post_breakpoint(),
+ JvmtiExport::can_access_local_variables(),
+ JvmtiExport::can_post_on_exceptions(),
+ JvmtiExport::can_pop_frame(),
+ JvmtiExport::can_get_owned_monitor_info(),
+ JvmtiExport::can_walk_any_space());
+}
+
+bool AOTCodeCache::Header::verify_jvmti_state(ciEnv* env) const {
+ uint cached_state = _config.jvmti_state();
+ // Conservatively require exact match.
+ uint state = encode_jvmti_state(env->jvmti_can_hotswap_or_post_breakpoint(),
+ env->jvmti_can_access_local_variables(),
+ env->jvmti_can_post_on_exceptions(),
+ env->jvmti_can_pop_frame(),
+ env->jvmti_can_get_owned_monitor_info(),
+ env->jvmti_can_walk_any_space());
+ return cached_state == state;
+}
+
+bool AOTCodeCache::verify_jvmti_state(ciEnv* env) {
+ AOTCodeCache* cache = open_for_use();
+ if (cache == nullptr) {
+ return false;
+ }
+ return cache->_load_header->verify_jvmti_state(env);
+}
+
+#ifdef COMPILER2
+bool AOTCodeCache::Header::verify_c2_state() const {
+ uint current_c2_ea_state = get_c2_ea_state();
+ uint cached_c2_ea_state = _config.c2_ea_state();
+ if ((cached_c2_ea_state & ~current_c2_ea_state) != 0) {
+ log_trace(aot, codecache, nmethod)("AOT Tier4 code skipped: incompatible C2 EA state: %u vs current %u", cached_c2_ea_state, current_c2_ea_state);
+ return false;
+ }
+ bool cached_reduce_initial_cm = _config.reduce_initial_cm();
+ if (cached_reduce_initial_cm && !ReduceInitialCardMarks) {
+ log_trace(aot, codecache, nmethod)("AOT Tier4 code skipped: incompatible ReduceInitialCardMarks: %u vs current %u", cached_reduce_initial_cm, ReduceInitialCardMarks);
+ return false;
+ }
+ return true;
}
+#endif // COMPILER2
-bool AOTCodeCache::Config::verify_cpu_features(AOTCodeCache* cache) const {
+bool AOTCodeCache::Header::verify_cpu_features(AOTCodeCache* cache) const {
LogStreamHandle(Debug, aot, codecache, init) log;
- uint offset = _cpu_features_offset;
- uint cpu_features_size = *(uint *)cache->addr(offset);
- assert(cpu_features_size == (uint)VM_Version::cpu_features_size(), "must be");
- offset += sizeof(uint);
+ if (_cpu_features_size != (uint)VM_Version::cpu_features_size()) {
+ if (load_failure_log().is_enabled()) {
+ ResourceMark rm;
+ load_failure_log().print_cr("AOT Code Cache disabled: different cpu features size %d vs current %d",
+ _cpu_features_size, (uint)VM_Version::cpu_features_size());
+ }
+ // Assert in debug VM
+ assert(false, "different cpu features size %d vs current %d",
+ _cpu_features_size, (uint)VM_Version::cpu_features_size());
+ return false;
+ }
+ uint offset = _cpu_features_offset;
+ assert(is_aligned(offset, DATA_ALIGNMENT), "should be");
void* cached_cpu_features_buffer = (void *)cache->addr(offset);
if (log.is_enabled()) {
ResourceMark rm; // required for stringStream::as_string()
@@ -560,13 +907,7 @@ bool check_config(T saved, T current, const char* name) {
}
}
-bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const {
- // check CPU features before checking flags that may be
- // auto-configured in response to them
- if (!verify_cpu_features(cache)) {
- return false;
- }
-
+bool AOTCodeCache::Config::verify() const {
// Tests for config options which might affect validity of adapters,
// stubs or nmethods. Currently we take a pessemistic stand and
// drop the whole cache if any of these are changed.
@@ -592,6 +933,10 @@ bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const {
load_failure_log().print_cr(COMPRESSED_OOPS_HINT);
return false;
}
+ if ((_compressedKlassBase == nullptr || CompressedKlassPointers::base() == nullptr) && (_compressedKlassBase != CompressedKlassPointers::base())) {
+ load_failure_log().print_cr("AOT Code Cache disabled: incompatible CompressedKlassPointers::base(): %p vs current %p", _compressedKlassBase, CompressedKlassPointers::base());
+ return false;
+ }
#if defined(X86) && !defined(ZERO)
// switching off UseUnalignedLoadStores can affect validity of fill
@@ -611,21 +956,131 @@ bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const {
}
#endif // (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
+ uint current_jvmti_state = AOTCodeCache::get_jvmti_state();
+ if (AOTCodeCaching &&_jvmti_state != current_jvmti_state) {
+ load_failure_log().print_cr("AOT nmethod loading disabled: incompatible JVMTI state: %u vs current %u", _jvmti_state, current_jvmti_state);
+ FLAG_SET_ERGO(AOTCodeCaching, false);
+ }
+
return true;
}
+// Layout of AOT code cache
+//
+// Header
+// CPU features data
+// AOT preload code entries 'AOTCodeEntry preload_entries[preload_entries_count]' array
+// AOT preload code
+// AOT entries for adapters, stubs, code 'AOTCodeEntry load_entries[entries_count]' array
+// AOT adapters, stubs, code
+// 'uint search_entries[2 * entries_count]' array
+// C strings data
+// 'uint string_lengths[strings_count]' array
+// const char* strings; all strings
+//
+
+// returns end of section offset
+size_t AOTCodeCache::Header::verify_section(uint offset,
+ uint count,
+ size_t unit_size,
+ size_t low_limit,
+ const char* section_name) const {
+ if ((size_t)offset < low_limit || !is_aligned(offset, DATA_ALIGNMENT)) {
+ load_failure_log().print_cr("AOT Code Cache disabled: %s offset %u is invalid", section_name, offset);
+ return 0;
+ }
+ size_t end_offset = 0;
+ if (count > 0) {
+ size_t ssize = count * unit_size;
+ uint size = (uint)ssize;
+ if (ssize != (size_t)size) { // overflow
+ load_failure_log().print_cr("AOT Code Cache disabled: %s count too big %u", section_name, count);
+ return 0;
+ }
+ if (!contains(offset, size)) {
+ load_failure_log().print_cr("AOT Code Cache disabled: %s offset %u + size %u >= %u AOT Code Cache size", section_name, offset, size, _cache_size);
+ return 0;
+ }
+ end_offset = align_up(offset + ssize, DATA_ALIGNMENT);
+ } else { // count == 0
+ if (offset > _cache_size) {
+ load_failure_log().print_cr("AOT Code Cache disabled: %s offset %u + 0 >= %u AOT Code Cache size", section_name, offset, _cache_size);
+ return 0;
+ }
+ end_offset = offset;
+ }
+ return end_offset;
+}
+
bool AOTCodeCache::Header::verify(uint load_size) const {
if (_version != AOT_CODE_VERSION) {
load_failure_log().print_cr("AOT Code Cache disabled: different AOT Code version %d vs %d recorded in AOT Code header", AOT_CODE_VERSION, _version);
return false;
}
- if (load_size < _cache_size) {
- load_failure_log().print_cr("AOT Code Cache disabled: AOT Code Cache size %d < %d recorded in AOT Code header", load_size, _cache_size);
+ if (load_size != _cache_size) {
+ load_failure_log().print_cr("AOT Code Cache disabled: AOT Code Cache size %u != %u recorded in AOT Code header", load_size, _cache_size);
+ return false;
+ }
+
+ // Check CPU features data
+ uint cpu_offset = cpu_features_offset();
+ uint cpu_size = cpu_features_size();
+ size_t head_end = align_up(sizeof(Header), DATA_ALIGNMENT);
+ if ((size_t)cpu_offset != head_end) {
+ load_failure_log().print_cr("AOT Code Cache disabled: CPU features offset %u is invalid", cpu_offset);
+ return 0;
+ }
+ size_t cpu_end = verify_section(cpu_offset, cpu_size, sizeof(char), head_end, "CPU features");
+ if (cpu_end == 0) {
+ return false;
+ }
+
+ // Check preload entries
+ uint preload_offset = preload_entries_offset();
+ uint preload_count = preload_entries_count();
+ size_t entry_size = sizeof(AOTCodeEntry);
+ size_t preload_end = verify_section(preload_offset, preload_count, entry_size, cpu_end, "preload entries");
+ if (preload_end == 0) {
+ return false;
+ }
+
+ // Check normal entries
+ uint normal_offset = entries_offset();
+ uint normal_count = entries_count();
+ size_t normal_end = verify_section(normal_offset, normal_count, entry_size, preload_end, "normal entries");
+ if (normal_end == 0) {
+ return false;
+ }
+
+ uint search_offset = search_table_offset();
+ uint search_count = normal_count * 2;
+ size_t search_end = verify_section(search_offset, search_count, sizeof(uint), normal_end, "search table");
+ if (search_end == 0) {
+ return false;
+ }
+
+ // Check strings
+ uint str_offset = strings_offset();
+ uint str_count = strings_count();
+ if ((size_t)str_offset != search_end) {
+ load_failure_log().print_cr("AOT Code Cache disabled: strings table offset %u is invalid", str_offset);
+ return 0;
+ }
+ size_t str_end = verify_section(str_offset, str_count, sizeof(uint), search_end, "strings table");
+ if (str_end == 0) {
return false;
}
return true;
}
+bool AOTCodeCache::failed() const {
+ return AtomicAccess::load(&_failed);
+}
+
+void AOTCodeCache::set_failed() {
+ AtomicAccess::store(&_failed, true);
+}
+
AOTCodeCache* AOTCodeCache::open_for_use() {
if (AOTCodeCache::is_on_for_use()) {
return AOTCodeCache::cache();
@@ -642,37 +1097,112 @@ AOTCodeCache* AOTCodeCache::open_for_dump() {
return nullptr;
}
-void copy_bytes(const char* from, address to, uint size) {
- assert((int)size > 0, "sanity");
- memcpy(to, from, size);
- log_trace(aot, codecache)("Copied %d bytes from " INTPTR_FORMAT " to " INTPTR_FORMAT, size, p2i(from), p2i(to));
+bool AOTCodeCache::is_address_in_aot_cache(address p) {
+ // AOT code cache may be not opened yet.
+ // There could be a failure reading from AOT code cache
+ // at some point and we can't use open_for_use() after that.
+ // cache_buffer() should not be null when cache is used
+ // unless there was failure loading AOT code cache.
+ AOTCodeCache* cache = _cache;
+ if (cache == nullptr || cache->cache_buffer() == nullptr) {
+ return false;
+ }
+ if ((p >= (address)cache->cache_buffer()) &&
+ (p < (address)(cache->cache_buffer() + cache->load_size()))) {
+ return true;
+ }
+ return false;
}
-AOTCodeReader::AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry) {
+AOTCodeReader::AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry, CompileTask* task) {
+ precond(cache != nullptr);
_cache = cache;
_entry = entry;
- _load_buffer = cache->cache_buffer();
+
+ // entry's offset bound checks were done in find_entry() and verify_nmethod_entry().
+ _entry_buffer = (address)cache->cache_buffer() + entry->offset();
+
+ // Next two fields will be reset when processing entry's code
_read_position = 0;
- _lookup_failed = false;
- _name = nullptr;
- _reloc_data = nullptr;
- _reloc_count = 0;
- _oop_maps = nullptr;
+ _read_limit = entry->size();
+
+ _failure = nullptr;
+
+ // Values used by restore(code_blob).
_entry_kind = AOTCodeEntry::None;
- _stub_data = nullptr;
_id = -1;
+ _stub_data = nullptr;
+ if (task != nullptr) {
+ _compile_id = task->compile_id();
+ _comp_level = task->comp_level();
+ assert((uint)task->comp_level() == entry->comp_level(), "sanity");
+ assert(task->is_aot_preload() == entry->for_preload(), "sanity");
+ } else {
+ _compile_id = 0;
+ _comp_level = 0;
+ }
+ _name = nullptr;
+ _reloc_data = nullptr;
+ _reloc_count = 0;
+ _oop_maps = nullptr;
+ _immutable_data = nullptr;
+
+ _cached_stub_data_count = 0;
+ _cached_stub_data = nullptr;
+
+ _id_for_reloc_count = 0;
+ _id_for_reloc = nullptr;
+
+#ifndef PRODUCT
+ _asm_remarks_count = 0;
+ _asm_remarks_data = nullptr;
+ _asm_remarks_strings = nullptr;
+
+ _dbg_strings_count = 0;
+ _dbg_strings_data = nullptr;
+ _dbg_strings = nullptr;
+#endif
+
+ _oop_list = nullptr;
+ _metadata_list = nullptr;
+ _reloc_imm_oop_list = nullptr;
+ _reloc_imm_metadata_list = nullptr;
}
-void AOTCodeReader::set_read_position(uint pos) {
- if (pos == _read_position) {
- return;
+// Get current pointer to AOT code entry and update it to next position.
+bool AOTCodeReader::get_set_read_position(address* pos, uint nbytes) {
+ if (_read_position > _read_limit || nbytes > (_read_limit - _read_position)) {
+ set_lookup_failed("Damaged AOT code");
+ return false;
+ }
+ *pos = position_addr();
+ _read_position += nbytes;
+ return true;
+}
+
+bool AOTCodeReader::update_read_position(uint nbytes) {
+ address dummy;
+ return get_set_read_position(&dummy, nbytes);
+}
+
+bool AOTCodeReader::align_read_position(uint alignment) {
+ uint padding = alignment - (_read_position & (alignment - 1));
+ if (padding == alignment) {
+ return true;
}
- assert(pos < _cache->load_size(), "offset:%d >= file size:%d", pos, _cache->load_size());
- _read_position = pos;
+ return update_read_position(padding);
}
-uint AOTCodeReader::align_read_int() {
- return align_up(_read_position, sizeof(int));
+bool AOTCodeReader::read_int(int* val) {
+ if (!align_read_position(sizeof(int))) {
+ return false;
+ }
+ address pos;
+ if (!get_set_read_position(&pos, sizeof(int))) {
+ return false;
+ }
+ *val = *(int*)pos;
+ return true;
}
bool AOTCodeCache::set_write_position(uint pos) {
@@ -694,8 +1224,7 @@ bool AOTCodeCache::align_write_bytes(uint alignment) {
if (padding == alignment) {
return true;
}
- uint n = write_bytes((const void*)&align_buffer, padding);
- if (n != padding) {
+ if (!write_bytes((const void*)&align_buffer, padding)) {
return false;
}
log_trace(aot, codecache)("Adjust write alignment to %d bytes in AOT Code Cache", alignment);
@@ -714,11 +1243,14 @@ bool AOTCodeCache::align_write_int() {
// Check to see if AOT code cache has required space to store "nbytes" of data
address AOTCodeCache::reserve_bytes(uint nbytes) {
+ precond((int)nbytes > 0);
assert(for_dump(), "Code Cache file is not created");
uint new_position = _write_position + nbytes;
- if (new_position >= (uint)((char*)_store_entries - _store_buffer)) {
- log_warning(aot,codecache)("Failed to ensure %d bytes at offset %d in AOT Code Cache. Increase AOTCodeMaxSize.",
- nbytes, _write_position);
+ // Take into account new AOTCodeEntry created after code and data are stored
+ AOTCodeEntry* entries = _store_entries - 1; // Allocated in reverse
+ if (new_position >= (uint)((char*)entries - _store_buffer)) {
+ log_error(aot, codecache)("Failed to ensure %d bytes at offset %d in AOT Code Cache. Increase AOTCodeMaxSize.",
+ nbytes, _write_position);
set_failed();
report_store_failure();
return nullptr;
@@ -732,72 +1264,326 @@ address AOTCodeCache::reserve_bytes(uint nbytes) {
return buffer;
}
-uint AOTCodeCache::write_bytes(const void* buffer, uint nbytes) {
- assert(for_dump(), "Code Cache file is not created");
+static void copy_bytes(const char* from, address to, uint size) {
+ precond((int)size > 0); // size <= max_jint which is AOTCodeMaxSize limit
+ memcpy(to, from, size);
+ log_trace(aot, codecache)("Copied %d bytes from " INTPTR_FORMAT " to " INTPTR_FORMAT, size, p2i(from), p2i(to));
+}
+
+bool AOTCodeCache::write_bytes(const void* buffer, uint nbytes) {
if (nbytes == 0) {
- return 0;
- }
- uint new_position = _write_position + nbytes;
- if (new_position >= (uint)((char*)_store_entries - _store_buffer)) {
- log_warning(aot, codecache)("Failed to write %d bytes at offset %d to AOT Code Cache. Increase AOTCodeMaxSize.",
- nbytes, _write_position);
- set_failed();
- report_store_failure();
- return 0;
+ return true;
}
- copy_bytes((const char* )buffer, (address)(_store_buffer + _write_position), nbytes);
- log_trace(aot, codecache)("Wrote %d bytes at offset %d to AOT Code Cache", nbytes, _write_position);
- _write_position += nbytes;
- if (_store_size < _write_position) {
- _store_size = _write_position;
+ address dest = reserve_bytes(nbytes);
+ if (dest == nullptr) {
+ return false;
}
- return nbytes;
+ copy_bytes((const char* )buffer, dest, nbytes);
+ return true;
}
-void* AOTCodeEntry::operator new(size_t x, AOTCodeCache* cache) {
- return (void*)(cache->add_entry());
+bool AOTCodeCache::write_int(const int value) {
+ if (!align_write_int()) {
+ return false;
+ }
+ int* dest = (int*)reserve_bytes(sizeof(int));
+ if (dest == nullptr) {
+ return false;
+ }
+ *dest = value;
+ return true;
}
-static bool check_entry(AOTCodeEntry::Kind kind, uint id, AOTCodeEntry* entry) {
- if (entry->kind() == kind) {
- assert(entry->id() == id, "sanity");
- return true; // Found
+bool AOTCodeCache::skip_aot_code(uint comp_level) {
+ // DisableAOTCode uses decimal values as bitmask:
+ // Tier 1 (A1) | 1
+ // Tier 2 (A1 + counters) | 10
+ // Tier 3 (A1 + counters + mdo) | 100 (not used now - Tier3 is not cached)
+ // Tier 4 (A4) | 1000
+ // Tier 5 (AP4) | 10000
+ // All C1 tiers | 111
+ // All tiers | 11111
+ switch (comp_level) {
+ case CompLevel_simple:
+ if ((DisableAOTCode % 10) == 1) {
+ return true;
+ }
+ break;
+ case CompLevel_limited_profile:
+ if ((DisableAOTCode / 10) % 10 == 1) {
+ return true;
+ }
+ break;
+ case CompLevel_full_profile: {
+ return true; // Tier3 is not cached now
+ }
+ case CompLevel_full_optimization:
+ if ((DisableAOTCode / 1000) % 10 == 1) {
+ return true;
+ }
+ break;
+ case CompLevel_preload_optimization:
+ if ((DisableAOTCode / 10000) % 10 == 1) {
+ return true; // skip AOT preload code (level 5);
+ }
+ break;
+ default: return true;
}
return false;
}
-AOTCodeEntry* AOTCodeCache::find_entry(AOTCodeEntry::Kind kind, uint id) {
- assert(_for_use, "sanity");
- uint count = _load_header->entries_count();
- if (_load_entries == nullptr) {
- // Read it
- _search_entries = (uint*)addr(_load_header->entries_offset()); // [id, index]
- _load_entries = (AOTCodeEntry*)(_search_entries + 2 * count);
- log_debug(aot, codecache, init)("Read %d entries table at offset %d from AOT Code Cache", count, _load_header->entries_offset());
+AOTCodeEntry* AOTCodeCache::find_code_entry(const methodHandle& method, uint comp_level) {
+ assert(is_using_code(), "AOT code caching should be enabled");
+ if (!method->in_aot_cache()) {
+ return nullptr;
}
- // Binary search
- int l = 0;
- int h = count - 1;
- while (l <= h) {
- int mid = (l + h) >> 1;
- int ix = mid * 2;
- uint is = _search_entries[ix];
- if (is == id) {
- int index = _search_entries[ix + 1];
- AOTCodeEntry* entry = &(_load_entries[index]);
- if (check_entry(kind, id, entry)) {
- return entry; // Found
- }
- // Linear search around to handle id collission
- for (int i = mid - 1; i >= l; i--) { // search back
- ix = i * 2;
- is = _search_entries[ix];
- if (is != id) {
- break;
+
+ MethodCounters* mc = method->method_counters();
+ // Generically, the carry bit means that the counter has reached a saturation value.
+ // In that ending state, the counter has finished its work of gathering information.
+ // Therefore AOT T4 code uses the carry bit to indicate a recompilation request.
+ if (mc != nullptr && mc->invocation_counter()->carry()) {
+ return nullptr; // Already requested JIT compilation
+ }
+
+ if (skip_aot_code(comp_level)) {
+ return nullptr;
+ }
+
+#ifdef COMPILER2
+ if (comp_level == CompLevel_full_optimization && !_cache->_load_header->verify_c2_state()) {
+ return nullptr; // Mismatched C2 flags
+ }
+#endif
+
+ TraceTime t1("Total time to find AOT code", &_t_totalFind, enable_timers(), false);
+ if (is_on() && _cache->cache_buffer() != nullptr) {
+ uint id = (uint)cast_to_u4(AOTCacheAccess::method_to_narrow_ptr(method()));
+ AOTCodeEntry* entry = _cache->find_entry(AOTCodeEntry::Nmethod, id, comp_level);
+ if (entry == nullptr) {
+ LogStreamHandle(Info, aot, codecache, nmethod) log;
+ if (log.is_enabled()) {
+ ResourceMark rm;
+ const char* target_name = method->name_and_sig_as_C_string();
+ log.print_cr("Missing entry for '%s' (comp_level %d, id: " UINT32_FORMAT_X_0 ")", target_name, (uint)comp_level, id);
+ }
+#ifdef ASSERT
+ } else {
+ assert(!entry->has_clinit_barriers(), "only preload code should have clinit barriers");
+ ResourceMark rm;
+ assert(method() == entry->method(), "AOTCodeCache: saved nmethod's method %p (name: %s id: " UINT32_FORMAT_X_0
+ ") is different from the method %p (name: %s, id: " UINT32_FORMAT_X_0 ") being looked up" ,
+ entry->method(), entry->method()->name_and_sig_as_C_string(), entry->id(), method(), method()->name_and_sig_as_C_string(), id);
+#endif
+ }
+
+#ifdef COMPILER2
+ if (entry != nullptr && entry->has_vectors() && !EnableVectorSupport) {
+ log_debug(aot, codecache, nmethod)("AOT Tier4 code skipped: incompatible EnableVectorSupport: true vs current %u", EnableVectorSupport);
+ return nullptr;
+ }
+#endif
+
+ CompilerDirectiveMatcher matcher(method, comp_level);
+ DirectiveSet* directives = matcher.directive_set();
+ if (directives->IgnoreAOTCompiledOption || directives->ExcludeOption) {
+ LogStreamHandle(Info, aot, codecache, compilation) log;
+ if (log.is_enabled()) {
+ log.print_cr("Ignore AOT code entry on level %d for ", comp_level);
+ method->print_value_on(&log);
+ }
+ return nullptr;
+ }
+
+ return entry;
+ }
+ return nullptr;
+}
+
+Method* AOTCodeEntry::method() const {
+ assert(_kind == Nmethod, "invalid kind %d", _kind);
+ assert(AOTCodeCache::is_on_for_use(), "must be");
+ return AOTCacheAccess::narrow_ptr_to_method(cast_from_u4(_id));
+}
+
+void* AOTCodeEntry::operator new(size_t x, AOTCodeCache* cache) {
+ return (void*)(cache->add_entry());
+}
+
+bool AOTCodeEntry::not_entrant() const {
+ return AtomicAccess::load(&_not_entrant);
+}
+
+void AOTCodeEntry::set_not_entrant() {
+ AtomicAccess::store(&_not_entrant, true);
+}
+
+bool AOTCodeEntry::is_verified() const {
+ return AtomicAccess::load(&_verified);
+}
+
+void AOTCodeEntry::set_verified() {
+ AtomicAccess::store(&_verified, true);
+}
+
+bool AOTCodeEntry::try_set_not_entrant() {
+ // If old value is 'true' - other thread set it already.
+ return AtomicAccess::xchg(&_not_entrant, true);
+}
+
+void AOTCodeCache::set_load_entries() {
+ // Get address of entry tables
+ precond(_load_header != nullptr); // Loading AOT code
+ uint count = _load_header->entries_count();
+ uint offset = _load_header->entries_offset();
+ _load_entries = (AOTCodeEntry*)addr(offset);
+ _search_entries = (uint*)addr(_load_header->search_table_offset()); // [id, index]
+ log_debug(aot, codecache, init)("Read %d entries table at offset %d from AOT Code Cache", count, offset);
+}
+
+// Bound checks for AOT nmethod.
+bool AOTCodeCache::verify_nmethod_entry(AOTCodeEntry* entry, uint id, uint low_bound, uint high_bound) {
+ uint entry_offset = entry->offset();
+ uint entry_size = entry->size();
+ // entry_size for preload should be at least sizeof(uint) to read cached id.
+ if (entry_offset < low_bound || entry_offset >= high_bound ||
+ !is_aligned(entry_offset, DATA_ALIGNMENT) ||
+ entry_size < sizeof(uint) || entry_size > (high_bound - entry_offset)) {
+ log_warning(aot, codecache, nmethod)("Damaged AOT nmethod entry %u " INTPTR_FORMAT " [%u + %u]", id, p2i(entry), entry_offset, entry_size);
+ return false; // Data is damaged, skip the rest of entries
+ }
+ // First is cached 'uint id' and then method's name
+ uint name_off = entry->name_offset();
+ uint name_sz = entry->name_size();
+ if (name_sz == 0 || name_off != sizeof(uint) || name_off >= entry_size ||
+ name_sz > (entry_size - name_off) ||
+ *addr(entry_offset + name_off + name_sz - 1) != '\0') {
+ log_warning(aot, codecache, nmethod)("Damaged method's name in AOT nmethod entry %u " INTPTR_FORMAT " [%u + %u], name [%u + %u] ", id, p2i(entry), entry_offset, entry_size, name_off, name_sz);
+ return false;
+ }
+ uint code_off = entry->code_offset();
+ if (code_off < (name_off + name_sz) || code_off >= entry_size ||
+ sizeof(nmethod) > (entry_size - code_off) ||
+ !is_aligned(entry_offset + code_off, DATA_ALIGNMENT)) {
+ log_warning(aot, codecache, nmethod)("Damaged code offset in AOT nmethod entry %u " INTPTR_FORMAT " [%u + %u], code [%u] ", id, p2i(entry), entry_offset, entry_size, code_off);
+ return false;
+ }
+ uint archived_nm_offset = entry_offset + code_off;
+ nmethod* archived_nm = (nmethod*)addr(archived_nm_offset);
+ if (!archived_nm->is_nmethod()) {
+ log_warning(aot, codecache, nmethod)("Damaged code kind in AOT nmethod entry %u " INTPTR_FORMAT " [%u + %u], code [%u] ", id, p2i(entry), entry_offset, entry_size, code_off);
+ return false;
+ }
+ int nm_size = archived_nm->size();
+ if (nm_size < (int)sizeof(nmethod) || (uint)nm_size > (entry_size - code_off) ||
+ !is_aligned(nm_size, oopSize)) {
+ log_warning(aot, codecache, nmethod)("Damaged nmethod size in AOT nmethod entry %u " INTPTR_FORMAT " [%u + %u], nmethod [%u + %d] ", id, p2i(entry), entry_offset, entry_size, code_off, nm_size);
+ return false;
+ }
+ return true;
+}
+
+AOTCodeEntry* AOTCodeCache::find_entry(AOTCodeEntry::Kind kind, uint id, uint comp_level) {
+ AOTCodeEntry* entry = search_entry(kind, id, comp_level);
+ if (entry != nullptr) {
+ // Verify entry
+ uint entries_offset = _load_header->entries_offset();
+ uint low_bound = entries_offset + _load_header->entries_count() * sizeof(AOTCodeEntry);
+ uint high_bound = _load_header->search_table_offset(); // Following section
+
+ if (kind == AOTCodeEntry::Nmethod) {
+ if (!verify_nmethod_entry(entry, id, low_bound, high_bound)) {
+ return nullptr; // Data is damaged, skip
+ }
+ } else { // stubs, adapters
+ uint entry_offset = entry->offset();
+ uint entry_size = entry->size();
+ if (entry_offset < low_bound || entry_offset >= high_bound ||
+ !is_aligned(entry_offset, DATA_ALIGNMENT) ||
+ entry_size == 0 || entry_size > (high_bound - entry_offset)) {
+ log_warning(aot, codecache, stubs)("Damaged AOT code blob entry %u " INTPTR_FORMAT " [%u + %u]", id, p2i(entry), entry_offset, entry_size);
+ return nullptr; // Data is damaged, skip
+ }
+ uint code_off = entry->code_offset();
+ // Non-nmethod blobs record code first and name last.
+ // sizeof(int) for _reloc_count.
+ if (code_off != 0 || entry_size < (sizeof(CodeBlob) + sizeof(int))) {
+ log_warning(aot, codecache, stubs)("Damaged code offset in AOT code blob entry %u " INTPTR_FORMAT " [%u + %u], code [%u] ", id, p2i(entry), entry_offset, entry_size, code_off);
+ return nullptr;
+ }
+ uint name_off = entry->name_offset();
+ uint name_sz = entry->name_size();
+ if (name_sz == 0 || name_off >= entry_size ||
+ name_off < (sizeof(CodeBlob) + sizeof(int)) ||
+ name_sz > (entry_size - name_off) ||
+ *addr(entry_offset + name_off + name_sz - 1) != '\0') {
+ log_warning(aot, codecache, stubs)("Damaged code blob name in AOT code blob entry %u " INTPTR_FORMAT " [%u + %u], name [%u + %u] ", id, p2i(entry), entry_offset, entry_size, name_off, name_sz);
+ return nullptr;
+ }
+ CodeBlob* archived_blob = (CodeBlob*)addr(entry_offset); // + code_off (0)
+ if ((kind == AOTCodeEntry::Adapter) && !archived_blob->is_adapter_blob()) {
+ log_warning(aot, codecache, stubs)("Damaged code blob kind in AOT code blob entry %u " INTPTR_FORMAT " [%u + %u], code [%u] ", id, p2i(entry), entry_offset, entry_size, code_off);
+ return nullptr;
+ }
+ int blob_size = archived_blob->size();
+ uint code_to_name_dist = name_off - code_off;
+ // sizeof(int) for the following _reloc_count
+ if (blob_size < (int)sizeof(CodeBlob) || (uint)blob_size > (code_to_name_dist - sizeof(int)) ||
+ !is_aligned(blob_size, oopSize)) {
+ log_warning(aot, codecache, stubs)("Damaged code blob size in AOT code blob entry %u " INTPTR_FORMAT " [%u + %u], code [%u + %d] ", id, p2i(entry), entry_offset, entry_size, code_off, blob_size);
+ return nullptr;
+ }
+ }
+ }
+ return entry;
+}
+
+static bool check_entry(AOTCodeEntry::Kind kind, uint id, uint comp_level, AOTCodeEntry* entry) {
+ if (entry->kind() == kind && entry->id() == id) {
+ if (kind != AOTCodeEntry::Nmethod || // adapters and stubs have only one version
+ // Look only for normal AOT code entry, preload code is handled separately
+ (!entry->not_entrant() && (entry->comp_level() == comp_level))) {
+ return true; // Found
+ }
+ }
+ return false;
+}
+
+AOTCodeEntry* AOTCodeCache::search_entry(AOTCodeEntry::Kind kind, uint id, uint comp_level) {
+ assert(_for_use, "sanity");
+ uint count = _load_header->entries_count();
+ assert(_load_entries != nullptr && _search_entries != nullptr, "should be set in init2");
+ // Binary search
+ int l = 0;
+ int h = count - 1;
+ while (l <= h) {
+ int mid = (l + h) >> 1;
+ int ix = mid * 2;
+ uint is = _search_entries[ix];
+ if (is == id) {
+ uint index = _search_entries[ix + 1];
+ if (index >= count) {
+ log_warning(aot, codecache)("Damaged entries search table: index %u >= count %u", index, count);
+ return nullptr;
+ }
+ AOTCodeEntry* entry = &(_load_entries[index]);
+ if (check_entry(kind, id, comp_level, entry)) {
+ return entry; // Found
+ }
+ // Linear search around
+ for (int i = mid - 1; i >= l; i--) { // search back
+ ix = i * 2;
+ is = _search_entries[ix];
+ if (is != id) {
+ break;
}
index = _search_entries[ix + 1];
+ if (index >= count) {
+ log_warning(aot, codecache)("Damaged entries search table: index %u >= count %u", index, count);
+ return nullptr;
+ }
AOTCodeEntry* entry = &(_load_entries[index]);
- if (check_entry(kind, id, entry)) {
+ if (check_entry(kind, id, comp_level, entry)) {
return entry; // Found
}
}
@@ -808,12 +1594,16 @@ AOTCodeEntry* AOTCodeCache::find_entry(AOTCodeEntry::Kind kind, uint id) {
break;
}
index = _search_entries[ix + 1];
+ if (index >= count) {
+ log_warning(aot, codecache)("Damaged entries search table: index %u >= count %u", index, count);
+ return nullptr;
+ }
AOTCodeEntry* entry = &(_load_entries[index]);
- if (check_entry(kind, id, entry)) {
+ if (check_entry(kind, id, comp_level, entry)) {
return entry; // Found
}
}
- break; // Not found match
+ break; // No match found
} else if (is < id) {
l = mid + 1;
} else {
@@ -823,30 +1613,133 @@ AOTCodeEntry* AOTCodeCache::find_entry(AOTCodeEntry::Kind kind, uint id) {
return nullptr;
}
-extern "C" {
- static int uint_cmp(const void *i, const void *j) {
- uint a = *(uint *)i;
- uint b = *(uint *)j;
- return a > b ? 1 : a < b ? -1 : 0;
+void AOTCodeCache::invalidate_entry(AOTCodeEntry* entry) {
+ assert(entry!= nullptr, "all entries should be read already");
+ if (entry->try_set_not_entrant()) {
+ return; // Someone invalidated it already
+ }
+#ifdef ASSERT
+ assert(_load_entries != nullptr, "sanity");
+ assert(entry->is_loaded() || entry->for_preload(), "invalidate only AOT code in use or a preload code");
+ bool found = false;
+ uint i = 0;
+ uint count = 0;
+ if (entry->for_preload()) {
+ count = _load_header->preload_entries_count();
+ AOTCodeEntry* preload_entry = (AOTCodeEntry*)addr(_load_header->preload_entries_offset());
+ for (; i < count; i++) {
+ if (entry == &preload_entry[i]) {
+ break;
+ }
+ }
+ } else {
+ count = _load_header->entries_count();
+ for(; i < count; i++) {
+ if (entry == &(_load_entries[i])) {
+ break;
+ }
+ }
+ }
+ found = (i < count);
+ assert(found, "entry should exist");
+#endif // ASSERT
+
+ uint name_offset = entry->offset() + entry->name_offset();
+ const char* name = _load_buffer + name_offset;
+ uint level = entry->comp_level();
+ uint comp_id = entry->comp_id();
+ bool for_preload = entry->for_preload();
+ bool clinit_brs = entry->has_clinit_barriers();
+ log_info(aot, codecache, nmethod)("Invalidated entry for '%s' (comp_id %d, comp_level %d, hash: " UINT32_FORMAT_X_0 "%s%s)",
+ name, comp_id, level, entry->id(), (for_preload ? "P" : "A"), (clinit_brs ? ", has clinit barriers" : ""));
+
+ if (!for_preload && (entry->comp_level() == CompLevel_full_optimization)) {
+ // Invalidate preload code if normal AOT C2 code is invalidated,
+ // most likely because some dependencies changed during run.
+ // We can still use normal AOT code if preload code is
+ // invalidated - normal AOT code has less restrictions.
+ MethodCounters* mc = entry->method()->method_counters();
+ if (mc != nullptr && mc->aot_preload_code_entry() != nullptr) {
+ AOTCodeEntry* preload_entry = mc->aot_preload_code_entry();
+ if (preload_entry != nullptr) {
+ assert(preload_entry->for_preload(), "expecting only such entries here");
+ invalidate_entry(preload_entry);
+ }
+ }
+ }
+}
+
+// +1 for preload code
+const int AOTCompLevel_count = CompLevel_count + 1; // 6 levels indexed from 0 to 5
+
+struct AOTCodeEntryStats {
+private:
+ uint _kind_cnt[AOTCodeEntry::Kind_count];
+ uint _nmethod_cnt[AOTCompLevel_count];
+ uint _clinit_barriers_cnt;
+
+public:
+ static void check_kind(uint kind) {
+ assert(kind > AOTCodeEntry::None && kind < AOTCodeEntry::Kind_count, "Invalid AOTCodeEntry kind %d", kind);
+ }
+ static void check_complevel(uint lvl) {
+ assert(lvl > CompLevel_none && lvl < AOTCompLevel_count, "Invalid compilation level %d", lvl);
+ }
+
+ void inc_entry_cnt(uint kind) { check_kind(kind); _kind_cnt[kind] += 1; }
+ void inc_nmethod_cnt(uint lvl) { check_complevel(lvl); _nmethod_cnt[lvl] += 1; }
+ void inc_clinit_barriers_cnt() { _clinit_barriers_cnt += 1; }
+
+ AOTCodeEntryStats() {
+ memset(this, 0, sizeof(AOTCodeEntryStats));
+ }
+
+ void collect_entry_stats(AOTCodeEntry* entry) {
+ inc_entry_cnt(entry->kind());
+ if (entry->is_nmethod()) {
+ entry->for_preload() ? inc_nmethod_cnt(AOTCompLevel_count-1)
+ : inc_nmethod_cnt(entry->comp_level());
+ if (entry->has_clinit_barriers()) {
+ inc_clinit_barriers_cnt();
+ }
+ }
}
+
+ uint entry_count(uint kind) const { check_kind(kind); return _kind_cnt[kind]; }
+ uint nmethod_count(uint lvl) const { check_complevel(lvl); return _nmethod_cnt[lvl]; }
+ uint clinit_barriers_count() const { return _clinit_barriers_cnt; }
+};
+
+static int uint_cmp(const void *i, const void *j) {
+ uint a = *(uint *)i;
+ uint b = *(uint *)j;
+ return a > b ? 1 : a < b ? -1 : 0;
}
-void AOTCodeCache::store_cpu_features(char*& buffer, uint buffer_size) {
- uint* size_ptr = (uint *)buffer;
- *size_ptr = buffer_size;
- buffer += sizeof(uint);
+static int preload_cmp(const void *i, const void *j) {
+ AOTCodeEntry* ea = *(AOTCodeEntry**)i;
+ AOTCodeEntry* eb = *(AOTCodeEntry**)j;
+ uint a = ea->comp_id();
+ uint b = eb->comp_id();
+ return a > b ? 1 : a < b ? -1 : 0;
+}
+char* AOTCodeCache::store_cpu_features(char* buffer, uint buffer_size) {
+ // Align to 8 bytes - see Aarch64 version of get_cpu_features_name()
+ precond(is_aligned(buffer, DATA_ALIGNMENT));
VM_Version::store_cpu_features(buffer);
log_debug(aot, codecache, exit)("CPU features recorded in AOTCodeCache: %s", VM_Version::features_string());
buffer += buffer_size;
- buffer = align_up(buffer, DATA_ALIGNMENT);
+ return align_up(buffer, DATA_ALIGNMENT);
}
bool AOTCodeCache::finish_write() {
if (!align_write()) {
return false;
}
- uint strings_offset = _write_position;
+ // End of AOT code
+ uint code_size = _write_position;
+ uint strings_offset = code_size;
int strings_count = store_strings();
if (strings_count < 0) {
return false;
@@ -856,31 +1749,35 @@ bool AOTCodeCache::finish_write() {
}
uint strings_size = _write_position - strings_offset;
- uint entries_count = 0; // Number of entrant (useful) code entries
- uint entries_offset = _write_position;
+ uint code_count = _store_entries_cnt;
+ if (code_count == 0) {
+ log_info(aot, codecache, exit)("AOT Code Cache was not created: no entries");
+ return false; // Nothing to write
+ }
+ {
+ _aot_code_directory = CachedCodeDirectory::create();
+ assert(_aot_code_directory != nullptr, "Sanity check");
- uint store_count = _store_entries_cnt;
- if (store_count > 0) {
uint header_size = (uint)align_up(sizeof(AOTCodeCache::Header), DATA_ALIGNMENT);
- uint code_count = store_count;
uint search_count = code_count * 2;
uint search_size = search_count * sizeof(uint);
uint entries_size = (uint)align_up(code_count * sizeof(AOTCodeEntry), DATA_ALIGNMENT); // In bytes
- // _write_position includes size of code and strings
+ // _write_position should include code and strings
uint code_alignment = code_count * DATA_ALIGNMENT; // We align_up code size when storing it.
uint cpu_features_size = VM_Version::cpu_features_size();
- uint total_cpu_features_size = sizeof(uint) + cpu_features_size; // sizeof(uint) to store cpu_features_size
uint total_size = header_size + _write_position + code_alignment + search_size + entries_size +
- align_up(total_cpu_features_size, DATA_ALIGNMENT);
+ align_up(cpu_features_size, DATA_ALIGNMENT);
assert(total_size < max_aot_code_size(), "AOT Code size (" UINT32_FORMAT " bytes) is greater than AOTCodeMaxSize(" UINT32_FORMAT " bytes).", total_size, max_aot_code_size());
// Allocate in AOT Cache buffer
char* buffer = (char *)AOTCacheAccess::allocate_aot_code_region(total_size + DATA_ALIGNMENT);
+ assert(buffer != nullptr, "CDS should already throw OOM error");
char* start = align_up(buffer, DATA_ALIGNMENT);
char* current = start + header_size; // Skip header
+ current = align_up(current, DATA_ALIGNMENT);
uint cpu_features_offset = current - start;
- store_cpu_features(current, cpu_features_size);
+ current = store_cpu_features(current, cpu_features_size);
assert(is_aligned(current, DATA_ALIGNMENT), "sanity check");
assert(current < start + total_size, "sanity check");
@@ -888,105 +1785,168 @@ bool AOTCodeCache::finish_write() {
uint* search = NEW_C_HEAP_ARRAY(uint, search_count, mtCode);
AOTCodeEntry* entries_address = _store_entries; // Pointer to latest entry
- uint adapters_count = 0;
- uint shared_blobs_count = 0;
- uint stubgen_blobs_count = 0;
- uint C1_blobs_count = 0;
- uint C2_blobs_count = 0;
+ AOTCodeEntryStats stats;
uint max_size = 0;
+
+ // First, sort preload entries based on compile_id.
+ AOTCodeEntry** preload_sorted = NEW_C_HEAP_ARRAY(AOTCodeEntry*, code_count, mtCode);
+ uint preload_entries_cnt = 0;
// AOTCodeEntry entries were allocated in reverse in store buffer.
// Process them in reverse order to cache first code first.
- for (int i = store_count - 1; i >= 0; i--) {
- entries_address[i].set_next(nullptr); // clear pointers before storing data
- uint size = align_up(entries_address[i].size(), DATA_ALIGNMENT);
+ for (int i = code_count - 1; i >= 0; i--) {
+ AOTCodeEntry* entry = &entries_address[i];
+ if (entry->for_preload()) {
+ assert(!entry->not_entrant(), "AOT code should not be not_entrant during assembly phase");
+ preload_sorted[preload_entries_cnt] = entry;
+ stats.collect_entry_stats(entry);
+ preload_entries_cnt++;
+ }
+ }
+ if (preload_entries_cnt > 0) {
+ qsort(preload_sorted, preload_entries_cnt, sizeof(AOTCodeEntry*), preload_cmp);
+ }
+ // Store AOTCodeEntry for preload code first.
+ current = align_up(current, DATA_ALIGNMENT);
+ uint preload_entries_offset = current - start;
+ AOTCodeEntry* preload_entries = (AOTCodeEntry*)current;
+ for (int i = 0; i < (int)preload_entries_cnt; i++) {
+ AOTCodeEntry* entry = preload_sorted[i];
+ assert(entry->for_preload(), "sanity");
+ copy_bytes((const char*)entry, (address)current, sizeof(AOTCodeEntry));
+ current += sizeof(AOTCodeEntry);
+ }
+
+ // Now write the data for preload AOTCodeEntry
+ current = align_up(current, DATA_ALIGNMENT);
+ for (int i = 0; i < (int)preload_entries_cnt; i++) {
+ AOTCodeEntry* entry = &preload_entries[i];
+ uint size = align_up(entry->size(), DATA_ALIGNMENT);
if (size > max_size) {
max_size = size;
}
- copy_bytes((_store_buffer + entries_address[i].offset()), (address)current, size);
- entries_address[i].set_offset(current - start); // New offset
+ copy_bytes((_store_buffer + entry->offset()), (address)current, size);
+ entry->set_offset(current - start); // New offset
current += size;
- uint n = write_bytes(&(entries_address[i]), sizeof(AOTCodeEntry));
- if (n != sizeof(AOTCodeEntry)) {
- FREE_C_HEAP_ARRAY(search);
- return false;
+ }
+ FREE_C_HEAP_ARRAY(preload_sorted);
+
+ // Store the rest of AOTCodeEntry
+ uint entries_count = 0;
+ current = align_up(current, DATA_ALIGNMENT);
+ uint new_entries_offset = current - start;
+ AOTCodeEntry* code_entries = (AOTCodeEntry*)current;
+ for (int i = code_count - 1; i >= 0; i--) {
+ AOTCodeEntry* entry = &entries_address[i];
+ if (entry->for_preload()) {
+ continue;
}
- search[entries_count*2 + 0] = entries_address[i].id();
+ assert(!entry->not_entrant(), "AOT code should not be not_entrant during assembly phase");
+ copy_bytes((const char*)entry, (address)current, sizeof(AOTCodeEntry));
+ stats.collect_entry_stats(entry);
+ current += sizeof(AOTCodeEntry);
+ search[entries_count*2 + 0] = entry->id();
search[entries_count*2 + 1] = entries_count;
entries_count++;
- AOTCodeEntry::Kind kind = entries_address[i].kind();
- if (kind == AOTCodeEntry::Adapter) {
- adapters_count++;
- } else if (kind == AOTCodeEntry::SharedBlob) {
- shared_blobs_count++;
- } else if (kind == AOTCodeEntry::StubGenBlob) {
- stubgen_blobs_count++;
- } else if (kind == AOTCodeEntry::C1Blob) {
- C1_blobs_count++;
- } else if (kind == AOTCodeEntry::C2Blob) {
- C2_blobs_count++;
- }
- }
- if (entries_count == 0) {
- log_info(aot, codecache, exit)("AOT Code Cache was not created: no entires");
- FREE_C_HEAP_ARRAY(search);
- return true; // Nothing to write
- }
- assert(entries_count <= store_count, "%d > %d", entries_count, store_count);
+ }
+
+ // Now write the data for AOTCodeEntry
+ current = align_up(current, DATA_ALIGNMENT);
+ for (int i = 0; i < (int)entries_count; i++) {
+ AOTCodeEntry* entry = &code_entries[i];
+ uint size = align_up(entry->size(), DATA_ALIGNMENT);
+ if (size > max_size) {
+ max_size = size;
+ }
+ copy_bytes((_store_buffer + entry->offset()), (address)current, size);
+ entry->set_offset(current - start); // New offset
+ current += size;
+ }
+
+ uint total_entries_cnt = preload_entries_cnt + entries_count;
+ assert(total_entries_cnt == code_count, "%d != %d", total_entries_cnt, code_count);
+
+ current = align_up(current, DATA_ALIGNMENT);
+ uint search_table_offset = current - start;
+ if (entries_count > 0) {
+ // Sort and store search table
+ qsort(search, entries_count, 2*sizeof(uint), uint_cmp);
+ search_size = 2 * entries_count * sizeof(uint);
+ copy_bytes((const char*)search, (address)current, search_size);
+ current += search_size;
+ }
+ FREE_C_HEAP_ARRAY(search);
+
// Write strings
if (strings_count > 0) {
copy_bytes((_store_buffer + strings_offset), (address)current, strings_size);
strings_offset = (current - start); // New offset
current += strings_size;
+ } else {
+ strings_offset = (current - start); // New offset
}
- uint new_entries_offset = (current - start); // New offset
- // Sort and store search table
- qsort(search, entries_count, 2*sizeof(uint), uint_cmp);
- search_size = 2 * entries_count * sizeof(uint);
- copy_bytes((const char*)search, (address)current, search_size);
- FREE_C_HEAP_ARRAY(search);
- current += search_size;
-
- // Write entries
- entries_size = entries_count * sizeof(AOTCodeEntry); // New size
- copy_bytes((_store_buffer + entries_offset), (address)current, entries_size);
- current += entries_size;
+ // align end to get aligned size
+ current = align_up(current, DATA_ALIGNMENT);
uint size = (current - start);
assert(size <= total_size, "%d > %d", size , total_size);
- log_debug(aot, codecache, exit)(" Adapters: total=%u", adapters_count);
- log_debug(aot, codecache, exit)(" Shared Blobs: total=%d", shared_blobs_count);
- log_debug(aot, codecache, exit)(" StubGen Blobs: total=%d", stubgen_blobs_count);
- log_debug(aot, codecache, exit)(" C1 Blobs: total=%d", C1_blobs_count);
- log_debug(aot, codecache, exit)(" C2 Blobs: total=%d", C2_blobs_count);
- log_debug(aot, codecache, exit)(" AOT code cache size: %u bytes, max entry's size: %u bytes", size, max_size);
+ uint extrs_len = _table->extrs_length();
+ uint extrs2_len = _table->extrs2_length();
+ assert(_table->final_extrs_length() == extrs_len, "%u != %u",
+ _table->final_extrs_length(), extrs_len);
// Finalize header
AOTCodeCache::Header* header = (AOTCodeCache::Header*)start;
- header->init(size, (uint)strings_count, strings_offset,
- entries_count, new_entries_offset,
- adapters_count, shared_blobs_count,
- stubgen_blobs_count, C1_blobs_count,
- C2_blobs_count, cpu_features_offset);
-
- log_info(aot, codecache, exit)("Wrote %d AOT code entries to AOT Code Cache", entries_count);
+ header->init(size,
+ cpu_features_size, cpu_features_offset,
+ preload_entries_cnt, preload_entries_offset,
+ entries_count, new_entries_offset, search_table_offset,
+ (uint)strings_count, strings_offset,
+ stats.entry_count(AOTCodeEntry::Adapter), stats.entry_count(AOTCodeEntry::SharedBlob),
+ stats.entry_count(AOTCodeEntry::StubGenBlob), stats.entry_count(AOTCodeEntry::C1Blob),
+ stats.entry_count(AOTCodeEntry::C2Blob),
+ extrs2_len, extrs_len);
+
+ _aot_code_directory->set_aot_code_data(size, start);
+
+ log_stats_on_exit(stats);
+ LogStreamHandle(Info, aot, codecache, exit) log;
+ if (log.is_enabled()) {
+ log.print_cr(" AOT code cache size: %u bytes", size);
+ log.print_cr(" header size: %u", header_size);
+ log.print_cr(" CPU features data: %u", cpu_features_size);
+ log.print_cr(" total code size: %u (max code's size: %u)", code_size, max_size);
+ log.print_cr(" entries size: %u", entries_size);
+ log.print_cr(" entry search table: %u", search_size);
+ log.print_cr(" C strings size: %u", strings_size);
+ log.print_cr("Wrote %d AOT code entries to AOT Code Cache", total_entries_cnt);
+ }
}
return true;
}
//------------------Store/Load AOT code ----------------------
+// It is called during production run too to publish stub addresses.
bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name, AOTStubData* stub_data, CodeBuffer* code_buffer) {
assert(AOTCodeEntry::is_valid_entry_kind(entry_kind), "invalid entry_kind %d", entry_kind);
// we only expect stub data and a code buffer for a multi stub blob
- assert(AOTCodeEntry::is_multi_stub_blob(entry_kind) == (stub_data != nullptr),
+ bool is_multi_stub = AOTCodeEntry::is_multi_stub_blob(entry_kind);
+ assert(is_multi_stub == (stub_data != nullptr),
"entry_kind %d does not match stub_data pointer %p",
entry_kind, stub_data);
assert((stub_data == nullptr) == (code_buffer == nullptr),
"stub data and code buffer must both be null or both non null");
+ // 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() || (is_on_for_dump() && _aot_code_directory != nullptr)) {
+ return false; // AOT code cache was already dumped or failed.
+ }
+
// If this is a stub and the cache is on for either load or dump we
// need to insert the stub entries into the AOTCacheAddressTable so
// that relocs which refer to entries defined by this blob get
@@ -1018,7 +1978,8 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind
if (AOTCodeEntry::is_blob(entry_kind) && !is_dumping_stub()) {
return false;
}
- log_debug(aot, codecache, stubs)("Writing blob '%s' (id=%u, kind=%s) to AOT Code Cache", name, id, aot_code_entry_kind_name[entry_kind]);
+ log_debug(aot, codecache, stubs)("Writing blob '%s' (id=%u, kind=%s) to AOT Code Cache",
+ name, id, aot_code_entry_kind_name[entry_kind]);
#ifdef ASSERT
LogStreamHandle(Trace, aot, codecache, stubs) log;
@@ -1027,12 +1988,7 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind
blob.print_on(&log);
}
#endif
- // 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;
}
@@ -1063,7 +2019,7 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind
int reloc_count;
address reloc_data;
- if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) {
+ if (is_multi_stub) {
CodeSection* cs = code_buffer->code_section(CodeBuffer::SECT_INSTS);
reloc_count = (cs->has_locs() ? cs->locs_count() : 0);
reloc_data = (reloc_count > 0 ? (address)cs->locs_start() : nullptr);
@@ -1071,22 +2027,24 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind
reloc_count = blob.relocation_size() / sizeof(relocInfo);
reloc_data = (address)blob.relocation_begin();
}
- uint n = cache->write_bytes(&reloc_count, sizeof(int));
- if (n != sizeof(int)) {
+
+ if (!cache->write_int(reloc_count)) {
return false;
}
- if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) {
- // align to heap word size before writing the relocs so we can
- // install them into a code buffer when they get restored
- if (!cache->align_write()) {
+ bool store_relocations = (reloc_count > 0);
+ if (store_relocations) {
+ if (is_multi_stub) {
+ // align to heap word size before writing the relocs so we can
+ // install them into a code buffer when they get restored
+ if (!cache->align_write()) {
+ return false;
+ }
+ }
+ uint reloc_data_size = (uint)(reloc_count * sizeof(relocInfo));
+ if (!cache->write_bytes(reloc_data, reloc_data_size)) {
return false;
}
}
- uint reloc_data_size = (uint)(reloc_count * sizeof(relocInfo));
- n = cache->write_bytes(reloc_data, reloc_data_size);
- if (n != reloc_data_size) {
- return false;
- }
bool has_oop_maps = false;
if (blob.oop_maps() != nullptr) {
@@ -1105,19 +2063,22 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind
}
}
- // now we have added all the other data we can write details of any
- // extra the AOT relocations
+ // All addresses referenced by the blob have now been added to the address
+ // table, so their IDs can be recorded in the relocation data.
+
+ // Don't skip assert for unknown external address in adapter and stub code.
+ bool assert_for_unknown_address = true;
bool write_ok = true;
- if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) {
- if (reloc_count > 0) {
+ if (store_relocations) {
+ if (is_multi_stub) {
CodeSection* cs = code_buffer->code_section(CodeBuffer::SECT_INSTS);
RelocIterator iter(cs);
- write_ok = cache->write_relocations(blob, iter);
+ write_ok = cache->write_relocations(blob, iter, assert_for_unknown_address);
+ } else {
+ RelocIterator iter(&blob);
+ write_ok = cache->write_relocations(blob, iter, assert_for_unknown_address);
}
- } else {
- RelocIterator iter(&blob);
- write_ok = cache->write_relocations(blob, iter);
}
if (!write_ok) {
@@ -1129,11 +2090,8 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind
}
#ifndef PRODUCT
- // Write asm remarks after relocation info
- if (!cache->write_asm_remarks(blob)) {
- return false;
- }
- if (!cache->write_dbg_strings(blob)) {
+ // Write asm remarks and dbg strings after relocation info
+ if (!cache->write_asm_rem_and_dbg_str(blob.asm_remarks(), blob.dbg_strings())) {
return false;
}
#endif /* PRODUCT */
@@ -1141,8 +2099,7 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind
// Write name after code comments
uint name_offset = cache->_write_position - entry_position;
uint name_size = (uint)strlen(name) + 1; // Includes '/0'
- n = cache->write_bytes(name, name_size);
- if (n != name_size) {
+ if (!cache->write_bytes(name, name_size)) {
return false;
}
@@ -1150,38 +2107,40 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind
AOTCodeEntry* entry = new(cache) AOTCodeEntry(entry_kind, encode_id(entry_kind, id),
entry_position, entry_size, name_offset, name_size,
- blob_offset, has_oop_maps, blob.content_begin());
+ blob_offset, has_oop_maps);
log_debug(aot, codecache, stubs)("Wrote code blob '%s' (id=%u, kind=%s) to AOT Code Cache", name, id, aot_code_entry_kind_name[entry_kind]);
return true;
}
-bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name) {
- assert(!AOTCodeEntry::is_blob(entry_kind),
- "wrong entry kind for numeric id %d", id);
- return store_code_blob(blob, entry_kind, (uint)id, name, nullptr, nullptr);
+bool AOTCodeCache::store_adapter(CodeBlob& blob, uint id, const char* name) {
+ return store_code_blob(blob, AOTCodeEntry::Adapter, id, name, nullptr, nullptr);
}
bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id) {
assert(AOTCodeEntry::is_single_stub_blob(entry_kind),
- "wrong entry kind for blob id %s", StubInfo::name(id));
+ "wrong entry kind for blob %u '%s'", (uint)id, StubInfo::name(id));
return store_code_blob(blob, entry_kind, (uint)id, StubInfo::name(id), nullptr, nullptr);
}
-bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id, AOTStubData* stub_data, CodeBuffer* code_buffer) {
- assert(AOTCodeEntry::is_multi_stub_blob(entry_kind),
- "wrong entry kind for multi stub blob id %s", StubInfo::name(id));
- return store_code_blob(blob, entry_kind, (uint)id, StubInfo::name(id), stub_data, code_buffer);
+bool AOTCodeCache::store_multi_stub_blob(CodeBlob& blob, BlobId id, AOTStubData* stub_data, CodeBuffer* code_buffer) {
+ return store_code_blob(blob, AOTCodeEntry::StubGenBlob, (uint)id, StubInfo::name(id), stub_data, code_buffer);
}
bool AOTCodeCache::write_stub_data(CodeBlob &blob, AOTStubData *stub_data) {
if (!align_write_int()) {
return false;
}
+ uint* count_ptr = (uint *)reserve_bytes(sizeof(uint));
+ if (count_ptr == nullptr) {
+ return false;
+ }
+ uint count = 0;
+
BlobId blob_id = stub_data->blob_id();
StubId stub_id = StubInfo::stub_base(blob_id);
+ assert(sizeof(StubId) == sizeof(int), "sanity");
address blob_base = blob.code_begin();
int stub_cnt = StubInfo::stub_count(blob_id);
- int n;
LogStreamHandle(Trace, aot, codecache, stubs) log;
@@ -1206,31 +2165,31 @@ bool AOTCodeCache::write_stub_data(CodeBlob &blob, AOTStubData *stub_data) {
GrowableArray& addresses = stub_data->address_array();
int base = range.start_index();
if (base >= 0) {
- n = write_bytes(&stub_id, sizeof(StubId));
- if (n != sizeof(StubId)) {
+ if (!write_int(checked_cast(stub_id))) {
return false;
}
+ count++;
address start = addresses.at(base);
assert (blob_base <= start, "sanity");
uint offset = (uint)(start - blob_base);
- n = write_bytes(&offset, sizeof(uint));
- if (n != sizeof(int)) {
+ if (!write_int(offset)) {
return false;
}
+ count++;
address end = addresses.at(base + 1);
assert (start < end, "sanity");
offset = (uint)(end - start);
- n = write_bytes(&offset, sizeof(uint));
- if (n != sizeof(int)) {
+ if (!write_int(offset)) {
return false;
}
+ count++;
// write number of secondary and extra entries
- int count = range.count() - 2;
- n = write_bytes(&count, sizeof(int));
- if (n != sizeof(int)) {
+ int range_count = range.count() - 2;
+ if (!write_int(range_count)) {
return false;
}
- for (int j = 0; j < count; j++) {
+ count++;
+ for (int j = 0; j < range_count; j++) {
address next = addresses.at(base + 2 + j);
if (next != nullptr) {
// n.b. This maps next == end to the stub length which
@@ -1245,10 +2204,10 @@ bool AOTCodeCache::write_stub_data(CodeBlob &blob, AOTStubData *stub_data) {
// extra is the common handler target
offset = NULL_ADDRESS_MARKER;
}
- n = write_bytes(&offset, sizeof(uint));
- if (n != sizeof(int)) {
+ if (!write_int(offset)) {
return false;
}
+ count++;
}
if (log.is_enabled()) {
log.print_cr("======== wrote stub %s and %d addresses up to offset %d",
@@ -1259,10 +2218,11 @@ bool AOTCodeCache::write_stub_data(CodeBlob &blob, AOTStubData *stub_data) {
// we should have exhausted all stub ids in the blob
assert(stub_id == StubId::NO_STUBID, "sanity");
// write NO_STUBID as an end marker
- n = write_bytes(&stub_id, sizeof(StubId));
- if (n != sizeof(StubId)) {
+ if (!write_int(checked_cast(stub_id))) {
return false;
}
+ count++;
+ *count_ptr = count;
if (log.is_enabled()) {
log.print_cr("======== Stub data ends at offset %d", _write_position);
@@ -1288,72 +2248,137 @@ CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, c
if (AOTCodeEntry::is_blob(entry_kind) && !is_using_stub()) {
return nullptr;
}
- log_debug(aot, codecache, stubs)("Reading blob '%s' (id=%u, kind=%s) from AOT Code Cache", name, id, aot_code_entry_kind_name[entry_kind]);
+ log_debug(aot, codecache, stubs)("Loading blob '%s' (id=%u, kind=%s) from AOT Code Cache",
+ name, id, aot_code_entry_kind_name[entry_kind]);
AOTCodeEntry* entry = cache->find_entry(entry_kind, encode_id(entry_kind, id));
if (entry == nullptr) {
return nullptr;
}
- AOTCodeReader reader(cache, entry);
+ AOTCodeReader reader(cache, entry, nullptr);
CodeBlob* blob = reader.compile_code_blob(name, entry_kind, id, stub_data);
- log_debug(aot, codecache, stubs)("%sRead blob '%s' (id=%u, kind=%s) from AOT Code Cache",
- (blob == nullptr? "Failed to " : ""), name, id, aot_code_entry_kind_name[entry_kind]);
+ log_debug(aot, codecache, stubs)("%s blob '%s' (id=%u, kind=%s) from AOT Code Cache",
+ (blob == nullptr? "Failed to load" : "Loaded"), name, id,
+ aot_code_entry_kind_name[entry_kind]);
return blob;
}
-CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, const char* name) {
- assert(!AOTCodeEntry::is_blob(entry_kind),
- "wrong entry kind for numeric id %d", id);
- return load_code_blob(entry_kind, (uint)id, name, nullptr);
+CodeBlob* AOTCodeCache::load_adapter(uint id, const char* name) {
+ return load_code_blob(AOTCodeEntry::Adapter, id, name, nullptr);
}
CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, BlobId id) {
assert(AOTCodeEntry::is_single_stub_blob(entry_kind),
- "wrong entry kind for blob id %s", StubInfo::name(id));
+ "wrong entry kind for blob %u %s", (uint)id, StubInfo::name(id));
return load_code_blob(entry_kind, (uint)id, StubInfo::name(id), nullptr);
}
-CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, BlobId id, AOTStubData* stub_data) {
- assert(AOTCodeEntry::is_multi_stub_blob(entry_kind),
- "wrong entry kind for blob id %s", StubInfo::name(id));
- return load_code_blob(entry_kind, (uint)id, StubInfo::name(id), stub_data);
+CodeBlob* AOTCodeCache::load_multi_stub_blob(BlobId id, AOTStubData* stub_data) {
+ return load_code_blob(AOTCodeEntry::StubGenBlob, (uint)id, StubInfo::name(id), stub_data);
}
-CodeBlob* AOTCodeReader::compile_code_blob(const char* name, AOTCodeEntry::Kind entry_kind, int id, AOTStubData* stub_data) {
- uint entry_position = _entry->offset();
+// Cached code blob layout
+// - copy of CodeBlob
+// - reloc_count
+// - relocations
+// - oop maps
+// - multi-stub blob data
+// - count
+// - int[]
+// - id_for_relocations array
+// - asm_remarks
+// - dbg_strings
+// - name
- // Read name
- uint name_offset = entry_position + _entry->name_offset();
- uint name_size = _entry->name_size(); // Includes '/0'
- const char* stored_name = addr(name_offset);
+CodeBlob* AOTCodeReader::compile_code_blob(const char* name, AOTCodeEntry::Kind entry_kind, int id, AOTStubData* stub_data) {
+ bool is_multi_stub = AOTCodeEntry::is_multi_stub_blob(entry_kind);
- if (strncmp(stored_name, name, (name_size - 1)) != 0) {
+ // Read name. Name's bound checks were done in find_entry().
+ uint name_offset = _entry->name_offset();
+ const char* stored_name = (const char*)addr(name_offset);
+ if (strcmp(stored_name, name) != 0) {
log_warning(aot, codecache, stubs)("Saved blob's name '%s' is different from the expected name '%s'",
stored_name, name);
- set_lookup_failed(); // Skip this blob
+ set_lookup_failed("Saved blob's name is different"); // Skip this blob
return nullptr;
}
_name = stored_name;
- // Read archived code blob and related info
- uint offset = entry_position + _entry->blob_offset();
- CodeBlob* archived_blob = (CodeBlob*)addr(offset);
- offset += archived_blob->size();
+ // Name was recorded last in this entry. Use its offset as read limit.
+ _read_limit = name_offset;
+
+ // Read archived code blob and related info.
+ // Code's bound checks were done in find_entry().
+ assert(_entry->code_offset() == _read_position, "code offset for non-nmethod code blobs should be 0");
+ CodeBlob* archived_blob = (CodeBlob*)position_addr();
+ if (!update_read_position(archived_blob->size())) {
+ return nullptr;
+ }
+
+ if (!read_int(&_reloc_count)) {
+ return nullptr;
+ }
+ int blob_reloc_count = (int)(archived_blob->relocation_size() / sizeof(relocInfo));
+ if (!is_multi_stub &&_reloc_count != blob_reloc_count) {
+ log_warning(aot, codecache, stubs)("Saved blob '%s' has different relocation count %d vs cached %d ",
+ name, blob_reloc_count, _reloc_count);
+ set_lookup_failed("Saved blob's relocation count is different"); // Skip this blob
+ return nullptr;
+ }
+ bool load_relocations = (_reloc_count > 0);
- _reloc_count = *(int*)addr(offset);
- offset += sizeof(int);
- if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) {
- // position of relocs will have been aligned to heap word size so
- // we can install them into a code buffer
- offset = align_up(offset, DATA_ALIGNMENT);
+ if (load_relocations) {
+ if (is_multi_stub) {
+ // position of relocs will have been aligned to heap word size so
+ // we can install them into a code buffer
+ if (!align_read_position(DATA_ALIGNMENT)) {
+ return nullptr;
+ }
+ }
+ uint reloc_size = _reloc_count * sizeof(relocInfo);
+ if (!get_set_read_position(&_reloc_data, reloc_size)) {
+ return nullptr;
+ }
}
- _reloc_data = (address)addr(offset);
- offset += _reloc_count * sizeof(relocInfo);
- set_read_position(offset);
if (_entry->has_oop_maps()) {
_oop_maps = read_oop_map_set();
+ if (_oop_maps == nullptr) {
+ return nullptr;
+ }
+ }
+
+ if (stub_data != nullptr) {
+ if (!read_int(&_cached_stub_data_count)) {
+ return nullptr;
+ }
+ uint stub_data_size = (uint)_cached_stub_data_count * sizeof(uint);
+ if (!get_set_read_position((address*)&_cached_stub_data, stub_data_size)) {
+ return nullptr;
+ }
+ }
+
+ if (load_relocations) {
+ if (!read_int(&_id_for_reloc_count)) {
+ return nullptr;
+ }
+ uint id_for_reloc_size = (uint)_id_for_reloc_count * sizeof(uint);
+ if (!get_set_read_position((address*)&_id_for_reloc, id_for_reloc_size)) {
+ return nullptr;
+ }
+ }
+
+#ifndef PRODUCT
+ if (!read_asm_rem_and_dbg_str()) {
+ return nullptr;
+ }
+#endif
+
+ if (_read_position != _read_limit) {
+ assert(false, " _read_position '%u' != _read_limit '%u'", _read_position, _read_limit);
+ set_lookup_failed("Damaged AOT code blob: unaccounted data");
+ return nullptr;
}
// record current context for use by that callback
@@ -1382,15 +2407,17 @@ CodeBlob* AOTCodeReader::compile_code_blob(const char* name, AOTCodeEntry::Kind
void AOTCodeReader::restore(CodeBlob* code_blob) {
precond(AOTCodeCache::is_on_for_use());
precond(_name != nullptr);
- precond(_reloc_data != nullptr);
+ precond(_reloc_data != nullptr || _reloc_count == 0);
+ bool is_multi_stub = AOTCodeEntry::is_multi_stub_blob(_entry_kind);
code_blob->set_name(_name);
// Saved relocations need restoring except for the case of a
// multi-stub blob which has no runtime relocations. However, we may
// still have saved some (re-)load time relocs that were attached to
// the generator's code buffer. We don't attach them to the blob but
- // they get processed below by fix_relocations.
- if (!AOTCodeEntry::is_multi_stub_blob(_entry_kind)) {
+ // they get processed below by read_relocations.
+ if (!is_multi_stub) {
+ // Allocates space for mutable data and copy relocation data
code_blob->restore_mutable_data(_reloc_data);
}
code_blob->set_oop_maps(_oop_maps);
@@ -1404,6 +2431,15 @@ void AOTCodeReader::restore(CodeBlob* code_blob) {
}
// publish entries found either in stub_data or as offsets in blob
AOTCodeCache::publish_stub_addresses(*code_blob, blob_id, _stub_data);
+ } else if (code_blob->is_nmethod()) {
+ nmethod* nm = code_blob->as_nmethod();
+ nm->set_compile_id(_compile_id);
+ nm->set_aot_code_entry(_entry);
+ nm->set_immutable_data(_immutable_data);
+ // Restore metadata and oops sections which are used
+ // by read_relocations().
+ nm->copy_values(_oop_list);
+ nm->copy_values(_metadata_list);
}
// Now that all the entry points are in the address table we can
@@ -1422,7 +2458,7 @@ void AOTCodeReader::restore(CodeBlob* code_blob) {
// wrap it with a CodeBuffer and then reattach the relocs to the
// code buffer.
- if (AOTCodeEntry::is_multi_stub_blob(_entry_kind)) {
+ if (is_multi_stub) {
// the blob doesn't have any proper runtime relocs but we can
// reinstate the AOT-load time relocs we saved from the code
// buffer that generated this blob in a new code buffer and use
@@ -1432,21 +2468,21 @@ void AOTCodeReader::restore(CodeBlob* code_blob) {
relocInfo* locs = (relocInfo*)_reloc_data;
code_buffer.insts()->initialize_shared_locs(locs, _reloc_count);
code_buffer.insts()->set_locs_end(locs + _reloc_count);
- CodeSection *cs = code_buffer.code_section(CodeBuffer::SECT_INSTS);
+ CodeSection* cs = code_buffer.code_section(CodeBuffer::SECT_INSTS);
RelocIterator reloc_iter(cs);
- fix_relocations(code_blob, reloc_iter);
+ read_relocations(code_blob, reloc_iter);
}
} else {
// the AOT-load time relocs will be in the blob's restored relocs
RelocIterator reloc_iter(code_blob);
- fix_relocations(code_blob, reloc_iter);
+ read_relocations(code_blob, reloc_iter, _reloc_imm_oop_list, _reloc_imm_metadata_list);
}
#ifndef PRODUCT
code_blob->asm_remarks().init();
- read_asm_remarks(code_blob->asm_remarks());
+ restore_asm_remarks(code_blob->asm_remarks());
code_blob->dbg_strings().init();
- read_dbg_strings(code_blob->dbg_strings());
+ restore_dbg_strings(code_blob->dbg_strings());
#endif // PRODUCT
}
@@ -1465,13 +2501,15 @@ void AOTCodeReader::read_stub_data(CodeBlob* code_blob, AOTStubData* stub_data)
address blob_base = code_blob->code_begin();
uint blob_size = (uint)(code_blob->code_end() - blob_base);
- uint offset = align_read_int();
+ uint offset = (uint)((address)_cached_stub_data - _entry_buffer);
LogStreamHandle(Trace, aot, codecache, stubs) log;
if (log.is_enabled()) {
- log.print_cr("======== Stub data starts at offset %d", offset);
+ log.print_cr("======== Stub data starts at offset %u", offset);
}
+ int iter = 0;
// read stub and entries until we see NO_STUBID
- StubId stub_id = *(StubId*)addr(offset); offset += sizeof(StubId);
+ StubId stub_id = (StubId)_cached_stub_data[iter++];
+
// we ought to have at least one saved stub in the blob
assert(stub_id != StubId::NO_STUBID, "blob %s contains no stubs!", StubInfo::name(stub_data->blob_id()));
while (stub_id != StubId::NO_STUBID) {
@@ -1482,21 +2520,21 @@ void AOTCodeReader::read_stub_data(CodeBlob* code_blob, AOTStubData* stub_data)
assert(range.start_index() < 0, "repeated entry for stub %s", StubInfo::name(stub_id));
int address_base = addresses.length();
// start is an offset from the blob base
- uint start = *(uint*)addr(offset); offset += sizeof(uint);
+ uint start = _cached_stub_data[iter++];
assert(start < blob_size, "stub %s start offset %d exceeds buffer length %d", StubInfo::name(stub_id), start, blob_size);
address stub_start = blob_base + start;
addresses.append(stub_start);
// end is an offset from the stub start
- uint end = *(uint*)addr(offset); offset += sizeof(uint);
+ uint end = _cached_stub_data[iter++];
assert(start + end <= blob_size, "stub %s end offset %d exceeds remaining buffer length %d", StubInfo::name(stub_id), end, blob_size - start);
addresses.append(stub_start + end);
// read count of secondary entries plus extras
- int entries_count = *(int*)addr(offset); offset += sizeof(int);
+ int entries_count = _cached_stub_data[iter++];
assert(entries_count >= (StubInfo::entry_count(stub_id) - 1), "not enough entries for %s", StubInfo::name(stub_id));
for (int i = 0; i < entries_count; i++) {
// entry offset is an offset from the stub start less than or
// equal to end
- uint entry = *(uint*)addr(offset); offset += sizeof(uint);
+ uint entry = _cached_stub_data[iter++];
if (entry <= end) {
// entry addresses may not address end but extras can
assert(entry < end || i >= StubInfo::entry_count(stub_id),
@@ -1511,17 +2549,16 @@ void AOTCodeReader::read_stub_data(CodeBlob* code_blob, AOTStubData* stub_data)
}
if (log.is_enabled()) {
log.print_cr("======== read stub %s and %d addresses up to offset %d",
- StubInfo::name(stub_id), 2 + entries_count, offset);
+ StubInfo::name(stub_id), 2 + entries_count, (int)(offset + iter*sizeof(uint)));
}
range.init_entry(address_base, 2 + entries_count);
// move on to next stub or NO_STUBID
- stub_id = *(StubId*)addr(offset); offset += sizeof(StubId);
+ stub_id = (StubId)_cached_stub_data[iter++];
}
if (log.is_enabled()) {
- log.print_cr("======== Stub data ends at offset %d", offset);
+ log.print_cr("======== Stub data ends at offset %d", (int)(offset + iter * sizeof(uint)));
}
-
- set_read_position(offset);
+ assert(iter == _cached_stub_data_count, "%u != %u", iter, _cached_stub_data_count);
}
void AOTCodeCache::publish_external_addresses(GrowableArray& addresses) {
@@ -1582,53 +2619,688 @@ void AOTCodeCache::publish_stub_addresses(CodeBlob &code_blob, BlobId blob_id, A
}
}
-// ------------ process code and data --------------
+AOTCodeEntry* AOTCodeCache::store_nmethod(nmethod* nm, AbstractCompiler* compiler, bool for_preload) {
+ if (!is_dumping_code()) {
+ return nullptr;
+ }
+ assert(CDSConfig::is_dumping_aot_code(), "should be called only when allowed");
+ AOTCodeCache* cache = open_for_dump();
+ precond(cache != nullptr);
+ precond(!nm->is_osr_method()); // AOT compilation is requested only during AOT cache assembly phase
+ if (!compiler->is_c1() && !compiler->is_c2()) {
+ // Only c1 and c2 compilers
+ return nullptr;
+ }
+ int comp_level = nm->comp_level();
+ if (comp_level == CompLevel_full_profile) {
+ // Do not cache C1 compiles with full profile i.e. tier3
+ return nullptr;
+ }
+ assert(comp_level == CompLevel_simple || comp_level == CompLevel_limited_profile || comp_level == CompLevel_full_optimization, "must be");
-// Can't use -1. It is valid value for jump to iteself destination
-// used by static call stub: see NativeJump::jump_destination().
-#define BAD_ADDRESS_ID -2
+ TraceTime t1("Total time to store AOT code", &_t_totalStore, enable_timers(), false);
+ AOTCodeEntry* entry = cache->write_nmethod(nm, for_preload);
+ {
+ ResourceMark rm;
+ const char* name = nm->method()->name_and_sig_as_C_string();
+ const char* msg = (entry != nullptr) ? ": wrote to AOT Code Cache" :
+ "is skipped: store to AOT Code Cache failed";
+ log_info(aot, codecache, nmethod)("%d (A%s%d) '%s' AOT%s %s",
+ nm->compile_id(), (for_preload ? "P" : ""), comp_level, name,
+ (nm->has_clinit_barriers() ? ", has clinit barriers" : ""), msg);
+ }
+ return entry;
+}
-bool AOTCodeCache::write_relocations(CodeBlob& code_blob, RelocIterator& iter) {
- if (!align_write_int()) {
- return false;
+AOTCodeEntry* AOTCodeCache::write_nmethod(nmethod* nm, bool for_preload) {
+ assert(!nm->has_clinit_barriers() || (ClassInitBarrierMode > 0), "sanity");
+ _compile_id = nm->compile_id();
+ _comp_level = nm->comp_level();
+ Method* method = nm->method();
+ if (!AOTCacheAccess::can_generate_aot_code(method)) {
+ ResourceMark rm;
+ log_info(aot, codecache, nmethod)("%d (A%s%d) '%s' AOT is skipped: method is not in AOT cache",
+ compile_id(), (for_preload ? "P" : ""), comp_level(), method->name_and_sig_as_C_string());
+ assert(AOTCacheAccess::can_generate_aot_code(method), "sanity");
+ return nullptr;
}
- GrowableArray reloc_data;
- LogStreamHandle(Trace, aot, codecache, reloc) log;
- while (iter.next()) {
- int idx = reloc_data.append(0); // default value
- switch (iter.type()) {
- case relocInfo::none:
- break;
- case relocInfo::runtime_call_type: {
- // Record offset of runtime destination
- CallRelocation* r = (CallRelocation*)iter.reloc();
- address dest = r->destination();
- if (dest == r->addr()) { // possible call via trampoline on Aarch64
- dest = (address)-1; // do nothing in this case when loading this relocation
- }
- int id = _table->id_for_address(dest, iter, &code_blob);
- if (id == BAD_ADDRESS_ID) {
- return false;
- }
- reloc_data.at_put(idx, id);
- break;
- }
- case relocInfo::runtime_call_w_cp_type:
- log_debug(aot, codecache, reloc)("runtime_call_w_cp_type relocation is not implemented");
- return false;
- case relocInfo::external_word_type: {
- // Record offset of runtime target
- address target = ((external_word_Relocation*)iter.reloc())->target();
- int id = _table->id_for_address(target, iter, &code_blob);
- if (id == BAD_ADDRESS_ID) {
- return false;
- }
- reloc_data.at_put(idx, id);
+ InstanceKlass* holder = method->method_holder();
+ bool builtin_loader = holder->class_loader_data()->is_builtin_class_loader_data();
+ if (!builtin_loader) {
+ ResourceMark rm;
+ log_info(aot, codecache, nmethod)("%d (A%s%d) '%s' AOT is skipped: loaded by custom class loader %s",
+ compile_id(), (for_preload ? "P" : ""), comp_level(), method->name_and_sig_as_C_string(),
+ holder->class_loader_data()->loader_name());
+ assert(builtin_loader, "sanity");
+ return nullptr;
+ }
+
+ assert(!nm->has_clinit_barriers() || for_preload, "only preload code has clinit barriers");
+
+ if (!align_write()) {
+ return nullptr;
+ }
+
+ uint entry_position = _write_position;
+
+ uint id = (uint)cast_to_u4(AOTCacheAccess::to_narrow_ptr(nm->method()));
+ // Write id to compare with AOTCodeEntry::_id during production run.
+ if (!write_int(id)) {
+ return nullptr;
+ }
+ // Write name
+ uint name_offset = 0;
+ uint name_size = 0;
+ {
+ ResourceMark rm;
+ const char* name = method->name_and_sig_as_C_string();
+ log_info(aot, codecache, nmethod)("%d (A%s%d) '%s' AOT%s: writing to AOT Code Cache",
+ compile_id(), (for_preload ? "P" : ""), comp_level(), name,
+ (nm->has_clinit_barriers() ? ", has clinit barriers" : ""));
+
+ LogStreamHandle(Info, aot, codecache, loader) log;
+ if (log.is_enabled()) {
+ oop loader = holder->class_loader();
+ oop domain = holder->protection_domain();
+ stringStream ss;
+ ss.print("Holder: ");
+ holder->print_value_on(&ss);
+ ss.print(" loader: ");
+ if (loader == nullptr) {
+ ss.print("nullptr");
+ } else {
+ loader->print_value_on(&ss);
+ }
+ ss.print(" domain: ");
+ if (domain == nullptr) {
+ ss.print("nullptr");
+ } else {
+ domain->print_value_on(&ss);
+ }
+ log.print_cr("%s", ss.freeze());
+ }
+ name_offset = _write_position - entry_position;
+ name_size = (uint)strlen(name) + 1; // Includes '\0'
+ if (!write_bytes(name, name_size)) {
+ return nullptr;
+ }
+ }
+
+ // Write nmethod's code blob
+ if (!align_write()) {
+ return nullptr;
+ }
+ uint blob_offset = _write_position - entry_position;
+ address archive_buffer = reserve_bytes(nm->size());
+ if (archive_buffer == nullptr) {
+ return nullptr;
+ }
+ CodeBlob::archive_blob(nm, archive_buffer);
+
+ uint reloc_data_size = nm->relocation_size();
+ if (!write_int(reloc_data_size)) { // for verification on load
+ return nullptr;
+ }
+ if (!write_bytes((address)nm->relocation_begin(), reloc_data_size)) {
+ return nullptr;
+ }
+
+ // Write oops and metadata present in the nmethod's data region
+ if (!write_oops(nm)) {
+ if (lookup_failed() && !failed()) {
+ // Skip this method and reposition file
+ set_write_position(entry_position);
+ }
+ return nullptr;
+ }
+ if (!write_metadata(nm)) {
+ if (lookup_failed() && !failed()) {
+ // Skip this method and reposition file
+ set_write_position(entry_position);
+ }
+ return nullptr;
+ }
+
+ bool has_oop_maps = false;
+ if (nm->oop_maps() != nullptr) {
+ if (!write_oop_map_set(*nm)) {
+ return nullptr;
+ }
+ has_oop_maps = true;
+ }
+
+ uint immutable_data_size = nm->immutable_data_size();
+ if (!write_int(immutable_data_size)) { // for verification on load
+ return nullptr;
+ }
+ if (!write_bytes(nm->immutable_data_begin(), immutable_data_size)) {
+ return nullptr;
+ }
+
+ JavaThread* thread = JavaThread::current();
+ HandleMark hm(thread);
+ GrowableArray oop_list;
+ GrowableArray metadata_list;
+
+ nm->create_reloc_immediates_list(thread, oop_list, metadata_list);
+ if (!write_nmethod_reloc_immediates(oop_list, metadata_list)) {
+ if (lookup_failed() && !failed()) {
+ // Skip this method and reposition file
+ set_write_position(entry_position);
+ }
+ return nullptr;
+ }
+
+ RelocIterator iter(nm);
+ if (!write_relocations(*nm, iter, AOTAssertOnUnknownExternalAddress,
+ &oop_list, &metadata_list)) {
+ if (!failed()) {
+ // Skip this method and reposition file
+ set_write_position(entry_position);
+ }
+ return nullptr;
+ }
+
+#ifndef PRODUCT
+ // Write asm remarks and dbg strings after relocation info
+ if (!write_asm_rem_and_dbg_str(nm->asm_remarks(), nm->dbg_strings())) {
+ return nullptr;
+ }
+#endif /* PRODUCT */
+
+ uint entry_size = _write_position - entry_position;
+ AOTCodeEntry* entry = new (this) AOTCodeEntry(AOTCodeEntry::Nmethod, id,
+ entry_position, entry_size,
+ name_offset, name_size,
+ blob_offset, has_oop_maps,
+ comp_level(), compile_id(),
+ nm->has_clinit_barriers(), for_preload);
+ return entry;
+}
+
+bool AOTCodeCache::load_nmethod(ciEnv* env, ciMethod* target, int entry_bci, AbstractCompiler* compiler) {
+ precond(is_using_code());
+ assert(entry_bci == InvocationEntryBci, "unexpected entry_bci=%d", entry_bci);
+ CompileTask* task = env->task();
+ AOTCodeEntry* entry = task->aot_code_entry();
+ precond(entry != nullptr);
+ if (entry->not_entrant()) {
+ // AOT entry could be invalidated while task was in queue
+ task->set_failure_reason("AOT code entry was marked not-entrant while waiting in compilation queue");
+ return false;
+ }
+ TraceTime t1("Total time to load AOT code", &_t_totalLoad, enable_timers(), false);
+ task->mark_aot_load_start(os::elapsed_counter());
+
+ AOTCodeCache* cache = open_for_use();
+ if (cache == nullptr) {
+ return false;
+ }
+ AOTCodeReader reader(cache, entry, task);
+ bool success = reader.compile_nmethod(env, target, compiler);
+ if (reader.lookup_failed()) {
+ assert(!success, "sanity");
+ task->set_failure_reason(reader.lookup_failure());
+ }
+ if (success) {
+ assert(task->is_success(), "sanity");
+ task->set_num_inlined_bytecodes(entry->num_inlined_bytecodes());
+ } else if (!env->failing() || !env->codecache_full()) {
+ task->set_failure_reason(reader.lookup_failure());
+ // Invalidate AOT entry if there were issues in restoring AOT code or in dependencies
+ entry->set_load_fail();
+ entry->set_not_entrant();
+ }
+ task->mark_aot_load_finish(os::elapsed_counter());
+ return success;
+}
+
+// Cached nmethod layout
+// - id (encoded Method*)
+// - name of method
+// - copy of nmethod
+// - relocation_size
+// - relocations
+// - oops
+// - metadata
+// - oop maps
+// - immutable_data_size
+// - immutable_data
+// - embedded oops and metadata
+// - id_for_relocations array
+// - asm_remarks
+// - dbg_strings
+
+bool AOTCodeReader::compile_nmethod(ciEnv* env, ciMethod* target, AbstractCompiler* compiler) {
+ CompileTask* task = env->task();
+ nmethod* nm = nullptr;
+
+ // Read ID.
+ uint cached_id;
+ if (!read_int((int*)&cached_id)) {
+ return false;
+ }
+ // Read name next. Name's bound checks were done in find_entry().
+ const char* cached_name;
+ if (!get_set_read_position((address*)&cached_name, _entry->name_size())) {
+ return false;
+ }
+ bool preload = task->is_aot_preload();
+ {
+ VM_ENTRY_MARK;
+ ResourceMark rm;
+ methodHandle method(THREAD, target->get_Method());
+ const char* target_name = method->name_and_sig_as_C_string();
+ uint id = (uint)cast_to_u4(AOTCacheAccess::method_to_narrow_ptr(method()));
+ if (id != cached_id) {
+ log_warning(aot, codecache, nmethod)("Cached method id '%u' is different from the expected '%u'",
+ cached_id, id);
+ set_lookup_failed("Cached method id is different"); // Skip this nmethod
+ return false;
+ }
+ if (strcmp(cached_name, target_name) != 0) {
+ log_warning(aot, codecache, nmethod)("Saved method name '%s' is different from the expected name '%s'",
+ cached_name, target_name);
+ set_lookup_failed("Saved method name is different"); // Skip this nmethod
+ return false;
+ }
+ bool clinit_brs = _entry->has_clinit_barriers();
+ log_info(aot, codecache, nmethod)("%d (A%s%d) '%s' AOT nmethod id: " UINT32_FORMAT_X_0 "%s: %sloading from AOT Code Cache",
+ compile_id(), (preload ? "P" : ""), comp_level(), target_name, id,
+ (clinit_brs ? ", has clinit barriers" : ""), (preload ? "pre" : ""));
+ }
+
+ if (!align_read_position(DATA_ALIGNMENT)) {
+ return false;
+ }
+ if (_entry->code_offset() != _read_position) {
+ set_lookup_failed("Cached nmethod code offset is wrong"); // Skip this nmethod
+ return false;
+ }
+ assert(_entry->code_offset() == _read_position, "code for nmethod should be after name");
+ nmethod* archived_nm = (nmethod*)position_addr();
+ if (!update_read_position(archived_nm->size())) {
+ return false;
+ }
+
+ OopRecorder* oop_recorder = new OopRecorder(env->arena());
+ env->set_oop_recorder(oop_recorder);
+
+ // Set fields for AOTCodeReader::restore()
+ _name = "nmethod";
+
+ int reloc_size;
+ if (!read_int(&reloc_size)) {
+ return false;
+ }
+ if (reloc_size != archived_nm->relocation_size()) {
+ set_lookup_failed("Cached nmethod relocation size is wrong"); // Skip this nmethod
+ return false;
+ }
+ if (!get_set_read_position(&_reloc_data, (uint)reloc_size)) {
+ return false;
+ }
+
+ // Read oops and metadata
+ VM_ENTRY_MARK
+ GrowableArray oop_list;
+ GrowableArray metadata_list;
+
+ if (!read_oop_metadata_list(THREAD, oop_list, metadata_list, oop_recorder)) {
+ return false;
+ }
+ _oop_list = &oop_list;
+ _metadata_list = &metadata_list;
+
+ if (_entry->has_oop_maps()) {
+ _oop_maps = read_oop_map_set();
+ if (_oop_maps == nullptr) {
+ return false;
+ }
+ }
+
+ int immutable_data_size;
+ if (!read_int(&immutable_data_size)) {
+ return false;
+ }
+ if (immutable_data_size != archived_nm->immutable_data_size()) {
+ set_lookup_failed("Cached nmethod immutable_data size is wrong"); // Skip this nmethod
+ return false;
+ }
+ if (!get_set_read_position(&_immutable_data, (uint)immutable_data_size)) {
+ return false;
+ }
+
+ GrowableArray reloc_immediate_oop_list;
+ GrowableArray reloc_immediate_metadata_list;
+ if (!read_oop_metadata_list(THREAD, reloc_immediate_oop_list, reloc_immediate_metadata_list, nullptr)) {
+ return false;
+ }
+ _reloc_imm_oop_list = &reloc_immediate_oop_list;
+ _reloc_imm_metadata_list = &reloc_immediate_metadata_list;
+
+ if (!read_int(&_id_for_reloc_count)) {
+ return false;
+ }
+ uint id_for_reloc_size = (uint)_id_for_reloc_count * sizeof(uint);
+ if (!get_set_read_position((address*)&_id_for_reloc, id_for_reloc_size)) {
+ return false;
+ }
+
+#ifndef PRODUCT
+ if (!read_asm_rem_and_dbg_str()) {
+ return false;
+ }
+#endif
+
+ if (_read_position != _read_limit) {
+ assert(false, " _read_position '%u' != _read_limit '%u'", _read_position, _read_limit);
+ set_lookup_failed("Damaged AOT nmethod: unaccounted data");
+ return false;
+ }
+
+ // Read Dependencies (compressed already)
+ Dependencies* dependencies = new Dependencies(env);
+ dependencies->set_content(_immutable_data, archived_nm->dependencies_size());
+ env->set_dependencies(dependencies);
+
+ TraceTime t1("Total time to register AOT nmethod", &_t_totalRegister, enable_timers(), false);
+ bool install_code = !VerifyAOTCode;
+ nm = env->register_aot_method(THREAD, target, compiler, archived_nm, this, install_code);
+
+ if (VerifyAOTCode && !env->failing()) {
+ // AOT code is not installed in CodeCache with VerifyAOTCode.
+ // Report AOT code sizes as the task result.
+ _entry->set_verified();
+ task->mark_success();
+ task->set_nm_content_size(archived_nm->content_size());
+ task->set_nm_insts_size(archived_nm->insts_size());
+ task->set_nm_total_size(archived_nm->total_size());
+ log_info(aot, codecache, nmethod)("%d (A%s%d) '%s': Verified nmethod from AOT Code Cache",
+ compile_id(), (preload ? "P" : ""), comp_level(), cached_name);
+ return true;
+ }
+ bool success = task->is_success();
+ if (success) {
+ log_info(aot, codecache, nmethod)("%d (A%s%d) '%s': Loaded nmethod from AOT Code Cache",
+ compile_id(), (preload ? "P" : ""), comp_level(), cached_name);
+#ifdef ASSERT
+ LogStreamHandle(Debug, aot, codecache, nmethod) log;
+ if (log.is_enabled()) {
+ FlagSetting fs(PrintRelocations, true);
+ nm->print_on(&log);
+ nm->decode2(&log);
+ }
+#endif
+ }
+
+ return success;
+}
+
+bool skip_preload(methodHandle mh) {
+ if (!mh->method_holder()->is_loaded()) {
+ return true;
+ }
+ CompilerDirectiveMatcher matcher(mh, CompLevel_full_optimization);
+ DirectiveSet* directives = matcher.directive_set();
+ if (directives->DontPreloadOption || directives->ExcludeOption) {
+ LogStreamHandle(Info, aot, codecache, init) log;
+ if (log.is_enabled()) {
+ ResourceMark rm;
+ stringStream ss;
+ ss.print("Exclude preloading code for ");
+ mh->print_value_on(&ss);
+ log.print_cr("%s", ss.freeze());
+ }
+ return true;
+ }
+ return false;
+}
+
+void AOTCodeCache::preload_code(JavaThread* thread) {
+ if (!is_using_code()) {
+ return;
+ }
+ if (!CompilerConfig::is_c2_enabled()) {
+ log_debug(aot, codecache, init)("AOT preload code skipped: C2 compiler disabled");
+ return;
+ }
+
+ if (skip_aot_code(CompLevel_preload_optimization)) {
+ return; // no preloaded code (level 5);
+ }
+
+ if (CompilationPolicy::compiler_count(CompLevel_full_optimization) == 0) {
+ // Since we reuse the CompilerBroker API to install AOT code, we're required to have a JIT compiler for the
+ // level we want (that is CompLevel_full_optimization).
+ return;
+ }
+
+#ifdef COMPILER2
+ // Check C2 flags
+ if (!_cache->_load_header->verify_c2_state()) {
+ return;
+ }
+#endif
+
+ _cache->preload_aot_code(thread);
+}
+
+void AOTCodeCache::preload_aot_code(TRAPS) {
+ TraceTime t1("Total time to preload AOT code", &_t_totalPreload, enable_timers(), false);
+ assert(_for_use, "sanity");
+ uint preload_entries_count = _load_header->preload_entries_count();
+ if (preload_entries_count > 0) {
+ load_info_log().print_cr("Read %d preload entries from AOT Code Cache", preload_entries_count);
+ uint entries_offset = _load_header->preload_entries_offset();
+ // Boundaries of preload code
+ uint low_bound = entries_offset + preload_entries_count * sizeof(AOTCodeEntry);
+ uint high_bound = _load_header->entries_offset(); // Following section
+ AOTCodeEntry* preload_entry = (AOTCodeEntry*)addr(entries_offset);
+ // AOTCodePreloadStop is inclusive
+ uint last_preload_index = MIN2(preload_entries_count - 1, AOTCodePreloadStop);
+ for (uint i = AOTCodePreloadStart; i <= last_preload_index; i++) {
+ AOTCodeEntry* entry = &preload_entry[i];
+ if (!entry->for_preload()) {
+ continue;
+ }
+ if (!verify_nmethod_entry(entry, i, low_bound, high_bound)) {
+ return; // Data is damaged, skip the rest of entries
+ }
+ uint entry_offset = entry->offset();
+ uint entry_size = entry->size();
+
+ // Check entry->id() vs cached id - should match
+ uint cached_id = *(uint*)addr(entry_offset);
+ if (!check_entry(AOTCodeEntry::Nmethod, cached_id, CompLevel_full_optimization, entry)) {
+ continue;
+ }
+ Method* m = AOTCacheAccess::narrow_ptr_to_method(cast_from_u4(entry->id()));
+ if (m == nullptr) {
+ log_warning(aot, codecache, init)("Damaged method id '%u' in preload AOT code entry %u " INTPTR_FORMAT " [%u + %u]", entry->id(), i, p2i(entry), entry_offset, entry_size);
+ continue;
+ }
+ methodHandle mh(THREAD, m);
+ if (skip_preload(mh)) {
+ continue; // Exclude preloading for this method
+ }
+ assert(mh->method_holder()->is_loaded(), "");
+ if (!mh->method_holder()->is_linked()) {
+ ResourceMark rm;
+ log_debug(aot, codecache, init)("Preload AOT code for %s skipped: method holder is not linked",
+ mh->name_and_sig_as_C_string());
+ continue; // skip
+ }
+#ifdef COMPILER2
+ if (entry->has_vectors() && !EnableVectorSupport) {
+ log_debug(aot, codecache, init)("AOT preload code skipped: incompatible EnableVectorSupport: true vs current %u", EnableVectorSupport);
+ continue;
+ }
+#endif
+ CompileBroker::preload_aot_method(mh, entry, CHECK);
+ }
+ }
+}
+
+// ------------ process code and data --------------
+
+// Do nothing for relocation when:
+// - call's destination is trampoline in nmethod
+// - jump's destination is address of the instruction itself
+// - jump's destination is (address)-1
+// See NativeJump::jump_destination() for example.
+
+#define NO_RELOCATION_ID -1
+
+// Bad address - skip this method compilation.
+
+#define BAD_ADDRESS_ID -2
+
+// The next two functions, write_relocations and read_relocations,
+// handle relocation information for AOT code. The relocations are written
+// by the compiler backend during AOT cache assembly. Their targeting information
+// gets re-encoded into the AOT cache so that the relocations can be restored later
+// during the production run.
+//
+// Relocs targeting oops and metadata record a global list index.
+// Relocs targeting internal addresses record a local offset.
+// Relocs targeting external addresses (including calls) record a global address ID.
+// Certain special non-address constants are treated like external addresses.
+// In all cases, the reloc is edited to replace a live address with a small integer,
+// as the nmethod is placed into the AOT cache. During the production run, the process
+// is reversed, as the small integers are decoded back into live addresses.
+//
+// Consider a later refactoring cleanup that moves more of this logic into
+// relocInfo.cpp. Perhaps relocInfo.cpp should "know more about" the external
+// address IDs and oop indexes? Perhaps we want a protocol for relocs
+// named [un]pack_aot_data?
+// [[PR 30778 COMMENT: to fit the writer/reader pattern better, here is a proposed
+// rename write_id_for_relocations/restore_relocations -> [write/read]_relocations.
+// It is a pure name change, but we can save for after the PR if desired.]]
+bool AOTCodeCache::write_relocations(CodeBlob& code_blob, RelocIterator& iter,
+ bool assert_for_unknown_external_address,
+ GrowableArray* oop_list,
+ GrowableArray* metadata_list) {
+ if (!align_write_int()) {
+ return false;
+ }
+ GrowableArray reloc_data;
+ LogStreamHandle(Trace, aot, codecache, reloc) log;
+ while (iter.next()) {
+ int idx = reloc_data.append(0); // default value
+ switch (iter.type()) {
+ case relocInfo::none:
+ break;
+ case relocInfo::oop_type: {
+ oop_Relocation* r = (oop_Relocation*)iter.reloc();
+ if (r->oop_is_immediate()) {
+ assert(oop_list != nullptr, "sanity check");
+ // store index of oop in the reloc immediate oop list
+ Handle h(JavaThread::current(), r->oop_value());
+ int oop_idx = oop_list->find(h);
+ assert(oop_idx != -1, "sanity check");
+ reloc_data.at_put(idx, (uint)oop_idx);
+ }
+ break;
+ }
+ case relocInfo::metadata_type: {
+ metadata_Relocation* r = (metadata_Relocation*)iter.reloc();
+ if (r->metadata_is_immediate()) {
+ assert(metadata_list != nullptr, "sanity check");
+ // store index of metadata in the reloc immediate metadata list
+ int metadata_idx = metadata_list->find(r->metadata_value());
+ assert(metadata_idx != -1, "sanity check");
+ reloc_data.at_put(idx, (uint)metadata_idx);
+ }
+ break;
+ }
+ case relocInfo::virtual_call_type: // Fall through. They all call resolve_*_call blobs.
+ case relocInfo::opt_virtual_call_type:
+ case relocInfo::static_call_type:
+ case relocInfo::runtime_call_type: {
+ CallRelocation* r = (CallRelocation*)iter.reloc();
+ address dest = r->destination();
+ int id;
+ if (dest == r->addr() || dest == (address)-1) {
+ // Unresolved jumps, see NativeJump::jump_destination() for example.
+ // Do nothing in this case when loading this relocation
+ id = NO_RELOCATION_ID;
+ } else {
+#ifdef USE_TRAMPOLINE_STUB_FIX_OWNER
+ if (code_blob.is_nmethod()) {
+ address tr = trampoline_stub_Relocation::get_trampoline_for(r->addr(), code_blob.as_nmethod());
+ // During assembly phase call's target should be trampoline.
+ // See trampoline_stub_Relocation::pd_fix_owner_after_move().
+ if (tr != nullptr) {
+ if (nativeCall_at(r->addr())->raw_destination() != tr) {
+ return false; // Skip this entry
+ }
+ id = NO_RELOCATION_ID;
+ reloc_data.at_put(idx, id);
+ break;
+ }
+ }
+#endif
+ id = _table->id_for_address(dest, iter, &code_blob, true /* assert_for_unknown_address */);
+ if (id == BAD_ADDRESS_ID) {
+ return false;
+ }
+ }
+ reloc_data.at_put(idx, id);
+ break;
+ }
+ case relocInfo::runtime_call_w_cp_type:
+ log_debug(aot, codecache, reloc)("runtime_call_w_cp_type relocation is not implemented");
+ return false;
+ case relocInfo::trampoline_stub_type: {
+ address dest = ((trampoline_stub_Relocation*)iter.reloc())->destination();
+ int id = _table->id_for_address(dest, iter, &code_blob, true /* assert_for_unknown_address */);
+ if (id == BAD_ADDRESS_ID) {
+ return false;
+ }
+ reloc_data.at_put(idx, id);
+ break;
+ }
+ case relocInfo::static_stub_type:
+ break;
+ case relocInfo::external_word_type: {
+ // Record offset of runtime target
+ address target = ((external_word_Relocation*)iter.reloc())->target();
+ int id = _table->id_for_address(target, iter, &code_blob, assert_for_unknown_external_address);
+ if (id == BAD_ADDRESS_ID) {
+ return false;
+ }
+ reloc_data.at_put(idx, id);
+ break;
+ }
+ case relocInfo::internal_word_type: {
+ address target = ((internal_word_Relocation*)iter.reloc())->target();
+ // assert to make sure that delta fits into 32 bits
+ assert(CodeCache::contains((void *)target), "Wrong internal_word_type relocation");
+ uint delta = (uint)(target - code_blob.content_begin());
+ reloc_data.at_put(idx, delta);
+ break;
+ }
+ case relocInfo::section_word_type: {
+ address target = ((section_word_Relocation*)iter.reloc())->target();
+ assert(CodeCache::contains((void *)target), "Wrong section_word_type relocation");
+ uint delta = (uint)(target - code_blob.content_begin());
+ reloc_data.at_put(idx, delta);
+ break;
+ }
+ case relocInfo::patchable_barrier_type: // fall through
+ case relocInfo::barrier_type: {
+ // Patched by GC during nmethod registration
+ assert(code_blob.is_nmethod(), "Only nmethod has barrier relocation");
+ if (!code_blob.is_nmethod()) {
+ log_debug(aot, codecache, reloc)("barrier relocation is used only for nmethods");
+ return false;
+ }
break;
}
- case relocInfo::internal_word_type:
+ case relocInfo::poll_type:
break;
- case relocInfo::section_word_type:
+ case relocInfo::poll_return_type:
break;
case relocInfo::post_call_nop_type:
break;
@@ -1645,7 +3317,9 @@ bool AOTCodeCache::write_relocations(CodeBlob& code_blob, RelocIterator& iter) {
// Write additional relocation data: uint per relocation
// Write the count first
int count = reloc_data.length();
- write_bytes(&count, sizeof(int));
+ if (!write_int(count)) {
+ return false;
+ }
if (log.is_enabled()) {
log.print_cr("======== extra relocations count=%d", count);
log.print( " {");
@@ -1654,8 +3328,7 @@ bool AOTCodeCache::write_relocations(CodeBlob& code_blob, RelocIterator& iter) {
for (GrowableArrayIterator iter = reloc_data.begin();
iter != reloc_data.end(); ++iter) {
uint value = *iter;
- int n = write_bytes(&value, sizeof(uint));
- if (n != sizeof(uint)) {
+ if (!write_int(value)) {
return false;
}
if (log.is_enabled()) {
@@ -1667,113 +3340,902 @@ bool AOTCodeCache::write_relocations(CodeBlob& code_blob, RelocIterator& iter) {
}
}
}
- if (log.is_enabled()) {
- log.print_cr("}");
- }
+ if (log.is_enabled()) {
+ log.print_cr("}");
+ }
+ return true;
+}
+
+void AOTCodeReader::read_relocations(CodeBlob *code_blob, RelocIterator& iter,
+ GrowableArray* oop_list,
+ GrowableArray* metadata_list) {
+ uint* reloc_data = _id_for_reloc;
+
+ LogStreamHandle(Trace, aot, codecache, reloc) log;
+ if (log.is_enabled()) {
+ log.print_cr("======== extra relocations count=%d", _id_for_reloc_count);
+ log.print(" {");
+ for(int i = 0; i < _id_for_reloc_count; i++) {
+ if (i == 0) {
+ log.print("%d", reloc_data[i]);
+ } else {
+ log.print(", %d", reloc_data[i]);
+ }
+ }
+ log.print_cr("}");
+ }
+
+ int j = 0;
+ while (iter.next()) {
+ switch (iter.type()) {
+ case relocInfo::none:
+ break;
+ case relocInfo::oop_type: {
+ assert(code_blob->is_nmethod(), "sanity check");
+ oop_Relocation* r = (oop_Relocation*)iter.reloc();
+ if (r->oop_is_immediate()) {
+ assert(oop_list != nullptr, "sanity check");
+ Handle h = oop_list->at(reloc_data[j]);
+ r->set_value(cast_from_oop(h()));
+ } else {
+ r->fix_oop_relocation();
+ }
+ break;
+ }
+ case relocInfo::metadata_type: {
+ assert(code_blob->is_nmethod(), "sanity check");
+ metadata_Relocation* r = (metadata_Relocation*)iter.reloc();
+ Metadata* m;
+ if (r->metadata_is_immediate()) {
+ assert(metadata_list != nullptr, "sanity check");
+ m = metadata_list->at(reloc_data[j]);
+ } else {
+ // Get already updated value from nmethod.
+ int index = r->metadata_index();
+ m = code_blob->as_nmethod()->metadata_at(index);
+ }
+ r->set_value((address)m);
+ break;
+ }
+ case relocInfo::virtual_call_type: // Fall through. They all call resolve_*_call blobs.
+ case relocInfo::opt_virtual_call_type:
+ case relocInfo::static_call_type:
+ case relocInfo::runtime_call_type: {
+ int id = reloc_data[j];
+ if (id != NO_RELOCATION_ID) { // Skip relocation with such ID
+ address dest = _cache->address_for_id(id);
+ ((CallRelocation*)iter.reloc())->set_destination(dest);
+ }
+ break;
+ }
+ case relocInfo::runtime_call_w_cp_type:
+ // this relocation should not be in cache (see write_relocations)
+ assert(false, "runtime_call_w_cp_type relocation is not implemented");
+ break;
+ case relocInfo::trampoline_stub_type: {
+ int id = reloc_data[j];
+ address dest = _cache->address_for_id(id);
+ trampoline_stub_Relocation* r = (trampoline_stub_Relocation*)iter.reloc();
+ r->set_destination(dest);
+#ifdef USE_TRAMPOLINE_STUB_FIX_OWNER
+ if (code_blob->is_nmethod()) {
+ assert(nativeCall_at(r->owner())->raw_destination() == r->addr(),
+ "Unpatched call's target should be trampoline");
+ // Change call's destination to direct target if distance allows.
+ r->fix_owner_after_move();
+ }
+#endif // USE_TRAMPOLINE_STUB_FIX_OWNER
+ break;
+ }
+ case relocInfo::static_stub_type:
+ break;
+ case relocInfo::external_word_type: {
+ address target = _cache->address_for_id(reloc_data[j]);
+ // Add external address to global table
+ int index = ExternalsRecorder::find_index(target);
+ // Update index in relocation
+ Relocation::add_jint(iter.data(), index);
+ external_word_Relocation* reloc = (external_word_Relocation*)iter.reloc();
+ assert(reloc->target() == target, "sanity");
+ reloc->set_value(target); // Patch address in the code
+ break;
+ }
+ case relocInfo::internal_word_type: {
+ uint delta = reloc_data[j];
+ internal_word_Relocation* r = (internal_word_Relocation*)iter.reloc();
+ r->fix_relocation_after_aot_load(code_blob->content_begin(), delta);
+ break;
+ }
+ case relocInfo::section_word_type: {
+ uint delta = reloc_data[j];
+ section_word_Relocation* r = (section_word_Relocation*)iter.reloc();
+ r->fix_relocation_after_aot_load(code_blob->content_begin(), delta);
+ break;
+ }
+ case relocInfo::patchable_barrier_type: // fall through
+ case relocInfo::barrier_type: {
+ // Patched by GC during nmethod registration
+ assert(code_blob->is_nmethod(), "Only nmethod has barrier relocation");
+ break;
+ }
+ case relocInfo::poll_type:
+ break;
+ case relocInfo::poll_return_type:
+ break;
+ case relocInfo::post_call_nop_type:
+ break;
+ default:
+ assert(false,"relocation %d unimplemented", (int)iter.type());
+ break;
+ }
+ if (log.is_enabled()) {
+ iter.print_current_on(&log);
+ }
+ j++;
+ }
+ assert(j == _id_for_reloc_count, "%d != %d", j, _id_for_reloc_count);
+}
+
+bool AOTCodeCache::write_nmethod_reloc_immediates(GrowableArray& oop_list, GrowableArray& metadata_list) {
+ int count = oop_list.length();
+ if (!write_int(count)) {
+ return false;
+ }
+ for (GrowableArrayIterator iter = oop_list.begin();
+ iter != oop_list.end(); ++iter) {
+ Handle h = *iter;
+ if (!write_oop(h())) {
+ return false;
+ }
+ }
+
+ count = metadata_list.length();
+ if (!write_int(count)) {
+ return false;
+ }
+ for (GrowableArrayIterator iter = metadata_list.begin();
+ iter != metadata_list.end(); ++iter) {
+ Metadata* m = *iter;
+ if (!write_metadata(m)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AOTCodeCache::write_metadata(nmethod* nm) {
+ int count = nm->metadata_count()-1;
+ if (!write_int(count)) {
+ return false;
+ }
+ for (Metadata** p = nm->metadata_begin(); p < nm->metadata_end(); p++) {
+ if (!write_metadata(*p)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AOTCodeCache::write_metadata(Metadata* m) {
+ if (m == nullptr) {
+ DataKind kind = DataKind::Null;
+ if (!write_kind(kind)) {
+ return false;
+ }
+ } else if (m == (Metadata*)Universe::non_oop_word()) {
+ DataKind kind = DataKind::No_Data;
+ if (!write_kind(kind)) {
+ return false;
+ }
+ } else if (m->is_klass()) {
+ if (!write_klass((Klass*)m)) {
+ return false;
+ }
+ } else if (m->is_method()) {
+ if (!write_method((Method*)m)) {
+ return false;
+ }
+ } else if (m->is_methodCounters()) {
+ DataKind kind = DataKind::MethodCnts;
+ if (!write_kind(kind)) {
+ return false;
+ }
+ if (!write_method(((MethodCounters*)m)->method())) {
+ return false;
+ }
+ log_debug(aot, codecache, metadata)("%d (A%d): Wrote MethodCounters : " INTPTR_FORMAT, compile_id(), comp_level(), p2i(m));
+ } else { // Not supported
+ fatal("metadata : " INTPTR_FORMAT " unimplemented", p2i(m));
+ return false;
+ }
+ return true;
+}
+
+Metadata* AOTCodeReader::read_metadata(JavaThread* thread) {
+ DataKind kind;
+ if (!read_kind(&kind)) {
+ return nullptr;
+ }
+ Metadata* m = nullptr;
+ if (kind == DataKind::Null) {
+ m = (Metadata*)nullptr;
+ } else if (kind == DataKind::No_Data) {
+ m = (Metadata*)Universe::non_oop_word();
+ } else if (kind == DataKind::Klass) {
+ m = (Metadata*)read_klass(thread);
+ } else if (kind == DataKind::Method) {
+ m = (Metadata*)read_method();
+ } else if (kind == DataKind::MethodCnts) {
+ if (!read_kind(&kind)) {
+ return nullptr;
+ }
+ assert(kind == DataKind::Method, "%d is wrong kind for method", (int)kind);
+ m = (Metadata*)read_method();
+ if (m != nullptr) {
+ Method* method = (Method*)m;
+ m = method->get_method_counters(Thread::current());
+ if (m == nullptr) {
+ set_lookup_failed("Failed to read MethodCounters");
+ log_debug(aot, codecache, metadata)("%d (A%d): Failed to read MethodCounters", compile_id(), comp_level());
+ } else {
+ log_debug(aot, codecache, metadata)("%d (A%d): Read MethodCounters : " INTPTR_FORMAT, compile_id(), comp_level(), p2i(m));
+ }
+ }
+ } else {
+ set_lookup_failed("Unknown metadata kind");
+ log_debug(aot, codecache, metadata)("%d (A%d): Unknown metadata's kind: %d", compile_id(), comp_level(), (int)kind);
+ }
+ return m;
+}
+
+bool AOTCodeCache::write_method(Method* method) {
+ ResourceMark rm; // To method's name printing
+ if (AOTCacheAccess::can_generate_aot_code(method)) {
+ DataKind kind = DataKind::Method;
+ if (!write_kind(kind)) {
+ return false;
+ }
+ narrowPtr method_narrow_ptr = AOTCacheAccess::to_narrow_ptr(method);
+ if (!write_int((int)checked_cast(method_narrow_ptr))) {
+ return false;
+ }
+ log_debug(aot, codecache, metadata)("%d (A%d): Registered method: %s @ 0x%08x",
+ compile_id(), comp_level(), method->name_and_sig_as_C_string(), cast_to_u4(method_narrow_ptr));
+ return true;
+ }
+ log_debug(aot, codecache, metadata)("%d (A%d): Method can not be registered: %s",
+ compile_id(), comp_level(), method->name_and_sig_as_C_string());
+ set_lookup_failed();
+ return false;
+}
+
+Method* AOTCodeReader::read_method() {
+ narrowPtr method_narrow_ptr;
+ if (!read_int((int*)&method_narrow_ptr)) {
+ return nullptr;
+ }
+
+ Method* m = AOTCacheAccess::narrow_ptr_to_method(method_narrow_ptr);
+ if (m == nullptr || !AOTMetaspace::in_aot_cache((address)m)) {
+ // Something changed in CDS
+ set_lookup_failed("Method is not in AOT cache");
+ log_debug(aot, codecache, metadata)("%d (A%d): Lookup failed for method: " INTPTR_FORMAT " is not in AOT cache",
+ compile_id(), comp_level(), p2i((address)m));
+ return nullptr;
+ }
+ assert(m->is_method(), "sanity");
+ ResourceMark rm;
+ if (m->is_old()) {
+ set_lookup_failed("Method was redefined");
+ log_debug(aot, codecache, metadata)("%d (A%d): Lookup failed for '%s': method was redefined",
+ compile_id(), comp_level(), m->name_and_sig_as_C_string());
+ return nullptr;
+ }
+ Klass* k = m->method_holder();
+ if (!k->is_instance_klass()) {
+ set_lookup_failed("Method holder is not instance klass");
+ log_debug(aot, codecache, metadata)("%d (A%d): Lookup failed for '%s': holder %s is not instance klass",
+ compile_id(), comp_level(), m->name_and_sig_as_C_string(), k->external_name());
+ return nullptr;
+ } else if (!AOTMetaspace::in_aot_cache((address)k)) {
+ set_lookup_failed("Method holder is not in AOT cache");
+ log_debug(aot, codecache, metadata)("%d (A%d): Lookup failed for '%s': holder %s is not in AOT cache",
+ compile_id(), comp_level(), m->name_and_sig_as_C_string(), k->external_name());
+ return nullptr;
+ } else if (!InstanceKlass::cast(k)->is_loaded() || !InstanceKlass::cast(k)->is_linked()) {
+ set_lookup_failed("Method holder is not loaded or not linked");
+ log_debug(aot, codecache, metadata)("%d (A%d): Lookup failed for '%s': holder %s is not %s",
+ compile_id(), comp_level(), m->name_and_sig_as_C_string(), k->external_name(),
+ (!InstanceKlass::cast(k)->is_loaded() ? "loaded" : "linked"));
+ return nullptr;
+ }
+ log_debug(aot, codecache, metadata)("%d (A%d): Found method '%s' in AOT cache",
+ compile_id(), comp_level(), m->name_and_sig_as_C_string());
+ return m;
+}
+
+bool AOTCodeCache::write_klass(Klass* klass) {
+ uint array_dim = 0;
+ uint array_desc = ArrayProperties::Invalid().value();
+ bool can_write = true;
+ if (klass->is_objArray_klass()) {
+ // We must check klass and its bottom_klass.
+ can_write = AOTCacheAccess::can_generate_aot_code(klass);
+ ObjArrayKlass* oak = ObjArrayKlass::cast(klass);
+ array_dim = oak->dimension();
+ if (klass->is_refined_objArray_klass()) {
+ // Preserve property of object array
+ Klass::KlassKind kind;
+ LayoutKind layout;
+ if (klass->is_flatArray_klass()) {
+ kind = Klass::FlatArrayKlassKind;
+ layout = FlatArrayKlass::cast(klass)->layout_kind();
+ } else {
+ kind = Klass::RefArrayKlassKind;
+ layout = LayoutKind::REFERENCE;
+ }
+ array_desc = ArrayDescription(kind, oak->properties(), layout).value();
+ }
+ klass = oak->bottom_klass(); // overwrites klass
+ }
+ uint init_state = 0;
+ if (klass->is_instance_klass()) {
+ // A few classes are initialized in the assembly phase before code generation.
+ // When run in production, the code will assume they are initialized, again.
+ InstanceKlass* ik = InstanceKlass::cast(klass);
+ init_state = (ik->is_initialized() ? 1 : 0);
+ can_write &= AOTCacheAccess::can_generate_aot_code_for(ik);
+ } else {
+ assert(klass->is_typeArray_klass(), "must be");
+ can_write &= AOTCacheAccess::can_generate_aot_code(klass);
+ }
+ ResourceMark rm;
+ uint state = (array_dim << 1) | (init_state & 1);
+ if (can_write) {
+ DataKind kind = DataKind::Klass;
+ if (!write_kind(kind)) {
+ return false;
+ }
+ // Record state of instance klass initialization and array dimensions.
+ if (!write_int(state)) {
+ return false;
+ }
+ // Record array's properties
+ if (!write_int(array_desc)) {
+ return false;
+ }
+ narrowPtr klass_narrow_ptr = AOTCacheAccess::to_narrow_ptr(klass);
+ if (!write_int((int)checked_cast(klass_narrow_ptr))) {
+ return false;
+ }
+ log_debug(aot, codecache, metadata)("%d (A%d): Registered klass: %s%s%s @ 0x%08x",
+ compile_id(), comp_level(), klass->external_name(),
+ (!klass->is_instance_klass() ? "" : (init_state == 1 ? " (initialized)" : " (not-initialized)")),
+ (array_dim > 0 ? " (object array)" : ""), cast_to_u4(klass_narrow_ptr));
+ return true;
+ }
+ log_debug(aot, codecache, metadata)("%d (A%d): Klass is not registered: %s%s%s",
+ compile_id(), comp_level(), klass->external_name(),
+ (!klass->is_instance_klass() ? "" : (init_state == 1 ? " (initialized)" : " (not-initialized)")),
+ (array_dim > 0 ? " (object array)" : ""));
+ set_lookup_failed();
+ return false;
+}
+
+Klass* AOTCodeReader::read_klass(JavaThread* thread) {
+ uint state;
+ if (!read_int((int*)&state)) {
+ return nullptr;
+ }
+ uint array_desc;
+ if (!read_int((int*)&array_desc)) {
+ return nullptr;
+ }
+ narrowPtr klass_narrow_ptr;
+ if (!read_int((int*)&klass_narrow_ptr)) {
+ return nullptr;
+ }
+ uint init_state = (state & 1);
+ uint array_dim = (state >> 1);
+ Klass* k = AOTCacheAccess::narrow_ptr_to_klass(klass_narrow_ptr);
+ if (k == nullptr || !AOTMetaspace::in_aot_cache((address)k)) {
+ // Something changed in CDS
+ set_lookup_failed("Klass is not in AOT cache");
+ log_debug(aot, codecache, metadata)("%d (A%d): Lookup failed klass: " INTPTR_FORMAT " is not in AOT cache",
+ compile_id(), comp_level(), p2i((address)k));
+ return nullptr;
+ }
+ assert(k->is_klass(), "sanity");
+ ResourceMark rm;
+ if (k->is_instance_klass() && !InstanceKlass::cast(k)->is_loaded()) {
+ set_lookup_failed("Klass is not loaded");
+ log_debug(aot, codecache, metadata)("%d (A%d): Lookup failed for klass %s: not loaded",
+ compile_id(), comp_level(), k->external_name());
+ return nullptr;
+ }
+ // If the klass was seen to be initialized during AOT cache assembly,
+ // it must also be initialized here, in the production run, when this nmethod loads.
+ // Exception: "preload" code (AP4) can handle uninitialized classes.
+ // There is no exception for A1, A2 code or A4 code.
+ //
+ // Note: This init-state check is of minor importance compared to the clinit deps
+ // list/counter on this nmethod's CompileTrainingData, which has already determined
+ // the nmethod's readiness to load. (For many more details, see comments near
+ // CompileTrainingData::_init_deps_left.) This check is superficially similar,
+ // but it checks a relatively small number of classes, which may or may not be
+ // necessary to the nmethod's compiled code. Think of it as an extra sanity check.
+ // But it is an approximate check, and may fail spuriously, discarding good AOT code.
+ // After JDK-8380476 FIXME: Maybe get rid of this approximate check; replace with
+ // a more accurate and efficient check based on dependencies.hpp (new DepType).
+ // (Also, the metadata for arrays should have its own DataKind::ArrayKlass.)
+ if (k->is_instance_klass() && !InstanceKlass::cast(k)->is_initialized() && (init_state == 1) && !_entry->for_preload()) {
+ set_lookup_failed("Klass is not initialized");
+ log_debug(aot, codecache, metadata)("%d (A%d): Lookup failed for klass %s: not initialized",
+ compile_id(), comp_level(), k->external_name());
+ return nullptr;
+ }
+ if (array_dim > 0) {
+ assert(k->is_instance_klass() || k->is_typeArray_klass(), "sanity check");
+ Klass* ak = k->array_klass_or_null(array_dim);
+ if (ak != nullptr && array_desc != ArrayProperties::Invalid().value()) {
+ ArrayDescription adesc = ArrayDescription::from_value(array_desc);
+ ak = ObjArrayKlass::cast(ak)->klass_from_description(adesc, thread);
+ }
+ if (ak == nullptr) {
+ set_lookup_failed("Lookup failed for array klass");
+ log_debug(aot, codecache, metadata)("%d (A%d): Lookup failed for %d-dimensions array klass %s",
+ compile_id(), comp_level(), array_dim, k->external_name());
+ return nullptr;
+ }
+ log_debug(aot, codecache, metadata)("%d (A%d): Found %d-dimensions array klass %s",
+ compile_id(), comp_level(), array_dim, k->external_name());
+ return ak;
+ } else {
+ log_debug(aot, codecache, metadata)("%d (A%d): Found klass %s",
+ compile_id(), comp_level(), k->external_name());
+ return k;
+ }
+}
+
+bool AOTCodeCache::write_oop(jobject& jo) {
+ oop obj = JNIHandles::resolve(jo);
+ return write_oop(obj);
+}
+
+bool AOTCodeCache::write_oop(oop obj) {
+ DataKind kind;
+ if (obj == nullptr) {
+ kind = DataKind::Null;
+ if (!write_kind(kind)) {
+ return false;
+ }
+ } else if (cast_from_oop(obj) == Universe::non_oop_word()) {
+ kind = DataKind::No_Data;
+ if (!write_kind(kind)) {
+ return false;
+ }
+ } else if (java_lang_Class::is_instance(obj)) {
+ if (java_lang_Class::is_primitive(obj)) {
+ int bt = (int)java_lang_Class::primitive_type(obj);
+ kind = DataKind::Primitive;
+ if (!write_kind(kind)) {
+ return false;
+ }
+ if (!write_int(bt)) {
+ return false;
+ }
+ log_debug(aot, codecache, oops)("%d (A%d): Wrote primitive type klass: %s",
+ compile_id(), comp_level(), type2name((BasicType)bt));
+ return true;
+ } else {
+ Klass* klass = java_lang_Class::as_Klass(obj);
+ if (!write_klass(klass)) {
+ return false;
+ }
+ }
+ } else if (java_lang_String::is_instance(obj)) {
+ int k = AOTCacheAccess::get_archived_object_permanent_index(obj); // k >= 0 means obj is a "permanent heap object"
+ ResourceMark rm;
+ size_t length_sz = 0;
+ const char* string = java_lang_String::as_utf8_string(obj, length_sz);
+ if (k >= 0) {
+ kind = DataKind::String;
+ if (!write_kind(kind)) {
+ return false;
+ }
+ if (!write_int(k)) {
+ return false;
+ }
+ log_debug(aot, codecache, oops)("%d (A%d): Wrote String object: " PTR_FORMAT " : %s",
+ compile_id(), comp_level(), p2i(obj), string);
+ return true;
+ }
+ // Not archived String object - bailout
+ set_lookup_failed();
+ log_debug(aot, codecache, oops)("%d (A%d): Failed to write String object: " PTR_FORMAT " : %s",
+ compile_id(), comp_level(), p2i(obj), string);
+ return false;
+ } else if (java_lang_Module::is_instance(obj)) {
+ set_lookup_failed();
+ log_debug(aot, codecache, oops)("%d (A%d): Module object is not supported: " PTR_FORMAT " : %s",
+ compile_id(), comp_level(), p2i(obj), obj->klass()->external_name());
+ return false;
+ } else if (java_lang_ClassLoader::is_instance(obj)) {
+ if (obj == SystemDictionary::java_system_loader()) {
+ kind = DataKind::SysLoader;
+ } else if (obj == SystemDictionary::java_platform_loader()) {
+ kind = DataKind::PlaLoader;
+ } else {
+ ResourceMark rm;
+ set_lookup_failed();
+ log_debug(aot, codecache, oops)("%d (A%d): Not supported Class Loader: " PTR_FORMAT " : %s",
+ compile_id(), comp_level(), p2i(obj), obj->klass()->external_name());
+ return false;
+ }
+ if (!write_kind(kind)) {
+ return false;
+ }
+ log_debug(aot, codecache, oops)("%d (A%d): Wrote ClassLoader: java_%s_loader",
+ compile_id(), comp_level(), (kind == DataKind::SysLoader ? "system" : "platform"));
+ return true;
+ } else {
+ ResourceMark rm;
+ int k = AOTCacheAccess::get_archived_object_permanent_index(obj); // k >= 0 means obj is a "permanent heap object"
+ if (k >= 0) {
+ kind = DataKind::MH_Oop;
+ if (!write_kind(kind)) {
+ return false;
+ }
+ if (!write_int(k)) {
+ return false;
+ }
+ log_debug(aot, codecache, oops)("%d (A%d): Wrote MH object: " PTR_FORMAT " : %s",
+ compile_id(), comp_level(), p2i(obj), obj->klass()->external_name());
+ return true;
+ }
+ // Not archived Java object - bailout
+ set_lookup_failed();
+ log_debug(aot, codecache, oops)("%d (A%d): Failed to write Java object: " PTR_FORMAT " : %s",
+ compile_id(), comp_level(), p2i(obj), obj->klass()->external_name());
+ return false;
+ }
+ return true;
+}
+
+oop AOTCodeReader::read_oop(JavaThread* thread) {
+ oop obj = nullptr;
+ DataKind kind;
+ if (!read_kind(&kind)) {
+ return nullptr;
+ }
+ if (kind == DataKind::Null) {
+ return nullptr;
+ } else if (kind == DataKind::No_Data) {
+ return cast_to_oop(Universe::non_oop_word());
+ } else if (kind == DataKind::Klass) {
+ Klass* k = read_klass(thread);
+ if (k == nullptr) {
+ return nullptr;
+ }
+ obj = k->java_mirror();
+ if (obj == nullptr) {
+ set_lookup_failed("Lookup failed for java_mirror");
+ log_debug(aot, codecache, oops)("%d (A%d): Lookup failed for java_mirror of klass %s",
+ compile_id(), comp_level(), k->external_name());
+ return nullptr;
+ }
+ } else if (kind == DataKind::Primitive) {
+ BasicType bt;
+ if (!read_int((int*)&bt)) {
+ return nullptr;
+ }
+ obj = java_lang_Class::primitive_mirror(bt);
+ log_debug(aot, codecache, oops)("%d (A%d): Read primitive type klass: %s", compile_id(), comp_level(), type2name(bt));
+ } else if (kind == DataKind::String) {
+ int k;
+ if (!read_int(&k)) {
+ return nullptr;
+ }
+ obj = AOTCacheAccess::get_archived_object(k);
+ if (obj == nullptr) {
+ set_lookup_failed("Lookup failed for String object");
+ log_debug(aot, codecache, oops)("%d (A%d): Lookup failed for String object", compile_id(), comp_level());
+ return nullptr;
+ }
+ assert(java_lang_String::is_instance(obj), "must be string");
+
+ ResourceMark rm;
+ size_t length_sz = 0;
+ const char* string = java_lang_String::as_utf8_string(obj, length_sz);
+ log_debug(aot, codecache, oops)("%d (A%d): Read String object: %s", compile_id(), comp_level(), string);
+ } else if (kind == DataKind::SysLoader) {
+ obj = SystemDictionary::java_system_loader();
+ log_debug(aot, codecache, oops)("%d (A%d): Read java_system_loader", compile_id(), comp_level());
+ } else if (kind == DataKind::PlaLoader) {
+ obj = SystemDictionary::java_platform_loader();
+ log_debug(aot, codecache, oops)("%d (A%d): Read java_platform_loader", compile_id(), comp_level());
+ } else if (kind == DataKind::MH_Oop) {
+ int k;
+ if (!read_int(&k)) {
+ return nullptr;
+ }
+ obj = AOTCacheAccess::get_archived_object(k);
+ if (obj == nullptr) {
+ set_lookup_failed("Lookup failed for MH object");
+ log_debug(aot, codecache, oops)("%d (A%d): Lookup failed for MH object", compile_id(), comp_level());
+ return nullptr;
+ }
+ ResourceMark rm;
+ log_debug(aot, codecache, oops)("%d (A%d): Read MH object: " PTR_FORMAT " : %s",
+ compile_id(), comp_level(), p2i(obj), obj->klass()->external_name());
+ } else {
+ set_lookup_failed("Unknown oop's kind");
+ log_debug(aot, codecache, oops)("%d (A%d): Unknown oop's kind: %d",
+ compile_id(), comp_level(), (int)kind);
+ return nullptr;
+ }
+ return obj;
+}
+
+bool AOTCodeReader::read_oop_metadata_list(JavaThread* current, GrowableArray &oop_list, GrowableArray &metadata_list, OopRecorder* oop_recorder) {
+ assert(oop_recorder == nullptr || oop_recorder->is_unused(), "only use fresh new OopRecorder");
+ int count;
+ if (!read_int(&count)) {
+ return false;
+ }
+ for (int i = 0; i < count; i++) {
+ oop obj = read_oop(current);
+ if (lookup_failed()) {
+ return false;
+ }
+ Handle h(current, obj);
+ oop_list.append(h);
+ if (oop_recorder != nullptr) {
+ jobject jo = JNIHandles::make_local(current, obj);
+ // Use allocate_oop_index() to preserve indexes recorded in Dependencies
+ oop_recorder->allocate_oop_index(jo);
+ }
+ LogStreamHandle(Trace, aot, codecache, oops) log;
+ if (log.is_enabled()) {
+ ResourceMark rm;
+ stringStream ss;
+ ss.print("%d (A%d): %d: " INTPTR_FORMAT " ", compile_id(), comp_level(), i, p2i(obj));
+ if (obj == Universe::non_oop_word()) {
+ ss.print("non-oop word");
+ } else if (obj == nullptr) {
+ ss.print("nullptr-oop");
+ } else {
+ obj->print_value_on(&ss);
+ }
+ log.print_cr("%s", ss.freeze());
+ }
+ }
+
+ if (!read_int(&count)) {
+ return false;
+ }
+ for (int i = 0; i < count; i++) {
+ Metadata* m = read_metadata(current);
+ if (lookup_failed()) {
+ return false;
+ }
+ metadata_list.append(m);
+ if (oop_recorder != nullptr) {
+ // Use allocate_metadata_index() to preserve indexes recorded in Dependencies
+ oop_recorder->allocate_metadata_index(m);
+ }
+ LogStreamHandle(Trace, aot, codecache, metadata) log;
+ if (log.is_enabled()) {
+ ResourceMark rm;
+ stringStream ss;
+ ss.print("%d (A%d): %d: " INTPTR_FORMAT " ", compile_id(), comp_level(), i, p2i(m));
+ if (m == (Metadata*)Universe::non_oop_word()) {
+ ss.print("non-metadata word");
+ } else if (m == nullptr) {
+ ss.print("nullptr-metadata");
+ } else {
+ Metadata::print_value_on_maybe_null(&ss, m);
+ }
+ log.print_cr("%s", ss.freeze());
+ }
+ }
+ return true;
+}
+
+bool AOTCodeCache::write_oop_map_set(CodeBlob& cb) {
+ ImmutableOopMapSet* oopmaps = cb.oop_maps();
+ int oopmaps_size = oopmaps->nr_of_bytes();
+ if (!write_int(oopmaps_size)) {
+ return false;
+ }
+ if (!write_bytes(oopmaps, oopmaps->nr_of_bytes())) {
+ return false;
+ }
+ return true;
+}
+
+ImmutableOopMapSet* AOTCodeReader::read_oop_map_set() {
+ int size;
+ if (!read_int(&size)) {
+ return nullptr;
+ }
+ address oopmaps;
+ if (!get_set_read_position(&oopmaps, (uint)size)) {
+ return nullptr;
+ }
+ return (ImmutableOopMapSet*)oopmaps;
+}
+
+bool AOTCodeCache::write_oops(nmethod* nm) {
+ int count = nm->oops_count()-1;
+ if (!write_int(count)) {
+ return false;
+ }
+ for (oop* p = nm->oops_begin(); p < nm->oops_end(); p++) {
+ if (!write_oop(*p)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+#ifndef PRODUCT
+bool AOTCodeCache::write_asm_rem_and_dbg_str(AsmRemarks& asm_remarks, DbgStrings& dbg_strings) {
+ GrowableArray remarks_str;
+ GrowableArray remarks_str_len;
+ uint remarks_str_size = 0;
+ if (!write_asm_remarks(asm_remarks, remarks_str, remarks_str_len, &remarks_str_size)) {
+ return false;
+ }
+ if (!write_int(remarks_str_size)) {
+ return false;
+ }
+
+ GrowableArray dbg_str;
+ GrowableArray dbg_str_len;
+ uint dbg_str_size = 0;
+ if (!write_dbg_strings(dbg_strings, dbg_str, dbg_str_len, &dbg_str_size)) {
+ return false;
+ }
+ if (!write_int(dbg_str_size)) {
+ return false;
+ }
+
+ uint remarks_str_start = _write_position;
+ assert(remarks_str.length() == remarks_str_len.length(), "sanity");
+ for (int i = 0; i < remarks_str.length(); i++) {
+ const char* str = remarks_str.at(i);
+ uint len = remarks_str_len.at(i);
+ if (!write_bytes(str, len)) {
+ return false;
+ }
+ }
+ assert(remarks_str_size == (_write_position - remarks_str_start), "%u != (%u - %u)",
+ remarks_str_size, _write_position, remarks_str_start);
+
+ uint dbg_str_start = _write_position;
+ for (int i = 0; i < dbg_str.length(); i++) {
+ const char* str = dbg_str.at(i);
+ uint len = dbg_str_len.at(i);
+ if (!write_bytes(str, len)) {
+ return false;
+ }
+ log_trace(aot, codecache)("Wrote dbg string %d '%s'", i, str);
+ }
+ assert(dbg_str_size == (_write_position - dbg_str_start), "%u != (%u - %u)",
+ dbg_str_size, _write_position, dbg_str_start);
return true;
}
-void AOTCodeReader::fix_relocations(CodeBlob *code_blob, RelocIterator& iter) {
- uint offset = align_read_int();
- int reloc_count = *(int*)addr(offset);
- offset += sizeof(int);
- uint* reloc_data = (uint*)addr(offset);
- offset += (reloc_count * sizeof(uint));
- set_read_position(offset);
+bool AOTCodeReader::read_asm_rem_and_dbg_str() {
+ if (!read_int(&_asm_remarks_count)) {
+ return false;
+ }
+ if (_asm_remarks_count < 0) {
+ log_warning(aot, codecache)("Damaged asm remarks: negative count %d", _asm_remarks_count);
+ return false;
+ }
+ uint asm_remarks_size = (uint)_asm_remarks_count * sizeof(AsmRemData);
+ if (!get_set_read_position((address*)&_asm_remarks_data, asm_remarks_size)) {
+ return false;
+ }
+ int asm_remarks_str_size;
+ if (!read_int(&asm_remarks_str_size)) {
+ return false;
+ }
+ if (asm_remarks_str_size < 0) {
+ log_warning(aot, codecache)("Damaged asm remarks: negative size %d", asm_remarks_str_size);
+ return false;
+ }
- LogStreamHandle(Trace, aot, codecache, reloc) log;
- if (log.is_enabled()) {
- log.print_cr("======== extra relocations count=%d", reloc_count);
- log.print(" {");
- for(int i = 0; i < reloc_count; i++) {
- if (i == 0) {
- log.print("%d", reloc_data[i]);
- } else {
- log.print(", %d", reloc_data[i]);
- }
- }
- log.print_cr("}");
+ if (!read_int(&_dbg_strings_count)) {
+ return false;
+ }
+ if (_dbg_strings_count < 0) {
+ log_warning(aot, codecache)("Damaged dbg strings: negative count %d", _dbg_strings_count);
+ return false;
+ }
+ uint dbg_str_size = (uint)_dbg_strings_count * sizeof(DbgStrData);
+ if (!get_set_read_position((address*)&_dbg_strings_data, dbg_str_size)) {
+ return false;
+ }
+ int dbg_strings_size;
+ if (!read_int(&dbg_strings_size)) {
+ return false;
+ }
+ if (dbg_strings_size < 0) {
+ log_warning(aot, codecache)("Damaged dbg strings: negative size %d", dbg_strings_size);
+ return false;
}
- int j = 0;
- while (iter.next()) {
- switch (iter.type()) {
- case relocInfo::none:
- break;
- case relocInfo::runtime_call_type: {
- address dest = _cache->address_for_id(reloc_data[j]);
- if (dest != (address)-1) {
- ((CallRelocation*)iter.reloc())->set_destination(dest);
- }
- break;
- }
- case relocInfo::runtime_call_w_cp_type:
- // this relocation should not be in cache (see write_relocations)
- assert(false, "runtime_call_w_cp_type relocation is not implemented");
- break;
- case relocInfo::external_word_type: {
- address target = _cache->address_for_id(reloc_data[j]);
- // Add external address to global table
- int index = ExternalsRecorder::find_index(target);
- // Update index in relocation
- Relocation::add_jint(iter.data(), index);
- external_word_Relocation* reloc = (external_word_Relocation*)iter.reloc();
- assert(reloc->target() == target, "sanity");
- reloc->set_value(target); // Patch address in the code
- break;
- }
- case relocInfo::internal_word_type: {
- internal_word_Relocation* r = (internal_word_Relocation*)iter.reloc();
- r->fix_relocation_after_aot_load(aot_code_entry()->dumptime_content_start_addr(), code_blob->content_begin());
- break;
- }
- case relocInfo::section_word_type: {
- section_word_Relocation* r = (section_word_Relocation*)iter.reloc();
- r->fix_relocation_after_aot_load(aot_code_entry()->dumptime_content_start_addr(), code_blob->content_begin());
- break;
- }
- case relocInfo::post_call_nop_type:
- break;
- default:
- assert(false,"relocation %d unimplemented", (int)iter.type());
- break;
+ uint strings_size = static_cast(asm_remarks_str_size);
+ const char* strings_base;
+ if (!get_set_read_position((address*)&strings_base, strings_size)) {
+ return false;
+ }
+ _asm_remarks_strings = _asm_remarks_count == 0 ? nullptr :
+ NEW_C_HEAP_ARRAY(const char*, _asm_remarks_count, mtCode);
+ uint offset = 0;
+ int i = 0;
+ uint off = 0;
+ uint len = 0;
+ for (; i < _asm_remarks_count; i++) {
+ off = _asm_remarks_data[i].str_offset;
+ len = _asm_remarks_data[i].str_len;
+ if (offset != off || len == 0 || off > strings_size || len > (strings_size - off)) {
+ break;
}
- if (log.is_enabled()) {
- iter.print_current_on(&log);
+ const char* str = strings_base + off;
+ if (str[len - 1] != '\0') {
+ break;
}
- j++;
+ _asm_remarks_strings[i] = str;
+ offset += len;
+ }
+ if (i < _asm_remarks_count) {
+ log_warning(aot, codecache)("Damaged asm remark %d [%u + %u] in AOT entry %u " INTPTR_FORMAT " [%u + %u]", i, off, len, _entry->id(), p2i(_entry), _entry->offset(), _entry->size());
+ return false;
+ }
+ if (offset != strings_size) {
+ log_warning(aot, codecache)("Damaged asm remarks: size %u vs cached %u , in AOT entry %u " INTPTR_FORMAT " [%u + %u]", offset, (uint)asm_remarks_str_size, _entry->id(), p2i(_entry), _entry->offset(), _entry->size());
+ return false;
}
- assert(j == reloc_count, "sanity");
-}
-bool AOTCodeCache::write_oop_map_set(CodeBlob& cb) {
- if (!align_write_int()) {
+ strings_size = static_cast(dbg_strings_size);
+ if (!get_set_read_position((address*)&strings_base, strings_size)) {
return false;
}
- ImmutableOopMapSet* oopmaps = cb.oop_maps();
- int oopmaps_size = oopmaps->nr_of_bytes();
- if (!write_bytes(&oopmaps_size, sizeof(int))) {
+ _dbg_strings = _dbg_strings_count == 0 ? nullptr :
+ NEW_C_HEAP_ARRAY(const char*, _dbg_strings_count, mtCode);
+ offset = 0;
+ i = 0;
+ off = 0;
+ len = 0;
+ for (; i < _dbg_strings_count; i++) {
+ off = _dbg_strings_data[i].str_offset;
+ len = _dbg_strings_data[i].str_len;
+ if (offset != off || len == 0 || off > strings_size || len > (strings_size - off)) {
+ break;
+ }
+ const char* str = strings_base + off;
+ if (str[len - 1] != '\0') {
+ break;
+ }
+ _dbg_strings[i] = str;
+ offset += len;
+ }
+ if (i < _dbg_strings_count) {
+ log_warning(aot, codecache)("Damaged dbg strings %d [%u + %u] in AOT entry %u " INTPTR_FORMAT " [%u + %u]", i, off, len, _entry->id(), p2i(_entry), _entry->offset(), _entry->size());
return false;
}
- uint n = write_bytes(oopmaps, oopmaps->nr_of_bytes());
- if (n != (uint)oopmaps->nr_of_bytes()) {
+ if (offset != strings_size) {
+ log_warning(aot, codecache)("Damaged dbg strings: size %u vs cached %u , in AOT entry %u " INTPTR_FORMAT " [%u + %u]", offset, (uint)dbg_strings_size, _entry->id(), p2i(_entry), _entry->offset(), _entry->size());
return false;
}
return true;
}
-ImmutableOopMapSet* AOTCodeReader::read_oop_map_set() {
- uint offset = align_read_int();
- int size = *(int *)addr(offset);
- offset += sizeof(int);
- ImmutableOopMapSet* oopmaps = (ImmutableOopMapSet *)addr(offset);
- offset += size;
- set_read_position(offset);
- return oopmaps;
-}
-
-#ifndef PRODUCT
-bool AOTCodeCache::write_asm_remarks(CodeBlob& cb) {
+bool AOTCodeCache::write_asm_remarks(AsmRemarks& asm_remarks, GrowableArray& remarks, GrowableArray& remarks_len, uint* size) {
if (!align_write_int()) {
return false;
}
@@ -1783,43 +4245,51 @@ bool AOTCodeCache::write_asm_remarks(CodeBlob& cb) {
return false;
}
uint count = 0;
- bool result = cb.asm_remarks().iterate([&] (uint offset, const char* str) -> bool {
- log_trace(aot, codecache, stubs)("asm remark offset=%d, str='%s'", offset, str);
- uint n = write_bytes(&offset, sizeof(uint));
- if (n != sizeof(uint)) {
+ uint str_offset = 0;
+ bool result = asm_remarks.iterate([&] (uint offset, const char* str) -> bool {
+ if (!write_int(offset)) {
+ return false;
+ }
+ if (!write_int(str_offset)) {
return false;
}
- const char* cstr = add_C_string(str);
- int id = _table->id_for_C_string((address)cstr);
- assert(id != BAD_ADDRESS_ID, "asm remark string '%s' not found in AOTCodeAddressTable", str);
- n = write_bytes(&id, sizeof(int));
- if (n != sizeof(int)) {
+ uint len = (uint)strlen(str) + 1;
+ if (!write_int(len)) {
return false;
}
+ remarks.append(str);
+ remarks_len.append(len);
+ str_offset += len;
count += 1;
return true;
});
- *count_ptr = count;
+ if (result) {
+ *count_ptr = count;
+ *size = str_offset;
+ log_trace(aot, codecache)("Wrote asm remarks count=%u size=%u", count, str_offset);
+ } else {
+ *count_ptr = 0;
+ *size = 0;
+ log_trace(aot, codecache)("Failed write asm remark %u offset=%u", count, str_offset);
+ }
return result;
}
-void AOTCodeReader::read_asm_remarks(AsmRemarks& asm_remarks) {
+void AOTCodeReader::restore_asm_remarks(AsmRemarks& asm_remarks) {
// Read asm remarks
- uint offset = align_read_int();
- uint count = *(uint *)addr(offset);
- offset += sizeof(uint);
- for (uint i = 0; i < count; i++) {
- uint remark_offset = *(uint *)addr(offset);
- offset += sizeof(uint);
- int remark_string_id = *(uint *)addr(offset);
- offset += sizeof(int);
- const char* remark = (const char*)_cache->address_for_C_string(remark_string_id);
+ int count = _asm_remarks_count;
+ log_trace(aot, codecache)("Restoring %d asm remarks", count);
+ for (int i = 0; i < count; i++) {
+ uint remark_offset = _asm_remarks_data[i].offset;
+ const char* remark = _asm_remarks_strings[i];
asm_remarks.insert(remark_offset, remark);
}
- set_read_position(offset);
+ if (_asm_remarks_strings != nullptr) {
+ FREE_C_HEAP_ARRAY(_asm_remarks_strings);
+ }
}
-bool AOTCodeCache::write_dbg_strings(CodeBlob& cb) {
+bool AOTCodeCache::write_dbg_strings(DbgStrings& dbg_strings, GrowableArray& strings, GrowableArray&strings_len, uint* size) {
if (!align_write_int()) {
return false;
}
@@ -1829,34 +4299,45 @@ bool AOTCodeCache::write_dbg_strings(CodeBlob& cb) {
return false;
}
uint count = 0;
- bool result = cb.dbg_strings().iterate([&] (const char* str) -> bool {
- log_trace(aot, codecache, stubs)("dbg string=%s", str);
- const char* cstr = add_C_string(str);
- int id = _table->id_for_C_string((address)cstr);
- assert(id != BAD_ADDRESS_ID, "db string '%s' not found in AOTCodeAddressTable", str);
- uint n = write_bytes(&id, sizeof(int));
- if (n != sizeof(int)) {
+ uint str_offset = 0;
+ bool result = dbg_strings.iterate([&] (const char* str) -> bool {
+ if (!write_int(str_offset)) {
+ return false;
+ }
+ uint len = (uint)strlen(str) + 1;
+ if (!write_int(len)) {
return false;
}
+ strings.append(str);
+ strings_len.append(len);
+ str_offset += len;
+
count += 1;
return true;
});
- *count_ptr = count;
+ if (result) {
+ *count_ptr = count;
+ *size = str_offset;
+ log_trace(aot, codecache)("Wrote dbg strings count=%u size=%u", count, str_offset);
+ } else {
+ *count_ptr = 0;
+ *size = 0;
+ log_trace(aot, codecache)("Failed write dbg string %u offset=%u", count, str_offset);
+ }
return result;
}
-void AOTCodeReader::read_dbg_strings(DbgStrings& dbg_strings) {
+void AOTCodeReader::restore_dbg_strings(DbgStrings& dbg_strings) {
// Read dbg strings
- uint offset = align_read_int();
- uint count = *(uint *)addr(offset);
- offset += sizeof(uint);
- for (uint i = 0; i < count; i++) {
- int string_id = *(uint *)addr(offset);
- offset += sizeof(int);
- const char* str = (const char*)_cache->address_for_C_string(string_id);
+ int count = _dbg_strings_count;
+ log_trace(aot, codecache)("Restoring %d dbg strings", count);
+ for (int i = 0; i < count; i++) {
+ const char* str = _dbg_strings[i];
dbg_strings.insert(str);
}
- set_read_position(offset);
+ if (_dbg_strings != nullptr) {
+ FREE_C_HEAP_ARRAY(_dbg_strings);
+ }
}
#endif // PRODUCT
@@ -1865,9 +4346,10 @@ void AOTCodeReader::read_dbg_strings(DbgStrings& dbg_strings) {
// address table ids for generated routine entry adresses, external
// addresses and C string addresses are partitioned into positive
// integer ranges defined by the following positive base and max
-// values i.e. [_extrs_base, _extrs_base + _extrs_max -1],
-// [_stubs_base, _stubs_base + _stubs_max -1], [_c_str_base,
-// _c_str_base + _C_strings_count],
+// values i.e.
+// [_extrs_base, _extrs_base + _extrs_max -1],
+// [_stubs_base, _stubs_base + _stubs_max -1],
+// [_c_str_base, _c_str_base + _C_strings_count],
#define _extrs_max 500
#define _stubs_max static_cast(EntryId::NUM_ENTRYIDS)
@@ -1917,16 +4399,17 @@ void AOTCodeAddressTable::init_extrs() {
initializing_extrs = true;
_extrs_addr = NEW_C_HEAP_ARRAY(address, _extrs_max, mtCode);
-
- _extrs_length = 0;
+ memset(_extrs_addr, 0, _extrs_max * sizeof(address));
{
// Required by initial stubs
ADD_EXTERNAL_ADDRESS(SharedRuntime::exception_handler_for_return_address); // used by forward_exception
+ ADD_EXTERNAL_ADDRESS(CompressedKlassPointers::base_addr());
ADD_EXTERNAL_ADDRESS(CompressedOops::base_addr()); // used by call_stub
ADD_EXTERNAL_ADDRESS(Thread::current); // used by call_stub
ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_StackOverflowError);
ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_delayed_StackOverflowError);
+ ADD_EXTERNAL_ADDRESS(StubRoutines::crc_table_addr());
ADD_EXTERNAL_ADDRESS(StubRoutines::verify_oop_count_addr()); // used by generate_verify_oop()
ADD_EXTERNAL_ADDRESS(StubRoutines::verify_oop_subroutine_entry_address());
if (ValueTypeReturnedAsFields) {
@@ -1960,6 +4443,12 @@ void AOTCodeAddressTable::init_extrs() {
#endif /* PRODUCT */
ADD_EXTERNAL_ADDRESS(SharedRuntime::enable_stack_reserved_zone);
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::rc_trace_method_entry);
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::reguard_yellow_pages);
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::dtrace_method_entry);
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::dtrace_method_exit);
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::complete_monitor_unlocking_C);
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::OSR_migration_end);
#if defined(AMD64) && !defined(ZERO)
ADD_EXTERNAL_ADDRESS(SharedRuntime::montgomery_multiply);
@@ -1974,14 +4463,12 @@ void AOTCodeAddressTable::init_extrs() {
ADD_EXTERNAL_ADDRESS(SharedRuntime::dlog);
ADD_EXTERNAL_ADDRESS(SharedRuntime::dlog10);
ADD_EXTERNAL_ADDRESS(SharedRuntime::dpow);
-#ifndef ZERO
- ADD_EXTERNAL_ADDRESS(SharedRuntime::drem);
-#endif
ADD_EXTERNAL_ADDRESS(SharedRuntime::dsin);
ADD_EXTERNAL_ADDRESS(SharedRuntime::dtan);
ADD_EXTERNAL_ADDRESS(SharedRuntime::f2i);
ADD_EXTERNAL_ADDRESS(SharedRuntime::f2l);
#ifndef ZERO
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::drem);
ADD_EXTERNAL_ADDRESS(SharedRuntime::frem);
#endif
ADD_EXTERNAL_ADDRESS(SharedRuntime::l2d);
@@ -1990,28 +4477,39 @@ void AOTCodeAddressTable::init_extrs() {
ADD_EXTERNAL_ADDRESS(SharedRuntime::lmul);
ADD_EXTERNAL_ADDRESS(SharedRuntime::lrem);
-#if INCLUDE_JVMTI
- ADD_EXTERNAL_ADDRESS(&JvmtiExport::_should_notify_object_alloc);
-#endif /* INCLUDE_JVMTI */
-
ADD_EXTERNAL_ADDRESS(ThreadIdentifier::unsafe_offset());
- // already added
- // ADD_EXTERNAL_ADDRESS(Thread::current);
+ ADD_EXTERNAL_ADDRESS(ObjectMonitorTable::current_table_address());
ADD_EXTERNAL_ADDRESS(os::javaTimeMillis);
ADD_EXTERNAL_ADDRESS(os::javaTimeNanos);
#ifndef PRODUCT
ADD_EXTERNAL_ADDRESS(os::breakpoint);
-#endif
-
- ADD_EXTERNAL_ADDRESS(StubRoutines::crc_table_addr());
-#ifndef PRODUCT
ADD_EXTERNAL_ADDRESS(&SharedRuntime::_partial_subtype_ctr);
+#if defined(AARCH64)
+ ADD_EXTERNAL_ADDRESS(JavaThread::verify_cross_modify_fence_failure);
#endif
+#endif // !PRODUCT
+
+#if INCLUDE_JVMTI
+ ADD_EXTERNAL_ADDRESS(&JvmtiExport::_should_notify_object_alloc);
+#endif /* INCLUDE_JVMTI */
+ // For JVMTI
+ ADD_EXTERNAL_ADDRESS(MountUnmountDisabler::notify_jvmti_events_address());
+ ADD_EXTERNAL_ADDRESS(MountUnmountDisabler::global_vthread_transition_disable_count_address());
#if INCLUDE_JFR
ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::write_checkpoint);
ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::return_lease);
+ ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::load_barrier);
+ ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::epoch_address());
+ ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::epoch_generation_address());
+ ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::signal_address());
+ ADD_EXTERNAL_ADDRESS(&VMContinuations)
+#endif
+ // For JFR
+ ADD_EXTERNAL_ADDRESS(os::elapsed_counter);
+#if defined(X86) && !defined(ZERO)
+ ADD_EXTERNAL_ADDRESS(Rdtsc::elapsed_counter);
#endif
ADD_EXTERNAL_ADDRESS(UpcallLinker::handle_uncaught_exception); // used by upcall_stub_exception_handler
@@ -2024,8 +4522,6 @@ void AOTCodeAddressTable::init_extrs() {
ADD_EXTERNAL_ADDRESS(SharedRuntime::resolve_opt_virtual_call_C);
ADD_EXTERNAL_ADDRESS(SharedRuntime::resolve_virtual_call_C);
ADD_EXTERNAL_ADDRESS(SharedRuntime::resolve_static_call_C);
- // already added
- // ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_delayed_StackOverflowError);
ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_AbstractMethodError);
ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_IncompatibleClassChangeError);
ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_NullPointerException_at_call);
@@ -2060,6 +4556,7 @@ void AOTCodeAddressTable::init_extrs() {
ADD_EXTERNAL_ADDRESS(Runtime1::move_appendix_patching);
ADD_EXTERNAL_ADDRESS(Runtime1::predicate_failed_trap);
ADD_EXTERNAL_ADDRESS(Runtime1::unimplemented_entry);
+ ADD_EXTERNAL_ADDRESS(Runtime1::trace_block_entry);
ADD_EXTERNAL_ADDRESS(Runtime1::new_null_free_array);
ADD_EXTERNAL_ADDRESS(Runtime1::load_flat_array);
ADD_EXTERNAL_ADDRESS(Runtime1::store_flat_array);
@@ -2068,11 +4565,14 @@ void AOTCodeAddressTable::init_extrs() {
ADD_EXTERNAL_ADDRESS(Runtime1::buffer_value_args_no_receiver);
ADD_EXTERNAL_ADDRESS(Runtime1::throw_identity_exception);
ADD_EXTERNAL_ADDRESS(Runtime1::throw_illegal_monitor_state_exception);
- // already added
- // ADD_EXTERNAL_ADDRESS(Thread::current);
- ADD_EXTERNAL_ADDRESS(CompressedKlassPointers::base_addr());
+
+ ResourceMark rm;
+ GrowableArray external_addresses;
+ // publish static addresses referred to by C1 runtime
+ Runtime1::init_AOTAddressTable(external_addresses);
+ add_external_addresses(external_addresses);
}
-#endif
+#endif // COMPILER1
#ifdef COMPILER2
{
@@ -2093,66 +4593,89 @@ void AOTCodeAddressTable::init_extrs() {
ADD_EXTERNAL_ADDRESS(OptoRuntime::rethrow_C);
ADD_EXTERNAL_ADDRESS(OptoRuntime::slow_arraycopy_C);
ADD_EXTERNAL_ADDRESS(OptoRuntime::register_finalizer_C);
+ ADD_EXTERNAL_ADDRESS(OptoRuntime::compile_method_C);
ADD_EXTERNAL_ADDRESS(OptoRuntime::load_unknown_value_C);
ADD_EXTERNAL_ADDRESS(OptoRuntime::store_unknown_value_C);
ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_end_first_transition_C);
ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_start_final_transition_C);
ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_start_transition_C);
ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_end_transition_C);
-#if defined(AARCH64) && ! defined(PRODUCT)
- ADD_EXTERNAL_ADDRESS(JavaThread::verify_cross_modify_fence_failure);
-#endif // AARCH64 && !PRODUCT
+ ADD_EXTERNAL_ADDRESS(Parse::trap_stress_counter_address());
// Used by lookup_secondary_supers_table
ADD_EXTERNAL_ADDRESS(Klass::on_secondary_supers_verification_failure);
}
+#if defined(AMD64) || defined(AARCH64)
+ ADD_EXTERNAL_ADDRESS(C2_MacroAssembler::abort_verify_int_in_range);
+ ADD_EXTERNAL_ADDRESS(C2_MacroAssembler::abort_verify_long_in_range);
+#endif // defined(AMD64) || defined(AARCH64)
+
+#ifndef PRODUCT
+ if (CountCompiledCalls) {
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::nof_normal_calls_addr());
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::nof_static_calls_addr());
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::nof_interface_calls_addr());
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::nof_inlined_calls_addr());
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::nof_inlined_static_calls_addr());
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::nof_inlined_interface_calls_addr());
+ ADD_EXTERNAL_ADDRESS(SharedRuntime::nof_megamorphic_calls_addr());
+ }
+#endif // NOT PRODUCT
#endif // COMPILER2
+ ADD_EXTERNAL_ADDRESS(BarrierSetNMethod::nmethod_stub_entry_barrier); // used by method_entry_barrier
#if INCLUDE_G1GC
- ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_field_pre_entry);
- ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_pre_narrow_oop_entry); // used by arraycopy stubs
- ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_pre_oop_entry); // used by arraycopy stubs
- ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_post_entry); // used by arraycopy stubs
+ if (UseG1GC) {
+ ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_field_pre_entry);
+ ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_pre_narrow_oop_entry); // used by arraycopy stubs
+ ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_pre_oop_entry); // used by arraycopy stubs
+ ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_post_entry); // used by arraycopy stubs
+ ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::clone_addr());
+ }
#endif
- ADD_EXTERNAL_ADDRESS(BarrierSetNMethod::nmethod_stub_entry_barrier); // used by method_entry_barrier
-
#if INCLUDE_SHENANDOAHGC
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::write_barrier_pre);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::write_barrier_pre_narrow);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_strong);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_strong_narrow);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_strong_narrow_narrow);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_weak);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_weak_narrow);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_weak_narrow_narrow);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_phantom);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_phantom_narrow);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_phantom_narrow_narrow);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::arraycopy_barrier_oop);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::arraycopy_barrier_narrow_oop);
- ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::clone);
+ if (UseShenandoahGC) {
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::write_barrier_pre);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::write_barrier_pre_narrow);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_strong);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_strong_narrow);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_strong_narrow_narrow);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_weak);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_weak_narrow);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_weak_narrow_narrow);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_phantom);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_phantom_narrow);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_phantom_narrow_narrow);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::arraycopy_barrier_oop);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::arraycopy_barrier_narrow_oop);
+ ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::clone);
+ }
#endif
#if INCLUDE_ZGC
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr());
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_store_good_addr());
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_weak_oop_field_preloaded_addr());
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr());
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_load_barrier_on_weak_oop_field_preloaded_addr());
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_load_barrier_on_phantom_oop_field_preloaded_addr());
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr());
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr());
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr());
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr());
- ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_array_addr());
-
- ADD_EXTERNAL_ADDRESS(ZPointerVectorLoadBadMask);
- ADD_EXTERNAL_ADDRESS(ZPointerVectorStoreBadMask);
- ADD_EXTERNAL_ADDRESS(ZPointerVectorStoreGoodMask);
+ if (UseZGC) {
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_store_good_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_weak_oop_field_preloaded_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_load_barrier_on_weak_oop_field_preloaded_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_load_barrier_on_phantom_oop_field_preloaded_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_array_addr());
+ ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::clone_addr());
+
+ ADD_EXTERNAL_ADDRESS(ZPointerVectorLoadBadMask);
+ ADD_EXTERNAL_ADDRESS(ZPointerVectorStoreBadMask);
+ ADD_EXTERNAL_ADDRESS(ZPointerVectorStoreGoodMask);
#if defined(AMD64)
- ADD_EXTERNAL_ADDRESS(&ZPointerLoadShift);
- extern address ZPointerLoadShiftTableAddr;
- ADD_EXTERNAL_ADDRESS(&ZPointerLoadShiftTableAddr);
+ ADD_EXTERNAL_ADDRESS(&ZPointerLoadShift);
+ extern address ZPointerLoadShiftTableAddr;
+ ADD_EXTERNAL_ADDRESS(&ZPointerLoadShiftTableAddr);
#endif
+ } // UseZGC
#endif // INCLUDE_ZGC
+
#ifndef ZERO
#if defined(AMD64) || defined(AARCH64) || defined(RISCV64)
ADD_EXTERNAL_ADDRESS(MacroAssembler::debug64);
@@ -2172,21 +4695,26 @@ void AOTCodeAddressTable::init_extrs() {
log_debug(aot, codecache, init)("External addresses opened and recorded");
// allocate storage for stub entries
_stubs_addr = NEW_C_HEAP_ARRAY(address, _stubs_max, mtCode);
+ memset(_stubs_addr, 0, _stubs_max * sizeof(address));
log_debug(aot, codecache, init)("Stub addresses opened");
}
void AOTCodeAddressTable::init_extrs2() {
assert(initializing_extrs && !_extrs_complete,
"invalid sequence for init_extrs2");
-
{
- ADD_EXTERNAL_ADDRESS(Continuation::prepare_thaw); // used by cont_thaw
- ADD_EXTERNAL_ADDRESS(Continuation::thaw_entry()); // used by cont_thaw
- ADD_EXTERNAL_ADDRESS(ContinuationEntry::thaw_call_pc_address()); // used by cont_preempt_stub
- }
+ ADD_EXTERNAL_ADDRESS(Continuation::prepare_thaw); // used by cont_thaw
+ ADD_EXTERNAL_ADDRESS(Continuation::thaw_entry()); // used by cont_thaw
+ ADD_EXTERNAL_ADDRESS(ContinuationEntry::thaw_call_pc_address()); // used by cont_preempt_stub
+ ADD_EXTERNAL_ADDRESS(Continuation::freeze_entry()); // used by gen_continuation_yield
+ }
+ _extrs2_length = _extrs_length;
+ _final_extrs_length = _extrs_length + add_c1_extrs_blobs(true /* count_only*/);
+ // C1 GC blobs have to be recorded later but we need to mark current
+ // extrs table as complete for stubs loading.
_extrs_complete = true;
initializing_extrs = false;
- log_debug(aot, codecache, init)("External addresses recorded and closed");
+ log_info(aot, codecache, init)("External addresses %d recorded", _extrs_length);
}
void AOTCodeAddressTable::add_external_addresses(GrowableArray& addresses) {
@@ -2215,25 +4743,85 @@ void AOTCodeAddressTable::add_stub_entry(EntryId entry_id, address a) {
void AOTCodeAddressTable::set_shared_stubs_complete() {
assert(!_shared_stubs_complete, "repeated close for shared stubs!");
_shared_stubs_complete = true;
- log_debug(aot, codecache, init)("Shared stubs closed");
+ log_info(aot, codecache, init)("Shared stubs recorded and closed");
+}
+
+uint AOTCodeAddressTable::add_c1_extrs_blobs(bool count_only) {
+ if (CompilationPolicy::c1_count() == 0 && CompilationPolicy::ac1_count() == 0) {
+ assert(count_only, "should be called only from init_extrs2 when C1 is disabled");
+ return 0;
+ }
+ uint count = 0;
+ // Use the same conditions as in set_c1_stubs_complete()
+#ifdef COMPILER1
+#if INCLUDE_G1GC
+ if (UseG1GC) {
+ if (!count_only) {
+ G1BarrierSetC1* bs = (G1BarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
+ ADD_EXTERNAL_ADDRESS(bs->pre_barrier_c1_runtime_code_blob()->code_begin());
+ }
+ count += 1;
+ }
+#endif // INCLUDE_G1GC
+#if INCLUDE_ZGC
+ if (UseZGC) {
+ if (!count_only) {
+ ZBarrierSetC1* bs = (ZBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
+ ADD_EXTERNAL_ADDRESS(bs->_load_barrier_on_oop_field_preloaded_runtime_stub);
+ ADD_EXTERNAL_ADDRESS(bs->_load_barrier_on_weak_oop_field_preloaded_runtime_stub);
+ ADD_EXTERNAL_ADDRESS(bs->_store_barrier_on_oop_field_with_healing);
+ ADD_EXTERNAL_ADDRESS(bs->_store_barrier_on_oop_field_without_healing);
+ }
+ count += 4;
+ }
+#endif // INCLUDE_ZGC
+#if INCLUDE_SHENANDOAHGC
+ if (UseShenandoahGC) {
+ ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
+ if (ShenandoahSATBBarrier) {
+ if (!count_only) {
+ ADD_EXTERNAL_ADDRESS(bs->keepalive_barrier_c1_runtime_code_blob()->code_begin());
+ }
+ count += 1;
+ }
+ if (ShenandoahLoadRefBarrier) {
+ if (!count_only) {
+ ADD_EXTERNAL_ADDRESS(bs->load_reference_barrier_strong_rt_code_blob()->code_begin());
+ ADD_EXTERNAL_ADDRESS(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin());
+ ADD_EXTERNAL_ADDRESS(bs->load_reference_barrier_weak_rt_code_blob()->code_begin());
+ ADD_EXTERNAL_ADDRESS(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin());
+ }
+ count += 4;
+ }
+ }
+#endif // INCLUDE_SHENANDOAHGC
+#endif // COMPILER1
+ return count;
}
void AOTCodeAddressTable::set_c1_stubs_complete() {
assert(!_c1_stubs_complete, "repeated close for c1 stubs!");
+ assert(_extrs_length == _extrs2_length, "%u != %u", _extrs_length, _extrs2_length);
+
+ uint count = add_c1_extrs_blobs(false /* count_only*/);
+ assert(_extrs2_length == _extrs_length - count, "%u != %u", _extrs2_length, (_extrs_length - count));
+ assert(_extrs_length == _final_extrs_length, "%u != %u", _extrs_length, _final_extrs_length);
+
_c1_stubs_complete = true;
- log_debug(aot, codecache, init)("C1 stubs closed");
+ log_info(aot, codecache, init)("C1 stubs recorded and closed");
+ log_info(aot, codecache, init)("External C1 addresses %u recorded", count);
}
void AOTCodeAddressTable::set_c2_stubs_complete() {
assert(!_c2_stubs_complete, "repeated close for c2 stubs!");
_c2_stubs_complete = true;
- log_debug(aot, codecache, init)("C2 stubs closed");
+ log_info(aot, codecache, init)("C2 stubs recorded and closed");
}
void AOTCodeAddressTable::set_stubgen_stubs_complete() {
assert(!_stubgen_stubs_complete, "repeated close for stubgen stubs!");
_stubgen_stubs_complete = true;
- log_debug(aot, codecache, init)("StubGen stubs closed");
+ log_info(aot, codecache, init)("StubGen stubs recorded and closed");
}
#ifdef PRODUCT
@@ -2259,32 +4847,47 @@ void AOTCodeCache::init_C_strings_caching() {
_C_strings_ix = new(mtCode) GrowableArray(INITIAL_STR_CACHE_SIZE, 0, -1, mtCode);
}
-void AOTCodeCache::load_strings() {
+bool AOTCodeCache::load_strings() {
uint strings_count = _load_header->strings_count();
if (strings_count == 0) {
- return;
+ return true;
}
uint strings_offset = _load_header->strings_offset();
// First is the array of cached strings length
uint* string_lengths = (uint*)addr(strings_offset);
strings_offset += (strings_count * sizeof(uint));
+ uint space_left = _load_size - strings_offset;
// We don't need to duplcate strings from AOT cache to C heap
// because we don't remove AOT code cache anymore.
- _cached_C_strings = NEW_C_HEAP_ARRAY(const char*, strings_count, mtCode);
+ const char** strings_ref = NEW_C_HEAP_ARRAY(const char*, strings_count, mtCode);
+ uint strings_size = 0;
char* start = (char*)addr(strings_offset);
char* p = start;
for (uint i = 0; i < strings_count; i++) {
- _cached_C_strings[i] = p;
uint len = string_lengths[i];
+ strings_size += len;
+ if (len == 0 || len > space_left || start[strings_size - 1] != '\0') {
+ load_failure_log().print_cr("AOT Code Cache disabled: C string %u is damaged", i);
+ FREE_C_HEAP_ARRAY(strings_ref);
+ return false;
+ }
+ space_left -= len;
+ strings_ref[i] = p;
log_trace(aot, codecache, stringtable)("load_strings: _C_strings[%d] " INTPTR_FORMAT " '%s'", i, p2i(p), p);
p += len;
}
- uint strings_size = _load_header->entries_offset() - strings_offset;
- assert((uint)(p - start) <= strings_size, "(" INTPTR_FORMAT " - " INTPTR_FORMAT ") = %d > %d ", p2i(p), p2i(start), (uint)(p - start), strings_size);
- _C_strings_count = strings_count;
- log_debug(aot, codecache, init)(" Loaded %d C strings of total length %d at offset %d from AOT Code Cache", _C_strings_count, strings_size, strings_offset);
+ if (space_left >= DATA_ALIGNMENT) {
+ load_failure_log().print_cr("AOT Code Cache disabled: unaccounted space after C strings");
+ FREE_C_HEAP_ARRAY(strings_ref);
+ return false;
+ }
+
+ _cached_C_strings = strings_ref;
+ _C_strings_count = strings_count;
+ log_info(aot, codecache, init)(" Loaded %d C strings of total length %d at offset %d from AOT Code Cache", _C_strings_count, strings_size, strings_offset);
+ return true;
}
int AOTCodeCache::store_strings() {
@@ -2304,8 +4907,7 @@ int AOTCodeCache::store_strings() {
length += len;
assert(len < 1000, "big string: %s", str);
lengths[i] = len;
- uint n = write_bytes(str, len);
- if (n != len) {
+ if (!write_bytes(str, len)) {
return -1;
}
}
@@ -2387,9 +4989,6 @@ static int search_address(address addr, address* table, uint length) {
address AOTCodeAddressTable::address_for_id(int idx) {
assert(_extrs_complete || initializing_extrs, "AOT Code Cache VM runtime addresses table is not complete");
- if (idx == -1) {
- return (address)-1;
- }
if (idx >= (_c_str_base + _C_strings_count)) {
fatal("recorded id: %d > recorded count %d", idx, (_c_str_base + _C_strings_count));
return nullptr;
@@ -2399,82 +4998,98 @@ address AOTCodeAddressTable::address_for_id(int idx) {
return nullptr;
}
uint id = (uint)idx;
+ address result = nullptr;
// no need to compare unsigned id against 0
if (id < _extrs_length) {
- return _extrs_addr[id - _extrs_base];
- }
+ result = _extrs_addr[id - _extrs_base];
+ } else
if (id >= _stubs_base && id < _c_str_base) {
- return _stubs_addr[id - _stubs_base];
- }
+ result = _stubs_addr[id - _stubs_base];
+ } else
if (id >= _c_str_base && id < (uint)(_c_str_base + _C_strings_count)) {
- return address_for_C_string(id - _c_str_base);
+ result = address_for_C_string(id - _c_str_base);
}
- fatal("Incorrect id %d for AOT Code Cache addresses table", id);
- return nullptr;
+ if (result == nullptr) {
+ fatal("Incorrect id %d for AOT Code Cache addresses table", id);
+ }
+ log_trace(aot, codecache)("Address " INTPTR_FORMAT " retrieved from AOT Code Cache address table for index '%u'",
+ p2i(result), id);
+
+ return result;
}
-int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeBlob* code_blob) {
+int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeBlob* code_blob, bool assert_for_unknown_address) {
assert(_extrs_complete || initializing_extrs, "AOT Code Cache VM runtime addresses table is not complete");
- int id = -1;
- if (addr == (address)-1) { // Static call stub has jump to itself
- return id;
- }
// fast path for stubs and external addresses
if (_hash_table != nullptr) {
- int *result = _hash_table->get(addr);
+ int* result = _hash_table->get(addr);
if (result != nullptr) {
- id = *result;
+ int id = *result;
log_trace(aot, codecache)("Address " INTPTR_FORMAT " retrieved from AOT Code Cache address hash table with index '%d'",
p2i(addr), id);
return id;
}
}
// Seach for C string
- id = id_for_C_string(addr);
+ int id = id_for_C_string(addr);
if (id != BAD_ADDRESS_ID) {
return id + _c_str_base;
}
if (StubRoutines::contains(addr) || CodeCache::find_blob(addr) != nullptr) {
// Search for a matching stub entry
id = search_address(addr, _stubs_addr, _stubs_max);
- if (id == BAD_ADDRESS_ID) {
+ if (id != BAD_ADDRESS_ID) {
+ return id + _stubs_base;
+ } else if (assert_for_unknown_address) {
+#ifdef ASSERT
StubCodeDesc* desc = StubCodeDesc::desc_for(addr);
+ reloc.print_current_on(tty);
+ code_blob->print_on(tty);
+ code_blob->print_code_on(tty);
const char* sub_name = (desc != nullptr) ? desc->name() : "";
assert(false, "Address " INTPTR_FORMAT " for Stub:%s is missing in AOT Code Cache addresses table", p2i(addr), sub_name);
- } else {
- return id + _stubs_base;
+#endif
}
} else {
- // Search in runtime functions
- id = search_address(addr, _extrs_addr, _extrs_length);
- if (id == BAD_ADDRESS_ID) {
+ // Search in runtime functions.
+ // Until C1 runtime finish initialization search only addresses
+ // recorded before it.
+ uint len = _c1_stubs_complete ? _extrs_length : _extrs2_length;
+ id = search_address(addr, _extrs_addr, len);
+ if (id != BAD_ADDRESS_ID) {
+ return _extrs_base + id;
+ } else if (assert_for_unknown_address) {
+#ifdef ASSERT
ResourceMark rm;
const int buflen = 1024;
char* func_name = NEW_RESOURCE_ARRAY(char, buflen);
int offset = 0;
if (os::dll_address_to_function_name(addr, func_name, buflen, &offset)) {
-#ifdef ASSERT
reloc.print_current_on(tty);
code_blob->print_on(tty);
code_blob->print_code_on(tty);
- assert(false, "Address " INTPTR_FORMAT " for runtime target <%s+%d>/('%s') is missing in AOT Code Cache addresses table", p2i(addr), func_name, offset, (const char*)addr);
-#endif
+ assert(false, "Address " INTPTR_FORMAT " for runtime target <%s+%d> is missing in AOT Code Cache addresses table", p2i(addr), func_name, offset);
} else {
-#ifdef ASSERT
reloc.print_current_on(tty);
code_blob->print_on(tty);
code_blob->print_code_on(tty);
os::find(addr, tty);
- assert(false, "Address " INTPTR_FORMAT " for /('%s') is missing in AOT Code Cache addresses table", p2i(addr), (const char*)addr);
-#endif
+ assert(false, "Address " INTPTR_FORMAT " for is missing in AOT Code Cache addresses table", p2i(addr));
}
- } else {
- return _extrs_base + id;
+#endif
}
}
+ assert(id == BAD_ADDRESS_ID, "id: %d", id);
+ log_debug(aot, codecache)("Address " INTPTR_FORMAT " is missing in AOT Code Cache addresses table", p2i(addr));
return id;
}
+#undef _extrs_max
+#undef _stubs_max
+#undef _extrs_base
+#undef _stubs_base
+#undef _all_max
+
AOTRuntimeConstants AOTRuntimeConstants::_aot_runtime_constants;
void AOTRuntimeConstants::initialize_from_runtime() {
@@ -2521,17 +5136,45 @@ address AOTRuntimeConstants::card_table_base_address() {
return (address)&_aot_runtime_constants._card_table_base;
}
+void AOTCodeEntry::print(outputStream* st) const {
+ st->print_cr(" AOT Code Cache entry " INTPTR_FORMAT " [kind: %d, id: " UINT32_FORMAT_X_0 ", offset: %d, size: %d, comp_level: %d, comp_id: %d, %s%s%s%s]",
+ p2i(this), (int)_kind, _id, _offset, _size, _comp_level, _comp_id,
+ (_not_entrant? "not_entrant" : "entrant"),
+ (_loaded ? ", loaded" : ""),
+ (_has_clinit_barriers ? ", has_clinit_barriers" : ""),
+ (_for_preload ? ", for_preload" : ""));
+}
+
// This is called after initialize() but before init2()
-// and _cache is not set yet.
+// and _cache is not set yet - use `opened_cache`.
void AOTCodeCache::print_on(outputStream* st) {
if (opened_cache != nullptr && opened_cache->for_use()) {
- st->print_cr("\nAOT Code Cache");
+ st->print_cr("\nAOT Code Cache Preload entries");
+
+ uint preload_count = opened_cache->_load_header->preload_entries_count();
+ AOTCodeEntry* preload_entries = (AOTCodeEntry*)opened_cache->addr(opened_cache->_load_header->preload_entries_offset());
+ for (uint i = 0; i < preload_count; i++) {
+ AOTCodeEntry* entry = &preload_entries[i];
+
+ uint entry_position = entry->offset();
+ uint name_offset = entry->name_offset() + entry_position;
+ const char* saved_name = opened_cache->addr(name_offset);
+
+ st->print_cr("%4u: %10s Id:%u AP%u size=%u '%s' %s%s%s",
+ i, aot_code_entry_kind_name[entry->kind()], entry->id(), entry->comp_level(),
+ entry->size(), saved_name,
+ entry->has_clinit_barriers() ? " has_clinit_barriers" : "",
+ entry->is_loaded() ? " loaded" : "",
+ entry->not_entrant() ? " not_entrant" : "");
+ }
+
+ st->print_cr("\nAOT Code Cache entries");
+
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);
+ uint* search_entries = (uint*)opened_cache->addr(opened_cache->_load_header->search_table_offset()); // [id, index]
+ AOTCodeEntry* load_entries = (AOTCodeEntry*)opened_cache->addr(opened_cache->_load_header->entries_offset());
for (uint i = 0; i < count; i++) {
- // Use search_entries[] to order ouput
int index = search_entries[2*i + 1];
AOTCodeEntry* entry = &(load_entries[index]);
@@ -2539,15 +5182,17 @@ void AOTCodeCache::print_on(outputStream* st) {
uint name_offset = entry->name_offset() + entry_position;
const char* saved_name = opened_cache->addr(name_offset);
- 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);
+ st->print_cr("%4u: %10s idx:%4u Id:%u A%u size=%u '%s' %s%s",
+ i, aot_code_entry_kind_name[entry->kind()], index, entry->id(), entry->comp_level(),
+ entry->size(), saved_name,
+ entry->is_loaded() ? " loaded" : "",
+ entry->not_entrant() ? " not_entrant" : "");
}
}
}
// methods for managing entries in multi-stub blobs
-
AOTStubData::AOTStubData(BlobId blob_id) :
_blob_id(blob_id),
_cached_blob(nullptr),
@@ -2588,9 +5233,7 @@ AOTStubData::AOTStubData(BlobId blob_id) :
bool AOTStubData::load_code_blob() {
assert(is_using(), "should not call");
assert(!is_invalid() && _cached_blob == nullptr, "repeated init");
- _cached_blob = AOTCodeCache::load_code_blob(AOTCodeEntry::StubGenBlob,
- _blob_id,
- this);
+ _cached_blob = AOTCodeCache::load_multi_stub_blob(_blob_id, this);
if (_cached_blob == nullptr) {
set_invalid();
return false;
@@ -2602,9 +5245,7 @@ bool AOTStubData::load_code_blob() {
bool AOTStubData::store_code_blob(CodeBlob& new_blob, CodeBuffer *code_buffer) {
assert(is_dumping(), "should not call");
assert(_cached_blob == nullptr, "should not be loading and storing!");
- if (!AOTCodeCache::store_code_blob(new_blob,
- AOTCodeEntry::StubGenBlob,
- _blob_id, this, code_buffer)) {
+ if (!AOTCodeCache::store_multi_stub_blob(new_blob, _blob_id, this, code_buffer)) {
set_invalid();
return false;
} else {
@@ -2719,3 +5360,170 @@ void AOTStubData::check_stored(StubId stub_id) {
}
}
#endif
+
+//============ Statistic ============
+
+void AOTCodeCache::print_timers_on(outputStream* st) {
+ if (is_using_code()) {
+ st->print_cr (" AOT Code Preload Time: %7.3f s", _t_totalPreload.seconds());
+ st->print_cr (" AOT Code Load Time: %7.3f s", _t_totalLoad.seconds());
+ st->print_cr (" nmethod register: %7.3f s", _t_totalRegister.seconds());
+ st->print_cr (" find AOT code entry: %7.3f s", _t_totalFind.seconds());
+ }
+ if (is_dumping_code()) {
+ st->print_cr (" AOT Code Store Time: %7.3f s", _t_totalStore.seconds());
+ }
+}
+
+void AOTCodeCache::log_stats_on_exit(AOTCodeEntryStats& stats) {
+ LogStream log_aot(LogLevel::Info, LogTagSetMapping::tagset());
+ LogStream log_exit(LogLevel::Info, LogTagSetMapping::tagset());
+ LogStream& log = log_aot.is_enabled() ? log_aot : log_exit;
+ if (log.is_enabled()) {
+ for (uint kind = AOTCodeEntry::Adapter; kind < AOTCodeEntry::Kind_count; kind++) {
+ log.print_cr(" %s: total=%u", aot_code_entry_kind_name[kind], stats.entry_count(kind));
+ if (kind == AOTCodeEntry::Nmethod) {
+ for (uint lvl = CompLevel_simple; lvl < AOTCompLevel_count; lvl++) {
+ log.print(" Tier %d: total=%u", lvl, stats.nmethod_count(lvl));
+ if (lvl == AOTCompLevel_count-1) { // AOT Preload
+ log.print(", has_clinit_barriers=%u", stats.clinit_barriers_count());
+ }
+ log.cr();
+ }
+ }
+ }
+ }
+}
+
+struct AOTCodeStats {
+private:
+ struct {
+ uint _loaded_cnt;
+ uint _invalidated_cnt;
+ uint _load_failed_cnt;
+ } _entry_kinds[AOTCodeEntry::Kind_count],
+ _nmethods[AOTCompLevel_count];
+
+ static void check_kind(uint kind) {
+ AOTCodeEntryStats::check_kind(kind);
+ }
+ static void check_complevel(uint lvl) {
+ AOTCodeEntryStats::check_complevel(lvl);
+ }
+ void inc_entry_loaded_cnt(uint kind) { check_kind(kind); _entry_kinds[kind]._loaded_cnt += 1; }
+ void inc_entry_invalidated_cnt(uint kind) { check_kind(kind); _entry_kinds[kind]._invalidated_cnt += 1; }
+ void inc_entry_load_failed_cnt(uint kind) { check_kind(kind); _entry_kinds[kind]._load_failed_cnt += 1; }
+
+ void inc_nmethod_loaded_cnt(uint lvl) { check_complevel(lvl); _nmethods[lvl]._loaded_cnt += 1; }
+ void inc_nmethod_invalidated_cnt(uint lvl) { check_complevel(lvl); _nmethods[lvl]._invalidated_cnt += 1; }
+ void inc_nmethod_load_failed_cnt(uint lvl) { check_complevel(lvl); _nmethods[lvl]._load_failed_cnt += 1; }
+
+ void inc_loaded_cnt(AOTCodeEntry* entry) {
+ inc_entry_loaded_cnt(entry->kind());
+ if (entry->is_nmethod()) {
+ entry->for_preload() ? inc_nmethod_loaded_cnt(CompLevel_preload_optimization)
+ : inc_nmethod_loaded_cnt(entry->comp_level());
+ }
+ }
+
+ void inc_invalidated_cnt(AOTCodeEntry* entry) {
+ inc_entry_invalidated_cnt(entry->kind());
+ if (entry->is_nmethod()) {
+ entry->for_preload() ? inc_nmethod_invalidated_cnt(CompLevel_preload_optimization)
+ : inc_nmethod_invalidated_cnt(entry->comp_level());
+ }
+ }
+
+ void inc_load_failed_cnt(AOTCodeEntry* entry) {
+ inc_entry_load_failed_cnt(entry->kind());
+ if (entry->is_nmethod()) {
+ entry->for_preload() ? inc_nmethod_load_failed_cnt(CompLevel_preload_optimization)
+ : inc_nmethod_load_failed_cnt(entry->comp_level());
+ }
+ }
+public:
+ AOTCodeStats() {
+ memset(this, 0, sizeof(AOTCodeStats));
+ }
+ void collect_entry_runtime_stats(AOTCodeEntry* entry) {
+ if (entry->is_loaded()) {
+ inc_loaded_cnt(entry);
+ }
+ if (entry->not_entrant()) {
+ inc_invalidated_cnt(entry);
+ }
+ if (entry->load_fail()) {
+ inc_load_failed_cnt(entry);
+ }
+ }
+
+ uint entry_loaded_count(uint kind) const { check_kind(kind); return _entry_kinds[kind]._loaded_cnt; }
+ uint entry_invalidated_count(uint kind) const { check_kind(kind); return _entry_kinds[kind]._invalidated_cnt; }
+ uint entry_load_failed_count(uint kind) const { check_kind(kind); return _entry_kinds[kind]._load_failed_cnt; }
+
+ uint nmethod_loaded_count(uint lvl) const { check_complevel(lvl); return _nmethods[lvl]._loaded_cnt; }
+ uint nmethod_invalidated_count(uint lvl) const { check_complevel(lvl); return _nmethods[lvl]._invalidated_cnt; }
+ uint nmethod_load_failed_count(uint lvl) const { check_complevel(lvl); return _nmethods[lvl]._load_failed_cnt; }
+};
+
+static void print_helper(outputStream* st, const char* name, int count) {
+ if (count > 0) {
+ st->print(" %s=%d", name, count);
+ }
+}
+
+void AOTCodeCache::print_statistics_on(outputStream* st) {
+ AOTCodeCache* cache = open_for_use();
+ if (cache != nullptr) {
+ AOTCodeEntryStats estats;
+ AOTCodeStats cstats;
+
+ uint preload_count = cache->_load_header->preload_entries_count();
+ uint count = cache->_load_header->entries_count();
+ if ((preload_count + count) == 0) {
+ return;
+ }
+ AOTCodeEntry* preload_entries = (AOTCodeEntry*)cache->addr(cache->_load_header->preload_entries_offset());
+ for (uint i = 0; i < preload_count; i++) {
+ AOTCodeEntry* entry = &preload_entries[i];
+ estats.collect_entry_stats(entry);
+ cstats.collect_entry_runtime_stats(entry);
+
+ }
+
+ AOTCodeEntry* load_entries = (AOTCodeEntry*)cache->addr(cache->_load_header->entries_offset());
+ for (uint i = 0; i < count; i++) {
+ AOTCodeEntry* entry = &load_entries[i];
+ estats.collect_entry_stats(entry);
+ cstats.collect_entry_runtime_stats(entry);
+ }
+
+ st->print_cr("AOT Code Cache: ");
+
+ for (uint kind = AOTCodeEntry::Adapter; kind < AOTCodeEntry::Kind_count; kind++) {
+ if (estats.entry_count(kind) > 0) {
+ st->print(" %s:", aot_code_entry_kind_name[kind]);
+ print_helper(st, "total", estats.entry_count(kind));
+ print_helper(st, "loaded", cstats.entry_loaded_count(kind));
+ print_helper(st, "invalidated", cstats.entry_invalidated_count(kind));
+ print_helper(st, "failed", cstats.entry_load_failed_count(kind));
+ st->cr();
+ }
+ if (kind == AOTCodeEntry::Nmethod) {
+ for (uint lvl = CompLevel_simple; lvl < AOTCompLevel_count; lvl++) {
+ if (estats.nmethod_count(lvl) > 0) {
+ st->print(" AOT Code T%d", lvl);
+ print_helper(st, "total", estats.nmethod_count(lvl));
+ print_helper(st, "loaded", cstats.nmethod_loaded_count(lvl));
+ print_helper(st, "invalidated", cstats.nmethod_invalidated_count(lvl));
+ print_helper(st, "failed", cstats.nmethod_load_failed_count(lvl));
+ if (lvl == AOTCompLevel_count-1) {
+ print_helper(st, "has_clinit_barriers", estats.clinit_barriers_count());
+ }
+ st->cr();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp
index 4cfa0064ff0..6fe864f1f71 100644
--- a/src/hotspot/share/code/aotCodeCache.hpp
+++ b/src/hotspot/share/code/aotCodeCache.hpp
@@ -26,29 +26,45 @@
#define SHARE_CODE_AOTCODECACHE_HPP
#include "gc/shared/collectedHeap.hpp"
-#include "gc/shared/gc_globals.hpp"
+#include "memory/allocation.hpp"
+#include "nmt/memTag.hpp"
+#include "oops/oopsHierarchy.hpp"
#include "runtime/stubInfo.hpp"
+#include "runtime/vm_version.hpp"
+#include "utilities/exceptions.hpp"
#include "utilities/hashTable.hpp"
/*
- * AOT Code Cache collects code from Code Cache and corresponding metadata
- * during application training run.
- * In following "production" runs this code and data can be loaded into
- * Code Cache skipping its generation.
+ * AOTCodeCache compiles AOT code during "assembly" phase by using metadata
+ * (classes and profiling) collected during application training run.
+ * Additionally special AOT "preload" code is generated with class initialization
+ * barriers which can be called on first Java method invocation.
+ * Compiled AOT code is collected and stored in AOT cache.
+ * In following "production" runs this code and data can be loaded from
+ * AOT cache skipping execution in the Interpreter and JIT compilation
+ * for corresponding hot methods.
*/
-class CodeBuffer;
-class RelocIterator;
+class AbstractCompiler;
class AOTCodeCache;
-class AOTCodeReader;
-class AdapterBlob;
-class ExceptionBlob;
-class ImmutableOopMapSet;
class AsmRemarks;
+class ciEnv;
+class ciMethod;
+class CodeBlob;
+class CompileTask;
class DbgStrings;
-
-enum class vmIntrinsicID : int;
-enum CompLevel : signed char;
+template
+class GrowableArray;
+class ImmutableOopMapSet;
+class JavaThread;
+class Klass;
+class methodHandle;
+class Metadata;
+class Method;
+class nmethod;
+class OopRecorder;
+class outputStream;
+class RelocIterator;
#define DO_AOTCODEENTRY_KIND(Fn) \
Fn(None) \
@@ -57,9 +73,28 @@ enum CompLevel : signed char;
Fn(C1Blob) \
Fn(C2Blob) \
Fn(StubGenBlob) \
+ Fn(Nmethod) \
// Descriptor of AOT Code Cache's entry
+//
+// These entries live in an array at the beginning of the "ac" region of the AOT cache.
+// State bits are mutated in place (monotonically) to manage the AOT code asset.
+// Main states are stored only, loaded, loaded & not_entrant, loaded & unloaded nmethod.
+//
+// For an nmethod, the id field is a narrow Method pointer.
+// For an adapter or blob, it is an adapter ID or BlobId.
+// Many fields are used only for nmethods; see comments per declaration.
+//
+// The nmethod is not directly pointed at from here; we indirect through Method::code,
+// which adds some uncertainty, since code is subject to concurrent update.
+//
+// Most of the ac region is read-only (never copied on write), but the AOTCodeEntry
+// blocks are modified. An alternative could be to move all remaining mutable ac data
+// into unmapped per-process data, indexed in parallel to the AOTCodeEntry blocks.
+// That does not seem to pay off, although it may if large pages ever store the ac bits.
+// The current organization is simpler, given small pages.
class AOTCodeEntry {
+ friend class VMStructs;
public:
enum Kind : s1 {
#define DECL_KIND_ENUM(kind) kind,
@@ -69,40 +104,69 @@ class AOTCodeEntry {
};
private:
- AOTCodeEntry* _next;
- Kind _kind;
- uint _id; // Adapter's id, vmIntrinsic::ID for stub or name's hash for nmethod
+ Kind _kind;
+ // Next field is exposed to external profilers - keep it as boolean.
+ bool _for_preload; // "preload" nmethod - runs before classes initialized
+ bool _not_entrant; // Deoptimized
+ bool _verified; // VerifyAOTCode
+ uint8_t _has_clinit_barriers:1, // Generated code has class init checks (only in for_preload code)
+ _has_oop_maps:1,
+ _has_vectors:1, // nmethod uses Vector API
+ _loaded:1, // Code was loaded for use
+ _load_fail:1; // Failed to load due to some klass state
+ // Note: Both _loaded and _load_fail are written only by a compiler thread which owns
+ // this AOTCodeEntry. The other bitfields are constants (set at AOT dump). Still,
+ // consider refactoring this loose cluster of booleans.
+
+ uint _id; // Adapter's id, BlobId for stub or Method's offset in AOTCache for nmethod
uint _offset; // Offset to entry
uint _size; // Entry size
- uint _name_offset; // Code blob name
+ uint _name_offset; // Compiled method name, adapter name, StubInfo::name()
uint _name_size;
- uint _blob_offset; // Start of code in cache
- bool _has_oop_maps;
- address _dumptime_content_start_addr; // CodeBlob::content_begin() at dump time; used for applying relocations
+ uint _code_offset; // Start of code in cache (zero for blobs)
+ uint _comp_level; // compilation level, nmethod only
+ uint _comp_id; // compilation id, nmethod only
+ uint _num_inlined_bytecodes; // nmethod only
+ uint _inline_instructions_size; // size from training run, nmethod only
public:
AOTCodeEntry(Kind kind, uint id,
uint offset, uint size,
uint name_offset, uint name_size,
- uint blob_offset, bool has_oop_maps,
- address dumptime_content_start_addr) {
- _next = nullptr;
+ uint code_offset, bool has_oop_maps,
+ uint comp_level = 0,
+ uint comp_id = 0,
+ bool has_clinit_barriers = false,
+ bool for_preload = false) {
_kind = kind;
+
+ _for_preload = for_preload;
+ _has_clinit_barriers = has_clinit_barriers;
+ _has_oop_maps = has_oop_maps;
+ _has_vectors = false; // will be set later
+ _loaded = false;
+ _load_fail = false;
+ _not_entrant = false;
+ _verified = false;
+
_id = id;
_offset = offset;
_size = size;
_name_offset = name_offset;
_name_size = name_size;
- _blob_offset = blob_offset;
- _has_oop_maps = has_oop_maps;
- _dumptime_content_start_addr = dumptime_content_start_addr;
+ _code_offset = code_offset;
+
+ _comp_level = comp_level;
+ _comp_id = comp_id;
+ _num_inlined_bytecodes = 0;
+ _inline_instructions_size = 0;
}
+
void* operator new(size_t x, AOTCodeCache* cache);
// Delete is a NOP
void operator delete( void *ptr ) {}
- AOTCodeEntry* next() const { return _next; }
- void set_next(AOTCodeEntry* next) { _next = next; }
+ Method* method() const;
Kind kind() const { return _kind; }
uint id() const { return _id; }
@@ -113,15 +177,46 @@ class AOTCodeEntry {
uint size() const { return _size; }
uint name_offset() const { return _name_offset; }
uint name_size() const { return _name_size; }
- uint blob_offset() const { return _blob_offset; }
+ uint code_offset() const { return _code_offset; }
+
bool has_oop_maps() const { return _has_oop_maps; }
- address dumptime_content_start_addr() const { return _dumptime_content_start_addr; }
+
+ bool has_vectors() const { return _has_vectors; }
+ void set_has_vectors(bool val) { _has_vectors = val; }
+
+ uint num_inlined_bytecodes() const { return _num_inlined_bytecodes; }
+ void set_inlined_bytecodes(int bytes) { _num_inlined_bytecodes = bytes; }
+
+ uint inline_instructions_size() const { return _inline_instructions_size; }
+ void set_inline_instructions_size(int size) { _inline_instructions_size = size; }
+
+ uint comp_level() const { return _comp_level; }
+ uint comp_id() const { return _comp_id; }
+
+ bool has_clinit_barriers() const { return _has_clinit_barriers; }
+ bool for_preload() const { return _for_preload; }
+ bool is_loaded() const { return _loaded; }
+ void set_loaded() { _loaded = true; }
+
+ bool is_verified() const;
+ void set_verified();
+
+ // Use atomic access to _not_entrant field.
+ bool not_entrant() const;
+ void set_not_entrant();
+ bool try_set_not_entrant();
+
+ bool load_fail() const { return _load_fail; }
+ void set_load_fail() { _load_fail = true; }
+
+ void print(outputStream* st) const NOT_CDS_RETURN;
static bool is_valid_entry_kind(Kind kind) { return kind > None && kind < Kind_count; }
static bool is_blob(Kind kind) { return kind == SharedBlob || kind == C1Blob || kind == C2Blob || kind == StubGenBlob; }
static bool is_single_stub_blob(Kind kind) { return kind == SharedBlob || kind == C1Blob || kind == C2Blob; }
static bool is_multi_stub_blob(Kind kind) { return kind == StubGenBlob; }
static bool is_adapter(Kind kind) { return kind == Adapter; }
+ bool is_nmethod() const { return _kind == Nmethod; }
};
// we use a hash table to speed up translation of external addresses
@@ -134,37 +229,75 @@ class AOTCodeAddressHashTable : public HashTable<
AnyObj::C_HEAP,
mtCode> {};
-// Addresses of stubs, blobs and runtime finctions called from compiled code.
+// Addresses of stubs, blobs and runtime functions called from compiled code.
+// There is a separate naming/indexing scheme for each.
+//
+// - The extrs are native C addresses used by code (via relocInfo::external_word).
+// They are keyed by their insertion position, according to the order of calls to
+// ADD_EXTERNAL_ADDRESS. The indexes must agree across the processes sharing the
+// AOT cache. The JVM currently checks only the lengths, to detect missing or
+// extra calls to ADD_EXTERNAL_ADDRESS in the APH or PR, perhaps due to flag
+// mismatches, which should have been caught earlier. Consider making an enum
+// for these guys, like the stubs have.
+//
+// - The stubs are well-known runtime support subroutines used by compiled code.
+// They are keyed by an enum EntryId, and populated by publish_stub_entries. They are
+// not C addresses but rather pointers to JVM-assembled code. Some stubs are
+// found in the AOT cache, but they are always OK to regenerate. Some stubs are
+// optional, in which case their array slots could be empty (null), and then you
+// get a fatal crash if you try to refer to one. Consistency of stub presence/absence
+// and stub (code) format and content between assembly and production runs should be
+// guaranteed by checks which enforce compatible command line compiler and runtime
+// configuration options across those runs. Missing checks or incorrect assumptions
+// regarding compatibility may also lead to a crash.
+//
+// - C strings are addresses of strings that are used in compiled code. They are
+// addressed by content. That is, if you need a string, spell it out, and either
+// a pre-existing string of the same spelling will be found for you, or a new
+// string will be added. They are sequentially assigned id numbers, one per
+// unique string, in the order of request.
+//
class AOTCodeAddressTable : public CHeapObj {
private:
- address* _extrs_addr;
- address* _stubs_addr;
+ AOTCodeAddressHashTable* _hash_table;
+
+ address* _extrs_addr; // array sequenced by dynamic ADD_EXTERNAL_ADDRESS calls
+ address* _stubs_addr; // array indexed by statically defined enum EntryId
uint _extrs_length;
+ uint _extrs2_length; // recorded in init_extrs2()
+ uint _final_extrs_length; // expected final count
+ //GrowableArray _C_strings; // array searched by content, indexed by int id
+ // _C_strings is a C static pointer, not a field; see init_C_strings_caching.
bool _extrs_complete;
bool _shared_stubs_complete;
bool _c1_stubs_complete;
bool _c2_stubs_complete;
bool _stubgen_stubs_complete;
- AOTCodeAddressHashTable* _hash_table;
void hash_address(address addr, int idx);
public:
AOTCodeAddressTable() :
+ _hash_table(nullptr),
_extrs_addr(nullptr),
_stubs_addr(nullptr),
_extrs_length(0),
+ _extrs2_length(0),
+ _final_extrs_length(0),
_extrs_complete(false),
_shared_stubs_complete(false),
_c1_stubs_complete(false),
_c2_stubs_complete(false),
- _stubgen_stubs_complete(false),
- _hash_table(nullptr)
+ _stubgen_stubs_complete(false)
{ }
void init_extrs();
void init_extrs2();
+ uint extrs_length() const { return _extrs_length; }
+ uint extrs2_length() const { return _extrs2_length; }
+ uint final_extrs_length() const { return _final_extrs_length;}
void add_stub_entry(EntryId entry_id, address entry);
void add_external_addresses(GrowableArray& addresses) NOT_CDS_RETURN;
+ uint add_c1_extrs_blobs(bool count_only);
void set_shared_stubs_complete();
void set_c1_stubs_complete();
void set_c2_stubs_complete();
@@ -172,7 +305,7 @@ class AOTCodeAddressTable : public CHeapObj {
const char* add_C_string(const char* str);
int id_for_C_string(address str);
address address_for_C_string(int idx);
- int id_for_address(address addr, RelocIterator iter, CodeBlob* code_blob);
+ int id_for_address(address addr, RelocIterator iter, CodeBlob* code_blob, bool assert_for_unknown_address);
address address_for_id(int id);
};
@@ -218,7 +351,7 @@ class AOTStubData : public StackObj {
// Array of addresses owned by stubs. Each stub appends addresses to
// this array as a block, whether at the end of generation or at the
// end of restoration from the cache. The first two addresses in
- // each block are the "start" and "end2 address of the stub. Any
+ // each block are the "start" and "end" address of the stub. Any
// other visible addresses located within the range [start,end)
// follow, either extra entries, data addresses or SEGV-protected
// subrange start, end and handler addresses. In the special case
@@ -279,10 +412,20 @@ class AOTStubData : public StackObj {
do_var(int, AllocatePrefetchLines) /* stubs and nmethods */ \
do_var(int, AllocatePrefetchStepSize) /* stubs and nmethods */ \
do_var(uint, CodeEntryAlignment) /* array copy stubs and nmethods */ \
- do_var(bool, UseCompressedOops) /* stubs and nmethods */ \
do_var(bool, EnableContended) /* nmethods */ \
do_var(intx, OptoLoopAlignment) /* array copy stubs and nmethods */ \
do_var(bool, RestrictContended) /* nmethods */ \
+ do_var(int, ContendedPaddingWidth) \
+ do_var(uint, GCCardSizeInBytes) \
+ do_var(bool, AlwaysSafeConstructors) \
+ do_var(bool, UseCondCardMark) \
+ do_var(bool, DoJVMTIVirtualThreadTransitions) \
+ do_var(bool, PreserveFramePointer) \
+ do_var(intx, StackReservedPages) \
+ do_var(intx, StackShadowPages) \
+ do_var(bool, UseTLAB) \
+ do_var(bool, ZeroTLAB) \
+ do_var(bool, UseAdler32Intrinsics) \
do_var(bool, UseAESCTRIntrinsics) \
do_var(bool, UseAESIntrinsics) \
do_var(bool, UseBASE64Intrinsics) \
@@ -301,16 +444,42 @@ class AOTStubData : public StackObj {
do_var(bool, UseSHA3Intrinsics) \
do_var(bool, UseSHA512Intrinsics) \
do_var(bool, UseIntPolyIntrinsics) \
+ do_var(bool, UseVectorizedHashCodeIntrinsic) \
do_var(bool, UseVectorizedMismatchIntrinsic) \
- do_var(bool, ValueTypeReturnedAsFields) \
do_var(bool, VMContinuations) \
do_var(bool, VerifyOops) \
+ do_var(bool, CountCompiledCalls) \
+ do_var(bool, DTraceMethodProbes) \
+ do_var(bool, DTraceAllocProbes) \
do_fun(int, CompressedKlassPointers_shift, CompressedKlassPointers::shift()) \
do_fun(bool, JavaAssertions_systemClassDefault, JavaAssertions::systemClassDefault()) \
do_fun(bool, JavaAssertions_userClassDefault, JavaAssertions::userClassDefault()) \
+ do_fun(bool, COOP_use_implicit_null_checks, CompressedOops::use_implicit_null_checks()) \
+ do_fun(size_t, os_vm_page_size, os::vm_page_size()) \
do_fun(CollectedHeap::Name, Universe_heap_kind, Universe::heap()->kind()) \
// END
+#if INCLUDE_ZGC
+#define AOTCODECACHE_CONFIGS_ZGC_DO(do_var, do_fun) \
+ do_fun(uintptr_t, ZAddressHeapBaseShift_for_VerifyOops, (UseZGC && VerifyOops) ? ZAddressHeapBaseShift : 0) \
+ // END
+#else
+#define AOTCODECACHE_CONFIGS_ZGC_DO(do_var, do_fun)
+#endif
+
+#if INCLUDE_SHENANDOAHGC
+#define AOTCODECACHE_CONFIGS_SHENANDOAHGC_DO(do_var, do_fun) \
+ do_var(bool, ExplicitGCInvokesConcurrent) \
+ do_var(bool, ShenandoahImplicitGCInvokesConcurrent) \
+ do_var(bool, ShenandoahLoadRefBarrier) \
+ do_var(bool, ShenandoahSATBBarrier) \
+ do_var(bool, ShenandoahCloneBarrier) \
+ do_var(bool, ShenandoahCardBarrier) \
+ // END
+#else
+#define AOTCODECACHE_CONFIGS_SHENANDOAHGC_DO(do_var, do_fun)
+#endif
+
#ifdef COMPILER2
#define AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) \
do_var(intx, ArrayOperationPartialInlineSize) /* array copy stubs and nmethods */ \
@@ -320,6 +489,8 @@ class AOTStubData : public StackObj {
do_var(bool, UseMulAddIntrinsic) \
do_var(bool, UseMultiplyToLenIntrinsic) \
do_var(bool, UseSquareToLenIntrinsic) \
+ do_var(bool, StackTraceInThrowable) \
+ do_var(bool, OmitStackTraceInFastThrow) \
// END
#else
#define AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun)
@@ -331,12 +502,16 @@ class AOTStubData : public StackObj {
do_var(intx, PrefetchCopyIntervalInBytes) /* array copy stubs */ \
do_var(int, SoftwarePrefetchHintDistance) /* array fill stubs */ \
do_var(bool, UseBlockZeroing) \
+ do_var(bool, UseCryptoPmullForCRC32) \
do_var(bool, UseSecondarySupersCache) \
do_var(bool, UseSIMDForArrayEquals) /* array copy stubs and nmethods */ \
do_var(bool, UseSIMDForBigIntegerShiftIntrinsics) \
do_var(bool, UseSIMDForMemoryOps) /* array copy stubs and nmethods */ \
do_var(bool, UseSIMDForSHA3Intrinsic) /* SHA3 stubs */ \
do_var(bool, UseSimpleArrayEquals) \
+ do_fun(int, VM_zva_length, UseBlockZeroing ? VM_Version::zva_length() : 0) \
+ do_fun(int, VM_dcache_line_size, VM_Version::dcache_line_size()) \
+ do_fun(bool, VM_use_rop_protection, VM_Version::use_rop_protection()) \
// END
#else
#define AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun)
@@ -345,8 +520,12 @@ class AOTStubData : public StackObj {
#if defined(X86) && !defined(ZERO)
#define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) \
do_var(int, AVX3Threshold) /* array copy stubs and nmethods */ \
+ do_var(int, CopyAVX3Threshold) \
do_var(bool, EnableX86ECoreOpts) /* nmethods */ \
+ do_var(bool, UseCountTrailingZerosInstruction) \
do_var(bool, UseLibmIntrinsic) \
+ do_var(bool, UseSSE42Intrinsics) \
+ do_var(bool, CheckJNICalls) \
// END
#else
#define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun)
@@ -360,6 +539,7 @@ class AOTStubData : public StackObj {
do_var(bool, UseCtxFencei) /* method entry barrier stub */ \
do_var(bool, UseSecondarySupersCache) /* secondary supers cache in nmethods */ \
do_var(bool, UseZabha) /* narrow cmpxchg selection in nmethods */ \
+ do_var(bool, UsePopCountInstruction) /* other platforms record it in CPU features */ \
do_fun(int, SatpMode, (int)VM_Version::satp_mode.value()) \
do_fun(int, RVZicbozBlockSize, (int)VM_Version::zicboz_block_size.value()) \
// END
@@ -369,6 +549,8 @@ class AOTStubData : public StackObj {
#define AOTCODECACHE_CONFIGS_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_GENERIC_DO(do_var, do_fun) \
+ AOTCODECACHE_CONFIGS_ZGC_DO(do_var, do_fun) \
+ AOTCODECACHE_CONFIGS_SHENANDOAHGC_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) \
@@ -378,7 +560,98 @@ class AOTStubData : public StackObj {
#define AOTCODECACHE_DECLARE_VAR(type, name) type _saved_ ## name;
#define AOTCODECACHE_DECLARE_FUN(type, name, func) type _saved_ ## name;
+enum class DataKind: int {
+ No_Data = -1,
+ Null = 0,
+ Klass = 1,
+ Method = 2,
+ String = 3,
+ MH_Oop = 4,
+ Primitive = 5, // primitive Class object
+ SysLoader = 6, // java_system_loader
+ PlaLoader = 7, // java_platform_loader
+ MethodCnts= 8
+};
+
+struct AOTCodeEntryStats;
+
+// The AOT code cache lives in two places: First, an AOTCache file that contains code
+// also has a memory-mappable "ac region", which in turn contains mappable C data
+// structures. Second, a per-process, heap-allocated AOTCodeCache contains all the
+// pointers and metadata required to properly read or write the mapped ac region.
+// The AOTCodeCache object is always only using or dumping the region, never both.
+//
+// The region layout is described in detail below, near the field declarations.
+// Most of the region is used read-only, but AOTCodeEntry state flags are updated.
+// The region has very few relocatable pointers (unlike other AOT cache regions).
+// Barring AOT cache load failure, the whole AOT cache lives forever (in the process).
+// This makes the ac region a good place to keep immutable data that might be needed
+// in the future, such as code, metadata, strings, and tables.
+//
+// Each code asset is described by an AOTCodeEntry block, with an offset to more data.
+// That data is a pre-image of an AOT-compiled nmethod, not directly executable.
+// It can be quickly copied into the JVM code heap and patched to make it executable.
+// Some immutable, position-independent parts of the nmethod are kept in the ac region.
+// These parts do not need to be copied. They include scopes, dependencies, PC tables.
+//
+// The CDS function FileMapHeader::validate validates the AOTCache as a whole.
+// After the AOTCodeCache is mapped, additional code-specific validation is done:
+// - the cpu_features array is checked against the JVM's VM_Version
+// - the Header::config checks code-sensitive flags (AOTCodeCache::Config::verify)
+// - each entry lookup/load also checks flags (verify_c2_state, is_compilation_valid)
+//
+// Compiled code in the JVM depends on several environmental assumptions.
+// - clinit (state, order) - this is guarded in code (AP4) or before load (A1/A2/A4)
+// - constants (stable/final) - the JIT can just read and trust; AOT must always load
+// - native addresses/data - link via relocInfo; AOT sometimes needs a GOT indirection
+// - machine features - the JIT can just assume; AOT refuses to load mismatched code
+//
+// AOT and JIT use many of the same relocInfo features. Some constants have to be
+// AOT-compiled using extra indirection through a GOT-like table (AOTRuntimeConstants).
+// While the JIT often compiles native constants straight into code, the AOT compiler
+// needs to perform link-editing when installing code, for addresses which could not be
+// determined when the AOT cache was dumped. The AOTCodeAddressTable provides the
+// mappings required when link-editing the installed AOT code. One subtle disadvantage
+// of AOT is that the size of the JVM code heap, and its placement relative to native C
+// addresses, are hard to predict, so AOT code must prepare its call sites and external
+// data references to handle the longest possible addressing distances. A JIT can
+// sometimes predict that a short call will be just fine, and avoid the overhead of a
+// long call instruction sequence.
+//
+// In light of how JIT and AOT code work, here is the value of joining them together.
+//
+// Because the JIT always compiles with full knowledge of the present machine, and the
+// most recent application code (classes) and behavior (clinit states, profiles), the
+// JIT can usually produce better, faster code than an AOT compiler that must guess at
+// future conditions on the same machine. This is why AOT code does not replace JIT
+// code. Rather it allows the JIT to focus on the most important optimization tasks,
+// while less performance sensitive parts of the application, which used to run in the
+// interpreter or lower tiers, can run faster in high-tier AOT modes (AP4 and A4).
+//
+// Because AP4 executes clinit barriers like the interpreter, it can serve as a faster
+// alternative to the interpreter. This is why AP4 methods are loaded very eagerly at
+// startup. (Note that they are in their own array, ready to load ASAP.) However, as
+// soon as all the clinits required by an A4 method are done, the JVM prefers it to the
+// AP4 method, since performing clinit barriers is expensive, even in optimized code.
+// So the AP4 method is a win, but only until the corresponding A4 method is done
+// waiting for the clinits it depends on. (See the declaration of CompileTrainingData
+// for a full discussion of how that waiting is managed.)
+//
+// After the A4 method is swapped in, there is still a mild performance penalty due to
+// constants being loaded (instead of folded into other constants, which is normal
+// business for the JIT). To enable replacement of A4 methods by faster code, each A4
+// method has an invocation counter (like a T2/T3 method). When that invocation counter
+// reaches a level similar to what was seen in the training run (before the training
+// run JIT compiled a T4 method), the JVM launches a JIT task to recompile the A4
+// method with a fresh T4 method. When that JIT task is done, calls to the T4 method
+// run at full speed.
+//
+// Because there was less reliance on the interpreter during startup, and because the
+// JIT didn't have to work so hard to speed startup (since AOT code filled the gap),
+// the JIT can get right to work on the methods that need the most attention.
+//
class AOTCodeCache : public CHeapObj {
+ friend class AOTMapLogger;
// Classes used to describe AOT code cache.
protected:
@@ -387,7 +660,9 @@ class AOTCodeCache : public CHeapObj {
// Special configs that cannot be checked with macros
address _compressedOopBase;
- int _compressedOopShift;
+ int _compressedOopShift;
+ address _compressedKlassBase;
+ size_t _codeCacheSize;
#if defined(X86) && !defined(ZERO)
bool _useUnalignedLoadStores;
@@ -397,75 +672,139 @@ class AOTCodeCache : public CHeapObj {
bool _avoidUnalignedAccesses;
#endif
- uint _cpu_features_offset; // offset in the cache where cpu features are stored
+#ifdef COMPILER2
+ bool _reduceInitialCardMarks;
+ uint _c2_ea_state;
+#endif
+
+ uint _jvmti_state;
+
public:
- void record(uint cpu_features_offset);
- bool verify_cpu_features(AOTCodeCache* cache) const;
- bool verify(AOTCodeCache* cache) const;
+ void record();
+ bool verify() const;
+
+#ifdef COMPILER2
+ bool reduce_initial_cm() const { return _reduceInitialCardMarks; }
+ uint c2_ea_state() const { return _c2_ea_state; }
+#endif
+ uint jvmti_state() const { return _jvmti_state; }
};
+ // Header of the "ac" region within the AOT cache.
class Header : public CHeapObj {
private:
+ // Here should be version and other verification fields
enum {
- AOT_CODE_VERSION = 1
+ AOT_CODE_VERSION = 2 // Bump this number in releases where Header layout changes
};
uint _version; // AOT code version (should match when reading code cache)
uint _cache_size; // cache size in bytes
- uint _strings_count; // number of recorded C strings
- uint _strings_offset; // offset to recorded C strings
+ uint _cpu_features_size;
+ uint _cpu_features_offset; // offset in the cache where cpu features are stored
+ uint _preload_entries_count; // entries for pre-loading code
+ uint _preload_entries_offset;
uint _entries_count; // number of recorded entries
uint _entries_offset; // offset of AOTCodeEntry array describing entries
+ uint _search_table_offset; // offset of table for looking up an AOTCodeEntry
+ uint _strings_count; // number of recorded C strings
+ uint _strings_offset; // offset to recorded C strings
uint _adapters_count;
uint _shared_blobs_count;
uint _stubgen_blobs_count;
uint _C1_blobs_count;
uint _C2_blobs_count;
+ uint _extrs2_length; // recorded in init_extrs2()
+ uint _final_extrs_length; // final external addresses count
+
Config _config; // must be the last element as there is trailing data stored immediately after Config
+ bool contains(uint offset, uint size) const {
+ return offset <= _cache_size && size <= (_cache_size - offset);
+ }
+
public:
void init(uint cache_size,
- uint strings_count, uint strings_offset,
- uint entries_count, uint entries_offset,
- uint adapters_count, uint shared_blobs_count,
- uint stubgen_blobs_count, uint C1_blobs_count,
- uint C2_blobs_count, uint cpu_features_offset) {
- _version = AOT_CODE_VERSION;
- _cache_size = cache_size;
- _strings_count = strings_count;
- _strings_offset = strings_offset;
- _entries_count = entries_count;
- _entries_offset = entries_offset;
- _adapters_count = adapters_count;
- _shared_blobs_count = shared_blobs_count;
- _stubgen_blobs_count = stubgen_blobs_count;
- _C1_blobs_count = C1_blobs_count;
- _C2_blobs_count = C2_blobs_count;
- _config.record(cpu_features_offset);
+ uint cpu_features_size, uint cpu_features_offset,
+ uint preload_entries_count, uint preload_entries_offset,
+ uint entries_count, uint entries_offset,
+ uint search_table_offset,
+ uint strings_count, uint strings_offset,
+ uint adapters_count, uint shared_blobs_count,
+ uint stubgen_blobs_count,
+ uint C1_blobs_count, uint C2_blobs_count,
+ uint extrs2_length, uint final_extrs_length) {
+ _version = AOT_CODE_VERSION;
+ _cache_size = cache_size;
+ _cpu_features_size = cpu_features_size;
+ _cpu_features_offset = cpu_features_offset;
+ _preload_entries_count = preload_entries_count;
+ _preload_entries_offset = preload_entries_offset;
+ _entries_count = entries_count;
+ _entries_offset = entries_offset;
+ _search_table_offset = search_table_offset;
+ _strings_count = strings_count;
+ _strings_offset = strings_offset;
+ _adapters_count = adapters_count;
+ _shared_blobs_count = shared_blobs_count;
+ _stubgen_blobs_count = stubgen_blobs_count;
+ _C1_blobs_count = C1_blobs_count;
+ _C2_blobs_count = C2_blobs_count;
+ _extrs2_length = extrs2_length;
+ _final_extrs_length = final_extrs_length;
+
+ _config.record();
}
-
uint cache_size() const { return _cache_size; }
- uint strings_count() const { return _strings_count; }
- uint strings_offset() const { return _strings_offset; }
+ uint cpu_features_size() const { return _cpu_features_size; }
+ uint cpu_features_offset() const { return _cpu_features_offset; }
+ uint preload_entries_count() const { return _preload_entries_count; }
+ uint preload_entries_offset() const { return _preload_entries_offset; }
uint entries_count() const { return _entries_count; }
uint entries_offset() const { return _entries_offset; }
+ uint strings_count() const { return _strings_count; }
+ uint strings_offset() const { return _strings_offset; }
+ uint search_table_offset() const { return _search_table_offset; }
uint adapters_count() const { return _adapters_count; }
uint stubgen_blobs_count() const { return _stubgen_blobs_count; }
uint shared_blobs_count() const { return _shared_blobs_count; }
uint C1_blobs_count() const { return _C1_blobs_count; }
uint C2_blobs_count() const { return _C2_blobs_count; }
-
+ uint extrs2_length() const { return _extrs2_length; }
+ uint final_extrs_length() const { return _final_extrs_length; }
+ uint nmethods_count() const { return _preload_entries_count
+ + _entries_count
+ - _stubgen_blobs_count
+ - _shared_blobs_count
+ - _C1_blobs_count
+ - _C2_blobs_count
+ - _adapters_count; }
+#ifdef COMPILER2
+ bool verify_c2_state() const;
+#endif
+ bool verify_jvmti_state(ciEnv* env) const;
+ bool verify_cpu_features(AOTCodeCache* cache) const;
bool verify(uint load_size) const;
+ size_t verify_section(uint offset,
+ uint count,
+ size_t unit_size,
+ size_t low_limit,
+ const char* section_name) const;
bool verify_config(AOTCodeCache* cache) const { // Called after Universe initialized
- return _config.verify(cache);
+ // check CPU features before checking flags that may be
+ // auto-configured in response to them
+ if (!verify_cpu_features(cache)) {
+ return false;
+ }
+ return _config.verify();
}
};
// Continue with AOTCodeCache class definition.
private:
Header* _load_header;
- char* _load_buffer; // Aligned buffer for loading cached code
- char* _store_buffer; // Aligned buffer for storing cached code
+ char* _load_buffer; // Aligned buffer for loading AOT code
+ char* _store_buffer; // Aligned buffer for storing AOT code
char* _C_store_buffer; // Original unaligned buffer
uint _write_position; // Position in _store_buffer
@@ -478,10 +817,62 @@ class AOTCodeCache : public CHeapObj {
AOTCodeAddressTable* _table;
- AOTCodeEntry* _load_entries; // Used when reading cache
- uint* _search_entries; // sorted by ID table [id, index]
+ // What's in the AOT cache "ac" region? (It's what this object indexes.)
+ //
+ // The first read is of a CDS-style size/pointer handle to the ac region.
+ // CachedCodeDirectory { uint ac_size; char* ac_data; }
+ // After this, the @N values are relative to ac_data.
+ // It might be right after the CachedCodeDirectory. It might be far away.
+ //
+ // @0: Header (all the fixed fields that size and place objects in the ac region.
+ // See AOTCodeCache::Header::verify for a detailed walk through what follows.
+ //
+ // @cpu_features_offset: uchar[cpu_features_size] same layout as VM_Version
+ //
+ // @preload_entries_offset: AOTCodeEntry[preload_entries_count] "preload" AP4 code
+ // Sorted by compile_id (from the training run).
+ // Queued for load at startup, in that order (see preload_aot_code).
+ // See AOTCodePreload{Start,Stop} if bisection debugging needed.
+ //
+ // @(preload_entries[0]._offset): nmethod preimage for preload entry #0
+ // @(preload_entries[1]._offset): nmethod preimage for preload entry #1
+ // ...
+ //
+ // @entries_offset: AOTCodeEntry[entries_count] other code (A1/A2/A4/stub/adapter)
+ // Main (non-preload) code array, presented in dump order (arbitrary).
+ //
+ // @(entries[0]._offset): preimage for main entry #0 (nmethod, blob, etc.)
+ // @(entries[1]._offset): preimage for main entry #1
+ // ...
+ //
+ // @search_table_offset: uint[entries_count][2] id/index pairs, sorted by id
+ // The id matches AOTCodeEntry::_id. The index point into the entries array.
+ // If several entries for one id, use local linear search.
+ //
+ // @strings_offset: uint[strings_count] length of each C string, in recording order
+ // @strings_offset+4*strings_count: each string, in recording order
+ // This is not indexed; it is linearly processed at startup into _cached_C_strings.
+ // The AOTCodeAddressTable provides access to them.
+ //
+ // _table: AOTCodeAddressTable (in C heap)
+ // This table contains all the extrs, stubs, C strings, organized in heap arrays.
+ //
+ // compiled code also refers to oops and metadata, not accessed here.
+ // They are stored in other parts of the AOT cache, found via AOTCacheAccess.
+
+ // The following items are used for reading and writing the AOT cache.
+ AOTCodeEntry* _load_entries; // Used when reading cache @entries_offset
+ uint* _search_entries; // sorted by ID table [id, index] @search_table_offset
AOTCodeEntry* _store_entries; // Used when writing cache
- uint _store_entries_cnt;
+ uint _store_entries_cnt; // total entries count
+
+ // These guys can stash data to help log messages in the APH. Move to the writer obj?
+ uint _compile_id;
+ uint _comp_level;
+ uint compile_id() const { return _compile_id; }
+ uint comp_level() const { return _comp_level; }
+
+ ReservedSpace _reserved_space; // Reserved space to map AOT code cache
static AOTCodeCache* open_for_use();
static AOTCodeCache* open_for_dump();
@@ -491,7 +882,12 @@ class AOTCodeCache : public CHeapObj {
bool align_write_int();
bool align_write_bytes(uint alignment);
address reserve_bytes(uint nbytes);
- uint write_bytes(const void* buffer, uint nbytes);
+ bool write_bytes(const void* buffer, uint nbytes);
+ bool write_int(const int val);
+ bool write_kind(DataKind kind) {
+ return write_int(static_cast(kind));
+ }
+ Header* load_header() const { return _load_header; }
const char* addr(uint offset) const { return _load_buffer + offset; }
static AOTCodeAddressTable* addr_table() {
return is_on() && (cache()->_table != nullptr) ? cache()->_table : nullptr;
@@ -502,20 +898,26 @@ class AOTCodeCache : public CHeapObj {
bool lookup_failed() const { return _lookup_failed; }
void add_stub_entry(EntryId entry_id, address entry) NOT_CDS_RETURN;
+
+ ~AOTCodeCache();
+
public:
AOTCodeCache(bool is_dumping, bool is_using);
const char* cache_buffer() const { return _load_buffer; }
- bool failed() const { return _failed; }
- void set_failed() { _failed = true; }
+ // Use atomic access to _failed field.
+ bool failed() const;
+ void set_failed();
+
+ static bool is_address_in_aot_cache(address p) NOT_CDS_RETURN_(false);
static uint max_aot_code_size();
uint load_size() const { return _load_size; }
uint write_position() const { return _write_position; }
static void init_C_strings_caching();
- void load_strings();
+ bool load_strings();
int store_strings();
static void set_shared_stubs_complete() NOT_CDS_RETURN;
@@ -528,27 +930,51 @@ class AOTCodeCache : public CHeapObj {
address address_for_C_string(int idx) const { return _table->address_for_C_string(idx); }
address address_for_id(int id) const { return _table->address_for_id(id); }
- bool for_use() const { return _for_use && !_failed; }
- bool for_dump() const { return _for_dump && !_failed; }
+ bool for_use() const { return _for_use && !failed(); }
+ bool for_dump() const { return _for_dump && !failed(); }
AOTCodeEntry* add_entry() {
_store_entries_cnt++;
_store_entries -= 1;
return _store_entries;
}
+ void preload_aot_code(TRAPS);
- AOTCodeEntry* find_entry(AOTCodeEntry::Kind kind, uint id);
+ void set_load_entries();
+ bool verify_nmethod_entry(AOTCodeEntry* entry, uint id, uint low_bound, uint high_bound);
+ AOTCodeEntry* find_entry(AOTCodeEntry::Kind kind, uint id, uint comp_level = 0);
+ AOTCodeEntry* search_entry(AOTCodeEntry::Kind kind, uint id, uint comp_level);
+ void invalidate_entry(AOTCodeEntry* entry);
- void store_cpu_features(char*& buffer, uint buffer_size);
+ char* store_cpu_features(char* buffer, uint buffer_size);
+ static uint get_jvmti_state();
bool finish_write();
- bool write_relocations(CodeBlob& code_blob, RelocIterator& iter);
+ void log_stats_on_exit(AOTCodeEntryStats& stats);
+
+ bool write_klass(Klass* klass);
+ bool write_method(Method* method);
+
+ bool write_relocations(CodeBlob& code_blob, RelocIterator& iter,
+ bool assert_for_unknown_external_address,
+ GrowableArray* oop_list = nullptr,
+ GrowableArray* metadata_list = nullptr);
+
bool write_oop_map_set(CodeBlob& cb);
+ bool write_nmethod_reloc_immediates(GrowableArray& oop_list, GrowableArray& metadata_list);
+
+ bool write_oop(jobject& jo);
+ bool write_oop(oop obj);
+ bool write_metadata(Metadata* m);
+ bool write_oops(nmethod* nm);
+ bool write_metadata(nmethod* nm);
bool write_stub_data(CodeBlob& blob, AOTStubData *stub_data);
+
#ifndef PRODUCT
- bool write_asm_remarks(CodeBlob& cb);
- bool write_dbg_strings(CodeBlob& cb);
+ bool write_asm_remarks(AsmRemarks& asm_remarks, GrowableArray& remarks, GrowableArray& remarks_len, uint* size);
+ bool write_dbg_strings(DbgStrings& dbg_strings, GrowableArray& strings, GrowableArray&strings_len, uint* size);
+ bool write_asm_rem_and_dbg_str(AsmRemarks& asm_remarks, DbgStrings& dbg_strings);
#endif // PRODUCT
private:
@@ -565,37 +991,30 @@ class AOTCodeCache : public CHeapObj {
const char* name,
AOTStubData* stub_data) NOT_CDS_RETURN_(nullptr);
-public:
- // save and restore API for non-enumerable code blobs
- static bool store_code_blob(CodeBlob& blob,
- AOTCodeEntry::Kind entry_kind,
- uint id,
- const char* name) NOT_CDS_RETURN_(false);
+ AOTCodeEntry* write_nmethod(nmethod* nm, bool for_preload);
- static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind,
- uint id, const char* name) NOT_CDS_RETURN_(nullptr);
+public:
+ // save and restore API for adapters
+ static bool store_adapter(CodeBlob& blob, uint id, const char* name) NOT_CDS_RETURN_(false);
- // save and restore API for enumerable code blobs
+ static CodeBlob* load_adapter(uint id, const char* name) NOT_CDS_RETURN_(nullptr);
- // API for single-stub blobs
- static bool store_code_blob(CodeBlob& blob,
- AOTCodeEntry::Kind entry_kind,
+ // save and restore for single-stub blobs
+ static bool store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind,
BlobId id) NOT_CDS_RETURN_(false);
- static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind,
- BlobId id) NOT_CDS_RETURN_(nullptr);
+ static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, BlobId id) NOT_CDS_RETURN_(nullptr);
- // API for multi-stub blobs -- for use by class StubGenerator.
+ // save and restore for multi-stub blobs - for use by class StubGenerator
+ static bool store_multi_stub_blob(CodeBlob& blob, BlobId id, AOTStubData* stub_data,
+ CodeBuffer *code_buffer) NOT_CDS_RETURN_(false);
- static bool store_code_blob(CodeBlob& blob,
- AOTCodeEntry::Kind kind,
- BlobId id,
- AOTStubData* stub_data,
- CodeBuffer *code_buffer) NOT_CDS_RETURN_(false);
+ static CodeBlob* load_multi_stub_blob(BlobId id, AOTStubData* stub_data) NOT_CDS_RETURN_(nullptr);
- static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind,
- BlobId id,
- AOTStubData* stub_data) NOT_CDS_RETURN_(nullptr);
+ // save and restore API nmethods
+ static AOTCodeEntry* store_nmethod(nmethod* nm, AbstractCompiler* compiler, bool for_preload) NOT_CDS_RETURN_(nullptr);
+
+ static bool load_nmethod(ciEnv* env, ciMethod* target, int entry_bci, AbstractCompiler* compiler) NOT_CDS_RETURN_(false);
static void publish_external_addresses(GrowableArray& addresses) NOT_CDS_RETURN;
// publish all entries for a code blob in code cache address table
@@ -615,7 +1034,8 @@ class AOTCodeCache : public CHeapObj {
DEBUG_ONLY( static bool _passed_init2; )
static bool open_cache(bool is_dumping, bool is_using);
- bool verify_config() {
+
+ bool verify_config_on_use() {
if (for_use()) {
return _load_header->verify_config(this);
}
@@ -630,67 +1050,151 @@ class AOTCodeCache : public CHeapObj {
static void initialize() NOT_CDS_RETURN;
static void init2() NOT_CDS_RETURN;
static void init3() NOT_CDS_RETURN;
- static void dump() NOT_CDS_RETURN;
+ static bool dump() NOT_CDS_RETURN_(false);
+ static bool is_code_load_thread_on() NOT_CDS_RETURN_(false);
static bool is_on() CDS_ONLY({ return cache() != nullptr; }) NOT_CDS_RETURN_(false);
static bool is_on_for_use() CDS_ONLY({ return is_on() && _cache->for_use(); }) NOT_CDS_RETURN_(false);
static bool is_on_for_dump() CDS_ONLY({ return is_on() && _cache->for_dump(); }) NOT_CDS_RETURN_(false);
+ static bool is_dumping_code() 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_code() 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 bool verify_jvmti_state(ciEnv* env) NOT_CDS_RETURN_(true);
+
+ // It is used before AOTCodeCache is initialized.
+ static bool maybe_dumping_code() NOT_CDS_RETURN_(false);
+
+ static void invalidate(AOTCodeEntry* entry) NOT_CDS_RETURN;
+ static bool skip_aot_code(uint comp_level) NOT_CDS_RETURN_(true);
+ static AOTCodeEntry* find_code_entry(const methodHandle& method, uint comp_level) NOT_CDS_RETURN_(nullptr);
+ static void preload_code(JavaThread* thread) NOT_CDS_RETURN;
static const char* add_C_string(const char* str) NOT_CDS_RETURN_(str);
static void print_on(outputStream* st) NOT_CDS_RETURN;
+ static void print_statistics_on(outputStream* st) NOT_CDS_RETURN;
+ static void print_timers_on(outputStream* st) NOT_CDS_RETURN;
};
-// Concurent AOT code reader
+// Control block for loading a single AOTCodeEntry asset from the "ac" region
+// into the code cache. We use a few JIT-like worker threads to do this work,
+// so loading is done concurrently. Each AOTCodeEntry is processed as its own
+// sequential task within a worker thread, using the reader's position cursor.
+// Loading copies code from the "ac" region, and also fixes up pointers used by
+// the code, including reloc-info and oops and metadata arrays.
class AOTCodeReader {
private:
AOTCodeCache* _cache;
- const AOTCodeEntry* _entry;
- const char* _load_buffer; // Loaded cached code buffer
- uint _read_position; // Position in _load_buffer
+ AOTCodeEntry* _entry;
+ address _entry_buffer; // AOT code buffer for this entry
+ uint _read_position; // Position in _entry_buffer
+ uint _read_limit; // for bound checks
uint read_position() const { return _read_position; }
- void set_read_position(uint pos);
- uint align_read_int();
- const char* addr(uint offset) const { return _load_buffer + offset; }
- bool _lookup_failed; // Failed to lookup for info (skip only this code load)
- void set_lookup_failed() { _lookup_failed = true; }
- void clear_lookup_failed() { _lookup_failed = false; }
- bool lookup_failed() const { return _lookup_failed; }
+ // convenience method to convert offset in AOTCodeEntry data to its address
+ address addr(uint offset) const {
+ assert(offset <= _read_limit, "%u > %u ", offset, _read_limit);
+ return _entry_buffer + offset;
+ }
+ address position_addr() const { return addr(_read_position); }
+ bool get_set_read_position(address* pos, uint nbytes);
+ bool update_read_position(uint nbytes);
+ bool align_read_position(uint alignment);
+ bool read_int(int* val);
+ bool read_kind(DataKind* kind) {
+ return read_int(reinterpret_cast(kind));
+ }
+
+
+ uint _compile_id;
+ uint _comp_level;
+ uint compile_id() const { return _compile_id; }
+ uint comp_level() const { return _comp_level; }
// Values used by restore(code_blob).
// They should be set before calling it.
- const char* _name;
- address _reloc_data;
- int _reloc_count;
- ImmutableOopMapSet* _oop_maps;
AOTCodeEntry::Kind _entry_kind;
int _id;
AOTStubData* _stub_data;
- AOTCodeEntry* aot_code_entry() { return (AOTCodeEntry*)_entry; }
+ const char* _name;
+ int _reloc_count;
+ address _reloc_data;
+ ImmutableOopMapSet* _oop_maps;
+ address _immutable_data;
+
+ int _cached_stub_data_count;
+ uint* _cached_stub_data; // uint[] array
+
+ int _id_for_reloc_count;
+ uint* _id_for_reloc;
+
+#ifndef PRODUCT
+ struct AsmRemData {
+ uint offset; // remark's offset in code
+ uint str_offset; // string's offset in code's entry
+ uint str_len; // string's len for bound check
+ };
+
+ struct DbgStrData {
+ uint str_offset; // string's offset in code's entry
+ uint str_len; // string's len for bound check
+ };
+
+ int _asm_remarks_count;
+ AsmRemData* _asm_remarks_data;
+ const char** _asm_remarks_strings;
+
+ int _dbg_strings_count;
+ DbgStrData* _dbg_strings_data;
+ const char** _dbg_strings;
+#endif
+
+ GrowableArray* _oop_list;
+ GrowableArray* _metadata_list;
+ GrowableArray* _reloc_imm_oop_list;
+ GrowableArray* _reloc_imm_metadata_list;
+
+ const char* _failure; // Failed to lookup for info (skip only this code load)
+ void set_lookup_failed(const char* failure) { _failure = failure; }
+
+ Klass* read_klass(JavaThread* thread);
+ Method* read_method();
+
+ oop read_oop(JavaThread* thread);
+ Metadata* read_metadata(JavaThread* thread);
+
+ bool read_oop_metadata_list(JavaThread* thread, GrowableArray &oop_list, GrowableArray &metadata_list, OopRecorder* oop_recorder);
ImmutableOopMapSet* read_oop_map_set();
void read_stub_data(CodeBlob* code_blob, AOTStubData *stub_data);
- void fix_relocations(CodeBlob* code_blob, RelocIterator& iter);
+ void read_relocations(CodeBlob* code_blob, RelocIterator& iter,
+ GrowableArray* oop_list = nullptr,
+ GrowableArray* metadata_list = nullptr);
+
#ifndef PRODUCT
- void read_asm_remarks(AsmRemarks& asm_remarks);
- void read_dbg_strings(DbgStrings& dbg_strings);
+ void restore_asm_remarks(AsmRemarks& asm_remarks);
+ void restore_dbg_strings(DbgStrings& dbg_strings);
+ bool read_asm_rem_and_dbg_str();
#endif // PRODUCT
public:
- AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry);
+ AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry, CompileTask* task);
+
+ bool compile_nmethod(ciEnv* env, ciMethod* target, AbstractCompiler* compiler);
CodeBlob* compile_code_blob(const char* name, AOTCodeEntry::Kind entry_kind, int id, AOTStubData* stub_data = nullptr);
void restore(CodeBlob* code_blob);
+
+ bool lookup_failed() const { return _failure != nullptr; }
+ const char* lookup_failure() const { return _failure; }
};
// code cache internal runtime constants area used by AOT code
diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp
index 4d136b76906..5d323f5f015 100644
--- a/src/hotspot/share/code/codeBlob.cpp
+++ b/src/hotspot/share/code/codeBlob.cpp
@@ -194,6 +194,12 @@ CodeBlob::CodeBlob(const char* name, CodeBlobKind kind, int size, uint16_t heade
assert(_mutable_data == blob_end(), "sanity");
}
+#ifdef ASSERT
+CodeBlob::~CodeBlob() {
+ assert(_oop_maps == nullptr || AOTCodeCache::is_address_in_aot_cache((address)_oop_maps), "Not flushed");
+}
+#endif
+
void CodeBlob::purge() {
assert(_mutable_data != nullptr, "should never be null");
if (_mutable_data != blob_end()) {
@@ -202,7 +208,7 @@ void CodeBlob::purge() {
_mutable_data_size = 0;
_relocation_size = 0;
}
- if (_oop_maps != nullptr) {
+ if (_oop_maps != nullptr && !AOTCodeCache::is_address_in_aot_cache((address)_oop_maps)) {
delete _oop_maps;
_oop_maps = nullptr;
}
@@ -252,8 +258,8 @@ void CodeBlob::prepare_for_archiving_impl() {
_oop_maps = nullptr;
_mutable_data = nullptr;
#ifndef PRODUCT
- asm_remarks().clear();
- dbg_strings().clear();
+ asm_remarks().clear_ref();
+ dbg_strings().clear_ref();
#endif /* PRODUCT */
}
diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp
index e95836c8dd3..9e2fc0b2ade 100644
--- a/src/hotspot/share/code/codeBlob.hpp
+++ b/src/hotspot/share/code/codeBlob.hpp
@@ -176,9 +176,7 @@ class CodeBlob {
public:
- ~CodeBlob() {
- assert(_oop_maps == nullptr, "Not flushed");
- }
+ ~CodeBlob() NOT_DEBUG_RETURN;
// Returns the space needed for CodeBlob
static unsigned int allocation_size(CodeBuffer* cb, int header_size);
diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp
index 05785f694ff..0236ea9bc44 100644
--- a/src/hotspot/share/code/codeCache.cpp
+++ b/src/hotspot/share/code/codeCache.cpp
@@ -22,6 +22,7 @@
*
*/
+#include "code/aotCodeCache.hpp"
#include "code/codeBlob.hpp"
#include "code/codeCache.hpp"
#include "code/codeHeapState.hpp"
@@ -233,14 +234,31 @@ void CodeCache::initialize_heaps() {
assert(heap_available(CodeBlobType::MethodNonProfiled), "MethodNonProfiled heap is always available for segmented code heap");
uint64_t compiler_buffer_size_uint64 = 0;
- COMPILER1_PRESENT(compiler_buffer_size_uint64 += (uint64_t)CompilationPolicy::c1_count() * Compiler::code_buffer_size());
- COMPILER2_PRESENT(compiler_buffer_size_uint64 += (uint64_t)CompilationPolicy::c2_count() * C2Compiler::initial_code_buffer_size());
+ // Take into account AOT C1 threads in case C1 JIT compilation is disabled,
+ // the space is needed for, at least, one buffer to initialize C1 runtime.
+ int c1_count = MAX2(CompilationPolicy::c1_count(), (CompilationPolicy::ac1_count() > 0 ? 1 : 0));
+ int c2_count = CompilationPolicy::c2_count();
+ COMPILER1_PRESENT(compiler_buffer_size_uint64 += (uint64_t)c1_count * Compiler::code_buffer_size());
+ COMPILER2_PRESENT(compiler_buffer_size_uint64 += (uint64_t)c2_count * C2Compiler::initial_code_buffer_size());
if (compiler_buffer_size_uint64 > (uint64_t)CODE_CACHE_SIZE_LIMIT) {
err_msg msg("CICompilerCount is too large (%" PRIdPTR "): compiler buffer size exceeds the CodeCache size limit", CICompilerCount);
vm_exit_during_initialization(msg);
}
size_t compiler_buffer_size = integer_cast_permit_tautology(compiler_buffer_size_uint64);
+ // During AOT assembly phase more compiler threads are used
+ // and C2 temp buffer is bigger.
+ // But due to rounding issue the total code cache size could be smaller
+ // than during production run. We can not use AOT code in such case
+ // because branch and call instructions will be incorrect.
+ //
+ // Increase code cache size to guarantee that total size
+ // will be bigger during assembly phase.
+ if (AOTCodeCache::maybe_dumping_code()) {
+ cache_size += align_up(compiler_buffer_size, min_size);
+ cache_size = MIN2(cache_size, CODE_CACHE_SIZE_LIMIT);
+ }
+
if (!non_nmethod.set) {
non_nmethod.size += compiler_buffer_size;
}
@@ -1348,11 +1366,12 @@ static void check_live_nmethods_dependencies(DepChange& changes) {
// Determine if dependency is already checked. table->put(...) returns
// 'true' if the dependency is added (i.e., was not in the hashtable).
if (table->put(*current_sig, 1)) {
- if (deps.check_dependency() != nullptr) {
+ Klass* witness = deps.check_dependency();
+ if (witness != nullptr) {
// Dependency checking failed. Print out information about the failed
// dependency and finally fail with an assert. We can fail here, since
// dependency checking is never done in a product build.
- tty->print_cr("Failed dependency:");
+ deps.print_dependency(tty, witness, true);
changes.print();
nm->print();
nm->print_dependencies_on(tty);
@@ -1378,6 +1397,13 @@ void CodeCache::mark_for_deoptimization(DeoptimizationScope* deopt_scope, KlassD
NoSafepointVerifier nsv;
for (DepChange::ContextStream str(changes, nsv); str.next(); ) {
InstanceKlass* d = str.klass();
+ {
+ LogStreamHandle(Trace, dependencies) log;
+ if (log.is_enabled()) {
+ log.print("Processing context ");
+ d->name()->print_value_on(&log);
+ }
+ }
d->mark_dependent_nmethods(deopt_scope, changes);
}
@@ -1655,20 +1681,16 @@ void CodeCache::print_internals() {
int i = 0;
FOR_ALL_ALLOCABLE_HEAPS(heap) {
- if ((_nmethod_heaps->length() >= 1) && Verbose) {
- tty->print_cr("-- %s --", (*heap)->name());
- }
+ int heap_total = 0;
+ tty->print_cr("-- %s --", (*heap)->name());
FOR_ALL_BLOBS(cb, *heap) {
total++;
+ heap_total++;
if (cb->is_nmethod()) {
nmethod* nm = (nmethod*)cb;
- if (Verbose && nm->method() != nullptr) {
- ResourceMark rm;
- char *method_name = nm->method()->name_and_sig_as_C_string();
- tty->print("%s", method_name);
- if(nm->is_not_entrant()) { tty->print_cr(" not-entrant"); }
- }
+ tty->print("%4d: ", heap_total);
+ CompileTask::print(tty, nm, (nm->is_not_entrant() ? "non-entrant" : ""), true, true);
nmethodCount++;
diff --git a/src/hotspot/share/code/debugInfoRec.cpp b/src/hotspot/share/code/debugInfoRec.cpp
index 9eda46ad682..c4e71ad640b 100644
--- a/src/hotspot/share/code/debugInfoRec.cpp
+++ b/src/hotspot/share/code/debugInfoRec.cpp
@@ -138,6 +138,8 @@ DebugInformationRecorder::DebugInformationRecorder(OopRecorder* oop_recorder)
_all_chunks = new GrowableArray(300);
_next_chunk = _next_chunk_limit = nullptr;
+ _has_vectors = false;
+
add_new_pc_offset(PcDesc::lower_offset_limit); // sentinel record
DEBUG_ONLY(_recording_state = rs_null);
diff --git a/src/hotspot/share/code/debugInfoRec.hpp b/src/hotspot/share/code/debugInfoRec.hpp
index b38f663f34f..03482581dfa 100644
--- a/src/hotspot/share/code/debugInfoRec.hpp
+++ b/src/hotspot/share/code/debugInfoRec.hpp
@@ -154,6 +154,8 @@ class DebugInformationRecorder: public ResourceObj {
DebugInfoWriteStream* stream() const { return _stream; }
+ bool has_vectors() const { return _has_vectors; }
+ void set_has_vectors() { _has_vectors = true; }
private:
friend class ScopeDesc;
@@ -203,6 +205,8 @@ class DebugInformationRecorder: public ResourceObj {
int serialize_scope_values(GrowableArray* values);
int find_sharable_decode_offset(int stream_offset);
+ bool _has_vectors;
+
#ifndef PRODUCT
bool recorders_frozen();
void mark_recorders_frozen();
diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp
index 627b72dfcd3..d62e8d19c28 100644
--- a/src/hotspot/share/code/dependencies.cpp
+++ b/src/hotspot/share/code/dependencies.cpp
@@ -604,9 +604,13 @@ void Dependencies::print_dependency(DepType dept, GrowableArray* ar
}
if (witness != nullptr) {
bool put_star = !Dependencies::is_concrete_klass(witness);
- st->print_cr(" witness = %s%s",
- (put_star? "*": ""),
- witness->external_name());
+ st->print(" witness = %s%s",
+ (put_star ? "*": ""),
+ witness->external_name());
+ if (witness->is_instance_klass()) {
+ st->print(" (%s)", InstanceKlass::cast(witness)->init_state_name());
+ }
+ st->cr();
}
}
diff --git a/src/hotspot/share/code/dependencies.hpp b/src/hotspot/share/code/dependencies.hpp
index ee756e1fbf2..849984da785 100644
--- a/src/hotspot/share/code/dependencies.hpp
+++ b/src/hotspot/share/code/dependencies.hpp
@@ -360,6 +360,12 @@ class Dependencies: public ResourceObj {
return _size_in_bytes;
}
+ void set_content(address content_bytes, int size_in_bytes) {
+ assert(_content_bytes == nullptr, "not initialized expected");
+ _content_bytes = content_bytes;
+ _size_in_bytes = size_in_bytes;
+ }
+
OopRecorder* oop_recorder() { return _oop_recorder; }
CompileLog* log() { return _log; }
diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp
index 635853b549a..dcced8bf929 100644
--- a/src/hotspot/share/code/nmethod.cpp
+++ b/src/hotspot/share/code/nmethod.cpp
@@ -24,6 +24,7 @@
#include "asm/assembler.inline.hpp"
#include "cds/cdsConfig.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/codeCache.hpp"
#include "code/compiledIC.hpp"
#include "code/dependencies.hpp"
@@ -57,6 +58,7 @@
#include "oops/method.inline.hpp"
#include "oops/methodData.hpp"
#include "oops/oop.inline.hpp"
+#include "oops/trainingData.hpp"
#include "oops/weakHandle.inline.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiThreadState.hpp"
@@ -984,14 +986,17 @@ int nmethod::total_size() const {
}
const char* nmethod::compile_kind() const {
- if (is_osr_method()) return "osr";
+ if (is_osr_method()) return "osr";
+ if (aot_preloaded()) return "AP";
+ if (is_aot()) return "A";
+
if (method() != nullptr && is_native_method()) {
if (method()->is_continuation_native_intrinsic()) {
return "cnt";
}
return "c2n";
}
- return nullptr;
+ return "jit";
}
const char* nmethod::compiler_name() const {
@@ -1088,6 +1093,31 @@ nmethod* nmethod::new_native_nmethod(const methodHandle& method,
return nm;
}
+void nmethod::record_nmethod_dependency() {
+ // To make dependency checking during class loading fast, record
+ // the nmethod dependencies in the classes it is dependent on.
+ // This allows the dependency checking code to simply walk the
+ // class hierarchy above the loaded class, checking only nmethods
+ // which are dependent on those classes. The slow way is to
+ // check every nmethod for dependencies which makes it linear in
+ // the number of methods compiled. For applications with a lot
+ // classes the slow way is too slow.
+ for (Dependencies::DepStream deps(this); deps.next(); ) {
+ if (deps.type() == Dependencies::call_site_target_value) {
+ // CallSite dependencies are managed on per-CallSite instance basis.
+ oop call_site = deps.argument_oop(0);
+ MethodHandles::add_dependent_nmethod(call_site, this);
+ } else {
+ InstanceKlass* ik = deps.context_type();
+ if (ik == nullptr) {
+ continue; // ignore things like evol_method
+ }
+ // record this nmethod as dependent on this klass
+ ik->add_dependent_nmethod(this);
+ }
+ }
+}
+
nmethod* nmethod::new_nmethod(const methodHandle& method,
int compile_id,
int entry_bci,
@@ -1139,29 +1169,59 @@ nmethod* nmethod::new_nmethod(const methodHandle& method,
handler_table, nul_chk_table, compiler, comp_level, flags);
if (nm != nullptr) {
- // To make dependency checking during class loading fast, record
- // the nmethod dependencies in the classes it is dependent on.
- // This allows the dependency checking code to simply walk the
- // class hierarchy above the loaded class, checking only nmethods
- // which are dependent on those classes. The slow way is to
- // check every nmethod for dependencies which makes it linear in
- // the number of methods compiled. For applications with a lot
- // classes the slow way is too slow.
- for (Dependencies::DepStream deps(nm); deps.next(); ) {
- if (deps.type() == Dependencies::call_site_target_value) {
- // CallSite dependencies are managed on per-CallSite instance basis.
- oop call_site = deps.argument_oop(0);
- MethodHandles::add_dependent_nmethod(call_site, nm);
- } else {
- InstanceKlass* ik = deps.context_type();
- if (ik == nullptr) {
- continue; // ignore things like evol_method
- }
- // record this nmethod as dependent on this klass
- ik->add_dependent_nmethod(nm);
- }
- }
- NOT_PRODUCT(if (nm != nullptr) note_java_nmethod(nm));
+ nm->record_nmethod_dependency();
+ NOT_PRODUCT(note_java_nmethod(nm));
+ }
+ }
+ // Do verification and logging outside CodeCache_lock.
+ if (nm != nullptr) {
+ // Safepoints in nmethod::verify aren't allowed because nm hasn't been installed yet.
+ DEBUG_ONLY(nm->verify();)
+ nm->log_new_nmethod();
+ }
+ return nm;
+}
+
+#if INCLUDE_CDS
+nmethod* nmethod::restore(address code_cache_buffer,
+ const methodHandle& method,
+ AOTCodeReader* aot_code_reader)
+{
+ // This will call AOTCodeReader::restore() which restores some nmethods data
+ CodeBlob::restore(code_cache_buffer, aot_code_reader);
+ nmethod* nm = (nmethod*)code_cache_buffer;
+ nm->set_method(method());
+ nm->_gc_epoch = CodeCache::gc_epoch();
+
+ // Create cache after PcDesc data is copied - it will be used to initialize cache
+ nm->_pc_desc_container = new PcDescContainer(nm->scopes_pcs_begin());
+
+ nm->post_init();
+ return nm;
+}
+
+nmethod* nmethod::new_nmethod(nmethod* archived_nm,
+ const methodHandle& method,
+ AOTCodeReader* aot_code_reader)
+{
+ nmethod* nm = nullptr;
+ int nmethod_size = archived_nm->size();
+ int comp_level = archived_nm->comp_level();
+ // create nmethod
+ {
+ MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+ address code_cache_buffer = (address)CodeCache::allocate(nmethod_size, CodeCache::get_code_blob_type(comp_level));
+ if (code_cache_buffer != nullptr) {
+ nm = archived_nm->restore(code_cache_buffer, method, aot_code_reader);
+ AOTCodeEntry* entry = nm->aot_code_entry();
+ assert(entry != nullptr, "AOTCodeReader::restore() should set it");
+ // Mark the AOT code entry as loaded before releasing CodeCache_lock.
+ // This guarantees that CodeCache iterators can not observe the restored
+ // AOT nmethod before its AOT entry is loaded, preserving the normal
+ // installation vs deoptimization protocol.
+ entry->set_loaded();
+ nm->record_nmethod_dependency();
+ NOT_PRODUCT(note_java_nmethod(nm));
}
}
// Do verification and logging outside CodeCache_lock.
@@ -1172,6 +1232,7 @@ nmethod* nmethod::new_nmethod(const methodHandle& method,
}
return nm;
}
+#endif // INCLUDE_CDS
// Fill in default values for various fields
void nmethod::init_defaults(CodeBuffer *code_buffer, CodeOffsets* offsets) {
@@ -1182,6 +1243,7 @@ void nmethod::init_defaults(CodeBuffer *code_buffer, CodeOffsets* offsets) {
_gc_data = nullptr;
_oops_do_mark_link = nullptr;
_compiled_ic_data = nullptr;
+ _aot_code_entry = nullptr;
_is_unloading_state = 0;
_state = not_installed;
@@ -1189,6 +1251,7 @@ void nmethod::init_defaults(CodeBuffer *code_buffer, CodeOffsets* offsets) {
_has_flushed_dependencies = false;
_is_unlinked = false;
_load_reported = false; // jvmti state
+ _aot_preloaded = false;
_deoptimization_status = not_marked;
@@ -1217,6 +1280,7 @@ void nmethod::post_init() {
// Flush generated code
ICache::invalidate_range(code_begin(), code_size());
+ // This will disarm entry barrier.
Universe::heap()->register_nmethod(this);
#ifdef COMPILER2
@@ -1327,7 +1391,7 @@ nmethod::nmethod(
#if defined(SUPPORT_DATA_STRUCTS)
if (AbstractDisassembler::show_structs()) {
if (PrintRelocations) {
- print_relocations();
+ print_relocations_on(tty);
tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
}
}
@@ -1391,6 +1455,7 @@ nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm.
_gc_data = nullptr;
_oops_do_mark_link = nullptr;
_compiled_ic_data = nullptr;
+ _aot_code_entry = nm._aot_code_entry;
// Relocate the OSR entry point from nm to the new nmethod.
if (nm._osr_entry_point == nullptr) {
@@ -1440,6 +1505,7 @@ nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm.
_has_flushed_dependencies = nm._has_flushed_dependencies;
_is_unlinked = nm._is_unlinked;
_load_reported = nm._load_reported;
+ _aot_preloaded = nm._aot_preloaded;
_deoptimization_status = nm._deoptimization_status;
@@ -1590,6 +1656,10 @@ bool nmethod::is_relocatable() {
return false;
}
+ if (is_aot()) {
+ return false;
+ }
+
if (is_marked_for_deoptimization()) {
return false;
}
@@ -1756,7 +1826,7 @@ nmethod::nmethod(
void nmethod::log_identity(xmlStream* log) const {
log->print(" compile_id='%d'", compile_id());
const char* nm_kind = compile_kind();
- if (nm_kind != nullptr) log->print(" compile_kind='%s'", nm_kind);
+ log->print(" compile_kind='%s'", nm_kind);
log->print(" compiler='%s'", compiler_name());
if (TieredCompilation) {
log->print(" level='%d'", comp_level());
@@ -1900,7 +1970,7 @@ void nmethod::print_nmethod(bool printmethod) {
tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
}
if (printmethod || PrintRelocations || CompilerOracle::has_option(mh, CompileCommandEnum::PrintRelocations)) {
- print_relocations();
+ print_relocations_on(tty);
tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
}
if (printmethod || PrintDependencies || CompilerOracle::has_option(mh, CompileCommandEnum::PrintDependencies)) {
@@ -1940,6 +2010,14 @@ inline void nmethod::initialize_immediate_oop(oop* dest, jobject handle) {
}
}
+void nmethod::copy_values(GrowableArray* array) {
+ int length = array->length();
+ assert((address)(oops_begin() + length) <= (address)oops_end(), "oops big enough");
+ oop* dest = oops_begin();
+ for (int index = 0 ; index < length; index++) {
+ dest[index] = array->at(index)();
+ }
+}
// Have to have the same name because it's called by a template
void nmethod::copy_values(GrowableArray* array) {
@@ -2005,6 +2083,26 @@ void nmethod::fix_oop_relocations(ICacheInvalidationContext* icic) {
}
}
+void nmethod::create_reloc_immediates_list(JavaThread* thread, GrowableArray& oop_list, GrowableArray& metadata_list) {
+ RelocIterator iter(this);
+ while (iter.next()) {
+ if (iter.type() == relocInfo::oop_type) {
+ oop_Relocation* reloc = iter.oop_reloc();
+ if (reloc->oop_is_immediate()) {
+ oop dest = reloc->oop_value();
+ Handle h(thread, dest);
+ oop_list.append_if_missing(h);
+ }
+ } else if (iter.type() == relocInfo::metadata_type) {
+ metadata_Relocation* reloc = iter.metadata_reloc();
+ if (reloc->metadata_is_immediate()) {
+ Metadata* m = reloc->metadata_value();
+ metadata_list.append_if_missing(m);
+ }
+ }
+ }
+}
+
static void install_post_call_nop_displacement(nmethod* nm, address pc) {
NativePostCallNop* nop = nativePostCallNop_at((address) pc);
intptr_t cbaddr = (intptr_t) nm;
@@ -2223,8 +2321,22 @@ void nmethod::unlink_from_method() {
}
}
-// Invalidate code
-bool nmethod::make_not_entrant(InvalidationReason invalidation_reason) {
+// Invalidate code, making it impossible for future calls to the nmethod to
+// proceed past its entry point. Future calls will instead be finished by a
+// different code path, lazily updating call sites that link to this this
+// nmethod.
+//
+// This function does nothing about existing activations of the nmethod. The
+// nmethod code is sometimes out of date, so it cannot execute further; in
+// such cases the user of this function must also ensure that existing
+// activations are deoptimized. In other cases, the nmethod code works
+// well enough to keep running the existing activations to completion.
+//
+// The invalidation reason is used as a label in logging the nmethod
+// transition. It also affects invalidation policy for any associated
+// AOTCodeCache entry.
+// TODO: move keep_aot_entry to caller or derive from invalidation_reason.
+bool nmethod::make_not_entrant(InvalidationReason invalidation_reason, bool keep_aot_entry) {
// This can be called while the system is already at a safepoint which is ok
NoSafepointVerifier nsv;
@@ -2288,6 +2400,13 @@ bool nmethod::make_not_entrant(InvalidationReason invalidation_reason) {
// Remove nmethod from method.
unlink_from_method();
+ if (!keep_aot_entry) {
+ // Keep AOT code if it was simply replaced
+ // otherwise make it not entrant too.
+ AOTCodeCache::invalidate(_aot_code_entry);
+ }
+
+ CompileBroker::log_not_entrant(this);
} // leave critical region under NMethodState_lock
#ifdef ASSERT
@@ -2301,7 +2420,19 @@ bool nmethod::make_not_entrant(InvalidationReason invalidation_reason) {
return true;
}
-// For concurrent GCs, there must be a handshake between unlink and flush
+// Remove an nmethod from service when a GC has determined its class is unreachable.
+// For concurrent GCs, there must be a handshake between unlink and flush.
+//
+// This function is roughly parallel to nmethod::make_not_entrant. Both remove
+// the nmethod from the in_use state, this one because the nmethod is unreachable,
+// and the other because its code is may be reachable, but should not be executed.
+// AOT interaction: While make_not_entrant marks associated AOT code assets unloaded,
+// this function does not touch them (see AOTCodeEntry).
+// After JDK-8380476 TODO: Maybe unload the AOT method and record a reason like UNLOADING,
+// so that the activity can be traced. AOT methods could get GC-ed if we support
+// custom class loaders. Perhaps two versions of a custom class loader could use
+// the same asset twice? In any case we need a comment like the above showing the
+// missing logic, even if we don't do anything yet. Log messages would be nice too.
void nmethod::unlink() {
if (is_unlinked()) {
// Already unlinked.
@@ -2334,7 +2465,7 @@ void nmethod::purge(bool unregister_nmethod) {
MutexLocker ml(CodeCache_lock, Mutex::_no_safepoint_check_flag);
// completely deallocate this method
- Events::log_nmethod_flush(Thread::current(), "flushing %s nmethod " INTPTR_FORMAT, is_osr_method() ? "osr" : "", p2i(this));
+ Events::log_nmethod_flush(Thread::current(), "flushing %s nmethod " INTPTR_FORMAT, compile_kind(), p2i(this));
LogTarget(Debug, codecache) lt;
if (lt.is_enabled()) {
@@ -2343,9 +2474,9 @@ void nmethod::purge(bool unregister_nmethod) {
const char* method_name = method()->name()->as_C_string();
const size_t codecache_capacity = CodeCache::capacity()/1024;
const size_t codecache_free_space = CodeCache::unallocated_capacity(CodeCache::get_code_blob_type(this))/1024;
- ls.print("Flushing nmethod %6d/" INTPTR_FORMAT ", level=%d, osr=%d, cold=%d, epoch=" UINT64_FORMAT ", cold_count=" UINT64_FORMAT ". "
+ ls.print("Flushing %s nmethod %6d/" INTPTR_FORMAT ", level=%d, cold=%d, epoch=" UINT64_FORMAT ", cold_count=" UINT64_FORMAT ". "
"Cache capacity: %zuKb, free space: %zuKb. method %s (%s)",
- _compile_id, p2i(this), _comp_level, is_osr_method(), is_cold(), _gc_epoch, CodeCache::cold_gc_count(),
+ compile_kind(), _compile_id, p2i(this), _comp_level, is_cold(), _gc_epoch, CodeCache::cold_gc_count(),
codecache_capacity, codecache_free_space, method_name, compiler_name());
}
@@ -2361,9 +2492,12 @@ void nmethod::purge(bool unregister_nmethod) {
if (_pc_desc_container != nullptr) {
delete _pc_desc_container;
}
- delete[] _compiled_ic_data;
+ if (_compiled_ic_data != nullptr) {
+ delete[] _compiled_ic_data;
+ }
- if (_immutable_data != blob_end()) {
+ // Don't remove _immutable_data reference from AOT code
+ if (_immutable_data != blob_end() && !AOTCodeCache::is_address_in_aot_cache((address)_immutable_data)) {
// Free memory if this was the last nmethod referencing immutable data
if (dec_immutable_data_ref_count() == 0) {
os::free(_immutable_data);
@@ -2434,6 +2568,21 @@ void nmethod::post_compiled_method(CompileTask* task) {
task->set_nm_insts_size(insts_size());
task->set_nm_total_size(total_size());
+ CompileTrainingData* ctd = task->training_data();
+ if (ctd != nullptr) {
+ // Record inline code size during training to help inlining during production run
+ assert(TrainingData::need_data(), "should be called only during training"); // training run
+ int inline_size = inline_instructions_size();
+ if (inline_size < 0) inline_size = 0;
+ ctd->set_inline_instructions_size(inline_size);
+ }
+
+ // task->is_aot_load() is true only for loaded AOT code.
+ // nmethod::_aot_code_entry is set for loaded and stored AOT code
+ // to invalidate the entry when nmethod is deoptimized.
+ // VerifyAOTCode is option to verify AOT code without installing.
+ guarantee((_aot_code_entry != nullptr) || !task->is_aot_load() || VerifyAOTCode, "sanity");
+
// JVMTI -- compiled method notification (must be done outside lock)
post_compiled_method_load_event();
@@ -3192,9 +3341,13 @@ void nmethod::verify() {
fatal("find_nmethod did not find this nmethod (" INTPTR_FORMAT ")", p2i(this));
}
- for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) {
- if (! p->verify(this)) {
- tty->print_cr("\t\tin nmethod at " INTPTR_FORMAT " (pcs)", p2i(this));
+ // If the Scopes data is in the AOT code cache, then we should avoid verification during shutdown.
+ // Note that the AOT cache can be alive even during shutdown (it is never closed).
+ if (!is_aot() || AOTCodeCache::is_on()) {
+ for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) {
+ if (! p->verify(this)) {
+ tty->print_cr("\t\tin nmethod at " INTPTR_FORMAT " (pcs)", p2i(this));
+ }
}
}
@@ -3205,7 +3358,9 @@ void nmethod::verify() {
assert(_oops_do_mark_link == nullptr, "_oops_do_mark_link for %s should be nullptr but is " PTR_FORMAT,
nm->method()->external_name(), p2i(_oops_do_mark_link));
- verify_scopes();
+ if (!is_aot() || AOTCodeCache::is_on()) {
+ verify_scopes();
+ }
CompiledICLocker nm_verify(this);
VerifyMetadataClosure vmc;
@@ -3360,6 +3515,9 @@ void nmethod::print_on_impl(outputStream* st) const {
p2i(scopes_data_begin()),
p2i(scopes_data_end()),
scopes_data_size());
+ if (AOTCodeCache::is_on() && _aot_code_entry != nullptr) {
+ _aot_code_entry->print(st);
+ }
}
void nmethod::print_code() {
@@ -3459,11 +3617,11 @@ void nmethod::print_scopes_on(outputStream* st) {
#endif
#ifndef PRODUCT // RelocIterator does support printing only then.
-void nmethod::print_relocations() {
+void nmethod::print_relocations_on(outputStream* st) {
ResourceMark m; // in case methods get printed via the debugger
- tty->print_cr("relocations:");
+ st->print_cr("relocations:");
RelocIterator iter(this);
- iter.print_on(tty);
+ iter.print_on(st);
}
#endif
@@ -4336,3 +4494,22 @@ void nmethod::print_statistics() {
}
#endif // !PRODUCT
+
+void nmethod::prepare_for_archiving_impl() {
+ CodeBlob::prepare_for_archiving_impl();
+ _deoptimization_generation = 0;
+ _gc_epoch = 0;
+ _osr_link = nullptr;
+ _method = nullptr;
+ _immutable_data = nullptr;
+ _pc_desc_container = nullptr;
+ _exception_cache = nullptr;
+ _gc_data = nullptr;
+ _oops_do_mark_link = nullptr;
+ _compiled_ic_data = nullptr;
+ _osr_entry_point = nullptr;
+ _compile_id = -1;
+ _deoptimization_status = not_marked;
+ _is_unloading_state = 0;
+ _state = not_installed;
+}
diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp
index 5e4a18d1245..8a81ed0a1c4 100644
--- a/src/hotspot/share/code/nmethod.hpp
+++ b/src/hotspot/share/code/nmethod.hpp
@@ -48,6 +48,8 @@ class JvmtiThreadState;
class MetadataClosure;
class NativeCallWrapper;
class OopIterateClosure;
+class AOTCodeReader;
+class AOTCodeEntry;
class ScopeDesc;
class xmlStream;
@@ -205,6 +207,8 @@ class nmethod : public CodeBlob {
CompiledICData* _compiled_ic_data;
+ AOTCodeEntry* _aot_code_entry;
+
// offsets for entry points
address _osr_entry_point; // entry point for on stack replacement
uint16_t _entry_offset; // entry point with class check
@@ -259,19 +263,26 @@ class nmethod : public CodeBlob {
enum : uint8_t {
UNSAFE_ACCESS = 1 << 0,
- WIDE_VECTORS = 1 << 1,
- MONITORS = 1 << 2,
+ WIDE_VECTORS = 1 << 1,
+ MONITORS = 1 << 2,
SCOPED_ACCESS = 1 << 3,
NEEDS_STACK_REPAIR = 1 << 4,
+ CLINIT_BARRIERS = 1 << 5
};
Flags() : _bits(0) {}
- Flags(bool has_unsafe_access, bool has_wide_vectors, bool has_monitors, bool has_scoped_access, bool needs_stack_repair) :
- _bits((has_unsafe_access ? UNSAFE_ACCESS : 0) |
- (has_wide_vectors ? WIDE_VECTORS : 0) |
- (has_monitors ? MONITORS : 0) |
- (has_scoped_access ? SCOPED_ACCESS : 0) |
- (needs_stack_repair ? NEEDS_STACK_REPAIR : 0))
+ Flags(bool has_unsafe_access,
+ bool has_wide_vectors,
+ bool has_monitors,
+ bool has_scoped_access,
+ bool needs_stack_repair,
+ bool has_clinit_barriers) :
+ _bits((has_unsafe_access ? UNSAFE_ACCESS : 0) |
+ (has_wide_vectors ? WIDE_VECTORS : 0) |
+ (has_monitors ? MONITORS : 0) |
+ (has_scoped_access ? SCOPED_ACCESS : 0) |
+ (needs_stack_repair ? NEEDS_STACK_REPAIR : 0) |
+ (has_clinit_barriers ? CLINIT_BARRIERS : 0))
{}
// May fault due to unsafe access
@@ -288,6 +299,9 @@ class nmethod : public CodeBlob {
// Stack has been extended and needs repair. See comment in MacroAssembler::remove_frame
bool needs_stack_repair() const { return (_bits & NEEDS_STACK_REPAIR) != 0; }
+
+ // AOT preload code has clinit barriers
+ bool has_clinit_barriers() const { return (_bits & CLINIT_BARRIERS) != 0; }
};
private:
@@ -303,6 +317,9 @@ class nmethod : public CodeBlob {
// Used by JVMTI to track if an event has been posted for this nmethod
bool _load_reported;
+ // This AOT code was preloaded before method is called
+ bool _aot_preloaded;
+
enum DeoptimizationStatus : u1 {
not_marked,
deoptimize,
@@ -494,6 +511,18 @@ class nmethod : public CodeBlob {
// transitions).
void oops_do_set_strong_done(nmethod* old_head);
+ void record_nmethod_dependency();
+#if INCLUDE_CDS
+ nmethod* restore(address code_cache_buffer,
+ const methodHandle& method,
+ AOTCodeReader* aot_code_reader);
+
+public:
+ // create nmethod using archived nmethod from AOT code cache
+ static nmethod* new_nmethod(nmethod* archived_nm,
+ const methodHandle& method,
+ AOTCodeReader* aot_code_reader);
+#endif
public:
enum class InvalidationReason : s1 {
NOT_INVALIDATED = -1,
@@ -624,10 +653,12 @@ class nmethod : public CodeBlob {
int exception_handler = -1);
Method* method () const { return _method; }
+ uint16_t entry_bci () const { return _entry_bci; }
bool is_native_method() const { return _method != nullptr && _method->is_native(); }
bool is_java_method () const { return _method != nullptr && !_method->is_native(); }
bool is_osr_method () const { return _entry_bci != InvocationEntryBci; }
+ int orig_pc_offset() const { return _orig_pc_offset; }
bool is_relocatable();
// Compiler task identification. Note that all OSR methods
@@ -635,6 +666,8 @@ class nmethod : public CodeBlob {
// and native method wrappers are also numbered independently if
// CICountNative is true.
int compile_id() const { return _compile_id; }
+ void set_compile_id(int compile_id) { _compile_id = compile_id; }
+ int comp_level() const { return _comp_level; }
const char* compile_kind() const;
inline bool is_compiled_by_c1 () const { return _compiler_type == compiler_c1; }
@@ -675,6 +708,8 @@ class nmethod : public CodeBlob {
address scopes_data_end () const { return _immutable_data + _immutable_data_ref_count_offset ; }
address immutable_data_ref_count_begin () const { return _immutable_data + _immutable_data_ref_count_offset ; }
+ void set_immutable_data(address data) { _immutable_data = data; }
+
// Sizes
int immutable_data_size() const { return _immutable_data_size; }
int consts_size () const { return int( consts_end () - consts_begin ()); }
@@ -715,6 +750,8 @@ class nmethod : public CodeBlob {
address verified_value_entry_point() const { return code_begin() + _verified_value_entry_offset; } // value type entry point (unpack all value type args) without class check
address verified_value_ro_entry_point() const { return code_begin() + _verified_value_ro_entry_offset; } // value type entry point (only unpack receiver) without class check
+ int inline_instructions_size() const { return insts_end() - verified_entry_point() - skipped_instructions_size(); }
+
enum : signed char { not_installed = -1, // in construction, only the owner doing the construction is
// allowed to advance state
in_use = 0, // executable nmethod
@@ -736,12 +773,13 @@ class nmethod : public CodeBlob {
bool make_in_use() {
return try_transition(in_use);
}
+
// Make the nmethod non entrant. The nmethod will continue to be
// 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(InvalidationReason invalidation_reason);
- bool make_not_used() { return make_not_entrant(InvalidationReason::NOT_USED); }
+ bool make_not_entrant(InvalidationReason invalidation_reason, bool keep_aot_entry = false);
+ bool make_not_used() { return make_not_entrant(InvalidationReason::NOT_USED, true /* keep AOT entry */); }
bool is_marked_for_deoptimization() const { return deoptimization_status() != not_marked; }
bool has_been_deoptimized() const { return deoptimization_status() == deoptimize_done; }
@@ -773,6 +811,10 @@ class nmethod : public CodeBlob {
bool has_scoped_access() const { return _flags.has_scoped_access(); }
bool has_wide_vectors() const { return _flags.has_wide_vectors(); }
bool needs_stack_repair() const { return _flags.needs_stack_repair(); }
+ bool has_clinit_barriers() const { return _flags.has_clinit_barriers(); }
+
+ bool aot_preloaded() const { return _aot_preloaded; }
+ void set_aot_preloaded(bool z) { _aot_preloaded = z; }
bool has_flushed_dependencies() const { return _has_flushed_dependencies; }
void set_has_flushed_dependencies(bool z) {
@@ -786,7 +828,9 @@ class nmethod : public CodeBlob {
_is_unlinked = true;
}
- int comp_level() const { return _comp_level; }
+ bool is_aot() const { return _aot_code_entry != nullptr; }
+ void set_aot_code_entry(AOTCodeEntry* entry) { _aot_code_entry = entry; }
+ AOTCodeEntry* aot_code_entry() const { return _aot_code_entry; }
// Support for oops in scopes and relocs:
// Note: index 0 is reserved for null.
@@ -807,6 +851,7 @@ class nmethod : public CodeBlob {
return &metadata_begin()[index - 1];
}
+ void copy_values(GrowableArray* array);
void copy_values(GrowableArray* oops);
void copy_values(GrowableArray* metadata);
void copy_values(GrowableArray* metadata) {} // Nothing to do
@@ -823,6 +868,8 @@ class nmethod : public CodeBlob {
void fix_oop_relocations(ICacheInvalidationContext* icic);
void fix_oop_relocations();
+ void create_reloc_immediates_list(JavaThread* thread, GrowableArray& oop_list, GrowableArray& metadata_list);
+
bool is_at_poll_return(address pc);
bool is_at_poll_or_poll_return(address pc);
@@ -989,8 +1036,6 @@ class nmethod : public CodeBlob {
void copy_scopes_pcs(PcDesc* pcs, int count);
void copy_scopes_data(address buffer, int size);
- int orig_pc_offset() { return _orig_pc_offset; }
-
// Post successful compilation
void post_compiled_method(CompileTask* task);
@@ -1020,7 +1065,7 @@ class nmethod : public CodeBlob {
#if defined(SUPPORT_DATA_STRUCTS)
// print output in opt build for disassembler library
- void print_relocations() PRODUCT_RETURN;
+ void print_relocations_on(outputStream* st) PRODUCT_RETURN;
void print_pcs_on(outputStream* st);
void print_scopes() { print_scopes_on(tty); }
void print_scopes_on(outputStream* st) PRODUCT_RETURN;
@@ -1090,6 +1135,8 @@ class nmethod : public CodeBlob {
void make_deoptimized();
void finalize_relocations();
+ void prepare_for_archiving_impl();
+
class Vptr : public CodeBlob::Vptr {
void print_on(const CodeBlob* instance, outputStream* st) const override {
ttyLocker ttyl;
@@ -1098,6 +1145,9 @@ class nmethod : public CodeBlob {
void print_value_on(const CodeBlob* instance, outputStream* st) const override {
instance->as_nmethod()->print_value_on_impl(st);
}
+ void prepare_for_archiving(CodeBlob* instance) const override {
+ ((nmethod*)instance)->prepare_for_archiving_impl();
+ };
};
static const Vptr _vpntr;
diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp
index 1d17f40b0c5..40717b6a741 100644
--- a/src/hotspot/share/code/relocInfo.cpp
+++ b/src/hotspot/share/code/relocInfo.cpp
@@ -179,12 +179,12 @@ RelocIterator::RelocIterator(CodeSection* cs, address begin, address limit) {
}
RelocIterator::RelocIterator(CodeBlob* cb) {
- initialize_misc();
if (cb->is_nmethod()) {
- _code = cb->as_nmethod();
- } else {
- _code = nullptr;
+ initialize(cb->as_nmethod(), nullptr, nullptr);
+ return;
}
+ initialize_misc();
+ _code = nullptr;
_current = cb->relocation_begin() - 1;
_end = cb->relocation_end();
_addr = cb->content_begin();
@@ -819,11 +819,11 @@ void internal_word_Relocation::fix_relocation_after_move(const CodeBuffer* src,
set_value(target);
}
-void internal_word_Relocation::fix_relocation_after_aot_load(address orig_base_addr, address current_base_addr) {
+void internal_word_Relocation::fix_relocation_after_aot_load(address current_base_addr, int delta) {
address target = _target;
if (target == nullptr) {
target = this->target();
- target = current_base_addr + (target - orig_base_addr);
+ target = current_base_addr + delta;
}
set_value(target);
}
@@ -889,8 +889,8 @@ void RelocIterator::print_current_on(outputStream* st) {
raw_oop = *oop_addr;
oop_value = r->oop_value();
}
- st->print(" | [oop_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT "]",
- p2i(oop_addr), p2i(raw_oop));
+ st->print(" | [oop_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT " index=%d]",
+ p2i(oop_addr), p2i(raw_oop), r->oop_index());
// Do not print the oop by default--we want this routine to
// work even during GC or other inconvenient times.
if (WizardMode && oop_value != nullptr) {
@@ -912,8 +912,8 @@ void RelocIterator::print_current_on(outputStream* st) {
raw_metadata = *metadata_addr;
metadata_value = r->metadata_value();
}
- st->print(" | [metadata_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT "]",
- p2i(metadata_addr), p2i(raw_metadata));
+ st->print(" | [metadata_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT " index=%d]",
+ p2i(metadata_addr), p2i(raw_metadata), r->metadata_index());
if (metadata_value != nullptr) {
st->print("metadata_value=" INTPTR_FORMAT ": ", p2i(metadata_value));
metadata_value->print_value_on(st);
@@ -953,7 +953,7 @@ void RelocIterator::print_current_on(outputStream* st) {
} else {
CodeBlob* cb = CodeCache::find_blob(dest);
if (cb != nullptr) {
- st->print(" %s", cb->name());
+ st->print(" Blob::%s", cb->name());
} else {
ResourceMark rm;
const int buflen = 1024;
diff --git a/src/hotspot/share/code/relocInfo.hpp b/src/hotspot/share/code/relocInfo.hpp
index ee5faea4206..c3bff6519a7 100644
--- a/src/hotspot/share/code/relocInfo.hpp
+++ b/src/hotspot/share/code/relocInfo.hpp
@@ -1310,6 +1310,31 @@ class trampoline_stub_Relocation : public Relocation {
void pack_data_to(CodeSection * dest) override;
void unpack_data() override;
+// Platforms that keep the call destination in a trampoline stub read and write it
+// through these. A platform needing USE_TRAMPOLINE_STUB_FIX_OWNER also needs them.
+#if defined(USE_TRAMPOLINE_STUB_DESTINATION) || defined(USE_TRAMPOLINE_STUB_FIX_OWNER)
+ address pd_destination ();
+ void pd_set_destination (address x);
+#else
+ address pd_destination () {
+ fatal("trampoline_stub_Relocation::destination() unimplemented");
+ return (address)-1;
+ }
+ void pd_set_destination (address x) {
+ fatal("trampoline_stub_Relocation::set_destination() unimplemented");
+ }
+#endif
+#ifdef USE_TRAMPOLINE_STUB_FIX_OWNER
+ void fix_owner_after_move() {
+ pd_fix_owner_after_move();
+ }
+#endif
+ address destination() {
+ return pd_destination();
+ }
+ void set_destination(address x) {
+ pd_set_destination(x);
+ }
// Find the trampoline stub for a call.
static address get_trampoline_for(address call, nmethod* code);
@@ -1400,7 +1425,7 @@ class internal_word_Relocation : public DataRelocation {
void unpack_data() override;
void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) override;
- void fix_relocation_after_aot_load(address orig_base_addr, address current_base_addr);
+ void fix_relocation_after_aot_load(address current_base_addr, int delta);
address target(); // if _target==nullptr, fetch addr from code stream
int section() { return _section; }
diff --git a/src/hotspot/share/compiler/abstractCompiler.cpp b/src/hotspot/share/compiler/abstractCompiler.cpp
index 386243850dd..09b9b292b3e 100644
--- a/src/hotspot/share/compiler/abstractCompiler.cpp
+++ b/src/hotspot/share/compiler/abstractCompiler.cpp
@@ -41,15 +41,27 @@ bool AbstractCompiler::should_perform_init() {
return false;
}
-bool AbstractCompiler::should_perform_shutdown() {
+void AbstractCompiler::wait_for_initialization() {
+ MonitorLocker locker(CompileThread_lock);
+ // Wait for initialized, failed or shut_down state
+ while (_compiler_state == uninitialized || _compiler_state == initializing) {
+ locker.wait();
+ }
+}
+
+bool AbstractCompiler::should_perform_shutdown(bool is_aot_comp_thread) {
// Since this method can be called by multiple threads, the lock ensures atomicity of
// decrementing '_num_compiler_threads' and the following operations.
MutexLocker only_one(CompileThread_lock);
- _num_compiler_threads--;
+ if (is_aot_comp_thread) {
+ _num_aot_compiler_threads--;
+ } else {
+ _num_compiler_threads--;
+ }
assert (CompileBroker::is_compilation_disabled_forever(), "Must be set, otherwise thread waits forever");
// Only the last thread will perform shutdown operations
- if (_num_compiler_threads == 0) {
+ if (_num_compiler_threads == 0 && _num_aot_compiler_threads == 0) {
return true;
}
return false;
diff --git a/src/hotspot/share/compiler/abstractCompiler.hpp b/src/hotspot/share/compiler/abstractCompiler.hpp
index 5ed256d640c..db02a1fd471 100644
--- a/src/hotspot/share/compiler/abstractCompiler.hpp
+++ b/src/hotspot/share/compiler/abstractCompiler.hpp
@@ -33,6 +33,7 @@ typedef void (*initializer)(void);
// Per-compiler statistics
class CompilerStatistics {
+ public:
class Data {
friend class VMStructs;
public:
@@ -50,9 +51,11 @@ class CompilerStatistics {
}
};
- public:
Data _standard; // stats for non-OSR compilations
Data _osr; // stats for OSR compilations
+ Data _bailout;
+ Data _invalidated;
+ Data _made_not_entrant;
uint _nmethods_size; //
uint _nmethods_code_size;
@@ -73,6 +76,7 @@ class CompilerStatistics {
class AbstractCompiler : public CHeapObj {
private:
volatile int _num_compiler_threads;
+ volatile int _num_aot_compiler_threads;
protected:
volatile int _compiler_state;
@@ -83,17 +87,22 @@ class AbstractCompiler : public CHeapObj {
// This thread will initialize the compiler runtime.
bool should_perform_init();
+ void wait_for_initialization();
+
private:
const CompilerType _type;
CompilerStatistics _stats;
public:
- AbstractCompiler(CompilerType type) : _num_compiler_threads(0), _compiler_state(uninitialized), _type(type) {}
+ AbstractCompiler(CompilerType type) : _num_compiler_threads(0),
+ _num_aot_compiler_threads(0),
+ _compiler_state(uninitialized),
+ _type(type) {}
// This function determines the compiler thread that will perform the
// shutdown of the corresponding compiler runtime.
- bool should_perform_shutdown();
+ bool should_perform_shutdown(bool is_aot_comp_thread);
// Name of this compiler
virtual const char* name() = 0;
@@ -152,11 +161,14 @@ class AbstractCompiler : public CHeapObj {
virtual void initialize () = 0;
void set_num_compiler_threads(int num) { _num_compiler_threads = num; }
- int num_compiler_threads() { return _num_compiler_threads; }
+ int num_compiler_threads() const { return _num_compiler_threads; }
+
+ void set_num_aot_compiler_threads(int num) { _num_aot_compiler_threads = num; }
+ int num_aot_compiler_threads() const { return _num_aot_compiler_threads; }
// Get/set state of compiler objects
- bool is_initialized() { return _compiler_state == initialized; }
- bool is_failed () { return _compiler_state == failed;}
+ bool is_initialized() const { return _compiler_state == initialized; }
+ bool is_failed () const { return _compiler_state == failed;}
void set_state (int state);
void set_shut_down () { set_state(shut_down); }
// Compilation entry point for methods
diff --git a/src/hotspot/share/compiler/aotCompileBroker.cpp b/src/hotspot/share/compiler/aotCompileBroker.cpp
new file mode 100644
index 00000000000..bf5fe0d815c
--- /dev/null
+++ b/src/hotspot/share/compiler/aotCompileBroker.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2023, 2026, 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 "cds/aotCacheAccess.hpp"
+#include "cds/archiveBuilder.hpp"
+#include "cds/cdsConfig.hpp"
+#include "code/aotCodeCache.hpp"
+#include "compiler/aotCompileBroker.hpp"
+#include "compiler/compilationPolicy.hpp"
+#include "compiler/compileBroker.hpp"
+#include "compiler/compiler_globals.hpp"
+#include "compiler/compilerDefinitions.hpp"
+#include "logging/logStream.hpp"
+#include "memory/allocation.hpp"
+#include "oops/method.inline.hpp"
+#include "oops/trainingData.hpp"
+#include "runtime/handles.inline.hpp"
+
+class AOTCompileIterator : StackObj {
+private:
+ CompLevel _comp_level;
+ bool _for_preload;
+ Thread* _thread;
+ GrowableArray _methods;
+
+public:
+ AOTCompileIterator(CompLevel comp_level, bool for_preload, JavaThread* thread)
+ : _comp_level(comp_level), _for_preload(for_preload), _thread(thread) {
+ assert(TrainingData::have_data(), "sanity");
+ }
+
+ bool include(MethodTrainingData* mtd) {
+ Method* m = mtd->holder();
+ if (m->is_native() || m->is_abstract() || !m->method_holder()->is_linked()) {
+ return false;
+ }
+ CompilerDirectiveMatcher matcher(methodHandle(_thread, m), _comp_level);
+ if (matcher.directive_set()->DontAOTCompileOption) {
+ return false;
+ }
+ int high_top_level = mtd->highest_top_level();
+ switch (_comp_level) {
+ case CompLevel_simple:
+ case CompLevel_full_optimization:
+ // For final C1/C2 compilations, we only compile when there was relevant compilation during training.
+ return _comp_level == high_top_level;
+ case CompLevel_limited_profile:
+ // For profiled C1 compilations, generate limited profile when there was limited/full
+ // profiled compilation in training.
+ return CompLevel_limited_profile <= high_top_level && high_top_level <= CompLevel_full_profile;
+ default:
+ // This includes CompLevel_full_profile which should be filtered out.
+ assert(false, "Unexpected AOT compilation level: %d", _comp_level);
+ }
+ // Do not include methods by default.
+ return false;
+ }
+
+ void apply(TrainingData* td) {
+ if (td->is_MethodTrainingData()) {
+ MethodTrainingData* mtd = td->as_MethodTrainingData();
+ if (mtd->has_holder() && include(mtd)) {
+ _methods.push(mtd->holder());
+ }
+ }
+ }
+
+ static MethodTrainingData* method_training_data(Method* m) {
+ if (m->method_holder()->is_loaded()) {
+ return MethodTrainingData::find(methodHandle(Thread::current(), m));
+ }
+ return nullptr;
+ }
+
+ // We sort methods by compile ID, presuming the methods that compiled earlier
+ // are more important. This only matters for preload code, which is loaded
+ // asynchronously; other levels are sorted for better consistency between training
+ // runs. Since we can accept methods from multiple levels, we use the compile ID
+ // from the lowest level.
+ static int compare_methods(Method** m1, Method** m2) {
+ int c1 = compile_id(*m1);
+ int c2 = compile_id(*m2);
+ if (c1 < c2) return -1;
+ if (c1 > c2) return +1;
+ return 0;
+ }
+
+ static int compile_id(Method* m) {
+ // Methods without recorded compilations are treated as "compiled last"
+ int id = INT_MAX;
+ MethodTrainingData* mtd = method_training_data(m);
+ if (mtd != nullptr) {
+ for (int level = CompLevel_simple; level <= CompilationPolicy::highest_compile_level(); level++) {
+ CompileTrainingData* ctd = mtd->last_toplevel_compile(level);
+ if (ctd != nullptr) {
+ id = MIN2(id, ctd->compile_id());
+ }
+ }
+ }
+ return id;
+ }
+
+ void schedule_compilations(TRAPS) {
+ for (int i = 0; i < _methods.length(); i++) {
+ Method* m = _methods.at(i);
+ methodHandle mh(THREAD, m);
+ assert(mh()->method_holder()->is_linked(), "required");
+ if (!AOTCacheAccess::can_generate_aot_code(m)) {
+ continue; // Method is not archived
+ }
+ assert(!HAS_PENDING_EXCEPTION, "");
+ CompileBroker::compile_method(mh, InvocationEntryBci, _comp_level,
+ 0, nullptr /* AOTCodeEntry* */,
+ _for_preload ? CompileTask::Reason_AOTCompileForPreload : CompileTask::Reason_AOTCompile,
+ THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ }
+ }
+ }
+
+ void print_compilation_status(ArchiveBuilder* builder) {
+ LogStreamHandle(Info, aot, compilation) logi;
+ if (!logi.is_enabled()) {
+ return;
+ }
+ int success_count = 0;
+ const int log_comp_level = _comp_level + (_for_preload ? 1 : 0);
+
+ for (int i = 0; i < _methods.length(); i++) {
+ Method* m = _methods.at(i);
+
+ bool is_preloaded_code = m->has_compiled_code() && m->code()->aot_preloaded();
+ bool is_success = !m->is_not_compilable(_comp_level) &&
+ m->has_compiled_code() && m->code()->is_aot();
+ // Successful preloaded AP4 code may mask failed normal A4 code later load.
+ if (is_success && is_preloaded_code != _for_preload) {
+ is_success = false;
+ }
+ if (is_success) {
+ success_count++;
+ }
+
+ LogStreamHandle(Debug, aot, compilation) log;
+ if (log.is_enabled()) {
+ ResourceMark rm;
+ log.print("[%4d] A%d Compiled %s [%p", i, log_comp_level, m->external_name(), m);
+ Method* requested_m = builder->to_requested(builder->get_buffered_addr(m));
+ log.print(" -> %p", requested_m);
+ log.print_cr("] {%d} [%d] (%s)", compile_id(m), AOTCodeCache::store_entries_cnt(), (is_success ? "success" : "FAILED"));
+ }
+ }
+
+ logi.print_cr("AOT Compilation for level A%d finished (%d successful out of %d total)",
+ log_comp_level, success_count, _methods.length());
+ }
+
+ void compile(ArchiveBuilder* builder, TRAPS) {
+ _methods.sort(&compare_methods);
+
+ intx tier2_invoke_notify_freq_log = Tier2InvokeNotifyFreqLog;
+ intx tier2_backedge_notify_freq_log = Tier2BackedgeNotifyFreqLog;
+ double compile_threshold_scaling = CompileThresholdScaling;
+ bool restore_freq_logs_flags = false;
+ if (_comp_level == CompLevel_limited_profile && _methods.length() > 0 &&
+ CompileThresholdScaling != CDSConfig::aot_compile_threshold_scaling()) {
+ // CDSConfig reset CompileThresholdScaling to default 1.0 and preserved original value.
+ // Restore scaling for A2 compilation based on preserved CompileThresholdScaling.
+ CompileThresholdScaling = CDSConfig::aot_compile_threshold_scaling();
+ Tier2InvokeNotifyFreqLog = CompilerConfig::jvmflag_scaled_freq_log(Tier2InvokeNotifyFreqLog);
+ Tier2BackedgeNotifyFreqLog = CompilerConfig::jvmflag_scaled_freq_log(Tier2BackedgeNotifyFreqLog);
+ restore_freq_logs_flags = true;
+ }
+ schedule_compilations(THREAD);
+ CompileBroker::wait_for_no_active_tasks();
+ print_compilation_status(builder);
+ if (restore_freq_logs_flags) {
+ Tier2InvokeNotifyFreqLog = tier2_invoke_notify_freq_log;
+ Tier2BackedgeNotifyFreqLog = tier2_backedge_notify_freq_log;
+ CompileThresholdScaling = compile_threshold_scaling;
+ }
+ }
+};
+
+// Top level driver for AOT code compilation. Runs after metadata and
+// Java object heap have been added to the AOT cache. Inside this pass,
+// the scope function CDSConfig::is_dumping_aot_code returns true.
+// Normal JIT compilation is suppressed, and the JIT pipeline is repurposed
+// to create AOT code (tasks are marked Reason_AOTCompileForPreload, etc.).
+// Some JIT policy flags are forcibly adjusted to settings required by AOT code.
+// These include -FoldStableValues, +ForceUnreachable, -DelayCompilerStubsGeneration,
+// +AOTCompileEagerly, CompileThresholdScaling=1.0
+void AOTCompileBroker::compile_aot_code(ArchiveBuilder* builder, TRAPS) {
+ assert(AOTCodeCache::is_dumping_code(), "sanity");
+ assert(CDSConfig::is_dumping_aot_code(), "AOT compilation must be enabled");
+ if (TrainingData::have_data()) {
+ // Drain normal JIT compilation tasks before submitting AOT compilation.
+ CompileBroker::wait_for_no_active_tasks();
+
+ ResourceMark rm;
+ CompLevel highest_level = CompilationPolicy::highest_compile_level();
+ if (highest_level >= CompLevel_full_optimization && ClassInitBarrierMode > 0 &&
+ !AOTCodeCache::skip_aot_code(CompLevel_preload_optimization)) {
+ AOTCompileIterator aci(CompLevel_full_optimization, true /*for_preload*/, THREAD);
+ TrainingData::iterate([&](TrainingData* td) {
+ aci.apply(td);
+ });
+ aci.compile(builder, THREAD);
+ }
+
+ for (int level = CompLevel_simple; level <= highest_level; level++) {
+ if (!AOTCodeCache::skip_aot_code(level) && CompileBroker::compiler(level) != nullptr) {
+ AOTCompileIterator aci((CompLevel)level, false /*for_preload*/, THREAD);
+ TrainingData::iterate([&](TrainingData* td) {
+ aci.apply(td);
+ });
+ aci.compile(builder, THREAD);
+ }
+ }
+ }
+}
diff --git a/src/hotspot/share/compiler/aotCompileBroker.hpp b/src/hotspot/share/compiler/aotCompileBroker.hpp
new file mode 100644
index 00000000000..2bb41fca875
--- /dev/null
+++ b/src/hotspot/share/compiler/aotCompileBroker.hpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023, 2026, 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_COMPILER_AOTCOMPILEBROKER_HPP
+#define SHARE_COMPILER_AOTCOMPILEBROKER_HPP
+
+#include "memory/allStatic.hpp"
+#include "utilities/exceptions.hpp"
+
+class ArchiveBuilder;
+
+class AOTCompileBroker : AllStatic {
+public:
+ static void compile_aot_code(ArchiveBuilder* builder, TRAPS) NOT_CDS_RETURN;
+};
+
+#endif // SHARE_COMPILER_AOTCOMPILEBROKER_HPP
diff --git a/src/hotspot/share/compiler/compilationLog.cpp b/src/hotspot/share/compiler/compilationLog.cpp
index 88bd29d6843..915aa1a4f11 100644
--- a/src/hotspot/share/compiler/compilationLog.cpp
+++ b/src/hotspot/share/compiler/compilationLog.cpp
@@ -36,6 +36,7 @@ CompilationLog::CompilationLog() : StringEventLog("Compilation events", "jit") {
}
void CompilationLog::log_compile(JavaThread* thread, CompileTask* task) {
+ ResourceMark rm;
StringLogMessage lm;
stringStream sstr(lm.buffer(), lm.size());
// msg.time_stamp().update_to(tty->time_stamp().ticks());
@@ -44,12 +45,13 @@ void CompilationLog::log_compile(JavaThread* thread, CompileTask* task) {
}
void CompilationLog::log_nmethod(JavaThread* thread, nmethod* nm) {
- log(thread, "nmethod %d%s " INTPTR_FORMAT " code [" INTPTR_FORMAT ", " INTPTR_FORMAT "]",
- nm->compile_id(), nm->is_osr_method() ? "%" : "",
+ log(thread, "nmethod %d %s " INTPTR_FORMAT " code [" INTPTR_FORMAT ", " INTPTR_FORMAT "]",
+ nm->compile_id(), nm->compile_kind(),
p2i(nm), p2i(nm->code_begin()), p2i(nm->code_end()));
}
void CompilationLog::log_failure(JavaThread* thread, CompileTask* task, const char* reason, const char* retry_message) {
+ ResourceMark rm;
StringLogMessage lm;
stringStream sstr(lm.buffer(), lm.size());
if (task == nullptr) {
diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp
index 2b451ebb5b3..47ad8e65010 100644
--- a/src/hotspot/share/compiler/compilationPolicy.cpp
+++ b/src/hotspot/share/compiler/compilationPolicy.cpp
@@ -23,6 +23,7 @@
*/
#include "cds/aotLinkedClassBulkLoader.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/scopeDesc.hpp"
#include "compiler/compilationPolicy.hpp"
#include "compiler/compileBroker.hpp"
@@ -52,6 +53,8 @@
int64_t CompilationPolicy::_start_time = 0;
int CompilationPolicy::_c1_count = 0;
int CompilationPolicy::_c2_count = 0;
+int CompilationPolicy::_ac1_count = 0;
+int CompilationPolicy::_ac2_count = 0;
double CompilationPolicy::_increase_threshold_at_ratio = 0;
CompilationPolicy::TrainingReplayQueue CompilationPolicy::_training_replay_queue;
@@ -82,28 +85,59 @@ bool CompilationPolicy::must_be_compiled(const methodHandle& m, int comp_level)
return !UseInterpreter; // must compile all methods
}
-void CompilationPolicy::maybe_compile_early(const methodHandle& m, TRAPS) {
+AOTCodeEntry* find_aot_code_entry(const methodHandle& method, int comp_level,
+ CompileTask::CompileReason compile_reason) {
+ precond(compile_reason == CompileTask::Reason_AOTLoad ||
+ compile_reason == CompileTask::Reason_Tiered ||
+ compile_reason == CompileTask::Reason_MustBeCompiled);
+ if (AOTCodeCache::is_using_code()) {
+ AOTCodeEntry* aot_code_entry = AOTCodeCache::find_code_entry(method, comp_level);
+ // There is harmless concurrency here when reading fields in the AOTCodeEntry.
+ // The _loaded field is monotonic, going true in ciEnv::register_aot_method under
+ // MethodCompileQueue_lock. We might race with other threads which quickly queue
+ // and then load the method, and harmlessly conclude that this code entry is
+ // still loadable. Even if we manage to queue a second load concurrently,
+ // CompileBroker::compile_method_base checks, under the MCQ lock, with
+ // compilation_is_in_queue and compilation_is_complete, to avoid duplicate work.
+ if (aot_code_entry != nullptr && !aot_code_entry->is_loaded() &&
+ !aot_code_entry->not_entrant() && !aot_code_entry->is_verified()) {
+ return aot_code_entry;
+ }
+ }
+ return nullptr;
+}
+
+void CompilationPolicy::maybe_compile_early(const methodHandle& m, MethodTrainingData* mtd, TRAPS) {
if (m->method_holder()->is_not_initialized()) {
// 'is_not_initialized' means not only '!is_initialized', but also that
// initialization has not been started yet ('!being_initialized')
// Do not force compilation of methods in uninitialized classes.
return;
}
- if (!m->is_native() && MethodTrainingData::have_data()) {
- MethodTrainingData* mtd = MethodTrainingData::find_fast(m);
- if (mtd == nullptr) {
- return; // there is no training data recorded for m
+ // Consider replacing conservatively compiled AOT Preload code with faster AOT code or normal JITed code
+ nmethod* nm = m->code();
+ bool recompile = (nm != nullptr) && nm->aot_preloaded();
+ CompLevel cur_level = static_cast(m->highest_comp_level());
+ CompLevel next_level = trained_transition(m, cur_level, mtd, THREAD);
+ if ((next_level != cur_level || recompile) && can_be_compiled(m, next_level) && !CompileBroker::compilation_is_in_queue(m)) {
+ // We are here because some of CTD have all init dependencies satisfied.
+ CompileTrainingData* ctd = mtd->compile_data_for_aot_code(next_level);
+ if (ctd == nullptr || (ctd->init_deps_left_acquire() > 0)) {
+ // Skip compilation because CTD is absent or not all dependencies are ready
+ return;
+ }
+ CompileTask::CompileReason reason = CompileTask::Reason_AOTLoad;
+ AOTCodeEntry* aot_code_entry = find_aot_code_entry(m, next_level, reason);
+ if (aot_code_entry == nullptr) {
+ // Request normal JIT compilation if there is no next_level aot code for this method
+ reason = CompileTask::Reason_MustBeCompiled;
}
- CompLevel cur_level = static_cast(m->highest_comp_level());
- CompLevel next_level = trained_transition(m, cur_level, mtd, THREAD);
- if (next_level != cur_level && can_be_compiled(m, next_level) && !CompileBroker::compilation_is_in_queue(m)) {
- if (PrintTieredEvents) {
- print_event(FORCE_COMPILE, m(), m(), InvocationEntryBci, next_level);
- }
- CompileBroker::compile_method(m, InvocationEntryBci, next_level, 0, CompileTask::Reason_MustBeCompiled, THREAD);
- if (HAS_PENDING_EXCEPTION) {
- CLEAR_PENDING_EXCEPTION;
- }
+ if (PrintTieredEvents) {
+ print_event(FORCE_COMPILE, m(), m(), InvocationEntryBci, next_level);
+ }
+ CompileBroker::compile_method(m, InvocationEntryBci, next_level, 0, aot_code_entry, reason, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
}
}
}
@@ -124,17 +158,42 @@ void CompilationPolicy::compile_if_required(const methodHandle& m, TRAPS) {
return;
}
- if (must_be_compiled(m)) {
+ if (must_be_compiled(m) && !CompileBroker::compilation_is_in_queue(m)) {
// This path is unusual, mostly used by the '-Xcomp' stress test mode.
CompLevel level = initial_compile_level(m);
+ CompileTask::CompileReason reason = CompileTask::Reason_MustBeCompiled;
if (PrintTieredEvents) {
print_event(FORCE_COMPILE, m(), m(), InvocationEntryBci, level);
}
- CompileBroker::compile_method(m, InvocationEntryBci, level, 0, CompileTask::Reason_MustBeCompiled, THREAD);
+ // Check AOT code too
+ if (TrainingData::have_data()) {
+ MethodTrainingData* mtd = MethodTrainingData::find_fast(m);
+ if (mtd != nullptr) {
+ CompileTrainingData* ctd = mtd->compile_data_for_aot_code(level);
+ if (ctd != nullptr && (ctd->init_deps_left_acquire() == 0)) {
+ AOTCodeEntry* aot_code_entry = find_aot_code_entry(m, level, reason);
+ if (aot_code_entry != nullptr) {
+ // This is blocked compilation - return here after it is finished
+ CompileBroker::compile_method(m, InvocationEntryBci, level, 0, aot_code_entry, reason, THREAD);
+ } // Request normal JIT compilation too for -Xcomp
+ }
+ }
+ }
+ // Request normal JIT compilation
+ CompileBroker::compile_method(m, InvocationEntryBci, level, 0, nullptr, reason, THREAD);
}
}
+// Exactly once, when each class is initialized, check it for
+// dependent CompileTrainingData blocks, and notify each one that
+// one of its class initialization dependencies has been satisfied.
+// See comments in class CompileTrainingData for details.
void CompilationPolicy::replay_training_at_init_impl(InstanceKlass* klass, JavaThread* current) {
+ // This method must run in a single TrainingReplayThread which
+ // services the training replay queue. There is no need for atomic
+ // test-and-set of has_init_deps_processed because it is only set
+ // here, after a test. The single thread cannot race with itself.
+ // FIXME: Add asserts (a) we're in a TRT, and (b) there's just one.
if (!klass->has_init_deps_processed()) {
ResourceMark rm;
log_debug(training)("Replay training: %s", klass->external_name());
@@ -144,16 +203,22 @@ void CompilationPolicy::replay_training_at_init_impl(InstanceKlass* klass, JavaT
guarantee(ktd->has_holder(), "");
ktd->notice_fully_initialized(); // sets klass->has_init_deps_processed bit
assert(klass->has_init_deps_processed(), "");
+
if (AOTCompileEagerly) {
+ GrowableArray mtds;
ktd->iterate_comp_deps([&](CompileTrainingData* ctd) {
if (ctd->init_deps_left_acquire() == 0) {
MethodTrainingData* mtd = ctd->method();
if (mtd->has_holder()) {
- const methodHandle mh(current, const_cast(mtd->holder()));
- CompilationPolicy::maybe_compile_early(mh, current);
+ mtds.push(mtd);
}
}
});
+ for (int i = 0; i < mtds.length(); i++) {
+ MethodTrainingData* mtd = mtds.at(i);
+ const methodHandle mh(current, const_cast(mtd->holder()));
+ CompilationPolicy::maybe_compile_early(mh, mtd, current);
+ }
}
}
}
@@ -161,7 +226,8 @@ void CompilationPolicy::replay_training_at_init_impl(InstanceKlass* klass, JavaT
void CompilationPolicy::replay_training_at_init(InstanceKlass* klass, JavaThread* current) {
assert(klass->is_initialized(), "");
- if (TrainingData::have_data() && klass->in_aot_cache()) {
+ if (TrainingData::have_data() && klass->in_aot_cache() &&
+ !CDSConfig::is_dumping_final_static_archive()) { // No need during assembly phase
_training_replay_queue.push(klass, TrainingReplayQueue_lock, current);
}
}
@@ -518,6 +584,16 @@ void CompilationPolicy::initialize() {
#ifdef _LP64
// Turn on ergonomic compiler count selection
+ if (AOTCodeCache::maybe_dumping_code()) {
+ // Assembly phase runs C1 and C2 compilation in separate phases,
+ // and can use all the CPU threads it can reach. Adjust the common
+ // options before policy starts overwriting them.
+ FLAG_SET_ERGO_IF_DEFAULT(UseDynamicNumberOfCompilerThreads, false);
+ FLAG_SET_ERGO_IF_DEFAULT(CICompilerCountPerCPU, false);
+ if (FLAG_IS_DEFAULT(CICompilerCount)) {
+ count = MAX2(count, os::active_processor_count());
+ }
+ }
if (FLAG_IS_DEFAULT(CICompilerCountPerCPU) && FLAG_IS_DEFAULT(CICompilerCount)) {
FLAG_SET_DEFAULT(CICompilerCountPerCPU, true);
}
@@ -526,6 +602,8 @@ void CompilationPolicy::initialize() {
int log_cpu = log2i(os::active_processor_count());
int loglog_cpu = log2i(MAX2(log_cpu, 1));
count = MAX2(log_cpu * loglog_cpu * 3 / 2, min_count);
+ }
+ if (FLAG_IS_DEFAULT(CICompilerCount)) {
// Make sure there is enough space in the code cache to hold all the compiler buffers
size_t c1_size = 0;
#ifdef COMPILER1
@@ -574,6 +652,15 @@ void CompilationPolicy::initialize() {
set_c1_count(MAX2(count / 3, 1));
set_c2_count(MAX2(count - c1_count(), 1));
}
+ if (AOTCodeCache::is_code_load_thread_on()) {
+ // At minimum we need 2 threads to load C1 and C2 AOT code in parallel
+ if (!c2_only) {
+ set_ac1_count(1);
+ }
+ if (!c1_only) {
+ set_ac2_count(1);
+ }
+ }
assert(count == c1_count() + c2_count(), "inconsistent compiler thread count");
set_increase_threshold_at_ratio();
} else {
@@ -693,7 +780,7 @@ CompileTask* CompilationPolicy::select_task(CompileQueue* compile_queue, JavaThr
Method* max_method = nullptr;
int64_t t = nanos_to_millis(os::javaTimeNanos());
- // Iterate through the queue and find a method with a maximum rate.
+ // Iterate through the queue and select highest priority task.
for (CompileTask* task = compile_queue->first(); task != nullptr;) {
CompileTask* next_task = task->next();
// If a method was unloaded or has been stale for some time, remove it from the queue.
@@ -703,6 +790,23 @@ CompileTask* CompilationPolicy::select_task(CompileQueue* compile_queue, JavaThr
task = next_task;
continue;
}
+ if (task->is_aot_load()) {
+ // AOTCodeCache tasks are on separate queue, and they should load fast. There is no need to walk
+ // the rest of the queue, just take the task and go.
+ //
+ // If the task will load "preload" (AP4) code, we could do a quick poll here
+ // to see if, in fact, the fully optimized AOT code (A4) is ready, because the
+ // clinit dependencies have been concurrently satisfied while the AP4 task was
+ // waiting on the ac2 loading queue. In addition, when the JVM finds that the
+ // A4 task is ready, it tries to queue it, but if the AP4 is already queued,
+ // the JVM will quietly drop the A4 request. This means the poorer AP4 code
+ // will be used, until the invocation counter times out and a proper JIT task
+ // is queued - the benefit from A4 is lost in this case.
+ //
+ // Patching this hole did not give performance benefits in early testing, but
+ // it may help with certain scales or configurations in the future.
+ return task;
+ }
if (task->is_blocking() && task->compile_reason() == CompileTask::Reason_Whitebox) {
// CTW tasks, submitted as blocking Whitebox requests, do not participate in rate
// selection and/or any level adjustments. Just return them in order.
@@ -714,20 +818,20 @@ CompileTask* CompilationPolicy::select_task(CompileQueue* compile_queue, JavaThr
if (PrintTieredEvents) {
print_event(REMOVE_FROM_QUEUE, method, method, task->osr_bci(), (CompLevel) task->comp_level());
}
- method->clear_queued_for_compilation();
compile_queue->remove_and_mark_stale(task);
+ method->clear_queued_for_compilation();
task = next_task;
continue;
}
update_rate(t, mh);
- if (max_task == nullptr || compare_methods(method, max_method)) {
- // Select a method with the highest rate
+ // Prefer Reason_MustBeCompiled task. Otherwise use method heuristics selection.
+ if (max_task == nullptr || compare_tasks(task, max_task)) {
max_task = task;
max_method = method;
}
if (task->is_blocking()) {
- if (max_blocking_task == nullptr || compare_methods(method, max_blocking_task->method())) {
+ if (max_blocking_task == nullptr || compare_tasks(task, max_blocking_task)) {
max_blocking_task = task;
}
}
@@ -753,7 +857,9 @@ CompileTask* CompilationPolicy::select_task(CompileQueue* compile_queue, JavaThr
max_task->set_comp_level(CompLevel_limited_profile);
max_task->transfer_directive(directive_matcher);
- if (CompileBroker::compilation_is_complete(max_method_h, max_task->osr_bci(), CompLevel_limited_profile)) {
+ if (CompileBroker::compilation_is_complete(max_method_h, max_task->osr_bci(), CompLevel_limited_profile,
+ nullptr /* AOTCodeEntry* */,
+ CompileTask::Reason_None)) {
if (PrintTieredEvents) {
print_event(REMOVE_FROM_QUEUE, max_method, max_method, max_task->osr_bci(), (CompLevel)max_task->comp_level());
}
@@ -839,7 +945,9 @@ void CompilationPolicy::compile(const methodHandle& mh, int bci, CompLevel level
MutexLocker ml(Compile_lock);
NoSafepointVerifier nsv;
if (mh->has_compiled_code()) {
+ // All new activations will use the interpreter.
mh->code()->make_not_used();
+ // And handle old activations by deoptimizing them...
}
// Deoptimize immediately (we don't have to wait for a compile).
JavaThread* jt = THREAD;
@@ -886,7 +994,24 @@ void CompilationPolicy::compile(const methodHandle& mh, int bci, CompLevel level
}
int hot_count = (bci == InvocationEntryBci) ? mh->invocation_count() : mh->backedge_count();
update_rate(nanos_to_millis(os::javaTimeNanos()), mh);
- CompileBroker::compile_method(mh, bci, level, hot_count, CompileTask::Reason_Tiered, THREAD);
+ CompileTask::CompileReason reason = CompileTask::Reason_Tiered;
+ // Check AOT code too
+ if (TrainingData::have_data() && (bci == InvocationEntryBci)) {
+ MethodTrainingData* mtd = MethodTrainingData::find_fast(mh);
+ if (mtd != nullptr) {
+ CompileTrainingData* ctd = mtd->compile_data_for_aot_code(level);
+ if (ctd != nullptr && (ctd->init_deps_left_acquire() == 0)) {
+ AOTCodeEntry* aot_code_entry = find_aot_code_entry(mh, level, reason);
+ if (aot_code_entry != nullptr) {
+ CompileBroker::compile_method(mh, InvocationEntryBci, level, hot_count, aot_code_entry, reason, THREAD);
+ if (UseInterpreter) {
+ return;
+ } // Request normal JIT compilation too for -Xcomp
+ }
+ }
+ }
+ }
+ CompileBroker::compile_method(mh, bci, level, hot_count, nullptr, reason, THREAD);
}
}
@@ -969,6 +1094,17 @@ bool CompilationPolicy::compare_methods(Method* x, Method* y) {
return false;
}
+bool CompilationPolicy::compare_tasks(CompileTask* x, CompileTask* y) {
+ assert(!x->is_aot_load() && !y->is_aot_load(), "AOT code caching tasks are not expected here");
+ bool x_must_compile = x->compile_reason() == CompileTask::Reason_MustBeCompiled;
+ bool y_must_compile = y->compile_reason() == CompileTask::Reason_MustBeCompiled;
+ if (x_must_compile != y_must_compile) {
+ return x_must_compile;
+ }
+ // Tasks have the same MustBeCompiled priority. Compare methods.
+ return compare_methods(x->method(), y->method());
+}
+
// Is method profiled enough?
bool CompilationPolicy::is_method_profiled(const methodHandle& method) {
MethodData* mdo = method->method_data();
@@ -1198,6 +1334,10 @@ CompLevel CompilationPolicy::trained_transition(const methodHandle& method, Comp
* 3 - C1 with full profiling (CompLevel_full_profile)
* 4 - C2 (CompLevel_full_optimization)
*
+ * Special AOT states:
+ * AP4 - C2 with "preload" clinit barriers (CompLevel_preload_optimization)
+ * A4 - C2 with less static final folding (CompLevel_full_optimization)
+ *
* Common state transition patterns:
* a. 0 -> 3 -> 4.
* The most common path. But note that even in this straightforward case
@@ -1223,6 +1363,11 @@ CompLevel CompilationPolicy::trained_transition(const methodHandle& method, Comp
* or because of a deopt that didn't require reprofiling (compilation won't happen in this case because
* the compiled version already exists).
*
+ * f. AP4 -> A4 -> 4 or just AP4 -> 4
+ * AP4 "preload" code is loaded before clinit dependencies are satisfied.
+ * After clinit dependencies A4 is loaded, with an invocation counter.
+ * After that counter finishes, the JIT may reoptimize to level 4.
+ *
* Note that since state 0 can be reached from any other state via deoptimization different loops
* are possible.
*
diff --git a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp
index e9a2de076ff..a9fdc3cb81e 100644
--- a/src/hotspot/share/compiler/compilationPolicy.hpp
+++ b/src/hotspot/share/compiler/compilationPolicy.hpp
@@ -246,7 +246,7 @@ class CompilationPolicy : AllStatic {
typedef CompilationPolicyUtils::Queue TrainingReplayQueue;
static int64_t _start_time;
- static int _c1_count, _c2_count;
+ static int _c1_count, _c2_count, _ac1_count, _ac2_count;
static double _increase_threshold_at_ratio;
static TrainingReplayQueue _training_replay_queue;
@@ -297,6 +297,7 @@ class CompilationPolicy : AllStatic {
inline static double weight(Method* method);
// Apply heuristics and return true if x should be compiled before y
inline static bool compare_methods(Method* x, Method* y);
+ inline static bool compare_tasks(CompileTask* x, CompileTask* y);
// Compute event rate for a given method. The rate is the number of event (invocations + backedges)
// per millisecond.
inline static void update_rate(int64_t t, const methodHandle& method);
@@ -313,6 +314,8 @@ class CompilationPolicy : AllStatic {
static void set_c1_count(int x) { _c1_count = x; }
static void set_c2_count(int x) { _c2_count = x; }
+ static void set_ac1_count(int x) { _ac1_count = x; }
+ static void set_ac2_count(int x) { _ac2_count = x; }
enum EventType { CALL, LOOP, COMPILE, FORCE_COMPILE, FORCE_RECOMPILE, REMOVE_FROM_QUEUE, UPDATE_IN_QUEUE, REPROFILE, MAKE_NOT_ENTRANT };
static void print_event_on(outputStream *st, EventType type, Method* m, Method* im, int bci, CompLevel level);
@@ -336,12 +339,14 @@ class CompilationPolicy : AllStatic {
// m must be compiled before executing it
static bool must_be_compiled(const methodHandle& m, int comp_level = CompLevel_any);
- static void maybe_compile_early(const methodHandle& m, TRAPS);
+ static void maybe_compile_early(const methodHandle& m, MethodTrainingData* mtd, TRAPS);
static void replay_training_at_init_impl(InstanceKlass* klass, JavaThread* current);
public:
static int min_invocations() { return Tier4MinInvocationThreshold; }
static int c1_count() { return _c1_count; }
static int c2_count() { return _c2_count; }
+ static int ac1_count() { return _ac1_count; }
+ static int ac2_count() { return _ac2_count; }
static int compiler_count(CompLevel comp_level);
// If m must_be_compiled then request a compilation from the CompileBroker.
// This supports the -Xcomp option.
diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp
index d97feeece77..0349e1f83b0 100644
--- a/src/hotspot/share/compiler/compileBroker.cpp
+++ b/src/hotspot/share/compiler/compileBroker.cpp
@@ -27,6 +27,7 @@
#include "classfile/symbolTable.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/codeCache.hpp"
#include "code/codeHeapState.hpp"
#include "code/dependencyContext.hpp"
@@ -67,7 +68,7 @@
#include "runtime/safepointVerifiers.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/threads.hpp"
-#include "runtime/threadSMR.hpp"
+#include "runtime/threadSMR.inline.hpp"
#include "runtime/timerTrace.hpp"
#include "runtime/vframe.inline.hpp"
#include "utilities/debug.hpp"
@@ -128,13 +129,19 @@ AbstractCompiler* CompileBroker::_compilers[2];
// The maximum numbers of compiler threads to be determined during startup.
int CompileBroker::_c1_count = 0;
int CompileBroker::_c2_count = 0;
+int CompileBroker::_ac1_count = 0;
+int CompileBroker::_ac2_count = 0;
// An array of compiler names as Java String objects
jobject* CompileBroker::_compiler1_objects = nullptr;
jobject* CompileBroker::_compiler2_objects = nullptr;
+jobject* CompileBroker::_ac1_objects = nullptr;
+jobject* CompileBroker::_ac2_objects = nullptr;
CompileLog** CompileBroker::_compiler1_logs = nullptr;
CompileLog** CompileBroker::_compiler2_logs = nullptr;
+CompileLog** CompileBroker::_ac1_logs = nullptr;
+CompileLog** CompileBroker::_ac2_logs = nullptr;
// These counters are used to assign an unique ID to each compilation.
volatile jint CompileBroker::_compilation_id = 0;
@@ -174,6 +181,7 @@ elapsedTimer CompileBroker::_t_bailedout_compilation;
uint CompileBroker::_total_bailout_count = 0;
uint CompileBroker::_total_invalidated_count = 0;
+uint CompileBroker::_total_not_entrant_count = 0;
uint CompileBroker::_total_compile_count = 0;
uint CompileBroker::_total_osr_compile_count = 0;
uint CompileBroker::_total_standard_compile_count = 0;
@@ -188,9 +196,13 @@ uint CompileBroker::_sum_nmethod_code_size = 0;
jlong CompileBroker::_peak_compilation_time = 0;
CompilerStatistics CompileBroker::_stats_per_level[CompLevel_full_optimization];
+CompilerStatistics CompileBroker::_aot_stats;
+CompilerStatistics CompileBroker::_aot_stats_per_level[CompLevel_full_optimization + 1];
CompileQueue* CompileBroker::_c2_compile_queue = nullptr;
CompileQueue* CompileBroker::_c1_compile_queue = nullptr;
+CompileQueue* CompileBroker::_ac1_compile_queue = nullptr;
+CompileQueue* CompileBroker::_ac2_compile_queue = nullptr;
bool compileBroker_init() {
if (LogEvents) {
@@ -341,35 +353,34 @@ void CompileQueue::add(CompileTask* task) {
* compilation is disabled.
*/
void CompileQueue::delete_all() {
- MutexLocker mu(MethodCompileQueue_lock);
- CompileTask* current = _first;
-
+ CompileTask* current;
+ {
+ MonitorLocker ml(MethodCompileQueue_lock);
+ current = _first;
+ // Detach queue
+ _first = nullptr;
+ _last = nullptr;
+ ml.notify_all();
+ }
// Iterate over all tasks in the compile queue
while (current != nullptr) {
CompileTask* next = current->next();
+ current->set_prev(nullptr);
+ current->set_next(nullptr);
if (!current->is_blocking()) {
// Non-blocking task. No one is waiting for it, delete it now.
delete current;
} else {
// Blocking task. By convention, it is the waiters responsibility
// to delete the task. We cannot delete it here, because we do not
- // coordinate with waiters. We will notify the waiters later.
+ // coordinate with waiters. Complete it and notify the waiters.
+ MonitorLocker ml(CompileTaskWait_lock);
+ current->set_failure_reason("Compilation is disabled.");
+ current->mark_complete();
+ ml.notify_all();
}
current = next;
}
- _first = nullptr;
- _last = nullptr;
-
- // Wake up all blocking task waiters to deal with remaining blocking
- // tasks. This is not a performance sensitive path, so we do this
- // unconditionally to simplify coding/testing.
- {
- MonitorLocker ml(Thread::current(), CompileTaskWait_lock);
- ml.notify_all();
- }
-
- // Wake up all threads that block on the queue.
- MethodCompileQueue_lock->notify_all();
}
/**
@@ -491,17 +502,15 @@ void CompileQueue::remove_and_mark_stale(CompileTask* task) {
// methods in the compile queue need to be marked as used on the stack
// so that they don't get reclaimed by Redefine Classes
void CompileQueue::mark_on_stack() {
- CompileTask* task = _first;
- while (task != nullptr) {
+ for (CompileTask* task = _first; task != nullptr; task = task->next()) {
task->mark_on_stack();
- task = task->next();
}
}
-CompileQueue* CompileBroker::compile_queue(int comp_level) {
- if (is_c2_compile(comp_level)) return _c2_compile_queue;
- if (is_c1_compile(comp_level)) return _c1_compile_queue;
+CompileQueue* CompileBroker::compile_queue(int comp_level, bool is_aot) {
+ if (is_c2_compile(comp_level)) return ((is_aot && (_ac2_count > 0)) ? _ac2_compile_queue : _c2_compile_queue);
+ if (is_c1_compile(comp_level)) return ((is_aot && (_ac1_count > 0)) ? _ac1_compile_queue : _c1_compile_queue);
return nullptr;
}
@@ -527,6 +536,12 @@ void CompileBroker::print_compile_queues(outputStream* st) {
if (_c2_compile_queue != nullptr) {
_c2_compile_queue->print(st);
}
+ if (_ac1_compile_queue != nullptr) {
+ _ac1_compile_queue->print(st);
+ }
+ if (_ac2_compile_queue != nullptr) {
+ _ac2_compile_queue->print(st);
+ }
}
void CompileQueue::print(outputStream* st) {
@@ -587,15 +602,17 @@ void CompileBroker::compilation_init(JavaThread* THREAD) {
// Set the interface to the current compiler(s).
_c1_count = CompilationPolicy::c1_count();
_c2_count = CompilationPolicy::c2_count();
+ _ac1_count = AOTCodeCache::is_using_code() ? CompilationPolicy::ac1_count() : 0;
+ _ac2_count = AOTCodeCache::is_using_code() ? CompilationPolicy::ac2_count() : 0;
#ifdef COMPILER1
- if (_c1_count > 0) {
+ if (_c1_count > 0 || _ac1_count > 0) {
_compilers[0] = new Compiler();
}
#endif // COMPILER1
#ifdef COMPILER2
- if (_c2_count > 0) {
+ if (_c2_count > 0 || _ac2_count > 0) {
_compilers[1] = new C2Compiler();
// Register c2 as c2 CompilerPhaseType idToPhase mapping is explicit.
// idToPhase mapping for c2 is in opto/phasetype.hpp
@@ -708,9 +725,17 @@ void CompileBroker::compilation_init(JavaThread* THREAD) {
CHECK);
}
+ if (AOTCodeCache::is_on()) {
+ log_info(aot, codecache, init)("CompileBroker is initialized");
+ }
_initialized = true;
}
+Handle CompileBroker::create_thread_oop(const char* name, TRAPS) {
+ Handle thread_oop = JavaThread::create_system_thread_object(name, CHECK_NH);
+ return thread_oop;
+}
+
void TrainingReplayThread::training_replay_thread_entry(JavaThread* thread, TRAPS) {
CompilationPolicy::replay_training_at_init_loop(thread);
}
@@ -812,7 +837,16 @@ JavaThread* CompileBroker::make_thread(ThreadType type, jobject thread_handle, C
if (new_thread != nullptr && new_thread->osthread() != nullptr) {
if (type == compiler_t) {
- CompilerThread::cast(new_thread)->set_compiler(comp);
+ CompilerThread* comp_thread = CompilerThread::cast(new_thread);
+ comp_thread->set_compiler(comp);
+ // Count AOT compiler thread
+ bool is_aot_thread = (queue != nullptr) &&
+ (queue == _ac1_compile_queue || queue == _ac2_compile_queue);
+ if (is_aot_thread) {
+ comp_thread->set_is_aot_thread();
+ int num = comp->num_aot_compiler_threads() + 1;
+ comp->set_num_aot_compiler_threads(num);
+ }
}
// Note that we cannot call os::set_priority because it expects Java
@@ -835,6 +869,11 @@ JavaThread* CompileBroker::make_thread(ThreadType type, jobject thread_handle, C
JavaThread::start_internal_daemon(THREAD, new_thread, thread_oop, NearMaxPriority);
} else { // osthread initialization failure
+ if (type == compiler_t && (queue == _ac1_compile_queue || queue == _ac2_compile_queue)) {
+ // Failure to create AOT compilation threads
+ vm_exit_during_initialization("java.lang.OutOfMemoryError",
+ os::native_thread_creation_failed_msg());
+ }
if (UseDynamicNumberOfCompilerThreads && type == compiler_t
&& comp->num_compiler_threads() > 0) {
// The new thread is not known to Thread-SMR yet so we can just delete.
@@ -874,11 +913,22 @@ static void print_compiler_threads(stringStream& msg) {
}
}
+static void print_compiler_thread(JavaThread *ct) {
+ if (trace_compiler_threads()) {
+ ResourceMark rm;
+ ThreadsListHandle tlh; // name() depends on the TLH.
+ assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct));
+ stringStream msg;
+ msg.print("Added initial compiler thread %s", ct->name());
+ print_compiler_threads(msg);
+ }
+}
+
void CompileBroker::init_compiler_threads() {
// Ensure any exceptions lead to vm_exit_during_initialization.
EXCEPTION_MARK;
#if !defined(ZERO)
- assert(_c2_count > 0 || _c1_count > 0, "No compilers?");
+ assert(_c2_count > 0 || _c1_count > 0 || _ac2_count > 0 || _ac1_count > 0, "No compilers?");
#endif // !ZERO
// Initialize the compilation queue
if (_c2_count > 0) {
@@ -893,6 +943,18 @@ void CompileBroker::init_compiler_threads() {
_compiler1_logs = NEW_C_HEAP_ARRAY(CompileLog*, _c1_count, mtCompiler);
}
+ if (_ac1_count > 0) { // AOT C1 is present
+ _ac1_compile_queue = new CompileQueue("C1 AOT code compile queue");
+ _ac1_objects = NEW_C_HEAP_ARRAY(jobject, _ac1_count, mtCompiler);
+ _ac1_logs = NEW_C_HEAP_ARRAY(CompileLog*, _ac1_count, mtCompiler);
+ }
+ if (_ac2_count > 0) { // AOT C2 is present
+ _ac2_compile_queue = new CompileQueue("C2 AOT code compile queue");
+ _ac2_objects = NEW_C_HEAP_ARRAY(jobject, _ac2_count, mtCompiler);
+ _ac2_logs = NEW_C_HEAP_ARRAY(CompileLog*, _ac2_count, mtCompiler);
+ }
+ char name_buffer[256];
+
for (int i = 0; i < _c2_count; i++) {
// Create a name for our thread.
jobject thread_handle = create_compiler_thread(_compilers[1], i, CHECK);
@@ -903,14 +965,7 @@ void CompileBroker::init_compiler_threads() {
JavaThread *ct = make_thread(compiler_t, thread_handle, _c2_compile_queue, _compilers[1], THREAD);
assert(ct != nullptr, "should have been handled for initial thread");
_compilers[1]->set_num_compiler_threads(i + 1);
- if (trace_compiler_threads()) {
- ResourceMark rm;
- ThreadsListHandle tlh; // name() depends on the TLH.
- assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct));
- stringStream msg;
- msg.print("Added initial compiler thread %s", ct->name());
- print_compiler_threads(msg);
- }
+ print_compiler_thread(ct);
}
}
@@ -924,18 +979,36 @@ void CompileBroker::init_compiler_threads() {
JavaThread *ct = make_thread(compiler_t, thread_handle, _c1_compile_queue, _compilers[0], THREAD);
assert(ct != nullptr, "should have been handled for initial thread");
_compilers[0]->set_num_compiler_threads(i + 1);
- if (trace_compiler_threads()) {
- ResourceMark rm;
- ThreadsListHandle tlh; // name() depends on the TLH.
- assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct));
- stringStream msg;
- msg.print("Added initial compiler thread %s", ct->name());
- print_compiler_threads(msg);
- }
+ print_compiler_thread(ct);
}
}
- if (UsePerfData) {
+ if (_ac2_count > 0) { // AOT C2 is present
+ assert(_ac2_count == 1, "update this code if more AOT threads are needed");
+ os::snprintf_checked(name_buffer, sizeof(name_buffer), "C2 AOT code caching CompilerThread");
+ Handle thread_oop = create_thread_oop(name_buffer, CHECK);
+ jobject thread_handle = JNIHandles::make_global(thread_oop);
+ _ac2_objects[0] = thread_handle;
+ _ac2_logs[0] = nullptr;
+
+ JavaThread *ct = make_thread(compiler_t, thread_handle, _ac2_compile_queue, _compilers[1], THREAD);
+ assert(ct != nullptr, "should have been handled for initial thread");
+ print_compiler_thread(ct);
+ }
+ if (_ac1_count > 0) { // AOT C1 is present
+ assert(_ac1_count == 1, "update this code if more AOT threads are needed");
+ os::snprintf_checked(name_buffer, sizeof(name_buffer), "C1 AOT code caching CompilerThread");
+ Handle thread_oop = create_thread_oop(name_buffer, CHECK);
+ jobject thread_handle = JNIHandles::make_global(thread_oop);
+ _ac1_objects[0] = thread_handle;
+ _ac1_logs[0] = nullptr;
+
+ JavaThread *ct = make_thread(compiler_t, thread_handle, _ac1_compile_queue, _compilers[0], THREAD);
+ assert(ct != nullptr, "should have been handled for initial thread");
+ print_compiler_thread(ct);
+ }
+
+ if (UsePerfData && (_c1_count + _c2_count) > 0) {
PerfDataManager::create_constant(SUN_CI, "threads", PerfData::U_Bytes, _c1_count + _c2_count, CHECK);
}
@@ -955,7 +1028,8 @@ void CompileBroker::init_compiler_threads() {
void CompileBroker::init_training_replay() {
// Ensure any exceptions lead to vm_exit_during_initialization.
EXCEPTION_MARK;
- if (TrainingData::have_data()) {
+ // No need to replay training during assembly phase. Only training data is used.
+ if (TrainingData::have_data() && !CDSConfig::is_dumping_final_static_archive()) {
Handle thread_oop = JavaThread::create_system_thread_object("Training replay thread", CHECK);
jobject thread_handle = JNIHandles::make_local(THREAD, thread_oop());
make_thread(training_replay_t, thread_handle, nullptr, nullptr, THREAD);
@@ -1055,6 +1129,12 @@ void CompileBroker::mark_on_stack() {
if (_c1_compile_queue != nullptr) {
_c1_compile_queue->mark_on_stack();
}
+ if (_ac1_compile_queue != nullptr) {
+ _ac1_compile_queue->mark_on_stack();
+ }
+ if (_ac2_compile_queue != nullptr) {
+ _ac2_compile_queue->mark_on_stack();
+ }
}
// ------------------------------------------------------------------
@@ -1062,6 +1142,7 @@ void CompileBroker::mark_on_stack() {
//
// Request compilation of a method.
void CompileBroker::compile_method_base(const methodHandle& method,
+ AOTCodeEntry* aot_code_entry,
int osr_bci,
int comp_level,
int hot_count,
@@ -1069,29 +1150,51 @@ void CompileBroker::compile_method_base(const methodHandle& method,
bool blocking,
Thread* thread) {
guarantee(!method->is_abstract(), "cannot compile abstract methods");
- assert(method->method_holder()->is_instance_klass(),
- "sanity check");
- assert(!method->method_holder()->is_not_initialized(),
- "method holder must be initialized");
+ precond(method->method_holder()->is_instance_klass());
+ precond(compile_reason != CompileTask::Reason_AOTPreload || aot_code_entry != nullptr);
+ assert(!method->method_holder()->is_not_initialized() ||
+ compile_reason == CompileTask::Reason_AOTPreload ||
+ CompileTask::reason_is_aot_compile(compile_reason), "method holder must be initialized");
assert(!method->is_method_handle_intrinsic(), "do not enqueue these guys");
+ bool aot_preload_comp = (compile_reason == CompileTask::Reason_AOTPreload) ||
+ (compile_reason == CompileTask::Reason_AOTCompileForPreload);
+ bool aot_comp = (compile_reason == CompileTask::Reason_AOTCompile) ||
+ (aot_code_entry != nullptr);
+ const char* aotc = aot_preload_comp ? "AP" : (aot_comp ? " A" : " ");
+
if (CIPrintRequests) {
- tty->print("request: ");
- method->print_short_name(tty);
+ ResourceMark rm;
+ stringStream ss;
+ ss.print("request %16s: %s%d", CompileTask::reason_name(compile_reason), aotc, comp_level);
if (osr_bci != InvocationEntryBci) {
- tty->print(" osr_bci: %d", osr_bci);
+ ss.print(" osr_bci: %d", osr_bci);
}
- tty->print(" level: %d comment: %s count: %d", comp_level, CompileTask::reason_name(compile_reason), hot_count);
- if (hot_count > 0) {
- tty->print(" hot: yes");
+ ss.print(" hot_count: %d", hot_count);
+ method->print_short_name(&ss);
+ tty->print_cr("%s", ss.freeze());
+ }
+ LogStreamHandle(Debug, aot, codecache, compilation) log;
+ if (log.is_enabled() && (AOTCodeCache::is_using_code() || AOTCodeCache::is_dumping_code())) {
+ ResourceMark rm;
+ MethodTrainingData* mtd = MethodTrainingData::have_data() ? MethodTrainingData::find_fast(method) : nullptr;
+ MethodCounters* mc = method->method_counters();
+ const char* name = method->name_and_sig_as_C_string(true /* use_double_colon */);
+ const char* osrn = (osr_bci != InvocationEntryBci) ? "% " : "";
+ log.print("request %16s: %s%d %s%s", CompileTask::reason_name(compile_reason), aotc, comp_level, osrn, name);
+ if (mtd != nullptr) {
+ log.print(" (MTD invoke: %d, backedge: %d)", mtd->invocation_count(), mtd->backedge_count());
}
- tty->cr();
+ if (mc != nullptr) {
+ log.print(" (MC invoke: %d, backedge: %d)", mc->invocation_counter()->count(),
+ mc->backedge_counter()->count());
+ }
+ log.cr();
}
-
// A request has been made for compilation. Before we do any
// real work, check to see if the method has been compiled
// in the meantime with a definitive result.
- if (compilation_is_complete(method, osr_bci, comp_level)) {
+ if (compilation_is_complete(method, osr_bci, comp_level, aot_code_entry, compile_reason)) {
return;
}
@@ -1120,11 +1223,15 @@ void CompileBroker::compile_method_base(const methodHandle& method,
// Tiered policy requires MethodCounters to exist before adding a method to
// the queue. Create if we don't have them yet.
- method->get_method_counters(thread);
+ if (method->get_method_counters(thread) == nullptr) {
+ return; // metaspace has hit an OOM
+ }
+
+ bool is_aot = (aot_code_entry != nullptr);
// Outputs from the following MutexLocker block:
- CompileTask* task = nullptr;
- CompileQueue* queue = compile_queue(comp_level);
+ CompileTask* task = nullptr;
+ CompileQueue* queue = compile_queue(comp_level, is_aot);
// Acquire our lock.
{
@@ -1140,7 +1247,7 @@ void CompileBroker::compile_method_base(const methodHandle& method,
// We need to check again to see if the compilation has
// completed. A previous compilation may have registered
// some result.
- if (compilation_is_complete(method, osr_bci, comp_level)) {
+ if (compilation_is_complete(method, osr_bci, comp_level, aot_code_entry, compile_reason)) {
return;
}
@@ -1194,7 +1301,7 @@ void CompileBroker::compile_method_base(const methodHandle& method,
task = create_compile_task(queue,
compile_id, method,
osr_bci, comp_level,
- hot_count, compile_reason,
+ hot_count, aot_code_entry, compile_reason,
blocking);
}
@@ -1203,9 +1310,54 @@ void CompileBroker::compile_method_base(const methodHandle& method,
}
}
+static bool is_blocking_compilation(DirectiveSet* directive, CompileTask::CompileReason compile_reason) {
+ return ReplayCompiles || !directive->BackgroundCompilationOption ||
+ (AOTPreloadBlocking && (compile_reason == CompileTask::Reason_AOTPreload));
+}
+
+void CompileBroker::preload_aot_method(const methodHandle& method, AOTCodeEntry* aot_code_entry, TRAPS) {
+ // Don't need most of the checks for AOT code preloading.
+ precond(_initialized);
+ precond(aot_code_entry != nullptr && aot_code_entry->for_preload());
+ // If the compiler is shut off due to code cache getting full
+ // fail out now so blocking compiles don't hang the java thread
+ if (should_compile_new_jobs()) {
+ int osr_bci = InvocationEntryBci;
+ int comp_level = CompLevel_full_optimization;
+ int hot_count = 0;
+ CompileTask::CompileReason compile_reason = CompileTask::Reason_AOTPreload;
+
+ AbstractCompiler *comp = CompileBroker::compiler(comp_level);
+ assert(comp != nullptr, "Ensure we have a compiler");
+
+ nmethod* method_code = method->code();
+ if (method_code != nullptr) {
+ if (compilation_is_complete(method, osr_bci, comp_level, aot_code_entry, compile_reason)) {
+ return;
+ }
+ }
+ if (method->is_not_compilable(comp_level)) {
+ return;
+ }
+
+ // JVMTI -- post_compile_event requires jmethod_id() that may require
+ // a lock the compiling thread can not acquire. Prefetch it here.
+ if (JvmtiExport::should_post_compiled_method_load()) {
+ method->jmethod_id();
+ }
+
+ CompilerDirectiveMatcher matcher(method, comp_level);
+ bool is_blocking = is_blocking_compilation(matcher.directive_set(), compile_reason);
+ // CompileBroker::compile_method can trap and can have pending async exception.
+ compile_method_base(method, aot_code_entry, osr_bci, comp_level, hot_count, compile_reason,
+ is_blocking, THREAD);
+ }
+}
+
nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
int comp_level,
int hot_count,
+ AOTCodeEntry* aot_code_entry,
CompileTask::CompileReason compile_reason,
TRAPS) {
// Do nothing if compilebroker is not initialized or compiles are submitted on level none
@@ -1218,13 +1370,14 @@ nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
CompilerDirectiveMatcher matcher(method, comp_level);
// CompileBroker::compile_method can trap and can have pending async exception.
- nmethod* nm = CompileBroker::compile_method(method, osr_bci, comp_level, hot_count, compile_reason, matcher.directive_set(), THREAD);
+ nmethod* nm = CompileBroker::compile_method(method, osr_bci, comp_level, hot_count, aot_code_entry, compile_reason, matcher.directive_set(), THREAD);
return nm;
}
nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
int comp_level,
int hot_count,
+ AOTCodeEntry* aot_code_entry,
CompileTask::CompileReason compile_reason,
DirectiveSet* directive,
TRAPS) {
@@ -1233,8 +1386,15 @@ nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
assert(method->method_holder()->is_instance_klass(), "not an instance method");
assert(osr_bci == InvocationEntryBci || (0 <= osr_bci && osr_bci < method->code_size()), "bci out of range");
assert(!method->is_abstract() && (osr_bci == InvocationEntryBci || !method->is_native()), "cannot compile abstract/native methods");
- assert(!method->method_holder()->is_not_initialized(), "method holder must be initialized");
+ assert(!method->method_holder()->is_not_initialized() ||
+ compile_reason == CompileTask::Reason_AOTPreload ||
+ CompileTask::reason_is_aot_compile(compile_reason), "method holder must be initialized");
// return quickly if possible
+ bool aot_compilation = CDSConfig::is_dumping_aot_code();
+ if (aot_compilation && !CompileTask::reason_is_aot_compile(compile_reason)) {
+ // Skip normal compilations when compiling AOT code to speedup AOT compilation
+ return nullptr;
+ }
// lock, make sure that the compilation
// isn't prohibited in a straightforward way.
@@ -1247,7 +1407,7 @@ nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
// standard compilation
nmethod* method_code = method->code();
if (method_code != nullptr) {
- if (compilation_is_complete(method, osr_bci, comp_level)) {
+ if (compilation_is_complete(method, osr_bci, comp_level, aot_code_entry, compile_reason)) {
return method_code;
}
}
@@ -1264,7 +1424,9 @@ nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
assert(!HAS_PENDING_EXCEPTION, "No exception should be present");
// some prerequisites that are compiler specific
- if (comp->is_c2()) {
+ if (compile_reason != CompileTask::Reason_AOTPreload &&
+ !CompileTask::reason_is_aot_compile(compile_reason) &&
+ comp->is_c2()) {
InternalOOMEMark iom(THREAD);
method->constants()->resolve_string_constants(CHECK_AND_CLEAR_NONASYNC_NULL);
// Resolve all classes seen in the signature of the method
@@ -1319,8 +1481,8 @@ nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
if (!should_compile_new_jobs()) {
return nullptr;
}
- bool is_blocking = !directive->BackgroundCompilationOption || ReplayCompiles;
- compile_method_base(method, osr_bci, comp_level, hot_count, compile_reason, is_blocking, THREAD);
+ bool is_blocking = is_blocking_compilation(directive, compile_reason);
+ compile_method_base(method, aot_code_entry, osr_bci, comp_level, hot_count, compile_reason, is_blocking, THREAD);
}
// return requested nmethod
@@ -1336,9 +1498,14 @@ nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
// CompileBroker::compilation_is_complete
//
// See if compilation of this method is already complete.
-bool CompileBroker::compilation_is_complete(const methodHandle& method,
- int osr_bci,
- int comp_level) {
+bool CompileBroker::compilation_is_complete(const methodHandle& method,
+ int osr_bci,
+ int comp_level,
+ AOTCodeEntry* aot_code_entry,
+ CompileTask::CompileReason compile_reason) {
+ if (CompileTask::reason_is_aot_compile(compile_reason)) {
+ return false;
+ }
bool is_osr = (osr_bci != standard_entry_bci);
if (is_osr) {
if (method->is_not_osr_compilable(comp_level)) {
@@ -1352,8 +1519,24 @@ bool CompileBroker::compilation_is_complete(const methodHandle& method,
return true;
} else {
nmethod* result = method->code();
- if (result == nullptr) return false;
- return comp_level == result->comp_level();
+ if (result == nullptr) {
+ return false;
+ }
+ if (result->is_aot()) {
+ // See if an AOTCodeEntry has already been AOT-loaded by register_aot_method.
+ // Both this logic and register_aot_method run under MethodCompileQueue_lock.
+ // Note that CompilationPolicy::find_aot_code_entry also checks AOT-loading,
+ // but tentatively, not under the lock.
+ if (aot_code_entry == nullptr) {
+ return false; // Allow replace AOT code with normal JITed code
+ } else if (aot_code_entry == result->aot_code_entry()) {
+ return true; // Same code already AOT-loaded.
+ } else if (result->aot_preloaded()) {
+ // Allow replace preloaded AOT code with regular AOT code of the same level
+ return (comp_level != result->comp_level());
+ }
+ }
+ return (comp_level == result->comp_level());
}
}
}
@@ -1468,10 +1651,12 @@ CompileTask* CompileBroker::create_compile_task(CompileQueue* queue,
int osr_bci,
int comp_level,
int hot_count,
+ AOTCodeEntry* aot_code_entry,
CompileTask::CompileReason compile_reason,
bool blocking) {
CompileTask* new_task = new CompileTask(compile_id, method, osr_bci, comp_level,
- hot_count, compile_reason, blocking);
+ hot_count, aot_code_entry, compile_reason,
+ blocking);
queue->add(new_task);
return new_task;
}
@@ -1490,40 +1675,13 @@ void CompileBroker::wait_for_completion(CompileTask* task) {
JavaThread* thread = JavaThread::current();
methodHandle method(thread, task->method());
- bool free_task = true;
{
- // Wait until the task is complete or compilation is shut down.
MonitorLocker ml(thread, CompileTaskWait_lock);
- while (!task->is_complete() && !is_compilation_disabled_forever()) {
+ while (!task->is_complete()) {
ml.wait();
}
}
-
- // It is harmless to check this status without the lock, because
- // completion is a stable property.
- if (!task->is_complete()) {
- // Task is not complete, likely because we are exiting for compilation
- // shutdown. The task can still be reached through the queue, or executed
- // by some compiler thread. There is no coordination with either MCQ lock
- // holders or compilers, therefore we cannot delete the task.
- //
- // This will leave task allocated, which leaks it. At this (degraded) point,
- // it is less risky to abandon the task, rather than attempting a more
- // complicated deletion protocol.
- free_task = false;
- }
-
- if (free_task) {
- assert(task->is_complete(), "Compilation should have completed");
- assert(task->next() == nullptr && task->prev() == nullptr,
- "Completed task should not be in the queue");
-
- // By convention, the waiter is responsible for deleting a
- // blocking CompileTask. Since there is only one waiter ever
- // waiting on a CompileTask, we know that no one else will
- // be using this CompileTask; we can delete it.
- delete task;
- }
+ delete task;
}
void CompileBroker::wait_for_no_active_tasks() {
@@ -1540,7 +1698,6 @@ bool CompileBroker::init_compiler_runtime() {
AbstractCompiler* comp = thread->compiler();
// Final sanity check - the compiler object must exist
guarantee(comp != nullptr, "Compiler object must exist");
-
{
// Must switch to native to allocate ci_env
ThreadToNativeFromVM ttn(thread);
@@ -1565,8 +1722,8 @@ bool CompileBroker::init_compiler_runtime() {
return false;
}
- // C1 specific check
- if (comp->is_c1() && (thread->get_buffer_blob() == nullptr)) {
+ // C1 specific check. AOT C1 thread does not use buffer to load AOT code.
+ if (comp->is_c1() && (thread->get_buffer_blob() == nullptr) && !thread->is_aot_thread()) {
warning("Initialization of %s thread failed (no space to run compilers)", thread->name());
return false;
}
@@ -1591,7 +1748,7 @@ void CompileBroker::free_buffer_blob_if_allocated(CompilerThread* thread) {
void CompileBroker::shutdown_compiler_runtime(AbstractCompiler* comp, CompilerThread* thread) {
free_buffer_blob_if_allocated(thread);
- if (comp->should_perform_shutdown()) {
+ if (comp->should_perform_shutdown(thread->is_aot_thread())) {
// There are two reasons for shutting down the compiler
// 1) compiler runtime initialization failed
// 2) The code cache is full and the following flag is set: -XX:-UseCodeCacheFlushing
@@ -1610,6 +1767,14 @@ void CompileBroker::shutdown_compiler_runtime(AbstractCompiler* comp, CompilerTh
_c2_compile_queue->delete_all();
}
+ if (_ac1_compile_queue != nullptr) {
+ _ac1_compile_queue->delete_all();
+ }
+
+ if (_ac2_compile_queue != nullptr) {
+ _ac2_compile_queue->delete_all();
+ }
+
// Set flags so that we continue execution with using interpreter only.
UseCompiler = false;
UseInterpreter = true;
@@ -1627,13 +1792,25 @@ CompileLog* CompileBroker::get_log(CompilerThread* ct) {
if (!LogCompilation) return nullptr;
AbstractCompiler *compiler = ct->compiler();
- bool c1 = compiler->is_c1();
- jobject* compiler_objects = c1 ? _compiler1_objects : _compiler2_objects;
- assert(compiler_objects != nullptr, "must be initialized at this point");
- CompileLog** logs = c1 ? _compiler1_logs : _compiler2_logs;
- assert(logs != nullptr, "must be initialized at this point");
- int count = c1 ? _c1_count : _c2_count;
-
+ jobject* compiler_objects = nullptr;
+ CompileLog** logs = nullptr;
+ int count = 0;
+ if (ct->queue() == _ac1_compile_queue) {
+ compiler_objects = _ac1_objects;
+ logs = _ac1_logs;
+ count = _ac1_count;
+ } else if (ct->queue() == _ac2_compile_queue) {
+ compiler_objects = _ac2_objects;
+ logs = _ac2_logs;
+ count = _ac2_count;
+ } else if (_c1_count + _c2_count > 0) {
+ bool c1 = compiler->is_c1();
+ compiler_objects = c1 ? _compiler1_objects : _compiler2_objects;
+ assert(compiler_objects != nullptr, "must be initialized at this point");
+ logs = c1 ? _compiler1_logs : _compiler2_logs;
+ assert(logs != nullptr, "must be initialized at this point");
+ count = c1 ? _c1_count : _c2_count;
+ }
// Find Compiler number by its threadObj.
oop compiler_obj = ct->threadObj();
int compiler_number = 0;
@@ -1670,6 +1847,8 @@ CompileLog* CompileBroker::get_log(CompilerThread* ct) {
void CompileBroker::compiler_thread_loop() {
CompilerThread* thread = CompilerThread::current();
CompileQueue* queue = thread->queue();
+ precond(queue != nullptr);
+
// For the thread that initializes the ciObjectFactory
// this resource mark holds all the shared objects
ResourceMark rm;
@@ -1756,7 +1935,9 @@ void CompileBroker::compiler_thread_loop() {
task->set_failure_reason("breakpoints are present");
}
- if (UseDynamicNumberOfCompilerThreads) {
+ // Don't use AOT compiler threads for dynamic C1 and C2 threads creation.
+ if (UseDynamicNumberOfCompilerThreads &&
+ (queue == _c1_compile_queue || queue == _c2_compile_queue)) {
possibly_add_compiler_threads(thread);
assert(!thread->has_pending_exception(), "should have been handled");
}
@@ -2038,19 +2219,19 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
if (WhiteBoxAPI && WhiteBox::compilation_locked) {
whitebox_lock_compilation();
}
- comp->compile_method(&ci_env, target, osr_bci, true, directive);
+ comp->compile_method(&ci_env, target, osr_bci, true /* install */, directive);
/* Repeat compilation without installing code for profiling purposes */
- int repeat_compilation_count = directive->RepeatCompilationOption;
+ int repeat_compilation_count = task->is_aot_load() ? 0 : directive->RepeatCompilationOption;
if (repeat_compilation_count > 0) {
CHeapStringHolder failure_reason;
- failure_reason.set(ci_env._failure_reason.get());
+ failure_reason.set(ci_env.failure_reason());
while (repeat_compilation_count > 0) {
ResourceMark rm(thread);
task->print_ul("NO CODE INSTALLED");
thread->timeout()->reset();
ci_env._failure_reason.clear();
- comp->compile_method(&ci_env, target, osr_bci, false, directive);
+ comp->compile_method(&ci_env, target, osr_bci, false /* install */, directive);
repeat_compilation_count--;
}
ci_env._failure_reason.set(failure_reason.get());
@@ -2058,11 +2239,18 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
}
- if (!ci_env.failing() && !task->is_success()) {
+ if (!ci_env.failing() && !task->is_success() && !task->is_aot_compile()) {
const char* reason = task->failure_reason();
assert(reason != nullptr, "compiler should always document failure");
- // Do not attempt further compilations of this method.
- ci_env.record_method_not_compilable(reason != nullptr ? reason : "compile failed: reason unknown");
+ if (reason == nullptr) {
+ reason = "compile failed: reason unknown";
+ }
+ if (task->is_aot_load()) {
+ ci_env._failure_reason.set(reason);
+ } else {
+ // Do not attempt further compilations of this method.
+ ci_env.record_method_not_compilable(reason);
+ }
}
// Copy this bit to the enclosing block:
@@ -2244,12 +2432,36 @@ void CompileBroker::update_compile_perf_data(CompilerThread* thread, const metho
//
// Collect statistics about the compilation.
+static int stats_level(int comp_level, bool aot_preload) {
+ return aot_preload ? (CompLevel_full_optimization + 1) : comp_level;
+}
+
+CompilerStatistics* CompileBroker::stats_for(int comp_level, bool aot_preload, bool is_aot) {
+ precond(!aot_preload || is_aot);
+ int level = stats_level(comp_level, aot_preload) - 1;
+ return is_aot ? &_aot_stats_per_level[level] : &_stats_per_level[level];
+}
+
void CompileBroker::collect_statistics(CompilerThread* thread, elapsedTimer time, CompileTask* task) {
bool success = task->is_success();
methodHandle method (thread, task->method());
int compile_id = task->compile_id();
bool is_osr = (task->osr_bci() != standard_entry_bci);
+ AbstractCompiler* comp = task->compiler();
+ assert(comp != nullptr, "Compiler object must exist");
const int comp_level = task->comp_level();
+ assert(comp_level > CompLevel_none && comp_level <= CompLevel_full_optimization,
+ "CompilerStatistics object does not exist for compilation level %d", comp_level);
+ bool aot_load = task->is_aot_load();
+ bool aot_preload = task->is_aot_preload();
+
+ nmethod* code = is_osr ? method->lookup_osr_nmethod_for(task->osr_bci(), comp_level, true /* match_level */)
+ : method->code();
+ // With VerifyAOTCode AOT code is not installed.
+ bool verify_aot_code = task->is_aot_load() && VerifyAOTCode;
+ // Check if compiled code was invalidated already
+ bool invalidated = !verify_aot_code && (code == nullptr || code->compile_id() != compile_id);
+
CompilerCounters* counters = thread->counters();
MutexLocker locker(CompileStatistics_lock);
@@ -2275,7 +2487,12 @@ void CompileBroker::collect_statistics(CompilerThread* thread, elapsedTimer time
_perf_total_bailout_count->inc();
}
_t_bailedout_compilation.add(time);
- } else if (!task->is_success()) {
+
+ if (CITime || log_is_enabled(Info, init)) {
+ CompilerStatistics* stats = stats_for(comp_level, aot_preload, aot_load);
+ stats->_bailout.update(time, 0);
+ }
+ } else if (invalidated) {
if (UsePerfData) {
_perf_last_invalidated_method->set_value(counters->current_method());
_perf_last_invalidated_type->set_value(counters->compile_type());
@@ -2283,9 +2500,14 @@ void CompileBroker::collect_statistics(CompilerThread* thread, elapsedTimer time
}
_total_invalidated_count++;
_t_invalidated_compilation.add(time);
+
+ if (CITime || log_is_enabled(Info, init)) {
+ CompilerStatistics* stats = stats_for(comp_level, aot_preload, aot_load);
+ stats->_invalidated.update(time, 0);
+ }
} else {
// Compilation succeeded
- if (CITime) {
+ if (CITime || log_is_enabled(Info, init)) {
int bytes_compiled = method->code_size() + task->num_inlined_bytecodes();
if (is_osr) {
_t_osr_compilation.add(time);
@@ -2296,8 +2518,15 @@ void CompileBroker::collect_statistics(CompilerThread* thread, elapsedTimer time
}
// Collect statistic per compilation level
- if (comp_level > CompLevel_none && comp_level <= CompLevel_full_optimization) {
- CompilerStatistics* stats = &_stats_per_level[comp_level-1];
+ CompilerStatistics* stats = stats_for(comp_level, aot_preload, aot_load);
+ if (aot_load) {
+ _aot_stats._standard.update(time, bytes_compiled);
+ _aot_stats._nmethods_size += task->nm_total_size();
+ _aot_stats._nmethods_code_size += task->nm_insts_size();
+ stats->_standard.update(time, bytes_compiled);
+ stats->_nmethods_size += task->nm_total_size();
+ stats->_nmethods_code_size += task->nm_insts_size();
+ } else if (comp_level > CompLevel_none && comp_level <= CompLevel_full_optimization) {
if (is_osr) {
stats->_osr.update(time, bytes_compiled);
} else {
@@ -2306,12 +2535,11 @@ void CompileBroker::collect_statistics(CompilerThread* thread, elapsedTimer time
stats->_nmethods_size += task->nm_total_size();
stats->_nmethods_code_size += task->nm_insts_size();
} else {
- assert(false, "CompilerStatistics object does not exist for compilation level %d", comp_level);
+ ShouldNotReachHere();
}
// Collect statistic per compiler
- AbstractCompiler* comp = task->compiler();
- if (comp) {
+ if (!aot_load) {
CompilerStatistics* stats = comp->stats();
if (is_osr) {
stats->_osr.update(time, bytes_compiled);
@@ -2320,8 +2548,6 @@ void CompileBroker::collect_statistics(CompilerThread* thread, elapsedTimer time
}
stats->_nmethods_size += task->nm_total_size();
stats->_nmethods_code_size += task->nm_insts_size();
- } else { // if (!comp)
- assert(false, "Compiler object must exist");
}
}
@@ -2383,6 +2609,18 @@ jlong CompileBroker::total_compilation_ticks() {
return _perf_total_compilation != nullptr ? _perf_total_compilation->get_value() : 0;
}
+void CompileBroker::log_not_entrant(nmethod* nm) {
+ _total_not_entrant_count++;
+ if (CITime || log_is_enabled(Info, init)) {
+ int level = nm->comp_level();
+ if (level == CompLevel_none) {
+ return; // native wrapper
+ }
+ CompilerStatistics* stats = stats_for(level, nm->aot_preloaded(), nm->is_aot());
+ stats->_made_not_entrant._count++;
+ }
+}
+
void CompileBroker::print_times(const char* name, CompilerStatistics* stats) {
tty->print_cr(" %s {speed: %6.3f bytes/s; standard: %6.3f s, %u bytes, %u methods; osr: %6.3f s, %u bytes, %u methods; nmethods_size: %u bytes; nmethods_code_size: %u bytes}",
name, stats->bytes_per_second(),
@@ -2391,11 +2629,101 @@ void CompileBroker::print_times(const char* name, CompilerStatistics* stats) {
stats->_nmethods_size, stats->_nmethods_code_size);
}
+static void print_helper(outputStream* st, const char* name, CompilerStatistics::Data data, bool print_time = true) {
+ if (data._count > 0) {
+ st->print("; %s: %4u methods", name, data._count);
+ if (print_time) {
+ st->print(" (in %.3fs)", data._time.seconds());
+ }
+ }
+}
+
+static void print_tier_helper(outputStream* st, const char* prefix, int tier, CompilerStatistics* stats) {
+ st->print(" %s%d: %5u methods", prefix, tier, stats->_standard._count);
+ if (stats->_standard._count > 0) {
+ st->print(" (in %.3fs)", stats->_standard._time.seconds());
+ }
+ print_helper(st, "osr", stats->_osr);
+ print_helper(st, "bailout", stats->_bailout);
+ print_helper(st, "invalid", stats->_invalidated);
+ print_helper(st, "not_entrant", stats->_made_not_entrant, false);
+ st->cr();
+}
+
+static void print_queue_info(outputStream* st, CompileQueue* queue) {
+ if (queue != nullptr) {
+ ThreadsListHandle tlh;
+ MutexLocker ml(MethodCompileQueue_lock);
+
+ uint total_cnt = 0;
+ uint active_cnt = 0;
+ for (JavaThread* jt : tlh) {
+ guarantee(jt != nullptr, "");
+ if (jt->is_Compiler_thread()) {
+ CompilerThread* ct = (CompilerThread*)jt;
+
+ guarantee(ct != nullptr, "");
+ if (ct->queue() == queue) {
+ ++total_cnt;
+ CompileTask* task = ct->task();
+ if (task != nullptr) {
+ ++active_cnt;
+ }
+ }
+ }
+ }
+
+ st->print(" %s (%d active / %d total threads): %u tasks",
+ queue->name(), active_cnt, total_cnt, queue->size());
+ if (queue->size() > 0) {
+ uint counts[] = {0, 0, 0, 0, 0}; // T1 ... T5
+ for (CompileTask* task = queue->first(); task != nullptr; task = task->next()) {
+ int tier = task->comp_level();
+ if (task->is_aot_preload()) {
+ assert(tier == CompLevel_full_optimization, "%d", tier);
+ tier = CompLevel_full_optimization + 1;
+ }
+ counts[tier-1]++;
+ }
+ st->print(":");
+ for (int tier = CompLevel_simple; tier <= CompilationPolicy::highest_compile_level() + 1; tier++) {
+ uint cnt = counts[tier-1];
+ if (cnt > 0) {
+ st->print(" T%d: %u tasks;", tier, cnt);
+ }
+ }
+ }
+ st->cr();
+ }
+}
+void CompileBroker::print_statistics_on(outputStream* st) {
+ st->print_cr(" Total: %u methods; %u bailouts, %u invalidated, %u non_entrant",
+ _total_compile_count, _total_bailout_count, _total_invalidated_count, _total_not_entrant_count);
+ for (int tier = CompLevel_simple; tier <= CompilationPolicy::highest_compile_level(); tier++) {
+ print_tier_helper(st, "Tier", tier, &_stats_per_level[tier-1]);
+ }
+ st->cr();
+
+ if (AOTCodeCaching) {
+ for (int tier = CompLevel_simple; tier <= CompilationPolicy::highest_compile_level() + 1; tier++) {
+ if (tier != CompLevel_full_profile) {
+ print_tier_helper(st, "AOT Code T", tier, &_aot_stats_per_level[tier - 1]);
+ }
+ }
+ st->cr();
+ }
+
+ print_queue_info(st, _c1_compile_queue);
+ print_queue_info(st, _c2_compile_queue);
+ print_queue_info(st, _ac1_compile_queue);
+ print_queue_info(st, _ac2_compile_queue);
+}
+
void CompileBroker::print_times(bool per_compiler, bool aggregate) {
if (per_compiler) {
if (aggregate) {
tty->cr();
- tty->print_cr("Individual compiler times (for compiled methods only)");
+ tty->print_cr("[%dms] Individual compiler times (for compiled methods only)", (int)tty->time_stamp().milliseconds());
tty->print_cr("------------------------------------------------");
tty->cr();
}
@@ -2405,6 +2733,9 @@ void CompileBroker::print_times(bool per_compiler, bool aggregate) {
print_times(comp->name(), comp->stats());
}
}
+ if (_aot_stats._standard._count > 0) {
+ print_times("AC", &_aot_stats);
+ }
if (aggregate) {
tty->cr();
tty->print_cr("Individual compilation Tier times (for compiled methods only)");
@@ -2417,6 +2748,13 @@ void CompileBroker::print_times(bool per_compiler, bool aggregate) {
os::snprintf_checked(tier_name, sizeof(tier_name), "Tier%d", tier);
print_times(tier_name, stats);
}
+ for (int tier = CompLevel_simple; tier <= CompilationPolicy::highest_compile_level() + 1; tier++) {
+ CompilerStatistics* stats = &_aot_stats_per_level[tier-1];
+ if (stats->_standard._bytes > 0) {
+ os::snprintf_checked(tier_name, sizeof(tier_name), "AOT Code T%d", tier);
+ print_times(tier_name, stats);
+ }
+ }
}
if (!aggregate) {
@@ -2458,6 +2796,10 @@ void CompileBroker::print_times(bool per_compiler, bool aggregate) {
CompileBroker::_t_invalidated_compilation.seconds(),
total_invalidated_count == 0 ? 0.0 : CompileBroker::_t_invalidated_compilation.seconds() / total_invalidated_count);
+ if (AOTCodeCaching) { // Check flags because AOT code cache could be closed already
+ tty->cr();
+ AOTCodeCache::print_timers_on(tty);
+ }
AbstractCompiler *comp = compiler(CompLevel_simple);
if (comp != nullptr) {
tty->cr();
diff --git a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp
index db2e9069913..daecc48e642 100644
--- a/src/hotspot/share/compiler/compileBroker.hpp
+++ b/src/hotspot/share/compiler/compileBroker.hpp
@@ -184,13 +184,13 @@ class CompileBroker: AllStatic {
static AbstractCompiler* _compilers[2];
// The maximum numbers of compiler threads to be determined during startup.
- static int _c1_count, _c2_count;
+ static int _c1_count, _c2_count, _ac1_count, _ac2_count;
// An array of compiler thread Java objects
- static jobject *_compiler1_objects, *_compiler2_objects;
+ static jobject *_compiler1_objects, *_compiler2_objects, *_ac1_objects, *_ac2_objects;
// An array of compiler logs
- static CompileLog **_compiler1_logs, **_compiler2_logs;
+ static CompileLog **_compiler1_logs, **_compiler2_logs, **_ac1_logs, **_ac2_logs;
// These counters are used for assigning id's to each compilation
static volatile jint _compilation_id;
@@ -199,6 +199,11 @@ class CompileBroker: AllStatic {
static CompileQueue* _c2_compile_queue;
static CompileQueue* _c1_compile_queue;
+ static CompileQueue* _ac1_compile_queue;
+ static CompileQueue* _ac2_compile_queue;
+ // Note: AOT nmethods are "virtually compiled" - copied in from the AOT cache.
+ // The dynamics of method replacement and dependency checks are common to AOT and JIT.
+ // See checks of is_aot_load and is_aot_compile for behavioral differences.
// performance counters
static PerfCounter* _perf_total_compilation;
@@ -234,6 +239,7 @@ class CompileBroker: AllStatic {
static uint _total_compile_count;
static uint _total_bailout_count;
static uint _total_invalidated_count;
+ static uint _total_not_entrant_count;
static uint _total_native_compile_count;
static uint _total_osr_compile_count;
static uint _total_standard_compile_count;
@@ -246,6 +252,8 @@ class CompileBroker: AllStatic {
static jlong _peak_compilation_time;
static CompilerStatistics _stats_per_level[];
+ static CompilerStatistics _aot_stats;
+ static CompilerStatistics _aot_stats_per_level[];
static volatile int _print_compilation_warning;
@@ -255,6 +263,7 @@ class CompileBroker: AllStatic {
training_replay_t
};
+ static Handle create_thread_oop(const char* name, TRAPS);
static JavaThread* make_thread(ThreadType type, jobject thread_oop, CompileQueue* queue, AbstractCompiler* comp, JavaThread* THREAD);
static void init_compiler_threads();
static void init_training_replay();
@@ -267,9 +276,14 @@ class CompileBroker: AllStatic {
int osr_bci,
int comp_level,
int hot_count,
+ AOTCodeEntry* aot_code_entry,
CompileTask::CompileReason compile_reason,
bool blocking);
static void wait_for_completion(CompileTask* task);
+public:
+ static void wait_for_no_active_tasks();
+
+private:
static void free_buffer_blob_if_allocated(CompilerThread* thread);
static void invoke_compiler_on_method(CompileTask* task);
@@ -277,9 +291,11 @@ class CompileBroker: AllStatic {
int compilable, const char* failure_reason);
static void update_compile_perf_data(CompilerThread *thread, const methodHandle& method, bool is_osr);
+ static CompilerStatistics* stats_for(int comp_level, bool aot_preload, bool is_aot);
static void collect_statistics(CompilerThread* thread, elapsedTimer time, CompileTask* task);
static void compile_method_base(const methodHandle& method,
+ AOTCodeEntry* aot_code_entry,
int osr_bci,
int comp_level,
int hot_count,
@@ -287,7 +303,7 @@ class CompileBroker: AllStatic {
bool blocking,
Thread* thread);
- static CompileQueue* compile_queue(int comp_level);
+ static CompileQueue* compile_queue(int comp_level, bool is_aot);
static bool init_compiler_runtime();
static void shutdown_compiler_runtime(AbstractCompiler* comp, CompilerThread* thread);
@@ -303,11 +319,14 @@ class CompileBroker: AllStatic {
return nullptr;
}
- static bool compilation_is_complete(const methodHandle& method, int osr_bci, int comp_level);
+ static bool initialized() { return _initialized; }
+ static bool compilation_is_complete(const methodHandle& method, int osr_bci, int comp_level,
+ AOTCodeEntry* aot_code_entry,
+ CompileTask::CompileReason compile_reason);
static bool compilation_is_in_queue(const methodHandle& method);
static void print_compile_queues(outputStream* st);
- static int queue_size(int comp_level) {
- CompileQueue *q = compile_queue(comp_level);
+ static int queue_size(int comp_level, bool is_aot = false) {
+ CompileQueue *q = compile_queue(comp_level, is_aot);
return q != nullptr ? q->size() : 0;
}
static void compilation_init(JavaThread* THREAD);
@@ -316,8 +335,12 @@ class CompileBroker: AllStatic {
int osr_bci,
int comp_level,
int hot_count,
+ AOTCodeEntry* aot_code_entry,
CompileTask::CompileReason compile_reason,
TRAPS);
+ static void preload_aot_method(const methodHandle& method,
+ AOTCodeEntry* aot_code_entry,
+ TRAPS);
static CompileQueue* c1_compile_queue();
static CompileQueue* c2_compile_queue();
@@ -326,6 +349,7 @@ class CompileBroker: AllStatic {
int osr_bci,
int comp_level,
int hot_count,
+ AOTCodeEntry* aot_code_entry,
CompileTask::CompileReason compile_reason,
DirectiveSet* directive,
TRAPS);
@@ -376,8 +400,6 @@ class CompileBroker: AllStatic {
return AtomicAccess::load(&_should_compile_new_jobs) == shutdown_compilation;
}
- static void wait_for_no_active_tasks();
-
static void handle_full_code_cache(CodeBlobType code_blob_type);
// Ensures that warning is only printed once.
static bool should_print_compiler_warning() {
@@ -436,9 +458,13 @@ class CompileBroker: AllStatic {
static jlong get_peak_compilation_time() { return _peak_compilation_time; }
static jlong get_total_compilation_time() { return _t_total_compilation.milliseconds(); }
+ static void log_not_entrant(nmethod* nm);
+
// Log that compilation profiling is skipped because metaspace is full.
static void log_metaspace_failure();
+ static void print_statistics_on(outputStream* st);
+
// CodeHeap State Analytics.
static void print_info(outputStream *out);
static void print_heapinfo(outputStream *out, const char* function, size_t granularity);
diff --git a/src/hotspot/share/compiler/compileTask.cpp b/src/hotspot/share/compiler/compileTask.cpp
index 6a5f706ec7a..9b0b68103b8 100644
--- a/src/hotspot/share/compiler/compileTask.cpp
+++ b/src/hotspot/share/compiler/compileTask.cpp
@@ -43,6 +43,7 @@ CompileTask::CompileTask(int compile_id,
int osr_bci,
int comp_level,
int hot_count,
+ AOTCodeEntry* aot_code_entry,
CompileReason compile_reason,
bool is_blocking) :
_compile_id(compile_id),
@@ -57,6 +58,7 @@ CompileTask::CompileTask(int compile_id,
_nm_insts_size(0),
_comp_level(comp_level),
_compiler(CompileBroker::compiler(comp_level)),
+ _aot_code_entry(aot_code_entry),
_comp_directive_matcher(method, static_cast(comp_level)),
_num_inlined_bytecodes(0),
_next(nullptr),
@@ -65,6 +67,8 @@ CompileTask::CompileTask(int compile_id,
_time_queued(0),
_time_started(0),
_time_finished(0),
+ _aot_load_start(0),
+ _aot_load_finish(0),
_hot_count(hot_count),
_compile_reason(compile_reason),
_failure_reason(nullptr),
@@ -167,22 +171,26 @@ void CompileTask::print_tty() {
void CompileTask::print_post(outputStream* st) {
bool is_osr_method = osr_bci() != InvocationEntryBci;
+ bool is_aot = is_aot_load_or_compile();
+ bool is_aot_preload = is_aot_preload_or_compile();
print_impl(st, is_unloaded() ? nullptr : method(), compile_id(), comp_level(),
- is_osr_method, osr_bci(), is_blocking(),
+ is_osr_method, osr_bci(), is_blocking(), is_aot, is_aot_preload,
compiler()->name(), nullptr, false /* short_form */, true /* cr */,
true /* after_compile_details */,
_num_inlined_bytecodes, _nm_total_size, _nm_insts_size,
- _time_created, _time_queued, _time_started, _time_finished);
+ _time_created, _time_queued, _time_started, _time_finished,
+ _aot_load_start, _aot_load_finish);
}
// ------------------------------------------------------------------
// CompileTask::print_impl
void CompileTask::print_impl(outputStream* st, Method* method, int compile_id, int comp_level,
- bool is_osr_method, int osr_bci, bool is_blocking,
+ bool is_osr_method, int osr_bci, bool is_blocking, bool is_aot, bool is_aot_preload,
const char* compiler_name,
const char* msg, bool short_form, bool cr, bool after_compile_details,
int inlined_bytecodes, int nm_total_size, int nm_insts_size,
- jlong time_created, jlong time_queued, jlong time_started, jlong time_finished) {
+ jlong time_created, jlong time_queued, jlong time_started, jlong time_finished,
+ jlong aot_load_start, jlong aot_load_finish) {
// Use stringStream to avoid breaking the line
stringStream sst;
if (after_compile_details) {
@@ -212,6 +220,13 @@ void CompileTask::print_impl(outputStream* st, Method* method, int compile_id, i
}
sst.print("%7s ", ss.freeze());
}
+ { // Time to load from AOT code cache
+ stringStream ss;
+ if (aot_load_start != 0 && aot_load_finish != 0) {
+ ss.print("A%.1f", TimeHelper::counter_to_millis(aot_load_finish - aot_load_start));
+ }
+ sst.print("%7s ", ss.freeze());
+ }
} else if (!short_form) {
// Print current time
sst.print(UINT64_FORMAT " ", (uint64_t) tty->time_stamp().milliseconds());
@@ -245,9 +260,11 @@ void CompileTask::print_impl(outputStream* st, Method* method, int compile_id, i
const char exception_char = has_exception_handler ? '!' : ' ';
const char blocking_char = is_blocking ? 'b' : ' ';
const char native_char = is_native ? 'n' : ' ';
+ const char aot_char = is_aot ? 'A' : ' ';
+ const char aot_preload_char = is_aot_preload ? 'P' : ' ';
// print method attributes
- sst.print("%c%c%c%c%c ", compile_type, sync_char, exception_char, blocking_char, native_char);
+ sst.print("%c%c%c%c%c%c%c ", compile_type, sync_char, exception_char, blocking_char, native_char, aot_char, aot_preload_char);
if (TieredCompilation) {
if (comp_level != -1) sst.print("%d ", comp_level);
@@ -290,8 +307,10 @@ void CompileTask::print_impl(outputStream* st, Method* method, int compile_id, i
// CompileTask::print_compilation
void CompileTask::print(outputStream* st, const char* msg, bool short_form, bool cr) {
bool is_osr_method = osr_bci() != InvocationEntryBci;
+ bool is_aot = is_aot_load_or_compile();
+ bool is_aot_preload = is_aot_preload_or_compile();
print_impl(st, is_unloaded() ? nullptr : method(), compile_id(), comp_level(),
- is_osr_method, osr_bci(), is_blocking(),
+ is_osr_method, osr_bci(), is_blocking(), is_aot, is_aot_preload,
compiler()->name(), msg, short_form, cr);
}
@@ -302,13 +321,20 @@ void CompileTask::log_task(xmlStream* log) {
methodHandle method(thread, this->method());
ResourceMark rm(thread);
+ bool is_osr_method = osr_bci() != InvocationEntryBci;
+ bool is_aot = is_aot_load_or_compile();
+ bool is_aot_preload = is_aot_preload_or_compile();
//
log->print(" compile_id='%d'", _compile_id);
- if (_osr_bci != CompileBroker::standard_entry_bci) {
+ if (is_osr_method) {
log->print(" compile_kind='osr'"); // same as nmethod::compile_kind
- } // else compile_kind='c2c'
+ } else if (is_aot_preload) {
+ log->print(" compile_kind='AP'");
+ } else if (is_aot) {
+ log->print(" compile_kind='A'");
+ } // else compile_kind='jit'
if (!method.is_null()) log->method(method());
- if (_osr_bci != CompileBroker::standard_entry_bci) {
+ if (is_osr_method) {
log->print(" osr_bci='%d'", _osr_bci);
}
if (_comp_level != CompilationPolicy::highest_compile_level()) {
@@ -317,7 +343,6 @@ void CompileTask::log_task(xmlStream* log) {
if (_is_blocking) {
log->print(" blocking='1'");
}
- log->stamp();
}
// ------------------------------------------------------------------
@@ -335,15 +360,17 @@ void CompileTask::log_task_queued() {
if (_hot_count != 0) {
xtty->print(" hot_count='%d'", _hot_count);
}
+ xtty->stamp();
xtty->end_elem();
}
// ------------------------------------------------------------------
// CompileTask::log_task_start
-void CompileTask::log_task_start(CompileLog* log) {
+void CompileTask::log_task_start(CompileLog* log) {
log->begin_head("task");
log_task(log);
+ log->stamp();
log->end_head();
}
@@ -485,8 +512,8 @@ void CompileTask::print_ul(const nmethod* nm, const char* msg) {
print_impl(&ls, nm->method(), nm->compile_id(),
nm->comp_level(), nm->is_osr_method(),
nm->is_osr_method() ? nm->osr_entry_bci() : -1,
- /*is_blocking*/ false,
- nm->compiler_name(),
+ /*is_blocking*/ false, nm->is_aot(),
+ nm->aot_preloaded(), nm->compiler_name(),
msg, /* short form */ true, /* cr */ true);
}
}
diff --git a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp
index afbff5b064e..2eaad96c2a8 100644
--- a/src/hotspot/share/compiler/compileTask.hpp
+++ b/src/hotspot/share/compiler/compileTask.hpp
@@ -33,7 +33,9 @@
#include "runtime/mutexLocker.hpp"
#include "utilities/xmlstream.hpp"
+class AOTCodeEntry;
class CompileTrainingData;
+class DirectiveSet;
enum class InliningResult { SUCCESS, FAILURE };
@@ -60,10 +62,14 @@ class CompileTask : public CHeapObj {
Reason_Replay, // ciReplay
Reason_Whitebox, // Whitebox API
Reason_MustBeCompiled, // Used for -Xcomp (see CompilationPolicy::must_be_compiled())
+ Reason_AOTLoad, // "virtual compilation" - copy in normal AOT code
+ Reason_AOTPreload, // "virtual compilation" - copy in "preload" AOT code
+ Reason_AOTCompile, // create normal AOT code (assembly phase only)
+ Reason_AOTCompileForPreload, // create "preload" AOT code (assembly phase only)
Reason_Count
};
- static const char* reason_name(CompileTask::CompileReason compile_reason) {
+ static const char* reason_name(CompileReason compile_reason) {
static const char* reason_names[] = {
"no_reason",
"count",
@@ -71,12 +77,21 @@ class CompileTask : public CHeapObj {
"tiered",
"replay",
"whitebox",
- "must_be_compiled"
+ "must_be_compiled",
+ "aot_load",
+ "aot_preload",
+ "aot_compile",
+ "aot_compile_for_preload",
};
static_assert(ARRAY_SIZE(reason_names) == Reason_Count);
return reason_names[compile_reason];
}
+ static bool reason_is_aot_compile(CompileReason compile_reason) {
+ return (compile_reason == Reason_AOTCompile) ||
+ (compile_reason == Reason_AOTCompileForPreload);
+ }
+
private:
static int _active_tasks;
int _compile_id;
@@ -91,6 +106,7 @@ class CompileTask : public CHeapObj {
CodeSection::csize_t _nm_insts_size;
int _comp_level;
AbstractCompiler* _compiler;
+ AOTCodeEntry* _aot_code_entry;
CompilerDirectiveMatcher _comp_directive_matcher;
int _num_inlined_bytecodes;
CompileTask* _next;
@@ -100,6 +116,8 @@ class CompileTask : public CHeapObj {
jlong _time_queued; // time when task was enqueued
jlong _time_started; // time when compilation started
jlong _time_finished; // time when compilation finished
+ jlong _aot_load_start;
+ jlong _aot_load_finish;
int _hot_count; // information about its invocation counter
CompileReason _compile_reason; // more info about the task
const char* _failure_reason;
@@ -110,7 +128,9 @@ class CompileTask : public CHeapObj {
public:
CompileTask(int compile_id, const methodHandle& method, int osr_bci, int comp_level,
- int hot_count, CompileReason compile_reason, bool is_blocking);
+ int hot_count, AOTCodeEntry* aot_code_entry,
+ CompileReason compile_reason,
+ bool is_blocking);
~CompileTask();
static void wait_for_no_active_tasks();
@@ -121,9 +141,13 @@ class CompileTask : public CHeapObj {
bool is_complete() const { return _is_complete; }
bool is_blocking() const { return _is_blocking; }
bool is_success() const { return _is_success; }
+ bool is_aot_load() const { return _aot_code_entry != nullptr; }
+ bool is_aot_preload() const { return (_compile_reason == Reason_AOTPreload); }
+ AOTCodeEntry* aot_code_entry() const { return _aot_code_entry; }
DirectiveSet* directive() const { return _comp_directive_matcher.directive_set(); }
void transfer_directive(CompilerDirectiveMatcher& matcher) { _comp_directive_matcher.transfer_from(matcher); }
CompileReason compile_reason() const { return _compile_reason; }
+
CodeSection::csize_t nm_content_size() { return _nm_content_size; }
void set_nm_content_size(CodeSection::csize_t size) { _nm_content_size = size; }
CodeSection::csize_t nm_insts_size() { return _nm_insts_size; }
@@ -141,11 +165,25 @@ class CompileTask : public CHeapObj {
}
}
+ bool is_aot_compile() const {
+ return reason_is_aot_compile(compile_reason());
+ }
+
+ // Next two functions are mostly used to print "A" or "AP".
+ bool is_aot_load_or_compile() const {
+ return is_aot_load() || (_compile_reason == Reason_AOTCompile);
+ }
+ bool is_aot_preload_or_compile() const {
+ return is_aot_preload() || (_compile_reason == Reason_AOTCompileForPreload);
+ }
+
void mark_complete() { _is_complete = true; }
void mark_success() { _is_success = true; }
void mark_queued(jlong time) { _time_queued = time; }
void mark_started(jlong time) { _time_started = time; }
void mark_finished(jlong time) { _time_finished = time; }
+ void mark_aot_load_start(jlong time) { _aot_load_start = time; }
+ void mark_aot_load_finish(jlong time) { _aot_load_finish = time; }
int comp_level() { return _comp_level;}
void set_comp_level(int comp_level) { _comp_level = comp_level;}
@@ -176,12 +214,14 @@ class CompileTask : public CHeapObj {
private:
static void print_impl(outputStream* st, Method* method, int compile_id, int comp_level,
bool is_osr_method = false, int osr_bci = -1, bool is_blocking = false,
+ bool is_aot = false, bool is_preload = false,
const char* compiler_name = nullptr,
const char* msg = nullptr, bool short_form = false, bool cr = true,
bool after_compile_details = false,
int inlined_bytecodes = 0, int nm_total_size = 0, int nm_insts_size = 0,
jlong time_created = 0, jlong time_queued = 0,
- jlong time_started = 0, jlong time_finished = 0);
+ jlong time_started = 0, jlong time_finished = 0,
+ jlong aot_load_start = 0, jlong aot_load_finish = 0);
public:
void print(outputStream* st = tty, const char* msg = nullptr, bool short_form = false, bool cr = true);
@@ -189,6 +229,7 @@ class CompileTask : public CHeapObj {
static void print(outputStream* st, const nmethod* nm, const char* msg = nullptr, bool short_form = false, bool cr = true) {
print_impl(st, nm->method(), nm->compile_id(), nm->comp_level(),
nm->is_osr_method(), nm->is_osr_method() ? nm->osr_entry_bci() : -1, /*is_blocking*/ false,
+ nm->is_aot(), nm->aot_preloaded(),
nm->compiler_name(), msg, short_form, cr);
}
static void print_ul(const nmethod* nm, const char* msg = nullptr);
diff --git a/src/hotspot/share/compiler/compilerDefinitions.hpp b/src/hotspot/share/compiler/compilerDefinitions.hpp
index e086fc8add6..2233fb6eced 100644
--- a/src/hotspot/share/compiler/compilerDefinitions.hpp
+++ b/src/hotspot/share/compiler/compilerDefinitions.hpp
@@ -61,6 +61,9 @@ enum CompLevel : s1 {
CompLevel_full_optimization = 4, // C2
CompLevel_count = 5
};
+// The next CompLevel constant, defined for AOT only, is not part of the enum.
+// TODO: consider moving it into the enum (but must refactor many uses)
+const int CompLevel_preload_optimization = CompLevel_full_optimization + 1; // AP4 tier
class CompilationModeFlag : AllStatic {
enum class Mode {
diff --git a/src/hotspot/share/compiler/compilerDirectives.cpp b/src/hotspot/share/compiler/compilerDirectives.cpp
index 1b0bdc99f08..03649addc60 100644
--- a/src/hotspot/share/compiler/compilerDirectives.cpp
+++ b/src/hotspot/share/compiler/compilerDirectives.cpp
@@ -22,6 +22,7 @@
*
*/
+#include "cds/cdsConfig.hpp"
#include "ci/ciMethod.hpp"
#include "ci/ciUtilities.inline.hpp"
#include "compiler/abstractCompiler.hpp"
@@ -214,7 +215,14 @@ bool DirectiveSet::should_print_memstat() const {
}
size_t DirectiveSet::mem_limit() const {
- return MemLimitOption < 0 ? -MemLimitOption : MemLimitOption;
+ size_t limit = MemLimitOption < 0 ? -MemLimitOption : MemLimitOption;
+#if INCLUDE_CDS
+ if (CDSConfig::is_dumping_aot_code()) {
+ // Double limit for AOT compilation because it generates more code.
+ limit = limit * 2;
+ }
+#endif
+ return limit;
}
bool DirectiveSet::should_crash_at_mem_limit() const {
diff --git a/src/hotspot/share/compiler/compilerDirectives.hpp b/src/hotspot/share/compiler/compilerDirectives.hpp
index ae814cdc491..ea709c6897c 100644
--- a/src/hotspot/share/compiler/compilerDirectives.hpp
+++ b/src/hotspot/share/compiler/compilerDirectives.hpp
@@ -52,7 +52,10 @@
cflags(DumpReplay, bool, false, DumpReplay) \
cflags(DumpInline, bool, false, DumpInline) \
cflags(CompilerDirectivesIgnoreCompileCommands, bool, CompilerDirectivesIgnoreCompileCommands, Unknown) \
- cflags(RepeatCompilation, intx, RepeatCompilation, RepeatCompilation)
+ cflags(RepeatCompilation, intx, RepeatCompilation, RepeatCompilation) \
+ cflags(DontAOTCompile, bool, false, DontAOTCompile) \
+ cflags(DontPreload, bool, false, DontPreload) \
+ cflags(IgnoreAOTCompiled, bool, false, IgnoreAOTCompiled)
#define compilerdirectives_common_string_flags(cflags) \
cflags(DisableIntrinsic, ccstrlist, DisableIntrinsic, DisableIntrinsic) \
cflags(ControlIntrinsic, ccstrlist, ControlIntrinsic, ControlIntrinsic)
diff --git a/src/hotspot/share/compiler/compilerOracle.hpp b/src/hotspot/share/compiler/compilerOracle.hpp
index bfed52f12e7..35cceea32cd 100644
--- a/src/hotspot/share/compiler/compilerOracle.hpp
+++ b/src/hotspot/share/compiler/compilerOracle.hpp
@@ -95,6 +95,9 @@ NOT_PRODUCT(option(TraceMergeStores, "TraceMergeStores", Ccstrlist)) \
option(CloneMapDebug, "CloneMapDebug", Bool) \
option(IncrementalInlineForceCleanup, "IncrementalInlineForceCleanup", Bool) \
option(MaxNodeLimit, "MaxNodeLimit", Intx) \
+ option(DontAOTCompile, "DontAOTCompile", Bool) \
+ option(DontPreload, "DontPreload", Bool) \
+ option(IgnoreAOTCompiled, "IgnoreAOTCompiled", Bool) \
NOT_PRODUCT(option(TestOptionInt, "TestOptionInt", Intx)) \
NOT_PRODUCT(option(TestOptionUint, "TestOptionUint", Uintx)) \
NOT_PRODUCT(option(TestOptionBool, "TestOptionBool", Bool)) \
diff --git a/src/hotspot/share/compiler/compilerThread.cpp b/src/hotspot/share/compiler/compilerThread.cpp
index 0139f864e12..5c215516894 100644
--- a/src/hotspot/share/compiler/compilerThread.cpp
+++ b/src/hotspot/share/compiler/compilerThread.cpp
@@ -40,7 +40,7 @@ CompilerThread::CompilerThread(CompileQueue* queue,
_compiler = nullptr;
_arena_stat = nullptr;
_timeout = nullptr;
-
+ _is_aot_thread = false;
#ifndef PRODUCT
_ideal_graph_printer = nullptr;
#endif
diff --git a/src/hotspot/share/compiler/compilerThread.hpp b/src/hotspot/share/compiler/compilerThread.hpp
index 255b1dc0a37..96b16dfb817 100644
--- a/src/hotspot/share/compiler/compilerThread.hpp
+++ b/src/hotspot/share/compiler/compilerThread.hpp
@@ -81,6 +81,8 @@ class CompilerThread : public JavaThread {
ArenaStatCounter* _arena_stat;
Timeout* _timeout;
+
+ bool _is_aot_thread; // Thread is used to load AOT code
public:
static CompilerThread* current() {
@@ -104,6 +106,9 @@ class CompilerThread : public JavaThread {
void set_compiler(AbstractCompiler* c);
AbstractCompiler* compiler() const { return _compiler; }
+ bool is_aot_thread() const { return _is_aot_thread; }
+ void set_is_aot_thread() { _is_aot_thread = true; }
+
CompileQueue* queue() const { return _queue; }
CompilerCounters* counters() const { return _counters; }
ArenaStatCounter* arena_stat() const { return _arena_stat; }
diff --git a/src/hotspot/share/compiler/compiler_globals.hpp b/src/hotspot/share/compiler/compiler_globals.hpp
index 6c7c4a73c93..08fc126850f 100644
--- a/src/hotspot/share/compiler/compiler_globals.hpp
+++ b/src/hotspot/share/compiler/compiler_globals.hpp
@@ -389,6 +389,41 @@
"available.") \
range(0, max_juint) \
\
+ /* AOT Code Caching flags */ \
+ \
+ product(uint, DisableAOTCode, 0, DIAGNOSTIC, \
+ "Disable AOT code on some compilation levels " \
+ "(T1=1; T2=10; T4=1000; T5/preload=10000) " \
+ "(T3 is disabled regardless - AOT T3 code is not generated)") \
+ constraint(DisableAOTCodeConstraintFunc, AtParse) \
+ \
+ product(uint, ClassInitBarrierMode, 0, DIAGNOSTIC, \
+ "Produce AOT preload code which could be called on first " \
+ "method invocation, add class initialization barriers, " \
+ "other checks and constraints if needed " \
+ "(0: no AOT preload code produced) " \
+ "(1: produce AOT preload code with uncommon trap for barriers)") \
+ range(0, 1) \
+ \
+ product(uint, AOTCodePreloadStart, 0, DIAGNOSTIC, \
+ "The index of the first AOT code to preload") \
+ \
+ product(uint, AOTCodePreloadStop, max_jint, DIAGNOSTIC, \
+ "The index of the last AOT code to preload") \
+ \
+ product(bool, AOTPreloadBlocking, false, DIAGNOSTIC, \
+ "Preload code is processed with blocking. Startup would not " \
+ "proceed until all preloaded code is done loading.") \
+ \
+ product(bool, VerifyAOTCode, false, DIAGNOSTIC, \
+ "Load AOT code but not install into CodeCache") \
+ \
+ develop(bool, AOTAssertOnUnknownExternalAddress, false, \
+ "Assert when external address is missing from the AOT Code " \
+ "external address table. By default (false) AOT compilation " \
+ "will be skipped for method which references such address.") \
+ \
+
// end of COMPILER_FLAGS
DECLARE_FLAGS(COMPILER_FLAGS)
diff --git a/src/hotspot/share/compiler/oopMap.cpp b/src/hotspot/share/compiler/oopMap.cpp
index a447f59b2cd..8b4e7fdc45d 100644
--- a/src/hotspot/share/compiler/oopMap.cpp
+++ b/src/hotspot/share/compiler/oopMap.cpp
@@ -111,7 +111,6 @@ OopMap::OopMap(int frame_size, int arg_count) {
#endif
}
-
OopMap::OopMap(OopMap::DeepCopyToken, OopMap* source) {
// This constructor does a deep copy
// of the source OopMap.
diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp
index 3f064c3569b..ade3d9a7952 100644
--- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp
+++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp
@@ -135,6 +135,32 @@ class ShenandoahBarrierSetC1 : public BarrierSetC1 {
virtual bool generate_c1_runtime_stubs(BufferBlob* buffer_blob);
+ // Next methods are used by AOT code caching
+ CodeBlob* keepalive_barrier_c1_runtime_code_blob() const {
+ assert(_keepalive_barrier_c1_runtime_code_blob != nullptr, "");
+ return _keepalive_barrier_c1_runtime_code_blob;
+ }
+
+ CodeBlob* load_reference_barrier_strong_rt_code_blob() const {
+ assert(_load_reference_barrier_strong_rt_code_blob != nullptr, "");
+ return _load_reference_barrier_strong_rt_code_blob;
+ }
+
+ CodeBlob* load_reference_barrier_strong_native_rt_code_blob() const {
+ assert(_load_reference_barrier_strong_native_rt_code_blob != nullptr, "");
+ return _load_reference_barrier_strong_native_rt_code_blob;
+ }
+
+ CodeBlob* load_reference_barrier_weak_rt_code_blob() const {
+ assert(_load_reference_barrier_weak_rt_code_blob != nullptr, "");
+ return _load_reference_barrier_weak_rt_code_blob;
+ }
+
+ CodeBlob* load_reference_barrier_phantom_rt_code_blob() const {
+ assert(_load_reference_barrier_phantom_rt_code_blob != nullptr, "");
+ return _load_reference_barrier_phantom_rt_code_blob;
+ }
+
protected:
virtual void store_at_resolved(LIRAccess& access, LIR_Opr value);
virtual LIR_Opr resolve_address(LIRAccess& access, bool resolve_in_register);
diff --git a/src/hotspot/share/gc/z/c1/zBarrierSetC1.hpp b/src/hotspot/share/gc/z/c1/zBarrierSetC1.hpp
index ec0881eec2e..58d7a7d2ae2 100644
--- a/src/hotspot/share/gc/z/c1/zBarrierSetC1.hpp
+++ b/src/hotspot/share/gc/z/c1/zBarrierSetC1.hpp
@@ -88,6 +88,7 @@ class ZStoreBarrierStubC1 : public CodeStub {
};
class ZBarrierSetC1 : public BarrierSetC1 {
+ friend class AOTCodeAddressTable;
private:
address _load_barrier_on_oop_field_preloaded_runtime_stub;
address _load_barrier_on_weak_oop_field_preloaded_runtime_stub;
diff --git a/src/hotspot/share/include/cds.h b/src/hotspot/share/include/cds.h
index 76de42db755..0068b9a1ec0 100644
--- a/src/hotspot/share/include/cds.h
+++ b/src/hotspot/share/include/cds.h
@@ -40,7 +40,7 @@
#define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8
#define CDS_PREIMAGE_ARCHIVE_MAGIC 0xcafea07c
#define CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION 13
-#define CURRENT_CDS_ARCHIVE_VERSION 20
+#define CURRENT_CDS_ARCHIVE_VERSION 21
typedef struct CDSFileMapRegion {
int _crc; // CRC checksum of this region.
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index 44208c22320..4560b2550dd 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -4159,6 +4159,10 @@ const char* InstanceKlass::init_state_name() const {
return state_names[init_state()];
}
+const char* InstanceKlass::state2name(ClassState s) {
+ return state_names[s];
+}
+
void InstanceKlass::print_class_flags(outputStream* st) const {
AccessFlags flags(compute_modifier_flags());
if (flags.is_public ()) st->print("public ");
@@ -4773,8 +4777,9 @@ void JNIid::verify(InstanceKlass* holder) {
void InstanceKlass::set_init_state(ClassState state) {
#ifdef ASSERT
+ // TODO enable: assert(state <= initialization_error, "only store known states");
bool good_state = in_aot_cache() ? (_init_state <= state)
- : (_init_state < state);
+ : (_init_state < state);
assert(good_state || state == allocated, "illegal state transition");
#endif
assert(_init_thread == nullptr, "should be cleared before state change");
diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp
index 1c9ddf84b76..0b20216aa49 100644
--- a/src/hotspot/share/oops/instanceKlass.hpp
+++ b/src/hotspot/share/oops/instanceKlass.hpp
@@ -601,6 +601,8 @@ class InstanceKlass: public Klass {
const char* init_state_name() const;
bool is_rewritten() const { return _misc_flags.rewritten(); }
+ static const char* state2name(ClassState state);
+
// is this a sealed class
bool is_sealed() const;
diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp
index 0bb1980333f..e0b76a9c640 100644
--- a/src/hotspot/share/oops/klass.cpp
+++ b/src/hotspot/share/oops/klass.cpp
@@ -23,6 +23,7 @@
*/
#include "cds/cdsConfig.hpp"
+#include "cds/cppVtables.hpp"
#include "cds/heapShared.inline.hpp"
#include "classfile/classLoader.hpp"
#include "classfile/classLoaderData.inline.hpp"
@@ -1112,6 +1113,23 @@ bool Klass::is_valid(Klass* k) {
return ClassLoaderDataGraph::is_valid(k->class_loader_data());
}
+#if INCLUDE_CDS
+// Check that this pointer is valid by checking that the vtbl pointer matches
+bool Klass::is_valid_aot_klass(const Klass* k) {
+ if (k == nullptr) {
+ return false;
+ } else if (!is_aligned(k, sizeof(MetaWord))) {
+ // Quick sanity check on pointer.
+ return false;
+ } else if (!os::is_readable_range(k, k + 1)) {
+ return false;
+ } else if (!k->in_aot_cache()) {
+ return false;
+ }
+ return CppVtables::is_valid_shared_klass(k);
+}
+#endif
+
Method* Klass::method_at_vtable(int index) {
#ifndef PRODUCT
assert(index >= 0, "valid vtable index");
diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp
index 88e12476983..f13daa8299d 100644
--- a/src/hotspot/share/oops/klass.hpp
+++ b/src/hotspot/share/oops/klass.hpp
@@ -814,6 +814,11 @@ class Klass : public Metadata {
// for error reporting
static bool is_valid(Klass* k);
+#if INCLUDE_CDS
+ // For klass in AOT cache
+ static bool is_valid_aot_klass(const Klass* k);
+#endif
+
static void on_secondary_supers_verification_failure(Klass* super, Klass* sub, bool linear_result, bool table_result, const char* msg);
};
diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp
index ce76c1dd82f..51dc5700a6d 100644
--- a/src/hotspot/share/oops/method.cpp
+++ b/src/hotspot/share/oops/method.cpp
@@ -429,9 +429,15 @@ Symbol* Method::klass_name() const {
}
void Method::metaspace_pointers_do(MetaspaceClosure* it) {
- log_trace(aot)("Iter(Method): %p", this);
-
- if (!method_holder()->is_rewritten() || Arguments::is_valhalla_enabled()) {
+ LogStreamHandle(Trace, aot) lsh;
+ if (lsh.is_enabled() && method_holder() != nullptr) {
+ lsh.print("Iter(Method): %p ", this);
+ print_external_name(&lsh);
+ lsh.cr();
+ }
+ // holder is null for MH intrinsic methods
+ if ((method_holder() != nullptr && !method_holder()->is_rewritten()) ||
+ Arguments::is_valhalla_enabled()) {
it->push(&_constMethod, MetaspaceClosure::_writable);
} else {
it->push(&_constMethod);
diff --git a/src/hotspot/share/oops/methodCounters.cpp b/src/hotspot/share/oops/methodCounters.cpp
index 8735758819f..03accf0f8f7 100644
--- a/src/hotspot/share/oops/methodCounters.cpp
+++ b/src/hotspot/share/oops/methodCounters.cpp
@@ -23,6 +23,7 @@
*/
#include "cds/cdsConfig.hpp"
+#include "code/aotCodeCache.hpp"
#include "compiler/compiler_globals.hpp"
#include "compiler/compilerOracle.hpp"
#include "memory/metaspaceClosure.hpp"
@@ -40,6 +41,9 @@ MethodCounters::MethodCounters(const methodHandle& mh) :
_highest_comp_level(0),
_highest_osr_comp_level(0)
{
+#if INCLUDE_CDS
+ set_aot_preload_code_entry(nullptr);
+#endif
set_interpreter_throwout_count(0);
JVMTI_ONLY(clear_number_of_breakpoints());
invocation_counter()->init();
@@ -73,6 +77,12 @@ MethodCounters* MethodCounters::allocate_with_exception(const methodHandle& mh,
void MethodCounters::clear_counters() {
invocation_counter()->reset();
backedge_counter()->reset();
+#if INCLUDE_CDS
+ if (aot_preload_code_entry() != nullptr) {
+ AOTCodeCache::invalidate(aot_preload_code_entry());
+ set_aot_preload_code_entry(nullptr);
+ }
+#endif
set_interpreter_throwout_count(0);
set_prev_time(0);
set_prev_event_count(0);
@@ -81,6 +91,15 @@ void MethodCounters::clear_counters() {
set_highest_osr_comp_level(0);
}
+#if INCLUDE_CDS
+void MethodCounters::deallocate_contents(ClassLoaderData* loader_data) {
+ if (aot_preload_code_entry() != nullptr) {
+ AOTCodeCache::invalidate(aot_preload_code_entry());
+ set_aot_preload_code_entry(nullptr);
+ }
+}
+#endif
+
void MethodCounters::metaspace_pointers_do(MetaspaceClosure* it) {
log_trace(aot, training)("Iter(MethodCounters): %p", this);
it->push(&_method);
@@ -88,6 +107,11 @@ void MethodCounters::metaspace_pointers_do(MetaspaceClosure* it) {
}
#if INCLUDE_CDS
+void MethodCounters::set_aot_preload_code_entry(AOTCodeEntry* entry) {
+ precond(entry == nullptr || entry->for_preload());
+ _aot_preload_code_entry = entry;
+}
+
void MethodCounters::remove_unshareable_info() {
}
void MethodCounters::restore_unshareable_info(TRAPS) {
diff --git a/src/hotspot/share/oops/methodCounters.hpp b/src/hotspot/share/oops/methodCounters.hpp
index 4e5ce51b7c1..35f96baa6e9 100644
--- a/src/hotspot/share/oops/methodCounters.hpp
+++ b/src/hotspot/share/oops/methodCounters.hpp
@@ -30,6 +30,7 @@
#include "oops/metadata.hpp"
#include "utilities/align.hpp"
+class AOTCodeEntry;
class MethodTrainingData;
class MethodCounters : public Metadata {
@@ -48,6 +49,15 @@ class MethodCounters : public Metadata {
Method* _method;
Metadata* _method_training_data;
+#if INCLUDE_CDS
+ // After JDK-8380476 FIXME: Jamming in a code reference here is a code
+ // smell. Since access to this value is infrequent, it does not
+ // need to be a pointer. (See AOTCodeCache::invalidate_entry.)
+ // Suggestion: Have finish_write build a two-column binary-search
+ // table in the AOT cache, to map method ID to preload method index.
+ // Searchable static indexes are a core competency for AOT.
+ AOTCodeEntry* _aot_preload_code_entry; // AOT Code Cache entry for preload code
+#endif
jlong _prev_time; // Previous time the rate was acquired
float _rate; // Events (invocation and backedge counter increments) per millisecond
int _invoke_mask; // per-method Tier0InvokeNotifyFreqLog
@@ -71,7 +81,7 @@ class MethodCounters : public Metadata {
static MethodCounters* allocate_no_exception(const methodHandle& mh);
static MethodCounters* allocate_with_exception(const methodHandle& mh, TRAPS);
- void deallocate_contents(ClassLoaderData* loader_data) {}
+ void deallocate_contents(ClassLoaderData* loader_data) NOT_CDS_RETURN;
static int method_counters_size() {
return align_up((int)sizeof(MethodCounters), wordSize) / wordSize;
@@ -165,6 +175,11 @@ class MethodCounters : public Metadata {
}
#if INCLUDE_CDS
+ void set_aot_preload_code_entry(AOTCodeEntry* entry);
+ AOTCodeEntry* aot_preload_code_entry() const {
+ return _aot_preload_code_entry;
+ }
+
void remove_unshareable_info();
void restore_unshareable_info(TRAPS);
#endif
diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp
index 6e35f05c9ce..f3bbacda4a8 100644
--- a/src/hotspot/share/oops/methodData.cpp
+++ b/src/hotspot/share/oops/methodData.cpp
@@ -1937,7 +1937,7 @@ void MethodData::remove_unshareable_info() {
}
void MethodData::restore_unshareable_info(TRAPS) {
- //_extra_data_lock = new Mutex(Mutex::nosafepoint, "MDOExtraData_lock");
+ // Do nothing. _extra_data_lock is created lazily.
}
#endif // INCLUDE_CDS
diff --git a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp
index 8c94058c660..f778c64cbe8 100644
--- a/src/hotspot/share/oops/trainingData.hpp
+++ b/src/hotspot/share/oops/trainingData.hpp
@@ -535,10 +535,76 @@ class CompileTrainingData : public TrainingData {
const bool _is_osr;
const int _compile_id;
+ // Size of nmethod's inlineable instructions during training
+ int _inline_instructions_size;
+
// classes that should be initialized before this JIT task runs
- DepList _init_deps;
+ DepList _init_deps; // set by JIT in TR, read by APH and PR
// Number of uninitialized classes left, when it's 0, all deps are satisfied
- volatile int _init_deps_left;
+ volatile int _init_deps_left; // set to deps length at AOT dump, counted down in PR
+
+ // The deps list and counter are crucial to gating the readiness of any nmethod
+ // in which clinit (class initialization) barriers have been optimized away.
+ // That is the difference between AP4 "preload" code (which has clinit barriers)
+ // and A4 code (which runs much faster, because it doesn't have them).
+ // The CTD::_init_deps list is built by a JIT task in the training run (TR),
+ // is consulted by the AOT compiler in the assembly phase (APH), and eventually
+ // is used again in the production run (PR) to ensure that the nmethod (for
+ // this CTD) gets loaded only after all its dependency classes have been
+ // initialized. The _init_deps_left down-counter counts the number of remaining
+ // not-yet-initialized classes on the deps-list; when it reaches zero, the
+ // nmethod can be loaded (from its AOTCodeEntry) by the ac1 or ac2 loader task.
+ //
+ // Like other metadata, the CTD is built in the TR and used in both APH and PR.
+ // Events from the TR JIT task are posted via ciObjectFactory::notice_new_object
+ // which calls notice_jit_observation, to add classes to the deps-list for the
+ // CTD associated with the TR JIT task. An AOT compiler task later recapitulates
+ // the TR JIT task, calling ciEnv::compute_init_state_for_aot_compile to read the
+ // deps-list of the same CTD. As the AOT cache is dumped, the classes on each CTD
+ // deps-list are polled by compute_init_deps_left, so each _init_deps_left
+ // down-counter is initialized to the number of classes on the deps-list.
+ //
+ // After each clinit, CompilationPolicy::replay_training_at_init and then
+ // KlassTrainingData::notice_fully_initialized poll each CTD that depends on the
+ // newly-initialized class. This is fast because the KTDs have an inverse
+ // list (comp_deps) that indexes back to CTDs that refer to them as init deps.
+ // The two functions cooperate to serialize the CTD polling activity.
+ // The first enqueues the class on the TrainingReplayQueue, while the second
+ // runs inside the unique TrainingReplayThread.
+ //
+ // The poll decrements the down-counter, and when it reaches zero, invokes
+ // the CompilationPolicy method maybe_compile_early on that CTD's method.
+ // The compilation policy rechecks each candidate method for suitability, and
+ // enqueues a CompileTask to handle the load from the AOTCodeEntry for the CTD
+ // which is ready. CompilationPolicy handles AOT loading as well, as part of its
+ // responsibility for the full lifecycle of compiled code.
+ //
+ // The effect is as if the PR quickly fast-forwards the JIT activity from the TR,
+ // replaying each "virtual JIT" task (i.e., AOT load) as soon as the clinit events
+ // occur, which each loaded method depends on. (Thus the term "replay training".)
+ // In addition, AP4 "preload" code, is queued for loading at startup, since it
+ // does not need to wait for any down counter or clinit event. This helps the
+ // class initializations get done sooner, compared with interpreter-based
+ // initialization.
+ //
+ // Note that AOTCodeEntries and their CTDs do not poll clinit deps-lists for
+ // loading. Rather, the class clinit events push notifications to CTDs, leading
+ // to queueing of load requests. By the time the load request is vetted and
+ // queued, the nmethod code is known to be ready to use. There are some late
+ // load-time checks (such as verify_c2_state and read_klass), which are of
+ // relatively minor importance, although they can prevent loading. At worst, the
+ // JVM will call in the JIT to cover for an unloadable method.
+ //
+ // A few special classes, like MethodType, are initialized within the AOT cache.
+ // These are present on the clinit deps-lists of many CTDs, but they will never be
+ // initialized in the PR. They contribute to the down-counter state via a special
+ // pass at startup called replay_training_at_init_for_preloaded_classes. This
+ // pass finds such pre-initialized classes and calls replay_training_at_init for
+ // them, since they will never have a normal clinit. In effect, their class
+ // initialization actions, including CTD down-counts, are fast-forwarded at
+ // startup. See has_aot_initialized_mirror, which recognizes such classes.
+ // Whether a class is AOT-initialized or not, if it has CTDs depending on it, it
+ // polls them exactly once, gated by the latch has_init_deps_processed.
public:
// ciRecords is a generic meachanism to memoize CI responses to arbitary queries. For each function we're interested in we record
@@ -652,7 +718,7 @@ class CompileTrainingData : public TrainingData {
bool is_osr,
int compile_id)
: TrainingData(), // empty key
- _method(mtd), _level(level), _is_osr(is_osr), _compile_id(compile_id), _init_deps_left(0) { }
+ _method(mtd), _level(level), _is_osr(is_osr), _compile_id(compile_id), _inline_instructions_size(0), _init_deps_left(0) { }
public:
ciRecords& ci_records() { return _ci_records; }
static CompileTrainingData* make(CompileTask* task) NOT_CDS_RETURN_(nullptr);
@@ -665,6 +731,9 @@ class CompileTrainingData : public TrainingData {
bool is_osr() const { return _is_osr; }
int compile_id() const { return _compile_id; }
+ int inline_instructions_size() const { return _inline_instructions_size; }
+ void set_inline_instructions_size(int size) { _inline_instructions_size = size; }
+
int init_dep_count() const {
TrainingDataLocker::assert_locked();
return _init_deps.length();
@@ -794,6 +863,15 @@ class MethodTrainingData : public TrainingData {
return nullptr;
}
+ CompileTrainingData* compile_data_for_aot_code(int level) const {
+ CompileTrainingData* ctd = last_toplevel_compile(level);
+ if (ctd == nullptr && level == CompLevel_limited_profile) {
+ // We compile CompLevel_limited_profile AOT code for CompLevel_full_profile
+ ctd = _last_toplevel_compiles[CompLevel_full_profile - 1];
+ }
+ return ctd;
+ }
+
void notice_compilation(int level, bool inlined = false) {
if (!inlined) {
_was_toplevel = true;
diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp
index 7741d420a46..083a3c94d48 100644
--- a/src/hotspot/share/opto/c2_globals.hpp
+++ b/src/hotspot/share/opto/c2_globals.hpp
@@ -935,6 +935,9 @@
"Use StoreStore barrier instead of Release barrier at the end " \
"of constructors") \
\
+ product(bool, PreloadReduceTraps, true, DIAGNOSTIC, \
+ "Preload code should avoid traps as much as possible.") \
+ \
develop(bool, KillPathsReachableByDeadTypeNode, true, \
"When a Type node becomes top, make paths where the node is " \
"used dead by replacing them with a Halt node. Turning this off " \
@@ -993,6 +996,20 @@
product(uint, HotCodeCallLevel, 1, EXPERIMENTAL, \
"Number of levels of callees to relocate per candidate") \
range(0, max_juint) \
+ \
+ /* Next three AOT code flags are used only during assembly phase */ \
+ /* for tier4 AOT compilation and they are ignored in other phases */ \
+ \
+ product(double, AOTCodeInvokeBase, 100.0, DIAGNOSTIC, \
+ "AOT code invocation base limit") \
+ range(1.0, 10000.0) \
+ \
+ product(double, AOTCodeInvokeScale, 1.0, DIAGNOSTIC, \
+ "scale AOT code invocation limit") \
+ range(0.001, 1000.0) \
+ \
+ product(bool, UseAOTCodeCounters, true, DIAGNOSTIC, \
+ "Use AOT code counter to trigger JIT compilation") \
// end of C2_FLAGS
diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp
index 580a4e72cbf..e836629cfbb 100644
--- a/src/hotspot/share/opto/c2compiler.cpp
+++ b/src/hotspot/share/opto/c2compiler.cpp
@@ -24,6 +24,7 @@
#include "classfile/vmClasses.hpp"
#include "classfile/vmIntrinsics.hpp"
+#include "code/aotCodeCache.hpp"
#include "compiler/compilationMemoryStatistic.hpp"
#include "compiler/compilerDefinitions.inline.hpp"
#include "jfr/support/jfrIntrinsics.hpp"
@@ -127,8 +128,15 @@ void C2Compiler::initialize() {
void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, bool install_code, DirectiveSet* directive) {
assert(is_initialized(), "Compiler thread must be initialized");
-
CompilationMemoryStatisticMark cmsm(directive);
+ CompileTask* task = env->task();
+ if (task->is_aot_load()) {
+ assert(install_code, "AOT code loading requires install_code");
+ AOTCodeCache::load_nmethod(env, target, entry_bci, this);
+ // We want to go quickly through AOT code load requests
+ // instead of spending time on normal compilation.
+ return;
+ }
bool subsume_loads = SubsumeLoads;
bool do_escape_analysis = DoEscapeAnalysis;
@@ -138,6 +146,8 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, boo
bool do_locks_coarsening = EliminateLocks;
bool do_superword = UseSuperWord;
bool do_stringopts = OptimizeStringConcat;
+ bool for_aot_preload = (task->compile_reason() == CompileTask::Reason_AOTCompileForPreload);
+ assert(!for_aot_preload || (ClassInitBarrierMode > 0), "sanity");
while (!env->failing()) {
ResourceMark rm;
@@ -150,6 +160,7 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, boo
do_locks_coarsening,
do_superword,
do_stringopts,
+ for_aot_preload,
install_code);
Compile C(env, target, entry_bci, options, directive);
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index b698172b801..b238fb91336 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -605,10 +605,15 @@ void Compile::print_compile_messages() {
if( PrintOpto ) {
if (is_osr_compilation()) {
- tty->print("[OSR]%3d", _compile_id);
- } else {
- tty->print("%3d", _compile_id);
+ tty->print("[OSR]");
+ } else if (env()->task()->is_aot_compile()) {
+ if (for_aot_preload()) {
+ tty->print("[PRE]");
+ } else {
+ tty->print("[AOT]");
+ }
}
+ tty->print("%3d", _compile_id);
}
#endif
}
@@ -644,9 +649,13 @@ void Compile::print_ideal_ir(const char* compile_phase_name) const {
NoSafepointVerifier nsv;
ttyLocker ttyl;
if (xtty != nullptr) {
+ CompileTask* task = env()->task();
+ bool aot_comp = task->is_aot_load_or_compile();
+ bool aot_preload_comp = task->is_aot_preload_or_compile();
xtty->head("ideal compile_id='%d'%s compile_phase='%s'",
compile_id(),
- is_osr_compilation() ? " compile_kind='osr'" : "",
+ is_osr_compilation() ? " compile_kind='osr'" :
+ (aot_preload_comp ? " compile_kind='AP'" : (aot_comp ? " compile_kind='A'" : "")),
compile_phase_name);
}
@@ -693,6 +702,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci,
_trace_opto_output(directive->TraceOptoOutputOption),
#endif
_clinit_barrier_on_entry(false),
+ _has_clinit_barriers(false),
_comp_arena(mtCompiler, Arena::Tag::tag_comp),
_barrier_set_state(BarrierSet::barrier_set()->barrier_set_c2()->create_barrier_state(comp_arena())),
_env(ci_env),
@@ -776,9 +786,14 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci,
set_print_intrinsics(directive->PrintIntrinsicsOption);
set_has_irreducible_loop(true); // conservative until build_loop_tree() reset it
- if (ProfileTraps) {
+ if ((ProfileTraps && !ci_env->is_aot_compile()) ||
+ AOTCodeCache::is_using_code()) {
// Make sure the method being compiled gets its own MDO,
// so we can at least track the decompile_count().
+ // Load recorded MDO from training run when AOT code
+ // is used - MDO may not be created yet in such case.
+ // No need for AOT compilation - it is not
+ // executed during assembly phase.
method()->ensure_method_data();
}
@@ -1003,6 +1018,7 @@ Compile::Compile(ciEnv* ci_env,
_trace_opto_output(directive->TraceOptoOutputOption),
#endif
_clinit_barrier_on_entry(false),
+ _has_clinit_barriers(false),
_comp_arena(mtCompiler, Arena::Tag::tag_comp),
_barrier_set_state(BarrierSet::barrier_set()->barrier_set_c2()->create_barrier_state(comp_arena())),
_env(ci_env),
@@ -1181,8 +1197,12 @@ void Compile::Init(bool aliasing) {
_max_node_limit = _directive->MaxNodeLimitOption;
- if (VM_Version::supports_fast_class_init_checks() && has_method() && !is_osr_compilation() && method()->needs_clinit_barrier()) {
+ if (VM_Version::supports_fast_class_init_checks() && has_method() && !is_osr_compilation() &&
+ (method()->needs_clinit_barrier() || (do_clinit_barriers() && method()->is_static()))) {
set_clinit_barrier_on_entry(true);
+ if (do_clinit_barriers()) {
+ set_has_clinit_barriers(true); // Entry clinit barrier is in prolog code.
+ }
}
if (debug_info()->recording_non_safepoints()) {
set_node_note_array(new(comp_arena()) GrowableArray
@@ -4994,6 +5014,10 @@ bool Compile::final_graph_reshaping() {
bool Compile::too_many_traps(ciMethod* method,
int bci,
Deoptimization::DeoptReason reason) {
+ if (PreloadReduceTraps && for_aot_preload()) {
+ // Preload code should not have traps, if possible.
+ return true;
+ }
assert(reason > Deoptimization::Reason_none && reason <= Deoptimization::Reason_LIMIT, "invalid reason");
ciMethodData* md = method->method_data();
if (md->is_empty()) {
@@ -5020,6 +5044,10 @@ bool Compile::too_many_traps(ciMethod* method,
// Less-accurate variant which does not require a method and bci.
bool Compile::too_many_traps(Deoptimization::DeoptReason reason,
ciMethodData* logmd) {
+ if (PreloadReduceTraps && for_aot_preload()) {
+ // Preload code should not have traps, if possible.
+ return true;
+ }
assert(reason > Deoptimization::Reason_none && reason <= Deoptimization::Reason_LIMIT, "invalid reason");
if (trap_count(reason) >= Deoptimization::per_method_trap_limit(reason)) {
// Too many traps globally.
@@ -5111,10 +5139,10 @@ bool Compile::needs_clinit_barrier(ciField* field, ciMethod* accessing_method) {
}
bool Compile::needs_clinit_barrier(ciInstanceKlass* holder, ciMethod* accessing_method) {
- if (holder->is_initialized()) {
+ if (holder->is_initialized() && !do_clinit_barriers()) {
return false;
}
- if (holder->is_being_initialized()) {
+ if (holder->is_being_initialized() || do_clinit_barriers()) {
if (accessing_method->holder() == holder) {
// Access inside a class. The barrier can be elided when access happens in ,
// , or a static method. In all those cases, there was an initialization
diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp
index 44077c9386f..008c6bd9a84 100644
--- a/src/hotspot/share/opto/compile.hpp
+++ b/src/hotspot/share/opto/compile.hpp
@@ -189,6 +189,8 @@ class Options {
const bool _do_locks_coarsening; // Do locks coarsening
const bool _do_superword; // Do SuperWord
const bool _do_stringopts; // Do StringOpts
+ const bool _for_aot_preload; // Generate AOT code for preload (before Java method execution),
+ // include class init barriers
const bool _install_code; // Install the code that was compiled
public:
Options(bool subsume_loads,
@@ -199,6 +201,7 @@ class Options {
bool do_locks_coarsening,
bool do_superword,
bool do_stringopts,
+ bool for_aot_preload,
bool install_code) :
_subsume_loads(subsume_loads),
_do_escape_analysis(do_escape_analysis),
@@ -208,6 +211,7 @@ class Options {
_do_locks_coarsening(do_locks_coarsening),
_do_superword(do_superword),
_do_stringopts(do_stringopts),
+ _for_aot_preload(for_aot_preload),
_install_code(install_code) {
}
@@ -221,6 +225,7 @@ class Options {
/* do_lock_coarsening = */ false,
/* do_superword = */ true,
/* do_stringopts = */ false,
+ /* for_aot_preload = */ false,
/* install_code = */ true
);
}
@@ -380,6 +385,7 @@ class Compile : public Phase {
bool _has_monitors; // Metadata transfered to nmethod to enable Continuations lock-detection fastpath
bool _has_scoped_access; // For shared scope closure
bool _clinit_barrier_on_entry; // True if clinit barrier is needed on nmethod entry
+ bool _has_clinit_barriers; // True if compiled code has clinit barriers
int _loop_opts_cnt; // loop opts round
bool _has_flat_accesses; // Any known flat array accesses?
bool _flat_accesses_share_alias; // Initially all flat array share a single slice
@@ -606,6 +612,9 @@ class Compile : public Phase {
bool do_superword() const { return _options._do_superword; }
bool do_stringopts() const { return _options._do_stringopts; }
+ bool do_clinit_barriers() const { return _options._for_aot_preload; }
+ bool for_aot_preload() const { return _options._for_aot_preload; }
+
// Other fixed compilation parameters.
ciMethod* method() const { return _method; }
int entry_bci() const { return _entry_bci; }
@@ -703,6 +712,8 @@ class Compile : public Phase {
void set_has_monitors(bool v) { _has_monitors = v; }
bool has_scoped_access() const { return _has_scoped_access; }
void set_has_scoped_access(bool v) { _has_scoped_access = v; }
+ bool has_clinit_barriers()const { return _has_clinit_barriers; }
+ void set_has_clinit_barriers(bool z) { _has_clinit_barriers = z; }
// check the CompilerOracle for special behaviours for this compile
bool method_has_option(CompileCommandEnum option) const {
diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp
index 732a2f2fe2f..28a727202d2 100644
--- a/src/hotspot/share/opto/doCall.cpp
+++ b/src/hotspot/share/opto/doCall.cpp
@@ -27,6 +27,7 @@
#include "ci/ciSymbols.hpp"
#include "classfile/vmIntrinsics.hpp"
#include "classfile/vmSymbols.hpp"
+#include "code/aotCodeCache.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
#include "interpreter/linkResolver.hpp"
@@ -113,6 +114,14 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
allow_inline = false;
}
+ if (AOTCodeCache::is_using_code()) {
+ // During production run, when AOT code is used,
+ // C2 compilation could be requested without collecting
+ // profiling. Instead use profiling from training run.
+ // Make sure training data is loaded for callee.
+ callee->ensure_method_data(true /*training_data_only*/);
+ }
+
// Note: When we get profiling during stage-1 compiles, we want to pull
// from more specific profile data which pertains to this inlining.
// Right now, ignore the information in jvms->caller(), and do method[bci].
@@ -533,6 +542,10 @@ bool Parse::can_not_compile_call_site(ciMethod *dest_method, ciInstanceKlass* kl
if (!holder_klass->is_being_initialized() &&
!holder_klass->is_initialized() &&
!holder_klass->is_interface()) {
+ if (C->env()->task()->is_aot_compile()) {
+ ResourceMark rm;
+ log_debug(aot, compilation)("Emitting uncommon trap (cannot compile call site) in AOT code for %s", holder_klass->name()->as_klass_external_name());
+ }
uncommon_trap(Deoptimization::Reason_uninitialized,
Deoptimization::Action_reinterpret,
holder_klass);
@@ -599,6 +612,20 @@ void Parse::do_call() {
return;
}
assert(holder_klass->is_loaded(), "");
+
+ if (bc() == Bytecodes::_invokestatic && C->do_clinit_barriers() &&
+ C->needs_clinit_barrier(holder_klass, method())) {
+ // Generate class init barrier for static method in AOT "preload" code.
+ clinit_barrier(holder_klass, method());
+ if (stopped()) {
+ // Constant folding of class initialization state check is not expected here.
+ // For AOT "preload" code all instance classes are considered initialized.
+ // See Reason_AOTCompileForPreload case in ciEnv::compute_init_state_for_aot_compile().
+ // But check it anyway to be safe.
+ return; // MUST uncommon-trap?
+ }
+ }
+
//assert((bc_callee->is_static() || is_invokedynamic) == !has_receiver , "must match bc"); // XXX invokehandle (cur_bc_raw)
// Note: this takes into account invokeinterface of methods declared in java/lang/Object,
// which should be invokevirtuals but according to the VM spec may be invokeinterfaces
diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp
index fd9f7090afd..54887860531 100644
--- a/src/hotspot/share/opto/graphKit.cpp
+++ b/src/hotspot/share/opto/graphKit.cpp
@@ -2512,6 +2512,15 @@ Node* GraphKit::uncommon_trap(int trap_request,
trap_request), bci());
}
+ if (PreloadReduceTraps && C->for_aot_preload() && (action != Deoptimization::Action_none)) {
+ ResourceMark rm;
+ ciMethod* cim = C->method();
+ log_debug(aot, codecache, deoptimization)("Uncommon trap in AOT preload code: reason=%s action=%s method=%s::%s bci=%d, %s",
+ Deoptimization::trap_reason_name(reason), Deoptimization::trap_action_name(action),
+ cim->holder()->name()->as_klass_external_name(), cim->name()->as_klass_external_name(),
+ bci(), comment);
+ }
+
CompileLog* log = C->log();
if (log != nullptr) {
int kid = (klass == nullptr)? -1: log->identify(klass);
@@ -3415,6 +3424,32 @@ bool GraphKit::seems_never_null(Node* obj, ciProfileData* data, bool& speculatin
return false;
}
+void GraphKit::guard_klass_is_initialized(Node* klass) {
+ assert(C->do_clinit_barriers(), "should be called only for clinit barriers");
+ if (ClassInitBarrierMode == 0) {
+ return;
+ }
+ precond(ClassInitBarrierMode == 1); // catch new value
+ int init_state_off = in_bytes(InstanceKlass::init_state_offset());
+ Node* adr = basic_plus_adr(top(), klass, init_state_off);
+ Node* init_state = LoadNode::make(_gvn, nullptr, immutable_memory(), adr,
+ adr->bottom_type()->is_ptr(), TypeInt::BYTE,
+ T_BYTE, MemNode::acquire);
+ init_state = _gvn.transform(init_state);
+
+ Node* initialized_state = makecon(TypeInt::make(InstanceKlass::fully_initialized));
+
+ Node* chk = _gvn.transform(new CmpINode(initialized_state, init_state));
+ Node* tst = _gvn.transform(new BoolNode(chk, BoolTest::eq));
+
+ { // uncommon trap on slow path
+ BuildCutout unless(this, tst, PROB_MAX);
+ // Do not deoptimize this nmethod. Go to Interpreter to initialize class.
+ uncommon_trap(Deoptimization::Reason_uninitialized, Deoptimization::Action_none);
+ }
+ C->set_has_clinit_barriers(true);
+}
+
void GraphKit::guard_klass_being_initialized(Node* klass) {
int init_state_off = in_bytes(InstanceKlass::init_state_offset());
Node* adr = off_heap_plus_addr(klass, init_state_off);
@@ -3453,9 +3488,14 @@ void GraphKit::guard_init_thread(Node* klass) {
}
void GraphKit::clinit_barrier(ciInstanceKlass* ik, ciMethod* context) {
+ if (C->do_clinit_barriers()) {
+ Node* klass = makecon(TypeKlassPtr::make(ik, Type::trust_interfaces));
+ guard_klass_is_initialized(klass);
+ return;
+ }
if (ik->is_being_initialized()) {
if (C->needs_clinit_barrier(ik, context)) {
- Node* klass = makecon(TypeKlassPtr::make(ik));
+ Node* klass = makecon(TypeKlassPtr::make(ik, Type::trust_interfaces));
guard_klass_being_initialized(klass);
guard_init_thread(klass);
insert_mem_bar(Op_MemBarCPUOrder);
@@ -3463,6 +3503,10 @@ void GraphKit::clinit_barrier(ciInstanceKlass* ik, ciMethod* context) {
} else if (ik->is_initialized()) {
return; // no barrier needed
} else {
+ if (C->env()->task()->is_aot_compile()) {
+ ResourceMark rm;
+ log_debug(aot, compilation)("Emitting uncommon trap (clinit barrier) in AOT code for %s", ik->name()->as_klass_external_name());
+ }
uncommon_trap(Deoptimization::Reason_uninitialized,
Deoptimization::Action_reinterpret,
nullptr);
diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp
index 88cf7484446..ae5aa75f5e0 100644
--- a/src/hotspot/share/opto/graphKit.hpp
+++ b/src/hotspot/share/opto/graphKit.hpp
@@ -433,6 +433,7 @@ class GraphKit : public Phase {
// Check the null_seen bit.
bool seems_never_null(Node* obj, ciProfileData* data, bool& speculating);
+ void guard_klass_is_initialized(Node* klass);
void guard_klass_being_initialized(Node* klass);
void guard_init_thread(Node* klass);
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index 72043317b37..9f30ff1c4f1 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -3256,7 +3256,10 @@ bool LibraryCallKit::inline_onspinwait() {
return true;
}
-bool LibraryCallKit::klass_needs_init_guard(Node* kls) {
+bool LibraryCallKit::klass_needs_init_guard(Node* kls) const {
+ if (C->do_clinit_barriers()) {
+ return true; // Generate guard in AOT preload code
+ }
if (!kls->is_Con()) {
return true;
}
diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp
index 2978c3720e3..87ce134747d 100644
--- a/src/hotspot/share/opto/library_call.hpp
+++ b/src/hotspot/share/opto/library_call.hpp
@@ -264,7 +264,7 @@ class LibraryCallKit : public GraphKit {
DecoratorSet mo_decorator_for_access_kind(AccessKind kind);
bool inline_unsafe_access(bool is_store, BasicType type, AccessKind kind, bool is_unaligned);
bool inline_unsafe_flat_access(bool is_store, AccessKind kind);
- static bool klass_needs_init_guard(Node* kls);
+ bool klass_needs_init_guard(Node* kls) const;
bool inline_unsafe_allocate();
bool inline_unsafe_newArray(bool uninitialized);
bool inline_newArray(bool null_free, bool atomic);
diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp
index 48725ad405d..1bb72826531 100644
--- a/src/hotspot/share/opto/memnode.cpp
+++ b/src/hotspot/share/opto/memnode.cpp
@@ -29,6 +29,7 @@
#include "classfile/javaClasses.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmIntrinsics.hpp"
+#include "code/aotCodeCache.hpp"
#include "compiler/compileLog.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/c2/barrierSetC2.hpp"
@@ -2290,7 +2291,9 @@ Node* LoadNode::Ideal_load_common(PhaseGVN* phase, bool can_reshape) {
const Type*
LoadNode::load_array_final_field(const TypeKlassPtr *tkls,
ciKlass* klass) const {
- assert(!UseCompactObjectHeaders || tkls->offset() != in_bytes(Klass::prototype_header_offset()),
+ assert(!UseCompactObjectHeaders ||
+ AOTCodeCache::is_on_for_dump() ||
+ tkls->offset() != in_bytes(Klass::prototype_header_offset()),
"must not happen");
if (tkls->isa_instklassptr() && tkls->offset() == in_bytes(InstanceKlass::access_flags_offset())) {
@@ -2512,10 +2515,14 @@ const Type* LoadNode::Value(PhaseGVN* phase) const {
assert(Opcode() == Op_LoadI, "must load an int from _layout_kind");
return TypeInt::make(static_cast(klass->as_flat_array_klass()->layout_kind()));
}
- if (UseCompactObjectHeaders && tkls->offset() == in_bytes(Klass::prototype_header_offset())) {
- // The field is Klass::_prototype_header. Return its (constant) value.
- assert(this->Opcode() == Op_LoadX, "must load a proper type from _prototype_header");
- return TypeX::make(klass->prototype_header());
+ // Class encoding and some class's values may change between runs.
+ // Force loading them when AOT code is generated.
+ if (!AOTCodeCache::is_on_for_dump()) {
+ if (UseCompactObjectHeaders && tkls->offset() == in_bytes(Klass::prototype_header_offset())) {
+ // The field is Klass::_prototype_header. Return its (constant) value.
+ assert(this->Opcode() == Op_LoadX, "must load a proper type from _prototype_header");
+ return TypeX::make(klass->prototype_header());
+ }
}
// Compute index into primary_supers array
juint depth = (tkls->offset() - in_bytes(Klass::primary_supers_offset())) / sizeof(Klass*);
diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp
index de7dcbae771..6f480f75454 100644
--- a/src/hotspot/share/opto/output.cpp
+++ b/src/hotspot/share/opto/output.cpp
@@ -913,6 +913,7 @@ void PhaseOutput::FillLocArray( int idx, MachSafePointNode* sfpt, Node *local,
} else if (t->base() == Type::VectorA || t->base() == Type::VectorS ||
t->base() == Type::VectorD || t->base() == Type::VectorX ||
t->base() == Type::VectorY || t->base() == Type::VectorZ) {
+ C->debug_info()->set_has_vectors();
array->append(new_loc_value( C->regalloc(), regnum, Location::vector ));
} else if (C->regalloc()->is_oop(local)) {
assert(t->base() == Type::OopPtr || t->base() == Type::InstPtr ||
@@ -1903,11 +1904,7 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) {
#if defined(SUPPORT_OPTO_ASSEMBLY)
// Dump the assembly code, including basic-block numbers
if (C->print_assembly()) {
- ttyLocker ttyl; // keep the following output all in one block
- if (!VMThread::should_terminate()) { // test this under the tty lock
- // print_metadata and dump_asm may safepoint which makes us loose the ttylock.
- // We call them first and write to a stringStream, then we retake the lock to
- // make sure the end tag is coherent, and that xmlStream->pop_tag is done thread safe.
+ if (!VMThread::should_terminate()) {
ResourceMark rm;
stringStream method_metadata_str;
if (C->method() != nullptr) {
@@ -1916,23 +1913,29 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) {
stringStream dump_asm_str;
dump_asm_on(&dump_asm_str, node_offsets, node_offset_limit);
+ // Make sure the end tag is coherent, and that xmlStream->pop_tag is done thread safe.
NoSafepointVerifier nsv;
- ttyLocker ttyl2;
+ ttyLocker ttyl;
// This output goes directly to the tty, not the compiler log.
// To enable tools to match it up with the compilation activity,
// be sure to tag this tty output with the compile ID.
if (xtty != nullptr) {
+ CompileTask* task = C->env()->task();
+ bool aot_comp = task->is_aot_load_or_compile();
+ bool aot_preload_comp = task->is_aot_preload_or_compile();
xtty->head("opto_assembly compile_id='%d'%s", C->compile_id(),
- C->is_osr_compilation() ? " compile_kind='osr'" : "");
+ C->is_osr_compilation() ? " compile_kind='osr'" :
+ (aot_preload_comp ? " compile_kind='AP'" : (aot_comp ? " compile_kind='A'" : "")));
}
+ const char* is_aot = C->env()->is_aot_compile() ? (C->for_aot_preload() ? "(AP) " : "(A) -") : "-----";
if (C->method() != nullptr) {
- tty->print_cr("----------------------- MetaData before Compile_id = %d ------------------------", C->compile_id());
+ tty->print_cr("----------------------- MetaData before Compile_id = %d %s-------------------", C->compile_id(), is_aot);
tty->print_raw(method_metadata_str.freeze());
} else if (C->stub_name() != nullptr) {
tty->print_cr("----------------------------- RuntimeStub %s -------------------------------", C->stub_name());
}
tty->cr();
- tty->print_cr("------------------------ OptoAssembly for Compile_id = %d -----------------------", C->compile_id());
+ tty->print_cr("------------------------ OptoAssembly for Compile_id = %d %s------------------", C->compile_id(), is_aot);
tty->print_raw(dump_asm_str.freeze());
tty->print_cr("--------------------------------------------------------------------------------");
if (xtty != nullptr) {
@@ -3305,9 +3308,7 @@ uint PhaseOutput::scratch_emit_size(const Node* n) {
}
void PhaseOutput::install() {
- if (!C->should_install_code()) {
- return;
- } else if (C->stub_function() != nullptr) {
+ if (C->should_install_code() && C->stub_function() != nullptr) {
install_stub(C->stub_name());
} else {
install_code(C->method(),
@@ -3360,15 +3361,19 @@ void PhaseOutput::install_code(ciMethod* target,
&_handler_table,
inc_table(),
compiler,
+ C->has_clinit_barriers(),
+ C->for_aot_preload(),
has_unsafe_access,
SharedRuntime::is_wide_vector(C->max_vector_size()),
C->has_monitors(),
C->has_scoped_access(),
- 0);
+ 0,
+ C->should_install_code());
if (C->log() != nullptr) { // Print code cache state into compiler log
C->log()->code_cache_state();
}
+ assert(!C->has_clinit_barriers() || C->for_aot_preload(), "class init barriers should be only in preload code");
}
}
void PhaseOutput::install_stub(const char* stub_name) {
diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp
index e7b4ed2f0b1..e2160955c17 100644
--- a/src/hotspot/share/opto/parse.hpp
+++ b/src/hotspot/share/opto/parse.hpp
@@ -608,6 +608,9 @@ class Parse : public GraphKit {
// helper function for call statistics
void count_compiled_calls(bool at_method_entry, bool is_inline) PRODUCT_RETURN;
+ // AOT compiled code invocations count
+ void count_aot_code_calls() NOT_CDS_RETURN;
+
Node_Notes* make_node_notes(Node_Notes* caller_nn);
// Helper functions for handling normal and abnormal exits.
@@ -638,8 +641,12 @@ class Parse : public GraphKit {
void stress_trap(IfNode* orig_iff, Node* counter, Node* incr_store);
// Increment counter used by StressUnstableIfTraps
void increment_trap_stress_counter(Node*& counter, Node*& incr_store);
+ static volatile int _trap_stress_counter;
public:
+ // Needed for AOT external address recording
+ static address trap_stress_counter_address() { return (address)&_trap_stress_counter; }
+
#ifndef PRODUCT
// Handle PrintOpto, etc.
void show_parse_info();
diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp
index 08d5fdb8e22..984ad391dc0 100644
--- a/src/hotspot/share/opto/parse1.cpp
+++ b/src/hotspot/share/opto/parse1.cpp
@@ -26,9 +26,11 @@
#include "ci/ciSignature.hpp"
#include "ci/ciTypeFlow.hpp"
#include "compiler/compileLog.hpp"
+#include "interpreter/invocationCounter.hpp"
#include "interpreter/linkResolver.hpp"
#include "memory/resourceArea.hpp"
#include "oops/method.hpp"
+#include "oops/trainingData.hpp"
#include "opto/addnode.hpp"
#include "opto/c2compiler.hpp"
#include "opto/castnode.hpp"
@@ -1226,9 +1228,11 @@ SafePointNode* Parse::create_entry_map() {
_caller->map()->delete_replaced_nodes();
// If this is an inlined method, we may have to do a receiver null check.
- if (_caller->has_method() && is_normal_parse() && !method()->is_static()) {
+ if (_caller->has_method() && is_normal_parse()) {
GraphKit kit(_caller);
- kit.null_check_receiver_before_call(method());
+ if (!method()->is_static()) {
+ kit.null_check_receiver_before_call(method());
+ }
_caller = kit.transfer_exceptions_into_jvms();
if (kit.stopped()) {
@@ -1297,6 +1301,149 @@ static bool is_auto_boxed_primitive(Node* n) {
n->in(0)->as_CallJava()->method()->is_boxing_method());
}
+#if INCLUDE_CDS
+// Even the highest tier of AOT code (A4) incorporates an invocation
+// counter on entry to an AOT nmethod. This is necessary because AOT
+// optimizations are performed on less accurate information than JIT
+// optimizations. A long-running JVM is likely to eventually
+// recompile AOT code with the JIT compiler, so as to reoptimize with
+// the fullest possible information. This helps AOT-assisted
+// workloads attain the same peak performance as pure JIT-based
+// workloads.
+//
+// One example of "less accurate" information for optimization is a
+// static final field value which the AOT compiler refuses to fold as
+// a constant, because many static finals cannot be predicted AOT.
+// Another is the exact configuration of block and type profiles; the
+// AOT compiler only sees profile information from the training run up
+// to the point at which the AOT cache was built, while a production
+// run may execute much longer, eventually adding very different
+// information to the profile.
+//
+// The counter helps decide when to reoptimize. The counter limit
+// chosen depends on the invocation count observed during the training
+// run. The limit is an approximately linear function of the observed
+// count, but at least about a hundred (see AOTCodeInvokeBase), and
+// saturating out to about a million (times AOTCodeInvokeScale).
+//
+// The diagnostic flag UseAOTCodeCounters can be used to determine
+// whether the counter overhead affected application performance.
+// Note that the counter is only on the root method of any given
+// nmethod. Inlined method calls are not counted. Counting overhead
+// is therefore expected to be small.
+//
+// After JDK-8380476 TODO: The interpreter also uses a backedge counter
+// to ensure eventual optimization of a method with a small invocation
+// count that sits in a long-running loop. A4 methods could be given an
+// equivalent counter, if they contain loops which may turn out to be
+// long-running. It is help to deoptimize a long-running loop to the
+// interpreter, without replacing the A4 method. The interpreter
+// could be primed with a high backedge count, so that the loop will
+// be reoptimized quickly by an OSR compilation. This whole concern
+// is a corner case of a corner case, since OSR assumes low invocation
+// count, while reoptimization to peak assumes a long-lived production
+// run.
+
+static int scale_limit(int64_t limit) {
+ // To scale invocation limit a hyperbolic saturation curve formula
+ // is used with upper limit 1M.
+ return (int)(AOTCodeInvokeBase + limit / (1.0 + limit / (1000000.0 * AOTCodeInvokeScale)));
+}
+
+void Parse::count_aot_code_calls() {
+ bool is_aot_compilation = C->env()->is_aot_compile();
+ if (UseAOTCodeCounters && (depth() == 1) && is_aot_compilation) {
+ // Clear out dead values from the debug info in following runtime call
+ kill_dead_locals();
+
+ // Use method invocations count during training run and compare
+ // to invocations of AOT code during production run to trigger JIT
+ // compilation and replace AOT code with normal JITed code.
+ ciMetadata* mcp = method()->ensure_method_counters();
+ assert(mcp != nullptr, "CompileBroker should create MethodCounters if it is missing");
+ const TypePtr* mc_type = TypeMetadataPtr::make(TypePtr::Constant, mcp, Type::Offset(0));
+ Node* mc = makecon(mc_type);
+
+ assert(MethodTrainingData::have_data(), "TrainingData should be present for AOT compilation");
+ methodHandle mh(Thread::current(), method()->get_Method());
+ MethodTrainingData* mtd = MethodTrainingData::find_fast(mh);
+ assert(mtd != nullptr, "AOT compiled method should have MethodTrainingData");
+ int64_t limit = mtd->invocation_count();
+ int step = InvocationCounter::count_increment;
+ int scaled_limit = step * scale_limit(limit);
+
+ // Count AOT compiled code invocations (use 32 bits because scaled limit fits into 32 bits)
+ intptr_t offset = in_bytes(MethodCounters::invocation_counter_offset());
+ Node* cnt_adr = off_heap_plus_addr(mc, offset);
+ Node* ctrl = control();
+ Node* cnt = make_load(ctrl, cnt_adr, TypeInt::INT, T_INT, MemNode::unordered);
+ Node* incr = _gvn.transform(new AddINode(cnt, intcon(step)));
+ store_to_memory(ctrl, cnt_adr, incr, T_INT, MemNode::unordered);
+ // Preserve memory for Phi node below
+ Node* st_mem = MergeMemNode::make(map()->memory());
+ _gvn.set_type_bottom(st_mem);
+
+ Node* lim = intcon(scaled_limit);
+ Node* chk = _gvn.transform( new CmpINode(incr, lim) );
+ Node* tst = _gvn.transform( new BoolNode(chk, BoolTest::lt) );
+ IfNode* iff = create_and_map_if(control(), tst, PROB_ALWAYS, (float)limit);
+
+ RegionNode* result_rgn = new RegionNode(4);
+ record_for_igvn(result_rgn);
+
+ Node* skip_call = _gvn.transform(new IfTrueNode(iff));
+ result_rgn->init_req(1, skip_call);
+
+ Node* in1_io = i_o();
+ Node* in1_mem = st_mem;
+ // These two phis are pre-filled with copies of the fast IO and Memory
+ Node* io_phi = PhiNode::make(result_rgn, in1_io, Type::ABIO);
+ Node* mem_phi = PhiNode::make(result_rgn, in1_mem, Type::MEMORY, TypePtr::BOTTOM);
+
+ Node* needs_call = _gvn.transform(new IfFalseNode(iff));
+ set_control(needs_call);
+
+ // InvocationCounter::carry_mask is private, calculate it from step
+ int carry_bits = step - 1;
+ Node* recomp_bit = intcon(carry_bits);
+ Node* rbit = _gvn.transform( new AndINode(incr, recomp_bit) );
+ Node* chk2 = _gvn.transform( new CmpINode(rbit, intcon(0)) );
+ Node* tst2 = _gvn.transform( new BoolNode(chk2, BoolTest::ne) );
+ IfNode* iff2 = create_and_map_if(control(), tst2, PROB_FAIR, COUNT_UNKNOWN);
+
+ Node* skip_call2 = _gvn.transform(new IfTrueNode(iff2));
+ result_rgn->init_req(2, skip_call2);
+
+ Node* needs_call2 = _gvn.transform(new IfFalseNode(iff2));
+ set_control(needs_call2);
+
+ Node* new_val = _gvn.transform(new OrINode(incr, recomp_bit));
+ store_to_memory(control(), cnt_adr, new_val, T_INT, MemNode::unordered);
+
+ const TypePtr* m_type = TypeMetadataPtr::make(method());
+ Node* m = makecon(m_type);
+ make_runtime_call(RC_NO_LEAF | RC_UNCOMMON,
+ OptoRuntime::compile_method_Type(),
+ OptoRuntime::compile_method_Java(),
+ "compile_method", TypePtr::BOTTOM, m);
+
+ // State before call
+ io_phi ->init_req(2, in1_io);
+ mem_phi->init_req(2, in1_mem);
+
+ // State after call
+ result_rgn->init_req(3, control());
+ io_phi ->init_req(3, i_o());
+ mem_phi->init_req(3, reset_memory());
+
+ set_all_memory( _gvn.transform(mem_phi) );
+ set_i_o( _gvn.transform(io_phi) );
+ set_control( _gvn.transform(result_rgn) );
+ }
+}
+
+#endif // INCLUDE_CDS
+
//-----------------------------do_method_entry--------------------------------
// Emit any code needed in the pseudo-block before BCI zero.
// The main thing to do is lock the receiver of a synchronized method.
@@ -1455,6 +1602,8 @@ void Parse::do_method_entry() {
}
}
}
+
+ CDS_ONLY( count_aot_code_calls(); )
}
//------------------------------init_blocks------------------------------------
@@ -1783,7 +1932,7 @@ void Parse::do_one_block() {
if (failing()) return;
assert(!have_se || stopped() || failing() || (sp() - pre_bc_sp) == depth,
- "incorrect depth prediction: sp=%d, pre_bc_sp=%d, depth=%d", sp(), pre_bc_sp, depth);
+ "incorrect depth prediction: bc=%s bci=%d, sp=%d, pre_bc_sp=%d, depth=%d", Bytecodes::name(bc()), bci(), sp(), pre_bc_sp, depth);
do_exceptions();
@@ -2489,6 +2638,9 @@ void Parse::call_register_finalizer() {
// Add check to deoptimize once holder klass is fully initialized.
void Parse::clinit_deopt() {
+ if (method()->holder()->is_initialized()) {
+ return; // in case do_clinit_barriers() is true
+ }
assert(C->has_method(), "only for normal compilations");
assert(depth() == 1, "only for main compiled method");
assert(is_normal_parse(), "no barrier needed on osr entry");
diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp
index fc406cf0b1a..ff2e7c0b0e6 100644
--- a/src/hotspot/share/opto/parse2.cpp
+++ b/src/hotspot/share/opto/parse2.cpp
@@ -1787,7 +1787,7 @@ inline int Parse::repush_if_args() {
}
// Used by StressUnstableIfTraps
-static volatile int _trap_stress_counter = 0;
+volatile int Parse::_trap_stress_counter = 0;
void Parse::increment_trap_stress_counter(Node*& counter, Node*& incr_store) {
Node* counter_addr = makecon(TypeRawPtr::make((address)&_trap_stress_counter));
diff --git a/src/hotspot/share/opto/rootnode.cpp b/src/hotspot/share/opto/rootnode.cpp
index 60167c5436a..bdac33faa44 100644
--- a/src/hotspot/share/opto/rootnode.cpp
+++ b/src/hotspot/share/opto/rootnode.cpp
@@ -22,6 +22,7 @@
*
*/
+#include "code/aotCodeCache.hpp"
#include "memory/allocation.inline.hpp"
#include "opto/callnode.hpp"
#include "opto/cfgnode.hpp"
@@ -70,6 +71,7 @@ HaltNode::HaltNode(Node* ctrl, Node* frameptr, const char* halt_reason, bool rea
init_req(TypeFunc::Memory, top);
init_req(TypeFunc::FramePtr, frameptr );
init_req(TypeFunc::ReturnAdr,top);
+ AOTCodeCache::add_C_string(halt_reason);
}
const Type *HaltNode::bottom_type() const { return Type::BOTTOM; }
diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp
index f5095700a1f..4a0f53f42ae 100644
--- a/src/hotspot/share/opto/runtime.cpp
+++ b/src/hotspot/share/opto/runtime.cpp
@@ -200,6 +200,7 @@ const TypeFunc* OptoRuntime::_fast_arraycopy_Type = nullptr;
const TypeFunc* OptoRuntime::_checkcast_arraycopy_Type = nullptr;
const TypeFunc* OptoRuntime::_generic_arraycopy_Type = nullptr;
const TypeFunc* OptoRuntime::_slow_arraycopy_Type = nullptr;
+const TypeFunc* OptoRuntime::_compile_method_Type = nullptr;
const TypeFunc* OptoRuntime::_unsafe_setmemory_Type = nullptr;
const TypeFunc* OptoRuntime::_array_fill_Type = nullptr;
const TypeFunc* OptoRuntime::_array_sort_Type = nullptr;
@@ -296,6 +297,14 @@ void OptoRuntime::complete_monitor_locking_C(oopDesc* obj, BasicLock* lock, Java
SharedRuntime::complete_monitor_locking_C(obj, lock, current);
}
+JRT_ENTRY(void, OptoRuntime::compile_method_C(Method* method, JavaThread* current))
+ methodHandle m(current, method);
+ CompLevel level = CompLevel_full_optimization;
+ CompileBroker::compile_method(m, InvocationEntryBci, level, 0, nullptr, CompileTask::Reason_MustBeCompiled, current);
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ }
+JRT_END
//=============================================================================
// Opto compiler runtime routines
@@ -711,6 +720,18 @@ static const TypeFunc* make_uncommon_trap_Type() {
return TypeFunc::make(domain, range);
}
+static const TypeFunc* make_compile_method_Type() {
+ // create input type (domain)
+ const Type** fields = TypeTuple::fields(1);
+ fields[TypeFunc::Parms+0] = TypePtr::NOTNULL; // method to be compiled
+ const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields);
+
+ // create result type (range)
+ fields = TypeTuple::fields(0);
+ const TypeTuple* range = TypeTuple::make(TypeFunc::Parms+0,fields);
+ return TypeFunc::make(domain, range);
+}
+
//-----------------------------------------------------------------------------
// Monitor Handling
@@ -2378,6 +2399,7 @@ void OptoRuntime::initialize_types() {
_checkcast_arraycopy_Type = make_arraycopy_Type(ac_checkcast);
_generic_arraycopy_Type = make_arraycopy_Type(ac_generic);
_slow_arraycopy_Type = make_arraycopy_Type(ac_slow);
+ _compile_method_Type = make_compile_method_Type();
_unsafe_setmemory_Type = make_setmemory_Type();
_array_fill_Type = make_array_fill_Type();
_array_sort_Type = make_array_sort_Type();
diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp
index a7c87fb43ab..8985a69f5e8 100644
--- a/src/hotspot/share/opto/runtime.hpp
+++ b/src/hotspot/share/opto/runtime.hpp
@@ -149,6 +149,7 @@ class OptoRuntime : public AllStatic {
static const TypeFunc* _checkcast_arraycopy_Type;
static const TypeFunc* _generic_arraycopy_Type;
static const TypeFunc* _slow_arraycopy_Type;
+ static const TypeFunc* _compile_method_Type;
static const TypeFunc* _unsafe_setmemory_Type;
static const TypeFunc* _array_fill_Type;
static const TypeFunc* _array_sort_Type;
@@ -233,6 +234,7 @@ class OptoRuntime : public AllStatic {
oopDesc* dest, jint dest_pos,
jint length, JavaThread* thread);
static void complete_monitor_locking_C(oopDesc* obj, BasicLock* lock, JavaThread* current);
+ static void compile_method_C(Method* method, JavaThread* current);
public:
static void monitor_notify_C(oopDesc* obj, JavaThread* current);
@@ -299,8 +301,9 @@ class OptoRuntime : public AllStatic {
static address slow_arraycopy_Java() { return _slow_arraycopy_Java; }
static address register_finalizer_Java() { return _register_finalizer_Java; }
- static address load_unknown_value_Java() { return _load_unknown_value_Java; }
- static address store_unknown_value_Java() { return _store_unknown_value_Java; }
+ static address compile_method_Java() { return _compile_method_Java; }
+ static address load_unknown_value_Java() { return _load_unknown_value_Java; }
+ static address store_unknown_value_Java() { return _store_unknown_value_Java; }
static address vthread_end_first_transition_Java() { return _vthread_end_first_transition_Java; }
static address vthread_start_final_transition_Java() { return _vthread_start_final_transition_Java; }
@@ -469,6 +472,11 @@ class OptoRuntime : public AllStatic {
return _slow_arraycopy_Type;
} // the full routine
+ static inline const TypeFunc* compile_method_Type() {
+ assert(_compile_method_Type != nullptr, "should be initialized");
+ return _compile_method_Type;
+ }
+
static inline const TypeFunc* unsafe_setmemory_Type() {
assert(_unsafe_setmemory_Type != nullptr, "should be initialized");
return _unsafe_setmemory_Type;
diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp
index 00d2bc57ff1..8a2a751fb70 100644
--- a/src/hotspot/share/prims/whitebox.cpp
+++ b/src/hotspot/share/prims/whitebox.cpp
@@ -1143,7 +1143,7 @@ bool WhiteBox::compile_method(Method* method, int comp_level, int bci, JavaThrea
bool is_blocking = !matcher.directive_set()->BackgroundCompilationOption;
// Compile method and check result
- nmethod* nm = CompileBroker::compile_method(mh, bci, comp_level, mh->invocation_count(), CompileTask::Reason_Whitebox, CHECK_false);
+ nmethod* nm = CompileBroker::compile_method(mh, bci, comp_level, mh->invocation_count(), nullptr, CompileTask::Reason_Whitebox, CHECK_false);
MutexLocker mu(THREAD, Compile_lock);
bool is_queued = mh->queued_for_compilation();
if ((!is_blocking && is_queued) || nm != nullptr) {
diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp
index b661acf488c..348727c439e 100644
--- a/src/hotspot/share/runtime/deoptimization.cpp
+++ b/src/hotspot/share/runtime/deoptimization.cpp
@@ -26,6 +26,7 @@
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmClasses.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/codeCache.hpp"
#include "code/debugInfoRec.hpp"
#include "code/nmethod.hpp"
@@ -1829,8 +1830,11 @@ static void log_uncommon_trap(nmethod* nm, Method* tm, intptr_t pc, frame& fr, i
if (class_name != nullptr) {
ls.print("%s class_name=%s ", unresolved ? "unresolved" : "", class_name);
}
- ls.print("cid=%4d %s level=%d ",
- nm->compile_id(), (is_osr ? "osr" : ""), nm->comp_level());
+ ls.print("cid=%4d %s%s%s level=%d ",
+ nm->compile_id(), (is_osr ? "osr" : " "),
+ (nm->is_aot() ? "aot " : ""),
+ (nm->aot_preloaded() ? "preload " : ""),
+ nm->comp_level());
ls.print_cr("pc=" INTPTR_FORMAT " relative_pc=" INTPTR_FORMAT,
pc, fr.pc() - nm->code_begin());
@@ -1881,7 +1885,7 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr
gather_statistics(reason, action, trap_bc);
// Ensure that we can record deopt. history:
- bool create_if_missing = ProfileTraps;
+ bool create_if_missing = ProfileTraps || AOTCodeCache::is_using_code();
methodHandle profiled_method;
profiled_method = trap_method;
diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp
index 24d55c2d52d..d30734ac4c1 100644
--- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp
+++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp
@@ -453,3 +453,24 @@ JVMFlag::Error ControlIntrinsicConstraintFunc(ccstrlist value, bool verbose) {
return JVMFlag::SUCCESS;
}
+JVMFlag::Error DisableAOTCodeConstraintFunc(uint value, bool verbose) {
+ const int max_modes = 5;
+ uint original_value = value;
+ for (int i = 0; i < max_modes; i++) {
+ if (value % 10 > 1) {
+ JVMFlag::printError(verbose,
+ "Invalid value (" UINT32_FORMAT ") "
+ "in DisableAOTCode at position %d\n", value, i);
+ return JVMFlag::VIOLATES_CONSTRAINT;
+ }
+ value = value / 10;
+ }
+ if (value != 0) {
+ JVMFlag::printError(verbose,
+ "Invalid value (" UINT32_FORMAT ") "
+ "for DisableAOTCode: maximal %d digits\n", original_value, max_modes);
+ return JVMFlag::VIOLATES_CONSTRAINT;
+ }
+ return JVMFlag::SUCCESS;
+}
+
diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp
index 45e91058e0b..3eaa31f20e6 100644
--- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp
+++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp
@@ -52,6 +52,7 @@
f(intx, InitArrayShortSizeConstraintFunc) \
f(ccstrlist, DisableIntrinsicConstraintFunc) \
f(ccstrlist, ControlIntrinsicConstraintFunc) \
+ f(uint, DisableAOTCodeConstraintFunc) \
COMPILER2_PRESENT( \
f(intx, InteriorEntryAlignmentConstraintFunc) \
f(intx, NodeLimitFudgeFactorConstraintFunc) \
diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp
index 2d1191e8eb0..d5fbd76666e 100644
--- a/src/hotspot/share/runtime/frame.cpp
+++ b/src/hotspot/share/runtime/frame.cpp
@@ -689,6 +689,8 @@ void frame::print_C_frame(outputStream* st, char* buf, int buflen, address pc) {
// First letter indicates type of the frame:
// J: Java frame (compiled)
// j: Java frame (interpreted)
+// A: Java frame (AOT compiled and loaded after class initialization)
+// P: Java frame (AOT compiled and preloaded before class initialization)
// V: VM frame (C/C++)
// v: Other frames running VM generated code (e.g. stubs, adapters, etc.)
// C: C/C++ frame
@@ -729,7 +731,8 @@ void frame::print_on_error(outputStream* st, char* buf, int buflen, bool verbose
nmethod* nm = _cb->as_nmethod();
Method* m = nm->method();
if (m != nullptr) {
- st->print("J %d%s", nm->compile_id(), (nm->is_osr_method() ? "%" : ""));
+ st->print("%s", (nm->aot_preloaded() ? "P" : (nm->is_aot() ? "A" : "J")));
+ st->print(" %d%s", nm->compile_id(), (nm->is_osr_method() ? "%" : ""));
st->print(" %s", nm->compiler_name());
m->name_and_sig_as_C_string(buf, buflen);
st->print(" %s", buf);
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 703b0776cad..31bb9fcc5d5 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -126,7 +126,11 @@ const size_t minimumSymbolTableSize = 1024;
\
product(bool, AOTCompatibleOopCompression, false, DIAGNOSTIC, \
"Always use HeapBasedNarrowOop mode, so that AOT code will " \
- "always work regardless of runtime heap range") \
+ "always work regardless of runtime heap range; this is set " \
+ "by default when assembling an AOT cache which contains code, " \
+ "and by default in a production run that uses that AOT cache; " \
+ "to diagnose possible effects on performance, you can assemble " \
+ "an AOT cache by disabling either AOTCodeCaching or this flag") \
\
product(bool, UseCompactObjectHeaders, true, \
"Use compact 64-bit object headers in 64-bit VM") \
@@ -995,7 +999,8 @@ const int ObjectAlignmentInBytes = 8;
"Use population count instruction") \
\
develop(bool, TraceMethodReplacement, false, \
- "Print when methods are replaced do to recompilation") \
+ "Print when AOT or tiered JIT methods are replaced due to " \
+ "recompilation") \
\
product(intx, MinPassesBeforeFlush, 10, DIAGNOSTIC, \
"Minimum number of sweeper passes before an nmethod " \
diff --git a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp
index 8130bfbc1eb..03d478c2a45 100644
--- a/src/hotspot/share/runtime/init.cpp
+++ b/src/hotspot/share/runtime/init.cpp
@@ -22,6 +22,7 @@
*
*/
+#include "cds/aotMetaspace.hpp"
#include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp"
#include "code/aotCodeCache.hpp"
@@ -129,6 +130,7 @@ jint init_globals() {
bytecodes_init();
classLoader_init1();
compilationPolicy_init();
+ AOTMetaspace::get_aot_code_region_size();
codeCache_init();
VM_Version_init(); // depends on codeCache_init for emitting code
VMRegImpl::set_regName(); // need this before generate_stubs (for printing oop maps).
diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp
index d854db4f131..4ec55fb052f 100644
--- a/src/hotspot/share/runtime/java.cpp
+++ b/src/hotspot/share/runtime/java.cpp
@@ -34,6 +34,7 @@
#include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/codeCache.hpp"
#include "compiler/compilationMemoryStatistic.hpp"
#include "compiler/compilationPolicy.hpp"
@@ -250,9 +251,12 @@ static void print_bytecode_count() {}
// General statistics printing (profiling ...)
-void print_statistics() {
+void print_statistics_before_exit() {
+ LogStreamHandle(Info, aot, codecache, stats) aot_log;
if (CITime) {
CompileBroker::print_times();
+ } else if (aot_log.is_enabled()) {
+ AOTCodeCache::print_timers_on(&aot_log);
}
#ifdef COMPILER1
@@ -285,6 +289,9 @@ void print_statistics() {
if (PrintNMethodStatistics) {
nmethod::print_statistics();
+ AOTCodeCache::print_statistics_on(tty);
+ } else if (aot_log.is_enabled()) {
+ AOTCodeCache::print_statistics_on(&aot_log);
}
if (CountCompiledCalls) {
print_method_invocation_histogram();
@@ -505,7 +512,7 @@ void before_exit(JavaThread* thread, bool halt) {
}
#endif
- print_statistics();
+ print_statistics_before_exit();
{ MutexLocker ml(BeforeExit_lock);
_before_exit_status = BEFORE_EXIT_DONE;
diff --git a/src/hotspot/share/runtime/java.hpp b/src/hotspot/share/runtime/java.hpp
index 2eb8d6b8ead..db2f44f9622 100644
--- a/src/hotspot/share/runtime/java.hpp
+++ b/src/hotspot/share/runtime/java.hpp
@@ -58,6 +58,8 @@ extern void vm_shutdown_during_initialization(const char* error, const char* mes
extern void vm_exit_during_cds_dumping(const char* error, const char* message = nullptr);
+extern void print_statistics_before_exit();
+
// This is defined in linkType.cpp due to linking restraints
extern bool is_vm_statically_linked();
diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
index b0fa8af184d..9e9ac9c65cd 100644
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -3247,7 +3247,7 @@ void AdapterHandlerLibrary::lookup_aot_cache(AdapterHandlerEntry* handler) {
const char* name = AdapterHandlerLibrary::name(handler);
const uint32_t id = AdapterHandlerLibrary::id(handler);
- CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::Adapter, id, name);
+ CodeBlob* blob = AOTCodeCache::load_adapter(id, name);
if (blob != nullptr) {
handler->set_adapter_blob(blob->as_adapter_blob());
}
@@ -3361,7 +3361,7 @@ bool AdapterHandlerLibrary::generate_adapter_code(AdapterHandlerEntry* handler,
// try to save generated code
const char* name = AdapterHandlerLibrary::name(handler);
const uint32_t id = AdapterHandlerLibrary::id(handler);
- bool success = AOTCodeCache::store_code_blob(*adapter_blob, AOTCodeEntry::Adapter, id, name);
+ bool success = AOTCodeCache::store_adapter(*adapter_blob, id, name);
assert(success || !AOTCodeCache::is_dumping_adapter(), "caching of adapter must be disabled");
}
#endif // ZERO
diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp
index cc7f8130982..c8f215082e3 100644
--- a/src/hotspot/share/runtime/stubDeclarations.hpp
+++ b/src/hotspot/share/runtime/stubDeclarations.hpp
@@ -238,6 +238,7 @@
do_stub(monitor_notifyAll, 0, false, false) \
do_stub(rethrow, 2, true, true) \
do_stub(slow_arraycopy, 0, false, false) \
+ do_stub(compile_method, 0, true, false) \
do_stub(register_finalizer, 0, false, false) \
do_stub(load_unknown_value, 0, true, false) \
do_stub(store_unknown_value, 0, true, false) \
diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
index 0e1b8519fc9..601c64c8c63 100644
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -35,6 +35,7 @@
#include "classfile/systemDictionary.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
+#include "code/aotCodeCache.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compilerThread.hpp"
#include "compiler/compileTask.hpp"
@@ -819,6 +820,9 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
initialize_class(vmSymbols::jdk_internal_vm_Continuation(), CHECK_JNI_ERR);
}
+ // Pre-load AOT compiled methods
+ AOTCodeCache::preload_code(CHECK_JNI_ERR);
+
if (NativeHeapTrimmer::enabled()) {
NativeHeapTrimmer::initialize();
}
diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp
index 68f61f08098..d6b26f209f6 100644
--- a/src/hotspot/share/runtime/vmStructs.cpp
+++ b/src/hotspot/share/runtime/vmStructs.cpp
@@ -29,6 +29,7 @@
#include "classfile/javaThreadStatus.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/codeBlob.hpp"
#include "code/codeCache.hpp"
#include "code/compiledIC.hpp"
@@ -544,8 +545,11 @@
nonstatic_field(nmethod, _immutable_data_size, int) \
nonstatic_field(nmethod, _compile_id, int) \
nonstatic_field(nmethod, _comp_level, CompLevel) \
+ nonstatic_field(nmethod, _aot_code_entry, AOTCodeEntry*) \
volatile_nonstatic_field(nmethod, _exception_cache, ExceptionCache*) \
\
+ nonstatic_field(AOTCodeEntry, _for_preload, bool) \
+ \
nonstatic_field(Deoptimization::UnrollBlock, _size_of_deoptimized_frame, int) \
nonstatic_field(Deoptimization::UnrollBlock, _caller_adjustment, int) \
nonstatic_field(Deoptimization::UnrollBlock, _number_of_frames, int) \
@@ -1107,6 +1111,7 @@
declare_toplevel_type(CompileTask) \
declare_toplevel_type(Deoptimization) \
declare_toplevel_type(Deoptimization::UnrollBlock) \
+ declare_toplevel_type(AOTCodeEntry) \
\
/************************/ \
/* ImmutableOopMap */ \
diff --git a/src/hotspot/share/utilities/nativeStackPrinter.cpp b/src/hotspot/share/utilities/nativeStackPrinter.cpp
index b243791c933..f1fa9bdb7a3 100644
--- a/src/hotspot/share/utilities/nativeStackPrinter.cpp
+++ b/src/hotspot/share/utilities/nativeStackPrinter.cpp
@@ -45,7 +45,7 @@ void NativeStackPrinter::print_stack_from_frame(outputStream* st, frame fr,
bool print_source_info, int max_frames) {
// see if it's a valid frame
if (fr.pc()) {
- st->print_cr("Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)");
+ st->print_cr("Native frames: (J=compiled Java code, A=AOT compiled, P=AOT preloaded, j=interpreted, Vv=VM code, C=native code)");
const int limit = max_frames == -1 ? StackPrintLimit
: MIN2(max_frames, StackPrintLimit);
int count = 0;
diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp
index 477155e75a7..8461f1edf6a 100644
--- a/src/hotspot/share/utilities/vmError.cpp
+++ b/src/hotspot/share/utilities/vmError.cpp
@@ -319,7 +319,7 @@ void VMError::print_stack_trace(outputStream* st, JavaThread* jt,
}
#else
if (jt->has_last_Java_frame()) {
- st->print_cr("Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)");
+ st->print_cr("Java frames: (J=compiled Java code, A=AOT compiled, P=AOT preloaded, j=interpreted, Vv=VM code)");
for (StackFrameStream sfs(jt, true /* update */, true /* process_frames */); !sfs.is_done(); sfs.next()) {
sfs.current()->print_on_error(st, buf, buflen, verbose);
st->cr();
diff --git a/test/hotspot/jtreg/ProblemList-AotJdk.txt b/test/hotspot/jtreg/ProblemList-AotJdk.txt
index bb5793cd4a3..e772230bc31 100644
--- a/test/hotspot/jtreg/ProblemList-AotJdk.txt
+++ b/test/hotspot/jtreg/ProblemList-AotJdk.txt
@@ -53,10 +53,21 @@ compiler/intrinsics/klass/TestIsPrimitive.java 0000000 generic-all
# However when AOTClassLinking is enabled, Integer is always resolved at JVM start-up.
compiler/ciReplay/TestInliningProtectionDomain.java 0000000 generic-all
+# The test is very sensetive to profiling date for String class and fail
+# when use training data from AOT cache created from training with
+# different Java application: TestSetupAOT.java
+compiler/stringopts/TestStringConcatIR.java 0000000 generic-all
+
# These tests fail often with AotJdk due to JDK-8323727
compiler/arguments/TestStressReflectiveCode.java 8323727 generic-all
compiler/arraycopy/TestCloneWithStressReflectiveCode.java 8323727 generic-all
+serviceability/sa/TestJhsdbJstackMixedWithXComp.java#xcomp-disable-tiered-compilation 8382548 linux-aarch64
+
+compiler/calls/NativeCalls.java 8383780 generic-all
+
+vmTestbase/vm/mlvm/indy/stress/jdi/breakpointInCompiledCode/Test.java 8383781 generic-all
+
#############################################################################
# Preview project specific failures go here at the end of the file.
diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups
index 54b5d12a484..05af7904a06 100644
--- a/test/hotspot/jtreg/TEST.groups
+++ b/test/hotspot/jtreg/TEST.groups
@@ -553,10 +553,15 @@ tier1_runtime_appcds = \
runtime/cds/appcds/aotCache/HelloAOTCache.java \
runtime/cds/appcds/aotCode \
runtime/cds/appcds/HelloTest.java \
- -runtime/cds/appcds/aotCode/AOTCodeFlags.java
+ -runtime/cds/appcds/aotCode/AOTCodeFlags.java \
+ -runtime/cds/appcds/aotCode/TestAOTCodeCounters.java \
+ -runtime/cds/appcds/aotCode/TestAOTCodeFeatures.java
tier1_runtime_appcds_exclude = \
runtime/cds/appcds/ \
+ runtime/cds/appcds/aotCode/AOTCodeFlags.java \
+ runtime/cds/appcds/aotCode/TestAOTCodeCounters.java \
+ runtime/cds/appcds/aotCode/TestAOTCodeFeatures.java \
-:tier1_runtime_appcds
tier1_runtime_no_cds = \
diff --git a/test/hotspot/jtreg/compiler/compilercontrol/commands/CompileLevelPrintTest.java b/test/hotspot/jtreg/compiler/compilercontrol/commands/CompileLevelPrintTest.java
index 340c55ef99c..1f4562defa5 100644
--- a/test/hotspot/jtreg/compiler/compilercontrol/commands/CompileLevelPrintTest.java
+++ b/test/hotspot/jtreg/compiler/compilercontrol/commands/CompileLevelPrintTest.java
@@ -165,12 +165,12 @@ static class Runner {
private static final Pattern reCompileCommand = Pattern.compile(
"CompileCommand: (.*)");
private static final Pattern reCompilation = Pattern.compile(
- "(\\d+) (C1|C2|no compiler): *(\\d+) ([ %][ s][ !][ b][ n]) ([-0-4 ]) +([^ ]+).*");
+ "(\\d+) (C1|C2|no compiler): *(\\d+) ([ %][ s][ !][ b][ n][ A][ P]) ([-0-4 ]) +([^ ]+).*");
private static final Pattern reExcludeCompile = Pattern.compile(
".*made not compilable on level (\\d) +([^ ]+) .* excluded by CompileCommand");
private static final Pattern reCompiledMethod = Pattern.compile(
".*-{35} Assembly -{35}\\n(?:\\[[0-9.]+s]\\[warning]\\[os] Loading hsdis library failed\\n)?\\nCompiled method \\((?:c1|c2)\\) (\\d+) ([Cc][12]): *"
- + "(\\d+) ([ %][ s][ !][ b][ n]) ([-0-4 ]) +([^ ]+) +(@ -?[0-9]+ +)?\\(\\d+ bytes\\)", Pattern.DOTALL);
+ + "(\\d+) ([ %][ s][ !][ b][ n][ A][ P]) ([-0-4 ]) +([^ ]+) +(@ -?[0-9]+ +)?\\(\\d+ bytes\\)", Pattern.DOTALL);
private static final Pattern reMethodData = Pattern.compile(
".*-{72}\\nstatic ([^\\n]+)\\n *interpreter_invocation_count: *\\d+\\n *invocation_counter: *\\d+", Pattern.DOTALL);
private static final Pattern reEndOfLog = Pattern.compile("");
diff --git a/test/hotspot/jtreg/compiler/inlining/LateInlinePrinting.java b/test/hotspot/jtreg/compiler/inlining/LateInlinePrinting.java
index 88b483076e3..0d1a284a4d4 100644
--- a/test/hotspot/jtreg/compiler/inlining/LateInlinePrinting.java
+++ b/test/hotspot/jtreg/compiler/inlining/LateInlinePrinting.java
@@ -84,19 +84,16 @@ public static void main(String[] args) throws Exception {
OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
analyzer.shouldHaveExitValue(0);
- analyzer.shouldContain("""
-compiler.inlining.LateInlinePrinting$TestLateInlining::test2 (7 bytes)
- @ 0 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined1 (1 bytes) inline (hot) late inline succeeded
- @ 3 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined2 (1 bytes) inline (hot) late inline succeeded
- """);
- analyzer.shouldContain("""
-compiler.inlining.LateInlinePrinting$TestLateInlining::test1 (13 bytes)
- @ 0 compiler.inlining.LateInlinePrinting$TestLateInlining::test3 (1 bytes) inline (hot) late inline succeeded
- @ 3 compiler.inlining.LateInlinePrinting$TestLateInlining::testFailInline (1 bytes) failed to inline: disallowed by CompileCommand
- @ 6 compiler.inlining.LateInlinePrinting$TestLateInlining::testFailInline (1 bytes) failed to inline: disallowed by CompileCommand
- @ 9 compiler.inlining.LateInlinePrinting$TestLateInlining::test2 (7 bytes) inline (hot) late inline succeeded
- @ 0 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined1 (1 bytes) inline (hot) late inline succeeded
- @ 3 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined2 (1 bytes) inline (hot) late inline succeeded
- """);
+ analyzer.shouldContain("compiler.inlining.LateInlinePrinting$TestLateInlining::test2 (7 bytes)");
+ analyzer.shouldContain("@ 0 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined1 (1 bytes) inline (hot) late inline succeeded");
+ analyzer.shouldContain("@ 3 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined2 (1 bytes) inline (hot) late inline succeeded");
+
+ analyzer.shouldContain("compiler.inlining.LateInlinePrinting$TestLateInlining::test1 (13 bytes)");
+ analyzer.shouldContain("@ 0 compiler.inlining.LateInlinePrinting$TestLateInlining::test3 (1 bytes) inline (hot) late inline succeeded");
+ analyzer.shouldContain("@ 3 compiler.inlining.LateInlinePrinting$TestLateInlining::testFailInline (1 bytes) failed to inline: disallowed by CompileCommand");
+ analyzer.shouldContain("@ 6 compiler.inlining.LateInlinePrinting$TestLateInlining::testFailInline (1 bytes) failed to inline: disallowed by CompileCommand");
+ analyzer.shouldContain("@ 9 compiler.inlining.LateInlinePrinting$TestLateInlining::test2 (7 bytes) inline (hot) late inline succeeded");
+ analyzer.shouldContain("@ 0 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined1 (1 bytes) inline (hot) late inline succeeded");
+ analyzer.shouldContain("@ 3 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined2 (1 bytes) inline (hot) late inline succeeded");
}
}
diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java
index 6fe4b31b9ef..1b74232a81f 100644
--- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java
+++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java
@@ -146,6 +146,7 @@ public class TestFramework {
"LogCompilation",
"UseCompactObjectHeaders",
"UseFMA",
+ "AOTCache",
// Riscv
"UseRVV",
"UseZbb",
diff --git a/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass/Launcher.java b/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass/Launcher.java
index 297c0312069..f525c14694b 100644
--- a/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass/Launcher.java
+++ b/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass/Launcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2026, 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
@@ -29,6 +29,8 @@
* java.instrument
* @requires vm.jvmti & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4)
* @requires vm.compMode != "Xcomp"
+ * @comment Can't use AOT cache because -XX:TypeProfileLevel=222 invalidates training data
+ * @requires !vm.cds.supports.aot.code.caching | vm.opt.AOTCache == null
* @build compiler.profiling.spectrapredefineclass.Agent
* @run driver jdk.test.lib.helpers.ClassFileInstaller compiler.profiling.spectrapredefineclass.Agent
* @run driver compiler.profiling.spectrapredefineclass.Launcher
diff --git a/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass_classloaders/Launcher.java b/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass_classloaders/Launcher.java
index e7f5736889c..ee77e86e3ba 100644
--- a/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass_classloaders/Launcher.java
+++ b/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass_classloaders/Launcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2026, 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
@@ -29,6 +29,8 @@
* java.instrument
* @requires vm.jvmti & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4)
* @requires vm.compMode != "Xcomp"
+ * @comment Can't use AOT cache because -XX:TypeProfileLevel=222 invalidates training data
+ * @requires !vm.cds.supports.aot.code.caching | vm.opt.AOTCache == null
* @build compiler.profiling.spectrapredefineclass_classloaders.Agent
* compiler.profiling.spectrapredefineclass_classloaders.Test
* compiler.profiling.spectrapredefineclass_classloaders.A
diff --git a/test/hotspot/jtreg/compiler/runtime/safepoints/TestMachTempsAcrossSafepoints.java b/test/hotspot/jtreg/compiler/runtime/safepoints/TestMachTempsAcrossSafepoints.java
index ecd8f58c5ed..126bd79f5af 100644
--- a/test/hotspot/jtreg/compiler/runtime/safepoints/TestMachTempsAcrossSafepoints.java
+++ b/test/hotspot/jtreg/compiler/runtime/safepoints/TestMachTempsAcrossSafepoints.java
@@ -34,6 +34,8 @@
* @key randomness
* @library /test/lib /
* @requires vm.gc.G1 & vm.bits == 64 & vm.opt.final.UseCompressedOops == true
+ * @comment Can't use AOT cache because training data may affect shape of test's code
+ * @requires !vm.cds.supports.aot.code.caching | vm.opt.AOTCache == null
* @run driver compiler.runtime.safepoints.TestMachTempsAcrossSafepoints
*/
diff --git a/test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java b/test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java
index df297ddbaa3..ce9610d089f 100644
--- a/test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java
+++ b/test/hotspot/jtreg/runtime/cds/MetaspaceAllocGaps.java
@@ -42,9 +42,12 @@ public class MetaspaceAllocGaps {
public static void main(String[] args) throws Exception {
String appJar = ClassFileInstaller.getJarPath("hello.jar");
for (int i = 0; i < 2; i++) {
+ // Exclude AOT training data for consistency.
String compressedOops = "-XX:" + (i == 0 ? "-" : "+") + "UseCompressedOops";
SimpleCDSAppTester.of("MetaspaceAllocGaps" + i)
.addVmArgs("-Xlog:aot=debug,aot+alloc=trace",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:-AOTRecordTraining",
"-XX:+UseCompactObjectHeaders")
.classpath(appJar)
.appCommandLine("Hello")
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompatibleOopCompression.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompatibleOopCompression.java
index 7d557a3b1c7..0aaf2f5fb77 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompatibleOopCompression.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompatibleOopCompression.java
@@ -26,7 +26,7 @@
/*
* @test
* @summary Sanity test for -XX:+AOTCompatibleOopCompression
- * @requires vm.cds.supports.aot.class.linking
+ * @requires vm.cds.supports.aot.code.caching
* @requires vm.bits == 64 & vm.opt.final.UseCompressedOops == true
* @library /test/lib
* @build AOTCompatibleOopCompression
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompileEagerly.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompileEagerly.java
index c740ecd96f5..76f7faf75f2 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompileEagerly.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompileEagerly.java
@@ -83,7 +83,7 @@ public static void main(String[] args) throws Exception {
System.out.println("Production Run with AOTCache and eager compilation explicitly ON");
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
"-XX:AOTCache=" + aotCacheFile,
- "-XX:+UnlockExperimentalVMOptions",
+ "-XX:+UnlockDiagnosticVMOptions",
"-XX:+AOTCompileEagerly",
"-cp", appJar, helloClass);
out = CDSTestUtils.executeAndLog(pb, "prod-eager-on");
@@ -93,7 +93,7 @@ public static void main(String[] args) throws Exception {
System.out.println("Production Run with AOTCache and eager compilation explicitly OFF");
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
"-XX:AOTCache=" + aotCacheFile,
- "-XX:+UnlockExperimentalVMOptions",
+ "-XX:+UnlockDiagnosticVMOptions",
"-XX:-AOTCompileEagerly",
"-cp", appJar, helloClass);
out = CDSTestUtils.executeAndLog(pb, "prod-eager-off");
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TestSetupAOTTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TestSetupAOTTest.java
index 118b4c6af1d..71aabc710ee 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TestSetupAOTTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TestSetupAOTTest.java
@@ -27,7 +27,7 @@
* @summary This is a test case for creating an AOT cache using the setup_aot/TestSetupAOT.java program, which
* is used for running HotSpot tests in the "AOT mode"
* (E.g., make test JTREG=AOT_JDK=true TEST=open/test/hotspot/jtreg/runtime/invokedynamic)
- * @requires vm.cds
+ * @requires vm.cds.supports.aot.class.linking
* @library /test/lib /test/setup_aot
* @build TestSetupAOTTest JavacBenchApp TestSetupAOT
* @run driver jdk.test.lib.helpers.ClassFileInstaller
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java
index 990a5e81e12..6e2cbd54c16 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java
@@ -90,11 +90,11 @@ public static void testIncompatibleFeature(String vmOption, String featureName,
public String[] vmArgs(RunMode runMode) {
if (runMode == RunMode.ASSEMBLY) {
if (mode == IncompatibilityMode.MISSING) {
- return new String[] {"-Xlog:aot+codecache*=debug"};
+ return new String[] {"-Xlog:aot+codecache+exit=debug"};
} else {
// UnlockDiagnosticVMOptions must precede vmOption because
// some tested CPU feature flags are diagnostic (e.g. UseZba on riscv).
- return new String[] {"-XX:+UnlockDiagnosticVMOptions", vmOption, "-Xlog:aot+codecache*=debug"};
+ return new String[] {"-XX:+UnlockDiagnosticVMOptions", vmOption, "-Xlog:aot+codecache+exit=debug"};
}
} else if (runMode == RunMode.PRODUCTION) {
if (mode == IncompatibilityMode.MISSING) {
@@ -102,12 +102,12 @@ public String[] vmArgs(RunMode runMode) {
vmOption,
// Prevent exiting VM on failure
"-XX:-AbortVMOnAOTCodeFailure",
- "-Xlog:aot+codecache*=debug"};
+ "-Xlog:aot+codecache+init=debug"};
} else {
return new String[] {"-XX:+UnlockDiagnosticVMOptions",
// Prevent exiting VM on failure
"-XX:-AbortVMOnAOTCodeFailure",
- "-Xlog:aot+codecache*=debug"};
+ "-Xlog:aot+codecache+init=debug"};
}
}
return new String[]{};
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java
index b0d65fe48be..3d1c3dac962 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java
@@ -26,7 +26,7 @@
* @test
* @summary Sanity test of AOT Code Cache with compressed oops configurations
* @requires vm.cds.supports.aot.code.caching
- * @requires vm.compMode != "Xcomp"
+ * @requires vm.compMode != "Xcomp" & vm.compMode != "Xint"
* @requires vm.bits == 64
* @requires vm.opt.final.UseCompressedOops
* @comment The test verifies AOT checks during VM startup and not code generation.
@@ -183,7 +183,7 @@ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception
line = list.get(i+2);
Matcher m = p.matcher(line);
if (!m.find()) {
- throw new RuntimeException("Pattern \"" + p + "\" not found in the output");
+ throw new RuntimeException("Pattern \"" + p + "\" not found in the output. Got \"" + line + "\"");
}
aotCacheBase = Long.valueOf(m.group(1), 16);
aotCacheShift = Integer.valueOf(m.group(2));
@@ -191,7 +191,7 @@ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception
line = list.get(i+6);
m = p.matcher(line);
if (!m.find()) {
- throw new RuntimeException("Pattern \"" + p + "\" not found in the output");
+ throw new RuntimeException("Pattern \"" + p + "\" not found in the output. Got \"" + line + "\"");
}
currentBase = Long.valueOf(m.group(1), 16);
currentShift = Integer.valueOf(m.group(2));
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java
index d7ff823734c..aa857b0167f 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java
@@ -27,11 +27,14 @@
* @requires vm.gc != "Z"
* @summary Sanity test of combinations of the AOT Code Caching diagnostic flags
* @requires vm.cds.supports.aot.code.caching
+ * @requires vm.compMode != "Xcomp" & vm.compMode != "Xint"
+ * @comment It runs for long time (hours) with -Xcomp and AOT code caching enabled
+ * on (virtual) machine with small number of cores
* @requires vm.compiler1.enabled & vm.compiler2.enabled
* @comment Both C1 and C2 JIT compilers are required because the test verifies
* compiler's runtime blobs generation.
* @library /test/lib /test/setup_aot
- * @build AOTCodeFlags
+ * @build AOTCodeFlags AOTCodeSimpleTestApp
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
* AOTCodeSimpleTestApp
* @run driver/timeout=1500 AOTCodeFlags
@@ -41,11 +44,14 @@
* @requires vm.gc.Z
* @summary Sanity test of combinations of the AOT Code Caching diagnostic flags
* @requires vm.cds.supports.aot.code.caching
+ * @requires vm.compMode != "Xcomp" & vm.compMode != "Xint"
+ * @comment It runs for long time (hours) with -Xcomp and AOT code caching enabled
+ * on (virtual) machine with small number of cores
* @requires vm.compiler1.enabled & vm.compiler2.enabled
* @comment Both C1 and C2 JIT compilers are required because the test verifies
* compiler's runtime blobs generation.
* @library /test/lib /test/setup_aot
- * @build AOTCodeFlags
+ * @build AOTCodeFlags AOTCodeSimpleTestApp
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
* AOTCodeSimpleTestApp
* @run driver/timeout=1500 AOTCodeFlags Z
@@ -55,11 +61,14 @@
* @requires vm.gc.Shenandoah
* @summary Sanity test of combinations of the AOT Code Caching diagnostic flags
* @requires vm.cds.supports.aot.code.caching
+ * @requires vm.compMode != "Xcomp" & vm.compMode != "Xint"
+ * @comment It runs for long time (hours) with -Xcomp and AOT code caching enabled
+ * on (virtual) machine with small number of cores
* @requires vm.compiler1.enabled & vm.compiler2.enabled
* @comment Both C1 and C2 JIT compilers are required because the test verifies
* compiler's runtime blobs generation.
* @library /test/lib /test/setup_aot
- * @build AOTCodeFlags
+ * @build AOTCodeFlags AOTCodeSimpleTestApp
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
* AOTCodeSimpleTestApp
* @run driver/timeout=1500 AOTCodeFlags Shenandoah
@@ -69,11 +78,14 @@
* @requires vm.gc.Parallel
* @summary Sanity test of combinations of the AOT Code Caching diagnostic flags
* @requires vm.cds.supports.aot.code.caching
+ * @requires vm.compMode != "Xcomp" & vm.compMode != "Xint"
+ * @comment It runs for long time (hours) with -Xcomp and AOT code caching enabled
+ * on (virtual) machine with small number of cores
* @requires vm.compiler1.enabled & vm.compiler2.enabled
* @comment Both C1 and C2 JIT compilers are required because the test verifies
* compiler's runtime blobs generation.
* @library /test/lib /test/setup_aot
- * @build AOTCodeFlags
+ * @build AOTCodeFlags AOTCodeSimpleTestApp
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
* AOTCodeSimpleTestApp
* @run driver/timeout=1500 AOTCodeFlags Parallel
@@ -90,10 +102,10 @@ public class AOTCodeFlags {
private static String gcName = null;
public static void main(String... args) throws Exception {
Tester t = new Tester(args.length == 0 ? null : args[0]);
- // mode bits 0 and 1 encode AOTAdapterCaching and AOTStubCaching settings
+ // mode bits 0,1,2 encode AOTAdapterCaching, AOTStubCaching and AOTCodeCaching settings
// aMode is used for assembly run, pMode for production run
- for (int aMode = 0; aMode < 4; aMode++) {
- for (int pMode = 0; pMode < 4; pMode++) {
+ for (int aMode = 0; aMode < 8; aMode++) {
+ for (int pMode = 0; pMode < 8; pMode++) {
t.setTestMode(aMode, pMode);
t.run(new String[] {"AOT", "--two-step-training"});
}
@@ -118,6 +130,10 @@ boolean isStubCachingOn(int mode) {
return (mode & 0x2) != 0;
}
+ boolean isCodeCachingOn(int mode) {
+ return (mode & 0x4) != 0;
+ }
+
public void setTestMode(int aMode, int pMode) {
this.aMode = aMode;
this.pMode = pMode;
@@ -128,6 +144,7 @@ public List getVMArgsForTestMode(int mode) {
list.add("-XX:+UnlockDiagnosticVMOptions");
list.add(isAdapterCachingOn(mode) ? "-XX:+AOTAdapterCaching" : "-XX:-AOTAdapterCaching");
list.add(isStubCachingOn(mode) ? "-XX:+AOTStubCaching" : "-XX:-AOTStubCaching");
+ list.add(isCodeCachingOn(mode) ? "-XX:+AOTCodeCaching" : "-XX:-AOTCodeCaching");
return list;
}
@@ -159,6 +176,7 @@ public String[] vmArgs(RunMode runMode) {
List args = getGCArgs();
args.addAll(List.of("-Xlog:aot+codecache+init=debug",
"-Xlog:aot+codecache+exit=debug",
+ "-Xlog:aot+codecache+nmethod=info",
"-Xlog:aot+codecache+stubs=debug"));
// Ensure compilations are finished before the JVM exits.
@@ -185,61 +203,84 @@ public String[] appCommandLine(RunMode runMode) {
@Override
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
if (runMode == RunMode.ASSEMBLY) {
- if (!isAdapterCachingOn(aMode) && !isStubCachingOn(aMode)) { // this is equivalent to completely disable AOT code cache
- out.shouldNotMatch("Adapters:\\s+total");
- out.shouldNotMatch("Shared Blobs:\\s+total");
- out.shouldNotMatch("C1 Blobs:\\s+total");
- out.shouldNotMatch("C2 Blobs:\\s+total");
+ if (!isAdapterCachingOn(aMode) && !isStubCachingOn(aMode) && !isCodeCachingOn(aMode)) {
+ // this is equivalent to completely disable AOT code cache
+ out.shouldNotMatch("Adapter:\\s+total");
+ out.shouldNotMatch("SharedBlob:\\s+total");
+ out.shouldNotMatch("C1Blob:\\s+total");
+ out.shouldNotMatch("C2Blob:\\s+total");
+ out.shouldNotMatch("StubGenBlob:\\s+total");
+ out.shouldNotMatch("Nmethod:\\s+total");
} else {
if (isAdapterCachingOn(aMode)) {
- // AOTAdapterCaching is on, non-zero adapters should be stored
- out.shouldMatch("Adapters:\\s+total=[1-9][0-9]+");
+ // AOTAdapterCaching is on, adapters should be stored
+ out.shouldMatch("Adapter:\\s+total=[1-9][0-9]+");
} else {
// AOTAdapterCaching is off, no adapters should be stored
- out.shouldMatch("Adapters:\\s+total=0");
+ out.shouldMatch("Adapter:\\s+total=0");
}
if (isStubCachingOn(aMode)) {
- // AOTStubCaching is on, non-zero stubs should be stored
- out.shouldMatch("Shared Blobs:\\s+total=[1-9][0-9]+");
- out.shouldMatch("C1 Blobs:\\s+total=[1-9][0-9]+");
- out.shouldMatch("C2 Blobs:\\s+total=[1-9][0-9]+");
+ // AOTStubCaching is on, stubs should be stored
+ out.shouldMatch("SharedBlob:\\s+total=[1-9][0-9]+");
+ out.shouldMatch("C1Blob:\\s+total=[1-9][0-9]+");
+ out.shouldMatch("C2Blob:\\s+total=[1-9][0-9]+");
+ out.shouldMatch("StubGenBlob:\\s+total=[1-9]+");
} else {
// AOTStubCaching is off, no stubs should be stored
- out.shouldMatch("Shared Blobs:\\s+total=0");
- out.shouldMatch("C1 Blobs:\\s+total=0");
- out.shouldMatch("C2 Blobs:\\s+total=0");
+ out.shouldMatch("SharedBlob:\\s+total=0");
+ out.shouldMatch("C1Blob:\\s+total=0");
+ out.shouldMatch("C2Blob:\\s+total=0");
+ out.shouldMatch("StubGenBlob:\\s+total=0");
+ }
+ if (isCodeCachingOn(aMode)) {
+ // AOTCodeCaching is on, nmethods should be stored
+ out.shouldMatch("Nmethod:\\s+total=[1-9][0-9]+");
+ } else {
+ // AOTCodeCaching is off, no nmethods should be stored
+ out.shouldMatch("Nmethod:\\s+total=0");
}
}
} else if (runMode == RunMode.PRODUCTION) {
- // Irrespective of assembly run mode, if both adapter and stub caching is disabled
+ // Irrespective of assembly run mode, if all types of code caching is disabled
// in production run, then it is equivalent to completely disabling AOT code cache
- if (!isAdapterCachingOn(pMode) && !isStubCachingOn(pMode)) {
- out.shouldNotMatch("Adapters:\\s+total");
- out.shouldNotMatch("Shared Blobs:\\s+total");
- out.shouldNotMatch("C1 Blobs:\\s+total");
- out.shouldNotMatch("C2 Blobs:\\s+total");
+ if (!isAdapterCachingOn(pMode) && !isStubCachingOn(pMode) && !isCodeCachingOn(pMode)) {
+ out.shouldNotMatch("Adapter:\\s+total");
+ out.shouldNotMatch("SharedBlob:\\s+total");
+ out.shouldNotMatch("C1Blob:\\s+total");
+ out.shouldNotMatch("C2Blob:\\s+total");
+ out.shouldNotMatch("StubGenBlob:\\s+total");
+ out.shouldNotMatch("Nmethod:\\s+total");
} else {
// If AOT code cache is effectively disabled in the assembly run, then production run
// would emit empty code cache message.
- if (!isAdapterCachingOn(aMode) && !isStubCachingOn(aMode)) {
- if (isAdapterCachingOn(pMode) || isStubCachingOn(pMode)) {
+ if (!isAdapterCachingOn(aMode) && !isStubCachingOn(aMode) && !isCodeCachingOn(aMode)) {
+ if (isAdapterCachingOn(pMode) || isStubCachingOn(pMode) || isCodeCachingOn(pMode)) {
out.shouldMatch("AOT Code Cache is empty");
}
} else {
if (isAdapterCachingOn(aMode)) {
if (isAdapterCachingOn(pMode)) {
- out.shouldMatch("Read blob.*kind=Adapter.*");
+ out.shouldMatch("Loaded blob.*kind=Adapter.*");
} else {
- out.shouldNotMatch("Read blob.*kind=Adapter.*");
+ out.shouldNotMatch("Loaded blob.*kind=Adapter.*");
}
}
if (isStubCachingOn(aMode)) {
if (isStubCachingOn(pMode)) {
- out.shouldMatch("Read blob.*kind=SharedBlob.*");
- out.shouldMatch("Read blob.*kind=C1Blob.*");
+ out.shouldMatch("Loaded blob.*kind=SharedBlob.*");
+ out.shouldMatch("Loaded blob.*kind=C1Blob.*");
+ out.shouldMatch("Loaded blob.*kind=StubGenBlob.*");
+ } else {
+ out.shouldNotMatch("Loaded blob.*kind=SharedBlob.*");
+ out.shouldNotMatch("Loaded blob.*kind=C1Blob.*");
+ out.shouldNotMatch("Loaded blob.*kind=StubGenBlob.*");
+ }
+ }
+ if (isCodeCachingOn(aMode)) {
+ if (isCodeCachingOn(pMode)) {
+ out.shouldMatch("Loaded nmethod .*");
} else {
- out.shouldNotMatch("Read blob.*kind=SharedBlob.*");
- out.shouldNotMatch("Read blob.*kind=C1Blob.*");
+ out.shouldNotMatch("Loaded nmethod .*");
}
}
}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeTest.java
index a27711b716b..2ff084d2c6e 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeTest.java
@@ -189,10 +189,10 @@ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception
} else if (runMode == RunMode.PRODUCTION) {
out.shouldMatch("aot,codecache,init.*\\s+Loaded [1-9]\\d+ AOT code entries from AOT Code Cache");
if (!withVerifyOops) { // Adapters are not cached for VerifyOops+Valhalla
- out.shouldMatch("aot,codecache,stubs.*\\s+Read blob.*kind=Adapter.*");
+ out.shouldMatch("aot,codecache,stubs.*\\s+Loaded blob.*kind=Adapter.*");
}
- out.shouldMatch("aot,codecache,stubs.*\\s+Read blob.*kind=SharedBlob.*");
- out.shouldMatch("aot,codecache,stubs.*\\s+Read blob.*kind=C1Blob.*");
+ out.shouldMatch("aot,codecache,stubs.*\\s+Loaded blob.*kind=SharedBlob.*");
+ out.shouldMatch("aot,codecache,stubs.*\\s+Loaded blob.*kind=C1Blob.*");
}
}
}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeTestUnknownExternAddress.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeTestUnknownExternAddress.java
new file mode 100644
index 00000000000..6ee6b8e4168
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeTestUnknownExternAddress.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2026, 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.
+ *
+ */
+
+/**
+ * @test
+ * @bug 8391718
+ * @summary Test AOTAssertOnUnknownExternalAddress flag with unknown external address
+ * @requires vm.flagless
+ * @requires vm.debug
+ * @comment the flag is debug
+ * @requires vm.cds.supports.aot.code.caching
+ * @requires vm.compiler2.enabled
+ * @library /test/lib /test/setup_aot
+ * @build ${test.main.class}
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
+ * TestUnknownAddress
+ * @run driver/timeout=480 ${test.main.class}
+ */
+
+import java.lang.reflect.*;
+import sun.misc.*;
+
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+
+public class AOTCodeTestUnknownExternAddress {
+
+ public static void main(String... args) throws Exception {
+ String appJar = ClassFileInstaller.getJarPath("app.jar");
+ String aotConfigFile = "app.aotconfig";
+ String aotCacheFile = "app.aot";
+ String appClass = "TestUnknownAddress";
+
+ ProcessBuilder pb;
+ OutputAnalyzer out;
+
+ // first make sure we have a valid aotConfigFile
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-XX:CompileCommand=compileonly,TestUnknownAddress::test*",
+ "-Xcomp", "-XX:-TieredCompilation",
+ "-Xlog:aot",
+ "-XX:AOTMode=record",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-cp", appJar, appClass);
+
+ out = CDSTestUtils.executeAndLog(pb, "train");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-XX:CompileCommand=compileonly,TestUnknownAddress::test*",
+ "-Xcomp", "-XX:-TieredCompilation",
+ "-Xlog:aot+codecache=debug",
+ "-Xlog:aot+codecache+exit=debug",
+ "-Xlog:aot+codecache+nmethod",
+ "-XX:-AOTAssertOnUnknownExternalAddress",
+ "-XX:AOTMode=create",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-XX:AOTCache=" + aotCacheFile,
+ "-cp", appJar);
+
+ out = CDSTestUtils.executeAndLog(pb, "assemble skip assert");
+ out.shouldNotContain("Address 0xffffffffffffffff is missing in AOT Code Cache addresses table");
+ out.shouldNotMatch("'TestUnknownAddress\\.test1\\(Z\\)V' AOT.*is skipped: store to AOT Code Cache failed");
+ out.shouldMatch("'TestUnknownAddress\\.test1\\(Z\\)V' AOT.*: wrote to AOT Code Cache");
+ out.shouldMatch("'TestUnknownAddress\\.test2\\(Z\\)V' AOT.*is skipped: store to AOT Code Cache failed");
+ out.shouldMatch("aot,codecache,exit.*Wrote [1-9]\\d* AOT code entries to AOT Code Cache");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-XX:CompileCommand=compileonly,TestUnknownAddress::test*",
+ "-Xcomp", "-XX:-TieredCompilation",
+ "-Xlog:aot+codecache+init=debug",
+ "-XX:AOTMode=on",
+ "-XX:AOTCache=" + aotCacheFile,
+ "-cp", appJar, appClass);
+
+ out = CDSTestUtils.executeAndLog(pb, "production");
+ out.shouldMatch("aot,codecache,init.*\\s+Loaded [1-9]\\d* AOT code entries from AOT Code Cache");
+ out.shouldHaveExitValue(0);
+
+ // Assembly run with assert
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-XX:CompileCommand=compileonly,TestUnknownAddress::test*",
+ "-Xcomp", "-XX:-TieredCompilation",
+ "-XX:-CreateCoredumpOnCrash",
+ "-Xlog:aot+codecache=debug",
+ "-Xlog:aot+codecache+exit=debug",
+ "-Xlog:aot+codecache+nmethod",
+ "-XX:+AOTAssertOnUnknownExternalAddress",
+ "-XX:AOTMode=create",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-XX:AOTCache=" + aotCacheFile,
+ "-cp", appJar);
+
+ out = ProcessTools.executeProcess(pb);
+ out.shouldContain("for is missing in AOT Code Cache addresses table");
+ out.shouldNotHaveExitValue(0);
+ }
+}
+
+class TestUnknownAddress {
+ private static Unsafe unsafe;
+ private static final long UNKNOWN_ADDRESS = 0x1234_5678_9000L;
+ private static final long MINUS_1_ADDRESS = -1L;
+
+ static {
+ try {
+ Field field = Unsafe.class.getDeclaredField("theUnsafe");
+ field.setAccessible(true);
+ unsafe = (Unsafe)field.get(null);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void test1(boolean b) {
+ if (b) {
+ unsafe.putLong(MINUS_1_ADDRESS, 42);
+ }
+ }
+
+ public static void test2(boolean b) {
+ if (b) {
+ unsafe.putLong(UNKNOWN_ADDRESS, 42);
+ }
+ }
+
+ public static void main(String[] args) {
+ test1(false);
+ test2(false);
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestAOTCodeCounters.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestAOTCodeCounters.java
new file mode 100644
index 00000000000..0aaa8a66ddf
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestAOTCodeCounters.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2026, 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.
+ *
+ */
+
+/**
+ * @test
+ * @summary Test AOT code invocation counters in C2 compiled AOT code
+ * @requires vm.cds.supports.aot.code.caching
+ * @requires vm.compiler2.enabled
+ * @comment C2 JIT compiler is required because the test verifies
+ * compiled code generation.
+ * @library /test/lib /test/setup_aot
+ * @build ${test.main.class} AOTCodeSimpleTestApp
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
+ * AOTCodeSimpleTestApp
+ * @run driver/timeout=480 ${test.main.class}
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Utils;
+
+public class TestAOTCodeCounters {
+ private static final Random RANDOM = Utils.getRandomInstance();
+
+ // Test default (Base=100. Scale=1.) and limit values.
+ static String randomBase = String.valueOf(RANDOM.nextDouble(1.0, 10000.0));
+ static String randomScale = String.valueOf(RANDOM.nextDouble(0.001, 1000.0));
+ static String[][] flags = {
+ {"-XX:AOTCodeInvokeBase=1.0", "-XX:AOTCodeInvokeScale=0.001"},
+ {"-XX:AOTCodeInvokeBase=1.0", "-XX:AOTCodeInvokeScale=1.0"},
+ {"-XX:AOTCodeInvokeBase=1.0", "-XX:AOTCodeInvokeScale=1000.0"},
+ {"-XX:AOTCodeInvokeBase=100.0", "-XX:AOTCodeInvokeScale=0.001"},
+ {"-XX:AOTCodeInvokeBase=100.0", "-XX:AOTCodeInvokeScale=1.0"},
+ {"-XX:AOTCodeInvokeBase=100.0", "-XX:AOTCodeInvokeScale=1000.0"},
+ {"-XX:AOTCodeInvokeBase=10000.0", "-XX:AOTCodeInvokeScale=0.001"},
+ {"-XX:AOTCodeInvokeBase=10000.0", "-XX:AOTCodeInvokeScale=1.0"},
+ {"-XX:AOTCodeInvokeBase=10000.0", "-XX:AOTCodeInvokeScale=1000.0"},
+ {"-XX:AOTCodeInvokeBase=" + randomBase, "-XX:AOTCodeInvokeScale=" + randomScale}
+ };
+
+ public static void main(String... args) throws Exception {
+ System.out.println("Random: -XX:AOTCodeInvokeBase=" + randomBase + " -XX:AOTCodeInvokeScale=" + randomScale);
+ for (int i = 0; i < flags.length; i++) {
+ Tester t = new Tester(flags[i]);
+ t.run(new String[] {"AOT", "--two-step-training"});
+ }
+ }
+
+ static class Tester extends CDSAppTester {
+ private String[] Counterflags;
+
+ public Tester(String[] flags) {
+ super("TestAOTCodeCounters");
+ Counterflags = flags;
+ }
+
+ @Override
+ public String classpath(RunMode runMode) {
+ return "app.jar";
+ }
+
+ @Override
+ public String[] vmArgs(RunMode runMode) {
+ List args = new ArrayList();
+
+ // Ensure compilations are finished before the JVM exits.
+ args.add("-Xbatch");
+
+ // Add flags for logs
+ args.addAll(List.of("-Xlog:aot+codecache+init=debug",
+ "-Xlog:aot+codecache+nmethod=info",
+ "-Xlog:aot+codecache+exit=debug"));
+ // Add diagnostic flags
+ args.addAll(List.of("-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+UseAOTCodeCounters",
+ "-XX:+AbortVMOnAOTCodeFailure"));
+ // Add feature flag
+ args.addAll(Arrays.asList(Counterflags));
+ return args.toArray(new String[args.size()]);
+ }
+
+ @Override
+ public String[] appCommandLine(RunMode runMode) {
+ return new String[] { "AOTCodeSimpleTestApp" };
+ }
+
+ @Override
+ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
+ if (runMode == RunMode.ASSEMBLY) {
+ out.shouldMatch("aot,codecache,exit.*\\s+AOT code cache size: [1-9]\\d+ bytes");
+ out.shouldMatch("Nmethod:\\s+total=[1-9][0-9]+");
+ } else if (runMode == RunMode.PRODUCTION) {
+ out.shouldMatch("aot,codecache,init.*\\s+Loaded [1-9]\\d+ AOT code entries from AOT Code Cache");
+ out.shouldMatch("Loaded nmethod .*");
+ }
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestAOTCodeFeatures.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestAOTCodeFeatures.java
new file mode 100644
index 00000000000..00a75baa157
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestAOTCodeFeatures.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2026, 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.
+ *
+ */
+
+/**
+ * @test
+ * @summary Sanity test AOTCodeCache flags
+ * @requires vm.cds.supports.aot.code.caching
+ * @requires vm.compiler1.enabled & vm.compiler2.enabled
+ * @comment Both C1 and C2 JIT compilers are required because the test verifies
+ * compiled code generation.
+ * @library /test/lib /test/setup_aot
+ * @build TestAOTCodeFeatures JavacBenchApp
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
+ * JavacBenchApp
+ * JavacBenchApp$ClassFile
+ * JavacBenchApp$FileManager
+ * JavacBenchApp$SourceFile
+ * @run driver/timeout=480 ${test.main.class}
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class TestAOTCodeFeatures {
+
+ // Too long to test both, default and opposite, values.
+ // Only test opposite.
+ static String[] flags = {
+ "-XX:-AOTCompileEagerly",
+ "-XX:ClassInitBarrierMode=0",
+ "-XX:-UseAOTCodeCounters",
+ "-XX:-PreloadReduceTraps",
+ "-XX:+AOTPreloadBlocking",
+ "-XX:AOTCodePreloadStart=2G",
+ "-XX:AOTCodePreloadStop=0"
+ };
+
+ public static void main(String... args) throws Exception {
+ for (int i = 0; i < flags.length; i++) {
+ Tester t = new Tester(flags[i]);
+ t.run(new String[] {"AOT", "--two-step-training"});
+ }
+ }
+
+ static class Tester extends CDSAppTester {
+ private String flagName;
+
+ public Tester(String name) {
+ super("TestAOTCodeFeatures");
+ flagName = name;
+ }
+
+ @Override
+ public String classpath(RunMode runMode) {
+ return "app.jar";
+ }
+
+ @Override
+ public String[] vmArgs(RunMode runMode) {
+ List args = new ArrayList();
+ // Add flags for logs
+ args.addAll(List.of("-Xlog:aot+codecache+init=debug",
+ "-Xlog:aot+codecache+exit=debug"));
+ // Add diagnostic flags
+ args.addAll(List.of("-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+AbortVMOnAOTCodeFailure"));
+ // Add feature flag
+ args.add(flagName);
+ return args.toArray(new String[args.size()]);
+ }
+
+ @Override
+ public String[] appCommandLine(RunMode runMode) {
+ return new String[] { "JavacBenchApp", "1" };
+ }
+
+ @Override
+ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
+ if (runMode == RunMode.ASSEMBLY) {
+ out.shouldMatch("aot,codecache,exit.*\\s+AOT code cache size: [1-9]\\d+ bytes");
+ } else if (runMode == RunMode.PRODUCTION) {
+ out.shouldMatch("aot,codecache,init.*\\s+Loaded [1-9]\\d+ AOT code entries from AOT Code Cache");
+ }
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestDisableAOTCode.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestDisableAOTCode.java
new file mode 100644
index 00000000000..cc295b7231c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestDisableAOTCode.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2026, 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.
+ *
+ */
+
+/**
+ * @test
+ * @summary Sanity test of the diagnostic flag -XX:DisableAOTCode=n
+ * @requires vm.cds.supports.aot.code.caching
+ * @requires vm.flagless
+ * @requires vm.compiler1.enabled & vm.compiler2.enabled
+ * @comment Both C1 and C2 JIT compilers are required because the test verifies
+ * all AOT code tiers generation (except tier 3).
+ * @library /test/lib /test/setup_aot
+ * @build TestDisableAOTCode JavacBenchApp
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
+ * JavacBenchApp
+ * JavacBenchApp$ClassFile
+ * JavacBenchApp$FileManager
+ * JavacBenchApp$SourceFile
+ * @run driver/timeout=480 TestDisableAOTCode
+ */
+
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class TestDisableAOTCode {
+
+ public static void main(String... args) throws Exception {
+ String appJar = ClassFileInstaller.getJarPath("app.jar");
+ String aotConfigFile = "app.aotconfig";
+ String aotCacheFile = "app.aot";
+ String appClass = "JavacBenchApp";
+ String appArgs = "1";
+
+ ProcessBuilder pb;
+ OutputAnalyzer out;
+
+ // first make sure we have a valid aotConfigFile
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot",
+ "-XX:AOTMode=record",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-cp", appJar, appClass, appArgs);
+
+ out = CDSTestUtils.executeAndLog(pb, "train");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+exit",
+ "-XX:AOTMode=create",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:DisableAOTCode=1",
+ "-cp", appJar);
+
+ out = CDSTestUtils.executeAndLog(pb, "assemble_no_AOT_T1");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 1: total=0");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 2:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 3: total=0");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 4:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 5:.*");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+exit",
+ "-XX:AOTMode=create",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:DisableAOTCode=10000",
+ "-cp", appJar);
+
+ out = CDSTestUtils.executeAndLog(pb, "assemble_no_AOT_T5");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 1:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 2:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 3: total=0");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 4:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 5: total=0");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+exit",
+ "-XX:AOTMode=create",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:DisableAOTCode=11111",
+ "-cp", appJar);
+
+ out = CDSTestUtils.executeAndLog(pb, "assemble_no_AOT_all");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 1: total=0");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 2: total=0");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 3: total=0");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 4: total=0");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 5: total=0");
+ out.shouldHaveExitValue(0);
+
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+exit",
+ "-XX:AOTMode=create",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-XX:AOTCache=" + aotCacheFile,
+ "-cp", appJar);
+
+ out = CDSTestUtils.executeAndLog(pb, "assemble_default");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 1:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 2:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 3: total=0");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 4:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 5:.*");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+init",
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+CITime",
+ "-cp", appJar, appClass, appArgs);
+
+ out = CDSTestUtils.executeAndLog(pb, "production_default");
+ out.shouldMatch(" AOT Code T1.*");
+ out.shouldMatch(" AOT Code T2.*");
+ out.shouldMatch(" AOT Code T4.*");
+ out.shouldMatch(" AOT Code T5.*");
+ // Tier 3 AOT code generation is not implemented
+ out.shouldNotMatch("\\s+AOT Code T3.*");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+init",
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+CITime",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:DisableAOTCode=1",
+ "-cp", appJar, appClass, appArgs);
+
+ out = CDSTestUtils.executeAndLog(pb, "production_no_AOT_T1");
+ out.shouldNotMatch(" AOT Code T1.*");
+ out.shouldMatch(" AOT Code T2.*");
+ out.shouldMatch(" AOT Code T4.*");
+ out.shouldMatch(" AOT Code T5.*");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+init",
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+CITime",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:DisableAOTCode=10000",
+ "-cp", appJar, appClass, appArgs);
+
+ out = CDSTestUtils.executeAndLog(pb, "production_no_AOT_T5");
+ out.shouldMatch(" AOT Code T1.*");
+ out.shouldMatch(" AOT Code T2.*");
+ out.shouldMatch(" AOT Code T4.*");
+ out.shouldNotMatch(" AOT Code T5.*");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+init",
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+CITime",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:DisableAOTCode=11111",
+ "-cp", appJar, appClass, appArgs);
+
+ out = CDSTestUtils.executeAndLog(pb, "production_no_AOT_all");
+ out.shouldNotMatch(" AOT Code T1.*");
+ out.shouldNotMatch(" AOT Code T2.*");
+ out.shouldNotMatch(" AOT Code T4.*");
+ out.shouldNotMatch(" AOT Code T5.*");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+init",
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:DisableAOTCode=15",
+ "-cp", appJar, appClass, appArgs);
+
+ out = CDSTestUtils.executeAndLog(pb, "production_wrong_value");
+ out.shouldContain("Improperly specified VM option 'DisableAOTCode=15'");
+ out.shouldHaveExitValue(1);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+init",
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:DisableAOTCode=111111",
+ "-cp", appJar, appClass, appArgs);
+
+ out = CDSTestUtils.executeAndLog(pb, "production_wrong_digits");
+ out.shouldContain("Improperly specified VM option 'DisableAOTCode=111111'");
+ out.shouldHaveExitValue(1);
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestVerifyAOTCode.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestVerifyAOTCode.java
new file mode 100644
index 00000000000..3c88ca3651d
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/TestVerifyAOTCode.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2026, 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.
+ *
+ */
+
+/**
+ * @test
+ * @summary Sanity test of the diagnostic flag -XX:+VerifyAOTCode
+ * @requires vm.cds.supports.aot.code.caching
+ * @requires vm.flagless
+ * @requires vm.compiler1.enabled & vm.compiler2.enabled
+ * @comment Both C1 and C2 JIT compilers are required because the test verifies
+ * all AOT code tiers generation (except tier 3).
+ * @library /test/lib /test/setup_aot
+ * @build TestVerifyAOTCode JavacBenchApp
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
+ * JavacBenchApp
+ * JavacBenchApp$ClassFile
+ * JavacBenchApp$FileManager
+ * JavacBenchApp$SourceFile
+ * @run driver/timeout=480 TestVerifyAOTCode
+ */
+
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class TestVerifyAOTCode {
+
+ public static void main(String... args) throws Exception {
+ String appJar = ClassFileInstaller.getJarPath("app.jar");
+ String aotConfigFile = "app.aotconfig";
+ String aotCacheFile = "app.aot";
+ String appClass = "JavacBenchApp";
+ String appArgs = "10";
+
+ ProcessBuilder pb;
+ OutputAnalyzer out;
+
+ // first make sure we have a valid aotConfigFile
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot",
+ "-XX:AOTMode=record",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-cp", appJar, appClass, appArgs);
+
+ out = CDSTestUtils.executeAndLog(pb, "train");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+exit",
+ "-XX:AOTMode=create",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-XX:AOTCache=" + aotCacheFile,
+ "-cp", appJar);
+
+ out = CDSTestUtils.executeAndLog(pb, "assemble");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 1:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 2:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 3:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 4:.*");
+ out.shouldMatch("aot,codecache,exit.*\\s+Tier 5:.*");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+init",
+ "-Xlog:aot+codecache+nmethod",
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+CITime",
+ "-cp", appJar, appClass, appArgs);
+
+ out = CDSTestUtils.executeAndLog(pb, "production_default");
+ out.shouldMatch("Loaded nmethod from AOT Code Cache");
+ out.shouldMatch(" AOT Code T1.*");
+ out.shouldMatch(" AOT Code T2.*");
+ out.shouldMatch(" AOT Code T4.*");
+ out.shouldMatch(" AOT Code T5.*");
+ // Tier 3 AOT code generation is not implemented
+ out.shouldNotMatch("\\s+AOT Code T3.*");
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot+codecache+init",
+ "-Xlog:aot+codecache+nmethod",
+ "-XX:AOTCache=" + aotCacheFile,
+ "-XX:+CITime",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+VerifyAOTCode",
+ "-cp", appJar, appClass, appArgs);
+
+ out = CDSTestUtils.executeAndLog(pb, "production_VerifyAOTCode");
+ out.shouldMatch("Verified nmethod from AOT Code Cache");
+ out.shouldNotMatch("Loaded nmethod from AOT Code Cache");
+ out.shouldMatch(" AOT Code T1.*");
+ out.shouldMatch(" AOT Code T2.*");
+ out.shouldMatch(" AOT Code T4.*");
+ out.shouldMatch(" AOT Code T5.*");
+ out.shouldHaveExitValue(0);
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java
index fc24f341ff3..9564e619ecd 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java
@@ -25,7 +25,7 @@
/*
* @test
* @summary "AOT" aliases for traditional CDS command-line options
- * @requires vm.cds
+ * @requires vm.cds.supports.aot.code.caching
* @requires vm.flagless
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* @build Hello
@@ -52,6 +52,11 @@ public static void main(String[] args) throws Exception {
}
static void positiveTests() throws Exception {
+ String hasTrainingDataPattern = "MethodTrainingData *= *[1-9]";
+ String hasAOTCodePattern = "Shared file region .ac. .: *[1-9]";
+ String noAOTCodePattern = "Shared file region .ac. .: *0";
+ String hasMappedAOTCodePattern = "Mapped [0-9]+ bytes at address 0x[0-9a-f]+ from AOT Code Cache";
+
//----------------------------------------------------------------------
printTestCase("Training Run");
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
@@ -63,6 +68,8 @@ static void positiveTests() throws Exception {
OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "train");
out.shouldContain("Hello World");
out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
+ out.shouldMatch(hasTrainingDataPattern);
+ out.shouldMatch(noAOTCodePattern);
out.shouldHaveExitValue(0);
//----------------------------------------------------------------------
@@ -78,6 +85,8 @@ static void positiveTests() throws Exception {
out = CDSTestUtils.executeAndLog(pb, "asm");
out.shouldContain("AOTCache creation is complete");
out.shouldMatch("hello[.]aot");
+ out.shouldMatch(hasTrainingDataPattern);
+ out.shouldMatch(hasAOTCodePattern);
out.shouldHaveExitValue(0);
//----------------------------------------------------------------------
@@ -85,11 +94,13 @@ static void positiveTests() throws Exception {
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
"-XX:AOTCache=" + aotCacheFile,
"-Xlog:aot",
+ "-Xlog:aot+codecache*",
"-cp", appJar, helloClass);
out = CDSTestUtils.executeAndLog(pb, "prod");
out.shouldContain("Using AOT-linked classes: true (static archive: has aot-linked classes)");
out.shouldContain("Opened AOT cache hello.aot.");
out.shouldContain("Hello World");
+ out.shouldMatch(hasMappedAOTCodePattern);
out.shouldHaveExitValue(0);
//----------------------------------------------------------------------
@@ -143,7 +154,7 @@ static void positiveTests() throws Exception {
"-XX:-AOTClassLinking",
"-XX:AOTConfiguration=" + aotConfigFile,
"-XX:AOTCache=" + aotCacheFile,
- "-Xlog:aot",
+ "-Xlog:aot=debug",
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+AOTCompatibleOopCompression", // avoid production run failure due to incompatible CompressedOops::base
"-cp", appJar);
@@ -151,6 +162,8 @@ static void positiveTests() throws Exception {
out.shouldContain("AOTClassLinking is updated to true (the same as when AOT configuration file");
out.shouldContain("AOTCache creation is complete");
out.shouldMatch("hello[.]aot");
+ out.shouldMatch(hasTrainingDataPattern);
+ out.shouldMatch(hasAOTCodePattern);
out.shouldHaveExitValue(0);
//----------------------------------------------------------------------
@@ -158,11 +171,13 @@ static void positiveTests() throws Exception {
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
"-XX:AOTCache=" + aotCacheFile,
"-Xlog:aot",
+ "-Xlog:aot+codecache*",
"-cp", appJar, helloClass);
out = CDSTestUtils.executeAndLog(pb, "prod");
out.shouldContain("Using AOT-linked classes: true");
out.shouldContain("Opened AOT cache hello.aot.");
out.shouldContain("Hello World");
+ out.shouldMatch(hasMappedAOTCodePattern);
out.shouldHaveExitValue(0);
//----------------------------------------------------------------------
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotProfile/AOTProfileFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/aotProfile/AOTProfileFlags.java
index 4ad42cb715b..7564f8880ec 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotProfile/AOTProfileFlags.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotProfile/AOTProfileFlags.java
@@ -74,6 +74,7 @@ static void trainAndRun(String testName, String trainingFlags, String production
// first make sure we have a valid aotConfigFile with default value of TypeProfileLevel
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot",
"-XX:AOTMode=record",
"-XX:AOTConfiguration=" + aotConfigFile,
"-XX:+UnlockExperimentalVMOptions",
@@ -84,6 +85,7 @@ static void trainAndRun(String testName, String trainingFlags, String production
out.shouldHaveExitValue(0);
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot",
"-XX:AOTMode=create",
"-XX:AOTConfiguration=" + aotConfigFile,
"-XX:AOTCache=" + aotCacheFile,
@@ -95,6 +97,7 @@ static void trainAndRun(String testName, String trainingFlags, String production
out.shouldHaveExitValue(0);
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot",
"-XX:AOTCache=" + aotCacheFile,
"-XX:+UnlockExperimentalVMOptions",
trainingFlags,
@@ -105,6 +108,7 @@ static void trainAndRun(String testName, String trainingFlags, String production
out.shouldHaveExitValue(0);
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xlog:aot",
"-XX:AOTCache=" + aotCacheFile,
"-XX:+UnlockExperimentalVMOptions",
productionFlags,
diff --git a/test/jdk/ProblemList-AotJdk.txt b/test/jdk/ProblemList-AotJdk.txt
index 37d7a80fa56..cb26a58a1e6 100644
--- a/test/jdk/ProblemList-AotJdk.txt
+++ b/test/jdk/ProblemList-AotJdk.txt
@@ -44,6 +44,16 @@ java/lang/invoke/DumpMethodHandleInternals.java 0000000 generic-
# this message will not be printed in the production run.
java/util/Locale/UseOldISOCodesTest.java 0000000 generic-all
+# These vector tests hit MemLimit during C2 compilation on Aarch64
+jdk/incubator/vector/Vector128ConversionTests.java 8337281 linux-aarch64,macosx-aarch64
+jdk/incubator/vector/Vector256ConversionTests.java 8337281 linux-aarch64,macosx-aarch64
+jdk/incubator/vector/Vector512ConversionTests.java 8337281 linux-aarch64,macosx-aarch64
+jdk/incubator/vector/Vector64ConversionTests.java#id0 8337281 linux-aarch64,macosx-aarch64
+jdk/incubator/vector/VectorMaxConversionTests.java#id0 8337281 linux-aarch64,macosx-aarch64
+
+java/math/BigInteger/SerializationTests.java 8393482 generic-all
+java/util/concurrent/locks/Lock/OOMEInAQS.java 8393490 generic-all
+
#############################################################################
# Preview project specific failures go here at the end of the file.
diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java
index 185d967487c..c47b786e48b 100644
--- a/test/jtreg-ext/requires/VMProps.java
+++ b/test/jtreg-ext/requires/VMProps.java
@@ -487,6 +487,7 @@ protected String vmCDSSupportsAOTClassLinking() {
*/
protected String vmCDSSupportsAOTCodeCaching() {
if ("true".equals(vmCDSSupportsAOTClassLinking()) &&
+ !"true".equals(isPreviewEnabled()) &&
!"zero".equals(vmFlavor()) &&
(Platform.isX64() || Platform.isAArch64() || Platform.isRISCV64())) {
return "true";
diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java
index 4f395321a43..d434e1c1fae 100644
--- a/test/lib/jdk/test/lib/cds/CDSAppTester.java
+++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java
@@ -25,6 +25,7 @@
import java.io.File;
import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.Platform;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.StringArrayUtils;
@@ -243,8 +244,13 @@ private String[] addCommonVMArgs(RunMode runMode) {
// In one-step workflow ASSEMBLY phase is not executed separately.
// Therefore AOTCompatibleOopCompression needs to be passed to the TRAINING phase,
// so that it can be propagated to the ASSEMBLY phase.
- if (runMode == RunMode.TRAINING || runMode == RunMode.ASSEMBLY) {
+ if (isAOTWorkflow() && isDumping(runMode)) {
cmdLine = StringArrayUtils.concat(cmdLine, "-XX:+UnlockDiagnosticVMOptions", "-XX:+AOTCompatibleOopCompression");
+ if (Platform.isDebugBuild()) {
+ // Always assert when AOT test code references new unknown external address.
+ // AOTAssertOnUnknownExternalAddress is debug flag not available in product VM.
+ cmdLine = StringArrayUtils.concat(cmdLine, "-XX:+AOTAssertOnUnknownExternalAddress");
+ }
}
return cmdLine;
}
diff --git a/test/setup_aot/TestSetupAOT.java b/test/setup_aot/TestSetupAOT.java
index 0ab17d6ca99..0a63ae48b14 100644
--- a/test/setup_aot/TestSetupAOT.java
+++ b/test/setup_aot/TestSetupAOT.java
@@ -52,7 +52,7 @@
// This is a larger set of classes than those stored in the JDK's default CDS archive.
// This customized cache can also have additional optimizations that are not
// enabled in the default CDS archive. For example, AOT-linked classes and lambda
-// expressions. In the future, it can also contain AOT profiles and AOT compiled methods.
+// expressions.
//
// We can use this customized AOT cache to run various HotSpot tests to improve
// coverage on AOT.
From 1b0a0558c2f457a835970e76e128173e4b29a6b0 Mon Sep 17 00:00:00 2001
From: Rajan Halade
Date: Thu, 8 Oct 2026 22:02:12 +0000
Subject: [PATCH 011/101] 8385585: EmSignRootG2CA.java test fails with:
couldn't determine EE certificate status
Reviewed-by: mdonovan, mullan
---
.../certification/EmSignRootG2CA.java | 170 ++++++++++--------
1 file changed, 91 insertions(+), 79 deletions(-)
diff --git a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/EmSignRootG2CA.java b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/EmSignRootG2CA.java
index 14e48a6e78f..60c2066e134 100644
--- a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/EmSignRootG2CA.java
+++ b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/EmSignRootG2CA.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, 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
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8319187
+ * @bug 8319187 8385585
* @summary Interoperability tests with eMudhra emSign Root CA G2 CS root
* @build ValidatePathWithParams
* @run main/othervm/manual -Djava.security.debug=certpath EmSignRootG2CA OCSP
@@ -74,86 +74,98 @@ public class EmSignRootG2CA {
"EJK8NSNmK3FNblQfOyFM7VE0uOGHOUwpMGVM2A==\n" +
"-----END CERTIFICATE-----";
- // Owner: CN=test, OU=test, O=test, L=test, ST=test, C=IN
- // Issuer: CN=emSign CS CA - G2, O=eMudhra Technologies Limited, OU=emSign PKI, C=IN
- // Serial number: 7c9ade672c0ad1b6
- // Valid from: Wed Aug 30 05:39:25 PDT 2023 until: Sat Aug 30 05:39:25 PDT 2025
+ // Owner: CN=eMudhra Technologies Limited, O=eMudhra Technologies Limited,
+ // L=Bangalore, ST=Karnataka, C=IN
+ // Issuer: CN=emSign CS CA - G2, O=eMudhra Technologies Limited,
+ // OU=emSign PKI, C=IN
+ // Serial number: e66c2d81f1cf97f024897e285626ad
+ // Valid from: Fri Sep 04 00:59:47 PDT 2026 until: Sat Sep 04 00:59:47
+ // PDT 2027
private static final String VALID = "-----BEGIN CERTIFICATE-----\n" +
- "MIIGNjCCBB6gAwIBAgIIfJreZywK0bYwDQYJKoZIhvcNAQEMBQAwZTELMAkGA1UE\n" +
- "BhMCSU4xEzARBgNVBAsTCmVtU2lnbiBQS0kxJTAjBgNVBAoTHGVNdWRocmEgVGVj\n" +
- "aG5vbG9naWVzIExpbWl0ZWQxGjAYBgNVBAMTEWVtU2lnbiBDUyBDQSAtIEcyMB4X\n" +
- "DTIzMDgzMDEyMzkyNVoXDTI1MDgzMDEyMzkyNVowWDELMAkGA1UEBhMCSU4xDTAL\n" +
- "BgNVBAgTBHRlc3QxDTALBgNVBAcTBHRlc3QxDTALBgNVBAoMBHRlc3QxDTALBgNV\n" +
- "BAsTBHRlc3QxDTALBgNVBAMTBHRlc3QwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw\n" +
- "ggGKAoIBgQC04pOiSFbl7Bd4wFYXzzyukKh+EmwIq8xRGQDkuYH+C6Zao36VAV+k\n" +
- "xGw7lmM3rf4YUcArgZYHfrxgPJNBbGrCi/YnEPYQTNwSrBAePUx1tt13LVBxHfNu\n" +
- "cQQT+kqE7064WsYfmfr/uzJZemqVH7lG82DN23+8E/235AIh3lz/pn7T9ByLj7TV\n" +
- "zWP40oT0UfQXQvWUpFevPONu/RksRP+NiKV3ji6/wYpvrfodzkrGxw2DPfOh4Iam\n" +
- "j6bBH2rkTMToH853plsQGr2ji8OndePfvDdk+5c33Jz1knCNPZSlYQIIp8scyz4z\n" +
- "jaUGdoC140FjEA1SMA2WzpRJoE7xjAidLv7jiV596/bTwrIM+IZhzBc8SKRmkdZ6\n" +
- "lYjPYJHPqRosRtfxcQne3pY6F4s1aOUtuGJaQS/AJkkykZoOx27plWM5SjtmlrL+\n" +
- "7g2/ihWT9CEagYuo44tqk9Tmp3P37+ADAmiXxP0zUxYIv77DSabdArrZ+AB5XUol\n" +
- "V8sxE1V6h0UCAwEAAaOCAXUwggFxMB8GA1UdIwQYMBaAFBWGyrZ0lhdIWDSCLM3S\n" +
- "4XWer0S3MB0GA1UdDgQWBBQ2k0TE2p46sYwI5M/a1XJ8M5Oc8DAOBgNVHQ8BAf8E\n" +
- "BAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwNwYDVR0fBDAwLjAsoCqgKIYmaHR0\n" +
- "cDovL2NybC5lbXNpZ24uY29tP2VtU2lnbkNTQ0FHMi5jcmwwTgYDVR0gBEcwRTA5\n" +
- "BgsrBgEEAYOOIQEAATAqMCgGCCsGAQUFBwIBFhxodHRwOi8vcmVwb3NpdG9yeS5l\n" +
- "bVNpZ24uY29tMAgGBmeBDAEEATBzBggrBgEFBQcBAQRnMGUwIgYIKwYBBQUHMAGG\n" +
- "Fmh0dHA6Ly9vY3NwLmVtU2lnbi5jb20wPwYIKwYBBQUHMAKGM2h0dHA6Ly9yZXBv\n" +
- "c2l0b3J5LmVtc2lnbi5jb20vY2VydHMvZW1TaWduQ1NDQUcyLmNydDAMBgNVHRMB\n" +
- "Af8EAjAAMA0GCSqGSIb3DQEBDAUAA4ICAQBKLa7j8fNpcnWNv7NegrMKTRy7gycI\n" +
- "qrMK848wISX6jl2wg6b275sWQHzQRxA6rbB76bF2HXLFcpITJPaz+vjetYOVQd4v\n" +
- "l8iZN52OpN6Pwrheiz7JhdLiHisN+2NKMmF899bH7w1l2Sr/FQl5vqk41gwwWMen\n" +
- "99Waf4Bp6p3lvBArK2BbabTs8+16xvmkHEK3d3l3Bu6qTEbQRgUI5XsVXmXXn8Pg\n" +
- "IANliTEsbsN9CMWrJ56ciEujU7w2L+IBfvKhl10N1AQNHwpQzwfFyz2BUbACN75o\n" +
- "feIUBarM3ssNzpnt7idgkCTwWVrdEL1NHyW967aEMWyVwaRrtkjFOW/0xuSr2rEI\n" +
- "jBpPj5RPdP6ZEaqnmg5PIgSrJ8FBjx6JmvVgZH/XEl5MZ7PsvJFfIMun6RxXtGn7\n" +
- "QP0+ipkRrI6USNFS84H53Q0WJhQWZUgd3cdm37wpFGvxOVEskIgJNW9SbOgiT9sB\n" +
- "zTIy3ceOK2onmUkDM2Q2+Hbc7A4BmNIlW4fpYXvZlM7IXSl9U3Voks92Hi45azgz\n" +
- "StWZv9+Ronmmp+b7JKCe7MZXIBHfj0JhAVNJiYTZ9BqkY2VRvuQPVUdKxske9fQ6\n" +
- "ciFJ5a6RDOhce6pFloaQu39ci2XCY1N4mIR3vFzpmBNkttlEXviK07XNTv9cnQt6\n" +
- "3CW5aMAsfTbmOw==\n" +
+ "MIIG6zCCBNOgAwIBAgIQAOZsLYHxz5fwJIl+KFYmrTANBgkqhkiG9w0BAQwFADBl\n" +
+ "MQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11\n" +
+ "ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEaMBgGA1UEAxMRZW1TaWduIENTIENB\n" +
+ "IC0gRzIwHhcNMjYwOTA0MDc1OTQ3WhcNMjcwOTA0MDc1OTQ3WjCBgzELMAkGA1UE\n" +
+ "BhMCSU4xEjAQBgNVBAgTCUthcm5hdGFrYTESMBAGA1UEBxMJQmFuZ2Fsb3JlMSUw\n" +
+ "IwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMSUwIwYDVQQDExxl\n" +
+ "TXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMIICIjANBgkqhkiG9w0BAQEFAAOC\n" +
+ "Ag8AMIICCgKCAgEA5Mh6duhdJ4xfZw3Iwnvy7KIauGVXCpX/EOS3ueDOKPCQ8XG+\n" +
+ "pWHP+bs1OioHxWwRAo0AVV/hYr9c+nAyAq0Y0RsZGeBDqZUVK0oECYdWvssPP/0o\n" +
+ "ZUcmiDvDI3NDYKXE8XqPrxd6uHZ0mFkJn+GQHUrTx7gOtNCscijmkeMip9jyLq8B\n" +
+ "m8c9B6TzkSXD6Q2tB76cRuHGLL7FHDzBut6GZElJicsoeI/AQtLfakz02Taea8Ue\n" +
+ "ILyLCnpq5jgij2zqk6ozMDd8lpHYvJCwZ7tFHfuxQBcWy9R2qUAeXBUX+Iymq+Me\n" +
+ "gpv8m3Ov1382p+iP6wpxp/LsDlr+Cg3uil19aFG2GO7WIxiIyDbSY3t6VjhZWFWP\n" +
+ "YbemMXhiKCMWZmX8bKVb8wJYjxUaBDpCoIq8+gj/XglUjq7vpXN8oARLW69z1fJi\n" +
+ "Yt0QAlEDLMcM0Rh3wmEr/sqtu7mkBU7Lp1nnqNx4bPXDf69SalbTnM3ukN7QYpgz\n" +
+ "d/cxzACOQioTwoCDFSHuOBhLuyo81xho9UKzpMR50r7FcePmiLiAtkgl7fZ02U1C\n" +
+ "DincqL+bLtpUxGcSmxNtI3NF+OqpcG+DUjUFDge7k+WgbFxvzahcxH5i5YdzIRf0\n" +
+ "y3NbodvHIYmVPcH0vMv1ntywwP1jy9VuAgT24cj+Es5rmPYy8xogrZa95kkCAwEA\n" +
+ "AaOCAXYwggFyMB8GA1UdIwQYMBaAFBWGyrZ0lhdIWDSCLM3S4XWer0S3MB0GA1Ud\n" +
+ "DgQWBBQy1M8ziJmPioriHY7mJB7+beUk5zAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0l\n" +
+ "BAwwCgYIKwYBBQUHAwMwNwYDVR0fBDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNp\n" +
+ "Z24uY29tP2VtU2lnbkNTQ0FHMi5jcmwwTwYDVR0gBEgwRjA6BgwrBgEEAYOOIQEA\n" +
+ "AQIwKjAoBggrBgEFBQcCARYcaHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbTAI\n" +
+ "BgZngQwBBAEwcwYIKwYBBQUHAQEEZzBlMCIGCCsGAQUFBzABhhZodHRwOi8vb2Nz\n" +
+ "cC5lbVNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVwb3NpdG9yeS5lbXNp\n" +
+ "Z24uY29tL2NlcnRzL2VtU2lnbkNTQ0FHMi5jcnQwDAYDVR0TAQH/BAIwADANBgkq\n" +
+ "hkiG9w0BAQwFAAOCAgEAL9uz2lWq/Dx1P9jot/DUvDS3u7wKf3qw5XH191kQ1B+S\n" +
+ "m9/uhkEptH4lanpXoB/QorvTSkSvu2D2LxQOjHbXdYzLJqDO4NNbFw3S7H5jRVWT\n" +
+ "dRBrb/2VREX5LJ2NBVgxETCpOMyXKgpmefcvVYxZ1jKa80H6Jqd7YzPpFhwsHglX\n" +
+ "/YQX+jYUWjdxkWSo48jlAbi81vUj5snn+AbrFnq8RRy1r22Z5k3zSFNGz2W9iWzS\n" +
+ "UjpSQoaz7e4/L+2flScEydmmv1F2MH9boP5XIZmNXKp6sTr55jZQmQiWysiERPJ2\n" +
+ "0bHhM9Q8qr+Sp8NWVZs5tIYnWJSyBasOctkVvl2ADdrIofR9cmWaP1/EdB5Hl1cx\n" +
+ "Aqo3FKfXK5ZNRHt7B7v11PoBiwzYqk45L03AABNmuZcNC3HkG5emfBrlY0Ud6QuK\n" +
+ "/8Oi4+G01H/6DFCqMqEFR/AFQtzPDnlaEL3XvoZyJcIJARP14XTyXXlUJt5fI0yq\n" +
+ "E6F0KsJCeCRcGKZcjLMWyN+5mPsNdT9EdQOpYJ8dXLtB5r2uN0xsjcOPpFTAIwRt\n" +
+ "fUW5MUFiZZvWqqadhI1EhKhWDTyTn8vJGWNiqXKbbfedck/k/B5Kc75A5nfuBB/u\n" +
+ "awVsnhMuAU7yVUOH37dGv4b4tKRnkW/L+owhEOfbWbiRrtmQ4z/u5+84yxddNiY=\n" +
"-----END CERTIFICATE-----";
- // Owner: CN=test, OU=test, O=test, L=test, ST=test, C=IN
- // Issuer: CN=emSign CS CA - G2, O=eMudhra Technologies Limited, OU=emSign PKI, C=IN
- // Serial number: cf02dedd03d2f509
- // Valid from: Thu Oct 05 22:38:51 PDT 2023 until: Sun Oct 05 22:38:51 PDT 2025
+ // Owner: CN=eMudhra Technologies Limited, O=eMudhra Technologies Limited,
+ // L=Bangalore, ST=Karnataka, C=IN
+ // Issuer: CN=emSign CS CA - G2, O=eMudhra Technologies Limited,
+ // OU=emSign PKI, C=IN
+ // Serial number: 399515f0e9db1ec53b695df8a4216
+ // Valid from: Sat Sep 05 10:12:31 PDT 2026 until: Sun Sep 05 10:12:31
+ // PDT 2027
private static final String REVOKED = "-----BEGIN CERTIFICATE-----\n" +
- "MIIGNzCCBB+gAwIBAgIJAM8C3t0D0vUJMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNV\n" +
- "BAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRl\n" +
- "Y2hub2xvZ2llcyBMaW1pdGVkMRowGAYDVQQDExFlbVNpZ24gQ1MgQ0EgLSBHMjAe\n" +
- "Fw0yMzEwMDYwNTM4NTFaFw0yNTEwMDYwNTM4NTFaMFgxCzAJBgNVBAYTAklOMQ0w\n" +
- "CwYDVQQIEwR0ZXN0MQ0wCwYDVQQHEwR0ZXN0MQ0wCwYDVQQKDAR0ZXN0MQ0wCwYD\n" +
- "VQQLEwR0ZXN0MQ0wCwYDVQQDEwR0ZXN0MIIBojANBgkqhkiG9w0BAQEFAAOCAY8A\n" +
- "MIIBigKCAYEAmUSghjvjUvVgYguH2PMLwW4TwtYsNDpAuGPqux53lI9v9S5u4oAv\n" +
- "m1Sa3MW7CeEnhHNAIFu/AKvNXSfkvnJpTozWstZMjd93DcNacteBG0fBKTkIq+5k\n" +
- "A8qIBiXWk8NORlbjV5bXnoW2pO7wbrALDK3FGf2JAQjuYWXE1mlVk0+SJewUSN+F\n" +
- "XTl63V3tcaqjxhoViY8/dCWc7pNTPgQ/f+Rmnm1bpE0hxVPpQ29+60lyoNtKiOWj\n" +
- "InKRKBV8jYkR/xI13bKWguaxZnswpf2MrophQTvO9ivPHADWhZlNYYjYYEMl4tbi\n" +
- "rG2EquJ7g8Jdo+aL3BggLv5gFkpfoEcaveNuUWy7ggUl7MNhvgDdWdoi6VY7R8Fi\n" +
- "F52+JqPByGpHkZKi0wPa3BaI7guGGyCn3TMe66kNTMS4ADxHktqQlpNSaYYl/84G\n" +
- "lnr2WxQt/W+sXoorlKc/Kh0ubbm6eDzPE8kkIDV2uIxUEgSL7SJQ95yf5XgRihoH\n" +
- "KoBA45iR5vCtAgMBAAGjggF1MIIBcTAfBgNVHSMEGDAWgBQVhsq2dJYXSFg0gizN\n" +
- "0uF1nq9EtzAdBgNVHQ4EFgQUDs5dk74eElzdEKdxIlkzISoWSFkwDgYDVR0PAQH/\n" +
- "BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDcGA1UdHwQwMC4wLKAqoCiGJmh0\n" +
- "dHA6Ly9jcmwuZW1zaWduLmNvbT9lbVNpZ25DU0NBRzIuY3JsME4GA1UdIARHMEUw\n" +
- "OQYLKwYBBAGDjiEBAAEwKjAoBggrBgEFBQcCARYcaHR0cDovL3JlcG9zaXRvcnku\n" +
- "ZW1TaWduLmNvbTAIBgZngQwBBAEwcwYIKwYBBQUHAQEEZzBlMCIGCCsGAQUFBzAB\n" +
- "hhZodHRwOi8vb2NzcC5lbVNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw\n" +
- "b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL2VtU2lnbkNTQ0FHMi5jcnQwDAYDVR0T\n" +
- "AQH/BAIwADANBgkqhkiG9w0BAQwFAAOCAgEAGa2XSoRkoIkHHHGXrdzTBCf/+KgK\n" +
- "FlHhqlBOk5rwLDX1sfNlmsaz10I69phE90Ac8Coa/xCrBaFrTYqRvmkY9gU19jkn\n" +
- "FdVcwQEHNku7Ro/Z/mbyi+aTBzHMTy0Vl4HqVnQInjV891n64SerUuAB7wNVOOho\n" +
- "GoBfpf6lzDzzuEmetFokHYv1tWGQqPF/dHLARQraUlQpWjsnOx0QcZ5cM79REONE\n" +
- "y6uzXT2vaatT3ns8Mtx8zooq+t8pnZlXJqlrwNTcnPad9gSsVu6vfsnWhLhz0VLG\n" +
- "sYPXcWIssLbBQW3v5z0l1Isj7vy2UFfbn8AmZ0PanPo3v3C2sk19DK+Zlc9xBAXc\n" +
- "KKwc4m8le6QkP/EB2wUA7ey5Cf29hjNDJpZznquEaWl9aKbBRdJDKsK88IBJjzK0\n" +
- "Gbpw9fYJ3txuGA7Q27gyaZAeGAIrFvOtRY0XFbr20qSh2GBBYN57+lBPh4UKqgy8\n" +
- "Z2Kk/2jK9k+nm41JYCmwVZHg3Va9RRfW8FkeE95gAUFPDWjeV+GvcimCbcB3DwaZ\n" +
- "9fy1qfV4xsduhC3ei6f7Ask8LbAEWaEIXmgK10YbIfhzomCyCzlA+E+gwkq/bmkv\n" +
- "B8hh27KWA6IRt7URI51MZlh0e8fULyXlOZcoJA/IPX9RdePa2RHFuPSypBHjoN7z\n" +
- "6bCML1XZ2xnHIAg=\n" +
+ "MIIG6jCCBNKgAwIBAgIPA5lRXw6dsexTtpXfikIWMA0GCSqGSIb3DQEBDAUAMGUx\n" +
+ "CzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVk\n" +
+ "aHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRowGAYDVQQDExFlbVNpZ24gQ1MgQ0Eg\n" +
+ "LSBHMjAeFw0yNjA5MDUxNzEyMzFaFw0yNzA5MDUxNzEyMzFaMIGDMQswCQYDVQQG\n" +
+ "EwJJTjESMBAGA1UECBMJS2FybmF0YWthMRIwEAYDVQQHEwlCYW5nYWxvcmUxJTAj\n" +
+ "BgNVBAoMHGVNdWRocmEgVGVjaG5vbG9naWVzIExpbWl0ZWQxJTAjBgNVBAMTHGVN\n" +
+ "dWRocmEgVGVjaG5vbG9naWVzIExpbWl0ZWQwggIiMA0GCSqGSIb3DQEBAQUAA4IC\n" +
+ "DwAwggIKAoICAQDamKtV3CD/F8V6OpeyMvQZakL/CLsYZVSlSHN34u2nxtpG5RIt\n" +
+ "buEMQfZl1lsLQlV39TgoCfrf7kY2S9xLZcMapUGMwT0HoP+rRnyVJpfhgq/ULlX/\n" +
+ "L2KgGxJyUP6XpVcdrlyrHOAvErFhdhdg9L4/tTwOoarHUZCNBB1Wx3Rl1sgCMVbl\n" +
+ "UcbzgJZulYhMJ+deEnHwJTPLO/GTFHek7hGy7bYoD2jhwUgG6Yjae5rZWHnLaKRg\n" +
+ "zACpgQl2sUAtJ7l8Zh0Ig14BTzM0OTfQutPaB3JYr81wZj4awhk39MDf4ob6kcEm\n" +
+ "8KqqYqlARhhFe8XOCZ5IrmQEzie2am0EZICk6g2nBvIcMUgPN9yFOGQdUaSSQe5+\n" +
+ "lA6skGA/ZADVCBf9E1j/tqABdcFdYiPvpInlRxXCK/xxbspupQhffyamqUtj7PT+\n" +
+ "tIdAaruiFV/PIx6MvmNpBhzyPt01QJlAy5GU//5/EnQ7HW/np9jjHr0QdZFbnis+\n" +
+ "O+VloqNSP7UW9eWo1mMwQWLGr/PQHNERGGsPb6gofxUJdKgp4kbg9rqfBkLdYLBV\n" +
+ "jVYAKIpOlP7b93eta5qr0DOImBLor5fvZOs7bljltp68S1mmVxMsvezmSJIcvLvG\n" +
+ "R0G4YWDvobhcCVmEk1nV580+/3OZEMbBk3R4oOB2MUv4eiCYrlpWI0xmjQIDAQAB\n" +
+ "o4IBdjCCAXIwHwYDVR0jBBgwFoAUFYbKtnSWF0hYNIIszdLhdZ6vRLcwHQYDVR0O\n" +
+ "BBYEFMQn0uMfp84zx4vz2EYeBnzyyh9fMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUE\n" +
+ "DDAKBggrBgEFBQcDAzA3BgNVHR8EMDAuMCygKqAohiZodHRwOi8vY3JsLmVtc2ln\n" +
+ "bi5jb20/ZW1TaWduQ1NDQUcyLmNybDBPBgNVHSAESDBGMDoGDCsGAQQBg44hAQAB\n" +
+ "AjAqMCgGCCsGAQUFBwIBFhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMAgG\n" +
+ "BmeBDAEEATBzBggrBgEFBQcBAQRnMGUwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3Nw\n" +
+ "LmVtU2lnbi5jb20wPwYIKwYBBQUHMAKGM2h0dHA6Ly9yZXBvc2l0b3J5LmVtc2ln\n" +
+ "bi5jb20vY2VydHMvZW1TaWduQ1NDQUcyLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqG\n" +
+ "SIb3DQEBDAUAA4ICAQBy0UYUXy/AwPgRmUWu1B0xeq9hU2vFwoFmGbOkd2sJqOOi\n" +
+ "KdIbpjvIdLtPchIasENu+oc55ENgmbD4u5WDxGspBjL18sbE6jmzmGY1dsbCgF17\n" +
+ "Pi9KvlX3LRtD8qTD2oJ5y/YJ7fMKW4j+h4ssNrmA2NboXhgX3e+idQeZ2ItAgV0k\n" +
+ "s3STGRkoOKdsuwpeD/+69nXVIIYusVSgOuYXZb/ViWcL3cMq75cZdR/WYixxU9ZV\n" +
+ "Z55NQKikOGVTuKBpDv9BEa5yqAK32RcajUl7R7bT7AuY+LnTRHtX0P4sCkohj1kP\n" +
+ "9JGoFWN1NIJeCou5GSRmuWLYpnMfi9oDvMPt2e7GUqKkNF+OXTxWfq5A0xtkLWGN\n" +
+ "3qEr9xD34ZnFP5h0c6sE8hzee65erhqSo6LqlQMGINP2gL+fUG+smdX0JQYbX9hP\n" +
+ "raKmp53+aHwolzn0R3t9otgZ6yVdAZLScNLXqDd55/gr/TDrfu4XzRTHK9RCrMzT\n" +
+ "trvbKKtqTuwEp1vQBoced60CI1ZjG41tipF1irhU+pyHk/Kt9YiExp6QZGoBznTY\n" +
+ "9Rvw5No0XoVQHLC56JbsGUot8bVVpGNXzF8PtWBmskqttsw1XTgSUOsnoopfb+Eb\n" +
+ "fpPUdqTDs47FRowITPbj4cgYzYRKWEc7xh8fozZ18lN8yAP6hA2OfDN3j4HrCA==\n" +
"-----END CERTIFICATE-----";
public static void main(String[] args) throws Exception {
@@ -174,6 +186,6 @@ public static void main(String[] args) throws Exception {
// Validate Revoked
pathValidator.validate(new String[]{REVOKED, INT},
ValidatePathWithParams.Status.REVOKED,
- "Thu Oct 05 22:51:36 PDT 2023", System.out);
+ "Sat Sep 05 10:32:30 PDT 2026", System.out);
}
}
From 5ec0824750e3c7576c1e02feb95602a32e426e49 Mon Sep 17 00:00:00 2001
From: huanglb
Date: Fri, 9 Oct 2026 02:22:48 +0000
Subject: [PATCH 012/101] 8391598: RISC-V: Avoid call-patching I-cache flushes
with Ziccid
Reviewed-by: gli, fyang, gcao
---
src/hotspot/cpu/riscv/globals_riscv.hpp | 2 ++
src/hotspot/cpu/riscv/nativeInst_riscv.cpp | 17 ++++++++++++++---
src/hotspot/cpu/riscv/vm_version_riscv.hpp | 2 ++
.../linux_riscv/vm_version_linux_riscv.cpp | 3 ++-
4 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp
index cb2be598762..50bbc7e2e99 100644
--- a/src/hotspot/cpu/riscv/globals_riscv.hpp
+++ b/src/hotspot/cpu/riscv/globals_riscv.hpp
@@ -119,6 +119,8 @@ define_pd_global(bool, ValueTypeReturnedAsFields, false);
product(bool, UseZicbom, false, EXPERIMENTAL, "Use Zicbom instructions") \
product(bool, UseZicbop, false, EXPERIMENTAL, "Use Zicbop instructions") \
product(bool, UseZicboz, false, EXPERIMENTAL, "Use Zicboz instructions") \
+ product(bool, UseZiccid, false, EXPERIMENTAL, \
+ "Use Ziccid to omit icache flushes for atomic call patching") \
product(bool, UseZicond, false, DIAGNOSTIC, "Use Zicond instructions") \
product(bool, UseZihintpause, false, EXPERIMENTAL, \
"Use Zihintpause instructions") \
diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
index 1f757f805b6..abd9423efa6 100644
--- a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
+++ b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020, 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.
@@ -85,8 +85,19 @@ void NativeCall::optimize_call(address dest, bool mt_safe) {
}
// We changed instruction stream
if (mt_safe) {
- // IC invalidate provides a leading full fence, it thus happens after we changed the instruction stream.
- ICache::invalidate_range(jmp_ins_pc, NativeInstruction::instruction_size);
+ if (UseZiccid) {
+ // Ziccid provides in-order instruction fetches and eventual store visibility
+ // without FENCE.I for cacheable, coherent memory. It requires Ziccif, which
+ // guarantees atomic, naturally aligned power-of-two instruction fetches up
+ // to min(ILEN, XLEN) bits. This aligned atomic 32-bit jal/jalr replacement
+ // satisfies Ziccif's patching rules, and old/new calls can safely execute
+ // concurrently, so ICache::invalidate_range can be omitted.
+ // Preserve the full data fence otherwise provided by ICache::invalidate_range.
+ OrderAccess::fence();
+ } else {
+ // IC invalidate provides a leading full fence after the instruction store.
+ ICache::invalidate_range(jmp_ins_pc, NativeInstruction::instruction_size);
+ }
}
}
diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp
index 5c6e1b30943..58901ae4d36 100644
--- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp
+++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp
@@ -281,6 +281,8 @@ class VM_Version : public Abstract_VM_Version {
decl(Zicbop , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbop)) \
/* Zicboz Cache Block Zero Operations */ \
decl(Zicboz , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicboz)) \
+ /* Ziccid Instruction/Data Coherence and Consistency */ \
+ decl(Ziccid , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZiccid)) \
/* Base Counters and Timers */ \
decl(Zicntr , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \
/* Zicond Conditional operations */ \
diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp
index 4c154f3ea40..2b8ac1edb89 100644
--- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp
+++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2023, Rivos Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -378,6 +378,7 @@ void VM_Version::xuantie_features() {
ext_Zicbom.enable_feature();
ext_Zicbop.enable_feature();
ext_Zicboz.enable_feature();
+ ext_Ziccid.enable_feature();
ext_Zicond.enable_feature();
ext_Zicntr.enable_feature();
ext_Zicsr.enable_feature();
From 1fd948de21cf42c6962ad244299c2f1d257133da Mon Sep 17 00:00:00 2001
From: huanglb
Date: Fri, 9 Oct 2026 02:23:28 +0000
Subject: [PATCH 013/101] 8392347: RISC-V: Use 32-byte code entry alignment on
Xuantie
Reviewed-by: gcao, gli, fyang
---
src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp
index 2b8ac1edb89..cc814b958e4 100644
--- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp
+++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp
@@ -393,4 +393,8 @@ void VM_Version::xuantie_features() {
unaligned_scalar.enable_feature(MISALIGNED_SCALAR_FAST);
unaligned_vector.enable_feature(MISALIGNED_VECTOR_FAST);
+
+ if (FLAG_IS_DEFAULT(CodeEntryAlignment)) {
+ FLAG_SET_DEFAULT(CodeEntryAlignment, 32);
+ }
}
From 952037afbf540c30a77f0ad284f98e7f368d8c9c Mon Sep 17 00:00:00 2001
From: Matthias Baesken
Date: Fri, 9 Oct 2026 07:27:26 +0000
Subject: [PATCH 014/101] 8393678: [windows] DefaultProxySelector.c leaks
handle 'session' if initJavaClass fails
Reviewed-by: dfuchs
---
src/java.base/windows/native/libnet/DefaultProxySelector.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/java.base/windows/native/libnet/DefaultProxySelector.c b/src/java.base/windows/native/libnet/DefaultProxySelector.c
index 06bc70114f3..f1cc50e4ce7 100644
--- a/src/java.base/windows/native/libnet/DefaultProxySelector.c
+++ b/src/java.base/windows/native/libnet/DefaultProxySelector.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2026, 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
@@ -65,6 +65,8 @@ Java_sun_net_spi_DefaultProxySelector_init(JNIEnv *env, jclass clazz) {
}
if (!initJavaClass(env)) {
+ WinHttpCloseHandle(session);
+ session = NULL;
return JNI_FALSE;
}
From ee1a2c955eef8638a30d392f1a8ebd93aa49be1e Mon Sep 17 00:00:00 2001
From: Volkan Yazici
Date: Fri, 9 Oct 2026 07:55:36 +0000
Subject: [PATCH 015/101] 8393061: java/nio/channels/Channels/Available.java
failing (lnx)
Reviewed-by: alanb, haosun
---
.../java/nio/channels/Channels/Available.java | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/test/jdk/java/nio/channels/Channels/Available.java b/test/jdk/java/nio/channels/Channels/Available.java
index 6f5c19d783a..9e37598bbb6 100644
--- a/test/jdk/java/nio/channels/Channels/Available.java
+++ b/test/jdk/java/nio/channels/Channels/Available.java
@@ -55,7 +55,7 @@ class Available {
/**
* Test InputStream.available when input stream reads from a SeekableByteChannel.
*/
- void testSeekableByteChannel(SeekableByteChannel ch) throws Exception {
+ private static void testSeekableByteChannel(SeekableByteChannel ch) throws Exception {
InputStream in = Channels.newInputStream(ch);
long size = ch.size();
long[] positions = new long[] {
@@ -68,20 +68,16 @@ void testSeekableByteChannel(SeekableByteChannel ch) throws Exception {
Integer.MAX_VALUE - size + 1L,
Integer.MAX_VALUE - 1L,
Integer.MAX_VALUE,
- Integer.MAX_VALUE + 1L,
- Long.MAX_VALUE - size - 1L,
- Long.MAX_VALUE - size,
- Long.MAX_VALUE - size + 1L,
- Long.MAX_VALUE - 1L,
- Long.MAX_VALUE
+ Integer.MAX_VALUE + 1L
};
for (long pos : positions) {
if (pos >= 0) {
- ch.position(pos);
int expectedAvailable = Math.clamp(size - pos, 0, Integer.MAX_VALUE);
- int available = in.available();
- System.err.format(" position = %d, available = %d%n", pos, available);
- assertEquals(expectedAvailable, available);
+ System.err.format(
+ " size = %d, position = %d, expected available = %d%n",
+ size, pos, expectedAvailable);
+ ch.position(pos);
+ assertEquals(expectedAvailable, in.available());
}
}
}
From f2b6aba29e9f24769844dc862294ecc001f77119 Mon Sep 17 00:00:00 2001
From: Prasanta Sadhukhan
Date: Fri, 9 Oct 2026 07:56:09 +0000
Subject: [PATCH 016/101] 8377972: Provide a way for an application to opt-in
to legacy Desktop.browse() behavior
Reviewed-by: prr, kizune
---
.../sun/lwawt/macosx/CDesktopPeer.java | 5 ++--
.../native/libawt_lwawt/awt/CDesktopPeer.m | 8 ++++---
.../classes/java/awt/peer/DesktopPeer.java | 24 ++++++++++++++++++-
.../classes/sun/awt/windows/WDesktopPeer.java | 9 +++++--
4 files changed, 37 insertions(+), 9 deletions(-)
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java
index cc0e253f23b..9e062c8197d 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2026, 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
@@ -39,7 +39,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
-
/**
* Concrete implementation of the interface {@code DesktopPeer} for MacOS X
*
@@ -80,7 +79,7 @@ public void mail(URI uri) throws IOException {
@Override
public void browse(URI uri) throws IOException {
- this.lsOpen(uri, BROWSE);
+ this.lsOpen(uri, isBrowseInsecureAllowed(uri) ? OPEN : BROWSE);
}
@Override
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m
index faacef5adea..65a1d2bc213 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m
@@ -42,16 +42,18 @@
NSURL *urlToOpen = [NSURL URLWithString:JavaStringToNSString(env, uri)];
NSURL *appURI = nil;
+ NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
- if (action == sun_lwawt_macosx_CDesktopPeer_BROWSE) {
+ if (action == sun_lwawt_macosx_CDesktopPeer_OPEN) {
+ // Open with the app associated with the URI scheme
+ appURI = [workspace URLForApplicationToOpenURL:urlToOpen];
+ } else if (action == sun_lwawt_macosx_CDesktopPeer_BROWSE) {
// To get the defaultBrowser
NSURL *httpsURL = [NSURL URLWithString:@"https://"];
- NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
appURI = [workspace URLForApplicationToOpenURL:httpsURL];
} else if (action == sun_lwawt_macosx_CDesktopPeer_MAIL) {
// To get the default mailer
NSURL *mailtoURL = [NSURL URLWithString:@"mailto://"];
- NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
appURI = [workspace URLForApplicationToOpenURL:mailtoURL];
}
diff --git a/src/java.desktop/share/classes/java/awt/peer/DesktopPeer.java b/src/java.desktop/share/classes/java/awt/peer/DesktopPeer.java
index c1b13a4e8f3..61b595c04a6 100644
--- a/src/java.desktop/share/classes/java/awt/peer/DesktopPeer.java
+++ b/src/java.desktop/share/classes/java/awt/peer/DesktopPeer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, 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
@@ -38,6 +38,7 @@
import java.io.File;
import java.io.IOException;
import java.net.URI;
+import java.util.Arrays;
import javax.swing.JMenuBar;
@@ -281,4 +282,25 @@ default boolean moveToTrash(File file) {
return false;
}
+ /**
+ * Returns whether or not Desktop.browse() will open URI in browser or
+ * in associated allowed app
+ *
+ * @param uri URI to be browsed
+ * @return whether or not associated app will be opened
+ */
+ default boolean isBrowseInsecureAllowed(URI uri) {
+ String allowList = System.getProperty("awt.desktop.browse_insecure");
+ String scheme = uri.getScheme();
+
+ // For http,https scheme URL should always open in browser
+ // For other schemes, it will depend on whether scheme is in allowList
+
+ return scheme != null && !(scheme.equals("http") || scheme.equals("https"))
+ && allowList != null
+ && (allowList.equals("*")
+ || Arrays.stream(allowList.split(","))
+ .map(String::trim)
+ .anyMatch(allowed -> allowed.equalsIgnoreCase(scheme)));
+ }
}
diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java
index e5b628dd74b..06df8223931 100644
--- a/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java
+++ b/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, 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
@@ -105,7 +105,12 @@ public void mail(URI uri) throws IOException {
@Override
public void browse(URI uri) throws IOException {
- this.launchUriInBrowser(uri);
+ if (isBrowseInsecureAllowed(uri)) {
+ this.ShellExecute(uri, ACTION_OPEN_VERB);
+ } else {
+ // Fall back to opening the URI in browser
+ this.launchUriInBrowser(uri);
+ }
}
private void ShellExecute(File file, String verb) throws IOException {
From 118ef3b70780bb95f266c1a88c23c492ce439c7c Mon Sep 17 00:00:00 2001
From: Quan Anh Mai
Date: Fri, 9 Oct 2026 08:19:49 +0000
Subject: [PATCH 017/101] 8393866: C2: TypeInstPtr with klass_is_exact should
not have abstract instance_klass
Reviewed-by: dlong, chagedorn, vlivanov
---
src/hotspot/share/opto/compile.cpp | 24 +++++++++++++++---------
src/hotspot/share/opto/type.cpp | 2 +-
2 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index b238fb91336..ad3ce015257 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -1576,9 +1576,13 @@ const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const {
if (xk && ik->equals(canonical_holder)) {
assert(tj == TypeInstPtr::make(to->ptr(), canonical_holder, is_known_inst, nullptr, Type::Offset(offset), instance_id,
TypePtr::MaybeFlat), "exact type should be canonical type");
+ } else if (is_known_inst) {
+ assert(xk, "Known instance should be exact type");
+ // Keep the type for known instances
+ tj = to = TypeInstPtr::make(to->ptr(), to->instance_klass(), true, nullptr, Type::Offset(offset), instance_id,
+ TypePtr::MaybeFlat);
} else {
- assert(xk || !is_known_inst, "Known instance should be exact type");
- tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, is_known_inst, nullptr, Type::Offset(offset), instance_id,
+ tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, false, nullptr, Type::Offset(offset), instance_id,
TypePtr::MaybeFlat);
}
}
@@ -1762,13 +1766,15 @@ Compile::AliasType* Compile::find_alias_type(const TypePtr* adr_type, bool no_cr
Type::str(adr_type), Type::str(flat), Type::str(flatten_alias_type(flat)));
assert(flat != TypePtr::BOTTOM, "cannot alias-analyze an untyped ptr: adr_type = %s",
Type::str(adr_type));
- if (flat->isa_instptr()) {
- const TypeOopPtr* foop = flat->is_oopptr();
- // Scalarizable allocations have exact klass always.
- bool exact = !foop->klass_is_exact() || foop->is_known_instance();
- const TypePtr* xoop = foop->cast_to_exactness(exact)->is_ptr();
- assert(foop == flatten_alias_type(xoop), "exactness must not affect alias type: foop = %s; xoop = %s",
- Type::str(foop), Type::str(xoop));
+ if (const TypeInstPtr* finst = flat->isa_instptr(); finst != nullptr) {
+ bool check_exact = !finst->klass_is_exact();
+ // Try casting the type to the other exactness if possible
+ if ((check_exact && finst->instance_klass()->is_loaded() && !finst->instance_klass()->is_abstract()) ||
+ (!check_exact && !finst->is_known_instance())) {
+ const TypePtr* xinst = finst->cast_to_exactness(check_exact)->is_ptr();
+ assert(finst == flatten_alias_type(xinst), "exactness must not affect alias type: finst = %s; xinst = %s",
+ Type::str(finst), Type::str(xinst));
+ }
}
}
#endif
diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp
index 2f318b2c3ce..51d7ede5a5b 100644
--- a/src/hotspot/share/opto/type.cpp
+++ b/src/hotspot/share/opto/type.cpp
@@ -4367,7 +4367,7 @@ TypeInstPtr::TypeInstPtr(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces,
assert(k != nullptr &&
(k->is_loaded() || o == nullptr),
"cannot have constants with non-loaded klass");
- assert(!xk || k->is_loaded(), "pointer to an oop of an exact type must be loaded");
+ assert(!xk || (k->is_loaded() && !k->is_abstract()), "pointer to an oop of an exact type must be concrete");
assert(!xk || interfaces->eq(k->as_instance_klass()), "inconsistency between k and interfaces");
};
From 850620f960d873475a4661a751e484b94378511c Mon Sep 17 00:00:00 2001
From: Matthias Baesken
Date: Fri, 9 Oct 2026 08:22:59 +0000
Subject: [PATCH 018/101] 8393706: [windows] DefaultProxySelector.c loop
continues after proxy_array was set to NULL
Reviewed-by: clanger, azeller
---
src/java.base/windows/native/libnet/DefaultProxySelector.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/java.base/windows/native/libnet/DefaultProxySelector.c b/src/java.base/windows/native/libnet/DefaultProxySelector.c
index f1cc50e4ce7..56bae4386d0 100644
--- a/src/java.base/windows/native/libnet/DefaultProxySelector.c
+++ b/src/java.base/windows/native/libnet/DefaultProxySelector.c
@@ -332,20 +332,24 @@ Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env,
jhost = (*env)->NewString(env, current->host, (jsize)wcslen(current->host));
if (jhost == NULL || (*env)->ExceptionCheck(env)) {
proxy_array = NULL;
+ goto noproxy;
}
isa = (*env)->CallStaticObjectMethod(env, isaddr_class,
isaddr_createUnresolvedID, jhost,
current->port);
if (isa == NULL || (*env)->ExceptionCheck(env)) {
proxy_array = NULL;
+ goto noproxy;
}
proxy = (*env)->NewObject(env, proxy_class, proxy_ctrID, type_proxy, isa);
if (proxy == NULL || (*env)->ExceptionCheck(env)) {
proxy_array = NULL;
+ goto noproxy;
}
(*env)->SetObjectArrayElement(env, proxy_array, index, proxy);
if ((*env)->ExceptionCheck(env)) {
proxy_array = NULL;
+ goto noproxy;
}
index++;
}
From 8e48fff3fbdeb0d61ac2d8c7c94a653559f62671 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?=
Date: Fri, 9 Oct 2026 09:46:15 +0000
Subject: [PATCH 019/101] 8377548: Implement dark theme for other specs and
tool guides
Reviewed-by: nbenalla, erikj
---
make/Docs.gmk | 20 +-
.../docs-resources/resources/jdk-default.css | 256 ++++++++++++++++--
.../docs-resources/resources/jdk-default.js | 181 +++++++++++++
make/data/docs-resources/resources/link.svg | 31 +++
.../classes/build/tools/fixuppandoc/Main.java | 33 ++-
.../classes/build/tools/jdwpgen/RootNode.java | 8 +-
.../doclets/formats/html/Navigation.java | 42 +--
.../formats/html/resources/script.js.template | 234 +++++++++-------
.../testNavigation/TestModuleNavigation.java | 18 +-
.../doclet/testNavigation/TestNavigation.java | 39 +--
.../doclet/testSpecTag/TestSpecTag.java | 3 +-
11 files changed, 636 insertions(+), 229 deletions(-)
create mode 100644 make/data/docs-resources/resources/jdk-default.js
create mode 100644 make/data/docs-resources/resources/link.svg
diff --git a/make/Docs.gmk b/make/Docs.gmk
index a4a4bc60363..b9d4a033450 100644
--- a/make/Docs.gmk
+++ b/make/Docs.gmk
@@ -705,22 +705,25 @@ endif
SPEC_HEADER_BLOCK := \
\
-